Skip to content

Commit a3eb99c

Browse files
kt474ElePT
andauthored
Remove control flow instruction filter when use_fractional_gates=true (#2366)
* Remove control flow instruction filter when `use_fractional_gates=true` * Update test and add reno * Fix unit tests * address comments * Use `dynamic_circuits` in backends filter * Update docstring * Add new plugin * Address comments & deprecate old plugins * Update qiskit_ibm_runtime/ibm_backend.py Co-authored-by: Elena Peña Tapia <[email protected]> * undeprecate IBMDynamicTranslationPlugin --------- Co-authored-by: Elena Peña Tapia <[email protected]>
1 parent 54b9b8c commit a3eb99c

File tree

8 files changed

+79
-42
lines changed

8 files changed

+79
-42
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ dependencies = [
9898
ibm_backend = "qiskit_ibm_runtime.transpiler.plugin:IBMTranslationPlugin"
9999
ibm_dynamic_circuits = "qiskit_ibm_runtime.transpiler.plugin:IBMDynamicTranslationPlugin"
100100
ibm_fractional = "qiskit_ibm_runtime.transpiler.plugin:IBMFractionalTranslationPlugin"
101+
ibm_dynamic_and_fractional = "qiskit_ibm_runtime.transpiler.plugin:IBMDynamicFractionalTranslationPlugin"
101102

102103
[project.urls]
103104
documentation = "https://quantum.cloud.ibm.com/docs/"

qiskit_ibm_runtime/ibm_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ def get_translation_stage_plugin(self) -> str:
497497
"""Return the default translation stage plugin name for IBM backends."""
498498
if not self.options.use_fractional_gates:
499499
return "ibm_dynamic_circuits"
500-
return "ibm_fractional"
500+
return "ibm_dynamic_and_fractional"
501501

502502

503503
class IBMRetiredBackend(IBMBackend):

qiskit_ibm_runtime/qiskit_runtime_service.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -528,14 +528,12 @@ def backends(
528528
filters=lambda x: ("rz" in x.basis_gates )
529529
)
530530
use_fractional_gates: Set True to allow for the backends to include
531-
fractional gates. Currently this feature cannot be used
532-
simultaneously with dynamic circuits, PEC, PEA, or gate
533-
twirling. When this flag is set, control flow instructions are
534-
automatically removed from the backend.
535-
When you use a dynamic circuits feature (e.g. ``if_else``) in your
536-
algorithm, you must disable this flag to create executable ISA circuits.
537-
This flag might be modified or removed when our backend
538-
supports dynamic circuits and fractional gates simultaneously.
531+
fractional gates. Note that our backends now
532+
support dynamic circuits and fractional gates simultaneously.
533+
You no longer have to disable this flag when
534+
using dynamic circuits features (e.g. ``if_else``) in your
535+
algorithm. Control flow instructions are not removed from the
536+
backend when this flag is set to True.
539537
If ``None``, then both fractional gates and control flow operations are
540538
included in the backends.
541539
@@ -562,12 +560,6 @@ def backends(
562560
IBMInputValueError: If an input is invalid.
563561
QiskitBackendNotFoundError: If the backend is not in any instance.
564562
"""
565-
if dynamic_circuits is True and use_fractional_gates:
566-
raise QiskitBackendNotFoundError(
567-
"Currently fractional_gates and dynamic_circuits feature cannot be "
568-
"simulutaneously enabled. Consider disabling one or the other."
569-
)
570-
571563
backends: List[IBMBackend] = []
572564

573565
unique_backends = set()
@@ -613,7 +605,9 @@ def backends(
613605
if dynamic_circuits is not None:
614606
backends = list(
615607
filter(
616-
lambda b: ("qasm3" in getattr(b.configuration(), "supported_features", []))
608+
lambda b: (
609+
"dynamic_circuits" in getattr(b.configuration(), "supported_features", [])
610+
)
617611
== dynamic_circuits,
618612
backends,
619613
)

qiskit_ibm_runtime/transpiler/plugin.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
FoldRzzAngle,
2929
)
3030

31+
from ..utils.deprecation import issue_deprecation_msg
32+
3133
_TERRA_VERSION = tuple(
3234
int(x) for x in re.match(r"\d+\.\d+\.\d", _terra_version_string).group(0).split(".")[:3]
3335
)
@@ -114,13 +116,22 @@ def pass_manager(
114116

115117

116118
class IBMFractionalTranslationPlugin(PassManagerStagePlugin):
117-
"""A translation stage plugin for targeting Qiskit circuits
119+
"""(DEPRECATED) A translation stage plugin for targeting Qiskit circuits
118120
to IBM Quantum systems with fractional gate support.
119121
120122
Currently coexistence of fractional gate operations and
121123
dynamic circuits is not assumed.
122124
"""
123125

126+
def __new__(cls, *args, **kwargs):
127+
issue_deprecation_msg(
128+
msg="Since backends now support running jobs that contain both "
129+
"fractional gates and dynamic circuit, IBMFractionalTranslationPlugin is deprecated",
130+
version="0.42.0",
131+
remedy="Use IBMDynamicFractionalTranslationPlugin instead.",
132+
)
133+
return super().__new__(cls, *args, **kwargs)
134+
124135
def pass_manager(
125136
self,
126137
pass_manager_config: PassManagerConfig,
@@ -154,3 +165,47 @@ def pass_manager(
154165
# Apply this pass after SU4 is translated.
155166
post_passes.append(FoldRzzAngle())
156167
return PassManager(pre_passes) + translator_pm + PassManager(post_passes)
168+
169+
170+
class IBMDynamicFractionalTranslationPlugin(PassManagerStagePlugin):
171+
"""A translation stage plugin for targeting Qiskit circuits
172+
to IBM Quantum systems with both dynamic circuits and fractional gate support.
173+
"""
174+
175+
def pass_manager(
176+
self,
177+
pass_manager_config: PassManagerConfig,
178+
optimization_level: Optional[int] = None,
179+
) -> PassManager:
180+
"""Build IBMTranslationPlugin PassManager."""
181+
182+
if _TERRA_VERSION[0] == 1:
183+
legacy_options = {"backend_props": pass_manager_config.backend_properties}
184+
else:
185+
legacy_options = {}
186+
187+
translator_pm = common.generate_translation_passmanager(
188+
target=pass_manager_config.target,
189+
basis_gates=pass_manager_config.basis_gates,
190+
approximation_degree=pass_manager_config.approximation_degree,
191+
coupling_map=pass_manager_config.coupling_map,
192+
unitary_synthesis_method=pass_manager_config.unitary_synthesis_method,
193+
unitary_synthesis_plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
194+
hls_config=pass_manager_config.hls_config,
195+
**legacy_options,
196+
)
197+
198+
instruction_durations = pass_manager_config.instruction_durations
199+
pre_passes = []
200+
post_passes = []
201+
target = pass_manager_config.target or pass_manager_config.basis_gates
202+
if instruction_durations and not "id" in target:
203+
pre_passes.append(ConvertIdToDelay(instruction_durations))
204+
if "rzz" in target:
205+
# Apply this pass after SU4 is translated.
206+
post_passes.append(FoldRzzAngle())
207+
208+
if (convert_pass := getattr(passes, "ConvertConditionsToIfOps", None)) is not None:
209+
# If `None`, we're dealing with Qiskit 2.0+ where it's unnecessary anyway.
210+
pre_passes += [convert_pass()] # pylint: disable=not-callable
211+
return PassManager(pre_passes) + translator_pm + PassManager(post_passes)

qiskit_ibm_runtime/utils/backend_decoder.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import dateutil.parser
2020
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
2121

22-
from qiskit.circuit import CONTROL_FLOW_OP_NAMES
23-
2422
from ..models import (
2523
BackendProperties,
2624
QasmBackendConfiguration,
@@ -85,17 +83,7 @@ def filter_raw_configuration(
8583
return
8684

8785
gate_map = get_standard_gate_name_mapping()
88-
if use_fractional_gates:
89-
raw_config["conditional"] = False
90-
if "supported_instructions" in raw_config:
91-
raw_config["supported_instructions"] = [
92-
i for i in raw_config["supported_instructions"] if i not in CONTROL_FLOW_OP_NAMES
93-
]
94-
if "supported_features" in raw_config:
95-
raw_config["supported_features"] = [
96-
g for g in raw_config["supported_features"] if g != "qasm3"
97-
]
98-
else:
86+
if not use_fractional_gates:
9987
if "basis_gates" in raw_config:
10088
raw_config["basis_gates"] = [
10189
g
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Since backends now support running jobs that contain both fractional gates and dynamic circuits, control flow
2+
instructions are no longer filtered out when using ``use_fractional_gates=True``. As a result, there is a new translation state plugin,
3+
:class:`~.IBMDynamicFractionalTranslationPlugin`, for targeting circuits with both
4+
dynamic circuits and fractional gates.
5+
6+
:class:`~.IBMFractionalTranslationPlugin` is deprecated
7+
since it is no longer necessary.

test/unit/test_backend_retrieval.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,8 @@ def test_get_backend_with_fractional_optin(self, use_fractional):
235235
"rzz" in test_backend.target,
236236
use_fractional or use_fractional is None,
237237
)
238-
self.assertEqual(
239-
"if_else" in test_backend.target.operation_names,
240-
not use_fractional or use_fractional is None,
241-
)
242-
self.assertEqual(
243-
"while_loop" in test_backend.target.operation_names,
244-
not use_fractional or use_fractional is None,
245-
)
238+
self.assertTrue("if_else" in test_backend.target.operation_names)
239+
self.assertTrue("while_loop" in test_backend.target.operation_names)
246240

247241
if use_fractional or use_fractional is None:
248242
self.assertAlmostEqual(test_backend.target["rx"][(0,)].error, 0.00019, places=5)

test/unit/test_sampler.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def test_sampler_validations(self):
162162
inst.run([(circ,)])
163163

164164
def test_run_dynamic_circuit_with_fractional_opted(self):
165-
"""Fractional opted backend cannot run dynamic circuits."""
165+
"""Fractional opted backend can run dynamic circuits."""
166166
service = FakeRuntimeService(
167167
channel="ibm_quantum_platform",
168168
token="my_token",
@@ -177,8 +177,7 @@ def test_run_dynamic_circuit_with_fractional_opted(self):
177177
)
178178

179179
inst = SamplerV2(mode=backend)
180-
with self.assertRaises(IBMInputValueError):
181-
inst.run([dynamic_circuit])
180+
inst.run([dynamic_circuit])
182181

183182
def test_run_fractional_circuit_without_fractional_opted(self):
184183
"""Fractional non-opted backend cannot run fractional circuits."""
@@ -198,7 +197,6 @@ def test_run_fractional_circuit_without_fractional_opted(self):
198197
inst.run([fractional_circuit])
199198

200199
@named_data(
201-
("with_fractional", True),
202200
("without_fractional", False),
203201
)
204202
def test_run_fractional_dynamic_mix(self, use_fractional):

0 commit comments

Comments
 (0)