|
3 | 3 | Sparse
|
4 | 4 | ======
|
5 | 5 |
|
| 6 | +In general, *sparse* matrices provide the same functionality as regular |
| 7 | +matrices. The difference lies in the way the elements of *sparse* matrices are |
| 8 | +represented and stored in memory. Only the non-zero elements of the latter are stored. |
| 9 | +This has some potential advantages: first, this |
| 10 | +may obviously lead to reduced memory usage and, second, clever |
| 11 | +storage methods may lead to reduced computation time through the use of |
| 12 | +sparse specific algorithms. We usually refer to the generically stored matrices |
| 13 | +as *dense* matrices. |
| 14 | + |
| 15 | +Aesara's sparse package provides efficient algorithms, but its use is not recommended |
| 16 | +in all cases or for all matrices. As an obvious example, consider the case where |
| 17 | +the *sparsity proportion* is very low. The *sparsity proportion* refers to the |
| 18 | +ratio of the number of zero elements to the number of all elements in a matrix. |
| 19 | +A low sparsity proportion may result in the use of more space in memory |
| 20 | +since not only the actual data is stored, but also the position of nearly every |
| 21 | +element of the matrix. This would also require more computation |
| 22 | +time whereas a dense matrix representation along with regular optimized algorithms might do a |
| 23 | +better job. Other examples may be found at the nexus of the specific purpose and structure |
| 24 | +of the matrices. More documentation may be found in the |
| 25 | +`SciPy Sparse Reference <http://docs.scipy.org/doc/scipy/reference/sparse.html>`_. |
| 26 | + |
| 27 | +Since sparse matrices are not stored in contiguous arrays, there are several |
| 28 | +ways to represent them in memory. This is usually designated by the so-called ``format`` |
| 29 | +of the matrix. Since Aesara's sparse matrix package is based on the SciPy |
| 30 | +sparse package, complete information about sparse matrices can be found |
| 31 | +in the SciPy documentation. Like SciPy, Aesara does not implement sparse formats for |
| 32 | +arrays with a number of dimensions different from two. |
| 33 | + |
| 34 | +So far, Aesara implements two ``formats`` of sparse matrix: ``csc`` and ``csr``. |
| 35 | +Those are almost identical except that ``csc`` is based on the *columns* of the |
| 36 | +matrix and ``csr`` is based on its *rows*. They both have the same purpose: |
| 37 | +to provide for the use of efficient algorithms performing linear algebra operations. |
| 38 | +A disadvantage is that they fail to give an efficient way to modify the sparsity structure |
| 39 | +of the underlying matrix, i.e. adding new elements. This means that if you are |
| 40 | +planning to add new elements in a sparse matrix very often in your computational graph, |
| 41 | +perhaps a tensor variable could be a better choice. |
| 42 | + |
| 43 | +More documentation may be found in the :ref:`Sparse Library Reference <libdoc_sparse>`. |
| 44 | + |
| 45 | +Before going further, here are the ``import`` statements that are assumed for the rest of the |
| 46 | +tutorial: |
| 47 | + |
| 48 | +>>> import aesara |
| 49 | +>>> import numpy as np |
| 50 | +>>> import scipy.sparse as sp |
| 51 | +>>> from aesara import sparse |
| 52 | + |
| 53 | +Compressed Sparse Format |
| 54 | +------------------------ |
| 55 | + |
| 56 | +.. Changes to this section should also result in changes to library/sparse/index.txt. |
| 57 | +
|
| 58 | +Aesara supports two *compressed sparse formats*: ``csc`` and ``csr``, respectively based on columns |
| 59 | +and rows. They have both the same attributes: ``data``, ``indices``, ``indptr`` and ``shape``. |
| 60 | + |
| 61 | + * The ``data`` attribute is a one-dimensional ``ndarray`` which contains all the non-zero |
| 62 | + elements of the sparse matrix. |
| 63 | + |
| 64 | + * The ``indices`` and ``indptr`` attributes are used to store the position of the data in the |
| 65 | + sparse matrix. |
| 66 | + |
| 67 | + * The ``shape`` attribute is exactly the same as the ``shape`` attribute of a dense (i.e. generic) |
| 68 | + matrix. It can be explicitly specified at the creation of a sparse matrix if it cannot be inferred |
| 69 | + from the first three attributes. |
| 70 | + |
| 71 | +Which format should I use? |
| 72 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 73 | + |
| 74 | +At the end, the format does not affect the length of the ``data`` and ``indices`` attributes. They are both |
| 75 | +completely fixed by the number of elements you want to store. The only thing that changes with the format |
| 76 | +is ``indptr``. In ``csc`` format, the matrix is compressed along columns so a lower number of columns will |
| 77 | +result in less memory use. On the other hand, with the ``csr`` format, the matrix is compressed along |
| 78 | +the rows and with a matrix that have a lower number of rows, ``csr`` format is a better choice. So here is the rule: |
| 79 | + |
| 80 | +.. note:: |
| 81 | + |
| 82 | + If shape[0] > shape[1], use ``csc`` format. Otherwise, use ``csr``. |
| 83 | + |
| 84 | +Sometimes, since the sparse module is young, ops does not exist for both format. So here is |
| 85 | +what may be the most relevant rule: |
| 86 | + |
| 87 | +.. note:: |
| 88 | + |
| 89 | + Use the format compatible with the ops in your computation graph. |
| 90 | + |
| 91 | +The documentation about the ops and their supported format may be found in |
| 92 | +the :ref:`Sparse Library Reference <libdoc_sparse>`. |
| 93 | + |
| 94 | +Handling Sparse in Aesara |
| 95 | +------------------------- |
| 96 | + |
| 97 | +Most of the ops in Aesara depend on the ``format`` of the sparse matrix. |
| 98 | +That is why there are two kinds of constructors of sparse variables: |
| 99 | +``csc_matrix`` and ``csr_matrix``. These can be called with the usual |
| 100 | +``name`` and ``dtype`` parameters, but no ``broadcastable`` flags are |
| 101 | +allowed. This is forbidden since the sparse package, as the SciPy sparse module, |
| 102 | +does not provide any way to handle a number of dimensions different from two. |
| 103 | +The set of all accepted ``dtype`` for the sparse matrices can be found in |
| 104 | +``sparse.all_dtypes``. |
| 105 | + |
| 106 | +>>> sparse.all_dtypes # doctest: +SKIP |
| 107 | +set(['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', |
| 108 | + 'float32', 'float64', 'complex64', 'complex128']) |
| 109 | + |
| 110 | +To and Fro |
| 111 | +~~~~~~~~~~ |
| 112 | + |
| 113 | +To move back and forth from a dense matrix to a sparse matrix representation, Aesara |
| 114 | +provides the ``dense_from_sparse``, ``csr_from_dense`` and |
| 115 | +``csc_from_dense`` functions. No additional detail must be provided. Here is |
| 116 | +an example that performs a full cycle from sparse to sparse: |
| 117 | + |
| 118 | +>>> x = sparse.csc_matrix(name='x', dtype='float32') |
| 119 | +>>> y = sparse.dense_from_sparse(x) |
| 120 | +>>> z = sparse.csc_from_dense(y) |
| 121 | + |
| 122 | +Properties and Construction |
| 123 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 124 | + |
| 125 | +Although sparse variables do not allow direct access to their properties, |
| 126 | +this can be accomplished using the ``csm_properties`` function. This will return |
| 127 | +a tuple of one-dimensional ``tensor`` variables that represents the internal characteristics |
| 128 | +of the sparse matrix. |
| 129 | + |
| 130 | +In order to reconstruct a sparse matrix from some properties, the functions ``CSC`` |
| 131 | +and ``CSR`` can be used. This will create the sparse matrix in the desired |
| 132 | +format. As an example, the following code reconstructs a ``csc`` matrix into |
| 133 | +a ``csr`` one. |
| 134 | + |
| 135 | +>>> x = sparse.csc_matrix(name='x', dtype='int64') |
| 136 | +>>> data, indices, indptr, shape = sparse.csm_properties(x) |
| 137 | +>>> y = sparse.CSR(data, indices, indptr, shape) |
| 138 | +>>> f = aesara.function([x], y) |
| 139 | +>>> a = sp.csc_matrix(np.asarray([[0, 1, 1], [0, 0, 0], [1, 0, 0]])) |
| 140 | +>>> print(a.toarray()) |
| 141 | +[[0 1 1] |
| 142 | + [0 0 0] |
| 143 | + [1 0 0]] |
| 144 | +>>> print(f(a).toarray()) |
| 145 | +[[0 0 1] |
| 146 | + [1 0 0] |
| 147 | + [1 0 0]] |
| 148 | + |
| 149 | +The last example shows that one format can be obtained from transposition of |
| 150 | +the other. Indeed, when calling the ``transpose`` function, |
| 151 | +the sparse characteristics of the resulting matrix cannot be the same as the one |
| 152 | +provided as input. |
| 153 | + |
| 154 | +Structured Operation |
| 155 | +~~~~~~~~~~~~~~~~~~~~ |
| 156 | + |
| 157 | +Several ops are set to make use of the very peculiar structure of the sparse |
| 158 | +matrices. These ops are said to be *structured* and simply do not perform any |
| 159 | +computations on the zero elements of the sparse matrix. They can be thought as being |
| 160 | +applied only to the data attribute of the latter. Note that these structured ops |
| 161 | +provide a structured gradient. More explication below. |
| 162 | + |
| 163 | +>>> x = sparse.csc_matrix(name='x', dtype='float32') |
| 164 | +>>> y = sparse.structured_add(x, 2) |
| 165 | +>>> f = aesara.function([x], y) |
| 166 | +>>> a = sp.csc_matrix(np.asarray([[0, 0, -1], [0, -2, 1], [3, 0, 0]], dtype='float32')) |
| 167 | +>>> print(a.toarray()) |
| 168 | +[[ 0. 0. -1.] |
| 169 | + [ 0. -2. 1.] |
| 170 | + [ 3. 0. 0.]] |
| 171 | +>>> print(f(a).toarray()) |
| 172 | +[[ 0. 0. 1.] |
| 173 | + [ 0. 0. 3.] |
| 174 | + [ 5. 0. 0.]] |
| 175 | + |
| 176 | +.. _tutsparse_gradient: |
| 177 | + |
| 178 | +Gradient |
| 179 | +~~~~~~~~ |
| 180 | + |
| 181 | +The gradients of the ops in the sparse module can also be structured. Some ops provide |
| 182 | +a *flag* to indicate if the gradient is to be structured or not. The documentation can |
| 183 | +be used to determine if the gradient of an op is regular or structured or if its |
| 184 | +implementation can be modified. Similarly to structured ops, when a structured gradient is calculated, the |
| 185 | +computation is done only for the non-zero elements of the sparse matrix. |
| 186 | + |
| 187 | +More documentation regarding the gradients of specific ops can be found in the |
| 188 | +:ref:`Sparse Library Reference <libdoc_sparse>`. |
| 189 | + |
| 190 | + |
6 | 191 | .. toctree::
|
7 | 192 | :maxdepth: 1
|
8 | 193 |
|
9 |
| - sparse |
10 | 194 | sparse_api
|
11 | 195 | sandbox
|
0 commit comments