From 520f01e249758fdfde90c364b68cbf77d84a3bc3 Mon Sep 17 00:00:00 2001 From: qottmann Date: Mon, 22 Jul 2024 18:05:26 +0200 Subject: [PATCH 01/17] fix lie closure to work with sums of paulis --- pennylane/pauli/dla/lie_closure.py | 20 ++++++++++++++++++-- tests/pauli/dla/test_lie_closure.py | 4 +++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index aadfad83210..d5d204d8cdd 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -14,6 +14,7 @@ """A function to compute the Lie closure of a set of operators""" # pylint: disable=too-many-arguments import itertools +import warnings from copy import copy from functools import reduce from typing import Iterable, Union @@ -32,6 +33,7 @@ def lie_closure( verbose: bool = False, pauli: bool = False, tol: float = None, + dtype = None ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. @@ -49,6 +51,10 @@ def lie_closure( This can help with performance to avoid unnecessary conversions to :class:`~pennylane.operation.Operator` and vice versa. Default is ``False``. tol (float): Numerical tolerance for the linear independence check used in :class:`~.PauliVSpace`. + dtype: Dtype for the underlyding matrix representation in :class:`~.PauliVSpace`. + For generators that are pure Pauli words (:class:`~PauliWord`), it can be set + to ``float``. For sums of Paulis (i.e. :class:`~PauliSentence`) it should be + set to ``complex`` (default). Returns: Union[list[:class:`~.PauliSentence`], list[:class:`~.Operator`]]: a basis of either :class:`~.PauliSentence` or :class:`~.Operator` instances that is closed under @@ -78,7 +84,7 @@ def lie_closure( This can be done in short via ``lie_closure`` as follows. >>> ops = [X(0) @ X(1), Z(0), Z(1)] - >>> dla = qml.lie_closure(ops) + >>> dla = qml.lie_closure(ops, dtype=float) >>> print(dla) [X(1) @ X(0), Z(0), @@ -125,7 +131,10 @@ def lie_closure( for op in generators ] - vspace = PauliVSpace(generators, tol=tol) + if dtype is None: + dtype = complex + + vspace = PauliVSpace(generators, tol=tol, dtype=dtype) epoch = 0 old_length = 0 # dummy value @@ -134,8 +143,11 @@ def lie_closure( while (new_length > old_length) and (epoch < max_iterations): if verbose: print(f"epoch {epoch+1} of lie_closure, DLA size is {new_length}") + print(f"Basis: {vspace}") for ps1, ps2 in itertools.combinations(vspace.basis, 2): com = ps1.commutator(ps2) + com.simplify() + if len(com) == 0: # skip because operators commute continue @@ -143,6 +155,7 @@ def lie_closure( # remove common factor 2 with Pauli commutators for pw, val in com.items(): com[pw] = val.imag / 2 + vspace.add(com, tol=tol) # Updated number of linearly independent PauliSentences from previous and current step @@ -150,6 +163,9 @@ def lie_closure( new_length = len(vspace) epoch += 1 + if epoch == max_iterations: + warnings.warn(f"reached the maximum number of iterations {max_iterations}", UserWarning) + if verbose > 0: print(f"After {epoch} epochs, reached a DLA size of {new_length}") diff --git a/tests/pauli/dla/test_lie_closure.py b/tests/pauli/dla/test_lie_closure.py index fc2f4361e90..7927b5f252a 100644 --- a/tests/pauli/dla/test_lie_closure.py +++ b/tests/pauli/dla/test_lie_closure.py @@ -344,7 +344,9 @@ def test_max_iterations(self, capsys): PauliSentence({PauliWord({i: "X", (i + 1) % n: "Z"}): 1.0}) for i in range(n - 1) ] - res = qml.pauli.lie_closure(generators, verbose=True, max_iterations=1) + with pytest.warns(UserWarning, match="reached the maximum number of iterations"): + res = qml.pauli.lie_closure(generators, verbose=True, max_iterations=1) + captured = capsys.readouterr() assert ( captured.out From 1f23bb733760144a4f61f71561368cea2eab6194 Mon Sep 17 00:00:00 2001 From: qottmann Date: Mon, 22 Jul 2024 18:05:52 +0200 Subject: [PATCH 02/17] black --- pennylane/pauli/dla/lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index d5d204d8cdd..04113f3961b 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -33,7 +33,7 @@ def lie_closure( verbose: bool = False, pauli: bool = False, tol: float = None, - dtype = None + dtype=None, ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. From f2882b9c215fffa4ecf245510ec8d5c4a8cfbc25 Mon Sep 17 00:00:00 2001 From: qottmann Date: Mon, 22 Jul 2024 18:07:52 +0200 Subject: [PATCH 03/17] [ci skip] From 2e92770fc1f83eca3c2adb85fa16a42f1358e0dd Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 00:14:27 +0200 Subject: [PATCH 04/17] updates --- pennylane/pauli/dla/lie_closure.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 04113f3961b..6a9b261b585 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -143,7 +143,7 @@ def lie_closure( while (new_length > old_length) and (epoch < max_iterations): if verbose: print(f"epoch {epoch+1} of lie_closure, DLA size is {new_length}") - print(f"Basis: {vspace}") + for ps1, ps2 in itertools.combinations(vspace.basis, 2): com = ps1.commutator(ps2) com.simplify() @@ -156,6 +156,7 @@ def lie_closure( for pw, val in com.items(): com[pw] = val.imag / 2 + print(com) vspace.add(com, tol=tol) # Updated number of linearly independent PauliSentences from previous and current step From 604ab3d392641e4cb041ae3712df46cd3110f4d6 Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 13:47:58 +0200 Subject: [PATCH 05/17] remove print --- pennylane/pauli/dla/lie_closure.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 6a9b261b585..26b6d679cd5 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -156,7 +156,6 @@ def lie_closure( for pw, val in com.items(): com[pw] = val.imag / 2 - print(com) vspace.add(com, tol=tol) # Updated number of linearly independent PauliSentences from previous and current step From 9ff10af7c272c4a62272e8a090788cb203ba34aa Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:35:19 +0200 Subject: [PATCH 06/17] test for summed heisenberg model --- tests/pauli/dla/test_lie_closure.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/pauli/dla/test_lie_closure.py b/tests/pauli/dla/test_lie_closure.py index 7927b5f252a..f035f24370c 100644 --- a/tests/pauli/dla/test_lie_closure.py +++ b/tests/pauli/dla/test_lie_closure.py @@ -502,6 +502,17 @@ def test_lie_closure_heisenberg_generators_even(self): res = qml.pauli.lie_closure(generators) assert len(res) == 4 * ((2 ** (n - 2)) ** 2 - 1) + + @pytest.mark.parametrize("n, res", [(3, 4), (4, 12), (5, 40)]) + def test_lie_closure_heisenberg(self, n, res): + """Test the resulting DLA from Heisenberg model with summed generators""" + genXX = [X(i) @ X(i+1) for i in range(n-1)] + genYY = [Y(i) @ Y(i+1) for i in range(n-1)] + genZZ = [Z(i) @ Z(i+1) for i in range(n-1)] + + generators = [qml.sum(XX + YY + ZZ) for XX, YY, ZZ in zip(genXX, genYY, genZZ)] + g = qml.lie_closure(generators, dtype=complex) + assert len(g) == res def test_universal_gate_set(self): """Test universal gate set""" From 6bb1ee6e65497780af6fe6ebb38a14c9381fac7f Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:36:08 +0200 Subject: [PATCH 07/17] test for summed heisenberg model --- tests/pauli/dla/test_lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pauli/dla/test_lie_closure.py b/tests/pauli/dla/test_lie_closure.py index f035f24370c..cf254755664 100644 --- a/tests/pauli/dla/test_lie_closure.py +++ b/tests/pauli/dla/test_lie_closure.py @@ -503,7 +503,7 @@ def test_lie_closure_heisenberg_generators_even(self): res = qml.pauli.lie_closure(generators) assert len(res) == 4 * ((2 ** (n - 2)) ** 2 - 1) - @pytest.mark.parametrize("n, res", [(3, 4), (4, 12), (5, 40)]) + @pytest.mark.parametrize("n, res", [(3, 4), (4, 12)]) def test_lie_closure_heisenberg(self, n, res): """Test the resulting DLA from Heisenberg model with summed generators""" genXX = [X(i) @ X(i+1) for i in range(n-1)] From d1d0df08fbea031d127ed2f06e19f148c5471ed4 Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:36:12 +0200 Subject: [PATCH 08/17] black formatting --- tests/pauli/dla/test_lie_closure.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/pauli/dla/test_lie_closure.py b/tests/pauli/dla/test_lie_closure.py index cf254755664..e61eae472b9 100644 --- a/tests/pauli/dla/test_lie_closure.py +++ b/tests/pauli/dla/test_lie_closure.py @@ -502,13 +502,13 @@ def test_lie_closure_heisenberg_generators_even(self): res = qml.pauli.lie_closure(generators) assert len(res) == 4 * ((2 ** (n - 2)) ** 2 - 1) - + @pytest.mark.parametrize("n, res", [(3, 4), (4, 12)]) def test_lie_closure_heisenberg(self, n, res): """Test the resulting DLA from Heisenberg model with summed generators""" - genXX = [X(i) @ X(i+1) for i in range(n-1)] - genYY = [Y(i) @ Y(i+1) for i in range(n-1)] - genZZ = [Z(i) @ Z(i+1) for i in range(n-1)] + genXX = [X(i) @ X(i + 1) for i in range(n - 1)] + genYY = [Y(i) @ Y(i + 1) for i in range(n - 1)] + genZZ = [Z(i) @ Z(i + 1) for i in range(n - 1)] generators = [qml.sum(XX + YY + ZZ) for XX, YY, ZZ in zip(genXX, genYY, genZZ)] g = qml.lie_closure(generators, dtype=complex) From e21c978543cb8e461a02dbbeb48b6542a331771f Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:48:54 +0200 Subject: [PATCH 09/17] changelog --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index cf17ad442a4..68aaac3d804 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -138,6 +138,9 @@ * `qml.AmplitudeEmbedding` has better support for features using low precision integer data types. [(#5969)](https://github.com/PennyLaneAI/pennylane/pull/5969) +* `qml.lie_closure` works with sums of Paulis. + [(#6023)](https://github.com/PennyLaneAI/pennylane/pull/6023) +

