def run_sweep( self, program: 'cirq.Circuit', 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(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(self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int) -> Dict[str, List[np.ndarray]]: param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters( circuit, param_resolver) self._check_all_resolved(resolved_circuit) measurements = {} # type: Dict[str, List[np.ndarray]] if repetitions == 0: for _, op, _ in resolved_circuit.findall_operations_with_gate_type( ops.MeasurementGate): measurements[protocols.measurement_key(op)] = np.empty([0, 1]) for _ in range(repetitions): all_step_results = self._base_iterator( resolved_circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0) for step_result in all_step_results: for k, v in step_result.measurements.items(): if not k in measurements: measurements[k] = [] measurements[k].append(np.array(v, dtype=bool)) return {k: np.array(v) for k, v in measurements.items()}
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 _base_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int ) -> Iterator['cirq.contrib.quimb.mps_simulator.MPSSimulatorStepResult']: """Iterator over MPSSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. 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 in the computational basis. Represented as a big endian int. Yields: MPSStepResult from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield MPSSimulatorStepResult( measurements={}, state=MPSState( qubit_map, self.simulation_options, self.grouping, initial_state=initial_state, ), ) return state = MPSState( qubit_map, self.simulation_options, self.grouping, initial_state=initial_state, ) noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: measurements: Dict[str, List[int]] = collections.defaultdict(list) for op in flatten_to_ops(op_tree): if isinstance(op.gate, ops.MeasurementGate): key = str(protocols.measurement_key(op)) measurements[key].extend( state.perform_measurement(op.qubits, self.prng)) elif protocols.has_mixture(op): state.apply_op(op, self.prng) else: raise NotImplementedError( f"Unrecognized operation: {op!r}") yield MPSSimulatorStepResult(measurements=measurements, state=state)
def _verify_unique_measurement_keys(operations: Iterable['cirq.Operation']): seen: Set[str] = set() for op in operations: if protocols.is_measurement(op): key = protocols.measurement_key(op) if key in seen: raise ValueError('Measurement key {} repeated'.format(key)) seen.add(key)
def _verify_unique_measurement_keys(operations: Iterable[ops.Operation]): seen = set() # type: Set[str] for op in operations: if ops.MeasurementGate.is_measurement(op): key = protocols.measurement_key(op) if key in seen: raise ValueError('Measurement key {} repeated'.format(key)) seen.add(key)
def find_measurement_keys(circuit: circuits.Circuit) -> Set[str]: keys: Set[str] = set() for _, _, gate in circuit.findall_operations_with_gate_type( ops.MeasurementGate): key = protocols.measurement_key(gate) if key in keys: raise ValueError('Repeated Measurement key {}'.format(key)) keys.add(key) return keys
def sample_measurement_ops( self, measurement_ops: List[ops.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 wave function. 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`. """ bounds = {} # type: Dict[str, Tuple] all_qubits = [] # type: List[ops.Qid] meas_ops = {} current_index = 0 for op in measurement_ops: gate = op.gate if not isinstance(gate, ops.MeasurementGate): raise ValueError('{} was not a MeasurementGate'.format(gate)) key = protocols.measurement_key(gate) meas_ops[key] = gate if key in bounds: raise ValueError( 'Duplicate MeasurementGate with key {}'.format(key)) bounds[key] = (current_index, current_index + len(op.qubits)) all_qubits.extend(op.qubits) current_index += len(op.qubits) indexed_sample = self.sample(all_qubits, repetitions, seed=seed) results = {} for k, (s, e) in bounds.items(): before_invert_mask = indexed_sample[:, s:e] results[k] = before_invert_mask ^ (np.logical_and( before_invert_mask < 2, meas_ops[k].full_invert_mask())) return results
def _verify_unique_measurement_keys(circuit: circuits.Circuit): result = collections.Counter( protocols.measurement_key(op, default=None) for op in ops.flatten_op_tree(iter(circuit))) result[None] = 0 duplicates = [k for k, v in result.most_common() if v > 1] if duplicates: raise ValueError('Measurement key {} repeated'.format( ",".join(duplicates)))
def _verify_unique_measurement_keys(operations: Iterable['cirq.Operation']): """Raises an error if a measurement key is repeated in the given set of Operations.""" seen_keys: Set[str] = set() for op in operations: if protocols.is_measurement(op): key = protocols.measurement_key(op) if key in seen_keys: raise ValueError('Measurement key {} repeated'.format(key)) seen_keys.add(key)
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(meas) if key in seen: raise ValueError(f'Measurement key {key} repeated') seen.add(key)
def _verify_unique_measurement_keys(operations: Iterable[ops.Operation]): seen: Set[str] = set() for op in operations: meas = ops.op_gate_of_type(op, ops.MeasurementGate) if meas: key = protocols.measurement_key(meas) if key in seen: raise ValueError('Measurement key {} repeated'.format(key)) seen.add(key)
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(op) if key in measurement_id_map: continue measurement_id_map[key] = f'm{index}' index += 1 return measurement_id_map
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(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 _serialize_measurement_gate(self, gate: 'cirq.MeasurementGate', targets: Sequence[int]) -> dict: key = protocols.measurement_key(gate) if chr(31) in key or chr(30) in key: raise ValueError( 'Measurement gates for IonQ API cannot have a key with a ascii unit' f'or record separator in it. Key was {key}') return { 'gate': 'meas', 'key': key, 'targets': ','.join(str(t) for t in targets) }
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(action) args.log_of_measurement_results[key] = [index] return True
def _base_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int ) -> Iterator['cirq.CliffordSimulatorStepResult']: """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. 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 in the computational basis. Represented as a big endian int. Yields: CliffordStepResult from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield CliffordSimulatorStepResult(measurements={}, state=CliffordState( qubit_map, initial_state=initial_state)) return state = CliffordState(qubit_map, initial_state=initial_state) for moment in circuit: measurements: Dict[ str, List[np.ndarray]] = collections.defaultdict(list) for op in moment: if isinstance(op.gate, ops.MeasurementGate): key = protocols.measurement_key(op) measurements[key].extend( state.perform_measurement(op.qubits, self._prng)) elif protocols.has_unitary(op): state.apply_unitary(op) else: raise NotImplementedError( f"Unrecognized operation: {op!r}") yield CliffordSimulatorStepResult(measurements=measurements, state=state)
def _simulate_measurement(self, op: ops.Operation, data: _StateAndBuffer, indices: List[int], measurements: Dict[str, List[bool]], num_qubits: int) -> None: """Simulate an op that is a measurement in the computataional basis.""" meas = ops.op_gate_of_type(op, ops.MeasurementGate) # TODO: support measurement outside computational basis. if meas: invert_mask = meas.invert_mask or num_qubits * (False, ) # Measure updates inline. bits, _ = wave_function.measure_state_vector( data.state, indices, data.state) corrected = [bit ^ mask for bit, mask in zip(bits, invert_mask)] key = protocols.measurement_key(meas) measurements[key].extend(corrected)
def _measure_to_proto(gate: 'cirq.MeasurementGate', qubits: Sequence['cirq.Qid']): if len(qubits) == 0: raise ValueError('Measurement gate on no qubits.') invert_mask = None if gate.invert_mask: invert_mask = gate.invert_mask + (False, ) * (gate.num_qubits() - len(gate.invert_mask)) if invert_mask and len(invert_mask) != len(qubits): raise ValueError('Measurement gate had invert mask of length ' 'different than number of qubits it acts on.') return operations_pb2.Measurement( targets=[_qubit_to_proto(q) for q in qubits], key=protocols.measurement_key(gate), invert_mask=invert_mask)
def _run_sweep_repeat(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] if repetitions == 0: for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate): measurements[protocols.measurement_key(op)] = np.empty([0, 1]) for _ in range(repetitions): all_step_results = self._base_iterator( circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0) for step_result in all_step_results: for k, v in step_result.measurements.items(): if not k in measurements: measurements[k] = [] measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()}
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(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 _base_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int ) -> Iterator['cirq.CliffordSimulatorStepResult']: """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. 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. Yields: CliffordStepResult from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield CliffordSimulatorStepResult(measurements={}, state=CliffordState( qubit_map, initial_state=initial_state)) else: state = CliffordState(qubit_map, initial_state=initial_state) for moment in circuit: measurements = collections.defaultdict( list) # type: Dict[str, List[np.ndarray]] for op in moment: print(type(op)) if protocols.has_unitary(op): state.apply_unitary(op) elif protocols.is_measurement(op): key = protocols.measurement_key(op) measurements[key].extend( state.perform_measurement(op.qubits)) yield CliffordSimulatorStepResult(measurements=measurements, state=state)
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(meas) if key in meas_key_id_map: continue meas_id = 'm_{}'.format(key) if self.is_valid_qasm_id(meas_id): meas_comments[key] = None else: meas_id = 'm{}'.format(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 _measure_to_proto_dict(gate: 'cirq.MeasurementGate', qubits: Sequence['cirq.Qid']): if len(qubits) == 0: raise ValueError('Measurement gate on no qubits.') invert_mask = None if gate.invert_mask: invert_mask = gate.invert_mask + (False, ) * (gate.num_qubits() - len(gate.invert_mask)) if invert_mask and len(invert_mask) != len(qubits): raise ValueError('Measurement gate had invert mask of length ' 'different than number of qubits it acts on.') measurement = { 'targets': [_qubit_to_proto_dict(q) for q in qubits], 'key': protocols.measurement_key(gate), } if invert_mask: measurement['invert_mask'] = [json.dumps(x) for x in invert_mask] return {'measurement': measurement}
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(action) args.log_of_measurement_results[key] = [index] return True
def run_sweep( self, program: 'cirq.Circuit', params: study.Sweepable, repetitions: int = 1, ) -> 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. 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) trial_results = [] # type: List[study.Result] 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) trial_results.append( study.Result.from_single_parameter_set( params=param_resolver, measurements=measurements)) return trial_results
def _run(self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int) -> Dict[str, List[np.ndarray]]: """Repeats measurements multiple times. Args: circuit: The circuit to simulate. param_resolver: A ParamResolver for determining values of Symbols. repetitions: How many measurements to perform final_simulator_state: The final state of the simulator. Returns: A dictionay of measurement key (e.g. qubit) to a list of arrays that are the measurements. """ param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters( circuit, param_resolver) self._check_all_resolved(resolved_circuit) measurements = {} # type: Dict[str, List[np.ndarray]] if repetitions == 0: for _, op, _ in resolved_circuit.findall_operations_with_gate_type( ops.MeasurementGate): measurements[protocols.measurement_key(op)] = np.empty([0, 1]) for _ in range(repetitions): all_step_results = self._base_iterator( resolved_circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0) for step_result in all_step_results: for k, v in step_result.measurements.items(): if not k in measurements: measurements[k] = [] measurements[k].append(np.array(v, dtype=int)) return {k: np.array(v) for k, v in measurements.items()}
def _simulate_measurement(self, op: ops.Operation, data: _StateAndBuffer, indices: List[int], measurements: Dict[str, List[int]], num_qubits: int) -> None: """Simulate an op that is a measurement in the computational basis.""" # TODO: support measurement outside computational basis. if isinstance(op.gate, ops.MeasurementGate): meas = op.gate invert_mask = meas.full_invert_mask() # Measure updates inline. bits, _ = wave_function.measure_state_vector( data.state, indices, out=data.state, qid_shape=data.state.shape, seed=self.prng) corrected = [ bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask) ] key = protocols.measurement_key(meas) measurements[key].extend(corrected)
def _base_iterator(self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: Union[int, np.ndarray], perform_measurements: bool = True) -> Iterator: qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qid_shape = protocols.qid_shape(qubits) qubit_map = {q: i for i, q in enumerate(qubits)} initial_matrix = density_matrix_utils.to_valid_density_matrix( initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype) if len(circuit) == 0: yield DensityMatrixStepResult(initial_matrix, {}, qubit_map, self._dtype) return state = _StateAndBuffers(len(qid_shape), initial_matrix.reshape(qid_shape * 2)) def on_stuck(bad_op: ops.Operation): return TypeError( "Can't simulate operations that don't implement " "SupportsUnitary, SupportsConsistentApplyUnitary, " "SupportsMixture, SupportsChannel or is a measurement: {!r}". format(bad_op)) def keep(potential_op: ops.Operation) -> bool: return (protocols.has_channel(potential_op) or (ops.op_gate_of_type(potential_op, ops.MeasurementGate) is not None) or isinstance(potential_op, (ops.SamplesDisplay, ops.WaveFunctionDisplay, ops.DensityMatrixDisplay))) noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for moment in noisy_moments: measurements = collections.defaultdict( list) # type: Dict[str, List[int]] channel_ops_and_measurements = protocols.decompose( moment, keep=keep, on_stuck_raise=on_stuck) for op in channel_ops_and_measurements: indices = [qubit_map[qubit] for qubit in op.qubits] if isinstance(op, (ops.SamplesDisplay, ops.WaveFunctionDisplay, ops.DensityMatrixDisplay)): continue # TODO: support more general measurements. meas = ops.op_gate_of_type(op, ops.MeasurementGate) if meas: if perform_measurements: invert_mask = meas.full_invert_mask() # Measure updates inline. bits, _ = density_matrix_utils.measure_density_matrix( state.tensor, indices, qid_shape=qid_shape, out=state.tensor) corrected = [ bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask) ] key = protocols.measurement_key(meas) measurements[key].extend(corrected) else: # TODO: Use apply_channel similar to apply_unitary. self._apply_op_channel(op, state, indices) yield DensityMatrixStepResult(density_matrix=state.tensor, measurements=measurements, qubit_map=qubit_map, dtype=self._dtype)
def _base_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: Union[int, np.ndarray], perform_measurements: bool = True) -> Iterator: qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) num_qubits = len(qubits) qubit_map = {q: i for i, q in enumerate(qubits)} matrix = density_matrix_utils.to_valid_density_matrix( initial_state, num_qubits, self._dtype) if len(circuit) == 0: yield DensityMatrixStepResult(matrix, {}, qubit_map, self._dtype) matrix = np.reshape(matrix, (2,) * num_qubits * 2) def on_stuck(bad_op: ops.Operation): return TypeError( "Can't simulate operations that don't implement " "SupportsUnitary, SupportsApplyUnitary, SupportsMixture, " "SupportsChannel or is a measurement: {!r}".format(bad_op)) def keep(potential_op: ops.Operation) -> bool: return (protocols.has_channel(potential_op) or (ops.op_gate_of_type(potential_op, ops.MeasurementGate) is not None) or isinstance(potential_op, (ops.SamplesDisplay, ops.WaveFunctionDisplay, ops.DensityMatrixDisplay)) ) matrix = np.reshape(matrix, (2,) * num_qubits * 2) noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for moment in noisy_moments: measurements = collections.defaultdict( list) # type: Dict[str, List[bool]] channel_ops_and_measurements = protocols.decompose( moment, keep=keep, on_stuck_raise=on_stuck) for op in channel_ops_and_measurements: indices = [qubit_map[qubit] for qubit in op.qubits] if isinstance(op, (ops.SamplesDisplay, ops.WaveFunctionDisplay, ops.DensityMatrixDisplay)): continue # TODO: support more general measurements. meas = ops.op_gate_of_type(op, ops.MeasurementGate) if meas: if perform_measurements: invert_mask = meas.invert_mask or num_qubits * (False,) # Measure updates inline. bits, _ = density_matrix_utils.measure_density_matrix( matrix, indices, matrix) corrected = [bit ^ mask for bit, mask in zip(bits, invert_mask)] key = protocols.measurement_key(meas) measurements[key].extend(corrected) else: # TODO: Use apply_channel similar to apply_unitary. gate = cast(ops.GateOperation, op).gate channel = protocols.channel(gate) sum_buffer = np.zeros((2,) * 2 * num_qubits, dtype=self._dtype) buffer = np.empty((2,) * 2 * num_qubits, dtype=self._dtype) out = np.empty((2,) * 2 * num_qubits, dtype=self._dtype) for krauss in channel: krauss_tensor = np.reshape(krauss.astype(self._dtype), (2,) * gate.num_qubits() * 2) result = linalg.targeted_conjugate_about(krauss_tensor, matrix, indices, buffer=buffer, out=out) sum_buffer += result np.copyto(dst=matrix, src=sum_buffer) yield DensityMatrixStepResult( density_matrix=matrix, measurements=measurements, qubit_map=qubit_map, dtype=self._dtype)