Skip to content

Commit

Permalink
Add support for BackendV2 in NoiseModel.from_backend
Browse files Browse the repository at this point in the history
  • Loading branch information
itoko committed Apr 21, 2022
1 parent a21260c commit 6294344
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 53 deletions.
129 changes: 105 additions & 24 deletions qiskit/providers/aer/noise/device/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,52 @@
from numpy import inf, exp, allclose

import qiskit.quantum_info as qi
from .parameters import readout_error_values
from .parameters import _NANOSECOND_UNITS
from .parameters import gate_param_values
from .parameters import readout_error_values
from .parameters import thermal_relaxation_values
from .parameters import _NANOSECOND_UNITS

from ..errors.readout_error import ReadoutError
from ..errors.standard_errors import depolarizing_error
from ..errors.standard_errors import thermal_relaxation_error
from ..noiseerror import NoiseError

logger = logging.getLogger(__name__)


def basic_device_readout_errors(properties):
def basic_device_readout_errors(properties, target=None):
"""
Return readout error parameters from a devices BackendProperties.
Return readout error parameters from a devices Target or BackendProperties.
If ``target`` is supplied, ``properties`` will not be used.
And return no errors if the target has no qubit properties or each qubit properties
does not have ``"prob_meas1_prep0"`` and ``"prob_meas0_prep1"`` attributes.
Args:
properties (BackendProperties): device backend properties
target (Target): device backend target
Returns:
list: A list of pairs ``(qubits, ReadoutError)`` for qubits with
non-zero readout error values.
"""
errors = []
for qubit, value in enumerate(readout_error_values(properties)):
if value is not None and not allclose(value, [0, 0]):
probabilities = [[1 - value[0], value[0]], [value[1], 1 - value[1]]]
errors.append(([qubit], ReadoutError(probabilities)))
if target is None:
# create from BackendProperties
for qubit, value in enumerate(readout_error_values(properties)):
if value is not None and not allclose(value, [0, 0]):
probabilities = [[1 - value[0], value[0]], [value[1], 1 - value[1]]]
errors.append(([qubit], ReadoutError(probabilities)))
else:
# create from Target
for q in range(target.num_qubits):
prop = target['measure'][(q,)]
if hasattr(prop, "prob_meas1_prep0") and hasattr(prop, "prob_meas0_prep1"):
p0m1, p1m0 = prop.prob_meas1_prep0, prop.prob_meas0_prep1
else:
p0m1, p1m0 = prop.error, prop.error
probabilities = [[1 - p0m1, p0m1], [p1m0, 1 - p1m0]]
errors.append(([q], ReadoutError(probabilities)))

return errors


Expand All @@ -59,7 +77,8 @@ def basic_device_gate_errors(properties,
gate_length_units='ns',
temperature=0,
standard_gates=None,
warnings=True):
warnings=True,
target=None):
"""
Return QuantumErrors derived from a devices BackendProperties.
Expand All @@ -84,18 +103,43 @@ def basic_device_gate_errors(properties,
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).
warnings (bool): PLAN TO BE DEPRECATED, Display warnings (Default: True).
target (Target): device backend target (Default: None). When this is supplied,
several options are disabled:
`properties`, `gate_lengths` and `gate_length_units` are not used
during the construction of gate errors.
Default values are always used for `standard_gates` and `warnings`.
Returns:
list: A list of tuples ``(label, qubits, QuantumError)``, for gates
with non-zero quantum error terms, where `label` is the label of the
noisy gate, `qubits` is the list of qubits for the gate.
Raises:
NoiseError: If invalid arguments are supplied.
"""
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)

if target is not None:
if not standard_gates or not warnings:
warn("When 'target' is supplied, `standard_gates` and `warnings` are ignored,"
" and their default values are always used.", UserWarning)

if gate_lengths:
raise NoiseError("When 'target' is supplied, `gate_lengths` option is disabled."
"Use `duration` property in InstructionProperties in 'target' instead.")

return _basic_device_target_gate_errors(
target=target,
gate_error=gate_error,
thermal_relaxation=thermal_relaxation,
temperature=temperature
)

# Initilize empty errors
depol_error = None
relax_error = None
Expand Down Expand Up @@ -151,20 +195,57 @@ def basic_device_gate_errors(properties,
qubits, error_param, relax_error, standard_gates, warnings=warnings)

# Combine errors
if depol_error is None and relax_error is None:
# No error for this gate
pass
elif depol_error is not None and relax_error is None:
# Append only the depolarizing error
errors.append((name, qubits, depol_error))
# Append only the relaxation error
elif relax_error is not None and depol_error is None:
errors.append((name, qubits, relax_error))
else:
# Append a combined error of depolarizing error
# followed by a relaxation error
combined_error = depol_error.compose(relax_error)
combined_error = _combine_depol_and_relax_error(depol_error, relax_error)
if combined_error:
errors.append((name, qubits, combined_error))

return errors


def _combine_depol_and_relax_error(depol_error, relax_error):
if depol_error and relax_error:
return depol_error.compose(relax_error)
if depol_error:
return depol_error
if relax_error:
return relax_error
return None


def _basic_device_target_gate_errors(target,
gate_error=True,
thermal_relaxation=True,
temperature=0):
"""Return QuantumErrors derived from a devices Target."""
errors = []
for op_name, inst_prop_dic in target.items():
for qubits, inst_prop in inst_prop_dic.items():
depol_error = None
relax_error = None
# Get relaxation error
if thermal_relaxation:
relax_params = {q: (target.qubit_properties[q].t1,
target.qubit_properties[q].t2,
target.qubit_properties[q].frequency)
for q in qubits}
relax_error = _device_thermal_relaxation_error(
qubits=qubits,
gate_time=inst_prop.duration,
relax_params=relax_params,
temperature=temperature,
)
# Get depolarizing error
if gate_error:
depol_error = _device_depolarizing_error(
qubits=qubits,
error_param=inst_prop.error,
relax_error=relax_error,
)
# Combine errors
combined_error = _combine_depol_and_relax_error(depol_error, relax_error)
if combined_error:
errors.append((op_name, qubits, combined_error))

