Skip to content

Commit 50d5a0a

Browse files
committed
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.
1 parent e2cff94 commit 50d5a0a

File tree

8 files changed

+220
-295
lines changed

8 files changed

+220
-295
lines changed

argus-core/src/signals.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -633,21 +633,21 @@ mod tests {
633633

634634
// signal_ops_impl!(u64, lhs + rhs);
635635
// signal_ops_impl!(u64, lhs * rhs);
636-
signal_ops_impl!(u64, lhs / rhs);
636+
// signal_ops_impl!(u64, lhs / rhs);
637637

638-
signal_ops_impl!(i64, -sig);
638+
// signal_ops_impl!(i64, -sig);
639639
// signal_ops_impl!(i64, lhs + rhs);
640640
// signal_ops_impl!(i64, lhs * rhs);
641-
signal_ops_impl!(i64, lhs / rhs);
641+
// signal_ops_impl!(i64, lhs / rhs);
642642

643-
signal_ops_impl!(f32, -sig);
644-
signal_ops_impl!(f32, lhs + rhs);
645-
signal_ops_impl!(f32, lhs * rhs);
643+
// signal_ops_impl!(f32, -sig);
644+
// signal_ops_impl!(f32, lhs + rhs);
645+
// signal_ops_impl!(f32, lhs * rhs);
646646
// signal_ops_impl!(f32, lhs / rhs);
647647

648-
signal_ops_impl!(f64, -sig);
649-
signal_ops_impl!(f64, lhs + rhs);
650-
signal_ops_impl!(f64, lhs * rhs);
648+
// signal_ops_impl!(f64, -sig);
649+
// signal_ops_impl!(f64, lhs + rhs);
650+
// signal_ops_impl!(f64, lhs * rhs);
651651
// signal_ops_impl!(f64, lhs / rhs);
652652
}
653653
}

argus-core/src/signals/bool_ops.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,37 @@
11
use super::interpolation::Linear;
2+
use super::InterpolationMethod;
23
use crate::signals::Signal;
34

45
impl core::ops::Not for Signal<bool> {
56
type Output = Signal<bool>;
67

78
fn not(self) -> Self::Output {
8-
use Signal::*;
9-
match self {
10-
Empty => self,
11-
Constant { value } => Signal::constant(!value),
12-
signal => signal.into_iter().map(|(&t, v)| (t, !v)).collect(),
13-
}
9+
self.logical_not()
1410
}
1511
}
1612

1713
impl core::ops::Not for &Signal<bool> {
1814
type Output = Signal<bool>;
1915

2016
fn not(self) -> Self::Output {
21-
use Signal::*;
22-
match self {
23-
Empty => Empty,
24-
Constant { value } => Signal::constant(!value),
25-
signal => signal.into_iter().map(|(&t, &v)| (t, !v)).collect(),
26-
}
17+
self.logical_not()
2718
}
2819
}
2920

3021
impl Signal<bool> {
22+
/// Apply logical not for each sample across the signal.
23+
pub fn logical_not(&self) -> Self {
24+
self.unary_op(|&v| !v)
25+
}
26+
3127
/// Apply logical conjunction for each sample across the two signals.
3228
///
3329
/// Here, the conjunction is taken at all signal points where either of the signals
3430
/// are sampled, and where they intersect (using interpolation).
3531
///
3632
/// See [`Signal::sync_with_intersection`].
37-
pub fn and(&self, other: &Self) -> Self {
38-
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs && *rhs)
33+
pub fn and<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
34+
self.binary_op::<_, _, I>(other, |lhs, rhs| *lhs && *rhs)
3935
}
4036

4137
/// Apply logical disjunction for each sample across the two signals.
@@ -44,39 +40,39 @@ impl Signal<bool> {
4440
/// are sampled, and where they intersect (using interpolation).
4541
///
4642
/// See [`Signal::sync_with_intersection`].
47-
pub fn or(&self, other: &Self) -> Self {
48-
self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs || *rhs)
43+
pub fn or<I: InterpolationMethod<bool>>(&self, other: &Self) -> Self {
44+
self.binary_op::<_, _, I>(other, |lhs, rhs| *lhs || *rhs)
4945
}
5046
}
5147

5248
impl core::ops::BitAnd<&Signal<bool>> for Signal<bool> {
5349
type Output = Signal<bool>;
5450

5551
fn bitand(self, other: &Signal<bool>) -> Self::Output {
56-
self.and(other)
52+
self.and::<Linear>(other)
5753
}
5854
}
5955

6056
impl core::ops::BitAnd<&Signal<bool>> for &Signal<bool> {
6157
type Output = Signal<bool>;
6258

6359
fn bitand(self, other: &Signal<bool>) -> Self::Output {
64-
self.and(other)
60+
self.and::<Linear>(other)
6561
}
6662
}
6763

6864
impl core::ops::BitOr<&Signal<bool>> for Signal<bool> {
6965
type Output = Signal<bool>;
7066

7167
fn bitor(self, other: &Signal<bool>) -> Self::Output {
72-
self.or(other)
68+
self.or::<Linear>(other)
7369
}
7470
}
7571

