How do I create an nalgebra static matrix by copying from a dynamic matrix view?

Viewed 539

I'm trying to create a na::Matrix2x3 by copying from the first two rows of a na::Matrix3. The result will be stored in a struct.

I don't know how to do this and the documentation is confusing. I tried clone(), into(), from(...), etc., but nothing seems to work.

Here is a reproducible example where I have tried to use .clone() (playground):

extern crate nalgebra as na; // 0.27.1

fn main() {
    let proj33 = na::Matrix3::<f64>::zeros();
    let proj: na::Matrix2x3<f64> = proj33.rows(0, 2).clone();
}
error[E0308]: mismatched types
 --> src/main.rs:5:36
  |
5 |     let proj: na::Matrix2x3<f64> = proj33.rows(0, 2).clone();
  |               ------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Const`, found struct `Dynamic`
  |               |
  |               expected due to this
  |
  = note: expected struct `Matrix<_, Const<2_usize>, _, ArrayStorage<f64, 2_usize, 3_usize>>`
             found struct `Matrix<_, Dynamic, _, SliceStorage<'_, f64, Dynamic, Const<3_usize>, Const<1_usize>, Const<3_usize>>>`

This seems to work, but it is wasteful as it initializes the matrix to zeros first:

let mut proj = na::Matrix2x3::<f64>::zeros();
proj.copy_from(&proj33.rows(0, 2));

In Eigen (C++) I would have done this:

const Eigen::Matrix<double, 2, 3> proj = proj33.topRows(2);
3 Answers

Okay, I have figured it out. I have to use fixed_slice instead of (or in addition to) just rows --- since the size of the output (Matrix2x3) is known to the compiler, the slice must be as well.

The key is to use:

.fixed_slice::<2, 3>(0, 0).into()

This works for dynamic matrices (playground):

extern crate nalgebra as na;

fn main() {
    let proj33 = na::DMatrix::identity(3, 3);
    let proj: na::Matrix2x3<f64> = proj33.fixed_slice::<2, 3>(0, 0).into();
}

and dynamic matrix views (aka matrices with SliceStorage instead of ArrayStorage) too (playground):

extern crate nalgebra as na;

fn main() {
    let proj33 = na::DMatrix::identity(3, 3);
    let proj33_view = proj33.rows(0, 2);
    dbg!(&proj33_view);
    let proj: na::Matrix2x3<f64> = proj33_view.fixed_slice::<2, 3>(0, 0).into();
}

However, when slicing a dynamic matrix, bear in mind that it will panic if you slice out of bounds.

…or probably better suited: fixed_rows()

let proj33 = na::Matrix2x3::<f64>::zeros();
let proj: na::Matrix2x3<f64> = proj33.fixed_rows::<2>(0).into();

Now, I kinda agree with @Shepmaster that this probably really is completely unnecessary, but if you absolutely cannot stand initializing the Matrix with zeroes, you can use a bit of unsafe:

extern crate nalgebra as na;

fn main() {
    let proj33 = na::Matrix3::<f64>::zeros();
    let proj = unsafe {
        // create a new matrix with probably a bunch of garbage in it
        let mut p = na::Matrix2x3::<f64>::new_uninitialized();
        // copy the data from the other matrix into the unitialized one
        (*p.as_mut_ptr()).copy_from(&proj33.rows(0, 2));
        // pinky promise that it's initialized now
        p.assume_init()
    };
    
    println!("{}", proj);
}

Disclaimer: Matrix::new_unitialized isn't super well documented, so I don't know if this is actually safe or if there are some invariants I'm unaware of. I don't see why this shouldn't be okay, but those are of course famous last words.

Related