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