Skip to content

Add Manim visualization for Axplorer Turán C_4 problem solver#2

Open
honicky wants to merge 10 commits into
claude/formalize-mercers-theorem-7R430from
claude/axplorer-turan-visualization-bgbt7
Open

Add Manim visualization for Axplorer Turán C_4 problem solver#2
honicky wants to merge 10 commits into
claude/formalize-mercers-theorem-7R430from
claude/axplorer-turan-visualization-bgbt7

Conversation

@honicky
Copy link
Copy Markdown
Owner

@honicky honicky commented May 12, 2026

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

  • No Manim in trajectory.py: The data module is purely declarative (integers, lists, dicts) so V2 can swap in real logs without touching scene code.
  • Live constraint verification: Act 1 uses find_4_cycles to verify each rejected edge genuinely closes a C_4 against the running graph, not just trusting the curated list.
  • Circular layout convention: All 15 vertices equally spaced on a circle, vertex 0 at top, clockwise; documented in trajectory.py and implemented in styles.circle_positions.
  • ValueTracker-driven counters: Score displays update only when the rounded value changes, keeping animation cheap despite living on screen for ~90 seconds.
  • Modular scene helpers: _phase_anims, _build_loop, _transformer_box encapsulate reusable animation patterns.

https://claude.ai/code/session_019ULwJ2F13HKX9F2jBpWykg

claude added 2 commits May 12, 2026 18:19
…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
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +18 to +20
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

claude added 8 commits May 12, 2026 18:45
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants