diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c717ee85..1e1f536c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,15 +13,15 @@ jobs: python-version: ['3.12'] include: - BACKEND: 'numpy' - python-version: '3.9' + python-version: '3.10' - BACKEND: 'numpy' - python-version: '3.11' + python-version: '3.13' TENSORLY_TENALG_BACKEND: ['einsum'] - BACKEND: 'jax' python-version: '3.13' TENSORLY_TENALG_BACKEND: ['einsum'] - BACKEND: 'tensorflow' - python-version: '3.12' + python-version: '3.13' TENSORLY_TENALG_BACKEND: ['einsum'] steps: diff --git a/examples/applications/plot_image_compression.py b/examples/applications/plot_image_compression.py index 51e5acd50..bbec12191 100644 --- a/examples/applications/plot_image_compression.py +++ b/examples/applications/plot_image_compression.py @@ -12,7 +12,6 @@ from scipy.ndimage import zoom from tensorly.decomposition import parafac from tensorly.decomposition import tucker -from math import ceil random_state = 12345 diff --git a/examples/decomposition/plot_permute_factors.py b/examples/decomposition/plot_permute_factors.py index 77ef8c7c1..14c75e605 100644 --- a/examples/decomposition/plot_permute_factors.py +++ b/examples/decomposition/plot_permute_factors.py @@ -13,7 +13,6 @@ # The permuted tensor (or list of tensors) and list of permutation for each permuted tensors are returned. # Tensorly CPTensor should be used as an input to permute their factors and weights simultaneously. -import tensorly as tl from tensorly.random import random_cp from tensorly.cp_tensor import cp_permute_factors import matplotlib.pyplot as plt diff --git a/tensorly/__init__.py b/tensorly/__init__.py index 522857054..dd4a8bccc 100644 --- a/tensorly/__init__.py +++ b/tensorly/__init__.py @@ -31,7 +31,6 @@ pad_tt_rank, ) from .tt_matrix import ( - tt_matrix_to_tensor, tt_matrix_to_tensor, validate_tt_matrix_rank, tt_matrix_to_unfolded, @@ -72,7 +71,6 @@ where, conj, index, - index_update, clip, max, min, diff --git a/tensorly/backend/core.py b/tensorly/backend/core.py index 37193cb19..adfe068f3 100644 --- a/tensorly/backend/core.py +++ b/tensorly/backend/core.py @@ -1,9 +1,3 @@ -import inspect -import importlib -import os -import sys -import threading -import types import warnings import math @@ -304,6 +298,11 @@ def to_numpy(tensor): def copy(tensor): """Return a copy of the given tensor""" raise NotImplementedError + + @staticmethod + def ndim(tensor): + """Return the number of dimensions of a tensor""" + return tensor.ndim @staticmethod def concatenate(tensors, axis=0): diff --git a/tensorly/backend/cupy_backend.py b/tensorly/backend/cupy_backend.py index a702a2e15..151b47b4a 100644 --- a/tensorly/backend/cupy_backend.py +++ b/tensorly/backend/cupy_backend.py @@ -10,7 +10,6 @@ ) raise ImportError(message) from error -import warnings from .core import ( Backend, @@ -39,10 +38,6 @@ def to_numpy(tensor): return cp.asnumpy(tensor) return tensor - @staticmethod - def ndim(tensor): - return tensor.ndim - @staticmethod def clip(tensor, a_min=None, a_max=None): return cp.clip(tensor, a_min, a_max) diff --git a/tensorly/backend/jax_backend.py b/tensorly/backend/jax_backend.py index 830d830a3..3a19aec99 100644 --- a/tensorly/backend/jax_backend.py +++ b/tensorly/backend/jax_backend.py @@ -51,10 +51,6 @@ def copy(self, tensor): return self.tensor(tensor.copy(), **self.context(tensor)) # return copy.copy(tensor) - @staticmethod - def ndim(tensor): - return tensor.ndim - @staticmethod def lstsq(a, b, rcond=None): return np.linalg.lstsq(a, b, rcond=rcond, numpy_resid=True) diff --git a/tensorly/backend/numpy_backend.py b/tensorly/backend/numpy_backend.py index 1c4cd5f7d..5133eb085 100644 --- a/tensorly/backend/numpy_backend.py +++ b/tensorly/backend/numpy_backend.py @@ -25,10 +25,6 @@ def is_tensor(tensor): def to_numpy(tensor): return np.copy(tensor) - @staticmethod - def ndim(tensor): - return tensor.ndim - @staticmethod def clip(tensor, a_min=None, a_max=None): return np.clip(tensor, a_min, a_max) diff --git a/tensorly/backend/paddle_backend.py b/tensorly/backend/paddle_backend.py index 899f40475..07896fa9a 100644 --- a/tensorly/backend/paddle_backend.py +++ b/tensorly/backend/paddle_backend.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from packaging.version import Version import warnings @@ -158,10 +158,6 @@ def to_numpy(tensor: paddle.Tensor): def shape(tensor: paddle.Tensor): return tuple(tensor.shape) - @staticmethod - def ndim(tensor: paddle.Tensor): - return tensor.ndim - @staticmethod def clip(tensor: paddle.Tensor, a_min=None, a_max=None, inplace=False): if inplace: @@ -176,7 +172,7 @@ def all(tensor: paddle.Tensor): def transpose(self, tensor: paddle.Tensor, axes: int | Sequence[int] | None = None): axes = axes or list(range(self.ndim(tensor)))[::-1] - if not isinstance(axes, (tuple, list)): + if not isinstance(axes, tuple | list): axes = list(axes) return tensor.transpose(axes) diff --git a/tensorly/backend/pytorch_backend.py b/tensorly/backend/pytorch_backend.py index ec0e891e3..39a999500 100644 --- a/tensorly/backend/pytorch_backend.py +++ b/tensorly/backend/pytorch_backend.py @@ -85,10 +85,6 @@ def to_numpy(tensor): def shape(tensor): return tuple(tensor.shape) - @staticmethod - def ndim(tensor): - return tensor.dim() - @staticmethod def arange(start, stop=None, step=1.0, *args, **kwargs): if stop is None: diff --git a/tensorly/backend/tensorflow_backend.py b/tensorly/backend/tensorflow_backend.py index 7c17c2d3f..6f6343b00 100644 --- a/tensorly/backend/tensorflow_backend.py +++ b/tensorly/backend/tensorflow_backend.py @@ -180,7 +180,6 @@ def logsumexp(tensor, axis=0): "mean", "sum", "moveaxis", - "ndim", "arange", "sort", "argsort", diff --git a/tensorly/contrib/sparse/tests/test_decomposition.py b/tensorly/contrib/sparse/tests/test_decomposition.py index bd4dcdead..5c103fef2 100644 --- a/tensorly/contrib/sparse/tests/test_decomposition.py +++ b/tensorly/contrib/sparse/tests/test_decomposition.py @@ -1,5 +1,4 @@ from ..decomposition import parafac -from ..tenalg import multi_mode_dot from ..cp_tensor import cp_to_tensor from .... import backend as tl diff --git a/tensorly/cp_tensor.py b/tensorly/cp_tensor.py index 2f059f188..3e4f58363 100644 --- a/tensorly/cp_tensor.py +++ b/tensorly/cp_tensor.py @@ -177,7 +177,7 @@ def _validate_cp_tensor(cp_tensor): if isinstance(cp_tensor, CPTensor): # it's already been validated at creation return cp_tensor.shape, cp_tensor.rank - elif isinstance(cp_tensor, (float, int)): # 0-order tensor + elif isinstance(cp_tensor, float | int): # 0-order tensor return 0, 0 weights, factors = cp_tensor diff --git a/tensorly/datasets/data_imports.py b/tensorly/datasets/data_imports.py index 96c6f6b84..c347ad964 100644 --- a/tensorly/datasets/data_imports.py +++ b/tensorly/datasets/data_imports.py @@ -4,10 +4,6 @@ from os.path import dirname import numpy as np -from urllib.request import urlopen -import scipy.io -from zipfile import ZipFile -from io import BytesIO import tensorly as tl diff --git a/tensorly/datasets/tests/test_imports.py b/tensorly/datasets/tests/test_imports.py index bfc31470f..b0cc91f04 100644 --- a/tensorly/datasets/tests/test_imports.py +++ b/tensorly/datasets/tests/test_imports.py @@ -1,4 +1,3 @@ -import pytest from ..data_imports import ( load_IL2data, diff --git a/tensorly/decomposition/_constrained_cp.py b/tensorly/decomposition/_constrained_cp.py index bc1192f1b..98d596dfe 100644 --- a/tensorly/decomposition/_constrained_cp.py +++ b/tensorly/decomposition/_constrained_cp.py @@ -121,7 +121,7 @@ def initialize_constrained_parafac( factors.append(U[:, :rank]) - elif isinstance(init, (tuple, list, CPTensor)): + elif isinstance(init, tuple | list | CPTensor): try: weights, factors = CPTensor(init) diff --git a/tensorly/decomposition/_cp.py b/tensorly/decomposition/_cp.py index 2c90faec3..a27d84661 100755 --- a/tensorly/decomposition/_cp.py +++ b/tensorly/decomposition/_cp.py @@ -99,7 +99,7 @@ def initialize_cp( kt = CPTensor((None, factors)) - elif isinstance(init, (tuple, list, CPTensor)): + elif isinstance(init, tuple | list | CPTensor): # TODO: Test this try: if normalize_factors is True: diff --git a/tensorly/decomposition/_parafac2.py b/tensorly/decomposition/_parafac2.py index 9350d774c..bc44686c1 100644 --- a/tensorly/decomposition/_parafac2.py +++ b/tensorly/decomposition/_parafac2.py @@ -1,5 +1,6 @@ from warnings import warn -from typing import Iterable, Optional, Sequence, Literal, Union +from typing import Literal +from collections.abc import Iterable, Sequence import tensorly as tl from ._base_decomposition import DecompositionMixin @@ -140,7 +141,7 @@ def initialize_decomposition( projections = _compute_projections(tensor_slices, (A, B, C), svd) return Parafac2Tensor((None, [A, B, C], projections)) - elif isinstance(init, (tuple, list, Parafac2Tensor, CPTensor)): + elif isinstance(init, tuple | list | Parafac2Tensor | CPTensor): try: decomposition = Parafac2Tensor.from_CPTensor(init, parafac2_tensor_ok=True) except ValueError: @@ -404,9 +405,9 @@ def parafac2( svd: SVD_TYPES = "truncated_svd", normalize_factors: bool = False, tol: float = 1.0e-8, - nn_modes: Optional[Union[Sequence[int], Literal["all"]]] = None, + nn_modes: Sequence[int] | Literal["all"] | None = None, random_state=None, - verbose: Union[bool, int] = False, + verbose: bool | int = False, return_errors: bool = False, n_iter_parafac: int = 5, linesearch: bool = True, diff --git a/tensorly/decomposition/_tt.py b/tensorly/decomposition/_tt.py index f1df533c9..56a126dc7 100644 --- a/tensorly/decomposition/_tt.py +++ b/tensorly/decomposition/_tt.py @@ -1,7 +1,7 @@ import tensorly as tl from ._base_decomposition import DecompositionMixin from ..tt_tensor import validate_tt_rank, TTTensor -from ..tt_matrix import validate_tt_matrix_rank, TTMatrix +from ..tt_matrix import TTMatrix from ..tenalg.svd import svd_interface diff --git a/tensorly/decomposition/_tucker.py b/tensorly/decomposition/_tucker.py index 2b09e8cb3..37d4ef05c 100644 --- a/tensorly/decomposition/_tucker.py +++ b/tensorly/decomposition/_tucker.py @@ -8,9 +8,6 @@ validate_tucker_rank, tucker_normalize, ) -from ..solvers.penalizations import ( - process_regularization_weights, -) from ..solvers.nnls import hals_nnls, fista, active_set_nnls from math import sqrt import warnings diff --git a/tensorly/decomposition/tests/test_constrained_parafac.py b/tensorly/decomposition/tests/test_constrained_parafac.py index 032cdbef2..29ee1f961 100644 --- a/tensorly/decomposition/tests/test_constrained_parafac.py +++ b/tensorly/decomposition/tests/test_constrained_parafac.py @@ -11,7 +11,6 @@ assert_array_almost_equal, assert_class_wrapper_correctly_passes_arguments, ) -from ...random import random_cp def test_constrained_parafac_nonnegative(monkeypatch): diff --git a/tensorly/decomposition/tests/test_cp.py b/tensorly/decomposition/tests/test_cp.py index e004e2de4..576d68df1 100644 --- a/tensorly/decomposition/tests/test_cp.py +++ b/tensorly/decomposition/tests/test_cp.py @@ -18,7 +18,6 @@ CP_NN_HALS, ) from ...cp_tensor import cp_to_tensor -from ...cp_tensor import cp_to_tensor from ...random import random_cp from ...tenalg import khatri_rao from ... import backend as T diff --git a/tensorly/decomposition/tests/test_tr_svd.py b/tensorly/decomposition/tests/test_tr_svd.py index 17738d735..7e41fc44b 100644 --- a/tensorly/decomposition/tests/test_tr_svd.py +++ b/tensorly/decomposition/tests/test_tr_svd.py @@ -1,13 +1,10 @@ -import pytest -import tensorly as tl -from .._tr_svd import tensor_ring, TensorRing +from .._tr_svd import tensor_ring from ...random import random_tr from ...testing import ( assert_, assert_array_almost_equal, assert_raises, - assert_class_wrapper_correctly_passes_arguments, ) diff --git a/tensorly/random/base.py b/tensorly/random/base.py index 35c9e2bee..4f7cdfc1f 100644 --- a/tensorly/random/base.py +++ b/tensorly/random/base.py @@ -1,4 +1,3 @@ -import numpy as np from ..cp_tensor import cp_to_tensor, CPTensor, cp_normalize, validate_cp_rank from ..tucker_tensor import tucker_to_tensor, TuckerTensor, validate_tucker_rank from ..tt_tensor import tt_to_tensor, TTTensor, validate_tt_rank diff --git a/tensorly/solvers/penalizations.py b/tensorly/solvers/penalizations.py index 69b4ac2b5..e4aa3650f 100644 --- a/tensorly/solvers/penalizations.py +++ b/tensorly/solvers/penalizations.py @@ -1,6 +1,3 @@ -import tensorly as tl -import numpy as np -import copy import warnings @@ -24,10 +21,10 @@ def process_regularization_weights(ridge_coefficients, sparsity_coefficients, n_ list of floats sparsity coefficients, processed """ - if ridge_coefficients is None or isinstance(ridge_coefficients, (int, float)): + if ridge_coefficients is None or isinstance(ridge_coefficients, int | float): # Populate None or the input float in a list for all modes ridge_coefficients = [ridge_coefficients] * n_modes - if sparsity_coefficients is None or isinstance(sparsity_coefficients, (int, float)): + if sparsity_coefficients is None or isinstance(sparsity_coefficients, int | float): # Populate None or the input float in a list for all modes sparsity_coefficients = [sparsity_coefficients] * n_modes # Convert None to 0 diff --git a/tensorly/solvers/tests/test_admm.py b/tensorly/solvers/tests/test_admm.py index 9de325a16..d5f8f8224 100644 --- a/tensorly/solvers/tests/test_admm.py +++ b/tensorly/solvers/tests/test_admm.py @@ -2,14 +2,13 @@ import tensorly as tl from tensorly.solvers.admm import admm -from tensorly.testing import assert_, assert_array_equal, assert_array_almost_equal -from tensorly import tensor_to_vec, truncated_svd +from tensorly.testing import assert_array_almost_equal import pytest # Author: Jean Kossaifi skip_tensorflow = pytest.mark.skipif( (tl.get_backend() == "tensorflow"), - reason=f"Indexing with list not supported in TensorFlow", + reason="Indexing with list not supported in TensorFlow", ) diff --git a/tensorly/solvers/tests/test_nnls.py b/tensorly/solvers/tests/test_nnls.py index 4bca6d934..2300fbcc8 100644 --- a/tensorly/solvers/tests/test_nnls.py +++ b/tensorly/solvers/tests/test_nnls.py @@ -6,14 +6,14 @@ fista, active_set_nnls, ) -from tensorly.testing import assert_, assert_array_equal, assert_array_almost_equal -from tensorly import tensor_to_vec, truncated_svd +from tensorly.testing import assert_array_almost_equal +from tensorly import tensor_to_vec import pytest # Author: Jean Kossaifi skip_tensorflow = pytest.mark.skipif( (tl.get_backend() == "tensorflow"), - reason=f"Indexing with list not supported in TensorFlow", + reason="Indexing with list not supported in TensorFlow", ) diff --git a/tensorly/solvers/tests/test_penalizations.py b/tensorly/solvers/tests/test_penalizations.py index e07b630fb..a7b5ede82 100644 --- a/tensorly/solvers/tests/test_penalizations.py +++ b/tensorly/solvers/tests/test_penalizations.py @@ -1,4 +1,3 @@ -import numpy as np import tensorly as tl from tensorly.solvers.penalizations import process_regularization_weights diff --git a/tensorly/tenalg/einsum_tenalg/generalised_inner_product.py b/tensorly/tenalg/einsum_tenalg/generalised_inner_product.py index b11cc1c46..d2daf1608 100644 --- a/tensorly/tenalg/einsum_tenalg/generalised_inner_product.py +++ b/tensorly/tenalg/einsum_tenalg/generalised_inner_product.py @@ -1,5 +1,4 @@ from ... import backend as T -import numpy as np # Author: Jean Kossaifi # License: BSD 3 clause diff --git a/tensorly/tenalg/proximal.py b/tensorly/tenalg/proximal.py index 6dc201b4d..28aeb7e77 100644 --- a/tensorly/tenalg/proximal.py +++ b/tensorly/tenalg/proximal.py @@ -391,8 +391,8 @@ def unimodality_prox(tensor): ) # Next line finds mutual peak points values = tl.tensor( - tl.to_numpy((tensor - monotone_decreasing >= 0)) - * tl.to_numpy((tensor - monotone_increasing >= 0)), + tl.to_numpy(tensor - monotone_decreasing >= 0) + * tl.to_numpy(tensor - monotone_increasing >= 0), **tl.context(tensor) ) diff --git a/tensorly/tenalg/tenalg_utils.py b/tensorly/tenalg/tenalg_utils.py index 4580101d5..b34ea90e2 100644 --- a/tensorly/tenalg/tenalg_utils.py +++ b/tensorly/tenalg/tenalg_utils.py @@ -34,7 +34,7 @@ def _validate_contraction_modes(shape1, shape2, modes, batched_modes=False): if len(modes1) != len(modes2): if batched_modes: - message = f"Both tensors must have the same number of batched modes" + message = "Both tensors must have the same number of batched modes" else: message = ( "Both tensors must have the same number of modes to contract along. " diff --git a/tensorly/tenalg/tests/test_batched_tensordot.py b/tensorly/tenalg/tests/test_batched_tensordot.py index 25825d8b7..d20d294ad 100644 --- a/tensorly/tenalg/tests/test_batched_tensordot.py +++ b/tensorly/tenalg/tests/test_batched_tensordot.py @@ -3,7 +3,6 @@ from ...testing import assert_array_almost_equal, assert_raises, assert_ from ... import random from .. import tensordot -import pytest def test_batched_tensordot(): diff --git a/tensorly/tenalg/tests/test_outer_product.py b/tensorly/tenalg/tests/test_outer_product.py index 7a372e0a3..8b74ed5a7 100644 --- a/tensorly/tenalg/tests/test_outer_product.py +++ b/tensorly/tenalg/tests/test_outer_product.py @@ -1,6 +1,5 @@ import tensorly as tl from tensorly import testing -from tensorly import random from tensorly import tenalg from .. import outer, batched_outer diff --git a/tensorly/tenalg/tests/test_proximal.py b/tensorly/tenalg/tests/test_proximal.py index 5c614f7a7..de0c6f6c9 100644 --- a/tensorly/tenalg/tests/test_proximal.py +++ b/tensorly/tenalg/tests/test_proximal.py @@ -22,7 +22,7 @@ # Author: Jean Kossaifi skip_tensorflow = pytest.mark.skipif( (tl.get_backend() == "tensorflow"), - reason=f"Indexing with list not supported in TensorFlow", + reason="Indexing with list not supported in TensorFlow", ) diff --git a/tensorly/tenalg/tests/test_svd.py b/tensorly/tenalg/tests/test_svd.py index 04dd91e49..c248156cd 100644 --- a/tensorly/tenalg/tests/test_svd.py +++ b/tensorly/tenalg/tests/test_svd.py @@ -2,7 +2,6 @@ from ...testing import assert_ from ..svd import svd_interface import tensorly as tl -from ...testing import assert_ @pytest.mark.parametrize("shape", [(10, 5), (10, 10), (5, 10)]) diff --git a/tensorly/tests/test_backend.py b/tensorly/tests/test_backend.py index 08386b5b1..4c7333c7f 100644 --- a/tensorly/tests/test_backend.py +++ b/tensorly/tests/test_backend.py @@ -3,7 +3,6 @@ import pytest from time import time import numpy as np -from scipy.linalg import svd from scipy import special import tensorly as tl @@ -174,13 +173,13 @@ def test_svd_time(): t = time() _ = tl.truncated_svd(M, 4) t = time() - t - assert_(t <= 0.1, f"Partial_SVD took too long, maybe full_matrices set wrongly") + assert_(t <= 0.1, "Partial_SVD took too long, maybe full_matrices set wrongly") M = tl.tensor(np.random.random_sample((10000, 4))) t = time() _ = tl.truncated_svd(M, 4) t = time() - t - assert_(t <= 0.1, f"Partial_SVD took too long, maybe full_matrices set wrongly") + assert_(t <= 0.1, "Partial_SVD took too long, maybe full_matrices set wrongly") def test_svd(): diff --git a/tensorly/tests/test_parafac2_tensor.py b/tensorly/tests/test_parafac2_tensor.py index d1e501c81..99e57adde 100644 --- a/tensorly/tests/test_parafac2_tensor.py +++ b/tensorly/tests/test_parafac2_tensor.py @@ -1,5 +1,3 @@ -import numpy as np -import pytest from .. import backend as tl from ..base import unfold, tensor_to_vec diff --git a/tensorly/tt_matrix.py b/tensorly/tt_matrix.py index 0711b1057..fe510757b 100644 --- a/tensorly/tt_matrix.py +++ b/tensorly/tt_matrix.py @@ -40,7 +40,7 @@ def validate_tt_matrix_rank(tensorized_shape, rank="same"): if n_dim * 2 != len(tensorized_shape): msg = ( - f"The order of the give tensorized shape is not a multiple of 2." + "The order of the give tensorized shape is not a multiple of 2." "However, there should be as many dimensions for the left side (number of rows)" " as of the right side (number of columns). " " For instance, to convert a matrix of size (8, 9) to the TT-format, " @@ -75,7 +75,7 @@ def _tt_matrix_n_param(tensorized_shape, rank): if n_dim * 2 != len(tensorized_shape): msg = ( - f"The order of the give tensorized shape is not a multiple of 2." + "The order of the give tensorized shape is not a multiple of 2." "However, there should be as many dimensions for the left side (number of rows)" " as of the right side (number of columns). " " For instance, to convert a matrix of size (8, 9) to the TT-format, " diff --git a/tensorly/tt_tensor.py b/tensorly/tt_tensor.py index 98bc28cfb..d04486865 100644 --- a/tensorly/tt_tensor.py +++ b/tensorly/tt_tensor.py @@ -15,7 +15,7 @@ def _validate_tt_tensor(tt_tensor): if isinstance(tt_tensor, TTTensor): # it's already been validated at creation return tt_tensor.shape, tt_tensor.rank - elif isinstance(tt_tensor, (float, int)): # 0-order tensor + elif isinstance(tt_tensor, float | int): # 0-order tensor return 0, 0 rank = [] @@ -74,7 +74,7 @@ def tt_to_tensor(factors): output_tensor : ndarray tensor whose TT/MPS decomposition was given by 'factors' """ - if isinstance(factors, (float, int)): # 0-order tensor + if isinstance(factors, float | int): # 0-order tensor return factors full_shape = [f.shape[1] for f in factors] diff --git a/tensorly/utils/tests/test_deprecation.py b/tensorly/utils/tests/test_deprecation.py index 8ed21d30f..3a3b88c4a 100644 --- a/tensorly/utils/tests/test_deprecation.py +++ b/tensorly/utils/tests/test_deprecation.py @@ -1,4 +1,4 @@ -from ..deprecation import deprecated, DefineDeprecated +from ..deprecation import deprecated def test_deprecated():