Dense Matrices

A DenseMatrix<T> represents a dense matrix with arbitrary elements.

How dense matrices are stored

The elements of a DenseMatrix<T> are stored in a one-dimensional array. The elements can be stored in two ways: column major and row major. Column major order means that the elements of each column are stored in adjacent elements of the array. Row major order means that the elements of each row are stored in adjacent elements of the array.

For historical reasons, most linear algebra algorithms have been optimized for column major storage. For this reason, column major order is the default. Row major order is supported transparently. Conversions are performed when necessary. Very often, row major order can be used without any significant loss of speed.

The element order is usually only important when initializing the elements of a matrix. No application should have any other explicit dependency on the way matrix elements are stored.

The element order is given by the ElementOrder property, which is of type MatrixElementOrder. Possible values are ColumnMajor, RowMajor and NotApplicable.

Constructing general dense matrices

Dense vectors are constructed with the Create method of the Matrix class. This method has many overloads. All overloads take a generic type argument that specifies the element type. In most cases, type inference eliminates the need to specify the element type explicitly.

The first overload has two arguments: the number of rows and the number of columns in the matrix. This creates a matrix with the specified number of rows and columns. The element type must be specified as the generic type argument. All elements are initially set to zero. The example below creates a matrix with 3 rows and 4 columns:

var m1 = Matrix.Create<double>(3, 4);

A third argument can be added that specifies the order in which matrix elements are stored. This argument is of the MatrixElementOrder enumeration type. The following creates a matrix with 3 rows and 4 columns, whose elements are stored in row major order:

var m2 = Matrix.Create<double>(3, 4, MatrixElementOrder.RowMajor);

Another overload takes a 2-dimensional array as its only argument. It constructs an equivalent matrix, with the same dimensions and values as the array. Creating a matrix from a one-dimensional array is also possible, but some care must be taken to make sure the elements are in the correct order. The overload takes 4 arguments: the dimensions of the matrix, an array of matrix elements, and a MatrixElementOrder value that specifies whether the elements in the array are listed row-wise or column-wise.

The example below shows three ways to create the same matrix: using a 2D array, using a 1D array in column-major order, and using a 1D array in row-major order:

double[,] elements2D = { { 11, 12, 21 }, { 22, 31, 32 } };
var m3 = Matrix.Create(elements2D);
double[] elements = { 11, 21, 31, 12, 22, 32 };
var m4 = Matrix.Create(3, 2, elements, MatrixElementOrder.ColumnMajor);
double[] elementsByRow = { 11, 12, 21, 22, 31, 32 };
var m5 = Matrix.Create(3, 2, elementsByRow, MatrixElementOrder.RowMajor);

By default, the matrix elements are copied from the element array to a private storage array. This may become expensive when the number of elements is large. You can let the new matrix reuse the element array by supplying a fifth argument: a Boolean that indicates whether the array should be reused THe default is false.

It is important to note the effects of reusing the element array. Any change to an element of the element array will change the corresponding element of the matrix, and vice versa. In general, the element array should not be used for any other purpose.

One last overload takes 3 arguments: the dimensions of the matrix, and a delegate that is used to initialize the elements of the matrix. The delegate maps the row and column index of the element to its value:

var m6 = Matrix.Create(4, 4, (i, j) => 1.0 / (1.0 + i + j));