zigzag-engine/examples/trace_merge.rs
Maximus Gorog c51e3274f9 Stage 2 complete: Construction 17 validated on real 3D data
Zigzag engine (6802 lines, 184 tests):
- Construction 17 normalisation: working through dimension 3+
- Import from homotopy-rs JSON: working (scalar, two_scalars, half_braid)
- Piece extraction via Embedding/restrict_diagram: working
- Type checking pipeline: working (Eckmann-Hilton half_braid passes)
- Essential identity detection: validated with full 2-diagram test

Bugs found and fixed:
- assemble_factorisations losing cospan legs during reassembly
- RewriteN::slice() using source offsets instead of target indices
- singular_preimage() not handling passthrough heights
- restrict_rewrite() not accounting for accumulated cone offsets
- Embedding::preimage() using regular_preimage for Singular case

Added vis-engine-spec.md: visualization engine specification
- 6-layer architecture from math primitives to scene graph
- SVG renderer for 2D, WebGL2 for 3D, custom hit testing
- Spring constraint integration point for semiotic rendering
- No external dependencies - game engine approach

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-09 05:26:15 -06:00

117 lines
5.1 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Trace the merge topology of half_braid
//!
//! Run with: cargo run --example trace_merge
use std::fs;
use zigzag_engine::diagram::Diagram;
use zigzag_engine::import::load_homotopy_diagram_n;
fn main() {
let json = fs::read_to_string("fixtures/half_braid.json")
.expect("Failed to read half_braid.json");
let half_braid = load_homotopy_diagram_n(&json)
.expect("Failed to parse");
println!("=== MERGE TOPOLOGY ANALYSIS ===\n");
// Source 2-diagram structure
if let Diagram::DiagramN(src) = half_braid.source() {
println!("SOURCE 2-diagram ({} cospans):", src.cospans.len());
println!("Heights: r0, s0, r1, s1, r2");
println!();
// Print y-coordinates for each height
println!("Height mappings (using layout_coords logic):");
for i in 0..=src.cospans.len() {
let y = (i as f64) - 1.0;
println!(" r{}: y = {:.1}", i, y);
if i < src.cospans.len() {
let y_sing = i as f64;
println!(" s{}: y = {:.1} ← SCALAR HERE", i, y_sing);
}
}
println!();
}
// Target 2-diagram structure
let target = half_braid.target();
if let Diagram::DiagramN(tgt) = &target {
println!("TARGET 2-diagram ({} cospans):", tgt.cospans.len());
println!("Heights: r0, s0, r1");
println!();
println!("Height mappings:");
for i in 0..=tgt.cospans.len() {
let y = (i as f64) - 1.0;
println!(" r{}: y = {:.1}", i, y);
if i < tgt.cospans.len() {
let y_sing = i as f64;
println!(" s{}: y = {:.1} ← MERGED SCALAR HERE", i, y_sing);
}
}
println!();
}
println!("=== VISIBLE ELEMENT ANALYSIS ===\n");
println!("The 2 VERTICES (geom_dim=0) are the TWO INPUT SCALARS:");
println!(" vertex (s0,s0,s0): z=-0.5, the FIRST scalar from source s0");
println!(" vertex (s1,s0,s0): z=+0.5, the SECOND scalar from source s1");
println!();
println!("The 3 WIRES (geom_dim=1) are the BOUNDARIES between regions:");
println!(" wire (r0,s0,s0): z=-1.0, LEFT boundary (below both scalars)");
println!(" wire (r1,s0,s0): z= 0.0, MIDDLE boundary (between the two scalars)");
println!(" wire (r2,s0,s0): z=+1.0, RIGHT boundary (above both scalars)");
println!();
println!("=== Y-SHAPE TOPOLOGY ===\n");
println!("The MERGE contracts source heights r0,s0,r1,s1,r2 into target heights r0,s0,r1");
println!();
println!("Mapping:");
println!(" Source r0 (y=-1) → Target r0 (y=-1) [PRESERVED]");
println!(" Source s0 (y= 0) → Target s0 (y= 0) [MERGED INTO]");
println!(" Source r1 (y= 0) → Target s0 (y= 0) [ABSORBED]");
println!(" Source s1 (y= 1) → Target s0 (y= 0) [MERGED INTO]");
println!(" Source r2 (y=+1) → Target r1 (y= 0) [CONTRACTED DOWN]");
println!();
println!("For the 3 visible wires:");
println!();
println!("Wire r0 (z=-1, LEFT EDGE):");
println!(" Source endpoint (x=-1): y=-1 (at source height r0)");
println!(" Merge waypoint (x= 0): y= 0 (at merge height s0)");
println!(" Target endpoint (x=+1): y=-1 (at target height r0)");
println!(" → This wire DIPS DOWN to the merge then back up");
println!();
println!("Wire r1 (z=0, MIDDLE/STEM):");
println!(" Source endpoint (x=-1): y= 0 (at source height r1, between s0 and s1)");
println!(" Merge waypoint (x= 0): y= 0 (at merge height s0)");
println!(" Target endpoint (x=+1): y= 0 (at target height s0)");
println!(" → This is the STEM - stays at y=0 throughout");
println!();
println!("Wire r2 (z=+1, RIGHT EDGE):");
println!(" Source endpoint (x=-1): y=+1 (at source height r2)");
println!(" Merge waypoint (x= 0): y= 0 (at merge height s0)");
println!(" Target endpoint (x=+1): y= 0 (at target height r1)");
println!(" → This wire comes DOWN from above into the merge");
println!();
println!("=== THE Y-SHAPE ===\n");
println!("Looking at y-z plane (height vs depth) at different x (time) slices:\n");
println!("At SOURCE (x=-1): At MERGE (x=0): At TARGET (x=+1):");
println!(" ");
println!("y=+1 ──●r2── y=+1 y=+1 ");
println!(" │ ╲ ");
println!(" │ ╲ ");
println!("y= 0 ──●r1── ←s1 scalar y= 0 ●●● (merge) y= 0 ──●r1,r2── ");
println!(" ↑ ↑ ");
println!(" vertices merged ");
println!("y=-1 ──●r0── ←s0 scalar y=-1 y=-1 ──●r0── ");
println!(" ");
println!(" z: -1 0 +1 -1 0 +1 -1 0 +1 ");
}