GSoC — The Final Status Report

2 minute read

In this blog post, I will describe the work I have done after the midterm evaluation.

Integrating Fastor

As I mentioned in the previous post, the next step was for me to fully integrate Fastor as a backend into algebra-plugins. To that end, I opened PR #78.

algebra-plugins Architecture

Integrating Fastor into algebra-plugins was not an easy feat. The architecture of algebra-plugins is quite complicated and very C++ template-heavy (in order to minimize code duplication). Moreover, there are many things which do not make sense to a beginner. For example, it is not immediately obvious to the newcomer why we need to write code both in the math subdirectory as well as the frontend subdirectory. Moreover, the frontend names like eigen_eigen and eigen_cmath are not the most illuminating either.

Uniform Function Names

Regardless, I got to work in the math subdirectory first. Inside this directory, all the functions used by the other projects and the rest of the repository are defined in terms of the math backend functions. A specific library might call the function dot(), as Eigen does, or it might call it inner() (short for inner product), as Fastor does.

algebra-plugins uses the functions defined in the math directory to provide an uniform API, regardless of the backend. The name that was decided upon for algebra-plugins for this function was dot().

Uniform Type Names

Type names are another thing which have a lot of variation from library to library. Eigen calls their type as simply Matrix, whereas SMatrix, for example, calls their matrix type SMatrix (unsurprisingly). For this reason, we have to define types such as matrix_type and vector3 in the storage directory.s

As I spent time mucking around in the algebra-plugins repository and writing code, these little things started to make more and more sense to me.

Expression Templates

In order to understand how to write the code for Fastor in algebra-plugins, I spent time looking at how the code for the other backends was written. An important optimization that many math libraries use is expression templates. This is important because C++ is a eager evaluation language (as opposed to, say, Haskell, which uses lazy evaluation).

Imagine we have the line of code auto result = 3 * v1 + 5 * v2;, where v1 and v2 are vectors. If the expression were to be lazily evaluated, then every element of the resultant vector would not be computed until the whole expression had been read. In C++ (and other eager evaluation languages) what happens is as follows:

  1. The subexpression 3 * v1 is read. Since this is something that can be computed, C++ computes it and puts it in temporary storage, say temp1.
  2. The subexpression 5 * v2 is read. Once again, since this is something that can be computed, C++ computes it and puts it in temporary storage, say temp2.
  3. The result of adding temp1 and temp2 is stored in result.

Obviously, a lot of unnecessary work is being done here. Expression Templates solve this issue by recording the work to be done in lightweight structs using templates until the expression needs to be evaluated, and only then does it fully evaluate the expression.

Expression Templates and algebra-plugins

Because of expression templates, many functions have multiple definitions

Leave a comment