combine device noise models into single model

This commit is contained in:
cjwood 2018-12-14 15:54:43 -05:00
parent 748a9d2cf8
commit 68edb62b96
4 changed files with 345 additions and 172 deletions

View File

@ -11,7 +11,7 @@ This module contains classes and functions to build a noise model for
simulating a Qiskit quantum circuit in the presence of errors.
Noise Models
----------------------
------------
Noise models for a noisy simulator are represented using the `NoiseModel`
class. This can be used to generate a custom noise model, or an automatic
noise model can be generated for a device using built in functions.
@ -21,20 +21,14 @@ Automatically Generated Noise Models
Approximate noise models for a hardware device can be generated from the
device properties using the functions from the `device` submodule.
* `device.depolarizing_noise_model`: Generates a noise model based on
one-qubit depolarizing errors acting after X90 pulses during u1, u2,
and u3 gates, two-qubit depolarizing errors acting after cx gates,
and readout errors acting after measurement. The error parameters
are tuned for each individual qubit based on 1 and 2-qubit error
parameters from the device backend properties.
* `device.thermal_relaxation_noise_model`: Generates a noise mode
based on one-qubit thermal relaxation errors sacting after X90
pulses during u1, u2, and u3 gates, acting on both qubits after cx
gates, and readout errors acting after measurement. The error
parameters are tuned for each individual qubit based on the T_1,
T_2, and single qubit gate time parameters from the device backend
properties.
Basic device noise model
------------------------
Generates a noise mode based on 1 and 2 qubit gate errors consisting of
a depolarizing error followed by a thermal relaxation error, and readout
errors on measurement outcomes. The error parameters are tuned for each
individual qubit based on the T_1, 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.
Custom Noise Models
-------------------
@ -42,26 +36,31 @@ Custom noise models may be constructed by adding errors to a NoiseModel
object. Errors are represented using by the `QuantumError` and
`ReadoutError` classes from the `noise.errors` module:
* `QuantumError`: Errors that affect the quantum state during a
simulation. They may be applied after specific circuit gates or
reset operations, or before measure operations of qubits.
Quantum errors
--------------
Errors that affect the quantum state during a simulation. They may be
applied after specific circuit gates or reset operations, or before
measure operations of qubits.
* `ReadoutError`: Errors that apply to classical bit registers
after a measurement. They do not change the quantum state of the
system, only the recorded classical measurement outcome.
ReadoutError
------------
Errors that apply to classical bit registers after a measurement. They
do not change the quantum state of the system, only the recorded
classical measurement outcome.
Helper functions exist for generating standard quantum error channels in
the `noise.errors` module. These functions are:
the `noise.errors` module. These allow simple generation of the follow
canonical types of quantum errors:
* `kraus_error`
* `mixed_unitary_error`
* `coherent_unitary_error`
* `pauli_error`
* `depolarizing_error`
* `thermal_relaxation_error`
* `phase_amplitude_damping_error`
* `amplitude_damping_error`
* `phase_damping_error`
Kraus error
Mixed unitary error
Coherent unitary error
Pauli error
Depolarizing error
Thermal relaxation error
Amplitude damping error
Phase damping error
Combined phase and amplitude damping error
"""
from .noise_model import NoiseModel

View File

@ -7,6 +7,7 @@
"""Device noise models module Qiskit Aer."""
from .models import depolarizing_noise_model
from .models import thermal_relaxation_noise_model
from .models import basic_device_noise_model
from .models import basic_device_readout_errors
from .models import basic_device_gate_errors
from . import parameters

View File

@ -12,39 +12,60 @@ Simplified noise models for devices backends.
from numpy import inf, exp
from .parameters import readout_error_values
from .parameters import gate_time_values
from .parameters import gate_error_values
from .parameters import gate_param_values
from .parameters import thermal_relaxation_values
from ..noiseerror import NoiseError
from ..noise_model import NoiseModel
from ..errors.readout_error import ReadoutError
from ..errors.standard_errors import depolarizing_error
from ..errors.standard_errors import thermal_relaxation_error
def depolarizing_noise_model(properties,
def basic_device_noise_model(properties,
readout_error=True,
thermal_relaxation=True,
temperature=0,
gate_times=None,
standard_gates=True):
"""Generae a depolarizing noise model from device backend properties.
"""Approximate device noise model derived from backend properties.
Params:
properties (BackendProperties): backend properties
readout_error (Bool): Include readout errors in model
(Default: True)
readout_errors (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_times (list): Custom gate times for thermal relaxation errors.
Used to extend or override the gate times in
the backend properties (Default: None).
standard_gates (bool): If true return errors as standard
qobj gates. If false return as unitary
qobj instructions (Default: True)
Returns:
NoiseModel: An approximate noise model for the backend.
NoiseModel: An approximate noise model for the device backend.
Additional Information:
The noise model will consist:
* Depolarizing errors on gates defined be defined in
`BackendProperties.gates`.
* Single qubit readout errors on measurements if `readout_errors`
is set to True.
The noise model includes the following errors:
If readout_error is True:
* Single qubit readout errors on measurements.
If thermal_relaxation is True:
* Single-qubit gate errors consisting of a depolarizing error
followed by a thermal relaxation error for the qubit the gate
acts on.
* Two-qubit gate errors consisting of a 2-qubit depolarizing
error followed by single qubit thermal relaxation errors for
all qubits participating in the gate.
Else if thermal_relaxation is False:
* Single-qubit depolarizing gate errors.
* Multi-qubit depolarizing gate errors.
For best practice in simulating a backend make sure that the
circuit is compiled using the set of basis gates in the noise
@ -52,43 +73,6 @@ def depolarizing_noise_model(properties,
`basis_gates = noise_model.basis_gates`
and using the device coupling map with:
`coupling_map = backend.configuration().coupling_map`
"""
noise_model = NoiseModel()
# Add single-qubit readout errors
if readout_error:
for qubits, error in device_readout_errors(properties):
noise_model.add_readout_error(error, qubits)
# Add depolarizing gate errors
gate_errors = device_depolarizing_errors(properties,
standard_gates=standard_gates)
for name, qubits, error in gate_errors:
noise_model.add_quantum_error(error, name, qubits)
return noise_model
def thermal_relaxation_noise_model(properties,
readout_error=True,
temperature=0,
gate_times=None):
"""Thermal relaxation noise model derived from backend properties.
Params:
properties (BackendProperties): backend properties
readout_errors (Bool): Include readout errors in model
(Default: True)
temperature (double): qubit temperature in milli-Kelvin (mK)
(Default: 0)
gate_times (list): Override device gate times with custom
values. If None use gate times from
backend properties. (Default: None)
Returns:
NoiseModel: An approximate noise model for the device backend.
Additional Information:
Secifying custom gate times:
@ -103,62 +87,28 @@ def thermal_relaxation_noise_model(properties,
the backend properties, the `gate_times` value will override the
gate time value from the backend properties.
If non-default values are used gate_times should be a list
The noise model will consist:
* Single qubit readout errors on measurements.
* Single-qubit relaxation errors on all qubits participating in
a noisy quantum gate. The relaxation strength is determined by
the individual qubit T1 and T2 values and the gate_time of the
gate as defined in `BackendProperties.gates`.
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`
"""
noise_model = NoiseModel()
# Add single-qubit readout errors
if readout_error:
for qubits, error in device_readout_errors(properties):
for qubits, error in basic_device_readout_errors(properties):
noise_model.add_readout_error(error, qubits)
# Add depolarizing gate errors
gate_errors = device_thermal_relaxation_errors(properties,
# Add gate errors
gate_errors = basic_device_gate_errors(properties,
thermal_relaxation=thermal_relaxation,
gate_times=gate_times,
temperature=temperature,
gate_times=gate_times)
standard_gates=standard_gates)
for name, qubits, error in gate_errors:
noise_model.add_quantum_error(error, name, qubits)
return noise_model
def device_depolarizing_errors(properties, standard_gates=True):
"""Get depolarizing noise quantum error objects for backend gates
Args:
properties (BackendProperties): device backend properties
standard_gates (bool): If true return errors as standard
qobj gates. If false return as unitary
qobj instructions (Default: True)
Returns:
list: A list of tuples (name, qubits, error).
"""
errors = []
for name, qubits, value in gate_error_values(properties):
if value is not None and value > 0:
error = depolarizing_error(value, len(qubits),
standard_gates=standard_gates)
errors.append((name, qubits, error))
return errors
def device_readout_errors(properties):
def basic_device_readout_errors(properties):
"""Get readout error objects for each qubit from backend properties
Args:
@ -176,7 +126,9 @@ def device_readout_errors(properties):
return errors
def device_thermal_relaxation_errors(properties, temperature=0, gate_times=None):
def basic_device_gate_errors(properties, thermal_relaxation=True,
gate_times=None, temperature=0,
standard_gates=True):
"""Get depolarizing noise quantum error objects for backend gates
Args:
@ -193,25 +145,112 @@ def device_thermal_relaxation_errors(properties, temperature=0, gate_times=None)
Additional Information:
If non-default values are used gate_times should be a list
of tuples (name, value) where name is the gate name string,
and value is the gate time in nanoseconds.
of tuples (name, qubits, value) where name is the gate name string,
qubits is a list of qubits or None to apply gate time to this
gate one any set of qubits, and value is the gate time in
nanoseconds.
"""
# Generate custom gate time dict
custom_times = {}
if thermal_relaxation:
# If including thermal relaxation errors load
# T1, T2, and frequency values from properties
relax_params = thermal_relaxation_values(properties)
# If we are specifying custom gate times include
# them in the custom times dict
if gate_times:
custom_times = {name: value for name, value in gate_times}
else:
custom_times = {}
# Append device gate times to custom gate times
device_gate_times = gate_time_values(properties)
relax_values = thermal_relaxation_values(properties)
errors = []
for name, qubits, value in device_gate_times:
for name, qubits, value in gate_times:
if name in custom_times:
gate_time = custom_times[name]
custom_times[name].append((qubits, value))
else:
gate_time = value
if gate_time is not None and gate_time > 0:
custom_times[name] = [(qubits, value)]
# Get the device gate parameters from properties
device_gate_params = gate_param_values(properties)
# Construct quantum errors
errors = []
for name, qubits, gate_time, gate_error in device_gate_params:
# Check for custom gate time
relax_time = gate_time
# Override with custom value
if name in custom_times:
filtered = [val for q, val in custom_times[name]
if q is None or q == qubits]
if filtered:
# get first value
relax_time = filtered[0]
# Get depolarizing error channel
depol_error = _device_depolarizing_error(qubits, gate_error,
relax_time,
relax_params,
temperature,
thermal_relaxation,
standard_gates)
# Get relaxation error
relax_error = _device_thermal_relaxation_error(qubits, relax_time,
relax_params,
temperature,
thermal_relaxation)
# 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)
errors.append((name, qubits, combined_error))
return errors
def _device_depolarizing_error(qubits, gate_error, gate_time, relax_params,
temperature, thermal_relaxation=True,
standard_gates=True):
"""Construct a depolarizing_error for device"""
error = None
if not thermal_relaxation:
# Model gate error entirely as depolarizing error
p_depol = _depol_error_value_one_qubit(gate_error)
else:
# Model gate error as thermal relaxation and depolarizing
# error.
# Get depolarizing probability
if len(qubits) == 1:
t1, t2, _ = relax_params[qubits[0]]
p_depol = _depol_error_value_one_qubit(gate_error,
gate_time,
t1=t1, t2=t2)
elif len(qubits) == 2:
q0_t1, q0_t2, _ = relax_params[qubits[0]]
q1_t1, q1_t2, _ = relax_params[qubits[1]]
p_depol = _depol_error_value_two_qubit(gate_error,
gate_time,
qubit0_t1=q0_t1,
qubit0_t2=q0_t2,
qubit1_t1=q1_t1,
qubit1_t2=q1_t2)
else:
raise NoiseError("Device noise model only supports" +
"1 and 2-qubit gates when using "
"thermal_relaxation=True.")
if p_depol > 0:
error = depolarizing_error(p_depol, len(qubits),
standard_gates=standard_gates)
return error
def _device_thermal_relaxation_error(qubits, gate_time, relax_params,
temperature, thermal_relaxation=True):
"""Construct a thermal_relaxation_error for device"""
# Check trivial case
if not thermal_relaxation or gate_time is None or gate_time == 0:
return None
# convert gate time to same units as T1 and T2 (microseconds)
gate_time = gate_time / 1000
# Construct a tensor product of single qubit relaxation errors
@ -219,7 +258,21 @@ def device_thermal_relaxation_errors(properties, temperature=0, gate_times=None)
first = True
error = None
for qubit in reversed(qubits):
t1, t2, freq = relax_values[qubit]
t1, t2, freq = relax_params[qubit]
population = _excited_population(freq, temperature)
if first:
error = thermal_relaxation_error(t1, t2, gate_time,
population)
first = False
else:
single = thermal_relaxation_error(t1, t2, gate_time,
population)
error = error.kron(single)
return error
def _excited_population(freq, temperature):
"""Return excited state population"""
population = 0
if freq != inf and temperature != 0:
# Compute the excited state population from qubit
@ -234,14 +287,101 @@ def device_thermal_relaxation_errors(properties, temperature=0, gate_times=None)
if temperature < 0:
# negative temperate implies |1> is thermal ground
population = 1 - population
if first:
error = thermal_relaxation_error(t1, t2, gate_time,
population)
first = False
return population
def _depol_error_value_one_qubit(gate_error, gate_time=0, t1=inf, t2=inf):
"""Return 2-qubit depolarizing channel probability for device model"""
# Check trivial case where there is no gate error
if gate_error is None:
return None
if gate_error == 0:
return 0
# Check t1 and t2 are valid
if t1 <= 0:
raise NoiseError("Invalid T_1 relaxation time parameter: T_1 <= 0.")
if t2 <= 0:
raise NoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.")
if t2 - 2 * t1 > 0:
raise NoiseError("Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.")
# If T1 or T2 we have only a depolarizing error model
# in this case p_depol = dim * gate_error / (dim - 1)
# with dim = 2 for 1-qubit
if gate_time is None:
gate_time = 0
if gate_time == 0 or (t1 == inf and t2 == inf):
if gate_error is not None and gate_error > 0:
return 2 * gate_error
else:
single = thermal_relaxation_error(t1, t2, gate_time,
population)
error = error.kron(single)
if error is not None:
errors.append((name, qubits, error))
return errors
return 0
# Otherwise we calculate the depolarizing error probability to account
# for the difference between the relaxation error and gate error
if t1 == inf:
par1 = 1
else:
par1 = exp(-gate_time / t1)
if t2 == inf:
par2 = 1
else:
par2 = exp(-gate_time / t2)
p_depol = 1 + 3 * (2 * gate_error - 1) / (par1 + 2 * par2)
return p_depol
def _depol_error_value_two_qubit(gate_error, gate_time=0,
qubit0_t1=inf, qubit0_t2=inf,
qubit1_t1=inf, qubit1_t2=inf):
"""Return 2-qubit depolarizing channel probability for device model"""
# Check trivial case where there is no gate error
if gate_error is None:
return None
if gate_error == 0:
return 0
# Check t1 and t2 are valid
if qubit0_t1 <= 0 or qubit1_t1 <= 0:
raise NoiseError("Invalid T_1 relaxation time parameter: T_1 <= 0.")
if qubit0_t2 <= 0 or qubit1_t2 <= 0:
raise NoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.")
if qubit0_t2 - 2 * qubit0_t1 > 0 or qubit1_t2 - 2 * qubit1_t1 > 0:
raise NoiseError("Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.")
# If T1 or T2 we have only a depolarizing error model
# in this case p_depol = dim * gate_error / (dim - 1)
# with dim = 4 for 2-qubits
if gate_time is None:
gate_time = 0
if gate_time == 0 or (qubit0_t1 == inf and qubit0_t2 == inf and
qubit1_t1 == inf and qubit1_t2 == inf):
if gate_error is not None and gate_error > 0:
return 4 * gate_error / 3
else:
return 0
# Otherwise we calculate the depolarizing error probability to account
# for the difference between the relaxation error and gate error
if qubit0_t1 == inf:
q0_par1 = 1
else:
q0_par1 = exp(-gate_time / qubit0_t1)
if qubit0_t2 == inf:
q0_par2 = 1
else:
q0_par2 = exp(-gate_time / qubit0_t2)
if qubit1_t1 == inf:
q1_par1 = 1
else:
q1_par1 = exp(-gate_time / qubit1_t1)
if qubit1_t2 == inf:
q1_par2 = 1
else:
q1_par2 = exp(-gate_time / qubit1_t2)
denom = (q0_par1 + q1_par1 + q0_par1 * q1_par1 +
4 * q0_par2 * q1_par2 +
2 * (q0_par2 + q1_par2) +
2 * (q1_par1 * q0_par2 + q0_par1 * q1_par2))
p_depol = 1 + (5 / 3) * (4 * gate_error - 3) / denom
return p_depol

View File

@ -17,6 +17,39 @@ _NANOSECOND_UNITS = {'s': 1e9, 'ms': 1e6, 'µs': 1e3, 'us': 1e3, 'ns': 1}
_GHZ_UNITS = {'Hz': 1e-9, 'KHz': 1e-6, 'MHz': 1e-3, 'GHz': 1, 'THz': 1e3}
def gate_param_values(properties):
"""Get gate error values for backend gate from backend properties
Args:
properties (BackendProperties): device backend properties
Returns:
list: A list of tuples (name, qubits, time, error). If gate
error or gate_time information is not available None will be
returned for value.
"""
values = []
for gate in properties.gates:
name = gate.gate
qubits = gate.qubits
# Check for gate time information
gate_time = None # default value
time_param = _check_for_item(gate.parameters, 'gate_time')
if hasattr(time_param, 'value'):
gate_time = time_param.value
if hasattr(time_param, 'unit'):
# Convert gate time to ns
gate_time *= _NANOSECOND_UNITS.get(time_param.unit, 1)
# Check for gate error information
gate_error = None # default value
error_param = _check_for_item(gate.parameters, 'gate_error')
if hasattr(error_param, 'value'):
gate_error = error_param.value
values.append((name, qubits, gate_time, gate_error))
return values
def gate_error_values(properties):
"""Get gate error values for backend gate from backend properties