1use std::ops::{Bound, RangeBounds};
2
3use bumpalo::{Bump, collections::CollectIn};
4use rand::{Rng, rngs::SmallRng};
5
6use super::{
7 Action, Clock, EPSILON, Effect, Location, LocationIdx, PgError, PgGuard, ProgramGraph,
8 Transition, TransitionsIterator, Var,
9};
10use crate::{BooleanExpr, Time, Val, program_graph::TimeRange};
11
12#[derive(Debug)]
19pub struct ProgramGraphRun<'def> {
20 current_states: Vec<Location>,
21 vars: Vec<Val>,
22 clocks: Vec<Time>,
23 def: &'def ProgramGraph,
24 bump: Bump,
25}
26
27impl<'def> Clone for ProgramGraphRun<'def> {
28 fn clone(&self) -> Self {
29 Self {
30 current_states: self.current_states.clone(),
31 vars: self.vars.clone(),
32 clocks: self.clocks.clone(),
33 def: self.def,
34 bump: Bump::new(),
35 }
36 }
37}
38
39impl<'def> ProgramGraphRun<'def> {
40 pub fn new(program_graph: &'def ProgramGraph) -> Self {
42 Self {
43 current_states: program_graph.initial_states.clone(),
44 vars: program_graph.vars.clone(),
45 clocks: vec![0; program_graph.clocks as usize],
46 def: program_graph,
47 bump: Bump::new(),
48 }
49 }
50
51 #[inline]
70 pub fn current_states(&self) -> &[Location] {
71 &self.current_states
72 }
73
74 #[inline]
75 fn transitions<'a>(
76 &'a self,
77 ) -> TransitionsIterator<'a, impl Iterator<Item = &'a (Action, Vec<Transition>)>> {
78 let iters = self
79 .current_states
80 .iter()
81 .map(|loc| self.def.locations[loc.0 as usize].0.iter())
82 .collect_in::<bumpalo::collections::Vec<_>>(&self.bump)
83 .into_bump_slice_mut();
84 TransitionsIterator::new(iters, &self.bump)
85 }
86
87 pub fn possible_transitions(
93 &self,
94 ) -> impl Iterator<Item = (Action, impl Iterator<Item = impl Iterator<Item = Location>>)> {
95 self.transitions().map(move |(action, loc_transitions)| {
96 (
97 action,
98 loc_transitions.iter().map(move |transitions| {
99 transitions
100 .iter()
101 .filter(move |(post_state, guard, constraints)| {
102 self.check_transition(action, *post_state, guard.as_ref(), constraints)
103 })
104 .map(|(post_state, ..)| *post_state)
105 }),
106 )
107 })
108 }
109
110 pub fn nosync_possible_transitions(
119 &self,
120 ) -> Result<impl Iterator<Item = (Action, impl Iterator<Item = Location>)>, PgError> {
121 if self.current_states.len() == 1 {
122 let current_loc = self.current_states[0];
123 Ok(self.def.locations[current_loc.0 as usize].0.iter().map(
124 move |(action, transitions)| {
125 (
126 *action,
127 transitions
128 .iter()
129 .filter(move |(post_state, guard, constraints)| {
130 self.check_transition(
131 *action,
132 *post_state,
133 guard.as_ref(),
134 constraints,
135 )
136 })
137 .map(|(post_state, ..)| *post_state),
138 )
139 },
140 ))
141 } else {
142 Err(PgError::Sync)
143 }
144 }
145
146 fn check_transition(
147 &self,
148 action: Action,
149 post_state: Location,
150 guard: Option<&BooleanExpr<Var>>,
151 constraints: &[(Clock, TimeRange)],
152 ) -> bool {
153 let (_, ref invariants) = self.def.locations[post_state.0 as usize];
154 if action != EPSILON
155 && let Effect::Effects(_, ref resets) = self.def.effects[action.0 as usize]
156 {
157 self.active_transition(guard, constraints, invariants, resets)
158 } else {
159 self.active_autonomous_transition(guard, constraints, invariants)
160 }
161 }
162
163 fn active_transition(
164 &self,
165 guard: Option<&PgGuard>,
166 constraints: &[(Clock, TimeRange)],
167 invariants: &[(Clock, TimeRange)],
168 resets: &[Clock],
169 ) -> bool {
170 guard.is_none_or(|guard| guard.eval::<SmallRng>(&|var| self.vars[var.0 as usize], None))
171 && constraints.iter().all(|(c, range)| {
172 let time = self.clocks[c.0 as usize];
173 range.contains(&time)
174 })
175 && invariants.iter().all(|(c, range)| {
176 let time = if resets.binary_search(c).is_ok() {
177 0
178 } else {
179 self.clocks[c.0 as usize]
180 };
181 range.contains(&time)
182 })
183 }
184
185 #[inline]
186 fn active_autonomous_transition(
187 &self,
188 guard: Option<&PgGuard>,
189 constraints: &[(Clock, TimeRange)],
190 invariants: &[(Clock, TimeRange)],
191 ) -> bool {
192 guard.is_none_or(|guard| guard.eval::<SmallRng>(&|var| self.vars[var.0 as usize], None))
193 && constraints.iter().chain(invariants).all(|(c, range)| {
194 let time = self.clocks[c.0 as usize];
195 range.contains(&time)
196 })
197 }
198
199 fn active_transitions(
200 &self,
201 action: Action,
202 post_states: &[Location],
203 resets: &[Clock],
204 ) -> bool {
205 self.current_states
206 .iter()
207 .zip(post_states)
208 .all(|(current_state, post_state)| {
209 self.def
210 .guards(*current_state, action, *post_state)
211 .any(|(guard, constraints)| {
212 self.active_transition(
213 guard,
214 constraints,
215 &self.def.locations[post_state.0 as usize].1,
216 resets,
217 )
218 })
219 })
220 }
221
222 fn active_autonomous_transitions(&self, post_states: &[Location]) -> bool {
223 self.current_states
224 .iter()
225 .zip(post_states)
226 .all(|(current_state, post_state)| {
227 self.def
228 .guards(*current_state, EPSILON, *post_state)
229 .any(|(guard, constraints)| {
230 self.active_autonomous_transition(
231 guard,
232 constraints,
233 &self.def.locations[post_state.0 as usize].1,
234 )
235 })
236 })
237 }
238
239 pub fn transition<R: Rng>(
244 &mut self,
245 action: Action,
246 post_states: &[Location],
247 rng: &mut R,
248 ) -> Result<(), PgError> {
249 self.bump.reset();
250 if post_states.len() != self.current_states.len() {
251 return Err(PgError::MismatchingPostStates);
252 }
253 if let Some(ps) = post_states
254 .iter()
255 .find(|ps| ps.0 >= self.def.locations.len() as LocationIdx)
256 {
257 return Err(PgError::MissingLocation(*ps));
258 }
259 if action == EPSILON {
260 if !self.active_autonomous_transitions(post_states) {
261 return Err(PgError::UnsatisfiedGuard);
262 }
263 } else if action.0 >= self.def.effects.len() as LocationIdx {
264 return Err(PgError::MissingAction(action));
265 } else if let Effect::Effects(ref effects, ref resets) = self.def.effects[action.0 as usize]
266 {
267 if self.active_transitions(action, post_states, resets) {
268 effects.iter().for_each(|(var, effect)| {
269 self.vars[var.0 as usize] =
270 effect.eval(&|var| self.vars[var.0 as usize], Some(rng))
271 });
272 resets
273 .iter()
274 .for_each(|clock| self.clocks[clock.0 as usize] = 0);
275 } else {
276 return Err(PgError::UnsatisfiedGuard);
277 }
278 } else {
279 return Err(PgError::Communication(action));
280 }
281 self.current_states.copy_from_slice(post_states);
282 Ok(())
283 }
284
285 #[inline]
287 pub fn can_wait(&self, delta: Time) -> bool {
288 self.current_states
289 .iter()
290 .flat_map(|current_state| self.def.locations[current_state.0 as usize].1.iter())
291 .all(|(c, range)| {
292 let start_time = self.clocks[c.0 as usize];
294 let end_time = start_time + delta;
295 range.contains(&end_time)
297 })
298 }
299
300 #[inline]
304 pub fn wait(&mut self, delta: Time) -> Result<(), PgError> {
305 self.bump.reset();
306 if self.can_wait(delta) {
307 self.clocks.iter_mut().for_each(|t| *t += delta);
308 Ok(())
309 } else {
310 Err(PgError::Invariant)
311 }
312 }
313
314 pub(crate) fn send<'a, R: Rng>(
315 &'a mut self,
316 action: Action,
317 post_states: &[Location],
318 rng: &'a mut R,
319 ) -> Result<Vec<Val>, PgError> {
320 self.bump.reset();
321 if action == EPSILON {
322 Err(PgError::NotSend(action))
323 } else if self.active_transitions(action, post_states, &[]) {
324 if let Effect::Send(effects) = &self.def.effects[action.0 as usize] {
325 let vals = effects
326 .iter()
327 .map(|effect| effect.eval(&|var| self.vars[var.0 as usize], Some(rng)))
328 .collect();
329 self.current_states.copy_from_slice(post_states);
330 Ok(vals)
331 } else {
332 Err(PgError::NotSend(action))
333 }
334 } else {
335 Err(PgError::UnsatisfiedGuard)
336 }
337 }
338
339 pub(crate) fn receive(
340 &mut self,
341 action: Action,
342 post_states: &[Location],
343 vals: &[Val],
344 ) -> Result<(), PgError> {
345 self.bump.reset();
346 if action == EPSILON {
347 Err(PgError::NotReceive(action))
348 } else if self.active_transitions(action, post_states, &[]) {
349 if let Effect::Receive(ref vars) = self.def.effects[action.0 as usize] {
350 if vars.len() == vals.len()
352 && vals.iter().zip(vars).all(|(val, var)| {
353 self.vars
354 .get(var.0 as usize)
355 .expect("variable exists")
356 .r#type()
357 == val.r#type()
358 })
359 {
360 vals.iter().zip(vars).for_each(|(&val, var)| {
361 *self.vars.get_mut(var.0 as usize).expect("variable exists") = val
362 });
363 self.current_states.copy_from_slice(post_states);
364 Ok(())
365 } else {
366 Err(PgError::TypeMismatch)
367 }
368 } else {
369 Err(PgError::NotReceive(action))
370 }
371 } else {
372 Err(PgError::UnsatisfiedGuard)
373 }
374 }
375
376 pub fn is_waiting(&self) -> bool {
379 let unsatisfied_lower_bound = |(c, range): &(Clock, TimeRange)| {
380 let time = self.clocks[c.0 as usize];
381 let bound = range.start_bound();
382 match bound {
383 Bound::Included(b) => time < *b,
384 Bound::Excluded(b) => time <= *b,
385 Bound::Unbounded => false,
386 }
387 };
388 let satisfied_upper_bound = |(c, range): &(Clock, TimeRange)| {
389 let time = self.clocks[c.0 as usize];
390 let bound = range.end_bound();
391 match bound {
392 Bound::Included(b) => time <= *b,
393 Bound::Excluded(b) => time < *b,
394 Bound::Unbounded => true,
395 }
396 };
397 self.can_wait(1)
398 && self.transitions().any(move |(_, loc_transitions)| {
399 loc_transitions.iter().any(move |transitions| {
400 transitions.iter().any(move |(post_state, _, constraints)| {
401 let invariants = self.def.locations[post_state.0 as usize].1.as_slice();
402 (constraints.iter().any(unsatisfied_lower_bound)
403 && constraints.iter().all(satisfied_upper_bound))
404 || (invariants.iter().any(unsatisfied_lower_bound)
405 && invariants.iter().all(satisfied_upper_bound))
406 })
407 })
408 })
409 }
410}