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 _get_measurement_shapes( circuit: 'cirq.AbstractCircuit', ) -> Dict[str, Tuple[int, Tuple[int, ...]]]: """Gets the shapes of measurements in the given circuit. Returns: A mapping from measurement key name to a tuple of (num_instances, qid_shape), where num_instances is the number of times that key appears in the circuit and qid_shape is the shape of measured qubits for the key, as determined by the `cirq.qid_shape` protocol. Raises: ValueError: if the qid_shape of different instances of the same measurement key disagree. """ qid_shapes: Dict[str, Tuple[int, ...]] = {} num_instances: Dict[str, int] = collections.Counter() for op in circuit.all_operations(): key = protocols.measurement_key_name(op, default=None) if key is not None: qid_shape = protocols.qid_shape(op) prev_qid_shape = qid_shapes.setdefault(key, qid_shape) if qid_shape != prev_qid_shape: raise ValueError( "Different qid shapes for repeated measurement: " f"key={key!r}, prev_qid_shape={prev_qid_shape}, qid_shape={qid_shape}" ) num_instances[key] += 1 return { k: (num_instances[k], qid_shape) for k, qid_shape in qid_shapes.items() }
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 _verify_unique_measurement_keys(operations: Iterable[ops.Operation]): seen: Set[str] = set() for op in operations: if isinstance(op.gate, ops.MeasurementGate): meas = op.gate key = protocols.measurement_key_name(meas) if key in seen: raise ValueError(f'Measurement key {key} repeated') seen.add(key)
def _strat_act_on_state_vector_from_channel( action: Any, args: 'cirq.StateVectorSimulationState', qubits: Sequence['cirq.Qid']) -> bool: index = args._state.apply_channel(action, args.get_axes(qubits), args.prng) if index is None: return NotImplemented if protocols.is_measurement(action): key = protocols.measurement_key_name(action) args._classical_data.record_channel_measurement(key, index) return True
def _write_qasm(self, output_func: Callable[[str], None]) -> None: self.args.validate_version('2.0') # Generate nice line spacing line_gap = [0] def output_line_gap(n): line_gap[0] = max(line_gap[0], n) def output(text): if line_gap[0] > 0: output_func('\n' * line_gap[0]) line_gap[0] = 0 output_func(text) # Comment header if self.header: for line in self.header.split('\n'): output(('// ' + line).rstrip() + '\n') output('\n') # Version output('OPENQASM 2.0;\n') output('include "qelib1.inc";\n') output_line_gap(2) # Function definitions # None yet # Register definitions # Qubit registers output(f"// Qubits: [{', '.join(map(str, self.qubits))}]\n") if len(self.qubits) > 0: output(f'qreg q[{len(self.qubits)}];\n') # Classical registers # Pick an id for the creg that will store each measurement already_output_keys: Set[str] = set() for meas in self.measurements: key = protocols.measurement_key_name(meas) if key in already_output_keys: continue already_output_keys.add(key) meas_id = self.args.meas_key_id_map[key] comment = self.meas_comments[key] if comment is None: output(f'creg {meas_id}[{len(meas.qubits)}];\n') else: output( f'creg {meas_id}[{len(meas.qubits)}]; // Measurement: {comment}\n' ) output_line_gap(2) # Operations self._write_operations(self.operations, output, output_line_gap)
def _generate_measurement_ids(self) -> Dict[str, str]: index = 0 measurement_id_map: Dict[str, str] = {} for op in self.operations: if isinstance(op.gate, ops.MeasurementGate): key = protocols.measurement_key_name(op) if key in measurement_id_map: continue measurement_id_map[key] = f'm{index}' index += 1 return measurement_id_map
def _strat_act_on_state_vector_from_channel( action: Any, args: 'cirq.ActOnStateVectorArgs', qubits: Sequence['cirq.Qid']) -> bool: kraus_operators = protocols.kraus(action, default=None) if kraus_operators is None: return NotImplemented def prepare_into_buffer(k: int): linalg.targeted_left_multiply( left_matrix=kraus_tensors[k], right_target=args.target_tensor, target_axes=args.get_axes(qubits), out=args.available_buffer, ) shape = protocols.qid_shape(action) kraus_tensors = [ e.reshape(shape * 2).astype(args.target_tensor.dtype) for e in kraus_operators ] p = args.prng.random() weight = None fallback_weight = 0 fallback_weight_index = 0 for index in range(len(kraus_tensors)): prepare_into_buffer(index) weight = np.linalg.norm(args.available_buffer)**2 if weight > fallback_weight: fallback_weight_index = index fallback_weight = weight p -= weight if p < 0: break assert weight is not None, "No Kraus operators" if p >= 0 or weight == 0: # Floating point error resulted in a malformed sample. # Fall back to the most likely case. prepare_into_buffer(fallback_weight_index) weight = fallback_weight index = fallback_weight_index args.available_buffer /= np.sqrt(weight) args.swap_target_tensor_for(args.available_buffer) if protocols.is_measurement(action): key = protocols.measurement_key_name(action) args._classical_data.record_channel_measurement(key, index) return True
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 _write_quil(self, output_func: Callable[[str], None]) -> None: output_func('# Created using Cirq.\n\n') if len(self.measurements) > 0: measurements_declared: Set[str] = set() for m in self.measurements: key = protocols.measurement_key_name(m) if key in measurements_declared: continue measurements_declared.add(key) output_func( f'DECLARE {self.measurement_id_map[key]} BIT[{len(m.qubits)}]\n' ) output_func('\n') def keep(op: 'cirq.Operation') -> bool: return protocols.quil(op, formatter=self.formatter) is not None def fallback(op): if len(op.qubits) not in [1, 2]: return NotImplemented mat = protocols.unitary(op, None) if mat is None: return NotImplemented # Following code is a safety measure # Could not find a gate that doesn't decompose into a gate # with a _quil_ implementation # coverage: ignore if len(op.qubits) == 1: return QuilOneQubitGate(mat).on(*op.qubits) return QuilTwoQubitGate(mat).on(*op.qubits) def on_stuck(bad_op): return ValueError(f'Cannot output operation as QUIL: {bad_op!r}') for main_op in self.operations: decomposed = protocols.decompose(main_op, keep=keep, fallback_decomposer=fallback, on_stuck_raise=on_stuck) for decomposed_op in decomposed: output_func( protocols.quil(decomposed_op, formatter=self.formatter))
def _generate_measurement_ids(self) -> Tuple[Dict[str, str], Dict[str, Optional[str]]]: # Pick an id for the creg that will store each measurement meas_key_id_map = {} # type: Dict[str, str] meas_comments = {} # type: Dict[str, Optional[str]] meas_i = 0 for meas in self.measurements: key = protocols.measurement_key_name(meas) if key in meas_key_id_map: continue meas_id = f'm_{key}' if self.is_valid_qasm_id(meas_id): meas_comments[key] = None else: meas_id = f'm{meas_i}' meas_i += 1 meas_comments[key] = ' '.join(key.split('\n')) meas_key_id_map[key] = meas_id return meas_key_id_map, meas_comments
def _strat_act_on_state_vector_from_mixture( action: Any, args: 'cirq.ActOnStateVectorArgs', qubits: Sequence['cirq.Qid']) -> bool: mixture = protocols.mixture(action, default=None) if mixture is None: return NotImplemented probabilities, unitaries = zip(*mixture) index = args.prng.choice(range(len(unitaries)), p=probabilities) shape = protocols.qid_shape(action) * 2 unitary = unitaries[index].astype(args.target_tensor.dtype).reshape(shape) linalg.targeted_left_multiply(unitary, args.target_tensor, args.get_axes(qubits), out=args.available_buffer) args.swap_target_tensor_for(args.available_buffer) if protocols.is_measurement(action): key = protocols.measurement_key_name(action) args._classical_data.record_channel_measurement(key, index) return True
def sample_measurement_ops( self, measurement_ops: List['cirq.GateOperation'], repetitions: int = 1, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> Dict[str, np.ndarray]: """Samples from the system at this point in the computation. Note that this does not collapse the state vector. In contrast to `sample` which samples qubits, this takes a list of `cirq.GateOperation` instances whose gates are `cirq.MeasurementGate` instances and then returns a mapping from the key in the measurement gate to the resulting bit strings. Different measurement operations must not act on the same qubits. Args: measurement_ops: `GateOperation` instances whose gates are `MeasurementGate` instances to be sampled form. repetitions: The number of samples to take. seed: A seed for the pseudorandom number generator. 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 the operation's gates are not `MeasurementGate` instances or a qubit is acted upon multiple times by different operations from `measurement_ops`. """ # Sanity checks. seen_measurement_keys: Set[str] = set() for op in measurement_ops: gate = op.gate if not isinstance(gate, ops.MeasurementGate): raise ValueError(f'{op.gate} was not a MeasurementGate') key = protocols.measurement_key_name(gate) if key in seen_measurement_keys: raise ValueError(f'Duplicate MeasurementGate with key {key}') seen_measurement_keys.add(key) # Find measured qubits, ensuring a consistent ordering. measured_qubits = [] seen_qubits: Set[cirq.Qid] = set() for op in measurement_ops: for q in op.qubits: if q not in seen_qubits: seen_qubits.add(q) measured_qubits.append(q) # Perform whole-system sampling of the measured qubits. indexed_sample = self.sample(measured_qubits, repetitions, seed=seed) # Extract results for each measurement. results: Dict[str, np.ndarray] = {} qubits_to_index = {q: i for i, q in enumerate(measured_qubits)} for op in measurement_ops: gate = cast(ops.MeasurementGate, op.gate) out = np.zeros(shape=(repetitions, len(op.qubits)), dtype=np.int8) inv_mask = gate.full_invert_mask() for i, q in enumerate(op.qubits): out[:, i] = indexed_sample[:, qubits_to_index[q]] if inv_mask[i]: out[:, i] ^= out[:, i] < 2 results[gate.key] = out return results
def _measurement_key_name_(self) -> str: return protocols.measurement_key_name(self.sub_operation, NotImplemented)