Code source de qiskit.providers.aer.noise.noise_model
# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
#
# 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
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Noise model class for Qiskit Aer simulators.
"""
import json
import logging
from typing import Optional
from warnings import warn, catch_warnings, filterwarnings
from numpy import ndarray
from qiskit.circuit import Instruction, Delay
from qiskit.providers.exceptions import BackendPropertyError
from qiskit.providers.models import BackendProperties
from qiskit.transpiler import PassManager
from .device.models import _excited_population, _truncate_t2_value
from .device.models import basic_device_gate_errors
from .device.models import basic_device_readout_errors
from .errors.quantum_error import QuantumError
from .errors.readout_error import ReadoutError
from .noiseerror import NoiseError
from .passes import RelaxationNoisePass
from ..backends.backend_utils import BASIS_GATES
logger = logging.getLogger(__name__)
class AerJSONEncoder(json.JSONEncoder):
"""
JSON encoder for NumPy arrays and complex numbers.
This functions as the standard JSON Encoder but adds support
for encoding:
complex numbers z as lists [z.real, z.imag]
ndarrays as nested lists.
"""
# pylint: disable=method-hidden,arguments-differ
def default(self, obj):
if isinstance(obj, ndarray):
return obj.tolist()
if isinstance(obj, complex):
return [obj.real, obj.imag]
if hasattr(obj, "to_dict"):
return obj.to_dict()
return super().default(obj)
class QuantumErrorLocation(Instruction):
"""Instruction for referencing a multi-qubit error in a NoiseModel"""
_directive = True
def __init__(self, qerror):
"""Construct a new quantum error location instruction.
Args:
qerror (QuantumError): the quantum error to reference.
"""
super().__init__("qerror_loc", qerror.num_qubits, 0, [], label=qerror.id)
[docs]class NoiseModel:
"""Noise model class for Qiskit Aer simulators.
This class is used to represent noise model for the
:class:`~qiskit.providers.aer.QasmSimulator`. It can be used to construct
custom noise models for simulator, or to automatically generate a basic
device noise model for an IBMQ backend. See the
:mod:`~qiskit.providers.aer.noise` module documentation for additional
information.
**Example: Basic device noise model**
An approximate :class:`NoiseModel` can be generated automatically from the
properties of real device backends from the IBMQ provider using the
:meth:`~NoiseModel.from_backend` method.
.. code-block:: python
from qiskit import IBMQ, Aer
from qiskit.providers.aer.noise import NoiseModel
provider = IBMQ.load_account()
backend = provider.get_backend('ibmq_vigo')
noise_model = NoiseModel.from_backend(backend)
print(noise_model)
**Example: Custom noise model**
Custom noise models can be used by adding :class:`QuantumError` to circuit
gate, reset or measure instructions, and :class:`ReadoutError` to measure
instructions.
.. code-block:: python
import qiskit.providers.aer.noise as noise
# Error probabilities
prob_1 = 0.001 # 1-qubit gate
prob_2 = 0.01 # 2-qubit gate
# Depolarizing quantum errors
error_1 = noise.depolarizing_error(prob_1, 1)
error_2 = noise.depolarizing_error(prob_2, 2)
# Add errors to noise model
noise_model = noise.NoiseModel()
noise_model.add_all_qubit_quantum_error(error_1, ['rz', 'sx', 'x'])
noise_model.add_all_qubit_quantum_error(error_2, ['cx'])
print(noise_model)
"""
# Checks for standard 1-3 qubit instructions
_1qubit_instructions = set([
'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x',
'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg'])
_2qubit_instructions = set([
'swap', 'cx', 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx',
'ryy', 'rzz', 'rzx'])
_3qubit_instructions = set(['ccx', 'cswap'])
def __init__(self, basis_gates=None):
"""Initialize an empty noise model.
Args:
basis_gates (list[str] or None): Specify an initial basis_gates
for the noise model. If None a default value of ['id', 'rz', 'sx', 'cx']
is used (Default: None).
Additional Information:
Errors added to the noise model will have their instruction
appended to the noise model basis_gates if the instruction is in
the :class:`~qiskit.providers.aer.QasmSimulator` basis_gates. If
the instruction is not in the
:class:`~qiskit.providers.aer.QasmSimulator` basis_gates it is
assumed to be a label for a standard gate, and that gate should be
added to the `NoiseModel` basis_gates either using the init method,
or the :meth:`add_basis_gates` method.
"""
if basis_gates is None:
# Default basis gates is id, rz, sx, cx so that all standard
# non-identity instructions can be unrolled to rz, sx, cx,
# and identities won't be unrolled
self._basis_gates = set(['id', 'rz', 'sx', 'cx'])
else:
self._basis_gates = set(
name for name, _ in self._instruction_names_labels(basis_gates))
# Store gates with a noise model defined
self._noise_instructions = set()
# Store qubits referenced in noise model.
# These include gate qubits in local quantum and readout errors,
# and both gate and noise qubits for non-local quantum errors.
self._noise_qubits = set()
# Default (all-qubit) quantum errors are stored as:
# dict(str: QuantumError)
# where they keys are the instruction str label
self._default_quantum_errors = {}
# Local quantum errors are stored as:
# dict(str: dict(tuple: QuantumError))
# where the outer keys are the instruction str label and the
# inner dict keys are the gate qubits
self._local_quantum_errors = {}
# Non-local quantum errors are stored as:
# dict(str: dict(tuple: dict(tuple: QuantumError)))
# where the outer keys are the instruction str label, the middle dict
# keys are the gate qubits, and the inner most dict keys are
# the noise qubits.
self._nonlocal_quantum_errors = {}
# Default (all-qubit) readout error is stored as a single
# ReadoutError object since there may only be one defined.
self._default_readout_error = None
# Local readout errors are stored as:
# dict(tuple: ReadoutError)
# where the dict keys are the gate qubits.
self._local_readout_errors = {}
# Custom noise passes
self._custom_noise_passes = []
@property
def basis_gates(self):
"""Return basis_gates for compiling to the noise model."""
# Convert noise instructions to basis_gates string
return sorted(self._basis_gates)
@property
def noise_instructions(self):
"""Return the set of noisy instructions for this noise model."""
return sorted(self._noise_instructions)
@property
def noise_qubits(self):
"""Return the set of noisy qubits for this noise model."""
return sorted(self._noise_qubits)
[docs] @classmethod
def from_backend(cls, backend,
gate_error=True,
readout_error=True,
thermal_relaxation=True,
temperature=0,
gate_lengths=None,
gate_length_units='ns',
standard_gates=None,
warnings=True):
"""Return a noise model derived from a devices backend properties.
This function generates a noise model based on:
* 1 and 2 qubit gate errors consisting of a
:func:`depolarizing_error` followed
by a :func:`thermal_relaxation_error`.
* Single qubit :class:`ReadoutError` on all measurements.
The Error error parameters are tuned for each individual qubit based on
the :math:`T_1`, :math:`T_2`, frequency and readout error parameters for
each qubit, and the gate error and gate time parameters for each gate
obtained from the device backend properties.
**Additional Information**
The noise model includes the following errors:
* If ``readout_error=True`` include single qubit readout
errors on measurements.
* If ``gate_error=True`` and ``thermal_relaxation=True`` include:
* Single-qubit gate errors consisting of a :func:`depolarizing_error`
followed by a :func:`thermal_relaxation_error` for the qubit the
gate acts on.
* Two-qubit gate errors consisting of a 2-qubit
:func:`depolarizing_error` followed by single qubit
:func:`thermal_relaxation_error` on each qubit participating in
the gate.
* If ``gate_error=True`` is ``True`` and ``thermal_relaxation=False``:
* An N-qubit :func:`depolarizing_error` on each N-qubit gate.
* If ``gate_error=False`` and ``thermal_relaxation=True`` include
single-qubit :func:`thermal_relaxation_errors` on each qubits
participating in a multi-qubit gate.
For best practice in simulating a backend make sure that the
circuit is compiled using the set of basis gates in the noise
module by setting ``basis_gates=noise_model.basis_gates``
and using the device coupling map with
``coupling_map=backend.configuration().coupling_map``
**Specifying custom gate times**
The ``gate_lengths`` kwarg can be used to specify custom gate times
to add gate errors using the :math:`T_1` and :math:`T_2` values from
the backend properties. This should be passed as a list of tuples
``gate_lengths=[(name, value), ...]``
where ``name`` is the gate name string, and ``value`` is the gate time
in nanoseconds.
If a custom gate is specified that already exists in
the backend properties, the ``gate_lengths`` value will override the
gate time value from the backend properties.
If non-default values are used gate_lengths should be a list
Args:
backend (BackendV1): backend.
gate_error (bool): Include depolarizing gate errors (Default: True).
readout_error (Bool): Include readout errors in model
(Default: True).
thermal_relaxation (Bool): Include thermal relaxation errors
(Default: True).
temperature (double): qubit temperature in milli-Kelvin (mK) for
thermal relaxation errors (Default: 0).
gate_lengths (list): Custom gate times for thermal relaxation errors.
Used to extend or override the gate times in
the backend properties (Default: None))
gate_length_units (str): Time units for gate length values in
gate_lengths. Can be 'ns', 'ms', 'us',
or 's' (Default: 'ns').
standard_gates (bool): DEPRECATED, If true return errors as standard
qobj gates. If false return as unitary
qobj instructions (Default: None)
warnings (bool): Display warnings (Default: True).
Returns:
NoiseModel: An approximate noise model for the device backend.
Raises:
NoiseError: If the input backend is not valid.
"""
backend_interface_version = getattr(backend, "version", None)
if not isinstance(backend_interface_version, int):
backend_interface_version = 0
if backend_interface_version == 2:
raise NoiseError(
"NoiseModel.from_backend does not currently support V2 Backends.")
if backend_interface_version <= 1:
properties = backend.properties()
configuration = backend.configuration()
basis_gates = configuration.basis_gates
num_qubits = configuration.num_qubits
dt = getattr(configuration, "dt", 0)
if not properties:
raise NoiseError('Qiskit backend {} does not have a '
'BackendProperties'.format(backend))
elif isinstance(backend, BackendProperties):
warn(
'Passing BackendProperties instead of a "backend" object '
'has been deprecated as of qiskit-aer 0.10.0 and will be '
'removed no earlier than 3 months from that release date. '
'Duration dependent delay relaxation noise requires a '
'backend object.', DeprecationWarning, stacklevel=2)
properties = backend
basis_gates = set()
for prop in properties.gates:
basis_gates.add(prop.gate)
basis_gates = list(basis_gates)
num_qubits = len(properties.qubits)
dt = 0 # disable delay noise if dt is unknown
else:
raise NoiseError('{} is not a Qiskit backend or'
' BackendProperties'.format(backend))
noise_model = NoiseModel(basis_gates=basis_gates)
# Add single-qubit readout errors
if readout_error:
for qubits, error in basic_device_readout_errors(properties):
noise_model.add_readout_error(error, qubits, warnings=warnings)
if standard_gates is not None:
warn(
'"standard_gates" option has been deprecated as of qiskit-aer 0.10.0'
' and will be removed no earlier than 3 months from that release date.',
DeprecationWarning, stacklevel=2)
# Add gate errors
with catch_warnings():
filterwarnings(
"ignore",
category=DeprecationWarning,
module="qiskit.providers.aer.noise.device.models"
)
gate_errors = basic_device_gate_errors(
properties,
gate_error=gate_error,
thermal_relaxation=thermal_relaxation,
gate_lengths=gate_lengths,
gate_length_units=gate_length_units,
temperature=temperature,
standard_gates=standard_gates,
warnings=warnings)
for name, qubits, error in gate_errors:
noise_model.add_quantum_error(error, name, qubits, warnings=warnings)
if thermal_relaxation:
# Add delay errors via RelaxationNiose pass
try:
excited_state_populations = [
_excited_population(
freq=properties.frequency(q), temperature=temperature
) for q in range(num_qubits)]
except BackendPropertyError:
excited_state_populations = None
try:
t1s = [properties.t1(q) for q in range(num_qubits)]
t2s = [properties.t2(q) for q in range(num_qubits)]
delay_pass = RelaxationNoisePass(
t1s=t1s,
t2s=[_truncate_t2_value(t1, t2) for t1, t2 in zip(t1s, t2s)],
dt=dt,
op_types=Delay,
excited_state_populations=excited_state_populations
)
noise_model._custom_noise_passes.append(delay_pass)
except BackendPropertyError:
# Device does not have the required T1 or T2 information
# in its properties
pass
return noise_model
[docs] def is_ideal(self): # pylint: disable=too-many-return-statements
"""Return True if the noise model has no noise terms."""
# Get default errors
if self._default_quantum_errors:
return False
if self._default_readout_error:
return False
if self._local_quantum_errors:
return False
if self._local_readout_errors:
return False
if self._nonlocal_quantum_errors:
return False
if self._custom_noise_passes:
return False
return True
def __repr__(self):
"""Noise model repr"""
return "<NoiseModel on {}>".format(list(self._noise_instructions))
def __str__(self):
"""Noise model string representation"""
# Check if noise model is ideal
if self.is_ideal():
return "NoiseModel: Ideal"
# Get default errors
default_error_ops = []
for inst in self._default_quantum_errors:
default_error_ops.append('{}'.format(inst))
if self._default_readout_error is not None:
if 'measure' not in default_error_ops:
default_error_ops.append('measure')
# Get local errors
local_error_ops = []
for inst, dic in self._local_quantum_errors.items():
for qubits in dic.keys():
local_error_ops.append((inst, qubits))
for qubits in self._local_readout_errors:
tmp = ('measure', qubits)
if tmp not in local_error_ops:
local_error_ops.append(tmp)
# Get nonlocal errors
nonlocal_error_ops = []
for inst, dic in self._nonlocal_quantum_errors.items():
for qubits, errors in dic.items():
for noise_qubits in errors:
nonlocal_error_ops.append((inst, qubits, noise_qubits))
output = "NoiseModel:"
output += "\n Basis gates: {}".format(self.basis_gates)
if self._noise_instructions:
output += "\n Instructions with noise: {}".format(
list(self._noise_instructions))
if self._noise_qubits:
output += "\n Qubits with noise: {}".format(
list(self._noise_qubits))
if default_error_ops:
output += "\n All-qubits errors: {}".format(default_error_ops)
if local_error_ops:
output += "\n Specific qubit errors: {}".format(
local_error_ops)
if nonlocal_error_ops:
output += "\n Non-local specific qubit errors: {}".format(
nonlocal_error_ops)
return output
def __eq__(self, other):
"""Test if two noise models are equal."""
# This returns True if both noise models have:
# the same basis_gates
# the same noise_qubits
# the same noise_instructions
if (not isinstance(other, NoiseModel) or
self.basis_gates != other.basis_gates or
self.noise_qubits != other.noise_qubits or
self.noise_instructions != other.noise_instructions):
return False
# Check default readout errors is equal
if not self._readout_errors_equal(other):
return False
# Check quantum errors equal
if not self._all_qubit_quantum_errors_equal(other):
return False
if not self._local_quantum_errors_equal(other):
return False
if not self._nonlocal_quantum_errors_equal(other):
return False
# If we made it here they are equal
return True
[docs] def add_basis_gates(self, instructions, warnings=False):
"""Add additional gates to the noise model basis_gates.
This should be used to add any gates that are identified by a
custom gate label in the noise model.
Args:
instructions (list[str] or
list[Instruction]): the instructions error applies to.
warnings (bool): [DEPRECATED] display warning if instruction is not in
QasmSimulator basis_gates (Default: False).
"""
for name, _ in self._instruction_names_labels(instructions):
# If the instruction is in the default basis gates for the
# AerSimulator we add it to the basis gates.
if name in BASIS_GATES['automatic']:
if name not in ['measure', 'reset', 'initialize',
'kraus', 'superop', 'roerror']:
self._basis_gates.add(name)
elif warnings:
warn('"warnings" option has been deprecated as of qiskit-aer 0.10.0'
' and will be removed no earlier than 3 months from that release date.',
DeprecationWarning, stacklevel=2)
logger.warning(
"Warning: Adding a gate \"%s\" to basis_gates which is "
"not in AerSimulator basis_gates.", name)
[docs] def add_all_qubit_quantum_error(self, error, instructions, warnings=True):
"""
Add a quantum error to the noise model that applies to all qubits.
Args:
error (QuantumError): the quantum error object.
instructions (str or list[str] or
Instruction or
list[Instruction]): the instructions error applies to.
warnings (bool): Display warning if appending to an instruction that
already has an error (Default: True).
Raises:
NoiseError: if the input parameters are invalid.
Additional Information:
If the error object is ideal it will not be added to the model.
"""
# Format input as QuantumError
if not isinstance(error, QuantumError):
try:
error = QuantumError(error)
except NoiseError:
raise NoiseError("Input is not a valid quantum error.")
# Check if error is ideal and if so don't add to the noise model
if error.ideal():
return
# Add instructions
for name, label in self._instruction_names_labels(instructions):
self._check_number_of_qubits(error, name)
if label in self._default_quantum_errors:
new_error = self._default_quantum_errors[label].compose(error)
self._default_quantum_errors[label] = new_error
if warnings:
logger.warning(
"WARNING: all-qubit error already exists for "
"instruction \"%s\", "
"composing with additional error.", label)
else:
self._default_quantum_errors[label] = error
# Check if a specific qubit error has been applied for this instruction
if label in self._local_quantum_errors:
local_qubits = self._keys2str(
self._local_quantum_errors[label].keys())
if warnings:
logger.warning(
"WARNING: all-qubit error for instruction "
"\"%s\" will not apply to qubits: "
"%s as specific error already exists.", label,
local_qubits)
self._noise_instructions.add(label)
self.add_basis_gates(name)
[docs] def add_quantum_error(self, error, instructions, qubits, warnings=True):
"""
Add a quantum error to the noise model.
Args:
error (QuantumError): the quantum error object.
instructions (str or list[str] or
Instruction or
list[Instruction]): the instructions error applies to.
qubits (Sequence[int]): qubits instruction error applies to.
warnings (bool): Display warning if appending to an instruction that
already has an error (Default: True).
Raises:
NoiseError: if the input parameters are invalid.
Additional Information:
If the error object is ideal it will not be added to the model.
"""
# Error checking
if not isinstance(error, QuantumError):
try:
error = QuantumError(error)
except NoiseError:
raise NoiseError("Input is not a valid quantum error.")
try:
qubits = tuple(qubits)
except TypeError as ex:
raise NoiseError("Qubits must be convertible to a tuple of integers") from ex
# Check if error is ideal and if so don't add to the noise model
if error.ideal():
return
# Add noise qubits
for qubit in qubits:
self._noise_qubits.add(qubit)
# Add instructions
for name, label in self._instruction_names_labels(instructions):
self._check_number_of_qubits(error, name)
if not isinstance(label, str):
raise NoiseError("Qobj invalid instructions.")
# Check number of qubits is correct for standard instructions
self._check_number_of_qubits(error, name)
if label in self._local_quantum_errors:
qubit_dict = self._local_quantum_errors[label]
else:
qubit_dict = {}
# Convert qubits list to hashable string
if error.num_qubits != len(qubits):
raise NoiseError("Number of qubits ({}) does not match "
" the error size ({})".format(
len(qubits), error.num_qubits))
if qubits in qubit_dict:
new_error = qubit_dict[qubits].compose(error)
qubit_dict[qubits] = new_error
if warnings:
logger.warning(
"WARNING: quantum error already exists for "
"instruction \"%s\" on qubits %s "
", appending additional error.", label, qubits)
else:
qubit_dict[qubits] = error
# Add updated dictionary
self._local_quantum_errors[label] = qubit_dict
# Check if all-qubit error is already defined for this instruction
if label in self._default_quantum_errors:
if warnings:
logger.warning(
"WARNING: Specific error for instruction \"%s\" "
"on qubits %s overrides previously defined "
"all-qubit error for these qubits.", label, qubits)
self._noise_instructions.add(label)
self.add_basis_gates(name)
[docs] def add_nonlocal_quantum_error(self,
error,
instructions,
qubits,
noise_qubits,
warnings=True):
"""
Add a non-local quantum error to the noise model (DEPRECATED).
.. deprecated:: 0.9.0
Adding nonlocal noise to a noise model is deprecated and will
be removed no earlier than 3 months from the qiskit-aer 0.9.0
release date. To add non-local noise to a circuit you should
write a custom qiskit transpiler pass.
Args:
error (QuantumError): the quantum error object.
instructions (str or list[str] or
Instruction or
list[Instruction]): the instructions error applies to.
qubits (Sequence[int]): qubits instruction error applies to.
noise_qubits (Sequence[int]): Specify the exact qubits the error
should be applied to if different
to the instruction qubits.
warnings (bool): Display warning if appending to an instruction that
already has an error (Default: True).
Raises:
NoiseError: if the input parameters are invalid.
Additional Information:
If the error object is ideal it will not be added to the model.
"""
warn('Adding nonlocal noise to a noise model is deprecated as of'
' qiskit-aer 0.9.0 and will be removed no earlier than 3'
' months from that release date. To add non-local noise to'
' a circuit you should write a custom qiskit transpiler pass.',
DeprecationWarning)
# Error checking
if not isinstance(error, QuantumError):
try:
error = QuantumError(error)
except NoiseError:
raise NoiseError("Input is not a valid quantum error.")
try:
qubits = tuple(qubits)
noise_qubits = tuple(noise_qubits)
except TypeError as ex:
raise NoiseError("Qubits must be convertible to a tuple of integers") from ex
# Check if error is ideal and if so don't add to the noise model
if error.ideal():
return
# Add noise qubits
for qubit in qubits:
self._noise_qubits.add(qubit)
for qubit in noise_qubits:
self._noise_qubits.add(qubit)
# Add instructions
for name, label in self._instruction_names_labels(instructions):
if label in self._nonlocal_quantum_errors:
gate_qubit_dict = self._nonlocal_quantum_errors[label]
else:
gate_qubit_dict = {}
if qubits in gate_qubit_dict:
noise_qubit_dict = gate_qubit_dict[qubits]
if noise_qubits in noise_qubit_dict:
new_error = noise_qubit_dict[noise_qubits].compose(error)
noise_qubit_dict[noise_qubits] = new_error
else:
noise_qubit_dict[noise_qubits] = error
gate_qubit_dict[qubits] = noise_qubit_dict
if warnings:
logger.warning(
"Warning: nonlocal error already exists for "
"instruction \"%s\" on qubits %s."
"Composing additional error.", label, qubits)
else:
gate_qubit_dict[qubits] = {noise_qubits: error}
# Add updated dictionary
self._nonlocal_quantum_errors[label] = gate_qubit_dict
self._noise_instructions.add(label)
self.add_basis_gates(name, warnings=False)
[docs] def add_all_qubit_readout_error(self, error, warnings=True):
"""
Add a single-qubit readout error that applies measure on all qubits.
Args:
error (ReadoutError): the quantum error object.
warnings (bool): Display warning if appending to an instruction that
already has an error (Default: True)
Raises:
NoiseError: if the input parameters are invalid.
Additional Information:
If the error object is ideal it will not be added to the model.
"""
# Error checking
if not isinstance(error, ReadoutError):
try:
error = ReadoutError(error)
except NoiseError:
raise NoiseError("Input is not a valid readout error.")
# Check if error is ideal and if so don't add to the noise model
if error.ideal():
return
# Check number of qubits is correct for standard instructions
if error.number_of_qubits != 1:
raise NoiseError(
"All-qubit readout errors must defined as single-qubit errors."
)
if self._default_readout_error is not None:
if warnings:
logger.warning(
"WARNING: all-qubit readout error already exists, "
"overriding with new readout error.")
self._default_readout_error = error
# Check if a specific qubit error has been applied for this instruction
if self._local_readout_errors:
local_qubits = self._keys2str(self._local_readout_errors.keys())
if warnings:
logger.warning(
"WARNING: The all-qubit readout error will not "
"apply to measure of qubits qubits: %s "
"as specific readout errors already exist.", local_qubits)
self._noise_instructions.add("measure")
[docs] def add_readout_error(self, error, qubits, warnings=True):
"""
Add a readout error to the noise model.
Args:
error (ReadoutError): the quantum error object.
qubits (list[int] or tuple[int]): qubits instruction error applies to.
warnings (bool): Display warning if appending to an instruction that
already has an error [Default: True]
Raises:
NoiseError: if the input parameters are invalid.
Additional Information:
If the error object is ideal it will not be added to the model.
"""
# Error checking
if not isinstance(error, ReadoutError):
try:
error = ReadoutError(error)
except NoiseError:
raise NoiseError("Input is not a valid readout error.")
try:
qubits = tuple(qubits)
except TypeError as ex:
raise NoiseError("Qubits must be convertible to a tuple of integers") from ex
# Check if error is ideal and if so don't add to the noise model
if error.ideal():
return
# Add noise qubits
for qubit in qubits:
self._noise_qubits.add(qubit)
# Check error matches qubit size
if error.number_of_qubits != len(qubits):
raise NoiseError(
"Number of qubits ({}) does not match the readout "
"error size ({})".format(len(qubits), error.number_of_qubits))
# Check if we are overriding a previous error
if qubits in self._local_readout_errors:
if warnings:
logger.warning(
"WARNING: readout error already exists for qubits "
"%s, overriding with new readout error.", qubits)
self._local_readout_errors[qubits] = error
# Check if all-qubit readout error is already defined
if self._default_readout_error is not None:
if warnings:
logger.warning(
"WARNING: Specific readout error on qubits "
"%s overrides previously defined "
"all-qubit readout error for these qubits.", qubits)
self._noise_instructions.add("measure")
[docs] def to_dict(self, serializable=False):
"""
Return the noise model as a dictionary.
Args:
serializable (bool): if `True`, return a dict containing only types
that can be serializable by the stdlib `json` module.
Returns:
dict: a dictionary for a noise model.
"""
error_list = []
# Add default quantum errors
for name, error in self._default_quantum_errors.items():
error_dict = error.to_dict()
error_dict["operations"] = [name]
error_list.append(error_dict)
# Add specific qubit errors
for name, qubit_dict in self._local_quantum_errors.items():
for qubits, error in qubit_dict.items():
error_dict = error.to_dict()
error_dict["operations"] = [name]
error_dict["gate_qubits"] = [qubits]
error_list.append(error_dict)
# Add non-local errors
for name, qubit_dict in self._nonlocal_quantum_errors.items():
for qubits, noise_qubit_dict in qubit_dict.items():
for noise_qubits, error in noise_qubit_dict.items():
error_dict = error.to_dict()
error_dict["operations"] = [name]
error_dict["gate_qubits"] = [qubits]
error_dict["noise_qubits"] = [noise_qubits]
error_list.append(error_dict)
# Add default readout error
if self._default_readout_error is not None:
error_dict = self._default_readout_error.to_dict()
error_list.append(error_dict)
# Add local readout error
for qubits, error in self._local_readout_errors.items():
error_dict = error.to_dict()
error_dict["gate_qubits"] = [qubits]
error_list.append(error_dict)
ret = {"errors": error_list}
if serializable:
ret = json.loads(json.dumps(ret, cls=AerJSONEncoder))
return ret
[docs] @staticmethod
def from_dict(noise_dict):
"""
Load NoiseModel from a dictionary.
Args:
noise_dict (dict): A serialized noise model.
Returns:
NoiseModel: the noise model.
Raises:
NoiseError: if dict cannot be converted to NoiseModel.
"""
warn('from_dict has been deprecated as of qiskit-aer 0.10.0'
' and will be removed no earlier than 3 months from that release date.',
DeprecationWarning, stacklevel=2)
# Return noise model
noise_model = NoiseModel()
# Get error terms
errors = noise_dict.get('errors', [])
for error in errors:
error_type = error['type']
# Add QuantumError
if error_type == 'qerror':
noise_ops = tuple(
zip(error['instructions'], error['probabilities']))
instruction_names = error['operations']
all_gate_qubits = error.get('gate_qubits', None)
all_noise_qubits = error.get('noise_qubits', None)
with catch_warnings():
filterwarnings("ignore",
category=DeprecationWarning,
module="qiskit.providers.aer.noise")
qerror = QuantumError(noise_ops)
qerror._id = error.get('id', None) or qerror.id
if all_gate_qubits is not None:
for gate_qubits in all_gate_qubits:
# Load non-local quantum error
if all_noise_qubits is not None:
for noise_qubits in all_noise_qubits:
noise_model.add_nonlocal_quantum_error(
qerror,
instruction_names,
gate_qubits,
noise_qubits,
warnings=False)
# Add local quantum error
else:
noise_model.add_quantum_error(
qerror,
instruction_names,
gate_qubits,
warnings=False)
else:
# Add all-qubit quantum error
noise_model.add_all_qubit_quantum_error(
qerror, instruction_names, warnings=False)
# Add ReadoutError
elif error_type == 'roerror':
probabilities = error['probabilities']
all_gate_qubits = error.get('gate_qubits', None)
roerror = ReadoutError(probabilities)
# Add local readout error
if all_gate_qubits is not None:
for gate_qubits in all_gate_qubits:
noise_model.add_readout_error(
roerror, gate_qubits, warnings=False)
# Add all-qubit readout error
else:
noise_model.add_all_qubit_readout_error(
roerror, warnings=False)
# Invalid error type
else:
raise NoiseError("Invalid error type: {}".format(error_type))
return noise_model
def _instruction_names_labels(self, instructions):
"""Return two lists of instruction name strings and label strings."""
if not isinstance(instructions, (list, tuple)):
instructions = [instructions]
names_labels = []
for inst in instructions:
# If instruction does not have a label we use the name
# as the label
if isinstance(inst, Instruction):
name = inst.name
label = getattr(inst, 'label', inst.name)
names_labels.append((name, label))
elif isinstance(inst, str):
names_labels.append((inst, inst))
else:
raise NoiseError('Invalid instruction type {}'.format(inst))
return names_labels
def _check_number_of_qubits(self, error, name):
"""
Check if error is corrected number of qubits for standard instruction.
Args:
error (QuantumError): the quantum error object.
name (str): qobj instruction name to apply error to.
Raises:
NoiseError: If instruction and error qubit number do not match.
"""
def error_message(gate_qubits):
msg = "{} qubit QuantumError".format(error.num_qubits) + \
" cannot be applied to {} qubit".format(gate_qubits) + \
" instruction \"{}\".".format(name)
return msg
if name in self._1qubit_instructions and error.num_qubits != 1:
raise NoiseError(error_message(1))
if name in self._2qubit_instructions and error.num_qubits != 2:
raise NoiseError(error_message(2))
if name in self._3qubit_instructions and error.num_qubits != 3:
raise NoiseError(error_message(3))
def _keys2str(self, keys):
"""Convert dicitonary keys to comma seperated print string."""
tmp = "".join(["{}, ".format(key) for key in keys])
return tmp[:-2]
def _readout_errors_equal(self, other):
"""Check two noise models have equal readout errors"""
# Check default readout error is equal
if self._default_readout_error != other._default_readout_error:
return False
# Check local readout errors are equal
if sorted(self._local_readout_errors.keys()) != sorted(
other._local_readout_errors.keys()):
return False
for key in self._local_readout_errors:
if self._local_readout_errors[key] != other._local_readout_errors[
key]:
return False
return True
def _all_qubit_quantum_errors_equal(self, other):
"""Check two noise models have equal local quantum errors"""
if sorted(self._default_quantum_errors.keys()) != sorted(
other._default_quantum_errors.keys()):
return False
for key in self._default_quantum_errors:
if self._default_quantum_errors[
key] != other._default_quantum_errors[key]:
return False
return True
def _local_quantum_errors_equal(self, other):
"""Check two noise models have equal local quantum errors"""
if sorted(self._local_quantum_errors.keys()) != sorted(
other._local_quantum_errors.keys()):
return False
for key in self._local_quantum_errors:
inner_dict1 = self._local_quantum_errors[key]
inner_dict2 = other._local_quantum_errors[key]
if sorted(inner_dict1.keys()) != sorted(inner_dict2.keys()):
return False
for inner_key in inner_dict1:
if inner_dict1[inner_key] != inner_dict2[inner_key]:
return False
if self._local_quantum_errors[key] != other._local_quantum_errors[
key]:
return False
return True
def _nonlocal_quantum_errors_equal(self, other):
"""Check two noise models have equal non-local quantum errors"""
if sorted(self._nonlocal_quantum_errors.keys()) != sorted(
other._nonlocal_quantum_errors.keys()):
return False
for key in self._nonlocal_quantum_errors:
inner_dict1 = self._nonlocal_quantum_errors[key]
inner_dict2 = other._nonlocal_quantum_errors[key]
if sorted(inner_dict1.keys()) != sorted(inner_dict2.keys()):
return False
for inner_key in inner_dict1:
iinner_dict1 = inner_dict1[inner_key]
iinner_dict2 = inner_dict2[inner_key]
if sorted(iinner_dict1.keys()) != sorted(iinner_dict2.keys()):
return False
for iinner_key in iinner_dict1:
if iinner_dict1[iinner_key] != iinner_dict2[iinner_key]:
return False
return True
def _pass_manager(self) -> Optional[PassManager]:
"""
Return the pass manager that add custom noises defined as noise passes
(stored in the _custom_noise_passes field). Note that the pass manager
does not include passes to add other noises (stored in the different field).
"""
passes = []
passes.extend(self._custom_noise_passes)
if len(passes) > 0:
return PassManager(passes)
return None