Example #1
0
 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))
Example #2
0
 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
Example #4
0
File: mux.py Project: sjuvekar/Cirq
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)
Example #5
0
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
Example #7
0
 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
Example #9
0
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()
Example #10
0
 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
     )
Example #11
0
    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)
Example #12
0
    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))
Example #13
0
 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)
Example #14
0
 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)
Example #15
0
 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]
Example #16
0
 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,
     )
Example #17
0
 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]
Example #18
0
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
Example #19
0
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
Example #20
0
 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.')
Example #21
0
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
Example #22
0
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
Example #24
0
    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
Example #25
0
 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)
Example #28
0
    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