problemreductions/models/graph/
partition_into_triangles.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::variant::VariantParam;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "PartitionIntoTriangles",
15 display_name: "Partition Into Triangles",
16 aliases: &[],
17 dimensions: &[
18 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
19 ],
20 category: crate::registry::ProblemCategory::Graph,
21 module_path: module_path!(),
22 description: "Partition vertices into triangles (K3 subgraphs)",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E) with |V| divisible by 3" },
25 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
55pub struct PartitionIntoTriangles<G> {
56 graph: G,
58}
59
60impl<G: Graph> PartitionIntoTriangles<G> {
61 pub fn new(graph: G) -> Self {
66 assert!(
67 graph.num_vertices().is_multiple_of(3),
68 "Number of vertices ({}) must be divisible by 3",
69 graph.num_vertices()
70 );
71 Self { graph }
72 }
73
74 pub fn graph(&self) -> &G {
76 &self.graph
77 }
78
79 pub fn num_vertices(&self) -> usize {
81 self.graph.num_vertices()
82 }
83
84 pub fn num_edges(&self) -> usize {
86 self.graph.num_edges()
87 }
88}
89
90impl<G> Problem for PartitionIntoTriangles<G>
91where
92 G: Graph + VariantParam,
93{
94 const NAME: &'static str = "PartitionIntoTriangles";
95 type Solution = Vec<usize>;
96 type Value = crate::types::Or;
97
98 crate::problem_parameters![("num_vertices", num_vertices),];
99
100 fn variant() -> Vec<(&'static str, &'static str)> {
101 crate::variant_params![G]
102 }
103
104 fn evaluate(
105 &self,
106 config: &Self::Solution,
107 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
108 Ok({
109 crate::types::Or({
110 let n = self.graph.num_vertices();
111 let q = n / 3;
112
113 if config.len() != n {
115 return Err(crate::traits::EvaluationError::InvalidConfiguration(
116 "partition assignment length does not match the graph vertices".into(),
117 ));
118 }
119
120 if config.iter().any(|&c| c >= q) {
122 return Err(crate::traits::EvaluationError::InvalidConfiguration(
123 "partition assignment contains an out-of-range group".into(),
124 ));
125 }
126
127 let mut counts = vec![0usize; q];
129 for &c in config {
130 counts[c] += 1;
131 }
132
133 if counts.iter().any(|&c| c != 3) {
135 return Ok(crate::types::Or(false));
136 }
137
138 let mut group_verts = vec![[0usize; 3]; q];
140 let mut group_pos = vec![0usize; q];
141
142 for (v, &g) in config.iter().enumerate() {
143 let pos = group_pos[g];
144 group_verts[g][pos] = v;
145 group_pos[g] = pos + 1;
146 }
147
148 for verts in &group_verts {
150 if !self.graph.has_edge(verts[0], verts[1]) {
151 return Ok(crate::types::Or(false));
152 }
153 if !self.graph.has_edge(verts[0], verts[2]) {
154 return Ok(crate::types::Or(false));
155 }
156 if !self.graph.has_edge(verts[1], verts[2]) {
157 return Ok(crate::types::Or(false));
158 }
159 }
160
161 true
162 })
163 })
164 }
165}
166
167impl<G> crate::solvers::BruteForceProblem for PartitionIntoTriangles<G>
168where
169 G: Graph + VariantParam,
170{
171 fn dimensions(&self) -> Vec<usize> {
172 let q = self.graph.num_vertices() / 3;
173 vec![q; self.graph.num_vertices()]
174 }
175}
176
177crate::declare_variants! {
178 default PartitionIntoTriangles<SimpleGraph> => "2^num_vertices",
179}
180
181crate::register_brute_force! {
182 PartitionIntoTriangles<SimpleGraph>,
183}
184
185#[cfg(feature = "example-db")]
186pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
187 vec![crate::example_db::specs::ModelExampleSpec {
188 id: "partition_into_triangles_simplegraph",
189 instance: Box::new(PartitionIntoTriangles::new(SimpleGraph::new(
190 6,
191 vec![(0, 1), (0, 2), (1, 2), (3, 4), (3, 5), (4, 5), (0, 3)],
192 ))),
193 optimal_config: serde_json::json!(vec![0, 0, 0, 1, 1, 1]),
194 optimal_value: serde_json::json!(true),
195 }]
196}
197
198#[cfg(test)]
199#[path = "../../unit_tests/models/graph/partition_into_triangles.rs"]
200mod tests;