From 2ee3d868eee80293e81bde0aa45f6b82abc8c79e Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Wed, 19 Jun 2024 11:52:13 +0200 Subject: [PATCH 1/8] Adapt to deprecation --- filter_functions/basis.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/filter_functions/basis.py b/filter_functions/basis.py index a69fefd..2aa326a 100644 --- a/filter_functions/basis.py +++ b/filter_functions/basis.py @@ -47,7 +47,6 @@ import numpy as np import opt_einsum as oe from numpy import linalg as nla -from numpy.core import ndarray from scipy import linalg as sla from sparse import COO @@ -56,7 +55,7 @@ __all__ = ['Basis', 'expand', 'ggm_expand', 'normalize'] -class Basis(ndarray): +class Basis(np.ndarray): r""" Class for operator bases. There are several ways to instantiate a Basis object: @@ -217,12 +216,12 @@ def __eq__(self, other: object) -> bool: # Not ndarray return np.equal(self, other) - return np.allclose(self.view(ndarray), other.view(ndarray), + return np.allclose(self.view(np.ndarray), other.view(np.ndarray), atol=self._atol, rtol=self._rtol) - def __contains__(self, item: ndarray) -> bool: + def __contains__(self, item: np.ndarray) -> bool: """Implement 'in' operator.""" - return any(np.isclose(item.view(ndarray), self.view(ndarray), + return any(np.isclose(item.view(np.ndarray), self.view(np.ndarray), rtol=self._rtol, atol=self._atol).all(axis=(1, 2))) def __array_wrap__(self, out_arr, context=None): @@ -232,7 +231,7 @@ def __array_wrap__(self, out_arr, context=None): https://github.com/numpy/numpy/issues/5819#issue-72454838 """ if out_arr.ndim: - return ndarray.__array_wrap__(self, out_arr, context) + return np.ndarray.__array_wrap__(self, out_arr, context) def _print_checks(self) -> None: """Print checks for debug purposes.""" @@ -265,7 +264,7 @@ def isorthonorm(self) -> bool: actual = U.conj() @ U.T target = np.identity(dim) atol = self._eps*(self.d**2)**3 - self._isorthonorm = np.allclose(actual.view(ndarray), target, + self._isorthonorm = np.allclose(actual.view(np.ndarray), target, atol=atol, rtol=self._rtol) return self._isorthonorm @@ -284,7 +283,10 @@ def istraceless(self) -> bool: elif nonzero[0].size == 1: # Single element has nonzero trace, check if (proportional to) # identity - elem = self[nonzero][0].view(ndarray) if self.ndim == 3 else self.view(ndarray) + if self.ndim == 3: + elem = self[nonzero][0].view(np.ndarray) + else: + elem = self.view(np.ndarray) offdiag_nonzero = elem[~np.eye(self.d, dtype=bool)].nonzero() diag_equal = np.diag(elem) == elem[0, 0] if diag_equal.all() and not offdiag_nonzero[0].any(): @@ -597,7 +599,7 @@ def _full_from_partial(elems: Sequence, traceless: bool, labels: Sequence[str]) # sort Identity label to the front, default to first if not found # (should not happen since traceless checks that it is present) id_idx = next((i for i, elem in enumerate(elems) - if np.allclose(Id.view(ndarray), elem.view(ndarray), + if np.allclose(Id.view(np.ndarray), elem.view(np.ndarray), rtol=elems._rtol, atol=elems._atol)), 0) labels.insert(0, labels.pop(id_idx)) @@ -606,7 +608,7 @@ def _full_from_partial(elems: Sequence, traceless: bool, labels: Sequence[str]) return basis, labels -def _norm(b: Sequence) -> ndarray: +def _norm(b: Sequence) -> np.ndarray: """Frobenius norm with two singleton dimensions inserted at the end.""" b = np.asanyarray(b) norm = nla.norm(b, axis=(-1, -2)) @@ -633,8 +635,8 @@ def normalize(b: Basis) -> Basis: return (b/_norm(b)).squeeze().view(Basis) -def expand(M: Union[ndarray, Basis], basis: Union[ndarray, Basis], - normalized: bool = True, hermitian: bool = False, tidyup: bool = False) -> ndarray: +def expand(M: Union[np.ndarray, Basis], basis: Union[np.ndarray, Basis], + normalized: bool = True, hermitian: bool = False, tidyup: bool = False) -> np.ndarray: r""" Expand the array *M* in the basis given by *basis*. @@ -684,8 +686,8 @@ def cast(arr): return util.remove_float_errors(coefficients) if tidyup else coefficients -def ggm_expand(M: Union[ndarray, Basis], traceless: bool = False, - hermitian: bool = False) -> ndarray: +def ggm_expand(M: Union[np.ndarray, Basis], traceless: bool = False, + hermitian: bool = False) -> np.ndarray: r""" Expand the matrix *M* in a Generalized Gell-Mann basis [Bert08]_. This function makes use of the explicit construction prescription of @@ -767,7 +769,7 @@ def cast(arr): return coeffs.squeeze() if square else coeffs -def equivalent_pauli_basis_elements(idx: Union[Sequence[int], int], N: int) -> ndarray: +def equivalent_pauli_basis_elements(idx: Union[Sequence[int], int], N: int) -> np.ndarray: """ Get the indices of the equivalent (up to identities tensored to it) basis elements of Pauli bases of qubits at position idx in the total @@ -780,7 +782,7 @@ def equivalent_pauli_basis_elements(idx: Union[Sequence[int], int], N: int) -> n return elem_idx -def remap_pauli_basis_elements(order: Sequence[int], N: int) -> ndarray: +def remap_pauli_basis_elements(order: Sequence[int], N: int) -> np.ndarray: """ For a N-qubit Pauli basis, transpose the order of the subsystems and return the indices that permute the old basis to the new. From b5d31b1c7092adf12d01d82ea309b38d277d405a Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 19 Sep 2024 11:31:38 +0200 Subject: [PATCH 2/8] Relax accuracy --- tests/test_gradient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_gradient.py b/tests/test_gradient.py index 98499a1..9c060f9 100644 --- a/tests/test_gradient.py +++ b/tests/test_gradient.py @@ -134,7 +134,7 @@ def test_gradient_calculation_random_pulse(self): spectral_noise_density=gradient_testutil.one_over_f_noise, c_id=[f'c{i}' for i in range(len(u_ctrl))], n_coeffs_deriv=None ) - self.assertArrayAlmostEqual(ana_grad, fin_diff_grad, rtol=1e-6, atol=1e-8) + self.assertArrayAlmostEqual(ana_grad, fin_diff_grad, rtol=1e-5, atol=1e-7) def test_caching(self): """Make sure calculation works with or without cached intermediates.""" From 58980af4081be158174bcb648bc75d4cff426946 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 7 Oct 2024 19:06:40 +0200 Subject: [PATCH 3/8] Pin qutip-qtrl version Numpy 2.0 will be fixed above 0.1.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2e85ad3..3dec217 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ def extract_version(version_file): 'bloch_sphere_visualization': ['qutip', 'matplotlib'], 'fancy_progressbar': ['ipynbname', 'jupyter'], 'doc': ['jupyter', 'nbsphinx', 'numpydoc', 'sphinx', 'sphinx_rtd_theme', - 'ipympl', 'qutip-qip', 'qutip-qtrl'], + 'ipympl', 'qutip-qip', 'qutip-qtrl>0.1.3'], 'tests': ['pytest>=4.6', 'pytest-cov', 'codecov']} extras_require['all'] = list({dep for deps in extras_require.values() for dep in deps}) From ca468332f1a6c644ab8914ad9f947cd96cdb97bf Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Sun, 13 Oct 2024 15:25:12 +0200 Subject: [PATCH 4/8] Add tests for single-element basis properties --- tests/test_basis.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_basis.py b/tests/test_basis.py index 9f5d3e4..c1fd995 100644 --- a/tests/test_basis.py +++ b/tests/test_basis.py @@ -24,12 +24,12 @@ from copy import copy from itertools import product +import filter_functions as ff import numpy as np import pytest -from sparse import COO - -import filter_functions as ff from filter_functions import util +from scipy import stats, linalg +from sparse import COO from tests import testutil from tests.testutil import rng @@ -152,9 +152,15 @@ def test_basis_properties(self): base._print_checks() - basis = util.paulis[1].view(ff.Basis) - self.assertTrue(basis.isorthonorm) - self.assertArrayEqual(basis.T, basis.view(np.ndarray).T) + orthonorm = stats.ortho_group(d).rvs() + self.assertTrue(orthonorm.view(ff.Basis).isorthonorm) + + herm = 1j * linalg.logm(stats.unitary_group(d).rvs()) + self.assertTrue(herm.view(ff.Basis).isherm) + + traceless = stats.multivariate_normal().rvs((d, d)) + traceless -= traceless.trace() / d + self.assertTrue(traceless.view(ff.Basis).istraceless) def test_transpose(self): arr = rng.normal(size=(2, 3, 3)) From 46ab275acaf3958feaa6d33d37d44fbe26da3e8b Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Sun, 13 Oct 2024 16:10:48 +0200 Subject: [PATCH 5/8] Actually do that --- tests/test_basis.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_basis.py b/tests/test_basis.py index c1fd995..5da71c1 100644 --- a/tests/test_basis.py +++ b/tests/test_basis.py @@ -152,16 +152,22 @@ def test_basis_properties(self): base._print_checks() - orthonorm = stats.ortho_group(d).rvs() + # single element always considered orthonormal + orthonorm = rng.normal(size=(d, d)) self.assertTrue(orthonorm.view(ff.Basis).isorthonorm) - herm = 1j * linalg.logm(stats.unitary_group(d).rvs()) + herm = testutil.rand_herm(d).squeeze() self.assertTrue(herm.view(ff.Basis).isherm) - traceless = stats.multivariate_normal().rvs((d, d)) - traceless -= traceless.trace() / d + herm[0, 1] += 1 + self.assertFalse(herm.view(ff.Basis).isherm) + + traceless = testutil.rand_herm_traceless(d).squeeze() self.assertTrue(traceless.view(ff.Basis).istraceless) + traceless[0, 0] += 1 + self.assertFalse(traceless.view(ff.Basis).istraceless) + def test_transpose(self): arr = rng.normal(size=(2, 3, 3)) b = arr.view(ff.Basis) From c87a7b64b1769b592f9e03b99aa05877f07c5ac7 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 24 Oct 2024 22:36:34 +0200 Subject: [PATCH 6/8] Adapt to __array_wrap__ deprecation --- filter_functions/basis.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/filter_functions/basis.py b/filter_functions/basis.py index 2aa326a..bf47b49 100644 --- a/filter_functions/basis.py +++ b/filter_functions/basis.py @@ -224,14 +224,18 @@ def __contains__(self, item: np.ndarray) -> bool: return any(np.isclose(item.view(np.ndarray), self.view(np.ndarray), rtol=self._rtol, atol=self._atol).all(axis=(1, 2))) - def __array_wrap__(self, out_arr, context=None): + def __array_wrap__(self, arr, context=None, return_scalar=False): """ Fixes problem that ufuncs return 0-d arrays instead of scalars. https://github.com/numpy/numpy/issues/5819#issue-72454838 """ - if out_arr.ndim: - return np.ndarray.__array_wrap__(self, out_arr, context) + try: + return super().__array_wrap__(arr, context, return_scalar=True) + except TypeError: + if arr.ndim: + # Numpy < 2 + return np.ndarray.__array_wrap__(self, arr, context) def _print_checks(self) -> None: """Print checks for debug purposes.""" From f9f0adfe769ad49a90b91ccee9f4a978663205f6 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 24 Oct 2024 22:37:32 +0200 Subject: [PATCH 7/8] Adapt to nonzero 0d deprecation --- filter_functions/basis.py | 2 +- tests/test_basis.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/filter_functions/basis.py b/filter_functions/basis.py index bf47b49..97f14ea 100644 --- a/filter_functions/basis.py +++ b/filter_functions/basis.py @@ -281,7 +281,7 @@ def istraceless(self) -> bool: if self._istraceless is None: trace = np.einsum('...jj', self) trace = util.remove_float_errors(trace, self.d**2) - nonzero = trace.nonzero() + nonzero = np.atleast_1d(trace).nonzero() if nonzero[0].size == 0: self._istraceless = True elif nonzero[0].size == 1: diff --git a/tests/test_basis.py b/tests/test_basis.py index 5da71c1..edfacb1 100644 --- a/tests/test_basis.py +++ b/tests/test_basis.py @@ -28,7 +28,6 @@ import numpy as np import pytest from filter_functions import util -from scipy import stats, linalg from sparse import COO from tests import testutil from tests.testutil import rng From 09e186c2dccf619d25cac90443bf1f562dbc957f Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Tue, 5 Nov 2024 19:51:17 +0100 Subject: [PATCH 8/8] Temporarily pin numpy in doc --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3dec217..8b268a8 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ def extract_version(version_file): 'bloch_sphere_visualization': ['qutip', 'matplotlib'], 'fancy_progressbar': ['ipynbname', 'jupyter'], 'doc': ['jupyter', 'nbsphinx', 'numpydoc', 'sphinx', 'sphinx_rtd_theme', - 'ipympl', 'qutip-qip', 'qutip-qtrl>0.1.3'], + 'ipympl', 'qutip-qip', 'qutip-qtrl', 'numpy<2'], 'tests': ['pytest>=4.6', 'pytest-cov', 'codecov']} extras_require['all'] = list({dep for deps in extras_require.values() for dep in deps})