problemreductions/rules/
isomorphicspanningtree_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
7use crate::models::graph::IsomorphicSpanningTree;
8use crate::reduction;
9use crate::rules::traits::{ReduceTo, ReductionResult};
10use crate::topology::{Graph, SimpleGraph};
11
12#[derive(Debug, Clone)]
13pub struct ReductionISTToILP {
14 target: ILP<bool>,
15 n: usize,
16}
17
18impl ReductionResult for ReductionISTToILP {
19 type Source = IsomorphicSpanningTree<SimpleGraph>;
20 type Target = ILP<bool>;
21
22 fn target_problem(&self) -> &ILP<bool> {
23 &self.target
24 }
25
26 fn extract_solution(
28 &self,
29 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
30 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
31 crate::rules::traits::validate_target_witness(
32 self.target_problem(),
33 target_solution,
34 |value| value.value.is_some(),
35 "target ILP assignment is infeasible",
36 )?;
37
38 crate::rules::ilp_helpers::one_hot_decode_rows(target_solution, self.n, self.n, 0)
39 }
40}
41
42#[crate::aggregate_reduction(ilp_feasibility)]
43impl crate::rules::AggregateReductionResult for ReductionISTToILP {}
44
45#[reduction(transform = upper_bound {
49 max_constraint_magnitude_bits = "2",
50 num_vars = "num_vertices * num_vertices",
51 num_constraints = "2 * num_vertices + num_vertices * (num_vertices - 1)^2",
52 num_nonzeros = "2 * num_vertices^2 + 2 * num_vertices * (num_vertices - 1)^2",
53})]
54impl ReduceTo<ILP<bool>> for IsomorphicSpanningTree<SimpleGraph> {
55 type Result = ReductionISTToILP;
56
57 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
58 let n = self.num_vertices();
59 let num_vars = n * n;
60
61 let mut constraints = Vec::new();
62
63 for u in 0..n {
66 let terms: Vec<(usize, i64)> = (0..n).map(|v| (u * n + v, 1)).collect();
67 constraints.push(LinearConstraint::eq(terms, 1));
68 }
69
70 for v in 0..n {
73 let terms: Vec<(usize, i64)> = (0..n).map(|u| (u * n + v, 1)).collect();
74 constraints.push(LinearConstraint::eq(terms, 1));
75 }
76
77 for (u, w) in self.tree().edges() {
81 for v in 0..n {
82 for z in 0..n {
83 if v != z && !self.graph().has_edge(v, z) {
84 constraints.push(LinearConstraint::le(
85 vec![(u * n + v, 1), (w * n + z, 1)],
86 1,
87 ));
88 }
89 }
90 }
91 }
92
93 let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
94 .map_err(Self::target_construction)?;
95 Ok(ReductionISTToILP { target, n })
96 }
97}
98
99#[cfg(feature = "example-db")]
100pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
101 use crate::export::SolutionPair;
102 use crate::topology::SimpleGraph;
103 vec![crate::example_db::specs::RuleExampleSpec {
104 id: "isomorphicspanningtree_to_ilp",
105 build: || {
106 let source = IsomorphicSpanningTree::new(
108 SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]),
109 SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3)]),
110 );
111 crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
113 source,
114 SolutionPair {
115 source_config: serde_json::json!(vec![0, 1, 2, 3]),
116 target_config: serde_json::json!(vec![
118 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
119 ]),
120 },
121 )
122 },
123 }]
124}
125
126#[cfg(test)]
127#[path = "../unit_tests/rules/isomorphicspanningtree_ilp.rs"]
128mod tests;