Skip to main content

problemreductions/models/misc/
feasible_register_assignment.rs

1//! Feasible Register Assignment problem implementation.
2//!
3//! Given a directed acyclic graph G = (V, A), K registers, and a fixed
4//! register assignment f: V → {0, ..., K-1}, determine whether there
5//! exists a topological ordering of the vertices such that no register
6//! conflict arises during execution. NP-complete [Bouchez et al., 2006].
7
8use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use serde::{Deserialize, Deserializer, Serialize};
11
12inventory::submit! {
13    ProblemSchemaEntry {
14        name: "FeasibleRegisterAssignment",
15        display_name: "Feasible Register Assignment",
16        aliases: &[],
17        dimensions: &[],
18        category: crate::registry::ProblemCategory::Misc,
19        module_path: module_path!(),
20        description: "Determine whether a DAG computation can be scheduled without register conflicts under a fixed assignment",
21        fields: &[
22            FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices n = |V|" },
23            FieldInfo { name: "arcs", type_name: "Vec<(usize, usize)>", description: "Directed arcs (v, u) meaning v depends on u" },
24            FieldInfo { name: "num_registers", type_name: "usize", description: "Number of registers K" },
25            FieldInfo { name: "assignment", type_name: "Vec<usize>", description: "Register assignment f(v) for each vertex" },
26        ],
27    }
28}
29
30/// The Feasible Register Assignment problem.
31///
32/// Given a directed acyclic graph G = (V, A) where arcs represent data
33/// dependencies, K registers, and an assignment f: V → {0, ..., K-1},
34/// determine whether there exists a topological evaluation ordering such
35/// that no two simultaneously live values share the same register.
36///
37/// # Representation
38///
39/// An arc `(v, u)` means vertex `v` depends on vertex `u` (i.e., `u` must
40/// be computed before `v`). Each variable represents a vertex, with domain
41/// `{0, ..., n-1}` giving its evaluation position (the config must be a
42/// valid permutation).
43///
44/// # Example
45///
46/// ```
47/// use problemreductions::models::misc::FeasibleRegisterAssignment;
48/// use problemreductions::{Problem, BruteForce};
49///
50/// // 4 vertices: v0 depends on v1 and v2, v1 depends on v3
51/// let problem = FeasibleRegisterAssignment::new(
52///     4,
53///     vec![(0, 1), (0, 2), (1, 3)],
54///     2,
55///     vec![0, 1, 0, 0],
56/// );
57/// let solver = BruteForce::new();
58/// let solution = solver.solve(&problem).unwrap();
59/// assert!(solution.is_some());
60/// ```
61#[derive(Debug, Clone, Serialize)]
62pub struct FeasibleRegisterAssignment {
63    /// Number of vertices.
64    num_vertices: usize,
65    /// Directed arcs (v, u) meaning v depends on u.
66    arcs: Vec<(usize, usize)>,
67    /// Number of registers K.
68    num_registers: usize,
69    /// Register assignment f(v) for each vertex.
70    assignment: Vec<usize>,
71    /// Precomputed: dependencies[v] = vertices that v depends on.
72    #[serde(skip)]
73    dependencies: Vec<Vec<usize>>,
74    /// Precomputed: dependents[u] = vertices that depend on u.
75    #[serde(skip)]
76    dependents: Vec<Vec<usize>>,
77}
78
79#[derive(Deserialize)]
80struct FeasibleRegisterAssignmentData {
81    num_vertices: usize,
82    arcs: Vec<(usize, usize)>,
83    num_registers: usize,
84    assignment: Vec<usize>,
85}
86
87impl<'de> Deserialize<'de> for FeasibleRegisterAssignment {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: Deserializer<'de>,
91    {
92        let data = FeasibleRegisterAssignmentData::deserialize(deserializer)?;
93        let (dependencies, dependents) = Self::build_adjacency(data.num_vertices, &data.arcs);
94        Ok(Self {
95            num_vertices: data.num_vertices,
96            arcs: data.arcs,
97            num_registers: data.num_registers,
98            assignment: data.assignment,
99            dependencies,
100            dependents,
101        })
102    }
103}
104
105impl FeasibleRegisterAssignment {
106    /// Create a new Feasible Register Assignment instance.
107    ///
108    /// # Panics
109    ///
110    /// Panics if any arc index is out of bounds (>= num_vertices),
111    /// if any arc is a self-loop, if the assignment length does not
112    /// match num_vertices, or if any assignment value >= num_registers.
113    pub fn new(
114        num_vertices: usize,
115        arcs: Vec<(usize, usize)>,
116        num_registers: usize,
117        assignment: Vec<usize>,
118    ) -> Self {
119        for &(v, u) in &arcs {
120            assert!(
121                v < num_vertices && u < num_vertices,
122                "Arc ({}, {}) out of bounds for {} vertices",
123                v,
124                u,
125                num_vertices
126            );
127            assert!(v != u, "Self-loop ({}, {}) not allowed in a DAG", v, u);
128        }
129        assert_eq!(
130            assignment.len(),
131            num_vertices,
132            "Assignment length {} does not match num_vertices {}",
133            assignment.len(),
134            num_vertices
135        );
136        if num_vertices > 0 {
137            assert!(
138                num_registers > 0,
139                "num_registers must be positive when there are vertices"
140            );
141        }
142        for (v, &r) in assignment.iter().enumerate() {
143            assert!(
144                r < num_registers,
145                "Assignment[{}] = {} is out of bounds for {} registers",
146                v,
147                r,
148                num_registers
149            );
150        }
151        let (dependencies, dependents) = Self::build_adjacency(num_vertices, &arcs);
152        Self {
153            num_vertices,
154            arcs,
155            num_registers,
156            assignment,
157            dependencies,
158            dependents,
159        }
160    }
161
162    /// Build dependency and dependent adjacency lists from arcs.
163    fn build_adjacency(
164        num_vertices: usize,
165        arcs: &[(usize, usize)],
166    ) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
167        let mut dependencies = vec![vec![]; num_vertices];
168        let mut dependents = vec![vec![]; num_vertices];
169        for &(v, u) in arcs {
170            dependencies[v].push(u);
171            dependents[u].push(v);
172        }
173        (dependencies, dependents)
174    }
175
176    /// Get the number of vertices.
177    pub fn num_vertices(&self) -> usize {
178        self.num_vertices
179    }
180
181    /// Get the number of arcs.
182    pub fn num_arcs(&self) -> usize {
183        self.arcs.len()
184    }
185
186    /// Get the number of registers.
187    pub fn num_registers(&self) -> usize {
188        self.num_registers
189    }
190
191    /// Count unordered vertex pairs that share a register.
192    pub fn num_same_register_pairs(&self) -> usize {
193        let mut counts = vec![0usize; self.num_registers];
194        for &register in &self.assignment {
195            counts[register] += 1;
196        }
197        counts
198            .into_iter()
199            .map(|count| count.saturating_sub(1) * count / 2)
200            .sum()
201    }
202
203    /// Get the arcs.
204    pub fn arcs(&self) -> &[(usize, usize)] {
205        &self.arcs
206    }
207
208    /// Get the register assignment.
209    pub fn assignment(&self) -> &[usize] {
210        &self.assignment
211    }
212
213    /// Check whether the given config (position assignment) is feasible.
214    ///
215    /// Returns `true` if the config is a valid permutation, respects
216    /// topological ordering, and has no register conflicts.
217    pub fn is_feasible(&self, config: &[usize]) -> bool {
218        let n = self.num_vertices;
219        if config.len() != n {
220            return false;
221        }
222
223        // Check valid permutation: each position 0..n-1 used exactly once
224        let mut order = vec![0usize; n]; // order[position] = vertex
225        let mut used = vec![false; n];
226        for (vertex, &position) in config.iter().enumerate() {
227            if position >= n {
228                return false;
229            }
230            if used[position] {
231                return false;
232            }
233            used[position] = true;
234            order[position] = vertex;
235        }
236
237        // Check topological ordering and register conflicts
238        let mut computed = vec![false; n];
239
240        for step in 0..n {
241            let vertex = order[step];
242
243            // Check dependencies: all dependencies must have been computed
244            for &dep in &self.dependencies[vertex] {
245                if !computed[dep] {
246                    return false;
247                }
248            }
249
250            // Check register conflict: the register assigned to this vertex
251            // must not be currently occupied by a live value.
252            let reg = self.assignment[vertex];
253            for &w in &order[..step] {
254                if self.assignment[w] == reg {
255                    let still_live = self.dependents[w]
256                        .iter()
257                        .any(|&d| d != vertex && !computed[d]);
258                    if still_live {
259                        return false;
260                    }
261                }
262            }
263
264            computed[vertex] = true;
265        }
266
267        true
268    }
269}
270
271impl Problem for FeasibleRegisterAssignment {
272    const NAME: &'static str = "FeasibleRegisterAssignment";
273    type Solution = Vec<usize>;
274    type Value = crate::types::Or;
275
276    crate::problem_parameters![
277        ("num_arcs", num_arcs),
278        ("num_registers", num_registers),
279        ("num_same_register_pairs", num_same_register_pairs),
280        ("num_vertices", num_vertices),
281    ];
282
283    fn variant() -> Vec<(&'static str, &'static str)> {
284        crate::variant_params![]
285    }
286
287    fn evaluate(
288        &self,
289        config: &Self::Solution,
290    ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
291        if config.len() != self.num_vertices {
292            return Err(crate::traits::EvaluationError::InvalidConfiguration(
293                "ordering length does not match the graph vertices".into(),
294            ));
295        }
296        if config.iter().any(|&position| position >= self.num_vertices) {
297            return Err(crate::traits::EvaluationError::InvalidConfiguration(
298                "ordering contains an out-of-range position".into(),
299            ));
300        }
301        Ok(crate::types::Or(self.is_feasible(config)))
302    }
303}
304
305impl crate::solvers::BruteForceProblem for FeasibleRegisterAssignment {
306    fn dimensions(&self) -> Vec<usize> {
307        vec![self.num_vertices; self.num_vertices]
308    }
309}
310
311crate::declare_variants! {
312    default FeasibleRegisterAssignment => "factorial(num_vertices)",
313}
314
315crate::register_brute_force! {
316    FeasibleRegisterAssignment,
317}
318
319#[cfg(feature = "example-db")]
320pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
321    vec![crate::example_db::specs::ModelExampleSpec {
322        id: "feasible_register_assignment",
323        // 4 vertices, arcs: (0,1),(0,2),(1,3), K=2, assignment [0,1,0,0]
324        // Valid order: v3, v1, v2, v0 -> config [3, 1, 2, 0]
325        instance: Box::new(FeasibleRegisterAssignment::new(
326            4,
327            vec![(0, 1), (0, 2), (1, 3)],
328            2,
329            vec![0, 1, 0, 0],
330        )),
331        // config[v] = position: v0 at pos 3, v1 at pos 1, v2 at pos 2, v3 at pos 0
332        // Order: v3(pos0), v1(pos1), v2(pos2), v0(pos3)
333        optimal_config: serde_json::json!(vec![3, 1, 2, 0]),
334        optimal_value: serde_json::json!(true),
335    }]
336}
337
338#[cfg(test)]
339#[path = "../../unit_tests/models/misc/feasible_register_assignment.rs"]
340mod tests;