Fix pickling of `NLayout` (#12298)

Previously the module of this was set incorrectly (stemming from its
move in gh-9064), at which point the `__getstate__`/`__setstate__`
pickling wouldn't work correctly any more.  Also, however, there was no
`__getnewargs__` and `new` didn't have a zero-argument form, so this
wouldn't have worked either.
This commit is contained in:
Jake Lishman 2024-04-25 22:45:10 +01:00 committed by GitHub
parent 686ff139a6
commit 136548c9d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 59 additions and 8 deletions

View File

@ -91,7 +91,7 @@ impl VirtualQubit {
/// physical qubit index on the coupling graph. /// physical qubit index on the coupling graph.
/// logical_qubits (int): The number of logical qubits in the layout /// logical_qubits (int): The number of logical qubits in the layout
/// physical_qubits (int): The number of physical qubits in the layout /// physical_qubits (int): The number of physical qubits in the layout
#[pyclass(module = "qiskit._accelerate.stochastic_swap")] #[pyclass(module = "qiskit._accelerate.nlayout")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NLayout { pub struct NLayout {
virt_to_phys: Vec<PhysicalQubit>, virt_to_phys: Vec<PhysicalQubit>,
@ -117,13 +117,13 @@ impl NLayout {
res res
} }
fn __getstate__(&self) -> (Vec<PhysicalQubit>, Vec<VirtualQubit>) { fn __reduce__(&self, py: Python) -> PyResult<Py<PyAny>> {
(self.virt_to_phys.clone(), self.phys_to_virt.clone()) Ok((
} py.get_type_bound::<Self>()
.getattr("from_virtual_to_physical")?,
fn __setstate__(&mut self, state: (Vec<PhysicalQubit>, Vec<VirtualQubit>)) { (self.virt_to_phys.to_object(py),),
self.virt_to_phys = state.0; )
self.phys_to_virt = state.1; .into_py(py))
} }
/// Return the layout mapping. /// Return the layout mapping.

View File

@ -13,12 +13,14 @@
"""Tests the layout object""" """Tests the layout object"""
import copy import copy
import pickle
import unittest import unittest
import numpy import numpy
from qiskit.circuit import QuantumRegister, Qubit from qiskit.circuit import QuantumRegister, Qubit
from qiskit.transpiler.layout import Layout from qiskit.transpiler.layout import Layout
from qiskit.transpiler.exceptions import LayoutError from qiskit.transpiler.exceptions import LayoutError
from qiskit._accelerate.nlayout import NLayout
from test import QiskitTestCase # pylint: disable=wrong-import-order from test import QiskitTestCase # pylint: disable=wrong-import-order
@ -511,5 +513,54 @@ class LayoutTest(QiskitTestCase):
self.assertEqual(permutation, [1, 2, 0]) self.assertEqual(permutation, [1, 2, 0])
class TestNLayout(QiskitTestCase):
"""This is a private class, so mostly doesn't need direct tests."""
def test_pickle(self):
"""Test that the layout roundtrips through pickle."""
v2p = [3, 5, 1, 2, 0, 4]
size = len(v2p)
layout = NLayout.from_virtual_to_physical(v2p)
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
roundtripped = pickle.loads(pickle.dumps(layout))
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], v2p)
# No changes to `layout`.
roundtripped.swap_virtual(0, 1)
expected = [5, 3, 1, 2, 0, 4]
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], expected)
def test_copy(self):
"""Test that the layout roundtrips through copy."""
v2p = [3, 5, 1, 2, 0, 4]
size = len(v2p)
layout = NLayout.from_virtual_to_physical(v2p)
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
roundtripped = copy.copy(layout)
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], v2p)
# No changes to `layout`.
roundtripped.swap_virtual(0, 1)
expected = [5, 3, 1, 2, 0, 4]
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], expected)
def test_deepcopy(self):
"""Test that the layout roundtrips through deepcopy."""
v2p = [3, 5, 1, 2, 0, 4]
size = len(v2p)
layout = NLayout.from_virtual_to_physical(v2p)
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
roundtripped = copy.deepcopy(layout)
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], v2p)
# No changes to `layout`.
roundtripped.swap_virtual(0, 1)
expected = [5, 3, 1, 2, 0, 4]
self.assertEqual([layout.virtual_to_physical(x) for x in range(size)], v2p)
self.assertEqual([roundtripped.virtual_to_physical(x) for x in range(size)], expected)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()