Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support on QuantumInstance/run_circuits for Backends that don't handle QasmQobj #6299

Merged
merged 8 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions qiskit/utils/backend_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2020.
# (C) Copyright IBM 2018, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -75,7 +75,10 @@ def is_aer_provider(backend):
"""
if has_aer():
from qiskit.providers.aer import AerProvider
return isinstance(backend.provider(), AerProvider)
if isinstance(backend.provider(), AerProvider):
return True
from qiskit.providers.aer.backends.aerbackend import AerBackend
return isinstance(backend, AerBackend)

return False

Expand Down Expand Up @@ -129,6 +132,10 @@ def is_statevector_backend(backend):
Returns:
bool: True is statevector
"""
if has_aer():
from qiskit.providers.aer.backends import StatevectorSimulator
if isinstance(backend, StatevectorSimulator):
return True
return backend.name().startswith('statevector') if backend is not None else False


Expand Down
57 changes: 54 additions & 3 deletions qiskit/utils/measurement_error_mitigation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2019, 2020.
# (C) Copyright IBM 2019, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -17,6 +17,8 @@
from qiskit import compiler
from ..exceptions import QiskitError, MissingOptionalLibraryError

# pylint: disable=invalid-name


def get_measured_qubits(transpiled_circuits):
"""
Expand All @@ -39,7 +41,7 @@ def get_measured_qubits(transpiled_circuits):
for inst, qargs, _ in qc.data:
if inst.name != 'measure':
continue
measured_qubits.append(qargs[0][1])
measured_qubits.append(qargs[0].index)
measured_qubits_str = '_'.join([str(x) for x in measured_qubits])
if measured_qubits_str not in qubit_mappings:
qubit_mappings[measured_qubits_str] = []
Expand Down Expand Up @@ -93,7 +95,56 @@ def get_measured_qubits_from_qobj(qobj):
return sorted(qubit_index), qubit_mappings


# pylint: disable=invalid-name
def build_measurement_error_mitigation_circuits(qubit_list, fitter_cls, backend,
backend_config=None, compile_config=None):
"""Build measurement error mitigation circuits
Args:
qubit_list (list[int]): list of ordered qubits used in the algorithm
fitter_cls (callable): CompleteMeasFitter or TensoredMeasFitter
backend (BaseBackend): backend instance
backend_config (dict, optional): configuration for backend
compile_config (dict, optional): configuration for compilation
Returns:
QasmQobj: the Qobj with calibration circuits at the beginning
list[str]: the state labels for build MeasFitter
list[str]: the labels of the calibration circuits
Raises:
QiskitError: when the fitter_cls is not recognizable.
MissingOptionalLibraryError: Qiskit-Ignis not installed
"""
try:
from qiskit.ignis.mitigation.measurement import (complete_meas_cal,
CompleteMeasFitter, TensoredMeasFitter)
except ImportError as ex:
raise MissingOptionalLibraryError(
libname='qiskit-ignis',
name='build_measurement_error_mitigation_qobj',
pip_install='pip install qiskit-ignis') from ex

circlabel = 'mcal'

if not qubit_list:
raise QiskitError("The measured qubit list can not be [].")

if fitter_cls == CompleteMeasFitter:
meas_calibs_circuits, state_labels = \
complete_meas_cal(qubit_list=range(len(qubit_list)), circlabel=circlabel)
elif fitter_cls == TensoredMeasFitter:
# TODO support different calibration
raise QiskitError("Does not support TensoredMeasFitter yet.")
else:
raise QiskitError("Unknown fitter {}".format(fitter_cls))

# the provided `qubit_list` would be used as the initial layout to
# assure the consistent qubit mapping used in the main circuits.

tmp_compile_config = copy.deepcopy(compile_config)
tmp_compile_config['initial_layout'] = qubit_list
t_meas_calibs_circuits = compiler.transpile(meas_calibs_circuits, backend,
**backend_config, **tmp_compile_config)
return t_meas_calibs_circuits, state_labels, circlabel


def build_measurement_error_mitigation_qobj(qubit_list, fitter_cls, backend,
backend_config=None, compile_config=None,
run_config=None):
Expand Down
149 changes: 110 additions & 39 deletions qiskit/utils/quantum_instance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2020.
# (C) Copyright IBM 2018, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -22,6 +22,7 @@
from qiskit.utils import circuit_utils
from qiskit.exceptions import QiskitError
from .backend_utils import (is_ibmq_provider,
is_aer_provider,
is_statevector_backend,
is_simulator_backend,
is_local_backend,
Expand Down Expand Up @@ -295,20 +296,31 @@ def execute(self,
TODO: Maybe we can combine the circuits for the main ones and calibration circuits before
assembling to the qobj.
"""
from qiskit.utils.run_circuits import run_qobj
from qiskit.utils.run_circuits import run_qobj, run_circuits

from qiskit.utils.measurement_error_mitigation import \
(get_measured_qubits_from_qobj, build_measurement_error_mitigation_qobj)
(get_measured_qubits_from_qobj, get_measured_qubits,
build_measurement_error_mitigation_circuits,
build_measurement_error_mitigation_qobj)

# maybe compile
if not had_transpiled:
circuits = self.transpile(circuits)

# fix for providers that don't support QasmQobj until a bigger refactor happens
from qiskit.providers import BackendV1
circuit_job = isinstance(self._backend, BackendV1) and \
not is_aer_provider(self._backend) and \
not is_basicaer_provider(self._backend) and \
not is_ibmq_provider(self._backend)

