Beispiel #1
0
    def test_position_vector(self):

        # Test in 1D.
        grid = Grid(dimensions=1, length=4, scale=4.)
        test_output = [position_vector(i, grid) for i in range(grid.length)]
        correct_output = [-2, -1, 0, 1]
        self.assertEqual(correct_output, test_output)

        grid = Grid(dimensions=1, length=11, scale=2. * numpy.pi)
        for i in range(grid.length):
            self.assertAlmostEqual(-position_vector(i, grid),
                                   position_vector(grid.length - i - 1, grid))

        # Test in 2D.
        grid = Grid(dimensions=2, length=3, scale=3.)
        test_input = []
        test_output = []
        for i in range(3):
            for j in range(3):
                test_input += [(i, j)]
                test_output += [position_vector((i, j), grid)]
        correct_output = numpy.array([[-1., -1.], [-1., 0.], [-1., 1.],
                                      [0., -1.], [0., 0.], [0., 1.], [1., -1.],
                                      [1., 0.], [1., 1.]])
        self.assertAlmostEqual(0., numpy.amax(test_output - correct_output))
Beispiel #2
0
def dual_basis_u_operator(n_dimensions, grid_length, length_scale,
                          nuclear_charges, spinless):
    """Return the external potential operator in plane wave dual basis.

    Args:
        n_dimensions: An int giving the number of dimensions for the model.
        grid_length: Int, the number of points in one dimension of the grid.
        length_scale: Float, the real space length of a box dimension.
        nuclear_charges: 3D int array, the nuclear charges.
        spinless: Bool, whether to use the spinless model or not.

    Returns:
        operator: An instance of the FermionOperator class.
    """
    n_points = grid_length**n_dimensions
    volume = length_scale**float(n_dimensions)
    prefactor = -4.0 * numpy.pi / volume
    operator = None
    if spinless:
        spins = [None]
    else:
        spins = [0, 1]

    for grid_indices_p in itertools.product(range(grid_length),
                                            repeat=n_dimensions):
        coordinate_p = position_vector(grid_indices_p, grid_length,
                                       length_scale)
        for grid_indices_j in itertools.product(range(grid_length),
                                                repeat=n_dimensions):
            coordinate_j = position_vector(grid_indices_j, grid_length,
                                           length_scale)
            for momenta_indices in itertools.product(range(grid_length),
                                                     repeat=n_dimensions):
                momenta = momentum_vector(momenta_indices, grid_length,
                                          length_scale)
                momenta_squred = momenta.dot(momenta)
                if momenta_squred < EQ_TOLERANCE:
                    continue
                exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p)
                coefficient = prefactor / momenta_squred * \
                    nuclear_charges[grid_indices_j] * numpy.exp(exp_index)

                for spin_p in spins:
                    orbital_p = orbital_id(grid_length, grid_indices_p, spin_p)
                    operators = ((orbital_p, 1), (orbital_p, 0))
                    if operator is None:
                        operator = FermionOperator(operators, coefficient)
                    else:
                        operator += FermionOperator(operators, coefficient)

    return operator
Beispiel #3
0
def jordan_wigner_dual_basis_hamiltonian(grid, geometry=None, spinless=False,
                                         include_constant=False):
    """Return the dual basis Hamiltonian as QubitOperator.

    Args:
        grid (Grid): The discretization to use.
        geometry: A list of tuples giving the coordinates of each atom.
            example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))].
            Distances in atomic units. Use atomic symbols to specify atoms.
        spinless (bool): Whether to use the spinless model or not.
        include_constant (bool): Whether to include the Madelung constant.

    Returns:
        hamiltonian (QubitOperator)
    """
    jellium_op = jordan_wigner_dual_basis_jellium(
        grid, spinless, include_constant)

    if geometry is None:
        return jellium_op

    for item in geometry:
        if len(item[1]) != grid.dimensions:
            raise ValueError("Invalid geometry coordinate.")
        if item[0] not in periodic_hash_table:
            raise ValueError("Invalid nuclear element.")

    n_orbitals = grid.num_points()
    volume = grid.volume_scale()
    if spinless:
        n_qubits = n_orbitals
    else:
        n_qubits = 2 * n_orbitals
    prefactor = -2 * numpy.pi / volume
    external_potential = QubitOperator()

    for k_indices in grid.all_points_indices():
        momenta = momentum_vector(k_indices, grid)
        momenta_squared = momenta.dot(momenta)
        if momenta_squared < EQ_TOLERANCE:
            continue

        for p in range(n_qubits):
            index_p = grid_indices(p, grid, spinless)
            coordinate_p = position_vector(index_p, grid)

            for nuclear_term in geometry:
                coordinate_j = numpy.array(nuclear_term[1], float)

                exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p)
                coefficient = (prefactor / momenta_squared *
                               periodic_hash_table[nuclear_term[0]] *
                               numpy.exp(exp_index))
                external_potential += (QubitOperator((), coefficient) -
                                       QubitOperator(((p, 'Z'),), coefficient))

    return jellium_op + external_potential
