Skip to content

Fix #300: Add QuadraticAssignment model#622

Open
zazabap wants to merge 11 commits intomainfrom
issue-300-quadratic-assignment
Open

Fix #300: Add QuadraticAssignment model#622
zazabap wants to merge 11 commits intomainfrom
issue-300-quadratic-assignment

Conversation

@zazabap
Copy link
Collaborator

@zazabap zazabap commented Mar 13, 2026

Summary

  • Add QuadraticAssignment (QAP) problem model to src/models/algebraic/
  • Classical NP-hard facility-location optimization (Koopmans-Beckmann 1957, Sahni-Gonzalez 1976)
  • Minimizes total interaction cost: Σ c_{ij} · d_{f(i),f(j)} over injective assignments

Fixes #300

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

codecov bot commented Mar 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.83%. Comparing base (cd96c8a) to head (8e26781).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #622      +/-   ##
==========================================
+ Coverage   96.81%   96.83%   +0.01%     
==========================================
  Files         244      246       +2     
  Lines       31650    31801     +151     
==========================================
+ Hits        30642    30793     +151     
  Misses       1008     1008              

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

zazabap and others added 3 commits March 13, 2026 07:02
- Add QuadraticAssignment model in src/models/algebraic/
- Register in CLI (dispatch, aliases, create command)
- Add unit tests (creation, evaluation, solver, serialization)
- Add paper entry with CeTZ example diagram
- Regenerate problem schemas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add out-of-range location index test
- Add rectangular case test (n < m)
- Add #[should_panic] tests for constructor validation

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

zazabap commented Mar 13, 2026

Implementation Summary

Changes

  • src/models/algebraic/quadratic_assignment.rs — New QAP model (172 lines): struct, Problem/OptimizationProblem traits, declare_variants
  • src/unit_tests/models/algebraic/quadratic_assignment.rs — 10 unit tests: creation, evaluation (identity, swap, invalid, out-of-range), direction, serialization, solver, rectangular case, constructor panics
  • src/models/algebraic/mod.rs — Register module + export
  • src/models/mod.rs — Add to re-exports
  • src/lib.rs — Add to prelude
  • problemreductions-cli/src/dispatch.rs — load_problem + serialize_any_problem arms
  • problemreductions-cli/src/problem_name.rs — QAP alias + lowercase mapping
  • problemreductions-cli/src/commands/create.rs — Create handler with --matrix and --distance-matrix flags
  • problemreductions-cli/src/cli.rs — --distance-matrix flag + help table entry
  • docs/paper/reductions.typ — problem-def with formal definition, background, 4x4 example with CeTZ figure
  • docs/src/reductions/problem_schemas.json — Regenerated (28 schemas)

Deviations from Plan

  • The issue's example instance optimal cost is 36 (not 38 as stated in the issue for the identity assignment — the identity gives 38 but is not optimal)
  • Paper uses a different 4x4 example (optimal cost 22) for clearer exposition

Open Questions

  • Complexity string uses num_facilities ^ num_facilities (n^n) as Expr AST doesn't support factorial. This is an upper bound on the brute-force search space.

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 a new algebraic optimization model for the Quadratic Assignment Problem (QAP) and wires it into the library, CLI, and docs so it can be created/serialized/solved via existing infrastructure.

Changes:

  • Introduces QuadraticAssignment model with schema registration, variant declaration, and unit tests.
  • Exposes QAP through crate re-exports/prelude and CLI (alias resolution, dispatch load/serialize, pred create support).
  • Updates documentation outputs (schemas JSON + paper) to include the new problem.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/models/algebraic/quadratic_assignment.rs New QAP model implementation, schema registration, and variant declaration.
