# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=unpacking-non-sequence
"""
Pauli Transfer Matrix (PTM) representation of a Quantum Channel.
"""
import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.instruction import Instruction
from qiskit.exceptions import QiskitError
from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
from qiskit.quantum_info.operators.channel.superop import SuperOp
from qiskit.quantum_info.operators.channel.transformations import _to_ptm
[docs]class PTM(QuantumChannel):
r"""Pauli Transfer Matrix (PTM) representation of a Quantum Channel.
The PTM representation of an :math:`n`-qubit quantum channel
:math:`\mathcal{E}` is an :math:`n`-qubit :class:`SuperOp` :math:`R`
defined with respect to vectorization in the Pauli basis instead of
column-vectorization. The elements of the PTM :math:`R` are
given by
.. math::
R_{i,j} = \mbox{Tr}\left[P_i \mathcal{E}(P_j) \right]
where :math:`[P_0, P_1, ..., P_{4^{n}-1}]` is the :math:`n`-qubit Pauli basis in
lexicographic order.
Evolution of a :class:`~qiskit.quantum_info.DensityMatrix`
:math:`\rho` with respect to the PTM is given by
.. math::
|\mathcal{E}(\rho)\rangle\!\rangle_P = S_P |\rho\rangle\!\rangle_P
where :math:`|A\rangle\!\rangle_P` denotes vectorization in the Pauli basis
:math:`\langle i | A\rangle\!\rangle_P = \mbox{Tr}[P_i A]`.
See reference [1] for further details.
References:
1. C.J. Wood, J.D. Biamonte, D.G. Cory, *Tensor networks and graphical calculus
for open quantum systems*, Quant. Inf. Comp. 15, 0579-0811 (2015).
`arXiv:1111.6950 [quant-ph] <https://arxiv.org/abs/1111.6950>`_
"""
def __init__(self, data, input_dims=None, output_dims=None):
"""Initialize a PTM quantum channel operator.
Args:
data (QuantumCircuit or
Instruction or
BaseOperator or
matrix): data to initialize superoperator.
input_dims (tuple): the input subsystem dimensions.
[Default: None]
output_dims (tuple): the output subsystem dimensions.
[Default: None]
Raises:
QiskitError: if input data is not an N-qubit channel or
cannot be initialized as a PTM.
Additional Information:
If the input or output dimensions are None, they will be
automatically determined from the input data. The PTM
representation is only valid for N-qubit channels.
"""
# If the input is a raw list or matrix we assume that it is
# already a Chi matrix.
if isinstance(data, (list, np.ndarray)):
# Should we force this to be real?
ptm = np.asarray(data, dtype=complex)
# Determine input and output dimensions
dout, din = ptm.shape
if input_dims:
input_dim = np.product(input_dims)
else:
input_dim = int(np.sqrt(din))
if output_dims:
output_dim = np.product(input_dims)
else:
output_dim = int(np.sqrt(dout))
if output_dim**2 != dout or input_dim**2 != din or input_dim != output_dim:
raise QiskitError("Invalid shape for PTM matrix.")
else:
# Otherwise we initialize by conversion from another Qiskit
# object into the QuantumChannel.
if isinstance(data, (QuantumCircuit, Instruction)):
# If the input is a Terra QuantumCircuit or Instruction we
# convert it to a SuperOp
data = SuperOp._init_instruction(data)
else:
# We use the QuantumChannel init transform to initialize
# other objects into a QuantumChannel or Operator object.
data = self._init_transformer(data)
input_dim, output_dim = data.dim
# Now that the input is an operator we convert it to a PTM object
rep = getattr(data, '_channel_rep', 'Operator')
ptm = _to_ptm(rep, data._data, input_dim, output_dim)
if input_dims is None:
input_dims = data.input_dims()
if output_dims is None:
output_dims = data.output_dims()
# Check input is N-qubit channel
num_qubits = int(np.log2(input_dim))
if 2**num_qubits != input_dim:
raise QiskitError("Input is not an n-qubit Pauli transfer matrix.")
# Check and format input and output dimensions
input_dims = self._automatic_dims(input_dims, input_dim)
output_dims = self._automatic_dims(output_dims, output_dim)
super().__init__(ptm, input_dims, output_dims, 'PTM')
@property
def _bipartite_shape(self):
"""Return the shape for bipartite matrix"""
return (self._output_dim, self._output_dim, self._input_dim,
self._input_dim)
[docs] def conjugate(self):
"""Return the conjugate of the QuantumChannel."""
# Since conjugation is basis dependent we transform
# to the SuperOp representation to compute the
# conjugate channel
return PTM(SuperOp(self).conjugate())
[docs] def transpose(self):
"""Return the transpose of the QuantumChannel."""
# Since conjugation is basis dependent we transform
# to the SuperOp representation to compute the
# conjugate channel
return PTM(SuperOp(self).transpose())
[docs] def compose(self, other, qargs=None, front=False):
"""Return the composed quantum channel self @ other.
Args:
other (QuantumChannel): a quantum channel.
qargs (list or None): a list of subsystem positions to apply
other on. If None apply on all
subsystems [default: None].
front (bool): If True compose using right operator multiplication,
instead of left multiplication [default: False].
Returns:
PTM: The quantum channel self @ other.
Raises:
QiskitError: if other has incompatible dimensions.
Additional Information:
Composition (``@``) is defined as `left` matrix multiplication for
:class:`SuperOp` matrices. That is that ``A @ B`` is equal to ``B * A``.
Setting ``front=True`` returns `right` matrix multiplication
``A * B`` and is equivalent to the :meth:`dot` method.
"""
if qargs is None:
qargs = getattr(other, 'qargs', None)
if qargs is not None:
return PTM(
SuperOp(self).compose(other, qargs=qargs, front=front))
# Convert other to PTM
if not isinstance(other, PTM):
other = PTM(other)
input_dims, output_dims = self._get_compose_dims(other, qargs, front)
if front:
data = np.dot(self._data, other.data)
else:
data = np.dot(other.data, self._data)
return PTM(data, input_dims, output_dims)
[docs] def power(self, n):
"""The matrix power of the channel.
Args:
n (int): compute the matrix power of the superoperator matrix.
Returns:
PTM: the matrix power of the SuperOp converted to a PTM channel.
Raises:
QiskitError: if the input and output dimensions of the
QuantumChannel are not equal, or the power is not
an integer.
"""
if n > 0:
return super().power(n)
return PTM(SuperOp(self).power(n))
[docs] def tensor(self, other):
"""Return the tensor product channel self ⊗ other.
Args:
other (QuantumChannel): a quantum channel.
Returns:
PTM: the tensor product channel self ⊗ other as a PTM object.
Raises:
QiskitError: if other cannot be converted to a channel.
"""
if not isinstance(other, PTM):
other = PTM(other)
input_dims = other.input_dims() + self.input_dims()
output_dims = other.output_dims() + self.output_dims()
data = np.kron(self._data, other.data)
return PTM(data, input_dims, output_dims)
[docs] def expand(self, other):
"""Return the tensor product channel other ⊗ self.
Args:
other (QuantumChannel): a quantum channel.
Returns:
PTM: the tensor product channel other ⊗ self as a PTM object.
Raises:
QiskitError: if other cannot be converted to a channel.
"""
if not isinstance(other, PTM):
other = PTM(other)
input_dims = self.input_dims() + other.input_dims()
output_dims = self.output_dims() + other.output_dims()
data = np.kron(other.data, self._data)
return PTM(data, input_dims, output_dims)
def _evolve(self, state, qargs=None):
"""Evolve a quantum state by the quantum channel.
Args:
state (DensityMatrix or Statevector): The input state.
qargs (list): a list of quantum state subsystem positions to apply
the quantum channel on.
Returns:
DensityMatrix: the output quantum state as a density matrix.
Raises:
QiskitError: if the quantum channel dimension does not match the
specified quantum state subsystem dimensions.
"""
return SuperOp(self)._evolve(state, qargs)