terrylyons / libalgebra

This C++ headers only library provides tools for manipulating elements of algebras; the tensor algebra, free lie algebra etc. Early versions can be found in sourceforge. It is capable of calculations over many rings including the arbitrary precision rationals from gmp/mpir . The associated library libalgebra_tests has many examples of how to use the code. the pypy package wraps a version of it - with a simple interface (and vastly reduced functionality)
12 stars 2 forks source link

Modernisation project #36

Open inakleinbottle opened 2 years ago

inakleinbottle commented 2 years ago

Following #34, there are a lot of "low-hanging fruit" improvements that we can make here without impacting the functionality of the library itself. I think if we are moving to C++11, which is a breaking change, we can use the opportunity to make sure the library interface is the best it can be without causing too many problems for current consumers of the library. (I'm thinking LibRDE in particular, but we won't be talking about any changes that should break that compatibility at this time.) At this point, these are all suggestions and this is the start of a discussion, rather than me saying that will be done.

  1. Move constructors and move assignment operators. Easily implemented (the compiler probably fills these in already but we should check that). We can make this explicit by simply adding cls_name(cls_name&&) noexcept = default; to the various classes and a similar thing for assignment. This is the easy side of ensuring that we make use of move semantics to it's full potential.
  2. Initialiser list constructors wherever appropriate. I already added a simple initialiser list constructor to _tensor_basis, and adding it to all of the vector/algebra types would be a huge quality of life improvement.
  3. Make use standard library threads over boost::threads. Not strictly necessary, but any time we can thin out the dependencies beyond the standard library is probably a good thing.
  4. Added noexcept specifier to functions that cannot emit an exception. This is a tricky one because it requires checking callees are also exception safe. This should allow the optimising compiler to do some more work to clean up the function calls and hopefully give us some performance improvements.
  5. Similarly, a lot of the functions around bases and similar can be marked constexpr so they can be evaluated at compile time in certain contexts. This is not applicable to all, since C++11 has very strict requirements on the content of constexpr functions (thinks like if statements are not constexpr until C++17 I believe), but one can still get an awful lot done with ternary operators.

In addition to these "easy" things, there are some other things that I would like to address in the general design and layout of the library.

  1. Circular dependencies should not exist any more, so forward declarations in libalgebra.h shouldn't be necessary any more. This should mean we can remove these forward declarations and use this opportunity to greatly simplify file structure. Moreover, this means we can make each header independently includable; for example, after this change we could realistically include only libalgebra/tensor.h to only get the tensor classes and dependencies thereof. This might be desirable in some cases. A very interesting side effect of this is that we can implement std::hash for _tensor_basis so we can use std::unordered_map without having to hack around the the hash template parameter.
  2. Since variadic templates are available in C++11, we can make use of these to (hopefully) simplify, in tandem with point 5 above, the mechanisms for computing the size of the tensor algebra and Lie algebra bases at compile time. This should eliminate some build time warnings that are emitted related to the "hacky" mechanism that exists at the moment.
  3. In a similar vein, we can make use of "template template arguments" and parameter packs to simplify the interface for selecting the underlying vector type. This will allow us to select a dense tensor by writing
    using dense_tensor = alg::free_tensor<double_field, 5, 5, alg::vectors::dense_vector>

    rather than the much more insane

    using tensor_basis = alg::free_tensor_basis<5, 5>;
    using dense_tensor_vect = alg::vectors::dense_vector<tensor_basis, double_field>;
    using dense_tensor = alg::free_tensor<double_field, 5, 5, dense_tensor_vect>;

    which is how it has to be done at the moment. I've already implemented this in a separate branch that we can merge later if desired. (See the vector-template-arg branch.)