graph LR P["Difference cover\nN, d"] S["Combinatorial search\nDcGenerator + ThreadPool"] R["Reinforcement learning\nPolicyNetwork"] P --> S P --> R S -->|"exhaustive,\nguaranteed โ "| SOL["Base quorum"] R -->|"learned,\nno guarantee ๐ฒ"| SOL style P fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style S fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style R fill:#fff9c4,stroke:#f57f17,stroke-width:3px style SOL fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px
graph TD T["Search tree"] ROT["Rotation classes\nnecklaces"] BR["Rotation + reflection\nbracelets"] T --> ROT ROT -->|"add CheckRev"| BR ROT -->|"~2ร redundant"| X["equivalent branches\nre-explored โ"] style T fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style ROT fill:#fff9c4,stroke:#f57f17,stroke-width:3px style BR fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style X fill:#f8bbd0,stroke:#c2185b,stroke-width:3px
graph LR A["extend prefix\na[t+1] = max"] --> B{"a[1] == max - a[t]?"} B -->|"no"| C["recurse normally"] B -->|"yes"| D["CheckRev(max)"] D -->|"rev == -1\nreverse smaller"| E["PRUNE โ๏ธ"] D -->|"rev != -1"| F["recurse"] style A fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style B fill:#fff9c4,stroke:#f57f17,stroke-width:3px style D fill:#fff9c4,stroke:#f57f17,stroke-width:3px style E fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style F fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style C fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px
Bracelet symmetry vs necklace-only (speedup ร) 1.0ร 1.2ร N=73, d=9 (1T) 1.17ร N=91, d=10 (1T) 1.14ร N=105, d=11 (10T) 1.11ร N=91, d=10 (10T) 1.07ร Pruning only triggers at the symmetric frontier โ ~10โ17% overall.
Mean residue coverage vs episode 12.0 12.5 13.0 13.5 14.0 0 20k 40k episodes fixed: 12.53 โ 13.91 โ old (no-op grads): 12.29 โ 12.31 (flat) ๐ด
Solution-finding rate โ entropy sign, 16 trials fixed (+ฮฒ) 11 / 16 old (โฮฒ) 5 / 16 The old sign sharpens the policy (H โ 0.05) and gets stuck; +ฮฒ keeps searching. ๐ฒ
graph TD subgraph OLD["Before ยท 2T forwards"] O1["rollout\nforward ร T"] --> O2["backward\nforward ร T again"] end subgraph NEW["After ยท T forwards"] N1["rollout\nforward ร T"] --> N2["backward\nreuse cached โ "] end style O1 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style O2 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style N1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style N2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px
graph LR W1["worker 1"] --> NET["shared network"] W2["worker 2"] --> NET W3["worker N"] --> NET NET --> OUT["mutex only on output โ "] style W1 fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style W2 fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style W3 fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style NET fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style OUT fill:#fff9c4,stroke:#f57f17,stroke-width:3px
40 000 episodes, N=31 d=6 (lower is better) pre-opt ยท 1 thread 75.3 s post-opt ยท 1 thread 50.8 s ยท 1.48ร post-opt ยท 10 threads 5.34 s ยท 9.5ร Cache + buffers: 1.48ร single-thread. Hogwild: 9.5ร scaling on 10 threads.
graph LR B["bounds guard"] --> V["validated input"] V --> A["_GLIBCXX_ASSERTIONS"] A --> O["valid cover โ "] style B fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style V fill:#fff9c4,stroke:#f57f17,stroke-width:3px style A fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px style O fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px