From 94d8f649f3923ce0fac23102579f93741fb5f337 Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 11:50:53 +0200 Subject: [PATCH 01/14] add einsum to qutip/core/dimensions.py --- qutip/core/dimensions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index f00afc1ff8..eb9b922984 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -351,6 +351,11 @@ def from_tensor_rep(tensorrep, dims): def _frozen(*args, **kwargs): raise RuntimeError("Dimension cannot be modified.") +def einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', optimize=False): + operands_array = [to_tensor_rep(op) for op in operands] + result = np.einsum(subscripts, *operands_array) + dims = [[d] for d in result.shape] + return from_tensor_rep(result, dims) class MetaSpace(type): def __call__(cls, *args: SpaceLike, rep: str = None) -> "Space": From 69c9dca76bf71c091f32fe565b3c099f881cc18a Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 13:05:52 +0200 Subject: [PATCH 02/14] fix einsum scalar result --- qutip/core/dimensions.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index eb9b922984..4e1aba1062 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -351,9 +351,29 @@ def from_tensor_rep(tensorrep, dims): def _frozen(*args, **kwargs): raise RuntimeError("Dimension cannot be modified.") -def einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', optimize=False): +def einsum(subscripts, *operands): + """ + Implementation of numpy.einsum for Qobj. + Returns the result of einsum as a Qobj (or numpy.complex128 if the result is a scalar). + Parameters + ---------- + subscripts: str + Specifies the subscripts for summation as comma separated list of subscript labels. + An implicit (classical Einstein summation) calculation is performed unless the explicit + indicator ‘->’ is included as well as subscript labels of the precise output form. + + operands: list of array_like + These are the arrays for the operation. + + Returns + ------- + Qobj (numpy.complex128) + Result of einsum as Qobj (numpy.complex128 if result is scalar) + """ operands_array = [to_tensor_rep(op) for op in operands] result = np.einsum(subscripts, *operands_array) + if result.shape == (): + return result dims = [[d] for d in result.shape] return from_tensor_rep(result, dims) From b21de624ae352e42aed5a128182112b878636482 Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 13:40:20 +0200 Subject: [PATCH 03/14] add einsum tests --- qutip/tests/core/test_dimensions.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/qutip/tests/core/test_dimensions.py b/qutip/tests/core/test_dimensions.py index 7be9d27600..0b38a70144 100644 --- a/qutip/tests/core/test_dimensions.py +++ b/qutip/tests/core/test_dimensions.py @@ -6,7 +6,7 @@ from qutip.core.dimensions import ( flatten, unflatten, enumerate_flat, deep_remove, deep_map, dims_idxs_to_tensor_idxs, dims_to_tensor_shape, dims_to_tensor_perm, - collapse_dims_super, collapse_dims_oper, Dimensions + collapse_dims_super, collapse_dims_oper, einsum, Dimensions ) @@ -189,3 +189,13 @@ def test_dims_comparison(): assert Dimensions([[1], [2]])[0] != Dimensions([[1], [2]])[1] assert not Dimensions([[1], [2]])[1] != Dimensions([[1], [2]])[1] assert not Dimensions([[1], [2]])[0] != Dimensions([[1], [2]])[0] + + +@pytest.mark.parametrize(["subscripts", "operands", "expected"], [ + pytest.param("ii", [qutip.sigmaz()], 0), + pytest.param("ij", [qutip.sigmax()], qutip.sigmax()), + pytest.param("ij->ji", [qutip.sigmay()], qutip.sigmay().trans()), + pytest.param("ij,ji", [qutip.sigmaz(), qutip.sigmaz()], 2), +]) +def test_einsum(subscripts, operands, expected): + assert einsum(subscripts, *operands) == expected \ No newline at end of file From 4113df40c8dd82f98e4c0c547af037e2edc9598d Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 14:33:09 +0200 Subject: [PATCH 04/14] fix einsum comments --- qutip/core/dimensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index 4e1aba1062..f507150c76 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -354,7 +354,7 @@ def _frozen(*args, **kwargs): def einsum(subscripts, *operands): """ Implementation of numpy.einsum for Qobj. - Returns the result of einsum as a Qobj (or numpy.complex128 if the result is a scalar). + Evaluates the Einstein summation convention on the operands. Parameters ---------- subscripts: str From 7d5771b55f95a2dfbc565b61a778eaf53d719d5d Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 14:41:57 +0200 Subject: [PATCH 05/14] fix format --- qutip/core/dimensions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index f507150c76..9c3a51c7e8 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -351,6 +351,7 @@ def from_tensor_rep(tensorrep, dims): def _frozen(*args, **kwargs): raise RuntimeError("Dimension cannot be modified.") + def einsum(subscripts, *operands): """ Implementation of numpy.einsum for Qobj. @@ -358,13 +359,11 @@ def einsum(subscripts, *operands): Parameters ---------- subscripts: str - Specifies the subscripts for summation as comma separated list of subscript labels. - An implicit (classical Einstein summation) calculation is performed unless the explicit - indicator ‘->’ is included as well as subscript labels of the precise output form. - + Specifies the subscripts for summation as comma + separated list of subscript labels. operands: list of array_like These are the arrays for the operation. - + Returns ------- Qobj (numpy.complex128) @@ -377,6 +376,7 @@ def einsum(subscripts, *operands): dims = [[d] for d in result.shape] return from_tensor_rep(result, dims) + class MetaSpace(type): def __call__(cls, *args: SpaceLike, rep: str = None) -> "Space": """ From 31d8a4335c6724ea2b94b763da35908faa7811a0 Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Mon, 14 Oct 2024 14:49:51 +0200 Subject: [PATCH 06/14] add changelog --- doc/changes/2545.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changes/2545.feature diff --git a/doc/changes/2545.feature b/doc/changes/2545.feature new file mode 100644 index 0000000000..3514cff823 --- /dev/null +++ b/doc/changes/2545.feature @@ -0,0 +1 @@ +Implements a numpy.einsum version for Qobj dimensions (Evaluates the Einstein summation convention on the operands.) \ No newline at end of file From ea16d10f059f3673f8f6c00a7d56d3ddf7106285 Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Wed, 16 Oct 2024 18:22:29 +0200 Subject: [PATCH 07/14] fix compound systems --- qutip/core/dimensions.py | 2 +- qutip/tests/core/test_dimensions.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index 9c3a51c7e8..eb13af82bf 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -369,7 +369,7 @@ def einsum(subscripts, *operands): Qobj (numpy.complex128) Result of einsum as Qobj (numpy.complex128 if result is scalar) """ - operands_array = [to_tensor_rep(op) for op in operands] + operands_array = [to_tensor_rep(op).reshape(op.shape) for op in operands] result = np.einsum(subscripts, *operands_array) if result.shape == (): return result diff --git a/qutip/tests/core/test_dimensions.py b/qutip/tests/core/test_dimensions.py index 0b38a70144..c11b310ba7 100644 --- a/qutip/tests/core/test_dimensions.py +++ b/qutip/tests/core/test_dimensions.py @@ -196,6 +196,9 @@ def test_dims_comparison(): pytest.param("ij", [qutip.sigmax()], qutip.sigmax()), pytest.param("ij->ji", [qutip.sigmay()], qutip.sigmay().trans()), pytest.param("ij,ji", [qutip.sigmaz(), qutip.sigmaz()], 2), + pytest.param("ii", [qutip.tensor(qutip.thermal_dm(2,1), qutip.thermal_dm(2,1))], 1), + pytest.param("ik, kj", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz()), + qutip.tensor(qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(4)) ]) def test_einsum(subscripts, operands, expected): assert einsum(subscripts, *operands) == expected \ No newline at end of file From e00d16810ce836589f91346f50db95be5162c5cd Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Wed, 16 Oct 2024 18:25:49 +0200 Subject: [PATCH 08/14] add 3 qubit test --- qutip/tests/core/test_dimensions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qutip/tests/core/test_dimensions.py b/qutip/tests/core/test_dimensions.py index c11b310ba7..d4ab6354f3 100644 --- a/qutip/tests/core/test_dimensions.py +++ b/qutip/tests/core/test_dimensions.py @@ -198,7 +198,9 @@ def test_dims_comparison(): pytest.param("ij,ji", [qutip.sigmaz(), qutip.sigmaz()], 2), pytest.param("ii", [qutip.tensor(qutip.thermal_dm(2,1), qutip.thermal_dm(2,1))], 1), pytest.param("ik, kj", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz()), - qutip.tensor(qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(4)) + qutip.tensor(qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(4)), + pytest.param("ik, kj", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz(), qutip.sigmaz()), + qutip.tensor(qutip.sigmaz(), qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(8)) ]) def test_einsum(subscripts, operands, expected): assert einsum(subscripts, *operands) == expected \ No newline at end of file From 8ddc14d08daa82c85d83de3f0a7dacef7e115f7b Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Fri, 18 Oct 2024 13:49:16 +0200 Subject: [PATCH 09/14] fix compound dims --- qutip/core/dimensions.py | 4 ++-- qutip/tests/core/test_dimensions.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index eb13af82bf..fb5bf527a7 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -369,11 +369,11 @@ def einsum(subscripts, *operands): Qobj (numpy.complex128) Result of einsum as Qobj (numpy.complex128 if result is scalar) """ - operands_array = [to_tensor_rep(op).reshape(op.shape) for op in operands] + operands_array = [to_tensor_rep(op) for op in operands] result = np.einsum(subscripts, *operands_array) if result.shape == (): return result - dims = [[d] for d in result.shape] + dims = [d for d in result.shape[:result.ndim // 2]], [[d for d in result.shape[result.ndim // 2:]]] return from_tensor_rep(result, dims) diff --git a/qutip/tests/core/test_dimensions.py b/qutip/tests/core/test_dimensions.py index d4ab6354f3..96b2ed4cde 100644 --- a/qutip/tests/core/test_dimensions.py +++ b/qutip/tests/core/test_dimensions.py @@ -196,11 +196,10 @@ def test_dims_comparison(): pytest.param("ij", [qutip.sigmax()], qutip.sigmax()), pytest.param("ij->ji", [qutip.sigmay()], qutip.sigmay().trans()), pytest.param("ij,ji", [qutip.sigmaz(), qutip.sigmaz()], 2), - pytest.param("ii", [qutip.tensor(qutip.thermal_dm(2,1), qutip.thermal_dm(2,1))], 1), - pytest.param("ik, kj", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz()), - qutip.tensor(qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(4)), - pytest.param("ik, kj", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz(), qutip.sigmaz()), - qutip.tensor(qutip.sigmaz(), qutip.sigmaz(), qutip.sigmaz())], qutip.qeye(8)) + pytest.param("ijij", [qutip.tensor(qutip.thermal_dm(2,1), qutip.thermal_dm(2,1))], 1), + pytest.param("ikjl,jm->ikml", [qutip.tensor(qutip.sigmaz(), qutip.sigmaz()), + qutip.sigmaz()], qutip.tensor(qutip.qeye(2), qutip.sigmaz())), + pytest.param("ijkl->kjil", [qutip.tensor(qutip.sigmam(), qutip.sigmaz())], qutip.tensor(qutip.sigmap(), qutip.sigmaz())) ]) def test_einsum(subscripts, operands, expected): assert einsum(subscripts, *operands) == expected \ No newline at end of file From a63234376133b890b6012374a2843a9e147a082f Mon Sep 17 00:00:00 2001 From: fmayo96 Date: Fri, 18 Oct 2024 13:51:51 +0200 Subject: [PATCH 10/14] fix format --- qutip/core/dimensions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index fb5bf527a7..fbcaa82aa6 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -373,7 +373,8 @@ def einsum(subscripts, *operands): result = np.einsum(subscripts, *operands_array) if result.shape == (): return result - dims = [d for d in result.shape[:result.ndim // 2]], [[d for d in result.shape[result.ndim // 2:]]] + dims = [d for d in result.shape[:result.ndim // 2]], + [[d for d in result.shape[result.ndim // 2:]]] return from_tensor_rep(result, dims) From 49f019d089d759bcd6fedb0d1f9e4f9cf3b5ece1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Gigu=C3=A8re?= Date: Fri, 18 Oct 2024 09:45:29 -0400 Subject: [PATCH 11/14] Update qutip/core/dimensions.py --- qutip/core/dimensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py index fbcaa82aa6..56fe353964 100644 --- a/qutip/core/dimensions.py +++ b/qutip/core/dimensions.py @@ -373,8 +373,10 @@ def einsum(subscripts, *operands): result = np.einsum(subscripts, *operands_array) if result.shape == (): return result - dims = [d for d in result.shape[:result.ndim // 2]], - [[d for d in result.shape[result.ndim // 2:]]] + dims = [ + [d for d in result.shape[:result.ndim // 2]], + [d for d in result.shape[result.ndim // 2:]] + ] return from_tensor_rep(result, dims) From f05eff50d4ce74205a1319b117805f074fbd0730 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 18 Nov 2024 11:32:23 +0900 Subject: [PATCH 12/14] Merge pull request #2554 from ausstein/patch-1 c_{k,real} should not have -i in the HEOM user guide --- doc/guide/heom/bosonic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guide/heom/bosonic.rst b/doc/guide/heom/bosonic.rst index 48856a1187..fa56357320 100644 --- a/doc/guide/heom/bosonic.rst +++ b/doc/guide/heom/bosonic.rst @@ -271,7 +271,7 @@ coefficients of this expansion are, for the real part, :math:`C_{real}(t)`: \end{cases} c_{k,real} &= \begin{cases} - \lambda \gamma [\cot(\beta \gamma / 2) - i] & k = 0\\ + \lambda \gamma [\cot(\beta \gamma / 2) ] & k = 0\\ \frac{4 \lambda \gamma \nu_k }{ (\nu_k^2 - \gamma^2)\beta} & k \geq 1\\ \end{cases} From 978382cec3cae75f8577d10195fadb18b14d2d79 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 19 Nov 2024 09:59:05 +0100 Subject: [PATCH 13/14] Improve performance of qt.Qobj by static version check --- qutip/core/data/dense.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutip/core/data/dense.pyx b/qutip/core/data/dense.pyx index 22c4360aa1..120bd032a0 100644 --- a/qutip/core/data/dense.pyx +++ b/qutip/core/data/dense.pyx @@ -37,10 +37,11 @@ __all__ = [ class OrderEfficiencyWarning(EfficiencyWarning): pass +is_numpy1 = np.lib.NumpyVersion(np.__version__) < '2.0.0b1' cdef class Dense(base.Data): def __init__(self, data, shape=None, copy=True): - if np.lib.NumpyVersion(np.__version__) < '2.0.0b1': + if is_numpy1: # np2 accept None which act as np1's False copy = builtins.bool(copy) base = np.array(data, dtype=np.complex128, order='K', copy=copy) From 13ef614837be0605efe7d8ea146476b962901a75 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 19 Nov 2024 12:06:45 +0100 Subject: [PATCH 14/14] add news entry --- doc/changes/2557.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changes/2557.misc diff --git a/doc/changes/2557.misc b/doc/changes/2557.misc new file mode 100644 index 0000000000..4f470efc77 --- /dev/null +++ b/doc/changes/2557.misc @@ -0,0 +1 @@ +Improve performance of qt.Qobj by using static numpy version check