1use std::sync::Arc;
2use std::sync::atomic::{AtomicBool, Ordering};
3
4use bumpalo::Bump;
5use bumpalo::collections::CollectIn;
6use log::trace;
7use rand::rngs::SmallRng;
8use rand::seq::IteratorRandom;
9use rand::{RngExt, SeedableRng, make_rng};
10use thiserror::Error;
11
12use crate::channel_system::{
13 Action, Channel, ChannelSystem, ChannelSystemRun, CsError, Event, EventType, Location, PgId,
14};
15use crate::{BooleanExpr, Oracle, RunOutcome, Time, Tracer, Val};
16
17#[derive(Debug, Clone, Copy, Error)]
19pub enum TsError {
20 #[error("error from channel system {0:?}")]
22 ChannelSystem(CsError),
23 #[error("default port value is not the type of the channel {0:?}")]
26 WrongPortType(Channel),
27}
28
29impl From<CsError> for TsError {
30 fn from(value: CsError) -> Self {
31 Self::ChannelSystem(value)
32 }
33}
34
35#[derive(Debug, Clone, Copy)]
37pub enum Atom {
38 State(Channel, usize),
40 Event(Channel),
42}
43
44#[derive(Debug, Clone)]
46pub struct TransitionSystem {
47 cs: ChannelSystem,
48 ports: Vec<Channel>,
50 vals: Vec<Vec<Val>>,
51 predicates: Vec<BooleanExpr<Atom>>,
52}
53
54impl TransitionSystem {
55 pub fn new(cs: ChannelSystem) -> Self {
57 Self {
58 ports: Vec::new(),
59 vals: Vec::new(),
60 cs,
61 predicates: Vec::new(),
62 }
63 }
64
65 pub fn add_port(&mut self, channel: Channel, mut value: Vec<Val>) -> Result<(), TsError> {
68 let types = self.cs.channel(channel)?.0;
69 if types.len() != value.len() || types.iter().zip(&value).any(|(t, val)| val.r#type() != *t)
70 {
71 return Err(TsError::WrongPortType(channel));
72 }
73 if let Err(index) = self.ports.binary_search(&channel) {
76 self.ports.insert(index, channel);
77 value.shrink_to_fit();
78 self.vals.insert(index, value);
79 }
80 assert!(self.ports.is_sorted());
81 assert_eq!(self.ports.len(), self.vals.len());
82 Ok(())
83 }
84
85 pub fn add_predicate(&mut self, predicate: BooleanExpr<Atom>) -> Result<(), TsError> {
88 let _ = predicate.eval::<SmallRng>(
90 &|port| match port {
91 Atom::State(channel, idx) => {
92 let index = self
93 .ports
94 .binary_search(&channel)
95 .expect("port must have been initialized");
96 self.vals[index][idx]
97 }
98 Atom::Event(..) => Val::Boolean(false),
99 },
100 None,
101 );
102 self.predicates.push(predicate);
103 Ok(())
104 }
105
106 pub fn shrink(&mut self) {
109 self.ports.shrink_to_fit();
110 self.vals.shrink_to_fit();
111 }
112
113 pub fn new_run(&self) -> TransitionSystemRun<'_> {
115 let mut vals = self.vals.clone();
116 vals.shrink_to_fit();
117 let mut pg_list = Vec::from_iter(self.cs.program_graph_ids());
118 pg_list.shrink_to_fit();
119 TransitionSystemRun {
120 cs: self.cs.new_instance(),
121 ports: &self.ports,
122 vals,
123 predicates: &self.predicates,
124 last_event: None,
125 pg_list,
126 rng: make_rng(),
127 bump: Bump::new(),
128 }
129 }
130}
131
132#[derive(Debug)]
137pub struct TransitionSystemRun<'def> {
138 cs: ChannelSystemRun<'def>,
139 ports: &'def [Channel],
140 vals: Vec<Vec<Val>>,
141 predicates: &'def [BooleanExpr<Atom>],
142 last_event: Option<(Action, Event)>,
143 pg_list: Vec<PgId>,
144 rng: SmallRng,
145 bump: Bump,
146}
147
148impl<'def> Clone for TransitionSystemRun<'def> {
149 fn clone(&self) -> Self {
150 Self {
151 cs: self.cs.clone(),
152 ports: self.ports,
153 vals: self.vals.clone(),
154 predicates: self.predicates,
155 last_event: self.last_event.clone(),
156 pg_list: self.pg_list.clone(),
157 rng: self.rng.clone(),
158 bump: Bump::new(),
159 }
160 }
161}
162
163impl<'def> TransitionSystemRun<'def> {
164 pub fn transition(&mut self) {
168 self.last_event = self.montecarlo_transition();
169 if let Some((_, ref event)) = self.last_event
170 && let EventType::Send(ref vals) = event.event_type
171 && let Ok(index) = self.ports.binary_search(&event.channel)
172 {
173 self.vals[index].copy_from_slice(vals);
177 }
178 }
179
180 #[inline]
182 pub fn last_event(&self) -> Option<&(Action, Event)> {
183 self.last_event.as_ref()
184 }
185
186 #[inline]
187 fn time(&self) -> Time {
188 self.cs.time()
189 }
190
191 #[inline]
192 fn time_tick(&mut self) {
193 self.cs.wait(1).expect("time error")
194 }
195
196 fn labels(&self) -> impl Iterator<Item = bool> {
197 self.predicates.iter().map(|prop| {
198 prop.eval::<SmallRng>(
199 &|port| match port {
200 Atom::State(channel, idx) => {
201 let port_idx = self
202 .ports
203 .binary_search(&channel)
204 .expect("port must exist and be initialized");
205 self.vals[port_idx][idx]
206 }
207 Atom::Event(channel) => {
208 Val::Boolean(self.last_event.as_ref().is_some_and(|(_, e)| {
209 e.channel == channel && matches!(e.event_type, EventType::Send(..))
210 }))
211 }
212 },
213 None,
214 )
215 })
216 }
217
218 #[inline]
219 fn state(&self) -> &[Vec<Val>] {
220 &self.vals
221 }
222
223 pub(crate) fn experiment<O: Oracle>(
225 &mut self,
226 mut oracle: O,
227 running: Arc<AtomicBool>,
228 ) -> RunOutcome {
229 let mut labels = Vec::from_iter(self.labels());
231 oracle.update_state(&labels);
233 while oracle.output_guarantees().any(|b| b.is_none()) {
234 self.transition();
235 if !running.load(Ordering::Relaxed) {
236 trace!("run stopped");
237 return None;
238 } else if self.last_event().is_some() {
239 labels.clear();
240 labels.extend(self.labels());
241 oracle.update_state(&labels);
242 } else if self.cs.is_waiting() {
243 self.time_tick();
244 oracle.update_time(self.time());
245 } else {
246 break;
247 }
248 }
249 trace!("run complete");
250 let verified = Vec::from_iter(oracle.final_output_guarantees());
251 Some(verified)
252 }
253
254 pub(crate) fn trace<T, O: Oracle>(
257 &mut self,
258 mut oracle: O,
259 mut tracer: T,
260 model_data: &T::ModelData,
261 ) -> RunOutcome
262 where
263 T: Tracer,
264 {
265 trace!("new run starting");
266 let mut labels = Vec::from_iter(self.labels());
268 oracle.update_state(&labels);
270 while oracle.output_guarantees().any(|b| b.is_none()) {
273 self.transition();
274 if let Some((action, event)) = self.last_event() {
275 tracer.trace(model_data, *action, event, self.time(), self.state());
276 labels.clear();
277 labels.extend(self.labels());
278 oracle.update_state(&labels);
279 } else if self.cs.is_waiting() {
280 self.time_tick();
281 oracle.update_time(self.time());
282 } else {
283 break;
284 }
285 }
286 trace!("run complete");
287 let verified = Vec::from_iter(oracle.final_output_guarantees());
288 Some(verified)
289 }
290
291 fn montecarlo_transition(&mut self) -> Option<(Action, Event)> {
292 let mut rand1 = SmallRng::from_rng(&mut self.rng);
293 let mut pgs_left = self.pg_list.len();
295 while pgs_left > 0 {
296 let pg_select = self.rng.random_range(0..pgs_left);
298 let pg_id = self.pg_list[pg_select];
299 pgs_left -= 1;
302 self.pg_list.swap(pg_select, pgs_left);
303 if self
308 .cs
309 .program_graph(pg_id)
310 .expect("pg exists")
311 .current_states()
312 .len()
313 == 1
314 {
315 while let Some((action, post_state)) = self
316 .cs
317 .nosync_possible_transitions_pg(pg_id)
318 .expect("pg exists")
319 .filter_map(|(action, post_states)| {
320 post_states.choose(&mut rand1).map(|loc| (action, loc))
321 })
322 .choose(&mut self.rng)
323 {
324 let event = self
325 .cs
326 .transition(pg_id, action, &[post_state])
327 .expect("successful transition");
328 if event.is_some() {
329 return event.map(|ev| (action, ev));
330 }
331 }
332 } else {
333 use bumpalo::collections::Vec as BumpVec;
334
335 self.bump.reset();
336 while let Some((action, post_states)) = self
337 .cs
338 .possible_transitions_pg(pg_id)
339 .expect("pg exists")
340 .filter_map(|(action, post_states)| {
341 post_states
342 .map(|locs| locs.choose(&mut rand1))
343 .collect_in::<Option<BumpVec<Location>>>(&self.bump)
344 .map(|locs| (action, locs))
346 })
347 .choose(&mut self.rng)
348 {
349 let event = self
350 .cs
351 .transition(pg_id, action, post_states.as_slice())
352 .expect("successful transition");
353 if event.is_some() {
354 return event.map(|ev| (action, ev));
355 }
356 }
357 }
358 }
359 None
360 }
361}