Gromacs
2020.4

#include <gromacs/linearalgebra/sparsematrix.h>
Sparse matrix storage format.
This structure specifies a storage format for a sparse matrix. The memory requirements are only proportional to the number of nonzero elements, and it provides a reasonably fast way to perform matrixvector multiplications.
The data format is very similar to a neighborlist. It is optimized for fast access, but it is difficult to add entries. If you are constructing a matrix you should either do it in exactly the order specified here, or use some other more flexible intermediate structure.
The index array is of size nrow+1. All nonzero matrix elements on row i are stored in positions index[i] through index[i+1]1 in the arrays column and value. The column array contains the column index for each entry, in ascending order, and the corresponding position in the value array contains the floating point matrix element.
index[nrow] should be equal to the total number of elements stored.
Thus, to find the value of matrix element [5,4] you should loop over positions index[5] to index[6]1 in column until you either find the value 4, or a higher value (meaning the element was zero).
It is fairly easy to construct the matrix onthefly if you can do it rowbyrow.
IMPORTANT: If compressed_symmetric is set to TRUE, you should only store EITHER the upper OR lower triangle (and the diagonal), and the other half is assumed to be symmetric. Otherwise, if compressed_symmetric==FALSE, no symmetry is implied and all elements should be stored.
The symmetry compression saves us a factor 2 both in storage and matrix multiplication CPUtime, which can be very useful for huge eigenproblems.
If you are unsure, just set compressed_symmetric to FALSE and list all elements. If you enable it but still list all elements (both upper and lower triangle) you will be sorry...
Internally, the sparse data is stored as a separate list for each row, where the list element is a structure with a column and (floatingpoint) data value. This makes it possible, although not completely transparent, to update values in random access order. The drawback is that the structure will allocate nrow memory regions. The matrix data could be stored in a single contiguous array with indices for each row, but then we could only insert elements at the end without copying the entire matrix.
After you have
In other words: Not perfect, but it works.
Public Attributes  
gmx_bool  compressed_symmetric 
Store half elements and assume symmetry. More...  
int  nrow 
Number of rows in matrix.  
int *  ndata 
Number of entries on each row (list)  
int *  nalloc 
Allocated entry list length for each row.  
gmx_sparsematrix_entry_t **  data 
data[i] is a list with entries on row i  
gmx_bool gmx_sparsematrix::compressed_symmetric 
Store half elements and assume symmetry.