problemreductions/models/graph/
min_max_multicenter.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::{Min, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinMaxMulticenter",
16 display_name: "Min-Max Multicenter",
17 aliases: &["pCenter"],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i32", &["i32", "One"]),
21 ],
22 module_path: module_path!(),
23 description: "Find K centers minimizing the maximum weighted distance from any vertex to its nearest center (vertex p-center)",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
26 FieldInfo { name: "vertex_weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
27 FieldInfo { name: "edge_lengths", type_name: "Vec<W>", description: "Edge lengths l: E -> R" },
28 FieldInfo { name: "k", type_name: "usize", description: "Number of centers to place" },
29 ],
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct MinMaxMulticenter<G, W: WeightElement> {
62 graph: G,
64 vertex_weights: Vec<W>,
66 edge_lengths: Vec<W>,
68 k: usize,
70}
71
72impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
73 pub fn new(graph: G, vertex_weights: Vec<W>, edge_lengths: Vec<W>, k: usize) -> Self {
81 assert_eq!(
82 vertex_weights.len(),
83 graph.num_vertices(),
84 "vertex_weights length must match num_vertices"
85 );
86 assert_eq!(
87 edge_lengths.len(),
88 graph.num_edges(),
89 "edge_lengths length must match num_edges"
90 );
91 let zero = W::Sum::zero();
92 assert!(
93 vertex_weights
94 .iter()
95 .all(|weight| weight.to_sum() >= zero.clone()),
96 "vertex_weights must be non-negative"
97 );
98 assert!(
99 edge_lengths
100 .iter()
101 .all(|length| length.to_sum() >= zero.clone()),
102 "edge_lengths must be non-negative"
103 );
104 assert!(k > 0, "k must be positive");
105 assert!(k <= graph.num_vertices(), "k must not exceed num_vertices");
106 Self {
107 graph,
108 vertex_weights,
109 edge_lengths,
110 k,
111 }
112 }
113
114 pub fn graph(&self) -> &G {
116 &self.graph
117 }
118
119 pub fn vertex_weights(&self) -> &[W] {
121 &self.vertex_weights
122 }
123
124 pub fn edge_lengths(&self) -> &[W] {
126 &self.edge_lengths
127 }
128
129 pub fn k(&self) -> usize {
131 self.k
132 }
133
134 pub fn num_vertices(&self) -> usize {
136 self.graph().num_vertices()
137 }
138
139 pub fn num_edges(&self) -> usize {
141 self.graph().num_edges()
142 }
143
144 pub fn num_centers(&self) -> usize {
146 self.k
147 }
148
149 fn shortest_distances(&self, config: &[usize]) -> Option<Vec<W::Sum>> {
157 let n = self.graph.num_vertices();
158 if config.len() != n || config.iter().any(|&selected| selected > 1) {
159 return None;
160 }
161 let edges = self.graph.edges();
162
163 let mut adj: Vec<Vec<(usize, W::Sum)>> = vec![Vec::new(); n];
164 for (idx, &(u, v)) in edges.iter().enumerate() {
165 let len = self.edge_lengths[idx].to_sum();
166 adj[u].push((v, len.clone()));
167 adj[v].push((u, len));
168 }
169
170 let mut dist: Vec<Option<W::Sum>> = vec![None; n];
172 let mut visited = vec![false; n];
173
174 for (v, &selected) in config.iter().enumerate() {
176 if selected == 1 {
177 dist[v] = Some(W::Sum::zero());
178 }
179 }
180
181 for _ in 0..n {
182 let mut u = None;
184 for v in 0..n {
185 if visited[v] {
186 continue;
187 }
188 if let Some(ref dv) = dist[v] {
189 match u {
190 None => u = Some(v),
191 Some(prev) => {
192 if *dv < dist[prev].clone().unwrap() {
193 u = Some(v);
194 }
195 }
196 }
197 }
198 }
199 let u = match u {
200 Some(v) => v,
201 None => break, };
203 visited[u] = true;
204
205 let du = dist[u].clone().unwrap();
206 for &(next, ref len) in &adj[u] {
207 if visited[next] {
208 continue;
209 }
210 let new_dist = du.clone() + len.clone();
211 let update = match &dist[next] {
212 None => true,
213 Some(d) => new_dist < *d,
214 };
215 if update {
216 dist[next] = Some(new_dist);
217 }
218 }
219 }
220
221 dist.into_iter().collect()
222 }
223}
224
225impl<G, W> Problem for MinMaxMulticenter<G, W>
226where
227 G: Graph + crate::variant::VariantParam,
228 W: WeightElement + crate::variant::VariantParam,
229{
230 const NAME: &'static str = "MinMaxMulticenter";
231 type Value = Min<W::Sum>;
232
233 fn variant() -> Vec<(&'static str, &'static str)> {
234 crate::variant_params![G, W]
235 }
236
237 fn dims(&self) -> Vec<usize> {
238 vec![2; self.graph.num_vertices()]
239 }
240
241 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
242 if config.len() != self.graph.num_vertices() || config.iter().any(|&selected| selected > 1)
243 {
244 return Min(None);
245 }
246
247 let num_selected = config.iter().filter(|&&selected| selected == 1).count();
249 if num_selected != self.k {
250 return Min(None);
251 }
252
253 let distances = match self.shortest_distances(config) {
255 Some(d) => d,
256 None => {
257 return Min(None);
258 }
259 };
260
261 let mut max_wd = W::Sum::zero();
263 for (v, dist) in distances.iter().enumerate() {
264 let wd = self.vertex_weights[v].to_sum() * dist.clone();
265 if wd > max_wd {
266 max_wd = wd;
267 }
268 }
269
270 Min(Some(max_wd))
271 }
272}
273
274crate::declare_variants! {
275 default MinMaxMulticenter<SimpleGraph, i32> => "1.4969^num_vertices",
276 MinMaxMulticenter<SimpleGraph, crate::types::One> => "1.4969^num_vertices",
277}
278
279#[cfg(feature = "example-db")]
280pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
281 vec![crate::example_db::specs::ModelExampleSpec {
282 id: "min_max_multicenter_simplegraph_i32",
283 instance: Box::new(MinMaxMulticenter::new(
284 SimpleGraph::new(
285 6,
286 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 5), (1, 4)],
287 ),
288 vec![1i32; 6],
289 vec![1i32; 7],
290 2,
291 )),
292 optimal_config: vec![0, 1, 0, 0, 1, 0],
293 optimal_value: serde_json::json!(1),
294 }]
295}
296
297#[cfg(test)]
298#[path = "../../unit_tests/models/graph/min_max_multicenter.rs"]
299mod tests;