From 8a0cc0dfe9f666ae85f37dc60b65b9f12c553f34 Mon Sep 17 00:00:00 2001 From: Jonathan Wenger Date: Fri, 9 Oct 2020 10:09:59 +0200 Subject: [PATCH] Package restructuring and naming (#223) * moved linops up in the namespace * isort * imports unified * tests moved to correct dir and changed name back to linops * renamed back to linops * doctest problems fixed * refactored aslinop into own file * pylint fix * doctest bug fixed in aslinop * api doc structure unified * unified import in tests --- benchmarks/random_variables.py | 2 +- docs/source/index.rst | 7 ++-- docs/source/public_api/linalg.rst | 7 ---- docs/source/public_api/linops.rst | 5 +++ .../{probnum.rst => random_variables.rst} | 8 ++-- src/probnum/__init__.py | 2 +- .../linalg/linearsolvers/linearsolvers.py | 3 +- .../linalg/linearsolvers/matrixbased.py | 5 ++- src/probnum/{linalg => }/linops/__init__.py | 24 ++++++++--- .../kronecker.py => linops/_kronecker.py} | 20 ++++----- .../_linear_operator.py} | 40 +----------------- src/probnum/linops/_utils.py | 41 +++++++++++++++++++ src/probnum/random_variables/_arithmetic.py | 6 +-- src/probnum/random_variables/_normal.py | 2 +- .../test_linearsolvers/test_linearsolvers.py | 6 +-- .../{test_linalg => }/test_linops/__init__.py | 0 .../test_linops/test_kronecker.py | 2 +- .../test_linops/test_linearoperators.py | 2 +- tests/test_random_variables/test_normal.py | 6 ++- .../test_random_variable.py | 2 +- 20 files changed, 102 insertions(+), 88 deletions(-) create mode 100644 docs/source/public_api/linops.rst rename docs/source/public_api/{probnum.rst => random_variables.rst} (54%) rename src/probnum/{linalg => }/linops/__init__.py (50%) rename src/probnum/{linalg/linops/kronecker.py => linops/_kronecker.py} (96%) rename src/probnum/{linalg/linops/linearoperators.py => linops/_linear_operator.py} (93%) create mode 100644 src/probnum/linops/_utils.py rename tests/{test_linalg => }/test_linops/__init__.py (100%) rename tests/{test_linalg => }/test_linops/test_kronecker.py (99%) rename tests/{test_linalg => }/test_linops/test_linearoperators.py (99%) diff --git a/benchmarks/random_variables.py b/benchmarks/random_variables.py index 0601a197f..074578a65 100644 --- a/benchmarks/random_variables.py +++ b/benchmarks/random_variables.py @@ -4,8 +4,8 @@ import numpy as np -import probnum.linalg.linops as linops from benchmarks.benchmark_utils import SPD_MATRIX_5x5 +from probnum import linops from probnum import random_variables as rvs # Module level variables diff --git a/docs/source/index.rst b/docs/source/index.rst index 5a84ca048..0ac745bfb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -48,11 +48,12 @@ To learn how to use ProbNum check out the `quickstart guide >> import numpy as np - >>> from probnum.linalg.linops import LinearOperator + >>> from probnum.linops import LinearOperator >>> def mv(v): ... return np.array([2 * v[0] - v[1], 3 * v[1]]) ... @@ -575,39 +573,3 @@ def trace(self): raise ValueError("The trace is only defined for square linear operators.") else: return np.trace(self.A) - - -def aslinop(A): - """ - Return `A` as a :class:`LinearOperator`. - - Parameters - ---------- - A : array-like or LinearOperator or RandomVariable or object - Argument to be represented as a linear operator. When `A` is an object it needs - to have the attributes `.shape` and `.matvec`. - - Notes - ----- - If `A` has no `.dtype` attribute, the data type is determined by calling - :func:`LinearOperator.matvec()` - set the `.dtype` attribute to prevent this - call upon the linear operator creation. - - See Also - -------- - LinearOperator : Class representing linear operators. - - Examples - -------- - >>> from probnum.linalg.linops import aslinop - >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32) - >>> aslinop(M) - <2x3 MatrixMult with dtype=int32> - """ - if isinstance(A, scipy.sparse.linalg.LinearOperator): - return A - elif isinstance(A, (np.ndarray, scipy.sparse.spmatrix)): - return MatrixMult(A=A) - else: - op = scipy.sparse.linalg.aslinearoperator(A) - return LinearOperator(op) diff --git a/src/probnum/linops/_utils.py b/src/probnum/linops/_utils.py new file mode 100644 index 000000000..df38f2474 --- /dev/null +++ b/src/probnum/linops/_utils.py @@ -0,0 +1,41 @@ +import numpy as np +import scipy.sparse + +from . import _linear_operator + + +def aslinop(A) -> _linear_operator.LinearOperator: + """ + Return ``A`` as a :class:`LinearOperator`. + + Parameters + ---------- + A : array-like or LinearOperator or RandomVariable or object + Argument to be represented as a linear operator. When `A` is an object it needs + to have the attributes `.shape` and `.matvec`. + + See Also + -------- + LinearOperator : Class representing linear operators. + + Notes + ----- + If `A` has no `.dtype` attribute, the data type is determined by calling + :func:`LinearOperator.matvec()` - set the `.dtype` attribute to prevent this + call upon the linear operator creation. + + Examples + -------- + >>> import numpy as np + >>> from probnum.linops import aslinop + >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32) + >>> aslinop(M) + <2x3 MatrixMult with dtype=int32> + """ + if isinstance(A, scipy.sparse.linalg.LinearOperator): + return A + elif isinstance(A, (np.ndarray, scipy.sparse.spmatrix)): + return _linear_operator.MatrixMult(A=A) + else: + op = scipy.sparse.linalg.aslinearoperator(A) + return _linear_operator.LinearOperator(op) diff --git a/src/probnum/random_variables/_arithmetic.py b/src/probnum/random_variables/_arithmetic.py index ebf54395a..869ed8631 100644 --- a/src/probnum/random_variables/_arithmetic.py +++ b/src/probnum/random_variables/_arithmetic.py @@ -6,8 +6,8 @@ import numpy as np +import probnum.linops as _linear_operators from probnum import utils as _utils -from probnum.linalg import linops as _linops from ._dirac import Dirac as _Dirac from ._normal import Normal as _Normal @@ -280,8 +280,8 @@ def _matmul_normal_dirac(norm_rv: _Normal, dirac_rv: _Dirac) -> _Normal: ), ) elif norm_rv.ndim == 2 and norm_rv.shape[0] > 1: - cov_update = _linops.Kronecker( - _linops.Identity(dirac_rv.shape[0]), dirac_rv.support + cov_update = _linear_operators.Kronecker( + _linear_operators.Identity(dirac_rv.shape[0]), dirac_rv.support ) return _Normal( diff --git a/src/probnum/random_variables/_normal.py b/src/probnum/random_variables/_normal.py index eae08cda4..2c9568a09 100644 --- a/src/probnum/random_variables/_normal.py +++ b/src/probnum/random_variables/_normal.py @@ -6,8 +6,8 @@ import scipy.linalg import scipy.stats +from probnum import linops from probnum import utils as _utils -from probnum.linalg import linops from probnum.type import ( ArrayLikeGetitemArgType, FloatArgType, diff --git a/tests/test_linalg/test_linearsolvers/test_linearsolvers.py b/tests/test_linalg/test_linearsolvers/test_linearsolvers.py index f4c49a29c..e9d20cadd 100644 --- a/tests/test_linalg/test_linearsolvers/test_linearsolvers.py +++ b/tests/test_linalg/test_linearsolvers/test_linearsolvers.py @@ -7,9 +7,8 @@ import scipy.sparse.linalg import probnum -from probnum import linalg +from probnum import linalg, linops from probnum import random_variables as rvs -from probnum.linalg import linops from tests.testing import NumpyAssertions @@ -383,7 +382,8 @@ def callback_iterates_CG(xk): cov=linops.SymmetricKronecker(A=linops.Identity(A.shape[1])), ) A0 = rvs.Normal( - mean=linops.Identity(A.shape[1]), cov=linops.SymmetricKronecker(A) + mean=linops.Identity(A.shape[1]), + cov=linops.SymmetricKronecker(A), ) for kwargs in [{"assume_A": "sympos", "rtol": 10 ** -6}]: with self.subTest(): diff --git a/tests/test_linalg/test_linops/__init__.py b/tests/test_linops/__init__.py similarity index 100% rename from tests/test_linalg/test_linops/__init__.py rename to tests/test_linops/__init__.py diff --git a/tests/test_linalg/test_linops/test_kronecker.py b/tests/test_linops/test_kronecker.py similarity index 99% rename from tests/test_linalg/test_linops/test_kronecker.py rename to tests/test_linops/test_kronecker.py index d32232d79..87ffbaf65 100644 --- a/tests/test_linalg/test_linops/test_kronecker.py +++ b/tests/test_linops/test_kronecker.py @@ -4,7 +4,7 @@ import numpy as np -from probnum.linalg import linops +from probnum import linops from tests.testing import NumpyAssertions diff --git a/tests/test_linalg/test_linops/test_linearoperators.py b/tests/test_linops/test_linearoperators.py similarity index 99% rename from tests/test_linalg/test_linops/test_linearoperators.py rename to tests/test_linops/test_linearoperators.py index b49b504c6..8933a5472 100644 --- a/tests/test_linalg/test_linops/test_linearoperators.py +++ b/tests/test_linops/test_linearoperators.py @@ -5,7 +5,7 @@ import numpy as np import scipy.sparse -from probnum.linalg import linops +from probnum import linops from tests.testing import NumpyAssertions diff --git a/tests/test_random_variables/test_normal.py b/tests/test_random_variables/test_normal.py index 7e499d59a..8a9741fca 100644 --- a/tests/test_random_variables/test_normal.py +++ b/tests/test_random_variables/test_normal.py @@ -7,8 +7,8 @@ import scipy.stats import probnum +from probnum import linops from probnum import random_variables as rvs -from probnum.linalg import linops from tests.testing import NumpyAssertions @@ -223,7 +223,9 @@ def test_symmetric_samples(self): A = np.random.uniform(size=(n, n)) A = 0.5 * (A + A.T) + n * np.eye(n) rv = rvs.Normal( - mean=np.eye(A.shape[0]), cov=linops.SymmetricKronecker(A=A), random_state=1 + mean=np.eye(A.shape[0]), + cov=linops.SymmetricKronecker(A=A), + random_state=1, ) rv = rv.sample(size=10) for i, B in enumerate(rv): diff --git a/tests/test_random_variables/test_random_variable.py b/tests/test_random_variables/test_random_variable.py index d416b1419..5fba46f91 100644 --- a/tests/test_random_variables/test_random_variable.py +++ b/tests/test_random_variables/test_random_variable.py @@ -7,8 +7,8 @@ import scipy.stats import probnum +from probnum import linops from probnum import random_variables as rvs -from probnum.linalg import linops from tests.testing import NumpyAssertions