problemreductions/models/graph/
partition_into_forests.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::variant::VariantParam;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "PartitionIntoForests",
16 display_name: "Partition into Forests",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 ],
21 category: crate::registry::ProblemCategory::Graph,
22 module_path: module_path!(),
23 description: "Partition vertices into K classes each inducing an acyclic subgraph",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
26 FieldInfo { name: "num_forests", type_name: "usize", description: "num_forests: number of forest classes K (>= 1)" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
58pub struct PartitionIntoForests<G> {
59 graph: G,
61 num_forests: usize,
63}
64
65impl<G: Graph> PartitionIntoForests<G> {
66 pub fn new(graph: G, num_forests: usize) -> Self {
71 assert!(num_forests >= 1, "num_forests must be at least 1");
72 Self { graph, num_forests }
73 }
74
75 pub fn graph(&self) -> &G {
77 &self.graph
78 }
79
80 pub fn num_forests(&self) -> usize {
82 self.num_forests
83 }
84
85 pub fn num_vertices(&self) -> usize {
87 self.graph.num_vertices()
88 }
89
90 pub fn num_edges(&self) -> usize {
92 self.graph.num_edges()
93 }
94}
95
96impl<G> Problem for PartitionIntoForests<G>
97where
98 G: Graph + VariantParam,
99{
100 const NAME: &'static str = "PartitionIntoForests";
101 type Solution = Vec<usize>;
102 type Value = crate::types::Or;
103
104 crate::problem_parameters![
105 ("num_vertices", num_vertices),
106 ("num_edges", num_edges),
107 ("num_forests", num_forests),
108 ];
109
110 fn variant() -> Vec<(&'static str, &'static str)> {
111 crate::variant_params![G]
112 }
113
114 fn evaluate(
115 &self,
116 config: &Self::Solution,
117 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
118 if config.len() != self.graph.num_vertices() {
119 return Err(crate::traits::EvaluationError::InvalidConfiguration(
120 "partition assignment length does not match the graph vertices".into(),
121 ));
122 }
123 if config.iter().any(|&part| part >= self.num_forests) {
124 return Err(crate::traits::EvaluationError::InvalidConfiguration(
125 "partition assignment contains an out-of-range forest".into(),
126 ));
127 }
128 Ok({
129 crate::types::Or(is_valid_forest_partition(
130 &self.graph,
131 self.num_forests,
132 config,
133 ))
134 })
135 }
136}
137
138impl<G> crate::solvers::BruteForceProblem for PartitionIntoForests<G>
139where
140 G: Graph + VariantParam,
141{
142 fn dimensions(&self) -> Vec<usize> {
143 vec![self.num_forests; self.graph.num_vertices()]
144 }
145}
146
147fn is_valid_forest_partition<G: Graph>(graph: &G, num_forests: usize, config: &[usize]) -> bool {
149 let n = graph.num_vertices();
150
151 if config.len() != n {
153 return false;
154 }
155 if config.iter().any(|&c| c >= num_forests) {
156 return false;
157 }
158
159 let mut parent: Vec<usize> = (0..n).collect();
163
164 fn find(parent: &mut Vec<usize>, x: usize) -> usize {
165 if parent[x] != x {
166 parent[x] = find(parent, parent[x]);
167 }
168 parent[x]
169 }
170
171 for (u, v) in graph.edges() {
172 if config[u] != config[v] {
173 continue;
175 }
176 let ru = find(&mut parent, u);
178 let rv = find(&mut parent, v);
179 if ru == rv {
180 return false; }
182 parent[ru] = rv; }
184
185 true
186}
187
188crate::declare_variants! {
189 default PartitionIntoForests<SimpleGraph> => "num_forests^num_vertices",
190}
191
192crate::register_brute_force! {
193 PartitionIntoForests<SimpleGraph>,
194}
195
196#[cfg(feature = "example-db")]
197pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
198 vec![crate::example_db::specs::ModelExampleSpec {
199 id: "partition_into_forests_simplegraph",
200 instance: Box::new(PartitionIntoForests::new(
201 SimpleGraph::new(
202 6,
203 vec![(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)],
204 ),
205 2,
206 )),
207 optimal_config: serde_json::json!(vec![0, 1, 1, 0, 1, 1]),
210 optimal_value: serde_json::json!(true),
211 }]
212}
213
214#[cfg(test)]
215#[path = "../../unit_tests/models/graph/partition_into_forests.rs"]
216mod tests;