Skip to content

Commit

Permalink
Merge pull request #1492 from qiboteam/to_stinespring
Browse files Browse the repository at this point in the history
Add `to_stinespring` to `quantum_info.superoperator_transformations`
  • Loading branch information
scarrazza authored Oct 28, 2024
2 parents 099d7a0 + 475279b commit 0b97e33
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
6 changes: 6 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,12 @@ To Chi
.. autofunction:: qibo.quantum_info.to_chi


To Stinespring
""""""""""""""

.. autofunction:: qibo.quantum_info.to_stinespring


Choi to Liouville
"""""""""""""""""

Expand Down
49 changes: 46 additions & 3 deletions src/qibo/quantum_info/superoperator_transformations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Module with the most commom superoperator transformations."""

import warnings
from typing import Optional
from typing import List, Optional, Tuple, Union

import numpy as np
from scipy.optimize import minimize
Expand Down Expand Up @@ -303,6 +303,50 @@ def to_chi(
return channel


def to_stinespring(
channel,
partition: Optional[Union[List[int], Tuple[int, ...]]] = None,
nqubits: Optional[int] = None,
initial_state_env=None,
backend=None,
):
"""Convert quantum ``channel`` :math:`U` to its Stinespring representation :math:`U_{0}`.
It uses the Kraus representation as an intermediate step.
Args:
channel (ndarray): quantum channel.
nqubits (int, optional): total number of qubits in the system that is
interacting with the environment. Must be equal or greater than
the number of qubits ``channel`` acts on. If ``None``,
defaults to the number of qubits in ``channel``.
Defauts to ``None``.
initial_state_env (ndarray, optional): statevector representing the
initial state of the enviroment. If ``None``, it assumes the
environment in its ground state. Defaults to ``None``.
backend (:class:`qibo.backends.abstract.Backend`, optional): backend
to be used in the execution. If ``None``, it uses
:class:`qibo.backends.GlobalBackend`. Defaults to ``None``.
Returns:
ndarray: Quantum channel in its Stinespring representation :math:`U_{0}`.
"""
backend = _check_backend(backend)

if partition is None:
nqubits_channel = int(np.log2(channel.shape[-1]))
partition = tuple(range(nqubits_channel))

channel = kraus_to_stinespring(
[(partition, channel)],
nqubits=nqubits,
initial_state_env=initial_state_env,
backend=backend,
)

return channel


def choi_to_liouville(choi_super_op, order: str = "row", backend=None):
"""Converts Choi representation :math:`\\Lambda` of quantum channel
to its Liouville representation :math:`\\mathcal{E}`.
Expand All @@ -320,7 +364,6 @@ def choi_to_liouville(choi_super_op, order: str = "row", backend=None):
\\Lambda_{\\alpha\\beta, \\, \\gamma\\delta} \\mapsto
\\Lambda_{\\delta\\beta, \\, \\gamma\\alpha} \\equiv \\mathcal{E}
Args:
choi_super_op: Choi representation of quantum channel.
order (str, optional): If ``"row"``, reshuffling is performed
Expand Down Expand Up @@ -640,7 +683,7 @@ def choi_to_stinespring(
if nqubits is None:
nqubits = int(np.log2(kraus_ops[0].shape[0]))

nqubits_list = [tuple(range(nqubits)) for _ in range(len(kraus_ops))]
nqubits_list = [tuple(range(nqubits))] * len(kraus_ops)

kraus_ops = list(zip(nqubits_list, kraus_ops))

Expand Down
24 changes: 24 additions & 0 deletions tests/test_quantum_info_superoperator_transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from qibo import matrices
from qibo.config import PRECISION_TOL
from qibo.quantum_info.linalg_operations import partial_trace
from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector
from qibo.quantum_info.superoperator_transformations import (
chi_to_choi,
Expand Down Expand Up @@ -40,6 +41,7 @@
to_choi,
to_liouville,
to_pauli_liouville,
to_stinespring,
unvectorization,
vectorization,
)
Expand Down Expand Up @@ -363,6 +365,28 @@ def test_to_chi(backend, normalize, order, pauli_order):
backend.assert_allclose(chi, test_chi, atol=PRECISION_TOL)


@pytest.mark.parametrize("partition", [None, (0,)])
@pytest.mark.parametrize("test_a0", [test_a0])
def test_to_stinespring(backend, test_a0, partition):
test_a0_ = backend.cast(test_a0)
state = random_density_matrix(2, seed=8, backend=backend)

target = test_a0_ @ state @ backend.np.conj(test_a0_.T)

environment = (1, 2)

global_state = backend.identity_density_matrix(len(environment), normalize=True)
global_state = backend.np.kron(state, global_state)

stinespring = to_stinespring(
test_a0_, partition=partition, nqubits=len(environment) + 1, backend=backend
)
stinespring = stinespring @ global_state @ backend.np.conj(stinespring.T)
stinespring = partial_trace(stinespring, traced_qubits=environment, backend=backend)

backend.assert_allclose(stinespring, target, atol=PRECISION_TOL)


@pytest.mark.parametrize("test_superop", [test_superop])
@pytest.mark.parametrize("order", ["row", "column"])
def test_choi_to_liouville(backend, order, test_superop):
Expand Down

0 comments on commit 0b97e33

Please sign in to comment.