Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add average_gate_fidelity, gate_error, and modify process_fidelity functions #3660

Merged
merged 7 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions qiskit/quantum_info/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
:toctree: ../stubs/

state_fidelity
average_gate_fidelity
process_fidelity
gate_error

Random
======
Expand Down Expand Up @@ -95,6 +98,8 @@
from .operators.quaternion import Quaternion
from .operators.channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM
from .operators.measures import process_fidelity
from .operators import average_gate_fidelity
from .operators import gate_error
from .states import Statevector, DensityMatrix
from .states.states import basis_state, projector, purity
from .states.measures import state_fidelity
Expand Down
1 change: 1 addition & 0 deletions qiskit/quantum_info/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
from .pauli import Pauli, pauli_group
from .channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM
from .quaternion import Quaternion
from .measures import process_fidelity, average_gate_fidelity, gate_error
221 changes: 164 additions & 57 deletions qiskit/quantum_info/operators/measures.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,80 +13,187 @@
# that they have been altered from the originals.

# pylint: disable=invalid-name

"""
A collection of useful quantum information functions for operators.
"""

import warnings
import numpy as np

from qiskit.exceptions import QiskitError
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info.operators import SuperOp
from qiskit.quantum_info.operators.operator import Operator
from qiskit.quantum_info.operators.pauli import Pauli
from qiskit.quantum_info.operators.channel import SuperOp


def process_fidelity(channel1, channel2, require_cptp=True):
"""Return the process fidelity between two quantum channels.
def process_fidelity(channel,
target=None,
require_cp=True,
require_tp=False,
require_cptp=False):
r"""Return the process fidelity of a noisy quantum channel.

This is given by
This process fidelity :math:`F_{\text{pro}}` is given by

F_p(E1, E2) = Tr[S2^dagger.S1])/dim^2
.. math::
F_{\text{pro}}(\mathcal{E}, U)
= \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2}

where S1 and S2 are the SuperOp matrices for channels E1 and E2,
and dim is the dimension of the input output statespace.
where :math:`S_{\mathcal{E}}, S_{U}` are the
:class:`~qiskit.quantum_info.SuperOp` matrices for the input quantum
*channel* :math:`\cal{E}` and *target* unitary :math:`U` respectively,
and :math:`d` is the dimension of the *channel*.

Args:
channel1 (QuantumChannel or matrix): a quantum channel or unitary matrix.
channel2 (QuantumChannel or matrix): a quantum channel or unitary matrix.
require_cptp (bool): require input channels to be CPTP [Default: True].
channel (QuantumChannel): noisy quantum channel.
target (Operator or None): target unitary operator.
If `None` target is the identity operator [Default: None].
require_cp (bool): require channel to be completely-positive
[Default: True].
require_tp (bool): require channel to be trace-preserving
[Default: False].
require_cptp (bool): (DEPRECATED) require input channels to be
CPTP [Default: False].

Returns:
array_like: The state fidelity F(state1, state2).
float: The process fidelity :math:`F_{\text{pro}}`.

Raises:
QiskitError: if inputs channels do not have the same dimensions,
have different input and output dimensions, or are not CPTP with
`require_cptp=True`.
QiskitError: if the channel and target do not have the same
dimensions, or have different input and output dimensions, or are
not completely-positive (with ``require_cp=True``) or not
trace-preserving (with ``require_tp=True``).
"""
# First we must determine if input is to be interpreted as a unitary matrix
# or as a channel.
# If input is a raw numpy array we will interpret it as a unitary matrix.
is_cptp1 = None
is_cptp2 = None
if isinstance(channel1, (list, np.ndarray)):
channel1 = Operator(channel1)
if require_cptp:
is_cptp1 = channel1.is_unitary()
if isinstance(channel2, (list, np.ndarray)):
channel2 = Operator(channel2)
if require_cptp:
is_cptp2 = channel2.is_unitary()

# Next we convert inputs SuperOp objects
# This works for objects that also have a `to_operator` or `to_channel` method
s1 = SuperOp(channel1)
s2 = SuperOp(channel2)

# Check inputs are CPTP
# Format inputs
if isinstance(channel, (list, np.ndarray, Operator, Pauli)):
channel = Operator(channel)
else:
channel = SuperOp(channel)
input_dim, output_dim = channel.dim
if input_dim != output_dim:
raise QiskitError(
'Quantum channel must have equal input and output dimensions.')

if target is not None:
# Multiple channel by adjoint of target
target = Operator(target)
if (input_dim, output_dim) != target.dim:
raise QiskitError(
'Quantum channel and target must have the same dimensions.')
channel = channel @ target.adjoint()

# Validate complete-positivity and trace-preserving
if require_cptp:
# Only check SuperOp if we didn't already check unitary inputs
if is_cptp1 is None:
is_cptp1 = s1.is_cptp()
if not is_cptp1:
raise QiskitError('channel1 is not CPTP')
if is_cptp2 is None:
is_cptp2 = s2.is_cptp()
if not is_cptp2:
raise QiskitError('channel2 is not CPTP')

# Check dimensions match
input_dim1, output_dim1 = s1.dim
input_dim2, output_dim2 = s2.dim
if input_dim1 != output_dim1 or input_dim2 != output_dim2:
raise QiskitError('Input channels must have same size input and output dimensions.')
if input_dim1 != input_dim2:
raise QiskitError('Input channels have different dimensions.')

# Compute process fidelity
fidelity = np.trace(s1.compose(s2.adjoint()).data) / (input_dim1 ** 2)
return fidelity
# require_cptp kwarg is DEPRECATED
# Remove in future qiskit version
warnings.warn(
"Please use `require_cp=True, require_tp=True` "
"instead of `require_cptp=True`.", DeprecationWarning)
require_cp = True
require_tp = True
if isinstance(channel, Operator) and (require_cp or require_tp):
is_unitary = channel.is_unitary()
# Validate as unitary
if require_cp and not is_unitary:
raise QiskitError('channel is not completely-positive')
if require_tp and not is_unitary:
raise QiskitError('channel is not trace-preserving')
else:
# Validate as QuantumChannel
if require_cp and not channel.is_cp():
raise QiskitError('channel is not completely-positive')
if require_tp and not channel.is_tp():
raise QiskitError('channel is not trace-preserving')

# Compute process fidelity with identity channel
if isinstance(channel, Operator):
# |Tr[U]/dim| ** 2
fid = np.abs(np.trace(channel.data) / input_dim)**2
else:
# Tr[S] / (dim ** 2)
fid = np.trace(channel.data) / (input_dim**2)
return float(np.real(fid))


def average_gate_fidelity(channel,
target=None,
require_cp=True,
require_tp=False):
r"""Return the average gate fidelity of a noisy quantum channel.

