problemreductions/models/graph/
min_max_multicenter.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::{Min, One, 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", "i64", &["i64", "One"]),
21 ],
22 category: crate::registry::ProblemCategory::Graph,
23 module_path: module_path!(),
24 description: "Find K centers minimizing the maximum weighted distance from any vertex to its nearest center (vertex p-center)",
25 fields: MinMaxMulticenterI64CreateSpec::FIELDS,
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MinMaxMulticenter<G, W: WeightElement> {
58 graph: G,
60 vertex_weights: Vec<W>,
62 edge_lengths: Vec<W>,
64 k: usize,
66}
67
68macro_rules! min_max_multicenter_create_spec {
69 ($name:ident, $weight:ty, $one:expr $(, $weights:ident, $edge_weights:ident)?) => {
70 #[derive(Debug, Deserialize, crate::CreateSpec)]
71 struct $name {
72 #[create(codec = "edge-list")]
73 graph: Vec<(usize, usize)>,
74 num_vertices: Option<usize>,
75 $(
76 #[create(codec = "comma-separated")]
77 $weights: Option<Vec<$weight>>,
78 )?
79 $(
80 #[create(codec = "comma-separated")]
81 $edge_weights: Option<Vec<$weight>>,
82 )?
83 k: usize,
84 }
85
86 impl TryFrom<$name> for MinMaxMulticenter<SimpleGraph, $weight> {
87 type Error = crate::registry::ConstructionError;
88
89 fn try_from(spec: $name) -> Result<Self, Self::Error> {
90 let graph = simple_graph_from_create(spec.graph, spec.num_vertices)?;
91 let vertex_weights = { $(if let Some(value) = spec.$weights { value } else)? { vec![$one; graph.num_vertices()] } };
92 if vertex_weights.len() != graph.num_vertices() {
93 return Err(format!(
94 "weights has length {}, expected {}",
95 vertex_weights.len(),
96 graph.num_vertices()
97 )
98 .into());
99 }
100 let edge_lengths = { $(if let Some(value) = spec.$edge_weights { value } else)? { vec![$one; graph.num_edges()] } };
101 if edge_lengths.len() != graph.num_edges() {
102 return Err(format!(
103 "edge_weights has length {}, expected {}",
104 edge_lengths.len(),
105 graph.num_edges()
106 )
107 .into());
108 }
109 let zero = <$weight as WeightElement>::Sum::zero();
110 if vertex_weights
111 .iter()
112 .any(|weight| weight.to_sum() < zero.clone())
113 {
114 return Err("weights must be non-negative".to_string().into());
115 }
116 if edge_lengths
117 .iter()
118 .any(|weight| weight.to_sum() < zero.clone())
119 {
120 return Err("edge_weights must be non-negative".to_string().into());
121 }
122 if spec.k == 0 || spec.k > graph.num_vertices() {
123 return Err(format!("k must be between 1 and {}", graph.num_vertices()).into());
124 }
125 Ok(Self::new(graph, vertex_weights, edge_lengths, spec.k))
126 }
127 }
128 };
129}
130
131min_max_multicenter_create_spec!(
132 MinMaxMulticenterI64CreateSpec,
133 i64,
134 1,
135 weights,
136 edge_weights
137);
138min_max_multicenter_create_spec!(MinMaxMulticenterOneCreateSpec, One, One);
139
140fn simple_graph_from_create(
141 edges: Vec<(usize, usize)>,
142 num_vertices: Option<usize>,
143) -> Result<SimpleGraph, crate::registry::ConstructionError> {
144 if edges.is_empty() && num_vertices.is_none() {
145 return Err("num_vertices is required for an empty graph"
146 .to_string()
147 .into());
148 }
149 for (index, &(u, v)) in edges.iter().enumerate() {
150 if u == v {
151 return Err(format!("graph edge {index} is a self-loop at vertex {u}").into());
152 }
153 }
154 let inferred = edges
155 .iter()
156 .flat_map(|&(u, v)| [u, v])
157 .max()
158 .map(|vertex| vertex.checked_add(1).ok_or("vertex count overflows usize"))
159 .transpose()?
160 .unwrap_or(0);
161 let num_vertices = num_vertices.unwrap_or(inferred);
162 if num_vertices < inferred {
163 return Err(format!("num_vertices {num_vertices} is too small for graph endpoints; need at least {inferred}").into());
164 }
165 Ok(SimpleGraph::new(num_vertices, edges))
166}
167
168impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
169 pub fn new(graph: G, vertex_weights: Vec<W>, edge_lengths: Vec<W>, k: usize) -> Self {
177 assert_eq!(
178 vertex_weights.len(),
179 graph.num_vertices(),
180 "vertex_weights length must match num_vertices"
181 );
182 assert_eq!(
183 edge_lengths.len(),
184 graph.num_edges(),
185 "edge_lengths length must match num_edges"
186 );
187 let zero = W::Sum::zero();
188 assert!(
189 vertex_weights
190 .iter()
191 .all(|weight| weight.to_sum() >= zero.clone()),
192 "vertex_weights must be non-negative"
193 );
194 assert!(
195 edge_lengths
196 .iter()
197 .all(|length| length.to_sum() >= zero.clone()),
198 "edge_lengths must be non-negative"
199 );
200 assert!(k > 0, "k must be positive");
201 assert!(k <= graph.num_vertices(), "k must not exceed num_vertices");
202 Self {
203 graph,
204 vertex_weights,
205 edge_lengths,
206 k,
207 }
208 }
209
210 pub fn graph(&self) -> &G {
212 &self.graph
213 }
214
215 pub fn vertex_weights(&self) -> &[W] {
217 &self.vertex_weights
218 }
219
220 pub fn edge_lengths(&self) -> &[W] {
222 &self.edge_lengths
223 }
224
225 pub fn k(&self) -> usize {
227 self.k
228 }
229
230 pub fn num_vertices(&self) -> usize {
232 self.graph().num_vertices()
233 }
234
235 pub fn num_edges(&self) -> usize {
237 self.graph().num_edges()
238 }
239
240 pub fn num_centers(&self) -> usize {
242 self.k
243 }
244
245 fn shortest_distances(&self, config: &[bool]) -> Option<Vec<W::Sum>> {
253 let n = self.graph.num_vertices();
254 if config.len() != n {
255 return None;
256 }
257 let edges = self.graph.edges();
258
259 let mut adj: Vec<Vec<(usize, W::Sum)>> = vec![Vec::new(); n];
260 for (idx, &(u, v)) in edges.iter().enumerate() {
261 let len = self.edge_lengths[idx].to_sum();
262 adj[u].push((v, len.clone()));
263 adj[v].push((u, len));
264 }
265
266 let mut dist: Vec<Option<W::Sum>> = vec![None; n];
268 let mut visited = vec![false; n];
269
270 for (v, &selected) in config.iter().enumerate() {
272 if selected {
273 dist[v] = Some(W::Sum::zero());
274 }
275 }
276
277 for _ in 0..n {
278 let mut u = None;
280 for v in 0..n {
281 if visited[v] {
282 continue;
283 }
284 if let Some(ref dv) = dist[v] {
285 match u {
286 None => u = Some(v),
287 Some(prev) => {
288 if *dv < dist[prev].clone().unwrap() {
289 u = Some(v);
290 }
291 }
292 }
293 }
294 }
295 let u = match u {
296 Some(v) => v,
297 None => break, };
299 visited[u] = true;
300
301 let du = dist[u].clone().unwrap();
302 for &(next, ref len) in &adj[u] {
303 if visited[next] {
304 continue;
305 }
306 let new_dist = du.clone() + len.clone();
307 let update = match &dist[next] {
308 None => true,
309 Some(d) => new_dist < *d,
310 };
311 if update {
312 dist[next] = Some(new_dist);
313 }
314 }
315 }
316
317 dist.into_iter().collect()
318 }
319}
320
321impl<G, W> Problem for MinMaxMulticenter<G, W>
322where
323 G: Graph + crate::variant::VariantParam,
324 W: WeightElement + crate::variant::VariantParam,
325{
326 const NAME: &'static str = "MinMaxMulticenter";
327 type Solution = Vec<bool>;
328 type Value = Min<W::Sum>;
329
330 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
331
332 fn variant() -> Vec<(&'static str, &'static str)> {
333 crate::variant_params![G, W]
334 }
335
336 fn evaluate(
337 &self,
338 config: &Self::Solution,
339 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
340 Ok({
341 if config.len() != self.graph.num_vertices() {
342 return Err(crate::traits::EvaluationError::InvalidConfiguration(
343 "center-selection length does not match the graph vertices".into(),
344 ));
345 }
346
347 let num_selected = config.iter().filter(|&&selected| selected).count();
349 if num_selected != self.k {
350 return Ok(Min(None));
351 }
352
353 let distances = match self.shortest_distances(config) {
355 Some(d) => d,
356 None => {
357 return Ok(Min(None));
358 }
359 };
360
361 let mut max_wd = W::Sum::zero();
363 for (v, dist) in distances.iter().enumerate() {
364 let wd = W::checked_mul_sum(
365 self.vertex_weights[v].to_sum(),
366 dist.clone(),
367 "multiplying min-max multicenter vertex weight by distance",
368 )?;
369 if wd > max_wd {
370 max_wd = wd;
371 }
372 }
373
374 Min(Some(max_wd))
375 })
376 }
377}
378
379impl<G, W> crate::solvers::BruteForceProblem for MinMaxMulticenter<G, W>
380where
381 G: Graph + crate::variant::VariantParam,
382 W: WeightElement + crate::variant::VariantParam,
383{
384 fn dimensions(&self) -> Vec<usize> {
385 vec![2; self.graph.num_vertices()]
386 }
387}
388
389crate::declare_variants! {
390 default MinMaxMulticenter<SimpleGraph, i64> => "1.4969^num_vertices" create MinMaxMulticenterI64CreateSpec,
391 MinMaxMulticenter<SimpleGraph, One> => "1.4969^num_vertices" create MinMaxMulticenterOneCreateSpec,
392}
393
394crate::register_brute_force! {
395 MinMaxMulticenter<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
396 MinMaxMulticenter<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
397}
398
399#[cfg(feature = "example-db")]
400pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
401 vec![crate::example_db::specs::ModelExampleSpec {
402 id: "min_max_multicenter_simplegraph",
403 instance: Box::new(MinMaxMulticenter::new(
404 SimpleGraph::new(
405 6,
406 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 5), (1, 4)],
407 ),
408 vec![1i64; 6],
409 vec![1i64; 7],
410 2,
411 )),
412 optimal_config: serde_json::json!(vec![false, true, false, false, true, false]),
413 optimal_value: serde_json::json!(1),
414 }]
415}
416
417#[cfg(test)]
418#[path = "../../unit_tests/models/graph/min_max_multicenter.rs"]
419mod tests;