Skip to content

Commit 79e3a06

Browse files
GiggleLiuclaude
andauthored
feat: Add grid graph mapping for unit disk reductions (#13)
* feat(topology): Add GridGraph type for square and triangular lattices Implement GridGraph, a weighted graph on a 2D integer lattice where edges are determined by distance (unit disk graph property). This type supports both square and triangular lattice geometries and is foundational for graph-to-grid-graph reductions from UnitDiskMapping.jl. Key components: - GridType enum: Square and Triangular (with offset_even_cols option) - GridNode<W>: Node with integer row/col coordinates and generic weight - GridGraph<W>: Implements the Graph trait with distance-based edges - physical_position(): Converts grid coords to physical coords per grid type 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(mapping): Add CopyLine structure for graph embedding Implement the copy-line technique for embedding arbitrary graphs into 2D grids. Each vertex becomes an L-shaped path that allows connections through crossings. - Add CopyLine struct with vertex, slot, and segment information - Implement create_copylines() for generating copy lines from graph and vertex order - Add remove_order() helper for computing vertex removal order - Add mis_overhead_copyline() for calculating MIS overhead - Include serde serialization support - Add comprehensive tests for path, triangle, star graphs and edge cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(mapping): Add gadget trait and basic gadgets for square lattice Implement a gadget system for resolving crossings in grid graph embeddings. Each gadget transforms a pattern in the source graph to an equivalent pattern in the mapped graph while preserving MIS properties. Gadgets implemented: - Cross<CON>: Crossing gadget (connected/disconnected variants) - Turn: 90-degree turn gadget - Branch: T-junction gadget - BranchFix: Branch simplification - WTurn: W-shaped turn - TCon: T-connection - TrivialTurn: Simple diagonal turn - EndTurn: Line termination - BranchFixB: Alternate branch fix Also adds crossing_ruleset_square() for the default square lattice ruleset. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(mapping): Add MappingGrid for intermediate representation * feat(mapping): Add map_graph function for graph to grid mapping Add graph-to-grid mapping functions that embed arbitrary graphs into 2D grid representations using the copy-line technique. Includes: - embed_graph: Creates intermediate MappingGrid from graph - map_graph: Full pipeline returning GridGraph with MIS overhead - MappingResult: Result type with config back-mapping support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(mapping): Improve map_graph code quality - Extract embed_graph_internal to eliminate duplicate create_copylines call - Add # Panics documentation to embed_graph and map_graph_with_order - Replace unwrap() with expect() for better error messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(mapping): Add triangular lattice support Add triangular lattice gadgets (TriCross, TriTurn, TriBranch) and map_graph_triangular functions for embedding graphs into triangular lattice grid graphs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add integration tests for grid mapping Add comprehensive integration tests for the graph to grid mapping system, covering both square and triangular lattice mappings. Tests verify: - Basic graph types (path, triangle, star, complete, cycle) - MappingResult serialization and config mapping - Copy line properties and vertex preservation - Cross-lattice consistency between square and triangular - Edge cases (disconnected graphs, bipartite, etc.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add documentation and exports for grid mapping Add comprehensive module-level documentation for the grid mapping module including overview of the copy-line technique, usage examples, and descriptions of submodules. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add implementation plan for grid graph reductions * test: Add standard graph and gadget tests from UnitDiskMapping.jl * tests: Add MIS verification tests (ignored) from UnitDiskMapping.jl Add comprehensive test suite mirroring GenericTensorNetworks tests: - MIS overhead formula verification for various graphs - Config back-mapping validation Tests are marked #[ignore] because the Rust implementation uses sparse node placement (nodes at slot boundaries) while Julia uses dense placement (nodes at every cell). This makes the mis_overhead formula incompatible. The tests document the expected behavior and can be enabled when/if the implementation is updated to match Julia's dense placement. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Use ILPSolver for MIS verification tests - Replace BruteForce solver with ILPSolver (requires ilp feature) - Tests remain #[ignore] because implementation uses sparse node placement while MIS overhead formula requires dense placement The Rust implementation differs from Julia (UnitDiskMapping.jl): - Julia: places nodes at every cell along copy lines (dense) - Rust: places nodes only at slot boundaries (sparse) This affects the mis_overhead formula correctness. Full parity requires implementing dense node placement. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * tests: Enable MIS verification tests with ilp feature Remove #[ignore] attributes from MIS verification tests. Gate the module with #[cfg(feature = "ilp")] so tests only run when ILP solver is available. These tests verify the relationship: mis_overhead + original_MIS = mapped_MIS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add Unit Disk Mapping documentation from UnitDiskMapping.jl Add comprehensive documentation for the IS → GridGraph reduction: - Grid Graph (Unit Disk Graph) problem definition - Copy-line construction method - Crossing gadgets for edge conflicts - MIS overhead formula - Solution back-mapping - QUBO mapping extension - Petersen graph example Add GridGraph node to reduction graph and update summary table. Add citation for cai2023 (GenericTensorNetworks paper). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct Unit Disk Mapping citation to Nguyen et al. PRX Quantum 2023 - Change @cai2023 (GenericTensorNetworks SIAM paper) to @nguyen2023 (the actual Unit Disk Mapping PRX Quantum paper) - Update overhead claim from "polynomial" to "at most quadratic" per paper - Fix Petersen graph example: 29×41 grid with overhead=88 (from docs) - Simplify summary table overhead to O(n²) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add triangular lattice citation and Petersen graph visualization - Add @pan2025 citation for triangular Rydberg atom array encoding - Create export_petersen_mapping example to generate JSON for visualization - Add JSON data files: petersen_source.json, petersen_square.json, petersen_triangular.json - Add typst figure showing Petersen graph and its King's subgraph mapping - Update Petersen example with actual computed values (42×46 grid, overhead=174) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Use cetz for graph visualization with unit disk edges - Import cetz for proper graph drawing - Add udg-edges() function to compute unit disk graph edges from positions - Refactor draw-petersen-cetz() to use cetz canvas - Refactor draw-grid-cetz() to show edges based on radius from JSON - Edge connections now visible in the King's subgraph mapping 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use spacing-based radius for sparse grid graph visualization The Rust implementation stores sparse waypoints (spacing=4 apart) rather than dense copy line nodes. The JSON radius (1.5) is for dense unit disk graphs, but our sparse nodes are 4 apart. Fix: Use spacing+0.5 as the unit distance to connect adjacent waypoints on copy lines, making the L-shaped structure visible. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Generate dense King's subgraph nodes from copy line data The JSON stores sparse waypoints at spacing intervals, but King's subgraph needs nodes at every cell along copy lines for proper 8-connectivity. Add dense-copyline-nodes() to interpolate all cells along L-shaped paths: - Vertical segment: all cells from vstart to vstop - Horizontal segment: all cells from vslot to hstop - Uses radius=1.5 to connect 8-neighbors (dist 1 and sqrt(2)) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add dense_locations for proper King's subgraph visualization Add CopyLine::dense_locations() that generates nodes at every cell along the L-shaped copy line path, matching UnitDiskMapping.jl's copyline_locations: - Grow up: from center to vstart (all cells) - Grow down: from center to vstop (all cells, with offset for first) - Grow right: from center to hstop (all cells) - Center node at (I, J+1) Update export_petersen_mapping example to output dense King's subgraph: - 281 nodes with 419 edges (vs 54 sparse waypoints) - Proper 8-connectivity with radius=1.5 Simplify typst visualization to use JSON grid_graph directly. Add tests for dense_locations matching Julia behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Complete map_config_back with copyline-based solution extraction - Add weighted voting on copyline cells to extract original vertex selection - Endpoint cells (weight=1) count double for better accuracy - Add strict tests for simple graphs (bull, diamond, k23, triangle) - Add lenient tests for complex graphs (petersen, cubical, house) - Add pathdecomposition module with MinhThiTrick vertex ordering - Add small_graphs module with standard test graphs - Remove unused gadget unapply functions (copyline approach is simpler) - All 8 map_config_back tests pass, build clean with -D warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Fix node count in Petersen graph King's subgraph example The square (King's subgraph) mapping has 281 nodes, not 54. The 54 nodes figure belongs to the triangular lattice mapping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add WeightedGadget struct and Weightable trait * feat: implement Weightable for TriTurn, TriBranch, and TriCross * feat: add all triangular gadget definitions * feat: implement Weightable for all triangular gadgets * feat: add triangular_weighted_ruleset function * feat: add trace_centers function for center location tracking * feat: add map_weights function for weight mapping * feat: add weighted gadget MIS verification tests and interface tests * docs: Add weighted tests coverage implementation plan Plan to cover missing weighted MIS tests from Julia's UnitDiskMapping.jl: - Triangular copy line MIS overhead (8 configurations) - Map configurations back for 6 standard graphs - Enhanced interface tests with random weights - Configuration count preservation tests Square lattice Weighted() mode is out of scope for this plan. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add triangular copy line weighted MIS overhead test Add functions for triangular weighted mode copy line support: - copyline_weighted_locations_triangular: generates weighted node locations for copy lines in triangular mode with weight=2 for regular nodes and weight=1 for turn points - mis_overhead_copyline_triangular: calculates MIS overhead as sum(weights)/2 Add test that verifies all 8 configurations from Julia's triangular.jl test suite produce correct weighted MIS values when solved via ILP. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add triangular map configurations back verification Add test for mapping standard graphs (bull, petersen, cubical, house, diamond, tutte) and verifying that map_config_back produces valid independent sets. This test mirrors Julia's "triangular map configurations back" test. The test is currently ignored because the triangular mapping implementation is incomplete - it lacks proper gadget application (TriCross, TriTurn, TriBranch). When the triangular mapping is completed, this test should pass and verify the correctness of the solution mapping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add enhanced triangular weighted interface test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add configuration count preservation test Add test_triangular_config_count_preserved to verify the MIS overhead formula (mapped_MIS = original_MIS + overhead) holds for the diamond graph. This is a simplified version of Julia's CountingMax test. Currently ignored because triangular mapping is incomplete. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add triangular crossing gadgets implementation plan Plan to implement apply_triangular_crossing_gadgets: - Pattern matching for TriangularGadget trait - Gadget application function - Integration into map_graph_triangular - 9 tasks with TDD approach 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add source_matrix and mapped_matrix to TriangularGadget Add helper methods with default implementations to generate boolean matrices from gadget source and mapped graphs for pattern matching. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add pattern_matches_triangular function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add apply_triangular_gadget function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add TriangularTapeEntry and crossat_triangular 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add apply_triangular_crossing_gadgets function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: integrate apply_triangular_crossing_gadgets into mapping Update map_graph_triangular_with_order to call apply_triangular_crossing_gadgets after adding copy line nodes, and include gadget MIS overhead in the total calculation. Convert TriangularTapeEntry to TapeEntry for storage in MappingResult. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Implement triangular crossing gadget application Add apply_triangular_crossing_gadgets function to resolve crossings on triangular lattice mappings: - source_matrix() and mapped_matrix() methods for TriangularGadget - pattern_matches_triangular() for gadget pattern detection - apply_triangular_gadget() to transform grid cells - TriangularTapeEntry struct to record applied gadgets - crossat_triangular() to calculate crossing positions - triangular_tape_entry_mis_overhead() for MIS overhead lookup Integration into map_graph_triangular_with_order: - Uses dense_locations for proper node placement - Marks edge connections before gadget application - Iterates through actual edges for gadget matching Note: MIS overhead formula may need further calibration to match Julia's UnitDiskMapping.jl exactly. Triangular MIS verification tests remain ignored pending formula investigation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Triangular mapping infrastructure improvements Enhance gadgets.rs: - Add source_matrix() and mapped_matrix() methods to Pattern trait - Improve pattern matching with proper cell state handling Enhance grid.rs: - Add connect() and is_occupied() methods for edge marking - Improve cell state tracking Enhance map_graph.rs: - Edge connection marking before gadget application - Improved MIS overhead calculation Enhance grid_graph.rs: - Additional helper methods for grid graph operations Update grid_mapping_tests.rs: - Comprehensive triangular MIS verification tests (ignored pending formula fix) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Implement apply_triangular_crossing_gadgets Add triangular crossing gadget application infrastructure: - Add apply_triangular_crossing_gadgets function that iterates all vertex pairs and applies matching triangular gadgets - Add pattern_matches_triangular and apply_triangular_gadget helpers - Add TriangularTapeEntry struct and crossat_triangular function - Add triangular_tape_entry_mis_overhead for overhead calculation - Add dense_locations_triangular for correct node placement in triangular mode (includes endpoint node for proper crossings) - Track processed crossing points to avoid double-applying trivial gadgets Tests passing: 136/136 (no regressions), 2/8 triangular MIS overhead tests pass (path_graph, house), 2/5 config_back tests pass (diamond, house). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct triangular copyline rightward segment length Fix the rightward horizontal segment generation to match Julia's formula: - stop = J + spacing*(hstop-vslot) - 1 (not J + spacing*(hstop-vslot)) - Loop from J+2 to stop (inclusive), weight 1 on last node - Remove separate endpoint addition that was adding an extra node This fixes path graph to produce 21 nodes (matching Julia) instead of 23. The path graph unweighted MIS test now passes with overhead=18. Also adds: - alpha_tensor module for gadget verification using alpha tensors - SourceCell enum for triangular pattern matching with Connected state - Test assertions for path graph node count (21), overhead (18), grid size (18x18) Remaining work: triangular graphs with crossings (triangle, diamond, etc.) need simplifier gadgets (DanglingLeg) to match Julia's node counts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct MIS overhead formula and alpha tensor verification Alpha tensor verification: - Add build_standard_unit_disk_edges() using Euclidean distance with radius 1.5 to match Julia's unitdisk_graph for gadget verification - All 5 triangular gadgets now pass alpha tensor verification: TriTurn, TriCross<true>, TriCross<false>, TriTConLeft, TriBranch MIS overhead formula: - The overhead formula with s=spacing computes mapped_weighted_MIS directly, not a constant offset - Update test expectations: overhead == mapped_MIS (not overhead + original_MIS == mapped_MIS) - All 9 triangular MIS overhead tests pass (including Tutte at ~69s) Test results: - 11 alpha tensor tests: PASS - 9 MIS overhead tests: PASS (8 normal + 1 ignored/slow) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Address PR review comments from Copilot - Use usize::try_from() for row/col conversion in map_graph.rs to properly handle potential negative coordinate values - Add debug_assert! guards in grid.rs cross_at() for 1-indexed slots - Add safety comment explaining slot reuse invariant in copyline.rs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add tests for existing features and refactor Weightable Tests added: - symmetry_tests: 13 tests for RotatedGadget and ReflectedGadget - weighted_mode_tests: 9 tests for trace_centers and map_weights - copyline_tests: 9 tests for CopyLine features Refactor: - Weightable implementations now delegate to TriangularGadget trait methods for consistency between gadget structure and weights 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Use smallgraph from topology in standard_graphs tests Address PR review comment from GiggleLiu: - Replace local graph definitions with smallgraph() from topology module - Use for loops to test multiple graphs in single test function - Consolidate 12 individual tests into 3 comprehensive tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Split mapping tests into modular structure Reorganize tests/grid_mapping_tests.rs (3519 lines) into separate files mirroring the src/rules/mapping/ structure: - tests/rules/mapping/common.rs - Shared MIS solvers and helpers - tests/rules/mapping/copyline.rs - CopyLine functionality tests - tests/rules/mapping/gadgets.rs - Square and triangular gadget tests - tests/rules/mapping/map_graph.rs - Square lattice mapping tests - tests/rules/mapping/triangular.rs - Triangular lattice mapping tests - tests/rules/mapping/weighted.rs - Weighted mode tests 75 tests pass, 7 fail (pre-existing triangular gadget MIS issues). Original grid_mapping_tests.rs retained for now with full test suite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Remove legacy grid_mapping_tests.rs The tests have been reorganized into the modular structure under tests/rules/mapping/. The old monolithic file is no longer needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Rewrite triangular gadgets to match Julia UnitDiskMapping Key fixes: - Fix connected_nodes() to use 1-indexed values (matching Julia convention) - Change TRIANGULAR_UNIT_RADIUS from 1.5 to 1.1 (matching Julia) - Use GridType::Triangular instead of Square for final grid graph - Fix alpha tensor verification to use triangular unit disk edges - Fix edge comparison to use strict less than (<) like Julia All 13 triangular gadgets now produce identical results to Julia: - TriCross<true/false>, TriTurn, TriBranch, TriBranchFix, TriBranchFixB - TriTConLeft, TriTConDown, TriTConUp - TriTrivialTurnLeft, TriTrivialTurnRight, TriEndTurn, TriWTurn MIS overhead tests pass for: bull, diamond, house, cubical, petersen 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Resolve Clippy warnings and add more test coverage - Add #[allow(clippy::needless_range_loop)] to pattern matching functions that use index for both array access and coordinate calculation - Add #[allow(clippy::type_complexity)] to trait definitions with complex return types (Pattern, TriangularGadget) - Add tests for map_config_back_via_centers function - Add tests for weighted gadgets (weight conservation, positive weights) - Add integration tests for solution extraction - Auto-fix unused imports and other minor warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Address remaining Clippy lints for CI - Use saturating_sub for underflow protection (implicit_saturating_sub) - Use is_multiple_of instead of % 2 == 0 (manual_is_multiple_of) - Collapse else { if } to else if (collapsible_else_if) - Simplify (2 - 1) to 1 in test assertion (identity_op) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use saturating_sub in cross_at for release safety Address Copilot review: prevent potential underflow in release mode by using saturating_sub instead of direct subtraction for 1-indexed slot parameters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add tests for grid and copyline to improve coverage - Fix display character assertions in grid.rs tests (●, ◉, ◇) - Add tests for CellState display, MappingGrid display, format_with_config - Add copyline tests for hstop/vstop extent, weights, center location - Add triangular spacing test for copylines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Update Petersen mapping visualizations with both lattice types - Regenerate JSON files with current mapping implementation - Add triangular lattice visualization function with sqrt(3)/2 y-scaling - Update figure to show all three: source, King's subgraph, triangular - Update numbers: King's 30×42/220 nodes/Δ=88, triangular 42×60/404 nodes/Δ=384 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use pre-computed edges from JSON instead of recomputing The Typst visualization now uses the edges stored in the JSON files rather than recomputing them. This ensures the visualization matches the actual graph structure, especially for triangular lattice where the edge computation requires proper coordinate transformation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct triangular lattice basis to match Rust implementation Rust uses: x = row + offset, y = col * sqrt(3)/2 Previous Typst had row/col swapped. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add mapped_boundary_config function for config extraction Add mapped_boundary_config function that computes binary boundary config from pin values in the mapped graph, following Julia's UnitDiskMapping implementation. This is the first step in implementing map_config_back for square lattice mapping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add map_config_back_pattern for single gadget config unapply 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add map_config_copyback for extracting vertex configs from copylines This implements Julia's map_config_copyback! function which extracts original vertex configurations from copyline locations in a 2D config matrix. For each copyline, it counts selected nodes and subtracts the MIS overhead (len/2) to get the original vertex value. Also includes trace_centers_square helper for tracing center locations through square lattice gadget transformations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add SquarePattern enum for dynamic dispatch during config unapply Add SquarePattern enum that wraps all square lattice gadget patterns for dynamic dispatch during solution unapplication. The enum provides: - from_tape_idx: Maps tape indices (0-12 for crossing gadgets, 100-105 for simplifier gadgets) to concrete pattern instances - map_config_back: Dispatches to the correct map_config_back_pattern implementation based on the variant This enables Task 5 (map_config_back_gadgets) to iterate over the tape in reverse and unapply gadgets without needing to hardcode pattern types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: implement proper map_config_back following Julia's unapply algorithm Replace weighted voting approach with Julia's exact algorithm: 1. Convert flat grid config to 2D matrix 2. Unapply gadgets in reverse order via unapply_gadgets() 3. Extract vertex configs from copylines via map_config_copyback() Add unapply_gadgets() function that iterates tape in reverse order, using SquarePattern::from_tape_idx to dispatch to appropriate map_config_back_pattern calls. Export SquarePattern from gadgets module for use in map_graph. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Rename mapping to unitdiskmapping and split gadgets.rs - Rename src/rules/mapping to src/rules/unitdiskmapping for clarity - Split gadgets.rs (2809 lines) into: - gadgets.rs (391 lines): Pattern trait and helper functions - gadgets_unweighted.rs (1033 lines): Square lattice gadget implementations - Update all imports and test paths accordingly - Add tests/julia/ folder with Julia ground truth generation scripts The gadgets split improves maintainability by separating the trait definition from the concrete implementations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct DanglingLeg pattern and rename copyline functions DanglingLeg fix: - Source: 3 nodes at (2,2), (3,2), (4,2) (was 4 nodes) - Mapped: 1 node at (4,2) (was 2 nodes) - Matches Julia's UnitDiskMapping.jl exactly Function renames (no formula changes): - dense_locations → copyline_locations - dense_locations_triangular → copyline_locations_triangular 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Update export_petersen_mapping to use renamed function - Use copyline_locations instead of dense_locations - Regenerate petersen_square.json and petersen_triangular.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Generate weighted and unweighted Petersen mappings Update export_petersen_mapping.rs to generate three JSON files: - petersen_square_weighted.json (220 nodes, King's subgraph) - petersen_square_unweighted.json (220 nodes, all weight=1) - petersen_triangular.json (340 nodes, triangular weighted) Update reductions.typ to use the new weighted square mapping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add Julia comparison tests for square lattice mapping Compare Rust mapping output with Julia's UnitDiskMapping.jl traces: - Grid size, node count, MIS overhead - Copy line parameters (vslot, hslot, vstart, vstop, hstop) - Grid node positions from copyline_locations All 4 graphs (bull, diamond, house, petersen) match Julia exactly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use 0-indexed coordinates for triangular mapping - Update copyline_locations_triangular formula to produce 0-indexed coords - Update crossat_triangular to use 0-indexed calculation - Add SQUARE_SPACING and SQUARE_PADDING exports - Add export_mapping_stages example for Julia/Rust comparison - Add compare_mapping.typ for visual comparison with Julia The coordinate formulas now subtract 1 from Julia's 1-indexed formulas: - Center I: padding + 1 (was padding + 2) - Center J: padding (was padding + 1) - crossat row: (hslot-1)*spacing + 1 + padding - crossat col: (vslot-1)*spacing + padding 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add triangular lattice visualization support - Add triangular parameter to draw_grid and draw_comparison functions - Offset odd rows by 0.5 for proper triangular lattice display - Pass triangular: true for Triangular Weighted mode in compare_graph - Fix JSON paths to be relative from document location * fix: Use Julia's exact logic for crossing cell marking (strict matching) - Fix grid.cross_at() to return 0-indexed coordinates using Julia's formula: row = (hslot-1)*spacing + 1 + padding, col = (vslot-1)*spacing + padding - Refactor gadgets_unweighted::crossat() to delegate to grid.cross_at() (single source of truth, DRY principle) - Use strict equality matching in pattern_matches() like Julia - Grid correctly marks cells as Connected at crossat positions, so TCon gadgets match with strict equality (C==C) Julia's approach: 1. Mark grid cells as Connected at crossat(I, J-1) and crossat(I±1, J) 2. Pattern source_matrix has Connected at same relative positions 3. Use strict equality: pattern Connected == grid Connected All 4 square unweighted Julia comparison tests pass: - diamond, bull, house, petersen 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct connect() to convert Occupied cells to Connected Julia's connect_cell! converts plain Occupied cells (MCell()) to Connected cells at crossing points where copylines meet. Changes: - grid.rs: Document that connect() converts Occupied -> Connected - export_mapping_stages.rs: Fix conditional logic to match Julia (if row-1 is occupied, mark it; otherwise mark row+1) - julia_comparison.rs: Add 4 tests to verify Connected cell positions match Julia exactly (diamond, bull, house, petersen) - dump_bull_mapping.jl: Export grid_nodes_copylines_only with state field (O=Occupied, D=Doubled, C=Connected) - compare_mapping.typ: Update to show Connected cells in both Julia and Rust visualizations All 4 square unweighted tests pass. All 4 Connected cell position tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Correct square gadget names in export script Update square_gadget_name() to match actual pattern indices from gadgets_unweighted.rs: - 7: RotatedTCon (was EndTurn) - 8: ReflectedCross<true> (was BranchFixB) - 9: ReflectedTrivialTurn (was Unknown) - 10: BranchFixB (was Unknown) - 11: EndTurn (new) - 12: ReflectedRotatedTCon (new) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add weight checking to triangular gadget matching and Makefile compare target - Fix pattern_matches_triangular to check weights (matching Julia's WeightedGadget) - Fix add_node to keep weight unchanged when doubling (Julia asserts weights match) - Add export_weighted function for weighted square mode - Add Makefile targets: julia-export, rust-export, compare - Export all 3 modes (unweighted, weighted, triangular) for 4 graphs - 11/12 combinations now match Julia exactly (bull weighted has minor diff) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Fix weighted mode square lattice mapping to match Julia exactly Key changes: - Add source_weights() and mapped_weights() to Pattern trait with defaults - Implement custom weights for TrivialTurn, EndTurn, TCon, BranchFixB, Branch, DanglingLeg - Add weight delegation in RotatedGadget and ReflectedGadget - Create apply_weighted_gadget that uses mapped_weights when applying - Create apply_weighted_crossing_gadgets for weighted crossing resolution - Update apply_weighted_simplifier_gadgets to use weighted apply Critical fix: export_mapping_stages now uses actual copyline weights (weight 1 at segment endpoints) instead of hardcoded weight 2. This enables DanglingLeg pattern matching which requires [1,2,2] weight pattern. Bull weighted now matches Julia: 40 nodes, 11 tape entries, overhead 32. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Triangular weighted MIS tests now match Julia exactly Key fixes: - Use offset_even_cols=true for 0-indexed coords to match Julia's 1-indexed offset pattern (even Rust cols = odd Julia cols) - Update Julia dump script to compute weighted MIS for triangular mode - Fix test formula: mapped_weighted_mis == overhead (not original + overhead) - Use Julia's vertex order in comparison tests for consistent mappings - Update grid_graph tests to use radius 1.1 (dist < radius excludes exact matches) All triangular mapping tests now verify against Julia trace data: - Node count, edge count, overhead, and weighted MIS all match 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: Clean up debug files and update test data - Remove debug example files (test_centers.rs, debug_*.rs) - Update Julia trace JSON files with correct weighted MIS values - Add gadgets_ground_truth.rs test module for gadget verification - Fix Tutte test to skip gracefully when Julia trace file is missing - Update petersen_triangular.json with correct overhead (374) - Update reductions.typ with correct coordinate convention comments - Update export_petersen_mapping.rs to use radius 1.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add MIS overhead tests for cubical and tutte graphs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add full map_config_back verification for standard graphs (ignored) Add test for map_config_back on standard graphs (bull, diamond, house, petersen, cubical) that verifies the extracted configuration is a valid independent set and has the correct MIS size. The test is currently ignored because map_config_back has a bug where unapply_gadgets incorrectly adds nodes to copyline locations, causing all vertices to be marked as selected instead of extracting the correct subset. The debug_bull.rs example demonstrates this issue. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add weighted copyline MIS overhead verification Add test that verifies the weighted MIS of a copyline graph equals the expected overhead calculated using Julia's weighted formula: (hslot - vstart) * spacing + (vstop - hslot) * spacing + max((hstop - vslot) * spacing - 2, 0) This matches Julia's weighted.jl "copy lines" testset. The degenerate case (5, 5, 5) is excluded due to a difference in center node weight handling between Rust (min 1) and Julia (can be 0). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add weighted mode map_config_back verification for standard graphs Adds comprehensive test that verifies the weighted mode map_config_back functionality for standard graphs (bull, diamond, house, petersen): 1. Uses map_graph_triangular (weighted mode) with uniform source weights 2. Uses map_weights to add source weights to center locations 3. Solves weighted MIS on the grid graph 4. Extracts configuration at trace_centers 5. Verifies the extracted config is a valid independent set 6. Verifies the count at centers equals the original graph MIS This test mirrors Julia's weighted.jl "map configurations back" testset. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add triangular mode map_config_back verification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add test plan for Julia test parity * update * fix: Use native grid weights for weighted MIS tests The weighted mode test was incorrectly adding source weights (0.5) on top of grid weights. This breaks the gadget's weight structure that enforces mutual exclusion for adjacent original vertices. Fixed by: - Using grid graph's native weights (from gadgets) instead of mapped source weights - The gadget weights alone properly encode the crossing constraints - Added all standard graphs to map_config_back tests (20 graphs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Extract doubled_cells before gadgets for correct map_config_back The map_config_back function was incorrectly using weight==2 from copyline_locations to detect doubled cells. This is wrong because: - weight==2 means interior node weight, not two copylines overlapping - Julia checks ug.content[loc...].doubled after unapply! restores grid Fixed by: - Add doubled_cells field to MappingResult (HashSet of (row, col)) - Add doubled_cells() method to MappingGrid to extract Doubled cells - Extract doubled_cells BEFORE applying gadgets (since gadgets overwrite cell states, and we don't restore grid like Julia's unapply!) - Update map_config_copyback to use doubled_cells set This fixes map_config_back for all 20 standard graphs in square mode. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use source_weights=0.2 in triangular map_config_back tests Match Julia's test approach exactly: - Use source_weights of 0.2 (not 0.5) for each vertex - Call map_weights to add source weights at center locations - This ensures weighted MIS solver selects centers that form valid IS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: Run tests with --include-ignored in Makefile 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * save work * chore: Clean up PR 13 - remove redundant files - Remove gadgets_unweighted_backup.rs (2,809 lines, unused) - Remove duplicate JSON files (*_rust_square, *_stages) - Remove unused *_mapping_trace.json files - Archive and remove Julia source files (see issue #17) - Update compare_mapping.typ to use non-duplicate file names - Remove julia-export Makefile target Total reduction: ~36,000 lines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add KSG and Triangular lattice refactoring design Design decisions: - Split unweighted/weighted gadgets into independent types (no wrapper) - Rename: KsgCross, WeightedKsgCross, WeightedTriCross - Organize by lattice type: ksg/ and triangular/ modules - API: ksg::map_unweighted(), ksg::map_weighted(), triangular::map_weighted() - Clean break migration (delete old files) Includes Julia vs Rust naming comparison for issue #8. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add KSG/Triangular refactoring implementation plan 15 tasks covering: - Create ksg/ and triangular/ directory structure - Extract shared Pattern trait - Create KSG unweighted gadgets (Ksg* prefix) - Create KSG weighted gadgets (WeightedKsg* prefix) - Create triangular weighted gadgets (WeightedTri* prefix) - Split mapping functions - Update all test imports - Delete old files - Post Julia comparison to issue #8 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create ksg/ and triangular/ directory structure * feat: extract Pattern trait to shared traits module Extract the Pattern trait, PatternCell enum, and helper functions (pattern_matches, apply_gadget, unapply_gadget) from gadgets.rs into a new traits.rs module. This shared module will be used by both the King's SubGraph (KSG) and triangular lattice modules. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create KSG unweighted gadgets with Ksg prefix Add ksg/gadgets.rs containing all gadget structs for the King's SubGraph (KSG) unweighted mapping, renamed with Ksg prefix: - KsgCross, KsgTurn, KsgWTurn, KsgBranch, KsgBranchFix, KsgTCon - KsgTrivialTurn, KsgEndTurn, KsgBranchFixB, KsgDanglingLeg - KsgRotatedGadget, KsgReflectedGadget wrapper types - KsgPattern enum for dynamic dispatch - KsgTapeEntry struct for recording gadget applications - KsgPatternBoxed trait for boxed pattern operations - Application functions: apply_crossing_gadgets, apply_simplifier_gadgets, crossing_ruleset_indices, tape_entry_mis_overhead 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create KSG weighted gadgets with WeightedKsg prefix Add independent weighted gadget implementations for King's SubGraph (KSG) mapping. Each weighted gadget implements the Pattern trait directly with actual weight methods: - WeightedKsgCross<const CON: bool> - WeightedKsgTurn - WeightedKsgWTurn - WeightedKsgBranch - WeightedKsgBranchFix - WeightedKsgTCon - WeightedKsgTrivialTurn - WeightedKsgEndTurn - WeightedKsgBranchFixB - WeightedKsgDanglingLeg Key differences from unweighted: - source_weights() returns actual weight vectors (typically vec![2; n]) - mapped_weights() returns actual weight vectors - mis_overhead() returns 2x the unweighted value Also includes: - WeightedKsgPattern enum for dynamic dispatch - WeightedKsgTapeEntry struct - apply_weighted_crossing_gadgets function - apply_weighted_simplifier_gadgets function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create KSG mapping functions Add ksg/mapping.rs with renamed functions for KSG lattice mapping: - map_unweighted (from map_graph) - map_unweighted_with_method (from map_graph_with_method) - map_unweighted_with_order (from map_graph_with_order) - map_weighted, map_weighted_with_method, map_weighted_with_order (new) - MappingResult struct (generic over tape entry type) - embed_graph function - map_config_copyback function - trace_centers (from trace_centers_square) - Helper functions: embed_graph_internal, unapply_gadgets, unapply_weighted_gadgets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: update KSG module exports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create triangular weighted gadgets with WeightedTri prefix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: create triangular mapping functions Add triangular/mapping.rs with renamed functions from triangular.rs and weighted.rs: - map_weighted (from map_graph_triangular) - map_weighted_with_method (from map_graph_triangular_with_method) - map_weighted_with_order (from map_graph_triangular_with_order) - weighted_ruleset (from triangular_weighted_ruleset) - trace_centers (delegating to weighted.rs) - map_weights (delegating to weighted.rs) Also includes SPACING and PADDING constants and helper functions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: update triangular module exports Reorganize triangular module to use directory-based structure with comprehensive exports: - Add gadgets.rs and mapping.rs as submodules - Re-export all public items from gadgets and mapping for convenient access - Include legacy Tri* types and functions for backward compatibility - Add SPACING and PADDING constants 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: update main mod.rs to export ksg and triangular modules Update the main unitdiskmapping/mod.rs to export the new ksg and triangular modules as the primary API, while keeping backward compatibility exports for existing code. - Add ksg and triangular as public modules - Re-export shared types (CopyLine, MappingGrid, etc.) from traits - Add backward compatibility re-exports with old function names - Keep internal modules (gadgets, map_graph, etc.) private 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: update KSG/triangular module structure and fix test imports - Fix Pattern trait imports in ksg/gadgets.rs and ksg/gadgets_weighted.rs - Update triangular/mapping.rs to use ksg::MappingResult - Update weighted.rs to use ksg::MappingResult - Remove map_config_back_via_centers from old map_graph.rs - Update test files to use new weighted triangular gadgets directly - Add missing backward compatibility exports to mod.rs - Fix triangular/mod.rs legacy imports 959 lib tests pass, 240/242 integration tests pass. Two tests related to map_config_back_via_centers need further work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use triangular trace_centers for triangular mapping tests The triangular and weighted tests were failing because map_config_back_via_centers was calling the KSG trace_centers instead of the triangular-specific one. Fix by manually calling trace_centers and building the config in the tests. All 1201 tests now pass (959 lib + 242 integration). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: remove old backward-compat files and fix paper figures - Delete gadgets.rs, gadgets_unweighted.rs, map_graph.rs (no longer needed) - Fix export_petersen_mapping.rs to use actual grid_graph from mapping result - Fix export_mapping_stages.rs to use WeightedKsgTapeEntry for weighted mode - Fix triangular visualization in reductions.typ to match Rust coordinate convention - Fix unused variable/import warnings in tests - Add note that weighted/unweighted KSG share identical topology - Regenerate paper figures with correct topology 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: resolve all clippy warnings - Use `to_vec()` instead of `iter().copied().collect()` - Change `&mut Vec<T>` to `&mut [T]` for function parameters - Add type aliases for complex types (PatternFactory, SourceGraph) - Use `iter().enumerate()` instead of indexing with loop variable 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: compact test data JSON and rename julia/ to data/ - Rename tests/julia/ to tests/data/ - Compact JSON format: use arrays instead of objects for coordinates - {"row": r, "col": c} -> [r, c] - Tape entries: [row, col, type, index] - Update Rust deserializers to handle compact format - Size reduction: 1.2MB -> 125KB (89.8% reduction) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add KSG weighted gadget tests and fix CI timeout - Add 13 new tests for WeightedKsg* gadgets: - test_weighted_ksg_*_mis_equivalence for all 11 gadget types - test_all_ksg_weighted_gadgets_valid_structure - test_all_ksg_weighted_gadgets_mis_equivalence - Export WeightedKsg* types from unitdiskmapping module - Mark test_mis_overhead_tutte as #[ignore] (too slow for CI) - Coverage of ksg/gadgets_weighted.rs improved by +8.89% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * improve test coverage * docs: fix and improve doctest examples - Make GraphConstraint example fully compilable with complete implementation - Add proper type annotations to GraphProblem and IndependentSetT examples - Convert macro usage examples to text blocks (user templates) - Make reduction workflow example compile-checkable - Fix ILP solver example with concrete types - Simplify topology example to avoid incomplete constructors All doctests now pass with `cargo test --doc --all-features -- --include-ignored` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: resolve all clippy warnings - Remove redundant closures in export_mapping_stages - Simplify wildcard pattern in match expression - Remove always-true comparisons for unsigned types - Allow if_same_then_else for gadget type matching (order matters) - Remove tautological boolean assertion All clippy checks now pass with -D warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: use is_multiple_of for clippy lint on Rust 1.93+ Replace manual `% 2 == 0` checks with `.is_multiple_of(2)` to fix clippy::manual_is_multiple_of lint on CI (Rust 1.93.0). Note: Requires Rust 1.90+ where is_multiple_of was stabilized. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: revert is_multiple_of for i32 types Revert grid_graph.rs to use % operator instead of is_multiple_of since i32 doesn't support this method yet. Add allow attribute for clippy::manual_is_multiple_of lint. Other files (alpha_tensor.rs, common.rs, etc.) use usize which does support is_multiple_of in Rust 1.90+. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: revert is_multiple_of in benchmarks due to type inference Revert bench_spin_glass to use % operator instead of is_multiple_of since the compiler can't infer the integer type in the closure. Add allow attribute for clippy::manual_is_multiple_of lint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a0b6480 commit 79e3a06

131 files changed

Lines changed: 35888 additions & 403 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ keywords = ["np-hard", "optimization", "reduction", "sat", "graph"]
99
categories = ["algorithms", "science"]
1010

1111
[features]
12-
default = []
12+
default = ["ilp"]
1313
ilp = ["good_lp"]
1414

1515
[dependencies]
@@ -22,9 +22,9 @@ num-traits = "0.2"
2222
good_lp = { version = "1.8", default-features = false, features = ["highs"], optional = true }
2323
inventory = "0.3"
2424
ordered-float = "5.0"
25+
rand = "0.8"
2526

2627
[dev-dependencies]
27-
rand = "0.8"
2828
proptest = "1.0"
2929
criterion = "0.5"
3030

Makefile

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
# Makefile for problemreductions
22

3-
.PHONY: help build test fmt clippy doc mdbook paper clean coverage
3+
.PHONY: help build test fmt clippy doc mdbook paper clean coverage rust-export compare
44

55
# Default target
66
help:
77
@echo "Available targets:"
8-
@echo " build - Build the project"
9-
@echo " test - Run all tests"
10-
@echo " fmt - Format code with rustfmt"
11-
@echo " fmt-check - Check code formatting"
12-
@echo " clippy - Run clippy lints"
13-
@echo " doc - Build mdBook documentation"
14-
@echo " mdbook - Build and serve mdBook (with live reload)"
15-
@echo " paper - Build Typst paper (requires typst)"
16-
@echo " coverage - Generate coverage report (requires cargo-llvm-cov)"
17-
@echo " clean - Clean build artifacts"
18-
@echo " check - Quick check (fmt + clippy + test)"
8+
@echo " build - Build the project"
9+
@echo " test - Run all tests"
10+
@echo " fmt - Format code with rustfmt"
11+
@echo " fmt-check - Check code formatting"
12+
@echo " clippy - Run clippy lints"
13+
@echo " doc - Build mdBook documentation"
14+
@echo " mdbook - Build and serve mdBook (with live reload)"
15+
@echo " paper - Build Typst paper (requires typst)"
16+
@echo " coverage - Generate coverage report (requires cargo-llvm-cov)"
17+
@echo " clean - Clean build artifacts"
18+
@echo " check - Quick check (fmt + clippy + test)"
19+
@echo " rust-export - Generate Rust mapping JSON exports"
20+
@echo " compare - Generate and compare Rust mapping exports"
1921

2022
# Build the project
2123
build:
2224
cargo build --all-features
2325

24-
# Run all tests
26+
# Run all tests (including ignored tests)
2527
test:
26-
cargo test --all-features
28+
cargo test --all-features -- --include-ignored
2729

2830
# Format code
2931
fmt:
@@ -62,3 +64,32 @@ clean:
6264
# Quick check before commit
6365
check: fmt-check clippy test
6466
@echo "✅ All checks passed!"
67+
68+
# Generate Rust mapping JSON exports for all graphs and modes
69+
GRAPHS := diamond bull house petersen
70+
MODES := unweighted weighted triangular
71+
rust-export:
72+
@for graph in $(GRAPHS); do \
73+
for mode in $(MODES); do \
74+
echo "Exporting $$graph ($$mode)..."; \
75+
cargo run --example export_mapping_stages -- $$graph $$mode; \
76+
done; \
77+
done
78+
79+
# Generate Rust exports and show comparison
80+
compare: rust-export
81+
@echo ""
82+
@echo "=== Julia vs Rust Comparison ==="
83+
@for graph in $(GRAPHS); do \
84+
echo ""; \
85+
echo "=== $$graph ==="; \
86+
echo "-- unweighted --"; \
87+
echo "Julia: $$(jq '{nodes: .num_grid_nodes, overhead: .mis_overhead, tape: .num_tape_entries}' tests/julia/$${graph}_unweighted_trace.json)"; \
88+
echo "Rust: $$(jq '{nodes: .stages[3].num_nodes, overhead: .total_overhead, tape: ((.crossing_tape | length) + (.simplifier_tape | length))}' tests/julia/$${graph}_rust_unweighted.json)"; \
89+
echo "-- weighted --"; \
90+
echo "Julia: $$(jq '{nodes: .num_grid_nodes, overhead: .mis_overhead, tape: .num_tape_entries}' tests/julia/$${graph}_weighted_trace.json)"; \
91+
echo "Rust: $$(jq '{nodes: .stages[3].num_nodes, overhead: .total_overhead, tape: ((.crossing_tape | length) + (.simplifier_tape | length))}' tests/julia/$${graph}_rust_weighted.json)"; \
92+
echo "-- triangular --"; \
93+
echo "Julia: $$(jq '{nodes: .num_grid_nodes, overhead: .mis_overhead, tape: .num_tape_entries}' tests/julia/$${graph}_triangular_trace.json)"; \
94+
echo "Rust: $$(jq '{nodes: .stages[3].num_nodes, overhead: .total_overhead, tape: ((.crossing_tape | length) + (.simplifier_tape | length))}' tests/julia/$${graph}_rust_triangular.json)"; \
95+
done

benches/solver_benchmarks.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ fn bench_satisfiability(c: &mut Criterion) {
8888
}
8989

9090
/// Benchmark SpinGlass on varying sizes.
91+
#[allow(clippy::manual_is_multiple_of)] // Type inference issues with is_multiple_of
9192
fn bench_spin_glass(c: &mut Criterion) {
9293
let mut group = c.benchmark_group("SpinGlass");
9394

docs/paper/compare_mapping.typ

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#import "@preview/cetz:0.3.2"
2+
3+
// Load JSON data for different modes
4+
// Paths relative to this document (docs/paper/)
5+
#let load_julia_unweighted(name) = json("../../tests/julia/" + name + "_unweighted_trace.json")
6+
#let load_julia_weighted(name) = json("../../tests/julia/" + name + "_weighted_trace.json")
7+
#let load_julia_triangular(name) = json("../../tests/julia/" + name + "_triangular_trace.json")
8+
#let load_rust_square(name) = json("../../tests/julia/" + name + "_rust_unweighted.json")
9+
#let load_rust_triangular(name) = json("../../tests/julia/" + name + "_rust_triangular.json")
10+
11+
// Color scheme
12+
#let julia_color = rgb("#2196F3") // Blue
13+
#let rust_color = rgb("#FF5722") // Orange
14+
#let both_color = rgb("#4CAF50") // Green
15+
16+
// Create position key for comparison
17+
#let pos_key(r, c) = str(r) + "," + str(c)
18+
19+
// Convert Julia 1-indexed nodes to 0-indexed (Rust is already 0-indexed)
20+
// Preserves state field if present (O=Occupied, D=Doubled, C=Connected)
21+
#let julia_to_0indexed(nodes) = nodes.map(n => (
22+
row: n.row - 1,
23+
col: n.col - 1,
24+
weight: if "weight" in n { n.weight } else { 1 },
25+
state: if "state" in n { n.state } else { "O" }
26+
))
27+
28+
// Extract copyline nodes from Julia copy_lines (convert to 0-indexed)
29+
#let julia_copylines_to_nodes(copy_lines) = {
30+
let nodes = ()
31+
for cl in copy_lines {
32+
for loc in cl.locations {
33+
nodes.push((
34+
row: loc.row - 1,
35+
col: loc.col - 1,
36+
weight: if "weight" in loc { loc.weight } else { 1 }
37+
))
38+
}
39+
}
40+
nodes
41+
}
42+
43+
// Color for connected cells
44+
#let connected_color = rgb("#E91E63") // Pink/Magenta for Connected
45+
46+
// Draw grid with nodes - all positions are 0-indexed
47+
// triangular: if true, offset odd rows by 0.5 for triangular lattice
48+
// Shows Connected cells (state="C") with a different color (ring)
49+
#let draw_grid(nodes, grid_size, title, node_color: black, unit: 4pt, triangular: false) = {
50+
let rows = grid_size.at(0)
51+
let cols = grid_size.at(1)
52+
let occupied = nodes.map(n => pos_key(n.row, n.col))
53+
54+
// Helper to compute x position with optional triangular offset
55+
let get_x(r, c) = {
56+
if triangular and calc.rem(r, 2) == 1 { c + 1.0 } // odd rows offset by 0.5
57+
else { c + 0.5 }
58+
}
59+
60+
cetz.canvas(length: unit, {
61+
import cetz.draw: *
62+
content((cols / 2, rows + 2), text(size: 7pt, weight: "bold", title))
63+
64+
// Draw empty grid sites as small dots
65+
for r in range(0, rows) {
66+
for c in range(0, cols) {
67+
let key = pos_key(r, c)
68+
if not occupied.contains(key) {
69+
let y = rows - r - 0.5
70+
let x = get_x(r, c)
71+
circle((x, y), radius: 0.08, fill: luma(200), stroke: none)
72+
}
73+
}
74+
}
75+
76+
// Draw filled nodes - Connected cells shown with different color
77+
for node in nodes {
78+
let r = node.row
79+
let c = node.col
80+
let w = if "weight" in node { node.weight } else { 1 }
81+
let s = if "state" in node { node.state } else { "O" }
82+
let y = rows - r - 0.5
83+
let x = get_x(r, c)
84+
let radius = if w == 1 { 0.25 } else if w == 2 { 0.35 } else { 0.45 }
85+
// Connected cells shown with ring (stroke) instead of fill
86+
if s == "C" {
87+
circle((x, y), radius: radius, fill: none, stroke: 1.5pt + connected_color)
88+
} else {
89+
circle((x, y), radius: radius, fill: node_color, stroke: none)
90+
}
91+
}
92+
})
93+
}
94+
95+
// Compare two node sets
96+
#let compare_nodes(julia_nodes, rust_nodes) = {
97+
let julia_keys = julia_nodes.map(n => pos_key(n.row, n.col))
98+
let rust_keys = rust_nodes.map(n => pos_key(n.row, n.col))
99+
let both = ()
100+
let julia_only = ()
101+
let rust_only = ()
102+
103+
for n in julia_nodes {
104+
let key = pos_key(n.row, n.col)
105+
if rust_keys.contains(key) { both.push((n.row, n.col)) }
106+
else { julia_only.push((n.row, n.col)) }
107+
}
108+
for n in rust_nodes {
109+
let key = pos_key(n.row, n.col)
110+
if not julia_keys.contains(key) { rust_only.push((n.row, n.col)) }
111+
}
112+
(both: both, julia_only: julia_only, rust_only: rust_only)
113+
}
114+
115+
// Draw comparison grid
116+
#let draw_comparison(julia_nodes, rust_nodes, grid_size, title, unit: 4pt, triangular: false) = {
117+
let rows = grid_size.at(0)
118+
let cols = grid_size.at(1)
119+
let cmp = compare_nodes(julia_nodes, rust_nodes)
120+
let all_occupied = cmp.both + cmp.julia_only + cmp.rust_only
121+
let occupied_keys = all_occupied.map(((r, c)) => pos_key(r, c))
122+
123+
let get_x(r, c) = {
124+
if triangular and calc.rem(r, 2) == 1 { c + 1.0 }
125+
else { c + 0.5 }
126+
}
127+
128+
cetz.canvas(length: unit, {
129+
import cetz.draw: *
130+
content((cols / 2, rows + 3), text(size: 7pt, weight: "bold", title))
131+
content((cols / 2, rows + 1.5), text(size: 5pt)[
132+
#box(fill: both_color, width: 4pt, height: 4pt) #cmp.both.len()
133+
#box(fill: julia_color, width: 4pt, height: 4pt) #cmp.julia_only.len()
134+
#box(fill: rust_color, width: 4pt, height: 4pt) #cmp.rust_only.len()
135+
])
136+
137+
for r in range(0, rows) {
138+
for c in range(0, cols) {
139+
let key = pos_key(r, c)
140+
if not occupied_keys.contains(key) {
141+
let y = rows - r - 0.5
142+
let x = get_x(r, c)
143+
circle((x, y), radius: 0.08, fill: luma(200), stroke: none)
144+
}
145+
}
146+
}
147+
148+
for (r, c) in cmp.both {
149+
circle((get_x(r, c), rows - r - 0.5), radius: 0.35, fill: both_color, stroke: none)
150+
}
151+
for (r, c) in cmp.julia_only {
152+
circle((get_x(r, c), rows - r - 0.5), radius: 0.35, fill: julia_color, stroke: none)
153+
}
154+
for (r, c) in cmp.rust_only {
155+
circle((get_x(r, c), rows - r - 0.5), radius: 0.35, fill: rust_color, stroke: none)
156+
}
157+
})
158+
}
159+
160+
// Compare a single mode
161+
// triangular: if true, use triangular lattice visualization (offset odd rows)
162+
#let compare_mode(name, mode, julia, rust, triangular: false) = {
163+
let grid_size = julia.grid_size
164+
// Use grid_nodes_copylines_only if available (has state: O, D, C), else fall back to copy_lines
165+
let julia_copylines = if "grid_nodes_copylines_only" in julia {
166+
julia_to_0indexed(julia.grid_nodes_copylines_only)
167+
} else {
168+
julia_copylines_to_nodes(julia.copy_lines)
169+
}
170+
let julia_before_simp = julia_to_0indexed(julia.at("grid_nodes_before_simplifiers", default: julia.grid_nodes))
171+
let julia_final = julia_to_0indexed(julia.grid_nodes)
172+
// Rust stages: 0=copylines only, 1=with connections, 2=after crossing, 3=after simplifiers
173+
let rust_stage1 = rust.stages.at(1).grid_nodes // with connections (matches Julia copylines_only)
174+
let rust_stage2 = rust.stages.at(2).grid_nodes // after crossing gadgets
175+
let rust_stage3 = rust.stages.at(3).grid_nodes // after simplifiers
176+
177+
[
178+
= #name - #mode
179+
180+
== Overview
181+
#table(
182+
columns: 3,
183+
stroke: 0.5pt,
184+
[*Metric*], [*Julia*], [*Rust*],
185+
[Vertices], [#julia.num_vertices], [#rust.num_vertices],
186+
[Grid], [#grid_size.at(0#grid_size.at(1)], [#rust.stages.at(0).grid_size.at(0#rust.stages.at(0).grid_size.at(1)],
187+
[Final Nodes], [#julia.num_grid_nodes], [#rust.stages.at(3).num_nodes],
188+
[Before Simpl], [#julia.num_grid_nodes_before_simplifiers], [#rust.stages.at(2).num_nodes],
189+
[Overhead], [#julia.mis_overhead], [#rust.total_overhead],
190+
[Tape], [#julia.tape.len()], [#(rust.crossing_tape.len() + rust.simplifier_tape.len())],
191+
)
192+
193+
== Copylines with Connections
194+
#grid(
195+
columns: 3,
196+
gutter: 0.3em,
197+
draw_grid(julia_copylines, grid_size, "Julia", node_color: julia_color, triangular: triangular),
198+
draw_grid(rust_stage1, grid_size, "Rust", node_color: rust_color, triangular: triangular),
199+
draw_comparison(julia_copylines, rust_stage1, grid_size, "Diff", triangular: triangular),
200+
)
201+
202+
== After Crossing Gadgets
203+
#grid(
204+
columns: 3,
205+
gutter: 0.3em,
206+
draw_grid(julia_before_simp, grid_size, "Julia", node_color: julia_color, triangular: triangular),
207+
draw_grid(rust_stage2, grid_size, "Rust", node_color: rust_color, triangular: triangular),
208+
draw_comparison(julia_before_simp, rust_stage2, grid_size, "Diff", triangular: triangular),
209+
)
210+
211+
== Final
212+
#grid(
213+
columns: 3,
214+
gutter: 0.3em,
215+
draw_grid(julia_final, grid_size, "Julia", node_color: julia_color, triangular: triangular),
216+
draw_grid(rust_stage3, grid_size, "Rust", node_color: rust_color, triangular: triangular),
217+
draw_comparison(julia_final, rust_stage3, grid_size, "Diff", triangular: triangular),
218+
)
219+
220+
#pagebreak()
221+
]
222+
}
223+
224+
// Compare all 3 modes for a graph
225+
#let compare_graph(name) = {
226+
let julia_uw = load_julia_unweighted(name)
227+
let julia_w = load_julia_weighted(name)
228+
let julia_tri = load_julia_triangular(name)
229+
let rust_sq = load_rust_square(name)
230+
let rust_tri = load_rust_triangular(name)
231+
232+
[
233+
#compare_mode(name, "UnWeighted (Square)", julia_uw, rust_sq)
234+
#compare_mode(name, "Weighted (Square)", julia_w, rust_sq)
235+
#compare_mode(name, "Triangular Weighted", julia_tri, rust_tri, triangular: true)
236+
]
237+
}
238+
239+
// Document setup
240+
#set page(margin: 0.6cm, paper: "a4")
241+
#set text(size: 6pt)
242+
243+
#align(center, text(size: 12pt, weight: "bold")[Julia vs Rust Mapping Comparison])
244+
#v(0.3em)
245+
246+
#compare_graph("diamond")
247+
#compare_graph("bull")
248+
#compare_graph("house")
249+
#compare_graph("petersen")

0 commit comments

Comments
 (0)