The average gate fidelity :math:`F_{\text{ave}}` is given by

.. math::
F_{\text{ave}}(\mathcal{E}, U)
&= \int d\psi \langle\psi|U^\dagger
\mathcal{E}(|\psi\rangle\!\langle\psi|)U|\psi\rangle \\
&= \frac{d F_{\text{pro}}(\mathcal{E}, U) + 1}{d + 1}

where :math:`F_{\text{pro}}(\mathcal{E}, U)` is the
:meth:`~qiskit.quantum_info.process_fidelity` of the input quantum
*channel* :math:`\mathcal{E}` with a *target* unitary :math:`U`, and
:math:`d` is the dimension of the *channel*.

Args:
channel (QuantumChannel): noisy quantum channel.
target (Operator or None): target unitary operator.
If `None` target is the identity operator [Default: None].
require_cp (bool): require channel to be completely-positive
[Default: True].
require_tp (bool): require channel to be trace-preserving
[Default: False].

Returns:
float: The average gate fidelity :math:`F_{\text{ave}}`.

Raises:
QiskitError: if the channel and target do not have the same
dimensions, or have different input and output dimensions, or are
not completely-positive (with ``require_cp=True``) or not
trace-preserving (with ``require_tp=True``).
"""
if isinstance(channel, (list, np.ndarray, Operator, Pauli)):
channel = Operator(channel)
else:
channel = SuperOp(channel)
dim, _ = channel.dim
f_pro = process_fidelity(channel,
target=target,
require_cp=require_cp,
require_tp=require_tp)
return (dim * f_pro + 1) / (dim + 1)


def gate_error(channel, target=None, require_cp=True, require_tp=False):
r"""Return the gate error of a noisy quantum channel.

The gate error :math:`E` is given by the average gate infidelity

.. math::
E(\mathcal{E}, U) = 1 - F_{\text{ave}}(\mathcal{E}, U)

where :math:`F_{\text{ave}}(\mathcal{E}, U)` is the
:meth:`~qiskit.quantum_info.average_gate_fidelity` of the input
quantum *channel* :math:`\mathcal{E}` with a *target* unitary
:math:`U`.

Args:
channel (QuantumChannel): noisy quantum channel.
target (Operator or None): target unitary operator.
If `None` target is the identity operator [Default: None].
require_cp (bool): require channel to be completely-positive
[Default: True].
require_tp (bool): require channel to be trace-preserving
[Default: False].

Returns:
float: The average gate error :math:`E`.

Raises:
QiskitError: if the channel and target do not have the same
dimensions, or have different input and output dimensions, or are
not completely-positive (with ``require_cp=True``) or not
trace-preserving (with ``require_tp=True``).
"""
return 1 - average_gate_fidelity(
channel, target=target, require_cp=require_cp, require_tp=require_tp)
22 changes: 22 additions & 0 deletions releasenotes/notes/opertor-measures-33682f22a4c58d68.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
features:
- |
Added ``average_gate_fidelity`` and ``gate_error`` functions for
``qiskit.quantum_info`` operator and quantum channel objects.
issues:
- |
List known issues here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
upgrade:
- |
Changed second argument of ``process_fidelity`` function to be optional.
If a target unitary is not specified, the process fidelity of the input
channel with the identity operator will be returned.
deprecations:
- |
The ``require_cptp`` kwarg of the ``process_fidelity`` function has been
deprecated. It is superseded by two separate kwargs ``require_cp`` and
``require_tp``.