Skip to content

Commit

Permalink
fix!: add explicit interpolation method for more functions
Browse files Browse the repository at this point in the history
  • Loading branch information
anand-bala committed Oct 4, 2023
1 parent f97d593 commit 91441d4
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 110 deletions.
36 changes: 20 additions & 16 deletions argus-core/src/signals/cmp_ops.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
use std::cmp::Ordering;

use super::interpolation::Linear;
use super::traits::SignalPartialOrd;
use super::{InterpolationMethod, Signal};

impl<T> SignalPartialOrd for Signal<T>
impl<T> SignalPartialOrd<T> for Signal<T>
where
T: PartialOrd + Clone,
Linear: InterpolationMethod<T>,
{
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
fn signal_cmp<F, I>(&self, other: &Self, op: F) -> Option<Signal<bool>>
where
F: Fn(Ordering) -> bool,
I: InterpolationMethod<T>,
{
// This has to be manually implemented and cannot use the apply2 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.
// the union of the sample points in self and other
let sync_points = self.sync_with_intersection::<Linear>(other)?;
let sync_points = self.sync_with_intersection::<I>(other)?;
let sig: Option<Signal<bool>> = sync_points
.into_iter()
.map(|t| {
let lhs = self.interpolate_at::<Linear>(t).unwrap();
let rhs = other.interpolate_at::<Linear>(t).unwrap();
let lhs = self.interpolate_at::<I>(t).unwrap();
let rhs = other.interpolate_at::<I>(t).unwrap();
let cmp = lhs.partial_cmp(&rhs);
cmp.map(|v| (t, op(v)))
})
Expand All @@ -35,16 +34,18 @@ where
impl<T> Signal<T>
where
T: PartialOrd + Clone,
Linear: InterpolationMethod<T>,
{
/// Compute the time-wise min of two signals
pub fn min(&self, other: &Self) -> Self {
let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
pub fn min<I>(&self, other: &Self) -> Self
where
I: InterpolationMethod<T>,
{
let time_points = self.sync_with_intersection::<I>(other).unwrap();
time_points
.into_iter()
.map(|t| {
let lhs = self.interpolate_at::<Linear>(t).unwrap();
let rhs = other.interpolate_at::<Linear>(t).unwrap();
let lhs = self.interpolate_at::<I>(t).unwrap();
let rhs = other.interpolate_at::<I>(t).unwrap();
if lhs < rhs {
(t, lhs)
} else {
Expand All @@ -55,13 +56,16 @@ where
}

/// Compute the time-wise max of two signals
pub fn max(&self, other: &Self) -> Self {
let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
pub fn max<I>(&self, other: &Self) -> Self
where
I: InterpolationMethod<T>,
{
let time_points = self.sync_with_intersection::<I>(other).unwrap();
time_points
.into_iter()
.map(|t| {
let lhs = self.interpolate_at::<Linear>(t).unwrap();
let rhs = other.interpolate_at::<Linear>(t).unwrap();
let lhs = self.interpolate_at::<I>(t).unwrap();
let rhs = other.interpolate_at::<I>(t).unwrap();
if lhs > rhs {
(t, lhs)
} else {
Expand Down
6 changes: 3 additions & 3 deletions argus-core/src/signals/num_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ impl<T> Signal<T> {
pub fn sub<U, I>(&self, other: &Signal<T>) -> Signal<U>
where
for<'t> &'t T: core::ops::Sub<Output = U>,
T: Clone,
T: Clone + PartialOrd,
I: InterpolationMethod<T>,
{
self.binary_op::<_, _, I>(other, |u, v| u - v)
self.binary_op_with_intersection::<_, _, I>(other, |u, v| u - v)
}

/// Perform sample-wise division of the two signals.
Expand Down Expand Up @@ -86,7 +86,7 @@ impl<T> Signal<T> {
T: Clone + PartialOrd,
I: InterpolationMethod<T>,
{
self.binary_op::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
self.binary_op_with_intersection::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
}
}

Expand Down
6 changes: 2 additions & 4 deletions argus-core/src/signals/shift_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@ use core::time::Duration;

use itertools::Itertools;

use super::interpolation::Linear;
use super::{InterpolationMethod, Signal};

impl<T> Signal<T>
where
T: Copy,
Linear: InterpolationMethod<T>,
{
/// Shift all samples in the signal by `delta` amount to the left.
///
/// This essentially filters out all samples with time points greater than `delta`,
/// and subtracts `delta` from the rest of the time points.
pub fn shift_left(&self, delta: Duration) -> Self {
pub fn shift_left<I: InterpolationMethod<T>>(&self, delta: Duration) -> Self {
match self {
Signal::Sampled { values, time_points } => {
// We want to skip any time points < delta, and subtract the rest.
Expand All @@ -34,7 +32,7 @@ where
if idx > 0 && first_t != &delta {
// The shifted signal will not start at 0, and we have a previous
// index to interpolate from.
let v = self.interpolate_at::<Linear>(delta).unwrap();
let v = self.interpolate_at::<I>(delta).unwrap();
new_samples.push((Duration::ZERO, v));
}
// Shift the rest of the samples
Expand Down
14 changes: 9 additions & 5 deletions argus-core/src/signals/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,27 @@ macro_rules! impl_signal_cmp {
($cmp:ident) => {
paste! {
/// Compute the time-wise comparison of two signals
fn [<signal_ $cmp>](&self, other: &Rhs) -> Option<Signal<bool>> {
self.signal_cmp(other, |ord| ord.[<is_ $cmp>]())
fn [<signal_ $cmp>]<I>(&self, other: &Rhs) -> Option<Signal<bool>>
where
I: InterpolationMethod<T>
{
self.signal_cmp::<_, I>(other, |ord| ord.[<is_ $cmp>]())
}
}
};
}

/// A time-wise partial ordering defined for signals
pub trait SignalPartialOrd<Rhs = Self> {
pub trait SignalPartialOrd<T, Rhs = Self> {
/// Compare two signals within each of their domains (using [`PartialOrd`]) and
/// apply the given function `op` to the ordering to create a signal.
///
/// This function returns `None` if the comparison isn't possible, namely, when
/// either of the signals are empty.
fn signal_cmp<F>(&self, other: &Rhs, op: F) -> Option<Signal<bool>>
fn signal_cmp<F, I>(&self, other: &Rhs, op: F) -> Option<Signal<bool>>
where
F: Fn(Ordering) -> bool;
F: Fn(Ordering) -> bool,
I: InterpolationMethod<T>;

impl_signal_cmp!(lt);
impl_signal_cmp!(le);
Expand Down
29 changes: 29 additions & 0 deletions argus-core/src/signals/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,35 @@ impl<T> Signal<T> {
}
}
}

pub(crate) fn binary_op_with_intersection<U, F, Interp>(&self, other: &Signal<T>, op: F) -> Signal<U>
where
T: Clone + PartialOrd,
F: Fn(&T, &T) -> U,
Interp: InterpolationMethod<T>,
{
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(op(v1, v2)),
(lhs, rhs) => {
// We determine the range of the signal (as the output signal can only be
// defined in the domain where both signals are defined).
let time_points = lhs.sync_with_intersection::<Interp>(rhs).unwrap();
// Now, at each of the merged time points, we sample each signal and operate on
// them
time_points
.into_iter()
.map(|t| {
let v1 = lhs.interpolate_at::<Interp>(t).unwrap();
let v2 = rhs.interpolate_at::<Interp>(t).unwrap();
(t, op(&v1, &v2))
})
.collect()
}
}
}
}

fn partial_min<T>(a: T, b: T) -> Option<T>
Expand Down
Loading

0 comments on commit 91441d4

Please sign in to comment.