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 module_path: module_path!(),
24 description: "Partition vertices into triples each inducing at least two edges (P3 or triangle)",
25 fields: &[
26 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E) with |V| divisible by 3" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
62pub struct PartitionIntoPathsOfLength2<G> {
63 graph: G,
65}
66
67impl<G: Graph> PartitionIntoPathsOfLength2<G> {
68 pub fn new(graph: G) -> Self {
73 assert_eq!(
74 graph.num_vertices() % 3,
75 0,
76 "Number of vertices ({}) must be divisible by 3",
77 graph.num_vertices()
78 );
79 Self { graph }
80 }
81
82 pub fn graph(&self) -> &G {
84 &self.graph
85 }
86
87 pub fn num_vertices(&self) -> usize {
89 self.graph.num_vertices()
90 }
91
92 pub fn num_edges(&self) -> usize {
94 self.graph.num_edges()
95 }
96
97 pub fn num_groups(&self) -> usize {
99 self.graph.num_vertices() / 3
100 }
101
102 pub fn is_valid_partition(&self, config: &[usize]) -> bool {
108 let n = self.graph.num_vertices();
109 let q = self.num_groups();
110
111 if config.len() != n {
112 return false;
113 }
114
115 if config.iter().any(|&g| g >= q) {
117 return false;
118 }
119
120 let mut group_sizes = vec![0usize; q];
122 for &g in config {
123 group_sizes[g] += 1;
124 }
125
126 if group_sizes.iter().any(|&s| s != 3) {
128 return false;
129 }
130
131 let mut group_edge_counts = vec![0usize; q];
133 for (u, v) in self.graph.edges() {
134 if config[u] == config[v] {
135 group_edge_counts[config[u]] += 1;
136 }
137 }
138 if group_edge_counts.iter().any(|&c| c < 2) {
139 return false;
140 }
141
142 true
143 }
144}
145
146impl<G> Problem for PartitionIntoPathsOfLength2<G>
147where
148 G: Graph + VariantParam,
149{
150 const NAME: &'static str = "PartitionIntoPathsOfLength2";
151 type Value = crate::types::Or;
152
153 fn variant() -> Vec<(&'static str, &'static str)> {
154 crate::variant_params![G]
155 }
156
157 fn dims(&self) -> Vec<usize> {
158 let q = self.num_groups();
159 vec![q; self.graph.num_vertices()]
160 }
161
162 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
163 crate::types::Or(self.is_valid_partition(config))
164 }
165}
166
167crate::declare_variants! {
168 default PartitionIntoPathsOfLength2<SimpleGraph> => "3^num_vertices",
169}
170
171#[cfg(feature = "example-db")]
172pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
173 vec![crate::example_db::specs::ModelExampleSpec {
174 id: "partition_into_paths_of_length_2_simplegraph",
175 instance: Box::new(PartitionIntoPathsOfLength2::new(SimpleGraph::new(
176 9,
177 vec![
178 (0, 1),
179 (1, 2),
180 (3, 4),
181 (4, 5),
182 (6, 7),
183 (7, 8),
184 (0, 3),
185 (2, 5),
186 (3, 6),
187 (5, 8),
188 (1, 4),
189 (4, 7),
190 ],
191 ))),
192 optimal_config: vec![0, 0, 0, 1, 1, 1, 2, 2, 2],
193 optimal_value: serde_json::json!(true),
194 }]
195}
196
197#[cfg(test)]
198#[path = "../../unit_tests/models/graph/partition_into_paths_of_length_2.rs"]
199mod tests;