Skip to content

Commit

Permalink
Choi representation (#4132)
Browse files Browse the repository at this point in the history
  • Loading branch information
viathor committed May 26, 2021
1 parent 6c72b5b commit b28b163
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,9 @@
dirac_notation,
eye_tensor,
fidelity,
kraus_to_choi,
one_hot,
operation_to_choi,
QUANTUM_STATE_LIKE,
QuantumState,
quantum_state,
Expand Down
5 changes: 5 additions & 0 deletions cirq-core/cirq/qis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

"""Tools and methods for quantum information science."""

from cirq.qis.channels import (
kraus_to_choi,
operation_to_choi,
)

from cirq.qis.clifford_tableau import CliffordTableau

from cirq.qis.measures import (
Expand Down
34 changes: 34 additions & 0 deletions cirq-core/cirq/qis/channels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2021 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tools for analyzing and manipulating quantum channels."""
from typing import Sequence

import numpy as np

from cirq import protocols


def kraus_to_choi(kraus_operators: Sequence[np.ndarray]) -> np.ndarray:
"""Returns the unique Choi matrix corresponding to a Kraus representation of a channel."""
d = np.prod(kraus_operators[0].shape)
c = np.zeros((d, d), dtype=np.complex128)
for k in kraus_operators:
v = np.reshape(k, d)
c += np.outer(v, v.conj())
return c


def operation_to_choi(operation: 'protocols.SupportsChannel') -> np.ndarray:
"""Returns the unique Choi matrix associated with a superoperator."""
return kraus_to_choi(protocols.channel(operation))
93 changes: 93 additions & 0 deletions cirq-core/cirq/qis/channels_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright 2021 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for channels."""
import numpy as np
import pytest

import cirq


def apply_channel(channel: cirq.SupportsChannel, rho: np.ndarray) -> np.ndarray:
ks = cirq.channel(channel)
d_out, d_in = ks[0].shape
assert rho.shape == (d_in, d_in)
out = np.zeros((d_out, d_out), dtype=np.complex128)
for k in ks:
out += k @ rho @ k.conj().T
return out


def expected_choi(channel: cirq.SupportsChannel) -> np.ndarray:
ks = cirq.channel(channel)
d_out, d_in = ks[0].shape
d = d_in * d_out
c = np.zeros((d, d), dtype=np.complex128)
for i in range(d_in):
for j in range(d_in):
e_ij = np.zeros((d_in, d_in))
e_ij[i, j] = 1
c += np.kron(apply_channel(channel, e_ij), e_ij)
return c


@pytest.mark.parametrize(
'kraus_operators, expected_choi',
(
([np.eye(2)], np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 1]])),
(
[
np.eye(2) / 2,
np.array([[0, 1], [1, 0]]) / 2,
np.array([[0, -1j], [1j, 0]]) / 2,
np.diag([1, -1]) / 2,
],
np.eye(4) / 2,
),
(
[
np.array([[1, 0, 0], [0, 0, 1]]) / np.sqrt(2),
np.array([[1, 0, 0], [0, 0, -1]]) / np.sqrt(2),
],
np.diag([1, 0, 0, 0, 0, 1]),
),
),
)
def test_kraus_to_choi(kraus_operators, expected_choi):
"""Verifies that cirq.kraus_to_choi computes the correct Choi matrix."""
assert np.allclose(cirq.kraus_to_choi(kraus_operators), expected_choi)


@pytest.mark.parametrize(
'channel',
(
cirq.I,
cirq.X,
cirq.CNOT,
cirq.depolarize(0.1),
cirq.depolarize(0.1, n_qubits=2),
cirq.amplitude_damp(0.2),
),
)
def test_operation_to_choi(channel):
"""Verifies that cirq.choi correctly computes the Choi matrix."""
n_qubits = cirq.num_qubits(channel)
actual = cirq.operation_to_choi(channel)
expected = expected_choi(channel)
assert np.isclose(np.trace(actual), 2 ** n_qubits)
assert np.all(actual == expected)


def test_choi_on_completely_dephasing_channel():
"""Checks that cirq.choi returns the right matrix for the completely dephasing channel."""
assert np.all(cirq.operation_to_choi(cirq.phase_damp(1)) == np.diag([1, 0, 0, 1]))

0 comments on commit b28b163

Please sign in to comment.