Skip to content

Implement 6 problem-to-QUBO reductions (Issue #18)#29

Merged
GiggleLiu merged 13 commits intomainfrom
issue-18-qubo-reductions
Feb 9, 2026
Merged

Implement 6 problem-to-QUBO reductions (Issue #18)#29
GiggleLiu merged 13 commits intomainfrom
issue-18-qubo-reductions

Conversation

@GiggleLiu
Copy link
Contributor

@GiggleLiu GiggleLiu commented Feb 8, 2026

Summary

Implements 6 problem-to-QUBO reductions for Issue #18, with full test coverage, documentation, and an example program. MaxCut→QUBO was removed as redundant (reachable via MaxCut↔SpinGlass↔QUBO).

Reductions added

  • IndependentSet → QUBO — penalty method: Q[i,i] = -w_i, Q[i,j] = P for edges
  • VertexCovering → QUBO — penalty method: Q[i,i] = w_i - P·deg(i), Q[i,j] = P for edges
  • KColoring → QUBO — one-hot encoding with constraint + edge penalties (n·K variables)
  • SetPacking → QUBO — IS on intersection graph
  • KSatisfiability(K=2) → QUBO — per-clause penalty gadgets for Max-2-SAT
  • ILP (binary) → QUBO — Q = -diag(c + 2Pb^T A) + P A^T A with slack variables

Bug fixes

  • ILP→QUBO slack variable signs — Le now uses +1 (Ax + s = b), Ge uses -1 (Ax - s = b); previously swapped
  • ILP→QUBO slack count formula — changed from ceil(log2(range)) to ceil(log2(range + 1)) to represent all valid slack values 0..range

Files added/modified

  • src/rules/{independentset,vertexcovering,coloring,setpacking,ksatisfiability,ilp}_qubo.rs — 6 reduction implementations
  • src/unit_tests/rules/*_qubo.rs — unit tests (closed-loop + edge cases + coverage for slack/swap branches)
  • tests/suites/reductions.rs — 6 integration tests against ground truth JSON from qubogen
  • examples/qubo_reductions.rs — example program with practical stories for all 6 reductions
  • docs/paper/reductions.typ — 6 new theorems with step-by-step mathematical proofs
  • README.md — added Acknowledgments section (ProblemReductions.jl, UnitDiskMapping.jl, qubogen)
  • src/unit_tests/rules/graph.rs — fix path-finding tests for new QUBO edges in reduction graph

Removed (redundant)

  • MaxCut → QUBO — redundant via MaxCut ↔ SpinGlass ↔ QUBO path

Test plan

  • make test — 1622 tests pass (1512 unit + 61 integration + 49 doc)
  • make clippy — no warnings
  • make paper — Typst paper compiles with new theorems
  • cargo run --example qubo_reductions --features ilp — example runs successfully
  • All 6 integration tests validate against qubogen ground truth JSON

🤖 Generated with Claude Code

- Add scripts/generate_qubo_tests.py using qubogen to generate QUBO
  ground truth for 7 problem types: MaxCut, VertexCovering, IndependentSet,
  Coloring, SetPacking, KSatisfiability (2-SAT), and ILP
- Add scripts/ Python project managed by uv (pyproject.toml, uv.lock)
- Add tests/data/qubo/*.json with brute-force optimal solutions
- Add `make qubo-testdata` target to regenerate test data
- Update .gitignore for .venv/

Ref #18

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 8, 2026

Codecov Report

❌ Patch coverage is 99.83897% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.27%. Comparing base (e27ef70) to head (054bbce).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rules/ilp_qubo.rs 98.92% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #29      +/-   ##
==========================================
+ Coverage   97.21%   97.27%   +0.05%     
==========================================
  Files         160      172      +12     
  Lines       25169    25778     +609     
==========================================
+ Hits        24469    25076     +607     
- Misses        700      702       +2     

☔ 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.

GiggleLiu and others added 2 commits February 8, 2026 14:09
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@GiggleLiu
Copy link
Contributor Author

[action]

GiggleLiu and others added 6 commits February 9, 2026 17:06
Implement three problem-to-QUBO reductions with closed-loop unit tests:
- IndependentSet → QUBO: penalty formulation with P = 1 + Σw_i
- VertexCovering → QUBO: constraint penalty for uncovered edges
- MaxCut → QUBO: unconstrained cut maximization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement three more problem-to-QUBO reductions with closed-loop tests:
- KColoring → QUBO: one-hot encoding with n*K variables
- SetPacking → QUBO: IS on intersection graph structure
- KSatisfiability(K=2) → QUBO: Max-2-SAT penalty formulation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement binary ILP to QUBO reduction with slack variables for
inequality constraints. Feature-gated behind `ilp`. Supports
Maximize, Minimize, and equality/inequality constraints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 7 integration tests comparing Rust QUBO reductions against
Python-generated ground truth JSON datasets for IS, VC, MaxCut,
KColoring, SetPacking, KSatisfiability, and ILP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Demonstrate all 7 problem-to-QUBO reductions with practical stories:
wireless towers (IS), security cameras (VC), network partitioning
(MaxCut), map coloring, delivery zones (SP), switch config (2-SAT),
and project selection (ILP).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 7 QUBO reduction theorems with step-by-step mathematical proofs
  (IS, VC, MaxCut, KColoring, SetPacking, 2-SAT, ILP → QUBO)
- Update QUBO definition to clarify upper-triangular matrix convention
- Update problem definitions with "Reduces to: QUBO" cross-references
- Add 7 rows to the summary table
- Add Acknowledgments section to README crediting ProblemReductions.jl,
  UnitDiskMapping.jl, and qubogen
- Fix graph path-finding tests for new QUBO reduction edges
- Update .gitignore for pkgref/ reference packages directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@GiggleLiu GiggleLiu changed the title Add QUBO test data generation for problem-to-QUBO reductions Implement 7 problem-to-QUBO reductions (Issue #18) Feb 9, 2026
…age tests

- Fix ILP→QUBO slack variable signs (Le→+1, Ge→-1) and count formula
  (ceil(log2(range+1)) to represent all valid slack values)
- Remove redundant MaxCut→QUBO reduction (reachable via MaxCut→SpinGlass→QUBO)
- Add round-trip tests covering ILP Ge/Le slack branches, KColoring
  reversed edge ordering, and KSat reversed variable ordering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@GiggleLiu GiggleLiu changed the title Implement 7 problem-to-QUBO reductions (Issue #18) Implement 6 problem-to-QUBO reductions (Issue #18) Feb 9, 2026
@GiggleLiu GiggleLiu requested a review from Copilot February 9, 2026 11:48
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

Adds multiple problem→QUBO reductions to the reduction registry, along with unit + integration tests (including ground-truth JSON fixtures), a runnable example, and accompanying documentation/paper updates for Issue #18.

Changes:

  • Implement new reductions to QUBO<f64> for IndependentSet, VertexCovering, KColoring, SetPacking, KSatisfiability(K=2), and (feature-gated) binary ILP.
  • Add unit tests per reduction plus integration tests that validate solutions against ground-truth JSON instances (generated via scripts/generate_qubo_tests.py).
  • Update docs/paper, reduction graph artifacts, repo tooling (Makefile target + Python scripts), and acknowledgments.

Reviewed changes

Copilot reviewed 33 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/rules/mod.rs Registers and re-exports the new QUBO reductions (and ILP→QUBO behind feature flag).
src/rules/independentset_qubo.rs Implements IndependentSet→QUBO reduction (penalty method).
src/rules/vertexcovering_qubo.rs Implements VertexCovering→QUBO reduction (edge-violation penalty).
src/rules/coloring_qubo.rs Implements KColoring→QUBO reduction using one-hot encoding + edge penalties.
src/rules/setpacking_qubo.rs Implements SetPacking→QUBO reduction (intersection-graph IS form).
src/rules/ksatisfiability_qubo.rs Implements KSatisfiability(K=2)→QUBO clause-penalty gadget.
src/rules/ilp_qubo.rs Implements binary ILP→QUBO (adds slack bits; builds quadratic penalty form).
src/unit_tests/rules/independentset_qubo.rs Unit tests for IS→QUBO closed-loop correctness + size metadata.
src/unit_tests/rules/vertexcovering_qubo.rs Unit tests for VC→QUBO on common small graphs + size metadata.
src/unit_tests/rules/coloring_qubo.rs Unit tests for KColoring→QUBO (valid colorings count + edge-order branch).
src/unit_tests/rules/setpacking_qubo.rs Unit tests for SetPacking→QUBO across overlap scenarios + size metadata.
src/unit_tests/rules/ksatisfiability_qubo.rs Unit tests for 2-SAT→QUBO including swap-branch coverage.
src/unit_tests/rules/ilp_qubo.rs Unit tests for ILP→QUBO across sense/constraint types + slack branches.
src/unit_tests/rules/graph.rs Updates reduction-graph path tests to reflect new direct IS→QUBO edge + shortest-path APIs.
tests/suites/reductions.rs Adds integration tests that load ground-truth JSON and validate extracted solutions.
tests/data/qubo/independentset_to_qubo.json Ground-truth QUBO fixture for IndependentSet instance.
tests/data/qubo/vertexcovering_to_qubo.json Ground-truth QUBO fixture for VertexCovering instance.
tests/data/qubo/coloring_to_qubo.json Ground-truth QUBO fixture for Coloring instance.
tests/data/qubo/setpacking_to_qubo.json Ground-truth QUBO fixture for SetPacking instance.
tests/data/qubo/ksatisfiability_to_qubo.json Ground-truth QUBO fixture for KSatisfiability(2) instance.
tests/data/qubo/ilp_to_qubo.json Ground-truth QUBO fixture for ILP instance.
examples/qubo_reductions.rs Adds an example program demonstrating the six reductions end-to-end.
scripts/generate_qubo_tests.py Adds Python generator to produce ground-truth QUBO JSON via qubogen.
scripts/pyproject.toml Declares Python deps for script execution via uv.
scripts/uv.lock Locks Python dependencies for reproducible test-data generation.
scripts/.python-version Pins Python version for script environment.
docs/paper/reductions.typ Adds/updates formal reduction definitions, theorems, and proofs for QUBO reductions.
docs/paper/reduction_graph.json Updates paper’s reduction graph artifact with new nodes/edges.
docs/plans/2026-02-08-qubo-reductions-plan.md Adds implementation plan document for the work.
README.md Adds acknowledgments referencing inspiration/test-data sources.
Makefile Adds qubo-testdata target to regenerate ground-truth JSON via uv.
.gitignore Ignores Python virtualenv and reference package directory.
.claude/skills/issue-to-pr.md Updates internal workflow documentation around planning/brainstorming.
.claude/rules/adding-reductions.md Documents expected workflow for adding reductions (tests, examples, docs).
.claude/CLAUDE.md Notes make qubo-testdata and documents new repo directories.

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

Comment on lines +86 to +100
Comparison::Le => {
// Ax <= b → Ax + s = b, s ∈ {0, ..., floor(b)}
let slack_range = constraint.rhs;
if slack_range > 0.0 {
slack_sizes[k] = (slack_range + 1.0).log2().ceil() as usize;
}
}
Comparison::Ge => {
// Ax >= b → Ax - s = b, s ∈ {0, ..., sum(a) - b}
let sum_a: f64 = constraint.terms.iter().map(|&(_, c)| c).sum();
let slack_range = sum_a - constraint.rhs;
if slack_range > 0.0 {
slack_sizes[k] = (slack_range + 1.0).log2().ceil() as usize;
}
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

ILP→QUBO slack sizing assumes coefficients are nonnegative: for Le it uses slack_range = rhs, and for Ge it uses slack_range = sum_a - rhs where sum_a is the raw sum of coefficients. This is incorrect when constraints contain negative coefficients (which ILP supports elsewhere), and can under-allocate slack bits, making feasible ILPs unsatisfiable in the QUBO encoding. Compute min_lhs/max_lhs over x∈{0,1}^n (e.g., max_lhs = Σ max(0,a_i), min_lhs = Σ min(0,a_i)) and derive slack ranges as rhs - min_lhs for Le and max_lhs - rhs for Ge (with appropriate feasibility checks).

Copilot uses AI. Check for mistakes.

for (k, constraint) in self.constraints.iter().enumerate() {
for &(var, coef) in &constraint.terms {
a_dense[k][var] = coef;
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

When building a_dense, coefficients for a variable are assigned with a_dense[k][var] = coef. If the constraint term list contains the same var multiple times, later entries overwrite earlier ones instead of being summed, changing the meaning of the constraint in the QUBO. Consider accumulating (+=) or normalizing/deduplicating terms before constructing the dense row.

Suggested change
a_dense[k][var] = coef;
// Accumulate coefficients in case the same variable appears multiple times
a_dense[k][var] += coef;

Copilot uses AI. Check for mistakes.
Comment on lines +600 to +602
let weights: Vec<i32> = data.source.weights.iter().map(|&w| w as i32).collect();
let sp = SetPacking::with_weights(data.source.sets, weights);
let reduction = ReduceTo::<QUBO>::reduce_to(&sp);
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

This converts JSON weights from f64 to i32 via as, which truncates (e.g. 1.5→1) and can change which SetPacking solution is optimal relative to the ground truth data. Either store integer weights in the JSON, scale them to integers (e.g. multiply by a common factor), or instantiate SetPacking<f64> / a float-weighted reduction so the test uses the same weights as the ground truth.

Copilot uses AI. Check for mistakes.
fn test_ilp_to_qubo_ge_with_slack() {
// Ge constraint with slack_range > 1 to exercise slack variable code path.
// 3 vars: minimize x0 + x1 + x2
// s.t. x0 + x1 + x2 >= 1 (sum_a=3, b=1, slack_range=2, ns=ceil(log2(2))=1)
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The comment here is out of date with the updated slack-bit formula (ceil(log2(slack_range + 1))). For slack_range=2 the correct value is ceil(log2(3))=2, not ceil(log2(2))=1. Please update the comment to match the implementation so the test remains self-explanatory.

Suggested change
// s.t. x0 + x1 + x2 >= 1 (sum_a=3, b=1, slack_range=2, ns=ceil(log2(2))=1)
// s.t. x0 + x1 + x2 >= 1 (sum_a=3, b=1, slack_range=2, ns=ceil(log2(slack_range + 1))=ceil(log2(3))=2)

Copilot uses AI. Check for mistakes.
edges = [(0, 1), (1, 2), (2, 3), (0, 3)]
n_nodes = 4
penalty = 8.0
g = qubogen.Graph(edges=np.array(edges), n_nodes=n_nodes)
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

Variable g is not used.

Suggested change
g = qubogen.Graph(edges=np.array(edges), n_nodes=n_nodes)

Copilot uses AI. Check for mistakes.
"""

import json
import os
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

Import of 'os' is not used.

Suggested change
import os

Copilot uses AI. Check for mistakes.
GiggleLiu and others added 3 commits February 9, 2026 20:04
Add Glover, Kochenberger & Du (2019) "Quantum Bridge Analytics I" as
primary reference for QUBO formulations. Cite Lucas (2014) for IS, VC,
KColoring, and ILP reductions. Update summary table citations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move 6 QUBO reductions from "Trivial" to new "Penalty-Method QUBO
Reductions" section. Add introductory paragraph explaining the penalty
method with label for cross-referencing. Each proof now references the
shared penalty-method description. Remove gray fill from summary table
for QUBO rows.

Trivial: complement/isomorphism (IS↔VC, IS↔SP, VC→SC, Matching→SP, SG↔QUBO)
Penalty-method: IS→QUBO, VC→QUBO, KColoring→QUBO, SP→QUBO, 2SAT→QUBO, ILP→QUBO
Non-trivial: gadget-based (SAT→IS, SAT→3-Coloring, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make SetPacking→QUBO reduction generic over weight type W: Into<f64>
  (supports both i32 and f64 weights, removing the f64→i32 truncation)
- Fix ILP slack range to use min_lhs/max_lhs for negative coefficients
- Fix duplicate variable accumulation (= → +=) in ILP constraint matrix
- Remove unused import and variable in Python test generator
- Update test comments to match corrected formulas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@GiggleLiu GiggleLiu merged commit 6649410 into main Feb 9, 2026
5 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.

2 participants