Skip to content

Commit 49a8359

Browse files
author
GBathie
committed
Add LTL operators: Release, Strong Next, Weak Next
1 parent 929ad2e commit 49a8359

File tree

5 files changed

+165
-20
lines changed

5 files changed

+165
-20
lines changed

src/bool/cv.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,39 @@ impl CharVec {
4848
let values = self.values.bitxor(sv.values);
4949
SatVec { values }
5050
}
51+
52+
#[inline]
53+
pub fn implies(self, rhs: Self) -> Self {
54+
let CharVec { values: x, length } = self;
55+
let CharVec {
56+
values: y,
57+
length: _l2,
58+
} = rhs;
59+
let values = x.not().bitor(y);
60+
let values = if self.length < 128 {
61+
values & ((1u128 << self.length) - 1)
62+
} else {
63+
values
64+
};
65+
CharVec { values, length }
66+
}
67+
68+
#[inline]
69+
pub fn equiv(self, rhs: Self) -> Self {
70+
let CharVec { values: x, length } = self;
71+
let CharVec {
72+
values: y,
73+
length: _l2,
74+
} = rhs;
75+
76+
let values = (x.bitxor(y)).not();
77+
let values = if self.length < 128 {
78+
values & ((1u128 << self.length) - 1)
79+
} else {
80+
values
81+
};
82+
CharVec { values, length }
83+
}
5184
}
5285

5386
impl Not for CharVec {
@@ -135,7 +168,7 @@ impl FromIterator<bool> for CharVec {
135168

136169
#[cfg(test)]
137170
mod tests {
138-
use rand::{rng, Rng};
171+
use rand::{Rng, rng};
139172

140173
use super::*;
141174

@@ -207,4 +240,44 @@ mod tests {
207240
assert_eq!(!(x1 | x2), !x1 & !x2);
208241
}
209242
}
243+
244+
#[test]
245+
fn x_implies_x() {
246+
for _ in 0..100 {
247+
let x = random_vec();
248+
assert_eq!(x.implies(x).values, (1 << x.length) - 1);
249+
}
250+
}
251+
252+
#[test]
253+
fn implies_def() {
254+
for _ in 0..100 {
255+
let (x, y) = random_pair();
256+
assert_eq!(x.implies(y), x.not() | y);
257+
}
258+
}
259+
260+
#[test]
261+
fn x_equiv_x() {
262+
for _ in 0..100 {
263+
let x = random_vec();
264+
assert_eq!(x.equiv(x).values, (1 << x.length) - 1);
265+
}
266+
}
267+
268+
#[test]
269+
fn equiv_iff_double_implies() {
270+
for _ in 0..100 {
271+
let (x, y) = random_pair();
272+
assert_eq!(x.equiv(y), x.implies(y) & y.implies(x));
273+
}
274+
}
275+
276+
#[test]
277+
fn equiv_def() {
278+
for _ in 0..100 {
279+
let (x, y) = random_pair();
280+
assert_eq!(x.equiv(y), (x & y) | (x.not() & y.not()));
281+
}
282+
}
210283
}

src/ltl/cm.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ impl CharMatrix {
7474
self.seqs.iter().map(|x| x.accepts()).collect()
7575
}
7676

77-
op_for_cm!(next, globally, finally);
77+
op_for_cm!(weak_next, strong_next, globally, finally);
7878
binop_for_cm!(bitor as or);
7979
binop_for_cm!(bitand as and);
8080
binop_for_cm!(until);
81+
binop_for_cm!(release);
8182
binop_for_cm!(implies);
8283
binop_for_cm!(equiv);
8384
}

src/ltl/cs.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,23 @@ impl CharSeq {
109109
CharSeq { values, length }
110110
}
111111

