def test_clifford_state_str(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) assert str(state) == "|00⟩"
def test_clifford_state_state_vector(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) np.testing.assert_equal(state.state_vector(), [1.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j])
def direct_fidelity_estimation(circuit: cirq.Circuit, qubits: List[cirq.Qid], sampler: cirq.Sampler, n_measured_operators: Optional[int], samples_per_term: int): """ Implementation of direct fidelity estimation, as per 'Direct Fidelity Estimation from Few Pauli Measurements' https://arxiv.org/abs/1104.4695 and 'Practical characterization of quantum devices without tomography' https://arxiv.org/abs/1104.3835. Args: circuit: The circuit to run the simulation on. qubits: The list of qubits. sampler: Either a noisy simulator or an engine. n_measured_operators: The total number of Pauli measurements, or None to explore each Pauli state once. samples_per_term: if set to 0, we use the 'sampler' parameter above as a noise (must be of type cirq.DensityMatrixSimulator) and simulate noise in the circuit. If greater than 0, we instead use the 'sampler' parameter directly to estimate the characteristic function. Returns: The estimated fidelity and a log of the run. """ # n_measured_operators is upper-case N in https://arxiv.org/abs/1104.3835 # Number of qubits, lower-case n in https://arxiv.org/abs/1104.3835 n_qubits = len(qubits) clifford_circuit = True clifford_state: Optional[cirq.CliffordState] = None try: clifford_state = cirq.CliffordState( qubit_map={qubits[i]: i for i in range(len(qubits))}) for gate in circuit.all_operations(): clifford_state.apply_unitary(gate) except ValueError: clifford_circuit = False # Computes for every \hat{P_i} of https://arxiv.org/abs/1104.3835 # estimate rho_i and Pr(i). We then collect tuples (rho_i, Pr(i), \hat{Pi}) # inside the variable 'pauli_traces'. if clifford_circuit: assert clifford_state is not None pauli_traces = _estimate_pauli_traces_clifford( n_qubits, cast(cirq.CliffordState, clifford_state), n_measured_operators) else: pauli_traces = _estimate_pauli_traces_general(qubits, circuit, n_measured_operators) p = np.asarray([x.Pr_i for x in pauli_traces]) if n_measured_operators is None: # Since we enumerate all the possible traces, the probs should add to 1. assert np.isclose(np.sum(p), 1.0, atol=1e-6) p /= np.sum(p) fidelity = 0.0 if samples_per_term == 0: # sigma in https://arxiv.org/abs/1104.3835 if not isinstance(sampler, cirq.DensityMatrixSimulator): raise TypeError('sampler is not a cirq.DensityMatrixSimulator ' 'but samples_per_term is zero.') noisy_simulator = cast(cirq.DensityMatrixSimulator, sampler) noisy_density_matrix = cast( cirq.DensityMatrixTrialResult, noisy_simulator.simulate(circuit)).final_density_matrix if clifford_circuit and n_measured_operators is None: # In case the circuit is Clifford and we compute an exhaustive list of # Pauli traces, instead of sampling we can simply enumerate them because # they all have the same probability. measured_pauli_traces = pauli_traces else: # Otherwise, randomly sample as per probability. measured_pauli_traces = np.random.choice(pauli_traces, size=len(pauli_traces), p=p) trial_results: List[TrialResult] = [] for pauli_trace in measured_pauli_traces: measure_pauli_string: cirq.PauliString = pauli_trace.P_i rho_i = pauli_trace.rho_i if samples_per_term > 0: sigma_i = asyncio.get_event_loop().run_until_complete( estimate_characteristic_function(circuit, measure_pauli_string, qubits, sampler, samples_per_term)) else: sigma_i, _ = compute_characteristic_function( circuit, measure_pauli_string, qubits, noisy_density_matrix) trial_results.append( TrialResult(pauli_trace=pauli_trace, sigma_i=sigma_i)) fidelity += sigma_i / rho_i estimated_fidelity = fidelity / len(pauli_traces) std_dev_estimate: Optional[float] std_dev_bound: Optional[float] if clifford_circuit: std_dev_estimate, std_dev_bound = _estimate_std_devs_clifford( estimated_fidelity, len(measured_pauli_traces)) else: std_dev_estimate, std_dev_bound = None, None dfe_intermediate_result = DFEIntermediateResult( clifford_state=clifford_state, pauli_traces=pauli_traces, trial_results=trial_results, std_dev_estimate=std_dev_estimate, std_dev_bound=std_dev_bound) return estimated_fidelity, dfe_intermediate_result
def test_clifford_state_initial_state(): q0 = cirq.LineQubit(0) with pytest.raises(ValueError, match='Out of range'): _ = cirq.CliffordState(qubit_map={q0: 0}, initial_state=2) state = cirq.CliffordState(qubit_map={q0: 0}, initial_state=1) np.testing.assert_allclose(state.state_vector(), [0, 1])
def test_stabilizerStateChForm_H(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) with pytest.raises(ValueError, match="|y> is equal to |z>"): state.ch_form._H_decompose(0, 1, 1, 0)
def test_clifford_stabilizerStateChForm_repr(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) assert repr(state) == 'StabilizerStateChForm(num_qubits=2)'
def test_clifford_tableau_repr(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) f = cirq.DensePauliString assert (repr(state.tableau) == "stabilizers: [{!r}, {!r}]".format( f("ZI"), f("IZ")))
def test_clifford_state_wave_function(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) np.testing.assert_equal(state.wave_function(), [1. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j])
def direct_fidelity_estimation(circuit: cirq.Circuit, qubits: List[cirq.Qid], sampler: cirq.Sampler, n_trials: int, n_clifford_trials: int, samples_per_term: int): """ Implementation of direct fidelity estimation, as per 'Direct Fidelity Estimation from Few Pauli Measurements' https://arxiv.org/abs/1104.4695 and 'Practical characterization of quantum devices without tomography' https://arxiv.org/abs/1104.3835. Args: circuit: The circuit to run the simulation on. qubits: The list of qubits. sampler: Either a noisy simulator or an engine. n_trial: The total number of Pauli measurements. n_clifford_trials: In case the circuit is Clifford, we specify the number of trials to estimate the noise-free pauli traces. samples_per_term: if set to 0, we use the 'sampler' parameter above as a noise (must be of type cirq.DensityMatrixSimulator) and simulate noise in the circuit. If greater than 0, we instead use the 'sampler' parameter directly to estimate the characteristic function. Returns: The estimated fidelity. """ # n_trials is upper-case N in https://arxiv.org/abs/1104.3835 # Number of qubits, lower-case n in https://arxiv.org/abs/1104.3835 n_qubits = len(qubits) d = 2**n_qubits clifford_circuit = True clifford_state: Optional[cirq.CliffordState] = None try: clifford_state = cirq.CliffordState( qubit_map={qubits[i]: i for i in range(len(qubits))}) for gate in circuit.all_operations(): clifford_state.apply_unitary(gate) except ValueError: clifford_circuit = False # Computes for every \hat{P_i} of https://arxiv.org/abs/1104.3835 # estimate rho_i and Pr(i). We then collect tuples (rho_i, Pr(i), \hat{Pi}) # inside the variable 'pauli_traces'. if clifford_circuit: print('Circuit is Clifford') assert clifford_state is not None pauli_traces = _estimate_pauli_traces_clifford( n_qubits, cast(cirq.CliffordState, clifford_state), n_clifford_trials) else: print('Circuit is not Clifford') pauli_traces = _estimate_pauli_traces_general(qubits, circuit) p = np.asarray([x['Pr_i'] for x in pauli_traces]) if not clifford_circuit: # For Clifford circuits, we do a Monte Carlo simulations, and thus there # is no guarantee that it adds up to 1.0 (but it should to the limit). assert np.isclose(np.sum(p), 1.0, atol=1e-6) # The package np.random.choice() is quite sensitive to probabilities not # summing up to 1.0. Even an absolute difference below 1e-6 (as checked just # above) does bother it, so we re-normalize the probs. p /= np.sum(p) fidelity = 0.0 if samples_per_term == 0: # sigma in https://arxiv.org/abs/1104.3835 if not isinstance(sampler, cirq.DensityMatrixSimulator): raise TypeError('sampler is not a cirq.DensityMatrixSimulator ' 'but samples_per_term is zero.') noisy_simulator = cast(cirq.DensityMatrixSimulator, sampler) noisy_density_matrix = cast( cirq.DensityMatrixTrialResult, noisy_simulator.simulate(circuit)).final_density_matrix for _ in range(n_trials): # Randomly sample as per probability. i = np.random.choice(len(pauli_traces), p=p) Pr_i = pauli_traces[i]['Pr_i'] measure_pauli_string: cirq.PauliString = pauli_traces[i]['P_i'] rho_i = pauli_traces[i]['rho_i'] if samples_per_term > 0: sigma_i = asyncio.get_event_loop().run_until_complete( estimate_characteristic_function(circuit, measure_pauli_string, qubits, sampler, samples_per_term)) else: sigma_i, _ = compute_characteristic_function( circuit, measure_pauli_string, qubits, noisy_density_matrix) fidelity += Pr_i * sigma_i / rho_i return fidelity / n_trials * d
def test_valid_apply_measurement(): q0 = cirq.LineQubit(0) state = cirq.CliffordState(qubit_map={q0: 0}, initial_state=1) measurements = {} _ = state.apply_measurement(cirq.measure(q0), measurements, np.random.RandomState()) assert measurements == {'0': [1]}
def test_clifford_tableau_repr(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) assert (repr(state.tableau) == "stabilizers: [Z0, Z1]")