1#![warn(missing_docs)]
7#![forbid(unsafe_code)]
8
9pub mod channel_system;
10mod cs_model;
11mod dummy_rng;
12mod grammar;
13mod oracle;
14mod pg_model;
15pub mod program_graph;
16mod smc;
17mod transition_system;
18
19use core::marker::Sync;
20pub use cs_model::{Atom, CsModel, CsModelRun};
21use dummy_rng::DummyRng;
22pub use grammar::*;
23use log::{info, trace};
24pub use oracle::*;
25pub use pg_model::{PgModel, PgModelRun};
26use rayon::iter::{IntoParallelIterator, ParallelIterator};
27pub use smc::*;
28use std::{
29 sync::{
30 Arc, Mutex,
31 atomic::{AtomicBool, AtomicU32, Ordering},
32 },
33 time::Instant,
34};
35pub use transition_system::{Tracer, TransitionSystem, TransitionSystemGenerator};
36
37pub type Time = u32;
39
40#[derive(Debug, Clone)]
42pub enum RunOutcome {
43 Incomplete,
45 Verified(Vec<bool>),
47}
48
49#[derive(Debug, Clone)]
54pub struct Scan<TsG, O> {
55 tsd: TsG,
56 oracle: O,
57 running: Arc<AtomicBool>,
58 successes: Arc<AtomicU32>,
59 failures: Arc<AtomicU32>,
60 violations: Arc<Mutex<Vec<u32>>>,
61}
62
63impl<TsG, O> Scan<TsG, O> {
64 pub fn new(tsd: TsG, oracle: O) -> Self {
66 Scan {
67 tsd,
68 oracle,
69 running: Arc::new(AtomicBool::new(false)),
70 successes: Arc::new(AtomicU32::new(0)),
71 failures: Arc::new(AtomicU32::new(0)),
72 violations: Arc::new(Mutex::new(Vec::new())),
73 }
74 }
75
76 fn reset(&self) {
77 self.successes.store(0, Ordering::Relaxed);
78 self.failures.store(0, Ordering::Relaxed);
79 self.violations.lock().unwrap().clear();
80 self.running.store(true, Ordering::Relaxed);
81 }
82
83 #[inline]
85 pub fn running(&self) -> bool {
86 self.running.load(Ordering::Relaxed)
87 }
88
89 #[inline]
91 pub fn successes(&self) -> u32 {
92 self.successes.load(Ordering::Relaxed)
93 }
94
95 #[inline]
97 pub fn failures(&self) -> u32 {
98 self.failures.load(Ordering::Relaxed)
99 }
100
101 #[inline]
103 pub fn violations(&self) -> Vec<u32> {
104 self.violations.lock().expect("lock").clone()
105 }
106}
107
108impl<TsG: TransitionSystemGenerator, O: Oracle> Scan<TsG, O> {
109 fn verification(&self, confidence: f64, precision: f64, duration: Time) {
110 let local_successes;
111 let local_failures;
112 let mut ts = self.tsd.generate();
113
114 let result = ts.experiment(duration, self.oracle.clone(), self.running.clone());
115 if !self.running.load(Ordering::Relaxed) {
116 return;
117 }
118 match result {
119 RunOutcome::Verified(guarantees) => {
120 if guarantees.iter().all(|b| *b) {
121 local_successes = self.successes.fetch_add(1, Ordering::Relaxed);
122 local_failures = self.failures.load(Ordering::Relaxed);
123 trace!("runs: {local_successes} successes");
125 } else {
126 local_successes = self.successes.load(Ordering::Relaxed);
127 local_failures = self.failures.fetch_add(1, Ordering::Relaxed);
128 let violations = &mut *self.violations.lock().unwrap();
129 violations.resize(violations.len().max(guarantees.len()), 0);
130 guarantees.iter().zip(violations.iter_mut()).for_each(
131 |(success, violations)| {
132 if !success {
133 *violations += 1;
134 }
135 },
136 );
137 trace!("runs: {local_failures} failures");
139 }
140 }
141 RunOutcome::Incomplete => return,
142 }
143 let runs = local_successes + local_failures;
144 let avg = if runs == 0 {
146 0.5f64
147 } else {
148 local_successes as f64 / runs as f64
149 };
150 if adaptive_bound(avg, confidence, precision) <= runs as f64 {
151 info!("adaptive bound satisfied");
152 self.running.store(false, Ordering::Relaxed);
153 }
154 }
155
156 pub fn adaptive(&self, confidence: f64, precision: f64, duration: Time) {
158 assert!(0f64 < confidence && confidence < 1f64);
160 assert!(0f64 < precision && precision < 1f64);
161
162 self.reset();
163
164 info!("verification starting");
166 let start_time = Instant::now();
167
168 let _ = (0..usize::MAX)
169 .map(|_| self.verification(confidence, precision, duration))
170 .take_while(|_| self.running.load(Ordering::Relaxed))
171 .count();
172
173 let elapsed = start_time.elapsed();
174 info!("verification time elapsed: {elapsed:0.2?}");
175 info!("verification terminating");
176 }
177
178 #[inline]
179 fn trace<'a, T>(&'a self, tracer: T, duration: Time)
180 where
181 T: Tracer<<<TsG as TransitionSystemGenerator>::Ts<'a> as TransitionSystem>::Event>,
182 {
183 let mut ts = self.tsd.generate();
184 ts.trace(duration, self.oracle.clone(), tracer)
185 }
186
187 pub fn traces<'a, T>(&'a self, runs: usize, tracer: T, duration: Time)
190 where
191 T: Clone + Tracer<<<TsG as TransitionSystemGenerator>::Ts<'a> as TransitionSystem>::Event>,
192 {
193 info!("tracing starting");
195 let start_time = Instant::now();
196
197 (0..runs).for_each(|_| self.trace::<T>(tracer.clone(), duration));
198
199 let elapsed = start_time.elapsed();
200 info!("tracing time elapsed: {elapsed:0.2?}");
201 info!("tracing terminating");
202 }
203}
204
205impl<TsG, O> Scan<TsG, O>
206where
207 TsG: TransitionSystemGenerator + Sync,
208 O: Oracle + Sync,
209{
210 pub fn par_adaptive(&self, confidence: f64, precision: f64, duration: Time) {
213 assert!(0f64 < confidence && confidence < 1f64);
214 assert!(0f64 < precision && precision < 1f64);
215
216 self.reset();
217
218 info!("verification starting");
220 let start_time = Instant::now();
221
222 let _ = (0..usize::MAX)
223 .into_par_iter()
224 .map(|_| self.verification(confidence, precision, duration))
225 .take_any_while(|_| self.running.load(Ordering::Relaxed))
226 .count();
227
228 let elapsed = start_time.elapsed();
229 info!("verification time elapsed: {elapsed:0.2?}");
230 info!("verification terminating");
231 }
232
233 pub fn par_traces<'a, T>(&'a self, runs: usize, tracer: T, duration: Time)
237 where
238 T: Clone
239 + Sync
240 + Tracer<<<TsG as TransitionSystemGenerator>::Ts<'a> as TransitionSystem>::Event>,
241 {
242 info!("tracing starting");
244 let start_time = Instant::now();
245
246 (0..runs)
247 .into_par_iter()
248 .for_each(|_| self.trace::<T>(tracer.clone(), duration));
249
250 let elapsed = start_time.elapsed();
251 info!("tracing time elapsed: {elapsed:0.2?}");
252 info!("tracing terminating");
253 }
254}