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 module_path: module_path!(),
22 description: "Partition vertices into K groups each inducing a clique",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
25 FieldInfo { name: "num_cliques", type_name: "usize", description: "num_cliques: maximum number of clique groups K (>= 1)" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
56#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
57pub struct PartitionIntoCliques<G> {
58 graph: G,
60 num_cliques: usize,
62}
63
64impl<G: Graph> PartitionIntoCliques<G> {
65 pub fn new(graph: G, num_cliques: usize) -> Self {
70 assert!(num_cliques >= 1, "num_cliques must be at least 1");
71 assert!(
72 num_cliques <= graph.num_vertices(),
73 "num_cliques must be at most num_vertices"
74 );
75 Self { graph, num_cliques }
76 }
77
78 pub fn graph(&self) -> &G {
80 &self.graph
81 }
82
83 pub fn num_cliques(&self) -> usize {
85 self.num_cliques
86 }
87
88 pub fn num_vertices(&self) -> usize {
90 self.graph.num_vertices()
91 }
92
93 pub fn num_edges(&self) -> usize {
95 self.graph.num_edges()
96 }
97}
98
99impl<G> Problem for PartitionIntoCliques<G>
100where
101 G: Graph + VariantParam,
102{
103 const NAME: &'static str = "PartitionIntoCliques";
104 type Value = crate::types::Or;
105
106 fn variant() -> Vec<(&'static str, &'static str)> {
107 crate::variant_params![G]
108 }
109
110 fn dims(&self) -> Vec<usize> {
111 vec![self.num_cliques; self.graph.num_vertices()]
112 }
113
114 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
115 crate::types::Or(is_valid_clique_partition(
116 &self.graph,
117 self.num_cliques,
118 config,
119 ))
120 }
121}
122
123fn is_valid_clique_partition<G: Graph>(graph: &G, num_cliques: usize, config: &[usize]) -> bool {
125 let n = graph.num_vertices();
126
127 if config.len() != n {
129 return false;
130 }
131 if config.iter().any(|&c| c >= num_cliques) {
132 return false;
133 }
134
135 for group in 0..num_cliques {
137 let members: Vec<usize> = (0..n).filter(|&v| config[v] == group).collect();
138 for i in 0..members.len() {
139 for j in (i + 1)..members.len() {
140 if !graph.has_edge(members[i], members[j]) {
141 return false;
142 }
143 }
144 }
145 }
146
147 true
148}
149
150crate::declare_variants! {
151 default PartitionIntoCliques<SimpleGraph> => "2^num_vertices",
152}
153
154#[cfg(feature = "example-db")]
155pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
156 vec![crate::example_db::specs::ModelExampleSpec {
157 id: "partition_into_cliques_simplegraph",
158 instance: Box::new(PartitionIntoCliques::new(
159 SimpleGraph::new(
160 6,
161 vec![
162 (0, 1),
163 (0, 2),
164 (1, 2),
165 (3, 4),
166 (3, 5),
167 (4, 5),
168 (0, 3),
169 (1, 4),
170 (2, 5),
171 ],
172 ),
173 3,
174 )),
175 optimal_config: vec![0, 0, 0, 1, 1, 1],
176 optimal_value: serde_json::json!(true),
177 }]
178}
179
180#[cfg(test)]
181#[path = "../../unit_tests/models/graph/partition_into_cliques.rs"]
182mod tests;