problemreductions/models/graph/
maximum_clique.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::{Max, One, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaximumClique",
16 display_name: "Maximum Clique",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "One", &["One", "i64"]),
21 ],
22 category: crate::registry::ProblemCategory::Graph,
23 module_path: module_path!(),
24 description: "Find maximum weight clique in a graph",
25 fields: MaximumCliqueCreateSpec::<One>::FIELDS,
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct MaximumClique<G, W> {
61 graph: G,
63 weights: Vec<W>,
65}
66
67#[derive(Debug, Deserialize, crate::CreateSpec)]
68struct MaximumCliqueCreateSpec<W> {
69 graph: SimpleGraph,
71 weights: Vec<W>,
73}
74
75impl<W: Clone + Default> TryFrom<MaximumCliqueCreateSpec<W>> for MaximumClique<SimpleGraph, W> {
76 type Error = crate::registry::ConstructionError;
77 fn try_from(spec: MaximumCliqueCreateSpec<W>) -> Result<Self, Self::Error> {
78 if spec.weights.len() != spec.graph.num_vertices() {
79 return Err(format!(
80 "weights has {} entries, expected {}",
81 spec.weights.len(),
82 spec.graph.num_vertices()
83 )
84 .into());
85 }
86 Ok(Self::new(spec.graph, spec.weights))
87 }
88}
89
90impl<G: Graph, W: Clone + Default> MaximumClique<G, W> {
91 pub fn new(graph: G, weights: Vec<W>) -> Self {
93 assert_eq!(
94 weights.len(),
95 graph.num_vertices(),
96 "weights length must match graph num_vertices"
97 );
98 Self { graph, weights }
99 }
100
101 pub fn graph(&self) -> &G {
103 &self.graph
104 }
105
106 pub fn weights(&self) -> &[W] {
108 &self.weights
109 }
110
111 pub fn is_weighted(&self) -> bool
113 where
114 W: WeightElement,
115 {
116 !W::IS_UNIT
117 }
118
119 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
121 is_clique_config(&self.graph, config)
122 }
123}
124
125impl<G: Graph, W: WeightElement> MaximumClique<G, W> {
126 pub fn num_vertices(&self) -> usize {
128 self.graph().num_vertices()
129 }
130
131 pub fn num_edges(&self) -> usize {
133 self.graph().num_edges()
134 }
135}
136
137impl<G, W> Problem for MaximumClique<G, W>
138where
139 G: Graph + crate::variant::VariantParam,
140 W: WeightElement + crate::variant::VariantParam,
141{
142 const NAME: &'static str = "MaximumClique";
143 type Solution = Vec<bool>;
144 type Value = Max<W::Sum>;
145
146 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
147
148 fn variant() -> Vec<(&'static str, &'static str)> {
149 crate::variant_params![G, W]
150 }
151
152 fn evaluate(
153 &self,
154 config: &Self::Solution,
155 ) -> Result<Max<W::Sum>, crate::traits::EvaluationError> {
156 if config.len() != self.graph.num_vertices() {
157 return Err(crate::traits::EvaluationError::InvalidConfiguration(
158 "vertex-selection length does not match the graph".into(),
159 ));
160 }
161 Ok({
162 if !is_clique_config(&self.graph, config) {
163 return Ok(Max(None));
164 }
165 let mut total = W::Sum::zero();
166 for (i, &selected) in config.iter().enumerate() {
167 if selected {
168 total = W::checked_add_to_sum(
169 total,
170 self.weights[i].to_sum(),
171 "summing selected clique weights",
172 )?;
173 }
174 }
175 Max(Some(total))
176 })
177 }
178}
179
180impl<G, W> crate::solvers::BruteForceProblem for MaximumClique<G, W>
181where
182 G: Graph + crate::variant::VariantParam,
183 W: WeightElement + crate::variant::VariantParam,
184{
185 fn dimensions(&self) -> Vec<usize> {
186 vec![2; self.graph.num_vertices()]
187 }
188}
189
190fn is_clique_config<G: Graph>(graph: &G, config: &[bool]) -> bool {
192 let selected: Vec<usize> = config
194 .iter()
195 .enumerate()
196 .filter(|(_, &v)| v)
197 .map(|(i, _)| i)
198 .collect();
199
200 for i in 0..selected.len() {
202 for j in (i + 1)..selected.len() {
203 if !graph.has_edge(selected[i], selected[j]) {
204 return false;
205 }
206 }
207 }
208 true
209}
210
211crate::impl_random_generate!(MaximumClique<SimpleGraph, i64>, crate::random::SimpleGraphRandomSpec, |spec| {
212 Ok(MaximumClique::new(spec.graph()?, vec![1; spec.num_vertices]))
213});
214crate::impl_random_generate!(MaximumClique<SimpleGraph, One>, crate::random::SimpleGraphRandomSpec, |spec| {
215 Ok(MaximumClique::new(spec.graph()?, vec![One; spec.num_vertices]))
216});
217
218#[derive(Debug, Deserialize, crate::CreateSpec)]
219struct MaximumCliqueOneCreateSpec {
220 graph: SimpleGraph,
222}
223
224impl TryFrom<MaximumCliqueOneCreateSpec> for MaximumClique<SimpleGraph, One> {
225 type Error = crate::registry::ConstructionError;
226 fn try_from(spec: MaximumCliqueOneCreateSpec) -> Result<Self, Self::Error> {
227 let weights = vec![One; spec.graph.num_vertices()];
228 Ok(Self::new(spec.graph, weights))
229 }
230}
231
232crate::declare_variants! {
233 MaximumClique<SimpleGraph, i64> => "1.1996^num_vertices" create MaximumCliqueCreateSpec<i64> random,
234 default MaximumClique<SimpleGraph, One> => "1.1996^num_vertices" create MaximumCliqueOneCreateSpec random,
235}
236
237crate::register_brute_force! {
238 MaximumClique<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
239 MaximumClique<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
240}
241
242#[cfg(feature = "example-db")]
243pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
244 vec![crate::example_db::specs::ModelExampleSpec {
245 id: "maximum_clique_simplegraph",
246 instance: Box::new(MaximumClique::new(
247 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
248 vec![1i64; 5],
249 )),
250 optimal_config: serde_json::json!(vec![false, false, true, true, true]),
251 optimal_value: serde_json::json!(3),
252 }]
253}
254
255#[cfg(test)]
264pub(crate) fn is_clique<G: Graph>(graph: &G, selected: &[bool]) -> bool {
265 assert_eq!(
266 selected.len(),
267 graph.num_vertices(),
268 "selected length must match num_vertices"
269 );
270
271 let selected_vertices: Vec<usize> = selected
273 .iter()
274 .enumerate()
275 .filter(|(_, &s)| s)
276 .map(|(i, _)| i)
277 .collect();
278
279 for i in 0..selected_vertices.len() {
281 for j in (i + 1)..selected_vertices.len() {
282 if !graph.has_edge(selected_vertices[i], selected_vertices[j]) {
283 return false;
284 }
285 }
286 }
287 true
288}
289
290#[cfg(test)]
291#[path = "../../unit_tests/models/graph/maximum_clique.rs"]
292mod tests;