![]() |
Blaze
3.6
|
Instantiating and setting up a vector is very easy and intuitive. However, there are a few rules to take care of:
StaticVector
or HybridVector
are default initialized (i.e. built-in data types are initialized to 0, class types are initialized via the default constructor).DynamicVector
or CompressedVector
remain uninitialized if they are of built-in type and are default constructed if they are of class type.
The DynamicVector
, HybridVector
and CompressedVector
classes offer a constructor that allows to immediately give the vector the required size. Whereas both dense vectors (i.e. DynamicVector
and HybridVector
) use this information to allocate memory for all vector elements, CompressedVector
merely acquires the size but remains empty.
All dense vector classes offer a constructor that allows for a direct, homogeneous initialization of all vector elements. In contrast, for sparse vectors the predicted number of non-zero elements can be specified
Alternatively, all dense vector classes offer a constructor for an initialization with a dynamic or static array. If the vector is initialized from a dynamic array, the constructor expects the actual size of the array as first argument, the array as second argument. In case of a static array, the fixed size of the array is used:
In addition, all dense and sparse vector classes can be directly initialized by means of an initializer list:
Dynamically sized vectors (such as e.g. HybridVector, DynamicVector or CompressedVector) are sized according to the size of the initializer list and all their elements are (copy) assigned the values of the list. For fixed size vectors (such as e.g. StaticVector) missing values are initialized as default and in case the size of the initializer list exceeds the size of the vector a std::invalid_argument
exception is thrown. In case of sparse vectors, only the non-zero elements are used to initialize the vector.
All dense and sparse vectors can be created as the copy of any other dense or sparse vector with the same transpose flag (i.e. blaze::rowVector or blaze::columnVector).
Note that it is not possible to create a StaticVector
as a copy of a vector with a different size:
There are several types of assignment to dense and sparse vectors: Homogeneous Assignment, Array Assignment, Copy Assignment, and Compound Assignment.
Sometimes it may be necessary to assign the same value to all elements of a dense vector. For this purpose, the assignment operator can be used:
Dense vectors can also be assigned a static array:
Alternatively, it is possible to directly assign an initializer list to a dense or sparse vector:
Dynamically sized vectors (such as e.g. HybridVector, DynamicVector or CompressedVector) are resized according to the size of the initializer list and all their elements are (copy) assigned the values of the list. For fixed size vectors (such as e.g. StaticVector) missing values are reset to their default value and in case the size of the initializer list exceeds the size of the vector a std::invalid_argument
exception is thrown. In case of sparse vectors, only the non-zero elements are considered.
For all vector types it is generally possible to assign another vector with the same transpose flag (i.e. blaze::columnVector or blaze::rowVector). Note that in case of StaticVectors
, the assigned vector is required to have the same size as the StaticVector
since the size of a StaticVector
cannot be adapted!
Next to plain assignment, it is also possible to use addition assignment, subtraction assignment, and multiplication assignment. Note however, that in contrast to plain assignment the size and the transpose flag of the vectors has be to equal in order to able to perform a compound assignment.
The easiest and most intuitive way to access a dense or sparse vector is via the subscript operator. The indices to access a vector are zero-based:
Whereas using the subscript operator on a dense vector only accesses the already existing element, accessing an element of a sparse vector via the subscript operator potentially inserts the element into the vector and may therefore be more expensive. Consider the following example:
Although the compressed vector is only used for read access within the for loop, using the subscript operator temporarily inserts 10 non-zero elements into the vector. Therefore the preferred way to traverse the non-zero elements of a sparse vector is to use iterators.
All vectors (sparse as well as dense) offer an alternate way via the begin()
, cbegin()
, end()
, and cend()
functions to traverse the currently contained elements by iterators. In case of non-const vectors, begin()
and end()
return an Iterator
, which allows a manipulation of the non-zero value, in case of a constant vector or in case cbegin()
or cend()
are used a ConstIterator
is returned:
Note that begin()
, cbegin()
, end()
, and cend()
are also available as free functions:
In contrast to dense vectors, that store all elements independent of their value and that offer direct access to all elements, spares vectors only store the non-zero elements contained in the vector. Therefore it is necessary to explicitly add elements to the vector.
The first option to add elements to a sparse vector is the subscript operator:
In case the element at the given index is not yet contained in the vector, it is automatically inserted. Otherwise the old value is replaced by the new value 2. The operator returns a reference to the sparse vector element.
An alternative to the subscript operator is the set()
function: In case the element is not yet contained in the vector the element is inserted, else the element's value is modified:
The insertion of elements can be better controlled via the insert()
function. In contrast to the subscript operator and the set()
function it emits an exception in case the element is already contained in the vector. In order to check for this case, the find()
function can be used:
Although the insert()
function is very flexible, due to performance reasons it is not suited for the setup of large sparse vectors. A very efficient, yet also very low-level way to fill a sparse vector is the append()
function. It requires the sparse vector to provide enough capacity to insert a new element. Additionally, the index of the new element must be larger than the index of the previous element. Violating these conditions results in undefined behavior!
The erase()
member functions can be used to remove elements from a sparse vector. The following example gives an impression of the five different flavors of erase()
:
A sparse vector only stores the non-zero elements contained in the vector. Therefore, whenever accessing a vector element at a specific index a lookup operation is required. Whereas the subscript operator is performing this lookup automatically, it is also possible to use the find()
, lowerBound()
, and upperBound()
member functions for a manual lookup.
The find()
function can be used to check whether a specific element is contained in a sparse vector. It specifically searches for the element at the given index. In case the element is found, the function returns an iterator to the element. Otherwise an iterator just past the last non-zero element of the compressed vector (the end()
iterator) is returned. Note that the returned iterator is subject to invalidation due to inserting operations via the subscript operator, the set()
function or the insert()
function!
The lowerBound()
function returns an iterator to the first element with an index not less then the given index. In combination with the upperBound()
function this function can be used to create a pair of iterators specifying a range of indices. Note that the returned iterator is subject to invalidation due to inserting operations via the subscript operator, the set()
function or the insert()
function!
The upperBound()
function returns an iterator to the first element with an index greater then the given index. In combination with the lowerBound()
function this function can be used to create a pair of iterators specifying a range of indices. Note that the returned iterator is subject to invalidation due to inserting operations via the subscript operator, the set()
function or the insert()
function!
Via the size()
member function, the current size of a dense or sparse vector can be queried:
Alternatively, the free function size()
can be used to query to current size of a vector. In contrast to the member function, the free function can also be used to query the size of vector expressions:
Via the capacity()
(member) function the internal capacity of a dense or sparse vector can be queried. Note that the capacity of a vector doesn't have to be equal to the size of a vector. In case of a dense vector the capacity will always be greater or equal than the size of the vector, in case of a sparse vector the capacity may even be less than the size.
For symmetry reasons, there is also a free function /c capacity() available that can be used to query the capacity:
Note, however, that it is not possible to query the capacity of a vector expression:
For both dense and sparse vectors the number of non-zero elements can be determined via the nonZeros()
member function. Sparse vectors directly return their number of non-zero elements, dense vectors traverse their elements and count the number of non-zero elements.
There is also a free function nonZeros()
available to query the current number of non-zero elements:
The free nonZeros()
function can also be used to query the number of non-zero elements in a vector expression. However, the result is not the exact number of non-zero elements, but may be a rough estimation:
The isEmpty()
function returns whether the total number of elements of the vector is zero:
The isnan()
function provides the means to check a dense or sparse vector for non-a-number elements:
If at least one element of the vector is not-a-number, the function returns true
, otherwise it returns false
. Please note that this function only works for vectors with floating point elements. The attempt to use it for a vector with a non-floating point element type results in a compile time error.
The isDefault()
function returns whether the given dense or sparse vector is in default state:
A vector is in default state if it appears to just have been default constructed. All resizable vectors (HybridVector
, DynamicVector
, or CompressedVector
) and CustomVector
are in default state if its size is equal to zero. A non-resizable vector (StaticVector
, all subvectors, element selections, rows, and columns) is in default state if all its elements are in default state. For instance, in case the vector is instantiated for a built-in integral or floating point data type, the function returns true
in case all vector elements are 0 and false
in case any vector element is not 0.
In order to check if all vector elements are identical, the isUniform()
function can be used:
Note that in case of sparse vectors the zero elements are also taken into account!
In order to check if all vector elements are zero, the isZero()
function can be used:
In order to calculate the length (magnitude) of a dense or sparse vector, both the length()
and sqrLength()
function can be used:
Note that both functions can only be used for vectors with built-in or complex element type!
As already mentioned, vectors can either be column vectors (blaze::columnVector) or row vectors (blaze::rowVector). A column vector cannot be assigned to a row vector and vice versa. However, vectors can be transposed via the trans()
function:
It is also possible to compute the conjugate transpose of a vector. This operation is available via the ctrans()
function:
Note that the ctrans()
function has the same effect as manually applying the conj()
and trans()
function in any order:
Via the reverse()
function is is possible to reverse the elements of a dense or sparse vector. The following examples demonstrates this by means of a dense vector:
The evaluate()
function forces an evaluation of the given vector expression and enables an automatic deduction of the correct result type of an operation. The following code example demonstrates its intended use for the multiplication of a dense and a sparse vector:
In this scenario, the evaluate()
function assists in deducing the exact result type of the operation via the auto
keyword. Please note that if evaluate()
is used in this way, no temporary vector is created and no copy operation is performed. Instead, the result is directly written to the target vector due to the return value optimization (RVO). However, if evaluate()
is used in combination with an explicit target type, a temporary will be created and a copy operation will be performed if the used type differs from the type returned from the function:
Sometimes it might be desirable to explicitly evaluate a sub-expression within a larger expression. However, please note that evaluate()
is not intended to be used for this purpose. This task is more elegantly and efficiently handled by the eval()
function:
In contrast to the evaluate()
function, eval()
can take the complete expression into account and therefore can guarantee the most efficient way to evaluate it (see also Intra-Statement Optimization).
The size of a StaticVector
is fixed by the second template parameter and a CustomVector
cannot be resized. In contrast, the size of DynamicVectors
, HybridVectors
as well as CompressedVectors
can be changed via the resize()
function:
Note that resizing a vector invalidates all existing views (see e.g. Subvectors) on the vector:
When the internal capacity of a vector is no longer sufficient, the allocation of a larger junk of memory is triggered. In order to avoid frequent reallocations, the reserve()
function can be used up front to set the internal capacity:
Note that the size of the vector remains unchanged, but only the internal capacity is set according to the specified value!
The internal capacity of vectors with dynamic memory is preserved in order to minimize the number of reallocations. For that reason, the resize()
and reserve()
functions can lead to memory overhead. The shrinkToFit()
member function can be used to minimize the internal capacity:
Please note that due to padding the capacity might not be reduced exactly to size()
. Please also note that in case a reallocation occurs, all iterators (including end()
iterators), all pointers and references to elements of the vector are invalidated.
In order to reset all elements of a vector, the reset()
function can be used:
In order to return a vector to its default state (i.e. the state of a default constructed vector), the clear()
function can be used:
Note that resetting or clearing both dense and sparse vectors does not change the capacity of the vectors.
Via the swap()
function it is possible to completely swap the contents of two vectors of the same type:
The normalize()
function can be used to scale any non-zero vector to a length of 1. In case the vector does not contain a single non-zero element (i.e. is a zero vector), the normalize()
function returns a zero vector.
Note that the normalize()
function only works for floating point vectors. The attempt to use it for an integral vector results in a compile time error.
The min()
and max()
functions can be used for a single vector or multiple vectors. If passed a single vector, the functions return the smallest and largest element of the given dense vector or the smallest and largest non-zero element of the given sparse vector, respectively:
For more information on the unary min()
and max()
reduction operations see the Reduction Operations section.
If passed two or more dense vectors, the min()
and max()
functions compute the componentwise minimum or maximum of the given vectors, respectively:
Please note that sparse vectors can only be used in the unary min()
and max()
functions. Also note that all forms of the min()
and max()
functions can be used to compute the smallest and largest element of a vector expression:
The softmax function, also called the normalized exponential function, of a given dense vector can be computed via softmax()
. The resulting dense vector consists of real values in the range (0..1], which add up to 1.
The abs()
function can be used to compute the absolute values of each element of a vector. For instance, the following computation
results in the vector
The sign()
function can be used to evaluate the sign of each element of a vector a. For each element i
the corresponding result is 1 if a[i] is greater than zero, 0 if a[i] is zero, and -1 if a[i] is less than zero. For instance, the following use of the sign()
function
results in the vector
The floor()
, ceil()
, trunc()
, and round()
functions can be used to round down/up each element of a vector, respectively:
The conj()
function can be applied on a dense or sparse vector to compute the complex conjugate of each element of the vector:
Additionally, vectors can be conjugated in-place via the conjugate()
function:
The real()
function can be used on a dense or sparse vector to extract the real part of each element of the vector:
The imag()
function can be used on a dense or sparse vector to extract the imaginary part of each element of the vector:
Via the sqrt()
and invsqrt()
functions the (inverse) square root of each element of a vector can be computed:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The cbrt()
and invcbrt()
functions can be used to compute the the (inverse) cubic root of each element of a vector:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The hypot()
function can be used to compute the componentwise hypotenous for a pair of dense vectors:
The clamp()
function can be used to restrict all elements of a vector to a specific range:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The pow()
function can be used to compute the exponential value of each element of a vector. If passed a vector and a numeric exponent, the function computes the exponential value of each element of the vector using the same exponent. If passed a second vector, the function computes the componentwise exponential value:
exp()
, exp2()
and exp10()
compute the base e/2/10 exponential of each element of a vector, respectively:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The log()
, log2()
and log10()
functions can be used to compute the natural, binary and common logarithm of each element of a vector:
The following trigonometric functions are available for both dense and sparse vectors:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The following hyperbolic functions are available for both dense and sparse vectors:
Note that in case of sparse vectors only the non-zero elements are taken into account!
The multi-valued inverse tangent is available for a pair of dense vectors:
The erf()
and erfc()
functions compute the (complementary) error function of each element of a vector:
Note that in case of sparse vectors only the non-zero elements are taken into account!
Via the unary and binary map()
functions it is possible to execute componentwise custom operations on vectors. The unary map()
function can be used to apply a custom operation on each element of a dense or sparse vector. For instance, the following example demonstrates a custom square root computation via a lambda:
The binary map()
function can be used to apply an operation pairwise to the elements of two dense vectors. The following example demonstrates the merging of two vectors of double precision values into a vector of double precision complex numbers:
Although the computation can be parallelized it is not vectorized and thus cannot perform at peak performance. However, it is also possible to create vectorized custom operations. See Custom Operations for a detailed overview of the possibilities of custom operations.
Please note that unary custom operations on vectors have been introduced in Blaze 3.0 in form of the forEach()
function. With the introduction of binary custom functions, the forEach()
function has been renamed to map()
. The forEach()
function can still be used (even for binary custom operations), but the function might be deprecated in future releases of Blaze.
The reduce()
function performs a total reduction of the elements of the given dense vector or the non-zero elements of the given sparse vector. The following examples demonstrate the total reduction of a dense and sparse vector:
As demonstrated in the examples it is possible to pass any binary callable as custom reduction operation. However, for instance in the case of lambdas the vectorization of the reduction operation is compiler dependent and might not perform at peak performance. However, it is also possible to create vectorized custom operations. See Custom Operations for a detailed overview of the possibilities of custom operations.
Please note that the evaluation order of the reduce()
function is unspecified. Thus the behavior is non-deterministic if the given reduction operation is not associative or not commutative. Also, the operation is undefined if the given reduction operation modifies the values.
The sum()
function reduces the elements of the given dense vector or the non-zero elements of the given sparse vector by means of addition:
Please note that the evaluation order of the sum()
function is unspecified.
The prod()
function reduces the elements of the given dense vector or the non-zero elements of the given sparse vector by means of multiplication:
The unary min()
function returns the smallest element of the given dense vector or the smallest non-zero element of the given sparse vector. It can only be used for element types that support the smaller-than relationship. In case the given vector currently has a size of 0, the returned value is the default value (e.g. 0 in case of fundamental data types).
The unary max()
function returns the largest element of the given dense vector or the largest non-zero element of the given sparse vector. It can only be used for element types that support the smaller-than relationship. In case the given vector currently has a size of 0, the returned value is the default value (e.g. 0 in case of fundamental data types).
The argmin()
function returns the index of the first smallest element of the given dense vector. This function can only be used for element types that support the smaller-than relationship. In case the given vector currently has a size of 0, the returned index is 0.
The argmax()
function returns the index of the first largest element of the given dense vector. This function can only be used for element types that support the smaller-than relationship. In case the given vector currently has a size of 0, the returned index is 0.
The norm()
function computes the L2 norm of the given dense or sparse vector:
The sqrNorm()
function computes the squared L2 norm of the given dense or sparse vector:
The l1Norm()
function computes the squared L1 norm of the given dense or sparse vector:
The l2Norm()
function computes the squared L2 norm of the given dense or sparse vector:
The l3Norm()
function computes the squared L3 norm of the given dense or sparse vector:
The l4Norm()
function computes the squared L4 norm of the given dense or sparse vector:
The lpNorm()
function computes the general Lp norm of the given dense or sparse vector, where the norm is specified by either a compile time or a runtime argument:
The linfNorm()
and maxNorm()
functions compute the infinity/maximum norm of the given dense or sparse vector:
By means of the uniform()
function it is possible to expand a scalar value into a dense, uniform vector. By default, the resulting uniform vector is a column vector, but it is possible to specify the transpose flag explicitly:
Via the expand()
function it is possible to convert a dense or sparse vector into a matrix. A column vector is expanded into a column-major matrix, a row vector is expanded into a row-major matrix. As demonstrated by the following examples, expand()
can be used with both runtime and compile time parameters:
The (arithmetic) mean of a dense or sparse vector can be computed via the mean()
function. In case of a sparse vector, both the non-zero and zero elements are taken into account. The following example demonstrates the computation of the mean of a dense vector:
In case the size of the given vector is 0, a std::invalid_argument is thrown.
The variance of a dense or sparse vector can be computed via the var()
function. In case of a sparse vector, both the non-zero and zero elements are taken into account. The following example demonstrates the computation of the variance of a dense vector:
In case the size of the given vector is smaller than 2, a std::invalid_argument is thrown.
The standard deviation of a dense or sparse vector can be computed via the stddev()
function. In case of a sparse vector, both the non-zero and zero elements are taken into account. The following example demonstrates the computation of the standard deviation of a dense vector:
In case the size of the given vector is smaller than 2, a std::invalid_argument is thrown.
The declzero()
operation can be used to explicitly declare any vector or vector expression as zero vector:
Any vector or vector expression that has been declared as zero vector via declzero()
will gain all the benefits of a zero vector, which range from reduced runtime checking to a considerable speed-up in computations:
declzero()
operation has the semantics of a cast: The caller is completely responsible and the system trusts the given information. Declaring a non-zero vector or vector expression as zero vector via the declzero()
operation leads to undefined behavior (which can be violated invariants or wrong computation results)!
Previous: Vector Types Next: Matrices