# CRT Torus Embedding: Iteration DAG

Open Direction #1 — tracing iteration paths through modulus space.

---

## 1. DAG Structure

The iteration of F with parameter regeneration forms a Directed Acyclic Graph:

**Nodes:** `(n, A_n, moduli_n, S_n, property_flags)`
- `n`: step index
- `A_n`: current set (integer lifts)
- `moduli_n`: (L₁⁽ⁿ⁾, L₂⁽ⁿ⁾, …, Lₖ⁽ⁿ⁾)
- `S_n`: involution center
- `property_flags`: Sidon? B_h? Golomb?

**Edges:** `(n, A_n, Ω_n, S_n) —[F]→ (n+1, A_{n+1}, Ω_{n+1}, S_{n+1})`
- `A_{n+1} = F_{Ω_n, S_n}(A_n)` (apply F with current moduli)
- `Ω_{n+1}` = next moduli (from regeneration rule)
- `S_{n+1}` = next involution center (fixed or adaptive)

**No cycles by design:** each step changes moduli (geometric growth α, β ≥ 1),
so `Ω_n` is strictly increasing in product M_n = ∏ L_i⁽ⁿ⁾. This prevents revisiting
the same state, keeping the graph acyclic.

---

## 2. Regeneration Rules

| Rule | Ω_{n+1} | S_{n+1} | Branching factor |
|------|----------|---------|------------------|
| Fixed | Ω_n (unchanged) | S_n | 1 (deterministic) |
| Geometric | (α·L₁⁽ⁿ⁾, β·L₂⁽ⁿ⁾) | S_n | 1 per (α,β) choice |
| Adaptive | chosen from candidate set | max(A_n)+min(A_n) | |candidates| per step |
| Exhaustive | primes from pool larger than current | either fixed or adaptive | |pool| per step |

The DAG explores all branches from adaptive/exhaustive rules.

---

## 3. Node Properties

Each node records:

```
Node {
  step: int
  A: List[int]           # current set (sorted)
  moduli: List[int]      # (L1, L2, ..., Lk)
  S: int                 # involution center
  M: int                 # product of moduli
  is_reflection_closed: bool   # A_n == S - A_n?
  is_injective: bool           # M > max(A)?
  sidon_status: bool           # is A_n a Sidon set?
  parent: Optional[NodeID]
  children: List[NodeID]
  depth: int
  terminal: bool               # no further steps possible
}
```

A node is **terminal** when:
- `A_n` is Sidon (goal reached), OR
- `M_n > 2·max(A_n)` (no-sum-alias regime — new collisions can't form,
  but wrapping could still break existing ones; if not already Sidon,
  try different moduli), OR
- `A_n` is F-invariant under current moduli (F(A_n) = A_n), OR
- No valid next moduli exist (Ω exhausted)

---

## 4. Path Tracing

A **path** through the DAG is a sequence of modulus choices:

```
Path P = (Ω₀, Ω₁, …, Ω_{m-1})
  where Ω_i = (L₁⁽ⁱ⁾, L₂⁽ⁱ⁾)
```

Each path transforms A₀ through m steps:

```
A₀ →[Ω₀] A₁ →[Ω₁] A₂ →[Ω₂] … →[Ω_{m-1}] A_m
```

**Goal:** find a path from A₀ to a Sidon set A_m.

### Shortest path search

Since the DAG is acyclic (growing moduli), BFS finds the shortest path:

```
Queue ← [(A₀, Ω₀)]
While Queue not empty:
  (A, Ω) ← pop
  M ← product(Ω)
  if M > 2·max(A): continue (preservation regime, no improvement)
  for each candidate Ω' in next_moduli(Ω):
    A' ← F_{Ω', S}(A)
    if A' is Sidon: return path (success!)
    push (A', Ω')
```

---

## 5. Search Heuristics

Not all modulus choices are equally useful. Heuristics prune the search:

1. **Prime preference** — use small primes as moduli (2,3,5,7,…) for dense
   coverage of the [max(A), 2·max(A)] window.

2. **Gap targeting** — choose moduli that match differences found in Dₐ
   (the M-difference condition). This avoids creating new collisions.

3. **Wrapping bias** — prefer moduli where existing collisions wrap
   differently (condition (a) of the Sidon theorem).

4. **Termination** — stop expanding a branch when M > 2·max(A), since
   F can no longer improve the Sidon status (only preserve).

---

## 6. Implementation

See `scripts/iteration_dag.py` for the DAG tracing implementation.

Example trace:

```
A₀ = {1, 2, 5, 6}, S = 7, Ω₀ = (3, 4), M = 12
  → A₁ = {2, 5, 9, 10}, Sidon = True. Path length 1. ✓

A₀ = {0, 1, 3, 8, 13}, S = 27, Ω₀ = (3, 5), M = 15
  → A₁ = {12, 1, 9, 14, 4}, Sidon = False. New collision.
  → Try Ω₁ = (5, 7):
    → A₂ = F_{5,7}(A₁), M = 35. Check Sidon...
```

---

## 7. Connection to Braid DAG

The iteration DAG is the discrete version of the braid group Cayley graph.
Each step F_{Ω,S} corresponds to a braid word: a sequence of generators
σᵢ that act on the current configuration. The moduli Ω = (L₁, L₂, …, L₁₆)
determine which generators are available (which strands cross).

In the full 16D chiral torus, each step applies a braid word, and the DAG
traces the orbit of A₀ under the braid group action. A terminal Sidon node
corresponds to a braid word that produces a collision-free configuration —
a braid invariant.

---

## 8. Dual-Model DAG Implementation

The Chiral DAG (`scripts/full_chiral_dag.py`) combines both braid models:

| Model | DAG action | Verifies | Verified |
|-------|-----------|----------|----------|
| Axis-swap | σₛ swaps reflection moduli of strands s, s+1 | YB, σ²=id, far commute | ✓ |
| Adjustment | crossing changes modulus values by ±2/±1 | Coprimality bound | ✓ |
| Spacing tracking | capacity_left = min spacing / 2 per strand | Word length bound | ✓ |

### Node structure

Each DAG node stores:
- `pairs`: current chiral pairing (L_id, L_ref) per strand
- `moduli`: flattened 16-modulus vector
- `A`: current set (CRT lifts)
- `M`: product of all moduli
- `capacity_left`: max remaining crossings per strand
- `braid_word`: cumulative braid word from root to this node

### Verified results (3-strand test, A₀ = [1,2,5,6])

| Metric | Value |
|--------|-------|
| Nodes explored | 65 |
| Sidon paths found | 3 |
| Axis-swaps tried | 43 |
| Adjustments tried | 21 |
| Shortest braid word | σ₁ |
| Root capacity | [6, 5, 5] |
| Max modulus (8-strand) | 16637 < 32767 ✓ |

### 8-strand configuration

8 strands × 2 moduli = 16 moduli, all pairwise coprime (product of 4 distinct
primes per strand). With spacing ~100+ between strands, capacity is 50+
crossings per strand.

### Usage

```python
from scripts.full_chiral_dag import ChiralDAG

dag = ChiralDAG(A0, S, n_strands=3, max_steps=8, max_branch=50)
dag.build(use_axis_swap=True, use_adjustment=True)
dag.summary()

# Export for visualization
dag.to_json("/path/to/export.json")
```
