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 = to_valid_density_matrix(initial_state, num_qubits, dtype=self._dtype) if len(circuit) == 0: yield DensityMatrixStepResult(matrix, {}, qubit_map, dtype=self._dtype) 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]] operations = moment.operations for op in operations: indices = [ num_qubits - 1 - qubit_map[qubit] for qubit in op.qubits ] indices.reverse() if isinstance(op, ops.MeasurementGate): # Not implemented raise NotImplementedError( "Measurement is not supported in qulacs simulator") else: 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)
def _channel_(self): if self._is_parameterized_(): return NotImplemented channel = protocols.channel(self.sub_gate, None) if channel is None: return NotImplemented do_nothing = np.eye(np.product(protocols.qid_shape(self.sub_gate)), dtype=np.float64) result = [e * np.sqrt(self.probability) for e in channel] result.append(np.sqrt(1 - float(self.probability)) * do_nothing) return result
def operation_to_channel_matrix( operation: 'protocols.SupportsChannel') -> np.ndarray: """Returns the matrix representation of a superoperator in standard basis. Let E: L(H1) -> L(H2) denote a linear map which takes linear operators on Hilbert space H1 to linear operators on Hilbert space H2 and let d1 = dim H1 and d2 = dim H2. Also, let Fij denote an operator whose matrix has one in ith row and jth column and zeros everywhere else. Note that d1-by-d1 operators Fij form a basis of L(H1). Similarly, d2-by-d2 operators Fij form a basis of L(H2). This function returns the matrix of E in these bases. Args: operation: Quantum channel. Returns: Matrix representation of operation. """ return kraus_to_channel_matrix(protocols.channel(operation))
def _strat_act_on_state_vector_from_channel(action: Any, args: 'cirq.ActOnStateVectorArgs' ) -> bool: kraus_operators = protocols.channel(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.axes, 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_i = 0 for i in range(len(kraus_tensors)): prepare_into_buffer(i) weight = np.linalg.norm(args.available_buffer)**2 if weight > fallback_weight: fallback_weight_i = i 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_i) weight = fallback_weight args.available_buffer /= np.sqrt(weight) args.swap_target_tensor_for(args.available_buffer) return True
def operation_to_choi(operation: 'protocols.SupportsChannel') -> np.ndarray: r"""Returns the unique Choi matrix associated with a superoperator. Choi matrix J(E) of a linear map E: L(H1) -> L(H2) which takes linear operators on Hilbert space H1 to linear operators on Hilbert space H2 is defined as $$ J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) $$ where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state and I: L(H1) -> L(H1) is the identity map. Note that J(E) is a square matrix with d1*d2 rows and columns where d1 = dim H1 and d2 = dim H2. Args: operation: Quantum channel. Returns: Choi matrix corresponding to operation. """ return kraus_to_choi(protocols.channel(operation))
def entanglement_fidelity(operation: 'cirq.SupportsChannel') -> float: r"""Returns entanglement fidelity of a given quantum channel. Entanglement fidelity $F_e$ of a quantum channel $E: L(H) \to L(H)$ is the overlap between the maximally entangled state $|\phi\rangle = \frac{1}{\sqrt{dim H}} \sum_i|i\rangle|i\rangle$ and the state obtained by sending one half of $|\phi\rangle$ through the channel $E$, i.e. $$ F_e = \langle\phi|(E \otimes I)(|\phi\rangle\langle\phi|)|\phi\rangle $$ where $I: L(H) \to L(H)$ is the identity map. Args: operation: Quantum channel whose entanglement fidelity is to be computed. Returns: Entanglement fidelity of the channel represented by operation. """ f = 0.0 for k in protocols.channel(operation): f += np.abs(np.trace(k))**2 n_qubits = protocols.num_qubits(operation) return float(f / 4**n_qubits)
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 _channel_(self) -> Union[Tuple[np.ndarray], NotImplementedType]: return protocols.channel(self.sub_operation, NotImplemented)
def simulate_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: study.Sweepable, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Any = None, ) -> List['SimulationTrialResult']: """Simulates the supplied Circuit or Schedule with Qulacs Args: program: The circuit or schedule 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. """ trial_results = [] # sweep for each parameters resolvers = study.to_resolvers(params) for resolver in resolvers: # result circuit cirq_circuit = protocols.resolve_parameters(program, resolver) qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( cirq_circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} num_qubits = len(qubits) # create state qulacs_state = self._get_qulacs_state(num_qubits) if initial_state is not None: cirq_state = wave_function.to_valid_state_vector( initial_state, num_qubits) qulacs_state.load(cirq_state) del cirq_state # create circuit qulacs_circuit = qulacs.QuantumCircuit(num_qubits) address_to_key = {} register_address = 0 for moment in cirq_circuit: operations = moment.operations for op in operations: indices = [ num_qubits - 1 - qubit_map[qubit] for qubit in op.qubits ] result = self._try_append_gate(op, qulacs_circuit, indices) if result: continue if isinstance(op.gate, ops.ResetChannel): qulacs_circuit.update_quantum_state(qulacs_state) qulacs_state.set_zero_state() qulacs_circuit = qulacs.QuantumCircuit(num_qubits) elif protocols.is_measurement(op): for index in indices: qulacs_circuit.add_gate( qulacs.gate.Measurement( index, register_address)) address_to_key[ register_address] = protocols.measurement_key( op.gate) register_address += 1 elif protocols.has_mixture(op): indices.reverse() qulacs_gates = [] gate = cast(ops.GateOperation, op).gate channel = protocols.channel(gate) 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.circuit.add_gate(qulacs_cptp_map) # perform simulation qulacs_circuit.update_quantum_state(qulacs_state) # fetch final state and measurement results final_state = qulacs_state.get_vector() measurements = collections.defaultdict(list) for register_index in range(register_address): key = address_to_key[register_index] value = qulacs_state.get_classical_value(register_index) measurements[key].append(value) # create result for this parameter result = SimulationTrialResult(params=resolver, measurements=measurements, final_simulator_state=final_state) trial_results.append(result) # release memory del qulacs_state del qulacs_circuit return trial_results
def _channel_(self) -> Union[Tuple[np.ndarray], NotImplementedType]: return protocols.channel(self._gate, NotImplemented)
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)