Esempio n. 1
0
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
Esempio n. 2
0
def test_example_9_candidate_swaps():
    Q = FIGURE_9A_PHYSICAL_QUBITS
    initial_mapping = dict(zip(q, Q))
    updater = SwapUpdater(FIGURE_9A_CIRCUIT, Q, initial_mapping)

    gate = cirq.CNOT(Q[0], Q[2])
    candidates = list(updater.generate_candidate_swaps([gate]))
    # The swaps connected to either q0 or q2 are:
    #   (Q0, Q1), (Q0, Q3), (Q2, Q1), (Q2, Q5)
    # But swapping (Q0, Q3) or (Q2, Q5) would negatively impact the distance
    # between q0 and q2, so those swaps are discarded.
    assert candidates == [(Q[0], Q[1]), (Q[2], Q[1])]
Esempio n. 3
0
def test_example_9():
    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)])
    updated_circuit = cirq.Circuit(
        SwapUpdater(FIGURE_9A_CIRCUIT, Q, initial_mapping,
                    lambda q1, q2: [cirq.SWAP(q1, q2)]).add_swaps())
    assert updated_circuit == cirq.Circuit(cirq.SWAP(Q[0], Q[1]),
                                           cirq.CNOT(Q[1], Q[2]),
                                           cirq.CNOT(Q[5], Q[2]),
                                           cirq.SWAP(Q[1], Q[4]),
                                           cirq.CNOT(Q[4], Q[5]),
                                           cirq.CNOT(Q[1], Q[4]),
                                           cirq.CNOT(Q[4], Q[3]),
                                           cirq.CNOT(Q[5], Q[4]),
                                           cirq.CNOT(Q[3], Q[0]))
Esempio n. 4
0
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)
Esempio n. 5
0
def test_pentagonal_split_and_merge():
    grid_2x3 = cirq.GridQubit.rect(2, 3, 4, 4)
    logical_qubits = list(
        cirq.NamedQubit(f'{x}{i}') for x in ('a', 'b') for i in range(3))
    initial_mapping = dict(zip(logical_qubits, grid_2x3))
    a1, a2, a3, b1, b2, b3 = logical_qubits
    logical_circuit = cirq.Circuit(qm.normal_move(a1, b1),
                                   qm.normal_move(a2, a3),
                                   qm.merge_move(a3, b1, b3))

    updater = SwapUpdater(logical_circuit, grid_2x3, initial_mapping)
    updated_circuit = cirq.Circuit(updater.add_swaps())

    # Whereas the original circuit's initial mapping was not valid due to
    # adjacency constraints, the updated circuit is valid.
    device = cirq.google.Sycamore
    with pytest.raises(ValueError):
        device.validate_circuit(
            logical_circuit.transform_qubits(lambda q: initial_mapping.get(q)))
    device.validate_circuit(updated_circuit)
Esempio n. 6
0
def test_disconnected_components():
    grid_2x3 = cirq.GridQubit.rect(2, 3)
    logical_qubits = list(
        cirq.NamedQubit(f'{x}{i}') for x in ('a', 'b') for i in range(3))
    initial_mapping = dict(zip(logical_qubits, grid_2x3))
    a1, a2, a3, b1, b2, b3 = logical_qubits
    # a2 and b2 are disallowed. That splits the device graph into 2 disconnected
    # components and makes a3 unreachable from a1.
    circuit = cirq.Circuit(cirq.ISWAP(a1, a3))
    allowed_qubits = set(grid_2x3) - {initial_mapping[a2], initial_mapping[b2]}
    with pytest.raises(KeyError):
        print(
            cirq.Circuit(
                SwapUpdater(circuit, allowed_qubits,
                            initial_mapping).add_swaps()))
Esempio n. 7
0
def test_bad_initial_mapping():
    grid_2x3 = cirq.GridQubit.rect(2, 3)
    logical_qubits = list(
        cirq.NamedQubit(f'{x}{i}') for x in ('a', 'b') for i in range(3))
    initial_mapping = dict(zip(logical_qubits, grid_2x3))
    a1, a2, a3, b1, b2, b3 = logical_qubits
    # Initial mapping puts a1 on a qubit that we're not allowed to use, but the
    # circuit uses a1.
    # That should cause the swap updater to throw.
    circuit = cirq.Circuit(cirq.ISWAP(a1, a3))
    allowed_qubits = set(grid_2x3) - {initial_mapping[a1]}
    with pytest.raises(KeyError):
        print(
            cirq.Circuit(
                SwapUpdater(circuit, allowed_qubits,
                            initial_mapping).add_swaps()))
Esempio n. 8
0
def test_holes_in_device_graph():
    grid_2x3 = cirq.GridQubit.rect(2, 3, 4, 4)
    logical_qubits = list(
        cirq.NamedQubit(f'{x}{i}') for x in ('a', 'b') for i in range(3))
    initial_mapping = dict(zip(logical_qubits, grid_2x3))
    a1, a2, a3, b1, b2, b3 = logical_qubits
    # Circuit has a gate that operate on (b1, b3) qubits.
    # The best way to add swaps would be between (b1, b2) and (b2, b3), but
    # we aren't allowed to use b2, so we're forced to route swaps around it.
    circuit = cirq.Circuit(cirq.ISWAP(b1, b3)**0.5)
    allowed_qubits = set(grid_2x3) - {initial_mapping[b2]}

    updated_circuit = cirq.Circuit(
        SwapUpdater(circuit, allowed_qubits, initial_mapping).add_swaps())
    cirq.google.Sycamore.validate_circuit(updated_circuit)
    for op in updated_circuit.all_operations():
        assert initial_mapping[b2] not in op.qubits
Esempio n. 9
0
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])
    }
Esempio n. 10
0
def test_already_optimal():
    grid_2x3 = cirq.GridQubit.rect(2, 3)
    logical_qubits = list(
        cirq.NamedQubit(f'{x}{i}') for x in ('a', 'b') for i in range(3))
    initial_mapping = dict(zip(logical_qubits, grid_2x3))
    a1, a2, a3, b1, b2, b3 = logical_qubits
    # Circuit has gates that already operate only on adjacent qubits.
    circuit = cirq.Circuit(
        cirq.ISWAP(a1, b1),
        cirq.ISWAP(a2, a3),
        cirq.ISWAP(b1, b2),
        cirq.ISWAP(a3, b3),
        cirq.ISWAP(b2, b3),
    )
    updated_circuit = cirq.Circuit(
        SwapUpdater(circuit, grid_2x3, initial_mapping).add_swaps())
    # The circuit was already optimal, so we didn't need to add any extra
    # operations, just map the logical qubits to physical qubits.
    assert circuit.transform_qubits(
        lambda q: initial_mapping.get(q)) == updated_circuit