def test_ffft_like_sorting(self):
        # Determine how to order the modes for correct input to the FFFT.
        system_size = 8
        s = '{:0' + str(3) + 'b}'
        first_round = [int(s.format(i)[::-1], 2) for i in range(system_size)]
        second_round = (list(range(system_size // 2, system_size)) +
                        list(range(system_size // 2)))

        # Generate array of size system_size.
        arr = [(i,) for i in range(system_size)]

        # Create arrays whose swap networks to resort are the same as should
        # be applied to the fermionic modes.
        arr_first = [None] * system_size
        arr_second = [None] * system_size
        for i in range(system_size):
            arr_first[i] = (first_round[arr[i][0]],)
            arr_second[i] = (second_round[arr[i][0]],)

        # Find the two rounds of swaps for parallel bubble sort into
        # row-major order.
        key = (index_of_position_in_1d_array, (0,))
        first_round_swaps = parallel_bubble_sort(arr_first, key, system_size)
        second_round_swaps = parallel_bubble_sort(arr_second, key, system_size)

        # Create the full list of swaps.
        swap_mode_list = first_round_swaps + second_round_swaps[::-1]
        swaps = [swap[0] for swap_layer in swap_mode_list
                 for swap in swap_layer]

        # Compare with the correct full swap network for the FFFT.
        self.assertListEqual(swaps, [1, 3, 5, 2, 4, 1, 3, 5, 3, 2, 4,
                                     1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3])
def fermionic_reorder(register, input_order, target_order=None):
    """Fermionically reorder the given register.

    Maps the input Jordan-Wigner order to the output order.

    Args:
        register (projectq.QuReg): Quantum register to reorder.
        input_order (list): The initial Jordan-Wigner canonical order.
        target_order (list): The desired Jordan-Wigner canonical order.
    """
    if not target_order:
        target_order = list(range(len(input_order)))

    key = (index_of_position_in_1d_array, [0])

    input_swaps = parallel_bubble_sort(list(itertools.product(input_order)),
                                       key, len(input_order))
    input_swaps = [
        swap[0] for swap_layer in input_swaps for swap in swap_layer
    ]

    output_swaps = parallel_bubble_sort(list(itertools.product(target_order)),
                                        key, len(input_order))
    output_swaps = [
        swap[0] for swap_layer in output_swaps for swap in swap_layer
    ]

    # Invert the output swaps to go from the input to the target.
    swaps = input_swaps + output_swaps[::-1]

    # Swap adjacent modes for all swaps in both rounds.
    for mode in swaps:
        C(Z) | (register[mode], register[mode + 1])
        Swap | (register[mode], register[mode + 1])
    def test_sort_2d_grid_embedded_in_1d_array_integration_test(self):
        from functools import partial

        key_2d = partial(index_of_position_in_1d_array, (0, 1))
        system_size = 8
        arr = [(i, j) for i in range(system_size)
               for j in range(system_size)]

        self.assertListEqual(list(map(partial(key_2d, system_size), arr))[:16],
                             list(range(0, 64, 8)) + list(range(1, 65, 8)))

        self.assertFalse(is_sorted_array_of_nd_positions(arr, key_2d,
                                                         system_size))
        swaps = parallel_bubble_sort(arr, key_2d, system_size)
        self.assertEqual(len(swaps), 50)
        self.assertTrue(is_sorted_array_of_nd_positions(arr, key_2d,
                                                        system_size))
Ejemplo n.º 4
0
def ffft_2d(engine, register, system_size):
    """Apply the 2D fermionic fast Fourier transform to a register.

    Args:
        engine (projectq.MainEngine): The simulator engine with the
                                      register.
        register (projectq.QuReg): The register to apply the 2D FFFT to.
        system_size (int): The side length of the system. register must
                           thus have system_size ** 2 qubits.

    Notes:
        This algorithm uses radix-2 decimation-in-time, so system_size
        must be a binary power. This decimation is head recursive (the
        2-mode FFFT is applied as the first part of the 4-mode FFFT,
        which is applied as the first part of the 8-mode FFFT, etc).
    """
    # Apply the FFFT along one axis of the register.
    for i in range(system_size):
        ffft(engine, register[system_size * i:system_size * i + system_size],
             system_size)
        Ph(3 * numpy.pi / 4) | register[0]

    # To apply the FFFT along the second axis, we must fermionically
    # swap qubits into the correct positions. In 2D this is equivalent
    # to flipping all qubits across the "diagonal" of the grid.
    key_2d = (index_of_position_in_1d_array, (0, 1))
    arr = [(i, j) for i in range(system_size) for j in range(system_size)]
    swaps = parallel_bubble_sort(arr, key_2d, system_size)
    all_swaps = [swap for swap_layer in swaps for swap in swap_layer]

    # Fermionically swap into position to FFFT along the second axis.
    for swap in all_swaps:
        Swap | (register[swap[0]], register[swap[1]])
        C(Z) | (register[swap[0]], register[swap[1]])

    # Perform the FFFT along the second axis.
    for i in range(system_size):
        ffft(engine, register[system_size * i:system_size * i + system_size],
             system_size)
        Ph(3 * numpy.pi / 4) | register[0]

    # Undo the fermionic swap network to restore the original ordering.
    for swap in all_swaps[::-1]:
        Swap | (register[swap[0]], register[swap[1]])
        C(Z) | (register[swap[0]], register[swap[1]])
 def test_sort_already_sorted(self):
     key = (index_of_position_in_1d_array, (2, 1, 0))
     arr = [(0, i, j) for i in range(5) for j in range(5)]
     parallel_bubble_sort(arr, key, system_side_length=5)
     self.assertListEqual(arr, [(0, i, j) for i in range(5)
                                for j in range(5)])
 def test_sort_scrambled(self):
     key = (index_of_position_in_1d_array, (0, 1))
     arr = [(1, 2), (0, 1), (2, 1), (1, 0), (0, 0), (2, 0)]
     parallel_bubble_sort(arr, key, system_side_length=3)
     self.assertListEqual(arr, [(0, 0), (1, 0), (2, 0),
                                (0, 1), (2, 1), (1, 2)])
 def test_sort_reverse_long_no_optional_parameters_specified(self):
     key = (index_of_position_in_1d_array, (2, 0, 1))
     arr = [(0, i, 0) for i in range(10, -1, -1)]
     parallel_bubble_sort(arr, key, system_side_length=11)
     self.assertListEqual(arr, [(0, i, 0) for i in range(11)])
 def test_sort_4_elements(self):
     key = (index_of_position_in_1d_array, (2, 1, 0))
     arr = [(1, 0, 0), (0, 0, 1), (0, 1, 0), (0, 0, 0)]
     parallel_bubble_sort(arr, key, system_side_length=2)
     self.assertListEqual(arr,
                          [(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0)])
Ejemplo n.º 9
0
def ffft_swap_networks(system_size, n_dimensions):
    """Compute the two networks of swaps required for the FFFT.

    Args:
        system_size (int): The system side length (the system is assumed
                           to be a hypercube).
        n_dimensions (int): Number of dimensions for the system.

    Notes:
        The reordering of the operators is the opposite of the ordering
        that would be applied to a state. This reordering is given in
        two stages. First, the bit-reversed output of the FFFT circuit
        (e.g. 0, 2, 1, 3 which is the four numbers 0, 1, 2, 3 after
        reversing their bit representations) is swapped to the standard
        order (e.g. 0, 1, 2, 3). Second, the standard order is swapped
        to the split-halves ordering used in fourier_transform (e.g.
        2, 3, 0, 1 which swaps the left and right halves of that range).

        Both rounds must be applied before the FFFT, but after the FFFT
        only the second round needs to be undone so as to restore to the
        order of the FFFT circuit. All reorderings must be done in both
        dimensions, and because the operator is fermionic all swaps must
        also be.
    """
    # Create a string formatting rule for converting a number into binary
    # using log2(system_size) digits.
    binary_format_rule = '{:0' + str(int(numpy.log2(system_size))) + 'b}'

    # Use the formatting rule to generate the bit-reversed output of the FFFT.
    bit_reversed_order = [
        int(binary_format_rule.format(i)[::-1], 2) for i in range(system_size)
    ]

    # Generate the split-halves order of fourier_transform.
    split_halves_order = (list(range(system_size // 2, system_size)) +
                          list(range(system_size // 2)))

    # Enumerate system coordinates.
    coordinates = list(
        itertools.product(*[range(system_size) for i in range(n_dimensions)]))

    # Create arrays of the grid coordinates corresponding to the two orders.
    # Within each dimension, the grid coordinates are in either bit-reversed
    # or split-halves ordering.
    bit_reversed_order_coordinates = [None] * system_size**n_dimensions
    split_halves_order_coordinates = [None] * system_size**n_dimensions
    for i in range(system_size**n_dimensions):
        # e.g. for 4x4, this will be [(0, 0), (0, 2), (0, 1), (0, 3), (2, 0),
        # (2, 2), (2, 1), (2, 3), (1, 0), ...]; it is ordered along both dims
        # by the bit-reversed order 0, 2, 1, 3. The split-halves coordinates
        # are constructed similarly.
        bit_reversed_order_coordinates[i] = tuple(
            map(bit_reversed_order.__getitem__, coordinates[i]))
        split_halves_order_coordinates[i] = tuple(
            map(split_halves_order.__getitem__, coordinates[i]))

    # Find the two rounds of swaps for parallel bubble sort into
    # row-major order.
    key = (index_of_position_in_1d_array, range(n_dimensions - 1, -1, -1))
    first_round_swaps = parallel_bubble_sort(bit_reversed_order_coordinates,
                                             key, system_size)
    second_round_swaps = (parallel_bubble_sort(split_halves_order_coordinates,
                                               key, system_size)[::-1])

    return [first_round_swaps, second_round_swaps]