def single_qubit_state_tomography(sampler: sim.Sampler, qubit: devices.GridQubit, circuit: circuits.Circuit, repetitions: int = 1000) -> TomographyResult: """Single-qubit state tomography. The density matrix of the output state of a circuit is measured by first doing projective measurements in the z-basis, which determine the diagonal elements of the matrix. A X/2 or Y/2 rotation is then added before the z-basis measurement, which determines the imaginary and real parts of the off-diagonal matrix elements, respectively. See Vandersypen and Chuang, Rev. Mod. Phys. 76, 1037 for details. Args: sampler: The quantum engine or simulator to run the circuits. qubit: The qubit under test. circuit: The circuit to execute on the qubit before tomography. repetitions: The number of measurements for each basis rotation. Returns: A TomographyResult object that stores and plots the density matrix. """ circuit_z = circuit + circuits.Circuit.from_ops(ops.measure(qubit, key='z')) results = sampler.run(circuit_z, repetitions=repetitions) rho_11 = np.mean(results.measurements['z']) rho_00 = 1.0 - rho_11 circuit_x = circuits.Circuit.from_ops(circuit, ops.X(qubit)**0.5, ops.measure(qubit, key='z')) results = sampler.run(circuit_x, repetitions=repetitions) rho_01_im = np.mean(results.measurements['z']) - 0.5 circuit_y = circuits.Circuit.from_ops(circuit, ops.Y(qubit)**-0.5, ops.measure(qubit, key='z')) results = sampler.run(circuit_y, repetitions=repetitions) rho_01_re = 0.5 - np.mean(results.measurements['z']) rho_01 = rho_01_re + 1j * rho_01_im rho_10 = np.conj(rho_01) rho = np.array([[rho_00, rho_01], [rho_10, rho_11]]) return TomographyResult(rho)
def single_qubit_randomized_benchmarking( sampler: sim.Sampler, qubit: devices.GridQubit, use_xy_basis: bool = True, *, num_clifford_range: Sequence[int] = range(10, 100, 10), num_circuits: int = 20, repetitions: int = 1000) -> RandomizedBenchMarkResult: """Clifford-based randomized benchmarking (RB) of a single qubit. A total of num_circuits random circuits are generated, each of which contains a fixed number of single-qubit Clifford gates plus one additional Clifford that inverts the whole sequence and a measurement in the z-basis. Each circuit is repeated a number of times and the average |0> state population is determined from the measurement outcomes of all of the circuits. The above process is done for different circuit lengths specified by the integers in num_clifford_range. For example, an integer 10 means the random circuits will contain 10 Clifford gates each plus one inverting Clifford. The user may use the result to extract an average gate fidelity, by analyzing the change in the average |0> state population at different circuit lengths. For actual experiments, one should choose num_clifford_range such that a clear exponential decay is observed in the results. See Barends et al., Nature 508, 500 for details. Args: sampler: The quantum engine or simulator to run the circuits. qubit: The qubit under test. use_xy_basis: Determines if the Clifford gates are built with x and y rotations (True) or x and z rotations (False). num_clifford_range: The different numbers of Cliffords in the RB study. num_circuits: The number of random circuits generated for each number of Cliffords. repetitions: The number of repetitions of each circuit. Returns: A RandomizedBenchMarkResult object that stores and plots the result. """ cliffords = _single_qubit_cliffords() c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1]) gnd_probs = [] for num_cfds in num_clifford_range: excited_probs_l = [] for _ in range(num_circuits): circuit = _random_single_q_clifford(qubit, num_cfds, c1, cfd_mats) circuit.append(ops.measure(qubit, key='z')) results = sampler.run(circuit, repetitions=repetitions) excited_probs_l.append(np.mean(results.measurements['z'])) gnd_probs.append(1.0 - np.mean(excited_probs_l)) return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)
def two_qubit_randomized_benchmarking( sampler: sim.Sampler, first_qubit: devices.GridQubit, second_qubit: devices.GridQubit, *, num_clifford_range: Sequence[int] = range(5, 50, 5), num_circuits: int = 20, repetitions: int = 1000) -> RandomizedBenchMarkResult: """Clifford-based randomized benchmarking (RB) of two qubits. A total of num_circuits random circuits are generated, each of which contains a fixed number of two-qubit Clifford gates plus one additional Clifford that inverts the whole sequence and a measurement in the z-basis. Each circuit is repeated a number of times and the average |00> state population is determined from the measurement outcomes of all of the circuits. The above process is done for different circuit lengths specified by the integers in num_clifford_range. For example, an integer 10 means the random circuits will contain 10 Clifford gates each plus one inverting Clifford. The user may use the result to extract an average gate fidelity, by analyzing the change in the average |00> state population at different circuit lengths. For actual experiments, one should choose num_clifford_range such that a clear exponential decay is observed in the results. The two-qubit Cliffords here are decomposed into CZ gates plus single-qubit x and y rotations. See Barends et al., Nature 508, 500 for details. Args: sampler: The quantum engine or simulator to run the circuits. first_qubit: The first qubit under test. second_qubit: The second qubit under test. num_clifford_range: The different numbers of Cliffords in the RB study. num_circuits: The number of random circuits generated for each number of Cliffords. repetitions: The number of repetitions of each circuit. Returns: A RandomizedBenchMarkResult object that stores and plots the result. """ cliffords = _single_qubit_cliffords() cfd_matrices = _two_qubit_clifford_matrices(first_qubit, second_qubit, cliffords) gnd_probs = [] for num_cfds in num_clifford_range: gnd_probs_l = [] for _ in range(num_circuits): circuit = _random_two_q_clifford(first_qubit, second_qubit, num_cfds, cfd_matrices, cliffords) circuit.append(ops.measure(first_qubit, second_qubit, key='z')) results = sampler.run(circuit, repetitions=repetitions) gnds = [(not r[0] and not r[1]) for r in results.measurements['z']] gnd_probs_l.append(np.mean(gnds)) gnd_probs.append(float(np.mean(gnd_probs_l))) return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)
def rabi_oscillations(sampler: sim.Sampler, qubit: devices.GridQubit, max_angle: float = 2 * np.pi, *, repetitions: int = 1000, num_points: int = 200) -> RabiResult: """Runs a Rabi oscillation experiment. Rotates a qubit around the x-axis of the Bloch sphere by a sequence of Rabi angles evenly spaced between 0 and max_angle. For each rotation, repeat the circuit a number of times and measure the average probability of the qubit being in the |1> state. Args: sampler: The quantum engine or simulator to run the circuits. qubit: The qubit under test. max_angle: The final Rabi angle in radians. repetitions: The number of repetitions of the circuit for each Rabi angle. num_points: The number of Rabi angles. Returns: A RabiResult object that stores and plots the result. """ theta = sympy.Symbol('theta') circuit = circuits.Circuit.from_ops(ops.X(qubit)**theta) circuit.append(ops.measure(qubit, key='z')) sweep = study.Linspace(key='theta', start=0.0, stop=max_angle / np.pi, length=num_points) results = sampler.run_sweep(circuit, params=sweep, repetitions=repetitions) angles = np.linspace(0.0, max_angle, num_points) excited_state_probs = np.zeros(num_points) for i in range(num_points): excited_state_probs[i] = np.mean(results[i].measurements['z']) return RabiResult(angles, excited_state_probs)