Beispiel #4
0
def dual_basis_external_potential(grid, geometry, spinless):
    """Return the external potential in the dual basis of arXiv:1706.00023.

    Args:
        grid (Grid): The discretization to use.
        geometry: A list of tuples giving the coordinates of each atom.
            example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))].
            Distances in atomic units. Use atomic symbols to specify atoms.
        spinless (bool): Whether to use the spinless model or not.

    Returns:
        FermionOperator: The dual basis operator.
    """
    prefactor = -4.0 * numpy.pi / grid.volume_scale()
    operator = None
    if spinless:
        spins = [None]
    else:
        spins = [0, 1]
    for pos_indices in grid.all_points_indices():
        coordinate_p = position_vector(pos_indices, grid)
        for nuclear_term in geometry:
            coordinate_j = numpy.array(nuclear_term[1], float)
            for momenta_indices in grid.all_points_indices():
                momenta = momentum_vector(momenta_indices, grid)
                momenta_squared = momenta.dot(momenta)
                if momenta_squared < EQ_TOLERANCE:
                    continue
                exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p)
                coefficient = (prefactor / momenta_squared *
                               periodic_hash_table[nuclear_term[0]] *
                               numpy.exp(exp_index))

                for spin_p in spins:
                    orbital_p = orbital_id(grid, pos_indices, spin_p)
                    operators = ((orbital_p, 1), (orbital_p, 0))
                    if operator is None:
                        operator = FermionOperator(operators, coefficient)
                    else:
                        operator += FermionOperator(operators, coefficient)
    return operator
Beispiel #5
0
def inverse_fourier_transform(hamiltonian, n_dimensions, grid_length,
                              length_scale, spinless):
    """Apply Fourier tranform to change hamiltonian in plane wave dual basis.

    .. math::

        a^\dagger_v = \sqrt{1/N} \sum_m {c^\dagger_m \exp(i k_v r_m)}
        a_v = \sqrt{1/N} \sum_m {c_m \exp(-i k_v r_m)}

    Args:
        hamiltonian: The hamiltonian in plane wave dual basis.
        n_dimensions: An int giving the number of dimensions for the model.
        grid_length: Int, the number of points in one dimension of the grid.
        length_scale: Float, the real space length of a box dimension.
        spinless: Bool, whether to use the spinless model or not.

    Returns:
        hamiltonian_t: An instance of the FermionOperator class.
    """
    hamiltonian_t = None

    for term in hamiltonian.terms:
        transformed_term = None
        for ladder_operator in term:
            position_indices = grid_indices(ladder_operator[0], n_dimensions,
                                            grid_length, spinless)
            position_vec = position_vector(position_indices, grid_length,
                                           length_scale)
            new_basis = None
            for momentum_indices in itertools.product(range(grid_length),
                                                      repeat=n_dimensions):
                momentum_vec = momentum_vector(momentum_indices, grid_length,
                                               length_scale)
                if spinless:
                    spin = None
                else:
                    spin = ladder_operator[0] % 2
                orbital = orbital_id(grid_length, momentum_indices, spin)
                exp_index = -1.0j * numpy.dot(position_vec, momentum_vec)
                if ladder_operator[1] == 1:
                    exp_index *= -1.0

                element = FermionOperator(((orbital, ladder_operator[1]), ),
                                          numpy.exp(exp_index))
                if new_basis is None:
                    new_basis = element
                else:
                    new_basis += element

            new_basis *= numpy.sqrt(1.0 / float(grid_length**n_dimensions))

            if transformed_term is None:
                transformed_term = new_basis
            else:
                transformed_term *= new_basis
        if transformed_term is None:
            continue

        # Coefficient.
        transformed_term *= hamiltonian.terms[term]

        if hamiltonian_t is None:
            hamiltonian_t = transformed_term
        else:
            hamiltonian_t += transformed_term

    return hamiltonian_t
