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