return errors


Expand Down
89 changes: 60 additions & 29 deletions qiskit/providers/aer/noise/noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"""
Noise model class for Qiskit Aer simulators.
"""

import copy
import json
import logging
from typing import Optional
Expand All @@ -21,6 +21,7 @@
from numpy import ndarray

from qiskit.circuit import Instruction, Delay
from qiskit.providers import QubitProperties
from qiskit.providers.exceptions import BackendPropertyError
from qiskit.providers.models import BackendProperties
from qiskit.transpiler import PassManager
Expand Down Expand Up @@ -277,7 +278,8 @@ def from_backend(cls, backend,
If non-default values are used gate_lengths should be a list
Args:
backend (BackendV1): backend.
backend (Backend): backend. For BackendV2, `standard_gates` and `warnings`
options are ignored, and their default values are used.
gate_error (bool): Include depolarizing gate errors (Default: True).
readout_error (Bool): Include readout errors in model
(Default: True).
Expand All @@ -294,31 +296,26 @@ def from_backend(cls, backend,
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).
warnings (bool): PLAN TO BE DEPRECATED, Display warnings (Default: True).
Returns:
NoiseModel: An approximate noise model for the device backend.
Raises:
NoiseError: If the input backend is not valid.
"""
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)

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):
target = None
if isinstance(backend, BackendProperties):
warn(
'Passing BackendProperties instead of a "backend" object '
'has been deprecated as of qiskit-aer 0.10.0 and will be '
Expand All @@ -331,22 +328,54 @@ def from_backend(cls, backend,
basis_gates.add(prop.gate)
basis_gates = list(basis_gates)
num_qubits = len(properties.qubits)
all_qubit_properties = [QubitProperties(t1=properties.t1(q),
t2=properties.t2(q),
frequency=properties.frequency(q))
for q in range(num_qubits)]
dt = 0 # disable delay noise if dt is unknown
elif backend_interface_version == 2:
if standard_gates is not None or not warnings:
warn("When a BackendV2 is supplied, `standard_gates` and `warnings`"
" are ignored, and their default values are used.", UserWarning)
properties = None
basis_gates = backend.operation_names
target = backend.target
if gate_lengths:
from .device.parameters import _NANOSECOND_UNITS
# Update target based on gate_lengths and gate_length_units
target = copy.deepcopy(target)
for op_name, qubits, value in gate_lengths:
prop = target[op_name][qubits]
prop.duration = value * _NANOSECOND_UNITS[gate_length_units]
target.update_instruction_properties(op_name, qubits, prop)
all_qubit_properties = backend.target.qubit_properties
if not all_qubit_properties:
warn(f"Qiskit backend {backend} has no QubitProperties, so the resulting"
" noise model will not include any thermal relaxation errors.", UserWarning)
dt = backend.dt
elif backend_interface_version <= 1:
properties = backend.properties()
configuration = backend.configuration()
basis_gates = configuration.basis_gates
all_qubit_properties = [QubitProperties(t1=properties.t1(q),
t2=properties.t2(q),
frequency=properties.frequency(q))
for q in range(configuration.num_qubits)]
dt = getattr(configuration, "dt", 0)
if not properties:
raise NoiseError('Qiskit backend {} does not have a '
'BackendProperties'.format(backend))
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):
for qubits, error in basic_device_readout_errors(properties, target=target):
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(
Expand All @@ -362,25 +391,27 @@ def from_backend(cls, backend,
gate_length_units=gate_length_units,
temperature=temperature,
standard_gates=standard_gates,
warnings=warnings)
warnings=warnings,
target=target,
)
for name, qubits, error in gate_errors:
noise_model.add_quantum_error(error, name, qubits, warnings=warnings)

if thermal_relaxation:
if thermal_relaxation and all_qubit_properties:
# Add delay errors via RelaxationNiose pass
try:
excited_state_populations = [
_excited_population(
freq=properties.frequency(q), temperature=temperature
) for q in range(num_qubits)]
freq=q.frequency, temperature=temperature
) for q in all_qubit_properties]
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)]
t1s = [prop.t1 for prop in all_qubit_properties]
t2s = [_truncate_t2_value(prop.t1, prop.t2) for prop in all_qubit_properties]
delay_pass = RelaxationNoisePass(
t1s=t1s,
t2s=[_truncate_t2_value(t1, t2) for t1, t2 in zip(t1s, t2s)],
t2s=t2s,
dt=dt,
op_types=Delay,
excited_state_populations=excited_state_populations
Expand Down
12 changes: 12 additions & 0 deletions test/terra/noise/test_noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,18 @@ def test_noise_model_from_mumbai(self):
result = AerSimulator().run(circ, noise_model=noise_model).result()
self.assertTrue(result.success)

def test_noise_model_from_backend_v2(self):
circ = QuantumCircuit(2)
circ.x(0)
circ.x(1)
circ.measure_all()

backend = mock.FakeBackendV2()
noise_model = NoiseModel.from_backend(backend)
circ = transpile(circ, backend, optimization_level=0)
result = AerSimulator().run(circ, noise_model=noise_model).result()
self.assertTrue(result.success)

def test_noise_model_from_invalid_t2_backend(self):
"""Test if issue user warning when creating a noise model from invalid t2 backend"""
from qiskit.providers.models.backendproperties import BackendProperties, Gate, Nduv
Expand Down

0 comments on commit 6294344

Please sign in to comment.