def validate_schedule(self, schedule): """ Raises an error if the given schedule is invalid on this device. Args: schedule: The schedule to validate Raises: ValueError: If the schedule is invalid """ super().validate_schedule(schedule) # Validate each scheduled operation in the schedule # If operation is a measurement, ensure only measurements come after it measurement_check_performed = False for so in schedule.scheduled_operations: self.validate_scheduled_operation(schedule, so) if (ops.op_gate_of_type(so.operation, ops.MeasurementGate) and not measurement_check_performed): later_ops = [ op for op in schedule.scheduled_operations if op.time + op.duration > so.time + so.duration ] for op in later_ops: if not ops.op_gate_of_type(op, ops.MeasurementGate): raise ValueError("Non-measurement operation after" " measurement") measurement_check_performed = True
def duration_of(self, operation): if ops.op_gate_of_type(operation, ops.XXPowGate): return self._twoq_gates_duration if (ops.op_gate_of_type(operation, ops.XPowGate) or ops.op_gate_of_type(operation, ops.YPowGate) or ops.op_gate_of_type(operation, ops.ZPowGate)): return self._oneq_gates_duration if ops.op_gate_of_type(operation, ops.MeasurementGate): return self._measurement_duration raise ValueError('Unsupported gate type: {!r}'.format(operation))
def duration_of(self, operation): if ops.op_gate_of_type(operation, ops.HPowGate): return cirq.Duration(nanos=10) if ops.op_gate_of_type(operation, ops.ZPowGate): return cirq.Duration(nanos=10) if ops.op_gate_of_type(operation, ops.CNotPowGate): return cirq.Duration(nanos=200) if ops.op_gate_of_type(operation, ops.MeasurementGate): return cirq.Duration(nanos=100) raise ValueError('Unsupported gate type: {!r}'.format(operation))
def continue_condition(op: ops.Operation, current_string: ops.PauliStringPhasor, is_first: bool) -> int: if ops.op_gate_of_type(op, ops.SingleQubitCliffordGate): return (CONTINUE if len(current_string.pauli_string) != 1 else STOP) if ops.op_gate_of_type(op, ops.CZPowGate): return STOP if stop_at_cz else CONTINUE if (isinstance(op, ops.PauliStringPhasor) and len(op.qubits) == 1 and (op.pauli_string[op.qubits[0]] == current_string.pauli_string[op.qubits[0]])): return SKIP return STOP
def _is_swaplike(op: ops.Operation): gate1 = ops.op_gate_of_type(op, ops.SwapPowGate) if gate1: return gate1.exponent == 1 gate2 = ops.op_gate_of_type(op, ops.ISwapPowGate) if gate2: return _is_integer((gate2.exponent - 1) / 2) gate3 = ops.op_gate_of_type(op, ops.FSimGate) if gate3: return _is_integer((gate3.theta - (np.pi / 2)) / np.pi) return False
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)))
def _keep(self, op: ops.Operation) -> bool: # Check if this is a CZ # Only keep partial CZ gates if allow_partial_czs cz_gate = ops.op_gate_of_type(op, ops.CZPowGate) if (cz_gate and (self.allow_partial_czs or value.canonicalize_half_turns(cz_gate.exponent) == 1)): return True # Measurement in Z basis? if ops.op_gate_of_type(op, ops.MeasurementGate): return True # SingleQubit known matrix if len(op.qubits) == 1 and protocols.has_unitary(op): return True return False
def optimize_circuit(self, circuit: circuits.Circuit): # Tracks qubit phases (in half turns; multiply by pi to get radians). qubit_phase = defaultdict(lambda: 0) # type: Dict[ops.Qid, float] def dump_tracked_phase(qubits: Iterable[ops.Qid], index: int) -> None: """Zeroes qubit_phase entries by emitting Z gates.""" for q in qubits: p = qubit_phase[q] if not decompositions.is_negligible_turn(p, self.tolerance): dump_op = ops.Z(q)**(p * 2) insertions.append((index, dump_op)) qubit_phase[q] = 0 deletions = [] # type: List[Tuple[int, ops.Operation]] inline_intos = [] # type: List[Tuple[int, ops.Operation]] insertions = [] # type: List[Tuple[int, ops.Operation]] for moment_index, moment in enumerate(circuit): for op in moment.operations: # Move Z gates into tracked qubit phases. h = _try_get_known_z_half_turns(op) if h is not None: q = op.qubits[0] qubit_phase[q] += h / 2 deletions.append((moment_index, op)) continue # Z gate before measurement is a no-op. Drop tracked phase. if ops.op_gate_of_type(op, ops.MeasurementGate): for q in op.qubits: qubit_phase[q] = 0 # If there's no tracked phase, we can move on. phases = [qubit_phase[q] for q in op.qubits] if all( decompositions.is_negligible_turn(p, self.tolerance) for p in phases): continue # Try to move the tracked phasing over the operation. phased_op = op for i, p in enumerate(phases): if not decompositions.is_negligible_turn( p, self.tolerance): phased_op = protocols.phase_by(phased_op, -p, i, default=None) if phased_op is not None: deletions.append((moment_index, op)) inline_intos.append( (moment_index, cast(ops.Operation, phased_op))) else: dump_tracked_phase(op.qubits, moment_index) dump_tracked_phase(qubit_phase.keys(), len(circuit)) circuit.batch_remove(deletions) circuit.batch_insert_into(inline_intos) circuit.batch_insert(insertions)
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 get_op_string(op_obj: ops.Operation): """Find the string representation for a given gate Params: op_obj: Gate object, out of: XXPowGate, XPowGate, YPowGate""" if isinstance(op_obj, ops.XXPowGate) or ops.op_gate_of_type( op_obj, ops.XXPowGate): op_str = 'MS' elif isinstance(op_obj, ops.XPowGate) or ops.op_gate_of_type( op_obj, ops.XPowGate): op_str = 'X' elif isinstance(op_obj, ops.YPowGate) or ops.op_gate_of_type( op_obj, ops.YPowGate): op_str = 'Y' elif isinstance(op_obj, ops.MeasurementGate) or ops.op_gate_of_type( op_obj, ops.MeasurementGate): op_str = 'Meas' else: raise ValueError('Got unknown gate:', op_obj) return op_str
def can_add_operation_into_moment(self, operation: ops.Operation, moment: ops.Moment) -> bool: if not super().can_add_operation_into_moment(operation, moment): return False if ops.op_gate_of_type(operation, ops.XXPowGate): return not self._check_if_XXPow_operation_interacts_with_any( cast(ops.GateOperation, operation), cast(Iterable[ops.GateOperation], moment.operations)) return True
def optimize_circuit(self, circuit: circuits.Circuit): state = _OptimizerState() for moment_index, moment in enumerate(circuit): for op in moment.operations: affected = [q for q in op.qubits if state.held_w_phases.get(q) is not None] # Collect, phase, and merge Ws. w = _try_get_known_phased_pauli(op) if w is not None: if decompositions.is_negligible_turn( w[0] - 1, self.tolerance): _potential_cross_whole_w(moment_index, op, self.tolerance, state) else: _potential_cross_partial_w(moment_index, op, state) continue if not affected: continue # Absorb Z rotations. t = _try_get_known_z_half_turns(op) if t is not None: _absorb_z_into_w(moment_index, op, state) continue # Dump coherent flips into measurement bit flips. if ops.op_gate_of_type(op, ops.MeasurementGate): _dump_into_measurement(moment_index, op, state) # Cross CZs using kickback. if _try_get_known_cz_half_turns(op) is not None: if len(affected) == 1: _single_cross_over_cz(moment_index, op, affected[0], state) else: _double_cross_over_cz(op, state) continue # Don't know how to handle this situation. Dump the gates. _dump_held(op.qubits, moment_index, state) # Put anything that's still held at the end of the circuit. _dump_held(state.held_w_phases.keys(), len(circuit), state) circuit.batch_remove(state.deletions) circuit.batch_insert_into(state.inline_intos) circuit.batch_insert(state.insertions)
def validate_moment(self, moment: 'cirq.Moment'): super().validate_moment(moment) for op in moment.operations: if ops.op_gate_of_type(op, ops.CZPowGate): for other in moment.operations: if (other is not op and self._check_if_exp11_operation_interacts( cast(ops.GateOperation, op), cast(ops.GateOperation, other))): raise ValueError( 'Adjacent Exp11 operations: {}.'.format(moment))
def can_add_operation_into_moment(self, operation: 'cirq.Operation', moment: 'cirq.Moment') -> bool: self.validate_moment(moment) if not super().can_add_operation_into_moment(operation, moment): return False if ops.op_gate_of_type(operation, ops.CZPowGate): return not self._check_if_exp11_operation_interacts_with_any( cast(ops.GateOperation, operation), cast(Iterable['cirq.GateOperation'], moment.operations)) return True
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 _simulate_reset(self, op: ops.Operation, data: _StateAndBuffer, indices: List[int]) -> None: """Simulate an op that is a reset to the |0> state.""" reset = ops.op_gate_of_type(op, ops.ResetChannel) if reset: # Do a silent measurement. bits, _ = wave_function.measure_state_vector( data.state, indices, out=data.state, qid_shape=data.state.shape) # Apply bit flip(s) to change the reset the bits to 0. for b, i, d in zip(bits, indices, protocols.qid_shape(reset)): if b == 0: continue # Already zero, no reset needed reset_unitary = _FlipGate(d, reset_value=b)(*op.qubits) self._simulate_unitary(reset_unitary, data, [i])
def duration_of(self, operation): if ops.op_gate_of_type(operation, ops.CZPowGate): return self._exp_z_duration if ops.op_gate_of_type(operation, ops.MeasurementGate): return self._measurement_duration if (ops.op_gate_of_type(operation, ops.XPowGate) or ops.op_gate_of_type(operation, ops.YPowGate) or ops.op_gate_of_type(operation, ops.PhasedXPowGate)): return self._exp_w_duration if ops.op_gate_of_type(operation, ops.ZPowGate): # Z gates are performed in the control software. return value.Duration() raise ValueError('Unsupported gate type: {!r}'.format(operation))
def optimization_at( self, circuit: circuits.Circuit, index: int, op: ops.Operation) -> Optional[circuits.PointOptimizationSummary]: if len(op.qubits) != 2: return None old_operations, indices, matrix = ( self._scan_two_qubit_ops_into_matrix(circuit, index, op.qubits)) old_interaction_count = len( [old_op for old_op in old_operations if len(old_op.qubits) == 2]) switch_to_new = False switch_to_new |= any( len(old_op.qubits) == 2 and not ops.op_gate_of_type(old_op, ops.CZPowGate) for old_op in old_operations) if not self.allow_partial_czs: switch_to_new |= any( isinstance(old_op, ops.GateOperation) and isinstance( old_op.gate, ops.CZPowGate) and old_op.gate.exponent != 1 for old_op in old_operations) # This point cannot be optimized using this method if not switch_to_new and old_interaction_count <= 1: return None # Find a max-3-cz construction. new_operations = ( two_qubit_decompositions.two_qubit_matrix_to_operations( op.qubits[0], op.qubits[1], matrix, self.allow_partial_czs, self.tolerance, False)) new_interaction_count = len( [new_op for new_op in new_operations if len(new_op.qubits) == 2]) switch_to_new |= new_interaction_count < old_interaction_count if not switch_to_new: return None return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 - index, clear_qubits=op.qubits, new_operations=new_operations)
def __init__(self, operations: ops.OP_TREE, qubits: Tuple[ops.Qid, ...], header: str = '', precision: int = 10, version: str = '2.0') -> None: self.operations = tuple(ops.flatten_op_tree(operations)) self.qubits = qubits self.header = header self.measurements = tuple( op for op in self.operations if ops.op_gate_of_type(op, ops.MeasurementGate)) # type: ignore meas_key_id_map, meas_comments = self._generate_measurement_ids() self.meas_comments = meas_comments qubit_id_map = self._generate_qubit_ids() self.args = protocols.QasmArgs(precision=precision, version=version, qubit_id_map=qubit_id_map, meas_key_id_map=meas_key_id_map)
def optimization_at(self, circuit: circuits.Circuit, index: int, op: ops.Operation): if ops.op_gate_of_type(op, AcquaintanceOpportunityGate): logical_indices = tuple(self.mapping[q] for q in op.qubits) logical_operations = self.execution_strategy.get_operations( logical_indices, op.qubits) clear_span = int(not self.execution_strategy.keep_acquaintance) return circuits.PointOptimizationSummary( clear_span=clear_span, clear_qubits=op.qubits, new_operations=logical_operations) if (isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate)): op.gate.update_mapping(self.mapping, op.qubits) return raise TypeError('Can only execute a strategy consisting of gates that ' 'are instances of AcquaintanceOpportunityGate or ' 'PermutationGate.')
def validate_circuit(self, circuit: circuits.Circuit): """ Raises an error if the given circuit is invalid on this device. A circuit is invalid if any of its moments are invalid or if there is a non-empty moment after a moment with a measurement. Args: circuit: The circuit to validate Raises: ValueError: If the given circuit can't be run on this device """ super().validate_circuit(circuit) # Measurements must be in the last non-empty moment has_measurement_occurred = False for moment in circuit: if has_measurement_occurred: if len(moment.operations) > 0: raise ValueError("Non-empty moment after measurement") for operation in moment.operations: if ops.op_gate_of_type(operation, ops.MeasurementGate): has_measurement_occurred = True
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)
def _cz_count(circuit): return sum(bool(ops.op_gate_of_type(op, ops.CZPowGate)) for op in circuit)
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)} state = wave_function.to_valid_state_vector(initial_state, num_qubits, self._dtype) if len(circuit) == 0: yield SparseSimulatorStep(state, {}, qubit_map, self._dtype) def on_stuck(bad_op: ops.Operation): return TypeError( "Can't simulate unknown operations that don't specify a " "_unitary_ method, a _decompose_ method, or " "(_has_unitary_ + _apply_unitary_) methods" ": {!r}".format(bad_op)) def keep(potential_op: ops.Operation) -> bool: return (protocols.has_unitary(potential_op) or protocols.is_measurement(potential_op)) state = np.reshape(state, (2, ) * num_qubits) buffer = np.empty((2, ) * num_qubits, dtype=self._dtype) for moment in circuit: measurements = collections.defaultdict( list) # type: Dict[str, List[bool]] non_display_ops = (op for op in moment if not isinstance(op, ( ops.SamplesDisplay, ops.WaveFunctionDisplay, ops.DensityMatrixDisplay))) unitary_ops_and_measurements = protocols.decompose( non_display_ops, keep=keep, on_stuck_raise=on_stuck) for op in unitary_ops_and_measurements: indices = [qubit_map[qubit] for qubit in op.qubits] # TODO: Support measurements outside of computational basis. # TODO: Support mixtures. 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, _ = wave_function.measure_state_vector( state, indices, state) corrected = [ bit ^ mask for bit, mask in zip(bits, invert_mask) ] key = protocols.measurement_key(meas) measurements[key].extend(corrected) else: result = protocols.apply_unitary( op, args=protocols.ApplyUnitaryArgs( state, buffer, indices)) if result is buffer: buffer = state state = result yield SparseSimulatorStep(state_vector=state, 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()) 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 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))
def _keep(self, op: ops.Operation) -> bool: # Don't change if it's already a SingleQubitCliffordGate return bool(ops.op_gate_of_type(op, ops.SingleQubitCliffordGate))
def __init__(self): circuits.PointOptimizer.__init__(self) self.no_decomp = lambda op: (not get_acquaintance_size( op) or ops.op_gate_of_type(op, AcquaintanceOpportunityGate))
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) 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**num_qubits)) noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) state = qulacs.DensityMatrix(num_qubits) state.load(matrix) 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] indices = [ num_qubits - 1 - qubit_map[qubit] for qubit in op.qubits ] indices.reverse() 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: # Not implemented raise NotImplementedError( "Measurement is not supported in qulacs simulator") else: # TODO: Use apply_channel similar to apply_unitary. gate = cast(ops.GateOperation, op).gate channel = protocols.channel(gate) qulacs_gates = [] for krauss in channel: krauss = krauss.astype(np.complex128) qulacs_gate = qulacs.gate.DenseMatrix(indices, krauss) qulacs_gates.append(qulacs_gate) qulacs_cptp_map = qulacs.gate.CPTP(qulacs_gates) qulacs_cptp_map.update_quantum_state(state) matrix = state.get_matrix() matrix = np.reshape(matrix, (2, ) * num_qubits * 2) yield DensityMatrixStepResult(density_matrix=matrix, measurements=measurements, qubit_map=qubit_map, dtype=self._dtype)