def split_into_matching_protocol_then_general(
    circuit: cirq.AbstractCircuit, predicate: Callable[[cirq.Operation], bool]
) -> tuple[cirq.AbstractCircuit, cirq.AbstractCircuit]:
    """Splits the circuit into a matching prefix and non-matching suffix.

    The splitting happens in a per-qubit fashion. A non-matching operation on
    qubit A will cause later operations on A to be part of the non-matching
    suffix, but later operations on other qubits will continue to be put into
    the matching part (as long as those qubits have had no non-matching operation
    up to that point). Measurement keys are handled equivalently.
    """
    blocked_qubits: set[cirq.Qid] = set()
    blocked_keys: set[cirq.MeasurementKey] = set()
    matching_prefix = circuits.Circuit()
    general_suffix = circuits.Circuit()
    for moment in circuit:
        matching_part = []
        general_part = []
        for op in moment:
            qs = set(op.qubits)
            keys = protocols.measurement_keys_touched(op)
            if predicate(op) and qs.isdisjoint(blocked_qubits) and keys.isdisjoint(blocked_keys):
                matching_part.append(op)
            else:
                blocked_qubits |= qs
                blocked_keys |= keys
                general_part.append(op)
        if matching_part:
            matching_prefix.append(circuits.Moment(matching_part))
        if general_part:
            general_suffix.append(circuits.Moment(general_part))
    return matching_prefix, general_suffix
Splits the circuit into a matching prefix and non-matching suffix.

    The splitting happens in a per-qubit fashion. A non-matching operation on
    qubit A will cause later operations on A to be part of the non-matching
    suffix, but later operations on other qubits will continue to be put into
    the matching part (as long as those qubits have had no non-matching operation
    up to that point). Measurement keys are handled equivalently.
    
cirq-core/cirq/sim/simulator.py