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