Skip to content

Commit 53d3dce

Browse files
committed
Deep analysis of Python-Clojure discrepancies and fix plan
## Summary Documentation-only PR: deep analysis of Python vs Clojure discrepancies and a TDD fix plan. ### Changes - Deep analysis documents (`deep-analysis-for-julien/`) comparing Python and Clojure implementations statement-by-statement - Consolidate CLAUDE.md documentation for the delphi project - Discrepancy fix plan (`docs/PLAN_DISCREPANCY_FIXES.md`) with prioritized list of fixes ## Test plan - [x] Documentation only — no code changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Squashed commits - Adapt agents background and support Gemini - Add sections - Add deep analysis of Delphi vs Clojure math pipeline - Fix inaccuracies found during statement-by-statement verification - Rename deep analysis folder - Replace AGENTS.md/GEMINI.md symlinks with proper delphi/CLAUDE.md - Address Copilot review: fix doc references, move deep-analysis-for-julien into delphi/docs/ commit-id:d2f65026
1 parent f00accb commit 53d3dce

11 files changed

Lines changed: 2829 additions & 8 deletions

delphi/CLAUDE.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,9 @@
22

33
This document provides comprehensive guidance for working with the Delphi system, including database interactions, environment configuration, Docker services, and the distributed job queue system. It serves as both documentation and a practical reference for day-to-day operations.
44

5-
## Documentation Directory
5+
## Documentation
66

7-
For a comprehensive list of all documentation files with descriptions, see:
8-
[delphi/docs/DOCUMENTATION_DIRECTORY.md](docs/DOCUMENTATION_DIRECTORY.md)
9-
10-
## Current work todos are located in
11-
12-
delphi/docs/JOB_QUEUE_SCHEMA.md
13-
delphi/docs/DISTRIBUTED_SYSTEM_ROADMAP.md
7+
**Warning:** Many docs in `docs/` are outdated and should not be trusted. Always verify against the actual code. Start with `docs/PLAN_DISCREPANCY_FIXES.md` (canonical fix plan) and `docs/CLJ-PARITY-FIXES-JOURNAL.md` (session journal) for current Clojure parity work.
148

159
## Helpful terminology
1610

@@ -368,3 +362,25 @@ The system uses AWS Auto Scaling Groups to manage capacity:
368362
- Large Instance ASG: 1 instance by default, scales up to 3 based on demand
369363

370364
CPU utilization triggers scaling actions (scale down when below 60%, scale up when above 80%).
365+
366+
367+
## Testing
368+
369+
Run tests with `pytest` on the `tests/` folder.
370+
371+
### Datasets of reference
372+
373+
In `real_data`, we have several datasets of real conversations, exported from Polis, that can be used for testing and development. Those at the root of `real_data` are public.
374+
In `real_data/.local`, we have some private datasets that can only be used internally. The comparer supports both public and private datasets via the `--include-local` flag.
375+
376+
### Regressions and golden snapshots
377+
378+
For regressions compared to the latest validated python code, there are both regression unit tests in `tests/`, as well as a test script that compares the output to "golden snapshots": `scripts/regression_comparer.py`. That script is more verbose than the tests, useful for debugging.
379+
380+
Some amount of numerical errors are OK, which is what the regression comparer library is for.
381+
382+
### Old Clojure reference implementation, and moving to Sklearn
383+
384+
For math, there is an older implementation in Clojure, in `polismath`. Until we can replace it, we run comparisons between the two implementations in `tests/*legacy*`. Those run the python code, and compare some of the output in some way to the `math blob`, which is the JSON output of the Clojure implementation, often stored in the PostgreSQL database, but for simplicity stored along the golden (python) snapshots used by the regression comparer, so we do not have to run Postgres nor Clojure to run those tests.
385+
386+
A lot of the current python code was ported from Clojure using an AI agent (Sonnet 3.5 last year), including a lot of home-made implementations of core algorithms. We are in the process of replacing those with standard implementations (such as sklearn for the PCA and K-means). This is ongoing work, and made harder by the fact that the Python code does not quite produce the same output as the Clojure code. So typically we have to check what the ported python code is doing differently from the clojure code, adjust the python code to match the clojure output, and then replace it with standard implementations, which may again produce slightly different output, so we have to adjust parameters until we get similar output.

