Skip to content

feat: Add grid graph mapping for unit disk reductions#13

Merged
GiggleLiu merged 117 commits intomainfrom
feat/grid-graph-mapping
Feb 2, 2026
Merged

feat: Add grid graph mapping for unit disk reductions#13
GiggleLiu merged 117 commits intomainfrom
feat/grid-graph-mapping

Conversation

@GiggleLiu
Copy link
Contributor

Summary

Implements graph-to-grid-graph mapping using the copy-line technique from UnitDiskMapping.jl, enabling reductions to unit disk graphs on both square and triangular lattices.

  • Add GridGraph type for weighted graphs on 2D integer lattices (square and triangular)
  • Add copy-line embedding infrastructure for mapping arbitrary graph vertices
  • Add gadget system with 9 square-lattice gadgets and 3 triangular-lattice gadgets
  • Add map_graph() and map_graph_triangular() functions for complete graph mapping
  • Add MappingResult with solution back-mapping via map_config_back()
  • Add 37 integration tests covering various graph types

Test Plan

  • All 766 library tests pass
  • Integration tests cover path, triangle, star, K4, empty, and disconnected graphs
  • Serialization tests verify MappingResult round-trips correctly
  • Both square and triangular lattice mappings tested

🤖 Generated with Claude Code

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 92.29053% with 311 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.12%. Comparing base (a0b6480) to head (f5e9740).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rules/unitdiskmapping/ksg/gadgets.rs 86.02% 179 Missing ⚠️
src/rules/unitdiskmapping/ksg/gadgets_weighted.rs 91.00% 88 Missing ⚠️
src/rules/unitdiskmapping/copyline.rs 96.76% 15 Missing ⚠️
src/rules/unitdiskmapping/ksg/mapping.rs 96.46% 14 Missing ⚠️
src/rules/unitdiskmapping/alpha_tensor.rs 96.16% 11 Missing ⚠️
src/rules/unitdiskmapping/grid.rs 98.70% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #13      +/-   ##
==========================================
- Coverage   98.15%   97.12%   -1.04%     
==========================================
  Files          61       75      +14     
  Lines       12773    20591    +7818     
==========================================
+ Hits        12537    19998    +7461     
- Misses        236      593     +357     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements graph-to-grid-graph mapping using the copy-line technique, enabling reductions from arbitrary graphs to unit disk graphs on square and triangular lattices. The implementation ports functionality from UnitDiskMapping.jl and adds comprehensive support for pathwidth-based vertex ordering.

