Skip to content

Commit 4d4dbc5

Browse files
committed
* Create cicuit module and circuit/library submodule
* Add MidCircuitMeasure(Instruction) to new circuit library * Add unit tests * Add release note * Modify convert_to_target to support additional instructions through "instruction_signatures", add arguments supported in original implementation. Allow to provide a custom_name_mapping, which was referenced in the error messages but not actually enabled. Update typing style.
1 parent c68c071 commit 4d4dbc5

File tree

13 files changed

+561
-23
lines changed

13 files changed

+561
-23
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Module for vendor-specific circuit objects."""
14+
15+
from .library import *
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Module for vendor-specific instructions."""
14+
15+
16+
from .mid_circuit_measure import MidCircuitMeasure
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""MidCircuitMeasure gate."""
14+
15+
from qiskit.circuit import Instruction
16+
17+
18+
class MidCircuitMeasure(Instruction):
19+
"""
20+
This instruction implements an alternative 'named' measurement definition
21+
(1 classical bit, 1 quantum bit), whose name can be used to map to a corresponding
22+
mid-circuit measurement instruction implementation on hardware.
23+
"""
24+
25+
def __init__(self, name: str = "measure_2", label: str = None) -> None:
26+
if not name.startswith("measure_"):
27+
raise ValueError(
28+
"Invalid name for mid-circuit measure instruction."
29+
"The provided name must start with `measure_`"
30+
)
31+
32+
super().__init__(name, 1, 1, [], label=label)

qiskit_ibm_runtime/utils/backend_converter.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
# copyright notice, and modified files need to carry a notice indicating
1111
# that they have been altered from the originals.
1212

13-
"""Converters for migration from IBM Quantum BackendV1 to BackendV2."""
13+
"""
14+
Converters from BackendConfiguration and BackendProperties
15+
model (BackendV1) to Target model (BackendV2).
16+
"""
1417

1518
from __future__ import annotations
1619

1720
import logging
1821
import warnings
19-
from typing import Any, Dict, List
22+
from typing import Any
2023

2124
from qiskit.circuit.controlflow import (
2225
CONTROL_FLOW_OP_NAMES,
@@ -26,17 +29,15 @@
2629
WhileLoopOp,
2730
)
2831
from qiskit.circuit.gate import Gate
32+
from qiskit.circuit import Instruction
2933
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
3034
from qiskit.circuit.parameter import Parameter
3135
from qiskit.providers.backend import QubitProperties
3236
from qiskit.transpiler.target import InstructionProperties, Target
3337

34-
from ..models import BackendConfiguration, BackendProperties
35-
from ..models.exceptions import BackendPropertyError
36-
37-
# is_fractional_gate used to be defined in this module and might be referenced
38-
# from here externally
39-
from .utils import is_fractional_gate # See comment above before removing
38+
from qiskit_ibm_runtime.models import BackendConfiguration, BackendProperties
39+
from qiskit_ibm_runtime.models.exceptions import BackendPropertyError
40+
from qiskit_ibm_runtime.utils.utils import is_fractional_gate
4041

4142

4243
logger = logging.getLogger(__name__)
@@ -48,34 +49,42 @@ def convert_to_target( # type: ignore[no-untyped-def]
4849
*,
4950
include_control_flow: bool = True,
5051
include_fractional_gates: bool = True,
52+
custom_name_mapping: dict[str, Any] | None = None,
53+
add_delay: bool = True,
54+
filter_faulty: bool = True,
5155
**kwargs,
5256
) -> Target:
5357
"""Decode transpiler target from backend data set.
5458
5559
This function generates :class:`.Target`` instance from intermediate
5660
legacy objects such as :class:`.BackendProperties` and :class:`.BackendConfiguration`.
61+
These objects were components of the legacy :class:`.BackendV1` model.
5762
5863
Args:
5964
configuration: Backend configuration as ``BackendConfiguration``
6065
properties: Backend property dictionary or ``BackendProperties``
6166
include_control_flow: Set True to include control flow instructions.
6267
include_fractional_gates: Set True to include fractioanl gates.
68+
custom_name_mapping: A name mapping must be supplied for the operation
69+
not included in Qiskit Standard Gate name mapping, otherwise the operation
70+
will be dropped in the resulting ``Target`` object.
71+
add_delay: If True, adds delay to the instruction set.
72+
filter_faulty: If True, this filters the non-operational qubits.
6373
6474
Returns:
6575
A ``Target`` instance.
6676
"""
67-
add_delay = True
68-
filter_faulty = True
69-
7077
if "defaults" in kwargs:
7178
warnings.warn(
7279
"Backend defaults have been completely from removed IBM Backends. They will be ignored."
7380
)
7481

7582
required = ["measure", "delay", "reset"]
7683

77-
# Load Qiskit object representation
84+
# Load qiskit object representation
7885
qiskit_inst_mapping = get_standard_gate_name_mapping()
86+
if custom_name_mapping:
87+
qiskit_inst_mapping.update(custom_name_mapping)
7988

8089
qiskit_control_flow_mapping = {
8190
"if_else": IfElseOp,
@@ -95,9 +104,12 @@ def convert_to_target( # type: ignore[no-untyped-def]
95104
# Create instruction property placeholder from backend configuration
96105
basis_gates = set(getattr(configuration, "basis_gates", []))
97106
supported_instructions = set(getattr(configuration, "supported_instructions", []))
107+
instruction_signatures = getattr(configuration, "instruction_signatures", [])
98108
gate_configs = {gate.name: gate for gate in configuration.gates}
99109
all_instructions = set.union(
100-
basis_gates, set(required), supported_instructions.intersection(CONTROL_FLOW_OP_NAMES)
110+
basis_gates,
111+
set(required),
112+
supported_instructions.intersection(CONTROL_FLOW_OP_NAMES),
101113
)
102114

103115
inst_name_map = {}
@@ -106,7 +118,7 @@ def convert_to_target( # type: ignore[no-untyped-def]
106118
faulty_qubits = set()
107119
unsupported_instructions = []
108120

109-
# Create name to Qiskit instruction object repr mapping
121+
# Create name to qiskit instruction object repr mapping
110122
for name in all_instructions:
111123
if name in qiskit_control_flow_mapping:
112124
if not include_control_flow:
@@ -132,9 +144,9 @@ def convert_to_target( # type: ignore[no-untyped-def]
132144
inst_name_map[name] = qiskit_gate
133145
elif name in gate_configs:
134146
# GateConfig model is a translator of QASM opcode.
135-
# This doesn't have quantum definition, so Qiskit transpiler doesn't perform
147+
# This doesn't have quantum definition, so qiskit transpiler doesn't perform
136148
# any optimization in quantum domain.
137-
# Usually GateConfig counterpart should exist in Qiskit namespace so this is rarely called.
149+
# Usually GateConfig counterpart should exist in qiskit namespace so this is rarely called.
138150
this_config = gate_configs[name]
139151
params = list(map(Parameter, getattr(this_config, "parameters", [])))
140152
coupling_map = getattr(this_config, "coupling_map", [])
@@ -155,12 +167,29 @@ def convert_to_target( # type: ignore[no-untyped-def]
155167
for name in unsupported_instructions:
156168
all_instructions.remove(name)
157169

170+
# Create name to qiskit-ibm-runtime instruction object repr mapping
171+
172+
for signature in instruction_signatures:
173+
name = signature.get("name")
174+
num_qubits = signature.get("num_qubits")
175+
num_clbits = signature.get("num_clbits")
176+
param_names = signature.get("parameters")
177+
# Add generic parameter name
178+
params = [Parameter(name) for name in param_names]
179+
180+
instruction = Instruction(
181+
name=name, num_qubits=num_qubits, num_clbits=num_clbits, params=params
182+
)
183+
inst_name_map[name] = instruction
184+
all_instructions.add(name)
185+
158186
# Create inst properties placeholder
159187
# Without any assignment, properties value is None,
160188
# which defines a global instruction that can be applied to any qubit(s).
161189
# The None value behaves differently from an empty dictionary.
162190
# See API doc of Target.add_instruction for details.
163191
prop_name_map = dict.fromkeys(all_instructions)
192+
164193
for name in all_instructions:
165194
if name in gate_configs:
166195
if coupling_map := getattr(gate_configs[name], "coupling_map", None):
@@ -173,7 +202,7 @@ def convert_to_target( # type: ignore[no-untyped-def]
173202
# Populate instruction properties
174203
if properties:
175204

176-
def _get_value(prop_dict: Dict, prop_name: str) -> Any:
205+
def _get_value(prop_dict: dict, prop_name: str) -> Any:
177206
if ndval := prop_dict.get(prop_name, None):
178207
return ndval[0]
179208
return None
@@ -285,11 +314,9 @@ def _get_value(prop_dict: Dict, prop_name: str) -> Any:
285314

286315
def qubit_props_list_from_props(
287316
properties: BackendProperties,
288-
) -> List[QubitProperties]:
289-
"""Uses BackendProperties to construct
290-
and return a list of QubitProperties.
291-
"""
292-
qubit_props: List[QubitProperties] = []
317+
) -> list[QubitProperties]:
318+
"""Uses BackendProperties to construct and return a list of QubitProperties."""
319+
qubit_props: list[QubitProperties] = []
293320
for qubit, _ in enumerate(properties.qubits):
294321
try:
295322
t_1 = properties.t1(qubit)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
The :mod:`qiskit_ibm_runtime` package has been extended with two new modules: :mod:`.circuit` and
2+
:mod:`.circuit.library`. These modules are designed to mirror the structure of the
3+
corresponding `qiskit` SDK modules, while providing vendor-specific implementations of
4+
circuit objects and instructions.
5+
6+
The first addition to this new circuit library is the :class:`.MidCircuitMeasure` class.
7+
This class enables the creation of instructions that follow the naming convention
8+
`measure_<identifier>`, which are mapped to specific mid-circuit measurement
9+
hardware instructions matching that pattern. The default name for this instruction is `"measure_2"`.
10+
Example usage::
11+
12+
from qiskit import QuantumCircuit
13+
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
14+
15+
measure_2 = MidCircuitMeasure()
16+
measure_reset = MidCircuitMeasure("measure_3")
17+
qc = QuantumCircuit(1, 1)
18+
qc.append(measure_2, [0], [0])
19+
qc.append(measure_reset, [0], [0])
20+
qc.measure([0], [0])
21+
22+
Output::
23+
24+
┌────────────┐┌────────────────┐┌─┐
25+
q: ┤0 ├┤0 ├┤M├
26+
│ Measure_2 ││ Measure_reset │└╥┘
27+
c: ╡0 ╞╡0 ╞═╩═
28+
└────────────┘└────────────────┘
29+
30+
31+
The :func:`.convert_to_target` utility has been updated to support an additional ``"instruction_signatures"`` field in
32+
backend configuration files (``configuration.json``). This field is intended to represent non-unitary, non-standard instructions
33+
reported by the backend and should respect the following schema::
34+
35+
"instruction_signatures" = [
36+
{
37+
"name": "measure_2",
38+
"num_qubits": 1,
39+
"num_clbits": 1,
40+
"parameters": [],
41+
"return_type": "Bool",
42+
"description": "An alternative measurement. This can be used as a mid-circuit measurement in a dynamic circuit. ",
43+
},
44+
{
45+
"name": "reset_2",
46+
"num_qubits": 1,
47+
"num_clbits": 1,
48+
"parameters": [],
49+
"return_type": "Bool",
50+
"description": "An alternative reset instruction.",
51+
}
52+
]
53+
54+
55+
56+
In addition to this change, the :func:`.convert_to_target` function now accepts a ``custom_name_mapping`` argument
57+
and exposes the ``add_delay`` and ``filter_faulty`` flags from the original core implementation.

test/unit/circuit/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.

0 commit comments

Comments
 (0)