1# Copyright 2019 The Cirq Developers
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tests for measures."""
15import numpy as np
16import pytest
17
18import cirq
19
20N = 15
21VEC1 = cirq.testing.random_superposition(N)
22VEC2 = cirq.testing.random_superposition(N)
23MAT1 = cirq.testing.random_density_matrix(N)
24MAT2 = cirq.testing.random_density_matrix(N)
25U = cirq.testing.random_unitary(N)
26
27
28def test_fidelity_symmetric():
29    np.testing.assert_allclose(cirq.fidelity(VEC1, VEC2), cirq.fidelity(VEC2, VEC1))
30    np.testing.assert_allclose(cirq.fidelity(VEC1, MAT1), cirq.fidelity(MAT1, VEC1))
31    np.testing.assert_allclose(
32        cirq.fidelity(cirq.density_matrix(MAT1), MAT2),
33        cirq.fidelity(cirq.density_matrix(MAT2), MAT1),
34    )
35
36
37def test_fidelity_between_zero_and_one():
38    assert 0 <= cirq.fidelity(VEC1, VEC2) <= 1
39    assert 0 <= cirq.fidelity(VEC1, MAT1) <= 1
40    assert 0 <= cirq.fidelity(cirq.density_matrix(MAT1), MAT2) <= 1
41
42
43def test_fidelity_invariant_under_unitary_transformation():
44    np.testing.assert_allclose(
45        cirq.fidelity(cirq.density_matrix(MAT1), MAT2),
46        cirq.fidelity(cirq.density_matrix(U @ MAT1 @ U.T.conj()), U @ MAT2 @ U.T.conj()),
47    )
48
49
50def test_fidelity_commuting_matrices():
51    d1 = np.random.uniform(size=N)
52    d1 /= np.sum(d1)
53    d2 = np.random.uniform(size=N)
54    d2 /= np.sum(d2)
55    mat1 = cirq.density_matrix(U @ np.diag(d1) @ U.T.conj())
56    mat2 = U @ np.diag(d2) @ U.T.conj()
57
58    np.testing.assert_allclose(
59        cirq.fidelity(mat1, mat2, qid_shape=(15,)), np.sum(np.sqrt(d1 * d2)) ** 2
60    )
61
62
63def test_fidelity_known_values():
64    vec1 = np.array([1, 1j, -1, -1j]) * 0.5
65    vec2 = np.array([1, -1, 1, -1], dtype=np.complex128) * 0.5
66    vec3 = np.array([1, 0, 0, 0], dtype=np.complex128)
67    tensor1 = np.reshape(vec1, (2, 2))
68    mat1 = cirq.density_matrix(np.outer(vec1, vec1.conj()))
69    mat2 = cirq.density_matrix(np.outer(vec2, vec2.conj()))
70    mat3 = 0.3 * mat1.density_matrix() + 0.7 * mat2.density_matrix()
71
72    np.testing.assert_allclose(cirq.fidelity(vec1, vec1), 1)
73    np.testing.assert_allclose(cirq.fidelity(vec2, vec2), 1)
74    np.testing.assert_allclose(cirq.fidelity(vec1, vec3), 0.25)
75    np.testing.assert_allclose(cirq.fidelity(vec1, tensor1), 1)
76    np.testing.assert_allclose(cirq.fidelity(tensor1, vec1), 1)
77    np.testing.assert_allclose(cirq.fidelity(mat1, mat1), 1)
78    np.testing.assert_allclose(cirq.fidelity(mat2, mat2), 1)
79    np.testing.assert_allclose(cirq.fidelity(vec1, mat1), 1)
80    np.testing.assert_allclose(cirq.fidelity(mat2, vec2), 1)
81    np.testing.assert_allclose(cirq.fidelity(vec1, vec2), 0)
82    np.testing.assert_allclose(cirq.fidelity(vec1, mat2), 0)
83    np.testing.assert_allclose(cirq.fidelity(mat1, vec2), 0)
84    np.testing.assert_allclose(cirq.fidelity(vec1, mat3), 0.3)
85    np.testing.assert_allclose(cirq.fidelity(tensor1, mat3), 0.3)
86    np.testing.assert_allclose(cirq.fidelity(mat3, tensor1), 0.3)
87    np.testing.assert_allclose(cirq.fidelity(mat3, vec2), 0.7)
88
89
90def test_fidelity_numpy_arrays():
91    vec1 = np.array([1, 0, 0, 0, 0, 0, 0, 0], dtype=np.complex64)
92    vec2 = np.array([1, 0, 0, 0], dtype=np.complex64)
93    tensor1 = np.reshape(vec1, (2, 2, 2))
94    tensor2 = np.reshape(vec2, (2, 2))
95    mat1 = np.outer(vec1, vec1.conj())
96
97    np.testing.assert_allclose(cirq.fidelity(vec1, vec1), 1)
98    np.testing.assert_allclose(cirq.fidelity(vec1, tensor1), 1)
99    np.testing.assert_allclose(cirq.fidelity(tensor1, vec1), 1)
100    np.testing.assert_allclose(cirq.fidelity(vec1, mat1), 1)
101    np.testing.assert_allclose(cirq.fidelity(tensor1, mat1), 1)
102    np.testing.assert_allclose(cirq.fidelity(tensor2, tensor2, qid_shape=(2, 2)), 1)
103    np.testing.assert_allclose(cirq.fidelity(mat1, mat1, qid_shape=(8,)), 1)
104
105    with pytest.raises(ValueError, match='dimension'):
106        _ = cirq.fidelity(vec1, vec1, qid_shape=(4,))
107
108    with pytest.raises(ValueError, match='Mismatched'):
109        _ = cirq.fidelity(vec1, vec2)
110
111    with pytest.raises(ValueError, match='ambiguous'):
112        _ = cirq.fidelity(tensor2, tensor2)
113
114    with pytest.raises(ValueError, match='ambiguous'):
115        _ = cirq.fidelity(mat1, mat1)
116
117
118def test_fidelity_ints():
119    assert cirq.fidelity(3, 4) == 0.0
120    assert cirq.fidelity(4, 4) == 1.0
121
122    with pytest.raises(ValueError, match='non-negative'):
123        _ = cirq.fidelity(-1, 2)
124    with pytest.raises(ValueError, match='maximum'):
125        _ = cirq.fidelity(4, 1, qid_shape=(2,))
126    with pytest.raises(ValueError, match='maximum'):
127        _ = cirq.fidelity(1, 4, qid_shape=(2,))
128
129
130def test_fidelity_product_states():
131    a, b = cirq.LineQubit.range(2)
132
133    np.testing.assert_allclose(
134        cirq.fidelity(cirq.KET_ZERO(a) * cirq.KET_ZERO(b), cirq.KET_ZERO(a) * cirq.KET_ZERO(b)), 1.0
135    )
136    np.testing.assert_allclose(
137        cirq.fidelity(cirq.KET_ZERO(a) * cirq.KET_ZERO(b), cirq.KET_ZERO(a) * cirq.KET_ONE(b)),
138        0.0,
139        atol=1e-7,
140    )
141    np.testing.assert_allclose(
142        cirq.fidelity(cirq.KET_ZERO(a) * cirq.KET_ZERO(b), cirq.KET_ZERO(a) * cirq.KET_PLUS(b)), 0.5
143    )
144    np.testing.assert_allclose(
145        cirq.fidelity(cirq.KET_ONE(a) * cirq.KET_ONE(b), cirq.KET_MINUS(a) * cirq.KET_PLUS(b)), 0.25
146    )
147    np.testing.assert_allclose(
148        cirq.fidelity(cirq.KET_MINUS(a) * cirq.KET_PLUS(b), cirq.KET_MINUS(a) * cirq.KET_PLUS(b)),
149        1.0,
150    )
151    np.testing.assert_allclose(
152        cirq.fidelity(cirq.KET_MINUS(a) * cirq.KET_PLUS(b), cirq.KET_PLUS(a) * cirq.KET_MINUS(b)),
153        0.0,
154        atol=1e-7,
155    )
156
157    with pytest.raises(ValueError, match='Mismatched'):
158        _ = cirq.fidelity(cirq.KET_MINUS(a), cirq.KET_PLUS(a) * cirq.KET_MINUS(b))
159    with pytest.raises(ValueError, match='qid shape'):
160        _ = cirq.fidelity(
161            cirq.KET_MINUS(a) * cirq.KET_PLUS(b),
162            cirq.KET_PLUS(a) * cirq.KET_MINUS(b),
163            qid_shape=(4,),
164        )
165
166
167def test_fidelity_fail_inference():
168    state_vector = cirq.one_hot(shape=(4,), dtype=np.complex128)
169    state_tensor = np.reshape(state_vector, (2, 2))
170    with pytest.raises(ValueError, match='Please specify'):
171        _ = cirq.fidelity(state_tensor, 4)
172
173
174def test_fidelity_bad_shape():
175    with pytest.raises(ValueError, match='Invalid quantum state'):
176        _ = cirq.fidelity(np.array([[[1.0]]]), np.array([[[1.0]]]), qid_shape=(1,))
177
178
179def test_von_neumann_entropy():
180    # 1x1 matrix
181    assert cirq.von_neumann_entropy(np.array([[1]])) == 0
182    # An EPR pair state (|00> + |11>)(<00| + <11|)
183    assert (
184        cirq.von_neumann_entropy(0.5 * np.array([1, 0, 0, 1] * np.array([[1], [0], [0], [1]]))) == 0
185    )
186    # Maximally mixed state
187    # yapf: disable
188    assert cirq.von_neumann_entropy(np.array(
189        [[0.5, 0],
190        [0, 0.5]])) == 1
191    # yapf: enable
192    # 2x2 random unitary, each column as a ket, each ket as a density matrix,
193    # linear combination of the two with coefficients 0.1 and 0.9
194    res = cirq.testing.random_unitary(2)
195    first_column = res[:, 0]
196    first_density_matrix = 0.1 * np.outer(first_column, np.conj(first_column))
197    second_column = res[:, 1]
198    second_density_matrix = 0.9 * np.outer(second_column, np.conj(second_column))
199    assert np.isclose(
200        cirq.von_neumann_entropy(first_density_matrix + second_density_matrix), 0.4689, atol=1e-04
201    )
202
203    assert np.isclose(
204        cirq.von_neumann_entropy(np.diag([0, 0, 0.1, 0, 0.2, 0.3, 0.4, 0])), 1.8464, atol=1e-04
205    )
206    # Random NxN matrix
207    probs = np.random.exponential(size=N)
208    probs /= np.sum(probs)
209    mat = U @ (probs * U).T.conj()
210
211    np.testing.assert_allclose(
212        cirq.von_neumann_entropy(mat), -np.sum(probs * np.log(probs) / np.log(2))
213    )
214    # QuantumState object
215    assert (
216        cirq.von_neumann_entropy(cirq.quantum_state(np.array([[0.5, 0], [0, 0.5]]), qid_shape=(2,)))
217        == 1
218    )
219    assert (
220        cirq.von_neumann_entropy(
221            cirq.quantum_state(np.array([[0.5, 0.5], [0.5, 0.5]]), qid_shape=(2, 2))
222        )
223        == 0
224    )
225
226
227@pytest.mark.parametrize(
228    'gate, expected_entanglement_fidelity',
229    (
230        (cirq.I, 1),
231        (cirq.X, 0),
232        (cirq.Y, 0),
233        (cirq.Z, 0),
234        (cirq.S, 1 / 2),
235        (cirq.CNOT, 1 / 4),
236        (cirq.TOFFOLI, 9 / 16),
237    ),
238)
239def test_entanglement_fidelity_of_unitary_channels(gate, expected_entanglement_fidelity):
240    assert np.isclose(cirq.entanglement_fidelity(gate), expected_entanglement_fidelity)
241
242
243@pytest.mark.parametrize('p', (0, 0.1, 0.2, 0.5, 0.8, 0.9, 1))
244@pytest.mark.parametrize(
245    'channel_factory, entanglement_fidelity_formula',
246    (
247        # Each Pauli error turns the maximally entangled state into an orthogonal state, so only
248        # the error-free term, whose pre-factor is 1 - p, contributes to entanglement fidelity.
249        (cirq.depolarize, lambda p: 1 - p),
250        (lambda p: cirq.depolarize(p, n_qubits=2), lambda p: 1 - p),
251        (lambda p: cirq.depolarize(p, n_qubits=3), lambda p: 1 - p),
252        # See e.g. https://quantumcomputing.stackexchange.com/questions/16074 for average fidelity,
253        # then use Horodecki formula F_avg = (N F_e + 1) / (N + 1) to find entanglement fidelity.
254        (cirq.amplitude_damp, lambda gamma: 1 / 2 - gamma / 4 + np.sqrt(1 - gamma) / 2),
255    ),
256)
257def test_entanglement_fidelity_of_noisy_channels(p, channel_factory, entanglement_fidelity_formula):
258    channel = channel_factory(p)
259    actual_entanglement_fidelity = cirq.entanglement_fidelity(channel)
260    expected_entanglement_fidelity = entanglement_fidelity_formula(p)
261    assert np.isclose(actual_entanglement_fidelity, expected_entanglement_fidelity)
262