Skip to main content

scan_core/oracle/
pmtl.rs

1mod numset;
2
3use crate::{Oracle, Time};
4use numset::NumSet;
5use std::hash::Hash;
6
7/// A Past-time Metric Temporal Logic (PMTL) formula.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub enum Pmtl<V>
10where
11    V: Clone,
12{
13    /// The true formula.
14    True,
15    /// The false formula.
16    False,
17    /// An atomic formula.
18    Atom(V),
19    /// Logical disjunction of a list of formulae.
20    And(Vec<Pmtl<V>>),
21    /// Logical conjunction of a list of formulae.
22    Or(Vec<Pmtl<V>>),
23    /// Logical negation of a formula.
24    Not(Box<Pmtl<V>>),
25    /// Logical implication of a antecedent formula and a consequent formula.
26    Implies(Box<(Pmtl<V>, Pmtl<V>)>),
27    /// Temporal historical predicate over a formula (with bounds).
28    Historically(Box<Pmtl<V>>, Time, Time),
29    /// Temporal previously predicate over a formula (with bounds).
30    Once(Box<Pmtl<V>>, Time, Time),
31    /// Temporal since predicate over a formula (with bounds).
32    Since(Box<(Pmtl<V>, Pmtl<V>)>, Time, Time),
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36enum ValPmtl {
37    True,
38    False,
39    Atom(usize, bool),
40    And(Vec<ValPmtl>),
41    Or(Vec<ValPmtl>),
42    Not(Box<ValPmtl>),
43    Implies(Box<(ValPmtl, ValPmtl)>),
44    Historically(Box<ValPmtl>, Time, Time, NumSet),
45    Once(Box<ValPmtl>, Time, Time, NumSet),
46    Since(Box<(ValPmtl, ValPmtl)>, Time, Time, NumSet),
47}
48
49impl From<&Pmtl<usize>> for ValPmtl {
50    fn from(value: &Pmtl<usize>) -> Self {
51        match value {
52            Pmtl::True => ValPmtl::True,
53            Pmtl::False => ValPmtl::False,
54            Pmtl::Atom(a) => ValPmtl::Atom(*a, false),
55            Pmtl::And(pmtls) => ValPmtl::And(pmtls.iter().map(|f| f.into()).collect()),
56            Pmtl::Or(pmtls) => ValPmtl::Or(pmtls.iter().map(|f| f.into()).collect()),
57            Pmtl::Not(pmtl) => ValPmtl::Not(Box::new(pmtl.as_ref().into())),
58            Pmtl::Implies(pmtls) => {
59                ValPmtl::Implies(Box::new(((&pmtls.0).into(), (&pmtls.1).into())))
60            }
61            Pmtl::Historically(pmtl, lb, ub) => {
62                ValPmtl::Historically(Box::new(pmtl.as_ref().into()), *lb, *ub, NumSet::empty())
63            }
64            Pmtl::Once(pmtl, lb, ub) => {
65                ValPmtl::Once(Box::new(pmtl.as_ref().into()), *lb, *ub, NumSet::empty())
66            }
67            Pmtl::Since(pmtls, lb, ub) => ValPmtl::Since(
68                Box::new(((&pmtls.0).into(), (&pmtls.1).into())),
69                *lb,
70                *ub,
71                NumSet::empty(),
72            ),
73        }
74    }
75}
76
77impl ValPmtl {
78    // From Ulus 2024: Online monitoring of metric temporal logic using sequential networks. Link: <http://arxiv.org/abs/1901.00175v2>
79    #[inline(always)]
80    pub fn output(&self, time: Time) -> bool {
81        match self {
82            ValPmtl::True => true,
83            ValPmtl::False => false,
84            ValPmtl::Atom(_atom, b) => *b,
85            ValPmtl::And(subs) => subs.iter().all(|f| f.output(time)),
86            ValPmtl::Or(subs) => subs.iter().any(|f| f.output(time)),
87            ValPmtl::Not(subformula) => !subformula.output(time),
88            ValPmtl::Implies(subformulae) => {
89                let (lhs, rhs) = subformulae.as_ref();
90                rhs.output(time) || !lhs.output(time)
91            }
92            ValPmtl::Historically(_sub, _lower_bound, _upper_bound, valuation) => {
93                !valuation.contains(time)
94            }
95            ValPmtl::Once(_sub, _lower_bound, _upper_bound, valuation) => valuation.contains(time),
96            ValPmtl::Since(_subformulae, _lower_bound, _upper_bound, valuation) => {
97                valuation.contains(time)
98            }
99        }
100    }
101
102    pub fn update_state(&mut self, state: &[bool], time: Time) {
103        match self {
104            ValPmtl::Atom(atom, b) => {
105                *b = state[*atom];
106            }
107            ValPmtl::And(subs) | ValPmtl::Or(subs) => {
108                // Make sure all subformulae are updated
109                subs.iter_mut().for_each(|f| f.update_state(state, time));
110            }
111            ValPmtl::Not(subformula) => {
112                subformula.update_state(state, time);
113            }
114            ValPmtl::Implies(subformulae) => {
115                let (lhs, rhs) = subformulae.as_mut();
116                // Make sure all subformulae are updated
117                lhs.update_state(state, time);
118                rhs.update_state(state, time);
119            }
120            ValPmtl::Historically(sub, lower_bound, upper_bound, valuation) => {
121                sub.update_state(state, time);
122                if !sub.output(time) {
123                    valuation.add_interval(
124                        lower_bound.saturating_add(time),
125                        upper_bound.saturating_add(time),
126                    );
127                }
128            }
129            ValPmtl::Once(sub, lower_bound, upper_bound, valuation) => {
130                sub.update_state(state, time);
131                if sub.output(time) {
132                    valuation.add_interval(
133                        lower_bound.saturating_add(time),
134                        upper_bound.saturating_add(time),
135                    );
136                }
137            }
138            ValPmtl::Since(subformulae, lower_bound, upper_bound, valuation) => {
139                let (lhs, rhs) = subformulae.as_mut();
140                // Make sure all subformulae are updated
141                lhs.update_state(state, time);
142                let out_lhs = lhs.output(time);
143                rhs.update_state(state, time);
144                let out_rhs = rhs.output(time);
145                if out_lhs {
146                    if out_rhs {
147                        valuation.add_interval(
148                            lower_bound.saturating_add(time),
149                            upper_bound.saturating_add(time),
150                        );
151                    }
152                } else if out_rhs {
153                    *valuation = NumSet::from_range(
154                        lower_bound.saturating_add(time),
155                        upper_bound.saturating_add(time),
156                    );
157                } else {
158                    *valuation = NumSet::empty();
159                }
160            }
161            _ => {
162                // nothing to do
163            }
164        }
165    }
166
167    pub fn update_time(&mut self, time: Time) {
168        match self {
169            ValPmtl::And(subs) | ValPmtl::Or(subs) => {
170                // Make sure all subformulae are updated
171                subs.iter_mut().for_each(|f| f.update_time(time));
172            }
173            ValPmtl::Not(subformula) => {
174                subformula.update_time(time);
175            }
176            ValPmtl::Implies(subformulae) => {
177                let (lhs, rhs) = subformulae.as_mut();
178                // Make sure all subformulae are updated
179                lhs.update_time(time);
180                rhs.update_time(time);
181            }
182            ValPmtl::Historically(sub, lower_bound, upper_bound, valuation) => {
183                sub.update_time(time);
184                if !sub.output(time) {
185                    valuation.add_interval(
186                        lower_bound.saturating_add(time),
187                        upper_bound.saturating_add(time),
188                    );
189                }
190            }
191            ValPmtl::Once(sub, lower_bound, upper_bound, valuation) => {
192                sub.update_time(time);
193                if sub.output(time) {
194                    valuation.add_interval(
195                        lower_bound.saturating_add(time),
196                        upper_bound.saturating_add(time),
197                    );
198                }
199            }
200            ValPmtl::Since(subformulae, lower_bound, upper_bound, valuation) => {
201                let (lhs, rhs) = subformulae.as_mut();
202                // Make sure all subformulae are updated
203                lhs.update_time(time);
204                let out_lhs = lhs.output(time);
205                rhs.update_time(time);
206                let out_rhs = rhs.output(time);
207                if out_lhs {
208                    if out_rhs {
209                        valuation.add_interval(
210                            lower_bound.saturating_add(time),
211                            upper_bound.saturating_add(time),
212                        );
213                    }
214                } else if out_rhs {
215                    *valuation = NumSet::from_range(
216                        lower_bound.saturating_add(time),
217                        upper_bound.saturating_add(time),
218                    );
219                } else {
220                    *valuation = NumSet::empty();
221                }
222            }
223            _ => {
224                // nothing to do
225            }
226        }
227    }
228
229    // Represents the knowledge that the given formula is always true, or always false, from the current moment.
230    pub fn valuation(&self, time: Time) -> Option<bool> {
231        match self {
232            ValPmtl::True => Some(true),
233            ValPmtl::False => Some(false),
234            ValPmtl::Atom(_, _) => None,
235            ValPmtl::And(items) => items
236                .iter()
237                .try_fold(true, |b, f| f.valuation(time).map(|v| b && v)),
238            ValPmtl::Or(items) => items
239                .iter()
240                .try_fold(false, |b, f| f.valuation(time).map(|v| b || v)),
241            ValPmtl::Not(f) => f.valuation(time).map(|b| !b),
242            ValPmtl::Implies(subformulae) => {
243                let lhs_val = subformulae.0.valuation(time);
244                let rhs_val = subformulae.1.valuation(time);
245                if rhs_val.is_some_and(|b| b) || lhs_val.is_some_and(|b| !b) {
246                    Some(true)
247                } else if rhs_val.is_some_and(|b| !b) && lhs_val.is_some_and(|b| b) {
248                    Some(false)
249                } else {
250                    None
251                }
252            }
253            // Valuation of Historically represents the set of time moments in which we know the formula to be false.
254            // If the argument of the operator is know to be always true, then Historically is always true.
255            // If the argument of the operator is know to be always false, then Historically is always false provided that the lower time bound is 0.
256            ValPmtl::Historically(subformula, lb, _, valuation) => subformula
257                .valuation(time)
258                .and_then(|v| (*lb == 0 || v).then_some(v))
259                .or_else(|| valuation.contains_unbounded_interval(time).then_some(false)),
260            // Valuation of Previously represents the set of time moments in which we know the formula to be false.
261            // If the argument of the operator is know to be always true, then Previously is always true provided that the lower time bound is 0.
262            // If the argument of the operator is know to be always false, then Previously is always false.
263            ValPmtl::Once(subformula, lb, _, valuation) => subformula
264                .valuation(time)
265                .and_then(|v| (*lb == 0 || !v).then_some(v))
266                .or_else(|| valuation.contains_unbounded_interval(time).then_some(true)),
267            // Valuation of Since represents the set of time moments in which we know the formula to be true if its lhs argument is true from then on.
268            // If the rhs argument of the operator is know to be always false, then Since is always false.
269            // If the Since valuation is always true and the lhs argument of the operator is know to be always true, then Since is always true.
270            // If the Since valuation is always true and the lhs argument of the operator is know to be always false, then Since is always false provided that the lower time bound is 0.
271            ValPmtl::Since(subformulae, lb, _, valuation) => {
272                if let Some(false) = subformulae.1.valuation(time) {
273                    Some(false)
274                } else if valuation.contains_unbounded_interval(time) {
275                    subformulae
276                        .0
277                        .valuation(time)
278                        .and_then(|v| (*lb == 0 || v).then_some(v))
279                } else {
280                    None
281                }
282            }
283        }
284    }
285}
286
287/// An oracle for PMTL properties over timed, dense traces.
288#[derive(Debug, Clone)]
289pub struct PmtlOracle {
290    assumes: Vec<ValPmtl>,
291    guarantees: Vec<ValPmtl>,
292    time: Time,
293}
294
295impl PmtlOracle {
296    /// Creates an oracle from assumes and guarantees PMTL formulae.
297    pub fn new(assumes: &[Pmtl<usize>], guarantees: &[Pmtl<usize>]) -> Self {
298        Self {
299            assumes: assumes.iter().map(|f| f.into()).collect(),
300            guarantees: guarantees.iter().map(|f| f.into()).collect(),
301            time: 0,
302        }
303    }
304}
305
306impl Oracle for PmtlOracle {
307    fn output_assumes(&self) -> impl Iterator<Item = Option<bool>> {
308        self.assumes.iter().map(|f| f.valuation(self.time))
309    }
310
311    fn output_guarantees(&self) -> impl Iterator<Item = Option<bool>> {
312        self.guarantees.iter().map(|f| f.valuation(self.time))
313    }
314
315    fn final_output_assumes(&self) -> impl Iterator<Item = bool> {
316        self.assumes.iter().map(|f| f.output(self.time))
317    }
318
319    fn final_output_guarantees(&self) -> impl Iterator<Item = bool> {
320        self.guarantees.iter().map(|f| f.output(self.time))
321    }
322
323    // From Ulus 2024: Online monitoring of metric temporal logic using sequential networks. Link: <http://arxiv.org/abs/1901.00175v2>
324    fn update_state(&mut self, state: &[bool]) {
325        self.assumes
326            .iter_mut()
327            .for_each(|f| f.update_state(state, self.time));
328        self.guarantees
329            .iter_mut()
330            .for_each(|f| f.update_state(state, self.time));
331    }
332
333    // From Ulus 2024: Online monitoring of metric temporal logic using sequential networks. Link: <http://arxiv.org/abs/1901.00175v2>
334    fn update_time(&mut self, time: Time) {
335        assert!(self.time <= time);
336        self.assumes.iter_mut().for_each(|f| f.update_time(time));
337        self.guarantees.iter_mut().for_each(|f| f.update_time(time));
338        self.time = time;
339    }
340}
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345
346    #[test]
347    fn since_1() {
348        let formula = Pmtl::Since(Box::new((Pmtl::Atom(0), Pmtl::Atom(1))), 0, Time::MAX);
349        let mut oracle = PmtlOracle::new(&[], &[formula]);
350        oracle.update_state(&[false, true]);
351        assert!(oracle.final_output_guarantees().any(|b| b));
352        oracle.update_state(&[false, true]);
353        assert!(oracle.final_output_guarantees().any(|b| b));
354        oracle.update_time(1);
355        assert!(oracle.final_output_guarantees().any(|b| b));
356        oracle.update_state(&[true, true]);
357        assert!(oracle.final_output_guarantees().any(|b| b));
358        oracle.update_time(2);
359        assert!(oracle.final_output_guarantees().any(|b| b));
360        oracle.update_state(&[true, true]);
361        assert!(oracle.final_output_guarantees().any(|b| b));
362        oracle.update_time(3);
363        assert!(oracle.final_output_guarantees().any(|b| b));
364        oracle.update_state(&[true, false]);
365        assert!(oracle.final_output_guarantees().any(|b| b));
366        oracle.update_time(4);
367        assert!(oracle.final_output_guarantees().any(|b| b));
368        oracle.update_state(&[false, false]);
369        assert!(oracle.final_output_guarantees().any(|b| !b));
370    }
371
372    #[test]
373    fn since_2() {
374        let formula = Pmtl::Since(Box::new((Pmtl::Atom(0), Pmtl::Atom(1))), 0, 2);
375        let mut oracle = PmtlOracle::new(&[], &[formula]);
376        oracle.update_state(&[false, true]);
377        assert!(oracle.final_output_guarantees().any(|b| b));
378        oracle.update_state(&[false, true]);
379        assert!(oracle.final_output_guarantees().any(|b| b));
380        oracle.update_time(1);
381        assert!(oracle.final_output_guarantees().any(|b| b));
382        oracle.update_state(&[true, true]);
383        assert!(oracle.final_output_guarantees().any(|b| b));
384        oracle.update_time(2);
385        assert!(oracle.final_output_guarantees().any(|b| b));
386        oracle.update_state(&[true, false]);
387        assert!(oracle.final_output_guarantees().any(|b| b));
388        oracle.update_time(3);
389        assert!(oracle.final_output_guarantees().any(|b| b));
390        oracle.update_state(&[true, false]);
391        assert!(oracle.final_output_guarantees().any(|b| b));
392        oracle.update_time(4);
393        assert!(oracle.final_output_guarantees().any(|b| b));
394        oracle.update_state(&[true, false]);
395        assert!(oracle.final_output_guarantees().any(|b| b));
396        oracle.update_time(5);
397        assert!(oracle.final_output_guarantees().any(|b| !b));
398    }
399
400    #[test]
401    fn since_3() {
402        let formula = Pmtl::Since(Box::new((Pmtl::Atom(0), Pmtl::Atom(1))), 1, 2);
403        let mut oracle = PmtlOracle::new(&[], &[formula]);
404        oracle.update_state(&[false, true]);
405        assert!(oracle.final_output_guarantees().any(|b| !b));
406        oracle.update_state(&[false, true]);
407        assert!(oracle.final_output_guarantees().any(|b| !b));
408        oracle.update_time(1);
409        assert!(oracle.final_output_guarantees().any(|b| !b));
410        oracle.update_state(&[true, true]);
411        assert!(oracle.final_output_guarantees().any(|b| !b));
412        oracle.update_time(2);
413        assert!(oracle.final_output_guarantees().any(|b| b));
414        oracle.update_state(&[true, false]);
415        assert!(oracle.final_output_guarantees().any(|b| b));
416        oracle.update_time(3);
417        assert!(oracle.final_output_guarantees().any(|b| b));
418        oracle.update_state(&[true, false]);
419        assert!(oracle.final_output_guarantees().any(|b| b));
420        oracle.update_time(4);
421        assert!(oracle.final_output_guarantees().any(|b| b));
422        oracle.update_state(&[true, false]);
423        assert!(oracle.final_output_guarantees().any(|b| b));
424        oracle.update_time(5);
425        assert!(oracle.final_output_guarantees().any(|b| !b));
426    }
427
428    //     #[test]
429    //     fn since_4() {
430    //         let formula = Pmtl::Since(Box::new((Pmtl::Atom(0), Pmtl::Atom(1))), 1, 2);
431    //         let oracle = PmtlOracle::new(&[], &[formula]);
432    //         let mut state = oracle.generate();
433    //         state.update(&[false, true], 0);
434    //         assert!(state.final_output_guarantees().any(|b| !b));
435    //         state.update(&[false, true], 1);
436    //         assert!(state.final_output_guarantees().any(|b| !b));
437    //         state.update(&[false, true], 2);
438    //         assert!(state.final_output_guarantees().any(|b| !b));
439    //         state.update(&[true, true], 2);
440    //         assert!(state.final_output_guarantees().any(|b| !b));
441    //         state.update(&[true, false], 3);
442    //         assert!(state.final_output_guarantees().any(|b| b));
443    //         state.update(&[true, false], 4);
444    //         assert!(state.final_output_guarantees().any(|b| b));
445    //         state.update(&[true, false], 5);
446    //         assert!(state.final_output_guarantees().any(|b| !b));
447    //     }
448
449    //     #[test]
450    //     fn historically() {
451    //         let formula = Pmtl::Historically(Box::new(Pmtl::Atom(0)), 1, 2);
452    //         let oracle = PmtlOracle::new(&[], &[formula]);
453    //         let mut state = oracle.generate();
454    //         state.update(&[false], 0);
455    //         assert!(state.final_output_guarantees().any(|b| b));
456    //         state.update(&[false], 0);
457    //         assert!(state.final_output_guarantees().any(|b| b));
458    //         state.update(&[true], 1);
459    //         assert!(state.final_output_guarantees().any(|b| !b));
460    //         state.update(&[true], 2);
461    //         assert!(state.final_output_guarantees().any(|b| !b));
462    //         state.update(&[true], 3);
463    //         assert!(state.final_output_guarantees().any(|b| b));
464    //         state.update(&[false], 3);
465    //         assert!(state.final_output_guarantees().any(|b| b));
466    //         state.update(&[true], 4);
467    //         assert!(state.final_output_guarantees().any(|b| !b));
468    //     }
469
470    //     #[test]
471    //     fn previously() {
472    //         let formula = Pmtl::Once(Box::new(Pmtl::Atom(0)), 1, 2);
473    //         let oracle = PmtlOracle::new(&[], &[formula]);
474    //         let mut state = oracle.generate();
475    //         state.update(&[false], 0);
476    //         assert!(state.final_output_guarantees().any(|b| !b));
477    //         state.update(&[false], 0);
478    //         assert!(state.final_output_guarantees().any(|b| !b));
479    //         state.update(&[true], 1);
480    //         assert!(state.final_output_guarantees().any(|b| !b));
481    //         state.update(&[false], 2);
482    //         assert!(state.final_output_guarantees().any(|b| b));
483    //         state.update(&[false], 3);
484    //         assert!(state.final_output_guarantees().any(|b| b));
485    //         state.update(&[false], 3);
486    //         assert!(state.final_output_guarantees().any(|b| b));
487    //         state.update(&[true], 4);
488    //         assert!(state.final_output_guarantees().any(|b| !b));
489    //     }
490}