Source code for qiskit.quantum_info.states.utils

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# 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.

"""
Quantum information utility functions for states.
"""

import numpy as np
import scipy.linalg as la

from qiskit.exceptions import QiskitError
from qiskit.quantum_info.states.statevector import Statevector
from qiskit.quantum_info.states.densitymatrix import DensityMatrix
from qiskit.quantum_info.operators.channel import SuperOp


[docs]def partial_trace(state, qargs): """Return reduced density matrix by tracing out part of quantum state. If all subsystems are traced over this returns the :meth:`~qiskit.quantum_info.DensityMatrix.trace` of the input state. Args: state (Statevector or DensityMatrix): the input state. qargs (list): The subsystems to trace over. Returns: DensityMatrix: The reduced density matrix. Raises: QiskitError: if input state is invalid. """ state = _format_state(state, validate=False) # If we are tracing over all subsystems we return the trace if sorted(qargs) == list(range(len(state.dims()))): # Should this raise an exception instead? # Or return a 1x1 density matrix? return state.trace() # Statevector case if isinstance(state, Statevector): trace_systems = len(state._dims) - 1 - np.array(qargs) new_dims = tuple(np.delete(np.array(state._dims), qargs)) new_dim = np.product(new_dims) arr = state._data.reshape(state._shape) rho = np.tensordot(arr, arr.conj(), axes=(trace_systems, trace_systems)) rho = np.reshape(rho, (new_dim, new_dim)) return DensityMatrix(rho, dims=new_dims) # Density matrix case # Trace first subsystem to avoid coping whole density matrix dims = state.dims(qargs) tr_op = SuperOp(np.eye(dims[0]).reshape(1, dims[0] ** 2), input_dims=[dims[0]], output_dims=[1]) ret = state.evolve(tr_op, [qargs[0]]) # Trace over remaining subsystems for qarg, dim in zip(qargs[1:], dims[1:]): tr_op = SuperOp(np.eye(dim).reshape(1, dim ** 2), input_dims=[dim], output_dims=[1]) ret = ret.evolve(tr_op, [qarg]) # Remove traced over subsystems which are listed as dimension 1 ret._reshape(tuple(np.delete(np.array(ret._dims), qargs))) return ret
[docs]def shannon_entropy(pvec, base=2): r"""Compute the Shannon entropy of a probability vector. The shannon entropy of a probability vector :math:`\vec{p} = [p_0, ..., p_{n-1}]` is defined as .. math: H(\vec{p}) = \sum_{i=0}^{n-1} p_i \log_b(p_i) where :math:`b` is the log base and (default 2), and :math:`0 \log_b(0) \equiv 0`. Args: pvec (array_like): a probability vector. base (int): the base of the logarithm [Default: 2]. Returns: float: The Shannon entropy H(pvec). """ if base == 2: def logfn(x): return - x * np.log2(x) elif base == np.e: def logfn(x): return - x * np.log(x) else: def logfn(x): return -x * np.log(x) / np.log(base) h_val = 0. for x in pvec: if 0 < x < 1: h_val += logfn(x) return h_val
def _format_state(state, validate=True): """Format input state into class object""" if isinstance(state, list): state = np.array(state, dtype=complex) if isinstance(state, np.ndarray): ndim = state.ndim if ndim == 1: state = Statevector(state) elif ndim == 2: dim1, dim2 = state.shape if dim2 == 1: state = Statevector(state) elif dim1 == dim2: state = DensityMatrix(state) if not isinstance(state, (Statevector, DensityMatrix)): raise QiskitError("Input is not a quantum state") if validate and not state.is_valid(): raise QiskitError("Input quantum state is not a valid") return state def _funm_svd(matrix, func): """Apply real scalar function to singular values of a matrix. Args: matrix (array_like): (N, N) Matrix at which to evaluate the function. func (callable): Callable object that evaluates a scalar function f. Returns: ndarray: funm (N, N) Value of the matrix function specified by func evaluated at `A`. """ unitary1, singular_values, unitary2 = la.svd(matrix) diag_func_singular = np.diag(func(singular_values)) return unitary1.dot(diag_func_singular).dot(unitary2)