delphi/docs/PLAN_DISCREPANCY_FIXES.md

Lines changed: 470 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Polis Math Pipeline: Overview and Architecture
2+
3+
## Document Index
4+
5+
This analysis is split across multiple files for manageability:
6+
7+
1. **01-overview-and-architecture.md** (this file) — High-level architecture, data flow, storage
8+
2. **02-pca-analysis.md** — PCA implementations (Clojure vs Python), mathematical formulas
9+
3. **03-clustering-analysis.md** — K-means clustering, silhouette, k-selection
10+
4. **04-repness-analysis.md** — Representativeness metrics, statistical tests, consensus
11+
5. **05-participant-filtering.md** — In-conv logic, comment priorities, vote structures
12+
6. **06-comment-routing.md** — TypeScript comment routing, topical vs prioritized
13+
7. **07-discrepancies.md** — All Clojure vs Python discrepancies (THE KEY DOCUMENT)
14+
8. **08-dead-code.md** — Dead/unreachable code identified
15+
9. **09-fix-plan.md** — Plan to bring Python to Clojure parity
16+
17+
---
18+
19+
## 1. High-Level Architecture
20+
21+
The Polis math pipeline takes raw participant votes on comments and produces:
22+
23+
- **PCA projections**: 2D coordinates for each participant in opinion space
24+
- **Clusters**: Hierarchical grouping (base clusters → group clusters → optional subgroups)
25+
- **Representativeness**: Which comments best characterize each group
26+
- **Consensus**: Comments that all groups broadly agree on
27+
- **Comment priorities**: Scores used to route the next comment to a participant
28+
29+
### 1.1 Two Implementations
30+
31+
| Aspect | Legacy (Clojure) | Delphi (Python) |
32+
|--------|-----------------|-----------------|
33+
| Location | `math/src/polismath/math/` | `delphi/polismath/` |
34+
| Framework | Plumbing Graph (DAG computation) | Imperative class methods |
35+
| Matrix repr | Custom `NamedMatrix` | pandas `DataFrame` |
36+
| PCA method | Power iteration (custom) | sklearn SVD |
37+
| Status | **CORRECT reference** | Has behavioral gaps |
38+
39+
### 1.2 Computation Flow
40+
41+
**Clojure** (`conversation.clj` lines 136–702):
42+
```
43+
votes → customs (cap ptpts/cmts)
44+
→ raw-rating-mat (NamedMatrix)
45+
→ rating-mat (zero out mod-out columns)
46+
→ mat (replace nil with column means)
47+
→ pca (power iteration)
48+
→ proj (sparsity-aware projection)
49+
→ proj-nmat
50+
→ in-conv (filter participants)
51+
→ base-clusters (k-means, k=100)
52+
→ base-clusters-proj → bucket-dists
53+
→ group-clusterings (k=2..max-k)
54+
→ group-clusterings-silhouettes
55+
→ group-k-smoother (buffer before switching k)
56+
→ group-clusters
57+
→ subgroup-clusterings → subgroup-clusters
58+
→ votes-base → group-votes → subgroup-votes
59+
→ comment-priorities
60+
→ group-aware-consensus
61+
→ repness, subgroup-repness
62+
→ consensus
63+
→ ptpt-stats, subgroup-ptpt-stats
64+
```
65+
66+
**Python** (`conversation.py`, `Conversation` class):
67+
```
68+
update_votes() → raw_rating_mat (DataFrame)
69+
→ _apply_moderation() → rating_mat
70+
→ _compute_vote_stats()
71+
→ recompute():
72+
→ _compute_pca()
73+
→ _compute_clusters()
74+
→ _compute_repness()
75+
→ _compute_participant_info()
76+
```
77+
78+
### 1.3 Key Architectural Differences
79+
80+
1. **Plumbing Graph vs Imperative**: Clojure uses a declarative computation graph where each node declares its dependencies. Python uses sequential method calls.
81+
82+
2. **Immutability**: Clojure's pipeline is functionally pure — each step produces new data. Python's `Conversation` class mutates `self` attributes.
83+
84+
3. **Missing in Python**: subgroup clustering, comment priorities computation, large-conv mini-batch PCA, k-smoother buffer, proper consensus selection (Clojure's `consensus-stats` + `select-consensus-comments`).
85+
86+
---
87+
88+
## 2. Data Storage
89+
90+
### 2.1 PostgreSQL (Source of Truth)
91+
92+
**Read by both implementations:**
93+
94+
| Table | Purpose | Key columns |
95+
|-------|---------|-------------|
96+
| `votes` | Individual vote records | `zid, pid, tid, vote, created` |
97+
| `comments` | All comments | `zid, tid, txt, mod, active, is_seed` |
98+
| `math_main` | Serialized math results | `zid, data, math_tick, caching_tick` |
99+
| `math_ticks` | Update tracking | `zid, math_env` |
100+
| `worker_tasks` | Task queue | `zid, math_env, task_type` |
101+
102+
**Vote sign convention at the boundary:**
103+
- Postgres stores: `AGREE = -1`, `DISAGREE = 1` (historical Polis convention)
104+
- Delphi internal: `AGREE = 1`, `DISAGREE = -1` (standard convention)
105+
- `postgres.py` line ~: `postgres_vote_to_delphi()` flips signs at the boundary
106+
107+
### 2.2 DynamoDB (Delphi Output)
108+
109+
Delphi writes results to these DynamoDB tables (`dynamodb.py`):
110+
111+
| Table | Content |
112+
|-------|---------|
113+
| `Delphi_PCAConversationConfig` | PCA config (center, components) |
114+
| `Delphi_PCAResults` | Summary results per conversation |
115+
| `Delphi_KMeansClusters` | Cluster assignments and centers |
116+
| `Delphi_CommentRouting` | Comment priority scores for routing |
117+
| `Delphi_RepresentativeComments` | Per-group representative comments |
118+
| `Delphi_PCAParticipantProjections` | Per-participant 2D projections |
119+
120+
### 2.3 Clojure Math Storage
121+
122+
Clojure serializes its entire conversation state (including all computed fields) into the `math_main` table as a single large JSON blob under the `data` column. The TypeScript server reads this blob via `getPca(zid, 0)` to extract `comment-priorities` for routing.
123+
124+
---
125+
126+
## 3. Pipeline Entry Points
127+
128+
### 3.1 Clojure
129+
130+
- **`conv_man.clj`**: Conversation manager, polls for new votes
131+
- **`conversation.clj:conv-update`** (line 766): Dispatches to `small-conv-update` or `large-conv-update` based on:
132+
- `ptpt-cutoff = 10000` participants
133+
- `cmt-cutoff = 5000` comments
134+
- `small-conv-update` = `eager-profiled-compiler(small-conv-update-graph)`
135+
- `large-conv-update` = same graph but with mini-batch PCA override
136+
137+
### 3.2 Python (Delphi)
138+
139+
- **`poller.py`**: Polls Postgres for new votes/moderation/tasks on separate threads
140+
- **`run_math_pipeline.py`**: CLI tool for one-shot processing
141+
- **`manager.py`**: Thread-safe management of multiple `Conversation` objects
142+
- **`conversation.py:Conversation.update_votes()`**: Main entry, calls `recompute()`
143+
144+
### 3.3 Default Configuration (Clojure)
145+
146+
From `conversation.clj` lines 142–152:
147+
```clojure
148+
{:n-comps 2
149+
:pca-iters 100
150+
:base-iters 100
151+
:base-k 100
152+
:max-k 5
153+
:group-iters 100
154+
:max-ptpts 100000
155+
:max-cmts 10000
156+
:group-k-buffer 4}
157+
```
158+
159+
Python matches `BASE_K=100`, `MAX_K=5`, but is missing `group-k-buffer` entirely.

0 commit comments

Comments
 (0)