Mendz.Library Matrix and SquareMatrix

This article kicks-off the dense matrix series. Before we dive in to the nitty-gritty, let's have a quick discussion about Mendz.Graphs' dense matrix.

In Mendz.Graphs, a dense matrix is a two-dimensional array T[,]. I want to highlight the following characteristics:
  1. In a matrix, all elements/entries have the same (data) type T.
  2. A matrix has an order m x n, where m is the number of rows and n is the number of columns.
  3. A matrix has a main diagonal, which is basically Ai,j where i = j.
  4. The elements/entries of the matrix can be initialized.
  5. You can get or set an element/entry Ai,j in the matrix.
The keywords to capture are "order", "entry" and "diagonal", which you'll find in the codes shared below.

In order to "centralize" the creation and initialization of dense matrices, I created the Matrix static class.

Matrix.cs
using System;
using System.Collections.Generic;
namespace Mendz.Library
{
    public static class Matrix<T>
    {
        public static T[,] Create(int rows, int columns, 
            T entry = default(T), T diagonal = default(T))
        {
            T[,] matrix = new T[rows, columns];
            Initialize(matrix, rows, columns, entry, diagonal, false);
            return matrix;
        }

        public static void Initialize(T[,] matrix, 
            T entry = default(T), T diagonal = default(T), bool force = true)
        {
            Initialize(matrix, matrix.GetLength(0), matrix.GetLength(1), 
                entry, diagonal, force);
        }

        private static void Initialize(T[,] matrix, int rows, int columns, 
            T entry = default(T), T diagonal = default(T), bool force = true)
        {
            bool isSetEntry = !EqualityComparer<T>.Default.Equals(entry, default(T));
            bool isSetDiagonal = !EqualityComparer<T>.Default.Equals(diagonal, default(T));
            if ((!isSetEntry && isSetDiagonal) || force)
            {
                for (int i = 0; i < rows; i++)
                {
                    matrix[i, i] = diagonal;
                }
            }
            else if (isSetEntry || isSetDiagonal || force)
            {
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < columns; j++)
                    {
                        if (i == j)
                        {
                            matrix[i, j] = diagonal;
                        }
                        else
                        {
                            matrix[i, j] = entry;
                        }
                    }
                }
            }
        }

        public static void CheckCoordinates(T[,] matrix, int row, int column)
        {
            CheckCoordinates((matrix.GetLength(0), matrix.GetLength(1)), row, column);
        }

        public static void CheckCoordinates((int rows, int columns) size, 
            int row, int column)
        {
            if (row < 0 || row >= size.rows)
            {
                throw new ArgumentOutOfRangeException("row");
            }
            if (column < 0 || column >= size.columns)
            {
                throw new ArgumentOutOfRangeException("column");
            }
        }
    }
}

The Initialize() method has optimizations to loop on the matrix only when necessary. It also has detection if the loop should only be performed on the main diagonal.

In order to "centralize" the creation and initialization of square matrices, I also created a SquareMatrix static class. Its Create() and Initialize() methods are simply wrapper calls to the Matrix Create() and Initialize() respectively. However, SquareMatrix introduces a couple of features unique to a square matrix. Specifically, the methods to create and initialize triangular matrices.

SquareMatrix.cs
using System.Collections.Generic;
namespace Mendz.Library
{
    public static class SquareMatrix<T>
    {
        public static T[,] Create(int order, T entry = default(T), T diagonal = default(T))
        {
            return Matrix<T>.Create(order, order, entry, diagonal);
        }

        public static T[,] CreateUpperTriangular(int order, T entry, 
            T diagonal = default(T))
        {
            T[,] matrix = new T[order, order];
            InitializeUpperTriangular(matrix, entry, diagonal, false);
            return matrix;
        }

        public static T[,] CreateLowerTriangular(int order, T entry, 
            T diagonal = default(T))
        {
            T[,] matrix = new T[order, order];
            InitializeLowerTriangular(matrix, entry, diagonal, false);
            return matrix;
        }

        public static void Initialize(T[,] matrix, 
            T entry = default(T), T diagonal = default(T), bool force = true)
        {
            Matrix<T>.Initialize(matrix, entry, diagonal, force);
        }

        public static void InitializeUpperTriangular(T[,] matrix, 
            T entry, T diagonal = default(T), bool force = true)
        {
            bool isSetEntry = !EqualityComparer<T>.Default.Equals(entry, default(T));
            bool isSetDiagonal = !EqualityComparer<T>.Default.Equals(diagonal, default(T));
            if (isSetEntry || isSetDiagonal || force)
            {
                int order = matrix.GetLength(0);
                for (int i = 0; i < order; i++)
                {
                    for (int j = i; j < order; j++)
                    {
                        if (i == j)
                        {
                            matrix[i, j] = diagonal;
                        }
                        else
                        {
                            matrix[i, j] = entry;
                        }
                    }
                }
            }
        }

        public static void InitializeLowerTriangular(T[,] matrix, 
            T entry, T diagonal = default(T), bool force = true)
        {
            bool isSetEntry = !EqualityComparer<T>.Default.Equals(entry, default(T));
            bool isSetDiagonal = !EqualityComparer<T>.Default.Equals(diagonal, default(T));
            if (isSetEntry || isSetDiagonal || force)
            {
                int order = matrix.GetLength(0);
                for (int i = 0; i < order; i++)
                {
                    for (int j = 0; j < order; j++)
                    {
                        if (i == j)
                        {
                            matrix[i, j] = diagonal;
                            break;
                        }
                        else
                        {
                            matrix[i, j] = entry;
                        }
                    }
                }
            }
        }

        public static void CheckCoordinates(T[,] matrix, int row, int column)
        {
            Matrix<T>.CheckCoordinates(matrix, row, column);
        }

        public static void CheckCoordinates(int order, int  row, int column)
        {
            Matrix<T>.CheckCoordinates((order, order), row, column);
        }
    }
}

Note that the triangular matrix initialization methods have optimizations as well to loop on the matrix only when necessary.

These static classes will be used by Mendz.Graphs to create and initialize dense matrices. Thus, Mendz.Graphs should now reference Mendz.Library.



Update: Enhanced with CheckCoordinates() methods.

Comments