def test_wrap_async_func(self): async def async_func(a, b): await duet.completed_future(None) return a + b assert duet.awaitable_func(async_func) is async_func assert duet.run(async_func, 1, 2) == 3
def collect( self, sampler: 'cirq.Sampler', *, concurrency: int = 2, max_total_samples: Optional[int] = None, ) -> None: """Collects needed samples from a sampler. Examples: ``` collector = cirq.PauliStringCollector(...) sampler.collect(collector, concurrency=3) print(collector.estimated_energy()) ``` Args: sampler: The simulator or service to collect samples from. concurrency: Desired number of sampling jobs to have in flight at any given time. max_total_samples: Optional limit on the maximum number of samples to collect. Returns: The collector's result after all desired samples have been collected. """ return duet.run( self.collect_async, sampler, concurrency=concurrency, max_total_samples=max_total_samples, )
def test_wrap_sync_func(self): def sync_func(a, b): return a + b wrapped = duet.awaitable_func(sync_func) assert inspect.iscoroutinefunction(wrapped) assert duet.awaitable_func(wrapped) is wrapped # Don't double-wrap assert duet.run(wrapped, 1, 2) == 3
def test_interrupt_not_included_in_stack_trace(self): async def func(): async with duet.new_scope() as scope: f = duet.AwaitableFuture() scope.spawn(lambda: f) f.set_exception(ValueError("oops!")) await duet.AwaitableFuture() with pytest.raises(ValueError, match="oops!") as exc_info: duet.run(func) stack_trace = "".join( traceback.format_exception(exc_info.type, exc_info.value, exc_info.tb)) assert "Interrupt" not in stack_trace assert isinstance(exc_info.value.__context__, impl.Interrupt) assert exc_info.value.__suppress_context__
def test_failed_future(self): async def func(value): try: await duet.failed_future(Exception()) return value * 2 except Exception: return value * 3 assert duet.run(func, 1) == 3
def test_nested_functions(self): async def func(value): value = await sub_func(value * 2) return value * 3 async def sub_func(value): value = await duet.completed_future(value * 5) return value * 7 assert duet.run(func, 1) == 2 * 3 * 5 * 7
def test_function_returning_none(self): side_effects = [] async def func(value): value = await duet.completed_future(value * 2) value = await duet.completed_future(value * 3) side_effects.append(value) assert duet.run(func, 1) is None assert side_effects == [2 * 3] # make sure func ran to completion
def test_nested_functions_returning_none(self): side_effects = [] async def func(value): value2 = await sub_func(value * 2) return value * 3, value2 async def sub_func(value): value = await duet.completed_future(value * 5) value = await duet.completed_future(value * 7) side_effects.append(value) assert duet.run(func, 1) == (3, None) assert side_effects == [2 * 5 * 7]
def test_failed_nested_generator(self): side_effects = [] async def func(value): try: await sub_func(value * 2) return value * 3 except Exception: return value * 5 async def sub_func(value): await duet.failed_future(Exception()) side_effects.append(value * 7) assert duet.run(func, 1) == 5 assert side_effects == []
def test_function(self): async def func(value): value = await duet.completed_future(value * 2) return value * 3 assert duet.run(func, 1) == 2 * 3
def test_future(self): def func(value): return duet.completed_future(value * 2) assert duet.run(func, 1) == 2
def test_failure_propagates(self, fail_func): with pytest.raises(Fail): duet.run(fail_func)
def direct_fidelity_estimation( circuit: cirq.Circuit, qubits: List[cirq.Qid], sampler: cirq.Sampler, n_measured_operators: Optional[int], samples_per_term: int, ): """Perform a direct fidelity estimation. This implementation of direct fidelity estimation, is that of '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. Raises: TypeError: If the circuit is not made up entirely of Clifford gates. """ # 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_tableau = cirq.CliffordTableau(n_qubits) try: for gate in circuit.all_operations(): tableau_args = clifford.CliffordTableauSimulationState( tableau=clifford_tableau, qubits=qubits ) cirq.act_on(gate, tableau_args) except TypeError: 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: # The stabilizers_basis variable only contains basis vectors. For # example, if we have n=3 qubits, then we should have 2**n=8 Pauli # states that we can sample, but the basis will still have 3 entries. We # must flip a coin for each, whether or not to include them. stabilizer_basis: List[cirq.DensePauliString] = clifford_tableau.stabilizers() pauli_traces = _estimate_pauli_traces_clifford( n_qubits, stabilizer_basis, 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[Result] = [] 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 = duet.run( estimate_characteristic_function, circuit, measure_pauli_string, sampler, samples_per_term, ) else: sigma_i, _ = compute_characteristic_function( measure_pauli_string, qubits, noisy_density_matrix ) trial_results.append(Result(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_tableau=clifford_tableau if clifford_circuit else None, 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