2

I'm trying to implement the exp function for a generic square MatrixMN

pub fn exp<N, R>(m: &MatrixMN<N, R, R>, k: usize) -> MatrixMN<N, R, R>
where
    N: Scalar + One + Zero,
    R: DimName + DimNameAdd<R>,
    <R as DimName>::Value: Mul<<R as DimName>::Value>,
    <<R as DimName>::Value as Mul<<R as DimName>::Value>>::Output: generic_array::ArrayLength<N>,
{
    let mut i = MatrixMN::<N, R, R>::identity();
    i.add(&m)
}

But I keep getting this errors like this.

error[E0599]: no method named `add` found for struct `nalgebra::base::matrix::Matrix<N, R, R, nalgebra::base::array_storage::ArrayStorage<N, R, R>>` in the current scope
  --> src/state_extrapolation.rs:24:7
   |
24 |     i.add(&m)
   |       ^^^ method not found in `nalgebra::base::matrix::Matrix<N, R, R, nalgebra::base::array_storage::ArrayStorage<N, R, R>>`
   |
   = note: the method `add` exists but the following trait bounds were not satisfied:
           `&mut nalgebra::base::matrix::Matrix<N, R, R, nalgebra::base::array_storage::ArrayStorage<N, R, R>> : nalgebra::base::dimension::DimNameAdd<_>`
           `&nalgebra::base::matrix::Matrix<N, R, R, nalgebra::base::array_storage::ArrayStorage<N, R, R>> : nalgebra::base::dimension::DimNameAdd<_>`
           `nalgebra::base::matrix::Matrix<N, R, R, nalgebra::base::array_storage::ArrayStorage<N, R, R>> : nalgebra::base::dimension::DimNameAdd<_>`

Is there a better way to pass generic matrices to functions?

I've also tried with something like this

pub fn exp2<M>(m: &M, k: usize) -> M
where
    M: nalgebra::base::Matrix<_, _, _, _>,
{
    let mut i = M::identity();

    i.add(&m)
}

But cannot come up with good traits for M.

nnnmmm
  • 7,964
  • 4
  • 22
  • 41
Fredrik Jansson
  • 3,764
  • 3
  • 30
  • 33

1 Answers1

3

It's easy to get lost in the traits when making things fully generic. My tips are:

  • Copy the signature of the impl block that implements similar functions to yours, e.g. the line DefaultAllocator: Allocator<N, R, R> from here allows getting rid of many constraints
  • Instead of Scalar, if it's going to be floats you will calculate with, it's easier to use RealField which gives you Scalar plus many other useful properties (like One and Zero required for the identity() function)
  • Follow the compiler errors – it suggested I add a use std::ops::Add, which made it work in the end.

Here is the code, playground:

use nalgebra::{
    base::allocator::Allocator, DefaultAllocator, DimName, DimNameAdd, MatrixN, RealField,
};
use std::ops::Add;

fn exp<N, R>(m: &MatrixN<N, R>, k: usize) -> MatrixN<N, R>
where
    N: RealField,
    R: DimName + DimNameAdd<R>,
    DefaultAllocator: Allocator<N, R, R>,
{
    let i = MatrixN::<N, R>::identity();
    m.add(i)
}
nnnmmm
  • 7,964
  • 4
  • 22
  • 41
  • Can I just ask, what does DefaultAllocator: Allocator actually do? – Fredrik Jansson Mar 27 '20 at 12:22
  • It seems like nalgebra allows switching out allocators with anything that implements the Allocator trait. I guess the Allocator trait needs to calculate the memory requirement for your matrix (size_of::() * R * C) which is why it needs R and C to be multiply-able. – nnnmmm Mar 27 '20 at 12:44
  • Syntactically, it's sort of an inverse trait bound, where you have a concrete type on the left and a generic trait that it satisfies on the right, like `f64: From`. – nnnmmm Mar 27 '20 at 12:44
  • Thanks! I'm glad I could help :) – nnnmmm Mar 27 '20 at 13:47