problemreductions/models/graph/
multiple_copy_file_allocation.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Serialize};
11use std::collections::VecDeque;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MultipleCopyFileAllocation",
16 display_name: "Multiple Copy File Allocation",
17 aliases: &[],
18 dimensions: &[],
19 module_path: module_path!(),
20 description: "Place file copies on graph vertices to minimize total storage plus access cost",
21 fields: &[
22 FieldInfo { name: "graph", type_name: "SimpleGraph", description: "The network graph G=(V,E)" },
23 FieldInfo { name: "usage", type_name: "Vec<i64>", description: "Usage frequencies u(v) for each vertex" },
24 FieldInfo { name: "storage", type_name: "Vec<i64>", description: "Storage costs s(v) for placing a copy at each vertex" },
25 ],
26 }
27}
28
29inventory::submit! {
30 ProblemSizeFieldEntry {
31 name: "MultipleCopyFileAllocation",
32 fields: &["num_vertices", "num_edges"],
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct MultipleCopyFileAllocation {
47 graph: SimpleGraph,
48 usage: Vec<i64>,
49 storage: Vec<i64>,
50}
51
52impl MultipleCopyFileAllocation {
53 pub fn new(graph: SimpleGraph, usage: Vec<i64>, storage: Vec<i64>) -> Self {
55 assert_eq!(
56 usage.len(),
57 graph.num_vertices(),
58 "usage length must match graph num_vertices"
59 );
60 assert_eq!(
61 storage.len(),
62 graph.num_vertices(),
63 "storage length must match graph num_vertices"
64 );
65 Self {
66 graph,
67 usage,
68 storage,
69 }
70 }
71
72 pub fn graph(&self) -> &SimpleGraph {
74 &self.graph
75 }
76
77 pub fn usage(&self) -> &[i64] {
79 &self.usage
80 }
81
82 pub fn storage(&self) -> &[i64] {
84 &self.storage
85 }
86
87 pub fn num_vertices(&self) -> usize {
89 self.graph.num_vertices()
90 }
91
92 pub fn num_edges(&self) -> usize {
94 self.graph.num_edges()
95 }
96
97 fn selected_vertices(&self, config: &[usize]) -> Option<Vec<usize>> {
98 if config.len() != self.graph.num_vertices() {
99 return None;
100 }
101
102 let mut selected = Vec::new();
103 for (vertex, &value) in config.iter().enumerate() {
104 match value {
105 0 => {}
106 1 => selected.push(vertex),
107 _ => return None,
108 }
109 }
110
111 if selected.is_empty() {
112 None
113 } else {
114 Some(selected)
115 }
116 }
117
118 fn shortest_distances(&self, selected: &[usize]) -> Option<Vec<usize>> {
119 let n = self.graph.num_vertices();
120 let mut distances = vec![usize::MAX; n];
121 let mut queue = VecDeque::new();
122
123 for &vertex in selected {
124 distances[vertex] = 0;
125 queue.push_back(vertex);
126 }
127
128 while let Some(vertex) = queue.pop_front() {
129 let next_distance = distances[vertex] + 1;
130 for neighbor in self.graph.neighbors(vertex) {
131 if distances[neighbor] == usize::MAX {
132 distances[neighbor] = next_distance;
133 queue.push_back(neighbor);
134 }
135 }
136 }
137
138 if distances.contains(&usize::MAX) {
139 None
140 } else {
141 Some(distances)
142 }
143 }
144
145 pub fn total_cost(&self, config: &[usize]) -> Option<i64> {
150 let selected = self.selected_vertices(config)?;
151 let distances = self.shortest_distances(&selected)?;
152
153 let storage_cost = selected
154 .into_iter()
155 .map(|vertex| self.storage[vertex])
156 .sum::<i64>();
157 let access_cost = distances
158 .into_iter()
159 .enumerate()
160 .map(|(vertex, distance)| self.usage[vertex] * distance as i64)
161 .sum::<i64>();
162
163 Some(storage_cost + access_cost)
164 }
165
166 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
168 self.total_cost(config).is_some()
169 }
170}
171
172impl Problem for MultipleCopyFileAllocation {
173 const NAME: &'static str = "MultipleCopyFileAllocation";
174 type Value = Min<i64>;
175
176 fn variant() -> Vec<(&'static str, &'static str)> {
177 crate::variant_params![]
178 }
179
180 fn dims(&self) -> Vec<usize> {
181 vec![2; self.graph.num_vertices()]
182 }
183
184 fn evaluate(&self, config: &[usize]) -> Min<i64> {
185 Min(self.total_cost(config))
186 }
187}
188
189#[cfg(feature = "example-db")]
190pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
191 vec![crate::example_db::specs::ModelExampleSpec {
192 id: "multiple_copy_file_allocation",
193 instance: Box::new(MultipleCopyFileAllocation::new(
194 SimpleGraph::new(6, vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]),
195 vec![5, 1, 1, 1, 1, 5],
196 vec![6, 2, 6, 6, 2, 6],
197 )),
198 optimal_config: vec![0, 1, 0, 0, 1, 0],
199 optimal_value: serde_json::json!(16),
200 }]
201}
202
203crate::declare_variants! {
204 default MultipleCopyFileAllocation => "2^num_vertices",
205}
206
207#[cfg(test)]
208#[path = "../../unit_tests/models/graph/multiple_copy_file_allocation.rs"]
209mod tests;