-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make TwoQubitWeylDecomposition pickle-able #7333
Changes from 5 commits
9fd0ff9
af2cecc
2027765
332f7d3
ebab460
5c2507b
f688f87
b462a32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
|
||
fixes: | ||
- | | ||
The class `TwoQubitWeylDecomposition` and its subclasses are now pickle-safe, which is a partial fix for | ||
`#7312 <https://github.com/Qiskit/qiskit-terra/issues/7312>`__ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
|
||
"""Tests for quantum synthesis methods.""" | ||
|
||
import pickle | ||
import unittest | ||
import contextlib | ||
import logging | ||
|
@@ -173,6 +174,28 @@ 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""" | ||
|
||
pkl = pickle.dumps(weyl1, protocol=max(4, pickle.DEFAULT_PROTOCOL)) | ||
weyl2 = pickle.loads(pkl) | ||
msg_base = f"weyl1:\n{weyl1}\nweyl2:\n{repr(weyl2)}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I learned recently that |
||
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 +230,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 +252,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 +597,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: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may also be worth adding a very similar test that
copy.deepcopy
andcopy.copy
work correctly as well/deepcopy
presumably will because it's a full pickle/unpickle by default, butcopy
might do something funny; I think it basically calls__getstate__
, then restores it with an immediate call to__setstate__
without recursing the pickle through the state. I would guess it does the right thing and calls__getnewargs_ex__
if defined, but I don't really know, and it might be worth a test, since Qiskit uses thecopy
module pretty extensively.