diff --git a/examples/chained_reduction_ksat_to_mis.rs b/examples/chained_reduction_ksat_to_mis.rs index 07f8d72d..9f662358 100644 --- a/examples/chained_reduction_ksat_to_mis.rs +++ b/examples/chained_reduction_ksat_to_mis.rs @@ -10,6 +10,7 @@ use problemreductions::rules::{MinimizeSteps, ReductionGraph}; use problemreductions::solvers::ILPSolver; use problemreductions::topology::SimpleGraph; use problemreductions::types::ProblemSize; +use problemreductions::variant::K3; // ANCHOR_END: imports pub fn run() { diff --git a/examples/export_mapping_stages.rs b/examples/export_mapping_stages.rs index 46d34e71..3caf8cde 100644 --- a/examples/export_mapping_stages.rs +++ b/examples/export_mapping_stages.rs @@ -8,8 +8,11 @@ //! cargo run --example export_mapping_stages -- petersen triangular use problemreductions::rules::unitdiskmapping::{ - create_copylines, ksg, mis_overhead_copyline, mis_overhead_copyline_triangular, triangular, - CopyLine, MappingGrid, + _internal::{ + create_copylines, mis_overhead_copyline, mis_overhead_copyline_triangular, CopyLine, + MappingGrid, + }, + ksg, triangular, }; use problemreductions::topology::smallgraph; use serde::Serialize; @@ -102,7 +105,7 @@ fn gadget_name(idx: usize) -> String { // The Typst script converts to 1-indexed for comparison with Julia. // DO NOT add +1 here - keep 0-indexed! fn extract_grid_nodes(grid: &MappingGrid) -> Vec { - use problemreductions::rules::unitdiskmapping::CellState; + use problemreductions::rules::unitdiskmapping::_internal::CellState; let mut nodes = Vec::new(); let (rows, cols) = grid.size(); for r in 0..rows { diff --git a/examples/reduction_factoring_to_ilp.rs b/examples/reduction_factoring_to_ilp.rs index 960a0265..a9bf1fc0 100644 --- a/examples/reduction_factoring_to_ilp.rs +++ b/examples/reduction_factoring_to_ilp.rs @@ -18,6 +18,7 @@ // Exports `docs/paper/examples/factoring_to_ilp.json` for use in paper code blocks. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::solvers::ILPSolver; diff --git a/examples/reduction_ilp_to_qubo.rs b/examples/reduction_ilp_to_qubo.rs index 2c1018cf..22588b3a 100644 --- a/examples/reduction_ilp_to_qubo.rs +++ b/examples/reduction_ilp_to_qubo.rs @@ -36,6 +36,7 @@ // ``` use problemreductions::export::*; +use problemreductions::models::optimization::{ILP, LinearConstraint, ObjectiveSense}; use problemreductions::prelude::*; pub fn run() { diff --git a/examples/reduction_kcoloring_to_ilp.rs b/examples/reduction_kcoloring_to_ilp.rs index 7a243abb..b52fac60 100644 --- a/examples/reduction_kcoloring_to_ilp.rs +++ b/examples/reduction_kcoloring_to_ilp.rs @@ -16,10 +16,12 @@ // Exports `docs/paper/examples/kcoloring_to_ilp.json` and `kcoloring_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::solvers::ILPSolver; use problemreductions::topology::small_graphs::petersen; use problemreductions::topology::{Graph, SimpleGraph}; +use problemreductions::variant::K3; pub fn run() { // 1. Create KColoring instance: Petersen graph (10 vertices, 15 edges) with 3 colors, χ=3 diff --git a/examples/reduction_kcoloring_to_qubo.rs b/examples/reduction_kcoloring_to_qubo.rs index 6bdfc1db..42a6f03c 100644 --- a/examples/reduction_kcoloring_to_qubo.rs +++ b/examples/reduction_kcoloring_to_qubo.rs @@ -32,6 +32,7 @@ use problemreductions::export::*; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::house; use problemreductions::topology::{Graph, SimpleGraph}; +use problemreductions::variant::K3; pub fn run() { println!("=== K-Coloring -> QUBO Reduction ===\n"); diff --git a/examples/reduction_ksatisfiability_to_qubo.rs b/examples/reduction_ksatisfiability_to_qubo.rs index d62686be..0886ef6e 100644 --- a/examples/reduction_ksatisfiability_to_qubo.rs +++ b/examples/reduction_ksatisfiability_to_qubo.rs @@ -38,6 +38,7 @@ use problemreductions::export::*; use problemreductions::prelude::*; +use problemreductions::variant::K3; pub fn run() { println!("=== K-Satisfiability (3-SAT) -> QUBO Reduction ===\n"); diff --git a/examples/reduction_maximumclique_to_ilp.rs b/examples/reduction_maximumclique_to_ilp.rs index 88d85d0a..978eddc2 100644 --- a/examples/reduction_maximumclique_to_ilp.rs +++ b/examples/reduction_maximumclique_to_ilp.rs @@ -15,6 +15,7 @@ // Exports `docs/paper/examples/maximumclique_to_ilp.json` and `maximumclique_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::octahedral; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/examples/reduction_maximumindependentset_to_ilp.rs b/examples/reduction_maximumindependentset_to_ilp.rs index 1407eb0a..babac97a 100644 --- a/examples/reduction_maximumindependentset_to_ilp.rs +++ b/examples/reduction_maximumindependentset_to_ilp.rs @@ -14,6 +14,7 @@ // Exports `docs/paper/examples/maximumindependentset_to_ilp.json` and `maximumindependentset_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::petersen; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/examples/reduction_maximummatching_to_ilp.rs b/examples/reduction_maximummatching_to_ilp.rs index c9cdd6ad..2c6ca348 100644 --- a/examples/reduction_maximummatching_to_ilp.rs +++ b/examples/reduction_maximummatching_to_ilp.rs @@ -14,6 +14,7 @@ // Exports `docs/paper/examples/maximummatching_to_ilp.json` and `maximummatching_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::petersen; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/examples/reduction_maximumsetpacking_to_ilp.rs b/examples/reduction_maximumsetpacking_to_ilp.rs index 1de5320a..5e6ce4b7 100644 --- a/examples/reduction_maximumsetpacking_to_ilp.rs +++ b/examples/reduction_maximumsetpacking_to_ilp.rs @@ -15,6 +15,7 @@ // Exports `docs/paper/examples/maximumsetpacking_to_ilp.json` and `maximumsetpacking_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; pub fn run() { diff --git a/examples/reduction_minimumdominatingset_to_ilp.rs b/examples/reduction_minimumdominatingset_to_ilp.rs index b2b2cae4..57aa18e6 100644 --- a/examples/reduction_minimumdominatingset_to_ilp.rs +++ b/examples/reduction_minimumdominatingset_to_ilp.rs @@ -14,6 +14,7 @@ // Exports `docs/paper/examples/minimumdominatingset_to_ilp.json` and `minimumdominatingset_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::petersen; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/examples/reduction_minimumsetcovering_to_ilp.rs b/examples/reduction_minimumsetcovering_to_ilp.rs index e921dd71..f7b28fa2 100644 --- a/examples/reduction_minimumsetcovering_to_ilp.rs +++ b/examples/reduction_minimumsetcovering_to_ilp.rs @@ -15,6 +15,7 @@ // Exports `docs/paper/examples/minimumsetcovering_to_ilp.json` and `minimumsetcovering_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; pub fn run() { diff --git a/examples/reduction_minimumvertexcover_to_ilp.rs b/examples/reduction_minimumvertexcover_to_ilp.rs index 923a08dc..af6a212e 100644 --- a/examples/reduction_minimumvertexcover_to_ilp.rs +++ b/examples/reduction_minimumvertexcover_to_ilp.rs @@ -14,6 +14,7 @@ // Exports `docs/paper/examples/minimumvertexcover_to_ilp.json` and `minimumvertexcover_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::topology::small_graphs::petersen; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/examples/reduction_satisfiability_to_kcoloring.rs b/examples/reduction_satisfiability_to_kcoloring.rs index 13d1daeb..27ecc4dd 100644 --- a/examples/reduction_satisfiability_to_kcoloring.rs +++ b/examples/reduction_satisfiability_to_kcoloring.rs @@ -18,6 +18,7 @@ use problemreductions::export::*; use problemreductions::prelude::*; use problemreductions::topology::{Graph, SimpleGraph}; +use problemreductions::variant::K3; pub fn run() { // 1. Create SAT instance: 5-variable, 3-clause formula with unit clauses diff --git a/examples/reduction_satisfiability_to_ksatisfiability.rs b/examples/reduction_satisfiability_to_ksatisfiability.rs index 817ce307..bc2d8d97 100644 --- a/examples/reduction_satisfiability_to_ksatisfiability.rs +++ b/examples/reduction_satisfiability_to_ksatisfiability.rs @@ -20,6 +20,7 @@ use problemreductions::export::*; use problemreductions::prelude::*; +use problemreductions::variant::K3; pub fn run() { // 1. Create SAT instance with varied clause sizes to demonstrate padding and splitting: diff --git a/examples/reduction_travelingsalesman_to_ilp.rs b/examples/reduction_travelingsalesman_to_ilp.rs index 681cb07d..28b629ba 100644 --- a/examples/reduction_travelingsalesman_to_ilp.rs +++ b/examples/reduction_travelingsalesman_to_ilp.rs @@ -15,6 +15,7 @@ // Exports `docs/paper/examples/travelingsalesman_to_ilp.json` and `travelingsalesman_to_ilp.result.json`. use problemreductions::export::*; +use problemreductions::models::optimization::ILP; use problemreductions::prelude::*; use problemreductions::solvers::ILPSolver; use problemreductions::topology::{Graph, SimpleGraph}; diff --git a/src/config.rs b/src/config.rs index a5e89c9d..31c09ac8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -109,12 +109,14 @@ pub fn config_to_index(config: &[usize], num_flavors: usize) -> usize { } /// Convert a binary configuration to a bitvec-style representation. -pub fn config_to_bits(config: &[usize]) -> Vec { +#[cfg(test)] +pub(crate) fn config_to_bits(config: &[usize]) -> Vec { config.iter().map(|&v| v != 0).collect() } /// Convert a bitvec-style representation to a binary configuration. -pub fn bits_to_config(bits: &[bool]) -> Vec { +#[cfg(test)] +pub(crate) fn bits_to_config(bits: &[bool]) -> Vec { bits.iter().map(|&b| if b { 1 } else { 0 }).collect() } diff --git a/src/graph_types.rs b/src/graph_types.rs deleted file mode 100644 index 4f0d15e7..00000000 --- a/src/graph_types.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Graph type markers for parametric problem modeling. -//! -//! ZST marker structs for graph types used as type parameters in problem definitions. -//! The subtype hierarchy is managed via `VariantParam` trait implementations (see `src/variant.rs`). - -/// Simple (arbitrary) graph - the most general graph type. -#[derive(Debug, Clone, Copy, Default)] -pub struct SimpleGraph; - -/// Unit disk graph - vertices are points, edges connect points within unit distance. -#[derive(Debug, Clone, Copy, Default)] -pub struct UnitDiskGraph; - -/// King's subgraph - a unit disk graph on a square grid with king's move connectivity. -#[derive(Debug, Clone, Copy, Default)] -pub struct KingsSubgraph; - -/// Triangular subgraph - a unit disk graph on a triangular lattice. -#[derive(Debug, Clone, Copy, Default)] -pub struct TriangularSubgraph; - -/// Hypergraph - most general graph type. Edges can connect any number of vertices. -#[derive(Debug, Clone, Copy, Default)] -pub struct HyperGraph; - -#[cfg(test)] -#[path = "unit_tests/graph_types.rs"] -mod tests; diff --git a/src/lib.rs b/src/lib.rs index 743813c7..ec8097f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,44 +64,40 @@ pub mod config; pub mod error; pub mod export; -pub mod graph_types; pub mod io; pub mod models; -pub mod polynomial; +pub(crate) mod polynomial; pub mod registry; pub mod rules; pub mod solvers; pub mod testing; pub mod topology; pub mod traits; -pub mod truth_table; +#[allow(dead_code)] +pub(crate) mod truth_table; pub mod types; pub mod variant; /// Prelude module for convenient imports. pub mod prelude { - pub use crate::config::{ - bits_to_config, config_to_bits, config_to_index, index_to_config, ConfigIterator, - }; - pub use crate::error::{ProblemError, Result}; + // Problem types pub use crate::models::graph::{ KColoring, MaxCut, MaximalIS, MaximumClique, MaximumIndependentSet, MaximumMatching, MinimumDominatingSet, MinimumVertexCover, TravelingSalesman, }; - pub use crate::models::optimization::{ - Comparison, LinearConstraint, ObjectiveSense, SpinGlass, VarBounds, ILP, QUBO, - }; + pub use crate::models::optimization::{SpinGlass, QUBO}; pub use crate::models::satisfiability::{CNFClause, KSatisfiability, Satisfiability}; pub use crate::models::set::{MaximumSetPacking, MinimumSetCovering}; - pub use crate::models::specialized::{BicliqueCover, CircuitSAT, Factoring, PaintShop, BMF}; - pub use crate::registry::{ComplexityClass, ProblemInfo, ProblemMetadata}; + pub use crate::models::specialized::{BicliqueCover, BMF, CircuitSAT, Factoring, PaintShop}; + + // Core traits pub use crate::rules::{ReduceTo, ReductionResult}; pub use crate::solvers::{BruteForce, Solver}; pub use crate::traits::{problem_size, OptimizationProblem, Problem, SatisfactionProblem}; - pub use crate::types::{ - Direction, NumericSize, One, ProblemSize, SolutionSize, Unweighted, WeightElement, - }; - pub use crate::variant::{CastToParent, KValue, VariantParam, K1, K2, K3, K4, K5, KN}; + + // Types + pub use crate::error::{ProblemError, Result}; + pub use crate::types::{Direction, One, ProblemSize, SolutionSize, Unweighted}; } // Re-export commonly used items at crate root diff --git a/src/models/graph/kcoloring.rs b/src/models/graph/kcoloring.rs index efa0619f..a3bc7bfb 100644 --- a/src/models/graph/kcoloring.rs +++ b/src/models/graph/kcoloring.rs @@ -89,6 +89,11 @@ impl KColoring { self.num_colors } + /// Check if a configuration is a valid coloring. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + is_valid_coloring(&self.graph, config, self.num_colors) + } + /// Check if a coloring is valid. fn is_valid_coloring(&self, config: &[usize]) -> bool { for (u, v) in self.graph.edges() { @@ -150,7 +155,7 @@ impl SatisfactionProblem for KColoring /// /// # Panics /// Panics if `coloring.len() != graph.num_vertices()`. -pub fn is_valid_coloring(graph: &G, coloring: &[usize], num_colors: usize) -> bool { +pub(crate) fn is_valid_coloring(graph: &G, coloring: &[usize], num_colors: usize) -> bool { assert_eq!( coloring.len(), graph.num_vertices(), diff --git a/src/models/graph/max_cut.rs b/src/models/graph/max_cut.rs index 7b0b2fc4..f829e524 100644 --- a/src/models/graph/max_cut.rs +++ b/src/models/graph/max_cut.rs @@ -136,6 +136,15 @@ impl MaxCut { pub fn edge_weights(&self) -> Vec { self.edge_weights.clone() } + + /// Compute the cut size for a given partition configuration. + pub fn cut_size(&self, config: &[usize]) -> W::Sum + where + W: WeightElement, + { + let partition: Vec = config.iter().map(|&c| c != 0).collect(); + cut_size(&self.graph, &self.edge_weights, &partition) + } } impl Problem for MaxCut @@ -186,7 +195,7 @@ where /// * `graph` - The graph structure /// * `edge_weights` - Weights for each edge (same order as `graph.edges()`) /// * `partition` - Boolean slice indicating which set each vertex belongs to -pub fn cut_size(graph: &G, edge_weights: &[W], partition: &[bool]) -> W::Sum +pub(crate) fn cut_size(graph: &G, edge_weights: &[W], partition: &[bool]) -> W::Sum where G: Graph, W: WeightElement, diff --git a/src/models/graph/maximal_is.rs b/src/models/graph/maximal_is.rs index 8da3b9da..193938ca 100644 --- a/src/models/graph/maximal_is.rs +++ b/src/models/graph/maximal_is.rs @@ -86,6 +86,11 @@ impl MaximalIS { !W::IS_UNIT } + /// Check if a configuration is a valid maximal independent set. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_maximal(config) + } + /// Check if a configuration is an independent set. fn is_independent(&self, config: &[usize]) -> bool { for (u, v) in self.graph.edges() { @@ -177,7 +182,8 @@ where /// /// # Panics /// Panics if `selected.len() != graph.num_vertices()`. -pub fn is_maximal_independent_set(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_maximal_independent_set(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_vertices(), diff --git a/src/models/graph/maximum_clique.rs b/src/models/graph/maximum_clique.rs index 6047b8dc..5161e906 100644 --- a/src/models/graph/maximum_clique.rs +++ b/src/models/graph/maximum_clique.rs @@ -88,6 +88,11 @@ impl MaximumClique { { !W::IS_UNIT } + + /// Check if a configuration is a valid clique. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + is_clique_config(&self.graph, config) + } } impl Problem for MaximumClique @@ -168,7 +173,8 @@ fn is_clique_config(graph: &G, config: &[usize]) -> bool { /// /// # Panics /// Panics if `selected.len() != graph.num_vertices()`. -pub fn is_clique(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_clique(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_vertices(), diff --git a/src/models/graph/maximum_independent_set.rs b/src/models/graph/maximum_independent_set.rs index 8177191e..a919c586 100644 --- a/src/models/graph/maximum_independent_set.rs +++ b/src/models/graph/maximum_independent_set.rs @@ -88,6 +88,11 @@ impl MaximumIndependentSet { { !W::IS_UNIT } + + /// Check if a configuration is a valid independent set. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + is_independent_set_config(&self.graph, config) + } } impl Problem for MaximumIndependentSet @@ -157,7 +162,8 @@ fn is_independent_set_config(graph: &G, config: &[usize]) -> bool { /// /// # Panics /// Panics if `selected.len() != graph.num_vertices()`. -pub fn is_independent_set(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_independent_set(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_vertices(), diff --git a/src/models/graph/maximum_matching.rs b/src/models/graph/maximum_matching.rs index 5e703561..0320e5b9 100644 --- a/src/models/graph/maximum_matching.rs +++ b/src/models/graph/maximum_matching.rs @@ -121,6 +121,11 @@ impl MaximumMatching { } /// Check if a configuration is a valid matching. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_valid_matching(config) + } + + /// Check if a configuration is a valid matching (internal). fn is_valid_matching(&self, config: &[usize]) -> bool { let mut vertex_used = vec![false; self.graph.num_vertices()]; @@ -213,7 +218,8 @@ where /// /// # Panics /// Panics if `selected.len() != graph.num_edges()`. -pub fn is_matching(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_matching(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_edges(), diff --git a/src/models/graph/minimum_dominating_set.rs b/src/models/graph/minimum_dominating_set.rs index 44544c1e..e7b1daec 100644 --- a/src/models/graph/minimum_dominating_set.rs +++ b/src/models/graph/minimum_dominating_set.rs @@ -88,6 +88,11 @@ impl MinimumDominatingSet { &self.weights } + /// Check if a configuration is a valid dominating set. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_dominating(config) + } + /// Check if a set of vertices is a dominating set. fn is_dominating(&self, config: &[usize]) -> bool { let n = self.graph.num_vertices(); @@ -163,7 +168,8 @@ where /// /// # Panics /// Panics if `selected.len() != graph.num_vertices()`. -pub fn is_dominating_set(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_dominating_set(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_vertices(), diff --git a/src/models/graph/minimum_vertex_cover.rs b/src/models/graph/minimum_vertex_cover.rs index fd21883a..6fd5f041 100644 --- a/src/models/graph/minimum_vertex_cover.rs +++ b/src/models/graph/minimum_vertex_cover.rs @@ -83,6 +83,11 @@ impl MinimumVertexCover { { !W::IS_UNIT } + + /// Check if a configuration is a valid vertex cover. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + is_vertex_cover_config(&self.graph, config) + } } impl Problem for MinimumVertexCover @@ -154,7 +159,8 @@ fn is_vertex_cover_config(graph: &G, config: &[usize]) -> bool { /// /// # Panics /// Panics if `selected.len() != graph.num_vertices()`. -pub fn is_vertex_cover(graph: &G, selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_vertex_cover(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_vertices(), diff --git a/src/models/graph/mod.rs b/src/models/graph/mod.rs index 7dd9d805..a924af19 100644 --- a/src/models/graph/mod.rs +++ b/src/models/graph/mod.rs @@ -11,22 +11,22 @@ //! - [`MaximumMatching`]: Maximum weight matching //! - [`TravelingSalesman`]: Traveling Salesman (minimum weight Hamiltonian cycle) -mod kcoloring; -mod max_cut; -mod maximal_is; -mod maximum_clique; -mod maximum_independent_set; -mod maximum_matching; -mod minimum_dominating_set; -mod minimum_vertex_cover; -mod traveling_salesman; +pub(crate) mod kcoloring; +pub(crate) mod max_cut; +pub(crate) mod maximal_is; +pub(crate) mod maximum_clique; +pub(crate) mod maximum_independent_set; +pub(crate) mod maximum_matching; +pub(crate) mod minimum_dominating_set; +pub(crate) mod minimum_vertex_cover; +pub(crate) mod traveling_salesman; -pub use kcoloring::{is_valid_coloring, KColoring}; -pub use max_cut::{cut_size, MaxCut}; -pub use maximal_is::{is_maximal_independent_set, MaximalIS}; -pub use maximum_clique::{is_clique, MaximumClique}; -pub use maximum_independent_set::{is_independent_set, MaximumIndependentSet}; -pub use maximum_matching::{is_matching, MaximumMatching}; -pub use minimum_dominating_set::{is_dominating_set, MinimumDominatingSet}; -pub use minimum_vertex_cover::{is_vertex_cover, MinimumVertexCover}; -pub use traveling_salesman::{is_hamiltonian_cycle, TravelingSalesman}; +pub use kcoloring::KColoring; +pub use max_cut::MaxCut; +pub use maximal_is::MaximalIS; +pub use maximum_clique::MaximumClique; +pub use maximum_independent_set::MaximumIndependentSet; +pub use maximum_matching::MaximumMatching; +pub use minimum_dominating_set::MinimumDominatingSet; +pub use minimum_vertex_cover::MinimumVertexCover; +pub use traveling_salesman::TravelingSalesman; diff --git a/src/models/graph/traveling_salesman.rs b/src/models/graph/traveling_salesman.rs index 35edcf2c..e99d1205 100644 --- a/src/models/graph/traveling_salesman.rs +++ b/src/models/graph/traveling_salesman.rs @@ -111,6 +111,11 @@ impl TravelingSalesman { !W::IS_UNIT } + /// Check if a configuration is a valid Hamiltonian cycle. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_valid_hamiltonian_cycle(config) + } + /// Check if a configuration forms a valid Hamiltonian cycle. fn is_valid_hamiltonian_cycle(&self, config: &[usize]) -> bool { if config.len() != self.graph.num_edges() { @@ -176,7 +181,7 @@ where /// /// # Panics /// Panics if `selected.len() != graph.num_edges()`. -pub fn is_hamiltonian_cycle(graph: &G, selected: &[bool]) -> bool { +pub(crate) fn is_hamiltonian_cycle(graph: &G, selected: &[bool]) -> bool { assert_eq!( selected.len(), graph.num_edges(), diff --git a/src/models/satisfiability/mod.rs b/src/models/satisfiability/mod.rs index 7e9796d5..9766fd1c 100644 --- a/src/models/satisfiability/mod.rs +++ b/src/models/satisfiability/mod.rs @@ -9,6 +9,3 @@ mod sat; pub use ksat::KSatisfiability; pub use sat::{CNFClause, Satisfiability}; - -// Validation utilities -pub use sat::is_satisfying_assignment; diff --git a/src/models/satisfiability/sat.rs b/src/models/satisfiability/sat.rs index a5ec9fec..53225758 100644 --- a/src/models/satisfiability/sat.rs +++ b/src/models/satisfiability/sat.rs @@ -162,6 +162,13 @@ impl Satisfiability { self.clauses.iter().all(|c| c.is_satisfied(assignment)) } + /// Check if a solution (config) is valid. + /// + /// For SAT, a valid solution is one that satisfies all clauses. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.evaluate(config) + } + /// Convert a usize config to boolean assignment. fn config_to_assignment(config: &[usize]) -> Vec { config.iter().map(|&v| v == 1).collect() @@ -201,7 +208,8 @@ impl SatisfactionProblem for Satisfiability {} /// * `num_vars` - Number of variables /// * `clauses` - Clauses as vectors of literals (1-indexed, signed) /// * `assignment` - Boolean assignment (0-indexed) -pub fn is_satisfying_assignment( +#[cfg(test)] +pub(crate) fn is_satisfying_assignment( _num_vars: usize, clauses: &[Vec], assignment: &[bool], diff --git a/src/models/set/maximum_set_packing.rs b/src/models/set/maximum_set_packing.rs index 26272c6e..c4fb0e1b 100644 --- a/src/models/set/maximum_set_packing.rs +++ b/src/models/set/maximum_set_packing.rs @@ -117,6 +117,11 @@ impl MaximumSetPacking { pub fn weights_ref(&self) -> &Vec { &self.weights } + + /// Check if a configuration is a valid set packing. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + is_valid_packing(&self.sets, config) + } } impl Problem for MaximumSetPacking @@ -194,7 +199,8 @@ fn is_valid_packing(sets: &[Vec], config: &[usize]) -> bool { } /// Check if a selection of sets forms a valid set packing. -pub fn is_set_packing(sets: &[Vec], selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_set_packing(sets: &[Vec], selected: &[bool]) -> bool { if selected.len() != sets.len() { return false; } diff --git a/src/models/set/minimum_set_covering.rs b/src/models/set/minimum_set_covering.rs index d2189c53..ed10981d 100644 --- a/src/models/set/minimum_set_covering.rs +++ b/src/models/set/minimum_set_covering.rs @@ -115,6 +115,13 @@ impl MinimumSetCovering { &self.weights } + /// Check if a configuration is a valid set cover. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + let covered = self.covered_elements(config); + covered.len() == self.universe_size + && (0..self.universe_size).all(|e| covered.contains(&e)) + } + /// Check which elements are covered by selected sets. pub fn covered_elements(&self, config: &[usize]) -> HashSet { let mut covered = HashSet::new(); @@ -180,7 +187,8 @@ where } /// Check if a selection of sets forms a valid set cover. -pub fn is_set_cover(universe_size: usize, sets: &[Vec], selected: &[bool]) -> bool { +#[cfg(test)] +pub(crate) fn is_set_cover(universe_size: usize, sets: &[Vec], selected: &[bool]) -> bool { if selected.len() != sets.len() { return false; } diff --git a/src/models/set/mod.rs b/src/models/set/mod.rs index 7120524e..0bee7cef 100644 --- a/src/models/set/mod.rs +++ b/src/models/set/mod.rs @@ -4,12 +4,8 @@ //! - [`MinimumSetCovering`]: Minimum weight set cover //! - [`MaximumSetPacking`]: Maximum weight set packing -mod maximum_set_packing; -mod minimum_set_covering; +pub(crate) mod maximum_set_packing; +pub(crate) mod minimum_set_covering; pub use maximum_set_packing::MaximumSetPacking; pub use minimum_set_covering::MinimumSetCovering; - -// Validation utilities -pub use maximum_set_packing::is_set_packing; -pub use minimum_set_covering::is_set_cover; diff --git a/src/models/specialized/biclique_cover.rs b/src/models/specialized/biclique_cover.rs index 75dbe4f6..31164a7b 100644 --- a/src/models/specialized/biclique_cover.rs +++ b/src/models/specialized/biclique_cover.rs @@ -164,6 +164,11 @@ impl BicliqueCover { false } + /// Check if a configuration is a valid biclique cover. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_valid_cover(config) + } + /// Check if all edges are covered. pub fn is_valid_cover(&self, config: &[usize]) -> bool { use crate::topology::Graph; @@ -190,7 +195,8 @@ impl BicliqueCover { } /// Check if a biclique configuration covers all edges. -pub fn is_biclique_cover( +#[cfg(test)] +pub(crate) fn is_biclique_cover( edges: &[(usize, usize)], left_bicliques: &[HashSet], right_bicliques: &[HashSet], diff --git a/src/models/specialized/bmf.rs b/src/models/specialized/bmf.rs index b0883d8f..69003086 100644 --- a/src/models/specialized/bmf.rs +++ b/src/models/specialized/bmf.rs @@ -172,12 +172,14 @@ impl BMF { } /// Compute the boolean matrix product. -pub fn boolean_matrix_product(b: &[Vec], c: &[Vec]) -> Vec> { +#[cfg(test)] +pub(crate) fn boolean_matrix_product(b: &[Vec], c: &[Vec]) -> Vec> { BMF::boolean_product(b, c) } /// Compute the Hamming distance between two boolean matrices. -pub fn matrix_hamming_distance(a: &[Vec], b: &[Vec]) -> usize { +#[cfg(test)] +pub(crate) fn matrix_hamming_distance(a: &[Vec], b: &[Vec]) -> usize { a.iter() .zip(b.iter()) .map(|(a_row, b_row)| { diff --git a/src/models/specialized/circuit.rs b/src/models/specialized/circuit.rs index a3736db5..d1dbeab6 100644 --- a/src/models/specialized/circuit.rs +++ b/src/models/specialized/circuit.rs @@ -238,6 +238,11 @@ impl CircuitSAT { &self.variables } + /// Check if a configuration is a valid satisfying assignment. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.count_satisfied(config) == self.circuit.num_assignments() + } + /// Convert a configuration to variable assignments. fn config_to_assignments(&self, config: &[usize]) -> HashMap { self.variables @@ -259,7 +264,8 @@ impl CircuitSAT { } /// Check if a circuit assignment is satisfying. -pub fn is_circuit_satisfying(circuit: &Circuit, assignments: &HashMap) -> bool { +#[cfg(test)] +pub(crate) fn is_circuit_satisfying(circuit: &Circuit, assignments: &HashMap) -> bool { circuit .assignments .iter() diff --git a/src/models/specialized/factoring.rs b/src/models/specialized/factoring.rs index e818b788..d08b0e7d 100644 --- a/src/models/specialized/factoring.rs +++ b/src/models/specialized/factoring.rs @@ -90,6 +90,11 @@ impl Factoring { (a, b) } + /// Check if a configuration is a valid factorization. + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.is_valid_factorization(config) + } + /// Check if the configuration is a valid factorization. pub fn is_valid_factorization(&self, config: &[usize]) -> bool { let (a, b) = self.read_factors(config); @@ -109,7 +114,8 @@ fn int_to_bits(n: u64, num_bits: usize) -> Vec { } /// Check if the given factors correctly factorize the target. -pub fn is_factoring(target: u64, a: u64, b: u64) -> bool { +#[cfg(test)] +pub(crate) fn is_factoring(target: u64, a: u64, b: u64) -> bool { a * b == target } diff --git a/src/models/specialized/mod.rs b/src/models/specialized/mod.rs index 521ceb8a..63b70c68 100644 --- a/src/models/specialized/mod.rs +++ b/src/models/specialized/mod.rs @@ -7,14 +7,14 @@ //! - [`BicliqueCover`]: Biclique cover on bipartite graphs //! - [`BMF`]: Boolean matrix factorization -mod biclique_cover; -mod bmf; -mod circuit; -mod factoring; -mod paintshop; +pub(crate) mod biclique_cover; +pub(crate) mod bmf; +pub(crate) mod circuit; +pub(crate) mod factoring; +pub(crate) mod paintshop; -pub use biclique_cover::{is_biclique_cover, BicliqueCover}; -pub use bmf::{boolean_matrix_product, matrix_hamming_distance, BMF}; -pub use circuit::{is_circuit_satisfying, Assignment, BooleanExpr, BooleanOp, Circuit, CircuitSAT}; -pub use factoring::{is_factoring, Factoring}; -pub use paintshop::{count_paint_switches, PaintShop}; +pub use biclique_cover::BicliqueCover; +pub use bmf::BMF; +pub use circuit::{Assignment, BooleanExpr, BooleanOp, Circuit, CircuitSAT}; +pub use factoring::Factoring; +pub use paintshop::PaintShop; diff --git a/src/models/specialized/paintshop.rs b/src/models/specialized/paintshop.rs index 7bb45651..4ee9386f 100644 --- a/src/models/specialized/paintshop.rs +++ b/src/models/specialized/paintshop.rs @@ -156,7 +156,8 @@ impl PaintShop { } /// Count color switches in a painted sequence. -pub fn count_paint_switches(coloring: &[usize]) -> usize { +#[cfg(test)] +pub(crate) fn count_paint_switches(coloring: &[usize]) -> usize { coloring.windows(2).filter(|w| w[0] != w[1]).count() } diff --git a/src/rules/circuit_spinglass.rs b/src/rules/circuit_spinglass.rs index b31f7358..30a019bf 100644 --- a/src/rules/circuit_spinglass.rs +++ b/src/rules/circuit_spinglass.rs @@ -30,8 +30,10 @@ pub struct LogicGadget { /// The SpinGlass problem encoding the gate. pub problem: SpinGlass, /// Input spin indices (0-indexed within the gadget). + #[allow(dead_code)] // read in tests pub inputs: Vec, /// Output spin indices (0-indexed within the gadget). + #[allow(dead_code)] // read in tests pub outputs: Vec, } diff --git a/src/rules/coloring_ilp.rs b/src/rules/coloring_ilp.rs index 0989ec20..f9e982da 100644 --- a/src/rules/coloring_ilp.rs +++ b/src/rules/coloring_ilp.rs @@ -150,9 +150,6 @@ macro_rules! impl_kcoloring_to_ilp { impl_kcoloring_to_ilp!(K1, K2, K3, K4); -// Keep the old type alias for backwards compatibility -pub type ReductionColoringToILP = ReductionKColoringToILP; - #[cfg(test)] #[path = "../unit_tests/rules/coloring_ilp.rs"] mod tests; diff --git a/src/rules/graph.rs b/src/rules/graph.rs index be67eee8..8183ac2c 100644 --- a/src/rules/graph.rs +++ b/src/rules/graph.rs @@ -32,36 +32,38 @@ pub(crate) struct ReductionEdgeData { /// JSON-serializable representation of the reduction graph. #[derive(Debug, Clone, Serialize)] -pub struct ReductionGraphJson { +pub(crate) struct ReductionGraphJson { /// List of problem type nodes. - pub nodes: Vec, + pub(crate) nodes: Vec, /// List of reduction edges. - pub edges: Vec, + pub(crate) edges: Vec, } impl ReductionGraphJson { /// Get the source node of an edge. - pub fn source_node(&self, edge: &EdgeJson) -> &NodeJson { + #[cfg_attr(not(test), allow(dead_code))] + pub(crate) fn source_node(&self, edge: &EdgeJson) -> &NodeJson { &self.nodes[edge.source] } /// Get the target node of an edge. - pub fn target_node(&self, edge: &EdgeJson) -> &NodeJson { + #[cfg_attr(not(test), allow(dead_code))] + pub(crate) fn target_node(&self, edge: &EdgeJson) -> &NodeJson { &self.nodes[edge.target] } } /// A node in the reduction graph JSON. #[derive(Debug, Clone, Serialize)] -pub struct NodeJson { +pub(crate) struct NodeJson { /// Base problem name (e.g., "MaximumIndependentSet"). - pub name: String, + pub(crate) name: String, /// Variant attributes as key-value pairs. - pub variant: BTreeMap, + pub(crate) variant: BTreeMap, /// Category of the problem (e.g., "graph", "set", "optimization", "satisfiability", "specialized"). - pub category: String, + pub(crate) category: String, /// Relative rustdoc path (e.g., "models/graph/maximum_independent_set"). - pub doc_path: String, + pub(crate) doc_path: String, } /// Internal reference to a problem variant, used as HashMap key. @@ -73,24 +75,24 @@ struct VariantRef { /// A single output field in the reduction overhead. #[derive(Debug, Clone, Serialize)] -pub struct OverheadFieldJson { +pub(crate) struct OverheadFieldJson { /// Output field name (e.g., "num_vars"). - pub field: String, + pub(crate) field: String, /// Formula as a human-readable string (e.g., "num_vertices"). - pub formula: String, + pub(crate) formula: String, } /// An edge in the reduction graph JSON. #[derive(Debug, Clone, Serialize)] -pub struct EdgeJson { +pub(crate) struct EdgeJson { /// Index into the `nodes` array for the source problem variant. - pub source: usize, + pub(crate) source: usize, /// Index into the `nodes` array for the target problem variant. - pub target: usize, + pub(crate) target: usize, /// Reduction overhead: output size as polynomials of input size. - pub overhead: Vec, + pub(crate) overhead: Vec, /// Relative rustdoc path for the reduction module. - pub doc_path: String, + pub(crate) doc_path: String, } /// A path through the variant-level reduction graph. @@ -641,7 +643,7 @@ impl ReductionGraph { /// Export the reduction graph as a JSON-serializable structure. /// /// Nodes and edges come directly from the variant-level graph. - pub fn to_json(&self) -> ReductionGraphJson { + pub(crate) fn to_json(&self) -> ReductionGraphJson { use crate::registry::ProblemSchemaEntry; // Build name -> module_path lookup from ProblemSchemaEntry inventory diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 785ba904..97f1bf71 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -58,35 +58,11 @@ mod minimumvertexcover_ilp; #[cfg(feature = "ilp")] mod travelingsalesman_ilp; -pub use circuit_spinglass::{ - and_gadget, not_gadget, or_gadget, set0_gadget, set1_gadget, xor_gadget, LogicGadget, - ReductionCircuitToSG, -}; -pub use coloring_qubo::ReductionKColoringToQUBO; -pub use factoring_circuit::ReductionFactoringToCircuit; pub use graph::{ - ChainedReduction, EdgeJson, ExecutablePath, NodeJson, ReductionGraph, ReductionGraphJson, - ReductionPath, ReductionStep, + ChainedReduction, ExecutablePath, ReductionGraph, ReductionPath, ReductionStep, }; #[cfg(test)] pub(crate) use graph::validate_overhead_variables; -pub use ksatisfiability_qubo::{Reduction3SATToQUBO, ReductionKSatToQUBO}; -pub use maximumindependentset_gridgraph::{ReductionISSimpleToGrid, ReductionISUnitDiskToGrid}; -pub use maximumindependentset_maximumsetpacking::{ReductionISToSP, ReductionSPToIS}; -pub use maximumindependentset_qubo::ReductionISToQUBO; -pub use maximumindependentset_triangular::ReductionISSimpleToTriangular; -pub use maximummatching_maximumsetpacking::ReductionMatchingToSP; -pub use maximumsetpacking_qubo::ReductionSPToQUBO; -pub use minimumvertexcover_maximumindependentset::{ReductionISToVC, ReductionVCToIS}; -pub use minimumvertexcover_minimumsetcovering::ReductionVCToSC; -pub use minimumvertexcover_qubo::ReductionVCToQUBO; -pub use sat_circuitsat::ReductionSATToCircuit; -pub use sat_coloring::ReductionSATToColoring; -pub use sat_ksat::{ReductionKSATToSAT, ReductionSATToKSAT}; -pub use sat_maximumindependentset::{BoolVar, ReductionSATToIS}; -pub use sat_minimumdominatingset::ReductionSATToDS; -pub use spinglass_maxcut::{ReductionMaxCutToSG, ReductionSGToMaxCut}; -pub use spinglass_qubo::{ReductionQUBOToSG, ReductionSGToQUBO}; pub use traits::{ReduceTo, ReductionAutoCast, ReductionResult}; /// Generates a variant-cast `ReduceTo` impl with `#[reduction]` registration. @@ -137,25 +113,3 @@ macro_rules! impl_variant_reduction { }; } -#[cfg(feature = "ilp")] -pub use coloring_ilp::{ReductionColoringToILP, ReductionKColoringToILP}; -#[cfg(feature = "ilp")] -pub use factoring_ilp::ReductionFactoringToILP; -#[cfg(feature = "ilp")] -pub use ilp_qubo::ReductionILPToQUBO; -#[cfg(feature = "ilp")] -pub use maximumclique_ilp::ReductionCliqueToILP; -#[cfg(feature = "ilp")] -pub use maximumindependentset_ilp::ReductionISToILP; -#[cfg(feature = "ilp")] -pub use maximummatching_ilp::ReductionMatchingToILP; -#[cfg(feature = "ilp")] -pub use maximumsetpacking_ilp::ReductionSPToILP; -#[cfg(feature = "ilp")] -pub use minimumdominatingset_ilp::ReductionDSToILP; -#[cfg(feature = "ilp")] -pub use minimumsetcovering_ilp::ReductionSCToILP; -#[cfg(feature = "ilp")] -pub use minimumvertexcover_ilp::ReductionVCToILP; -#[cfg(feature = "ilp")] -pub use travelingsalesman_ilp::ReductionTSPToILP; diff --git a/src/rules/unitdiskmapping/copyline.rs b/src/rules/unitdiskmapping/copyline.rs index d74d7ac3..aeda0485 100644 --- a/src/rules/unitdiskmapping/copyline.rs +++ b/src/rules/unitdiskmapping/copyline.rs @@ -425,6 +425,7 @@ pub fn mis_overhead_copyline(line: &CopyLine, spacing: usize, padding: usize) -> /// /// # Returns /// A tuple of (locations, weights) vectors. +#[allow(dead_code)] pub fn copyline_weighted_locations_triangular( line: &CopyLine, spacing: usize, diff --git a/src/rules/unitdiskmapping/mod.rs b/src/rules/unitdiskmapping/mod.rs index d807103b..d44f651c 100644 --- a/src/rules/unitdiskmapping/mod.rs +++ b/src/rules/unitdiskmapping/mod.rs @@ -25,24 +25,36 @@ //! let tri_result = triangular::map_weighted(3, &edges); //! ``` -pub mod alpha_tensor; +#[allow(dead_code)] +pub(crate) mod alpha_tensor; mod copyline; mod grid; pub mod ksg; -pub mod pathdecomposition; +pub(crate) mod pathdecomposition; mod traits; pub mod triangular; mod weighted; -// Re-export shared types -pub use copyline::{create_copylines, mis_overhead_copyline, remove_order, CopyLine}; -pub use grid::{CellState, MappingGrid}; -pub use pathdecomposition::{pathwidth, Layout, PathDecompositionMethod}; -pub use traits::{apply_gadget, pattern_matches, unapply_gadget, Pattern, PatternCell}; - // Re-export commonly used items from submodules for convenience pub use ksg::{GridKind, MappingResult}; -// Re-exports from private modules (not accessible via ksg:: or triangular::) -pub use copyline::{copyline_weighted_locations_triangular, mis_overhead_copyline_triangular}; -pub use weighted::{map_weights, trace_centers, Weightable}; +// Re-exports for unit tests (only needed in test builds) +#[cfg(test)] +pub(crate) use copyline::{ + copyline_weighted_locations_triangular, create_copylines, mis_overhead_copyline, CopyLine, +}; +#[cfg(test)] +pub(crate) use grid::{CellState, MappingGrid}; +#[cfg(test)] +pub(crate) use traits::{apply_gadget, unapply_gadget, Pattern}; +#[cfg(test)] +pub(crate) use weighted::{map_weights, trace_centers}; + +// Hidden re-exports for development tools (examples/export_mapping_stages.rs) +#[doc(hidden)] +pub mod _internal { + pub use super::copyline::{ + create_copylines, mis_overhead_copyline, mis_overhead_copyline_triangular, CopyLine, + }; + pub use super::grid::{CellState, MappingGrid}; +} diff --git a/src/rules/unitdiskmapping/pathdecomposition.rs b/src/rules/unitdiskmapping/pathdecomposition.rs index ec0ef889..cc930fde 100644 --- a/src/rules/unitdiskmapping/pathdecomposition.rs +++ b/src/rules/unitdiskmapping/pathdecomposition.rs @@ -409,10 +409,8 @@ impl PathDecompositionMethod { /// * `method` - The decomposition method to use /// /// # Example -/// ``` -/// use problemreductions::rules::unitdiskmapping::pathdecomposition::{pathwidth, PathDecompositionMethod}; /// -/// // Path graph: 0-1-2 +/// ```text /// let edges = vec![(0, 1), (1, 2)]; /// let layout = pathwidth(3, &edges, PathDecompositionMethod::greedy()); /// assert_eq!(layout.vertices.len(), 3); diff --git a/src/rules/unitdiskmapping/traits.rs b/src/rules/unitdiskmapping/traits.rs index 0fabe8ed..e89910fb 100644 --- a/src/rules/unitdiskmapping/traits.rs +++ b/src/rules/unitdiskmapping/traits.rs @@ -188,6 +188,7 @@ pub fn apply_gadget(pattern: &P, grid: &mut MappingGrid, i: usize, j /// Unapply a gadget pattern at position (i, j). #[allow(clippy::needless_range_loop)] +#[allow(dead_code)] pub fn unapply_gadget(pattern: &P, grid: &mut MappingGrid, i: usize, j: usize) { let source = pattern.source_matrix(); let (m, n) = pattern.size(); diff --git a/src/truth_table.rs b/src/truth_table.rs index 139373b3..479b0298 100644 --- a/src/truth_table.rs +++ b/src/truth_table.rs @@ -10,18 +10,6 @@ use serde::{Deserialize, Serialize}; /// /// The truth table stores the output for each possible input combination. /// For n input variables, there are 2^n rows in the table. -/// -/// # Example -/// -/// ``` -/// use problemreductions::truth_table::TruthTable; -/// -/// // Create AND gate truth table -/// let and_gate = TruthTable::from_outputs(2, vec![false, false, false, true]); -/// assert!(!and_gate.evaluate(&[false, false])); -/// assert!(!and_gate.evaluate(&[true, false])); -/// assert!(and_gate.evaluate(&[true, true])); -/// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct TruthTable { /// Number of input variables. diff --git a/src/unit_tests/graph_models.rs b/src/unit_tests/graph_models.rs index b8c9b0c9..8cb06ae3 100644 --- a/src/unit_tests/graph_models.rs +++ b/src/unit_tests/graph_models.rs @@ -3,10 +3,10 @@ //! Tests extracted from source files for better compilation times //! and clearer separation of concerns. -use crate::models::graph::{ - is_independent_set, is_valid_coloring, is_vertex_cover, KColoring, MaximumIndependentSet, - MinimumVertexCover, -}; +use crate::models::graph::kcoloring::is_valid_coloring; +use crate::models::graph::maximum_independent_set::is_independent_set; +use crate::models::graph::minimum_vertex_cover::is_vertex_cover; +use crate::models::graph::{KColoring, MaximumIndependentSet, MinimumVertexCover}; use crate::prelude::*; use crate::topology::{Graph, SimpleGraph}; use crate::traits::{OptimizationProblem, Problem}; diff --git a/src/unit_tests/graph_types.rs b/src/unit_tests/graph_types.rs deleted file mode 100644 index a2a097f8..00000000 --- a/src/unit_tests/graph_types.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::*; -use crate::variant::VariantParam; - -#[test] -fn test_graph_type_traits() { - // Test Default - let _: SimpleGraph = Default::default(); - let _: UnitDiskGraph = Default::default(); - let _: KingsSubgraph = Default::default(); - let _: TriangularSubgraph = Default::default(); - let _: HyperGraph = Default::default(); - - // Test Copy (SimpleGraph implements Copy, so no need to clone) - let g = SimpleGraph; - let _g2 = g; // Copy - let g = SimpleGraph; - let _g2 = g; - let _g3 = g; // still usable -} - -#[test] -fn test_planargraph_variant_param() { - use crate::topology::PlanarGraph; - assert_eq!(PlanarGraph::CATEGORY, "graph"); - assert_eq!(PlanarGraph::VALUE, "PlanarGraph"); - assert_eq!(PlanarGraph::PARENT_VALUE, Some("SimpleGraph")); -} - -#[test] -fn test_bipartitegraph_variant_param() { - use crate::topology::BipartiteGraph; - assert_eq!(BipartiteGraph::CATEGORY, "graph"); - assert_eq!(BipartiteGraph::VALUE, "BipartiteGraph"); - assert_eq!(BipartiteGraph::PARENT_VALUE, Some("SimpleGraph")); -} - -#[test] -fn test_marker_structs_exist() { - // Verify that all ZST marker structs still exist and can be instantiated - let _ = SimpleGraph; - let _ = UnitDiskGraph; - let _ = KingsSubgraph; - let _ = TriangularSubgraph; - let _ = HyperGraph; -} diff --git a/tests/suites/integration.rs b/tests/suites/integration.rs index 06b1a31b..5de6cd29 100644 --- a/tests/suites/integration.rs +++ b/tests/suites/integration.rs @@ -10,6 +10,7 @@ use problemreductions::models::set::*; use problemreductions::models::specialized::*; use problemreductions::prelude::*; use problemreductions::topology::{BipartiteGraph, SimpleGraph}; +use problemreductions::variant::K3; /// Test that all problem types can be instantiated and solved. mod all_problems_solvable { diff --git a/tests/suites/reductions.rs b/tests/suites/reductions.rs index a7120244..1dd7e76c 100644 --- a/tests/suites/reductions.rs +++ b/tests/suites/reductions.rs @@ -3,8 +3,10 @@ //! These tests verify that reduction chains work correctly and //! solutions can be properly extracted through the reduction pipeline. +use problemreductions::models::optimization::{ILP, LinearConstraint, ObjectiveSense}; use problemreductions::prelude::*; use problemreductions::topology::{Graph, SimpleGraph}; +use problemreductions::variant::{K2, K3}; /// Tests for MaximumIndependentSet <-> MinimumVertexCover reductions. mod is_vc_reductions { @@ -420,53 +422,8 @@ mod topology_tests { } } -/// Tests for TruthTable integration with reductions. -mod truth_table_tests { - use problemreductions::truth_table::TruthTable; - - #[test] - fn test_truth_table_to_sat() { - // Create a simple truth table (AND gate) - let and_gate = TruthTable::and(2); - - // Find satisfying assignments - let satisfying = and_gate.satisfying_assignments(); - - // AND gate: only [T, T] satisfies - assert_eq!(satisfying.len(), 1); - assert_eq!(satisfying[0], vec![true, true]); - } - - #[test] - fn test_truth_table_xor_to_sat() { - // XOR has exactly 2^(n-1) satisfying assignments for n inputs - let xor3 = TruthTable::xor(3); - let satisfying = xor3.satisfying_assignments(); - - // 3-XOR: exactly 4 satisfying assignments - assert_eq!(satisfying.len(), 4); - - // Each should have odd number of true inputs - for assignment in &satisfying { - let true_count = assignment.iter().filter(|&&b| b).count(); - assert_eq!(true_count % 2, 1); - } - } - - #[test] - fn test_truth_table_combined() { - // Test combining truth tables (useful for circuit construction) - let a = TruthTable::and(2); - let b = TruthTable::or(2); - - // a AND b (element-wise AND of two truth tables) - let combined = a.and_with(&b); - - // AND result: [F,F,F,T], OR result: [F,T,T,T] - // Combined: [F,F,F,T] - assert_eq!(combined.outputs_vec(), vec![false, false, false, true]); - } -} +// TruthTable integration tests moved to src/unit_tests/truth_table.rs +// (truth_table module is now pub(crate)) /// Tests for QUBO reductions against ground truth JSON. mod qubo_reductions {