Contributors ✍️

@@ -152,6 +155,7 @@ Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, Renke Huang, +Korbinian Kottmann, Christina Lee, Austin Huang, Christina Lee, From 9c8bbfefaa557eb5787bd23987fd64bb791be11a Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:49:06 +0200 Subject: [PATCH 10/17] formatting --- pennylane/pauli/dla/lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 26b6d679cd5..833105415e5 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -33,7 +33,7 @@ def lie_closure( verbose: bool = False, pauli: bool = False, tol: float = None, - dtype=None, + dtype = None, ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. From 7d76dc93cf096617136b66ddc7e036dd825fe898 Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 14:49:09 +0200 Subject: [PATCH 11/17] black formatting --- pennylane/pauli/dla/lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 833105415e5..26b6d679cd5 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -33,7 +33,7 @@ def lie_closure( verbose: bool = False, pauli: bool = False, tol: float = None, - dtype = None, + dtype=None, ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. From 3efb200e4fee686f8e14a64f882cf377f0050f04 Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 16:18:58 +0200 Subject: [PATCH 12/17] update --- pennylane/pauli/dla/lie_closure.py | 8 ++------ tests/pauli/dla/test_lie_closure.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 26b6d679cd5..3163b7dd979 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -32,8 +32,7 @@ def lie_closure( max_iterations: int = 10000, verbose: bool = False, pauli: bool = False, - tol: float = None, - dtype=None, + tol: float = None ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. @@ -131,10 +130,7 @@ def lie_closure( for op in generators ] - if dtype is None: - dtype = complex - - vspace = PauliVSpace(generators, tol=tol, dtype=dtype) + vspace = PauliVSpace(generators, tol=tol, dtype=float) epoch = 0 old_length = 0 # dummy value diff --git a/tests/pauli/dla/test_lie_closure.py b/tests/pauli/dla/test_lie_closure.py index e61eae472b9..50babf8e000 100644 --- a/tests/pauli/dla/test_lie_closure.py +++ b/tests/pauli/dla/test_lie_closure.py @@ -511,7 +511,7 @@ def test_lie_closure_heisenberg(self, n, res): genZZ = [Z(i) @ Z(i + 1) for i in range(n - 1)] generators = [qml.sum(XX + YY + ZZ) for XX, YY, ZZ in zip(genXX, genYY, genZZ)] - g = qml.lie_closure(generators, dtype=complex) + g = qml.lie_closure(generators) assert len(g) == res def test_universal_gate_set(self): From 417d531b99c47d28cedcafd6166281aa08c06d4d Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 16:19:01 +0200 Subject: [PATCH 13/17] black formatting --- pennylane/pauli/dla/lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 3163b7dd979..77b66fb5b6f 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -32,7 +32,7 @@ def lie_closure( max_iterations: int = 10000, verbose: bool = False, pauli: bool = False, - tol: float = None + tol: float = None, ) -> Iterable[Union[PauliWord, PauliSentence, Operator]]: r"""Compute the dynamical Lie algebra from a set of generators. From 3df11e838bdf5f222518695bf7dc99a2c27dbf8c Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 16:19:57 +0200 Subject: [PATCH 14/17] update docs --- pennylane/pauli/dla/lie_closure.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 77b66fb5b6f..cccf63ddc9e 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -50,10 +50,6 @@ def lie_closure( This can help with performance to avoid unnecessary conversions to :class:`~pennylane.operation.Operator` and vice versa. Default is ``False``. tol (float): Numerical tolerance for the linear independence check used in :class:`~.PauliVSpace`. - dtype: Dtype for the underlyding matrix representation in :class:`~.PauliVSpace`. - For generators that are pure Pauli words (:class:`~PauliWord`), it can be set - to ``float``. For sums of Paulis (i.e. :class:`~PauliSentence`) it should be - set to ``complex`` (default). Returns: Union[list[:class:`~.PauliSentence`], list[:class:`~.Operator`]]: a basis of either :class:`~.PauliSentence` or :class:`~.Operator` instances that is closed under @@ -83,7 +79,7 @@ def lie_closure( This can be done in short via ``lie_closure`` as follows. >>> ops = [X(0) @ X(1), Z(0), Z(1)] - >>> dla = qml.lie_closure(ops, dtype=float) + >>> dla = qml.lie_closure(ops) >>> print(dla) [X(1) @ X(0), Z(0), From b4895c3781f89407d25d0fcf1cf863aa45ac05a3 Mon Sep 17 00:00:00 2001 From: qottmann Date: Tue, 23 Jul 2024 17:04:34 +0200 Subject: [PATCH 15/17] remove dtype mention --- pennylane/pauli/dla/lie_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index cccf63ddc9e..a6e18eb53fb 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -126,7 +126,7 @@ def lie_closure( for op in generators ] - vspace = PauliVSpace(generators, tol=tol, dtype=float) + vspace = PauliVSpace(generators, tol=tol) epoch = 0 old_length = 0 # dummy value From 00cf476351aecf9cfe476753247786630e704d76 Mon Sep 17 00:00:00 2001 From: qottmann Date: Thu, 25 Jul 2024 14:04:15 +0200 Subject: [PATCH 16/17] black formatting --- pennylane/pauli/dla/lie_closure.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 7ed9a939314..01ed9c4a11a 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -139,7 +139,9 @@ def lie_closure( for ps1, ps2 in itertools.combinations(vspace.basis, 2): com = ps1.commutator(ps2) + print("1", com) com.simplify() + print("2", com) if len(com) == 0: # skip because operators commute continue From 8dd7c306ac96149ab117a5e8e672e95f3742a5d8 Mon Sep 17 00:00:00 2001 From: qottmann Date: Thu, 25 Jul 2024 16:43:50 +0200 Subject: [PATCH 17/17] remove prints --- pennylane/pauli/dla/lie_closure.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane/pauli/dla/lie_closure.py b/pennylane/pauli/dla/lie_closure.py index 01ed9c4a11a..7ed9a939314 100644 --- a/pennylane/pauli/dla/lie_closure.py +++ b/pennylane/pauli/dla/lie_closure.py @@ -139,9 +139,7 @@ def lie_closure( for ps1, ps2 in itertools.combinations(vspace.basis, 2): com = ps1.commutator(ps2) - print("1", com) com.simplify() - print("2", com) if len(com) == 0: # skip because operators commute continue