problemreductions/rules/
ksatisfiability_acyclicpartition.rs1use crate::models::formula::KSatisfiability;
9use crate::models::graph::{AcyclicPartition, KClique};
10use crate::reduction;
11use crate::rules::ksatisfiability_kclique::Reduction3SATToKClique;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::topology::{DirectedGraph, Graph, SimpleGraph};
14use crate::variant::K3;
15
16#[derive(Debug, Clone)]
18pub struct Reduction3SATToAcyclicPartition {
19 sat_to_clique: Reduction3SATToKClique,
20 target: AcyclicPartition<i64>,
21 source_vertex: usize,
22}
23
24impl ReductionResult for Reduction3SATToAcyclicPartition {
25 type Source = KSatisfiability<K3>;
26 type Target = AcyclicPartition<i64>;
27
28 fn target_problem(&self) -> &Self::Target {
29 &self.target
30 }
31
32 fn extract_solution(
33 &self,
34 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
35 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
36 crate::rules::traits::validate_target_witness(
37 self.target_problem(),
38 target_solution,
39 |value| value.0,
40 "target partition does not satisfy the acyclic partition constraints",
41 )?;
42 let source_label = target_solution[self.source_vertex];
43 let selected = target_solution[..self.sat_to_clique.target_problem().num_vertices()]
44 .iter()
45 .map(|&label| label == source_label)
46 .collect();
47 self.sat_to_clique.extract_solution(&selected)
48 }
49}
50
51#[crate::aggregate_reduction(identity)]
52impl crate::rules::AggregateReductionResult for Reduction3SATToAcyclicPartition {}
53
54#[reduction(
55 transform = upper_bound {
56 max_numeric_magnitude_bits = "4 * num_clauses + 6",
57 num_vertices = "(9 * num_clauses^2 + 3 * num_clauses + 6) / 2",
58 num_arcs = "18 * num_clauses^2 + 2",
59 }
60)]
61impl ReduceTo<AcyclicPartition<i64>> for KSatisfiability<K3> {
62 type Result = Reduction3SATToAcyclicPartition;
63
64 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
65 let sat_to_clique = ReduceTo::<KClique<SimpleGraph>>::reduce_to(self)?;
66 let clique = sat_to_clique.target_problem();
67 let n = clique.num_vertices();
68 let edges = clique.graph().edges();
69 let e = edges.len();
70 let (target_n, arc_count, capacity, magnitude, weight_bound, cost_bound) =
71 incidence_parameters(n, e, clique.k())?;
72 let items = target_n - 2;
73 let source_vertex = items;
74 let sink_vertex = items + 1;
75 let mut degree = vec![0i64; n];
76 for &(u, v) in &edges {
77 degree[u] += 1;
79 degree[v] += 1;
80 }
81 let mut arcs = Vec::with_capacity(arc_count);
82 let mut arc_costs = Vec::with_capacity(arc_count);
83 let profits = degree.iter().copied().chain(std::iter::repeat_n(0, e));
84 for (item, profit) in profits.enumerate() {
85 arcs.push((source_vertex, item));
86 arc_costs.push(magnitude);
87 arcs.push((item, sink_vertex));
88 arc_costs.push(magnitude - profit);
89 }
90 for (j, &(u, v)) in edges.iter().enumerate() {
91 arcs.push((u, n + j));
92 arc_costs.push(1);
93 arcs.push((v, n + j));
94 arc_costs.push(1);
95 }
96 let mut weights = vec![1; n];
97 weights.resize(items, 0);
98 weights.push(weight_bound - capacity);
99 weights.push(
100 weight_bound
101 - <Self as ReduceTo<AcyclicPartition<i64>>>::exact_i64(
102 n - clique.k(),
103 "representing unselected clique vertex count",
104 )?,
105 );
106 let target = AcyclicPartition::new(
107 DirectedGraph::new(target_n, arcs),
108 weights,
109 arc_costs,
110 weight_bound,
111 cost_bound,
112 );
113 Ok(Reduction3SATToAcyclicPartition {
114 sat_to_clique,
115 target,
116 source_vertex,
117 })
118 }
119}
120
121fn incidence_parameters(
123 n: usize,
124 e: usize,
125 k: usize,
126) -> Result<(usize, usize, i64, i64, i64, i64), crate::rules::ReductionError> {
127 type Source = KSatisfiability<K3>;
128 type Target = AcyclicPartition<i64>;
129 let overflow = || {
130 crate::rules::ReductionError::integer_overflow::<Source, Target>(
131 "encoding clique incidence partition parameters",
132 )
133 };
134 let items = n.checked_add(e).ok_or_else(overflow)?;
135 let target_n = items.checked_add(2).ok_or_else(overflow)?;
136 let arcs = items
137 .checked_add(e)
138 .and_then(|x| x.checked_mul(2))
139 .ok_or_else(overflow)?;
140 let l = i64::try_from(items).map_err(|_| overflow())?;
141 let k = i64::try_from(k).map_err(|_| overflow())?;
142 let capacity = k;
143 let magnitude = l
144 .checked_mul(2)
145 .and_then(|x| x.checked_add(1))
146 .ok_or_else(overflow)?;
147 let bound = l
148 .checked_add(capacity)
149 .and_then(|x| x.checked_mul(2))
150 .and_then(|x| x.checked_add(1))
151 .ok_or_else(overflow)?;
152 let twice_edges = k.checked_mul(k.saturating_sub(1)).ok_or_else(overflow)?;
153 let cost_bound = magnitude
154 .checked_mul(l)
155 .and_then(|x| x.checked_sub(twice_edges))
156 .ok_or_else(overflow)?;
157 Ok((target_n, arcs, capacity, magnitude, bound, cost_bound))
158}
159
160#[cfg(feature = "example-db")]
161pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
162 use crate::export::SolutionPair;
163 use crate::models::formula::CNFClause;
164
165 vec![crate::example_db::specs::RuleExampleSpec {
166 id: "ksatisfiability_to_acyclicpartition",
167 build: || {
168 crate::example_db::specs::rule_example_with_witness::<_, AcyclicPartition<i64>>(
169 KSatisfiability::<K3>::new(1, vec![CNFClause::new(vec![1, 1, 1])]),
170 SolutionPair {
171 source_config: serde_json::json!(vec![true]),
172 target_config: serde_json::json!(vec![0, 1, 1, 0, 0, 1, 1, 0, 1]),
173 },
174 )
175 },
176 }]
177}
178
179#[cfg(test)]
180#[path = "../unit_tests/rules/ksatisfiability_acyclicpartition.rs"]
181mod tests;