def test_termination_in_local_minimum():
    grid_2x5 = cirq.GridQubit.rect(2, 5)
    q = list(cirq.NamedQubit(f'q{i}') for i in range(6))
    # The initial mapping looks like:
    #   _|_0_|_1_|_2_|_3_|_4_|
    #   0|q0 |   |   |   |q4 |
    #   1|q1 |q2 |   |q3 |q5 |
    initial_mapping = {
        q[0]: cirq.GridQubit(0, 0),
        q[1]: cirq.GridQubit(1, 0),
        q[2]: cirq.GridQubit(1, 1),
        q[3]: cirq.GridQubit(1, 3),
        q[4]: cirq.GridQubit(0, 4),
        q[5]: cirq.GridQubit(1, 4),
    }
    # Here's the idea:
    #   * there are two "clumps" of qubits: (q0,q1,q2) and (q3,q4,q5)
    #   * the active gate(s) span the two clumps
    #   * there are also gates on qubits within clumps
    #   * intra-clump gate cost contribution outweighs inter-clump gate cost
    # In that case, we need to swap qubits away from their respective clumps in
    # order to progress beyond any of the active gates. But we never will
    # because doing so would increase the overall cost due to the intra-clump
    # contributions. In fact, no greedy algorithm would be able to make progress
    # in this case.
    circuit = cirq.Circuit()
    # Cross-clump active gates
    circuit.append(
        [cirq.CNOT(q[0], q[3]),
         cirq.CNOT(q[1], q[4]),
         cirq.CNOT(q[2], q[5])])
    # Intra-clump q0,q1,q2
    circuit.append(
        [cirq.CNOT(q[0], q[1]),
         cirq.CNOT(q[0], q[2]),
         cirq.CNOT(q[1], q[2])])
    # Intra-clump q3,q4,q5
    circuit.append(
        [cirq.CNOT(q[3], q[4]),
         cirq.CNOT(q[3], q[5]),
         cirq.CNOT(q[4], q[5])])

    updater = SwapUpdater(circuit, grid_2x5, initial_mapping,
                          lambda q1, q2: [cirq.SWAP(q1, q2)])
    # Iterate until the SwapUpdater is finished or an assertion fails, keeping
    # track of the ops generated by the previous iteration.
    prev_it = list(updater.update_iteration())
    while not updater.dlists.all_empty():
        cur_it = list(updater.update_iteration())

        # If the current iteration adds a SWAP, it better not be the same SWAP
        # as the previous iteration...
        # If we pick the same SWAP twice in a row, then we're going in a loop
        # forever without making any progress!
        def _is_swap(ops):
            return len(ops) == 1 and ops[0] == cirq.SWAP(*ops[0].qubits)

        if _is_swap(prev_it) and _is_swap(cur_it):
            assert set(prev_it[0].qubits) != set(cur_it[0].qubits)
        prev_it = cur_it
def test_decomposed_swaps():
    Q = FIGURE_9A_PHYSICAL_QUBITS
    initial_mapping = dict(zip(q, Q))
    updater = SwapUpdater(FIGURE_9A_CIRCUIT, Q, initial_mapping,
                          generate_decomposed_swap)
    # First iteration adds a swap between Q0 and Q1.
    # generate_decomposed_swap decomposes that into sqrt-iswap operations.
    assert list(updater.update_iteration()) == list(
        generate_decomposed_swap(Q[0], Q[1]))

    # Whatever the decomposed operations are, they'd better be equivalent to a
    # SWAP.
    swap_unitary = cirq.unitary(cirq.Circuit(cirq.SWAP(Q[0], Q[1])))
    generated_unitary = cirq.unitary(
        cirq.Circuit(generate_decomposed_swap(Q[0], Q[1])))
    cirq.testing.assert_allclose_up_to_global_phase(swap_unitary,
                                                    generated_unitary,
                                                    atol=1e-8)
def test_example_9_iterations():
    Q = FIGURE_9A_PHYSICAL_QUBITS
    initial_mapping = dict(zip(q, Q))
    updater = SwapUpdater(FIGURE_9A_CIRCUIT, Q, initial_mapping,
                          lambda q1, q2: [cirq.SWAP(q1, q2)])

    # First iteration adds a swap between Q0 and Q1.
    assert list(updater.update_iteration()) == [cirq.SWAP(Q[0], Q[1])]
    # Next two iterations add the active gates as-is.
    assert list(updater.update_iteration()) == [cirq.CNOT(Q[1], Q[2])]
    assert list(updater.update_iteration()) == [cirq.CNOT(Q[5], Q[2])]
    # Next iteration adds a swap between Q1 and Q4.
    assert list(updater.update_iteration()) == [cirq.SWAP(Q[1], Q[4])]
    # Remaining gates are added as-is.
    assert list(updater.update_iteration()) == [cirq.CNOT(Q[4], Q[5])]
    assert list(updater.update_iteration()) == [cirq.CNOT(Q[1], Q[4])]
    assert list(updater.update_iteration()) == [cirq.CNOT(Q[4], Q[3])]
    # The final two gates are added in the same iteration, since they operate on
    # mutually exclusive qubits and are both simultaneously active.
    assert set(updater.update_iteration()) == {
        cirq.CNOT(Q[5], Q[4]), cirq.CNOT(Q[3], Q[0])
    }