problemreductions/models/graph/
partition_into_paths_of_length_2.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
10use crate::topology::{Graph, SimpleGraph};
11use crate::traits::Problem;
12use crate::variant::VariantParam;
13use serde::{Deserialize, Serialize};
14
15inventory::submit! {
16 ProblemSchemaEntry {
17 name: "PartitionIntoPathsOfLength2",
18 display_name: "Partition into Paths of Length 2",
19 aliases: &[],
20 dimensions: &[
21 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
22 ],
23 category: crate::registry::ProblemCategory::Graph,
24 module_path: module_path!(),
25 description: "Partition vertices into triples each inducing at least two edges (P3 or triangle)",
26 fields: &[
27 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E) with |V| divisible by 3" },
28 ],
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(try_from = "PartitionIntoPathsOfLength2Data<G>")]
63#[serde(bound(deserialize = "G: Graph + Deserialize<'de>"))]
64pub struct PartitionIntoPathsOfLength2<G> {
65 graph: G,
67}
68
69#[derive(Deserialize)]
70#[serde(bound(deserialize = "G: Graph + Deserialize<'de>"))]
71struct PartitionIntoPathsOfLength2Data<G> {
72 graph: G,
73}
74
75impl<G> TryFrom<PartitionIntoPathsOfLength2Data<G>> for PartitionIntoPathsOfLength2<G>
76where
77 G: Graph,
78{
79 type Error = crate::registry::ConstructionError;
80
81 fn try_from(data: PartitionIntoPathsOfLength2Data<G>) -> Result<Self, Self::Error> {
82 Self::try_new(data.graph)
83 }
84}
85
86impl<G: Graph> PartitionIntoPathsOfLength2<G> {
87 pub fn new(graph: G) -> Self {
92 Self::try_new(graph).unwrap_or_else(|error| panic!("{error}"))
93 }
94
95 fn try_new(graph: G) -> Result<Self, crate::registry::ConstructionError> {
96 if !graph.num_vertices().is_multiple_of(3) {
97 return Err(format!(
98 "Number of vertices ({}) must be divisible by 3",
99 graph.num_vertices()
100 )
101 .into());
102 }
103 Ok(Self { graph })
104 }
105
106 pub fn graph(&self) -> &G {
108 &self.graph
109 }
110
111 pub fn num_vertices(&self) -> usize {
113 self.graph.num_vertices()
114 }
115
116 pub fn num_edges(&self) -> usize {
118 self.graph.num_edges()
119 }
120
121 pub fn num_groups(&self) -> usize {
123 self.graph.num_vertices() / 3
124 }
125
126 pub fn is_valid_partition(&self, config: &[usize]) -> bool {
132 let n = self.graph.num_vertices();
133 let q = self.num_groups();
134
135 if config.len() != n {
136 return false;
137 }
138
139 if config.iter().any(|&g| g >= q) {
141 return false;
142 }
143
144 let mut groups = vec![Vec::new(); q];
145 for (vertex, &group) in config.iter().enumerate() {
146 groups[group].push(vertex);
147 }
148
149 if groups.iter().any(|vertices| vertices.len() != 3) {
151 return false;
152 }
153
154 groups.iter().all(|vertices| {
156 let [a, b, c] = [vertices[0], vertices[1], vertices[2]];
157 usize::from(self.graph.has_edge(a, b))
158 + usize::from(self.graph.has_edge(a, c))
159 + usize::from(self.graph.has_edge(b, c))
160 >= 2
161 })
162 }
163}
164
165impl<G> Problem for PartitionIntoPathsOfLength2<G>
166where
167 G: Graph + VariantParam,
168{
169 const NAME: &'static str = "PartitionIntoPathsOfLength2";
170 type Solution = Vec<usize>;
171 type Value = crate::types::Or;
172
173 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
174
175 fn variant() -> Vec<(&'static str, &'static str)> {
176 crate::variant_params![G]
177 }
178
179 fn evaluate(
180 &self,
181 config: &Self::Solution,
182 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
183 if config.len() != self.graph.num_vertices() {
184 return Err(crate::traits::EvaluationError::InvalidConfiguration(
185 "partition assignment length does not match the graph vertices".into(),
186 ));
187 }
188 if config.iter().any(|&group| group >= self.num_groups()) {
189 return Err(crate::traits::EvaluationError::InvalidConfiguration(
190 "partition assignment contains an out-of-range group".into(),
191 ));
192 }
193 Ok(crate::types::Or(self.is_valid_partition(config)))
194 }
195}
196
197impl<G> crate::solvers::BruteForceProblem for PartitionIntoPathsOfLength2<G>
198where
199 G: Graph + VariantParam,
200{
201 fn dimensions(&self) -> Vec<usize> {
202 let q = self.num_groups();
203 vec![q; self.graph.num_vertices()]
204 }
205}
206
207crate::declare_variants! {
208 default PartitionIntoPathsOfLength2<SimpleGraph> => "3^num_vertices",
209}
210
211crate::register_brute_force! {
212 PartitionIntoPathsOfLength2<SimpleGraph>,
213}
214
215#[cfg(feature = "example-db")]
216pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
217 vec![crate::example_db::specs::ModelExampleSpec {
218 id: "partition_into_paths_of_length_2_simplegraph",
219 instance: Box::new(PartitionIntoPathsOfLength2::new(SimpleGraph::new(
220 9,
221 vec![
222 (0, 1),
223 (1, 2),
224 (3, 4),
225 (4, 5),
226 (6, 7),
227 (7, 8),
228 (0, 3),
229 (2, 5),
230 (3, 6),
231 (5, 8),
232 (1, 4),
233 (4, 7),
234 ],
235 ))),
236 optimal_config: serde_json::json!(vec![0, 0, 0, 1, 1, 1, 2, 2, 2]),
237 optimal_value: serde_json::json!(true),
238 }]
239}
240
241#[cfg(test)]
242#[path = "../../unit_tests/models/graph/partition_into_paths_of_length_2.rs"]
243mod tests;