11
votes

We have an application that stores a sparse matrix. This matrix has entries that mostly exist around the main diagonal of the matrix. I was wondering if there were any efficient algorithms (or existing libraries) that can efficiently handle sparse matrices of this kind? Preferably, this would be a generic implementation where each matrix entry can be a user-defined type.

Edit in response to a question/response:

When I say mostly around the main diagonal I mean that the characteristics of most of the matrices will be that most entries are clustered off of the main diagonal but there could be zeroes close to the diagonal and there could be non-zero values far out from the diagonal. I want something efficient for 'most' cases here.

What will I be using this for? I need to be able to have efficient access to all values in a row or all values in a column. The values stored would be Boolean values. An example would be:

  1. For all true values in a row, foreach column a true appears in set all the entries of the column to something
  2. For all false values in a row, set the entry to something

This was all done with linked lists previously but was very confusing to implement. I was hoping that with a sparse matrix I could improve the algorithm but finding the 'right' type of sparse matrix algorithm has proved difficult.

p.s. Thanks for the responses thus far

6
I updated my answer. So is performance efficiency more important than space efficiency? You say "efficient way to handle sparse matrices" and then in your use cases talk about multiple ways of accessing the data.Erich Mirabal
I would say performance is more important than space efficiency. We will be handling very large amounts of data anyways so I don't mind using lots of space for the matrix so long as it goes fasterJeffrey Cameron

6 Answers

10
votes

You could use an index based on the [row,col] of the cell. Since the data is on a diagonal, the typical approach of storing the row index and the associated column indeces with data is not optimal. Here is some code you could use to do it:

    public class SparseMatrix<T>
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public long Size { get; private set; }

        private Dictionary<long, T> _cells = new Dictionary<long, T>();

        public SparseMatrix(int w, int h)
        {
            this.Width = w;
            this.Height = h;
            this.Size = w * h;
        }

        public bool IsCellEmpty(int row, int col)
        {
            long index = row * Width + col;
            return _cells.ContainsKey(index);
        }

        public T this[int row, int col]
        {
            get
            {
                long index = row * Width + col;
                T result;
                _cells.TryGetValue(index, out result);
                return result;
            }
            set
            {
                long index = row * Width + col;
                _cells[index] = value;
            }
        }
    }

    static void Main()
    {
        var sm = new SparseMatrix<int>(512, 512);
        sm[42, 42] = 42;
        int val1 = sm[13, 13];
        int val2 = sm[42, 42];

        Console.WriteLine("VAL1 = " + val1); // prints out 0
        Console.WriteLine("VAL2 = " + val2); // prints out 42

        Console.ReadLine();
    }

Note that when T is a struct, you might have to call the IsCellEmpty since getting the contents of a cell will not be null and will have the default value for that type. You can also expand the code to give you a quick "SparseRatio" based on the Size property and _cells.Count.

EDIT:

Well, if you are interesting is speed, you can do the trade-off of space vs speed. Instead of having only one dictionary, have three! It triples your space, but it makes enumerating in any way you want real easy. Here is some new code that shows that:

    public class SparseMatrix<T>
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public long MaxSize { get; private set; }
        public long Count { get { return _cells.Count; } }

        private Dictionary<long, T> _cells = new Dictionary<long, T>();

        private Dictionary<int, Dictionary<int, T>> _rows = 
            new Dictionary<int, Dictionary<int, T>>();

        private Dictionary<int, Dictionary<int, T>> _columns = 
            new Dictionary<int, Dictionary<int, T>>();

        public SparseMatrix(int w, int h)
        {
            this.Width = w;
            this.Height = h;
            this.MaxSize = w * h;
        }

        public bool IsCellEmpty(int row, int col)
        {
            long index = row * Width + col;
            return _cells.ContainsKey(index);
        }

        public T this[int row, int col]
        {
            get
            {
                long index = row * Width + col;
                T result;
                _cells.TryGetValue(index, out result);
                return result;
            }
            set
            {
                long index = row * Width + col;
                _cells[index] = value;

                UpdateValue(col, row, _columns, value);
                UpdateValue(row, col, _rows, value);
            }
        }

        private void UpdateValue(int index1, int index2, 
            Dictionary<int, Dictionary<int, T>> parent, T value)
        {
            Dictionary<int, T> dict;
            if (!parent.TryGetValue(index1, out dict))
            {
                parent[index2] = dict = new Dictionary<int, T>();
            }
            dict[index2] = value;
        }
    }

If you want to iterate over all the entries, use _cells. If you want all the rows for a given column use _columns. If you want all the columns in a given row use _rows.

If you want to iterate in sorted order, you can start to add LINQ into the mix and/or use a sorted list with an inner class that encapsulates an entry (which would have to store the row or column and implement IComparable<T> for sorting to work).

4
votes

I guess a Dictionary<int, Dictionary<int, object >> would suffice.

3
votes

There are two questions here:

  • "Mostly around the main diagonal" is too vague. If the elements lie in bands, then use banded storage of the bands themselves, as vectors offset from the main diagonal. If the elements are scattered randomly in the vicinity of the main diagonal, then either use a banded form that may include some zeros in the bands, or use a pure sparse form that stores only the elements and their positions in the array.

  • What will you do with the matrix? If your goal is merely efficient storage, then a banded form will be efficient, with fast access to any element. If you will do linear algebra with the matrix, but never more than matrixvector multiplies, then the banded form will still work splendidly. If you work with matrixmatrix multiplies or matrix factorizations, where fill-in becomes a problem, then a pure sparse form may be more appropriate. For example, the product of two banded matrices will have additional bands, so the product of two tridiagonal matrices will be pentadiagonal. For a factorization, reorderings will sometimes be useful to minimize fill-in. (AMD is one choice, Approximate Minimum Degree permutation, but there are other schemes.)

2
votes

Here's a list of general data structure schemas. Each has its advantages and disadvantages, and are suitable for slightly different kinds of problems where sparse matrices arise. You'd probably want to implement them on top of existing data structures, such as List<> and Dictionary<>.

2
votes

I haven't used it, but Nmath Matrix handles these (not free).

Also, Extreme Optimization Numerical Libraries for .NET (not free).

Here's a free one: Math.NET Project (specifically MathNet.Numerics.LinearAlgebra.Sparse namespace)

1
votes

I think this could be done by using a class holding plain array, saving the horizontal offset applied between matrix rows and defining stripe of a row, e.g. the number of valid entries. So for a large matrix where only the diagonal and two neighbor elements are defined you'd create an array of 3 * number of rows and store 3 as the stripe width. The offset depends on the size of the matrix.

I'm not aware of anything free which already does this.