problemreductions/models/graph/
maximum_edge_weighted_k_clique.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry, VariantDimension};
15use crate::topology::{Graph, SimpleGraph};
16use crate::traits::Problem;
17use crate::types::{Max, WeightElement};
18use num_traits::Zero;
19use serde::{Deserialize, Serialize};
20
21inventory::submit! {
22 ProblemSchemaEntry {
23 name: "MaximumEdgeWeightedKClique",
24 display_name: "Maximum Edge-Weighted k-Clique",
25 aliases: &[],
26 dimensions: &[VariantDimension::new("weight", "i32", &["i32", "f64"])],
27 module_path: module_path!(),
28 description: "Select exactly k pairwise-adjacent vertices maximizing the total weight of induced clique edges",
29 fields: &[
30 FieldInfo { name: "graph", type_name: "SimpleGraph", description: "The underlying graph G=(V,E)" },
31 FieldInfo { name: "edge_weights", type_name: "Vec<W>", description: "Edge weights in graph edge order" },
32 FieldInfo { name: "k", type_name: "usize", description: "Required clique size" },
33 ],
34 }
35}
36
37inventory::submit! {
38 ProblemSizeFieldEntry {
39 name: "MaximumEdgeWeightedKClique",
40 fields: &["num_vertices", "num_edges"],
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct MaximumEdgeWeightedKClique<W: WeightElement> {
72 graph: SimpleGraph,
74 edge_weights: Vec<W>,
76 k: usize,
78}
79
80impl<W: WeightElement> MaximumEdgeWeightedKClique<W> {
81 pub fn new(graph: SimpleGraph, edge_weights: Vec<W>, k: usize) -> Self {
87 assert_eq!(
88 edge_weights.len(),
89 graph.num_edges(),
90 "edge_weights length must match graph num_edges"
91 );
92 assert!(
93 k <= graph.num_vertices(),
94 "k = {} must be <= num_vertices = {}",
95 k,
96 graph.num_vertices()
97 );
98 Self {
99 graph,
100 edge_weights,
101 k,
102 }
103 }
104
105 pub fn graph(&self) -> &SimpleGraph {
107 &self.graph
108 }
109
110 pub fn edge_weights(&self) -> &[W] {
112 &self.edge_weights
113 }
114
115 pub fn k(&self) -> usize {
117 self.k
118 }
119
120 pub fn num_vertices(&self) -> usize {
122 self.graph.num_vertices()
123 }
124
125 pub fn num_edges(&self) -> usize {
127 self.graph.num_edges()
128 }
129
130 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
132 is_k_clique_config(&self.graph, config, self.k)
133 }
134}
135
136impl<W> Problem for MaximumEdgeWeightedKClique<W>
137where
138 W: WeightElement + crate::variant::VariantParam,
139{
140 const NAME: &'static str = "MaximumEdgeWeightedKClique";
141 type Value = Max<W::Sum>;
142
143 fn variant() -> Vec<(&'static str, &'static str)> {
144 crate::variant_params![W]
145 }
146
147 fn dims(&self) -> Vec<usize> {
148 vec![2; self.graph.num_vertices()]
149 }
150
151 fn evaluate(&self, config: &[usize]) -> Max<W::Sum> {
152 if !is_k_clique_config(&self.graph, config, self.k) {
153 return Max(None);
154 }
155 let mut total = W::Sum::zero();
157 for ((u, v), weight) in self.graph.edges().iter().zip(self.edge_weights.iter()) {
158 if config.get(*u).copied().unwrap_or(0) == 1
159 && config.get(*v).copied().unwrap_or(0) == 1
160 {
161 total += weight.to_sum();
162 }
163 }
164 Max(Some(total))
165 }
166}
167
168fn is_k_clique_config(graph: &SimpleGraph, config: &[usize], k: usize) -> bool {
170 let n = graph.num_vertices();
171 if config.len() != n {
172 return false;
173 }
174 let selected: Vec<usize> = config
175 .iter()
176 .enumerate()
177 .filter(|(_, &v)| v == 1)
178 .map(|(i, _)| i)
179 .collect();
180 if selected.len() != k {
181 return false;
182 }
183 for i in 0..selected.len() {
184 for j in (i + 1)..selected.len() {
185 if !graph.has_edge(selected[i], selected[j]) {
186 return false;
187 }
188 }
189 }
190 true
191}
192
193crate::declare_variants! {
194 default MaximumEdgeWeightedKClique<i32> => "2^num_vertices",
195 MaximumEdgeWeightedKClique<f64> => "2^num_vertices",
196}
197
198#[cfg(feature = "example-db")]
199pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
200 vec![crate::example_db::specs::ModelExampleSpec {
201 id: "maximum_edge_weighted_k_clique_simplegraph_i32",
202 instance: Box::new(MaximumEdgeWeightedKClique::<i32>::new(
203 SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]),
204 vec![5, 4, -1, 1, 0],
205 3,
206 )),
207 optimal_config: vec![1, 1, 1, 0],
208 optimal_value: serde_json::json!(8),
209 }]
210}
211
212#[cfg(test)]
213#[path = "../../unit_tests/models/graph/maximum_edge_weighted_k_clique.rs"]
214mod tests;