tangy
tangy

Reputation: 3256

How do I convert an Armadillo matrix to cube?

I'm trying to recreate the following Python numpy code:

num_rows, num_cols = data.shape
N = 4
data = data.reshape(N, num_rows/N, num_cols)

in C++ using Armadillo matrices and cubes? How can this be done most efficiently. I dont think the resize/reshape operations are supported directly for moving from a 2d matrix to 3d cube?

Upvotes: 4

Views: 1904

Answers (2)

bnaecker
bnaecker

Reputation: 6440

The fastest way to construct such a cube is to use one of the advanced constructors. These allow you to directly create a new object from an arbitrary section of memory, even without copying any of the data. This is closest in spirit to the way NumPy does reshaping, in which a view of the original data is returned, rather than a copy.

You'd use the constructor like so:

// Assuming a is an arma::mat.
int N = 4;
arma::cube c(a.memptr(), N, a.n_rows / N, a.n_cols, false);

This takes the memory directly from a, without copying, and uses it as c's data.

Of course, this is fast, but dangerous. You are responsible for guaranteeing that the pointed-to memory is valid as long as c is around. This means that the lifetime of c must be strictly nested in the lifetime of a. This can be hard to ensure, especially when a and c are both created on the heap.

You can also allow c to copy a's data, by leaving off the last argument or setting it to true. This takes more time than the no-copy constructor, but probably less than assigning each slice from a's data, since this constructor does a single bulk memcpy of the underlying data.

All of this is subject to the row- vs. column-major point brought up by @ewcz's answer. Make sure you know what you get when you reshape, especially if you're using the advanced constructors.

Upvotes: 2

ewcz
ewcz

Reputation: 13087

Since armadillo uses column major ordering of data (in contrast to numpy which relies on row major ordering), simply putting the matrix into a cube with 1 slice and reshaping it will produce different result (the matrix B below). An alternative might be to construct the slices manually:

#include <iostream>
#include <armadillo>

int main(){

    const arma::uword N = 2;
    const arma::uword num_rows = 4;
    const arma::uword num_cols = 3;

    arma::mat A(num_rows, num_cols, arma::fill::randu);

    std::cout << A;

    arma::cube B(num_rows, num_cols, 1);
    B.slice(0) = A;
    B.reshape(num_rows/N, num_cols, N);
    std::cout << B;

    arma::cube C(num_rows/N, num_cols, N);
    for(arma::uword i = 0; i < N; ++i){
        C.slice(i) = A.rows(i*N, (i+1)*N-1);
    }
    std::cout << C;


    return 0;
}

Upvotes: 1

Related Questions