112-
/// LTL Next operator (X)
112+
/// LTL (strong) Next operator (X[!])
113113
#[inline]
114-
pub(crate) fn next(mut self) -> Self {
114+
pub(crate) fn strong_next(mut self) -> Self {
115115
self.values >>= 1;
116116
self
117117
}
118118

119+
/// LTL (weak) Next operator (X)
120+
#[inline]
121+
pub(crate) fn weak_next(mut self) -> Self {
122+
self.values >>= 1;
123+
if self.length > 0 {
124+
self.values |= 1u64 << (self.length - 1);
125+
}
126+
self
127+
}
128+
119129
/// LTL Globally operator (G)
120130
#[inline]
121131
pub(crate) fn globally(self) -> Self {
@@ -162,6 +172,12 @@ impl CharSeq {
162172
y |= x & (y >> 32);
163173
CharSeq { values: y, length }
164174
}
175+
176+
/// LTL Release operator (R)
177+
#[inline]
178+
pub(crate) fn release(self, rhs: Self) -> Self {
179+
self.not().until(rhs.not()).not()
180+
}
165181
}
166182

167183
impl Debug for CharSeq {
@@ -208,7 +224,12 @@ mod tests {
208224

209225
#[allow(non_snake_case)]
210226
pub(crate) fn X(phi: CharSeq) -> CharSeq {
211-
phi.next()
227+
phi.weak_next()
228+
}
229+
230+
#[allow(non_snake_case)]
231+
pub(crate) fn X_s(phi: CharSeq) -> CharSeq {
232+
phi.strong_next()
212233
}
213234

214235
#[allow(non_snake_case)]
@@ -226,6 +247,11 @@ mod tests {
226247
phi.until(psi)
227248
}
228249

250+
#[allow(non_snake_case)]
251+
pub(crate) fn R(phi: CharSeq, psi: CharSeq) -> CharSeq {
252+
phi.release(psi)
253+
}
254+
229255
fn random_seq_with_len(len: usize, rng: &mut impl Rng) -> CharSeq {
230256
let x: u64 = rng.random();
231257
let x = if len < 64 { x & ((1u64 << len) - 1) } else { x };
@@ -320,7 +346,7 @@ mod tests {
320346
fn f_as_phi_or_x_f_phi() {
321347
for _ in 0..100 {
322348
let x = random_seq();
323-
assert_eq!(F(x), x | X(F(x)));
349+
assert_eq!(F(x), x | X_s(F(x)));
324350
}
325351
}
326352

@@ -352,7 +378,23 @@ mod tests {
352378
fn expand_u() {
353379
for _ in 0..100 {
354380
let (x, y) = random_pair();
355-
assert_eq!(U(x, y), y | (x & X(U(x, y))));
381+
assert_eq!(U(x, y), y | (x & X_s(U(x, y))));
382+
}
383+
}
384+
385+
#[test]
386+
fn expand_r() {
387+
for _ in 0..100 {
388+
let (x, y) = random_pair();
389+
assert_eq!(R(x, y), y & (x | X(R(x, y))));
390+
}
391+
}
392+
393+
#[test]
394+
fn r_dual_u() {
395+
for _ in 0..100 {
396+
let (x, y) = random_pair();
397+
assert_eq!(R(x, y), !(U(!x, !y)));
356398
}
357399
}
358400

src/ops/binary.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ use crate::{bool::cv::CharVec, ltl::cm::CharMatrix};
1111
use super::traits::Commutativity;
1212

1313
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14-
/// Binary LTL Operators: Or, And, Until
14+
/// Binary LTL Operators: Or, And, Until, Release, Implication and Equivalence
1515
pub enum LtlBinaryOp {
1616
Or,
1717
And,
1818
Until,
19+
Release,
1920
Implies,
2021
Equivalent,
2122
}
@@ -24,12 +25,15 @@ impl LtlBinaryOp {
2425
/// Returns a list of all binary operators.
2526
pub(crate) fn all() -> Vec<LtlBinaryOp> {
2627
use LtlBinaryOp::*;
27-
vec![Or, And, Until, Implies, Equivalent]
28+
vec![Or, And, Until, Release, Implies, Equivalent]
2829
}
2930

3031
/// Whether this LTL operator is boolean.
3132
pub(crate) fn is_boolean(&self) -> bool {
32-
matches!(self, LtlBinaryOp::Or | LtlBinaryOp::And)
33+
matches!(
34+
self,
35+
LtlBinaryOp::Or | LtlBinaryOp::And | LtlBinaryOp::Implies | LtlBinaryOp::Equivalent
36+
)
3337
}
3438

3539
/// Apply the operator to two characteristic vectors.
@@ -41,6 +45,8 @@ impl LtlBinaryOp {
4145
match op {
4246
LtlBinaryOp::Or => lhs.bitor(rhs),
4347
LtlBinaryOp::And => lhs.bitand(rhs),
48+
LtlBinaryOp::Implies => lhs.implies(rhs),
49+
LtlBinaryOp::Equivalent => lhs.equiv(rhs),
4450
_ => panic!(
4551
"Cannot apply non-boolean operator {} to characteristic vectors",
4652
op
@@ -54,6 +60,7 @@ impl LtlBinaryOp {
5460
LtlBinaryOp::Or => lhs.or(rhs),
5561
LtlBinaryOp::And => lhs.and(rhs),
5662
LtlBinaryOp::Until => lhs.until(rhs),
63+
LtlBinaryOp::Release => lhs.release(rhs),
5764
LtlBinaryOp::Implies => lhs.implies(rhs),
5865
LtlBinaryOp::Equivalent => lhs.equiv(rhs),
5966
}
@@ -64,7 +71,7 @@ impl Commutativity for LtlBinaryOp {
6471
fn commutes(&self) -> bool {
6572
match self {
6673
LtlBinaryOp::Or | LtlBinaryOp::And | LtlBinaryOp::Equivalent => true,
67-
LtlBinaryOp::Until | LtlBinaryOp::Implies => false,
74+
LtlBinaryOp::Until | LtlBinaryOp::Release | LtlBinaryOp::Implies => false,
6875
}
6976
}
7077
}
@@ -85,6 +92,7 @@ impl<'a> TryFrom<&'a str> for LtlBinaryOp {
8592
/// | `"\|"` | [`LtlBinaryOp::Or`] |
8693
/// | `"&"` | [`LtlBinaryOp::And`] |
8794
/// | `"U"` | [`LtlBinaryOp::Until`]|
95+
/// | `"R"` | [`LtlBinaryOp::Release`]|
8896
/// | `"->"` | [`LtlBinaryOp::Implies`]|
8997
/// | `"<->"` | [`LtlBinaryOp::Equivalent`]|
9098
/// | Other value | `Error` |
@@ -93,6 +101,7 @@ impl<'a> TryFrom<&'a str> for LtlBinaryOp {
93101
"|" => Ok(LtlBinaryOp::Or),
94102
"&" => Ok(LtlBinaryOp::And),
95103
"U" => Ok(LtlBinaryOp::Until),
104+
"R" => Ok(LtlBinaryOp::Release),
96105
"->" => Ok(LtlBinaryOp::Implies),
97106
"<->" => Ok(LtlBinaryOp::Equivalent),
98107
_ => Err(InvalidBinaryOp(value)),
@@ -106,6 +115,7 @@ impl Display for LtlBinaryOp {
106115
LtlBinaryOp::And => write!(f, "&"),
107116
LtlBinaryOp::Or => write!(f, "|"),
108117
LtlBinaryOp::Until => write!(f, "U"),
118+
LtlBinaryOp::Release => write!(f, "R"),
109119
LtlBinaryOp::Implies => write!(f, "->"),
110120
LtlBinaryOp::Equivalent => write!(f, "<->"),
111121
}
@@ -127,6 +137,15 @@ mod test {
127137
let parsed = "U".try_into();
128138
assert_eq!(parsed, Ok(LtlBinaryOp::Until));
129139

140+
let parsed = "R".try_into();
141+
assert_eq!(parsed, Ok(LtlBinaryOp::Release));
142+
143+
let parsed = "->".try_into();
144+
assert_eq!(parsed, Ok(LtlBinaryOp::Implies));
145+
146+
let parsed = "<->".try_into();
147+
assert_eq!(parsed, Ok(LtlBinaryOp::Equivalent));
148+
130149
let parsed: Result<LtlBinaryOp, _> = ":".try_into();
131150
assert!(parsed.is_err());
132151
}

src/ops/unary.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,34 @@ use crate::ltl::cm::CharMatrix;
88
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
99
pub enum LtlUnaryOp {
1010
// Not,
11-
Next,
11+
WeakNext,
12+
StrongNext,
1213
Finally,
1314
Globally,
1415
}
1516

1617
impl LtlUnaryOp {
1718
pub(crate) fn all() -> Vec<LtlUnaryOp> {
1819
use LtlUnaryOp::*;
19-
vec![Next, Finally, Globally]
20-
// vec![Not, Next, Finally, Globally]
20+
vec![WeakNext, StrongNext, Finally, Globally]
21+
// vec![Not, WeakNext, StrongNext, Finally, Globally]
2122
}
2223

2324
pub(crate) fn is_boolean(&self) -> bool {
2425
match self {
2526
// LtlUnaryOp::Not => true,
26-
LtlUnaryOp::Next | LtlUnaryOp::Finally | LtlUnaryOp::Globally => false,
27+
LtlUnaryOp::WeakNext
28+
| LtlUnaryOp::StrongNext
29+
| LtlUnaryOp::Finally
30+
| LtlUnaryOp::Globally => false,
2731
}
2832
}
2933

3034
pub(crate) fn apply_cm(op: Self, cm: &CharMatrix) -> CharMatrix {
3135
match op {
3236
// LtlUnaryOp::Not => cm.not(),
33-
LtlUnaryOp::Next => cm.next(),
37+
LtlUnaryOp::WeakNext => cm.weak_next(),
38+
LtlUnaryOp::StrongNext => cm.strong_next(),
3439
LtlUnaryOp::Finally => cm.finally(),
3540
LtlUnaryOp::Globally => cm.globally(),
3641
}
@@ -50,13 +55,15 @@ impl<'a> TryFrom<&'a str> for LtlUnaryOp {
5055
///
5156
/// | String | Result |
5257
/// |:-------|:---------------------|
53-
/// | `"X"` | [`LtlUnaryOp::Next`] |
58+
/// | `"X"` | [`LtlUnaryOp::WeakNext`] |
59+
/// | `"X[!]"` | [`LtlUnaryOp::StrongNext`] |
5460
/// | `"F"` | [`LtlUnaryOp::Finally`] |
5561
/// | `"G"` | [`LtlUnaryOp::Globally`]|
5662
/// | Other value | `Error` |
5763
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
5864
match value {
59-
"X" => Ok(LtlUnaryOp::Next),
65+
"X" => Ok(LtlUnaryOp::WeakNext),
66+
"X[!]" => Ok(LtlUnaryOp::StrongNext),
6067
"F" => Ok(LtlUnaryOp::Finally),
6168
"G" => Ok(LtlUnaryOp::Globally),
6269
_ => Err(InvalidUnaryOp(value)),
@@ -67,7 +74,8 @@ impl<'a> TryFrom<&'a str> for LtlUnaryOp {
6774
impl Display for LtlUnaryOp {
6875
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6976
match self {
70-
LtlUnaryOp::Next => write!(f, "X"),
77+
LtlUnaryOp::WeakNext => write!(f, "X"),
78+
LtlUnaryOp::StrongNext => write!(f, "X[!]"),
7179
LtlUnaryOp::Finally => write!(f, "F"),
7280
LtlUnaryOp::Globally => write!(f, "G"),
7381
}
@@ -81,7 +89,9 @@ mod test {
8189
#[test]
8290
fn string_try_into_binary_op() {
8391
let parsed = "X".try_into();
84-
assert_eq!(parsed, Ok(LtlUnaryOp::Next));
92+
assert_eq!(parsed, Ok(LtlUnaryOp::WeakNext));
93+
let parsed = "X[!]".try_into();
94+
assert_eq!(parsed, Ok(LtlUnaryOp::StrongNext));
8595

8696
let parsed = "F".try_into();
8797
assert_eq!(parsed, Ok(LtlUnaryOp::Finally));

0 commit comments

Comments
 (0)