Multivectors and multiexpressions

The vex::multivector<T,N> class allows to store several equally sized device vectors and perform computations on each component in sync. Each operation is delegated to the underlying vectors, but usually results in the launch of a single fused kernel. Expressions may include values of std::array<T,N> where N is equal to the number of multivector components, or appropriately sized tuples. Each component gets the corresponding element of either the array or the tuple when the expression is applied. Similarly, vex::multivector::operator[]() or reduction of a multivector returns an instance of std::array<T,N>. vex::multivector::operator()() allows to access individual components of a multivector.

Some examples:

VEX_FUNCTION(bool, between, (double, a)(double, b)(double, c),
    return a <= b && b <= c;
    );

vex::Reductor<double, vex::SUM> sum(ctx);
vex::SpMat<double> A(ctx, ... );
std::array<double, 2> v = {6.0, 7.0};

vex::multivector<double, 2> X(ctx, N), Y(ctx, N);

// ...

X = sin(v * Y + 1);             // X(k) = sin(v[k] * Y(k) + 1);
v = sum( between(0, X, Y) );    // v[k] = sum( between( 0, X(k), Y(k) ) );
X = A * Y;                      // X(k) = A * Y(k);

Some operations can not be expressed with simple multivector arithmetic. For example, an operation of two dimensional rotation mixes components in the right hand side expressions:

\[\begin{split}y_0 = x_0 \cos(\alpha) - x_1 \sin(\alpha),\\ y_1 = x_0 \sin(\alpha) + x_1 \cos(\alpha).\end{split}\]

This may in principle be implemented as:

double alpha;
vex::multivector<double, 2> X(ctx, N), Y(ctx, N);

Y(0) = X(0) * cos(alpha) - X(1) * sin(alpha);
Y(1) = X(0) * sin(alpha) + X(1) * cos(alpha);

But this would result in two kernel launches instead of single fused launch. VexCL allows one to assign a tuple of expressions to a multivector, which will lead to the launch of a single fused kernel:

Y = std::make_tuple(
    X(0) * cos(alpha) - X(1) * sin(alpha),
    X(0) * sin(alpha) + X(1) * cos(alpha) );

vex::tie() function even allows to get rid of multivectors completely and fuse several vector expressions into a single kernel. We can rewrite the above examples with just vex::vector<double> instances:

vex::vector<double> x0(ctx, N), x1(ctx,N), y0(ctx, N), y1(ctx, N);

vex::tie(y0, y1) = std::make_tuple(
    x0 * cos(alpha) - x1 * sin(alpha),
    x0 * sin(alpha) + x1 * cos(alpha) );
template <typename T, size_t N>
class vex::multivector

Container for several equally sized instances of vex::vector<T>.

Inherits from vex::multivector_expression< Expr >

Public Functions

multivector(const std::vector<backend::command_queue> &queue, const std::vector<T> &host, backend::mem_flags flags = backend::MEM_READ_WRITE)

Constructor.

The host vector data is divided equally between the created multivector components. Each component gets continuous chunk of the source vector.

multivector(const std::vector<backend::command_queue> &queue, size_t size, const T *host = 0, backend::mem_flags flags = backend::MEM_READ_WRITE)

Constructor.

If host pointer is not NULL, it is copied to the underlying vector components of the multivector. Each component gets continuous chunk of the source vector.

multivector(size_t size)

Constructor.

Uses the most recently created VexCL context.

multivector(const multivector &mv)

Copy constructor.

multivector(multivector &&mv)

Move constructor.

void resize(const std::vector<backend::command_queue> &queue, size_t size)

Resizes the multivector.

This is equivalent to reconstructing the vector with the given parameters. Any data contained in the resized vector will be lost as a result.

void resize(size_t size)

Resizes the multivector.

Uses the most recently created VexCL context. This is equivalent to reconstructing the vector with the given parameters. Any data contained in the resized vector will be lost as a result.

void clear()

Fills the multivector with zeros.

size_t size() const

Returns size of the multivector (equals size of individual components).

const vex::vector<T> &operator()(size_t i) const

Returns i-th multivector component.

vex::vector<T> &operator()(size_t i)

Returns i-th multivector component.

const_iterator begin() const

Returns const iterator to the first element of the multivector.

iterator begin()

Returns const iterator to the first element of the multivector.

const_iterator end() const

Returns const iterator referring to the past-the-end element in the multivector.

iterator end()

Returns iterator referring to the past-the-end element in the multivector.

const_element operator[](size_t i) const

Returns i-th elements of all components packed in a std::array<T,N>.

element operator[](size_t i)

Assigns values from std::array<T,N> to i-th elements of all components.

const std::vector<backend::command_queue> &queue_list() const

Returns reference to the multivector’s queue list.

const multivector &operator=(const multivector &mv)

Assignment operator.

template <class Expr>
auto operator=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator+=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator-=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator*=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator/=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator%=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator&=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator|=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator^=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator<<=(const Expr &expr)

Assignment operator

template <class Expr>
auto operator>>=(const Expr &expr)

Assignment operator

template <class V, class E>
class iterator_type

Inherits from boost::iterator_facade< iterator_type< V, E >, sub_value_type, std::random_access_iterator_tag, E >

template <class... Expr>
auto vex::tie(const Expr&... expr)

Ties several vector expressions into a writeable tuple.

The following example results in a single kernel:

vex::vector<double> x(ctx, 1024);
vex::vector<double> y(ctx, 1024);

vex::tie(x,y) = std::make_tuple( x + y, y - x );

This is functionally equivalent to the following code, but does not use temporary vectors and is more efficient:

tmp_x = x + y;
tmp_y = y - x;
x = tmp_x;
y = tmp_y;