Beispiel #6
0
    def test_coefficients(self):

        # Test that the coefficients post-JW transform are as claimed in paper.
        grid = Grid(dimensions=2, length=3, scale=2.)
        spinless = 1
        n_orbitals = grid.num_points()
        n_qubits = (2**(1 - spinless)) * n_orbitals
        volume = grid.volume_scale()

        # Kinetic operator.
        kinetic = dual_basis_kinetic(grid, spinless)
        qubit_kinetic = jordan_wigner(kinetic)

        # Potential operator.
        potential = dual_basis_potential(grid, spinless)
        qubit_potential = jordan_wigner(potential)

        # Check identity.
        identity = tuple()
        kinetic_coefficient = qubit_kinetic.terms[identity]
        potential_coefficient = qubit_potential.terms[identity]

        paper_kinetic_coefficient = 0.
        paper_potential_coefficient = 0.
        for indices in grid.all_points_indices():
            momenta = momentum_vector(indices, grid)
            paper_kinetic_coefficient += float(n_qubits) * momenta.dot(
                momenta) / float(4. * n_orbitals)

            if momenta.any():
                potential_contribution = -numpy.pi * float(n_qubits) / float(
                    2. * momenta.dot(momenta) * volume)
                paper_potential_coefficient += potential_contribution

        self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient)
        self.assertAlmostEqual(potential_coefficient,
                               paper_potential_coefficient)

        # Check Zp.
        for p in range(n_qubits):
            zp = ((p, 'Z'), )
            kinetic_coefficient = qubit_kinetic.terms[zp]
            potential_coefficient = qubit_potential.terms[zp]

            paper_kinetic_coefficient = 0.
            paper_potential_coefficient = 0.
            for indices in grid.all_points_indices():
                momenta = momentum_vector(indices, grid)
                paper_kinetic_coefficient -= momenta.dot(momenta) / float(
                    4. * n_orbitals)

                if momenta.any():
                    potential_contribution = numpy.pi / float(
                        momenta.dot(momenta) * volume)
                    paper_potential_coefficient += potential_contribution

            self.assertAlmostEqual(kinetic_coefficient,
                                   paper_kinetic_coefficient)
            self.assertAlmostEqual(potential_coefficient,
                                   paper_potential_coefficient)

        # Check Zp Zq.
        if spinless:
            spins = [None]
        else:
            spins = [0, 1]

        for indices_a in grid.all_points_indices():
            for indices_b in grid.all_points_indices():

                paper_kinetic_coefficient = 0.
                paper_potential_coefficient = 0.

                position_a = position_vector(indices_a, grid)
                position_b = position_vector(indices_b, grid)
                differences = position_b - position_a

                for spin_a in spins:
                    for spin_b in spins:

                        p = orbital_id(grid, indices_a, spin_a)
                        q = orbital_id(grid, indices_b, spin_b)

                        if p == q:
                            continue

                        zpzq = ((min(p, q), 'Z'), (max(p, q), 'Z'))
                        if zpzq in qubit_potential.terms:
                            potential_coefficient = qubit_potential.terms[zpzq]

                        for indices_c in grid.all_points_indices():
                            momenta = momentum_vector(indices_c, grid)

                            if momenta.any():
                                potential_contribution = numpy.pi * numpy.cos(
                                    differences.dot(momenta)) / float(
                                        momenta.dot(momenta) * volume)
                                paper_potential_coefficient += (
                                    potential_contribution)

                        self.assertAlmostEqual(potential_coefficient,
                                               paper_potential_coefficient)