Skip to main content

problemreductions/models/misc/
minimum_code_generation_parallel_assignments.rs

1//! Minimum Code Generation for Parallel Assignments problem implementation.
2//!
3//! Given a set of simultaneous variable assignments, find an execution ordering
4//! (permutation) that minimizes the number of backward dependencies -- cases where
5//! a variable is overwritten before a later assignment reads its old value.
6
7use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13    ProblemSchemaEntry {
14        name: "MinimumCodeGenerationParallelAssignments",
15        display_name: "Minimum Code Generation (Parallel Assignments)",
16        aliases: &[],
17        dimensions: &[],
18        module_path: module_path!(),
19        description: "Find an ordering of parallel assignments minimizing backward dependencies",
20        fields: &[
21            FieldInfo { name: "num_variables", type_name: "usize", description: "Number of variables" },
22            FieldInfo { name: "assignments", type_name: "Vec<(usize, Vec<usize>)>", description: "Each assignment (target_var, read_vars)" },
23        ],
24    }
25}
26
27/// The Minimum Code Generation for Parallel Assignments problem.
28///
29/// Given a set V of variables and a collection of assignments A_i: "v_i <- op(B_i)"
30/// where v_i is the target variable and B_i is the set of variables read,
31/// find a permutation of the assignments that minimizes the number of backward
32/// dependencies. A backward dependency occurs when assignment pi(i) writes
33/// variable v and assignment pi(j) (j > i) reads v.
34///
35/// # Example
36///
37/// ```
38/// use problemreductions::models::misc::MinimumCodeGenerationParallelAssignments;
39/// use problemreductions::{Problem, Solver, BruteForce};
40///
41/// // 4 variables, 4 assignments:
42/// // A_0: a <- op(b, c)   -> (0, [1, 2])
43/// // A_1: b <- op(a)      -> (1, [0])
44/// // A_2: c <- op(d)      -> (2, [3])
45/// // A_3: d <- op(b, c)   -> (3, [1, 2])
46/// let assignments = vec![
47///     (0, vec![1, 2]),
48///     (1, vec![0]),
49///     (2, vec![3]),
50///     (3, vec![1, 2]),
51/// ];
52/// let problem = MinimumCodeGenerationParallelAssignments::new(4, assignments);
53/// let solver = BruteForce::new();
54/// let solution = solver.find_witness(&problem);
55/// assert!(solution.is_some());
56/// ```
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct MinimumCodeGenerationParallelAssignments {
59    num_variables: usize,
60    assignments: Vec<(usize, Vec<usize>)>,
61}
62
63impl MinimumCodeGenerationParallelAssignments {
64    /// Create a new MinimumCodeGenerationParallelAssignments instance.
65    ///
66    /// # Panics
67    /// Panics if any target variable or read variable index is >= num_variables.
68    pub fn new(num_variables: usize, assignments: Vec<(usize, Vec<usize>)>) -> Self {
69        for (i, (target, reads)) in assignments.iter().enumerate() {
70            assert!(
71                *target < num_variables,
72                "assignment {i}: target variable {target} >= num_variables {num_variables}"
73            );
74            for &r in reads {
75                assert!(
76                    r < num_variables,
77                    "assignment {i}: read variable {r} >= num_variables {num_variables}"
78                );
79            }
80        }
81        Self {
82            num_variables,
83            assignments,
84        }
85    }
86
87    /// Returns the number of variables.
88    pub fn num_variables(&self) -> usize {
89        self.num_variables
90    }
91
92    /// Returns the number of assignments.
93    pub fn num_assignments(&self) -> usize {
94        self.assignments.len()
95    }
96
97    /// Returns the assignments.
98    pub fn assignments(&self) -> &[(usize, Vec<usize>)] {
99        &self.assignments
100    }
101}
102
103impl Problem for MinimumCodeGenerationParallelAssignments {
104    const NAME: &'static str = "MinimumCodeGenerationParallelAssignments";
105    type Value = Min<usize>;
106
107    fn variant() -> Vec<(&'static str, &'static str)> {
108        crate::variant_params![]
109    }
110
111    fn dims(&self) -> Vec<usize> {
112        let m = self.num_assignments();
113        vec![m; m]
114    }
115
116    fn evaluate(&self, config: &[usize]) -> Min<usize> {
117        let m = self.num_assignments();
118
119        // Validate config length
120        if config.len() != m {
121            return Min(None);
122        }
123
124        // Validate permutation: all values must be distinct and in 0..m
125        let mut seen = vec![false; m];
126        for &pos in config {
127            if pos >= m || seen[pos] {
128                return Min(None);
129            }
130            seen[pos] = true;
131        }
132
133        // config[i] = position of assignment i in execution order
134        // Build execution order: order[pos] = assignment index
135        let mut order = vec![0usize; m];
136        for (assignment_idx, &pos) in config.iter().enumerate() {
137            order[pos] = assignment_idx;
138        }
139
140        // Count backward dependencies: for each pair (i, j) where i < j
141        // (i executes before j), check if the target variable of order[i]
142        // is in the read set of order[j]
143        let mut count = 0usize;
144        for (i, &earlier) in order.iter().enumerate() {
145            let (target_var, _) = &self.assignments[earlier];
146            for &later in &order[(i + 1)..] {
147                let (_, read_vars) = &self.assignments[later];
148                if read_vars.contains(target_var) {
149                    count += 1;
150                }
151            }
152        }
153
154        Min(Some(count))
155    }
156}
157
158crate::declare_variants! {
159    default MinimumCodeGenerationParallelAssignments => "2^num_assignments",
160}
161
162#[cfg(feature = "example-db")]
163pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
164    // 4 variables, 4 assignments:
165    // A_0: a <- op(b, c) -> (0, [1, 2])
166    // A_1: b <- op(a)    -> (1, [0])
167    // A_2: c <- op(d)    -> (2, [3])
168    // A_3: d <- op(b, c) -> (3, [1, 2])
169    //
170    // Optimal ordering: config [0, 3, 1, 2] means
171    // A_0 at position 0, A_1 at position 3, A_2 at position 1, A_3 at position 2
172    // Order: (A_0, A_2, A_3, A_1)
173    // Backward deps: A_0 writes a, A_1 reads a (later) -> 1
174    //                A_2 writes c, A_3 reads c (later) -> 1
175    //                Total: 2
176    let assignments = vec![(0, vec![1, 2]), (1, vec![0]), (2, vec![3]), (3, vec![1, 2])];
177    vec![crate::example_db::specs::ModelExampleSpec {
178        id: "minimum_code_generation_parallel_assignments",
179        instance: Box::new(MinimumCodeGenerationParallelAssignments::new(
180            4,
181            assignments,
182        )),
183        optimal_config: vec![0, 3, 1, 2],
184        optimal_value: serde_json::json!(2),
185    }]
186}
187
188#[cfg(test)]
189#[path = "../../unit_tests/models/misc/minimum_code_generation_parallel_assignments.rs"]
190mod tests;