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!(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!(f32, -sig);
|
||||
signal_ops_impl!(f32, lhs + rhs);
|
||||
signal_ops_impl!(f32, lhs * rhs);
|
||||
// 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!(f64, -sig);
|
||||
signal_ops_impl!(f64, lhs + rhs);
|
||||
signal_ops_impl!(f64, lhs * rhs);
|
||||
// signal_ops_impl!(f64, -sig);
|
||||
// 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::InterpolationMethod;
|
||||
use crate::signals::Signal;
|
||||
|
||||
impl core::ops::Not for Signal<bool> {
|
||||
type Output = Signal<bool>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
use Signal::*;
|
||||
match self {
|
||||
Empty => self,
|
||||
Constant { value } => Signal::constant(!value),
|
||||
signal => signal.into_iter().map(|(&t, v)| (t, !v)).collect(),
|
||||
}
|
||||
self.logical_not()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18,24 +14,24 @@ impl core::ops::Not for &Signal<bool> {
|
|||
type Output = Signal<bool>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
use Signal::*;
|
||||
match self {
|
||||
Empty => Empty,
|
||||
Constant { value } => Signal::constant(!value),
|
||||
signal => signal.into_iter().map(|(&t, &v)| (t, !v)).collect(),
|
||||
}
|
||||
self.logical_not()
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// Here, the conjunction is taken at all signal points where either of the signals
|
||||
/// are sampled, and where they intersect (using interpolation).
|
||||
///
|
||||
/// See [`Signal::sync_with_intersection`].
|
||||
pub fn and(&self, other: &Self) -> Self {
|
||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs && *rhs)
|
||||
pub fn and<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
|
||||
self.binary_op::<_, _, I>(other, |lhs, rhs| *lhs && *rhs)
|
||||
}
|
||||
|
||||
/// 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).
|
||||
///
|
||||
/// See [`Signal::sync_with_intersection`].
|
||||
pub fn or(&self, other: &Self) -> Self {
|
||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs || *rhs)
|
||||
pub fn or<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
|
||||
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>;
|
||||
|
||||
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>;
|
||||
|
||||
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>;
|
||||
|
||||
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>;
|
||||
|
||||
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 crate::signals::Signal;
|
||||
|
||||
impl<T> core::ops::Neg for Signal<T>
|
||||
where
|
||||
T: core::ops::Neg<Output = T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
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> Signal<T> {
|
||||
/// Perform sample-wise arithmetic negation over the signal.
|
||||
pub fn negate<U>(&self) -> Signal<U>
|
||||
where
|
||||
for<'a> &'a T: core::ops::Neg<Output = U>,
|
||||
{
|
||||
self.unary_op(|v| -v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Neg for &Signal<T>
|
||||
where
|
||||
for<'a> &'a T: core::ops::Neg<Output = T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
use Signal::*;
|
||||
match self {
|
||||
Empty => Signal::Empty,
|
||||
Constant { value } => Signal::constant(value.neg()),
|
||||
Sampled { values, time_points } => time_points
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(values.iter().map(|v| v.neg()))
|
||||
.collect(),
|
||||
}
|
||||
/// Perform sample-wise addition 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 add<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||
where
|
||||
for<'t> &'t T: core::ops::Add<Output = U>,
|
||||
T: Clone,
|
||||
I: InterpolationMethod<T>,
|
||||
{
|
||||
self.binary_op::<_, _, I>(other, |u, v| u + v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Add<&Signal<T>> for Signal<T>
|
||||
where
|
||||
T: Clone,
|
||||
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Add the given signal with another
|
||||
fn add(self, other: &Signal<T>) -> Signal<T> {
|
||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
|
||||
/// Perform sample-wise multiplication 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 mul<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||
where
|
||||
for<'t> &'t T: core::ops::Mul<Output = U>,
|
||||
T: Clone,
|
||||
I: InterpolationMethod<T>,
|
||||
{
|
||||
self.binary_op::<_, _, I>(other, |u, v| u * v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Add<&Signal<T>> for &Signal<T>
|
||||
where
|
||||
T: Clone,
|
||||
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Add the given signal with another
|
||||
fn add(self, other: &Signal<T>) -> Signal<T> {
|
||||
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
|
||||
/// Perform sample-wise subtraction 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 sub<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||
where
|
||||
for<'t> &'t T: core::ops::Sub<Output = U>,
|
||||
T: Clone,
|
||||
I: InterpolationMethod<T>,
|
||||
{
|
||||
self.binary_op::<_, _, I>(other, |u, v| u - v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Mul<&Signal<T>> for Signal<T>
|
||||
where
|
||||
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
|
||||
T: Clone,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Multiply the given signal with another
|
||||
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
|
||||
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
|
||||
/// Perform sample-wise division 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 div<U, I>(&self, other: &Signal<T>) -> Signal<U>
|
||||
where
|
||||
for<'t> &'t T: core::ops::Div<Output = U>,
|
||||
T: Clone,
|
||||
I: InterpolationMethod<T>,
|
||||
{
|
||||
self.binary_op::<_, _, I>(other, |u, v| u / v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Mul<&Signal<T>> for &Signal<T>
|
||||
where
|
||||
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
|
||||
T: Clone,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Multiply the given signal with another
|
||||
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 {
|
||||
/// Perform sample-wise exponentiation 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 pow<U, I>(&self, exponent: &Signal<T>) -> Signal<U>
|
||||
where
|
||||
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = U>,
|
||||
T: Clone,
|
||||
I: InterpolationMethod<T>,
|
||||
{
|
||||
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> {
|
||||
/// Return the absolute value for each sample in the signal
|
||||
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::time::Duration;
|
||||
use std::iter::zip;
|
||||
|
||||
use super::{InterpolationMethod, Sample, Signal};
|
||||
|
||||
|
|
@ -25,16 +24,16 @@ pub struct Neighborhood<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
|
||||
F: Fn(T) -> U,
|
||||
F: Fn(&T) -> U,
|
||||
Signal<U>: std::iter::FromIterator<(Duration, U)>,
|
||||
{
|
||||
use Signal::*;
|
||||
match self {
|
||||
Empty => Signal::Empty,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue