def test_3d2_with_spin(self):
        dimension = 3
        grid_length = 2
        n_spatial_orbitals = grid_length**dimension
        wigner_seitz_radius = 9.3

        spinless = False
        n_qubits = n_spatial_orbitals
        if not spinless:
            n_qubits *= 2
        n_particles_big = 9

        length_scale = wigner_seitz_length_scale(wigner_seitz_radius,
                                                 n_particles_big, dimension)

        self.grid3 = Grid(dimension, grid_length, length_scale)
        # Get the occupied orbitals of the plane-wave basis Hartree-Fock state.
        hamiltonian = jellium_model(self.grid3, spinless, plane_wave=True)
        hamiltonian = normal_ordered(hamiltonian)
        hamiltonian.compress()

        occupied_states = numpy.array(
            lowest_single_particle_energy_states(hamiltonian, n_particles_big))
        self.hf_state_index3 = numpy.sum(2**occupied_states)

        self.hf_state3 = csc_matrix(([1.0], ([self.hf_state_index3], [0])),
                                    shape=(2**n_qubits, 1))

        self.orbital_occupations3 = [
            digit == '1' for digit in bin(self.hf_state_index3)[2:]
        ][::-1]
        self.occupied_orbitals3 = [
            index for index, occupied in enumerate(self.orbital_occupations3)
            if occupied
        ]

        self.reversed_occupied_orbitals3 = list(self.occupied_orbitals3)
        for i in range(len(self.reversed_occupied_orbitals3)):
            self.reversed_occupied_orbitals3[i] = -1 + int(
                numpy.log2(self.hf_state3.shape[0])
            ) - self.reversed_occupied_orbitals3[i]

        self.reversed_hf_state_index3 = sum(
            2**index for index in self.reversed_occupied_orbitals3)

        operator = (FermionOperator('4^ 2^ 3^ 5 5 4', 2) +
                    FermionOperator('7^ 6^ 7 4', -3.7j) +
                    FermionOperator('3^ 7', 2.1))
        operator = normal_ordered(operator)
        transformed_operator = normal_ordered(
            fourier_transform(operator, self.grid3, spinless))

        expected = -0.2625 - 0.578125j
        # Calculated from expected = expectation(get_sparse_operator(
        #    transformed_operator), self.hf_state3)
        actual = expectation_db_operator_with_pw_basis_state(
            operator, self.reversed_occupied_orbitals3, n_spatial_orbitals,
            self.grid3, spinless)

        self.assertAlmostEqual(expected, actual)
    def test_2body_adjacent_number_operator_1D(self):
        operator = FermionOperator('3^ 2^ 2 1')
        operator = normal_ordered(operator)
        transformed_operator = normal_ordered(
            fourier_transform(operator, self.grid1, self.spinless))

        expected = expectation(get_sparse_operator(transformed_operator),
                               self.hf_state1)
        actual = expectation_db_operator_with_pw_basis_state(
            operator, self.reversed_occupied_orbitals1,
            self.n_spatial_orbitals, self.grid1, self.spinless)
        self.assertAlmostEqual(expected, actual)
Example #3
0
def operator_2d_fft_with_reordering(fermion_operator, system_size):
    """Apply the 2D FFT to an operator after reordering its modes.

    Args:
        fermion_operator (projectq.FermionOperator): Original operator.
        system_size (int): The side length of the system. register must
                           thus have system_size ** 2 qubits.

    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.
    """
    fermion_operator = normal_ordered(fermion_operator)
    grid = Grid(dimensions=2, length=system_size, scale=1.0)

    first_round_swaps, second_round_swaps = ffft_swap_networks(system_size,
                                                               n_dimensions=2)

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

    # Swap adjacent modes for all swaps in both rounds.
    for mode in swaps:
        fermion_operator = swap_adjacent_fermionic_modes(
            fermion_operator, mode)
        fermion_operator = normal_ordered(fermion_operator)

    # Apply the Fourier transform to the reordered operator.
    fft_result = fourier_transform(fermion_operator, grid, spinless=True)
    fft_result = normal_ordered(fft_result)

    # Undo the second round of swaps to restore the FFT's bit-reversed order.
    swap_mode_list = second_round_swaps
    swaps = [swap[0] for swap_layer in swap_mode_list for swap in swap_layer]
    for mode in swaps:
        fft_result = swap_adjacent_fermionic_modes(fft_result, mode)
        fft_result = normal_ordered(fft_result)

    return fft_result
    def test_8mode_ffft_with_external_swaps_on_single_logical_state(self):
        n_qubits = 8
        grid = Grid(dimensions=1, length=n_qubits, scale=1.0)

        eng = MainEngine()
        register = eng.allocate_qureg(n_qubits)

        state_index = 157

        prepare_logical_state(register, state_index)

        ffft(eng, register, n_qubits)
        Ph(3 * numpy.pi / 4) | register[0]
        eng.flush()
        wvfn = ordered_wavefunction(eng)

        fermion_operator = prepare_integer_fermion_operator(state_index)

        # Swap 01234567 to 45670123 for fourier_transform. Additionally,
        # the FFFT's ordering is 04261537, so swap 04261537 to 01234567,
        # and then 01234567 to 45670123.
        swap_mode_list = ([1, 3, 5, 2, 4, 1, 3, 5] +
                          [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3])
        for mode in swap_mode_list:
            fermion_operator = normal_ordered(fermion_operator)
            fermion_operator = swap_adjacent_fermionic_modes(
                fermion_operator, mode)

        ffft_result = fourier_transform(fermion_operator,
                                        grid, spinless=True)
        ffft_result = normal_ordered(ffft_result)

        # After the FFFT, swap 45670123 -> 01234567.
        swap_mode_list = [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3]
        for mode in swap_mode_list:
            ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode)

        converted_wvfn = numpy.zeros(2 ** n_qubits, dtype=complex)
        for term in ffft_result.terms:
            index = sum(2 ** site[0] for site in term)
            converted_wvfn[index] = ffft_result.terms[term]

        All(Measure) | register

        self.assertTrue(numpy.allclose(wvfn, converted_wvfn))
