problemreductions/models/graph/
minimum_sum_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: "MinimumSumMulticenter",
16 display_name: "Minimum Sum Multicenter",
17 aliases: &["pmedian"],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i32", &["i32"]),
21 ],
22 module_path: module_path!(),
23 description: "Find K centers minimizing total weighted distance (p-median problem)",
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)]
62pub struct MinimumSumMulticenter<G, W> {
63 graph: G,
65 vertex_weights: Vec<W>,
67 edge_lengths: Vec<W>,
69 k: usize,
71}
72
73impl<G: Graph, W: Clone + Default> MinimumSumMulticenter<G, W> {
74 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 assert!(k > 0, "k must be positive");
92 assert!(k <= graph.num_vertices(), "k must not exceed num_vertices");
93 Self {
94 graph,
95 vertex_weights,
96 edge_lengths,
97 k,
98 }
99 }
100
101 pub fn graph(&self) -> &G {
103 &self.graph
104 }
105
106 pub fn vertex_weights(&self) -> &[W] {
108 &self.vertex_weights
109 }
110
111 pub fn edge_lengths(&self) -> &[W] {
113 &self.edge_lengths
114 }
115
116 pub fn k(&self) -> usize {
118 self.k
119 }
120}
121
122impl<G: Graph, W: WeightElement> MinimumSumMulticenter<G, W> {
123 pub fn num_vertices(&self) -> usize {
125 self.graph().num_vertices()
126 }
127
128 pub fn num_edges(&self) -> usize {
130 self.graph().num_edges()
131 }
132
133 pub fn num_centers(&self) -> usize {
135 self.k
136 }
137
138 fn shortest_distances(&self, config: &[usize]) -> Option<Vec<W::Sum>> {
146 let n = self.graph.num_vertices();
147 let edges = self.graph.edges();
148
149 let mut adj: Vec<Vec<(usize, W::Sum)>> = vec![Vec::new(); n];
150 for (idx, &(u, v)) in edges.iter().enumerate() {
151 let len = self.edge_lengths[idx].to_sum();
152 adj[u].push((v, len.clone()));
153 adj[v].push((u, len));
154 }
155
156 let mut dist: Vec<Option<W::Sum>> = vec![None; n];
158 let mut visited = vec![false; n];
159
160 for (v, &selected) in config.iter().enumerate() {
162 if selected == 1 {
163 dist[v] = Some(W::Sum::zero());
164 }
165 }
166
167 for _ in 0..n {
168 let mut u = None;
170 for v in 0..n {
171 if visited[v] {
172 continue;
173 }
174 if let Some(ref dv) = dist[v] {
175 match u {
176 None => u = Some(v),
177 Some(prev) => {
178 if *dv < dist[prev].clone().unwrap() {
179 u = Some(v);
180 }
181 }
182 }
183 }
184 }
185 let u = match u {
186 Some(v) => v,
187 None => break, };
189 visited[u] = true;
190
191 let du = dist[u].clone().unwrap();
192 for &(next, ref len) in &adj[u] {
193 if visited[next] {
194 continue;
195 }
196 let new_dist = du.clone() + len.clone();
197 let update = match &dist[next] {
198 None => true,
199 Some(d) => new_dist < *d,
200 };
201 if update {
202 dist[next] = Some(new_dist);
203 }
204 }
205 }
206
207 dist.into_iter().collect()
208 }
209}
210
211impl<G, W> Problem for MinimumSumMulticenter<G, W>
212where
213 G: Graph + crate::variant::VariantParam,
214 W: WeightElement + crate::variant::VariantParam,
215{
216 const NAME: &'static str = "MinimumSumMulticenter";
217 type Value = Min<W::Sum>;
218
219 fn variant() -> Vec<(&'static str, &'static str)> {
220 crate::variant_params![G, W]
221 }
222
223 fn dims(&self) -> Vec<usize> {
224 vec![2; self.graph.num_vertices()]
225 }
226
227 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
228 let num_selected: usize = config.iter().sum();
230 if num_selected != self.k {
231 return Min(None);
232 }
233
234 let distances = match self.shortest_distances(config) {
236 Some(d) => d,
237 None => return Min(None),
238 };
239
240 let mut total = W::Sum::zero();
242 for (v, dist) in distances.iter().enumerate() {
243 total += self.vertex_weights[v].to_sum() * dist.clone();
244 }
245
246 Min(Some(total))
247 }
248}
249
250crate::declare_variants! {
251 default MinimumSumMulticenter<SimpleGraph, i32> => "2^num_vertices",
252}
253
254#[cfg(feature = "example-db")]
255pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
256 vec![crate::example_db::specs::ModelExampleSpec {
257 id: "minimum_sum_multicenter_simplegraph_i32",
258 instance: Box::new(MinimumSumMulticenter::new(
259 SimpleGraph::new(
260 7,
261 vec![
262 (0, 1),
263 (1, 2),
264 (2, 3),
265 (3, 4),
266 (4, 5),
267 (5, 6),
268 (0, 6),
269 (2, 5),
270 ],
271 ),
272 vec![1i32; 7],
273 vec![1i32; 8],
274 2,
275 )),
276 optimal_config: vec![0, 0, 1, 0, 0, 1, 0],
277 optimal_value: serde_json::json!(6),
278 }]
279}
280
281#[cfg(test)]
282#[path = "../../unit_tests/models/graph/minimum_sum_multicenter.rs"]
283mod tests;