From 9fd0ff9f224497cd481cb07a676ee019df827e2c Mon Sep 17 00:00:00 2001 From: "Lev S. Bishop" <18673315+levbishop@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:57:39 -0500 Subject: [PATCH 1/4] Make TwoQubitWeylDecomposition pickle-able Partial fix for #7312 --- .../synthesis/two_qubit_decompose.py | 8 ++++- .../notes/pickle_weyl-34e16e3aab2f7133.yaml | 7 +++++ test/python/quantum_info/test_synthesis.py | 29 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml diff --git a/qiskit/quantum_info/synthesis/two_qubit_decompose.py b/qiskit/quantum_info/synthesis/two_qubit_decompose.py index 2d209ecf97ed..9ce2116db800 100644 --- a/qiskit/quantum_info/synthesis/two_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/two_qubit_decompose.py @@ -133,7 +133,7 @@ def __init_subclass__(cls, **kwargs): ) @staticmethod - def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9)): + def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9), _unpickling=False): """Perform the Weyl chamber decomposition, and optionally choose a specialized subclass. The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63, @@ -145,6 +145,9 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9)): The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph]. """ + if _unpickling: + return super().__new__(cls) + pi = np.pi pi2 = np.pi / 2 pi4 = np.pi / 4 @@ -403,6 +406,9 @@ def actual_fidelity(self, **kwargs) -> float: trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix) return trace_to_fid(trace) + def __getnewargs_ex__(self): + return (self.unitary_matrix,), {"_unpickling": True} + def __repr__(self): """Represent with enough precision to allow copy-paste debugging of all corner cases""" prefix = f"{type(self).__qualname__}.from_bytes(" diff --git a/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml b/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml new file mode 100644 index 000000000000..726da1ebc66a --- /dev/null +++ b/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml @@ -0,0 +1,7 @@ +--- + +fixes: + - | + The class :class:`~qiskit.quantum_info.synthesis.two_qubit_decompose.TwoQubitWeylDecomposition` and its + subclasses are now pickle-safe, which is a partial fix for + `#7312 `__ diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 19b1b82e97f3..df9ac1aa9da7 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -12,6 +12,7 @@ """Tests for quantum synthesis methods.""" +import pickle import unittest import contextlib import logging @@ -173,6 +174,26 @@ def assertRoundTrip(self, weyl1: TwoQubitWeylDecomposition): self.assertEqual(maxdiff, 0, msg=f"K2r matrix differs by {maxdiff}" + msg_base) self.assertEqual(weyl1.requested_fidelity, weyl2.requested_fidelity, msg_base) + def assertRoundTripPickle(self, weyl1: TwoQubitWeylDecomposition): + """Fail if loads(dumps(weyl1)) not equal to weyl1""" + weyl2 = pickle.loads(pickle.dumps(weyl1)) + msg_base = f"weyl1:\n{weyl1}\nweyl2:\n{repr(weyl2)}" + self.assertEqual(type(weyl1), type(weyl2), msg_base) + maxdiff = np.max(abs(weyl1.unitary_matrix - weyl2.unitary_matrix)) + self.assertEqual(maxdiff, 0, msg=f"Unitary matrix differs by {maxdiff}\n" + msg_base) + self.assertEqual(weyl1.a, weyl2.a, msg=msg_base) + self.assertEqual(weyl1.b, weyl2.b, msg=msg_base) + self.assertEqual(weyl1.c, weyl2.c, msg=msg_base) + maxdiff = np.max(np.abs(weyl1.K1l - weyl2.K1l)) + self.assertEqual(maxdiff, 0, msg=f"K1l matrix differs by {maxdiff}" + msg_base) + maxdiff = np.max(np.abs(weyl1.K1r - weyl2.K1r)) + self.assertEqual(maxdiff, 0, msg=f"K1r matrix differs by {maxdiff}" + msg_base) + maxdiff = np.max(np.abs(weyl1.K2l - weyl2.K2l)) + self.assertEqual(maxdiff, 0, msg=f"K2l matrix differs by {maxdiff}" + msg_base) + maxdiff = np.max(np.abs(weyl1.K2r - weyl2.K2r)) + self.assertEqual(maxdiff, 0, msg=f"K2r matrix differs by {maxdiff}" + msg_base) + self.assertEqual(weyl1.requested_fidelity, weyl2.requested_fidelity, msg_base) + def check_two_qubit_weyl_decomposition(self, target_unitary, tolerance=1.0e-12): """Check TwoQubitWeylDecomposition() works for a given operator""" # pylint: disable=invalid-name @@ -207,6 +228,7 @@ def check_two_qubit_weyl_specialization( with self.assertDebugOnly(): decomp = decomposer(target_unitary, fidelity=fidelity) self.assertRoundTrip(decomp) + self.assertRoundTripPickle(decomp) self.assertEqual( np.max(np.abs(decomp.unitary_matrix - target_unitary)), 0, @@ -228,6 +250,7 @@ def check_two_qubit_weyl_specialization( with self.assertDebugOnly(): decomp2 = expected_specialization(target_unitary, fidelity=None) # Shouldn't raise self.assertRoundTrip(decomp2) + self.assertRoundTripPickle(decomp2) if expected_specialization is not TwoQubitWeylGeneral: with self.assertRaises(QiskitError) as exc: _ = expected_specialization(target_unitary, fidelity=1.0) @@ -572,6 +595,12 @@ def test_TwoQubitWeylDecomposition_repr(self, seed=42): weyl1 = TwoQubitWeylDecomposition(target, fidelity=0.99) self.assertRoundTrip(weyl1) + def test_TwoQubitWeylDecomposition_pickle(self, seed=42): + """Check that loads(dumps()) is exact round trip""" + target = random_unitary(4, seed=seed) + weyl1 = TwoQubitWeylDecomposition(target, fidelity=0.99) + self.assertRoundTripPickle(weyl1) + def test_two_qubit_weyl_decomposition_cnot(self): """Verify Weyl KAK decomposition for U~CNOT""" for k1l, k1r, k2l, k2r in K1K2S: From af2cecc294c3b53b75efe80cf1bb0024df79dd66 Mon Sep 17 00:00:00 2001 From: "Lev S. Bishop" <18673315+levbishop@users.noreply.github.com> Date: Wed, 1 Dec 2021 12:15:30 -0500 Subject: [PATCH 2/4] Remove docs xref --- releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml b/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml index 726da1ebc66a..63a322956132 100644 --- a/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml +++ b/releasenotes/notes/pickle_weyl-34e16e3aab2f7133.yaml @@ -2,6 +2,5 @@ fixes: - | - The class :class:`~qiskit.quantum_info.synthesis.two_qubit_decompose.TwoQubitWeylDecomposition` and its - subclasses are now pickle-safe, which is a partial fix for + The class `TwoQubitWeylDecomposition` and its subclasses are now pickle-safe, which is a partial fix for `#7312 `__ From 2027765fd4d1b8fbbe13a48890ff4f24c3887d0b Mon Sep 17 00:00:00 2001 From: "Lev S. Bishop" <18673315+levbishop@users.noreply.github.com> Date: Wed, 1 Dec 2021 15:26:15 -0500 Subject: [PATCH 3/4] Enforce minimum pickle protocol version 4 --- test/python/quantum_info/test_synthesis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index df9ac1aa9da7..b772469d7429 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -176,7 +176,9 @@ def assertRoundTrip(self, weyl1: TwoQubitWeylDecomposition): def assertRoundTripPickle(self, weyl1: TwoQubitWeylDecomposition): """Fail if loads(dumps(weyl1)) not equal to weyl1""" - weyl2 = pickle.loads(pickle.dumps(weyl1)) + + pkl = pickle.dumps(weyl1, protocol=max(4, pickle.DEFAULT_PROTOCOL)) + weyl2 = pickle.loads(pkl) msg_base = f"weyl1:\n{weyl1}\nweyl2:\n{repr(weyl2)}" self.assertEqual(type(weyl1), type(weyl2), msg_base) maxdiff = np.max(abs(weyl1.unitary_matrix - weyl2.unitary_matrix)) From f688f87947522d1639c945732fa0b6e72a64d58f Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 1 Nov 2022 19:27:55 +0000 Subject: [PATCH 4/4] Add release note --- .../notes/fix-twoqubit-pickle-8628047aa396919a.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/fix-twoqubit-pickle-8628047aa396919a.yaml diff --git a/releasenotes/notes/fix-twoqubit-pickle-8628047aa396919a.yaml b/releasenotes/notes/fix-twoqubit-pickle-8628047aa396919a.yaml new file mode 100644 index 000000000000..2a0f3b4548be --- /dev/null +++ b/releasenotes/notes/fix-twoqubit-pickle-8628047aa396919a.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + The class :class:`.TwoQubitWeylDecomposition` is now compatible with the + ``pickle`` protocol. Previously, it would fail to deserialize and would + raise a ``TypeError``. + See `#7312 `__.