def estimate_run_sweep_time( program: cirq.AbstractCircuit, params: cirq.Sweepable = None, repetitions: int = 1000, latency: Optional[float] = _BASE_LATENCY, ) -> float: """Compute the estimated time for running a parameter sweep across a single Circuit. This should approximate, in seconds, the time for the execution of a batch of circuits using Engine.run_sweep() on QCS at a time where there is no queue (such as a reserved slot). This estimation should be considered a rough approximation. Many factors can contribute to the execution time of a circuit, and the run time can also vary as the service's code changes frequently. Args: program: circuit to be executed params: a parameter sweep of variable resolvers to use with the circuit repetitions: number of repetitions to execute per parameter sweep latency: Optional latency to add (defaults to 1.5 seconds) """ width = len(program.all_qubits()) depth = len(program) sweeps = len(list(cirq.to_resolvers(params))) return _estimate_run_time_seconds(width, depth, sweeps, repetitions, latency)
def run_sweep( self, program: cirq.AbstractCircuit, params: cirq.Sweepable, repetitions: int = 1 ) -> Sequence[cirq.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' trial_results: List[cirq.Result] = [] for param_resolver in cirq.to_resolvers(params): id_str = uuid.uuid1() num_qubits = len(program.all_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(cirq.ResultDict(params=param_resolver, measurements=res_dict)) return trial_results
def __init__( self, circuit: cirq.AbstractCircuit, measurement: BitstringsMeasurement, params: Union[Sequence[TParamPair], cirq.ParamResolverOrSimilarType] = None, spec: Optional[ExecutableSpec] = None, problem_topology: Optional[cirq.NamedTopology] = None, initial_state: Optional[cirq.ProductState] = None, ): """Initialize the quantum executable. The actual fields in this class are immutable, but we allow more liberal input types which will be frozen in this __init__ method. Args: circuit: The circuit. This will be frozen before being set as an attribute. measurement: A description of the measurement properties or process. params: A cirq.ParamResolverOrSimilarType which will be frozen into a tuple of key value pairs. spec: Specification metadata about this executable that is not used by the quantum runtime, but is persisted in result objects to associate executables with results. problem_topology: Description of the multiqubit gate topology present in the circuit. If not specified, the circuit must be compatible with the device topology. initial_state: How to initialize the quantum system before running `circuit`. If not specified, the device will be initialized into the all-zeros state. """ # We care a lot about mutability in this class. No object is truly immutable in Python, # but we can get pretty close by following the example of dataclass(frozen=True), which # deletes this class's __setattr__ magic method. To set values ever, we use # object.__setattr__ in this __init__ function. # # We write our own __init__ function to be able to accept a wider range of input formats # that can be easily converted to our native, immutable format. object.__setattr__(self, 'circuit', circuit.freeze()) object.__setattr__(self, 'measurement', measurement) if isinstance(params, tuple) and all( isinstance(param_kv, tuple) and len(param_kv) == 2 for param_kv in params ): frozen_params = params elif isinstance(params, Sequence) and all( isinstance(param_kv, Sequence) and len(param_kv) == 2 for param_kv in params ): frozen_params = tuple((k, v) for k, v in params) elif study.resolver._is_param_resolver_or_similar_type(params): param_resolver = cirq.ParamResolver(cast(cirq.ParamResolverOrSimilarType, params)) frozen_params = tuple(param_resolver.param_dict.items()) else: raise ValueError(f"`params` should be a ParamResolverOrSimilarType, not {params}.") object.__setattr__(self, 'params', frozen_params) object.__setattr__(self, 'spec', spec) object.__setattr__(self, 'problem_topology', problem_topology) object.__setattr__(self, 'initial_state', initial_state) # Hash may be expensive to compute, especially for large circuits. # This should be safe since this class should be immutable. This line will # also check for hashibility of members at construction time. object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))
def run_sweep(self, program: cirq.AbstractCircuit, params: cirq.Sweepable, repetitions: int = 1) -> Sequence[cirq.Result]: """This will evaluate results on the circuit for every set of parameters in `params`. Args: program: Circuit to evaluate for each set of parameters in `params`. params: `cirq.Sweepable` of parameters which this function passes to `cirq.protocols.resolve_parameters` for evaluating the circuit. repetitions: Number of times to run each iteration through the `params`. For a given set of parameters, the `cirq.Result` will include a measurement for each repetition. Returns: A list of `cirq.Result` s. """ resolvers = [r for r in cirq.to_resolvers(params)] return self.executor( quantum_computer=self._quantum_computer, circuit=program.unfreeze(copy=False), resolvers=resolvers, repetitions=repetitions, transformer=self.transformer, )
def func( circuit: cirq.AbstractCircuit, *, context: Optional[cirq.TransformerContext] = cirq.TransformerContext(), atol: float = 1e-4, custom_arg: CustomArg = CustomArg(), ) -> cirq.FrozenCircuit: my_mock(circuit, context, atol, custom_arg) return circuit.freeze()
def t3( circuit: cirq.AbstractCircuit, context: Optional[cirq.TransformerContext] = None ) -> cirq.Circuit: assert context is not None context.logger.log("First INFO Log", "of T3 Start") circuit = t1(circuit, context=context) context.logger.log("Second INFO Log", "of T3 Middle") circuit = t2(circuit, context=context) context.logger.log("Third INFO Log", "of T3 End") return circuit.unfreeze()
def _generate_json( self, circuit: cirq.AbstractCircuit, param_resolver: cirq.ParamResolverOrSimilarType, ) -> str: """Generates the JSON string from a Circuit. The json format is defined as follows: [[op_string,gate_exponent,qubits]] which is a list of sequential quantum operations, each operation defined by: op_string: str that specifies the operation type: "X","Y","Z","MS" gate_exponent: float that specifies the gate_exponent of the operation qubits: list of qubits where the operation acts on. Args: circuit: Circuit to be run. param_resolver: Param resolver for resolving parameters in circuit. Returns: json formatted string of the sequence. Raises: RuntimeError: If the circuit is empty. """ seq_list: List[ Union[Tuple[str, float, List[int]], Tuple[str, float, float, List[int]]] ] = [] circuit = cirq.resolve_parameters(circuit, param_resolver) for op in circuit.all_operations(): line_qubit = cast(Tuple[cirq.LineQubit], op.qubits) op = cast(cirq.GateOperation, op) qubit_idx = [obj.x for obj in line_qubit] op_str = get_op_string(op) gate: Union[cirq.EigenGate, cirq.PhasedXPowGate] if op_str == 'R': gate = cast(cirq.PhasedXPowGate, op.gate) seq_list.append( (op_str, float(gate.exponent), float(gate.phase_exponent), qubit_idx) ) else: gate = cast(cirq.EigenGate, op.gate) seq_list.append((op_str, float(gate.exponent), qubit_idx)) if len(seq_list) == 0: raise RuntimeError('Cannot send an empty circuit') json_str = json.dumps(seq_list) return json_str
def estimate_run_time(program: cirq.AbstractCircuit, repetitions: int, latency: Optional[float] = _BASE_LATENCY) -> float: """Compute the estimated time for running a single circuit. This should approximate, in seconds, the time for the execution of a batch of circuits using Engine.run() on QCS at a time where there is no queue (such as a reserved slot). This estimation should be considered a rough approximation. Many factors can contribute to the execution time of a circuit, and the run time can also vary as the service's code changes frequently. Args: program: circuit to be executed repetitions: number of repetitions to execute latency: Optional latency to add (defaults to 1.5 seconds) """ width = len(program.all_qubits()) depth = len(program) return _estimate_run_time_seconds(width, depth, 1, repetitions, latency)
def serialize(self, circuit: cirq.AbstractCircuit) -> SerializedProgram: """Serialize the given circuit. Raises: ValueError: if the circuit has gates that are not supported or is otherwise invalid. """ self._validate_circuit(circuit) num_qubits = self._validate_qubits(circuit.all_qubits()) serialized_ops = self._serialize_circuit(circuit) # IonQ API does not support measurements, so we pass the measurement keys through # the metadata field. Here we split these out of the serialized ops. body = { 'qubits': num_qubits, 'circuit': [op for op in serialized_ops if op['gate'] != 'meas'], } metadata = self._serialize_measurements(op for op in serialized_ops if op['gate'] == 'meas') return SerializedProgram(body=body, metadata=metadata)
def place_circuit( self, circuit: cirq.AbstractCircuit, problem_topology: NamedTopology, shared_rt_info: 'cg.SharedRuntimeInfo', rs: np.random.RandomState, ) -> Tuple[cirq.FrozenCircuit, Dict[Any, cirq.Qid]]: """Place a circuit according to the hardcoded placements. Args: circuit: The circuit. problem_topology: The topologies (i.e. connectivity) of the circuit, use to look up the placement in `self.mapping`. shared_rt_info: A `cg.SharedRuntimeInfo` object; ignored for hardcoded placement. rs: A `RandomState`; ignored for hardcoded placement. Returns: A tuple of a new frozen circuit with the qubits placed and a mapping from input qubits or nodes to output qubits. Raises: CouldNotPlaceError: if the given problem_topology is not present in the hardcoded mapping. """ try: nt_mapping = self._mapping[problem_topology] except KeyError as e: raise CouldNotPlaceError(str(e)) circuit_mapping = { self.topo_node_to_qubit_func(nt_node): gridq for nt_node, gridq in nt_mapping.items() } circuit = circuit.unfreeze().transform_qubits(circuit_mapping).freeze() return circuit, circuit_mapping
def validate_circuit(self, circuit: cirq.AbstractCircuit): super().validate_circuit(circuit) _verify_unique_measurement_keys(circuit.all_operations())
def mock_tranformer_func( circuit: cirq.AbstractCircuit, *, context: Optional[cirq.TransformerContext] = None ) -> cirq.Circuit: my_mock(circuit, context) return circuit.unfreeze()
def __call__( self, circuit: cirq.AbstractCircuit, *, context: Optional[cirq.TransformerContext] = None ) -> cirq.Circuit: self.mock(circuit, context) return circuit.unfreeze()
def _validate_circuit(self, circuit: cirq.AbstractCircuit): if len(circuit) == 0: raise ValueError('Cannot serialize empty circuit.') if not circuit.are_all_measurements_terminal(): raise ValueError('All measurements in circuit must be at end of circuit.')