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. 14import itertools 15import math 16from typing import Sequence 17 18import numpy as np 19import pytest 20 21import cirq 22 23 24def sample_noisy_bitstrings( 25 circuit: cirq.Circuit, qubit_order: Sequence[cirq.Qid], depolarization: float, repetitions: int 26) -> np.ndarray: 27 assert 0 <= depolarization <= 1 28 dim = np.prod(circuit.qid_shape(), dtype=np.int64) 29 n_incoherent = int(depolarization * repetitions) 30 n_coherent = repetitions - n_incoherent 31 incoherent_samples = np.random.randint(dim, size=n_incoherent) 32 circuit_with_measurements = cirq.Circuit(circuit, cirq.measure(*qubit_order, key='m')) 33 r = cirq.sample(circuit_with_measurements, repetitions=n_coherent) 34 coherent_samples = r.data['m'].to_numpy() 35 return np.concatenate((coherent_samples, incoherent_samples)) 36 37 38def make_random_quantum_circuit(qubits: Sequence[cirq.Qid], depth: int) -> cirq.Circuit: 39 SQ_GATES = [cirq.X ** 0.5, cirq.Y ** 0.5, cirq.T] 40 circuit = cirq.Circuit() 41 cz_start = 0 42 for q in qubits: 43 circuit.append(cirq.H(q)) 44 for _ in range(depth): 45 for q in qubits: 46 random_gate = SQ_GATES[np.random.randint(len(SQ_GATES))] 47 circuit.append(random_gate(q)) 48 for q0, q1 in zip( 49 itertools.islice(qubits, cz_start, None, 2), 50 itertools.islice(qubits, cz_start + 1, None, 2), 51 ): 52 circuit.append(cirq.CNOT(q0, q1)) 53 cz_start = 1 - cz_start 54 for q in qubits: 55 circuit.append(cirq.H(q)) 56 return circuit 57 58 59@pytest.mark.parametrize( 60 'depolarization, estimator', 61 itertools.product( 62 (0.0, 0.2, 0.7, 1.0), 63 ( 64 cirq.hog_score_xeb_fidelity_from_probabilities, 65 cirq.linear_xeb_fidelity_from_probabilities, 66 cirq.log_xeb_fidelity_from_probabilities, 67 ), 68 ), 69) 70def test_xeb_fidelity(depolarization, estimator): 71 prng_state = np.random.get_state() 72 np.random.seed(0) 73 74 fs = [] 75 for _ in range(10): 76 qubits = cirq.LineQubit.range(5) 77 circuit = make_random_quantum_circuit(qubits, depth=12) 78 bitstrings = sample_noisy_bitstrings(circuit, qubits, depolarization, repetitions=5000) 79 80 f = cirq.xeb_fidelity(circuit, bitstrings, qubits, estimator=estimator) 81 amplitudes = cirq.final_state_vector(circuit) 82 f2 = cirq.xeb_fidelity( 83 circuit, bitstrings, qubits, amplitudes=amplitudes, estimator=estimator 84 ) 85 assert np.abs(f - f2) < 1e-6 86 87 fs.append(f) 88 89 estimated_fidelity = np.mean(fs) 90 expected_fidelity = 1 - depolarization 91 assert np.isclose(estimated_fidelity, expected_fidelity, atol=0.04) 92 93 np.random.set_state(prng_state) 94 95 96def test_linear_and_log_xeb_fidelity(): 97 prng_state = np.random.get_state() 98 np.random.seed(0) 99 100 depolarization = 0.5 101 102 fs_log = [] 103 fs_lin = [] 104 for _ in range(10): 105 qubits = cirq.LineQubit.range(5) 106 circuit = make_random_quantum_circuit(qubits, depth=12) 107 bitstrings = sample_noisy_bitstrings( 108 circuit, qubits, depolarization=depolarization, repetitions=5000 109 ) 110 111 f_log = cirq.log_xeb_fidelity(circuit, bitstrings, qubits) 112 f_lin = cirq.linear_xeb_fidelity(circuit, bitstrings, qubits) 113 114 fs_log.append(f_log) 115 fs_lin.append(f_lin) 116 117 assert np.isclose(np.mean(fs_log), 1 - depolarization, atol=0.01) 118 assert np.isclose(np.mean(fs_lin), 1 - depolarization, atol=0.09) 119 120 np.random.set_state(prng_state) 121 122 123def test_xeb_fidelity_invalid_qubits(): 124 q0, q1, q2 = cirq.LineQubit.range(3) 125 circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1)) 126 bitstrings = sample_noisy_bitstrings(circuit, (q0, q1, q2), 0.9, 10) 127 with pytest.raises(ValueError): 128 cirq.xeb_fidelity(circuit, bitstrings, (q0, q2)) 129 130 131def test_xeb_fidelity_invalid_bitstrings(): 132 q0, q1 = cirq.LineQubit.range(2) 133 circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1)) 134 bitstrings = [0, 1, 2, 3, 4] 135 with pytest.raises(ValueError): 136 cirq.xeb_fidelity(circuit, bitstrings, (q0, q1)) 137 138 139def test_xeb_fidelity_tuple_input(): 140 q0, q1 = cirq.LineQubit.range(2) 141 circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1)) 142 bitstrings = [0, 1, 2] 143 f1 = cirq.xeb_fidelity(circuit, bitstrings, (q0, q1)) 144 f2 = cirq.xeb_fidelity(circuit, tuple(bitstrings), (q0, q1)) 145 assert f1 == f2 146 147 148def test_least_squares_xeb_fidelity_from_expectations(): 149 prng_state = np.random.get_state() 150 np.random.seed(0) 151 152 depolarization = 0.5 153 154 n_qubits = 5 155 dim = 2 ** n_qubits 156 n_circuits = 10 157 qubits = cirq.LineQubit.range(n_qubits) 158 159 measured_expectations_lin = [] 160 exact_expectations_lin = [] 161 measured_expectations_log = [] 162 exact_expectations_log = [] 163 uniform_expectations_log = [] 164 for _ in range(n_circuits): 165 circuit = make_random_quantum_circuit(qubits, depth=12) 166 bitstrings = sample_noisy_bitstrings( 167 circuit, qubits, depolarization=depolarization, repetitions=5000 168 ) 169 amplitudes = cirq.final_state_vector(circuit) 170 probabilities = cirq.state_vector_to_probabilities(amplitudes) 171 172 measured_expectations_lin.append(dim * np.mean(probabilities[bitstrings])) 173 exact_expectations_lin.append(dim * np.sum(probabilities ** 2)) 174 175 measured_expectations_log.append(np.mean(np.log(dim * probabilities[bitstrings]))) 176 exact_expectations_log.append(np.sum(probabilities * np.log(dim * probabilities))) 177 uniform_expectations_log.append(np.mean(np.log(dim * probabilities))) 178 179 f_lin, r_lin = cirq.experiments.least_squares_xeb_fidelity_from_expectations( 180 measured_expectations_lin, exact_expectations_lin, [1.0] * n_circuits 181 ) 182 f_log, r_log = cirq.experiments.least_squares_xeb_fidelity_from_expectations( 183 measured_expectations_log, exact_expectations_log, uniform_expectations_log 184 ) 185 186 assert np.isclose(f_lin, 1 - depolarization, atol=0.01) 187 assert np.isclose(f_log, 1 - depolarization, atol=0.01) 188 np.testing.assert_allclose(np.sum(np.array(r_lin) ** 2), 0.0, atol=1e-2) 189 np.testing.assert_allclose(np.sum(np.array(r_log) ** 2), 0.0, atol=1e-2) 190 191 np.random.set_state(prng_state) 192 193 194def test_least_squares_xeb_fidelity_from_expectations_bad_length(): 195 with pytest.raises(ValueError) as exception_info: 196 _ = cirq.experiments.least_squares_xeb_fidelity_from_expectations([1.0], [1.0], [1.0, 2.0]) 197 assert '1, 1, and 2' in str(exception_info.value) 198 199 200def test_least_squares_xeb_fidelity_from_probabilities(): 201 prng_state = np.random.get_state() 202 np.random.seed(0) 203 204 depolarization = 0.5 205 206 n_qubits = 5 207 dim = 2 ** n_qubits 208 n_circuits = 10 209 qubits = cirq.LineQubit.range(n_qubits) 210 211 all_probabilities = [] 212 observed_probabilities = [] 213 for _ in range(n_circuits): 214 circuit = make_random_quantum_circuit(qubits, depth=12) 215 bitstrings = sample_noisy_bitstrings( 216 circuit, qubits, depolarization=depolarization, repetitions=5000 217 ) 218 amplitudes = cirq.final_state_vector(circuit) 219 probabilities = cirq.state_vector_to_probabilities(amplitudes) 220 221 all_probabilities.append(probabilities) 222 observed_probabilities.append(probabilities[bitstrings]) 223 224 f_lin, r_lin = cirq.least_squares_xeb_fidelity_from_probabilities( 225 dim, observed_probabilities, all_probabilities, None, True 226 ) 227 f_log_np, r_log_np = cirq.least_squares_xeb_fidelity_from_probabilities( 228 dim, observed_probabilities, all_probabilities, np.log, True 229 ) 230 f_log_math, r_log_math = cirq.least_squares_xeb_fidelity_from_probabilities( 231 dim, observed_probabilities, all_probabilities, math.log, False 232 ) 233 234 assert np.isclose(f_lin, 1 - depolarization, atol=0.01) 235 assert np.isclose(f_log_np, 1 - depolarization, atol=0.01) 236 assert np.isclose(f_log_math, 1 - depolarization, atol=0.01) 237 np.testing.assert_allclose(np.sum(np.array(r_lin) ** 2), 0.0, atol=1e-2) 238 np.testing.assert_allclose(np.sum(np.array(r_log_np) ** 2), 0.0, atol=1e-2) 239 np.testing.assert_allclose(np.sum(np.array(r_log_math) ** 2), 0.0, atol=1e-2) 240 241 np.random.set_state(prng_state) 242