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)]
60#[serde(try_from = "MaximumCliqueData<G, W>")]
61#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: Clone + Default + Deserialize<'de>"))]
62pub struct MaximumClique<G, W> {
63 graph: G,
65 weights: Vec<W>,
67}
68
69#[derive(Deserialize)]
70struct MaximumCliqueData<G, W> {
71 graph: G,
72 weights: Vec<W>,
73}
74
75impl<G, W> TryFrom<MaximumCliqueData<G, W>> for MaximumClique<G, W>
76where
77 G: Graph,
78 W: Clone + Default,
79{
80 type Error = crate::registry::ConstructionError;
81
82 fn try_from(data: MaximumCliqueData<G, W>) -> Result<Self, Self::Error> {
83 Self::try_new(data.graph, data.weights)
84 }
85}
86
87#[derive(Debug, Deserialize, crate::CreateSpec)]
88struct MaximumCliqueCreateSpec<W> {
89 graph: SimpleGraph,
91 weights: Vec<W>,
93}
94
95impl<W: Clone + Default> TryFrom<MaximumCliqueCreateSpec<W>> for MaximumClique<SimpleGraph, W> {
96 type Error = crate::registry::ConstructionError;
97 fn try_from(spec: MaximumCliqueCreateSpec<W>) -> Result<Self, Self::Error> {
98 Self::try_new(spec.graph, spec.weights)
99 }
100}
101
102impl<G: Graph, W: Clone + Default> MaximumClique<G, W> {
103 pub fn new(graph: G, weights: Vec<W>) -> Self {
105 Self::try_new(graph, weights).unwrap_or_else(|error| panic!("{error}"))
106 }
107
108 fn try_new(graph: G, weights: Vec<W>) -> Result<Self, crate::registry::ConstructionError> {
109 if weights.len() != graph.num_vertices() {
110 return Err(crate::registry::ConstructionError::length_mismatch(
111 "weights",
112 weights.len(),
113 graph.num_vertices(),
114 ));
115 }
116 Ok(Self { graph, weights })
117 }
118
119 pub fn graph(&self) -> &G {
121 &self.graph
122 }
123
124 pub fn weights(&self) -> &[W] {
126 &self.weights
127 }
128
129 pub fn is_weighted(&self) -> bool
131 where
132 W: WeightElement,
133 {
134 !W::IS_UNIT
135 }
136
137 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
139 is_clique_config(&self.graph, config)
140 }
141}
142
143impl<G: Graph, W: WeightElement> MaximumClique<G, W> {
144 pub fn num_vertices(&self) -> usize {
146 self.graph().num_vertices()
147 }
148
149 pub fn num_edges(&self) -> usize {
151 self.graph().num_edges()
152 }
153}
154
155impl<G, W> Problem for MaximumClique<G, W>
156where
157 G: Graph + crate::variant::VariantParam,
158 W: WeightElement + crate::variant::VariantParam,
159{
160 const NAME: &'static str = "MaximumClique";
161 type Solution = Vec<bool>;
162 type Value = Max<W::Sum>;
163
164 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
165
166 fn variant() -> Vec<(&'static str, &'static str)> {
167 crate::variant_params![G, W]
168 }
169
170 fn evaluate(
171 &self,
172 config: &Self::Solution,
173 ) -> Result<Max<W::Sum>, crate::traits::EvaluationError> {
174 if config.len() != self.graph.num_vertices() {
175 return Err(crate::traits::EvaluationError::InvalidConfiguration(
176 "vertex-selection length does not match the graph".into(),
177 ));
178 }
179 Ok({
180 if !is_clique_config(&self.graph, config) {
181 return Ok(Max(None));
182 }
183 let mut total = W::Sum::zero();
184 for (i, &selected) in config.iter().enumerate() {
185 if selected {
186 total = W::checked_add_to_sum(
187 total,
188 self.weights[i].to_sum(),
189 "summing selected clique weights",
190 )?;
191 }
192 }
193 Max(Some(total))
194 })
195 }
196}
197
198impl<G, W> crate::solvers::BruteForceProblem for MaximumClique<G, W>
199where
200 G: Graph + crate::variant::VariantParam,
201 W: WeightElement + crate::variant::VariantParam,
202{
203 fn dimensions(&self) -> Vec<usize> {
204 vec![2; self.graph.num_vertices()]
205 }
206}
207
208fn is_clique_config<G: Graph>(graph: &G, config: &[bool]) -> bool {
210 let selected: Vec<usize> = config
212 .iter()
213 .enumerate()
214 .filter(|(_, &v)| v)
215 .map(|(i, _)| i)
216 .collect();
217
218 for i in 0..selected.len() {
220 for j in (i + 1)..selected.len() {
221 if !graph.has_edge(selected[i], selected[j]) {
222 return false;
223 }
224 }
225 }
226 true
227}
228
229crate::impl_random_generate!(MaximumClique<SimpleGraph, i64>, crate::random::SimpleGraphRandomSpec, |spec| {
230 Ok(MaximumClique::new(spec.graph()?, vec![1; spec.num_vertices]))
231});
232crate::impl_random_generate!(MaximumClique<SimpleGraph, One>, crate::random::SimpleGraphRandomSpec, |spec| {
233 Ok(MaximumClique::new(spec.graph()?, vec![One; spec.num_vertices]))
234});
235
236#[derive(Debug, Deserialize, crate::CreateSpec)]
237struct MaximumCliqueOneCreateSpec {
238 graph: SimpleGraph,
240}
241
242impl TryFrom<MaximumCliqueOneCreateSpec> for MaximumClique<SimpleGraph, One> {
243 type Error = crate::registry::ConstructionError;
244 fn try_from(spec: MaximumCliqueOneCreateSpec) -> Result<Self, Self::Error> {
245 let weights = vec![One; spec.graph.num_vertices()];
246 Self::try_new(spec.graph, weights)
247 }
248}
249
250crate::declare_variants! {
251 MaximumClique<SimpleGraph, i64> => "1.1996^num_vertices" create MaximumCliqueCreateSpec<i64> random,
252 default MaximumClique<SimpleGraph, One> => "1.1996^num_vertices" create MaximumCliqueOneCreateSpec random,
253}
254
255crate::register_brute_force! {
256 MaximumClique<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
257 MaximumClique<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
258}
259
260#[cfg(feature = "example-db")]
261pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
262 vec![crate::example_db::specs::ModelExampleSpec {
263 id: "maximum_clique_simplegraph",
264 instance: Box::new(MaximumClique::new(
265 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
266 vec![1i64; 5],
267 )),
268 optimal_config: serde_json::json!(vec![false, false, true, true, true]),
269 optimal_value: serde_json::json!(3),
270 }]
271}
272
273#[cfg(test)]
282pub(crate) fn is_clique<G: Graph>(graph: &G, selected: &[bool]) -> bool {
283 assert_eq!(
284 selected.len(),
285 graph.num_vertices(),
286 "selected length must match num_vertices"
287 );
288
289 let selected_vertices: Vec<usize> = selected
291 .iter()
292 .enumerate()
293 .filter(|(_, &s)| s)
294 .map(|(i, _)| i)
295 .collect();
296
297 for i in 0..selected_vertices.len() {
299 for j in (i + 1)..selected_vertices.len() {
300 if !graph.has_edge(selected_vertices[i], selected_vertices[j]) {
301 return false;
302 }
303 }
304 }
305 true
306}
307
308#[cfg(test)]
309#[path = "../../unit_tests/models/graph/maximum_clique.rs"]
310mod tests;