taco_smt_encoder/expression_encoding/
ctx_mgr.rs1use std::{cmp::min, collections::HashMap, rc::Rc};
4
5use log::debug;
6use taco_display_utils::join_iterator;
7use taco_threshold_automaton::{
8 RuleDefinition,
9 expressions::Parameter,
10 general_threshold_automaton::{GeneralThresholdAutomaton, Rule},
11 path::{Configuration, InitializedPathBuilder, Path, PathBuilder, Transition},
12};
13
14use crate::{
15 SMTExpr, SMTSolution, SMTSolver,
16 expression_encoding::{SMTVariableContext, config_ctx::ConfigFromSMT, step_ctx::StepSMT},
17};
18
19pub struct SMTConfigMgr<CR, CC>
21where
22 CR: StepSMT,
23 CC: ConfigFromSMT,
24{
25 params: Rc<HashMap<Parameter, SMTExpr>>,
26 configs: Vec<Rc<CC>>,
27 configs_primed: Vec<Rc<CC>>,
28 step_steps: Vec<CR>,
29 steady_steps: Vec<CR>,
30}
31
32impl<CR, CC> SMTConfigMgr<CR, CC>
33where
34 CR: StepSMT,
35 CC: ConfigFromSMT,
36{
37 pub fn new(params: Rc<HashMap<Parameter, SMTExpr>>) -> Self {
39 Self {
40 params,
41 configs: Vec::new(),
42 configs_primed: Vec::new(),
43 step_steps: Vec::new(),
44 steady_steps: Vec::new(),
45 }
46 }
47
48 pub fn params(&self) -> &Rc<HashMap<Parameter, SMTExpr>> {
50 &self.params
51 }
52
53 pub fn push_steady(&mut self, step_ctx: CR) {
55 self.steady_steps.push(step_ctx);
56 }
57
58 pub fn pop_steady(&mut self) {
60 self.steady_steps.pop();
61 }
62
63 pub fn steady(&self) -> impl Iterator<Item = &CR> {
65 self.steady_steps.iter()
66 }
67
68 pub fn push_step(&mut self, step_ctx: CR) {
70 self.step_steps.push(step_ctx);
71 }
72
73 pub fn pop_step(&mut self) {
75 self.step_steps.pop();
76 }
77
78 pub fn step(&self) -> impl Iterator<Item = &CR> {
80 self.step_steps.iter()
81 }
82
83 pub fn push_cfg(&mut self, cfg_ctx: Rc<CC>) {
85 self.configs.push(cfg_ctx);
86 }
87
88 pub fn pop_cfg(&mut self) {
90 self.configs.pop();
91 }
92
93 pub fn configs(&self) -> impl Iterator<Item = &Rc<CC>> {
95 self.configs.iter()
96 }
97
98 pub fn configs_primed(&self) -> impl Iterator<Item = &Rc<CC>> {
100 self.configs_primed.iter()
101 }
102
103 pub fn push_cfg_primed(&mut self, cfg_ctx: Rc<CC>) {
105 self.configs_primed.push(cfg_ctx);
106 }
107
108 pub fn pop_cfg_primed(&mut self) {
110 self.configs_primed.pop();
111 }
112
113 pub fn extract_error_path(
115 &self,
116 res: SMTSolution,
117 solver: &mut SMTSolver,
118 ta: GeneralThresholdAutomaton,
119 ) -> Path {
120 let builder = PathBuilder::new(ta);
121
122 let param_assignment = self
123 .params
124 .get_solution(solver, res)
125 .expect("Failed to extract parameter assignment for error path");
126 let mut builder = builder
127 .add_parameter_assignment(param_assignment)
128 .expect("Parameter assignment returned by SMT solver invalid");
129
130 for (i, ((config, config_primed), steady_step)) in self
131 .configs
132 .iter()
133 .zip(self.configs_primed.iter())
134 .zip(self.steady_steps.iter())
135 .enumerate()
136 {
137 let config = config
138 .get_assigned_configuration(solver, res)
139 .expect("Failed to extract assigned configuration of config");
140
141 builder = builder
142 .add_configuration(config.clone())
143 .expect("Configuration config extracted from SMT solver is invalid");
144
145 builder = Self::add_transitions_steady_step_to_builder(
146 builder,
147 config,
148 steady_step,
149 solver,
150 res,
151 );
152
153 let primed_config = config_primed
154 .get_assigned_configuration(solver, res)
155 .expect("Failed to extract assigned configuration of primed_config");
156
157 builder = builder
158 .add_configuration(primed_config)
159 .expect("Configuration primed_config extracted from SMT solver is invalid");
160
161 if i < self.step_steps.len() {
163 builder = Self::add_transition_step_step_to_builder(
164 builder,
165 &self.step_steps[i],
166 solver,
167 res,
168 );
169 }
170 }
171
172 builder.build().expect("Extracted path is invalid!")
173 }
174
175 fn add_transitions_steady_step_to_builder(
178 mut builder: InitializedPathBuilder,
179 config: Configuration,
180 steady_step: &CR,
181 solver: &mut SMTSolver,
182 res: SMTSolution,
183 ) -> InitializedPathBuilder {
184 let mut rules = steady_step
185 .get_rules_applied(solver, res)
186 .expect("Failed to extract rules used")
187 .filter(|(_, n)| *n > 0)
188 .collect::<Vec<_>>();
189
190 let mut curr_config = config.clone();
191
192 let old_rules = rules.clone();
193 let old_config = config;
194
195 while !rules.is_empty() {
197 let mut n_apply = 0;
198
199 for (rule, n_to_apply) in rules.iter_mut().filter(|(r, _)| r.source() == r.target()) {
201 if curr_config.get_processes_in_location(rule.source()) > 0 {
202 n_apply = *n_to_apply;
204 *n_to_apply = 0;
205
206 (builder, curr_config) =
207 Self::add_transition_to_builder_and_return_updated_config(
208 builder,
209 curr_config,
210 rule.clone(),
211 n_apply,
212 );
213 }
214 }
215
216 for (rule, n_to_apply) in rules.iter_mut().filter(|(_, n)| *n > 0) {
217 if curr_config.get_processes_in_location(rule.source()) > 0 {
219 n_apply = min(
221 curr_config.get_processes_in_location(rule.source()),
222 *n_to_apply,
223 );
224
225 *n_to_apply -= n_apply;
226
227 (builder, curr_config) =
228 Self::add_transition_to_builder_and_return_updated_config(
229 builder,
230 curr_config,
231 rule.clone(),
232 n_apply,
233 );
234
235 break;
238 }
239 }
240
241 assert!(
242 n_apply > 0,
243 "Failed to order rules into chainable order! Configuration that rules must be applied on:\n{}\n\nRules left to apply:\n{}\n\nSet of rules to be ordered in this steady step:\n{}\n\nInitial configuration of the steady step:\n{}\n\n ",
244 curr_config,
245 join_iterator(rules.iter().map(|(r, i)| format!("{i} x {r}")), ",\n"),
246 join_iterator(old_rules.iter().map(|(r, i)| format!("{i} x {r}")), ",\n"),
247 old_config,
248 );
249
250 rules.retain(|(_, n)| *n > 0);
251 }
252
253 builder
254 }
255
256 fn add_transition_to_builder_and_return_updated_config(
260 mut builder: InitializedPathBuilder,
261 config: Configuration,
262 rule: Rule,
263 n: u32,
264 ) -> (InitializedPathBuilder, Configuration) {
265 let transition = Transition::new(rule, n);
266 let curr_config = config
267 .apply_rule(&transition)
268 .expect("Constructed transition invalid!");
269
270 builder = builder
271 .add_transition(transition)
272 .expect("Failed to add transition to builder");
273 builder = builder
274 .add_configuration(curr_config.clone())
275 .expect("Failed to add configuration computed in steady step");
276
277 (builder, curr_config)
278 }
279
280 fn add_transition_step_step_to_builder(
283 builder: InitializedPathBuilder,
284 step_step: &CR,
285 solver: &mut SMTSolver,
286 res: SMTSolution,
287 ) -> InitializedPathBuilder {
288 let mut rules_it = step_step
289 .get_rules_applied(solver, res)
290 .expect("Failed to extract rules applied");
291 let applied_rule = rules_it.next();
292
293 if applied_rule.is_none() {
294 debug!("Found an empty step step. Skipping!");
297 return builder;
298 }
299 let (applied_rule, n) = applied_rule.unwrap();
300
301 debug_assert!(
302 rules_it.next().is_none(),
303 "Expected only one rule to be applied in step step!"
304 );
305
306 debug_assert!(
307 n == 1,
308 "Expected rule in step step to be applied only once !"
309 );
310
311 let transition = Transition::new(applied_rule, 1);
312
313 builder
314 .add_transition(transition)
315 .expect("Failed to add transition of step step to builder")
316 }
317}