use serde::{Deserialize, Serialize};
use super::Constraint;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LTLOperator {
Always(Box<LTLFormula>),
Eventually(Box<LTLFormula>),
Next(Box<LTLFormula>),
Until(Box<LTLFormula>, Box<LTLFormula>),
Release(Box<LTLFormula>, Box<LTLFormula>),
And(Box<LTLFormula>, Box<LTLFormula>),
Or(Box<LTLFormula>, Box<LTLFormula>),
Not(Box<LTLFormula>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LTLFormula {
Atom(Constraint),
Operator(LTLOperator),
}
impl LTLFormula {
pub fn always(formula: LTLFormula) -> Self {
Self::Operator(LTLOperator::Always(Box::new(formula)))
}
pub fn eventually(formula: LTLFormula) -> Self {
Self::Operator(LTLOperator::Eventually(Box::new(formula)))
}
pub fn next(formula: LTLFormula) -> Self {
Self::Operator(LTLOperator::Next(Box::new(formula)))
}
pub fn until(phi: LTLFormula, psi: LTLFormula) -> Self {
Self::Operator(LTLOperator::Until(Box::new(phi), Box::new(psi)))
}
pub fn release(phi: LTLFormula, psi: LTLFormula) -> Self {
Self::Operator(LTLOperator::Release(Box::new(phi), Box::new(psi)))
}
pub fn and(left: LTLFormula, right: LTLFormula) -> Self {
Self::Operator(LTLOperator::And(Box::new(left), Box::new(right)))
}
pub fn or(left: LTLFormula, right: LTLFormula) -> Self {
Self::Operator(LTLOperator::Or(Box::new(left), Box::new(right)))
}
#[allow(clippy::should_implement_trait)]
pub fn not(formula: LTLFormula) -> Self {
Self::Operator(LTLOperator::Not(Box::new(formula)))
}
pub fn atom(constraint: Constraint) -> Self {
Self::Atom(constraint)
}
}
#[derive(Debug, Clone)]
pub struct LTLChecker {
formula: LTLFormula,
trace: Vec<f32>,
max_trace_length: usize,
}
impl LTLChecker {
pub fn new(formula: LTLFormula) -> Self {
Self {
formula,
trace: Vec::new(),
max_trace_length: 100,
}
}
pub fn with_max_trace_length(mut self, length: usize) -> Self {
self.max_trace_length = length;
self
}
pub fn push(&mut self, value: f32) {
self.trace.push(value);
if self.trace.len() > self.max_trace_length {
self.trace.remove(0);
}
}
pub fn check(&self) -> bool {
if self.trace.is_empty() {
return true;
}
self.check_formula(&self.formula, 0)
}
fn check_formula(&self, formula: <LFormula, pos: usize) -> bool {
if pos >= self.trace.len() {
return true;
}
match formula {
LTLFormula::Atom(constraint) => constraint.check(self.trace[pos]),
LTLFormula::Operator(op) => self.check_operator(op, pos),
}
}
fn check_operator(&self, op: <LOperator, pos: usize) -> bool {
match op {
LTLOperator::Always(phi) => {
(pos..self.trace.len()).all(|i| self.check_formula(phi, i))
}
LTLOperator::Eventually(phi) => {
(pos..self.trace.len()).any(|i| self.check_formula(phi, i))
}
LTLOperator::Next(phi) => {
self.check_formula(phi, pos + 1)
}
LTLOperator::Until(phi, psi) => {
for i in pos..self.trace.len() {
if self.check_formula(psi, i) {
return (pos..i).all(|j| self.check_formula(phi, j));
}
}
false
}
LTLOperator::Release(phi, psi) => {
for i in pos..self.trace.len() {
if self.check_formula(phi, i) {
return (pos..=i).all(|j| self.check_formula(psi, j));
}
if !self.check_formula(psi, i) {
return false;
}
}
(pos..self.trace.len()).all(|i| self.check_formula(psi, i))
}
LTLOperator::And(left, right) => {
self.check_formula(left, pos) && self.check_formula(right, pos)
}
LTLOperator::Or(left, right) => {
self.check_formula(left, pos) || self.check_formula(right, pos)
}
LTLOperator::Not(phi) => !self.check_formula(phi, pos),
}
}
pub fn violation(&self) -> f32 {
self.count_violations(&self.formula, 0) as f32
}
fn count_violations(&self, formula: <LFormula, pos: usize) -> usize {
if pos >= self.trace.len() {
return 0;
}
match formula {
LTLFormula::Atom(constraint) => {
if constraint.check(self.trace[pos]) {
0
} else {
1
}
}
LTLFormula::Operator(op) => match op {
LTLOperator::Always(phi) => (pos..self.trace.len())
.map(|i| self.count_violations(phi, i))
.sum(),
LTLOperator::Eventually(phi) => {
if (pos..self.trace.len()).any(|i| self.check_formula(phi, i)) {
0
} else {
1
}
}
LTLOperator::Next(phi) => self.count_violations(phi, pos + 1),
LTLOperator::And(left, right) => {
self.count_violations(left, pos) + self.count_violations(right, pos)
}
LTLOperator::Or(left, right) => self
.count_violations(left, pos)
.min(self.count_violations(right, pos)),
LTLOperator::Not(phi) => {
if self.check_formula(phi, pos) {
1
} else {
0
}
}
_ => {
if self.check_operator(op, pos) {
0
} else {
1
}
}
},
}
}
pub fn reset(&mut self) {
self.trace.clear();
}
pub fn trace_len(&self) -> usize {
self.trace.len()
}
}