Skip to content
Merged
4 changes: 4 additions & 0 deletions .github/ISSUE_TEMPLATE/problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ assignees: ''

---

## Motivation

<!-- One sentence: why is this problem useful to include? E.g. "Widely used in network design and has known reductions to QUBO." -->

## Definition

**Name:** <!-- e.g. MaximumIndependentSet. Use Maximum/Minimum prefix for optimization problems -->
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ assignees: ''

**Source:** <!-- e.g. MaximumIndependentSet. Browse existing problems: https://codingthrust.github.io/problem-reductions/ -->
**Target:** <!-- e.g. QUBO -->
**Motivation:** <!-- One sentence: why is this reduction useful? E.g. "Enables solving MIS on quantum annealers via QUBO formulation." -->
**Reference:** <!-- URL, paper, or textbook citation for this reduction -->

## Reduction Algorithm
Expand Down
20 changes: 11 additions & 9 deletions benches/solver_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn bench_independent_set(c: &mut Criterion) {
for n in [4, 6, 8, 10].iter() {
// Create a path graph with n vertices
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
let problem = MaximumIndependentSet::<SimpleGraph, i32>::new(*n, edges);
let problem = MaximumIndependentSet::new(SimpleGraph::new(*n, edges), vec![1i32; *n]);
let solver = BruteForce::new();

group.bench_with_input(BenchmarkId::new("path", n), n, |b, _| {
Expand All @@ -35,7 +35,7 @@ fn bench_vertex_covering(c: &mut Criterion) {

for n in [4, 6, 8, 10].iter() {
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
let problem = MinimumVertexCover::<SimpleGraph, i32>::new(*n, edges);
let problem = MinimumVertexCover::new(SimpleGraph::new(*n, edges), vec![1i32; *n]);
let solver = BruteForce::new();

group.bench_with_input(BenchmarkId::new("path", n), n, |b, _| {
Expand All @@ -51,8 +51,9 @@ fn bench_max_cut(c: &mut Criterion) {
let mut group = c.benchmark_group("MaxCut");

for n in [4, 6, 8, 10].iter() {
let edges: Vec<(usize, usize, i32)> = (0..*n - 1).map(|i| (i, i + 1, 1)).collect();
let problem = MaxCut::new(*n, edges);
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
let weights = vec![1i32; edges.len()];
let problem = MaxCut::new(SimpleGraph::new(*n, edges), weights);
let solver = BruteForce::new();

group.bench_with_input(BenchmarkId::new("path", n), n, |b, _| {
Expand Down Expand Up @@ -139,7 +140,7 @@ fn bench_coloring(c: &mut Criterion) {

for n in [3, 4, 5, 6].iter() {
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
let problem = KColoring::<K3, SimpleGraph>::new(*n, edges);
let problem = KColoring::<K3, _>::new(SimpleGraph::new(*n, edges));
let solver = BruteForce::new();

group.bench_with_input(BenchmarkId::new("path_3colors", n), n, |b, _| {
Expand All @@ -155,8 +156,9 @@ fn bench_matching(c: &mut Criterion) {
let mut group = c.benchmark_group("Matching");

for n in [4, 6, 8, 10].iter() {
let edges: Vec<(usize, usize, i32)> = (0..*n - 1).map(|i| (i, i + 1, 1)).collect();
let problem = MaximumMatching::new(*n, edges);
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
let weights = vec![1i32; edges.len()];
let problem = MaximumMatching::new(SimpleGraph::new(*n, edges), weights);
let solver = BruteForce::new();

group.bench_with_input(BenchmarkId::new("path", n), n, |b, _| {
Expand Down Expand Up @@ -196,7 +198,7 @@ fn bench_comparison(c: &mut Criterion) {

// MaximumIndependentSet with 8 vertices
let is_problem =
MaximumIndependentSet::<SimpleGraph, i32>::new(8, vec![(0, 1), (2, 3), (4, 5), (6, 7)]);
MaximumIndependentSet::new(SimpleGraph::new(8, vec![(0, 1), (2, 3), (4, 5), (6, 7)]), vec![1i32; 8]);
group.bench_function("MaximumIndependentSet", |b| {
b.iter(|| solver.find_best(black_box(&is_problem)))
});
Expand Down Expand Up @@ -226,7 +228,7 @@ fn bench_comparison(c: &mut Criterion) {
});

// MaxCut with 8 vertices
let mc_problem = MaxCut::new(8, vec![(0, 1, 1), (2, 3, 1), (4, 5, 1), (6, 7, 1)]);
let mc_problem = MaxCut::new(SimpleGraph::new(8, vec![(0, 1), (2, 3), (4, 5), (6, 7)]), vec![1, 1, 1, 1]);
group.bench_function("MaxCut", |b| {
b.iter(|| solver.find_best(black_box(&mc_problem)))
});
Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use problemreductions::prelude::*;
use problemreductions::topology::SimpleGraph;

// 1. Create: Independent Set on a path graph (4 vertices)
let problem = MaximumIndependentSet::<SimpleGraph, i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
let problem = MaximumIndependentSet::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]), vec![1i32; 4]);

// 2. Reduce: Transform to Minimum Vertex Cover
let reduction = ReduceTo::<MinimumVertexCover<SimpleGraph, i32>>::reduce_to(&problem);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_kcoloring_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create KColoring instance: Petersen graph (10 vertices, 15 edges) with 3 colors, χ=3
let (num_vertices, edges) = petersen();
let coloring = KColoring::<K3, SimpleGraph>::new(num_vertices, edges.clone());
let coloring = KColoring::<K3, _>::new(SimpleGraph::new(num_vertices, edges.clone()));

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&coloring);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_kcoloring_to_qubo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub fn run() {

// House graph: 5 vertices, 6 edges (square base + triangle roof), χ=3
let (num_vertices, edges) = house();
let kc = KColoring::<K3, SimpleGraph>::new(num_vertices, edges.clone());
let kc = KColoring::<K3, _>::new(SimpleGraph::new(num_vertices, edges.clone()));

// Reduce to QUBO
let reduction = ReduceTo::<QUBO>::reduce_to(&kc);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maxcut_to_spinglass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use problemreductions::topology::{Graph, SimpleGraph};

pub fn run() {
let (num_vertices, edges) = petersen();
let maxcut = MaxCut::<SimpleGraph, i32>::unweighted(num_vertices, edges.clone());
let maxcut = MaxCut::<_, i32>::unweighted(SimpleGraph::new(num_vertices, edges.clone()));

let reduction = ReduceTo::<SpinGlass<SimpleGraph, i32>>::reduce_to(&maxcut);
let sg = reduction.target_problem();
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maximumclique_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create MaximumClique instance: Octahedron (K_{2,2,2}), 6 vertices, 12 edges, clique number 3
let (num_vertices, edges) = octahedral();
let clique = MaximumClique::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let clique = MaximumClique::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&clique);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maximumindependentset_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create IS instance: Petersen graph
let (num_vertices, edges) = petersen();
let is = MaximumIndependentSet::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let is = MaximumIndependentSet::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&is);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn run() {

// Petersen graph: 10 vertices, 15 edges, 3-regular
let (num_vertices, edges) = petersen();
let source = MaximumIndependentSet::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let source = MaximumIndependentSet::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

println!("Source: MaximumIndependentSet on Petersen graph");
println!(" Vertices: {}", num_vertices);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create IS instance: Petersen graph
let (num_vertices, edges) = petersen();
let is = MaximumIndependentSet::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let is = MaximumIndependentSet::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// 2. Reduce to VC
let reduction = ReduceTo::<MinimumVertexCover<SimpleGraph, i32>>::reduce_to(&is);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maximumindependentset_to_qubo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn run() {

// Petersen graph: 10 vertices, 15 edges, 3-regular
let (num_vertices, edges) = petersen();
let is = MaximumIndependentSet::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let is = MaximumIndependentSet::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// Reduce to QUBO
let reduction = ReduceTo::<QUBO>::reduce_to(&is);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maximummatching_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create MaximumMatching instance: Petersen graph with unit weights
let (num_vertices, edges) = petersen();
let matching = MaximumMatching::<SimpleGraph, i32>::unweighted(num_vertices, edges.clone());
let matching = MaximumMatching::<_, i32>::unit_weights(SimpleGraph::new(num_vertices, edges.clone()));

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&matching);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_maximummatching_to_maximumsetpacking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn run() {

// Petersen graph with unit weights
let (num_vertices, edges) = petersen();
let source = MaximumMatching::<SimpleGraph, i32>::unweighted(num_vertices, edges.clone());
let source = MaximumMatching::<_, i32>::unit_weights(SimpleGraph::new(num_vertices, edges.clone()));

println!("Source: MaximumMatching on Petersen graph");
println!(" Vertices: {}", num_vertices);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_minimumdominatingset_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create MinimumDominatingSet instance: Petersen graph
let (num_vertices, edges) = petersen();
let ds = MinimumDominatingSet::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let ds = MinimumDominatingSet::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&ds);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_minimumvertexcover_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// 1. Create VC instance: Petersen graph (10 vertices, 15 edges), VC=6
let (num_vertices, edges) = petersen();
let vc = MinimumVertexCover::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let vc = MinimumVertexCover::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// 2. Reduce to ILP
let reduction = ReduceTo::<ILP>::reduce_to(&vc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use problemreductions::topology::{Graph, SimpleGraph};
pub fn run() {
// Petersen graph: 10 vertices, 15 edges, VC=6
let (num_vertices, edges) = petersen();
let vc = MinimumVertexCover::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let vc = MinimumVertexCover::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

let reduction = ReduceTo::<MaximumIndependentSet<SimpleGraph, i32>>::reduce_to(&vc);
let is = reduction.target_problem();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn run() {

// Petersen graph: 10 vertices, 15 edges, VC=6
let (num_vertices, edges) = petersen();
let source = MinimumVertexCover::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let source = MinimumVertexCover::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

println!("Source: MinimumVertexCover on Petersen graph");
println!(" Vertices: {}", num_vertices);
Expand Down
2 changes: 1 addition & 1 deletion examples/reduction_minimumvertexcover_to_qubo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn run() {

// Petersen graph: 10 vertices, 15 edges, VC=6
let (num_vertices, edges) = petersen();
let vc = MinimumVertexCover::<SimpleGraph, i32>::new(num_vertices, edges.clone());
let vc = MinimumVertexCover::new(SimpleGraph::new(num_vertices, edges.clone()), vec![1i32; num_vertices]);

// Reduce to QUBO
let reduction = ReduceTo::<QUBO>::reduce_to(&vc);
Expand Down
13 changes: 3 additions & 10 deletions examples/reduction_travelingsalesman_to_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,9 @@ use problemreductions::topology::{Graph, SimpleGraph};

pub fn run() {
// 1. Create TSP instance: K4 with weights
let problem = TravelingSalesman::<SimpleGraph, i32>::new(
4,
vec![
(0, 1, 10),
(0, 2, 15),
(0, 3, 20),
(1, 2, 35),
(1, 3, 25),
(2, 3, 30),
],
let problem = TravelingSalesman::new(
SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]),
vec![10, 15, 20, 35, 25, 30],
);

// 2. Reduce to ILP
Expand Down
2 changes: 1 addition & 1 deletion src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl FileFormat {
/// use problemreductions::models::graph::MaximumIndependentSet;
/// use problemreductions::topology::SimpleGraph;
///
/// let problem = MaximumIndependentSet::<SimpleGraph, i32>::new(3, vec![(0, 1), (1, 2)]);
/// let problem = MaximumIndependentSet::new(SimpleGraph::new(3, vec![(0, 1), (1, 2)]), vec![1i32; 3]);
/// write_problem(&problem, "problem.json", FileFormat::Json).unwrap();
/// ```
pub fn write_problem<T: Serialize, P: AsRef<Path>>(
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//! use problemreductions::topology::SimpleGraph;
//!
//! // Create an Independent Set problem on a triangle graph
//! let problem = MaximumIndependentSet::<SimpleGraph, i32>::new(3, vec![(0, 1), (1, 2), (0, 2)]);
//! let problem = MaximumIndependentSet::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]), vec![1i32; 3]);
//!
//! // Solve with brute force
//! let solver = BruteForce::new();
Expand Down
60 changes: 21 additions & 39 deletions src/models/graph/kcoloring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! such that no two adjacent vertices have the same color.

use crate::registry::{FieldInfo, ProblemSchemaEntry};
use crate::topology::{Graph, SimpleGraph};
use crate::topology::Graph;
use crate::traits::{Problem, SatisfactionProblem};
use crate::variant::{KValue, VariantParam, KN};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -39,7 +39,8 @@ inventory::submit! {
/// use problemreductions::{Problem, Solver, BruteForce};
///
/// // Triangle graph needs at least 3 colors
/// let problem = KColoring::<K3, SimpleGraph>::new(3, vec![(0, 1), (1, 2), (0, 2)]);
/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]);
/// let problem = KColoring::<K3, _>::new(graph);
///
/// let solver = BruteForce::new();
/// let solutions = solver.find_all_satisfying(&problem);
Expand All @@ -65,34 +66,15 @@ fn default_num_colors<K: KValue>() -> usize {
K::K.unwrap_or(0)
}

impl<K: KValue> KColoring<K, SimpleGraph> {
/// Create a new K-Coloring problem.
///
/// # Arguments
/// * `num_vertices` - Number of vertices
/// * `edges` - List of edges as (u, v) pairs
///
/// # Panics
/// Panics if `K` is `KN` (use [`from_graph_with_k`](Self::from_graph_with_k) instead).
pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self {
let graph = SimpleGraph::new(num_vertices, edges);
Self {
graph,
num_colors: K::K.expect("KN requires from_graph_with_k"),
_phantom: std::marker::PhantomData,
}
}
}

impl<K: KValue, G: Graph> KColoring<K, G> {
/// Create a K-Coloring problem from an existing graph.
/// Create a new K-Coloring problem from a graph.
///
/// # Panics
/// Panics if `K` is `KN` (use [`KColoring::<KN, G>::from_graph_with_k`] instead).
pub fn from_graph(graph: G) -> Self {
/// Panics if `K` is `KN` (use [`KColoring::<KN, G>::with_k`] instead).
pub fn new(graph: G) -> Self {
Self {
graph,
num_colors: K::K.expect("KN requires from_graph_with_k"),
num_colors: K::K.expect("KN requires with_k"),
_phantom: std::marker::PhantomData,
}
}
Expand Down Expand Up @@ -124,9 +106,9 @@ impl<G: Graph> KColoring<KN, G> {
/// Create a K-Coloring problem with an explicit number of colors.
///
/// Only available for `KN` (runtime K). For compile-time K types like
/// `K3`, use [`from_graph`](KColoring::from_graph) which derives K
/// from the type parameter.
pub fn from_graph_with_k(graph: G, num_colors: usize) -> Self {
/// `K3`, use [`new`](KColoring::new) which derives K from the type
/// parameter.
pub fn with_k(graph: G, num_colors: usize) -> Self {
Self {
graph,
num_colors,
Expand Down Expand Up @@ -158,22 +140,22 @@ where
impl<K: KValue, G: Graph + VariantParam> SatisfactionProblem for KColoring<K, G> {}

/// Check if a coloring is valid for a graph.
pub fn is_valid_coloring(
num_vertices: usize,
edges: &[(usize, usize)],
coloring: &[usize],
num_colors: usize,
) -> bool {
if coloring.len() != num_vertices {
return false;
}
///
/// # Panics
/// Panics if `coloring.len() != graph.num_vertices()`.
pub fn is_valid_coloring<G: Graph>(graph: &G, coloring: &[usize], num_colors: usize) -> bool {
assert_eq!(
coloring.len(),
graph.num_vertices(),
"coloring length must match num_vertices"
);
// Check all colors are valid
if coloring.iter().any(|&c| c >= num_colors) {
return false;
}
// Check no adjacent vertices have same color
for &(u, v) in edges {
if u < coloring.len() && v < coloring.len() && coloring[u] == coloring[v] {
for (u, v) in graph.edges() {
if coloring[u] == coloring[v] {
return false;
}
}
Expand Down
Loading