Changes:

  • Adds GridGraph type for weighted graphs on 2D integer lattices (square and triangular)
  • Implements copy-line embedding infrastructure with path decomposition algorithms
  • Adds triangular lattice mapping support and gadget system
  • Includes 22 well-known small graphs for testing/benchmarking
  • Adds comprehensive documentation and 37 integration tests

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/topology/grid_graph.rs GridGraph type with unit disk property for square/triangular lattices
src/topology/small_graphs.rs Collection of 22 well-known small graphs for testing
src/topology/mod.rs Module exports for grid graph types
src/rules/mapping/copyline.rs Copy-line embedding technique with path decomposition
src/rules/mapping/grid.rs Mapping grid intermediate representation
src/rules/mapping/map_graph.rs Main mapping functions and solution back-mapping
src/rules/mapping/triangular.rs Triangular lattice support with specialized gadgets
src/rules/mapping/pathdecomposition.rs Path decomposition algorithms (greedy and branch-and-bound)
src/rules/mapping/mod.rs Mapping module organization and exports
src/rules/mod.rs Rules module export additions
Cargo.toml Dependency updates (rand moved to dependencies, default features changed)
docs/paper/* Documentation updates with Petersen graph mapping examples
examples/export_petersen_mapping.rs Example code for graph mapping visualization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


[features]
default = []
default = ["ilp"]
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The default feature is changed from [] to ["ilp"], which means users who don't want the ILP feature will now get it by default. This is a breaking change that should be noted in the PR description or reconsidered.

Suggested change
default = ["ilp"]
default = []

Copilot uses AI. Check for mistakes.
Comment on lines 47 to 48
let row = node.row as usize;
let col = node.col as usize;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The node_idx is cast to usize when stored in pos_to_idx (line 47-48), but GridNode.row and GridNode.col are of type i32. If nodes have negative coordinates, this will cause issues. Consider validating that row and col are non-negative when building the map, or handle the conversion more explicitly.

Suggested change
let row = node.row as usize;
let col = node.col as usize;
let row = usize::try_from(node.row).expect("Grid node row must be non-negative");
let col = usize::try_from(node.col).expect("Grid node col must be non-negative");

Copilot uses AI. Check for mistakes.
Comment on lines 164 to 165
let row = (h_slot - 1) * self.spacing + 2 + self.padding;
let col = (w - 1) * self.spacing + 1 + self.padding;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The cross_at method performs subtraction (h_slot - 1) and (w - 1) which could underflow if h_slot or w are 0. Consider adding a check or documentation that slots must be >= 1, or use saturating_sub to prevent panics.

Suggested change
let row = (h_slot - 1) * self.spacing + 2 + self.padding;
let col = (w - 1) * self.spacing + 1 + self.padding;
let row = h_slot.saturating_sub(1) * self.spacing + 2 + self.padding;
let col = w.saturating_sub(1) * self.spacing + 1 + self.padding;

Copilot uses AI. Check for mistakes.
Comment on lines 251 to 253
let islot = slots.iter().position(|&x| x == 0).unwrap();
slots[islot] = v + 1; // Store vertex+1 to distinguish from empty (0)
hslots[i] = islot + 1; // 1-indexed hslot
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The unwrap() on line 251 will panic if all slots are occupied. While this shouldn't happen if the algorithm is correct, consider handling this case more gracefully or adding a comment explaining why this is guaranteed not to panic.

Copilot uses AI. Check for mistakes.
good_lp = { version = "1.8", default-features = false, features = ["highs"], optional = true }
inventory = "0.3"
ordered-float = "5.0"
rand = "0.8"
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

rand is moved from dev-dependencies to dependencies. This adds a runtime dependency for all users. Verify that rand is actually needed in the main library code (e.g., in pathdecomposition.rs for random restarts) and not just in tests.

Copilot uses AI. Check for mistakes.
let min_col = locs.iter().map(|l| l.1).min().unwrap_or(0).saturating_sub(1);
let max_col = locs.iter().map(|l| l.1).max().unwrap_or(0) + 1;

for (idx, node) in self.grid_graph.nodes().iter().enumerate() {
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The conversion from i32 to usize for row and col can panic if the coordinates are negative. Since the code constructs grid nodes at lines 294 and 304 with as i32 casts, negative values should not occur, but this assumption is fragile. Consider adding a comment or assertion explaining why coordinates are guaranteed non-negative.

Suggested change
for (idx, node) in self.grid_graph.nodes().iter().enumerate() {
for (idx, node) in self.grid_graph.nodes().iter().enumerate() {
// Grid nodes are constructed with non-negative coordinates (see construction
// around lines 294 and 304), so these casts to usize are safe under the
// intended invariants. We assert this here to catch violations early.
debug_assert!(node.row >= 0 && node.col >= 0, "grid node has negative coordinates: row={}, col={}", node.row, node.col);

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 137
pub fn dense_locations(&self, padding: usize, spacing: usize) -> Vec<(usize, usize, usize)> {
let mut locs = Vec::new();
let mut nline = 0usize;

// Center location (I, J) - matches Julia's center_location
let i = (spacing * (self.hslot - 1) + padding + 2) as isize;
let j = (spacing * (self.vslot - 1) + padding + 1) as isize;
let spacing = spacing as isize;

// Grow up: from I down to start
let start = i + spacing * (self.vstart as isize - self.hslot as isize) + 1;
if self.vstart < self.hslot {
nline += 1;
}
for row in (start..=i).rev() {
if row >= 0 {
let weight = if row != start { 2 } else { 1 };
locs.push((row as usize, j as usize, weight));
}
}

// Grow down: from I to stop
let stop = i + spacing * (self.vstop as isize - self.hslot as isize) - 1;
if self.vstop > self.hslot {
nline += 1;
}
for row in i..=stop {
if row >= 0 {
if row == i {
// Special: first node going down is offset by (1, 1)
locs.push(((row + 1) as usize, (j + 1) as usize, 2));
} else {
let weight = if row != stop { 2 } else { 1 };
locs.push((row as usize, j as usize, weight));
}
}
}

// Grow right: from J+2 to stop
let stop_col = j + spacing * (self.hstop as isize - self.vslot as isize) - 1;
if self.hstop > self.vslot {
nline += 1;
}
for col in (j + 2)..=stop_col {
if col >= 0 {
let weight = if col != stop_col { 2 } else { 1 };
locs.push((i as usize, col as usize, weight));
}
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

In dense_locations, there are checks if row >= 0 and if col >= 0 before casting to usize. However, negative values could still be cast as usize on lines 106, 122, 135 if the arithmetic produces negative j. Consider adding similar checks for j before casting, or documenting why j is guaranteed non-negative.

Copilot uses AI. Check for mistakes.
Comment on lines 47 to 75
pub fn center_location(&self, padding: usize, spacing: usize) -> (usize, usize) {
let row = spacing * (self.hslot - 1) + padding + 2;
let col = spacing * (self.vslot - 1) + padding + 1;
(row, col)
}

/// Generate grid locations for this copy line.
/// Returns Vec<(row, col, weight)> where weight indicates importance.
///
/// The copy line forms an L-shape:
/// - Vertical segment from vstart to vstop
/// - Horizontal segment at hslot from vslot to hstop
pub fn locations(&self, padding: usize, spacing: usize) -> Vec<(usize, usize, usize)> {
let mut locs = Vec::new();

// The center column for this copy line's vertical segment
let col = spacing * (self.vslot - 1) + padding + 1;

// Vertical segment: from vstart to vstop
for v in self.vstart..=self.vstop {
let row = spacing * (v - 1) + padding + 2;
// Weight is 1 for regular positions
locs.push((row, col, 1));
}

// Horizontal segment: at hslot, from vslot+1 to hstop
let hrow = spacing * (self.hslot - 1) + padding + 2;
for h in (self.vslot + 1)..=self.hstop {
let hcol = spacing * (h - 1) + padding + 1;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Methods center_location and locations perform subtractions like (self.hslot - 1), (self.vslot - 1), and (v - 1) which will underflow if these values are 0. The same issue appears in other locations (line 67, 73, 75). Based on the code context, slots appear to be 1-indexed, but this assumption should be documented or enforced with assertions.

Copilot uses AI. Check for mistakes.
Comment on lines +287 to +384
let mut copylines = vec![
CopyLine {
vertex: 0,
vslot: 0,
hslot: 0,
vstart: 0,
vstop: 0,
hstop: 0,
};
num_vertices
];

for (i, &v) in vertex_order.iter().enumerate() {
copylines[v] = CopyLine::new(
v,
i + 1, // vslot is 1-indexed position in order
hslots[i],
vstarts[i],
vstops[i],
hstops[i],
);
}

copylines
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The initialization on lines 287-297 creates a default CopyLine with all fields set to 0. Since slots are 1-indexed, this creates invalid copylines with vslot=0, hslot=0, etc. These will cause underflow panics if used before being overwritten. Consider using Option<CopyLine> or a different initialization approach to avoid this issue.

Suggested change
let mut copylines = vec![
CopyLine {
vertex: 0,
vslot: 0,
hslot: 0,
vstart: 0,
vstop: 0,
hstop: 0,
};
num_vertices
];
for (i, &v) in vertex_order.iter().enumerate() {
copylines[v] = CopyLine::new(
v,
i + 1, // vslot is 1-indexed position in order
hslots[i],
vstarts[i],
vstops[i],
hstops[i],
);
}
copylines
let mut copylines: Vec<Option<CopyLine>> = vec![None; num_vertices];
for (i, &v) in vertex_order.iter().enumerate() {
copylines[v] = Some(CopyLine::new(
v,
i + 1, // vslot is 1-indexed position in order
hslots[i],
vstarts[i],
vstops[i],
hstops[i],
));
}
copylines
.into_iter()
.map(|cl| cl.expect("all vertices must have an associated CopyLine"))
.collect()

Copilot uses AI. Check for mistakes.
@GiggleLiu
Copy link
Contributor Author

MIS Overhead Formula Fix

Changes

  • Alpha tensor verification: Added build_standard_unit_disk_edges() using Euclidean distance with radius 1.5 to match Julia's unitdisk_graph. All 5 triangular gadgets (TriTurn, TriCross<true/false>, TriTConLeft, TriBranch) now pass verification.

  • MIS overhead formula: Corrected test expectations. The overhead formula with s=spacing computes mapped_weighted_MIS directly (not a constant offset). Updated tests to verify overhead == mapped_MIS.

Test Results

Test Suite Status
Alpha tensor (11 tests) ✅ PASS
MIS overhead (9 tests) ✅ PASS

Note: The Tutte graph test (46 vertices) takes ~69 seconds due to ILP solving on the large triangular lattice, hence marked as #[ignore] in normal runs.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 32 out of 33 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


#[test]
fn test_map_weights_one() {

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Empty line at line 77 appears to be unintentional whitespace that should be removed for code cleanliness.

Copilot uses AI. Check for mistakes.

[features]
default = []
default = ["ilp"]
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Enabling the ilp feature by default may significantly increase compile times and binary size for users who don't need ILP solving capabilities. The ilp feature pulls in the good_lp dependency with HiGHS solver. Consider keeping default = [] and documenting that users should enable the ilp feature when needed. This follows Rust best practices of keeping default feature sets minimal.

Suggested change
default = ["ilp"]
default = []

Copilot uses AI. Check for mistakes.
good_lp = { version = "1.8", default-features = false, features = ["highs"], optional = true }
inventory = "0.3"
ordered-float = "5.0"
rand = "0.8"
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Moving rand from dev-dependencies to regular dependencies adds it as a runtime dependency. Since rand is only used in pathdecomposition.rs for random choices in the greedy algorithm, consider either: (1) making it optional behind a feature flag, or (2) using a simpler deterministic selection strategy as the default. Adding rand increases the dependency footprint for all users even if they never use the greedy path decomposition method.

Copilot uses AI. Check for mistakes.
@GiggleLiu GiggleLiu requested a review from Copilot January 31, 2026 10:55
GiggleLiu and others added 14 commits January 31, 2026 18:56
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
- 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>
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>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 64 out of 126 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

GiggleLiu and others added 3 commits January 31, 2026 18:59
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>
…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>
- 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>
GiggleLiu and others added 19 commits January 31, 2026 18:59
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>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>
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>
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>
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>
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>
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>
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>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
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>
- 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>
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>
- 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>
@GiggleLiu GiggleLiu force-pushed the feat/grid-graph-mapping branch from 15328f9 to b7cc58c Compare January 31, 2026 11:01
GiggleLiu and others added 9 commits January 31, 2026 19:15
- 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>
- 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>
- 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>
- 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>
- 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>
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>
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>
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>
@GiggleLiu GiggleLiu merged commit 79e3a06 into main Feb 2, 2026
4 of 6 checks passed
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.

1 participant