Skip to content

Commit

Permalink
Fix handling of simulator backends in BackendV2Converter (Qiskit#9630)
Browse files Browse the repository at this point in the history
This commit fixes some issues in the BackendV2Converter class around its
usage wrapping BackendV1 based simulators (such as Qiskit Aer with the
current release as of this commit 0.11.x). The BackendV2 converter was
incorrectly handling some edge cases in Target creation with simulators
that have ideal gates. The first issue was that the handling of ideal
gates without any properties was incorrect and could not handle gates
that took > 1 qubit. This has been corrected so the converter will work
as expect. The second issue is the `online_date` field in the
`BackendConfiguration` is optional and may not exist, especially on
simulators which that field never really applies for. With these fixes
the BackendV2Converter can be used to wrap BackendV1 simulators.

Fixes Qiskit#9562

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
mtreinish and mergify[bot] authored Feb 22, 2023
1 parent 3512bbd commit 789707c
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 4 deletions.
6 changes: 2 additions & 4 deletions qiskit/providers/backend_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ def convert_to_target(
for op in combined_global_ops:
if op not in target:
if op in name_mapping:
target.add_instruction(
name_mapping[op], {(bit,): None for bit in range(target.num_qubits)}
)
target.add_instruction(name_mapping[op], name=op)
else:
raise QiskitError(
f"Operation name '{op}' does not have a known mapping. Use "
Expand Down Expand Up @@ -220,7 +218,7 @@ def __init__(
provider=backend.provider,
name=backend.name(),
description=self._config.description,
online_date=self._config.online_date,
online_date=getattr(self._config, "online_date", None),
backend_version=self._config.backend_version,
)
self._options = self._backend._options
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Fixed an issue with the :class:`~.BackendV2Converter` class when wrapping
a :class:`~.BackendV1` based simulator which would error if either
the ``online_date`` field in the :class:`~.BackendConfiguration` for the
simulator was not present or if the simulator backend supports ideal
implementations of gates that involve more than 1 qubit.
Fixed `#9562 <https://github.com/Qiskit/qiskit-terra/issues/9562>`__
265 changes: 265 additions & 0 deletions test/python/providers/test_fake_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# pylint: disable=missing-module-docstring

import operator
import unittest

from test import combine
from ddt import ddt, data
Expand All @@ -34,6 +35,30 @@
from qiskit.providers.backend_compat import BackendV2Converter
from qiskit.providers.backend import BackendV2
from qiskit.utils import optionals
from qiskit.circuit.library import (
SXGate,
MCPhaseGate,
MCXGate,
RZGate,
RXGate,
U2Gate,
U1Gate,
U3Gate,
YGate,
ZGate,
PauliGate,
SwapGate,
RGate,
MCXGrayCode,
RYGate,
)
from qiskit.circuit import ControlledGate, Parameter
from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
from qiskit.quantum_info.operators.channel.kraus import Kraus
from qiskit.quantum_info.operators.channel import SuperOp
from qiskit.extensions import Initialize, UnitaryGate
from qiskit.extensions.quantum_initializer import DiagonalGate, UCGate
from qiskit.circuit.controlflow import IfElseOp, WhileLoopOp, ForLoopOp

FAKE_PROVIDER_FOR_BACKEND_V2 = FakeProviderForBackendV2()
FAKE_PROVIDER = FakeProvider()
Expand Down Expand Up @@ -193,3 +218,243 @@ def test_converter_delay_circuit(self):
qc.measure_all()
res = transpile(qc, backend_v2)
self.assertIn("delay", res.count_ops())

@unittest.skipUnless(optionals.HAS_AER, "Aer required for this test")
def test_converter_simulator(self):
class MCSXGate(ControlledGate):
def __init__(self, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcsx",
1 + num_ctrl_qubits,
[],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=SXGate(),
)

class MCYGate(ControlledGate):
def __init__(self, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcy",
1 + num_ctrl_qubits,
[],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=YGate(),
)

class MCZGate(ControlledGate):
def __init__(self, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcz",
1 + num_ctrl_qubits,
[],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=ZGate(),
)

class MCRXGate(ControlledGate):
def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcrx",
1 + num_ctrl_qubits,
[theta],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=RXGate(theta),
)

class MCRYGate(ControlledGate):
def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcry",
1 + num_ctrl_qubits,
[theta],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=RYGate(theta),
)

class MCRZGate(ControlledGate):
def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcrz",
1 + num_ctrl_qubits,
[theta],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=RZGate(theta),
)

