def test_execute_with_zne_transpiled_qiskit_circuit(): """Tests ZNE when transpiling to a Qiskit device. Note transpiling can introduce idle (unused) qubits to the circuit. """ from qiskit.test.mock import FakeSantiago santiago = FakeSantiago() backend = qiskit.providers.aer.AerSimulator.from_backend(santiago) def execute(circuit: qiskit.QuantumCircuit, shots: int = 8192) -> float: job = qiskit.execute(circuit, backend, shots=shots) return job.result().get_counts().get("00", 0.0) / shots qreg = qiskit.QuantumRegister(2) creg = qiskit.ClassicalRegister(2) circuit = qiskit.QuantumCircuit(qreg, creg) for _ in range(10): circuit.x(qreg) circuit.measure(qreg, creg) circuit = qiskit.transpile(circuit, backend, optimization_level=0) true_value = 1.0 zne_value = execute_with_zne(circuit, execute) # Note: Unmitigated value is also (usually) within 10% of the true value. # This is more to test usage than effectiveness. assert abs(zne_value - true_value) < 0.1
def test_execute_with_zne(): """Tests a random identity circuit execution with zero-noise extrapolation. """ rand_circ = random_one_qubit_identity_circuit(num_cliffords=TEST_DEPTH) qp = measure(rand_circ, qid=0) result = execute_with_zne(qp, basic_executor, scale_noise=scale_noise) assert np.isclose(result, 1.0, atol=1.0e-1)
def track_zne( circuit_type: str, nqubits: int, depth: int, observable: Observable, ) -> float: """Returns the ZNE error mitigation factor, i.e., the ratio (error without ZNE) / (error with ZNE). Args: circuit_type: Type of benchmark circuit. nqubits: Number of qubits in the benchmark circuit. depth: Some proxy of depth in the benchmark circuit. observable: Observable to compute the expectation value of. """ circuit = get_benchmark_circuit(circuit_type, nqubits, depth) true_value = raw.execute( circuit, compute_density_matrix_noiseless, observable ) raw_value = raw.execute( circuit, mitiq_cirq.compute_density_matrix, observable ) zne_value = zne.execute_with_zne( circuit, mitiq_cirq.compute_density_matrix, observable, ) return np.real(abs(true_value - raw_value) / abs(true_value - zne_value))
def test_execute_with_zne(): true_zne_value = 1.0 circuit = measure( random_one_qubit_identity_circuit(num_cliffords=TEST_DEPTH), 0) base = qiskit_executor(circuit) zne_value = zne.execute_with_zne(circuit, qiskit_executor) assert abs(true_zne_value - zne_value) < abs(true_zne_value - base)
def test_execute_with_zne(): (qp, ) = benchmarks.generate_rb_circuits( n_qubits=1, num_cliffords=TEST_DEPTH, trials=1, return_type="pyquil", ) result = zne.execute_with_zne(qp, noiseless_executor) assert np.isclose(result, 1.0, atol=1e-5)
def test_execute_with_zne_no_noise(fold_method, factory, num_to_average): """Tests execute_with_zne with noiseless simulation.""" zne_value = execute_with_zne( circ, executor, num_to_average=num_to_average, scale_noise=fold_method, factory=factory([1.0, 2.0, 3.0]), ) assert np.isclose(zne_value, 0.0)
def qaoa_cost(params: np.ndarray) -> float: qaoa_prog = qaoa_ansatz(params) if scale_noise is None and factory is None: return noisy_backend(qaoa_prog) else: assert scale_noise is not None return execute_with_zne( qaoa_prog, executor=noisy_backend, scale_noise=scale_noise, factory=factory, )
def test_qiskit_execute_with_zne(): true_zne_value = 1.0 circuit = qiskit_measure( *generate_rb_circuits( n_qubits=1, num_cliffords=TEST_DEPTH, trials=1, return_type="qiskit", ), 0, ) base = qiskit_executor(circuit) zne_value = execute_with_zne(circuit, qiskit_executor) assert abs(true_zne_value - zne_value) < abs(true_zne_value - base)
def test_with_observable_batched_factory(executor): observable = Observable(PauliString(spec="Z")) circuit = cirq.Circuit(cirq.H.on(cirq.LineQubit(0))) * 20 noisy_value = observable.expectation(circuit, sample_bitstrings) zne_value = execute_with_zne( circuit, executor=functools.partial(executor, noise_model=cirq.depolarize), observable=observable, factory=PolyFactory(scale_factors=[1, 3, 5], order=2), ) true_value = observable.expectation( circuit, functools.partial(compute_density_matrix, noise_level=(0, ))) assert abs(zne_value - true_value) <= abs(noisy_value - true_value)
def test_with_observable_adaptive_factory(executor): observable = Observable(PauliString(spec="Z")) circuit = cirq.Circuit(cirq.H.on(cirq.LineQubit(0))) * 20 noisy_value = observable.expectation(circuit, sample_bitstrings) zne_value = execute_with_zne( circuit, executor=functools.partial(executor, noise_model=cirq.amplitude_damp), observable=observable, factory=AdaExpFactory(steps=4, asymptote=0.5), ) true_value = observable.expectation( circuit, functools.partial(compute_density_matrix, noise_level=(0, ))) assert abs(zne_value - true_value) <= abs(noisy_value - true_value)
def test_execute_with_zne_bad_arguments(): """Tests errors are raised when execute_with_zne is called with bad args. """ with pytest.raises(TypeError, match="Argument `executor` must be callable"): execute_with_zne(circ, None) with pytest.raises(TypeError, match="Argument `factory` must be of type"): execute_with_zne(circ, executor, factory=RichardsonFactory) with pytest.raises(TypeError, match="Argument `scale_noise` must be"): execute_with_zne(circ, executor, scale_noise=None)
def test_execute_with_zne_with_supported_circuits(circuit_type): # Define a circuit equivalent to the identity qreg = cirq.LineQubit.range(2) cirq_circuit = cirq.Circuit( cirq.H.on_each(qreg), cirq.CNOT(*qreg), cirq.CNOT(*qreg), cirq.H.on_each(qreg), ) # Convert to one of the supported program types circuit = convert_from_mitiq(cirq_circuit, circuit_type) expected = generic_executor(circuit, noise_level=0.0) unmitigated = generic_executor(circuit) # Use odd scale factors for deterministic results fac = RichardsonFactory([1.0, 3.0, 5.0]) zne_value = execute_with_zne(circuit, generic_executor, factory=fac) # Test zero noise limit is better than unmitigated expectation value assert abs(unmitigated - expected) > abs(zne_value - expected)
def test_with_observable_two_qubits(executor): observable = Observable(PauliString(spec="XX", coeff=-1.21), PauliString(spec="ZZ", coeff=0.7)) circuit = cirq.Circuit(cirq.H.on(cirq.LineQubit(0)), cirq.CNOT.on(*cirq.LineQubit.range(2))) circuit += [circuit, cirq.inverse(circuit)] * 20 noisy_value = observable.expectation(circuit, sample_bitstrings) zne_value = execute_with_zne( circuit, executor=functools.partial(executor, noise_model=cirq.depolarize), observable=observable, factory=PolyFactory(scale_factors=[1, 3, 5], order=2), ) true_value = observable.expectation( circuit, functools.partial(compute_density_matrix, noise_level=(0, ))) assert abs(zne_value - true_value) <= 3 * abs(noisy_value - true_value)
def rand_circuit_zne( n_qubits: int, depth: int, trials: int, noise: float, fac: Optional[Factory] = None, scale_noise: Callable[[QPROGRAM, float], QPROGRAM] = fold_gates_at_random, op_density: float = 0.99, silent: bool = True, seed: Optional[int] = None, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """Benchmarks a zero-noise extrapolation method and noise scaling executor by running on randomly sampled quantum circuits. Args: n_qubits: The number of qubits. depth: The depth in moments of the random circuits. trials: The number of random circuits to average over. noise: The noise level of the depolarizing channel for simulation. fac: The Factory giving the extrapolation method. scale_noise: The method for scaling noise, e.g. fold_gates_at_random op_density: The expected proportion of qubits that are acted on in any moment. silent: If False will print out statements every tenth trial to track progress. seed: Optional seed for random number generator. Returns: The triple (exacts, unmitigateds, mitigateds) where each is a list whose values are the expectations of that trial in noiseless, noisy, and error-mitigated runs respectively. """ exacts = [] unmitigateds = [] mitigateds = [] qubits = [NamedQubit(str(xx)) for xx in range(n_qubits)] if seed: rnd_state = np.random.RandomState(seed) else: rnd_state = None for ii in range(trials): if not silent and ii % 10 == 0: print(ii) qc = random_circuit( qubits, n_moments=depth, op_density=op_density, random_state=rnd_state, ) wvf = qc.final_state_vector() # calculate the exact obs = sample_projector(n_qubits, seed=rnd_state) exact = np.conj(wvf).T @ obs @ wvf # make sure it is real exact = np.real_if_close(exact) assert np.isreal(exact) # create the simulation type def obs_sim(circ: Circuit) -> float: # we only want the expectation value not the variance # this is why we return [0] return noisy_simulation(circ, noise, obs) # evaluate the noisy answer unmitigated = obs_sim(qc) # evaluate the ZNE answer mitigated = execute_with_zne(qp=qc, executor=obs_sim, scale_noise=scale_noise, factory=fac) exacts.append(exact) unmitigateds.append(unmitigated) mitigateds.append(mitigated) return np.asarray(exacts), np.asarray(unmitigateds), np.asarray(mitigateds)
def test_execute_with_zne(): qp = random_one_qubit_identity_circuit(num_cliffords=TEST_DEPTH) result = execute_with_zne(qp, noiseless_executor) assert np.isclose(result, 1.0, atol=1e-5)
def test_execute_with_zne(): rand_circ = random_identity_circuit(depth=TEST_DEPTH) qp = measure(rand_circ, qid=0) result = execute_with_zne(qp, basic_executor, None, scale_noise) assert np.isclose(result, 1.0, atol=1.0e-1)