ETL  C++ library for vector and matrix computations
When working on Machine Learning algorithms, I was in need of a simple library to ease working with vectors and matrix. This is the reason why I started developing ETL (Expression Template Library).
ETL is a small header only library for C++ that provides vector and matrix classes with support for Expression Templates to perform very efficient operations on them.
The library supports statically sized and dynamically sized vector and matrix structures with efficient elementwise operations. All the operations are implemented lazily with Expression Templates, they are only implemented once the expression is assigned to a concrete structure.
Data structures
Several structures are available:

fast_vector<T, Rows>
: A vector of size Rows with elements of type T. This must be used when you know the size of the vector at compiletime. 
dyn_vector<T>
: A vector with element of type T. The size of the vector can be set at runtime. 
fast_matrix<T, Rows,Columns>
: A matrix of size Rows x Columns with elements of type T. This must be used when you know the size of the matrix at compiletime. 
dyn_matrix<T>
: A matrix with element of type T. The size of the matrix can be set at runtime.
All the structures are sizeinvariant, once set they cannot be grown or shrinked.
In every operations that involves fast version of the structures, all the sizes are known at compiletime, this gives the compiler a lot of opportunities for optimization.
Elementwise operations
Classic elementwise operations can be done on vector and matrix as if it was done on scalars. Matrices and vectors can also be added, subtracted, divided, ... by scalars.
Here is an example of what can be done:
etl::dyn_vector<double> a({1.0,2.0,3.0}); etl::dyn_vector<double> b({3.0,2.0,1.0}); etl::dyn_vector<double> c(1.4 * (a + b) / b + b + a / 1.2);
All the operations are only executed once the expression is evaluated to construct the dyn_vector. No temporaries are involved. This is as efficient as if a single for loop was used and each element was computed directly.
You can easily assign the same value to a structure by using the operator = on it.
Unary operators
Several unary operators are available. Each operation is performed on every element of the vector or the matrix.
Available operators:
log
abs
sign
max/min
sigmoid

noise
: Add standard normal noise to each element 
logistic_noise
: Add normal noise of mean zero and variance sigmoid(x) to each element exp
softplus
bernoulli
Several transformations are also available:

hflip
: Flip the vector or the matrix horizontally 
vflip
: Flip the vector or the matrix vertically 
fflip
: Flip the vector or the matrix horizontally and vertically. It is the equivalent ofhflip(vflip(x))

dim/row/col
: Return a vector representing a sub part of a matrix (a row or a col) 
reshape
: Interpret a vector as a matrix
Again, all these operations are performed lazily, they are only executed when the expression is assigned to something.
Lazy evaluation
All binary and unary operations are applied lazily, only when they are assigned to a concrete vector or matrix class.
The expression can be evaluated using the s(x)
function that returns a
concrete class (fast_vector,fast_matrix,dyn_vector,dyn_matrix) based on the
expression.
Reduction
Several reduction functions are available:
 sum: Return the sum of a vector or matrix
 mean: Return the sum of a vector or matrix
 dot: Return the dot product of two vector or matrices
Functions
The header convolution.hpp provides several convolution operations both in 1D (vector) and 2D (matrix). All the convolution are available in valid, full and same versions.
The header mutiplication.hpp provides the matrix multiplication operation
(mmult
). For now on, only the naive algorithm is available. I'll
probably add support for Strassen algorithm in the near future.
It is possible to pass an expression rather than an data structure to functions. You have to keep in mind that expression are lazy, therefore if you pass a + b to a matrix multiplication, an addition will be run each time an element is accessed (n^3 times), therefore, it is rarely efficient.
Examples
Here are some examples of these operators (taken from my Machine Learning Library):
h_a = sigmoid(b + mmul(reshape<1, num_visible>(v_a), w, t)); h_s = bernoulli(h_a);
h_a = min(max(b + mmul(reshape<1, num_visible>(v_a), w, t), 0.0), 6.0); h_s = ranged_noise(h_a, 6.0);
weight exp_sum = sum(exp(b + mmul(reshape<1, num_visible>(v_a), w, t))); h_a = exp(b + mmul(reshape<1, num_visible>(v_a), w, t)) / exp_sum; auto max = std::max_element(h_a.begin(), h_a.end()); h_s = 0.0; h_s(std::distance(h_a.begin(), max)) = 1.0;
Conclusion
This library is available on Github: etl. It is licensed under MIT license.
It is headeronly, therefore you don't have to build it. However, it uses some recent C++14 stuff, you'll need a recent version of Clang or G++ to be able to use it.
If you find an issue or have an idea to improve it, just post it on Github or as a comment here and I'll do my best to work on that. If you have any question on the usage of the library, I'd be glad to answer them.
Comments
Comments powered by Disqus