problemreductions/models/graph/
partition_into_cliques.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: "PartitionIntoCliques",
16 display_name: "Partition into Cliques",
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 groups each inducing a clique",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
26 FieldInfo { name: "num_cliques", type_name: "usize", description: "num_cliques: maximum number of clique groups K (>= 1)" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
58pub struct PartitionIntoCliques<G> {
59 graph: G,
61 num_cliques: usize,
63}
64
65impl<G: Graph> PartitionIntoCliques<G> {
66 pub fn new(graph: G, num_cliques: usize) -> Self {
71 assert!(num_cliques >= 1, "num_cliques must be at least 1");
72 assert!(
73 num_cliques <= graph.num_vertices(),
74 "num_cliques must be at most num_vertices"
75 );
76 Self { graph, num_cliques }
77 }
78
79 pub fn graph(&self) -> &G {
81 &self.graph
82 }
83
84 pub fn num_cliques(&self) -> usize {
86 self.num_cliques
87 }
88
89 pub fn num_vertices(&self) -> usize {
91 self.graph.num_vertices()
92 }
93
94 pub fn num_edges(&self) -> usize {
96 self.graph.num_edges()
97 }
98}
99
100impl<G> Problem for PartitionIntoCliques<G>
101where
102 G: Graph + VariantParam,
103{
104 const NAME: &'static str = "PartitionIntoCliques";
105 type Solution = Vec<usize>;
106 type Value = crate::types::Or;
107
108 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
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_cliques) {
124 return Err(crate::traits::EvaluationError::InvalidConfiguration(
125 "partition assignment contains an out-of-range clique".into(),
126 ));
127 }
128 Ok({
129 crate::types::Or(is_valid_clique_partition(
130 &self.graph,
131 self.num_cliques,
132 config,
133 ))
134 })
135 }
136}
137
138impl<G> crate::solvers::BruteForceProblem for PartitionIntoCliques<G>
139where
140 G: Graph + VariantParam,
141{
142 fn dimensions(&self) -> Vec<usize> {
143 vec![self.num_cliques; self.graph.num_vertices()]
144 }
145}
146
147fn is_valid_clique_partition<G: Graph>(graph: &G, num_cliques: 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_cliques) {
156 return false;
157 }
158
159 for group in 0..num_cliques {
161 let members: Vec<usize> = (0..n).filter(|&v| config[v] == group).collect();
162 for i in 0..members.len() {
163 for j in (i + 1)..members.len() {
164 if !graph.has_edge(members[i], members[j]) {
165 return false;
166 }
167 }
168 }
169 }
170
171 true
172}
173
174crate::declare_variants! {
175 default PartitionIntoCliques<SimpleGraph> => "2^num_vertices",
176}
177
178crate::register_brute_force! {
179 PartitionIntoCliques<SimpleGraph>,
180}
181
182#[cfg(feature = "example-db")]
183pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
184 vec![crate::example_db::specs::ModelExampleSpec {
185 id: "partition_into_cliques_simplegraph",
186 instance: Box::new(PartitionIntoCliques::new(
187 SimpleGraph::new(
188 6,
189 vec![
190 (0, 1),
191 (0, 2),
192 (1, 2),
193 (3, 4),
194 (3, 5),
195 (4, 5),
196 (0, 3),
197 (1, 4),
198 (2, 5),
199 ],
200 ),
201 3,
202 )),
203 optimal_config: serde_json::json!(vec![0, 0, 0, 1, 1, 1]),
204 optimal_value: serde_json::json!(true),
205 }]
206}
207
208#[cfg(test)]
209#[path = "../../unit_tests/models/graph/partition_into_cliques.rs"]
210mod tests;