def _has_unitary_(self): from cirq import protocols, devices qubits = devices.LineQid.for_gate(self) return all( protocols.has_unitary(op) for op in protocols.decompose_once_with_qubits( self._original, qubits))
def _decompose_single_qubit_operation(self, op: 'cirq.Operation', _: int) -> DecomposeResult: if isinstance(op.gate, ops.HPowGate) and op.gate.exponent == 1: return [ops.rx(np.pi).on(op.qubits[0]), ops.ry(-1 * np.pi / 2).on(op.qubits[0])] if protocols.has_unitary(op): gates = transformers.single_qubit_matrix_to_phased_x_z(protocols.unitary(op)) return [g.on(op.qubits[0]) for g in gates] return NotImplemented
def _strat_act_from_single_qubit_decompose( self, val: Any, qubits: Sequence['cirq.Qid'] ) -> bool: if num_qubits(val) == 1: if not has_unitary(val): return NotImplemented u = unitary(val) clifford_gate = SingleQubitCliffordGate.from_unitary(u) if clifford_gate is not None: # Gather the effective unitary applied so as to correct for the # global phase later. final_unitary = np.eye(2) for axis, quarter_turns in clifford_gate.decompose_rotation(): gate = axis ** (quarter_turns / 2) self._strat_apply_gate(gate, qubits) final_unitary = np.matmul(unitary(gate), final_unitary) # Find the entry with the largest magnitude in the input unitary. k = max(np.ndindex(*u.shape), key=lambda t: abs(u[t])) # Correct the global phase that wasn't conserved in the above # decomposition. self._state.apply_global_phase(u[k] / final_unitary[k]) return True return NotImplemented
def sample(program: 'cirq.Circuit', *, noise: 'cirq.NOISE_MODEL_LIKE' = None, param_resolver: Optional[study.ParamResolver] = None, repetitions: int = 1, dtype: Type[np.number] = np.complex64, seed: value.RANDOM_STATE_LIKE = None) -> study.TrialResult: """Simulates sampling from the given circuit. Args: program: The circuit to sample from. noise: Noise model to use while running the simulation. param_resolver: Parameters to run with the program. repetitions: The number of samples to take. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. Favors speed over precision by default, i.e. uses `numpy.complex64`. seed: The random seed to use for this simulator. """ noise_model = devices.NoiseModel.from_noise_model_like(noise) # State vector simulation is much faster, but only works if no randomness. if noise_model == devices.NO_NOISE and protocols.has_unitary(program): return sparse_simulator.Simulator(dtype=dtype, seed=seed).run( program=program, param_resolver=param_resolver, repetitions=repetitions) return density_matrix_simulator.DensityMatrixSimulator( dtype=dtype, noise=noise_model, seed=seed).run(program=program, param_resolver=param_resolver, repetitions=repetitions)
def sample(program: Union[circuits.Circuit, schedules.Schedule], *, noise: devices.NoiseModel = devices.NO_NOISE, param_resolver: Optional[study.ParamResolver] = None, repetitions: int = 1, dtype: Type[np.number] = np.complex64) -> study.TrialResult: """Simulates sampling from the given circuit or schedule. Args: program: The circuit or schedule to sample from. noise: Noise model to use while running the simulation. param_resolver: Parameters to run with the program. repetitions: The number of samples to take. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. Favors speed over precision by default, i.e. uses `numpy.complex64`. """ # State vector simulation is much faster, but only works if no randomness. if noise == devices.NO_NOISE and protocols.has_unitary(program): return sparse_simulator.Simulator(dtype=dtype).run( program=program, param_resolver=param_resolver, repetitions=repetitions) return density_matrix_simulator.DensityMatrixSimulator( dtype=dtype, noise=noise).run(program=program, param_resolver=param_resolver, repetitions=repetitions)
def _split_into_unitary_then_general( circuit: 'cirq.Circuit', ) -> Tuple['cirq.Circuit', 'cirq.Circuit']: """Splits the circuit into a unitary prefix and non-unitary suffix. The splitting happens in a per-qubit fashion. A non-unitary operation on qubit A will cause later operations on A to be part of the non-unitary suffix, but later operations on other qubits will continue to be put into the unitary part (as long as those qubits have had no non-unitary operation up to that point). """ blocked_qubits: Set[cirq.Qid] = set() unitary_prefix = circuits.Circuit() general_suffix = circuits.Circuit() for moment in circuit: unitary_part = [] general_part = [] for op in moment: qs = set(op.qubits) if not protocols.has_unitary(op) or not qs.isdisjoint(blocked_qubits): blocked_qubits |= qs if qs.isdisjoint(blocked_qubits): unitary_part.append(op) else: general_part.append(op) if unitary_part: unitary_prefix.append(ops.Moment(unitary_part)) if general_part: general_suffix.append(ops.Moment(general_part)) return unitary_prefix, general_suffix
def map_func(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': op_untagged = op.untagged if ( deep and isinstance(op_untagged, circuits.CircuitOperation) and merged_circuit_op_tag not in op.tags ): return op_untagged.replace( circuit=_rewrite_merged_k_qubit_unitaries( op_untagged.circuit, context=context, k=k, rewriter=rewriter, merged_circuit_op_tag=merged_circuit_op_tag, ).freeze() ).with_tags(*op.tags) if not (protocols.num_qubits(op) <= k and protocols.has_unitary(op)): return op if rewriter: return rewriter( cast(circuits.CircuitOperation, op_untagged) if merged_circuit_op_tag in op.tags else circuits.CircuitOperation(circuits.FrozenCircuit(op)) ) return ops.MatrixGate(protocols.unitary(op)).on(*op.qubits)
def _strat_act_on_stabilizer_ch_form_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnStabilizerCHFormArgs') -> bool: if num_qubits(val) == 1: if not has_unitary(val): return NotImplemented u = unitary(val) clifford_gate = SingleQubitCliffordGate.from_unitary(u) if clifford_gate is not None: # Gather the effective unitary applied so as to correct for the # global phase later. final_unitary = np.eye(2) for axis, quarter_turns in clifford_gate.decompose_rotation(): gate = None # type: Optional[cirq.Gate] if axis == pauli_gates.X: gate = common_gates.XPowGate(exponent=quarter_turns / 2) assert gate._act_on_(args) elif axis == pauli_gates.Y: gate = common_gates.YPowGate(exponent=quarter_turns / 2) assert gate._act_on_(args) else: assert axis == pauli_gates.Z gate = common_gates.ZPowGate(exponent=quarter_turns / 2) assert gate._act_on_(args) final_unitary = np.matmul(unitary(gate), final_unitary) # Find the entry with the largest magnitude in the input unitary. k = max(np.ndindex(*u.shape), key=lambda t: abs(u[t])) # Correct the global phase that wasn't conserved in the above # decomposition. args.state.omega *= u[k] / final_unitary[k] return True return NotImplemented
def final_state_vector( program: 'cirq.CIRCUIT_LIKE', *, initial_state: 'cirq.STATE_VECTOR_LIKE' = 0, param_resolver: 'cirq.ParamResolverOrSimilarType' = None, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, dtype: Type[np.number] = np.complex64, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> 'np.ndarray': """Returns the state vector resulting from acting operations on a state. By default the input state is the computational basis zero state, in which case the output is just the first column of the implied unitary matrix. Args: program: The circuit, gate, operation, or tree of operations to apply to the initial state in order to produce the result. param_resolver: 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: If an int, the state is set to the computational basis state corresponding to this state. Otherwise if this is a np.ndarray it is the full initial state. In this case it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. seed: The random seed to use for this simulator. Returns: The state vector resulting from applying the given unitary operations to the desired initial state. Specifically, a numpy array containing the the amplitudes in np.kron order, where the order of arguments to kron is determined by the qubit order argument (which defaults to just sorting the qubits that are present into an ascending order). Raises: ValueError: If the program doesn't have a well defined final state because it has non-unitary gates. """ circuit_like = _to_circuit(program) if not protocols.has_unitary( protocols.resolve_parameters(circuit_like, param_resolver)): raise ValueError( "Program doesn't have a single well defined final state vector " "because it is not unitary. " "Maybe you wanted `cirq.final_density_matrix`?\n" "\n" "Program: {!r}".format(circuit_like)) result = sparse_simulator.Simulator(dtype=dtype, seed=seed).simulate( program=circuit_like, initial_state=initial_state, qubit_order=qubit_order, param_resolver=param_resolver, ) return result.state_vector()
def can_merge_moment(m: 'cirq.Moment'): return all( protocols.num_qubits(op) == 1 and protocols.has_unitary(op) and tags_to_ignore.isdisjoint(op.tags) for op in m )
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.rsum2_cutoff, self.sum_prob_atol, self.grouping, initial_state=initial_state, ), ) return state = MPSState( qubit_map, self.rsum2_cutoff, self.sum_prob_atol, self.grouping, initial_state=initial_state, ) for moment in circuit: measurements: Dict[str, List[int]] = collections.defaultdict(list) for op in moment: 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_unitary(op): state.apply_unitary(op) else: raise NotImplementedError( f"Unrecognized operation: {op!r}") yield MPSSimulatorStepResult(measurements=measurements, state=state)
def _on_stuck_raise(self, op: ops.Operation): if len(op.qubits) == 1 and protocols.has_unitary(op): raise ValueError('Single qubit operation is not in the ' 'Clifford group: {!r}'.format(op)) raise TypeError("Don't know how to work with {!r}. " "It isn't composite or a 1-qubit operation " "with a known unitary effect.".format(op))
def map_func(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': if not (protocols.num_qubits(op) <= k and protocols.has_unitary(op)): return op if rewriter: return rewriter( cast(circuits.CircuitOperation, op.untagged ) if merged_circuit_op_tag in op.tags else circuits. CircuitOperation(circuits.FrozenCircuit(op))) return ops.MatrixGate(protocols.unitary(op)).on(*op.qubits)
def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs' ) -> Union[np.ndarray, None, NotImplementedType]: """Replicates the logic the simulators use to apply the equivalent sequence of GateOperations """ if not protocols.has_unitary(self.gate): return NotImplemented return protocols.apply_unitaries((self.gate.on(q) for q in self.qubits), self.qubits, args)
def is_supported_operation(op: 'cirq.Operation') -> bool: """Checks whether given operation can be simulated by this simulator.""" if protocols.is_measurement(op): return True if isinstance(op, GlobalPhaseOperation): return True if not protocols.has_unitary(op): return False u = cirq.unitary(op) if u.shape == (2, 2): return not SingleQubitCliffordGate.from_unitary(u) is None else: return op.gate in [cirq.CNOT, cirq.CZ]
def _decompose_two_qubit_operation(self, op: 'cirq.Operation', _) -> 'cirq.OP_TREE': if not protocols.has_unitary(op): return NotImplemented return two_qubit_to_cz.two_qubit_matrix_to_cz_operations( op.qubits[0], op.qubits[1], protocols.unitary(op), allow_partial_czs=self.allow_partial_czs, atol=self.atol, )
def is_supported_operation(op: 'cirq.Operation') -> bool: """Checks whether given operation can be simulated by this simulator.""" # TODO: support more general Pauli measurements if isinstance(op.gate, cirq.MeasurementGate): return True if isinstance(op, GlobalPhaseOperation): return True if not protocols.has_unitary(op): return False if len(op.qubits) == 1: u = unitary(op) return SingleQubitCliffordGate.from_unitary(u) is not None else: return op.gate in [cirq.CNOT, cirq.CZ]
def _strat_has_stabilizer_effect_from_unitary(val: Any) -> Optional[bool]: """Attempts to infer whether val has stabilizer effect from its unitary. Returns whether unitary of `val` normalizes the Pauli group. Works only for 2x2 unitaries. """ # Do not try this strategy if there is no unitary or if the number of # qubits is not 1 since that would be expensive. if not protocols.has_unitary(val) or protocols.num_qubits(val) != 1: return None unitary = protocols.unitary(val) return SingleQubitCliffordGate.from_unitary(unitary) is not None
def _strat_has_stabilizer_effect_from_unitary(val: Any) -> Optional[bool]: """Attempts to infer whether val has stabilizer effect from its unitary. Returns whether unitary of `val` normalizes the Pauli group. Works only for 2x2 unitaries. """ if not protocols.has_unitary(val): return None unitary = protocols.unitary(val) if unitary.shape == (2, 2): return SingleQubitCliffordGate.from_unitary(unitary) is not None return None
def decompose_operation(self, operation: 'cirq.Operation') -> ops.OP_TREE: if self.is_api_gate(operation): return operation assert protocols.has_unitary(operation), ( f'Operation {operation} that is not available on the IonQ API nor does it have a ' 'unitary matrix to use to decompose it to the API.') num_qubits = len(operation.qubits) if num_qubits == 1: return self._decompose_single_qubit(operation) if num_qubits == 2: return self._decompose_two_qubit(operation) raise ValueError('Operation {operation} not supported by IonQ API.')
def _needs_trajectories(circuit: circuits.Circuit) -> bool: """Checks if the circuit requires trajectory simulation.""" for op in circuit.all_operations(): test_op = (op if not protocols.is_parameterized(op) else protocols.resolve_parameters( op, {param: 1 for param in protocols.parameter_names(op)})) if not (protocols.has_unitary(test_op) or protocols.is_measurement(test_op)): return True return False
def assert_specifies_has_unitary_if_unitary(val: Any) -> None: """Checks that unitary values can be cheaply identifies as unitary.""" # pylint: disable=unused-variable __tracebackhide__ = True # pylint: enable=unused-variable assert not protocols.has_unitary(val) or hasattr(val, '_has_unitary_'), ( "Value is unitary but doesn't specify a _has_unitary_ method that " "can be used to cheaply verify this fact.\n" "\n" "val: {!r}".format(val))
def _keep(self, op: ops.Operation) -> bool: # Check if this is a CZ # Only keep partial CZ gates if allow_partial_czs if isinstance(op.gate, ops.CZPowGate) and ( self.allow_partial_czs or value.canonicalize_half_turns(op.gate.exponent) == 1): return True # Measurement in Z basis? if isinstance(op.gate, ops.MeasurementGate): return True # SingleQubit known matrix if len(op.qubits) == 1 and protocols.has_unitary(op): return True return False
def _keep(self, op: ops.Operation) -> bool: # Check if this is a CZ # Only keep partial CZ gates if allow_partial_czs if (isinstance(op, ops.GateOperation) and isinstance(op.gate, ops.CZPowGate) and (self.allow_partial_czs or op.gate.exponent == 1)): return True # Measurement? if ops.MeasurementGate.is_measurement(op): return True # SingleQubit known matrix if len(op.qubits) == 1 and protocols.has_unitary(op): return True return False
def _decompose_two_qubit_operation(self, op: 'cirq.Operation', _) -> DecomposeResult: if protocols.has_unitary(op): return two_qubit_to_sqrt_iswap.two_qubit_matrix_to_sqrt_iswap_operations( op.qubits[0], op.qubits[1], protocols.unitary(op), required_sqrt_iswap_count=self.required_sqrt_iswap_count, use_sqrt_iswap_inv=self.use_sqrt_iswap_inv, atol=self.atol, check_preconditions=False, clean_operations=True, ) if protocols.is_parameterized(op): return two_qubit_to_sqrt_iswap.parameterized_2q_op_to_sqrt_iswap_operations( op, use_sqrt_iswap_inv=self.use_sqrt_iswap_inv ) return NotImplemented
def _decompose_single_qubit_operation(self, op: 'cirq.Operation', moment_idx: int) -> DecomposeResult: """Decomposes (connected component of) 1-qubit operations using gates from this gateset. By default, rewrites every operation using a single `cirq.PhasedXZGate`. Args: op: A single-qubit operation (can be a tagged `cirq.CircuitOperation` wrapping a connected component of single qubit unitaries). moment_idx: Index of the moment in which operation `op` occurs. Returns: A `cirq.OP_TREE` implementing `op` using gates from this gateset OR None or NotImplemented if decomposition of `op` is unknown. """ return (ops.PhasedXZGate.from_matrix(protocols.unitary(op)).on( op.qubits[0]) if protocols.has_unitary(op) else NotImplemented)
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 _run(self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int) -> Dict[str, np.ndarray]: """See definition in `cirq.SimulatesSamples`.""" param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters( circuit, param_resolver) check_all_resolved(resolved_circuit) qubit_order = sorted(resolved_circuit.all_qubits()) # Simulate as many unitary operations as possible before having to # repeat work for each sample. unitary_prefix, general_suffix = ( split_into_matching_protocol_then_general(resolved_circuit, protocols.has_unitary) if protocols.has_unitary(self.noise) else (resolved_circuit[0:0], resolved_circuit)) step_result = None for step_result in self._base_iterator( circuit=unitary_prefix, qubit_order=qubit_order, initial_state=0, perform_measurements=False, ): pass assert step_result is not None # When an otherwise unitary circuit ends with non-demolition computation # basis measurements, we can sample the results more efficiently. general_ops = list(general_suffix.all_operations()) if all(isinstance(op.gate, ops.MeasurementGate) for op in general_ops): return step_result.sample_measurement_ops( measurement_ops=cast(List[ops.GateOperation], general_ops), repetitions=repetitions, seed=self._prng, ) qid_shape = protocols.qid_shape(qubit_order) intermediate_state = step_result.state_vector().reshape(qid_shape) return self._brute_force_samples( initial_state=intermediate_state, circuit=general_suffix, repetitions=repetitions, qubit_order=qubit_order, )
def _known_gate_with_no_decomposition(val: Any): """Checks whether `val` is a known gate with no default decomposition to default gateset.""" if isinstance(val, ops.MatrixGate): return protocols.qid_shape(val) not in [(2, ), (2, ) * 2, (2, ) * 3] if isinstance(val, ops.BaseDensePauliString) and not protocols.has_unitary(val): return True if isinstance(val, ops.ControlledGate): if protocols.is_parameterized(val): return True if isinstance( val.sub_gate, ops.MatrixGate) and protocols.num_qubits(val.sub_gate) > 1: return True if val.control_qid_shape != (2, ) * val.num_controls(): return True return _known_gate_with_no_decomposition(val.sub_gate) return False
def _strat_act_from_single_qubit_decompose( self, val: Any, qubits: Sequence['cirq.Qid'] ) -> bool: if num_qubits(val) == 1: if not has_unitary(val): return NotImplemented u = unitary(val) gate_and_phase = SingleQubitCliffordGate.from_unitary_with_global_phase(u) if gate_and_phase is not None: clifford_gate, global_phase = gate_and_phase # Apply gates. for gate in clifford_gate.decompose_gate(): self._strat_apply_gate(gate, qubits) # Apply global phase. self._state.apply_global_phase(global_phase) return True return NotImplemented