Skip to main content

problemreductions/models/misc/
dynamic_storage_allocation.rs

1//! Dynamic Storage Allocation problem implementation.
2//!
3//! Given items each with arrival time, departure time, and size, plus a
4//! memory size D, determine whether each item can be assigned a starting
5//! address such that every item fits within [0, D-1] and no two
6//! time-overlapping items share memory addresses.
7
8use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
9use crate::traits::Problem;
10use crate::types::Or;
11use serde::de::Error as _;
12use serde::{Deserialize, Deserializer, Serialize};
13
14inventory::submit! {
15    ProblemSchemaEntry {
16        name: "DynamicStorageAllocation",
17        display_name: "Dynamic Storage Allocation",
18        aliases: &[],
19        dimensions: &[],
20        module_path: module_path!(),
21        description: "Assign starting addresses for items with time intervals and sizes within bounded memory",
22        fields: &[
23            FieldInfo { name: "items", type_name: "Vec<(usize, usize, usize)>", description: "Items as (arrival, departure, size) tuples" },
24            FieldInfo { name: "memory_size", type_name: "usize", description: "Total memory size D" },
25        ],
26    }
27}
28
29inventory::submit! {
30    ProblemSizeFieldEntry {
31        name: "DynamicStorageAllocation",
32        fields: &["num_items", "memory_size"],
33    }
34}
35
36/// Dynamic Storage Allocation problem.
37///
38/// Each item `a` has arrival time `r(a)`, departure time `d(a)`, and size `s(a)`.
39/// The goal is to find a starting address `σ(a) ∈ {0, ..., D - s(a)}` for each item
40/// such that time-overlapping items do not overlap in memory.
41#[derive(Debug, Clone, Serialize)]
42pub struct DynamicStorageAllocation {
43    items: Vec<(usize, usize, usize)>,
44    memory_size: usize,
45}
46
47impl DynamicStorageAllocation {
48    fn validate_inputs(items: &[(usize, usize, usize)], memory_size: usize) -> Result<(), String> {
49        if items.is_empty() {
50            return Err("DynamicStorageAllocation requires at least one item".to_string());
51        }
52        if memory_size == 0 {
53            return Err("DynamicStorageAllocation requires a positive memory_size".to_string());
54        }
55        for (i, &(arrival, departure, size)) in items.iter().enumerate() {
56            if size == 0 {
57                return Err(format!("Item {i} has zero size; all sizes must be >= 1"));
58            }
59            if departure <= arrival {
60                return Err(format!(
61                    "Item {i} has departure ({departure}) <= arrival ({arrival}); departure must be strictly greater"
62                ));
63            }
64            if size > memory_size {
65                return Err(format!(
66                    "Item {i} has size ({size}) > memory_size ({memory_size}); every item must fit in memory"
67                ));
68            }
69        }
70        Ok(())
71    }
72
73    /// Try to create a new `DynamicStorageAllocation` instance.
74    pub fn try_new(items: Vec<(usize, usize, usize)>, memory_size: usize) -> Result<Self, String> {
75        Self::validate_inputs(&items, memory_size)?;
76        Ok(Self { items, memory_size })
77    }
78
79    /// Create a new `DynamicStorageAllocation` instance.
80    ///
81    /// # Panics
82    ///
83    /// Panics if any item has zero size, departure <= arrival, or size > memory_size.
84    pub fn new(items: Vec<(usize, usize, usize)>, memory_size: usize) -> Self {
85        Self::try_new(items, memory_size).unwrap_or_else(|message| panic!("{message}"))
86    }
87
88    /// The items as `(arrival, departure, size)` tuples.
89    pub fn items(&self) -> &[(usize, usize, usize)] {
90        &self.items
91    }
92
93    /// The total memory size D.
94    pub fn memory_size(&self) -> usize {
95        self.memory_size
96    }
97
98    /// The number of items.
99    pub fn num_items(&self) -> usize {
100        self.items.len()
101    }
102}
103
104#[derive(Deserialize)]
105struct DynamicStorageAllocationData {
106    items: Vec<(usize, usize, usize)>,
107    memory_size: usize,
108}
109
110impl<'de> Deserialize<'de> for DynamicStorageAllocation {
111    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
112    where
113        D: Deserializer<'de>,
114    {
115        let data = DynamicStorageAllocationData::deserialize(deserializer)?;
116        Self::try_new(data.items, data.memory_size).map_err(D::Error::custom)
117    }
118}
119
120impl Problem for DynamicStorageAllocation {
121    const NAME: &'static str = "DynamicStorageAllocation";
122    type Value = Or;
123
124    fn variant() -> Vec<(&'static str, &'static str)> {
125        crate::variant_params![]
126    }
127
128    fn dims(&self) -> Vec<usize> {
129        self.items
130            .iter()
131            .map(|&(_, _, s)| self.memory_size - s + 1)
132            .collect()
133    }
134
135    fn evaluate(&self, config: &[usize]) -> Or {
136        Or({
137            if config.len() != self.num_items() {
138                return Or(false);
139            }
140
141            // Check each item fits within memory
142            for (i, &(_, _, size)) in self.items.iter().enumerate() {
143                let start = config[i];
144                if start + size > self.memory_size {
145                    return Or(false);
146                }
147            }
148
149            // Check all pairs of time-overlapping items for memory non-overlap
150            for (i, &(r_i, d_i, s_i)) in self.items.iter().enumerate() {
151                let sigma_i = config[i];
152                for (j, &(r_j, d_j, s_j)) in self.items.iter().enumerate().skip(i + 1) {
153                    // Time overlap: r_i < d_j AND r_j < d_i
154                    if r_i < d_j && r_j < d_i {
155                        let sigma_j = config[j];
156                        // Memory overlap: NOT (sigma_i + s_i <= sigma_j OR sigma_j + s_j <= sigma_i)
157                        let no_memory_overlap =
158                            sigma_i + s_i <= sigma_j || sigma_j + s_j <= sigma_i;
159                        if !no_memory_overlap {
160                            return Or(false);
161                        }
162                    }
163                }
164            }
165            true
166        })
167    }
168}
169
170crate::declare_variants! {
171    default DynamicStorageAllocation => "(memory_size + 1)^num_items",
172}
173
174#[cfg(feature = "example-db")]
175pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
176    vec![crate::example_db::specs::ModelExampleSpec {
177        id: "dynamic_storage_allocation",
178        instance: Box::new(DynamicStorageAllocation::new(
179            vec![(0, 3, 2), (0, 2, 3), (1, 4, 1), (2, 5, 3), (3, 5, 2)],
180            6,
181        )),
182        optimal_config: vec![0, 2, 5, 2, 0],
183        optimal_value: serde_json::json!(true),
184    }]
185}
186
187#[cfg(test)]
188#[path = "../../unit_tests/models/misc/dynamic_storage_allocation.rs"]
189mod tests;