Example #5
0
    def test_4mode_ffft_with_external_swaps_all_logical_states(self):
        n_qubits = 4

        grid = Grid(dimensions=1, length=n_qubits, scale=1.0)

        for i in range(2**n_qubits):
            eng = MainEngine()
            register = eng.allocate_qureg(n_qubits)
            prepare_logical_state(register, i)

            ffft(eng, register, n_qubits)
            Ph(3 * numpy.pi / 4) | register[0]
            eng.flush()
            wvfn = ordered_wavefunction(eng)

            fermion_operator = prepare_integer_fermion_operator(i)

            # Reorder the modes for correct input to the FFFT.
            # Swap 0123 to 2301 for fourier_transform. Additionally, the
            # FFFT's ordering is 0213, so connect 0213 -> 0123 -> 2301.
            swap_mode_list = [1] + [1, 0, 2, 1]
            for mode in swap_mode_list:
                fermion_operator = normal_ordered(fermion_operator)
                fermion_operator = swap_adjacent_fermionic_modes(
                    fermion_operator, mode)

            ffft_result = fourier_transform(fermion_operator,
                                            grid,
                                            spinless=True)
            ffft_result = normal_ordered(ffft_result)

            swap_mode_list = [1, 0, 2, 1]  # After FFFT, swap 2301 -> 0123
            for mode in swap_mode_list:
                ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode)

            converted_wvfn = numpy.zeros(2**n_qubits, dtype=complex)
            for term in ffft_result.terms:
                index = sum(2**site[0] for site in term)
                converted_wvfn[index] = ffft_result.terms[term]

            All(Measure) | register

            self.assertTrue(numpy.allclose(wvfn, converted_wvfn))
    def test_8mode_ffft_with_external_swaps_equal_expectation_values(self):
        n_qubits = 8

        grid = Grid(dimensions=1, length=n_qubits, scale=1.0)
        dual_basis = jellium_model(grid, spinless=True, plane_wave=False)
        ffft_result = normal_ordered(dual_basis)

        # Swap 01234567 to 45670123 for fourier_transform. Additionally,
        # the FFFT's ordering is 04261537, so swap 04261537 to 01234567,
        # and then 01234567 to 45670123.
        swap_mode_list = ([1, 3, 5, 2, 4, 1, 3, 5] +
                          [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3])
        for mode in swap_mode_list:
            ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode)
            ffft_result = normal_ordered(ffft_result)

        ffft_result = fourier_transform(ffft_result,
                                        grid, spinless=True)
        ffft_result = normal_ordered(ffft_result)

        # After the FFFT, swap 45670123 -> 01234567.
        swap_mode_list = [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3]
        for mode in swap_mode_list:
            ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode)
            ffft_result = normal_ordered(ffft_result)

        jw_dual_basis = jordan_wigner(dual_basis)
        jw_plane_wave = jordan_wigner(ffft_result)

        # Do plane wave and dual basis calculations simultaneously.
        pw_engine = MainEngine()
        pw_wavefunction = pw_engine.allocate_qureg(n_qubits)
        db_engine = MainEngine()
        db_wavefunction = db_engine.allocate_qureg(n_qubits)

        pw_engine.flush()
        db_engine.flush()

        # Choose random state.
        state = numpy.zeros(2 ** n_qubits, dtype=complex)
        for i in range(len(state)):
            state[i] = (random.random() *
                        numpy.exp(1j * 2 * numpy.pi * random.random()))
        state /= numpy.linalg.norm(state)

        # Put randomly chosen state in the registers.
        pw_engine.backend.set_wavefunction(state, pw_wavefunction)
        db_engine.backend.set_wavefunction(state, db_wavefunction)

        prepare_logical_state(pw_wavefunction, i)
        prepare_logical_state(db_wavefunction, i)

        All(H) | [pw_wavefunction[1], pw_wavefunction[3]]
        All(H) | [db_wavefunction[1], db_wavefunction[3]]

        ffft(db_engine, db_wavefunction, n_qubits)
        Ph(3 * numpy.pi / 4) | db_wavefunction[0]

        # Flush the engine and compute expectation values and eigenvalues.
        pw_engine.flush()
        db_engine.flush()

        plane_wave_expectation_value = (
            pw_engine.backend.get_expectation_value(
                jw_dual_basis, pw_wavefunction))
        dual_basis_expectation_value = (
            db_engine.backend.get_expectation_value(
                jw_plane_wave, db_wavefunction))

        All(Measure) | pw_wavefunction
        All(Measure) | db_wavefunction

        self.assertAlmostEqual(plane_wave_expectation_value,
                               dual_basis_expectation_value)