From ea258fe4f05f5436aae8b74ca09c6ae7391fede5 Mon Sep 17 00:00:00 2001 From: Graham Enos Date: Tue, 20 Sep 2022 18:19:08 -0400 Subject: [PATCH] Fix: Ensure adding programs doesn't mutate the first Closes #1476 As noted in #1476, adding programs (like `p1 + p2`) currently mutates the first program (i.e. `p1` would change). This PR ensures that calibrations et. al. are copied rather than just assigned, and adds a new test case to `unit/test_program.py` to ensure against regression: ```shell $ poetry run pytest test/unit/test_program.py --verbose ==================================== test session starts ===================================== platform darwin -- Python 3.9.13, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/genos/rc/repos/pyquil/.venv/bin/python cachedir: .pytest_cache rootdir: /Users/genos/rc/repos/pyquil plugins: cov-2.12.1, freezegun-0.4.2, rerunfailures-9.1.1, xdist-2.3.0, mock-3.6.1, timeout-1.4.2, forked-1.3.0 collected 5 items test/unit/test_program.py::test_measure_qubits PASSED [ 20%] test/unit/test_program.py::test_parameterized_single_qubit_measurement_basis PASSED [ 40%] test/unit/test_program.py::test_parameterized_single_qubit_state_preparation PASSED [ 60%] test/unit/test_program.py::test_parameterized_readout_symmetrization PASSED [ 80%] test/unit/test_program.py::test_adding_does_not_mutate PASSED [100%] ``` --- pyquil/quil.py | 6 +++--- test/unit/test_program.py | 28 ++++++++++++++++++++++++++++ test/unit/test_quil.py | 0 3 files changed, 31 insertions(+), 3 deletions(-) mode change 100755 => 100644 test/unit/test_quil.py diff --git a/pyquil/quil.py b/pyquil/quil.py index 2fabf8642..15fd7b56c 100644 --- a/pyquil/quil.py +++ b/pyquil/quil.py @@ -874,9 +874,9 @@ def __add__(self, other: InstructionDesignator) -> "Program": p = Program() p.inst(self) p.inst(other) - p._calibrations = self.calibrations - p._waveforms = self.waveforms - p._frames = self.frames + p._calibrations = self.calibrations.copy() + p._waveforms = self.waveforms.copy() + p._frames = self.frames.copy() p._memory = self._memory.copy() if isinstance(other, Program): p.calibrations.extend(other.calibrations) diff --git a/test/unit/test_program.py b/test/unit/test_program.py index b4a474792..06d2bab7c 100644 --- a/test/unit/test_program.py +++ b/test/unit/test_program.py @@ -56,3 +56,31 @@ def test_parameterized_readout_symmetrization(): p += RX(symmetrization[0], 0) p += RX(symmetrization[1], 1) assert parameterized_readout_symmetrization([0, 1]).out() == p.out() + + +def test_adding_does_not_mutate(): + # https://github.com/rigetti/pyquil/issues/1476 + p1 = Program( + """ +DEFCAL RX(pi/2) 32: + FENCE 32 + NONBLOCKING PULSE 32 "rf" drag_gaussian(duration: 3.2e-08, fwhm: 8e-09, t0: 1.6e-08, anh: -190000000.0, alpha: -1.8848698349348032, scale: 0.30631340170943533, phase: 0.0, detuning: 1622438.2425563578) + FENCE 32 + +RX(pi/2) 32 +""" + ) + original_p1 = p1.copy() + p2 = Program( + """ +DEFCAL RX(pi/2) 33: + FENCE 33 + NONBLOCKING PULSE 33 "rf" drag_gaussian(duration: 2e-08, fwhm: 5e-09, t0: 1e-08, anh: -190000000.0, alpha: -0.9473497322033984, scale: 0.25680107985232403, phase: 0.0, detuning: 1322130.5458282642) + FENCE 33 + +RX(pi/2) 33 +""" + ) + p_all = p1 + p2 + assert p1 == original_p1 + assert p1.calibrations != p_all.calibrations diff --git a/test/unit/test_quil.py b/test/unit/test_quil.py old mode 100755 new mode 100644