13

How can I use a std::valarray to store/manipulate a 2D array?

I'd like to see an example of a 2D array with elements accessed by row/column indices. Something like this pseudo code:

matrix(i,j) = 42;

An example of how to initialize such an array would also be nice.

I'm already aware of Boost.MultiArray, Boost.uBlas, and Blitz++.

Feel free to answer why I shouldn't use valarray for my use case. However, I want the memory for the multidimensional array to be a contiguous (columns x rows) block. No Java-style nested arrays.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
  • Why would you want to use valarray? I haven't seen it in use anywhere and only know of arguments against its use. – Georg Fritzsche Feb 02 '10 at 21:28
  • 1
    @gf: I'm quite happy with boost for "dynamic" multidimensional arrays. But I stumbled upon valarray somewhere else in SO, and was curious on how it could be used for multi-dimensional arrays. Online examples on using valarray (particularly for my use case) seem scarce. – Emile Cormier Feb 02 '10 at 21:39
  • 4
    @GeorgFritzsche what are the arguments against using it? – johnbakers Jun 04 '13 at 08:59

5 Answers5

15

Off the top of my head:

template <class element_type>
class matrix
{
public:
    matrix(size_t width, size_t height): m_stride(width), m_height(height), m_storage(width*height) {  }

    element_type &operator()(size_t row, size_t column)
    {
        // column major
        return m_storage[std::slice(column, m_height, m_stride)][row];

        // row major
        return m_storage[std::slice(row, m_stride, m_height)][column];
    }

private:
    std::valarray<element_type> m_storage;
    size_t m_stride;
    size_t m_height;
};

