Skip to main content

problemreductions/models/graph/
multiple_copy_file_allocation.rs

1//! Multiple Copy File Allocation problem implementation.
2//!
3//! The Multiple Copy File Allocation problem asks for a placement of file copies
4//! on graph vertices that minimizes the combined storage and access cost.
5
6use 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/// Multiple Copy File Allocation problem.
37///
38/// Given an undirected graph G = (V, E), a usage value u(v) for each vertex,
39/// and a storage cost s(v) for each vertex, find a subset V' of copy vertices
40/// that minimizes:
41///
42/// Σ_{v ∈ V'} s(v) + Σ_{v ∈ V} u(v) · d(v, V')
43///
44/// where d(v, V') is the shortest-path distance from v to the nearest copy in V'.
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct MultipleCopyFileAllocation {
47    graph: SimpleGraph,
48    usage: Vec<i64>,
49    storage: Vec<i64>,
50}
51
52impl MultipleCopyFileAllocation {
53    /// Create a new Multiple Copy File Allocation instance.
54    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    /// Get a reference to the underlying graph.
73    pub fn graph(&self) -> &SimpleGraph {
74        &self.graph
75    }
76
77    /// Get the usage values.
78    pub fn usage(&self) -> &[i64] {
79        &self.usage
80    }
81
82    /// Get the storage costs.
83    pub fn storage(&self) -> &[i64] {
84        &self.storage
85    }
86
87    /// Get the number of vertices.
88    pub fn num_vertices(&self) -> usize {
89        self.graph.num_vertices()
90    }
91
92    /// Get the number of edges.
93    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    /// Compute the total storage plus access cost for a configuration.
146    ///
147    /// Returns `None` if the configuration is not binary, has the wrong length,
148    /// selects no copy vertices, or leaves some vertex unreachable from every copy.
149    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    /// Check whether a configuration is a valid placement (at least one copy, all reachable).
167    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;