def compute_displays_sweep( self, program: Union[circuits.Circuit, schedules.Schedule], params: Optional[study.Sweepable] = None, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Union[int, np.ndarray] = 0, ) -> List[study.ComputeDisplaysResult]: """Computes displays in the supplied Circuit or Schedule. In contrast to `compute_displays`, this allows for sweeping over different parameter values. Args: program: The circuit or schedule to simulate. params: Parameters to run with the program. qubit_order: Determines the canonical ordering of the qubits used to define the order of amplitudes in the wave function. initial_state: If an int, the state is set to the computational basis state corresponding to this state. Otherwise if it is a np.ndarray it is the full initial state, either a pure state or the full density matrix. If it is the pure state it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. If it is a mixed state it must be correctly sized and positive semidefinite with trace one. Returns: List of ComputeDisplaysResults for this run, one for each possible parameter resolver. """ circuit = (program.to_circuit() if isinstance(program, schedules.Schedule) else program) qubit_order = ops.QubitOrder.as_qubit_order(qubit_order) qubits = qubit_order.order_for(circuit.all_qubits()) compute_displays_results: List[study.ComputeDisplaysResult] = [] for param_resolver in study.to_resolvers(params): display_values: Dict[Hashable, Any] = {} # Compute the displays in the first Moment moment = circuit[0] matrix = density_matrix_utils.to_valid_density_matrix( initial_state, num_qubits=len(qubits), dtype=self._dtype) qubit_map = {q: i for i, q in enumerate(qubits)} _enter_moment_display_values_into_dictionary( display_values, moment, matrix, qubits, qubit_map) # Compute the displays in the rest of the Moments all_step_results = self.simulate_moment_steps( circuit, param_resolver, qubits, initial_state) for step_result, moment in zip(all_step_results, circuit[1:]): _enter_moment_display_values_into_dictionary( display_values, moment, step_result.density_matrix(), qubits, step_result._qubit_map) compute_displays_results.append( study.ComputeDisplaysResult(params=param_resolver, display_values=display_values)) return compute_displays_results
def expectation_from_density_matrix( self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid, int]) -> float: r"""Evaluate the expectation of this PauliString given a density matrix. Compute the expectation value of this PauliString with respect to an array representing a density matrix. By convention expectation values are defined for Hermitian operators, and so this method will fail if this PauliString is non-Hermitian. `state` must be an array representation of a density matrix and have shape `(2 ** n, 2 ** n)` or `(2, 2, ..., 2)` (2*n entries), where `state` is expressed over n qubits. `qubit_map` must assign an integer index to each qubit in this PauliString that determines which bit position of a computational basis state that qubit corresponds to. For example if `state` represents $|0\rangle |+\rangle$ and `q0, q1 = cirq.LineQubit.range(2)` then: cirq.X(q0).expectation(state, qubit_map={q0: 0, q1: 1}) = 0 cirq.X(q0).expectation(state, qubit_map={q0: 1, q1: 0}) = 1 Args: state: An array representing a valid density matrix. qubit_map: A map from all qubits used in this PauliString to the indices of the qubits that `state` is defined over. Returns: The expectation value of the input state. Raises: NotImplementedError if this PauliString is non-Hermitian. """ if abs(self.coefficient.imag) > 0.0001: raise NotImplementedError( "Cannot compute expectation value of a non-Hermitian " "PauliString <{}>. Coefficient must be real.".format(self)) # FIXME: Avoid enforcing specific complex type. This is necessary to # prevent an `apply_unitary` bug (Issue #2041). if state.dtype.kind != 'c': raise TypeError("Input state dtype must be np.complex64 or " "np.complex128") size = state.size num_qubits = int(np.sqrt(size)).bit_length() - 1 dim = 1 << num_qubits if state.shape != (dim, dim) and state.shape != (2, 2) * num_qubits: raise ValueError("Input array does not represent a density matrix " "with shape `(2 ** n, 2 ** n)` or `(2, ..., 2)`.") _validate_qubit_mapping(qubit_map, self.qubits, num_qubits) # HACK: avoid circular import from cirq.sim.density_matrix_utils import to_valid_density_matrix # Do not enforce reshaping if the state all axes are dimension 2. _ = to_valid_density_matrix(density_matrix_rep=state.reshape(dim, dim), num_qubits=num_qubits, dtype=state.dtype) return self._expectation_from_density_matrix_no_validation( state, qubit_map)
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, 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 expectation_from_density_matrix( self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid, int], *, atol: float = 1e-7, check_preconditions: bool = True) -> float: """Evaluate the expectation of this PauliSum given a density matrix. See `PauliString.expectation_from_density_matrix`. Args: state: An array representing a valid density matrix. qubit_map: A map from all qubits used in this PauliSum to the indices of the qubits that `state` is defined over. atol: Absolute numerical tolerance. check_preconditions: Whether to check that `state` represents a valid density matrix. Returns: The expectation value of the input state. """ if any(abs(p.coefficient.imag) > 0.0001 for p in self): raise NotImplementedError( "Cannot compute expectation value of a non-Hermitian " "PauliString <{}>. Coefficient must be real.".format(self)) # FIXME: Avoid enforce specific complex type. This is necessary to # prevent an `apply_unitary` bug (Issue #2041). if state.dtype.kind != 'c': raise TypeError("Input state dtype must be np.complex64 or " "np.complex128") size = state.size num_qubits = int(np.sqrt(size)).bit_length() - 1 _validate_qubit_mapping(qubit_map, self.qubits, num_qubits) dim = int(np.sqrt(size)) if state.shape != (dim, dim) and state.shape != (2, 2) * num_qubits: raise ValueError("Input array does not represent a density matrix " "with shape `(2 ** n, 2 ** n)` or `(2, ..., 2)`.") if check_preconditions: # HACK: avoid circular import from cirq.sim.density_matrix_utils import to_valid_density_matrix # Do not enforce reshaping if the state all axes are dimension 2. _ = to_valid_density_matrix(density_matrix_rep=state.reshape( dim, dim), num_qubits=num_qubits, dtype=state.dtype, atol=atol) return sum( p._expectation_from_density_matrix_no_validation(state, qubit_map) for p in self)
def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]): """Set the density matrix to a new density matrix. Args: density_matrix_repr: If this is an int, the density matrix is set to the computational basis state corresponding to this state. Otherwise if this is a np.ndarray it is the full state, either a pure state or the full density matrix. If it is the pure state it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. If it is a mixed state it must be correctly sized and positive semidefinite with trace one. """ density_matrix = density_matrix_utils.to_valid_density_matrix( density_matrix_repr, len(self._qubit_map), self._dtype) density_matrix = np.reshape(density_matrix, self.simulator_state().density_matrix.shape) np.copyto(dst=self.simulator_state().density_matrix, src=density_matrix)
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)
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)