# assemble
qobj = self.assemble(circuits)
if not circuit_job:
qobj = self.assemble(circuits)

if self._meas_error_mitigation_cls is not None:
qubit_index, qubit_mappings = get_measured_qubits_from_qobj(qobj)
qubit_index, qubit_mappings = get_measured_qubits(circuits) \
if circuit_job else get_measured_qubits_from_qobj(qobj)
qubit_index_str = '_'.join([str(x) for x in qubit_index]) + \
"_{}".format(self._meas_error_mitigation_shots or self._run_config.shots)
meas_error_mitigation_fitter, timestamp = \
Expand Down Expand Up @@ -337,39 +349,84 @@ def execute(self,
meas_error_mitigation_fitter is None

if build_cals_matrix:
logger.info("Updating qobj with the circuits for measurement error mitigation.")
use_different_shots = not (
self._meas_error_mitigation_shots is None
or self._meas_error_mitigation_shots == self._run_config.shots)
temp_run_config = copy.deepcopy(self._run_config)
if use_different_shots:
temp_run_config.shots = self._meas_error_mitigation_shots

cals_qobj, state_labels, circuit_labels = \
build_measurement_error_mitigation_qobj(qubit_index,
self._meas_error_mitigation_cls,
self._backend,
self._backend_config,
self._compile_config,
temp_run_config)
if use_different_shots or is_aer_qasm(self._backend):
cals_result = run_qobj(cals_qobj, self._backend, self._qjob_config,
self._backend_options,
self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += cals_result.time_taken
result = run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
if circuit_job:
logger.info("Updating to also run measurement error mitigation.")
use_different_shots = not (
self._meas_error_mitigation_shots is None
or self._meas_error_mitigation_shots == self._run_config.shots)
temp_run_config = copy.deepcopy(self._run_config)
if use_different_shots:
temp_run_config.shots = self._meas_error_mitigation_shots
cal_circuits, state_labels, circuit_labels = \
build_measurement_error_mitigation_circuits(
qubit_index,
self._meas_error_mitigation_cls,
self._backend,
self._backend_config,
self._compile_config)
if use_different_shots:
cals_result = run_circuits(cal_circuits,
self._backend,
qjob_config=self._qjob_config,
backend_options=self._backend_options,
noise_config=self._noise_config,
run_config=self._run_config.to_dict(),
job_callback=self._job_callback)
self._time_taken += cals_result.time_taken
result = run_circuits(circuits,
self._backend,
qjob_config=self.qjob_config,
backend_options=self.backend_options,
noise_config=self._noise_config,
run_config=self.run_config.to_dict(),
job_callback=self._job_callback)
self._time_taken += result.time_taken
else:
circuits[0:0] = cal_circuits
result = run_circuits(circuits,
self._backend,
qjob_config=self.qjob_config,
backend_options=self.backend_options,
noise_config=self._noise_config,
run_config=self.run_config.to_dict(),
job_callback=self._job_callback)
self._time_taken += result.time_taken
cals_result = result

else:
# insert the calibration circuit into main qobj if the shots are the same
qobj.experiments[0:0] = cals_qobj.experiments
result = run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
cals_result = result
logger.info("Updating qobj with the circuits for measurement error mitigation.")
use_different_shots = not (
self._meas_error_mitigation_shots is None
or self._meas_error_mitigation_shots == self._run_config.shots)
temp_run_config = copy.deepcopy(self._run_config)
if use_different_shots:
temp_run_config.shots = self._meas_error_mitigation_shots

cals_qobj, state_labels, circuit_labels = \
build_measurement_error_mitigation_qobj(qubit_index,
self._meas_error_mitigation_cls,
self._backend,
self._backend_config,
self._compile_config,
temp_run_config)
if use_different_shots or is_aer_qasm(self._backend):
cals_result = run_qobj(cals_qobj, self._backend, self._qjob_config,
self._backend_options,
self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += cals_result.time_taken
result = run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
else:
# insert the calibration circuit into main qobj if the shots are the same
qobj.experiments[0:0] = cals_qobj.experiments
result = run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
cals_result = result

logger.info("Building calibration matrix for measurement error mitigation.")
meas_error_mitigation_fitter = \
Expand All @@ -380,7 +437,14 @@ def execute(self,
self._meas_error_mitigation_fitters[qubit_index_str] = \
(meas_error_mitigation_fitter, time.time())
else:
result = run_qobj(qobj, self._backend, self._qjob_config,
result = run_circuits(circuits,
self._backend,
qjob_config=self.qjob_config,
backend_options=self.backend_options,
noise_config=self._noise_config,
run_config=self._run_config.to_dict(),
job_callback=self._job_callback) if circuit_job else \
run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
Expand All @@ -406,7 +470,14 @@ def execute(self,
result.results[n] = tmp_result.results[i]

else:
result = run_qobj(qobj, self._backend, self._qjob_config,
result = run_circuits(circuits,
self._backend,
qjob_config=self.qjob_config,
backend_options=self.backend_options,
noise_config=self._noise_config,
run_config=self._run_config.to_dict(),
job_callback=self._job_callback) if circuit_job else \
run_qobj(qobj, self._backend, self._qjob_config,
self._backend_options, self._noise_config,
self._skip_qobj_validation, self._job_callback)
self._time_taken += result.time_taken
Expand Down
Loading