nalgebra/base/
componentwise.rs

1// Non-conventional component-wise operators.
2
3use num::{Signed, Zero};
4use std::ops::{Add, Mul};
5
6use simba::scalar::{ClosedDivAssign, ClosedMulAssign};
7use simba::simd::SimdPartialOrd;
8
9use crate::base::allocator::{Allocator, SameShapeAllocator};
10use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint};
11use crate::base::dimension::Dim;
12use crate::base::storage::{Storage, StorageMut};
13use crate::base::{DefaultAllocator, Matrix, MatrixSum, OMatrix, Scalar};
14use crate::ClosedAddAssign;
15
16/// The type of the result of a matrix component-wise operation.
17pub type MatrixComponentOp<T, R1, C1, R2, C2> = MatrixSum<T, R1, C1, R2, C2>;
18
19impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
20    /// Computes the component-wise absolute value.
21    ///
22    /// # Example
23    ///
24    /// ```
25    /// # use nalgebra::Matrix2;
26    /// let a = Matrix2::new(0.0, 1.0,
27    ///                      -2.0, -3.0);
28    /// assert_eq!(a.abs(), Matrix2::new(0.0, 1.0, 2.0, 3.0))
29    /// ```
30    #[inline]
31    #[must_use]
32    pub fn abs(&self) -> OMatrix<T, R, C>
33    where
34        T: Signed,
35        DefaultAllocator: Allocator<R, C>,
36    {
37        let mut res = self.clone_owned();
38
39        for e in res.iter_mut() {
40            *e = e.abs();
41        }
42
43        res
44    }
45
46    // TODO: add other operators like component_ln, component_pow, etc. ?
47}
48
49macro_rules! component_binop_impl(
50    ($($binop: ident, $binop_mut: ident, $binop_assign: ident, $cmpy: ident, $Trait: ident . $op: ident . $op_assign: ident, $desc:expr, $desc_cmpy:expr, $desc_mut:expr);* $(;)*) => {$(
51        #[doc = $desc]
52        #[inline]
53        #[must_use]
54        pub fn $binop<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> MatrixComponentOp<T, R1, C1, R2, C2>
55            where T: $Trait,
56                  R2: Dim, C2: Dim,
57                  SB: Storage<T, R2, C2>,
58                  DefaultAllocator: SameShapeAllocator<R1, C1, R2, C2>,
59                  ShapeConstraint:  SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
60
61            assert_eq!(self.shape(), rhs.shape(), "Componentwise mul/div: mismatched matrix dimensions.");
62            let mut res = self.clone_owned_sum();
63
64            for j in 0 .. res.ncols() {
65                for i in 0 .. res.nrows() {
66                    unsafe {
67                        res.get_unchecked_mut((i, j)).$op_assign(rhs.get_unchecked((i, j)).clone());
68                    }
69                }
70            }
71
72            res
73        }
74
75        // componentwise binop plus Y.
76        #[doc = $desc_cmpy]
77        #[inline]
78        pub fn $cmpy<R2, C2, SB, R3, C3, SC>(&mut self, alpha: T, a: &Matrix<T, R2, C2, SB>, b: &Matrix<T, R3, C3, SC>, beta: T)
79            where T: $Trait + Zero + Mul<T, Output = T> + Add<T, Output = T>,
80                  R2: Dim, C2: Dim,
81                  R3: Dim, C3: Dim,
82                  SA: StorageMut<T, R1, C1>,
83                  SB: Storage<T, R2, C2>,
84                  SC: Storage<T, R3, C3>,
85                  ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> +
86                                   SameNumberOfRows<R1, R3> + SameNumberOfColumns<C1, C3> {
87            assert_eq!(self.shape(), a.shape(), "Componentwise mul/div: mismatched matrix dimensions.");
88            assert_eq!(self.shape(), b.shape(), "Componentwise mul/div: mismatched matrix dimensions.");
89
90            if beta.is_zero() {
91                for j in 0 .. self.ncols() {
92                    for i in 0 .. self.nrows() {
93                        unsafe {
94                            let res = alpha.clone() * a.get_unchecked((i, j)).clone().$op(b.get_unchecked((i, j)).clone());
95                            *self.get_unchecked_mut((i, j)) = res;
96                        }
97                    }
98                }
99            }
100            else {
101                for j in 0 .. self.ncols() {
102                    for i in 0 .. self.nrows() {
103                        unsafe {
104                            let res = alpha.clone() * a.get_unchecked((i, j)).clone().$op(b.get_unchecked((i, j)).clone());
105                            *self.get_unchecked_mut((i, j)) = beta.clone() * self.get_unchecked((i, j)).clone() + res;
106                        }
107                    }
108                }
109            }
110        }
111
112        #[doc = $desc_mut]
113        #[inline]
114        pub fn $binop_assign<R2, C2, SB>(&mut self, rhs: &Matrix<T, R2, C2, SB>)
115            where T: $Trait,
116                  R2: Dim,
117                  C2: Dim,
118                  SA: StorageMut<T, R1, C1>,
119                  SB: Storage<T, R2, C2>,
120                  ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
121
122            assert_eq!(self.shape(), rhs.shape(), "Componentwise mul/div: mismatched matrix dimensions.");
123
124            for j in 0 .. self.ncols() {
125                for i in 0 .. self.nrows() {
126                    unsafe {
127                        self.get_unchecked_mut((i, j)).$op_assign(rhs.get_unchecked((i, j)).clone());
128                    }
129                }
130            }
131        }
132
133        #[doc = $desc_mut]
134        #[inline]
135        #[deprecated(note = "This is renamed using the `_assign` suffix instead of the `_mut` suffix.")]
136        pub fn $binop_mut<R2, C2, SB>(&mut self, rhs: &Matrix<T, R2, C2, SB>)
137            where T: $Trait,
138                  R2: Dim,
139                  C2: Dim,
140                  SA: StorageMut<T, R1, C1>,
141                  SB: Storage<T, R2, C2>,
142                  ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
143            self.$binop_assign(rhs)
144        }
145    )*}
146);
147
148/// # Componentwise operations
149impl<T: Scalar, R1: Dim, C1: Dim, SA: Storage<T, R1, C1>> Matrix<T, R1, C1, SA> {
150    component_binop_impl!(
151        component_mul, component_mul_mut, component_mul_assign, cmpy, ClosedMulAssign.mul.mul_assign,
152        r"
153        Componentwise matrix or vector multiplication.
154
155        # Example
156
157        ```
158        # use nalgebra::Matrix2;
159        let a = Matrix2::new(0.0, 1.0, 2.0, 3.0);
160        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
161        let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0);
162
163        assert_eq!(a.component_mul(&b), expected);
164        ```
165        ",
166        r"
167        Computes componentwise `self[i] = alpha * a[i] * b[i] + beta * self[i]`.
168
169        # Example
170        ```
171        # use nalgebra::Matrix2;
172        let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0);
173        let a = Matrix2::new(0.0, 1.0, 2.0, 3.0);
174        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
175        let expected = (a.component_mul(&b) * 5.0) + m * 10.0;
176
177        m.cmpy(5.0, &a, &b, 10.0);
178        assert_eq!(m, expected);
179        ```
180        ",
181        r"
182        Inplace componentwise matrix or vector multiplication.
183
184        # Example
185        ```
186        # use nalgebra::Matrix2;
187        let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0);
188        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
189        let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0);
190
191        a.component_mul_assign(&b);
192
193        assert_eq!(a, expected);
194        ```
195        ";
196        component_div, component_div_mut, component_div_assign, cdpy, ClosedDivAssign.div.div_assign,
197        r"
198        Componentwise matrix or vector division.
199
200        # Example
201
202        ```
203        # use nalgebra::Matrix2;
204        let a = Matrix2::new(0.0, 1.0, 2.0, 3.0);
205        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
206        let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0);
207
208        assert_eq!(a.component_div(&b), expected);
209        ```
210        ",
211        r"
212        Computes componentwise `self[i] = alpha * a[i] / b[i] + beta * self[i]`.
213
214        # Example
215        ```
216        # use nalgebra::Matrix2;
217        let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0);
218        let a = Matrix2::new(4.0, 5.0, 6.0, 7.0);
219        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
220        let expected = (a.component_div(&b) * 5.0) + m * 10.0;
221
222        m.cdpy(5.0, &a, &b, 10.0);
223        assert_eq!(m, expected);
224        ```
225        ",
226        r"
227        Inplace componentwise matrix or vector division.
228
229        # Example
230        ```
231        # use nalgebra::Matrix2;
232        let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0);
233        let b = Matrix2::new(4.0, 5.0, 6.0, 7.0);
234        let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0);
235
236        a.component_div_assign(&b);
237
238        assert_eq!(a, expected);
239        ```
240        ";
241        // TODO: add other operators like bitshift, etc. ?
242    );
243
244    /// Computes the infimum (aka. componentwise min) of two matrices/vectors.
245    ///
246    /// # Example
247    ///
248    /// ```
249    /// # use nalgebra::Matrix2;
250    /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0);
251    /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0);
252    /// let expected = Matrix2::new(2.0, 2.0, -2.0, -2.0);
253    /// assert_eq!(u.inf(&v), expected)
254    /// ```
255    #[inline]
256    #[must_use]
257    pub fn inf(&self, other: &Self) -> OMatrix<T, R1, C1>
258    where
259        T: SimdPartialOrd,
260        DefaultAllocator: Allocator<R1, C1>,
261    {
262        self.zip_map(other, |a, b| a.simd_min(b))
263    }
264
265    /// Computes the supremum (aka. componentwise max) of two matrices/vectors.
266    ///
267    /// # Example
268    ///
269    /// ```
270    /// # use nalgebra::Matrix2;
271    /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0);
272    /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0);
273    /// let expected = Matrix2::new(4.0, 4.0, 1.0, 1.0);
274    /// assert_eq!(u.sup(&v), expected)
275    /// ```
276    #[inline]
277    #[must_use]
278    pub fn sup(&self, other: &Self) -> OMatrix<T, R1, C1>
279    where
280        T: SimdPartialOrd,
281        DefaultAllocator: Allocator<R1, C1>,
282    {
283        self.zip_map(other, |a, b| a.simd_max(b))
284    }
285
286    /// Computes the (infimum, supremum) of two matrices/vectors.
287    ///
288    /// # Example
289    ///
290    /// ```
291    /// # use nalgebra::Matrix2;
292    /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0);
293    /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0);
294    /// let expected = (Matrix2::new(2.0, 2.0, -2.0, -2.0), Matrix2::new(4.0, 4.0, 1.0, 1.0));
295    /// assert_eq!(u.inf_sup(&v), expected)
296    /// ```
297    #[inline]
298    #[must_use]
299    pub fn inf_sup(&self, other: &Self) -> (OMatrix<T, R1, C1>, OMatrix<T, R1, C1>)
300    where
301        T: SimdPartialOrd,
302        DefaultAllocator: Allocator<R1, C1>,
303    {
304        // TODO: can this be optimized?
305        (self.inf(other), self.sup(other))
306    }
307
308    /// Adds a scalar to `self`.
309    ///
310    /// # Example
311    ///
312    /// ```
313    /// # use nalgebra::Matrix2;
314    /// let u = Matrix2::new(1.0, 2.0, 3.0, 4.0);
315    /// let s = 10.0;
316    /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0);
317    /// assert_eq!(u.add_scalar(s), expected)
318    /// ```
319    #[inline]
320    #[must_use = "Did you mean to use add_scalar_mut()?"]
321    pub fn add_scalar(&self, rhs: T) -> OMatrix<T, R1, C1>
322    where
323        T: ClosedAddAssign,
324        DefaultAllocator: Allocator<R1, C1>,
325    {
326        let mut res = self.clone_owned();
327        res.add_scalar_mut(rhs);
328        res
329    }
330
331    /// Adds a scalar to `self` in-place.
332    ///
333    /// # Example
334    ///
335    /// ```
336    /// # use nalgebra::Matrix2;
337    /// let mut u = Matrix2::new(1.0, 2.0, 3.0, 4.0);
338    /// let s = 10.0;
339    /// u.add_scalar_mut(s);
340    /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0);
341    /// assert_eq!(u, expected)
342    /// ```
343    #[inline]
344    pub fn add_scalar_mut(&mut self, rhs: T)
345    where
346        T: ClosedAddAssign,
347        SA: StorageMut<T, R1, C1>,
348    {
349        for e in self.iter_mut() {
350            *e += rhs.clone()
351        }
352    }
353}