splineax 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splineax-0.2.0/PKG-INFO +84 -0
- splineax-0.2.0/README.md +69 -0
- splineax-0.2.0/pyproject.toml +40 -0
- splineax-0.2.0/src/splineax/__init__.py +30 -0
- splineax-0.2.0/src/splineax/operators/__init__.py +7 -0
- splineax-0.2.0/src/splineax/operators/_bcoo.py +78 -0
- splineax-0.2.0/src/splineax/operators/_bcsr.py +82 -0
- splineax-0.2.0/src/splineax/operators/_jacobian.py +610 -0
- splineax-0.2.0/src/splineax/operators/_operations.py +198 -0
- splineax-0.2.0/src/splineax/operators/_sparse.py +32 -0
- splineax-0.2.0/src/splineax/py.typed +0 -0
- splineax-0.2.0/src/splineax/solvers/__init__.py +22 -0
- splineax-0.2.0/src/splineax/solvers/_auto.py +114 -0
- splineax-0.2.0/src/splineax/solvers/_klu.py +576 -0
- splineax-0.2.0/src/splineax/solvers/_sparse.py +127 -0
- splineax-0.2.0/src/splineax/solvers/_spsolve.py +227 -0
splineax-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: splineax
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Sparse linear operators and solvers for Lineax
|
|
5
|
+
Author: Nardi Lam
|
|
6
|
+
Author-email: Nardi Lam <mail@nardilam.nl>
|
|
7
|
+
Requires-Dist: lineax>=0.1.1
|
|
8
|
+
Requires-Dist: jax>=0.4
|
|
9
|
+
Requires-Dist: equinox>=0.11
|
|
10
|
+
Requires-Dist: jaxtyping>=0.2
|
|
11
|
+
Requires-Dist: splineax-klujax>=0.5.0.post1
|
|
12
|
+
Requires-Dist: asdex>=0.5.1
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# splineax
|
|
17
|
+
|
|
18
|
+
Sparse linear operators and direct solvers for
|
|
19
|
+
[Lineax](https://github.com/patrick-kidger/lineax).
|
|
20
|
+
|
|
21
|
+
`splineax` lets you keep a linear system in its native sparse storage
|
|
22
|
+
(`jax.experimental.sparse.BCOO` / `BCSR`) and solve it with a sparse *direct* solver that
|
|
23
|
+
plugs straight into `lineax.linear_solve`. It also interfaces with [asdex](https://github.com/adrhill/asdex) for calculating sparse Jacobians and using them as operators.
|
|
24
|
+
|
|
25
|
+
- **Operators**: `BCOOLinearOperator`, `BCSRLinearOperator`, `SparseJacobianLinearOperator`.
|
|
26
|
+
- **Solvers**: `Spsolve` (any backend), `KLU` (CPU-only, SuiteSparse, factorization reuse),
|
|
27
|
+
and `AutoSparseLinearSolver` (picks one based on the platform).
|
|
28
|
+
- A `SparseLinearSolver` protocol for separating factorization from solving.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install git+https://github.com/nardi/splineax.git@v0.1.1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Example
|
|
37
|
+
|
|
38
|
+
Solve a 10000 x 10000 system. As a dense matrix it would need 10^8 entries, but kept
|
|
39
|
+
sparse it has only ~3 x 10^4 nonzeros, and the solver never materialises the dense form.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import jax.numpy as jnp
|
|
43
|
+
import lineax as lx
|
|
44
|
+
import numpy as np
|
|
45
|
+
from jax.experimental.sparse import BCOO
|
|
46
|
+
|
|
47
|
+
import splineax
|
|
48
|
+
|
|
49
|
+
n = 10000
|
|
50
|
+
np.random.seed(0)
|
|
51
|
+
|
|
52
|
+
# A large, randomly sparse matrix with a heavy diagonal (so it is invertible).
|
|
53
|
+
diagonal_indices = np.stack([np.arange(n), np.arange(n)], axis=1)
|
|
54
|
+
off_diagonal_indices = np.unique(np.random.randint(0, n, size=(2 * n, 2)), axis=0)
|
|
55
|
+
indices = jnp.concatenate([diagonal_indices, off_diagonal_indices])
|
|
56
|
+
values = jnp.concatenate(
|
|
57
|
+
[
|
|
58
|
+
np.full(n, float(n)),
|
|
59
|
+
np.random.uniform(low=-1, high=1, size=off_diagonal_indices.shape[0]),
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
matrix = BCOO((values, indices), shape=(n, n)).sum_duplicates()
|
|
63
|
+
|
|
64
|
+
operator = splineax.BCOOLinearOperator(matrix)
|
|
65
|
+
vectors = [jnp.ones(n), jnp.arange(n) % 2]
|
|
66
|
+
solver = splineax.AutoSparseLinearSolver()
|
|
67
|
+
|
|
68
|
+
# Calculate factorization once...
|
|
69
|
+
with solver.factorize(operator) as factorized_state:
|
|
70
|
+
# ...and reuse for multiple solves.
|
|
71
|
+
solution = lx.linear_solve(
|
|
72
|
+
operator, vectors[0], solver=solver, state=factorized_state
|
|
73
|
+
)
|
|
74
|
+
assert jnp.allclose(matrix @ solution.value, vectors[0], atol=1e-4)
|
|
75
|
+
|
|
76
|
+
solution = lx.linear_solve(
|
|
77
|
+
operator, vectors[1], solver=solver, state=factorized_state
|
|
78
|
+
)
|
|
79
|
+
assert jnp.allclose(matrix @ solution.value, vectors[1], atol=1e-4)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
Build the docs locally with `uv run mkdocs serve`, or view the [user guide and API reference here](https://nardi.github.io/splineax).
|
splineax-0.2.0/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# splineax
|
|
2
|
+
|
|
3
|
+
Sparse linear operators and direct solvers for
|
|
4
|
+
[Lineax](https://github.com/patrick-kidger/lineax).
|
|
5
|
+
|
|
6
|
+
`splineax` lets you keep a linear system in its native sparse storage
|
|
7
|
+
(`jax.experimental.sparse.BCOO` / `BCSR`) and solve it with a sparse *direct* solver that
|
|
8
|
+
plugs straight into `lineax.linear_solve`. It also interfaces with [asdex](https://github.com/adrhill/asdex) for calculating sparse Jacobians and using them as operators.
|
|
9
|
+
|
|
10
|
+
- **Operators**: `BCOOLinearOperator`, `BCSRLinearOperator`, `SparseJacobianLinearOperator`.
|
|
11
|
+
- **Solvers**: `Spsolve` (any backend), `KLU` (CPU-only, SuiteSparse, factorization reuse),
|
|
12
|
+
and `AutoSparseLinearSolver` (picks one based on the platform).
|
|
13
|
+
- A `SparseLinearSolver` protocol for separating factorization from solving.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install git+https://github.com/nardi/splineax.git@v0.1.1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
Solve a 10000 x 10000 system. As a dense matrix it would need 10^8 entries, but kept
|
|
24
|
+
sparse it has only ~3 x 10^4 nonzeros, and the solver never materialises the dense form.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import jax.numpy as jnp
|
|
28
|
+
import lineax as lx
|
|
29
|
+
import numpy as np
|
|
30
|
+
from jax.experimental.sparse import BCOO
|
|
31
|
+
|
|
32
|
+
import splineax
|
|
33
|
+
|
|
34
|
+
n = 10000
|
|
35
|
+
np.random.seed(0)
|
|
36
|
+
|
|
37
|
+
# A large, randomly sparse matrix with a heavy diagonal (so it is invertible).
|
|
38
|
+
diagonal_indices = np.stack([np.arange(n), np.arange(n)], axis=1)
|
|
39
|
+
off_diagonal_indices = np.unique(np.random.randint(0, n, size=(2 * n, 2)), axis=0)
|
|
40
|
+
indices = jnp.concatenate([diagonal_indices, off_diagonal_indices])
|
|
41
|
+
values = jnp.concatenate(
|
|
42
|
+
[
|
|
43
|
+
np.full(n, float(n)),
|
|
44
|
+
np.random.uniform(low=-1, high=1, size=off_diagonal_indices.shape[0]),
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
matrix = BCOO((values, indices), shape=(n, n)).sum_duplicates()
|
|
48
|
+
|
|
49
|
+
operator = splineax.BCOOLinearOperator(matrix)
|
|
50
|
+
vectors = [jnp.ones(n), jnp.arange(n) % 2]
|
|
51
|
+
solver = splineax.AutoSparseLinearSolver()
|
|
52
|
+
|
|
53
|
+
# Calculate factorization once...
|
|
54
|
+
with solver.factorize(operator) as factorized_state:
|
|
55
|
+
# ...and reuse for multiple solves.
|
|
56
|
+
solution = lx.linear_solve(
|
|
57
|
+
operator, vectors[0], solver=solver, state=factorized_state
|
|
58
|
+
)
|
|
59
|
+
assert jnp.allclose(matrix @ solution.value, vectors[0], atol=1e-4)
|
|
60
|
+
|
|
61
|
+
solution = lx.linear_solve(
|
|
62
|
+
operator, vectors[1], solver=solver, state=factorized_state
|
|
63
|
+
)
|
|
64
|
+
assert jnp.allclose(matrix @ solution.value, vectors[1], atol=1e-4)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Documentation
|
|
68
|
+
|
|
69
|
+
Build the docs locally with `uv run mkdocs serve`, or view the [user guide and API reference here](https://nardi.github.io/splineax).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "splineax"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Sparse linear operators and solvers for Lineax"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "Nardi Lam", email = "mail@nardilam.nl" }]
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"lineax>=0.1.1",
|
|
10
|
+
"jax>=0.4",
|
|
11
|
+
"equinox>=0.11",
|
|
12
|
+
"jaxtyping>=0.2",
|
|
13
|
+
"splineax-klujax>=0.5.0.post1",
|
|
14
|
+
"asdex>=0.5.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.11.7,<0.12.0"]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = [
|
|
23
|
+
"mkdocs-material>=9.7.6",
|
|
24
|
+
"mkdocstrings[python]>=0.27",
|
|
25
|
+
"pytest>=9.1.0",
|
|
26
|
+
"pytest-markdown-docs>=0.9.2",
|
|
27
|
+
"ruff>=0.15.17",
|
|
28
|
+
"ty>=0.0.59",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
# Execute the code examples in the markdown docs (and README) as tests via
|
|
33
|
+
# pytest-markdown-docs. The superfences syntax lets dependent code blocks share state
|
|
34
|
+
# with a `{.python continuation}` fence, which also renders under Material for MkDocs.
|
|
35
|
+
addopts = "--markdown-docs --markdown-docs-syntax superfences"
|
|
36
|
+
testpaths = ["tests", "docs", "README.md"]
|
|
37
|
+
|
|
38
|
+
[tool.ruff.lint]
|
|
39
|
+
ignore = ["F722"]
|
|
40
|
+
select = ["E4", "E7", "E9", "F", "I"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .operators import (
|
|
2
|
+
BCOOLinearOperator as BCOOLinearOperator,
|
|
3
|
+
)
|
|
4
|
+
from .operators import (
|
|
5
|
+
BCSRLinearOperator as BCSRLinearOperator,
|
|
6
|
+
)
|
|
7
|
+
from .operators import (
|
|
8
|
+
JacobianColoring as JacobianColoring,
|
|
9
|
+
)
|
|
10
|
+
from .operators import (
|
|
11
|
+
SparseJacobianLinearOperator as SparseJacobianLinearOperator,
|
|
12
|
+
)
|
|
13
|
+
from .operators import (
|
|
14
|
+
SparseJacobianLinearOperatorColoring as SparseJacobianLinearOperatorColoring,
|
|
15
|
+
)
|
|
16
|
+
from .solvers import (
|
|
17
|
+
KLU as KLU,
|
|
18
|
+
)
|
|
19
|
+
from .solvers import (
|
|
20
|
+
AbstractSparseLinearSolver as AbstractSparseLinearSolver,
|
|
21
|
+
)
|
|
22
|
+
from .solvers import (
|
|
23
|
+
AutoSparseLinearSolver as AutoSparseLinearSolver,
|
|
24
|
+
)
|
|
25
|
+
from .solvers import (
|
|
26
|
+
SparseLinearSolver as SparseLinearSolver,
|
|
27
|
+
)
|
|
28
|
+
from .solvers import (
|
|
29
|
+
Spsolve as Spsolve,
|
|
30
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from ._bcoo import BCOOLinearOperator as BCOOLinearOperator
|
|
2
|
+
from ._bcsr import BCSRLinearOperator as BCSRLinearOperator
|
|
3
|
+
from ._jacobian import JacobianColoring as JacobianColoring
|
|
4
|
+
from ._jacobian import SparseJacobianLinearOperator as SparseJacobianLinearOperator
|
|
5
|
+
from ._jacobian import (
|
|
6
|
+
SparseJacobianLinearOperatorColoring as SparseJacobianLinearOperatorColoring,
|
|
7
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import equinox as eqx
|
|
2
|
+
import jax
|
|
3
|
+
import jax.numpy as jnp
|
|
4
|
+
from jax.experimental.sparse import BCOO
|
|
5
|
+
from jaxtyping import Array, Inexact
|
|
6
|
+
from lineax import AbstractLinearOperator, is_symmetric
|
|
7
|
+
from lineax._tags import transpose_tags
|
|
8
|
+
|
|
9
|
+
from ._operations import (
|
|
10
|
+
register_sparse_operator,
|
|
11
|
+
sparse_as_matrix,
|
|
12
|
+
sparse_in_structure,
|
|
13
|
+
sparse_mv,
|
|
14
|
+
sparse_out_structure,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BCOOLinearOperator(AbstractLinearOperator):
|
|
19
|
+
"""Wraps a `jax.experimental.sparse.BCOO` array into a linear operator.
|
|
20
|
+
|
|
21
|
+
If the matrix has shape `(a, b)` then matrix-vector multiplication (`self.mv`) is
|
|
22
|
+
defined in the usual way: as accepting a vector of shape `(b,)` and returning a
|
|
23
|
+
vector of shape `(a,)`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
matrix: Inexact[BCOO, "a b"]
|
|
27
|
+
tags: frozenset[object] = eqx.field(static=True)
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self, matrix: Inexact[BCOO, "a b"], tags: object | frozenset[object] = ()
|
|
31
|
+
):
|
|
32
|
+
"""**Arguments:**
|
|
33
|
+
|
|
34
|
+
- `matrix`: a two-dimensional `BCOO` array. For an array with shape `(a, b)`
|
|
35
|
+
then this operator can perform matrix-vector products on a vector of shape
|
|
36
|
+
`(b,)` to return a vector of shape `(a,)`.
|
|
37
|
+
- `tags`: any tags indicating whether this matrix has any particular properties,
|
|
38
|
+
like symmetry or positive-definite-ness. Note that these properties are
|
|
39
|
+
unchecked and you may get incorrect values elsewhere if these tags are
|
|
40
|
+
wrong.
|
|
41
|
+
"""
|
|
42
|
+
if matrix.ndim != 2:
|
|
43
|
+
raise ValueError(
|
|
44
|
+
"`BCOOLinearOperator(matrix=...)` should be 2-dimensional."
|
|
45
|
+
)
|
|
46
|
+
if not jnp.issubdtype(matrix.dtype, jnp.inexact):
|
|
47
|
+
matrix = BCOO(
|
|
48
|
+
(matrix.data.astype(float), matrix.indices), shape=matrix.shape
|
|
49
|
+
)
|
|
50
|
+
self.matrix = matrix
|
|
51
|
+
self.tags = tags if isinstance(tags, frozenset) else frozenset([tags])
|
|
52
|
+
|
|
53
|
+
def mv(self, vector: Inexact[Array, " b"]) -> Inexact[Array, " a"]:
|
|
54
|
+
return sparse_mv(self.matrix, vector)
|
|
55
|
+
|
|
56
|
+
def as_matrix(self) -> Inexact[Array, "a b"]:
|
|
57
|
+
return sparse_as_matrix(self.matrix)
|
|
58
|
+
|
|
59
|
+
def transpose(self) -> "BCOOLinearOperator":
|
|
60
|
+
if is_symmetric(self):
|
|
61
|
+
return self
|
|
62
|
+
matrix_T: BCOO = self.matrix.T
|
|
63
|
+
return BCOOLinearOperator(matrix_T, transpose_tags(self.tags))
|
|
64
|
+
|
|
65
|
+
def in_structure(self) -> jax.ShapeDtypeStruct:
|
|
66
|
+
return sparse_in_structure(self)
|
|
67
|
+
|
|
68
|
+
def out_structure(self) -> jax.ShapeDtypeStruct:
|
|
69
|
+
return sparse_out_structure(self)
|
|
70
|
+
|
|
71
|
+
def _conj(self) -> "BCOOLinearOperator":
|
|
72
|
+
matrix = BCOO(
|
|
73
|
+
(self.matrix.data.conj(), self.matrix.indices), shape=self.matrix.shape
|
|
74
|
+
)
|
|
75
|
+
return BCOOLinearOperator(matrix, self.tags)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
register_sparse_operator(BCOOLinearOperator)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import equinox as eqx
|
|
2
|
+
import jax
|
|
3
|
+
import jax.numpy as jnp
|
|
4
|
+
from jax.experimental.sparse import BCOO, BCSR
|
|
5
|
+
from jaxtyping import Array, Inexact
|
|
6
|
+
from lineax import AbstractLinearOperator, is_symmetric
|
|
7
|
+
from lineax._tags import transpose_tags
|
|
8
|
+
|
|
9
|
+
from ._operations import (
|
|
10
|
+
register_sparse_operator,
|
|
11
|
+
sparse_as_matrix,
|
|
12
|
+
sparse_in_structure,
|
|
13
|
+
sparse_mv,
|
|
14
|
+
sparse_out_structure,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BCSRLinearOperator(AbstractLinearOperator):
|
|
19
|
+
"""Wraps a `jax.experimental.sparse.BCSR` array into a linear operator.
|
|
20
|
+
|
|
21
|
+
If the matrix has shape `(a, b)` then matrix-vector multiplication (`self.mv`) is
|
|
22
|
+
defined in the usual way: as accepting a vector of shape `(b,)` and returning a
|
|
23
|
+
vector of shape `(a,)`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
matrix: Inexact[BCSR, "a b"]
|
|
27
|
+
tags: frozenset[object] = eqx.field(static=True)
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self, matrix: Inexact[BCSR, "a b"], tags: object | frozenset[object] = ()
|
|
31
|
+
):
|
|
32
|
+
"""**Arguments:**
|
|
33
|
+
|
|
34
|
+
- `matrix`: a two-dimensional `BCSR` array. For an array with shape `(a, b)`
|
|
35
|
+
then this operator can perform matrix-vector products on a vector of shape
|
|
36
|
+
`(b,)` to return a vector of shape `(a,)`.
|
|
37
|
+
- `tags`: any tags indicating whether this matrix has any particular properties,
|
|
38
|
+
like symmetry or positive-definite-ness. Note that these properties are
|
|
39
|
+
unchecked and you may get incorrect values elsewhere if these tags are
|
|
40
|
+
wrong.
|
|
41
|
+
"""
|
|
42
|
+
if matrix.ndim != 2:
|
|
43
|
+
raise ValueError(
|
|
44
|
+
"`BCSRLinearOperator(matrix=...)` should be 2-dimensional."
|
|
45
|
+
)
|
|
46
|
+
if not jnp.issubdtype(matrix.dtype, jnp.inexact):
|
|
47
|
+
matrix = BCSR(
|
|
48
|
+
(matrix.data.astype(jnp.float32), matrix.indices, matrix.indptr),
|
|
49
|
+
shape=matrix.shape,
|
|
50
|
+
)
|
|
51
|
+
self.matrix = matrix
|
|
52
|
+
self.tags = tags if isinstance(tags, frozenset) else frozenset([tags])
|
|
53
|
+
|
|
54
|
+
def mv(self, vector: Inexact[Array, " b"]) -> Inexact[Array, " a"]:
|
|
55
|
+
return sparse_mv(self.matrix, vector)
|
|
56
|
+
|
|
57
|
+
def as_matrix(self) -> Inexact[Array, "a b"]:
|
|
58
|
+
return sparse_as_matrix(self.matrix)
|
|
59
|
+
|
|
60
|
+
def transpose(self) -> "BCSRLinearOperator":
|
|
61
|
+
if is_symmetric(self):
|
|
62
|
+
return self
|
|
63
|
+
# `BCSR.transpose` is not implemented in JAX; round-trip through `BCOO`.
|
|
64
|
+
matrix_T_bcoo: BCOO = self.matrix.to_bcoo().T
|
|
65
|
+
matrix_T = BCSR.from_bcoo(matrix_T_bcoo)
|
|
66
|
+
return BCSRLinearOperator(matrix_T, transpose_tags(self.tags))
|
|
67
|
+
|
|
68
|
+
def in_structure(self) -> jax.ShapeDtypeStruct:
|
|
69
|
+
return sparse_in_structure(self)
|
|
70
|
+
|
|
71
|
+
def out_structure(self) -> jax.ShapeDtypeStruct:
|
|
72
|
+
return sparse_out_structure(self)
|
|
73
|
+
|
|
74
|
+
def _conj(self) -> "BCSRLinearOperator":
|
|
75
|
+
matrix = BCSR(
|
|
76
|
+
(self.matrix.data.conj(), self.matrix.indices, self.matrix.indptr),
|
|
77
|
+
shape=self.matrix.shape,
|
|
78
|
+
)
|
|
79
|
+
return BCSRLinearOperator(matrix, self.tags)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
register_sparse_operator(BCSRLinearOperator)
|