def run_sweep( self, program: 'cirq.Circuit', params: 'cirq.Sweepable', repetitions: int = 1, ) -> List['cirq.Result']: assert isinstance(program, circuits.Circuit) meas = list(program.findall_operations_with_gate_type( ops.MeasurementGate)) if len(meas) == 0: raise ValueError() elif len(meas) > 1: for _, m, _ in meas: assert len(m.qubits) == 1 results = [ study.Result( params=p, measurements={gate.key: np.zeros( (repetitions, 1), dtype=int) for _, _, gate in meas}) for p in study.to_resolvers(params) ] else: assert len(meas) == 1 i, op, gate = meas[0] n_qubits = len(op.qubits) k = gate.key results = [ study.Result( params=p, measurements={k: np.zeros( (repetitions, n_qubits), dtype=int)}) for p in study.to_resolvers(params) ] return results
def run_sweep( self, program: 'cirq.Circuit', params: study.Sweepable, repetitions: int = 1, ) -> List[study.TrialResult]: """Samples circuit as if every measurement resulted in zero. Args: program: The circuit to sample from. params: Parameters to run with the program. repetitions: The number of times to sample. Returns: TrialResult list for this run; one for each possible parameter resolver. """ if self.gate_set is not None: for op in program.all_operations(): assert self.gate_set.is_supported_operation(op), ( "Unsupported operation: %s" % op) measurements = {} # type: Dict[str, np.ndarray] for op in program.all_operations(): key = protocols.measurement_key(op, default=None) if key is not None: measurements[key] = np.zeros((repetitions, len(op.qubits)), dtype=np.int8) return [ study.TrialResult.from_single_parameter_set( params=param_resolver, measurements=measurements) for param_resolver in study.to_resolvers(params) ]
def compute_amplitudes_sweep( self, program: circuits.Circuit, bitstrings: Sequence[int], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Sequence[Sequence[complex]]: if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) n_qubits = len(program.all_qubits()) # qsim numbers qubits in reverse order from cirq bitstrings = [ format(bitstring, "b").zfill(n_qubits)[::-1] for bitstring in bitstrings ] options = {"i": "\n".join(bitstrings)} options.update(self.qsimh_options) param_resolvers = study.to_resolvers(params) trials_results = [] for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) options["c"] = solved_circuit.translate_cirq_to_qsim(qubit_order) options.update(self.qsimh_options) amplitudes = qsim.qsimh_simulate(options) trials_results.append(amplitudes) return trials_results
def compute_displays_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: Optional[study.Sweepable] = None, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Union[int, np.ndarray] = 0, ) -> List[study.ComputeDisplaysResult]: """Computes displays in the supplied Circuit or Schedule. In contrast to `compute_displays`, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits used to define the order of amplitudes in the wave function. initial_state: If an int, the state is set to the computational basis state corresponding to this state. Otherwise if it is a np.ndarray it is the full initial state, either a pure state or the full density matrix. If it is the pure state it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. If it is a mixed state it must be correctly sized and positive semidefinite with trace one. Returns: List of ComputeDisplaysResults for this run, one for each possible parameter resolver. """ circuit = (program.to_circuit() if isinstance(program, schedules.Schedule) else program) qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) qubits = qubit_order.order_for(circuit.all_qubits()) compute_displays_results: List[study.ComputeDisplaysResult] = [] for param_resolver in study.to_resolvers(params): display_values: Dict[Hashable, Any] = {} # Compute the displays in the first Moment moment = circuit[0] matrix = density_matrix_utils.to_valid_density_matrix( initial_state, num_qubits=len(qubits), dtype=self._dtype) qubit_map = {q: i for i, q in enumerate(qubits)} _enter_moment_display_values_into_dictionary( display_values, moment, matrix, qubits, qubit_map) # Compute the displays in the rest of the Moments all_step_results = self.simulate_moment_steps( circuit, param_resolver, qubits, initial_state) for step_result, moment in zip(all_step_results, circuit[1:]): _enter_moment_display_values_into_dictionary( display_values, moment, step_result.density_matrix(), qubits, step_result._qubit_map) compute_displays_results.append( study.ComputeDisplaysResult(params=param_resolver, display_values=display_values)) return compute_displays_results
def run_sweep( self, program: 'cirq.AbstractCircuit', params: study.Sweepable, repetitions: int = 1, ) -> List[study.Result]: """Samples circuit as if every measurement resulted in zero. Args: program: The circuit to sample from. params: Parameters to run with the program. repetitions: The number of times to sample. Returns: Result list for this run; one for each possible parameter resolver. Raises: ValueError if this sampler has a device and the circuit is not valid for the device. """ if self.device: self.device.validate_circuit(program) measurements = {} # type: Dict[str, np.ndarray] for op in program.all_operations(): key = protocols.measurement_key_name(op, default=None) if key is not None: measurements[key] = np.zeros((repetitions, len(op.qubits)), dtype=int) return [ study.Result(params=param_resolver, measurements=measurements) for param_resolver in study.to_resolvers(params) ]
def run_sweep( self, program: 'cirq.AbstractCircuit', params: study.Sweepable, repetitions: int = 1, ) -> List[study.Result]: """Samples circuit as if every measurement resulted in zero. Args: program: The circuit to sample from. params: Parameters to run with the program. repetitions: The number of times to sample. Returns: Result list for this run; one for each possible parameter resolver. Raises: ValueError: circuit is not valid for the sampler, due to invalid repeated keys or incompatibility with the sampler's device. """ if self.device: self.device.validate_circuit(program) shapes = self._get_measurement_shapes(program) return [ study.ResultDict( params=param_resolver, records={ k: np.zeros((repetitions, num_instances, len(qid_shape)), dtype=int) for k, (num_instances, qid_shape) in shapes.items() }, ) for param_resolver in study.to_resolvers(params) ]
def run_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, repetitions: int = 1, ) -> List[study.TrialResult]: """Runs the entire supplied Circuit, mimicking the quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: TrialResult list for this run; one for each possible parameter resolver. """ circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) param_resolvers = study.to_resolvers(params) trial_results = [] # type: List[study.TrialResult] for param_resolver in param_resolvers: measurements = self._run(circuit=circuit, param_resolver=param_resolver, repetitions=repetitions) trial_results.append( study.TrialResult(params=param_resolver, repetitions=repetitions, measurements=measurements)) return trial_results
def compute_amplitudes_sweep( self, program: circuits.Circuit, bitstrings: Sequence[str], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Sequence[Sequence[complex]]: if not isinstance(program, qsimc.QSimCircuit): raise ValueError('{!r} is not a QSimCircuit'.format(program)) # qsim numbers qubits in reverse order from cirq bitstrings = [bs[::-1] for bs in bitstrings] options = {'i': '\n'.join(bitstrings)} options.update(self.qsimh_options) param_resolvers = study.to_resolvers(params) trials_results = [] for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) options['c'] = solved_circuit.translate_cirq_to_qsim(qubit_order) options.update(self.qsimh_options) amplitudes = qsim.qsimh_simulate(options) trials_results.append(amplitudes) return trials_results
def run_sweep_iter( self, program: 'cirq.AbstractCircuit', params: 'cirq.Sweepable', repetitions: int = 1 ) -> Iterator['cirq.Result']: """Runs the supplied Circuit, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: Result list for this run; one for each possible parameter resolver. Raises: ValueError: If the circuit has no measurements. """ if not program.has_measurements(): raise ValueError("Circuit has no measurements to sample.") for param_resolver in study.to_resolvers(params): records = {} if repetitions == 0: for _, op, _ in program.findall_operations_with_gate_type(ops.MeasurementGate): records[protocols.measurement_key_name(op)] = np.empty([0, 1, 1]) else: records = self._run( circuit=program, param_resolver=param_resolver, repetitions=repetitions ) yield study.ResultDict(params=param_resolver, records=records)
def run_sweep( self, program: 'cirq.Circuit', params: study.Sweepable, repetitions: int = 1, ) -> List[study.TrialResult]: """Runs the supplied Circuit, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: TrialResult list for this run; one for each possible parameter resolver. """ if not program.has_measurements(): raise ValueError("Circuit has no measurements to sample.") _verify_unique_measurement_keys(program) trial_results = [] # type: List[study.TrialResult] for param_resolver in study.to_resolvers(params): measurements = self._run(circuit=program, param_resolver=param_resolver, repetitions=repetitions) trial_results.append( study.TrialResult.from_single_parameter_set( params=param_resolver, measurements=measurements)) return trial_results
def simulate_expectation_values_sweep( self, program: 'cirq.Circuit', observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']], params: 'study.Sweepable', qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, permit_terminal_measurements: bool = False, ) -> List[List[float]]: 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.' ) swept_evs = [] qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) qmap = {q: i for i, q in enumerate(qubit_order.order_for(program.all_qubits()))} if not isinstance(observables, List): observables = [observables] pslist = [ops.PauliSum.wrap(pslike) for pslike in observables] for param_resolver in study.to_resolvers(params): result = cast( state_vector_simulator.StateVectorTrialResult, self.simulate( program, param_resolver, qubit_order=qubit_order, initial_state=initial_state ), ) swept_evs.append( [ obs.expectation_from_state_vector(result.final_state_vector, qmap) for obs in pslist ] ) return swept_evs
def compute_amplitudes_sweep( self, program: circuits.Circuit, bitstrings: Sequence[int], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Sequence[Sequence[complex]]: """Computes the desired amplitudes using qsim. The initial state is assumed to be the all zeros state. Args: program: The circuit to simulate. bitstrings: The bitstrings whose amplitudes are desired, input as an string array where each string is formed from measured qubit values according to `qubit_order` from most to least significant qubit, i.e. in big-endian ordering. param_resolver: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. Returns: List of amplitudes. """ if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) # qsim numbers qubits in reverse order from cirq cirq_order = ops.QubitOrder.as_qubit_order(qubit_order).order_for( program.all_qubits()) num_qubits = len(cirq_order) bitstrings = [ format(bitstring, "b").zfill(num_qubits)[::-1] for bitstring in bitstrings ] options = {"i": "\n".join(bitstrings)} options.update(self.qsim_options) param_resolvers = study.to_resolvers(params) trials_results = [] if _needs_trajectories(program): translator_fn_name = "translate_cirq_to_qtrajectory" simulator_fn = qsim.qtrajectory_simulate else: translator_fn_name = "translate_cirq_to_qsim" simulator_fn = qsim.qsim_simulate for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) translator_fn = getattr(solved_circuit, translator_fn_name) options["c"] = translator_fn(cirq_order) options["s"] = self.get_seed() amplitudes = simulator_fn(options) trials_results.append(amplitudes) return trials_results
def simulate_sweep( self, program: circuits.Circuit, params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> List['SimulationTrialResult']: """Simulates the supplied Circuit. This method returns a result which allows access to the entire wave function. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. """ if not isinstance(program, qsimc.QSimCircuit): raise ValueError('{!r} is not a QSimCircuit'.format(program)) options = {} options.update(self.qsim_options) param_resolvers = study.to_resolvers(params) trials_results = [] for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) options['c'] = solved_circuit.translate_cirq_to_qsim(qubit_order) ordered_qubits = ops.QubitOrder.as_qubit_order( qubit_order).order_for(solved_circuit.all_qubits()) qubit_map = { qubit: index for index, qubit in enumerate(ordered_qubits) } qsim_state = qsim.qsim_simulate_fullstate(options) assert qsim_state.dtype == np.float32 assert qsim_state.ndim == 1 final_state = QSimSimulatorState(qsim_state, qubit_map) # create result for this parameter # TODO: We need to support measurements. result = QSimSimulatorTrialResult( params=prs, measurements={}, final_simulator_state=final_state) trials_results.append(result) return trials_results
def simulate_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable = study.ParamResolver({}), qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Union[int, np.ndarray] = 0, ) -> List['SimulationTrialResult']: """Simulates the entire supplied Circuit. This method returns a result which allows access to the entire wave function. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits used to define the order of amplitudes in the wave function. initial_state: If an int, the state is set to the computational basis state corresponding to this state. Otherwise if this is a np.ndarray it is the full initial state. In this case it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. Returns: List of SimulatorTrialResults for this run, one for each possible parameter resolver. """ circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) param_resolvers = study.to_resolvers(params or study.ParamResolver({})) trial_results = [] # type: List[SimulationTrialResult] qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) for param_resolver in param_resolvers: step_result = None all_step_results = self.simulate_moment_steps(circuit, param_resolver, qubit_order, initial_state) measurements = {} # type: Dict[str, np.ndarray] for step_result in all_step_results: for k, v in step_result.measurements.items(): measurements[k] = np.array(v, dtype=bool) if step_result: final_state = step_result.state() else: # Empty circuit, so final state should be initial state. num_qubits = len(qubit_order.order_for(circuit.all_qubits())) final_state = wave_function.to_valid_state_vector(initial_state, num_qubits) trial_results.append(SimulationTrialResult( params=param_resolver, measurements=measurements, final_state=final_state)) return trial_results
def simulate_sweep_iter( self, program: 'cirq.AbstractCircuit', params: 'cirq.Sweepable', qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> Iterator[TSimulationTrialResult]: """Simulates the supplied Circuit. This method returns a result which allows access to the entire state vector. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. This can be either a raw state or an `SimulationStateBase`. The form of the raw state depends on the simulation implementation. See documentation of the implementing class for details. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. """ qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) for param_resolver in study.to_resolvers(params): state = ( initial_state.copy() if isinstance(initial_state, SimulationStateBase) else initial_state ) all_step_results = self.simulate_moment_steps( program, param_resolver, qubit_order, state ) measurements: Dict[str, np.ndarray] = {} for step_result in all_step_results: for k, v in step_result.measurements.items(): measurements[k] = np.array(v, dtype=np.uint8) if ( 'final_simulator_state' in inspect.signature(self._create_simulator_trial_result).parameters ): yield self._create_simulator_trial_result( params=param_resolver, measurements=measurements, final_simulator_state=step_result._simulator_state(), ) else: yield self._create_simulator_trial_result( # pylint: disable=no-value-for-parameter, unexpected-keyword-arg, line-too-long params=param_resolver, measurements=measurements, final_step_result=step_result, # type: ignore )
def simulate_sweep( self, program: Union[Circuit, MPSimCircuit], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> List[Any]: """Simulates the supplied Circuit. This method returns a result which allows access to the entire wave function. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. """ if not isinstance(program, (Circuit, MPSimCircuit)): raise ValueError( f"Program is of type {type(program)} but should be either " "a cirq.Circuit or mpsim.mpsim_cirq.MPSimCircuit.") param_resolvers = study.to_resolvers(params) trial_results = [] for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) ordered_qubits = ops.QubitOrder.as_qubit_order( qubit_order).order_for(solved_circuit.all_qubits()) qubit_to_index_map = { qubit: index for index, qubit in enumerate(ordered_qubits) } mps = MPS(nqudits=len(solved_circuit.all_qubits())) # TODO: Account for an input ordering of operations to apply here operations = [ mps_operation_from_gate_operation(gate_operation, qubit_to_index_map) for gate_operation in solved_circuit.all_operations() ] mps.apply(operations, **self._options) trial_results.append(mps) return trial_results
def compute_amplitudes_sweep( self, program: circuits.Circuit, bitstrings: Sequence[int], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Sequence[Sequence[complex]]: if not isinstance(program, qcirc.QFlexCircuit): raise ValueError('{!r} is not a QFlexCircuit'.format(program)) if not isinstance(program.device, qdevice.QFlexVirtualDevice): # The circuit was not validated against the device # TODO: Make it compatible? Validate, but for which grid? raise ValueError('{!r} is not a QFlexVirtualDevice'.format( program.device)) param_resolvers = study.to_resolvers(params) trials_results = [] for prs in param_resolvers: from cirq import protocols # The QFlexVirtualDevice device is "inherited" from the original program solved_circuit = protocols.resolve_parameters(program, prs) sweep_return = [] # Not sure what these params could look like...for the moment for bitstring in bitstrings: options = { 'circuit_filename': solved_circuit.circuit_data, 'ordering_filename': solved_circuit.ordering_data, 'grid_filename': program.device.grid_data, 'final_state': bitstring } amplitudes = qflex.simulate(options) for amp in amplitudes: amplitude = complex(amp[1]) # For debugging purposes commented the following # state = amp[0] # print(input_initial_state + " --> " + state + ": " + \ # str(amplitude.real) + " " + str(amplitude.imag)) # the amplitude for bitstring is stored sweep_return.append(amplitude) trials_results.append(sweep_return) return trials_results
def compute_amplitudes_sweep( self, program: circuits.Circuit, bitstrings: Sequence[int], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Sequence[Sequence[complex]]: """Computes the desired amplitudes using qsim. The initial state is assumed to be the all zeros state. Args: program: The circuit to simulate. bitstrings: The bitstrings whose amplitudes are desired, input as an string array where each string is formed from measured qubit values according to `qubit_order` from most to least significant qubit, i.e. in big-endian ordering. param_resolver: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. Returns: List of amplitudes. """ if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) n_qubits = len(program.all_qubits()) # qsim numbers qubits in reverse order from cirq bitstrings = [ format(bitstring, 'b').zfill(n_qubits)[::-1] for bitstring in bitstrings ] options = {'i': '\n'.join(bitstrings)} options.update(self.qsim_options) param_resolvers = study.to_resolvers(params) trials_results = [] for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) options['c'] = solved_circuit.translate_cirq_to_qsim(qubit_order) amplitudes = qsim.qsim_simulate(options) trials_results.append(amplitudes) return trials_results
def run_sweep_iter( self, program: 'cirq.AbstractCircuit', params: 'cirq.Sweepable', repetitions: int = 1, ) -> Iterator['cirq.Result']: """Runs the supplied Circuit, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: Result list for this run; one for each possible parameter resolver. Raises: ValueError: If the circuit has no measurements. """ if not program.has_measurements(): raise ValueError("Circuit has no measurements to sample.") for param_resolver in study.to_resolvers(params): records = {} if repetitions == 0: for _, op, _ in program.findall_operations_with_gate_type( ops.MeasurementGate): records[protocols.measurement_key_name(op)] = np.empty( [0, 1, 1]) else: records = self._run(circuit=program, param_resolver=param_resolver, repetitions=repetitions) flat_records = False for k, v in records.items(): if v.ndim == 2: flat_records = True records[k] = v.reshape((v.shape[0], 1, v.shape[1])) if flat_records: warnings.warn( ('Starting in Cirq v0.15, values in the output of simulator._run must ' 'be 3D instead of 2D, with a new dimension between the existing two ' 'to capture "instances" of a key.'), DeprecationWarning, ) yield study.ResultDict(params=param_resolver, records=records)
def compute_samples_displays_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: Optional[study.Sweepable] = None ) -> List[study.ComputeDisplaysResult]: """Computes SamplesDisplays in the supplied Circuit or Schedule. In contrast to `compute_displays`, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. Returns: List of ComputeDisplaysResults for this run, one for each possible parameter resolver. """ circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) param_resolvers = study.to_resolvers(params or study.ParamResolver({})) compute_displays_results = [] # type: List[study.ComputeDisplaysResult] for param_resolver in param_resolvers: display_values = {} # type: Dict[Hashable, Any] preceding_circuit = circuits.Circuit() for i, moment in enumerate(circuit): displays = (op for op in moment if isinstance(op, ops.SamplesDisplay)) for display in displays: measurement_key = str(display.key) measurement_circuit = circuits.Circuit.from_ops( display.measurement_basis_change(), ops.measure(*display.qubits, key=measurement_key) ) measurements = self._run( preceding_circuit + measurement_circuit, param_resolver, display.num_samples) display_values[display.key] = ( display.value_derived_from_samples( measurements[measurement_key])) preceding_circuit.append(circuit[i]) compute_displays_results.append(study.ComputeDisplaysResult( params=param_resolver, display_values=display_values)) return compute_displays_results
def sample_sweep( program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, *, noise: 'cirq.NOISE_MODEL_LIKE' = None, repetitions: int = 1, dtype: Type[np.number] = np.complex64, seed: Optional[Union[int, np.random.RandomState]] = None ) -> List[study.TrialResult]: """Runs the supplied Circuit or Schedule, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. noise: Noise model to use while running the simulation. repetitions: The number of repetitions to simulate, per set of parameter values. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. Favors speed over precision by default, i.e. uses `numpy.complex64`. seed: The random seed to use for this simulator. Returns: TrialResult list for this run; one for each possible parameter resolver. """ if seed is None: prng = None elif isinstance(seed, np.random.RandomState): prng = seed else: prng = np.random.RandomState(seed) circuit = (program.to_circuit() if isinstance(program, schedules.Schedule) else program) trial_results = [] # type: List[study.TrialResult] for param_resolver in study.to_resolvers(params): measurements = sample(circuit, noise=noise, param_resolver=param_resolver, repetitions=repetitions, dtype=dtype, seed=prng) trial_results.append(measurements) return trial_results
def simulate_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> List['SimulationTrialResult']: """Simulates the supplied Circuit or Schedule. This method returns a result which allows access to the entire wave function. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. """ circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) param_resolvers = study.to_resolvers(params) trial_results = [] qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) for param_resolver in param_resolvers: all_step_results = self.simulate_moment_steps(circuit, param_resolver, qubit_order, initial_state) measurements = {} # type: Dict[str, np.ndarray] for step_result in all_step_results: for k, v in step_result.measurements.items(): measurements[k] = np.array(v, dtype=bool) trial_results.append( self._create_simulator_trial_result( params=param_resolver, measurements=measurements, final_simulator_state=step_result._simulator_state())) return trial_results
def sample_sweep( program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, *, noise: devices.NoiseModel = devices.NO_NOISE, repetitions: int = 1, dtype: Type[np.number] = np.complex64, seed: Optional[int] = None, ) -> List[study.TrialResult]: """Runs the supplied Circuit or Schedule, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. noise: Noise model to use while running the simulation. repetitions: The number of repetitions to simulate, per set of parameter values. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. Favors speed over precision by default, i.e. uses `numpy.complex64`. seed: The random seed to use for this simulator. Sets numpy's random seed. Setting numpy's seed different in between use of this class will lead to non-seeded behavior. Returns: TrialResult list for this run; one for each possible parameter resolver. """ if seed: np.random.seed(seed) circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) param_resolvers = study.to_resolvers(params) trial_results = [] # type: List[study.TrialResult] for param_resolver in param_resolvers: measurements = sample(circuit, noise=noise, param_resolver=param_resolver, repetitions=repetitions, dtype=dtype) trial_results.append(measurements) return trial_results
def simulate_sweep( self, program: 'cirq.Circuit', params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> List[TSimulationTrialResult]: """Simulates the supplied Circuit. This method returns a result which allows access to the entire state vector. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. This can be either a raw state or a `TActOnArgs`. The form of the raw state depends on the simulation implementation. See documentation of the implementing class for details. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. """ trial_results = [] qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) for param_resolver in study.to_resolvers(params): all_step_results = self.simulate_moment_steps( program, param_resolver, qubit_order, initial_state) measurements = {} # type: Dict[str, np.ndarray] for step_result in all_step_results: for k, v in step_result.measurements.items(): measurements[k] = np.array(v, dtype=np.uint8) trial_results.append( self._create_simulator_trial_result( params=param_resolver, measurements=measurements, final_simulator_state=step_result._simulator_state(), )) return trial_results
def sample_sweep( program: 'cirq.Circuit', params: study.Sweepable, *, noise: 'cirq.NOISE_MODEL_LIKE' = None, repetitions: int = 1, dtype: Type[np.number] = np.complex64, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> List[study.Result]: """Runs the supplied Circuit, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. noise: Noise model to use while running the simulation. repetitions: The number of repetitions to simulate, per set of parameter values. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. Favors speed over precision by default, i.e. uses `numpy.complex64`. seed: The random seed to use for this simulator. Returns: Result list for this run; one for each possible parameter resolver. """ prng = value.parse_random_state(seed) trial_results = [] # type: List[study.Result] for param_resolver in study.to_resolvers(params): measurements = sample( program, noise=noise, param_resolver=param_resolver, repetitions=repetitions, dtype=dtype, seed=prng, ) trial_results.append(measurements) return trial_results
def run_sweep(self, program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, repetitions: int = 1) -> List[study.TrialResult]: """Samples from the given Circuit or Schedule. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. Should be generated using AQTSampler.generate_circuit_from_list params: Parameters to run with the program. repetitions: The number of repetitions to simulate. The parameters remote_host and access_token are not used. Returns: TrialResult list for this run; one for each possible parameter resolver. """ meas_name = 'm' # TODO: Get measurement name from circuit circuit = (program if isinstance(program, circuits.Circuit) else program.to_circuit()) assert isinstance(circuit.device, IonDevice) param_resolvers = study.to_resolvers(params) trial_results = [] # type: List[study.TrialResult] for param_resolver in param_resolvers: id_str = uuid.uuid1() num_qubits = len(circuit.device.qubits) json_str = self._generate_json(circuit=circuit, param_resolver=param_resolver) results = self._send_json(json_str=json_str, id_str=id_str, repetitions=repetitions, num_qubits=num_qubits) results = results.astype(bool) res_dict = {meas_name: results} trial_results.append( study.TrialResult(params=param_resolver, measurements=res_dict)) return trial_results
def run_sweep_iter( self, program: 'cirq.Circuit', params: study.Sweepable, repetitions: int = 1, ) -> Iterator[study.Result]: """Runs the supplied Circuit, mimicking quantum hardware. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: Result list for this run; one for each possible parameter resolver. """ if not program.has_measurements(): raise ValueError("Circuit has no measurements to sample.") _verify_unique_measurement_keys(program) for param_resolver in study.to_resolvers(params): measurements = {} if repetitions == 0: for _, op, _ in program.findall_operations_with_gate_type( ops.MeasurementGate): measurements[protocols.measurement_key(op)] = np.empty( [0, 1]) else: measurements = self._run(circuit=program, param_resolver=param_resolver, repetitions=repetitions) yield study.Result.from_single_parameter_set( params=param_resolver, measurements=measurements)
def run_sweep(self, program: 'cirq.Circuit', params: study.Sweepable, repetitions: int = 1) -> List[study.Result]: """Samples from the given Circuit. In contrast to run, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. Should be generated using AQTSampler.generate_circuit_from_list params: Parameters to run with the program. repetitions: The number of repetitions to simulate. Returns: Result list for this run; one for each possible parameter resolver. """ # TODO: Use measurement name from circuit. # Github issue: https://github.com/quantumlib/Cirq/issues/2199 meas_name = 'm' assert isinstance(program.device, IonDevice) trial_results = [] # type: List[study.Result] for param_resolver in study.to_resolvers(params): id_str = uuid.uuid1() num_qubits = len(program.device.qubits) json_str = self._generate_json(circuit=program, param_resolver=param_resolver) results = self._send_json(json_str=json_str, id_str=id_str, repetitions=repetitions, num_qubits=num_qubits) results = results.astype(bool) res_dict = {meas_name: results} trial_results.append( study.Result(params=param_resolver, measurements=res_dict)) return trial_results
def simulate_expectation_values_sweep( self, program: 'cirq.Circuit', observables: Union['cirq.PauliSumLike', List['cirq.PauliSumLike']], params: 'study.Sweepable', qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, permit_terminal_measurements: bool = False, ) -> List[List[float]]: """Simulates the supplied circuit and calculates exact expectation values for the given observables on its final state. This method has no perfect analogy in hardware. Instead compare with Sampler.sample_expectation_values, which calculates estimated expectation values by sampling multiple times. Args: program: The circuit to simulate. observables: An observable or list of observables. param_resolver: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. permit_terminal_measurements: If the provided circuit ends with measurement(s), 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 values, with the value at index `n` corresponding to `observables[n]` from the input. Raises: ValueError if 'program' has terminal measurement(s) and 'permit_terminal_measurements' is False. (Note: We cannot test this until Cirq's `are_any_measurements_terminal` is released.) """ # TODO: replace with commented check when Cirq v0.10 is released. if not permit_terminal_measurements: raise ValueError( 'Automatic terminal measurement checking is not supported in qsim. ' 'Please check that your circuit has no terminal measurements, then ' 'set permit_terminal_measurements=True to bypass this error.') # 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.' # ) if not isinstance(observables, List): observables = [observables] psumlist = [ops.PauliSum.wrap(pslike) for pslike in observables] ordered_qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( program.all_qubits()) ordered_qubits = list(reversed(ordered_qubits)) num_qubits = len(ordered_qubits) qubit_map = { qubit: index for index, qubit in enumerate(ordered_qubits) } opsums_and_qubit_counts = [] for psum in psumlist: opsum = [] opsum_qubits = set() for pstr in psum: opstring = qsim.OpString() opstring.weight = pstr.coefficient for q, pauli in pstr.items(): op = pauli.on(q) opsum_qubits.add(q) qsimc.add_op_to_opstring(op, qubit_map, opstring) opsum.append(opstring) opsums_and_qubit_counts.append((opsum, len(opsum_qubits))) if initial_state is None: initial_state = 0 if not isinstance(initial_state, (int, np.ndarray)): raise TypeError('initial_state must be an int or state vector.') if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) options = {} options.update(self.qsim_options) param_resolvers = study.to_resolvers(params) if isinstance(initial_state, np.ndarray): if initial_state.dtype != np.complex64: raise TypeError( f'initial_state vector must have dtype np.complex64.') input_vector = initial_state.view(np.float32) if len(input_vector) != 2**num_qubits * 2: raise ValueError( f'initial_state vector size must match number of qubits.' f'Expected: {2**num_qubits * 2} Received: {len(input_vector)}' ) results = [] if _needs_trajectories(program): translator_fn_name = 'translate_cirq_to_qtrajectory' ev_simulator_fn = qsim.qtrajectory_simulate_expectation_values else: translator_fn_name = 'translate_cirq_to_qsim' ev_simulator_fn = qsim.qsim_simulate_expectation_values for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) translator_fn = getattr(solved_circuit, translator_fn_name) options['c'] = translator_fn(qubit_order) options['s'] = self.get_seed() if isinstance(initial_state, int): evs = ev_simulator_fn(options, opsums_and_qubit_counts, initial_state) elif isinstance(initial_state, np.ndarray): evs = ev_simulator_fn(options, opsums_and_qubit_counts, input_vector) results.append(evs) return results
def simulate_sweep( self, program: circuits.Circuit, params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Optional[Union[int, np.ndarray]] = None, ) -> List['SimulationTrialResult']: """Simulates the supplied Circuit. This method returns a result which allows access to the entire wave function. In contrast to simulate, this allows for sweeping over different parameter values. Args: program: The circuit to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. This can either be an integer representing a pure state (e.g. 11010) or a numpy array containing the full state vector. If none is provided, this is assumed to be the all-zeros state. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. Raises: TypeError: if an invalid initial_state is provided. """ if initial_state is None: initial_state = 0 if not isinstance(initial_state, (int, np.ndarray)): raise TypeError('initial_state must be an int or state vector.') if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) options = {} options.update(self.qsim_options) param_resolvers = study.to_resolvers(params) qubits = program.all_qubits() num_qubits = len(qubits) if isinstance(initial_state, np.ndarray): if initial_state.dtype != np.complex64: raise TypeError( f'initial_state vector must have dtype np.complex64.') input_vector = initial_state.view(np.float32) if len(input_vector) != 2**num_qubits * 2: raise ValueError( f'initial_state vector size must match number of qubits.' f'Expected: {2**num_qubits * 2} Received: {len(input_vector)}' ) trials_results = [] if _needs_trajectories(program): translator_fn_name = 'translate_cirq_to_qtrajectory' fullstate_simulator_fn = qsim.qtrajectory_simulate_fullstate else: translator_fn_name = 'translate_cirq_to_qsim' fullstate_simulator_fn = qsim.qsim_simulate_fullstate for prs in param_resolvers: solved_circuit = protocols.resolve_parameters(program, prs) translator_fn = getattr(solved_circuit, translator_fn_name) options['c'] = translator_fn(qubit_order) options['s'] = self.get_seed() ordered_qubits = ops.QubitOrder.as_qubit_order( qubit_order).order_for(qubits) # qsim numbers qubits in reverse order from cirq ordered_qubits = list(reversed(ordered_qubits)) qubit_map = { qubit: index for index, qubit in enumerate(ordered_qubits) } if isinstance(initial_state, int): qsim_state = fullstate_simulator_fn(options, initial_state) elif isinstance(initial_state, np.ndarray): qsim_state = fullstate_simulator_fn(options, input_vector) assert qsim_state.dtype == np.float32 assert qsim_state.ndim == 1 final_state = QSimSimulatorState(qsim_state, qubit_map) # create result for this parameter # TODO: We need to support measurements. result = QSimSimulatorTrialResult( params=prs, measurements={}, final_simulator_state=final_state) trials_results.append(result) return trials_results