Add Manim visualization for Axplorer Turán C_4 problem solver#2
Add Manim visualization for Axplorer Turán C_4 problem solver#2honicky wants to merge 10 commits into
Conversation
…em (V1) Hand-curated trajectory through the search→train→sample→search flywheel for ex(15, C_4): Act 1 plateaus at 24 edges, Act 2 a transformer trained on top-k emits near-bipartite samples, Act 3 the full loop climbs to the proven optimum (30). graph_utils (count_edges / find_4_cycles / has_4_cycle) is tested against C_4, K_4, the Petersen graph and 15-vertex trees; trajectory invariants are tested too. V2 will swap trajectory.py for real Axplorer logs. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
Checked in at axplorer-viz/preview/ so the V1 design can be reviewed without rendering. media/ stays git-ignored; this single preview will be removed once V2 wires up real Axplorer data. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84edada1a9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| from act1_naive_search import play_act1 # noqa: E402 | ||
| from act2_transformer import play_act2 # noqa: E402 | ||
| from act3_flywheel import play_act3 # noqa: E402 |
There was a problem hiding this comment.
Import act modules from the scenes directory
FullVideo prepends src to sys.path and then imports act1_naive_search / act2_transformer / act3_flywheel as top-level modules, but those files live in src/scenes. When this file is loaded from its path (as in the README render command), Python resolves those imports against src and raises ModuleNotFoundError, so the full-video render entrypoint cannot start unless src/scenes is manually added to PYTHONPATH.
Useful? React with 👍 / 👎.
| s2_label = Text(f"sample 2 · {s2['score']} edges", font=S.FONT, color=S.SCORE_COLOR, font_size=24) | ||
| s2_label.move_to(s1_label).set_x(OUT_CENTER[0]) | ||
|
|
||
| scene.play(FadeOut(sample1), FadeOut(struct_label), run_time=0.7) |
There was a problem hiding this comment.
Fade out the actual on-screen label before sample switch
After s1_label.animate.become(struct_label), the on-screen text is still the s1_label mobject; struct_label is only a template object and is never added to the scene. Fading out struct_label therefore leaves the displayed label visible during sample 2, causing overlapping/incorrect text in this transition.
Useful? React with 👍 / 👎.
- full_video.py: also add src/scenes/ to sys.path so the sibling act modules import even when the file isn't loaded via `manim` (which happens to add the file's dir to sys.path); now works as a plain import too. - act2_transformer.py: FadeOut the on-screen `s1_label` (which .become'd the struct label), not the never-added `struct_label` template, so the label doesn't linger and overlap "sample 2" during the transition. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
Re-rendered 1080p preview so the checked-in copy includes the fixed sample-1 -> sample-2 transition (no overlapping label). https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
…endor V2 of axplorer-viz will drive the Manim scenes from a real Axplorer run instead of the V1 hand-curated trajectory. This is the unmodified upstream checkout (commit 3298b1a); a minimal trajectory-logging patch follows in the next commit. Not affiliated with Axiom Math; attribution/LICENSE preserved. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
Minimal, well-commented changes (each marked `# [axplorer-viz patch]`): - train.py: --log_trajectory flag; one JSONL line/epoch after the Selection phase with top_k_objects/scores, model_samples_raw, model_samples_after_search, best_score_so_far, wall_time_seconds; also seeds random+numpy from --seed. - src/evaluator.py: sample_and_score grows an optional raw_token_out= list to surface a few pre-local-search samples (no behaviour change when unused). - src/envs/cycle.py: drop np.random.seed(None) in _add_edges_greedily so --seed is actually honored. See axplorer-viz/vendor/PATCH_NOTES.md. No refactors, no new deps. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
…ck to V1 - src/decode.py: decode Axplorer `single_integer` edge tokens (lex index in combinations(range(N),2)) back to (i,j); + roundtrip tests. - src/trajectory_loader.py: load_trajectory(jsonl) -> the same data trajectory.py exposes (representative graphs from the log, real best-score series, greedy 2-colouring for Act 2; Act 1 trace + Act 3 nested graphs synthesized since the log is per-epoch); `python -m src.trajectory_loader <log>` prints a summary. - src/trajectory.py: if logs/square_N15_run.jsonl exists, override the module-level names from it (TRAJECTORY_SOURCE reflects which is active); otherwise V1 hand-curated, unchanged. Scenes need zero changes. - tests for decode + loader (against a synthetic log); V1 trajectory tests skip when a real log is active. README rewritten for V1-vs-V2 + how to produce a log. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
…ribing The loader prefers this field over inferring N from the largest edge token. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
- logs/example_N15_run.jsonl: a real (short, reproducible: --seed 1234 --process_pool false) Axplorer run at N=15, committed as evidence the pipeline works end to end. + a loader integration test against it. - Finding (documented in README): N=15 is solved by Axplorer's initial random-construction phase (maximal greedy graphs land at 26-30 edges, the optimum 30 appears within a few hundred restarts) -- so best_score_so_far is flat from epoch 0 and the flywheel has nothing to do. The loop matters at larger N. The headline video therefore stays the V1 hand-curated idealization; the V2 plumbing is ready (point it at logs/square_N15_run.jsonl). - act3_flywheel.py now reads the ceiling, optimum, scores and N from `trajectory` instead of hardcoding 24/30/15, so it renders unchanged whether trajectory is V1 or a real log. V1 output is byte-for-byte the same. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
…for N=30 - trajectory.py: pick up any logs/square_N<n>_run.jsonl (not just N=15); when a log is found, let the loader read N and the optimum from the log itself (don't force the V1 N=15/opt=30 values). + _find_trajectory_log() helper and tests. Default is still V1 (no square_N* log committed). - README/logs: the intended V2 run is N=30 (greedy plateaus there, the flywheel has structure to learn) -- documented the overnight run command (~few hours on a GPU / MPS Mac), how the video auto-picks the log, and that ~30 vertices / ~85 edges renders but gets dense. The N=15 small reproducible run is now clearly the "sanity / shows-the-flywheel-is-unnecessary" run. To produce the V2 video: run train.py --N 30 ... --log_trajectory ../../logs/square_N30_run.jsonl, then manim full_video.py FullVideo. https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg
Summary
This PR introduces a complete Manim-based video explainer for Axplorer, an open-source extremal-combinatorics search tool. The visualization demonstrates how Axplorer solves the Turán 4-cycle problem on 15 vertices (maximize edges with no C_4 subgraph; optimum = 30 edges) through a three-act narrative: naive local search plateau → transformer-learned structure → iterative flywheel refinement.
Key Changes
Hand-curated trajectory data (
src/trajectory.py): Declarative graph states and operation sequences for all three acts. Every graph is verified C_4-free; the trajectory is staged for V1 but will be replaced with real Axplorer logs in V2.Graph utilities (
src/graph_utils.py): Core algorithms for 4-cycle detection (find_4_cycles,has_4_cycle) and edge counting, used both by the trajectory tests and live in Act 1 to verify constraint violations.Unified styling (
src/styles.py): Single source of truth for the dark Axplorer-blog palette, fonts, layout constants, and reusable drawing helpers (graph_group,cycle_polygon,int_counter). All scenes import from here for visual consistency.Three act scenes:
act1_naive_search.py: Greedy edge addition with live 4-cycle flash feedback; ends stuck at 24 edges.act2_transformer.py: Top-k pool flows into a transformer block; emits near-bipartite structured samples (27, 26 edges) with 2-colour highlight.act3_flywheel.py: Sample → local search → retrain loop; score climbs from 24 to 30 with live edge counter, loop phase indicator, and score-vs-iteration plot with naive ceiling dashed line.Full video orchestration (
src/scenes/full_video.py): Combines all three acts with opening card and fade transitions between acts.Comprehensive test suite (
tests/test_trajectory.py,tests/test_graph_utils.py): Validates that all curated graphs are C_4-free, edge counts match advertised scores, Act 1 operations genuinely walk SEED → NAIVE_PLATEAU with each reject closing a real 4-cycle, and Act 3 climbs to the proven optimum.Project configuration (
pyproject.toml,.gitignore,.python-version): uv-managed environment with Manim 0.18.x, NetworkX, NumPy; no LaTeX required (Pango text rendering only).Documentation (
README.md): Setup instructions, rendering commands, and clear note that V1 is hand-curated; V2 will parse real Axplorer logs.Notable Implementation Details
find_4_cyclesto verify each rejected edge genuinely closes a C_4 against the running graph, not just trusting the curated list.trajectory.pyand implemented instyles.circle_positions._phase_anims,_build_loop,_transformer_boxencapsulate reusable animation patterns.https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg