1# Copyright 2018 The Cirq Developers
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Abstract base classes for different types of simulators.
16
17Simulator types include:
18
19    SimulatesSamples: mimics the interface of quantum hardware.
20
21    SimulatesAmplitudes: computes amplitudes of desired bitstrings in the
22        final state of the simulation.
23
24    SimulatesFinalState: allows access to the final state of the simulation.
25
26    SimulatesIntermediateState: allows for access to the state of the simulation
27        as the simulation iterates through the moments of a cirq.
28"""
29
30import abc
31import collections
32from typing import (
33    Any,
34    Dict,
35    Iterator,
36    List,
37    Sequence,
38    Tuple,
39    Union,
40    Optional,
41    TYPE_CHECKING,
42    Set,
43    cast,
44    Callable,
45    TypeVar,
46    Generic,
47)
48
49import numpy as np
50
51from cirq import circuits, ops, protocols, study, value, work
52from cirq.sim.act_on_args import ActOnArgs
53
54if TYPE_CHECKING:
55    import cirq
56
57
58TStepResult = TypeVar('TStepResult', bound='StepResult')
59TSimulationTrialResult = TypeVar('TSimulationTrialResult', bound='SimulationTrialResult')
60TSimulatorState = TypeVar('TSimulatorState')
61TActOnArgs = TypeVar('TActOnArgs', bound=ActOnArgs)
62
63
64class SimulatesSamples(work.Sampler, metaclass=abc.ABCMeta):
65    """Simulator that mimics running on quantum hardware.
66
67    Implementors of this interface should implement the _run method.
68    """
69
70    def run_sweep(
71        self,
72        program: 'cirq.AbstractCircuit',
73        params: study.Sweepable,
74        repetitions: int = 1,
75    ) -> List[study.Result]:
76        return list(self.run_sweep_iter(program, params, repetitions))
77
78    # TODO(#3388) Add documentation for Raises.
79    # pylint: disable=missing-raises-doc
80    def run_sweep_iter(
81        self,
82        program: 'cirq.AbstractCircuit',
83        params: study.Sweepable,
84        repetitions: int = 1,
85    ) -> Iterator[study.Result]:
86        """Runs the supplied Circuit, mimicking quantum hardware.
87
88        In contrast to run, this allows for sweeping over different parameter
89        values.
90
91        Args:
92            program: The circuit to simulate.
93            params: Parameters to run with the program.
94            repetitions: The number of repetitions to simulate.
95
96        Returns:
97            Result list for this run; one for each possible parameter
98            resolver.
99        """
100        if not program.has_measurements():
101            raise ValueError("Circuit has no measurements to sample.")
102
103        _verify_unique_measurement_keys(program)
104
105        for param_resolver in study.to_resolvers(params):
106            measurements = {}
107            if repetitions == 0:
108                for _, op, _ in program.findall_operations_with_gate_type(ops.MeasurementGate):
109                    measurements[protocols.measurement_key_name(op)] = np.empty([0, 1])
110            else:
111                measurements = self._run(
112                    circuit=program, param_resolver=param_resolver, repetitions=repetitions
113                )
114            yield study.Result.from_single_parameter_set(
115                params=param_resolver, measurements=measurements
116            )
117
118    # pylint: enable=missing-raises-doc
119    @abc.abstractmethod
120    def _run(
121        self,
122        circuit: circuits.AbstractCircuit,
123        param_resolver: study.ParamResolver,
124        repetitions: int,
125    ) -> Dict[str, np.ndarray]:
126        """Run a simulation, mimicking quantum hardware.
127
128        Args:
129            circuit: The circuit to simulate.
130            param_resolver: Parameters to run with the program.
131            repetitions: Number of times to repeat the run. It is expected that
132                this is validated greater than zero before calling this method.
133
134        Returns:
135            A dictionary from measurement gate key to measurement
136            results. Measurement results are stored in a 2-dimensional
137            numpy array, the first dimension corresponding to the repetition
138            and the second to the actual boolean measurement results (ordered
139            by the qubits being measured.)
140        """
141        raise NotImplementedError()
142
143
144class SimulatesAmplitudes(metaclass=value.ABCMetaImplementAnyOneOf):
145    """Simulator that computes final amplitudes of given bitstrings.
146
147    Given a circuit and a list of bitstrings, computes the amplitudes
148    of the given bitstrings in the state obtained by applying the circuit
149    to the all zeros state. Implementors of this interface should implement
150    the compute_amplitudes_sweep_iter method.
151    """
152
153    def compute_amplitudes(
154        self,
155        program: 'cirq.AbstractCircuit',
156        bitstrings: Sequence[int],
157        param_resolver: 'study.ParamResolverOrSimilarType' = None,
158        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
159    ) -> Sequence[complex]:
160        """Computes the desired amplitudes.
161
162        The initial state is assumed to be the all zeros state.
163
164        Args:
165            program: The circuit to simulate.
166            bitstrings: The bitstrings whose amplitudes are desired, input
167                as an integer array where each integer is formed from measured
168                qubit values according to `qubit_order` from most to least
169                significant qubit, i.e. in big-endian ordering.
170            param_resolver: Parameters to run with the program.
171            qubit_order: Determines the canonical ordering of the qubits. This
172                is often used in specifying the initial state, i.e. the
173                ordering of the computational basis states.
174
175        Returns:
176            List of amplitudes.
177        """
178        return self.compute_amplitudes_sweep(
179            program, bitstrings, study.ParamResolver(param_resolver), qubit_order
180        )[0]
181
182    def compute_amplitudes_sweep(
183        self,
184        program: 'cirq.AbstractCircuit',
185        bitstrings: Sequence[int],
186        params: study.Sweepable,
187        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
188    ) -> Sequence[Sequence[complex]]:
189        """Wraps computed amplitudes in a list.
190
191        Prefer overriding `compute_amplitudes_sweep_iter`.
192        """
193        return list(self.compute_amplitudes_sweep_iter(program, bitstrings, params, qubit_order))
194
195    def _compute_amplitudes_sweep_to_iter(
196        self,
197        program: 'cirq.AbstractCircuit',
198        bitstrings: Sequence[int],
199        params: study.Sweepable,
200        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
201    ) -> Iterator[Sequence[complex]]:
202        if type(self).compute_amplitudes_sweep == SimulatesAmplitudes.compute_amplitudes_sweep:
203            raise RecursionError(
204                "Must define either compute_amplitudes_sweep or compute_amplitudes_sweep_iter."
205            )
206        yield from self.compute_amplitudes_sweep(program, bitstrings, params, qubit_order)
207
208    @value.alternative(
209        requires='compute_amplitudes_sweep', implementation=_compute_amplitudes_sweep_to_iter
210    )
211    def compute_amplitudes_sweep_iter(
212        self,
213        program: 'cirq.AbstractCircuit',
214        bitstrings: Sequence[int],
215        params: study.Sweepable,
216        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
217    ) -> Iterator[Sequence[complex]]:
218        """Computes the desired amplitudes.
219
220        The initial state is assumed to be the all zeros state.
221
222        Args:
223            program: The circuit to simulate.
224            bitstrings: The bitstrings whose amplitudes are desired, input
225                as an integer array where each integer is formed from measured
226                qubit values according to `qubit_order` from most to least
227                significant qubit, i.e. in big-endian ordering.
228            params: Parameters to run with the program.
229            qubit_order: Determines the canonical ordering of the qubits. This
230                is often used in specifying the initial state, i.e. the
231                ordering of the computational basis states.
232
233        Returns:
234            An Iterator over lists of amplitudes. The outer dimension indexes
235            the circuit parameters and the inner dimension indexes bitstrings.
236        """
237        raise NotImplementedError()
238
239
240class SimulatesExpectationValues(metaclass=value.ABCMetaImplementAnyOneOf):
241    """Simulator that computes exact expectation values of observables.
242
243    Given a circuit and an observable map, computes exact (to float precision)
244    expectation values for each observable at the end of the circuit.
245
246    Implementors of this interface should implement the
247    simulate_expectation_values_sweep_iter method.
248    """
249
250    def simulate_expectation_values(
251        self,
252        program: 'cirq.AbstractCircuit',
253        observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']],
254        param_resolver: 'study.ParamResolverOrSimilarType' = None,
255        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
256        initial_state: Any = None,
257        permit_terminal_measurements: bool = False,
258    ) -> List[float]:
259        """Simulates the supplied circuit and calculates exact expectation
260        values for the given observables on its final state.
261
262        This method has no perfect analogy in hardware. Instead compare with
263        Sampler.sample_expectation_values, which calculates estimated
264        expectation values by sampling multiple times.
265
266        Args:
267            program: The circuit to simulate.
268            observables: An observable or list of observables.
269            param_resolver: Parameters to run with the program.
270            qubit_order: Determines the canonical ordering of the qubits. This
271                is often used in specifying the initial state, i.e. the
272                ordering of the computational basis states.
273            initial_state: The initial state for the simulation. The form of
274                this state depends on the simulation implementation. See
275                documentation of the implementing class for details.
276            permit_terminal_measurements: If the provided circuit ends with
277                measurement(s), this method will generate an error unless this
278                is set to True. This is meant to prevent measurements from
279                ruining expectation value calculations.
280
281        Returns:
282            A list of expectation values, with the value at index `n`
283            corresponding to `observables[n]` from the input.
284
285        Raises:
286            ValueError if 'program' has terminal measurement(s) and
287            'permit_terminal_measurements' is False.
288        """
289        return self.simulate_expectation_values_sweep(
290            program,
291            observables,
292            study.ParamResolver(param_resolver),
293            qubit_order,
294            initial_state,
295            permit_terminal_measurements,
296        )[0]
297
298    def simulate_expectation_values_sweep(
299        self,
300        program: 'cirq.AbstractCircuit',
301        observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']],
302        params: 'study.Sweepable',
303        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
304        initial_state: Any = None,
305        permit_terminal_measurements: bool = False,
306    ) -> List[List[float]]:
307        """Wraps computed expectation values in a list.
308
309        Prefer overriding `simulate_expectation_values_sweep_iter`.
310        """
311        return list(
312            self.simulate_expectation_values_sweep_iter(
313                program,
314                observables,
315                params,
316                qubit_order,
317                initial_state,
318                permit_terminal_measurements,
319            )
320        )
321
322    def _simulate_expectation_values_sweep_to_iter(
323        self,
324        program: 'cirq.AbstractCircuit',
325        observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']],
326        params: 'study.Sweepable',
327        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
328        initial_state: Any = None,
329        permit_terminal_measurements: bool = False,
330    ) -> Iterator[List[float]]:
331        if (
332            type(self).simulate_expectation_values_sweep
333            == SimulatesExpectationValues.simulate_expectation_values_sweep
334        ):
335            raise RecursionError(
336                "Must define either simulate_expectation_values_sweep or "
337                "simulate_expectation_values_sweep_iter."
338            )
339        yield from self.simulate_expectation_values_sweep(
340            program,
341            observables,
342            params,
343            qubit_order,
344            initial_state,
345            permit_terminal_measurements,
346        )
347
348    @value.alternative(
349        requires='simulate_expectation_values_sweep',
350        implementation=_simulate_expectation_values_sweep_to_iter,
351    )
352    def simulate_expectation_values_sweep_iter(
353        self,
354        program: 'cirq.AbstractCircuit',
355        observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']],
356        params: 'study.Sweepable',
357        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
358        initial_state: Any = None,
359        permit_terminal_measurements: bool = False,
360    ) -> Iterator[List[float]]:
361        """Simulates the supplied circuit and calculates exact expectation
362        values for the given observables on its final state, sweeping over the
363        given params.
364
365        This method has no perfect analogy in hardware. Instead compare with
366        Sampler.sample_expectation_values, which calculates estimated
367        expectation values by sampling multiple times.
368
369        Args:
370            program: The circuit to simulate.
371            observables: An observable or list of observables.
372            params: Parameters to run with the program.
373            qubit_order: Determines the canonical ordering of the qubits. This
374                is often used in specifying the initial state, i.e. the
375                ordering of the computational basis states.
376            initial_state: The initial state for the simulation. The form of
377                this state depends on the simulation implementation. See
378                documentation of the implementing class for details.
379            permit_terminal_measurements: If the provided circuit ends in a
380                measurement, this method will generate an error unless this
381                is set to True. This is meant to prevent measurements from
382                ruining expectation value calculations.
383
384        Returns:
385            An Iterator over expectation-value lists. The outer index determines
386            the sweep, and the inner index determines the observable. For
387            instance, results[1][3] would select the fourth observable measured
388            in the second sweep.
389
390        Raises:
391            ValueError if 'program' has terminal measurement(s) and
392            'permit_terminal_measurements' is False.
393        """
394
395
396class SimulatesFinalState(
397    Generic[TSimulationTrialResult], metaclass=value.GenericMetaImplementAnyOneOf
398):
399    """Simulator that allows access to the simulator's final state.
400
401    Implementors of this interface should implement the simulate_sweep_iter
402    method. This simulator only returns the state of the quantum system
403    for the final step of a simulation. This simulator state may be a state
404    vector, the density matrix, or another representation, depending on the
405    implementation.  For simulators that also allow stepping through
406    a circuit see `SimulatesIntermediateState`.
407    """
408
409    def simulate(
410        self,
411        program: 'cirq.AbstractCircuit',
412        param_resolver: 'study.ParamResolverOrSimilarType' = None,
413        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
414        initial_state: Any = None,
415    ) -> TSimulationTrialResult:
416        """Simulates the supplied Circuit.
417
418        This method returns a result which allows access to the entire
419        simulator's final state.
420
421        Args:
422            program: The circuit to simulate.
423            param_resolver: Parameters to run with the program.
424            qubit_order: Determines the canonical ordering of the qubits. This
425                is often used in specifying the initial state, i.e. the
426                ordering of the computational basis states.
427            initial_state: The initial state for the simulation. The form of
428                this state depends on the simulation implementation. See
429                documentation of the implementing class for details.
430
431        Returns:
432            SimulationTrialResults for the simulation. Includes the final state.
433        """
434        return self.simulate_sweep(
435            program, study.ParamResolver(param_resolver), qubit_order, initial_state
436        )[0]
437
438    def simulate_sweep(
439        self,
440        program: 'cirq.AbstractCircuit',
441        params: study.Sweepable,
442        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
443        initial_state: Any = None,
444    ) -> List[TSimulationTrialResult]:
445        """Wraps computed states in a list.
446
447        Prefer overriding `simulate_sweep_iter`.
448        """
449        return list(self.simulate_sweep_iter(program, params, qubit_order, initial_state))
450
451    def _simulate_sweep_to_iter(
452        self,
453        program: 'cirq.AbstractCircuit',
454        params: study.Sweepable,
455        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
456        initial_state: Any = None,
457    ) -> Iterator[TSimulationTrialResult]:
458        if type(self).simulate_sweep == SimulatesFinalState.simulate_sweep:
459            raise RecursionError("Must define either simulate_sweep or simulate_sweep_iter.")
460        yield from self.simulate_sweep(program, params, qubit_order, initial_state)
461
462    @value.alternative(requires='simulate_sweep', implementation=_simulate_sweep_to_iter)
463    def simulate_sweep_iter(
464        self,
465        program: 'cirq.AbstractCircuit',
466        params: study.Sweepable,
467        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
468        initial_state: Any = None,
469    ) -> Iterator[TSimulationTrialResult]:
470        """Simulates the supplied Circuit.
471
472        This method returns a result which allows access to the entire final
473        simulator state. In contrast to simulate, this allows for sweeping
474        over different parameter values.
475
476        Args:
477            program: The circuit to simulate.
478            params: Parameters to run with the program.
479            qubit_order: Determines the canonical ordering of the qubits. This
480                is often used in specifying the initial state, i.e. the
481                ordering of the computational basis states.
482            initial_state: The initial state for the simulation. The form of
483                this state depends on the simulation implementation. See
484                documentation of the implementing class for details.
485
486        Returns:
487            Iterator over SimulationTrialResults for this run, one for each
488            possible parameter resolver.
489        """
490        raise NotImplementedError()
491
492
493class SimulatesIntermediateState(
494    Generic[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs],
495    SimulatesFinalState[TSimulationTrialResult],
496    metaclass=abc.ABCMeta,
497):
498    """A SimulatesFinalState that simulates a circuit by moments.
499
500    Whereas a general SimulatesFinalState may return the entire simulator
501    state at the end of a circuit, a SimulatesIntermediateState can
502    simulate stepping through the moments of a circuit.
503
504    Implementors of this interface should implement the _core_iterator
505    method.
506
507    Note that state here refers to simulator state, which is not necessarily
508    a state vector.
509    """
510
511    def simulate_sweep_iter(
512        self,
513        program: 'cirq.AbstractCircuit',
514        params: study.Sweepable,
515        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
516        initial_state: Any = None,
517    ) -> Iterator[TSimulationTrialResult]:
518        """Simulates the supplied Circuit.
519
520        This method returns a result which allows access to the entire
521        state vector. In contrast to simulate, this allows for sweeping
522        over different parameter values.
523
524        Args:
525            program: The circuit to simulate.
526            params: Parameters to run with the program.
527            qubit_order: Determines the canonical ordering of the qubits. This
528                is often used in specifying the initial state, i.e. the
529                ordering of the computational basis states.
530            initial_state: The initial state for the simulation. This can be
531                either a raw state or an `OperationTarget`. The form of the
532                raw state depends on the simulation implementation. See
533                documentation of the implementing class for details.
534
535        Returns:
536            List of SimulationTrialResults for this run, one for each
537            possible parameter resolver.
538        """
539        qubit_order = ops.QubitOrder.as_qubit_order(qubit_order)
540        for param_resolver in study.to_resolvers(params):
541            all_step_results = self.simulate_moment_steps(
542                program, param_resolver, qubit_order, initial_state
543            )
544            measurements = {}  # type: Dict[str, np.ndarray]
545            for step_result in all_step_results:
546                for k, v in step_result.measurements.items():
547                    measurements[k] = np.array(v, dtype=np.uint8)
548            yield self._create_simulator_trial_result(
549                params=param_resolver,
550                measurements=measurements,
551                final_step_result=step_result,
552            )
553
554    def simulate_moment_steps(
555        self,
556        circuit: circuits.AbstractCircuit,
557        param_resolver: 'study.ParamResolverOrSimilarType' = None,
558        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
559        initial_state: Any = None,
560    ) -> Iterator[TStepResult]:
561        """Returns an iterator of StepResults for each moment simulated.
562
563        If the circuit being simulated is empty, a single step result should
564        be returned with the state being set to the initial state.
565
566        Args:
567            circuit: The Circuit to simulate.
568            param_resolver: A ParamResolver for determining values of Symbols.
569            qubit_order: Determines the canonical ordering of the qubits. This
570                is often used in specifying the initial state, i.e. the
571                ordering of the computational basis states.
572            initial_state: The initial state for the simulation. This can be
573                either a raw state or a `TActOnArgs`. The form of the
574                raw state depends on the simulation implementation. See
575                documentation of the implementing class for details.
576
577        Returns:
578            Iterator that steps through the simulation, simulating each
579            moment and returning a StepResult for each moment.
580        """
581        param_resolver = study.ParamResolver(param_resolver)
582        resolved_circuit = protocols.resolve_parameters(circuit, param_resolver)
583        check_all_resolved(resolved_circuit)
584        actual_initial_state = 0 if initial_state is None else initial_state
585        return self._base_iterator(resolved_circuit, qubit_order, actual_initial_state)
586
587    def _base_iterator(
588        self,
589        circuit: circuits.AbstractCircuit,
590        qubit_order: ops.QubitOrderOrList,
591        initial_state: Any,
592    ) -> Iterator[TStepResult]:
593        """Iterator over StepResult from Moments of a Circuit.
594
595        This is a thin wrapper around `create_act_on_args` and `_core_iterator`.
596        Overriding this method was the old way of creating a circuit iterator,
597        and this method is planned to be formally put on the deprecation path.
598        Going forward, override the aforementioned two methods in custom
599        simulators.
600
601        Args:
602            circuit: The circuit to simulate.
603            qubit_order: Determines the canonical ordering of the qubits. This
604                is often used in specifying the initial state, i.e. the
605                ordering of the computational basis states.
606            initial_state: The initial state for the simulation. The form of
607                this state depends on the simulation implementation. See
608                documentation of the implementing class for details.
609
610        Yields:
611            StepResults from simulating a Moment of the Circuit.
612        """
613        qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits())
614        act_on_args = self._create_act_on_args(initial_state, qubits)
615        return self._core_iterator(circuit, act_on_args)
616
617    @abc.abstractmethod
618    def _create_act_on_args(
619        self,
620        initial_state: Any,
621        qubits: Sequence['cirq.Qid'],
622    ) -> 'cirq.OperationTarget[TActOnArgs]':
623        """Creates the OperationTarget state for a simulator.
624
625        Custom simulators should implement this method.
626
627        Args:
628            initial_state: The initial state for the simulation. The form of
629                this state depends on the simulation implementation. See
630                documentation of the implementing class for details.
631            qubits: Determines the canonical ordering of the qubits. This
632                is often used in specifying the initial state, i.e. the
633                ordering of the computational basis states.
634
635        Returns:
636            The `OperationTarget` for this simulator.
637        """
638
639    # TODO(#3388) Add documentation for Args.
640    # pylint: disable=missing-param-doc
641    @abc.abstractmethod
642    def _core_iterator(
643        self,
644        circuit: circuits.AbstractCircuit,
645        sim_state: 'cirq.OperationTarget[TActOnArgs]',
646        all_measurements_are_terminal: bool = False,
647    ) -> Iterator[TStepResult]:
648        """Iterator over StepResult from Moments of a Circuit.
649
650        Custom simulators should implement this method.
651
652        Args:
653            circuit: The circuit to simulate.
654            sim_state: The initial args for the simulation. The form of
655                this state depends on the simulation implementation. See
656                documentation of the implementing class for details.
657
658        Yields:
659            StepResults from simulating a Moment of the Circuit.
660        """
661
662    # pylint: enable=missing-param-doc
663    @abc.abstractmethod
664    def _create_simulator_trial_result(
665        self,
666        params: study.ParamResolver,
667        measurements: Dict[str, np.ndarray],
668        final_step_result: TStepResult,
669    ) -> TSimulationTrialResult:
670        """This method can be implemented to create a trial result.
671
672        Args:
673            params: The ParamResolver for this trial.
674            measurements: The measurement results for this trial.
675            final_step_result: The final step result of the simulation.
676
677        Returns:
678            The SimulationTrialResult.
679        """
680        raise NotImplementedError()
681
682
683class StepResult(Generic[TSimulatorState], metaclass=abc.ABCMeta):
684    """Results of a step of a SimulatesIntermediateState.
685
686    Attributes:
687        measurements: A dictionary from measurement gate key to measurement
688            results, ordered by the qubits that the measurement operates on.
689    """
690
691    def __init__(self, measurements: Optional[Dict[str, List[int]]] = None) -> None:
692        self.measurements = measurements or collections.defaultdict(list)
693
694    @abc.abstractmethod
695    def _simulator_state(self) -> TSimulatorState:
696        """Returns the simulator state of the simulator after this step.
697
698        This method starts with an underscore to indicate that it is private.
699        To access public state, see public methods on StepResult.
700
701        The form of the simulator_state depends on the implementation of the
702        simulation,see documentation for the implementing class for the form of
703        details.
704        """
705
706    @abc.abstractmethod
707    def sample(
708        self,
709        qubits: List[ops.Qid],
710        repetitions: int = 1,
711        seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
712    ) -> np.ndarray:
713        """Samples from the system at this point in the computation.
714
715        Note that this does not collapse the state vector.
716
717        Args:
718            qubits: The qubits to be sampled in an order that influence the
719                returned measurement results.
720            repetitions: The number of samples to take.
721            seed: A seed for the pseudorandom number generator.
722
723        Returns:
724            Measurement results with True corresponding to the ``|1⟩`` state.
725            The outer list is for repetitions, and the inner corresponds to
726            measurements ordered by the supplied qubits. These lists
727            are wrapped as a numpy ndarray.
728        """
729        raise NotImplementedError()
730
731    def sample_measurement_ops(
732        self,
733        measurement_ops: List[ops.GateOperation],
734        repetitions: int = 1,
735        seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
736    ) -> Dict[str, np.ndarray]:
737        """Samples from the system at this point in the computation.
738
739        Note that this does not collapse the state vector.
740
741        In contrast to `sample` which samples qubits, this takes a list of
742        `cirq.GateOperation` instances whose gates are `cirq.MeasurementGate`
743        instances and then returns a mapping from the key in the measurement
744        gate to the resulting bit strings. Different measurement operations must
745        not act on the same qubits.
746
747        Args:
748            measurement_ops: `GateOperation` instances whose gates are
749                `MeasurementGate` instances to be sampled form.
750            repetitions: The number of samples to take.
751            seed: A seed for the pseudorandom number generator.
752
753        Returns: A dictionary from measurement gate key to measurement
754            results. Measurement results are stored in a 2-dimensional
755            numpy array, the first dimension corresponding to the repetition
756            and the second to the actual boolean measurement results (ordered
757            by the qubits being measured.)
758
759        Raises:
760            ValueError: If the operation's gates are not `MeasurementGate`
761                instances or a qubit is acted upon multiple times by different
762                operations from `measurement_ops`.
763        """
764
765        # Sanity checks.
766        seen_measurement_keys: Set[str] = set()
767        for op in measurement_ops:
768            gate = op.gate
769            if not isinstance(gate, ops.MeasurementGate):
770                raise ValueError(f'{op.gate} was not a MeasurementGate')
771            key = protocols.measurement_key_name(gate)
772            if key in seen_measurement_keys:
773                raise ValueError(f'Duplicate MeasurementGate with key {key}')
774            seen_measurement_keys.add(key)
775
776        # Find measured qubits, ensuring a consistent ordering.
777        measured_qubits = []
778        seen_qubits: Set[cirq.Qid] = set()
779        for op in measurement_ops:
780            for q in op.qubits:
781                if q not in seen_qubits:
782                    seen_qubits.add(q)
783                    measured_qubits.append(q)
784
785        # Perform whole-system sampling of the measured qubits.
786        indexed_sample = self.sample(measured_qubits, repetitions, seed=seed)
787
788        # Extract results for each measurement.
789        results: Dict[str, np.ndarray] = {}
790        qubits_to_index = {q: i for i, q in enumerate(measured_qubits)}
791        for op in measurement_ops:
792            gate = cast(ops.MeasurementGate, op.gate)
793            out = np.zeros(shape=(repetitions, len(op.qubits)), dtype=np.int8)
794            inv_mask = gate.full_invert_mask()
795            for i, q in enumerate(op.qubits):
796                out[:, i] = indexed_sample[:, qubits_to_index[q]]
797                if inv_mask[i]:
798                    out[:, i] ^= out[:, i] < 2
799            results[gate.key] = out
800
801        return results
802
803
804@value.value_equality(unhashable=True)
805class SimulationTrialResult:
806    """Results of a simulation by a SimulatesFinalState.
807
808    Unlike Result these results contain the final simulator_state of the
809    system. This simulator_state is dependent on the simulation implementation
810    and may be, for example, the state vector or the density matrix of the
811    system.
812
813    Attributes:
814        params: A ParamResolver of settings used for this result.
815        measurements: A dictionary from measurement gate key to measurement
816            results. Measurement results are a numpy ndarray of actual boolean
817            measurement results (ordered by the qubits acted on by the
818            measurement gate.)
819    """
820
821    # TODO(#3388) Add documentation for Raises.
822    # pylint: disable=missing-raises-doc
823    def __init__(
824        self,
825        params: study.ParamResolver,
826        measurements: Dict[str, np.ndarray],
827        final_simulator_state: Any = None,
828        final_step_result: StepResult = None,
829    ) -> None:
830        """Initializes the `SimulationTrialResult` class.
831
832        Args:
833            params: A ParamResolver of settings used for this result.
834            measurements: A dictionary from measurement gate key to measurement
835                results. Measurement results are a numpy ndarray of actual
836                boolean measurement results (ordered by the qubits acted on by
837                the measurement gate.)
838            final_simulator_state: The final simulator state.
839            final_step_result: The step result coming from the simulation, that
840                can be used to get the final simulator state. This is primarily
841                for cases when calculating simulator state may be expensive and
842                unneeded. If this is provided, then final_simulator_state
843                should not be, and vice versa.
844        """
845        if [final_step_result, final_simulator_state].count(None) != 1:
846            raise ValueError(
847                'Exactly one of final_simulator_state and final_step_result should be provided'
848            )
849        self.params = params
850        self.measurements = measurements
851        self._final_step_result = final_step_result
852        self._final_simulator_state_cache = final_simulator_state
853
854    # pylint: enable=missing-raises-doc
855    @property
856    def _final_simulator_state(self):
857        if self._final_simulator_state_cache is None:
858            self._final_simulator_state_cache = self._final_step_result._simulator_state()
859        return self._final_simulator_state_cache
860
861    def __repr__(self) -> str:
862        return (
863            f'cirq.SimulationTrialResult(params={self.params!r}, '
864            f'measurements={self.measurements!r}, '
865            f'final_simulator_state={self._final_simulator_state!r})'
866        )
867
868    def __str__(self) -> str:
869        def bitstring(vals):
870            separator = ' ' if np.max(vals) >= 10 else ''
871            return separator.join(str(int(v)) for v in vals)
872
873        results = sorted([(key, bitstring(val)) for key, val in self.measurements.items()])
874        if not results:
875            return '(no measurements)'
876        return ' '.join([f'{key}={val}' for key, val in results])
877
878    def _repr_pretty_(self, p: Any, cycle: bool) -> None:
879        """Text output in Jupyter."""
880        if cycle:
881            # There should never be a cycle.  This is just in case.
882            p.text('SimulationTrialResult(...)')
883        else:
884            p.text(str(self))
885
886    def _value_equality_values_(self) -> Any:
887        measurements = {k: v.tolist() for k, v in sorted(self.measurements.items())}
888        return (self.params, measurements, self._final_simulator_state)
889
890    @property
891    def qubit_map(self) -> Dict[ops.Qid, int]:
892        """A map from Qid to index used to define the ordering of the basis in
893        the result.
894        """
895        return self._final_simulator_state.qubit_map
896
897    def _qid_shape_(self) -> Tuple[int, ...]:
898        return _qubit_map_to_shape(self.qubit_map)
899
900
901def _qubit_map_to_shape(qubit_map: Dict[ops.Qid, int]) -> Tuple[int, ...]:
902    qid_shape: List[int] = [-1] * len(qubit_map)
903    try:
904        for q, i in qubit_map.items():
905            qid_shape[i] = q.dimension
906    except IndexError:
907        raise ValueError(f'Invalid qubit_map. Qubit index out of bounds. Map is <{qubit_map!r}>.')
908    if -1 in qid_shape:
909        raise ValueError(f'Invalid qubit_map. Duplicate qubit index. Map is <{qubit_map!r}>.')
910    return tuple(qid_shape)
911
912
913def _verify_unique_measurement_keys(circuit: circuits.AbstractCircuit):
914    result = collections.Counter(
915        key
916        for op in ops.flatten_op_tree(iter(circuit))
917        for key in protocols.measurement_key_names(op)
918    )
919    if result:
920        duplicates = [k for k, v in result.most_common() if v > 1]
921        if duplicates:
922            raise ValueError(f"Measurement key {','.join(duplicates)} repeated")
923
924
925def check_all_resolved(circuit):
926    """Raises if the circuit contains unresolved symbols."""
927    if protocols.is_parameterized(circuit):
928        unresolved = [op for moment in circuit for op in moment if protocols.is_parameterized(op)]
929        raise ValueError(
930            'Circuit contains ops whose symbols were not specified in '
931            'parameter sweep. Ops: {}'.format(unresolved)
932        )
933
934
935def split_into_matching_protocol_then_general(
936    circuit: 'cirq.AbstractCircuit',
937    predicate: Callable[['cirq.Operation'], bool],
938) -> Tuple['cirq.AbstractCircuit', 'cirq.AbstractCircuit']:
939    """Splits the circuit into a matching prefix and non-matching suffix.
940
941    The splitting happens in a per-qubit fashion. A non-matching operation on
942    qubit A will cause later operations on A to be part of the non-matching
943    suffix, but later operations on other qubits will continue to be put into
944    the matching part (as long as those qubits have had no non-matching operation
945    up to that point).
946    """
947    blocked_qubits: Set[cirq.Qid] = set()
948    matching_prefix = circuits.Circuit()
949    general_suffix = circuits.Circuit()
950    for moment in circuit:
951        matching_part = []
952        general_part = []
953        for op in moment:
954            qs = set(op.qubits)
955            if not predicate(op) or not qs.isdisjoint(blocked_qubits):
956                blocked_qubits |= qs
957
958            if qs.isdisjoint(blocked_qubits):
959                matching_part.append(op)
960            else:
961                general_part.append(op)
962        if matching_part:
963            matching_prefix.append(ops.Moment(matching_part))
964        if general_part:
965            general_suffix.append(ops.Moment(general_part))
966    return matching_prefix, general_suffix
967