src/unit_tests/models/algebraic/quadratic_assignment.rs Unit tests for construction, evaluation, serialization, and brute-force solving.
src/models/algebraic/mod.rs Adds the new module + re-export from algebraic.
src/models/mod.rs Re-exports QuadraticAssignment from top-level models.
src/lib.rs Adds QuadraticAssignment to the crate prelude.
problemreductions-cli/src/problem_name.rs Adds QAP alias and resolves qap/quadraticassignment.
problemreductions-cli/src/dispatch.rs Enables CLI load/serialize for QuadraticAssignment.
problemreductions-cli/src/commands/create.rs Adds pred create QAP handling and --distance-matrix parsing.
problemreductions-cli/src/cli.rs Documents QAP flags and adds the --distance-matrix CLI argument.
docs/src/reductions/problem_schemas.json Adds schema entry for QuadraticAssignment.
docs/paper/reductions.typ Adds QAP display name and a new problem definition section.

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

Given $n$ facilities and $m$ locations ($n <= m$), a flow matrix $C in ZZ^(n times n)$ representing flows between facilities, and a distance matrix $D in ZZ^(m times m)$ representing distances between locations, find an injective assignment $f: {1, dots, n} -> {1, dots, m}$ that minimizes
$ sum_(i != j) C_(i j) dot D_(f(i), f(j)). $
][
The Quadratic Assignment Problem was introduced by Koopmans and Beckmann (1957) to model the optimal placement of economic activities (facilities) across geographic locations, minimizing total transportation cost weighted by inter-facility flows. It is NP-hard, as shown by Sahni and Gonzalez (1976) via reduction from the Hamiltonian Circuit problem. QAP is widely regarded as one of the hardest combinatorial optimization problems: even moderate instances ($n > 20$) challenge state-of-the-art exact solvers. Best exact approaches use branch-and-bound with Gilmore--Lawler bounds or Dreyfus--Wagner-like dynamic programming; the best known general algorithm runs in $O^*(n!)$ by exhaustive enumeration of all permutations#footnote[No algorithm significantly improving on brute-force permutation enumeration is known for general QAP.].
Comment on lines +390 to +405
{
"name": "QuadraticAssignment",
"description": "Minimize total cost of assigning facilities to locations",
"fields": [
{
"name": "cost_matrix",
"type_name": "Vec<Vec<i64>>",
"description": "Flow/cost matrix between facilities"
},
{
"name": "distance_matrix",
"type_name": "Vec<Vec<i64>>",
"description": "Distance matrix between locations"
}
]
},
module_path: module_path!(),
description: "Minimize total cost of assigning facilities to locations",
fields: &[
FieldInfo { name: "cost_matrix", type_name: "Vec<Vec<i64>>", description: "Flow/cost matrix between facilities" },
Comment on lines +121 to +146
fn evaluate(&self, config: &[usize]) -> SolutionSize<i64> {
let n = self.num_facilities();
let m = self.num_locations();

// Check that all assignments are valid locations
for &loc in config.iter().take(n) {
if loc >= m {
return SolutionSize::Invalid;
}
}

// Check injectivity: no two facilities assigned to the same location
let mut used = std::collections::HashSet::new();
for &loc in config.iter().take(n) {
if !used.insert(loc) {
return SolutionSize::Invalid;
}
}

// Compute objective: sum_{i != j} cost_matrix[i][j] * distance_matrix[config[i]][config[j]]
let mut total: i64 = 0;
for i in 0..n {
for j in 0..n {
if i != j {
total += self.cost_matrix[i][j] * self.distance_matrix[config[i]][config[j]];
}
Comment on lines +133 to +137
let mut used = std::collections::HashSet::new();
for &loc in config.iter().take(n) {
if !used.insert(loc) {
return SolutionSize::Invalid;
}
///
/// f(p) = sum_{i != j} C[i][j] * D[p(i)][p(j)]
///
/// where p is a permutation mapping facilities to locations.
Comment on lines +269 to +294
// QuadraticAssignment
"QuadraticAssignment" => {
let cost_str = args.matrix.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"QuadraticAssignment requires --matrix (cost) and --distance-matrix\n\n\
Usage: pred create QAP --matrix \"0,5;5,0\" --distance-matrix \"0,1;1,0\""
)
})?;
let dist_str = args.distance_matrix.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"QuadraticAssignment requires --distance-matrix\n\n\
Usage: pred create QAP --matrix \"0,5;5,0\" --distance-matrix \"0,1;1,0\""
)
})?;
let cost_matrix = parse_i64_matrix(cost_str).context("Invalid cost matrix")?;
let distance_matrix = parse_i64_matrix(dist_str).context("Invalid distance matrix")?;
(
ser(
problemreductions::models::algebraic::QuadraticAssignment::new(
cost_matrix,
distance_matrix,
),
)?,
resolved_variant.clone(),
)
}
Comment on lines +925 to +936
s.split(';')
.map(|row| {
row.trim()
.split(',')
.map(|v| {
v.trim()
.parse::<i64>()
.map_err(|e| anyhow::anyhow!("Invalid matrix value: {}", e))
})
.collect()
})
.collect()
zazabap and others added 3 commits March 13, 2026 15:58
- Fix Dreyfus-Wagner reference in paper (not applicable to QAP, replaced with cutting-plane methods)
- Add config length check in evaluate() to return Invalid instead of panicking
- Replace HashSet with Vec<bool> for faster injectivity checking
- Fix doc wording: "permutation" -> "injective mapping" for rectangular case
- Add input validation in CLI create to surface errors instead of panicking
- Improve parse_i64_matrix with row/col context in errors and ragged matrix detection
- Regenerate problem_schemas.json after merge
- Add tests for config length mismatch

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