std::valarray provides many interesting ways to access elements, via slices, masks, multidimentional slices, or an indirection table. See std::slice_array, std::gslice_array, std::mask_array, and std::indirect_array for more details.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • Ah yes, thank you. That's what I was looking for. I especially like that you showed both row/column major access. – Emile Cormier Feb 02 '10 at 22:07
  • Is the valarray data guaranteed to be contiguous, like vector? – Emile Cormier Feb 02 '10 at 22:09
  • From MSDN: The template class describes an object that controls a sequence of elements of type Type that are stored as an array, designed for performing high-speed mathematical operations, and optimized for computational performance. So, I would assume so. – MSN Feb 02 '10 at 22:18
  • @MSN There is an error in your ctor, you have two m_stride. `m_stride(width), m_stride(height)` – ggg Aug 07 '12 at 05:45
  • 4
    @MSN: Which compiler/stdlib were you using such that `matrix::operator()` compiled? On my copy of gcc 4.4.5 `slice_array` (as returned by `valarray::operator[slice slc]`) doesn't seem to have an `operator[]`. [Here's what I was trying](http://pastebin.com/YVa9CUGD). The [`clang` demo site](http://llvm.org/demo/) also chokes on it. – genpfault Nov 05 '12 at 20:27
  • @genpfault, I never compiled it. Which looking at the documentation for slice_array means this just won't work, although returning gslice_array would. – MSN Nov 05 '12 at 23:48
  • @MSN the answer is not correct - slice_array/gslice_array do not imply per-element access (as far as I understand these classes were aimed for SIMD operation). On the other side, one can "effectively" assign a row (at a specified stride) of values from another valarray. – Arty Jul 27 '16 at 23:25
  • Yes, as @genpfault mentioned, I'm able to use slice as a column accessor, but not as a row accessor. Any workaround for this? – Naveen Oct 03 '16 at 22:01
6
#include <iostream>
#include <valarray>

using namespace std;

typedef valarray<valarray<int> > va2d;

int main()
{
    int data[][3] = { {1, 2, 3}, {4, 5, 6} };
    va2d mat(valarray<int>(3), 2);
    for (int i = 0; i < 2; ++i)
    {
        for (int j = 0; j < 3; ++j)
           mat[ i ][ j ] = data[ i ][ j ];
    }
    for (int i = 0; i < 2; ++i)
        for (int j = 0; j < 3; ++j)
           cout << mat[ i ][ j ] << endl;
}

More on valarray:

  • It is optimized for numeric computation.
  • It is a vector like container with special member functions for slicing and dicing.
  • No iterators
  • Designed for vector machines and perform poorly on current ones: vector access may be faster
  • Was not supported by all compilers (check the documentation) / poorly implemented
  • See 26.1 for the types that can be used as a parameter to valarray<T>: E.g:

3 In addition, many member and related functions of valarray can be successfully instantiated and will exhibit well-defined behavior if and only if T satisfies additional requirements specified for each such member or related function.

4 [ Example: It is valid to instantiate valarray, but operator>() will not be successfully instantiated for valarray operands, since complex does not have any ordering operators. —end example ]

Edit#2: The standard gurantees that vector, like arrays, always use contiguous memory. Also, we have:

26.5.2 Class template valarray

1 The class template valarray is a one-dimensional smart array, with elements numbered sequentially from zero. It is a representation of the mathematical concept of an ordered set of values. The illusion of higher dimensionality may be produced by the familiar idiom of computed indices, together with the powerful subsetting capabilities provided by the generalized subscript operators.

and further:

26.5.2.3 valarray element access

4 Likewise, the expression &a[i] != &b[j] evaluates as true for any two arrays a and b and for any size_t i and size_t j such that i is less than the length of a and j is less than the length of b. This property indicates an absence of aliasing and may be used to advantage by optimizing compilers.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • Oops. Sorry. Didn't clarify my question fast enough. +1 anyways. – Emile Cormier Feb 02 '10 at 21:56
  • `valarray` is intended to contain multidimensional arrays without nesting. `valarray >` should only be necessary for an array of matrices/arrays of different sizes. – Potatoswatter Feb 02 '10 at 22:10
  • dirkgently answered before I clarified that I wanted contiguous memory. No need to downvote. – Emile Cormier Feb 02 '10 at 22:20
  • From the quotes you posted, it's not clear to me if the valarray data is guaranteed to be contiguous. Can you elaborate? – Emile Cormier Feb 02 '10 at 22:24
  • In valarray, the type T has some constraints (not enforced by the compiler). T cannot be a valarray, according to this http://gcc.gnu.org/ml/libstdc++/2000-q1/msg00123.html – ggg Dec 11 '13 at 08:43
1

If you want to test for the multidimensional array to be a contiguous (columns x rows) block. You can see this example with valarray.

template<class T>
class Array: public std::valarray<T> {
  size_t row,col; 
public:
  Array(){}
  Array(size_t nx) : std::valarray<T>(nx){row=nx,col=1;}
  Array(size_t nx, size_t ny) : std::valarray<T>(nx*ny){row=nx,col=ny;}
  virtual ~Array(){}
  T& operator()(size_t i,size_t j) { return (*this)[i*col+j];}
  size_t Size(size_t n) const { return (n<2) ? col: row;}
};
0

I didn't find any of the other responses particularly helpful so I thought I'd chip in with a solution I found. The top answer is kinda there, but as far as I can tell, there is no default way to index a slice. I don't know for the life of me why this isn't a feature but here's what you can do to make it work.

If you add a new class that extends slice_array you can give it an indexer method.

class slice_darray : slice_array<double> {
public:
    double& operator[](size_t idx){ return _Data(start() + idx * stride());}
};

Once you've done that you can just change the function in @MSN's post to something like

slice_darray& operator[](size_t row) { return (slice_darray&)(m_storage[std::slice(row * m_rows, m_rows, 1)]); 

And that should give you the behavior you want. You should be able to access the matrix by doing something like M[row][col]

  • What is `_Data`? Is it a member of `std::slice_array` in your particular standard library? – Emile Cormier Jun 13 '23 at 22:22
  • Oh, I understand now. `_Data` is reference to the original `valarray`. It's too bad `slice_array` already has this reference and you end up holding it twice in your extension class. Still, a clever workaround. – Emile Cormier Jun 13 '23 at 22:46
  • No, I still don't understand. You don't pass a reference/pointer to the original `valarray` when constructing your `slice_darray`. You're also returning a dangling reference in your second `operator[]`. – Emile Cormier Jun 13 '23 at 22:55
  • Yea agree, should be avoided – panic Jun 15 '23 at 12:44
  • The slice gets a reference to the valarry when its initialized as an indexer. So val[slice(a,b,c)]. The solution I wrote here is tested and works perfectly. – Defunked-Melon Jun 16 '23 at 19:25
-2

Here's an example that includes a bit of matrix manipulation

Liz Albin
  • 1,479
  • 8
  • 8
  • 2
    Could you at least include a short example that addresses the question? External sites might be down at some point. – Georg Fritzsche Feb 02 '10 at 21:30
  • 2
    I dont seem to be able to find a 2D `valarray` part in that example but a 2D matrix flattened out. I think the OP wants a `valarray< valarray >` thingy. – dirkgently Feb 02 '10 at 21:39
  • @dirkgently: I want the array memory to be contiguous, so no valarray< valarray >. Edited question. – Emile Cormier Feb 02 '10 at 21:51