Module hybridq.noise.utils
Authors: Salvatore Mandra (salvatore.mandra@nasa.gov), Jeffrey Marshall (jeffrey.s.marshall@nasa.gov)
Copyright © 2021, United States Government, as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
The HybridQ: A Hybrid Simulator for Quantum Circuits platform is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Expand source code
"""
Authors: Salvatore Mandra (salvatore.mandra@nasa.gov),
Jeffrey Marshall (jeffrey.s.marshall@nasa.gov)
Copyright © 2021, United States Government, as represented by the Administrator
of the National Aeronautics and Space Administration. All rights reserved.
The HybridQ: A Hybrid Simulator for Quantum Circuits platform is licensed under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from __future__ import annotations
from hybridq.dm.circuit import Circuit as SuperCircuit
from hybridq.noise.channel.channel import BaseChannel
from hybridq.noise.channel import channel
from tqdm.auto import tqdm
def add_depolarizing_noise(circuit: Circuit,
probs: {float, list[float, ...], dict[any, float]},
where: {'before', 'after'} = 'after',
verbose: bool = False):
"""
Given a `Circuit`, add global depolarizing noise after each instance
of a `Gate`, with the same locality as the gate.
Note, noise will not be added after an instance of `BaseChannel`
circuit: Circuit
The `Circuit` which will be modified. Note, a new `Circuit` is
returned (this is not in place).
probs: {float, list[float, ...], dict[any, float]}
Depolarizing probabilities for `circuit`. If `probs` is a single `float`,
the same probability is applied to all gates regardless the number of
qubits. If `probs` is a list, the k-th value is used as the probability
for all the k-qubit gates. If `probs` is a `dict`, `probs[k]` will be
used as probability for k-qubit gates. If `probs[k]` is missing, the
probability for a k-qubit gate will fallback to `probs[any]` (if
provided).
where: {'before', 'after', 'both'}
Add noise either `'before'` or `'after'` every gate (default: `after`).
verbose: bool, optional
Verbose output.
"""
from hybridq.circuit import Circuit
# Check 'where'
if where not in ['before', 'after']:
raise ValueError("'where' can only be either 'before' or 'after'")
# Convert circuit
circuit = Circuit(circuit)
# Convert probs
probs = channel.__get_params(keys=sorted(set(g.n_qubits for g in circuit)),
args=probs,
value_type=float)
# Define how to add noise
def _add_noise(g):
# Get probability
p = probs[g.n_qubits]
# Get noise
noise = channel.GlobalDepolarizingChannel(g.qubits, p)
# Return gates
return [g] if isinstance(g, BaseChannel) else (
[g, noise] if where == 'after' else [noise, g])
# Update circuit
return SuperCircuit(g for w in tqdm(
circuit, disable=not verbose, desc='Add depolarizing noise')
for g in _add_noise(w))
def add_dephasing_noise(circuit: Circuit,
probs: {float, list[float, ...], dict[any, float]},
pauli_indexes: {int, list[int, ...], dict[any,
int]} = 3,
where: {'before', 'after'} = 'after',
verbose: bool = False):
"""
Given a `Circuit`, add dephasing noise after each instance of a `Gate`,
which acts independently on the qubits of the gate.
Note, noise will not be added after an instance of `BaseChannel`
circuit: Circuit
The `Circuit` which will be modified. Note, a new `Circuit` is
returned (this is not in place).
probs: {float, list[float, ...], dict[any, float]}
Dephasing probabilities for `circuit`. If `probs` is a single `float`,
the same probability is applied to all qubits. If `probs` is a list,
the k-th value is used as the probability for the k-th qubit. If `probs`
is a `dict`, `probs[q]` will be used as probability for the qubit `q`.
If `probs[q]` is missing, the probability for a qubit `q` will fallback
to `probs[any]` (if provided).
pauli_indexes: {int, list[int, ...], dict[any, int]}
Pauli indexes representing the dephasing axis (Pauli matrix). If
`pauli_indexes` is a single `int`, the same axis is applied to all
qubits. If `pauli_indexes` is a list, the k-th value is used as the
axis for the k-th qubit. If `pauli_indexes` is a `dict`,
`pauli_indexes[q]` will be used as axis for the qubit `q`. If
`pauli_indexes[q]` is missing, the axis for a qubit `q` will fallback
to `pauli_indexes[any]` (if provided).
where: {'before', 'after', 'both'}
Add noise either `'before'` or `'after'` every gate (default: `after`).
verbose: bool, optional
Verbose output.
"""
from hybridq.circuit import Circuit
# Check 'where'
if where not in ['before', 'after']:
raise ValueError("'where' can only be either 'before' or 'after'")
# Convert circuit
circuit = Circuit(circuit)
# Get all qubits
qubits = circuit.all_qubits()
# Convert gammas and probs
probs = channel.__get_params(qubits, probs, value_type=float)
pauli_indexes = channel.__get_params(qubits, pauli_indexes, value_type=int)
# Define how to add noise
def _add_noise(g):
# Get gammas and probs
_probs = {q: probs[q] for q in g.qubits}
_axis = {q: pauli_indexes[q] for q in g.qubits}
# Get noise
noise = channel.LocalDephasingChannel(g.qubits,
p=_probs,
pauli_index=_axis)
# Return gates
return [g] if isinstance(
g, BaseChannel) else ((g,) + noise if where == 'after' else noise +
(g,))
# Update circuit
return SuperCircuit(g for w in tqdm(
circuit, disable=not verbose, desc='Add amplitude damping noise')
for g in _add_noise(w))
def add_amplitude_damping_noise(
circuit: Circuit,
gammas: {float, list[float, ...], dict[any, float]},
probs: {float, list[float, ...], dict[any, float]} = 1,
where: {'before', 'after'} = 'after',
verbose: bool = False):
"""
Given a `Circuit`, add amplitude damping noise after each instance of a
`Gate`. The noise will act independently on the qubits in the gate.
Note, noise will not be added after an instance of `BaseChannel`
circuit: Circuit
The `Circuit` which will be modified. Note, a new `Circuit` is
returned (this is not in place).
gammas: {float, list[float, ...], dict[any, float]}
Transition rate (0->1 and 1->0) for the amplitude damping noise
channel. If `gammas` is a single `float`, the same probability is applied
to all qubits. If `gammas` is a list, the k-th value is used as the
probability for the k-th qubit. If `gammas` is a `dict`, `gammas[q]` will
be used as probability for the qubit `q`. If `gammas[q]` is missing,
the probability for a qubit `q` will fallback to `gammas[any]` (if
provided).
probs: {float, list[float, ...], dict[any, float]}
Amplitude damping probabilities for `circuit`. If `probs` is a single
`float`, the same probability is applied to all qubits. If `probs` is a
list, the k-th value is used as the probability for the k-th qubit. If
`probs` is a `dict`, `probs[q]` will be used as probability for the qubit
`q`. If `probs[q]` is missing, the probability for a qubit `q` will
fallback to `probs[any]` (if provided).
where: {'before', 'after', 'both'}
Add noise either `'before'` or `'after'` every gate (default: `after`).
verbose: bool, optional
Verbose output.
"""
from hybridq.circuit import Circuit
# Check 'where'
if where not in ['before', 'after']:
raise ValueError("'where' can only be either 'before' or 'after'")
# Convert circuit
circuit = Circuit(circuit)
# Get all qubits
qubits = circuit.all_qubits()
# Convert gammas and probs
gammas = channel.__get_params(qubits, gammas, value_type=float)
probs = channel.__get_params(qubits, probs, value_type=float)
# Define how to add noise
def _add_noise(g):
# Get gammas and probs
_gammas = {q: gammas[q] for q in g.qubits}
_probs = {q: probs[q] for q in g.qubits}
# Get noise
noise = channel.AmplitudeDampingChannel(g.qubits,
gamma=_gammas,
p=_probs)
# Return gates
return [g] if isinstance(
g, BaseChannel) else ((g,) + noise if where == 'after' else noise +
(g,))
# Update circuit
return SuperCircuit(g for w in tqdm(
circuit, disable=not verbose, desc='Add amplitude damping noise')
for g in _add_noise(w))
Functions
def add_amplitude_damping_noise(circuit: Circuit, gammas: {float, list[float, ...], dict[any, float]}, probs: {float, list[float, ...], dict[any, float]} = 1, where: "{'before', 'after'}" = 'after', verbose: bool = False)
-
Given a
Circuit
, add amplitude damping noise after each instance of aGate
. The noise will act independently on the qubits in the gate. Note, noise will not be added after an instance ofBaseChannel
circuit: Circuit The
Circuit
which will be modified. Note, a newCircuit
is returned (this is not in place). gammas: {float, list[float, …], dict[any, float]} Transition rate (0->1 and 1->0) for the amplitude damping noise channel. Ifgammas
is a singlefloat
, the same probability is applied to all qubits. Ifgammas
is a list, the k-th value is used as the probability for the k-th qubit. Ifgammas
is adict
,gammas[q]
will be used as probability for the qubitq
. Ifgammas[q]
is missing, the probability for a qubitq
will fallback togammas[any]
(if provided). probs: {float, list[float, …], dict[any, float]} Amplitude damping probabilities forcircuit
. Ifprobs
is a singlefloat
, the same probability is applied to all qubits. Ifprobs
is a list, the k-th value is used as the probability for the k-th qubit. Ifprobs
is adict
,probs[q]
will be used as probability for the qubitq
. Ifprobs[q]
is missing, the probability for a qubitq
will fallback toprobs[any]
(if provided). where: {'before', 'after', 'both'} Add noise either'before'
or'after'
every gate (default:after
). verbose: bool, optional Verbose output.Expand source code
def add_amplitude_damping_noise( circuit: Circuit, gammas: {float, list[float, ...], dict[any, float]}, probs: {float, list[float, ...], dict[any, float]} = 1, where: {'before', 'after'} = 'after', verbose: bool = False): """ Given a `Circuit`, add amplitude damping noise after each instance of a `Gate`. The noise will act independently on the qubits in the gate. Note, noise will not be added after an instance of `BaseChannel` circuit: Circuit The `Circuit` which will be modified. Note, a new `Circuit` is returned (this is not in place). gammas: {float, list[float, ...], dict[any, float]} Transition rate (0->1 and 1->0) for the amplitude damping noise channel. If `gammas` is a single `float`, the same probability is applied to all qubits. If `gammas` is a list, the k-th value is used as the probability for the k-th qubit. If `gammas` is a `dict`, `gammas[q]` will be used as probability for the qubit `q`. If `gammas[q]` is missing, the probability for a qubit `q` will fallback to `gammas[any]` (if provided). probs: {float, list[float, ...], dict[any, float]} Amplitude damping probabilities for `circuit`. If `probs` is a single `float`, the same probability is applied to all qubits. If `probs` is a list, the k-th value is used as the probability for the k-th qubit. If `probs` is a `dict`, `probs[q]` will be used as probability for the qubit `q`. If `probs[q]` is missing, the probability for a qubit `q` will fallback to `probs[any]` (if provided). where: {'before', 'after', 'both'} Add noise either `'before'` or `'after'` every gate (default: `after`). verbose: bool, optional Verbose output. """ from hybridq.circuit import Circuit # Check 'where' if where not in ['before', 'after']: raise ValueError("'where' can only be either 'before' or 'after'") # Convert circuit circuit = Circuit(circuit) # Get all qubits qubits = circuit.all_qubits() # Convert gammas and probs gammas = channel.__get_params(qubits, gammas, value_type=float) probs = channel.__get_params(qubits, probs, value_type=float) # Define how to add noise def _add_noise(g): # Get gammas and probs _gammas = {q: gammas[q] for q in g.qubits} _probs = {q: probs[q] for q in g.qubits} # Get noise noise = channel.AmplitudeDampingChannel(g.qubits, gamma=_gammas, p=_probs) # Return gates return [g] if isinstance( g, BaseChannel) else ((g,) + noise if where == 'after' else noise + (g,)) # Update circuit return SuperCircuit(g for w in tqdm( circuit, disable=not verbose, desc='Add amplitude damping noise') for g in _add_noise(w))
def add_dephasing_noise(circuit: Circuit, probs: {float, list[float, ...], dict[any, float]}, pauli_indexes: {int, list[int, ...], dict[any, int]} = 3, where: "{'before', 'after'}" = 'after', verbose: bool = False)
-
Given a
Circuit
, add dephasing noise after each instance of aGate
, which acts independently on the qubits of the gate. Note, noise will not be added after an instance ofBaseChannel
circuit: Circuit The
Circuit
which will be modified. Note, a newCircuit
is returned (this is not in place). probs: {float, list[float, …], dict[any, float]} Dephasing probabilities forcircuit
. Ifprobs
is a singlefloat
, the same probability is applied to all qubits. Ifprobs
is a list, the k-th value is used as the probability for the k-th qubit. Ifprobs
is adict
,probs[q]
will be used as probability for the qubitq
. Ifprobs[q]
is missing, the probability for a qubitq
will fallback toprobs[any]
(if provided). pauli_indexes: {int, list[int, …], dict[any, int]} Pauli indexes representing the dephasing axis (Pauli matrix). Ifpauli_indexes
is a singleint
, the same axis is applied to all qubits. Ifpauli_indexes
is a list, the k-th value is used as the axis for the k-th qubit. Ifpauli_indexes
is adict
,pauli_indexes[q]
will be used as axis for the qubitq
. Ifpauli_indexes[q]
is missing, the axis for a qubitq
will fallback topauli_indexes[any]
(if provided). where: {'before', 'after', 'both'} Add noise either'before'
or'after'
every gate (default:after
). verbose: bool, optional Verbose output.Expand source code
def add_dephasing_noise(circuit: Circuit, probs: {float, list[float, ...], dict[any, float]}, pauli_indexes: {int, list[int, ...], dict[any, int]} = 3, where: {'before', 'after'} = 'after', verbose: bool = False): """ Given a `Circuit`, add dephasing noise after each instance of a `Gate`, which acts independently on the qubits of the gate. Note, noise will not be added after an instance of `BaseChannel` circuit: Circuit The `Circuit` which will be modified. Note, a new `Circuit` is returned (this is not in place). probs: {float, list[float, ...], dict[any, float]} Dephasing probabilities for `circuit`. If `probs` is a single `float`, the same probability is applied to all qubits. If `probs` is a list, the k-th value is used as the probability for the k-th qubit. If `probs` is a `dict`, `probs[q]` will be used as probability for the qubit `q`. If `probs[q]` is missing, the probability for a qubit `q` will fallback to `probs[any]` (if provided). pauli_indexes: {int, list[int, ...], dict[any, int]} Pauli indexes representing the dephasing axis (Pauli matrix). If `pauli_indexes` is a single `int`, the same axis is applied to all qubits. If `pauli_indexes` is a list, the k-th value is used as the axis for the k-th qubit. If `pauli_indexes` is a `dict`, `pauli_indexes[q]` will be used as axis for the qubit `q`. If `pauli_indexes[q]` is missing, the axis for a qubit `q` will fallback to `pauli_indexes[any]` (if provided). where: {'before', 'after', 'both'} Add noise either `'before'` or `'after'` every gate (default: `after`). verbose: bool, optional Verbose output. """ from hybridq.circuit import Circuit # Check 'where' if where not in ['before', 'after']: raise ValueError("'where' can only be either 'before' or 'after'") # Convert circuit circuit = Circuit(circuit) # Get all qubits qubits = circuit.all_qubits() # Convert gammas and probs probs = channel.__get_params(qubits, probs, value_type=float) pauli_indexes = channel.__get_params(qubits, pauli_indexes, value_type=int) # Define how to add noise def _add_noise(g): # Get gammas and probs _probs = {q: probs[q] for q in g.qubits} _axis = {q: pauli_indexes[q] for q in g.qubits} # Get noise noise = channel.LocalDephasingChannel(g.qubits, p=_probs, pauli_index=_axis) # Return gates return [g] if isinstance( g, BaseChannel) else ((g,) + noise if where == 'after' else noise + (g,)) # Update circuit return SuperCircuit(g for w in tqdm( circuit, disable=not verbose, desc='Add amplitude damping noise') for g in _add_noise(w))
def add_depolarizing_noise(circuit: Circuit, probs: {float, list[float, ...], dict[any, float]}, where: "{'before', 'after'}" = 'after', verbose: bool = False)
-
Given a
Circuit
, add global depolarizing noise after each instance of aGate
, with the same locality as the gate. Note, noise will not be added after an instance ofBaseChannel
circuit: Circuit The
Circuit
which will be modified. Note, a newCircuit
is returned (this is not in place). probs: {float, list[float, …], dict[any, float]} Depolarizing probabilities forcircuit
. Ifprobs
is a singlefloat
, the same probability is applied to all gates regardless the number of qubits. Ifprobs
is a list, the k-th value is used as the probability for all the k-qubit gates. Ifprobs
is adict
,probs[k]
will be used as probability for k-qubit gates. Ifprobs[k]
is missing, the probability for a k-qubit gate will fallback toprobs[any]
(if provided). where: {'before', 'after', 'both'} Add noise either'before'
or'after'
every gate (default:after
). verbose: bool, optional Verbose output.Expand source code
def add_depolarizing_noise(circuit: Circuit, probs: {float, list[float, ...], dict[any, float]}, where: {'before', 'after'} = 'after', verbose: bool = False): """ Given a `Circuit`, add global depolarizing noise after each instance of a `Gate`, with the same locality as the gate. Note, noise will not be added after an instance of `BaseChannel` circuit: Circuit The `Circuit` which will be modified. Note, a new `Circuit` is returned (this is not in place). probs: {float, list[float, ...], dict[any, float]} Depolarizing probabilities for `circuit`. If `probs` is a single `float`, the same probability is applied to all gates regardless the number of qubits. If `probs` is a list, the k-th value is used as the probability for all the k-qubit gates. If `probs` is a `dict`, `probs[k]` will be used as probability for k-qubit gates. If `probs[k]` is missing, the probability for a k-qubit gate will fallback to `probs[any]` (if provided). where: {'before', 'after', 'both'} Add noise either `'before'` or `'after'` every gate (default: `after`). verbose: bool, optional Verbose output. """ from hybridq.circuit import Circuit # Check 'where' if where not in ['before', 'after']: raise ValueError("'where' can only be either 'before' or 'after'") # Convert circuit circuit = Circuit(circuit) # Convert probs probs = channel.__get_params(keys=sorted(set(g.n_qubits for g in circuit)), args=probs, value_type=float) # Define how to add noise def _add_noise(g): # Get probability p = probs[g.n_qubits] # Get noise noise = channel.GlobalDepolarizingChannel(g.qubits, p) # Return gates return [g] if isinstance(g, BaseChannel) else ( [g, noise] if where == 'after' else [noise, g]) # Update circuit return SuperCircuit(g for w in tqdm( circuit, disable=not verbose, desc='Add depolarizing noise') for g in _add_noise(w))