Matrico f64vector Module
matrico
’s Double-Precision Flonum Vector f64vector
Module
For matrico
, the basic homogeneous vector support of the included srfi-4
module needs to be expanded to provide basic functional tools,
such as “map” and “reduce” functions.
I limit myself to the f64vector
variant of homogeneous vectors, as this type will be a basis for matrico
’s matrix type.
This module imports the fpmath
module,
solely for its fp*+
function which is needed for the implemented dot product function (f64vector-dot
see below).
However, in the future, once (chicken flonum)
provides fp*+
natively and former versions don’t need to be supported,
i.e. Ubuntu LTS repositories feature a CHICKEN including fp*+
, fpmath
can be removed from the list of imports.
As this module is useless without the (chicken srfi-4)
module, it is reexported.
You can take a look at the src/f64vector.scm
source file here.
Generators
First, a procedural generation of vectors needs to be available, to avoid the user having to do such operations via a mutation and consequentially also avoid an unnecessary zero initialization of the vector.
(f64vector-unfold dim fun)
- Returnsf64vector
with dimension-argument-many elements resulting from applying function argument to respective elements’ indices.
Another constructor of sorts is the concatenation of two or more vectors:
(f64vector-concat . vecs)
- Returnsf64vector
holding all argument list-of-vectors elements concatenated vector-by-vector.
Predicates
Next, a generalization of predicates for vectors, similar to any
and every
of SRFI-1 is provided.
These functions return true or false and are thus marked with a question-mark.
Naming-wise, I prefer Matlab’s all
to Scheme’s every
:
(f64vector-any? pred vec)
- Returns true if any element of argumentf64vector
fulfills predicate argument.(f64vector-all? pred vec)
- Returns true if all elements of argumentf64vector
fulfill predicate argument.
Testing elements of a vector on a predicate and returning matching elements and/or indices is not covered by these functions,
and may be added later on as a f64vector-find
function.
Mappers
An important sub-set of iterator functions are the mappers.
R6RS defines a vector-map
in the standard, R5RS does not,
and hence (I assume), CHICKEN’s srfi-4
does not provide a f64vector-map
.
For performance reasons the basic map operation is implemented for one, two, and variable number of vector arguments (see case-lambda
).
As opposed to lists, the index of a vector element is not only readily available without traversing the container,
but also a lot more relevant than for lists.
Hence, I added a variant of map, whose function has, in addition to the vector element, the element’s index as argument,
similar to Gauche’s vector-map-index
and vector-map
in SRFI-43.
For lists however, I made it a rule, that whenever I was considering list indices (and thus may want a map-index
),
I would rethink the algorithm.
A sibling of the map
function, is the for-each
function, which, opposed to map
, does not return a list (or vector),
but always iterates in order through the container.
Here, I implemented f64vector
versions of for-each
and for-each-index
,
whose practical use lies in my case only in side-effect operations, like printing to terminal or writing to file.
As a special case due to its importance for numerical algorithms, a specialized mapper is included,
which maps a scalar and two vectors of the same dimensions to a new vector,
where entries of the first vector are scaled by the scalar and added to the second vector.
This operation is known in the BLAS library as “axpy” - a (times) x plus y.
(f64vector-map fun vec)
- Returnsf64vector
of elements of function argument applied tof64vector
argument’s elements.(f64vector-map fun vec wec)
- Returnsf64vector
of elements of function argument applied tof64vector
arguments’ elements.(f64vector-map fun . vecs)
- Returnsf64vector
of elements of function argument applied tof64vector
arguments’ elements.(f64vector-map-index fun . vec)
- Returnsf64vector
of elements of function argument applied tof64vector
arguments’ elements and their index.(f64vector-foreach fun . vec)
- Returnsvoid
, apply function argument applied tof64vector
arguments’ elements in-order.(f64vector-foreach-index fun . vec)
- Returnsvoid
, apply function argument applied tof64vector
arguments’ elements in-order and their index.(f64vector-axpy a x y)
- Returnsf64vector
of elements given byflonum
a times firstf64vector
plus secondf64vector
.
Reducers
Finally, and of vital importance, the folders (or reducers) are defined.
Naturally, two folds, forward and backward, join the mappers.
It might not be obvious why one variant is not sufficient:
First, a “fold” reducer can return anything and not only f64vector
s or flonum
s.
Second, even if one thinks of numerical operations like summing the elements of a vector,
it may be beneficial to sum from the front (back) if the vector is sorted increasingly (decreasingly),
which is, for example, typically the case for Singular values of matrices, to prevent numerical annihilation.
Furthermore, the “backward fold” will play a central role in a specific matrix operation.
Due to its principal role in (numerical) linear algebra,
a specialized third reducer is included - the dot product (also known as: “inner product” or “scalar product”) of two vectors (of same dimension).
While the dot product is a prime example of a “map” (fp*
) and a “fold” (fp+
) operation,
again performance dictates to specialize this function.
(f64vector-fold fun ini . vec)
- Returns accumulator of applying function argument to accumulator (starting with initializer argument) and vector arguments’ entries from front to back.(f64vector-fold* fun ini . vec)
- Returns accumulator of applying function argument to accumulator (starting with initializer argument) and vector arguments’ entries from back to front.(f64vector-dot x y)
- Returns flonum resulting from dot product of same dimension vector arguments.
In principles the predicates and mappers could have been implemented as special cases of the reducers, which, while elegant, is not achieving best performance.
Altogether, the f64vector
module is balancing minimally required functions
with maximum performance for matrico
.
Next time, I will discuss CHICKEN Scheme’s outstanding module system.