7672
impl core::ops::BitOr<&Signal<bool>> for &Signal<bool> {
7773
type Output = Signal<bool>;
7874

7975
fn bitor(self, other: &Signal<bool>) -> Self::Output {
80-
self.or(other)
76+
self.or::<Linear>(other)
8177
}
8278
}

argus-core/src/signals/num_ops.rs

Lines changed: 75 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,92 @@
1-
use super::interpolation::Linear;
21
use super::{InterpolationMethod, SignalAbs};
32
use crate::signals::Signal;
43

5-
impl<T> core::ops::Neg for Signal<T>
6-
where
7-
T: core::ops::Neg<Output = T>,
8-
{
9-
type Output = Signal<T>;
10-
11-
fn neg(self) -> Self::Output {
12-
use Signal::*;
13-
match self {
14-
Empty => Signal::Empty,
15-
Constant { value } => Signal::constant(value.neg()),
16-
Sampled { values, time_points } => time_points.into_iter().zip(values.into_iter().map(|v| -v)).collect(),
17-
}
18-
}
19-
}
20-
21-
impl<T> core::ops::Neg for &Signal<T>
22-
where
23-
for<'a> &'a T: core::ops::Neg<Output = T>,
24-
{
25-
type Output = Signal<T>;
26-
27-
fn neg(self) -> Self::Output {
28-
use Signal::*;
29-
match self {
30-
Empty => Signal::Empty,
31-
Constant { value } => Signal::constant(value.neg()),
32-
Sampled { values, time_points } => time_points
33-
.iter()
34-
.copied()
35-
.zip(values.iter().map(|v| v.neg()))
36-
.collect(),
37-
}
38-
}
39-
}
40-
41-
impl<T> core::ops::Add<&Signal<T>> for Signal<T>
42-
where
43-
T: Clone,
44-
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
45-
Linear: InterpolationMethod<T>,
46-
{
47-
type Output = Signal<T>;
48-
49-
/// Add the given signal with another
50-
fn add(self, other: &Signal<T>) -> Signal<T> {
51-
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
52-
}
53-
}
54-
55-
impl<T> core::ops::Add<&Signal<T>> for &Signal<T>
56-
where
57-
T: Clone,
58-
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
59-
Linear: InterpolationMethod<T>,
60-
{
61-
type Output = Signal<T>;
62-
63-
/// Add the given signal with another
64-
fn add(self, other: &Signal<T>) -> Signal<T> {
65-
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
66-
}
67-
}
68-
69-
impl<T> core::ops::Mul<&Signal<T>> for Signal<T>
70-
where
71-
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
72-
T: Clone,
73-
Linear: InterpolationMethod<T>,
74-
{
75-
type Output = Signal<T>;
76-
77-
/// Multiply the given signal with another
78-
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
79-
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
4+
impl<T> Signal<T> {
5+
/// Perform sample-wise arithmetic negation over the signal.
6+
pub fn negate<U>(&self) -> Signal<U>
7+
where
8+
for<'a> &'a T: core::ops::Neg<Output = U>,
9+
{
10+
self.unary_op(|v| -v)
8011
}
81-
}
8212

83-
impl<T> core::ops::Mul<&Signal<T>> for &Signal<T>
84-
where
85-
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
86-
T: Clone,
87-
Linear: InterpolationMethod<T>,
88-
{
89-
type Output = Signal<T>;
90-
91-
/// Multiply the given signal with another
92-
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
93-
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
13+
/// Perform sample-wise addition of the two signals.
14+
///
15+
/// Here, a new point is computed for all signal points where either of the signals
16+
/// are sampled and where they intersect (using interpolation).
17+
pub fn add<U, I>(&self, other: &Signal<T>) -> Signal<U>
18+
where
19+
for<'t> &'t T: core::ops::Add<Output = U>,
20+
T: Clone,
21+
I: InterpolationMethod<T>,
22+
{
23+
self.binary_op::<_, _, I>(other, |u, v| u + v)
9424
}
95-
}
9625

97-
impl<T> core::ops::Sub<&Signal<T>> for &Signal<T>
98-
where
99-
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
100-
T: Clone + PartialOrd,
101-
Linear: InterpolationMethod<T>,
102-
{
103-
type Output = Signal<T>;
104-
105-
/// Subtract the given signal with another
106-
fn sub(self, other: &Signal<T>) -> Signal<T> {
107-
// This has to be manually implemented and cannot use the binary_op functions.
108-
// This is because if we have two signals that cross each other, then there is
109-
// an intermediate point where the two signals are equal. This point must be
110-
// added to the signal appropriately.
111-
use Signal::*;
112-
match (self, other) {
113-
// If either of the signals are empty, we return an empty signal.
114-
(Empty, _) | (_, Empty) => Signal::Empty,
115-
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(v1 - v2),
116-
(lhs, rhs) => {
117-
// the union of the sample points in self and other
118-
let sync_points = lhs.sync_with_intersection::<Linear>(rhs).unwrap();
119-
sync_points
120-
.into_iter()
121-
.map(|t| {
122-
let lhs = lhs.interpolate_at::<Linear>(t).unwrap();
123-
let rhs = rhs.interpolate_at::<Linear>(t).unwrap();
124-
(t, &lhs - &rhs)
125-
})
126-
.collect()
127-
}
128-
}
26+
/// Perform sample-wise multiplication of the two signals.
27+
///
28+
/// Here, a new point is computed for all signal points where either of the signals
29+
/// are sampled and where they intersect (using interpolation).
30+
pub fn mul<U, I>(&self, other: &Signal<T>) -> Signal<U>
31+
where
32+
for<'t> &'t T: core::ops::Mul<Output = U>,
33+
T: Clone,
34+
I: InterpolationMethod<T>,
35+
{
36+
self.binary_op::<_, _, I>(other, |u, v| u * v)
12937
}
130-
}
13138

132-
impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
133-
where
134-
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
135-
T: Clone + PartialOrd,
136-
Linear: InterpolationMethod<T>,
137-
{
138-
type Output = Signal<T>;
139-
140-
/// Subtract the given signal with another
141-
fn sub(self, other: &Signal<T>) -> Signal<T> {
142-
<&Self as core::ops::Sub>::sub(&self, other)
39+
/// Perform sample-wise subtraction of the two signals.
40+
///
41+
/// Here, a new point is computed for all signal points where either of the signals
42+
/// are sampled and where they intersect (using interpolation).
43+
pub fn sub<U, I>(&self, other: &Signal<T>) -> Signal<U>
44+
where
45+
for<'t> &'t T: core::ops::Sub<Output = U>,
46+
T: Clone,
47+
I: InterpolationMethod<T>,
48+
{
49+
self.binary_op::<_, _, I>(other, |u, v| u - v)
14350
}
144-
}
14551

146-
impl<T> core::ops::Div<&Signal<T>> for Signal<T>
147-
where
148-
for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
149-
T: Clone,
150-
Linear: InterpolationMethod<T>,
151-
{
152-
type Output = Signal<T>;
153-
154-
/// Divide the given signal with another
155-
fn div(self, rhs: &Signal<T>) -> Self {
156-
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
52+
/// Perform sample-wise division of the two signals.
53+
///
54+
/// Here, a new point is computed for all signal points where either of the signals
55+
/// are sampled and where they intersect (using interpolation).
56+
pub fn div<U, I>(&self, other: &Signal<T>) -> Signal<U>
57+
where
58+
for<'t> &'t T: core::ops::Div<Output = U>,
59+
T: Clone,
60+
I: InterpolationMethod<T>,
61+
{
62+
self.binary_op::<_, _, I>(other, |u, v| u / v)
15763
}
158-
}
15964

160-
impl<T> core::ops::Div<&Signal<T>> for &Signal<T>
161-
where
162-
for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
163-
T: Clone,
164-
Linear: InterpolationMethod<T>,
165-
{
166-
type Output = Signal<T>;
167-
168-
/// Divide the given signal with another
169-
fn div(self, rhs: &Signal<T>) -> Signal<T> {
170-
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
65+
/// Perform sample-wise exponentiation of the two signals.
66+
///
67+
/// Here, a new point is computed for all signal points where either of the signals
68+
/// are sampled and where they intersect (using interpolation).
69+
pub fn pow<U, I>(&self, exponent: &Signal<T>) -> Signal<U>
70+
where
71+
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = U>,
72+
T: Clone,
73+
I: InterpolationMethod<T>,
74+
{
75+
use num_traits::Pow;
76+
self.binary_op::<_, _, I>(exponent, |u, v| u.pow(v))
17177
}
172-
}
17378

174-
impl<T> Signal<T>
175-
where
176-
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = T>,
177-
T: Clone,
178-
Linear: InterpolationMethod<T>,
179-
{
180-
/// Returns the values in `self` to the power of the values in `other`
181-
pub fn pow(&self, other: &Self) -> Self {
182-
use num_traits::Pow;
183-
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs.pow(rhs))
79+
/// Perform sample-wise absolute difference of the two signals.
80+
///
81+
/// Here, a new point is computed for all signal points where either of the signals
82+
/// are sampled and where they intersect (using interpolation).
83+
pub fn abs_diff<U, I>(&self, other: &Signal<T>) -> Signal<U>
84+
where
85+
for<'t> &'t T: core::ops::Sub<Output = U>,
86+
T: Clone + PartialOrd,
87+
I: InterpolationMethod<T>,
88+
{
89+
self.binary_op::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
18490
}
18591
}
18692

@@ -202,6 +108,6 @@ signal_abs_impl!(i64, f32, f64);
202108
impl SignalAbs for Signal<u64> {
203109
/// Return the absolute value for each sample in the signal
204110
fn abs(self) -> Signal<u64> {
205-
self.unary_op(|v| v)
111+
self.unary_op(|&v| v)
206112
}
207113
}

0 commit comments

Comments
 (0)