1use 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)]
57#[serde(try_from = "MinMaxMulticenterData<G, W>")]
58#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>"))]
59pub struct MinMaxMulticenter<G, W: WeightElement> {
60 graph: G,
62 vertex_weights: Vec<W>,
64 edge_lengths: Vec<W>,
66 k: usize,
68}
69
70#[derive(Deserialize)]
71#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>"))]
72struct MinMaxMulticenterData<G, W: WeightElement> {
73 graph: G,
74 vertex_weights: Vec<W>,
75 edge_lengths: Vec<W>,
76 k: usize,
77}
78
79impl<G, W> TryFrom<MinMaxMulticenterData<G, W>> for MinMaxMulticenter<G, W>
80where
81 G: Graph,
82 W: WeightElement,
83{
84 type Error = crate::registry::ConstructionError;
85
86 fn try_from(data: MinMaxMulticenterData<G, W>) -> Result<Self, Self::Error> {
87 Self::try_new(data.graph, data.vertex_weights, data.edge_lengths, data.k)
88 }
89}
90
91macro_rules! min_max_multicenter_create_spec {
92 ($name:ident, $weight:ty, $one:expr $(, $weights:ident, $edge_weights:ident)?) => {
93 #[derive(Debug, Deserialize, crate::CreateSpec)]
94 struct $name {
95 #[create(codec = "edge-list")]
96 graph: Vec<(usize, usize)>,
97 num_vertices: Option<usize>,
98 $(
99 #[create(codec = "comma-separated")]
100 $weights: Option<Vec<$weight>>,
101 )?
102 $(
103 #[create(codec = "comma-separated")]
104 $edge_weights: Option<Vec<$weight>>,
105 )?
106 k: usize,
107 }
108
109 impl TryFrom<$name> for MinMaxMulticenter<SimpleGraph, $weight> {
110 type Error = crate::registry::ConstructionError;
111
112 fn try_from(spec: $name) -> Result<Self, Self::Error> {
113 let graph = simple_graph_from_create(spec.graph, spec.num_vertices)?;
114 let vertex_weights = { $(if let Some(value) = spec.$weights { value } else)? { vec![$one; graph.num_vertices()] } };
115 let edge_lengths = { $(if let Some(value) = spec.$edge_weights { value } else)? { vec![$one; graph.num_edges()] } };
116 Self::try_new(graph, vertex_weights, edge_lengths, spec.k)
117 }
118 }
119 };
120}
121
122min_max_multicenter_create_spec!(
123 MinMaxMulticenterI64CreateSpec,
124 i64,
125 1,
126 weights,
127 edge_weights
128);
129min_max_multicenter_create_spec!(MinMaxMulticenterOneCreateSpec, One, One);
130
131fn simple_graph_from_create(
132 edges: Vec<(usize, usize)>,
133 num_vertices: Option<usize>,
134) -> Result<SimpleGraph, crate::registry::ConstructionError> {
135 if edges.is_empty() && num_vertices.is_none() {
136 return Err("num_vertices is required for an empty graph"
137 .to_string()
138 .into());
139 }
140 for (index, &(u, v)) in edges.iter().enumerate() {
141 if u == v {
142 return Err(format!("graph edge {index} is a self-loop at vertex {u}").into());
143 }
144 }
145 let inferred = edges
146 .iter()
147 .flat_map(|&(u, v)| [u, v])
148 .max()
149 .map(|vertex| vertex.checked_add(1).ok_or("vertex count overflows usize"))
150 .transpose()?
151 .unwrap_or(0);
152 let num_vertices = num_vertices.unwrap_or(inferred);
153 if num_vertices < inferred {
154 return Err(format!("num_vertices {num_vertices} is too small for graph endpoints; need at least {inferred}").into());
155 }
156 Ok(SimpleGraph::new(num_vertices, edges))
157}
158
159impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
160 pub fn new(graph: G, vertex_weights: Vec<W>, edge_lengths: Vec<W>, k: usize) -> Self {
168 Self::try_new(graph, vertex_weights, edge_lengths, k)
169 .unwrap_or_else(|error| panic!("{error}"))
170 }
171
172 fn try_new(
173 graph: G,
174 vertex_weights: Vec<W>,
175 edge_lengths: Vec<W>,
176 k: usize,
177 ) -> Result<Self, crate::registry::ConstructionError> {
178 if vertex_weights.len() != graph.num_vertices() {
179 return Err(crate::registry::ConstructionError::length_mismatch(
180 "vertex_weights",
181 vertex_weights.len(),
182 graph.num_vertices(),
183 ));
184 }
185 if edge_lengths.len() != graph.num_edges() {
186 return Err(crate::registry::ConstructionError::length_mismatch(
187 "edge_lengths",
188 edge_lengths.len(),
189 graph.num_edges(),
190 ));
191 }
192 let zero = W::Sum::zero();
193 if !vertex_weights
194 .iter()
195 .all(|weight| weight.to_sum() >= zero.clone())
196 {
197 return Err("vertex_weights must be non-negative".into());
198 }
199 if !edge_lengths
200 .iter()
201 .all(|length| length.to_sum() >= zero.clone())
202 {
203 return Err("edge_lengths must be non-negative".into());
204 }
205 if k == 0 {
206 return Err("k must be positive".into());
207 }
208 if k > graph.num_vertices() {
209 return Err("k must not exceed num_vertices".into());
210 }
211 Ok(Self {
212 graph,
213 vertex_weights,
214 edge_lengths,
215 k,
216 })
217 }
218
219 pub fn graph(&self) -> &G {
221 &self.graph
222 }
223
224 pub fn vertex_weights(&self) -> &[W] {
226 &self.vertex_weights
227 }
228
229 pub fn edge_lengths(&self) -> &[W] {
231 &self.edge_lengths
232 }
233
234 pub fn k(&self) -> usize {
236 self.k
237 }
238
239 pub fn max_numeric_magnitude_bits(&self) -> u64 {
241 crate::types::max_numeric_magnitude_bits(
242 self.vertex_weights
243 .iter()
244 .chain(&self.edge_lengths)
245 .map(|value| value.to_sum()),
246 )
247 }
248
249 pub fn num_vertices(&self) -> usize {
251 self.graph().num_vertices()
252 }
253
254 pub fn num_edges(&self) -> usize {
256 self.graph().num_edges()
257 }
258
259 pub fn num_centers(&self) -> usize {
261 self.k
262 }
263
264 fn shortest_distances(
272 &self,
273 config: &[bool],
274 ) -> Result<Option<Vec<W::Sum>>, crate::traits::EvaluationError> {
275 let n = self.graph.num_vertices();
276 let edges = self.graph.edges();
277
278 let mut adj: Vec<Vec<(usize, W::Sum)>> = vec![Vec::new(); n];
279 for (idx, &(u, v)) in edges.iter().enumerate() {
280 let len = self.edge_lengths[idx].to_sum();
281 adj[u].push((v, len.clone()));
282 adj[v].push((u, len));
283 }
284
285 let mut dist: Vec<Option<W::Sum>> = vec![None; n];
287 let mut visited = vec![false; n];
288
289 for (v, &selected) in config.iter().enumerate() {
291 if selected {
292 dist[v] = Some(W::Sum::zero());
293 }
294 }
295
296 for _ in 0..n {
297 let mut u = None;
299 for v in 0..n {
300 if visited[v] {
301 continue;
302 }
303 if let Some(ref dv) = dist[v] {
304 match u {
305 None => u = Some(v),
306 Some(prev) => {
307 if *dv < dist[prev].clone().unwrap() {
308 u = Some(v);
309 }
310 }
311 }
312 }
313 }
314 let u = match u {
315 Some(v) => v,
316 None => break, };
318 visited[u] = true;
319
320 let du = dist[u].clone().unwrap();
321 for &(next, ref len) in &adj[u] {
322 if visited[next] {
323 continue;
324 }
325 let new_dist = W::checked_add_to_sum(
326 du.clone(),
327 len.clone(),
328 "adding min-max multicenter path lengths",
329 )?;
330 let update = match &dist[next] {
331 None => true,
332 Some(d) => new_dist < *d,
333 };
334 if update {
335 dist[next] = Some(new_dist);
336 }
337 }
338 }
339
340 Ok(dist.into_iter().collect())
341 }
342}
343
344impl<G, W> Problem for MinMaxMulticenter<G, W>
345where
346 G: Graph + crate::variant::VariantParam,
347 W: WeightElement + crate::variant::VariantParam,
348{
349 const NAME: &'static str = "MinMaxMulticenter";
350 type Solution = Vec<bool>;
351 type Value = Min<W::Sum>;
352
353 crate::problem_parameters![
354 ("max_numeric_magnitude_bits", max_numeric_magnitude_bits),
355 ("num_edges", num_edges),
356 ("num_vertices", num_vertices),
357 ];
358
359 fn variant() -> Vec<(&'static str, &'static str)> {
360 crate::variant_params![G, W]
361 }
362
363 fn evaluate(
364 &self,
365 config: &Self::Solution,
366 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
367 Ok({
368 if config.len() != self.graph.num_vertices() {
369 return Err(crate::traits::EvaluationError::InvalidConfiguration(
370 "center-selection length does not match the graph vertices".into(),
371 ));
372 }
373
374 let num_selected = config.iter().filter(|&&selected| selected).count();
376 if num_selected != self.k {
377 return Ok(Min(None));
378 }
379
380 let distances = match self.shortest_distances(config)? {
382 Some(d) => d,
383 None => {
384 return Ok(Min(None));
385 }
386 };
387
388 let mut max_wd = W::Sum::zero();
390 for (v, dist) in distances.iter().enumerate() {
391 let wd = W::checked_mul_sum(
392 self.vertex_weights[v].to_sum(),
393 dist.clone(),
394 "multiplying min-max multicenter vertex weight by distance",
395 )?;
396 if wd > max_wd {
397 max_wd = wd;
398 }
399 }
400
401 Min(Some(max_wd))
402 })
403 }
404}
405
406impl<G, W> crate::solvers::BruteForceProblem for MinMaxMulticenter<G, W>
407where
408 G: Graph + crate::variant::VariantParam,
409 W: WeightElement + crate::variant::VariantParam,
410{
411 fn dimensions(&self) -> Vec<usize> {
412 vec![2; self.graph.num_vertices()]
413 }
414}
415
416crate::declare_variants! {
417 default MinMaxMulticenter<SimpleGraph, i64> => "1.4969^num_vertices" create MinMaxMulticenterI64CreateSpec,
418 MinMaxMulticenter<SimpleGraph, One> => "1.4969^num_vertices" create MinMaxMulticenterOneCreateSpec,
419}
420
421crate::register_brute_force! {
422 MinMaxMulticenter<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
423 MinMaxMulticenter<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
424}
425
426#[cfg(feature = "example-db")]
427pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
428 vec![crate::example_db::specs::ModelExampleSpec {
429 id: "min_max_multicenter_simplegraph",
430 instance: Box::new(MinMaxMulticenter::new(
431 SimpleGraph::new(
432 6,
433 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 5), (1, 4)],
434 ),
435 vec![1i64; 6],
436 vec![1i64; 7],
437 2,
438 )),
439 optimal_config: serde_json::json!(vec![false, true, false, false, true, false]),
440 optimal_value: serde_json::json!(1),
441 }]
442}
443
444#[cfg(test)]
445#[path = "../../unit_tests/models/graph/min_max_multicenter.rs"]
446mod tests;
447
448crate::decision_problem_meta!(MinMaxMulticenter<SimpleGraph, One>, "DecisionMinMaxMulticenter");
449crate::register_decision_variant!(
450 MinMaxMulticenter<SimpleGraph, One>, "DecisionMinMaxMulticenter", "1.4969^num_vertices", &[],
451 "Does a feasible solution have objective value <= the bound?",
452 category: crate::registry::ProblemCategory::Graph,
453 dims: [
454 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
455 VariantDimension::new("weight", "One", &["One"]),
456 ],
457 fields: [
458 crate::registry::FieldInfo { name: "graph", type_name: "Vec<(usize,usize)>", description: "Graph edges as comma-separated vertex pairs." },
459 crate::registry::FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices, including isolated vertices." },
460 crate::registry::FieldInfo { name: "k", type_name: "usize", description: "Number of centers." },
461 crate::registry::FieldInfo { name: "bound", type_name: "i64", description: "Accept objective values <= this bound" },
462 ],
463 decode: |_, indices: Vec<usize>| crate::config::config_to_bits(&indices)
464);
465
466#[cfg(feature = "example-db")]
467pub(crate) fn decision_canonical_rule_example_specs(
468) -> Vec<crate::example_db::specs::RuleExampleSpec> {
469 vec![crate::example_db::specs::RuleExampleSpec {
470 id: "decision_min_max_multicenter_to_min_max_multicenter",
471 build: || {
472 let source = crate::models::decision::Decision::new(
473 MinMaxMulticenter::new(
474 SimpleGraph::new(
475 6,
476 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 5), (1, 4)],
477 ),
478 vec![crate::types::One; 6],
479 vec![crate::types::One; 7],
480 2,
481 ),
482 1,
483 );
484 let witness = serde_json::json!(vec![false, true, false, false, true, false]);
485 crate::example_db::specs::rule_example_with_witness::<
486 _,
487 MinMaxMulticenter<SimpleGraph, One>,
488 >(
489 source,
490 crate::export::SolutionPair {
491 source_config: witness.clone(),
492 target_config: witness,
493 },
494 )
495 },
496 }]
497}