Skip to content

Commit 0b97e33

Browse files
authored
Merge pull request #1492 from qiboteam/to_stinespring
Add `to_stinespring` to `quantum_info.superoperator_transformations`
2 parents 099d7a0 + 475279b commit 0b97e33

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

doc/source/api-reference/qibo.rst

+6
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,12 @@ To Chi
21752175
.. autofunction:: qibo.quantum_info.to_chi
21762176

21772177

2178+
To Stinespring
2179+
""""""""""""""
2180+
2181+
.. autofunction:: qibo.quantum_info.to_stinespring
2182+
2183+
21782184
Choi to Liouville
21792185
"""""""""""""""""
21802186

src/qibo/quantum_info/superoperator_transformations.py

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Module with the most commom superoperator transformations."""
22

33
import warnings
4-
from typing import Optional
4+
from typing import List, Optional, Tuple, Union
55

66
import numpy as np
77
from scipy.optimize import minimize
@@ -303,6 +303,50 @@ def to_chi(
303303
return channel
304304

305305

306+
def to_stinespring(
307+
channel,
308+
partition: Optional[Union[List[int], Tuple[int, ...]]] = None,
309+
nqubits: Optional[int] = None,
310+
initial_state_env=None,
311+
backend=None,
312+
):
313+
"""Convert quantum ``channel`` :math:`U` to its Stinespring representation :math:`U_{0}`.
314+
315+
It uses the Kraus representation as an intermediate step.
316+
317+
Args:
318+
channel (ndarray): quantum channel.
319+
nqubits (int, optional): total number of qubits in the system that is
320+
interacting with the environment. Must be equal or greater than
321+
the number of qubits ``channel`` acts on. If ``None``,
322+
defaults to the number of qubits in ``channel``.
323+
Defauts to ``None``.
324+
initial_state_env (ndarray, optional): statevector representing the
325+
initial state of the enviroment. If ``None``, it assumes the
326+
environment in its ground state. Defaults to ``None``.
327+
backend (:class:`qibo.backends.abstract.Backend`, optional): backend
328+
to be used in the execution. If ``None``, it uses
329+
:class:`qibo.backends.GlobalBackend`. Defaults to ``None``.
330+
331+
Returns:
332+
ndarray: Quantum channel in its Stinespring representation :math:`U_{0}`.
333+
"""
334+
backend = _check_backend(backend)
335+
336+
if partition is None:
337+
nqubits_channel = int(np.log2(channel.shape[-1]))
338+
partition = tuple(range(nqubits_channel))
339+
340+
channel = kraus_to_stinespring(
341+
[(partition, channel)],
342+
nqubits=nqubits,
343+
initial_state_env=initial_state_env,
344+
backend=backend,
345+
)
346+
347+
return channel
348+
349+
306350
def choi_to_liouville(choi_super_op, order: str = "row", backend=None):
307351
"""Converts Choi representation :math:`\\Lambda` of quantum channel
308352
to its Liouville representation :math:`\\mathcal{E}`.
@@ -320,7 +364,6 @@ def choi_to_liouville(choi_super_op, order: str = "row", backend=None):
320364
\\Lambda_{\\alpha\\beta, \\, \\gamma\\delta} \\mapsto
321365
\\Lambda_{\\delta\\beta, \\, \\gamma\\alpha} \\equiv \\mathcal{E}
322366
323-
324367
Args:
325368
choi_super_op: Choi representation of quantum channel.
326369
order (str, optional): If ``"row"``, reshuffling is performed
@@ -640,7 +683,7 @@ def choi_to_stinespring(
640683
if nqubits is None:
641684
nqubits = int(np.log2(kraus_ops[0].shape[0]))
642685

643-
nqubits_list = [tuple(range(nqubits)) for _ in range(len(kraus_ops))]
686+
nqubits_list = [tuple(range(nqubits))] * len(kraus_ops)
644687

645688
kraus_ops = list(zip(nqubits_list, kraus_ops))
646689

tests/test_quantum_info_superoperator_transformations.py

+24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from qibo import matrices
55
from qibo.config import PRECISION_TOL
6+
from qibo.quantum_info.linalg_operations import partial_trace
67
from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector
78
from qibo.quantum_info.superoperator_transformations import (
89
chi_to_choi,
@@ -40,6 +41,7 @@
4041
to_choi,
4142
to_liouville,
4243
to_pauli_liouville,
44+
to_stinespring,
4345
unvectorization,
4446
vectorization,
4547
)
@@ -363,6 +365,28 @@ def test_to_chi(backend, normalize, order, pauli_order):
363365
backend.assert_allclose(chi, test_chi, atol=PRECISION_TOL)
364366

365367

368+
@pytest.mark.parametrize("partition", [None, (0,)])
369+
@pytest.mark.parametrize("test_a0", [test_a0])
370+
def test_to_stinespring(backend, test_a0, partition):
371+
test_a0_ = backend.cast(test_a0)
372+
state = random_density_matrix(2, seed=8, backend=backend)
373+
374+
target = test_a0_ @ state @ backend.np.conj(test_a0_.T)
375+
376+
environment = (1, 2)
377+
378+
global_state = backend.identity_density_matrix(len(environment), normalize=True)
379+
global_state = backend.np.kron(state, global_state)
380+
381+
stinespring = to_stinespring(
382+
test_a0_, partition=partition, nqubits=len(environment) + 1, backend=backend
383+
)
384+
stinespring = stinespring @ global_state @ backend.np.conj(stinespring.T)
385+
stinespring = partial_trace(stinespring, traced_qubits=environment, backend=backend)
386+
387+
backend.assert_allclose(stinespring, target, atol=PRECISION_TOL)
388+
389+
366390
@pytest.mark.parametrize("test_superop", [test_superop])
367391
@pytest.mark.parametrize("order", ["row", "column"])
368392
def test_choi_to_liouville(backend, order, test_superop):

0 commit comments

Comments
 (0)