Example #1
0
    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
Example #2
0
    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,
        )
Example #3
0
    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
Example #4
0
    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__
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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]
Example #9
0
    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 == []
Example #10
0
    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
Example #11
0
    def test_future(self):
        def func(value):
            return duet.completed_future(value * 2)

        assert duet.run(func, 1) == 2
Example #12
0
 def test_failure_propagates(self, fail_func):
     with pytest.raises(Fail):
         duet.run(fail_func)
Example #13
0
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