problemreductions/rules/
graphpartitioning_qubo.rs1use crate::models::algebraic::QUBO;
8use crate::models::graph::GraphPartitioning;
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11use crate::topology::{Graph, SimpleGraph};
12
13#[derive(Debug, Clone)]
15pub struct ReductionGraphPartitioningToQUBO {
16 target: QUBO<i64>,
17}
18
19impl ReductionResult for ReductionGraphPartitioningToQUBO {
20 type Source = GraphPartitioning<SimpleGraph>;
21 type Target = QUBO<i64>;
22
23 fn target_problem(&self) -> &Self::Target {
24 &self.target
25 }
26
27 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_solution(self.target_problem(), target_solution)?;
32
33 Ok(target_solution.to_vec())
34 }
35}
36
37#[reduction(transform = exact {
38 num_vars = "num_vertices",
39})]
40impl ReduceTo<QUBO<i64>> for GraphPartitioning<SimpleGraph> {
41 type Result = ReductionGraphPartitioningToQUBO;
42
43 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
44 let n = self.num_vertices();
45 let overflow = |operation| {
46 crate::rules::ReductionError::integer_overflow::<Self, QUBO<i64>>(operation)
47 };
48 let n_i64 = i64::try_from(n)
49 .map_err(|_| overflow("converting the vertex count to a QUBO coefficient"))?;
50 let edge_count = i64::try_from(self.num_edges())
51 .map_err(|_| overflow("converting the edge count to a QUBO coefficient"))?;
52 let penalty = edge_count
53 .checked_add(1)
54 .ok_or_else(|| overflow("computing the balance penalty"))?;
55 let mut matrix = vec![vec![0i64; n]; n];
56 let mut degrees = vec![0usize; n];
57 let edges = self.graph().edges();
58
59 for &(u, v) in &edges {
60 degrees[u] += 1;
61 degrees[v] += 1;
62 }
63
64 for (i, row) in matrix.iter_mut().enumerate() {
65 let degree = i64::try_from(degrees[i])
66 .map_err(|_| overflow("converting a vertex degree to a QUBO coefficient"))?;
67 let balance_linear = penalty
68 .checked_mul(
69 1i64.checked_sub(n_i64)
70 .ok_or_else(|| overflow("computing a balance coefficient"))?,
71 )
72 .ok_or_else(|| overflow("computing a balance coefficient"))?;
73 row[i] = degree
74 .checked_add(balance_linear)
75 .ok_or_else(|| overflow("combining QUBO diagonal coefficients"))?;
76 for value in row.iter_mut().skip(i + 1) {
77 *value = penalty
78 .checked_mul(2)
79 .ok_or_else(|| overflow("computing a balance interaction coefficient"))?;
80 }
81 }
82
83 for (u, v) in edges {
84 let (lo, hi) = if u < v { (u, v) } else { (v, u) };
85 matrix[lo][hi] = matrix[lo][hi]
86 .checked_sub(2)
87 .ok_or_else(|| overflow("adding a cut interaction coefficient"))?;
88 }
89
90 Ok(ReductionGraphPartitioningToQUBO {
91 target: QUBO::from_matrix(matrix).map_err(|message| {
92 crate::rules::ReductionError::construction::<
93 GraphPartitioning<SimpleGraph>,
94 QUBO<i64>,
95 >(message)
96 })?,
97 })
98 }
99}
100
101#[cfg(feature = "example-db")]
102pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
103 use crate::export::SolutionPair;
104
105 vec![crate::example_db::specs::RuleExampleSpec {
106 id: "graphpartitioning_to_qubo",
107 build: || {
108 crate::example_db::specs::rule_example_with_witness::<_, QUBO<i64>>(
109 GraphPartitioning::new(SimpleGraph::new(
110 6,
111 vec![
112 (0, 1),
113 (0, 2),
114 (1, 2),
115 (1, 3),
116 (2, 3),
117 (2, 4),
118 (3, 4),
119 (3, 5),
120 (4, 5),
121 ],
122 )),
123 SolutionPair {
124 source_config: serde_json::json!(vec![false, false, false, true, true, true]),
125 target_config: serde_json::json!(vec![false, false, false, true, true, true]),
126 },
127 )
128 },
129 }]
130}
131
132#[cfg(test)]
133#[path = "../unit_tests/rules/graphpartitioning_qubo.rs"]
134mod tests;