Trait reform: big plans for nalgebra 0.11.0


This discussion is also on github.
Please, share what you would like to see added to the next release of nalgebra (in particular for computer-graphics related features)!


After #187, the next release will be the next big step toward what will be a version 1.0 in the future. It will include many breaking changes (especially for generic programming) but this is the last time it will break that much!

This trait reform is also a good opportunity to reorganize the internals of nalgebra, get rid of some hidden aberrations (like the ability to change the Rotation2 diagonal to arbitrary values), and take in account negative feedbacks (I actually read IRC logs) regarding its ergonomy for computer graphics applications.

The main goals are to:

  • allow generic programming through solid trait-based mathematical foundations. The current traits are too granular and not organized (with inheritance) in a coherent manner.
  • use the approx crate for approximate equality testing (i.e. don’t redefine our own).
  • add computer-graphics-related features to 3x3 and 4x4 raw matrices. The computer graphics community is used to work with 4x4 matrix to represent a general 3D transformation (or 3x3 matrices for 2D). Thus, people are usually frustrated when they can’t do anything CG-related with Matrix3 and Matrix4. This will change but they will still not be usable in a generic context (except for one case, see bellow).

Generic programming

The new trait hierarchy will:

  • mostly be implemented in a separate crate: alga. Note that this is based on the excellent work of @brendanzab and @WaDelma on algebra (I wanted to give a fresh reboot of this library with different design choices).
  • follow more strict mathematical notions and names. If several names are possible for the same structure, the simpler one is chosen (e.g. trait for rotations will be called Rotation instead of SpecialOrthogonalGroup).
  • provide the same (or more) features that already exist on nalgebra. You’ll just have to change the name of the traits you import, and sometimes the methods names as well. For example the SquareMatrix, Diagonal, Column, and Row will be merged into the traits SquareMatrix and SquareMatrixMut (the latter allows modifications).

Implementing your own instance of the traits of alga will be a bit harder as you’ll have to
select the right stack of algebraic structures starting with the most basic one (the Magma). On this other hand, this will force you to be sure that your type has the correct algebraic properties that generic code will rely on. Convenient macros might be added in future versions of alga to make this easier.

Right after the release, a small guide will be provided to help you update your code without having to search too much what is moved under which trait.

Computer Graphics features

It will be possible to:

  • build a projection/look-at/rotation/translation/scaling Matrix3/4 directy using dedicated
    constructors like Matrix3::new_translation.
  • append/prepend a rotation/translation/scaling to Matrix3/4.
  • transform a Point2/3 or a Vector2/3 using a Matrix3/4. The transformations will
    implicitly follow the rules of projective geometry (i.e. homogeneous coordinates). A
    Transformation trait will be the only one implemented by those matrices. Transformation will
    also work by simple multiplication.
  • convert a Rotation2/3, Isometry2/3 and Similarity2/3 using the na::cast function (which will be renamed na::convert). Users won’t have to use the to_homogeneous and from_homegenous if they are disturbed by the mathematical names. Under certain strict conditions checked at runtime, the inverse conversion (e.g. from Matrix3 to Rotation2) will be possible (and will return None if it fails).
  • multiply a Matrix3/4 with a Rotation/Isometry/Similarity.

Most free functions at the root of the crate will be unchanged (except for the trait bounds of their arguments).

Fixed issues

The following issues will be fixed: #205 , #200, #160, #145, #137, #36, and #32.

I have been working on this during the past couple of months and think I am halfway.
Before the release, I’ll also update kiss3d, ncollide, and nphysics in order to have a taste (and fix flaws) of the new design in extremely generic code.

Time for some updates on this! You will see the work done so far on the nexgen branch. This is basically a full rewrite of the library internals with lots of added features (and a few removed).

Also, I’d appreciate some help for whoever would be interested in working on integrating Blas/Lapack/matrixmultiply. All the relevant code to be modified are in the src/core/matrix.rs and src/core/ops.rs files.

Main modifications and new features

(Almost) everything is generic wrt. the dimension.

For example a 3x3 matrix is Matrix<f32, U3, U3, S> while a (column) vector is Matrix<f32, U3, U1, S>. In both cases S is some other type parameter that represents the matrix data storage type (more on this later). Of course, many type aliases are defined for, e.g., low dimensional matrices and vectors. For example, Vector1 to Vector6 are provided, as well as all square and rectangular matrices from Matrix1 to Matrix6 (for example a 2x3 matrix type alias is Matrix2x3).

Dynamically-sized matrix and vectors

Dynamical-sized matrices and vectors are declared just like statically-sized matrices, but with a special value for its dimension, i.e., Matrix<f32, Dynamic, Dynamic, S> and Matrix<f32, Dynamic, U1, S> (for dynamically-sized vectors). This is actually much like Eigen. This is where the last type parameter S (which you don’t have to worry about as long as you use the type aliases) plays its main role: dynamic entities will store their data as Vec while statically-sized matrices will use GenericArray.

Thus, because dynamic and static matrices use the same base type (Matrix<...>), they both share the same code.

Vector/Matrix slicing

It is now possible to take references to parts of a vector or matrix. For example, it is possible to extract a 2x2 submatrix from a 3x3 matrix using:

let m3 = Matrix3::new(...);
let m2 = m3.fixed_slice::<U2, U2>(0, 0);

Here, m2 will a 2D submatrix of m3 starting with its top-left component. Because this slice is simply a Matrix<f32, U2, U2, S> with some special value for the S storage parameter (which contain an internal reference to m3), they share most capabilities of non-slice matrices (e.g. matrix/matrix or matrix/vector multiplication, etc.)

Mutable slices, slices-of-slices, and custom strides are also possible.

If the slice size is not known at compile-time, it is possible to write, e.g.:

let m2 = m3.slice((0, 0), (2, 2));

Here, m2 is a dynamically-sized slice and its type is Matrix<f32, Dynamic, Dynamic, S> with some special value of the S storage parameter which contain an internal reference to m3.

Transformations

The whole set of common transformations are now included in nalgebra. In particular, we have types for:

  • Translation
  • Rotation matrix
  • Unit quaternion (3D rotation)
  • Unit complex (2D rotation)
  • Isometry (translation × rotation)
  • Similarity (translation × rotation × uniform scale)
  • Affine (translation × rotation × non-uniform scale × rotation)
  • Projective (arbitrary invertible transformation).

They can be used in generic code as they implement transformations traits from alga.

Less traits

If you don’t care about generic programming, you should not have to import tons of traits to use algebraic operators. That’s why most operators are now implemented as struct methods. Use traits from alga for generic programming.

What’s next

A few feature still have to be added or polished. Then, the next steps are:

Documentation

I’m working on a new website (much like the one for ncollide) that will document all those features. The rustdoc-generated doc put too much emphasis on trait bounds, making it very hard to read.

Performance

We basically need to integrate matrixmultiply, Blas and Lapack. Any contribution on that would be strongly appreciated. We also need to check that we did not regress from the previous version of nalgebra.

Unresolved issues

I get a lot of Broken MIR errors when compiling tests of benchmarks (mostly because it fails to recognize that an associated type is equal to some other exact type). I am not sure how to decrease the code base to report this as an issue to the rust compiler repo.

1 Like