예제 #1
0
def test_measure_observable_bad_grouper():
    circuit = cirq.Circuit(cirq.X(Q)**0.2)
    observables = [cirq.Z(Q), cirq.Z(cirq.NamedQubit('q2'))]
    with pytest.raises(ValueError, match=r'Unknown grouping function'):
        _ = measure_observables(
            circuit,
            observables,
            cirq.Simulator(seed=52),
            stopping_criteria=RepetitionsStoppingCriteria(50_000),
            grouper='super fancy grouper',
        )
예제 #2
0
def test_measure_observable_grouper(grouper):
    circuit = cirq.Circuit(cirq.X(Q)**0.2)
    observables = [cirq.Z(Q), cirq.Z(cirq.NamedQubit('q2'))]
    results = measure_observables(
        circuit,
        observables,
        cirq.Simulator(seed=52),
        stopping_criteria=RepetitionsStoppingCriteria(50_000),
        grouper=grouper,
    )
    assert len(results) == 2, 'two observables'
    np.testing.assert_allclose(0.8, results[0].mean, atol=0.05)
    np.testing.assert_allclose(1, results[1].mean, atol=1e-9)
예제 #3
0
    def sample_expectation_values(
        self,
        program: 'cirq.AbstractCircuit',
        observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']],
        *,
        num_samples: int,
        params: 'cirq.Sweepable' = None,
        permit_terminal_measurements: bool = False,
    ) -> Sequence[Sequence[float]]:
        """Calculates estimated expectation values from samples of a circuit.

        Please see also `cirq.work.observable_measurement.measure_observables`
        for more control over how to measure a suite of observables.

        This method can be run on any device or simulator that supports circuit sampling. Compare
        with `simulate_expectation_values` in simulator.py, which is limited to simulators
        but provides exact results.

        Args:
            program: The circuit which prepares a state from which we sample expectation values.
            observables: A list of observables for which to calculate expectation values.
            num_samples: The number of samples to take. Increasing this value increases the
                statistical accuracy of the estimate.
            params: Parameters to run with the program.
            permit_terminal_measurements: If the provided circuit ends in a measurement, this
                method will generate an error unless this is set to True. This is meant to
                prevent measurements from ruining expectation value calculations.

        Returns:
            A list of expectation-value lists. The outer index determines the sweep, and the inner
            index determines the observable. For instance, results[1][3] would select the fourth
            observable measured in the second sweep.

        Raises:
            ValueError: If the number of samples was not positive, if empty observables were
                supplied, or if the provided circuit has terminal measurements and
                `permit_terminal_measurements` is true.
        """
        if num_samples <= 0:
            raise ValueError(
                f'Expectation values require at least one sample. Received: {num_samples}.'
            )
        if not observables:
            raise ValueError('At least one observable must be provided.')
        if not permit_terminal_measurements and program.are_any_measurements_terminal(
        ):
            raise ValueError(
                'Provided circuit has terminal measurements, which may '
                'skew expectation values. If this is intentional, set '
                'permit_terminal_measurements=True.')

        # Wrap input into a list of pauli sum
        pauli_sums: List['cirq.PauliSum'] = (
            [ops.PauliSum.wrap(o) for o in observables] if isinstance(
                observables, List) else [ops.PauliSum.wrap(observables)])
        del observables

        # Flatten Pauli Sum into one big list of Pauli String
        # Keep track of which Pauli Sum each one was from.
        flat_pstrings: List['cirq.PauliString'] = []
        pstring_to_psum_i: Dict['cirq.PauliString', int] = {}
        for psum_i, pauli_sum in enumerate(pauli_sums):
            for pstring in pauli_sum:
                flat_pstrings.append(pstring)
                pstring_to_psum_i[pstring] = psum_i

        # Flatten Circuit Sweep into one big list of Params.
        # Keep track of their indices so we can map back.
        flat_params: List['cirq.ParamDictType'] = [
            pr.param_dict for pr in study.to_resolvers(params)
        ]
        circuit_param_to_sweep_i: Dict[FrozenSet[Tuple[str, Union[int, Tuple[
            int, int]]]], int] = {
                _hashable_param(param.items()): i
                for i, param in enumerate(flat_params)
            }

        obs_meas_results = measure_observables(
            circuit=program,
            observables=flat_pstrings,
            sampler=self,
            stopping_criteria=RepetitionsStoppingCriteria(
                total_repetitions=num_samples),
            readout_symmetrization=False,
            circuit_sweep=params,
            checkpoint=CheckpointFileOptions(checkpoint=False),
        )

        # Results are ordered by how they're grouped. Since we want the (circuit_sweep, pauli_sum)
        # nesting structure, we place the measured values according to the back-mappings we set up
        # above. We also do the sum operation to aggregate multiple PauliString measured values
        # for a given PauliSum.
        nested_results: List[List[float]] = [[0] * len(pauli_sums)
                                             for _ in range(len(flat_params))]
        for res in obs_meas_results:
            param_i = circuit_param_to_sweep_i[_hashable_param(
                res.circuit_params.items())]
            psum_i = pstring_to_psum_i[res.setting.observable]
            nested_results[param_i][psum_i] += res.mean

        return nested_results