Skip to content

Commit

Permalink
Create custom COO matrix representation for product operators, with u…
Browse files Browse the repository at this point in the history
…pdated product spaces (#1642)

* create custom COO matrix representation for product operators

* run linter

* add documentation to coo matrix

* fix linter issues

---------

Co-authored-by: Paul Hausner <paul.hausner@it.uu.se>
  • Loading branch information
leftaroundabout and paulhausner committed Mar 8, 2024
1 parent 9a5b9d7 commit 60a854a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 6 deletions.
25 changes: 19 additions & 6 deletions odl/operator/pspace_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from odl.operator.operator import Operator
from odl.operator.default_ops import ZeroOperator
from odl.space import ProductSpace
from odl.util import COOMatrix


__all__ = ('ProductSpaceOperator',
Expand Down Expand Up @@ -171,7 +172,16 @@ def __init__(self, operators, domain=None, range=None):
if not all(isinstance(op, Operator) for op in operators.data):
raise ValueError('sparse matrix `operator` contains non-'
'`Operator` entries')
# scipy sparse matrix not supported (deprecated due to API changes)
# keep now for backward compatibility
print('Warning: scipy.sparse.spmatrix is deprecated.')
self.__ops = COOMatrix(operators.data,
(operators.row, operators.col),
operators.shape)

elif isinstance(operators, COOMatrix):
self.__ops = operators

else:
self.__ops = self._convert_to_spmatrix(operators)

Expand Down Expand Up @@ -229,8 +239,9 @@ def __init__(self, operators, domain=None, range=None):
@staticmethod
def _convert_to_spmatrix(operators):
"""Convert an array-like object of operators to a sparse matrix."""

# Lazy import to improve `import odl` time
import scipy.sparse
# import scipy.sparse

# Convert ops to sparse representation. This is not trivial because
# operators can be indexable themselves and give the wrong impression
Expand Down Expand Up @@ -279,8 +290,8 @@ def _convert_to_spmatrix(operators):
# in `coo_matrix.__init__`
data_arr = np.empty(len(data), dtype=object)
data_arr[:] = data
return scipy.sparse.coo_matrix((data_arr, (irow, icol)),
shape=(nrows, ncols))

return COOMatrix(data_arr, (irow, icol), (nrows, ncols))

@property
def ops(self):
Expand Down Expand Up @@ -382,7 +393,7 @@ def derivative(self, x):
data[:] = deriv_ops
indices = [self.ops.row, self.ops.col]
shape = self.ops.shape
deriv_matrix = scipy.sparse.coo_matrix((data, indices), shape)
deriv_matrix = COOMatrix(data, indices, shape)
return ProductSpaceOperator(deriv_matrix, self.domain, self.range)

@property
Expand Down Expand Up @@ -431,7 +442,7 @@ def adjoint(self):
data[:] = adjoint_ops
indices = [self.ops.col, self.ops.row] # Swap col/row -> transpose
shape = (self.ops.shape[1], self.ops.shape[0])
adj_matrix = scipy.sparse.coo_matrix((data, indices), shape)
adj_matrix = COOMatrix(data, indices, shape)
return ProductSpaceOperator(adj_matrix, self.range, self.domain)

def __getitem__(self, index):
Expand Down Expand Up @@ -731,6 +742,7 @@ class BroadcastOperator(Operator):
ReductionOperator : Calculates sum of operator results.
DiagonalOperator : Case where each operator should have its own argument.
"""

def __init__(self, *operators):
"""Initialize a new instance
Expand Down Expand Up @@ -901,6 +913,7 @@ class ReductionOperator(Operator):
DiagonalOperator : Case where each operator should have its own argument.
SeparableSum : Corresponding construction for functionals.
"""

def __init__(self, *operators):
"""Initialize a new instance.
Expand Down Expand Up @@ -1145,7 +1158,7 @@ def __init__(self, *operators, **kwargs):
data = np.empty(n_ops, dtype=object)
data[:] = operators
shape = (n_ops, n_ops)
op_matrix = scipy.sparse.coo_matrix((data, (irow, icol)), shape)
op_matrix = COOMatrix(data, (irow, icol), shape)
super(DiagonalOperator, self).__init__(op_matrix, **kwargs)

self.__operators = tuple(operators)
Expand Down
2 changes: 2 additions & 0 deletions odl/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .testutils import *
from .utility import *
from .vectorization import *
from .sparse import *

__all__ = ()
__all__ += graphics.__all__
Expand All @@ -26,3 +27,4 @@
__all__ += testutils.__all__
__all__ += utility.__all__
__all__ += vectorization.__all__
__all__ += sparse.__all__
54 changes: 54 additions & 0 deletions odl/util/sparse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

"""
Sparse matrix representation for creating product space operators.
"""

__all__ = ('COOMatrix',)


class COOMatrix():
"""
Custom coo matrix representation for creating product space operators.
The columns, rows and data are stored in separate lists such that
A[i[k], j[k]] = data[k].
Note that, the class is only used as a container and does not provide
any matrix operations. Further, no checks are performed on the data thus
duplicate and out-of-order indices are allowed and the user is responsible
for ensuring the correct shape of the matrix.
"""

def __init__(self, data, index, shape):

# type check
if len(data) != len(index[0]) or len(data) != len(index[1]):
raise ValueError('data and index must have the same length')

self.__data = data
self.__index = index
self.__shape = shape

@property
def row(self):
"""Return the row indices of the matrix."""
return self.__index[0]

@property
def col(self):
"""Return the column indices of the matrix."""
return self.__index[1]

@property
def shape(self):
"""Return the shape of the matrix."""
return self.__shape

@property
def data(self):
"""Return the data of the matrix."""
return self.__data

def __repr__(self):
return f"COO matrix({self.data}, {self.index})"

0 comments on commit 60a854a

Please sign in to comment.