problemreductions/models/misc/
minimum_code_generation_parallel_assignments.rs1use 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 category: crate::registry::ProblemCategory::Misc,
19 module_path: module_path!(),
20 description: "Find an ordering of parallel assignments minimizing backward dependencies",
21 fields: &[
22 FieldInfo { name: "num_variables", type_name: "usize", description: "Number of variables" },
23 FieldInfo { name: "assignments", type_name: "Vec<(usize, Vec<usize>)>", description: "Each assignment (target_var, read_vars)" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(try_from = "MinimumCodeGenerationParallelAssignmentsData")]
60pub struct MinimumCodeGenerationParallelAssignments {
61 num_variables: usize,
62 assignments: Vec<(usize, Vec<usize>)>,
63}
64
65#[derive(Deserialize)]
66struct MinimumCodeGenerationParallelAssignmentsData {
67 num_variables: usize,
68 assignments: Vec<(usize, Vec<usize>)>,
69}
70
71impl TryFrom<MinimumCodeGenerationParallelAssignmentsData>
72 for MinimumCodeGenerationParallelAssignments
73{
74 type Error = crate::registry::ConstructionError;
75 fn try_from(data: MinimumCodeGenerationParallelAssignmentsData) -> Result<Self, Self::Error> {
76 Self::try_new(data.num_variables, data.assignments)
77 }
78}
79
80impl MinimumCodeGenerationParallelAssignments {
81 pub fn new(num_variables: usize, assignments: Vec<(usize, Vec<usize>)>) -> Self {
86 Self::try_new(num_variables, assignments).unwrap_or_else(|error| panic!("{error}"))
87 }
88
89 fn try_new(
90 num_variables: usize,
91 assignments: Vec<(usize, Vec<usize>)>,
92 ) -> Result<Self, crate::registry::ConstructionError> {
93 for (i, (target, reads)) in assignments.iter().enumerate() {
94 if *target >= num_variables {
95 return Err(format!(
96 "assignment {i}: target variable {target} >= num_variables {num_variables}"
97 )
98 .into());
99 }
100 for &r in reads {
101 if r >= num_variables {
102 return Err(format!(
103 "assignment {i}: read variable {r} >= num_variables {num_variables}"
104 )
105 .into());
106 }
107 }
108 }
109 Ok(Self {
110 num_variables,
111 assignments,
112 })
113 }
114
115 pub fn num_variables(&self) -> usize {
117 self.num_variables
118 }
119
120 pub fn num_assignments(&self) -> usize {
122 self.assignments.len()
123 }
124
125 pub fn assignments(&self) -> &[(usize, Vec<usize>)] {
127 &self.assignments
128 }
129}
130
131impl Problem for MinimumCodeGenerationParallelAssignments {
132 const NAME: &'static str = "MinimumCodeGenerationParallelAssignments";
133 type Solution = Vec<usize>;
134 type Value = Min<i64>;
135
136 crate::problem_parameters![
137 ("num_variables", num_variables),
138 ("num_assignments", num_assignments),
139 ];
140
141 fn variant() -> Vec<(&'static str, &'static str)> {
142 crate::variant_params![]
143 }
144
145 fn evaluate(
146 &self,
147 config: &Self::Solution,
148 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
149 Ok({
150 let m = self.num_assignments();
151
152 if config.len() != m {
154 return Err(crate::traits::EvaluationError::InvalidConfiguration(
155 "assignment length does not match the internal nodes".into(),
156 ));
157 }
158
159 if config.iter().any(|&position| position >= m) {
160 return Err(crate::traits::EvaluationError::InvalidConfiguration(
161 "assignment contains an out-of-range execution position".into(),
162 ));
163 }
164
165 let mut seen = vec![false; m];
167 for &pos in config {
168 if seen[pos] {
169 return Ok(Min(None));
170 }
171 seen[pos] = true;
172 }
173
174 let mut order = vec![0usize; m];
177 for (assignment_idx, &pos) in config.iter().enumerate() {
178 order[pos] = assignment_idx;
179 }
180
181 let mut count = 0usize;
185 for (i, &earlier) in order.iter().enumerate() {
186 let (target_var, _) = &self.assignments[earlier];
187 for &later in &order[(i + 1)..] {
188 let (_, read_vars) = &self.assignments[later];
189 if read_vars.contains(target_var) {
190 count += 1;
191 }
192 }
193 }
194
195 Min(Some(i64::try_from(count).map_err(|_| {
196 crate::traits::EvaluationError::IntegerOverflow(
197 "converting parallel instruction count to i64".into(),
198 )
199 })?))
200 })
201 }
202}
203
204impl crate::solvers::BruteForceProblem for MinimumCodeGenerationParallelAssignments {
205 fn dimensions(&self) -> Vec<usize> {
206 let m = self.num_assignments();
207 vec![m; m]
208 }
209}
210
211crate::declare_variants! {
212 default MinimumCodeGenerationParallelAssignments => "2^num_assignments",
213}
214
215crate::register_brute_force! {
216 MinimumCodeGenerationParallelAssignments,
217}
218
219#[cfg(feature = "example-db")]
220pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
221 let assignments = vec![(0, vec![1, 2]), (1, vec![0]), (2, vec![3]), (3, vec![1, 2])];
234 vec![crate::example_db::specs::ModelExampleSpec {
235 id: "minimum_code_generation_parallel_assignments",
236 instance: Box::new(MinimumCodeGenerationParallelAssignments::new(
237 4,
238 assignments,
239 )),
240 optimal_config: serde_json::json!(vec![0, 3, 1, 2]),
241 optimal_value: serde_json::json!(2),
242 }]
243}
244
245#[cfg(test)]
246#[path = "../../unit_tests/models/misc/minimum_code_generation_parallel_assignments.rs"]
247mod tests;