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 test_qsim_simulate_fullstate(self): qsim_fullstate_options = { 'c': '2\n0 cnot 0 1\n1 cnot 1 0\n2 cz 0 1\n', 't': 1, 'v': 0 } self.assertSequenceEqual( qsim.qsim_simulate_fullstate(qsim_fullstate_options).tolist(), [1., 0., 0., 0., 0., 0., 0., 0.])
def _sample_measure_results( self, program: circuits.Circuit, repetitions: int = 1, ) -> Dict[str, np.ndarray]: """Samples from measurement gates in the circuit. Note that this will execute the circuit 'repetitions' times. Args: program: The circuit to sample from. repetitions: The number of samples to take. Returns: A dictionary from measurement gate key to measurement results. Measurement results are stored in a 2-dimensional numpy array, the first dimension corresponding to the repetition and the second to the actual boolean measurement results (ordered by the qubits being measured.) Raises: ValueError: If there are multiple MeasurementGates with the same key, or if repetitions is negative. """ if not isinstance(program, qsimc.QSimCircuit): program = qsimc.QSimCircuit(program, device=program.device) # Compute indices of measured qubits ordered_qubits = ops.QubitOrder.DEFAULT.order_for(program.all_qubits()) num_qubits = len(ordered_qubits) qubit_map = { qubit: index for index, qubit in enumerate(ordered_qubits) } # Computes # - the list of qubits to be measured # - the start (inclusive) and end (exclusive) indices of each measurement # - a mapping from measurement key to measurement gate measurement_ops = [ op for _, op, _ in program.findall_operations_with_gate_type( ops.MeasurementGate) ] measured_qubits = [] # type: List[ops.Qid] bounds = {} # type: Dict[str, Tuple] meas_ops = {} # type: Dict[str, cirq.GateOperation] current_index = 0 for op in measurement_ops: gate = op.gate key = protocols.measurement_key(gate) meas_ops[key] = op if key in bounds: raise ValueError( "Duplicate MeasurementGate with key {}".format(key)) bounds[key] = (current_index, current_index + len(op.qubits)) measured_qubits.extend(op.qubits) current_index += len(op.qubits) # Set qsim options options = {} options.update(self.qsim_options) results = {} for key, bound in bounds.items(): results[key] = np.ndarray(shape=(repetitions, bound[1] - bound[0]), dtype=int) noisy = _needs_trajectories(program) if noisy: translator_fn_name = 'translate_cirq_to_qtrajectory' sampler_fn = qsim.qtrajectory_sample else: translator_fn_name = 'translate_cirq_to_qsim' sampler_fn = qsim.qsim_sample if not noisy and program.are_all_measurements_terminal( ) and repetitions > 1: print('Provided circuit has no intermediate measurements. ' + 'Sampling repeatedly from final state vector.') # Measurements must be replaced with identity gates to sample properly. # Simply removing them may omit qubits from the circuit. for i in range(len(program.moments)): program.moments[i] = ops.Moment( op if not isinstance(op.gate, ops.MeasurementGate) else [ops.IdentityGate(1).on(q) for q in op.qubits] for op in program.moments[i]) options['c'] = program.translate_cirq_to_qsim( ops.QubitOrder.DEFAULT) options['s'] = self.get_seed() final_state = qsim.qsim_simulate_fullstate(options, 0) full_results = sim.sample_state_vector(final_state.view( np.complex64), range(num_qubits), repetitions=repetitions, seed=self._prng) for i in range(repetitions): for key, op in meas_ops.items(): meas_indices = [qubit_map[qubit] for qubit in op.qubits] for j, q in enumerate(meas_indices): results[key][i][j] = full_results[i][q] else: translator_fn = getattr(program, translator_fn_name) options['c'] = translator_fn(ops.QubitOrder.DEFAULT) for i in range(repetitions): options['s'] = self.get_seed() measurements = sampler_fn(options) for key, bound in bounds.items(): for j in range(bound[1] - bound[0]): results[key][i][j] = int(measurements[bound[0] + j]) return 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. This is currently unsupported in qsim; the recommended workaround is to prepend the circuit with X gates as necessary. Returns: List of SimulationTrialResults for this run, one for each possible parameter resolver. Raises: ValueError: if an initial_state is provided. """ if initial_state is not None: raise ValueError( f'initial_state is not supported; received {initial_state}') 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) 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['s'] = self.get_seed() ordered_qubits = ops.QubitOrder.as_qubit_order( qubit_order).order_for(solved_circuit.all_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) } 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