diff --git a/.github/workflows/oldest-support.yaml b/.github/workflows/oldest-support.yaml new file mode 100644 index 00000000..7f68aeb6 --- /dev/null +++ b/.github/workflows/oldest-support.yaml @@ -0,0 +1,40 @@ +# This should mirror regression tests. Ideally we find a way in the future to fold the two together + +name: Test Oldest Dependencies + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + UV_SYSTEM_PYTHON: 1 + +jobs: + test-oldest: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Use uv since it has easy lowest resolution + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.9' # Find a way to globally define our minimum python once + + - name: Install dependencies with minimum versions + run: | + uv sync --resolution lowest-direct --extra dev + + - name: Run tests + run: | + uv run --resolution lowest-direct pytest ./tests \ No newline at end of file diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index bf95a30b..aded3e15 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -34,9 +34,6 @@ jobs: run: | python -m pip install --upgrade coverage coveralls sphinx_rtd_theme pip install ".[dev]" - - name: Check auto-formatters - run: | - black --check . - name: Run tests run: | coverage run --source pyttb -m pytest tests/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35c904e0..bdc356f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,12 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.284 + rev: v0.7.2 hooks: - id: ruff args: [ --fix, --exit-non-zero-on-fix ] - - repo: https://github.com/psf/black - rev: 24.1.1 - hooks: - - id: black - language_version: python + - id: ruff-format - repo: https://github.com/kynan/nbstripout - rev: 0.6.1 + rev: 0.8.0 hooks: - id: nbstripout args: [ diff --git a/CHANGELOG.md b/CHANGELOG.md index b3486022..39c33161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,7 +135,7 @@ # v1.3.3 (2022-07-11) - Fixed indexing bug in `tensor.mttkrp` (Issue #35, PR #36) - Updated LICENSE to compliant format (Issue #33 , PR #34) -- Now using [coveralls.io](https://coveralls.io/github/sandialabs/pyttb) for coverage reporting +- Now using [coveralls.io](https://coveralls.io/github/sandialabs/pyttb?branch=main) for coverage reporting - Now using [readthedocs.io](https://pyttb.readthedocs.io/en/latest/) for documentation # v1.3.2 (2022-07-06) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 710d8a82..4aaf4c8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ current or filing a new [issue](https://github.com/sandialabs/pyttb/issues). 1. Run autoformatters and linting from root of project (they will change your code) ```commandline ruff check . --fix - black . + ruff format ``` 1. Ruff's `--fix` won't necessarily address everything and may point out issues that need manual attention 1. [We](./.pre-commit-config.yaml) optionally support [pre-commit hooks](https://pre-commit.com/) for this @@ -52,15 +52,15 @@ current or filing a new [issue](https://github.com/sandialabs/pyttb/issues). 1. Run tests (at desired fidelity) 1. Just doctests (enabled by default) ```commandline - pytest + pytest . ``` 1. Functional tests ```commandline - pytest . + pytest tests ``` 1. With coverage ```commandline - pytest . --cov=pyttb --cov-report=term-missing + pytest tests --cov=pyttb --cov-report=term-missing ``` 1. (Optionally) Building documentation and tutorials diff --git a/pyproject.toml b/pyproject.toml index 3b0e8b09..590c0a9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,10 @@ readme = "README.md" requires-python = ">=3.9" dependencies = [ - "numpy", - "numpy_groupies", - "scipy", - "matplotlib", + "numpy<3.0,>=1.24", + "numpy_groupies>0.11", + "scipy>1.9", + "matplotlib>3.7", ] classifiers = [ @@ -32,18 +32,19 @@ documentation = "https://pyttb.readthedocs.io" [project.optional-dependencies] dev = [ - "black[jupyter]", - "mypy", - "nbstripout", - "pytest", - "pytest-cov", - "ruff==0.0.284", - "pre-commit", + "mypy>=1.10,<1.14.0", + # Also in pre-commit + "nbstripout>=0.8,<0.9", + "pytest>8.0", + "pytest-cov>5.0", + # Also in pre-commit + "ruff>=0.7,<0.8", + "pre-commit>=4.0,<5.0", ] doc = [ "sphinx >= 4.0", - "sphinx_rtd_theme", - "myst-nb", + "sphinx_rtd_theme>2.0", + "myst-nb>1.0", ] profiling = [ "gprof2dot", @@ -65,7 +66,7 @@ version = {attr = "pyttb.__version__"} requires = ["setuptools>=61.0", "numpy", "numpy_groupies", "scipy", "wheel"] build-backend = "setuptools.build_meta" -[tool.ruff] +[tool.ruff.lint] select = ["E", "F", "PL", "W", "I", "N", "NPY", "RUF", "B"] ignore = [ # Ignored in conversion to ruff since not previously enforced @@ -83,13 +84,18 @@ ignore = [ # There is ongoing discussion about logging/warning etc "B028", ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # See see https://github.com/astral-sh/ruff/issues/3172 for details on this becoming simpler # Everything but I, F (to catch import mess and potential logic errors) "tests/**.py" = ["E", "PL", "W", "N", "NPY", "RUF", "B"] # Ignore everything for now "docs/**.py" = ["E", "F", "PL", "W", "I", "N", "NPY", "RUF", "B"] +"docs/**.ipynb" = ["E", "F", "PL", "W", "I", "N", "NPY", "RUF", "B"] +"profiling/**.ipynb" = ["E", "F", "PL", "W", "I", "N", "NPY", "RUF", "B"] + +[tool.ruff.format] +docstring-code-format = true [tool.mypy] warn_unused_configs = true diff --git a/pyttb/cp_apr.py b/pyttb/cp_apr.py index 62210f9d..293b60a7 100644 --- a/pyttb/cp_apr.py +++ b/pyttb/cp_apr.py @@ -1138,7 +1138,7 @@ def tt_cp_apr_pqnr( # noqa: PLR0912,PLR0913,PLR0915 @overload -def tt_calcpi_prowsubprob( # noqa: PLR0913 +def tt_calcpi_prowsubprob( Data: ttb.sptensor, Model: ttb.ktensor, rank: int, @@ -1150,7 +1150,7 @@ def tt_calcpi_prowsubprob( # noqa: PLR0913 @overload -def tt_calcpi_prowsubprob( # noqa: PLR0913 +def tt_calcpi_prowsubprob( Data: ttb.tensor, Model: ttb.ktensor, rank: int, diff --git a/pyttb/gcp/fg_est.py b/pyttb/gcp/fg_est.py index 722b2d85..32ccd008 100644 --- a/pyttb/gcp/fg_est.py +++ b/pyttb/gcp/fg_est.py @@ -17,7 +17,7 @@ @overload -def estimate( # noqa: PLR0913 +def estimate( model: ttb.ktensor, data_subs: np.ndarray, data_vals: np.ndarray, @@ -30,7 +30,7 @@ def estimate( # noqa: PLR0913 @overload -def estimate( # noqa: PLR0913 +def estimate( model: ttb.ktensor, data_subs: np.ndarray, data_vals: np.ndarray, @@ -43,7 +43,7 @@ def estimate( # noqa: PLR0913 @overload -def estimate( # noqa: PLR0913 +def estimate( model: ttb.ktensor, data_subs: np.ndarray, data_vals: np.ndarray, diff --git a/pyttb/hosvd.py b/pyttb/hosvd.py index fffabb87..c26bca06 100644 --- a/pyttb/hosvd.py +++ b/pyttb/hosvd.py @@ -46,7 +46,7 @@ def hosvd( # noqa: PLR0912,PLR0913,PLR0915 Example ------- - >>> data = np.array([[29, 39.], [63., 85.]]) + >>> data = np.array([[29, 39.0], [63.0, 85.0]]) >>> tol = 1e-4 >>> disable_printing = -1 >>> tensorInstance = ttb.tensor(data) diff --git a/pyttb/khatrirao.py b/pyttb/khatrirao.py index a11ba689..e5f718e0 100644 --- a/pyttb/khatrirao.py +++ b/pyttb/khatrirao.py @@ -25,13 +25,13 @@ def khatrirao(*matrices: np.ndarray, reverse: bool = False) -> np.ndarray: Examples -------- - >>> A = np.random.normal(size=(5,2)) - >>> B = np.random.normal(size=(5,2)) - >>> _ = khatrirao(A,B) #<-- Khatri-Rao of A and B - >>> _ = khatrirao(B,A,reverse=True) #<-- same thing as above - >>> _ = khatrirao(A,A,B) #<-- passing multiple items - >>> _ = khatrirao(B,A,A,reverse = True) #<-- same as above - >>> _ = khatrirao(*[A,A,B]) #<-- passing a list via unpacking items + >>> A = np.random.normal(size=(5, 2)) + >>> B = np.random.normal(size=(5, 2)) + >>> _ = khatrirao(A, B) # <-- Khatri-Rao of A and B + >>> _ = khatrirao(B, A, reverse=True) # <-- same thing as above + >>> _ = khatrirao(A, A, B) # <-- passing multiple items + >>> _ = khatrirao(B, A, A, reverse=True) # <-- same as above + >>> _ = khatrirao(*[A, A, B]) # <-- passing a list via unpacking items """ # Determine if list of matrices of multiple matrix arguments if len(matrices) == 1 and isinstance(matrices[0], list): diff --git a/pyttb/ktensor.py b/pyttb/ktensor.py index 2660ba0e..5ae1f05c 100644 --- a/pyttb/ktensor.py +++ b/pyttb/ktensor.py @@ -104,9 +104,9 @@ def __init__( Create a :class:`pyttb.ktensor` from weights and a list of factor matrices: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) ktensor of shape (2, 2) @@ -121,8 +121,8 @@ def __init__( Create a :class:`pyttb.ktensor` from a :class:`list` of factor matrices (without providing weights): - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor([fm0, fm1]) >>> print(K) @@ -325,7 +325,7 @@ def from_vector( >>> rank = 2 >>> shape = (2, 3, 4) - >>> data = np.arange(1, rank*sum(shape)+1).astype(float) + >>> data = np.arange(1, rank * sum(shape) + 1).astype(float) >>> K = ttb.ktensor.from_vector(data[:], shape, False) >>> print(K) ktensor of shape (2, 3, 4) @@ -438,9 +438,9 @@ def arrange( -------- Create the initial :class:`pyttb.ktensor`: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) ktensor of shape (2, 2) @@ -454,7 +454,7 @@ def arrange( Arrange the columns of the factor matrices using a permutation: - >>> p = [1,0] + >>> p = [1, 0] >>> K.arrange(permutation=p) >>> print(K) ktensor of shape (2, 2) @@ -470,7 +470,7 @@ def arrange( decreasing order: >>> K.arrange() - >>> print(K) # doctest: +ELLIPSIS + >>> print(K) # doctest: +ELLIPSIS ktensor of shape (2, 2) weights=[89.4427... 27.2029...] factor_matrices[0] = @@ -483,7 +483,7 @@ def arrange( Absorb the weights into the second factor: >>> K.arrange(weight_factor=1) - >>> print(K) # doctest: +ELLIPSIS + >>> print(K) # doctest: +ELLIPSIS ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = @@ -563,7 +563,7 @@ def copy(self) -> ktensor: Create a copy of the :class:`pyttb.ktensor` and change the weights: >>> K2 = K.copy() - >>> K2.weights = np.array([2., 3.]) + >>> K2.weights = np.array([2.0, 3.0]) >>> print(K2) # doctest: +ELLIPSIS ktensor of shape (2, 3, 4) weights=[2. 3.] @@ -613,9 +613,9 @@ def double(self) -> np.ndarray: Examples -------- - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor(factor_matrices, weights) >>> K.double() @@ -649,9 +649,9 @@ def extract( -------- Create a :class:`pyttb.ktensor`: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) ktensor of shape (2, 2) @@ -734,9 +734,9 @@ def fixsigns(self, other: Optional[ktensor] = None) -> ktensor: # noqa: PLR0912 -------- Create a :class:`pyttb.ktensor` with negative large magnitude entries: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> K.factor_matrices[0][1, 1] = -K.factor_matrices[0][1, 1] >>> K.factor_matrices[1][1, 1] = -K.factor_matrices[1][1, 1] @@ -872,9 +872,9 @@ def full(self) -> ttb.tensor: Examples -------- - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) ktensor of shape (2, 2) @@ -885,7 +885,7 @@ def full(self) -> ttb.tensor: factor_matrices[1] = [[5. 6.] [7. 8.]] - >>> print(K.full()) # doctest: +NORMALIZE_WHITESPACE + >>> print(K.full()) # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2) data[:, :] = [[29. 39.] @@ -954,9 +954,9 @@ def to_tenmat( Examples -------- - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) ktensor of shape (2, 2) @@ -967,12 +967,12 @@ def to_tenmat( factor_matrices[1] = [[5. 6.] [7. 8.]] - >>> K.full() # doctest: +NORMALIZE_WHITESPACE + >>> K.full() # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2) data[:, :] = [[29. 39.] [63. 85.]] - >>> K.to_tenmat(np.array([0])) # doctest: +NORMALIZE_WHITESPACE + >>> K.to_tenmat(np.array([0])) # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -1007,7 +1007,7 @@ def innerprod( Examples -------- - >>> K = ttb.ktensor.from_function(np.ones, (2,3,4), 2) + >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> print(K.innerprod(K)) 96.0 """ @@ -1043,7 +1043,7 @@ def isequal(self, other: ttb.ktensor) -> bool: Examples -------- - >>> K1 = ttb.ktensor.from_function(np.ones, (2,3,4), 2) + >>> K1 = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> weights = np.ones((2,)) >>> factor_matrices = [np.ones((2, 2)), np.ones((3, 2)), np.ones((4, 2))] >>> K2 = ttb.ktensor(factor_matrices, weights) @@ -1148,9 +1148,9 @@ def mask(self, W: Union[ttb.tensor, ttb.sptensor]) -> np.ndarray: -------- Create a :class:`pyttb.ktensor`: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> K = ttb.ktensor([fm0, fm1], weights) Create a mask :class:`pyttb.tensor` and extract the elements of the @@ -1261,7 +1261,7 @@ def norm(self) -> float: Examples -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) - >>> K.norm() # doctest: +ELLIPSIS + >>> K.norm() # doctest: +ELLIPSIS 9.79795897... """ # Compute the matrix of correlation coefficients @@ -1304,7 +1304,7 @@ def normalize( Examples -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) - >>> print(K.normalize()) # doctest: +ELLIPSIS + >>> print(K.normalize()) # doctest: +ELLIPSIS ktensor of shape (2, 3, 4) weights=[4.898... 4.898...] factor_matrices[0] = @@ -1405,14 +1405,14 @@ def nvecs(self, n: int, r: int, flipsign: bool = True) -> np.ndarray: >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> nvecs1 = K.nvecs(0, 1) - >>> print(nvecs1) # doctest: +ELLIPSIS + >>> print(nvecs1) # doctest: +ELLIPSIS [[0.70710678...] [0.70710678...]] Compute first 2 leading eigenvectors for dimension 0: >>> nvecs2 = K.nvecs(0, 2) - >>> print(nvecs2) # doctest: +ELLIPSIS + >>> print(nvecs2) # doctest: +ELLIPSIS [[ 0.70710678... 0.70710678...] [ 0.70710678... -0.70710678...]] """ @@ -1463,9 +1463,9 @@ def permute(self, order: np.ndarray) -> ktensor: Examples -------- - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) @@ -1515,9 +1515,9 @@ def redistribute(self, mode: int) -> ktensor: ------- Create a :class:`pyttb.ktensor`: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) @@ -1621,13 +1621,13 @@ def score( Create two :class:`pyttb.ktensor` instances and compute the score between them: - >>> factors = [np.ones((3,3)), np.ones((4,3)), np.ones((5,3))] - >>> weights = np.array([2., 1., 3.]) + >>> factors = [np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3))] + >>> weights = np.array([2.0, 1.0, 3.0]) >>> K = ttb.ktensor(factors, weights) - >>> factors_2 = [np.ones((3,2)), np.ones((4,2)), np.ones((5,2))] - >>> weights_2 = np.array([2., 4.]) + >>> factors_2 = [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))] + >>> weights_2 = np.array([2.0, 4.0]) >>> K2 = ttb.ktensor(factors_2, weights_2) - >>> score,Kperm,flag,perm = K.score(K2) + >>> score, Kperm, flag, perm = K.score(K2) >>> print(score) 0.875 >>> print(perm) @@ -1635,7 +1635,7 @@ def score( Compute score without using weights: - >>> score,Kperm,flag,perm = K.score(K2,weight_penalty=False) + >>> score, Kperm, flag, perm = K.score(K2, weight_penalty=False) >>> print(score) 1.0 >>> print(perm) @@ -1735,9 +1735,9 @@ def symmetrize(self) -> ktensor: -------- Create a :class:`pyttb.ktensor`: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) @@ -1754,7 +1754,7 @@ def symmetrize(self) -> ktensor: respect to any permutation of the factor matrices: >>> K1 = K.symmetrize() - >>> print(K1) # doctest: +ELLIPSIS + >>> print(K1) # doctest: +ELLIPSIS ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = @@ -1815,9 +1815,9 @@ def tolist(self, mode: Optional[int] = None) -> List[np.ndarray]: -------- Create a :class:`pyttb.ktensor` of all ones: - >>> weights = np.array([1., 2.]) - >>> fm0 = np.array([[1., 2.], [3., 4.]]) - >>> fm1 = np.array([[5., 6.], [7., 8.]]) + >>> weights = np.array([1.0, 2.0]) + >>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]]) + >>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> factor_matrices = [fm0, fm1] >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) @@ -1834,7 +1834,8 @@ def tolist(self, mode: Optional[int] = None) -> List[np.ndarray]: matrices: >>> fm_list = K.tolist() - >>> for fm in fm_list: print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + >>> for fm in fm_list: + ... print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE [[1. 2.8284...] [3. 5.6568...]] [[ 5. 8.4852...] @@ -1844,7 +1845,8 @@ def tolist(self, mode: Optional[int] = None) -> List[np.ndarray]: matrices: >>> fm_list = K.tolist(0) - >>> for fm in fm_list: print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + >>> for fm in fm_list: + ... print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE [[ 8.6023... 40. ] [25.8069... 80. ]] [[0.5812... 0.6...] @@ -2004,11 +2006,11 @@ def ttv( >>> rank = 2 >>> shape = (2, 3, 4) - >>> data = np.arange(1, rank*sum(shape)+1) + >>> data = np.arange(1, rank * sum(shape) + 1) >>> weights = 2 * np.ones(rank) >>> weights_and_data = np.concatenate((weights, data), axis=0) >>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True) - >>> K0 = K.ttv(np.array([1, 1, 1]),dims=1) # compute along a single dimension + >>> K0 = K.ttv(np.array([1, 1, 1]), dims=1) # compute along a single dimension >>> print(K0) ktensor of shape (2, 4) weights=[36. 54.] @@ -2034,7 +2036,7 @@ def ttv( Compute the product of a :class:`pyttb.ktensor` and multiple vectors out of order (results in a :class:`pyttb.ktensor`): - >>> K2 = K.ttv([vec4, vec3],np.array([2, 1])) + >>> K2 = K.ttv([vec4, vec3], np.array([2, 1])) >>> print(K2) ktensor of shape (2,) weights=[1800. 3564.] @@ -2297,27 +2299,31 @@ def vis( # noqa: PLR0912, PLR0913 Use plot K using default behavior K.vis() - >>> fig, axs = K.vis() # doctest: +ELLIPSIS + >>> fig, axs = K.vis() # doctest: +ELLIPSIS >>> plt.close(fig) Define a more realistic plot fuctions with x labels, control relative widths of each plot, and set mode titles. - >>> def mode_1_plot(v,ax): - ... ax.bar([1,2],v,width=0.2) - ... ax.set_xticks([1,2],labels=['neutron','electron'],rotation=45) - >>> def mode_2_plot(v,ax): - ... ax.plot(np.arange(v.shape[0]), v) - ... ax.set_xlabel('$v$, [m/s]') - >>> def mode_3_plot(v,ax): - ... ax.semilogx(np.logspace(-2,2,v.shape[0]),v) - ... ax.set_xlabel('$E$, [kJ]') + >>> def mode_1_plot(v, ax): + ... ax.bar([1, 2], v, width=0.2) + ... ax.set_xticks([1, 2], labels=["neutron", "electron"], rotation=45) + >>> def mode_2_plot(v, ax): + ... ax.plot(np.arange(v.shape[0]), v) + ... ax.set_xlabel("$v$, [m/s]") + >>> def mode_3_plot(v, ax): + ... ax.semilogx(np.logspace(-2, 2, v.shape[0]), v) + ... ax.set_xlabel("$E$, [kJ]") >>> plots = [mode_1_plot, mode_2_plot, mode_3_plot] - >>> fig, axs = K.vis(plots=plots, - ... rel_widths=[1,2,3],horz_space=0.4, - ... left_space=0.2,bot_space=0.2, - ... mode_titles=['Particle','Velocity','Energy']) # doctest: +ELLIPSIS + >>> fig, axs = K.vis( + ... plots=plots, + ... rel_widths=[1, 2, 3], + ... horz_space=0.4, + ... left_space=0.2, + ... bot_space=0.2, + ... mode_titles=["Particle", "Velocity", "Energy"], + ... ) # doctest: +ELLIPSIS >>> plt.close(fig) """ diff --git a/pyttb/pyttb_utils.py b/pyttb/pyttb_utils.py index 3a5a1f73..3fb2fcda 100644 --- a/pyttb/pyttb_utils.py +++ b/pyttb/pyttb_utils.py @@ -42,9 +42,9 @@ def tt_union_rows(MatrixA: np.ndarray, MatrixB: np.ndarray) -> np.ndarray: Examples -------- - >>> a = np.array([[1,2],[3,4]]) - >>> b = np.array([[0,0],[1,2],[3,4],[0,0]]) - >>> tt_union_rows(a,b) + >>> a = np.array([[1, 2], [3, 4]]) + >>> b = np.array([[0, 0], [1, 2], [3, 4], [0, 0]]) + >>> tt_union_rows(a, b) array([[0, 0], [1, 2], [3, 4]]) @@ -312,11 +312,11 @@ def tt_intersect_rows(MatrixA: np.ndarray, MatrixB: np.ndarray) -> np.ndarray: Examples -------- - >>> a = np.array([[1,2],[3,4]]) - >>> b = np.array([[0,0],[1,2],[3,4],[0,0]]) - >>> tt_intersect_rows(a,b) + >>> a = np.array([[1, 2], [3, 4]]) + >>> b = np.array([[0, 0], [1, 2], [3, 4], [0, 0]]) + >>> tt_intersect_rows(a, b) array([0, 1]) - >>> tt_intersect_rows(b,a) + >>> tt_intersect_rows(b, a) array([1, 2]) """ # TODO ismember and unique are very similar in function @@ -481,8 +481,8 @@ def tt_ismember_rows( Examples -------- >>> a = np.array([[4, 6], [1, 9], [2, 6]]) - >>> b = np.array([[2, 6],[2, 1],[2, 4],[4, 6],[4, 7],[5, 9],[5, 2],[5, 1]]) - >>> matched, results = tt_ismember_rows(a,b) + >>> b = np.array([[2, 6], [2, 1], [2, 4], [4, 6], [4, 7], [5, 9], [5, 2], [5, 1]]) + >>> matched, results = tt_ismember_rows(a, b) >>> print(results) [ 3 -1 0] >>> print(matched) diff --git a/pyttb/sptenmat.py b/pyttb/sptenmat.py index 9d7a5908..196f06b3 100644 --- a/pyttb/sptenmat.py +++ b/pyttb/sptenmat.py @@ -229,12 +229,12 @@ def copy(self) -> sptenmat: Create a :class:`pyttb.sptenmat` (ST1) and make a deep copy. Verify the deep copy (ST3) is not just a reference (like ST2) to the original. - >>> S1 = ttb.sptensor(shape=(2,2)) - >>> S1[0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2)) + >>> S1[0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> ST2 = ST1 >>> ST3 = ST1.copy() - >>> ST1[0,0] = 3 + >>> ST1[0, 0] = 3 >>> ST1.to_sptensor().isequal(ST2.to_sptensor()) True >>> ST1.to_sptensor().isequal(ST3.to_sptensor()) @@ -258,18 +258,18 @@ def to_sptensor(self) -> ttb.sptensor: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 - >>> S1 # doctest: +NORMALIZE_WHITESPACE + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 + >>> S1 # doctest: +NORMALIZE_WHITESPACE sparse tensor of shape (2, 2, 2) with 1 nonzeros [0, 0, 0] = 1.0 >>> ST1 = S1.to_sptenmat(np.array([0])) - >>> ST1 # doctest: +NORMALIZE_WHITESPACE + >>> ST1 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) [0, 0] = 1.0 - >>> ST1.to_sptensor() # doctest: +NORMALIZE_WHITESPACE + >>> ST1.to_sptensor() # doctest: +NORMALIZE_WHITESPACE sparse tensor of shape (2, 2, 2) with 1 nonzeros [0, 0, 0] = 1.0 """ @@ -294,10 +294,10 @@ def shape(self) -> Tuple[int, ...]: Examples -------- - >>> ttb.sptenmat().shape # empty sptenmat + >>> ttb.sptenmat().shape # empty sptenmat () - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> ST1.shape (2, 4) @@ -315,15 +315,15 @@ def double(self) -> sparse.coo_matrix: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> COO = ST1.double() - >>> type(COO) # doctest: +NORMALIZE_WHITESPACE + >>> type(COO) # doctest: +NORMALIZE_WHITESPACE - >>> COO.nnz # doctest: +NORMALIZE_WHITESPACE + >>> COO.nnz # doctest: +NORMALIZE_WHITESPACE 1 - >>> COO.toarray() # doctest: +NORMALIZE_WHITESPACE + >>> COO.toarray() # doctest: +NORMALIZE_WHITESPACE array([[1., 0., 0., 0.], [0., 0., 0., 0.]]) """ @@ -339,10 +339,10 @@ def full(self) -> ttb.tenmat: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) - >>> ST1.full() # doctest: +NORMALIZE_WHITESPACE + >>> ST1.full() # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) @@ -363,8 +363,8 @@ def nnz(self) -> int: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> ST1.nnz 1 @@ -378,8 +378,8 @@ def norm(self) -> float: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> ST1.norm() 1.0 @@ -392,8 +392,8 @@ def isequal(self, other: sptenmat) -> bool: Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) >>> ST2 = ttb.sptenmat() >>> ST1.isequal(ST2) @@ -419,10 +419,10 @@ def __pos__(self): Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) - >>> +ST1 # doctest: +NORMALIZE_WHITESPACE + >>> +ST1 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -436,10 +436,10 @@ def __neg__(self): Examples -------- - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) - >>> -ST1 # doctest: +NORMALIZE_WHITESPACE + >>> -ST1 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -457,8 +457,8 @@ def __setitem__(self, key, value): # noqa: PLR0912 -------- Create an empty :class:`pyttb.sptenmat`. - >>> ST = ttb.sptenmat(rdims=np.array([0]), tshape=(2,2,2)) - >>> ST # doctest: +NORMALIZE_WHITESPACE + >>> ST = ttb.sptenmat(rdims=np.array([0]), tshape=(2, 2, 2)) + >>> ST # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 4) with 0 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -466,7 +466,7 @@ def __setitem__(self, key, value): # noqa: PLR0912 Insert a new value into it. >>> ST[0, 0] = 1.0 - >>> ST # doctest: +NORMALIZE_WHITESPACE + >>> ST # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -475,7 +475,7 @@ def __setitem__(self, key, value): # noqa: PLR0912 Update an existing value in it. >>> ST[0, 0] = 2.0 - >>> ST # doctest: +NORMALIZE_WHITESPACE + >>> ST # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -547,14 +547,14 @@ def __repr__(self): Examples -------- - >>> ttb.sptenmat() # doctest: +NORMALIZE_WHITESPACE + >>> ttb.sptenmat() # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape () with 0 nonzeros rdims = [ ] (modes of sptensor corresponding to rows) cdims = [ ] (modes of sptensor corresponding to columns) - >>> S1 = ttb.sptensor(shape=(2,2,2)) - >>> S1[0,0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2, 2)) + >>> S1[0, 0, 0] = 1 >>> ST1 = S1.to_sptenmat(np.array([0])) - >>> ST1 # doctest: +NORMALIZE_WHITESPACE + >>> ST1 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (2, 2, 2) with 1 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) diff --git a/pyttb/sptensor.py b/pyttb/sptensor.py index 0d50b596..c7ffef70 100644 --- a/pyttb/sptensor.py +++ b/pyttb/sptensor.py @@ -116,7 +116,7 @@ def __init__( >>> subs = np.array([[1, 2, 1], [1, 3, 1]]) >>> vals = np.array([[6], [7]]) - >>> S = ttb.sptensor(subs,vals, shape) + >>> S = ttb.sptensor(subs, vals, shape) >>> S sparse tensor of shape (4, 4, 4) with 2 nonzeros [1, 2, 1] = 6 @@ -273,7 +273,7 @@ def from_aggregator( >>> subs = np.array([[1, 2], [1, 3], [1, 3]]) >>> vals = np.array([[6], [7], [8]]) >>> shape = (4, 4) - >>> S = ttb.sptensor.from_aggregator(subs,vals) + >>> S = ttb.sptensor.from_aggregator(subs, vals) >>> print(S) sparse tensor of shape (2, 4) with 2 nonzeros [1, 2] = 6 @@ -282,7 +282,7 @@ def from_aggregator( Create another :class:`pyttb.sptensor` but specify the shape explicitly. - >>> S = ttb.sptensor.from_aggregator(subs,vals,shape) + >>> S = ttb.sptensor.from_aggregator(subs, vals, shape) >>> print(S) sparse tensor of shape (4, 4) with 2 nonzeros [1, 2] = 6 @@ -291,7 +291,9 @@ def from_aggregator( Create another :class:`pyttb.sptensor` but aggregate using the mean of values corresponding to duplicate subscripts. - >>> S3 = ttb.sptensor.from_aggregator(subs,vals,shape,function_handle=np.mean) + >>> S3 = ttb.sptensor.from_aggregator( + ... subs, vals, shape, function_handle=np.mean + ... ) >>> print(S3) sparse tensor of shape (4, 4) with 2 nonzeros [1, 2] = 6.0 @@ -352,14 +354,14 @@ def copy(self) -> sptensor: Create a :class:`pyttb.sptensor` (S1) and make a deep copy. Verify the deep copy (S3) is not just a reference (like S2) to the original. - >>> S1 = ttb.sptensor(shape=(2,2)) - >>> S1[0,0] = 1 + >>> S1 = ttb.sptensor(shape=(2, 2)) + >>> S1[0, 0] = 1 >>> S2 = S1 >>> S3 = S1.copy() - >>> S1[0,0] = 3 - >>> S1[0,0] == S2[0,0] + >>> S1[0, 0] = 3 + >>> S1[0, 0] == S2[0, 0] True - >>> S1[0,0] == S3[0,0] + >>> S1[0, 0] == S3[0, 0] False """ return ttb.sptensor(self.subs, self.vals, self.shape, copy=True) @@ -375,7 +377,7 @@ def allsubs(self) -> np.ndarray: -------- Create an empty :class:`pyttb.sptensor` and generate all subscripts: - >>> S = ttb.sptensor(shape=(2,2)) + >>> S = ttb.sptensor(shape=(2, 2)) >>> S.allsubs() array([[0, 0], [0, 1], @@ -418,7 +420,7 @@ def collapse( Create a :class:`pyttb.sptensor` with two elements: >>> subs = np.array([[0, 0, 0], [0, 1, 0]]) - >>> vals = np.array([[6.], [7.]]) + >>> vals = np.array([[6.0], [7.0]]) >>> shape = (1, 2, 1) >>> S = ttb.sptensor(subs, vals, shape) @@ -438,7 +440,7 @@ def collapse( Collapse across all but one dimension, resulting in a :class:`numpy.ndarray`: - >>> S.collapse(dims=np.array([0,2])) + >>> S.collapse(dims=np.array([0, 2])) array([6., 7.]) """ if dims is None: @@ -503,11 +505,11 @@ def contract(self, i_0: int, i_1: int) -> Union[np.ndarray, sptensor, ttb.tensor Create a :class:`pyttb.sptensor` and contract, resulting in a :class:`pyttb.sptensor` since the result is sparse: - >>> subs = np.array([[1, 1, 1],[2, 2, 2]]) - >>> vals = np.array([[0.5],[1.5]]) - >>> shape=(4, 4, 4) + >>> subs = np.array([[1, 1, 1], [2, 2, 2]]) + >>> vals = np.array([[0.5], [1.5]]) + >>> shape = (4, 4, 4) >>> S = ttb.sptensor(subs, vals, shape) - >>> S.contract(1,2) + >>> S.contract(1, 2) sparse tensor of shape (4,) with 2 nonzeros [1] = 0.5 [2] = 1.5 @@ -560,7 +562,7 @@ def double(self) -> np.ndarray: :class:`numpy.ndarray`: >>> S = ttb.sptensor() - >>> S[0,1] = 1.5 + >>> S[0, 1] = 1.5 >>> S sparse tensor of shape (1, 2) with 1 nonzeros [0, 1] = 1.5 @@ -589,8 +591,8 @@ def elemfun(self, function_handle: Callable[[np.ndarray], np.ndarray]) -> sptens by 2: >>> S1 = ttb.sptensor() - >>> S1[2,2,2] = 1.5 - >>> S2 = S1.elemfun(lambda values: values*2) + >>> S1[2, 2, 2] = 1.5 + >>> S2 = S1.elemfun(lambda values: values * 2) >>> S2 sparse tensor of shape (3, 3, 3) with 1 nonzeros [2, 2, 2] = 3.0 @@ -649,7 +651,7 @@ def find(self) -> Tuple[np.ndarray, np.ndarray]: Examples -------- >>> S = ttb.sptensor() - >>> S[0,1] = 1 + >>> S[0, 1] = 1 >>> S.find() (array([[0, 1]]), array([[1.]])) """ @@ -671,7 +673,7 @@ def full(self) -> ttb.tensor: :class:`pyttb.tensor`: >>> S = ttb.sptensor() - >>> S[1,1] = 1 + >>> S[1, 1] = 1 >>> S.to_tensor() tensor of shape (2, 2) data[:, :] = @@ -755,7 +757,7 @@ def to_sptenmat( result. >>> ST3 = S.to_sptenmat(rdims=np.array([0]), cdims_cyclic="fc") - >>> ST3 # doctest: +NORMALIZE_WHITESPACE + >>> ST3 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (4, 4, 4) with 2 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 1, 2 ] (modes of sptensor corresponding to columns) @@ -765,7 +767,7 @@ def to_sptenmat( Backwards cyclic reverses the order. >>> ST4 = S.to_sptenmat(rdims=np.array([0]), cdims_cyclic="bc") - >>> ST4 # doctest: +NORMALIZE_WHITESPACE + >>> ST4 # doctest: +NORMALIZE_WHITESPACE sptenmat corresponding to a sptensor of shape (4, 4, 4) with 2 nonzeros rdims = [ 0 ] (modes of sptensor corresponding to rows) cdims = [ 2, 1 ] (modes of sptensor corresponding to columns) @@ -827,7 +829,8 @@ def innerprod( Create a :class:`pyttb.sptensor`: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -843,7 +846,7 @@ def innerprod( Compute inner product with rank-1 :class:`pyttb.ktensor` of all ones that is the same shape as `S`: - >>> factor_matrices = [np.ones((s,1)) for s in S.shape] + >>> factor_matrices = [np.ones((s, 1)) for s in S.shape] >>> K = ttb.ktensor(factor_matrices) >>> S.innerprod(K) 3.0 @@ -895,7 +898,8 @@ def isequal(self, other: Union[sptensor, ttb.tensor]) -> bool: Create a :class:`pyttb.sptensor`: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -909,7 +913,7 @@ def isequal(self, other: Union[sptensor, ttb.tensor]) -> bool: Compare with a tensor that should not be equal: - >>> T[0,0] = T[0,0] + 1 + >>> T[0, 0] = T[0, 0] + 1 >>> S.isequal(T) False """ @@ -935,7 +939,8 @@ def logical_and(self, other: Union[float, sptensor, ttb.tensor]) -> sptensor: Create a :class:`pyttb.sptensor`: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -945,7 +950,7 @@ def logical_and(self, other: Union[float, sptensor, ttb.tensor]) -> sptensor: nonzero pattern but different values: >>> T = S.to_tensor() - >>> T[0,0] = T[0,0] + 1 + >>> T[0, 0] = T[0, 0] + 1 >>> S.logical_and(T) sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -999,7 +1004,8 @@ def logical_not(self) -> sptensor: Create a :class:`pyttb.sptensor` and compute logical NOT: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -1041,7 +1047,8 @@ def logical_or( Create a :class:`pyttb.sptensor` and compute logical OR with itself: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S.logical_or(S) sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -1111,7 +1118,8 @@ def logical_xor( Create a :class:`pyttb.sptensor` and compute logical XOR with itself: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S.logical_xor(S) empty sparse tensor of shape (2, 2) @@ -1119,7 +1127,7 @@ def logical_xor( nonzero pattern: >>> T = S.to_tensor() - >>> T[1,0] = 1.0 + >>> T[1, 0] = 1.0 >>> S.logical_xor(T) tensor of shape (2, 2) data[:, :] = @@ -1169,7 +1177,8 @@ def mask(self, W: sptensor) -> np.ndarray: Create a :class:`pyttb.sptensor`: >>> S = ttb.sptensor() - >>> S[0,0] = 1; S[1,1] = 2 + >>> S[0, 0] = 1 + >>> S[1, 1] = 2 >>> S sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 1.0 @@ -1179,7 +1188,8 @@ def mask(self, W: sptensor) -> np.ndarray: from `S`: >>> W = ttb.sptensor() - >>> W[0,0] = 1; W[1,1] = 1; + >>> W[0, 0] = 1 + >>> W[1, 1] = 1 >>> S.mask(W) array([[1.], [2.]]) @@ -1188,7 +1198,8 @@ def mask(self, W: sptensor) -> np.ndarray: values and some zero values: >>> W = ttb.sptensor() - >>> W[0,0] = 1; W[1,0] = 1; + >>> W[0, 0] = 1 + >>> W[1, 0] = 1 >>> S.mask(W) array([[1.], [0.]]) @@ -1299,7 +1310,7 @@ def ndims(self) -> int: -------- Create a :class:`pyttb.sptensor` and return the number of dimensions: - >>> S = ttb.sptensor(shape=(1,2,3,4,5,6)) + >>> S = ttb.sptensor(shape=(1, 2, 3, 4, 5, 6)) >>> S empty sparse tensor of shape (1, 2, 3, 4, 5, 6) >>> S.ndims @@ -1336,14 +1347,14 @@ def norm(self) -> float: Create a :class:`pyttb.sptensor` from a diagonal matrix and compute its norm: - >>> S = ttb.tensor(np.diag([1.,2.,3.,4.])).to_sptensor() + >>> S = ttb.tensor(np.diag([1.0, 2.0, 3.0, 4.0])).to_sptensor() >>> S sparse tensor of shape (4, 4) with 4 nonzeros [0, 0] = 1.0 [1, 1] = 2.0 [2, 2] = 3.0 [3, 3] = 4.0 - >>> S.norm() # doctest: +ELLIPSIS + >>> S.norm() # doctest: +ELLIPSIS 5.47722557... """ return np.linalg.norm(self.vals).item() @@ -1383,14 +1394,14 @@ def nvecs(self, n: int, r: int, flipsign: bool = True) -> np.ndarray: Compute two mode-0 leading eigenvectors of `S`, making sign of largest element of each eigenvector positive (i.e., `flipsign` =True). - >>> S.nvecs(0, 2, flipsign=True) # doctest: +ELLIPSIS + >>> S.nvecs(0, 2, flipsign=True) # doctest: +ELLIPSIS array([[-0.4718..., 0.8816...], [ 0.8816..., 0.4718...]]) Compute the same `nvecs` of `S`, but do not adjust the sign of the largest element of each eigenvector. - >>> S.nvecs(0, 2, flipsign=False) # doctest: +ELLIPSIS + >>> S.nvecs(0, 2, flipsign=False) # doctest: +ELLIPSIS array([[ 0.4718..., -0.8816...], [-0.8816..., -0.4718...]]) """ @@ -1481,7 +1492,7 @@ def permute(self, order: np.ndarray) -> sptensor: Permute the order of the dimensions by reversing them: - >>> S1 = S.permute(np.array((1,0))) + >>> S1 = S.permute(np.array((1, 0))) >>> S1 sparse tensor of shape (2, 2) with 3 nonzeros [0, 0] = 1.0 @@ -1524,7 +1535,7 @@ def reshape( -------- Create a :class:`pyttb.sptensor` from a :class:`pyttb.tensor`: - >>> S = ttb.tensor(np.arange(9)+1, shape=(1, 3, 3)).to_sptensor() + >>> S = ttb.tensor(np.arange(9) + 1, shape=(1, 3, 3)).to_sptensor() >>> S sparse tensor of shape (1, 3, 3) with 9 nonzeros [0, 0, 0] = 1 @@ -1539,7 +1550,7 @@ def reshape( Reshape to a 2-way :class:`pyttb.sptensor`: - >>> S.reshape((1,9)) + >>> S.reshape((1, 9)) sparse tensor of shape (1, 9) with 9 nonzeros [0, 0] = 1 [0, 1] = 2 @@ -1555,7 +1566,7 @@ def reshape( The first two subscripts are reshaped from (1,3) to (3,1) and moved after the remaining subscript (i.e., corresponding to mode 2). - >>> S.reshape(new_shape=(3,1),old_modes=np.array((1,0))) + >>> S.reshape(new_shape=(3, 1), old_modes=np.array((1, 0))) sparse tensor of shape (3, 3, 1) with 9 nonzeros [0, 0, 0] = 1 [0, 1, 0] = 2 @@ -1618,7 +1629,7 @@ def scale( -------- Create a :class:`pyttb.sptensor` from a :class:`pyttb.tensor`: - >>> S = ttb.tensor(np.arange(9)+1, shape=(1, 3, 3)).to_sptensor() + >>> S = ttb.tensor(np.arange(9) + 1, shape=(1, 3, 3)).to_sptensor() >>> S sparse tensor of shape (1, 3, 3) with 9 nonzeros [0, 0, 0] = 1 @@ -1634,7 +1645,7 @@ def scale( Mode 2 is of length 3. Create a scaling factor array of length 3 and scale along mode 2: - >>> scaling_factor = np.array([1,2,3]) + >>> scaling_factor = np.array([1, 2, 3]) >>> S.scale(scaling_factor, np.array([2])) sparse tensor of shape (1, 3, 3) with 9 nonzeros [0, 0, 0] = 1 @@ -1720,14 +1731,14 @@ def squeeze(self) -> Union[sptensor, float]: Create a :class:`pyttb.sptensor` with a single element and squeeze all the dimensions: - >>> S = ttb.sptensor(np.array([[0,0,0,0,0]]), np.array([[3.14]])) + >>> S = ttb.sptensor(np.array([[0, 0, 0, 0, 0]]), np.array([[3.14]])) >>> S.squeeze() 3.14 Create a :class:`pyttb.sptensor` with and interior singleton dimension and squeeze it out: - >>> S = ttb.sptensor(np.array([[0,0,0],[1,0,1]]), np.array([[1.],[2.]])) + >>> S = ttb.sptensor(np.array([[0, 0, 0], [1, 0, 1]]), np.array([[1.0], [2.0]])) >>> S sparse tensor of shape (2, 1, 2) with 2 nonzeros [0, 0, 0] = 1.0 @@ -1773,13 +1784,13 @@ def subdims(self, region: Sequence[Union[int, np.ndarray, slice]]) -> np.ndarray >>> subs = np.array([[1, 1, 1], [1, 1, 3], [2, 2, 2], [2, 3, 2]]) >>> vals = np.array([[0.5], [1.5], [2.5], [3.5]]) >>> shape = (4, 4, 4) - >>> S = ttb.sptensor(subs,vals,shape) + >>> S = ttb.sptensor(subs, vals, shape) Define a region with subscripts 1 in mode 0, 1 in mode 1, and either 1 or 3 in mode 2, then find the location of the subscripts of the `S` for that region: - >>> region = [1, 1, np.array([1,3])] + >>> region = [1, 1, np.array([1, 3])] >>> subs_loc = S.subdims(region) >>> print(subs_loc) [0 1] @@ -1885,7 +1896,7 @@ def ttv( # noqa: PLR0912 Compute the product of `S` with a vector of ones across mode 0. The result is a :class:`pyttb.tensor`: - >>> S.ttv(np.ones(2),0) + >>> S.ttv(np.ones(2), 0) tensor of shape (2,) data[:] = [4. 2.] @@ -1900,7 +1911,7 @@ def ttv( # noqa: PLR0912 Compute the product of `S1` with a vector of ones across mode 1. The result is a :class:`pyttb.sptensor`: - >>> S1.ttv(np.ones(2),1) + >>> S1.ttv(np.ones(2), 1) sparse tensor of shape (2, 2) with 2 nonzeros [0, 0] = 3.0 [1, 1] = 3.0 @@ -1909,7 +1920,7 @@ def ttv( # noqa: PLR0912 dimensions. When all dimensions will be included in the product, `dims` does not need to be specified. The result is a scalar value. - >>> vectors = [(i+1)*np.ones(2) for i in range(len(S1.shape))] + >>> vectors = [(i + 1) * np.ones(2) for i in range(len(S1.shape))] >>> vectors [array([1., 1.]), array([2., 2.]), array([3., 3.])] >>> S1.ttv(vectors) @@ -2006,25 +2017,25 @@ def __getitem__(self, item): # noqa: PLR0912, PLR0915 -------- Create a 3-way :class:`pyttb.sptensor`: - >>> subs = np.array([[3,3,3],[1,1,0],[1,2,1]]) - >>> vals = np.array([[3],[5],[1]]) - >>> shape = (4,4,4) - >>> S = ttb.sptensor(subs,vals,shape) + >>> subs = np.array([[3, 3, 3], [1, 1, 0], [1, 2, 1]]) + >>> vals = np.array([[3], [5], [1]]) + >>> shape = (4, 4, 4) + >>> S = ttb.sptensor(subs, vals, shape) Use a single subscript (Case 1a): - >>> print(S[1,2,1]) + >>> print(S[1, 2, 1]) 1 Use a range of subscripts (Case 1b): - >>> S[3,3,:] + >>> S[3, 3, :] sparse tensor of shape (4,) with 1 nonzeros [3] = 3 Use an array of subscripts (Case 2a): - >>> M = np.array([[1,1,0],[1,1,1]]) + >>> M = np.array([[1, 1, 0], [1, 1, 1]]) >>> print(S[M]) [[5] [0]] @@ -2032,7 +2043,7 @@ def __getitem__(self, item): # noqa: PLR0912, PLR0915 Use linear subscripting, including negative subscript for offsets from the end of the linear subscripts into the sparse tensor data (Case 2b): - >>> print(S[[5,-1]]) + >>> print(S[[5, -1]]) [[5] [3]] """ @@ -2162,12 +2173,12 @@ def __setitem__(self, key, value): -------- Create a 3-way :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(3,4,5)) + >>> S = ttb.sptensor(shape=(3, 4, 5)) Set a single element using subscripts or a tuple: - >>> S[0,0,0] = 1 - >>> S[(0,0,0)] = 1 + >>> S[0, 0, 0] = 1 + >>> S[(0, 0, 0)] = 1 >>> S sparse tensor of shape (3, 4, 5) with 1 nonzeros [0, 0, 0] = 1.0 @@ -2177,7 +2188,7 @@ def __setitem__(self, key, value): Set a range of elements using a single value: - >>> S[0,0,1:3] = 2 + >>> S[0, 0, 1:3] = 2 >>> S sparse tensor of shape (3, 4, 5) with 3 nonzeros [0, 0, 0] = 1.0 @@ -2186,7 +2197,7 @@ def __setitem__(self, key, value): Set a range of elements using a :class:`pyttb.sptensor`: - >>> S[0:1,1:3,3:4] = 3*ttb.tenones((1,2,1)).to_sptensor() + >>> S[0:1, 1:3, 3:4] = 3 * ttb.tenones((1, 2, 1)).to_sptensor() >>> S sparse tensor of shape (3, 4, 5) with 5 nonzeros [0, 0, 0] = 1.0 @@ -2198,7 +2209,7 @@ def __setitem__(self, key, value): Grow the sparse tensor by assigning an element with a subscript outside the current shape: - >>> S[3,4,5] = 4 + >>> S[3, 4, 5] = 4 >>> S sparse tensor of shape (4, 5, 6) with 6 nonzeros [0, 0, 0] = 1.0 @@ -2211,7 +2222,7 @@ def __setitem__(self, key, value): Assign one or more values using an array of subscripts and a vector of values: - >>> S[S.subs] = 5*np.ones((S.vals.shape[0],1)) + >>> S[S.subs] = 5 * np.ones((S.vals.shape[0], 1)) >>> S sparse tensor of shape (4, 5, 6) with 6 nonzeros [0, 0, 0] = 5.0 @@ -2556,8 +2567,8 @@ def __eq__(self, other): Compare the :class:`pyttb.sptensor` to itself, returning all `True` values: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S == S sparse tensor of shape (2, 2) with 4 nonzeros [0, 0] = True @@ -2648,8 +2659,8 @@ def __ne__(self, other): -------- Compare a :class:`pyttb.sptensor` to itself, returning no `True` values: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S != S empty sparse tensor of shape (2, 2) @@ -2745,8 +2756,8 @@ def __sub__(self, other): Subtract a :class:`pyttb.sptensor` from itself, returning a sparse tensor: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S - S empty sparse tensor of shape (2, 2) @@ -2789,8 +2800,8 @@ def __add__(self, other): -------- Add a :class:`pyttb.sptensor` to itself, returning a sparse tensor: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S + S sparse tensor of shape (2, 2) with 1 nonzeros [1, 1] = 2.0 @@ -2817,8 +2828,8 @@ def __pos__(self): -------- Create a :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(2,2,2)) - >>> S[1,1,1] = 1 + >>> S = ttb.sptensor(shape=(2, 2, 2)) + >>> S[1, 1, 1] = 1 >>> S sparse tensor of shape (2, 2, 2) with 1 nonzeros [1, 1, 1] = 1.0 @@ -2839,8 +2850,8 @@ def __neg__(self): -------- Create a :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(2,2,2)) - >>> S[1,1,1] = 1 + >>> S = ttb.sptensor(shape=(2, 2, 2)) + >>> S[1, 1, 1] = 1 >>> S sparse tensor of shape (2, 2, 2) with 1 nonzeros [1, 1, 1] = 1.0 @@ -2866,8 +2877,8 @@ def __mul__(self, other): -------- Multiply a :class:`pyttb.sptensor` by a scalar: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S * 3 sparse tensor of shape (2, 2) with 1 nonzeros [1, 1] = 3.0 @@ -2875,8 +2886,8 @@ def __mul__(self, other): Multiply two sparse tensors with no overlap in subscripts of nonzeros, resulting in an empty sparse tensor: - >>> S2 = ttb.sptensor(shape=(2,2)) - >>> S2[1,0] = 1.0 + >>> S2 = ttb.sptensor(shape=(2, 2)) + >>> S2[1, 0] = 1.0 >>> S * S2 empty sparse tensor of shape (2, 2) """ @@ -2930,8 +2941,8 @@ def __rmul__(self, other): -------- Multiple scalar by a :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> 3 * S sparse tensor of shape (2, 2) with 1 nonzeros [1, 1] = 3.0 @@ -2953,8 +2964,8 @@ def __le__(self, other): # noqa: PLR0912 -------- Compare a :class:`pyttb.sptensor` with itself: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S <= S sparse tensor of shape (2, 2) with 4 nonzeros [1, 1] = 1.0 @@ -3053,8 +3064,8 @@ def __lt__(self, other): # noqa: PLR0912 -------- Compare a :class:`pyttb.sptensor` with itself: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S < S empty sparse tensor of shape (2, 2) @@ -3145,8 +3156,8 @@ def __ge__(self, other): -------- Compare a :class:`pyttb.sptensor` with itself: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S >= S sparse tensor of shape (2, 2) with 4 nonzeros [1, 1] = 1.0 @@ -3217,8 +3228,8 @@ def __gt__(self, other): -------- Compare a :class:`pyttb.sptensor` with itself: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 1.0 >>> S > S empty sparse tensor of shape (2, 2) @@ -3287,10 +3298,10 @@ def __truediv__(self, other): # noqa: PLR0912, PLR0915 -------- Divide a :class:`pyttb.sptensor` by a :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[1,1] = 2.0 - >>> S2 = ttb.sptensor(shape=(2,2)) - >>> S2[1,1] = 4.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[1, 1] = 2.0 + >>> S2 = ttb.sptensor(shape=(2, 2)) + >>> S2[1, 1] = 4.0 >>> S / S2 sparse tensor of shape (2, 2) with 4 nonzeros [1, 1] = 0.5 @@ -3414,8 +3425,8 @@ def __rtruediv__(self, other): -------- Divide a scalar by a :class:`pyttb.sptensor`: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[:,:] = 2.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[:, :] = 2.0 >>> 1 / S tensor of shape (2, 2) data[:, :] = @@ -3435,8 +3446,8 @@ def __repr__(self): # pragma: no cover -------- Create a :class:`pyttb.sptensor` and print it as a string: - >>> S = ttb.sptensor(shape=(2,2)) - >>> S[:,:] = 1.0 + >>> S = ttb.sptensor(shape=(2, 2)) + >>> S[:, :] = 1.0 >>> print(S) sparse tensor of shape (2, 2) with 4 nonzeros [0, 0] = 1.0 @@ -3508,14 +3519,14 @@ def ttm( -------- Create a :class:`pyttb.sptensor` with a region of elements set to 1: - >>> S = ttb.sptensor(shape=(2,2,2,2)) - >>> S[:,0:1,:,0:1] = 1 + >>> S = ttb.sptensor(shape=(2, 2, 2, 2)) + >>> S[:, 0:1, :, 0:1] = 1 Compute the product of `S` with multiple matrices of ones along the first two dimensions, transposing the matrices when multiplying: - >>> A = 2*np.ones((2,1)) - >>> S.ttm([A,A], dims=[0,1], transpose=True) + >>> A = 2 * np.ones((2, 1)) + >>> S.ttm([A, A], dims=[0, 1], transpose=True) tensor of shape (1, 1, 2, 2) data[0, 0, :, :] = [[8. 0.] @@ -3524,7 +3535,7 @@ def ttm( Compute sparse tensor matrix product specifying which two tensor dimensions to exclude in the multiplication: - >>> S.ttm([A,A], exclude_dims=[0,1], transpose=True) + >>> S.ttm([A, A], exclude_dims=[0, 1], transpose=True) tensor of shape (2, 2, 1, 1) data[0, 0, :, :] = [[8.]] @@ -3623,9 +3634,9 @@ def squash( Create a :class:`pyttb.sptensor` with a few entries and squash empty slices: - >>> S = ttb.sptensor(shape=(10,10,10)) - >>> S[0,1,2] = 1 - >>> S[0,1,3] = 2 + >>> S = ttb.sptensor(shape=(10, 10, 10)) + >>> S[0, 1, 2] = 1 + >>> S[0, 1, 3] = 2 >>> S sparse tensor of shape (10, 10, 10) with 2 nonzeros [0, 1, 2] = 1.0 @@ -3684,11 +3695,11 @@ def sptenrand( -------- Create a :class:`pyttb.sptensor`, specifying the number of nonzeros: - >>> S = ttb.sptenrand((2,2), nonzeros=1) + >>> S = ttb.sptenrand((2, 2), nonzeros=1) Create a :class:`pyttb.sptensor`, specifying the density of nonzeros: - >>> S2 = ttb.sptenrand((2,2), density=0.25) + >>> S2 = ttb.sptenrand((2, 2), density=0.25) """ if density is None and nonzeros is None: raise ValueError("Must set either density or nonzeros") diff --git a/pyttb/sumtensor.py b/pyttb/sumtensor.py index e5c35cec..93e68348 100644 --- a/pyttb/sumtensor.py +++ b/pyttb/sumtensor.py @@ -46,8 +46,8 @@ def __init__( ------- Create an empty :class:`pyttb.tensor`: - >>> T1 = ttb.tenones((3,4,5)) - >>> T2 = ttb.sptensor(shape=(3,4,5)) + >>> T1 = ttb.tenones((3, 4, 5)) + >>> T2 = ttb.sptensor(shape=(3, 4, 5)) >>> S = ttb.sumtensor([T1, T2]) """ if tensors is None: @@ -72,14 +72,14 @@ def copy(self) -> sumtensor: Examples -------- - >>> T1 = ttb.tensor(np.ones((3,2))) + >>> T1 = ttb.tensor(np.ones((3, 2))) >>> S1 = ttb.sumtensor([T1, T1]) >>> S2 = S1 >>> S3 = S2.copy() - >>> S1.parts[0][0,0] = 3 - >>> S1.parts[0][0,0] == S2.parts[0][0,0] + >>> S1.parts[0][0, 0] = 3 + >>> S1.parts[0][0, 0] == S2.parts[0][0, 0] True - >>> S1.parts[0][0,0] == S3.parts[0][0,0] + >>> S1.parts[0][0, 0] == S3.parts[0][0, 0] False """ return ttb.sumtensor(self.parts, copy=True) @@ -103,9 +103,9 @@ def __repr__(self): Examples -------- - >>> T1 = ttb.tenones((2,2)) - >>> T2 = ttb.sptensor(shape=(2,2)) - >>> ttb.sumtensor([T1, T2]) # doctest: +NORMALIZE_WHITESPACE + >>> T1 = ttb.tenones((2, 2)) + >>> T2 = ttb.sptensor(shape=(2, 2)) + >>> ttb.sumtensor([T1, T2]) # doctest: +NORMALIZE_WHITESPACE sumtensor of shape (2, 2) with 2 parts: Part 0: tensor of shape (2, 2) @@ -136,7 +136,7 @@ def ndims(self) -> int: Examples -------- >>> T1 = ttb.tenones((2, 2)) - >>> S = ttb.sumtensor([T1,T1]) + >>> S = ttb.sumtensor([T1, T1]) >>> S.ndims 2 """ @@ -192,7 +192,7 @@ def __add__(self, other): Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> S = ttb.sumtensor([T]) >>> len(S.parts) 1 @@ -234,7 +234,7 @@ def __radd__(self, other): Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> S = ttb.sumtensor([T]) >>> len(S.parts) 1 @@ -257,9 +257,9 @@ def full(self) -> ttb.tensor: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> S = ttb.sumtensor([T, T]) - >>> print(S.full()) # doctest: +NORMALIZE_WHITESPACE + >>> print(S.full()) # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2) data[:, :] = [[2. 2.] @@ -281,7 +281,7 @@ def double(self) -> np.ndarray: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> S = ttb.sumtensor([T, T]) >>> S.double() array([[2., 2.], @@ -303,7 +303,7 @@ def innerprod( Examples -------- - >>> T1 = ttb.tensor(np.array([[1., 0.], [0., 4.]])) + >>> T1 = ttb.tensor(np.array([[1.0, 0.0], [0.0, 4.0]])) >>> T2 = T1.to_sptensor() >>> S = ttb.sumtensor([T1, T2]) >>> T1.innerprod(T1) @@ -337,10 +337,10 @@ def mttkrp(self, U: Union[ttb.ktensor, List[np.ndarray]], n: int) -> np.ndarray: Examples -------- - >>> T1 = ttb.tenones((2,2,2)) + >>> T1 = ttb.tenones((2, 2, 2)) >>> T2 = T1.to_sptensor() >>> S = ttb.sumtensor([T1, T2]) - >>> U = [np.ones((2,2))] * 3 + >>> U = [np.ones((2, 2))] * 3 >>> T1.mttkrp(U, 2) array([[4., 4.], [4., 4.]]) @@ -395,11 +395,11 @@ def ttv( -------- >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) >>> S = ttb.sumtensor([T, T]) - >>> T.ttv(np.ones(2),0) + >>> T.ttv(np.ones(2), 0) tensor of shape (2,) data[:] = [4. 6.] - >>> S.ttv(np.ones(2),0) # doctest: +NORMALIZE_WHITESPACE + >>> S.ttv(np.ones(2), 0) # doctest: +NORMALIZE_WHITESPACE sumtensor of shape (2,) with 2 parts: Part 0: tensor of shape (2,) diff --git a/pyttb/tenmat.py b/pyttb/tenmat.py index 2add607f..683f0b6e 100644 --- a/pyttb/tenmat.py +++ b/pyttb/tenmat.py @@ -22,7 +22,7 @@ class tenmat: __slots__ = ("tshape", "rindices", "cindices", "data") - def __init__( # noqa: PLR0912, PLR0913 + def __init__( # noqa: PLR0912 self, data: Optional[np.ndarray] = None, rdims: Optional[np.ndarray] = None, @@ -51,7 +51,7 @@ def __init__( # noqa: PLR0912, PLR0913 -------- Create an empty :class:`pyttb.tenmat`. - >>> ttb.tenmat() # doctest: +NORMALIZE_WHITESPACE + >>> ttb.tenmat() # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape () rindices = [ ] (modes of tensor corresponding to rows) cindices = [ ] (modes of tensor corresponding to columns) @@ -61,7 +61,7 @@ def __init__( # noqa: PLR0912, PLR0913 >>> tshape = (2, 2, 2) >>> data = np.reshape(np.arange(np.prod(tshape), dtype=np.double), tshape) - >>> data # doctest: +NORMALIZE_WHITESPACE + >>> data # doctest: +NORMALIZE_WHITESPACE array([[[0., 1.], [2., 3.]], [[4., 5.], @@ -69,8 +69,8 @@ def __init__( # noqa: PLR0912, PLR0913 Manually matrize the tensor. - >>> flat_data = np.reshape(data, (2,4), order="F") - >>> flat_data # doctest: +NORMALIZE_WHITESPACE + >>> flat_data = np.reshape(data, (2, 4), order="F") + >>> flat_data # doctest: +NORMALIZE_WHITESPACE array([[0., 2., 1., 3.], [4., 6., 5., 7.]]) @@ -80,7 +80,7 @@ def __init__( # noqa: PLR0912, PLR0913 Extract original tensor shaped data. - >>> tm.to_tensor().double() # doctest: +NORMALIZE_WHITESPACE + >>> tm.to_tensor().double() # doctest: +NORMALIZE_WHITESPACE array([[[0., 1.], [2., 3.]], [[4., 5.], @@ -178,17 +178,17 @@ def copy(self) -> tenmat: Create a :class:`pyttb.tenmat` (TM1) and make a deep copy. Verify the deep copy (TM3) is not just a reference (like TM2) to the original. - >>> T1 = ttb.tensor(np.ones((3,2))) + >>> T1 = ttb.tensor(np.ones((3, 2))) >>> TM1 = T1.to_tenmat(np.array([0])) >>> TM2 = TM1 >>> TM3 = TM1.copy() - >>> TM1[0,0] = 3 + >>> TM1[0, 0] = 3 # Item to convert numpy boolean to python boolena for nicer printing - >>> (TM1[0,0] == TM2[0,0]).item() + >>> (TM1[0, 0] == TM2[0, 0]).item() True - >>> (TM1[0,0] == TM3[0,0]).item() + >>> (TM1[0, 0] == TM3[0, 0]).item() False """ # Create tenmat @@ -214,7 +214,7 @@ def to_tensor(self, copy: bool = True) -> ttb.tensor: >>> tshape = (2, 2, 2) >>> data = np.reshape(np.arange(np.prod(tshape), dtype=np.double), tshape) - >>> data # doctest: +NORMALIZE_WHITESPACE + >>> data # doctest: +NORMALIZE_WHITESPACE array([[[0., 1.], [2., 3.]], [[4., 5.], @@ -222,8 +222,8 @@ def to_tensor(self, copy: bool = True) -> ttb.tensor: Manually matrize the tensor. - >>> flat_data = np.reshape(data, (2,4), order="F") - >>> flat_data # doctest: +NORMALIZE_WHITESPACE + >>> flat_data = np.reshape(data, (2, 4), order="F") + >>> flat_data # doctest: +NORMALIZE_WHITESPACE array([[0., 2., 1., 3.], [4., 6., 5., 7.]]) @@ -233,7 +233,7 @@ def to_tensor(self, copy: bool = True) -> ttb.tensor: Extract original tensor shaped data. - >>> tm.to_tensor() # doctest: +NORMALIZE_WHITESPACE + >>> tm.to_tensor() # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2, 2) data[0, :, :] = [[0. 1.] @@ -264,16 +264,16 @@ def ctranspose(self) -> tenmat: -------- Create :class:`pyttb.tensor` then convert to :class:`pyttb.tenmat`. - >>> T = ttb.tenones((2,2,2)) + >>> T = ttb.tenones((2, 2, 2)) >>> TM = T.to_tenmat(rdims=np.array([0])) - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) data[:, :] = [[1. 1. 1. 1.] [1. 1. 1. 1.]] - >>> TM.ctranspose() # doctest: +NORMALIZE_WHITESPACE + >>> TM.ctranspose() # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 1, 2 ] (modes of tensor corresponding to rows) cindices = [ 0 ] (modes of tensor corresponding to columns) @@ -297,16 +297,16 @@ def double(self) -> np.ndarray: Examples -------- - >>> T = ttb.tenones((2,2,2)) + >>> T = ttb.tenones((2, 2, 2)) >>> TM = T.to_tenmat(rdims=np.array([0])) - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) data[:, :] = [[1. 1. 1. 1.] [1. 1. 1. 1.]] - >>> TM.double() # doctest: +NORMALIZE_WHITESPACE + >>> TM.double() # doctest: +NORMALIZE_WHITESPACE array([[1., 1., 1., 1.], [1., 1., 1., 1.]]) @@ -322,11 +322,11 @@ def ndims(self) -> int: Examples -------- - >>> TM = ttb.tenmat() # empty tenmat + >>> TM = ttb.tenmat() # empty tenmat >>> TM.ndims 0 - >>> TM = ttb.tenones((2,2,2)).to_tenmat(np.array([0])) + >>> TM = ttb.tenones((2, 2, 2)).to_tenmat(np.array([0])) >>> TM.ndims 2 """ @@ -338,16 +338,16 @@ def norm(self) -> float: Examples -------- - >>> T = ttb.tenones((2,2,2)) + >>> T = ttb.tenones((2, 2, 2)) >>> TM = T.to_tenmat(rdims=np.array([0])) - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) data[:, :] = [[1. 1. 1. 1.] [1. 1. 1. 1.]] - >>> TM.norm() # doctest: +ELLIPSIS + >>> TM.norm() # doctest: +ELLIPSIS 2.82... """ # default of np.linalg.norm is to vectorize the data and compute the vector @@ -362,11 +362,11 @@ def shape(self) -> Tuple[int, ...]: Examples -------- - >>> TM = ttb.tenmat() # empty tenmat + >>> TM = ttb.tenmat() # empty tenmat >>> TM.shape () - >>> TM = ttb.tenones((2,2,2)).to_tenmat(np.array([0])) + >>> TM = ttb.tenones((2, 2, 2)).to_tenmat(np.array([0])) >>> TM.shape (2, 4) """ @@ -380,8 +380,8 @@ def isequal(self, other: tenmat) -> bool: Examples -------- - >>> TM1 = ttb.tenmat() # empty tenmat - >>> TM2 = ttb.tenones((2,2,2)).to_tenmat(np.array([0])) + >>> TM1 = ttb.tenmat() # empty tenmat + >>> TM2 = ttb.tenones((2, 2, 2)).to_tenmat(np.array([0])) >>> TM1.isequal(TM2) False >>> TM1.isequal(TM1) @@ -404,16 +404,16 @@ def __setitem__(self, key, value): Examples -------- - >>> TM = ttb.tenones((2,2,2)).to_tenmat(np.array([0])) - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2, 2)).to_tenmat(np.array([0])) + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) data[:, :] = [[1. 1. 1. 1.] [1. 1. 1. 1.]] - >>> TM[0, 0] = 2. - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM[0, 0] = 2.0 + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) @@ -429,7 +429,7 @@ def __getitem__(self, item): Examples -------- - >>> TM = ttb.tenones((2,2,2)).to_tenmat(np.array([0])) + >>> TM = ttb.tenones((2, 2, 2)).to_tenmat(np.array([0])) >>> print(TM[0, 0]) 1.0 @@ -449,8 +449,8 @@ def __mul__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> TM * TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> TM * TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -506,8 +506,8 @@ def __rmul__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> TM * TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> TM * TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -531,15 +531,15 @@ def __add__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> TM + TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> TM + TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) data[:, :] = [[2. 2.] [2. 2.]] - >>> TM + 1.0 # doctest: +NORMALIZE_WHITESPACE + >>> TM + 1.0 # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -577,8 +577,8 @@ def __radd__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> 1.0 + TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> 1.0 + TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -602,15 +602,15 @@ def __sub__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> TM - TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> TM - TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) data[:, :] = [[0. 0.] [0. 0.]] - >>> TM - 1.0 # doctest: +NORMALIZE_WHITESPACE + >>> TM - 1.0 # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -648,8 +648,8 @@ def __rsub__(self, other): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> 1.0 - TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> 1.0 - TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -683,8 +683,8 @@ def __pos__(self): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> +TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> +TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -708,8 +708,8 @@ def __neg__(self): Examples -------- - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> -TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> -TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) @@ -736,7 +736,7 @@ def __repr__(self): -------- Print an empty :class:`pyttb.tenmat`. - >>> ttb.tenmat() # doctest: +NORMALIZE_WHITESPACE + >>> ttb.tenmat() # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape () rindices = [ ] (modes of tensor corresponding to rows) cindices = [ ] (modes of tensor corresponding to columns) @@ -744,8 +744,8 @@ def __repr__(self): Print a non-empty :class:`pyttb.tenmat`. - >>> TM = ttb.tenones((2,2)).to_tenmat(np.array([0])) - >>> TM # doctest: +NORMALIZE_WHITESPACE + >>> TM = ttb.tenones((2, 2)).to_tenmat(np.array([0])) + >>> TM # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1 ] (modes of tensor corresponding to columns) diff --git a/pyttb/tensor.py b/pyttb/tensor.py index 1fb48616..fe729fb7 100644 --- a/pyttb/tensor.py +++ b/pyttb/tensor.py @@ -88,7 +88,7 @@ def __init__( Create a :class:`pyttb.tensor` from a :class:`numpy.ndarray`: - >>> T = ttb.tensor(np.array([[1,2],[3,4]])) + >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) >>> print(T) tensor of shape (2, 2) data[:, :] = @@ -194,13 +194,13 @@ def copy(self) -> tensor: Examples -------- - >>> T1 = ttb.tensor(np.ones((3,2))) + >>> T1 = ttb.tensor(np.ones((3, 2))) >>> T2 = T1 >>> T3 = T2.copy() - >>> T1[0,0] = 3 - >>> T1[0,0] == T2[0,0] + >>> T1[0, 0] = 3 + >>> T1[0, 0] == T2[0, 0] True - >>> T1[0,0] == T3[0,0] + >>> T1[0, 0] == T3[0, 0] False """ return ttb.tensor(self.data, self.shape, copy=True) @@ -229,7 +229,7 @@ def collapse( Examples -------- - >>> T = ttb.tensor(np.ones((2,2))) + >>> T = ttb.tensor(np.ones((2, 2))) >>> T.collapse() 4.0 >>> T.collapse(np.array([0])) @@ -291,10 +291,10 @@ def contract(self, i1: int, i2: int) -> Union[np.ndarray, tensor]: Examples -------- - >>> T = ttb.tensor(np.ones((2,2))) + >>> T = ttb.tensor(np.ones((2, 2))) >>> T.contract(0, 1) 2.0 - >>> T = ttb.tensor(np.array([[[1,2],[3,4]],[[5,6],[7,8]]])) + >>> T = ttb.tensor(np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])) >>> print(T) tensor of shape (2, 2, 2) data[0, :, :] = @@ -303,15 +303,15 @@ def contract(self, i1: int, i2: int) -> Union[np.ndarray, tensor]: data[1, :, :] = [[5 6] [7 8]] - >>> T.contract(0,1) + >>> T.contract(0, 1) tensor of shape (2,) data[:] = [ 8. 10.] - >>> T.contract(0,2) + >>> T.contract(0, 2) tensor of shape (2,) data[:] = [ 7. 11.] - >>> T.contract(1,2) + >>> T.contract(1, 2) tensor of shape (2,) data[:] = [ 5. 13.] @@ -365,7 +365,7 @@ def double(self) -> np.ndarray: Examples -------- - >>> T = ttb.tensor(np.ones((2,2))) + >>> T = ttb.tensor(np.ones((2, 2))) >>> T.double() array([[1., 1.], [1., 1.]]) @@ -431,7 +431,7 @@ def to_sptensor(self) -> ttb.sptensor: Examples -------- - >>> T = ttb.tensor(np.array([[0,2],[3,0]])) + >>> T = ttb.tensor(np.array([[0, 2], [3, 0]])) >>> print(T) tensor of shape (2, 2) data[:, :] = @@ -505,7 +505,7 @@ def to_tenmat( >>> tshape = (2, 2, 2) >>> data = np.reshape(np.arange(np.prod(tshape)), tshape) >>> T = ttb.tensor(data) - >>> T # doctest: +NORMALIZE_WHITESPACE + >>> T # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2, 2) data[0, :, :] = [[0 1] @@ -527,7 +527,7 @@ def to_tenmat( result. >>> TM3 = T.to_tenmat(rdims=np.array([0]), cdims_cyclic="fc") - >>> TM3 # doctest: +NORMALIZE_WHITESPACE + >>> TM3 # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 1, 2 ] (modes of tensor corresponding to columns) @@ -538,7 +538,7 @@ def to_tenmat( Backwards cyclic reverses the order. >>> TM4 = T.to_tenmat(rdims=np.array([0]), cdims_cyclic="bc") - >>> TM4 # doctest: +NORMALIZE_WHITESPACE + >>> TM4 # doctest: +NORMALIZE_WHITESPACE matrix corresponding to a tensor of shape (2, 2, 2) rindices = [ 0 ] (modes of tensor corresponding to rows) cindices = [ 2, 1 ] (modes of tensor corresponding to columns) @@ -594,7 +594,7 @@ def innerprod( Examples -------- - >>> T = ttb.tensor(np.array([[1., 0.], [0., 4.]])) + >>> T = ttb.tensor(np.array([[1.0, 0.0], [0.0, 4.0]])) >>> T.innerprod(T) 17.0 >>> S = T.to_sptensor() @@ -623,11 +623,11 @@ def isequal(self, other: Union[tensor, ttb.sptensor]) -> bool: Examples -------- - >>> T1 = ttb.tensor(2 * np.ones((2,2))) - >>> T2 = 2 * ttb.tensor(np.ones((2,2))) + >>> T1 = ttb.tensor(2 * np.ones((2, 2))) + >>> T2 = 2 * ttb.tensor(np.ones((2, 2))) >>> T1.isequal(T2) True - >>> T2[0,0] = 1 + >>> T2[0, 0] = 1 >>> T1.isequal(T2) False """ @@ -774,7 +774,7 @@ def logical_and(self, other: Union[float, tensor]) -> tensor: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> T.logical_and(T).collapse() # All true 4.0 """ @@ -790,7 +790,7 @@ def logical_not(self) -> tensor: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> T.logical_not().collapse() # All false 0.0 """ @@ -808,7 +808,7 @@ def logical_or(self, other: Union[float, tensor]) -> tensor: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> T.logical_or(T.logical_not()).collapse() # All true 4.0 """ @@ -829,7 +829,7 @@ def logical_xor(self, other: Union[float, tensor]) -> tensor: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> T.logical_xor(T.logical_not()).collapse() # All true 4.0 """ @@ -855,7 +855,7 @@ def mask(self, W: tensor) -> np.ndarray: Examples -------- >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) - >>> W = ttb.tenones((2,2)) + >>> W = ttb.tenones((2, 2)) >>> T.mask(W) array([1, 3, 2, 4]) """ @@ -888,8 +888,8 @@ def mttkrp(self, U: Union[ttb.ktensor, List[np.ndarray]], n: int) -> np.ndarray: Examples -------- - >>> T = ttb.tenones((2,2,2)) - >>> U = [np.ones((2,2))] * 3 + >>> T = ttb.tenones((2, 2, 2)) + >>> U = [np.ones((2, 2))] * 3 >>> T.mttkrp(U, 2) array([[4., 4.], [4., 4.]]) @@ -955,8 +955,8 @@ def mttkrps(self, U: Union[ttb.ktensor, List[np.ndarray]]) -> List[np.ndarray]: Examples -------- - >>> T = ttb.tenones((2,2,2)) - >>> U = [np.ones((2,2))] * 3 + >>> T = ttb.tenones((2, 2, 2)) + >>> U = [np.ones((2, 2))] * 3 >>> T.mttkrps(U) [array([[4., 4.], [4., 4.]]), array([[4., 4.], @@ -990,7 +990,7 @@ def ndims(self) -> int: Examples -------- - >>> T = ttb.tenones((2,2)) + >>> T = ttb.tenones((2, 2)) >>> T.ndims 2 """ @@ -1005,7 +1005,7 @@ def nnz(self) -> int: Examples -------- - >>> T = ttb.tenones((2,2,2)) + >>> T = ttb.tenones((2, 2, 2)) >>> T.nnz 8 """ @@ -1018,7 +1018,7 @@ def norm(self) -> float: Examples -------- - >>> T = ttb.tenones((2,2,2,2)) + >>> T = ttb.tenones((2, 2, 2, 2)) >>> T.norm() 4.0 """ @@ -1055,10 +1055,10 @@ def nvecs(self, n: int, r: int, flipsign: bool = True) -> np.ndarray: Examples -------- >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) - >>> T.nvecs(0,1) # doctest: +ELLIPSIS + >>> T.nvecs(0, 1) # doctest: +ELLIPSIS array([[0.4045...], [0.9145...]]) - >>> T.nvecs(0,2) # doctest: +ELLIPSIS + >>> T.nvecs(0, 2) # doctest: +ELLIPSIS array([[ 0.4045..., 0.9145...], [ 0.9145..., -0.4045...]]) """ @@ -1108,7 +1108,7 @@ def permute(self, order: np.ndarray) -> tensor: data[:, :] = [[1 2] [3 4]] - >>> T1.permute(np.array((1,0))) + >>> T1.permute(np.array((1, 0))) tensor of shape (2, 2) data[:, :] = [[1 3] @@ -1139,10 +1139,10 @@ def reshape(self, shape: Tuple[int, ...]) -> tensor: Examples -------- - >>> T1 = ttb.tenones((2,2)) + >>> T1 = ttb.tenones((2, 2)) >>> T1.shape (2, 2) - >>> T2 = T1.reshape((4,1)) + >>> T2 = T1.reshape((4, 1)) >>> T2.shape (4, 1) """ @@ -1261,8 +1261,8 @@ def symmetrize( # noqa: PLR0912,PLR0915 Examples -------- - >>> T = ttb.tenones((2,2,2)) - >>> T.symmetrize(np.array([0,2])) + >>> T = ttb.tenones((2, 2, 2)) + >>> T.symmetrize(np.array([0, 2])) tensor of shape (2, 2, 2) data[0, :, :] = [[1. 1.] @@ -1426,14 +1426,14 @@ def ttm( Examples -------- - >>> T = ttb.tenones((2,2,2,2)) - >>> A = 2*np.ones((2,1)) - >>> T.ttm([A,A], dims=[0,1], transpose=True) + >>> T = ttb.tenones((2, 2, 2, 2)) + >>> A = 2 * np.ones((2, 1)) + >>> T.ttm([A, A], dims=[0, 1], transpose=True) tensor of shape (1, 1, 2, 2) data[0, 0, :, :] = [[16. 16.] [16. 16.]] - >>> T.ttm([A,A], exclude_dims=[0,1], transpose=True) + >>> T.ttm([A, A], exclude_dims=[0, 1], transpose=True) tensor of shape (2, 2, 1, 1) data[0, 0, :, :] = [[16.]] @@ -1625,11 +1625,11 @@ def ttv( Examples -------- >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) - >>> T.ttv(np.ones(2),0) + >>> T.ttv(np.ones(2), 0) tensor of shape (2,) data[:] = [4. 6.] - >>> T.ttv(np.ones(2),1) + >>> T.ttv(np.ones(2), 1) tensor of shape (2,) data[:] = [3. 7.] @@ -1704,9 +1704,9 @@ def ttsv( >>> T = ttb.tensor(np.array([[1, 2], [3, 4]])) >>> T.ttsv(np.ones(2)) 10.0 - >>> T.ttsv(np.ones(2),0) + >>> T.ttsv(np.ones(2), 0) array([3., 7.]) - >>> T.ttsv(np.ones(2),1) + >>> T.ttsv(np.ones(2), 1) array([[1, 2], [3, 4]]) """ @@ -1776,16 +1776,16 @@ def __setitem__(self, key, value): Examples -------- - >>> T = tenones((3,4,2)) + >>> T = tenones((3, 4, 2)) >>> # replaces subtensor - >>> T[0:2,0:2,0] = np.ones((2,2)) + >>> T[0:2, 0:2, 0] = np.ones((2, 2)) >>> # replaces two elements >>> T[np.array([[1, 1, 1], [1, 1, 2]])] = [5, 7] >>> # replaces two elements with linear indices >>> T[np.array([1, 13])] = [5, 7] >>> # grows tensor to accept new element - >>> T[1,1,2:3] = 1 - >>> T[1,1,4] = 1 + >>> T[1, 1, 2:3] = 1 + >>> T[1, 1, 4] = 1 """ access_type = get_index_variant(key) @@ -1921,16 +1921,16 @@ def __getitem__(self, item): # noqa: PLR0912 Examples -------- - >>> T = tenones((3,4,2,1)) - >>> T[0,0,0,0] # produces a scalar + >>> T = tenones((3, 4, 2, 1)) + >>> T[0, 0, 0, 0] # produces a scalar 1.0 >>> # produces a tensor of order 1 and size 1 - >>> T[1,1,1,:] # doctest: +NORMALIZE_WHITESPACE + >>> T[1, 1, 1, :] # doctest: +NORMALIZE_WHITESPACE tensor of shape (1,) data[:] = [1.] >>> # produces a tensor of size 2 x 2 x 1 - >>> T[0:2,[2, 3],1,:] # doctest: +NORMALIZE_WHITESPACE + >>> T[0:2, [2, 3], 1, :] # doctest: +NORMALIZE_WHITESPACE tensor of shape (2, 2, 1) data[0, :, :] = [[1.] @@ -1942,7 +1942,7 @@ def __getitem__(self, item): # noqa: PLR0912 >>> # Equivalent to selecting [0,0,0,0] and [1,1,1,0] separately >>> T[np.array([[0, 0, 0, 0], [1, 1, 1, 0]])] array([1., 1.]) - >>> T[[0,1,2]] # extracts the first three linearized indices + >>> T[[0, 1, 2]] # extracts the first three linearized indices array([1., 1., 1.]) """ # Case 0: Single Index Linear @@ -2570,7 +2570,7 @@ def tenones(shape: Tuple[int, ...]) -> tensor: tensor of shape (3,) data[:] = [1. 1. 1.] - >>> T = ttb.tenones((3,3)) + >>> T = ttb.tenones((3, 3)) >>> T tensor of shape (3, 3) data[:, :] = @@ -2601,7 +2601,7 @@ def tenzeros(shape: Tuple[int, ...]) -> tensor: tensor of shape (3,) data[:] = [0. 0. 0.] - >>> T = ttb.tenzeros((3,3)) + >>> T = ttb.tenzeros((3, 3)) >>> T tensor of shape (3, 3) data[:, :] = diff --git a/pyttb/ttensor.py b/pyttb/ttensor.py index 8b2c3999..cef017e9 100644 --- a/pyttb/ttensor.py +++ b/pyttb/ttensor.py @@ -60,9 +60,9 @@ def __init__( Set up input data # Create ttensor with explicit data description - >>> core_values = np.ones((2,2,2)) + >>> core_values = np.ones((2, 2, 2)) >>> core = ttb.tensor(core_values) - >>> factors = [np.ones((1,2))] * len(core_values.shape) + >>> factors = [np.ones((1, 2))] * len(core_values.shape) >>> K0 = ttb.ttensor(core, factors) """ if core is None and factors is None: @@ -99,19 +99,19 @@ def copy(self) -> ttensor: Examples -------- - >>> core_values = np.ones((2,2,2)) + >>> core_values = np.ones((2, 2, 2)) >>> core = ttb.tensor(core_values) - >>> factors = [np.ones((1,2))] * len(core_values.shape) + >>> factors = [np.ones((1, 2))] * len(core_values.shape) >>> first = ttb.ttensor(core, factors) >>> second = first >>> third = second.copy() - >>> first.factor_matrices[0][0,0] = 2 + >>> first.factor_matrices[0][0, 0] = 2 # Item to convert numpy boolean to python boolena for nicer printing - >>> (first.factor_matrices[0][0,0] == second.factor_matrices[0][0,0]).item() + >>> (first.factor_matrices[0][0, 0] == second.factor_matrices[0][0, 0]).item() True - >>> (first.factor_matrices[0][0,0] == third.factor_matrices[0][0,0]).item() + >>> (first.factor_matrices[0][0, 0] == third.factor_matrices[0][0, 0]).item() False """ return ttb.ttensor(self.core, self.factor_matrices, copy=True) diff --git a/tests/test_package.py b/tests/test_package.py index aa01ed7b..bde6b5c8 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -32,7 +32,7 @@ def test_formatting(): root_dir = os.path.dirname(os.path.dirname(__file__)) toml_file = os.path.join(root_dir, "pyproject.toml") subprocess.run( - f"black --check {root_dir} --config {toml_file}", + f"ruff format --check {root_dir} --config {toml_file}", check=True, shell=True, ) @@ -40,6 +40,17 @@ def test_formatting(): def test_typing(): """Run type checker on package""" + import matplotlib + from packaging.version import Version + + skip_untyped = "" + # Hack to support backwards compatibility testing + if Version(matplotlib.__version__) < Version("3.8.0"): + skip_untyped = "--disable-error-code=import-untyped" root_dir = os.path.dirname(os.path.dirname(__file__)) toml_file = os.path.join(root_dir, "pyproject.toml") - subprocess.run(f"mypy -p pyttb --config-file {toml_file}", check=True, shell=True) + subprocess.run( + f"mypy -p pyttb --config-file {toml_file} {skip_untyped}", + check=True, + shell=True, + )