1use crate::models::graph::MaxCut;
7use crate::models::graph::SpinGlass;
8use crate::reduction;
9use crate::rules::traits::{ReduceTo, ReductionResult};
10use crate::topology::{Graph, SimpleGraph};
11use crate::types::WeightElement;
12use num_traits::Zero;
13
14#[derive(Debug, Clone)]
16pub struct ReductionMaxCutToSG<W> {
17 target: SpinGlass<SimpleGraph, W>,
18}
19
20impl<W> ReductionResult for ReductionMaxCutToSG<W>
21where
22 W: WeightElement
23 + crate::variant::VariantParam
24 + PartialOrd
25 + num_traits::Num
26 + num_traits::Zero
27 + num_traits::Bounded
28 + std::ops::AddAssign
29 + std::ops::Mul<Output = W>,
30{
31 type Source = MaxCut<SimpleGraph, W>;
32 type Target = SpinGlass<SimpleGraph, W>;
33
34 fn target_problem(&self) -> &Self::Target {
35 &self.target
36 }
37
38 fn extract_solution(
39 &self,
40 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
41 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
42 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
43
44 Ok(target_solution.iter().map(|&spin| spin == 1).collect())
45 }
46}
47
48#[reduction(
49 transform = exact {
50 num_spins = "num_vertices",
51 num_interactions = "num_edges",
52 }
53)]
54impl ReduceTo<SpinGlass<SimpleGraph, i64>> for MaxCut<SimpleGraph, i64> {
55 type Result = ReductionMaxCutToSG<i64>;
56
57 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
58 let n = self.graph().num_vertices();
59 let edges_with_weights = self.edges();
60
61 let interactions: Vec<((usize, usize), i64)> = edges_with_weights
79 .into_iter()
80 .map(|(u, v, w)| ((u, v), w))
81 .collect();
82
83 let onsite = vec![0i64; n];
85
86 let target =
87 SpinGlass::<SimpleGraph, i64>::new(n, interactions, onsite).map_err(|cause| {
88 crate::rules::ReductionError::construction::<
89 MaxCut<SimpleGraph, i64>,
90 SpinGlass<SimpleGraph, i64>,
91 >(cause)
92 })?;
93
94 Ok(ReductionMaxCutToSG { target })
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct ReductionSGToMaxCut<W> {
101 target: MaxCut<SimpleGraph, W>,
102 ancilla: Option<usize>,
104}
105
106impl<W> ReductionResult for ReductionSGToMaxCut<W>
107where
108 W: WeightElement
109 + crate::variant::VariantParam
110 + PartialOrd
111 + num_traits::Num
112 + num_traits::Zero
113 + num_traits::Bounded
114 + std::ops::AddAssign
115 + std::ops::Mul<Output = W>,
116{
117 type Source = SpinGlass<SimpleGraph, W>;
118 type Target = MaxCut<SimpleGraph, W>;
119
120 fn target_problem(&self) -> &Self::Target {
121 &self.target
122 }
123
124 fn extract_solution(
125 &self,
126 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
127 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
128 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
129
130 Ok({
131 match self.ancilla {
132 None => target_solution
133 .iter()
134 .map(|&side| if side { 1 } else { -1 })
135 .collect(),
136 Some(anc) => {
137 let mut sol = target_solution.to_vec();
139 if !sol[anc] {
140 for x in sol.iter_mut() {
141 *x = !*x;
142 }
143 }
144 sol.remove(anc);
145 sol.into_iter()
146 .map(|side| if side { 1 } else { -1 })
147 .collect()
148 }
149 }
150 })
151 }
152}
153
154#[reduction(
155 transform = upper_bound {
156 num_vertices = "num_spins + 1",
157 num_edges = "num_interactions + num_spins",
158 }
159)]
160impl ReduceTo<MaxCut<SimpleGraph, i64>> for SpinGlass<SimpleGraph, i64> {
161 type Result = ReductionSGToMaxCut<i64>;
162
163 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
164 let n = self.num_spins();
165 let interactions = self.interactions();
166 let fields = self.fields();
167
168 let need_ancilla = fields.iter().any(|h| !h.is_zero());
170 let total_vertices = if need_ancilla { n + 1 } else { n };
171 let ancilla_idx = if need_ancilla { Some(n) } else { None };
172
173 let mut edges = Vec::new();
174 let mut weights = Vec::new();
175
176 for ((i, j), w) in interactions {
178 edges.push((i, j));
179 weights.push(w);
180 }
181
182 if need_ancilla {
186 for (i, h) in fields.iter().enumerate() {
187 if !h.is_zero() {
188 edges.push((i, n));
189 weights.push(*h);
190 }
191 }
192 }
193
194 let target = MaxCut::new(SimpleGraph::new(total_vertices, edges), weights);
195
196 Ok(ReductionSGToMaxCut {
197 target,
198 ancilla: ancilla_idx,
199 })
200 }
201}
202
203#[cfg(feature = "example-db")]
204pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
205 use crate::export::SolutionPair;
206
207 vec![
208 crate::example_db::specs::RuleExampleSpec {
209 id: "maxcut_to_spinglass",
210 build: || {
211 let (n, edges) = crate::topology::small_graphs::petersen();
212 let source = MaxCut::unweighted(SimpleGraph::new(n, edges));
213 crate::example_db::specs::rule_example_with_witness::<_, SpinGlass<SimpleGraph, i64>>(
214 source,
215 SolutionPair {
216 source_config: serde_json::json!(vec![
217 false, true, false, true, false, true, false, false, false, true
218 ]),
219 target_config: serde_json::json!(vec![-1, 1, -1, 1, -1, 1, -1, -1, -1, 1]),
220 },
221 )
222 },
223 },
224 crate::example_db::specs::RuleExampleSpec {
225 id: "spinglass_to_maxcut",
226 build: || {
227 let (n, edges) = crate::topology::small_graphs::petersen();
228 let couplings: Vec<((usize, usize), i64)> = edges
229 .iter()
230 .enumerate()
231 .map(|(i, &(u, v))| ((u, v), if i % 2 == 0 { 1 } else { -1 }))
232 .collect();
233 let source = SpinGlass::new(n, couplings, vec![0; n]).unwrap();
234 crate::example_db::specs::rule_example_with_witness::<_, MaxCut<SimpleGraph, i64>>(
235 source,
236 SolutionPair {
237 source_config: serde_json::json!(vec![1, -1, 1, 1, 1, -1, 1, -1, -1, 1]),
238 target_config: serde_json::json!(vec![
239 true, false, true, true, true, false, true, false, false, true
240 ]),
241 },
242 )
243 },
244 },
245 ]
246}
247
248#[cfg(test)]
249#[path = "../unit_tests/rules/spinglass_maxcut.rs"]
250mod tests;