From 8e3401d868629f1786578f17882ba34dd9410d40 Mon Sep 17 00:00:00 2001 From: Yael Ben-Haim Date: Tue, 11 Aug 2026 11:28:41 +0300 Subject: [PATCH 1/4] Add MeasureReset instruction --- .../circuit/library/__init__.py | 2 +- .../circuit/library/mid_circuit_measure.py | 27 ++- test/unit/circuit/test_mid_circ_meas.py | 167 ++++++++++++------ 3 files changed, 139 insertions(+), 57 deletions(-) diff --git a/qiskit_ibm_runtime/circuit/library/__init__.py b/qiskit_ibm_runtime/circuit/library/__init__.py index cc380c7d3f..73db6be690 100644 --- a/qiskit_ibm_runtime/circuit/library/__init__.py +++ b/qiskit_ibm_runtime/circuit/library/__init__.py @@ -12,4 +12,4 @@ """Module for vendor-specific instructions.""" -from .mid_circuit_measure import MidCircuitMeasure, MidCircuitReset +from .mid_circuit_measure import MeasureReset, MidCircuitMeasure, MidCircuitReset diff --git a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py index 49d92079b6..bcc430e71b 100644 --- a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py +++ b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py @@ -26,7 +26,7 @@ class MidCircuitMeasure(Instruction): def __init__(self, name: str = "measure_2", label: str | None = None) -> None: if not name.startswith("measure_"): raise ValueError( - "Invalid name for mid-circuit measure instruction." + "Invalid name for mid-circuit measure instruction. " "The provided name must start with `measure_`" ) @@ -44,8 +44,31 @@ class MidCircuitReset(Instruction): def __init__(self, name: str = "reset_2", label: str | None = None) -> None: if not name.startswith("reset_"): raise ValueError( - "Invalid name for mid-circuit reset instruction." + "Invalid name for mid-circuit reset instruction. " "The provided name must start with `reset_`" ) super().__init__(name, 1, 0, [], label=label) + + +class MeasureReset(Instruction): + """A reset that exposes the measurement it performs internally. + + This instruction implements a reset operation whose intermediate measurement + result is captured in a classical bit, making it observable to the user. + It uses 1 quantum bit and 1 classical bit, and its name must start with + ``measure_reset``. + + The semantics are: measure the qubit into the classical bit, then + conditionally flip the qubit back to |0> — i.e., a reset whose measurement + outcome is retained. + """ + + def __init__(self, name: str = "measure_reset", label: str | None = None) -> None: + if not name.startswith("measure_reset"): + raise ValueError( + "Invalid name for MeasureReset instruction. " + "The provided name must start with `measure_reset`" + ) + + super().__init__(name, 1, 1, [], label=label) diff --git a/test/unit/circuit/test_mid_circ_meas.py b/test/unit/circuit/test_mid_circ_meas.py index dd7927271e..d2bc9c90c1 100644 --- a/test/unit/circuit/test_mid_circ_meas.py +++ b/test/unit/circuit/test_mid_circ_meas.py @@ -17,7 +17,7 @@ from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.transpiler.exceptions import TranspilerError -from qiskit_ibm_runtime.circuit import MidCircuitMeasure, MidCircuitReset +from qiskit_ibm_runtime.circuit import MeasureReset, MidCircuitMeasure, MidCircuitReset from qiskit_ibm_runtime.fake_provider import FakeVigoV2 from ...ibm_test_case import IBMTestCase @@ -92,62 +92,121 @@ def test_transpiler_compat_with(self): transpiled = pm.run(qc) self.assertEqual(transpiled.data[0].operation.name, "measure_2") - class TestMidCircuitReset(IBMTestCase): - """Test MidCircuitReset instruction.""" - def test_instantiation(self): - """Test default instantiation.""" - mcr = MidCircuitReset() +class TestMidCircuitReset(IBMTestCase): + """Test MidCircuitReset instruction.""" + + def test_instantiation(self): + """Test default instantiation.""" + mcr = MidCircuitReset() + self.assertIs(mcr.base_class, MidCircuitReset) + self.assertIsInstance(mcr, Instruction) + self.assertEqual(mcr.name, "reset_2") + self.assertEqual(mcr.num_qubits, 1) + self.assertEqual(mcr.num_clbits, 0) + + def test_instantiation_name(self): + """Test instantiation with custom name.""" + with self.subTest("reset_3"): + mcr = MidCircuitReset("reset_3") self.assertIs(mcr.base_class, MidCircuitReset) self.assertIsInstance(mcr, Instruction) - self.assertEqual(mcr.name, "reset_2") + self.assertEqual(mcr.name, "reset_3") self.assertEqual(mcr.num_qubits, 1) self.assertEqual(mcr.num_clbits, 0) - def test_instantiation_name(self): - """Test instantiation with custom name.""" - with self.subTest("reset_3"): - mcr = MidCircuitReset("reset_3") - self.assertIs(mcr.base_class, MidCircuitReset) - self.assertIsInstance(mcr, Instruction) - self.assertEqual(mcr.name, "reset_3") - self.assertEqual(mcr.num_qubits, 1) - self.assertEqual(mcr.num_clbits, 0) - - with self.subTest("invalid_name"): - with self.assertRaises(ValueError): - MidCircuitReset("invalid_name") - - def test_circuit_integration(self): - """Test appending to circuit.""" - mcr = MidCircuitReset() - qc = QuantumCircuit(1, 2) - qc.append(mcr, [0]) - qc.append(mcr, [0]) - qc.reset(0) - self.assertIs(qc.data[0].operation, mcr) - self.assertIs(qc.data[1].operation, mcr) - - def test_transpiler_compat_without(self): - """Test that default pass manager FAILS if reset_2 is not in Target.""" - mcr = MidCircuitReset() - backend = FakeVigoV2() - pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) - qc = QuantumCircuit(1, 2) - qc.append(mcr, [0]) - with self.assertRaises(TranspilerError): - _ = pm.run(qc) - - def test_transpiler_compat_with(self): - """Test default PM passes if reset_2 is in Target. - - Verifies it does not modify the instruction. - """ - mcr = MidCircuitReset() - backend = GenericBackendV2(num_qubits=5, seed=0) - backend.target.add_instruction(mcr, {(i,): None for i in range(5)}) - pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) - qc = QuantumCircuit(1, 2) - qc.append(mcr, [0]) - transpiled = pm.run(qc) - self.assertEqual(transpiled.data[0].operation.name, "reset_2") + with self.subTest("invalid_name"): + with self.assertRaises(ValueError): + MidCircuitReset("invalid_name") + + def test_circuit_integration(self): + """Test appending to circuit.""" + mcr = MidCircuitReset() + qc = QuantumCircuit(1, 2) + qc.append(mcr, [0]) + qc.append(mcr, [0]) + qc.reset(0) + self.assertIs(qc.data[0].operation, mcr) + self.assertIs(qc.data[1].operation, mcr) + + def test_transpiler_compat_without(self): + """Test that default pass manager FAILS if reset_2 is not in Target.""" + mcr = MidCircuitReset() + backend = FakeVigoV2() + pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) + qc = QuantumCircuit(1, 2) + qc.append(mcr, [0]) + with self.assertRaises(TranspilerError): + _ = pm.run(qc) + + def test_transpiler_compat_with(self): + """Test default PM passes if reset_2 is in Target. + + Verifies it does not modify the instruction. + """ + mcr = MidCircuitReset() + backend = GenericBackendV2(num_qubits=5, seed=0) + backend.target.add_instruction(mcr, {(i,): None for i in range(5)}) + pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) + qc = QuantumCircuit(1, 2) + qc.append(mcr, [0]) + transpiled = pm.run(qc) + self.assertEqual(transpiled.data[0].operation.name, "reset_2") + + +class TestMeasureReset(IBMTestCase): + """Test MeasureReset instruction.""" + + def test_instantiation(self): + """Test default instantiation.""" + mr = MeasureReset() + self.assertIs(mr.base_class, MeasureReset) + self.assertIsInstance(mr, Instruction) + self.assertEqual(mr.name, "measure_reset") + self.assertEqual(mr.num_qubits, 1) + self.assertEqual(mr.num_clbits, 1) + + def test_instantiation_name(self): + """Test instantiation with custom name.""" + with self.subTest("measure_reset_2"): + mr = MeasureReset("measure_reset_2") + self.assertIs(mr.base_class, MeasureReset) + self.assertIsInstance(mr, Instruction) + self.assertEqual(mr.name, "measure_reset_2") + self.assertEqual(mr.num_qubits, 1) + self.assertEqual(mr.num_clbits, 1) + + with self.subTest("invalid_name"): + with self.assertRaises(ValueError): + MeasureReset("invalid_name") + + def test_circuit_integration(self): + """Test appending to circuit.""" + mr = MeasureReset() + qc = QuantumCircuit(1, 1) + qc.append(mr, [0], [0]) + self.assertIs(qc.data[0].operation, mr) + + def test_transpiler_compat_without(self): + """Test that default pass manager FAILS if measure_reset is not in Target.""" + mr = MeasureReset() + backend = FakeVigoV2() + pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) + qc = QuantumCircuit(1, 1) + qc.append(mr, [0], [0]) + with self.assertRaises(TranspilerError): + _ = pm.run(qc) + + def test_transpiler_compat_with(self): + """Test default PM passes if measure_reset is in Target. + + Verifies it does not modify the instruction. + """ + mr = MeasureReset() + backend = GenericBackendV2(num_qubits=5, seed=0) + backend.target.add_instruction(mr, {(i,): None for i in range(5)}) + pm = generate_preset_pass_manager(backend=backend, seed_transpiler=0) + qc = QuantumCircuit(1, 1) + qc.append(mr, [0], [0]) + transpiled = pm.run(qc) + self.assertEqual(transpiled.data[0].operation.name, "measure_reset") From 5a90dc0b96025a46312c19227d1a148c813d897d Mon Sep 17 00:00:00 2001 From: Yael Ben-Haim Date: Thu, 13 Aug 2026 11:18:50 +0300 Subject: [PATCH 2/4] Update qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py Co-authored-by: Samuele Ferracin --- qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py index bcc430e71b..102d6c2c9f 100644 --- a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py +++ b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py @@ -56,7 +56,7 @@ class MeasureReset(Instruction): This instruction implements a reset operation whose intermediate measurement result is captured in a classical bit, making it observable to the user. - It uses 1 quantum bit and 1 classical bit, and its name must start with + It uses one quantum bit and one classical bit, and its name must start with ``measure_reset``. The semantics are: measure the qubit into the classical bit, then From 2160d8015e21cb9ddafa15b27097eb2355f4c5a7 Mon Sep 17 00:00:00 2001 From: Yael Ben-Haim Date: Thu, 13 Aug 2026 11:19:05 +0300 Subject: [PATCH 3/4] Update qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py Co-authored-by: Samuele Ferracin --- qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py index 102d6c2c9f..ef9dfc39a4 100644 --- a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py +++ b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py @@ -60,7 +60,7 @@ class MeasureReset(Instruction): ``measure_reset``. The semantics are: measure the qubit into the classical bit, then - conditionally flip the qubit back to |0> — i.e., a reset whose measurement + conditionally flip the qubit back to ``|0>`` — i.e., a reset whose measurement outcome is retained. """ From b7217cd68c1067028e75482a96d78e25191d2c1c Mon Sep 17 00:00:00 2001 From: Yael Ben-Haim Date: Thu, 13 Aug 2026 11:26:55 +0300 Subject: [PATCH 4/4] addressing review comments about docstrings --- qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py index ef9dfc39a4..07999a79aa 100644 --- a/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py +++ b/qiskit_ibm_runtime/circuit/library/mid_circuit_measure.py @@ -19,7 +19,7 @@ class MidCircuitMeasure(Instruction): """Alternative 'named' measurement definition. This instruction implements an alternative 'named' measurement definition - (1 classical bit, 1 quantum bit), whose name can be used to map to a corresponding + (one classical bit, one quantum bit), whose name can be used to map to a corresponding mid-circuit measurement instruction implementation on hardware. """ @@ -37,7 +37,7 @@ class MidCircuitReset(Instruction): """Alternative 'named' reset definition. This instruction implements an alternative 'named' reset definition - (1 quantum bit), whose name can be used to map to a corresponding + (one quantum bit), whose name can be used to map to a corresponding mid-circuit reset instruction implementation on hardware. """ @@ -56,7 +56,8 @@ class MeasureReset(Instruction): This instruction implements a reset operation whose intermediate measurement result is captured in a classical bit, making it observable to the user. - It uses one quantum bit and one classical bit, and its name must start with + It uses one quantum bit and one classical bit, and its name can be used to + map to a corresponding hardware-level implementation, and must start with ``measure_reset``. The semantics are: measure the qubit into the classical bit, then