Sparse Matrices in C# QuickStart Sample

Illustrates using sparse vectors and matrices using the classes in the Numerics.NET.LinearAlgebra.Sparse namespace in C#.

View this sample in: Visual Basic F# IronPython

using System;
// The sparse vector and matrix classes reside in the 
// Numerics.NET.LinearAlgebra namespace.
using Numerics.NET;

namespace Numerics.NET.QuickStart.CSharp
{
    /// <summary>
    /// Illustrates using sparse vectors and matrices using the classes
    /// in the Numerics.NET.LinearAlgebra.Sparse namespace 
    /// of Numerics.NET.
    /// </summary>
    class SparseMatrices
    {
        static void Main(string[] args)
        {
            // The license is verified at runtime. We're using
            // a 30 day trial key here. For more information, see
            //     https://numerics.net/trial-key
            Numerics.NET.License.Verify("64542-18980-57619-62268");
            //
            // Sparse vectors
            //

            // The SparseVector class has three constructors. The
            // first simply takes the length of the vector. All elements
            // are initially zero.
            var v1 = Vector.CreateSparse<double>(1000000);

            // The second constructor lets you specify how many elements
            // are expected to be nonzero. This 'fill factor' is a number
            // between 0 and 1.
            var v2 = Vector.CreateSparse<double>(1000000, 1e-4);

            // The second constructor lets you specify how many elements
            // are expected to be nonzero. This 'fill factor' is a number
            // between 0 and 1.
            var v3 = Vector.CreateSparse<double>(1000000, 100);

            // The fourth constructor lets you specify the indexes of the nonzero
            // elements and their values as arrays:
            int[] indexes = { 1, 10, 100, 1000, 10000 };
            double[] values = { 1.0, 10.0, 100.0, 1000.0, 10000.0 };
            var v4 = Vector.CreateSparse(1000000, indexes, values);

            // Elements can be accessed individually:
            v1[1000] = 2.0;
            Console.WriteLine($"v1[1000] = {v1[1000]}");

            // The NonzeroCount returns how many elements are non zero:
            Console.WriteLine($"v1 has {v1.NonzeroCount} nonzeros");
            Console.WriteLine($"v4 has {v4.NonzeroCount} nonzeros");

            // The NonzeroElements property returns a collection of 
            // IndexValuePair structures that you can use to iterate
            // over the elements of the vector:
            Console.WriteLine("Nonzero elements of v4:");
            foreach(var pair in v4.NonzeroElements)
                Console.WriteLine("Element {0} = {1}", pair.Index, pair.Value);

            // All other vector methods and properties are also available,
            // Their implementations take advantage of sparsity.
            Console.WriteLine($"Norm(v4) = {v4.Norm()}");
            Console.WriteLine($"Sum(v4) = {v4.Sum()}");
            
            // Note that some operations convert a sparse vector to a
            // DenseVector, causing memory to be allocated for all
            // elements.
            
            //
            // Sparse Matrices
            //

            // All sparse matrix classes inherit from SparseMatrix. This is an abstract class.
            // There currently is only one implementation class:
            // SparseCompressedColumnMatrix. 

            // Sparse matrices are created by calling the CreateSparse factory
            // method on the Matrix class. It has 4 overloads:

            // The first overload takes the number of rows and columns as arguments:
            var m1 = Matrix.CreateSparse<double>(100000, 100000);

            // The second overload adds a fill factor:
            var m2 = Matrix.CreateSparse<double>(100000, 100000, 1e-5);

            // The third overload uses the actual number of nonzero elements rather than 
            // the fraction:
            var m3 = Matrix.CreateSparse<double>(10000, 10000, 20000);

            // The fourth overload lets you specify the locations and values of the
            // nonzero elements:
            int[] rows = { 1, 11, 111, 1111 };
            int[] columns = { 2, 22, 222, 2222 };
            double[] matrixValues = { 3.0, 33.0, 333.0, 3333.0 };
            var m4 = Matrix.CreateSparse(10000, 10000, rows, columns, matrixValues);

            // You can access elements as before...
            Console.WriteLine("m4[111, 222] = {0}", m4[111, 222]);
            m4[99, 22] = 99.0;

            // A series of Insert methods lets you build a sparse matrix from scratch:
            // A single value:
            m1.InsertEntry(25.0, 200, 500);
            // Multiple values:
            m1.InsertEntries(matrixValues, rows, columns);
            // Multiple values all in the same column:
            m1.InsertColumn(33, values, indexes);
            // Multiple values all in the same row:
            m1.InsertRow(55, values, indexes);

            // A clique is a 2-dimensional submatrix with indexed rows and columns.
            var clique = Matrix.CreateFromArray(2, 2, new double[] {11, 12, 21, 22},
                MatrixElementOrder.ColumnMajor);
            int[] cliqueIndexes = new int[] { 5, 8 };
            m1.InsertClique(clique, cliqueIndexes, cliqueIndexes);

            // You can use the NonzeroElements collection to iterate
            // over the nonzero elements of the matrix. The items
            // are of type RowColumnValueTriplet:
            Console.WriteLine("Nonzero elements of m1:");
            foreach(var triplet in m1.NonzeroElements)
                Console.WriteLine("m1[{0},{1}] = {2}", triplet.Row, triplet.Column, triplet.Value);

            // ... including rows and columns.
            var column = m4.GetColumn(22);
            Console.WriteLine("Nonzero elements in column 22 of m4:");
            foreach (var pair in column.NonzeroElements)
                Console.WriteLine("Element {0} = {1}", pair.Index, pair.Value);

            // Many matrix methods have been optimized to take advantage of sparsity:
            Console.WriteLine($"F-norm(m1) = {m1.FrobeniusNorm()}");
            
            // But beware: some revert to a dense algorithm and will fail on huge matrices:
            try
            {
                var inverse = m1.GetInverse();
            }
            catch (OutOfMemoryException e)
            {
                Console.WriteLine(e.Message);
            }

            Console.Write("Press Enter key to exit...");
            Console.ReadLine();
        }
    }
}