Skip to content

Commit 511ecbf

Browse files
mergify[bot]kevinhartmanElePT
authored
Fix pickle and repr for Duration. (backport #14174) (#14193)
* Fix pickle and repr for Duration. (#14174) (cherry picked from commit b0bc6d8) * Update releasenotes/notes/fix-duration-props-0543fe1d5e6e2820.yaml * Update releasenotes/notes/fix-duration-props-0543fe1d5e6e2820.yaml --------- Co-authored-by: Kevin Hartman <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]>
1 parent 2bca1b8 commit 511ecbf

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

crates/circuit/src/duration.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use pyo3::prelude::*;
1414
use pyo3::IntoPyObjectExt;
15+
use pyo3::PyTypeInfo;
1516

1617
/// A length of time used to express circuit timing.
1718
///
@@ -74,9 +75,7 @@ impl Duration {
7475
}
7576
}
7677
}
77-
}
7878

79-
impl Duration {
8079
fn __repr__(&self) -> String {
8180
match self {
8281
Duration::ns(t) => format!("Duration.ns({})", t),
@@ -86,4 +85,12 @@ impl Duration {
8685
Duration::dt(t) => format!("Duration.dt({})", t),
8786
}
8887
}
88+
89+
fn __reduce__(&self, py: Python) -> PyResult<Py<PyAny>> {
90+
(
91+
Duration::type_object(py).getattr(self.unit())?,
92+
(self.py_value(py)?,),
93+
)
94+
.into_py_any(py)
95+
}
8996
}

crates/circuit/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ mod rustworkx_core_vnext;
3535

3636
use pyo3::prelude::*;
3737
use pyo3::types::{PySequence, PyTuple};
38+
use pyo3::PyTypeInfo;
3839

3940
pub type BitType = u32;
4041
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq, FromPyObject)]
@@ -169,7 +170,31 @@ pub fn circuit(m: &Bound<PyModule>) -> PyResult<()> {
169170
m.add_class::<bit::PyClassicalRegister>()?;
170171
m.add_class::<bit::PyQuantumRegister>()?;
171172
m.add_class::<bit::PyAncillaRegister>()?;
173+
174+
// We need to explicitly add the auto-generated Python subclasses of Duration
175+
// to the module so that pickle can find them during deserialization.
172176
m.add_class::<duration::Duration>()?;
177+
m.add(
178+
"Duration_ns",
179+
duration::Duration::type_object(m.py()).getattr("ns")?,
180+
)?;
181+
m.add(
182+
"Duration_us",
183+
duration::Duration::type_object(m.py()).getattr("us")?,
184+
)?;
185+
m.add(
186+
"Duration_ms",
187+
duration::Duration::type_object(m.py()).getattr("ms")?,
188+
)?;
189+
m.add(
190+
"Duration_s",
191+
duration::Duration::type_object(m.py()).getattr("s")?,
192+
)?;
193+
m.add(
194+
"Duration_dt",
195+
duration::Duration::type_object(m.py()).getattr("dt")?,
196+
)?;
197+
173198
m.add_class::<circuit_data::CircuitData>()?;
174199
m.add_class::<circuit_instruction::CircuitInstruction>()?;
175200
m.add_class::<dag_circuit::DAGCircuit>()?;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
fixes:
3+
- |
4+
Added missing ``repr`` support for :class:`qiskit.circuit.Duration`.
5+
- |
6+
Added missing support for Python pickling of :class:`qiskit.circuit.Duration`.
7+
This was preventing parallel transpilation of circuits with
8+
:meth:`~.QuantumCircuit.delay` instructions that use duration
9+
expressions.

test/python/circuit/test_delay.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
# pylint: disable=missing-function-docstring
1414

1515
"""Test delay instruction for quantum circuits."""
16+
import copy
17+
import pickle
1618

1719
import numpy as np
1820

19-
from qiskit.circuit import Delay
21+
from qiskit.circuit import Delay, Duration
2022
from qiskit.circuit import Parameter, ParameterVector
2123
from qiskit.circuit import QuantumCircuit, CircuitInstruction
24+
from qiskit.circuit.classical import expr
2225
from qiskit.circuit.exceptions import CircuitError
2326
from test import QiskitTestCase # pylint: disable=wrong-import-order
2427

@@ -136,6 +139,21 @@ def circuit_from(delay):
136139
self.assertNotEqual(Delay(a, unit), Delay(a, "dt"))
137140
self.assertNotEqual(circuit_from(Delay(a, unit)), circuit_from(Delay(a, "dt")))
138141

142+
def test_delay_clone(self):
143+
"""Test that circuits with delays can be copied or pickled."""
144+
qc = QuantumCircuit(3)
145+
stretch = qc.add_stretch("a")
146+
qc.delay(100, qc.qubits[0])
147+
qc.delay(expr.lift(Duration.us(1)), 0)
148+
qc.delay(expr.lift(Duration.ns(2)), 0)
149+
qc.delay(expr.lift(Duration.ms(3)), 0)
150+
qc.delay(expr.lift(Duration.s(4)), 0)
151+
qc.delay(expr.lift(Duration.dt(5)), 0)
152+
qc.delay(stretch, [0, 1])
153+
self.assertEqual(qc, pickle.loads(pickle.dumps(qc)))
154+
self.assertEqual(qc, copy.copy(qc))
155+
self.assertEqual(qc, copy.deepcopy(qc))
156+
139157

140158
class TestParameterizedDelay(QiskitTestCase):
141159
"""Test delay instruction with parameterized duration."""

0 commit comments

Comments
 (0)