zazabap commented Mar 13, 2026

Review Pipeline Report

Check Result
Merge with main resolved 3 conflicts (additive entries in reductions.typ, problem_schemas.json, dispatch.rs)
Copilot comments 7 fixed (paper ref, length check, Vec optimization, doc wording, CLI validation, matrix parser, schemas regenerated)
Issue/human comments 2 checked, 0 action needed (quality check passed, implementation summary informational)
CI green (local make check passes; remote CI pending trigger)
Agentic test passed — CLI create/evaluate/solve and Rust API all work correctly
Board review-agentic → In Review

Copilot Comments Addressed

  1. reductions.typ:735 — Replaced Dreyfus-Wagner (Steiner Tree DP) with cutting-plane methods
  2. problem_schemas.json — Regenerated after merge
  3. quadratic_assignment.rs:17 — CLI flag naming (--matrix) is consistent with other problems (QUBO etc.)
  4. quadratic_assignment.rs:146 — Added config.len() != n early check returning Invalid
  5. quadratic_assignment.rs:137 — Replaced HashSet with Vec<bool> for O(1) injectivity checking
  6. quadratic_assignment.rs:32 — Updated doc: "permutation" → "injective mapping (permutation when n == m)"
  7. create.rs:338 — Added matrix squareness and n ≤ m validation before new() call
  8. create.rs:1081 — Added ragged matrix detection and row/col context in parse errors

🤖 Generated by review-pipeline

GiggleLiu and others added 4 commits March 14, 2026 02:33
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add factorial() function support to the expression parser (both proc macro
and runtime) and all Expr match sites (eval, display, canonical, big_o,
analysis). Change QuadraticAssignment's declare_variants! complexity from
"num_facilities ^ num_facilities" to "factorial(num_facilities)".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ssignment

# Conflicts:
#	docs/paper/reductions.typ
#	problemreductions-cli/src/cli.rs
#	problemreductions-cli/src/commands/create.rs
#	problemreductions-cli/src/dispatch.rs
#	problemreductions-cli/src/problem_name.rs
#	problemreductions-macros/src/parser.rs
#	src/expr.rs
#	src/lib.rs
#	src/unit_tests/trait_consistency.rs
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.

[Model] QuadraticAssignment

3 participants