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>
117 lines
5.1 KiB
Rust
117 lines
5.1 KiB
Rust
//! 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 ");
|
||
}
|