class MCRGate(ControlledGate):
def __init__(self, theta, phi, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcr",
1 + num_ctrl_qubits,
[theta, phi],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=RGate(theta, phi),
)

class MCU1Gate(ControlledGate):
def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcu1",
1 + num_ctrl_qubits,
[theta],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=U1Gate(theta),
)

class MCU2Gate(ControlledGate):
def __init__(self, theta, lam, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcu2",
1 + num_ctrl_qubits,
[theta, lam],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=U2Gate(theta, lam),
)

class MCU3Gate(ControlledGate):
def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcu3",
1 + num_ctrl_qubits,
[theta, phi, lam],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=U3Gate(theta, phi, lam),
)

class MCUGate(ControlledGate):
def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcu",
1 + num_ctrl_qubits,
[theta, phi, lam],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=U3Gate(theta, phi, lam),
)

class MCSwapGate(ControlledGate):
def __init__(self, num_ctrl_qubits, ctrl_state=None):
super().__init__(
"mcswap",
2 + num_ctrl_qubits,
[],
None,
num_ctrl_qubits,
ctrl_state=ctrl_state,
base_gate=SwapGate(),
)

from qiskit_aer import AerSimulator
from qiskit_aer.library import (
SaveExpectationValue,
SaveAmplitudes,
SaveStatevectorDict,
SaveSuperOp,
SaveClifford,
SaveMatrixProductState,
SaveDensityMatrix,
SaveProbabilities,
SaveStatevector,
SetDensityMatrix,
SetUnitary,
SaveState,
SetMatrixProductState,
SaveUnitary,
SetSuperOp,
SaveExpectationValueVariance,
SaveStabilizer,
SetStatevector,
SetStabilizer,
SaveAmplitudesSquared,
SaveProbabilitiesDict,
)
from qiskit_aer.noise.errors import ReadoutError
from qiskit_aer.noise.noise_model import QuantumErrorLocation

sim = AerSimulator()
phi = Parameter("phi")
lam = Parameter("lam")
backend = BackendV2Converter(
sim,
name_mapping={
"mcsx": MCSXGate,
"mcp": MCPhaseGate,
"mcphase": MCPhaseGate,
"quantum_channel": QuantumChannel,
"initialize": Initialize,
"save_expval": SaveExpectationValue,
"diagonal": DiagonalGate,
"save_amplitudes": SaveAmplitudes,
"roerror": ReadoutError,
"mcrx": MCRXGate,
"kraus": Kraus,
"save_statevector_dict": SaveStatevectorDict,
"mcx": MCXGate,
"mcu1": MCU1Gate,
"mcu2": MCU2Gate,
"mcu3": MCU3Gate,
"save_superop": SaveSuperOp,
"multiplexer": UCGate,
"mcy": MCYGate,
"superop": SuperOp,
"save_clifford": SaveClifford,
"save_matrix_product_state": SaveMatrixProductState,
"save_density_matrix": SaveDensityMatrix,
"save_probabilities": SaveProbabilities,
"if_else": IfElseOp,
"while_loop": WhileLoopOp,
"for_loop": ForLoopOp,
"save_statevector": SaveStatevector,
"mcu": MCUGate,
"set_density_matrix": SetDensityMatrix,
"qerror_loc": QuantumErrorLocation,
"unitary": UnitaryGate,
"mcz": MCZGate,
"pauli": PauliGate,
"set_unitary": SetUnitary,
"save_state": SaveState,
"mcswap": MCSwapGate,
"set_matrix_product_state": SetMatrixProductState,
"save_unitary": SaveUnitary,
"mcr": MCRGate,
"mcx_gray": MCXGrayCode,
"mcrz": MCRZGate,
"set_superop": SetSuperOp,
"save_expval_var": SaveExpectationValueVariance,
"save_stabilizer": SaveStabilizer,
"set_statevector": SetStatevector,
"mcry": MCRYGate,
"set_stabilizer": SetStabilizer,
"save_amplitudes_sq": SaveAmplitudesSquared,
"save_probabilities_dict": SaveProbabilitiesDict,
"cu2": U2Gate(phi, lam).control(),
},
)
self.assertIsInstance(backend, BackendV2)
res = transpile(self.circuit, backend)
job = backend.run(res)
result = job.result()
counts = result.get_counts()
max_count = max(counts.items(), key=operator.itemgetter(1))[0]
self.assertEqual(max_count, "11")

0 comments on commit 789707c

Please sign in to comment.