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. simulating a Qiskit quantum circuit in the presence of errors.
Noise Models Noise Models
---------------------- ------------
Noise models for a noisy simulator are represented using the `NoiseModel` 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 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. 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 Approximate noise models for a hardware device can be generated from the
device properties using the functions from the `device` submodule. device properties using the functions from the `device` submodule.
* `device.depolarizing_noise_model`: Generates a noise model based on Basic device noise model
one-qubit depolarizing errors acting after X90 pulses during u1, u2, ------------------------
and u3 gates, two-qubit depolarizing errors acting after cx gates, Generates a noise mode based on 1 and 2 qubit gate errors consisting of
and readout errors acting after measurement. The error parameters a depolarizing error followed by a thermal relaxation error, and readout
are tuned for each individual qubit based on 1 and 2-qubit error errors on measurement outcomes. The error parameters are tuned for each
parameters from the device backend properties. 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
* `device.thermal_relaxation_noise_model`: Generates a noise mode for each gate obtained from the device backend properties.
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.
Custom Noise Models 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 object. Errors are represented using by the `QuantumError` and
`ReadoutError` classes from the `noise.errors` module: `ReadoutError` classes from the `noise.errors` module:
* `QuantumError`: Errors that affect the quantum state during a Quantum errors
simulation. They may be applied after specific circuit gates or --------------
reset operations, or before measure operations of qubits. 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 ReadoutError
after a measurement. They do not change the quantum state of the ------------
system, only the recorded classical measurement outcome. 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 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` Kraus error
* `mixed_unitary_error` Mixed unitary error
* `coherent_unitary_error` Coherent unitary error
* `pauli_error` Pauli error
* `depolarizing_error` Depolarizing error
* `thermal_relaxation_error` Thermal relaxation error
* `phase_amplitude_damping_error` Amplitude damping error
* `amplitude_damping_error` Phase damping error
* `phase_damping_error` Combined phase and amplitude damping error
""" """
from .noise_model import NoiseModel from .noise_model import NoiseModel

View File

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

View File

@ -12,39 +12,60 @@ Simplified noise models for devices backends.
from numpy import inf, exp from numpy import inf, exp
from .parameters import readout_error_values from .parameters import readout_error_values
from .parameters import gate_time_values from .parameters import gate_param_values
from .parameters import gate_error_values
from .parameters import thermal_relaxation_values from .parameters import thermal_relaxation_values
from ..noiseerror import NoiseError
from ..noise_model import NoiseModel from ..noise_model import NoiseModel
from ..errors.readout_error import ReadoutError from ..errors.readout_error import ReadoutError
from ..errors.standard_errors import depolarizing_error from ..errors.standard_errors import depolarizing_error
from ..errors.standard_errors import thermal_relaxation_error from ..errors.standard_errors import thermal_relaxation_error
def depolarizing_noise_model(properties, def basic_device_noise_model(properties,
readout_error=True, readout_error=True,
thermal_relaxation=True,
temperature=0,
gate_times=None,
standard_gates=True): standard_gates=True):
"""Generae a depolarizing noise model from device backend properties. """Approximate device noise model derived from backend properties.
Params: Params:
properties (BackendProperties): backend properties properties (BackendProperties): backend properties
readout_error (Bool): Include readout errors in model readout_errors (Bool): Include readout errors in model
(Default: True) (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 standard_gates (bool): If true return errors as standard
qobj gates. If false return as unitary qobj gates. If false return as unitary
qobj instructions (Default: True) qobj instructions (Default: True)
Returns: Returns:
NoiseModel: An approximate noise model for the backend. NoiseModel: An approximate noise model for the device backend.
Additional Information: Additional Information:
The noise model will consist: The noise model includes the following errors:
* Depolarizing errors on gates defined be defined in
`BackendProperties.gates`. If readout_error is True:
* Single qubit readout errors on measurements if `readout_errors` * Single qubit readout errors on measurements.
is set to True.
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 For best practice in simulating a backend make sure that the
circuit is compiled using the set of basis gates in the noise 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` `basis_gates = noise_model.basis_gates`
and using the device coupling map with: and using the device coupling map with:
`coupling_map = backend.configuration().coupling_map` `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: Secifying custom gate times:
@ -103,62 +87,28 @@ def thermal_relaxation_noise_model(properties,
the backend properties, the `gate_times` value will override the the backend properties, the `gate_times` value will override the
gate time value from the backend properties. gate time value from the backend properties.
If non-default values are used gate_times should be a list 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() noise_model = NoiseModel()
# Add single-qubit readout errors # Add single-qubit readout errors
if readout_error: 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) noise_model.add_readout_error(error, qubits)
# Add depolarizing gate errors # Add gate errors
gate_errors = device_thermal_relaxation_errors(properties, gate_errors = basic_device_gate_errors(properties,
temperature=temperature, thermal_relaxation=thermal_relaxation,
gate_times=gate_times) gate_times=gate_times,
temperature=temperature,
standard_gates=standard_gates)
for name, qubits, error in gate_errors: for name, qubits, error in gate_errors:
noise_model.add_quantum_error(error, name, qubits) noise_model.add_quantum_error(error, name, qubits)
return noise_model return noise_model
def device_depolarizing_errors(properties, standard_gates=True): def basic_device_readout_errors(properties):
"""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):
"""Get readout error objects for each qubit from backend properties """Get readout error objects for each qubit from backend properties
Args: Args:
@ -176,7 +126,9 @@ def device_readout_errors(properties):
return errors 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 """Get depolarizing noise quantum error objects for backend gates
Args: Args:
@ -193,55 +145,243 @@ def device_thermal_relaxation_errors(properties, temperature=0, gate_times=None)
Additional Information: Additional Information:
If non-default values are used gate_times should be a list If non-default values are used gate_times should be a list
of tuples (name, value) where name is the gate name string, of tuples (name, qubits, value) where name is the gate name string,
and value is the gate time in nanoseconds. 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 # Generate custom gate time dict
custom_times = {} custom_times = {}
if gate_times: if thermal_relaxation:
custom_times = {name: value for name, value in gate_times} # If including thermal relaxation errors load
else: # T1, T2, and frequency values from properties
custom_times = {} relax_params = thermal_relaxation_values(properties)
# Append device gate times to custom gate times # If we are specifying custom gate times include
device_gate_times = gate_time_values(properties) # them in the custom times dict
relax_values = thermal_relaxation_values(properties) if gate_times:
errors = [] for name, qubits, value in gate_times:
for name, qubits, value in device_gate_times: if name in custom_times:
if name in custom_times: custom_times[name].append((qubits, value))
gate_time = custom_times[name]
else:
gate_time = value
if gate_time is not None and gate_time > 0:
# 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
# for any multi qubit gates
first = True
error = None
for qubit in reversed(qubits):
t1, t2, freq = relax_values[qubit]
population = 0
if freq != inf and temperature != 0:
# Compute the excited state population from qubit
# frequency and temperature
# Boltzman constant kB = 6.62607015e-34 (eV/K)
# Planck constant h = 6.62607015e-34 (eV.s)
# qubit temperature temperatue = T (mK)
# qubit frequency frequency = f (GHz)
# excited state population = 1/(1+exp((2hf*1e9)/(kbT*1e-3)))
exp_param = exp((95.9849 * freq) / abs(temperature))
population = 1 / (1 + exp_param)
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
else: else:
single = thermal_relaxation_error(t1, t2, gate_time, custom_times[name] = [(qubits, value)]
population) # Get the device gate parameters from properties
error = error.kron(single) device_gate_params = gate_param_values(properties)
if error is not None:
errors.append((name, qubits, error)) # 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 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
# for any multi qubit gates
first = True
error = None
for qubit in reversed(qubits):
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
# frequency and temperature
# Boltzman constant kB = 6.62607015e-34 (eV/K)
# Planck constant h = 6.62607015e-34 (eV.s)
# qubit temperature temperatue = T (mK)
# qubit frequency frequency = f (GHz)
# excited state population = 1/(1+exp((2hf*1e9)/(kbT*1e-3)))
exp_param = exp((95.9849 * freq) / abs(temperature))
population = 1 / (1 + exp_param)
if temperature < 0:
# negative temperate implies |1> is thermal ground
population = 1 - population
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:
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} _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): def gate_error_values(properties):
"""Get gate error values for backend gate from backend properties """Get gate error values for backend gate from backend properties