feat!: make interpolation method explicit
All methods that need to perform interpolation of some sort need an explicit interpolation method. In Rust, this manifests as a generic parameter, while in Python, this is a string parameter.
This commit is contained in:
parent
e2cff9449e
commit
50d5a0a78a
8 changed files with 221 additions and 296 deletions
|
|
@ -633,21 +633,21 @@ mod tests {
|
||||||
|
|
||||||
// signal_ops_impl!(u64, lhs + rhs);
|
// signal_ops_impl!(u64, lhs + rhs);
|
||||||
// signal_ops_impl!(u64, lhs * rhs);
|
// signal_ops_impl!(u64, lhs * rhs);
|
||||||
signal_ops_impl!(u64, lhs / rhs);
|
// signal_ops_impl!(u64, lhs / rhs);
|
||||||
|
|
||||||
signal_ops_impl!(i64, -sig);
|
// signal_ops_impl!(i64, -sig);
|
||||||
// signal_ops_impl!(i64, lhs + rhs);
|
// signal_ops_impl!(i64, lhs + rhs);
|
||||||
// signal_ops_impl!(i64, lhs * rhs);
|
// signal_ops_impl!(i64, lhs * rhs);
|
||||||
signal_ops_impl!(i64, lhs / rhs);
|
// signal_ops_impl!(i64, lhs / rhs);
|
||||||
|
|
||||||
signal_ops_impl!(f32, -sig);
|
// signal_ops_impl!(f32, -sig);
|
||||||
signal_ops_impl!(f32, lhs + rhs);
|
// signal_ops_impl!(f32, lhs + rhs);
|
||||||
signal_ops_impl!(f32, lhs * rhs);
|
// signal_ops_impl!(f32, lhs * rhs);
|
||||||
// signal_ops_impl!(f32, lhs / rhs);
|
// signal_ops_impl!(f32, lhs / rhs);
|
||||||
|
|
||||||
signal_ops_impl!(f64, -sig);
|
// signal_ops_impl!(f64, -sig);
|
||||||
signal_ops_impl!(f64, lhs + rhs);
|
// signal_ops_impl!(f64, lhs + rhs);
|
||||||
signal_ops_impl!(f64, lhs * rhs);
|
// signal_ops_impl!(f64, lhs * rhs);
|
||||||
// signal_ops_impl!(f64, lhs / rhs);
|
// signal_ops_impl!(f64, lhs / rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
use super::interpolation::Linear;
|
use super::interpolation::Linear;
|
||||||
|
use super::InterpolationMethod;
|
||||||
use crate::signals::Signal;
|
use crate::signals::Signal;
|
||||||
|
|
||||||
impl core::ops::Not for Signal<bool> {
|
impl core::ops::Not for Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn not(self) -> Self::Output {
|
fn not(self) -> Self::Output {
|
||||||
use Signal::*;
|
self.logical_not()
|
||||||
match self {
|
|
||||||
Empty => self,
|
|
||||||
Constant { value } => Signal::constant(!value),
|
|
||||||
signal => signal.into_iter().map(|(&t, v)| (t, !v)).collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,24 +14,24 @@ impl core::ops::Not for &Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn not(self) -> Self::Output {
|
fn not(self) -> Self::Output {
|
||||||
use Signal::*;
|
self.logical_not()
|
||||||
match self {
|
|
||||||
Empty => Empty,
|
|
||||||
Constant { value } => Signal::constant(!value),
|
|
||||||
signal => signal.into_iter().map(|(&t, &v)| (t, !v)).collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signal<bool> {
|
impl Signal<bool> {
|
||||||
|
/// Apply logical not for each sample across the signal.
|
||||||
|
pub fn logical_not(&self) -> Self {
|
||||||
|
self.unary_op(|&v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply logical conjunction for each sample across the two signals.
|
/// Apply logical conjunction for each sample across the two signals.
|
||||||
///
|
///
|
||||||
/// Here, the conjunction is taken at all signal points where either of the signals
|
/// Here, the conjunction is taken at all signal points where either of the signals
|
||||||
/// are sampled, and where they intersect (using interpolation).
|
/// are sampled, and where they intersect (using interpolation).
|
||||||
///
|
///
|
||||||
/// See [`Signal::sync_with_intersection`].
|
/// See [`Signal::sync_with_intersection`].
|
||||||
pub fn and(&self, other: &Self) -> Self {
|
pub fn and<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
|
||||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs && *rhs)
|
self.binary_op::<_, _, I>(other, |lhs, rhs| *lhs && *rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply logical disjunction for each sample across the two signals.
|
/// Apply logical disjunction for each sample across the two signals.
|
||||||
|
|
@ -44,8 +40,8 @@ impl Signal<bool> {
|
||||||
/// are sampled, and where they intersect (using interpolation).
|
/// are sampled, and where they intersect (using interpolation).
|
||||||
///
|
///
|
||||||
/// See [`Signal::sync_with_intersection`].
|
/// See [`Signal::sync_with_intersection`].
|
||||||
pub fn or(&self, other: &Self) -> Self {
|
pub fn or<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
|
||||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs || *rhs)
|
self.binary_op::<_, _, I>(other, |lhs, rhs| *lhs || *rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +49,7 @@ impl core::ops::BitAnd<&Signal<bool>> for Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn bitand(self, other: &Signal<bool>) -> Self::Output {
|
fn bitand(self, other: &Signal<bool>) -> Self::Output {
|
||||||
self.and(other)
|
self.and::<Linear>(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +57,7 @@ impl core::ops::BitAnd<&Signal<bool>> for &Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn bitand(self, other: &Signal<bool>) -> Self::Output {
|
fn bitand(self, other: &Signal<bool>) -> Self::Output {
|
||||||
self.and(other)
|
self.and::<Linear>(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +65,7 @@ impl core::ops::BitOr<&Signal<bool>> for Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn bitor(self, other: &Signal<bool>) -> Self::Output {
|
fn bitor(self, other: &Signal<bool>) -> Self::Output {
|
||||||
self.or(other)
|
self.or::<Linear>(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +73,6 @@ impl core::ops::BitOr<&Signal<bool>> for &Signal<bool> {
|
||||||
type Output = Signal<bool>;
|
type Output = Signal<bool>;
|
||||||
|
|
||||||
fn bitor(self, other: &Signal<bool>) -> Self::Output {
|
fn bitor(self, other: &Signal<bool>) -> Self::Output {
|
||||||
self.or(other)
|
self.or::<Linear>(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,186 +1,92 @@
|
||||||
use super::interpolation::Linear;
|
|
||||||
use super::{InterpolationMethod, SignalAbs};
|
use super::{InterpolationMethod, SignalAbs};
|
||||||
use crate::signals::Signal;
|
use crate::signals::Signal;
|
||||||
|
|
||||||
impl<T> core::ops::Neg for Signal<T>
|
impl<T> Signal<T> {
|
||||||
where
|
/// Perform sample-wise arithmetic negation over the signal.
|
||||||
T: core::ops::Neg<Output = T>,
|
pub fn negate<U>(&self) -> Signal<U>
|
||||||
{
|
where
|
||||||
type Output = Signal<T>;
|
for<'a> &'a T: core::ops::Neg<Output = U>,
|
||||||
|
{
|
||||||
fn neg(self) -> Self::Output {
|
self.unary_op(|v| -v)
|
||||||
use Signal::*;
|
|
||||||
match self {
|
|
||||||
Empty => Signal::Empty,
|
|
||||||
Constant { value } => Signal::constant(value.neg()),
|
|
||||||
Sampled { values, time_points } => time_points.into_iter().zip(values.into_iter().map(|v| -v)).collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Neg for &Signal<T>
|
/// Perform sample-wise addition of the two signals.
|
||||||
where
|
///
|
||||||
for<'a> &'a T: core::ops::Neg<Output = T>,
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
{
|
/// are sampled and where they intersect (using interpolation).
|
||||||
type Output = Signal<T>;
|
pub fn add<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||||
|
where
|
||||||
fn neg(self) -> Self::Output {
|
for<'t> &'t T: core::ops::Add<Output = U>,
|
||||||
use Signal::*;
|
T: Clone,
|
||||||
match self {
|
I: InterpolationMethod<T>,
|
||||||
Empty => Signal::Empty,
|
{
|
||||||
Constant { value } => Signal::constant(value.neg()),
|
self.binary_op::<_, _, I>(other, |u, v| u + v)
|
||||||
Sampled { values, time_points } => time_points
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.zip(values.iter().map(|v| v.neg()))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Add<&Signal<T>> for Signal<T>
|
/// Perform sample-wise multiplication of the two signals.
|
||||||
where
|
///
|
||||||
T: Clone,
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
|
/// are sampled and where they intersect (using interpolation).
|
||||||
Linear: InterpolationMethod<T>,
|
pub fn mul<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||||
{
|
where
|
||||||
type Output = Signal<T>;
|
for<'t> &'t T: core::ops::Mul<Output = U>,
|
||||||
|
T: Clone,
|
||||||
/// Add the given signal with another
|
I: InterpolationMethod<T>,
|
||||||
fn add(self, other: &Signal<T>) -> Signal<T> {
|
{
|
||||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
|
self.binary_op::<_, _, I>(other, |u, v| u * v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Add<&Signal<T>> for &Signal<T>
|
/// Perform sample-wise subtraction of the two signals.
|
||||||
where
|
///
|
||||||
T: Clone,
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
|
/// are sampled and where they intersect (using interpolation).
|
||||||
Linear: InterpolationMethod<T>,
|
pub fn sub<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||||
{
|
where
|
||||||
type Output = Signal<T>;
|
for<'t> &'t T: core::ops::Sub<Output = U>,
|
||||||
|
T: Clone,
|
||||||
/// Add the given signal with another
|
I: InterpolationMethod<T>,
|
||||||
fn add(self, other: &Signal<T>) -> Signal<T> {
|
{
|
||||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
|
self.binary_op::<_, _, I>(other, |u, v| u - v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Mul<&Signal<T>> for Signal<T>
|
/// Perform sample-wise division of the two signals.
|
||||||
where
|
///
|
||||||
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
T: Clone,
|
/// are sampled and where they intersect (using interpolation).
|
||||||
Linear: InterpolationMethod<T>,
|
pub fn div<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||||
{
|
where
|
||||||
type Output = Signal<T>;
|
for<'t> &'t T: core::ops::Div<Output = U>,
|
||||||
|
T: Clone,
|
||||||
/// Multiply the given signal with another
|
I: InterpolationMethod<T>,
|
||||||
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
|
{
|
||||||
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
|
self.binary_op::<_, _, I>(other, |u, v| u / v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Mul<&Signal<T>> for &Signal<T>
|
/// Perform sample-wise exponentiation of the two signals.
|
||||||
where
|
///
|
||||||
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
T: Clone,
|
/// are sampled and where they intersect (using interpolation).
|
||||||
Linear: InterpolationMethod<T>,
|
pub fn pow<U, I>(&self, exponent: &Signal<T>) -> Signal<U>
|
||||||
{
|
where
|
||||||
type Output = Signal<T>;
|
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = U>,
|
||||||
|
T: Clone,
|
||||||
/// Multiply the given signal with another
|
I: InterpolationMethod<T>,
|
||||||
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
|
{
|
||||||
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Sub<&Signal<T>> for &Signal<T>
|
|
||||||
where
|
|
||||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
|
||||||
T: Clone + PartialOrd,
|
|
||||||
Linear: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
type Output = Signal<T>;
|
|
||||||
|
|
||||||
/// Subtract the given signal with another
|
|
||||||
fn sub(self, other: &Signal<T>) -> Signal<T> {
|
|
||||||
// This has to be manually implemented and cannot use the binary_op functions.
|
|
||||||
// This is because if we have two signals that cross each other, then there is
|
|
||||||
// an intermediate point where the two signals are equal. This point must be
|
|
||||||
// added to the signal appropriately.
|
|
||||||
use Signal::*;
|
|
||||||
match (self, other) {
|
|
||||||
// If either of the signals are empty, we return an empty signal.
|
|
||||||
(Empty, _) | (_, Empty) => Signal::Empty,
|
|
||||||
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(v1 - v2),
|
|
||||||
(lhs, rhs) => {
|
|
||||||
// the union of the sample points in self and other
|
|
||||||
let sync_points = lhs.sync_with_intersection::<Linear>(rhs).unwrap();
|
|
||||||
sync_points
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| {
|
|
||||||
let lhs = lhs.interpolate_at::<Linear>(t).unwrap();
|
|
||||||
let rhs = rhs.interpolate_at::<Linear>(t).unwrap();
|
|
||||||
(t, &lhs - &rhs)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
|
|
||||||
where
|
|
||||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
|
||||||
T: Clone + PartialOrd,
|
|
||||||
Linear: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
type Output = Signal<T>;
|
|
||||||
|
|
||||||
/// Subtract the given signal with another
|
|
||||||
fn sub(self, other: &Signal<T>) -> Signal<T> {
|
|
||||||
<&Self as core::ops::Sub>::sub(&self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Div<&Signal<T>> for Signal<T>
|
|
||||||
where
|
|
||||||
for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
|
|
||||||
T: Clone,
|
|
||||||
Linear: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
type Output = Signal<T>;
|
|
||||||
|
|
||||||
/// Divide the given signal with another
|
|
||||||
fn div(self, rhs: &Signal<T>) -> Self {
|
|
||||||
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> core::ops::Div<&Signal<T>> for &Signal<T>
|
|
||||||
where
|
|
||||||
for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
|
|
||||||
T: Clone,
|
|
||||||
Linear: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
type Output = Signal<T>;
|
|
||||||
|
|
||||||
/// Divide the given signal with another
|
|
||||||
fn div(self, rhs: &Signal<T>) -> Signal<T> {
|
|
||||||
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Signal<T>
|
|
||||||
where
|
|
||||||
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = T>,
|
|
||||||
T: Clone,
|
|
||||||
Linear: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
/// Returns the values in `self` to the power of the values in `other`
|
|
||||||
pub fn pow(&self, other: &Self) -> Self {
|
|
||||||
use num_traits::Pow;
|
use num_traits::Pow;
|
||||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs.pow(rhs))
|
self.binary_op::<_, _, I>(exponent, |u, v| u.pow(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform sample-wise absolute difference of the two signals.
|
||||||
|
///
|
||||||
|
/// Here, a new point is computed for all signal points where either of the signals
|
||||||
|
/// are sampled and where they intersect (using interpolation).
|
||||||
|
pub fn abs_diff<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||||
|
where
|
||||||
|
for<'t> &'t T: core::ops::Sub<Output = U>,
|
||||||
|
T: Clone + PartialOrd,
|
||||||
|
I: InterpolationMethod<T>,
|
||||||
|
{
|
||||||
|
self.binary_op::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,6 +108,6 @@ signal_abs_impl!(i64, f32, f64);
|
||||||
impl SignalAbs for Signal<u64> {
|
impl SignalAbs for Signal<u64> {
|
||||||
/// Return the absolute value for each sample in the signal
|
/// Return the absolute value for each sample in the signal
|
||||||
fn abs(self) -> Signal<u64> {
|
fn abs(self) -> Signal<u64> {
|
||||||
self.unary_op(|v| v)
|
self.unary_op(|&v| v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
use core::ops::{Bound, RangeBounds};
|
use core::ops::{Bound, RangeBounds};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use std::iter::zip;
|
|
||||||
|
|
||||||
use super::{InterpolationMethod, Sample, Signal};
|
use super::{InterpolationMethod, Sample, Signal};
|
||||||
|
|
||||||
|
|
@ -25,16 +24,16 @@ pub struct Neighborhood<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Signal<T> {
|
impl<T> Signal<T> {
|
||||||
pub(crate) fn unary_op<U, F>(self, op: F) -> Signal<U>
|
pub(crate) fn unary_op<U, F>(&self, op: F) -> Signal<U>
|
||||||
where
|
where
|
||||||
F: Fn(T) -> U,
|
F: Fn(&T) -> U,
|
||||||
Signal<U>: std::iter::FromIterator<(Duration, U)>,
|
Signal<U>: std::iter::FromIterator<(Duration, U)>,
|
||||||
{
|
{
|
||||||
use Signal::*;
|
use Signal::*;
|
||||||
match self {
|
match self {
|
||||||
Empty => Signal::Empty,
|
Empty => Signal::Empty,
|
||||||
Constant { value } => Signal::constant(op(value)),
|
Constant { value } => Signal::constant(op(value)),
|
||||||
Sampled { values, time_points } => zip(time_points, values.into_iter().map(op)).collect(),
|
signal => signal.into_iter().map(|(&t, v)| (t, op(v))).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
||||||
use argus_core::expr::*;
|
use argus_core::expr::*;
|
||||||
use argus_core::prelude::*;
|
use argus_core::prelude::*;
|
||||||
use argus_core::signals::interpolation::Linear;
|
use argus_core::signals::interpolation::Linear;
|
||||||
use argus_core::signals::SignalPartialOrd;
|
use argus_core::signals::{InterpolationMethod, SignalPartialOrd};
|
||||||
|
|
||||||
use crate::semantics::QuantitativeSemantics;
|
use crate::semantics::QuantitativeSemantics;
|
||||||
use crate::traits::Trace;
|
use crate::traits::Trace;
|
||||||
|
|
@ -13,7 +13,11 @@ use crate::utils::lemire_minmax::MonoWedge;
|
||||||
pub struct BooleanSemantics;
|
pub struct BooleanSemantics;
|
||||||
|
|
||||||
impl BooleanSemantics {
|
impl BooleanSemantics {
|
||||||
pub fn eval(expr: &BoolExpr, trace: &impl Trace) -> ArgusResult<Signal<bool>> {
|
pub fn eval<BoolI, NumI>(expr: &BoolExpr, trace: &impl Trace) -> ArgusResult<Signal<bool>>
|
||||||
|
where
|
||||||
|
BoolI: InterpolationMethod<bool>,
|
||||||
|
NumI: InterpolationMethod<f64>,
|
||||||
|
{
|
||||||
let ret = match expr {
|
let ret = match expr {
|
||||||
BoolExpr::BoolLit(val) => Signal::constant(val.0),
|
BoolExpr::BoolLit(val) => Signal::constant(val.0),
|
||||||
BoolExpr::BoolVar(BoolVar { name }) => trace
|
BoolExpr::BoolVar(BoolVar { name }) => trace
|
||||||
|
|
@ -22,8 +26,8 @@ impl BooleanSemantics {
|
||||||
.clone(),
|
.clone(),
|
||||||
BoolExpr::Cmp(Cmp { op, lhs, rhs }) => {
|
BoolExpr::Cmp(Cmp { op, lhs, rhs }) => {
|
||||||
use argus_core::expr::Ordering::*;
|
use argus_core::expr::Ordering::*;
|
||||||
let lhs = QuantitativeSemantics::eval_num_expr::<f64>(lhs, trace)?;
|
let lhs = QuantitativeSemantics::eval_num_expr::<f64, NumI>(lhs, trace)?;
|
||||||
let rhs = QuantitativeSemantics::eval_num_expr::<f64>(rhs, trace)?;
|
let rhs = QuantitativeSemantics::eval_num_expr::<f64, NumI>(rhs, trace)?;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Eq => lhs.signal_eq(&rhs).unwrap(),
|
Eq => lhs.signal_eq(&rhs).unwrap(),
|
||||||
|
|
@ -35,46 +39,48 @@ impl BooleanSemantics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoolExpr::Not(Not { arg }) => {
|
BoolExpr::Not(Not { arg }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<BoolI, NumI>(arg, trace)?;
|
||||||
!&arg
|
!&arg
|
||||||
}
|
}
|
||||||
BoolExpr::And(And { args }) => {
|
BoolExpr::And(And { args }) => {
|
||||||
assert!(args.len() >= 2);
|
assert!(args.len() >= 2);
|
||||||
args.iter()
|
args.iter().map(|arg| Self::eval::<BoolI, NumI>(arg, trace)).try_fold(
|
||||||
.map(|arg| Self::eval(arg, trace))
|
Signal::const_true(),
|
||||||
.try_fold(Signal::const_true(), |acc, item| {
|
|acc, item| {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
Ok(acc.and(&item))
|
Ok(acc.and::<BoolI>(&item))
|
||||||
})?
|
},
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
BoolExpr::Or(Or { args }) => {
|
BoolExpr::Or(Or { args }) => {
|
||||||
assert!(args.len() >= 2);
|
assert!(args.len() >= 2);
|
||||||
args.iter()
|
args.iter().map(|arg| Self::eval::<BoolI, NumI>(arg, trace)).try_fold(
|
||||||
.map(|arg| Self::eval(arg, trace))
|
Signal::const_true(),
|
||||||
.try_fold(Signal::const_true(), |acc, item| {
|
|acc, item| {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
Ok(acc.or(&item))
|
Ok(acc.or::<BoolI>(&item))
|
||||||
})?
|
},
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
BoolExpr::Next(Next { arg }) => {
|
BoolExpr::Next(Next { arg }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<BoolI, NumI>(arg, trace)?;
|
||||||
compute_next(arg)?
|
compute_next(arg)?
|
||||||
}
|
}
|
||||||
BoolExpr::Oracle(Oracle { steps, arg }) => {
|
BoolExpr::Oracle(Oracle { steps, arg }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<BoolI, NumI>(arg, trace)?;
|
||||||
compute_oracle(arg, *steps)?
|
compute_oracle(arg, *steps)?
|
||||||
}
|
}
|
||||||
BoolExpr::Always(Always { arg, interval }) => {
|
BoolExpr::Always(Always { arg, interval }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<BoolI, NumI>(arg, trace)?;
|
||||||
compute_always(arg, interval)?
|
compute_always(arg, interval)?
|
||||||
}
|
}
|
||||||
BoolExpr::Eventually(Eventually { arg, interval }) => {
|
BoolExpr::Eventually(Eventually { arg, interval }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<BoolI, NumI>(arg, trace)?;
|
||||||
compute_eventually(arg, interval)?
|
compute_eventually(arg, interval)?
|
||||||
}
|
}
|
||||||
BoolExpr::Until(Until { lhs, rhs, interval }) => {
|
BoolExpr::Until(Until { lhs, rhs, interval }) => {
|
||||||
let lhs = Self::eval(lhs, trace)?;
|
let lhs = Self::eval::<BoolI, NumI>(lhs, trace)?;
|
||||||
let rhs = Self::eval(rhs, trace)?;
|
let rhs = Self::eval::<BoolI, NumI>(rhs, trace)?;
|
||||||
compute_until(lhs, rhs, interval)?
|
compute_until(lhs, rhs, interval)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -389,7 +395,7 @@ mod tests {
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
|
|
||||||
let rob = BooleanSemantics::eval(&spec, &trace).unwrap();
|
let rob = BooleanSemantics::eval::<Linear, Linear>(&spec, &trace).unwrap();
|
||||||
let expected = Signal::from_iter(vec![
|
let expected = Signal::from_iter(vec![
|
||||||
(Duration::from_secs_f64(0.0), false),
|
(Duration::from_secs_f64(0.0), false),
|
||||||
(Duration::from_secs_f64(0.7), false),
|
(Duration::from_secs_f64(0.7), false),
|
||||||
|
|
@ -421,7 +427,7 @@ mod tests {
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
let rob = BooleanSemantics::eval(&spec, &trace).unwrap();
|
let rob = BooleanSemantics::eval::<Linear, Linear>(&spec, &trace).unwrap();
|
||||||
|
|
||||||
let Signal::Sampled { values, time_points: _ } = rob else {
|
let Signal::Sampled { values, time_points: _ } = rob else {
|
||||||
panic!("boolean semantics should remain sampled");
|
panic!("boolean semantics should remain sampled");
|
||||||
|
|
@ -441,7 +447,7 @@ mod tests {
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
let rob = BooleanSemantics::eval(&spec, &trace).unwrap();
|
let rob = BooleanSemantics::eval::<Linear, Linear>(&spec, &trace).unwrap();
|
||||||
println!("{:#?}", rob);
|
println!("{:#?}", rob);
|
||||||
|
|
||||||
let Signal::Sampled { values, time_points: _ } = rob else {
|
let Signal::Sampled { values, time_points: _ } = rob else {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
||||||
use argus_core::expr::*;
|
use argus_core::expr::*;
|
||||||
use argus_core::prelude::*;
|
use argus_core::prelude::*;
|
||||||
use argus_core::signals::interpolation::Linear;
|
use argus_core::signals::interpolation::Linear;
|
||||||
use argus_core::signals::SignalAbs;
|
use argus_core::signals::{InterpolationMethod, SignalAbs};
|
||||||
use num_traits::{Num, NumCast};
|
use num_traits::{Num, NumCast};
|
||||||
|
|
||||||
use crate::traits::Trace;
|
use crate::traits::Trace;
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::utils::lemire_minmax::MonoWedge;
|
||||||
pub struct QuantitativeSemantics;
|
pub struct QuantitativeSemantics;
|
||||||
|
|
||||||
impl QuantitativeSemantics {
|
impl QuantitativeSemantics {
|
||||||
pub fn eval(expr: &BoolExpr, trace: &impl Trace) -> ArgusResult<Signal<f64>> {
|
pub fn eval<I: InterpolationMethod<f64>>(expr: &BoolExpr, trace: &impl Trace) -> ArgusResult<Signal<f64>> {
|
||||||
let ret = match expr {
|
let ret = match expr {
|
||||||
BoolExpr::BoolLit(val) => top_or_bot(&Signal::constant(val.0)),
|
BoolExpr::BoolLit(val) => top_or_bot(&Signal::constant(val.0)),
|
||||||
BoolExpr::BoolVar(BoolVar { name }) => trace
|
BoolExpr::BoolVar(BoolVar { name }) => trace
|
||||||
|
|
@ -22,23 +22,20 @@ impl QuantitativeSemantics {
|
||||||
.map(top_or_bot)?,
|
.map(top_or_bot)?,
|
||||||
BoolExpr::Cmp(Cmp { op, lhs, rhs }) => {
|
BoolExpr::Cmp(Cmp { op, lhs, rhs }) => {
|
||||||
use argus_core::expr::Ordering::*;
|
use argus_core::expr::Ordering::*;
|
||||||
let lhs = Self::eval_num_expr::<f64>(lhs, trace)?;
|
let lhs = Self::eval_num_expr::<f64, I>(lhs, trace)?;
|
||||||
let rhs = Self::eval_num_expr::<f64>(rhs, trace)?;
|
let rhs = Self::eval_num_expr::<f64, I>(rhs, trace)?;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Eq => -&((&lhs - &rhs).abs()),
|
Eq => lhs.abs_diff::<_, I>(&rhs).negate(),
|
||||||
NotEq => (&lhs - &rhs).abs(),
|
NotEq => lhs.abs_diff::<_, I>(&rhs).negate(),
|
||||||
Less { strict: _ } => &rhs - &lhs,
|
Less { strict: _ } => rhs.sub::<_, I>(&lhs),
|
||||||
Greater { strict: _ } => &lhs - &rhs,
|
Greater { strict: _ } => lhs.sub::<_, I>(&rhs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoolExpr::Not(Not { arg }) => {
|
BoolExpr::Not(Not { arg }) => Self::eval::<I>(arg, trace)?.negate(),
|
||||||
let arg = Self::eval(arg, trace)?;
|
|
||||||
-&arg
|
|
||||||
}
|
|
||||||
BoolExpr::And(And { args }) => {
|
BoolExpr::And(And { args }) => {
|
||||||
assert!(args.len() >= 2);
|
assert!(args.len() >= 2);
|
||||||
args.iter().map(|arg| Self::eval(arg, trace)).try_fold(
|
args.iter().map(|arg| Self::eval::<I>(arg, trace)).try_fold(
|
||||||
Signal::constant(f64::INFINITY),
|
Signal::constant(f64::INFINITY),
|
||||||
|acc, item| {
|
|acc, item| {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
|
|
@ -48,7 +45,7 @@ impl QuantitativeSemantics {
|
||||||
}
|
}
|
||||||
BoolExpr::Or(Or { args }) => {
|
BoolExpr::Or(Or { args }) => {
|
||||||
assert!(args.len() >= 2);
|
assert!(args.len() >= 2);
|
||||||
args.iter().map(|arg| Self::eval(arg, trace)).try_fold(
|
args.iter().map(|arg| Self::eval::<I>(arg, trace)).try_fold(
|
||||||
Signal::constant(f64::NEG_INFINITY),
|
Signal::constant(f64::NEG_INFINITY),
|
||||||
|acc, item| {
|
|acc, item| {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
|
|
@ -57,39 +54,40 @@ impl QuantitativeSemantics {
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
BoolExpr::Next(Next { arg }) => {
|
BoolExpr::Next(Next { arg }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<I>(arg, trace)?;
|
||||||
compute_next(arg)?
|
compute_next(arg)?
|
||||||
}
|
}
|
||||||
BoolExpr::Oracle(Oracle { steps, arg }) => {
|
BoolExpr::Oracle(Oracle { steps, arg }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<I>(arg, trace)?;
|
||||||
compute_oracle(arg, *steps)?
|
compute_oracle(arg, *steps)?
|
||||||
}
|
}
|
||||||
BoolExpr::Always(Always { arg, interval }) => {
|
BoolExpr::Always(Always { arg, interval }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<I>(arg, trace)?;
|
||||||
compute_always(arg, interval)?
|
compute_always(arg, interval)?
|
||||||
}
|
}
|
||||||
BoolExpr::Eventually(Eventually { arg, interval }) => {
|
BoolExpr::Eventually(Eventually { arg, interval }) => {
|
||||||
let arg = Self::eval(arg, trace)?;
|
let arg = Self::eval::<I>(arg, trace)?;
|
||||||
compute_eventually(arg, interval)?
|
compute_eventually(arg, interval)?
|
||||||
}
|
}
|
||||||
BoolExpr::Until(Until { lhs, rhs, interval }) => {
|
BoolExpr::Until(Until { lhs, rhs, interval }) => {
|
||||||
let lhs = Self::eval(lhs, trace)?;
|
let lhs = Self::eval::<I>(lhs, trace)?;
|
||||||
let rhs = Self::eval(rhs, trace)?;
|
let rhs = Self::eval::<I>(rhs, trace)?;
|
||||||
compute_until(lhs, rhs, interval)?
|
compute_until(lhs, rhs, interval)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_num_expr<T>(root: &NumExpr, trace: &impl Trace) -> ArgusResult<Signal<T>>
|
pub fn eval_num_expr<T, I>(root: &NumExpr, trace: &impl Trace) -> ArgusResult<Signal<T>>
|
||||||
where
|
where
|
||||||
T: Num + NumCast,
|
T: Num + NumCast + Clone,
|
||||||
for<'a> &'a Signal<T>: std::ops::Neg<Output = Signal<T>>,
|
for<'a> &'a T: core::ops::Neg<Output = T>,
|
||||||
for<'a> &'a Signal<T>: std::ops::Add<&'a Signal<T>, Output = Signal<T>>,
|
for<'a> &'a T: core::ops::Add<&'a T, Output = T>,
|
||||||
for<'a> &'a Signal<T>: std::ops::Sub<&'a Signal<T>, Output = Signal<T>>,
|
for<'a> &'a T: core::ops::Sub<&'a T, Output = T>,
|
||||||
for<'a> &'a Signal<T>: std::ops::Mul<&'a Signal<T>, Output = Signal<T>>,
|
for<'a> &'a T: core::ops::Mul<&'a T, Output = T>,
|
||||||
for<'a> &'a Signal<T>: std::ops::Div<&'a Signal<T>, Output = Signal<T>>,
|
for<'a> &'a T: core::ops::Div<&'a T, Output = T>,
|
||||||
Signal<T>: SignalAbs,
|
Signal<T>: SignalAbs,
|
||||||
|
I: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
match root {
|
match root {
|
||||||
NumExpr::IntLit(val) => Signal::constant(val.0).num_cast(),
|
NumExpr::IntLit(val) => Signal::constant(val.0).num_cast(),
|
||||||
|
|
@ -98,35 +96,35 @@ impl QuantitativeSemantics {
|
||||||
NumExpr::IntVar(IntVar { name }) => trace.get::<i64>(name.as_str()).unwrap().num_cast(),
|
NumExpr::IntVar(IntVar { name }) => trace.get::<i64>(name.as_str()).unwrap().num_cast(),
|
||||||
NumExpr::UIntVar(UIntVar { name }) => trace.get::<u64>(name.as_str()).unwrap().num_cast(),
|
NumExpr::UIntVar(UIntVar { name }) => trace.get::<u64>(name.as_str()).unwrap().num_cast(),
|
||||||
NumExpr::FloatVar(FloatVar { name }) => trace.get::<f64>(name.as_str()).unwrap().num_cast(),
|
NumExpr::FloatVar(FloatVar { name }) => trace.get::<f64>(name.as_str()).unwrap().num_cast(),
|
||||||
NumExpr::Neg(Neg { arg }) => Self::eval_num_expr::<T>(arg, trace).map(|sig| -&sig),
|
NumExpr::Neg(Neg { arg }) => Self::eval_num_expr::<T, I>(arg, trace).map(|sig| sig.negate()),
|
||||||
NumExpr::Add(Add { args }) => {
|
NumExpr::Add(Add { args }) => {
|
||||||
let mut ret: Signal<T> = Signal::<T>::zero();
|
let mut ret: Signal<T> = Signal::<T>::zero();
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
let arg = Self::eval_num_expr::<T>(arg, trace)?;
|
let arg = Self::eval_num_expr::<T, I>(arg, trace)?;
|
||||||
ret = &ret + &arg;
|
ret = ret.add::<_, I>(&arg);
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
NumExpr::Sub(Sub { lhs, rhs }) => {
|
NumExpr::Sub(Sub { lhs, rhs }) => {
|
||||||
let lhs = Self::eval_num_expr::<T>(lhs, trace)?;
|
let lhs = Self::eval_num_expr::<T, I>(lhs, trace)?;
|
||||||
let rhs = Self::eval_num_expr::<T>(rhs, trace)?;
|
let rhs = Self::eval_num_expr::<T, I>(rhs, trace)?;
|
||||||
Ok(&lhs - &rhs)
|
Ok(lhs.sub::<_, I>(&rhs))
|
||||||
}
|
}
|
||||||
NumExpr::Mul(Mul { args }) => {
|
NumExpr::Mul(Mul { args }) => {
|
||||||
let mut ret: Signal<T> = Signal::<T>::one();
|
let mut ret: Signal<T> = Signal::<T>::one();
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
let arg = Self::eval_num_expr::<T>(arg, trace)?;
|
let arg = Self::eval_num_expr::<T, I>(arg, trace)?;
|
||||||
ret = &ret * &arg;
|
ret = ret.mul::<_, I>(&arg);
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
NumExpr::Div(Div { dividend, divisor }) => {
|
NumExpr::Div(Div { dividend, divisor }) => {
|
||||||
let dividend = Self::eval_num_expr::<T>(dividend, trace)?;
|
let dividend = Self::eval_num_expr::<T, I>(dividend, trace)?;
|
||||||
let divisor = Self::eval_num_expr::<T>(divisor, trace)?;
|
let divisor = Self::eval_num_expr::<T, I>(divisor, trace)?;
|
||||||
Ok(÷nd / &divisor)
|
Ok(dividend.div::<_, I>(&divisor))
|
||||||
}
|
}
|
||||||
NumExpr::Abs(Abs { arg }) => {
|
NumExpr::Abs(Abs { arg }) => {
|
||||||
let arg = Self::eval_num_expr::<T>(arg, trace)?;
|
let arg = Self::eval_num_expr::<T, I>(arg, trace)?;
|
||||||
Ok(arg.abs())
|
Ok(arg.abs())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,9 +199,9 @@ fn compute_always(signal: Signal<f64>, interval: &Interval) -> ArgusResult<Signa
|
||||||
|
|
||||||
/// Compute timed always for the interval `[a, b]` (or, if `b` is `None`, `[a, ..]`.
|
/// Compute timed always for the interval `[a, b]` (or, if `b` is `None`, `[a, ..]`.
|
||||||
fn compute_timed_always(signal: Signal<f64>, a: Duration, b: Option<Duration>) -> ArgusResult<Signal<f64>> {
|
fn compute_timed_always(signal: Signal<f64>, a: Duration, b: Option<Duration>) -> ArgusResult<Signal<f64>> {
|
||||||
let z1 = -signal;
|
let z1 = signal.negate();
|
||||||
let z2 = compute_timed_eventually(z1, a, b)?;
|
let z2 = compute_timed_eventually(z1, a, b)?;
|
||||||
Ok(-z2)
|
Ok(z2.negate())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute untimed always
|
/// Compute untimed always
|
||||||
|
|
@ -418,6 +416,7 @@ mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use argus_core::expr::ExprBuilder;
|
use argus_core::expr::ExprBuilder;
|
||||||
|
use argus_core::signals::interpolation::Constant;
|
||||||
use argus_core::signals::AnySignal;
|
use argus_core::signals::AnySignal;
|
||||||
use itertools::assert_equal;
|
use itertools::assert_equal;
|
||||||
|
|
||||||
|
|
@ -465,7 +464,7 @@ mod tests {
|
||||||
let spec = expr_builder.float_const(5.0);
|
let spec = expr_builder.float_const(5.0);
|
||||||
let trace = MyTrace::default();
|
let trace = MyTrace::default();
|
||||||
|
|
||||||
let robustness = QuantitativeSemantics::eval_num_expr::<f64>(&spec, &trace).unwrap();
|
let robustness = QuantitativeSemantics::eval_num_expr::<f64, Linear>(&spec, &trace).unwrap();
|
||||||
|
|
||||||
assert!(matches!(robustness, Signal::Constant { value } if value == 5.0));
|
assert!(matches!(robustness, Signal::Constant { value } if value == 5.0));
|
||||||
}
|
}
|
||||||
|
|
@ -502,7 +501,7 @@ mod tests {
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
|
|
||||||
let rob = QuantitativeSemantics::eval_num_expr::<f64>(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval_num_expr::<f64, Linear>(&spec, &trace).unwrap();
|
||||||
let expected = Signal::from_iter(vec![
|
let expected = Signal::from_iter(vec![
|
||||||
(Duration::from_secs_f64(0.0), 1.3 + 2.5),
|
(Duration::from_secs_f64(0.0), 1.3 + 2.5),
|
||||||
(Duration::from_secs_f64(0.7), 3.0 + 4.0),
|
(Duration::from_secs_f64(0.7), 3.0 + 4.0),
|
||||||
|
|
@ -536,7 +535,7 @@ mod tests {
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
|
|
||||||
let rob = QuantitativeSemantics::eval_num_expr::<f64>(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval_num_expr::<f64, Linear>(&spec, &trace).unwrap();
|
||||||
let expected = Signal::from_iter(vec![
|
let expected = Signal::from_iter(vec![
|
||||||
(Duration::from_secs_f64(0.0), 1.3 + 2.5),
|
(Duration::from_secs_f64(0.0), 1.3 + 2.5),
|
||||||
(Duration::from_secs_f64(0.7), 3.0 + 4.0),
|
(Duration::from_secs_f64(0.7), 3.0 + 4.0),
|
||||||
|
|
@ -567,7 +566,7 @@ mod tests {
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
|
|
||||||
let rob = QuantitativeSemantics::eval(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval::<Linear>(&spec, &trace).unwrap();
|
||||||
let expected = Signal::from_iter(vec![
|
let expected = Signal::from_iter(vec![
|
||||||
(Duration::from_secs_f64(0.0), 0.0 - 1.3),
|
(Duration::from_secs_f64(0.0), 0.0 - 1.3),
|
||||||
(Duration::from_secs_f64(0.7), 0.0 - 3.0),
|
(Duration::from_secs_f64(0.7), 0.0 - 3.0),
|
||||||
|
|
@ -599,7 +598,7 @@ mod tests {
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
let rob = QuantitativeSemantics::eval(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval::<Linear>(&spec, &trace).unwrap();
|
||||||
println!("{:#?}", rob);
|
println!("{:#?}", rob);
|
||||||
let expected = Signal::from_iter(vec![
|
let expected = Signal::from_iter(vec![
|
||||||
(Duration::from_secs_f64(0.0), 4.0),
|
(Duration::from_secs_f64(0.0), 4.0),
|
||||||
|
|
@ -643,7 +642,7 @@ mod tests {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
let rob = QuantitativeSemantics::eval(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval::<Constant>(&spec, &trace).unwrap();
|
||||||
|
|
||||||
let expected = Signal::from_iter(vec![(Duration::from_secs(0), 2), (Duration::from_secs(5), 2)])
|
let expected = Signal::from_iter(vec![(Duration::from_secs(0), 2), (Duration::from_secs(5), 2)])
|
||||||
.num_cast::<f64>()
|
.num_cast::<f64>()
|
||||||
|
|
@ -674,7 +673,7 @@ mod tests {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let trace = MyTrace { signals };
|
let trace = MyTrace { signals };
|
||||||
let rob = QuantitativeSemantics::eval(&spec, &trace).unwrap();
|
let rob = QuantitativeSemantics::eval::<Constant>(&spec, &trace).unwrap();
|
||||||
|
|
||||||
let expected = Signal::from_iter(vec![(Duration::from_secs(4), 3), (Duration::from_secs(6), 3)])
|
let expected = Signal::from_iter(vec![(Duration::from_secs(4), 3), (Duration::from_secs(6), 3)])
|
||||||
.num_cast::<f64>()
|
.num_cast::<f64>()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use argus::signals::interpolation::{Constant, Linear};
|
||||||
use argus::{AnySignal, BooleanSemantics, QuantitativeSemantics, Signal, Trace};
|
use argus::{AnySignal, BooleanSemantics, QuantitativeSemantics, Signal, Trace};
|
||||||
use pyo3::exceptions::PyTypeError;
|
use pyo3::exceptions::PyTypeError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
@ -60,14 +62,26 @@ impl Trace for PyTrace {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn eval_bool_semantics(expr: &PyBoolExpr, trace: &PyTrace) -> PyResult<Py<BoolSignal>> {
|
#[pyo3(signature = (expr, trace, *, interpolation_method = "linear"))]
|
||||||
let sig = BooleanSemantics::eval(&expr.0, trace).map_err(PyArgusError::from)?;
|
fn eval_bool_semantics(expr: &PyBoolExpr, trace: &PyTrace, interpolation_method: &str) -> PyResult<Py<BoolSignal>> {
|
||||||
Python::with_gil(|py| Py::new(py, (BoolSignal, PySignal::new(sig, PyInterp::Linear))))
|
let interp = PyInterp::from_str(interpolation_method)?;
|
||||||
|
let sig = match interp {
|
||||||
|
PyInterp::Linear => BooleanSemantics::eval::<Linear, Linear>(&expr.0, trace).map_err(PyArgusError::from)?,
|
||||||
|
PyInterp::Constant => {
|
||||||
|
BooleanSemantics::eval::<Constant, Constant>(&expr.0, trace).map_err(PyArgusError::from)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Python::with_gil(|py| Py::new(py, (BoolSignal, PySignal::new(sig, interp))))
|
||||||
}
|
}
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn eval_robust_semantics(expr: &PyBoolExpr, trace: &PyTrace) -> PyResult<Py<FloatSignal>> {
|
#[pyo3(signature = (expr, trace, *, interpolation_method = "linear"))]
|
||||||
let sig = QuantitativeSemantics::eval(&expr.0, trace).map_err(PyArgusError::from)?;
|
fn eval_robust_semantics(expr: &PyBoolExpr, trace: &PyTrace, interpolation_method: &str) -> PyResult<Py<FloatSignal>> {
|
||||||
Python::with_gil(|py| Py::new(py, (FloatSignal, PySignal::new(sig, PyInterp::Linear))))
|
let interp = PyInterp::from_str(interpolation_method)?;
|
||||||
|
let sig = match interp {
|
||||||
|
PyInterp::Linear => QuantitativeSemantics::eval::<Linear>(&expr.0, trace).map_err(PyArgusError::from)?,
|
||||||
|
PyInterp::Constant => QuantitativeSemantics::eval::<Constant>(&expr.0, trace).map_err(PyArgusError::from)?,
|
||||||
|
};
|
||||||
|
Python::with_gil(|py| Py::new(py, (FloatSignal, PySignal::new(sig, interp))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(_py: Python, m: &PyModule) -> PyResult<()> {
|
pub fn init(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use argus::signals::interpolation::{Constant, Linear};
|
use argus::signals::interpolation::{Constant, Linear};
|
||||||
use argus::signals::Signal;
|
use argus::signals::Signal;
|
||||||
use pyo3::exceptions::PyValueError;
|
use pyo3::exceptions::PyValueError;
|
||||||
|
|
@ -14,6 +16,21 @@ pub enum PyInterp {
|
||||||
Constant,
|
Constant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for PyInterp {
|
||||||
|
type Err = PyErr;
|
||||||
|
|
||||||
|
fn from_str(method: &str) -> Result<Self, Self::Err> {
|
||||||
|
match method {
|
||||||
|
"linear" => Ok(PyInterp::Linear),
|
||||||
|
"constant" => Ok(PyInterp::Constant),
|
||||||
|
_ => Err(PyValueError::new_err(format!(
|
||||||
|
"unsupported interpolation method `{}`",
|
||||||
|
method
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_more::From, derive_more::TryInto)]
|
#[derive(Debug, Clone, derive_more::From, derive_more::TryInto)]
|
||||||
#[try_into(owned, ref, ref_mut)]
|
#[try_into(owned, ref, ref_mut)]
|
||||||
pub enum SignalKind {
|
pub enum SignalKind {
|
||||||
|
|
@ -126,11 +143,7 @@ macro_rules! impl_signals {
|
||||||
#[new]
|
#[new]
|
||||||
#[pyo3(signature = (*, interpolation_method = "linear"))]
|
#[pyo3(signature = (*, interpolation_method = "linear"))]
|
||||||
fn new(interpolation_method: &str) -> PyResult<(Self, PySignal)> {
|
fn new(interpolation_method: &str) -> PyResult<(Self, PySignal)> {
|
||||||
let interp = match interpolation_method {
|
let interp = PyInterp::from_str(interpolation_method)?;
|
||||||
"linear" => PyInterp::Linear,
|
|
||||||
"constant" => PyInterp::Constant,
|
|
||||||
_ => return Err(PyValueError::new_err(format!("unsupported interpolation method `{}`", interpolation_method))),
|
|
||||||
};
|
|
||||||
Ok((Self, PySignal::new(Signal::<$ty>::new(), interp)))
|
Ok((Self, PySignal::new(Signal::<$ty>::new(), interp)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,11 +151,7 @@ macro_rules! impl_signals {
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
#[pyo3(signature = (value, *, interpolation_method = "linear"))]
|
#[pyo3(signature = (value, *, interpolation_method = "linear"))]
|
||||||
fn constant(_: &PyType, py: Python<'_>, value: $ty, interpolation_method: &str) -> PyResult<Py<Self>> {
|
fn constant(_: &PyType, py: Python<'_>, value: $ty, interpolation_method: &str) -> PyResult<Py<Self>> {
|
||||||
let interp = match interpolation_method {
|
let interp = PyInterp::from_str(interpolation_method)?;
|
||||||
"linear" => PyInterp::Linear,
|
|
||||||
"constant" => PyInterp::Constant,
|
|
||||||
_ => return Err(PyValueError::new_err(format!("unsupported interpolation method `{}`", interpolation_method))),
|
|
||||||
};
|
|
||||||
Py::new(
|
Py::new(
|
||||||
py,
|
py,
|
||||||
(Self, PySignal::new(Signal::constant(value), interp))
|
(Self, PySignal::new(Signal::constant(value), interp))
|
||||||
|
|
@ -158,11 +167,7 @@ macro_rules! impl_signals {
|
||||||
.map(|(t, v)| (core::time::Duration::try_from_secs_f64(t).unwrap_or_else(|err| panic!("Value = {}, {}", t, err)), v))
|
.map(|(t, v)| (core::time::Duration::try_from_secs_f64(t).unwrap_or_else(|err| panic!("Value = {}, {}", t, err)), v))
|
||||||
).map_err(PyArgusError::from)?;
|
).map_err(PyArgusError::from)?;
|
||||||
|
|
||||||
let interp = match interpolation_method {
|
let interp = PyInterp::from_str(interpolation_method)?;
|
||||||
"linear" => PyInterp::Linear,
|
|
||||||
"constant" => PyInterp::Constant,
|
|
||||||
_ => return Err(PyValueError::new_err(format!("unsupported interpolation method `{}`", interpolation_method))),
|
|
||||||
};
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
Py::new(
|
Py::new(
|
||||||
py,
|
py,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue