def _spinful_fermi_hubbard_model(x_dimension, y_dimension, tunneling, coulomb,
                                 chemical_potential, magnetic_field, periodic,
                                 particle_hole_symmetry):

    # Initialize operator.
    n_sites = x_dimension * y_dimension
    n_spin_orbitals = 2 * n_sites
    hubbard_model = FermionOperator()

    # Loop through sites and add terms.
    for site in range(n_sites):

        # Get indices of right and bottom neighbors
        right_neighbor = _right_neighbor(site, x_dimension, y_dimension,
                                         periodic)
        bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension,
                                           periodic)

        # Avoid double-counting edges when one of the dimensions is 2
        # and the system is periodic
        if x_dimension == 2 and periodic and site % 2 == 1:
            right_neighbor = None
        if y_dimension == 2 and periodic and site >= x_dimension:
            bottom_neighbor = None

        # Add hopping terms with neighbors to the right and bottom.
        if right_neighbor is not None:
            hubbard_model += _hopping_term(up_index(site),
                                           up_index(right_neighbor),
                                           -tunneling)
            hubbard_model += _hopping_term(down_index(site),
                                           down_index(right_neighbor),
                                           -tunneling)
        if bottom_neighbor is not None:
            hubbard_model += _hopping_term(up_index(site),
                                           up_index(bottom_neighbor),
                                           -tunneling)
            hubbard_model += _hopping_term(down_index(site),
                                           down_index(bottom_neighbor),
                                           -tunneling)

        # Add local pair Coulomb interaction terms.
        hubbard_model += _coulomb_interaction_term(n_spin_orbitals,
                                                   up_index(site),
                                                   down_index(site), coulomb,
                                                   particle_hole_symmetry)

        # Add chemical potential and magnetic field terms.
        hubbard_model += number_operator(n_spin_orbitals, up_index(site),
                                         -chemical_potential - magnetic_field)
        hubbard_model += number_operator(n_spin_orbitals, down_index(site),
                                         -chemical_potential + magnetic_field)

    return hubbard_model
def _coulomb_interaction_term(n_sites,
                              i,
                              j,
                              coefficient,
                              particle_hole_symmetry,
                              bosonic=False):
    op_class = BosonOperator if bosonic else FermionOperator
    number_operator_i = number_operator(n_sites, i, parity=2 * bosonic - 1)
    number_operator_j = number_operator(n_sites, j, parity=2 * bosonic - 1)
    if particle_hole_symmetry:
        number_operator_i -= op_class((), 0.5)
        number_operator_j -= op_class((), 0.5)
    return coefficient * number_operator_i * number_operator_j
    def test_jw_get_ground_states_by_particle_number_herm_conserving(self):
        # Initialize a particle-number-conserving Hermitian operator
        ferm_op = FermionOperator('0^ 1') + FermionOperator('1^ 0') + \
            FermionOperator('1^ 2') + FermionOperator('2^ 1') + \
            FermionOperator('1^ 3', -.4) + FermionOperator('3^ 1', -.4)
        jw_hamiltonian = jordan_wigner(ferm_op)
        sparse_operator = get_sparse_operator(jw_hamiltonian)
        n_qubits = 4

        # Test each possible particle number
        for particle_number in range(n_qubits):
            # Get the ground energy and ground states at this particle number
            energy, states = jw_get_ground_states_by_particle_number(
                sparse_operator, particle_number)
            # Construct particle number operator
            num_op = get_sparse_operator(number_operator(n_qubits))
            # For each vector returned, make sure that it is indeed an
            # eigenvector of the original operator with the returned eigenvalue
            # and that it has the correct particle number
            for vec in states:
                # Check that it's an eigenvector with the correct eigenvalue
                op_vec_product = sparse_operator.dot(vec)
                difference = op_vec_product - energy * vec
                discrepancy = 0.
                if difference.nnz:
                    discrepancy = max(map(abs, difference.data))
                self.assertAlmostEqual(0., discrepancy)
                # Check that it has the correct particle number
                num = expectation(num_op, vec)
                self.assertAlmostEqual(num, particle_number)
示例#4
0
 def test_jordan_wigner_transm_op(self):
     n = number_operator(self.n_qubits)
     n_jw = jordan_wigner(n)
     self.assertEqual(self.n_qubits + 1, len(n_jw.terms))
     self.assertEqual(self.n_qubits / 2., n_jw.terms[()])
     for qubit in range(self.n_qubits):
         operators = ((qubit, 'Z'), )
         self.assertEqual(n_jw.terms[operators], -0.5)
示例#5
0
    def test_bk_jw_number_operators(self):
        # Check if a number operator has the same spectrum in both
        # JW and BK representations
        n_qubits = 2
        n1 = number_operator(n_qubits, 0)
        n2 = number_operator(n_qubits, 1)
        n = n1 + n2

        jw_n = jordan_wigner(n)
        bk_n = bravyi_kitaev(n)

        # Diagonalize and make sure the spectra are the same.
        jw_spectrum = eigenspectrum(jw_n)
        bk_spectrum = eigenspectrum(bk_n)

        self.assertAlmostEqual(0., numpy.amax(
            numpy.absolute(jw_spectrum - bk_spectrum)))
示例#6
0
    def test_bk_jw_number_operator(self):
        # Check if number operator has the same spectrum in both
        # BK and JW representations
        n = number_operator(1, 0)
        jw_n = jordan_wigner(n)
        bk_n = bravyi_kitaev(n)

        # Diagonalize and make sure the spectra are the same.
        jw_spectrum = eigenspectrum(jw_n)
        bk_spectrum = eigenspectrum(bk_n)

        self.assertAlmostEqual(0., numpy.amax(
            numpy.absolute(jw_spectrum - bk_spectrum)))
示例#7
0
    def test_bk_jw_number_operator_scaled(self):
        # Check if number operator has the same spectrum in both
        # JW and BK representations
        n_qubits = 1
        n = number_operator(n_qubits, 0, coefficient=2)  # eigenspectrum (0,2)
        jw_n = jordan_wigner(n)
        bk_n = bravyi_kitaev(n)

        # Diagonalize and make sure the spectra are the same.
        jw_spectrum = eigenspectrum(jw_n)
        bk_spectrum = eigenspectrum(bk_n)

        self.assertAlmostEqual(0., numpy.amax(
                               numpy.absolute(jw_spectrum - bk_spectrum)))
    def test_jw_restrict_jellium_ground_state_integration(self):
        n_qubits = 4
        grid = Grid(dimensions=1, length=n_qubits, scale=1.0)
        jellium_hamiltonian = jordan_wigner_sparse(
            jellium_model(grid, spinless=False))

        #  2 * n_qubits because of spin
        number_sparse = jordan_wigner_sparse(number_operator(2 * n_qubits))

        restricted_number = jw_number_restrict_operator(number_sparse, 2)
        restricted_jellium_hamiltonian = jw_number_restrict_operator(
            jellium_hamiltonian, 2)

        energy, ground_state = get_ground_state(restricted_jellium_hamiltonian)

        number_expectation = expectation(restricted_number, ground_state)
        self.assertAlmostEqual(number_expectation, 2)
def _spinless_fermi_hubbard_model(x_dimension, y_dimension, tunneling, coulomb,
                                  chemical_potential, magnetic_field, periodic,
                                  particle_hole_symmetry):

    # Initialize operator.
    n_sites = x_dimension * y_dimension
    hubbard_model = FermionOperator()

    # Loop through sites and add terms.
    for site in range(n_sites):

        # Get indices of right and bottom neighbors
        right_neighbor = _right_neighbor(site, x_dimension, y_dimension,
                                         periodic)
        bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension,
                                           periodic)

        # Avoid double-counting edges when one of the dimensions is 2
        # and the system is periodic
        if x_dimension == 2 and periodic and site % 2 == 1:
            right_neighbor = None
        if y_dimension == 2 and periodic and site >= x_dimension:
            bottom_neighbor = None

        # Add terms that couple with neighbors to the right and bottom.
        if right_neighbor is not None:
            # Add hopping term
            hubbard_model += _hopping_term(site, right_neighbor, -tunneling)
            # Add local Coulomb interaction term
            hubbard_model += _coulomb_interaction_term(n_sites, site,
                                                       right_neighbor, coulomb,
                                                       particle_hole_symmetry)
        if bottom_neighbor is not None:
            # Add hopping term
            hubbard_model += _hopping_term(site, bottom_neighbor, -tunneling)
            # Add local Coulomb interaction term
            hubbard_model += _coulomb_interaction_term(n_sites, site,
                                                       bottom_neighbor,
                                                       coulomb,
                                                       particle_hole_symmetry)

        # Add chemical potential. The magnetic field doesn't contribute.
        hubbard_model += number_operator(n_sites, site, -chemical_potential)

    return hubbard_model
    def test_jw_restrict_operator(self):
        """Test the scheme for restricting JW encoded operators to number"""
        # Make a Hamiltonian that cares mostly about number of electrons
        n_qubits = 6
        target_electrons = 3
        penalty_const = 100.
        number_sparse = jordan_wigner_sparse(number_operator(n_qubits))
        bias_sparse = jordan_wigner_sparse(
            sum([FermionOperator(((i, 1), (i, 0)), 1.0) for i
                 in range(n_qubits)], FermionOperator()))
        hamiltonian_sparse = penalty_const * (
            number_sparse - target_electrons *
            scipy.sparse.identity(2**n_qubits)).dot(
            number_sparse - target_electrons *
            scipy.sparse.identity(2**n_qubits)) + bias_sparse

        restricted_hamiltonian = jw_number_restrict_operator(
            hamiltonian_sparse, target_electrons, n_qubits)
        true_eigvals, _ = eigh(hamiltonian_sparse.A)
        test_eigvals, _ = eigh(restricted_hamiltonian.A)

        self.assertAlmostEqual(norm(true_eigvals[:20] - test_eigvals[:20]),
                               0.0)
示例#11
0
 def test_boson_number_reversed(self):
     n_term_rev2 = BosonOperator(((2, 0), (2, 1)))
     number_op2 = number_operator(3, 2, parity=1)
     expected = BosonOperator(()) + number_op2
     self.assertTrue(normal_ordered(n_term_rev2) == expected)
 def test_is_two_body_number_conserving_number(self):
     op = number_operator(5, 3)
     self.assertTrue(op.is_two_body_number_conserving())
示例#13
0
def jw_get_ground_states_by_particle_number(sparse_operator,
                                            particle_number,
                                            sparse=True,
                                            num_eigs=3):
    """For a Jordan-Wigner encoded Hermitian operator, compute the lowest
    eigenvalue and eigenstates at a particular particle number. The operator
    must conserve particle number.

    Args:
        sparse_operator(sparse): A Jordan-Wigner encoded sparse operator.
        particle_number(int): The particle number at which to compute
            ground states.
        sparse(boolean, optional): Whether to use sparse eigensolver.
            Default is True.
        num_eigs(int, optional): The number of eigenvalues to request from the
            sparse eigensolver. Needs to be at least as large as the degeneracy
            of the ground energy in order to obtain all ground states.
            Only used if `sparse=True`. Default is 3.

    Returns:
        ground_energy(float): The lowest eigenvalue of sparse_operator within
            the eigenspace of the number operator corresponding to
            particle_number.

        ground_states(list[ndarray]): A list of the corresponding eigenstates.

    Warning:
        The running time of this method is exponential in the number of qubits.
    """
    # Check if operator is Hermitian
    if not is_hermitian(sparse_operator):
        raise ValueError('sparse_operator must be Hermitian.')

    n_qubits = int(numpy.log2(sparse_operator.shape[0]))

    # Check if operator conserves particle number
    sparse_num_op = jordan_wigner_sparse(number_operator(n_qubits))
    com = commutator(sparse_num_op, sparse_operator)
    if com.nnz:
        maxval = max(map(abs, com.data))
        if maxval > EQ_TOLERANCE:
            raise ValueError('sparse_operator must conserve particle number.')

    # Get the operator restricted to the subspace of the desired
    # particle number
    restricted_operator = jw_number_restrict_operator(sparse_operator,
                                                      particle_number,
                                                      n_qubits)

    if sparse and num_eigs >= restricted_operator.shape[0] - 1:
        # Restricted operator too small for sparse eigensolver
        sparse = False

    # Compute eigenvalues and eigenvectors
    if sparse:
        eigvals, eigvecs = scipy.sparse.linalg.eigsh(restricted_operator,
                                                     k=num_eigs,
                                                     which='SA')
        if abs(max(eigvals) - min(eigvals)) < EQ_TOLERANCE:
            warnings.warn(
                'The lowest {} eigenvalues are degenerate. '
                'There may be more ground states; increase '
                'num_eigs or set sparse=False to get '
                'them.'.format(num_eigs), RuntimeWarning)
    else:
        dense_restricted_operator = restricted_operator.toarray()
        eigvals, eigvecs = numpy.linalg.eigh(dense_restricted_operator)

    # Get the ground energy
    if sparse:
        ground_energy = sorted(eigvals)[0]
    else:
        # No need to sort in the case of dense eigenvalue computation
        ground_energy = eigvals[0]

    # Get the indices of eigenvectors corresponding to the ground energy
    ground_state_indices = numpy.where(
        abs(eigvals - ground_energy) < EQ_TOLERANCE)

    ground_states = list()

    for i in ground_state_indices[0]:
        restricted_ground_state = eigvecs[:, i]
        # Expand this ground state to the whole vector space
        number_indices = jw_number_indices(particle_number, n_qubits)
        expanded_ground_state = scipy.sparse.csc_matrix(
            (restricted_ground_state.flatten(),
             (number_indices, [0] * len(number_indices))),
            shape=(2**n_qubits, 1))
        # Add the expanded ground state to the list
        ground_states.append(expanded_ground_state)

    return ground_energy, ground_states
示例#14
0
def reverse_jordan_wigner(qubit_operator, n_qubits=None):
    """Transforms a QubitOperator into a FermionOperator using the
    Jordan-Wigner transform.

    Operators are mapped as follows:
    Z_j -> I - 2 a^\dagger_j a_j
    X_j -> (a^\dagger_j + a_j) Z_{j-1} Z_{j-2} .. Z_0
    Y_j -> i (a^\dagger_j - a_j) Z_{j-1} Z_{j-2} .. Z_0

    Args:
        qubit_operator: the QubitOperator to be transformed.
        n_qubits: the number of qubits term acts on. If not set, defaults
                to the maximum qubit number acted on by term.

    Returns:
        transformed_term: An instance of the FermionOperator class.

    Raises:
        TypeError: Input must be a QubitOperator.
        TypeError: Invalid number of qubits specified.
        TypeError: Pauli operators must be X, Y or Z.
    """
    if not isinstance(qubit_operator, QubitOperator):
        raise TypeError('Input must be a QubitOperator.')
    if n_qubits is None:
        n_qubits = count_qubits(qubit_operator)
    if n_qubits < count_qubits(qubit_operator):
        raise ValueError('Invalid number of qubits specified.')

    # Loop through terms.
    transformed_operator = FermionOperator()
    for term in qubit_operator.terms:
        transformed_term = FermionOperator(())
        if term:
            working_term = QubitOperator(term)
            pauli_operator = term[-1]
            while pauli_operator is not None:

                # Handle Pauli Z.
                if pauli_operator[1] == 'Z':
                    transformed_pauli = FermionOperator(
                        ()) + number_operator(n_qubits, pauli_operator[0], -2.)

                # Handle Pauli X and Y.
                else:
                    raising_term = FermionOperator(((pauli_operator[0], 1), ))
                    lowering_term = FermionOperator(((pauli_operator[0], 0), ))
                    if pauli_operator[1] == 'Y':
                        raising_term *= 1.j
                        lowering_term *= -1.j

                    transformed_pauli = raising_term + lowering_term

                    # Account for the phase terms.
                    for j in reversed(range(pauli_operator[0])):
                        z_term = QubitOperator(((j, 'Z'), ))
                        working_term = z_term * working_term
                    term_key = list(working_term.terms)[0]
                    transformed_pauli *= working_term.terms[term_key]
                    working_term.terms[list(working_term.terms)[0]] = 1.

                # Get next non-identity operator acting below 'working_qubit'.
                assert len(working_term.terms) == 1
                working_qubit = pauli_operator[0] - 1
                for working_operator in reversed(list(working_term.terms)[0]):
                    if working_operator[0] <= working_qubit:
                        pauli_operator = working_operator
                        break
                    else:
                        pauli_operator = None

                # Multiply term by transformed operator.
                transformed_term *= transformed_pauli

        # Account for overall coefficient
        transformed_term *= qubit_operator.terms[term]
        transformed_operator += transformed_term
    return transformed_operator
示例#15
0
def fermi_hubbard(x_dimension,
                  y_dimension,
                  tunneling,
                  coulomb,
                  chemical_potential=0.,
                  magnetic_field=0.,
                  periodic=True,
                  spinless=False,
                  particle_hole_symmetry=False):
    """Return symbolic representation of a Fermi-Hubbard Hamiltonian.

    The idea of this model is that some fermions move around on a grid and the
    energy of the model depends on where the fermions are.
    The Hamiltonians of this model live on a grid of dimensions
    `x_dimension` x `y_dimension`.
    The grid can have periodic boundary conditions or not.
    In the standard Fermi-Hubbard model (which we call the "spinful" model),
    there is room for an "up" fermion and a "down" fermion at each site on the
    grid. In this model, there are a total of `2N` spin-orbitals,
    where `N = x_dimension * y_dimension` is the number of sites.
    In the spinless model, there is only one spin-orbital per site
    for a total of `N`.

    The Hamiltonian for the spinful model has the form

    .. math::

        \\begin{align}
        H = &- t \sum_{\langle i,j \\rangle} \sum_{\sigma}
                     (a^\dagger_{i, \sigma} a_{j, \sigma} +
                      a^\dagger_{j, \sigma} a_{i, \sigma})
             + U \sum_{i} a^\dagger_{i, \\uparrow} a_{i, \\uparrow}
                         a^\dagger_{j, \downarrow} a_{j, \downarrow}
            \\\\
            &- \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma}
             - h \sum_i (a^\dagger_{i, \\uparrow} a_{i, \\uparrow} -
                       a^\dagger_{i, \downarrow} a_{i, \downarrow})
        \\end{align}

    where

        - The indices :math:`\langle i, j \\rangle` run over pairs
          :math:`i` and :math:`j` of sites that are connected to each other
          in the grid
        - :math:`\sigma \in \\{\\uparrow, \downarrow\\}` is the spin
        - :math:`t` is the tunneling amplitude
        - :math:`U` is the Coulomb potential
        - :math:`\mu` is the chemical potential
        - :math:`h` is the magnetic field

    One can also construct the Hamiltonian for the spinless model, which
    has the form

    .. math::

        H = - t \sum_{k=1}^{N-1} (a_k^\dagger a_{k + 1} + a_{k+1}^\dagger a_k)
            + U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1}
            - \mu \sum_{k=1}^N a_k^\dagger a_k.

    Args:
        x_dimension (int): The width of the grid.
        y_dimension (int): The height of the grid.
        tunneling (float): The tunneling amplitude :math:`t`.
        coulomb (float): The attractive local interaction strength :math:`U`.
        chemical_potential (float, optional): The chemical potential
            :math:`\mu` at each site. Default value is 0.
        magnetic_field (float, optional): The magnetic field :math:`h`
            at each site. Default value is 0. Ignored for the spinless case.
        periodic (bool, optional): If True, add periodic boundary conditions.
            Default is True.
        spinless (bool, optional): If True, return a spinless Fermi-Hubbard
            model. Default is False.
        particle_hole_symmetry (bool, optional): If False, the repulsion
            term corresponds to:

            .. math::

                U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1}

            If True, the repulsion term is replaced by:

            .. math::

                U \sum_{k=1}^{N-1} (a_k^\dagger a_k - \\frac12)
                                   (a_{k+1}^\dagger a_{k+1} - \\frac12)

            which is unchanged under a particle-hole transformation.
            Default is False

    Returns:
        hubbard_model: An instance of the FermionOperator class.
    """
    tunneling = float(tunneling)
    coulomb = float(coulomb)
    chemical_potential = float(chemical_potential)
    magnetic_field = float(magnetic_field)

    # Initialize fermion operator class.
    n_sites = x_dimension * y_dimension
    n_spin_orbitals = n_sites

    if not spinless:
        n_spin_orbitals *= 2

    hubbard_model = FermionOperator.zero()

    # Select particle-hole symmetry
    if particle_hole_symmetry:
        coulomb_shift = FermionOperator((), 0.5)
    else:
        coulomb_shift = FermionOperator.zero()

    # Loop through sites and add terms.
    for site in range(n_sites):
        # Add chemical potential to the spinless case. The magnetic field
        # doesn't contribute.
        if spinless and chemical_potential:
            hubbard_model += number_operator(n_spin_orbitals, site,
                                             -chemical_potential)

        # With spin, add the chemical potential and magnetic field terms.
        elif not spinless:
            hubbard_model += number_operator(
                n_spin_orbitals, up_index(site),
                -chemical_potential - magnetic_field)
            hubbard_model += number_operator(
                n_spin_orbitals, down_index(site),
                -chemical_potential + magnetic_field)

            # Add local pair interaction terms.
            operator_1 = number_operator(n_spin_orbitals,
                                         up_index(site)) - coulomb_shift
            operator_2 = number_operator(n_spin_orbitals,
                                         down_index(site)) - coulomb_shift
            hubbard_model += coulomb * operator_1 * operator_2

        # Index coupled orbitals.
        right_neighbor = site + 1
        bottom_neighbor = site + x_dimension

        # Account for periodic boundaries.
        if periodic:
            if (x_dimension > 2) and ((site + 1) % x_dimension == 0):
                right_neighbor -= x_dimension
            if (y_dimension > 2) and (site + x_dimension + 1 > n_sites):
                bottom_neighbor -= x_dimension * y_dimension

        # Add transition to neighbor on right.
        if (right_neighbor) % x_dimension or (periodic and x_dimension > 2):
            if spinless:
                # Add Coulomb term.
                operator_1 = number_operator(n_spin_orbitals, site,
                                             1.0) - coulomb_shift
                operator_2 = number_operator(n_spin_orbitals, right_neighbor,
                                             1.0) - coulomb_shift
                hubbard_model += coulomb * operator_1 * operator_2

                # Add hopping term.
                operators = ((site, 1), (right_neighbor, 0))

            else:
                # Add hopping term.
                operators = ((up_index(site), 1), (up_index(right_neighbor),
                                                   0))
                hopping_term = FermionOperator(operators, -tunneling)
                hubbard_model += hopping_term
                hubbard_model += hermitian_conjugated(hopping_term)

                operators = ((down_index(site), 1),
                             (down_index(right_neighbor), 0))

            hopping_term = FermionOperator(operators, -tunneling)
            hubbard_model += hopping_term
            hubbard_model += hermitian_conjugated(hopping_term)

        # Add transition to neighbor below.
        if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2):
            if spinless:
                # Add Coulomb term.
                operator_1 = number_operator(n_spin_orbitals,
                                             site) - coulomb_shift
                operator_2 = number_operator(n_spin_orbitals,
                                             bottom_neighbor) - coulomb_shift
                hubbard_model += coulomb * operator_1 * operator_2

                # Add hopping term.
                operators = ((site, 1), (bottom_neighbor, 0))

            else:
                # Add hopping term.
                operators = ((up_index(site), 1), (up_index(bottom_neighbor),
                                                   0))
                hopping_term = FermionOperator(operators, -tunneling)
                hubbard_model += hopping_term
                hubbard_model += hermitian_conjugated(hopping_term)

                operators = ((down_index(site), 1),
                             (down_index(bottom_neighbor), 0))

            hopping_term = FermionOperator(operators, -tunneling)
            hubbard_model += hopping_term
            hubbard_model += hermitian_conjugated(hopping_term)

    return hubbard_model
 def test_is_molecular_term_number(self):
     op = number_operator(5, 3)
     self.assertTrue(op.is_molecular_term())
 def test_number_operator_nosite(self):
     op = number_operator(4)
     expected = (FermionOperator(((0, 1), (0, 0))) + FermionOperator(
         ((1, 1), (1, 0))) + FermionOperator(
             ((2, 1), (2, 0))) + FermionOperator(((3, 1), (3, 0))))
     self.assertTrue(op.isclose(expected))
示例#18
0
    def get_operators(self, guess="minao", pyscf_mol=None):
        if mpi.main_rank:
            # Run electronic structure calculations
            if pyscf_mol is None:
                self, pyscf_mol = run_pyscf_mod(guess,
                                                self.n_orbitals,
                                                self.n_electrons,
                                                self,
                                                run_casci=self.run_fci)

            # 'n_electrons' and 'n_orbitals' must not be 'None'.
            n_core_orbitals = (self.n_electrons - self.n_electrons) // 2
            occupied_indices = list(range(n_core_orbitals))
            active_indices = list(
                range(n_core_orbitals, n_core_orbitals + self.n_orbitals))

            hf_energy = self.hf_energy
            fci_energy = self.fci_energy

            mo_coeff = self.canonical_orbitals.astype(float)
            natom = pyscf_mol.natm
            #atom_charges = pyscf_mol.atom_charges().reshape(-1, 1)
            atom_charges = pyscf_mol.atom_charges().reshape(1, -1)
            atom_coords = pyscf_mol.atom_coords()
            rint = pyscf_mol.intor("int1e_r")

            Hamiltonian = self.get_molecular_hamiltonian(
                occupied_indices=occupied_indices,
                active_indices=active_indices)

            # Dipole operators from dipole integrals (AO)
            rx = create_1body_operator(mo_coeff,
                                       rint[0],
                                       ao=True,
                                       n_active_orbitals=self.n_orbitals)
            ry = create_1body_operator(mo_coeff,
                                       rint[1],
                                       ao=True,
                                       n_active_orbitals=self.n_orbitals)
            rz = create_1body_operator(mo_coeff,
                                       rint[2],
                                       ao=True,
                                       n_active_orbitals=self.n_orbitals)
            Dipole = np.array([rx, ry, rz])
        else:
            Hamiltonian = None
            Dipole = None
            hf_energy = None
            fci_energy = None
            mo_coeff = None
            natom = None
            atom_charges = None
            atom_coords = None

        # MPI broadcasting
        Hamiltonian = mpi.comm.bcast(Hamiltonian, root=0)
        Dipole = mpi.comm.bcast(Dipole, root=0)
        hf_energy = mpi.comm.bcast(hf_energy, root=0)
        fci_energy = mpi.comm.bcast(fci_energy, root=0)
        mo_coeff = mpi.comm.bcast(mo_coeff, root=0)
        natom = mpi.comm.bcast(natom, root=0)
        atom_charges = mpi.comm.bcast(atom_charges, root=0)
        atom_coords = mpi.comm.bcast(atom_coords, root=0)

        # Put values in self
        self.hf_energy = hf_energy
        self.fci_energy = fci_energy
        self.mo_coeff = mo_coeff
        self.natom = natom
        self.atom_charges = atom_charges
        self.atom_coords = atom_coords

        # Print out some results
        print_geom(self.geometry)
        prints("E[FCI] = ", fci_energy)
        prints("E[HF]  = ", hf_energy)
        prints("")

        S2 = s_squared_operator(self.n_orbitals)
        Number = number_operator(self.n_orbitals)

        return Hamiltonian, S2, Number, Dipole
示例#19
0
 def test_is_boson_preserving_number(self):
     op = number_operator(n_modes=5, mode=3, parity=1)
     self.assertTrue(op.is_boson_preserving())
示例#20
0
def _hubbard(parity,
             x_dimension,
             y_dimension,
             tunneling,
             local_interaction,
             coulomb,
             chemical_potential=0.,
             magnetic_field=0.,
             periodic=True,
             spinless=False,
             particle_hole_symmetry=False):
    r"""Returns a generic Hubbard-style Hamiltonian, to be used
    by the bose_hubbard and fermi_hubbard wrapper functions.

    See the corresponding bose_hubbard and fermi_hubbard for descriptions
    of the various arguments.

    Args:
        parity (int): parity=-1 returns the output as an instance
            of the FermionOperator class. Alternatively, parity=1
            returns the output as an instance of the BosonOperator
            class

    Returns:
        hubbard_model: An instance of the FermionOperator or
            BosonOperator class.
    """
    tunneling = float(tunneling)
    local_interaction = float(local_interaction)
    coulomb = float(coulomb)
    chemical_potential = float(chemical_potential)
    magnetic_field = float(magnetic_field)

    if parity == -1:
        Op = FermionOperator
    elif parity == 1:
        Op = BosonOperator

    # Initialize operator class.
    n_sites = x_dimension * y_dimension
    n_spin_orbitals = n_sites

    if not spinless:
        n_spin_orbitals *= 2

    hubbard_model = Op.zero()

    # Select particle-hole symmetry
    if particle_hole_symmetry:
        coulomb_shift = Op((), 0.5)
    else:
        coulomb_shift = Op.zero()

    # Loop through sites and add terms.
    for site in range(n_sites):
        # Add chemical potential to the spinless case. The magnetic field
        # doesn't contribute.
        if spinless and chemical_potential:
            hubbard_model += number_operator(n_spin_orbitals,
                                             site,
                                             -chemical_potential,
                                             parity=parity)

        # no Pauli-Exclusion principle, spinless particles interact on-site
        if parity == 1:
            int_op = number_operator(
                n_sites, site, local_interaction, parity=1) \
                * (number_operator(n_sites, site, parity=1)
                   - Op.identity())
            hubbard_model += int_op

        # With spin, add the chemical potential and magnetic field terms.
        elif not spinless:
            hubbard_model += number_operator(n_spin_orbitals,
                                             up_index(site),
                                             -chemical_potential -
                                             magnetic_field,
                                             parity=parity)
            hubbard_model += number_operator(n_spin_orbitals,
                                             down_index(site),
                                             -chemical_potential +
                                             magnetic_field,
                                             parity=parity)

            # Add local pair interaction terms.
            operator_1 = number_operator(
                n_spin_orbitals, up_index(site), parity=parity) \
                - coulomb_shift
            operator_2 = number_operator(
                n_spin_orbitals, down_index(site), parity=parity) \
                - coulomb_shift
            hubbard_model += coulomb * operator_1 * operator_2

        # Index coupled orbitals.
        right_neighbor = site + 1
        bottom_neighbor = site + x_dimension

        # Account for periodic boundaries.
        if periodic:
            if (x_dimension > 2) and ((site + 1) % x_dimension == 0):
                right_neighbor -= x_dimension
            if (y_dimension > 2) and (site + x_dimension + 1 > n_sites):
                bottom_neighbor -= x_dimension * y_dimension

        # Add transition to neighbor on right.
        if (right_neighbor) % x_dimension or (periodic and x_dimension > 2):
            if spinless:
                # Add Coulomb term.
                operator_1 = number_operator(
                    n_spin_orbitals, site, 1.0, parity=parity) - coulomb_shift
                operator_2 = number_operator(
                    n_spin_orbitals, right_neighbor, 1.0, parity=parity) \
                    - coulomb_shift
                hubbard_model += coulomb * operator_1 * operator_2

                # Add hopping term.
                operators = ((site, 1), (right_neighbor, 0))

            else:
                # Add hopping term.
                operators = ((up_index(site), 1), (up_index(right_neighbor),
                                                   0))
                hopping_term = Op(operators, -tunneling)
                hubbard_model += hopping_term
                hubbard_model += hermitian_conjugated(hopping_term)

                operators = ((down_index(site), 1),
                             (down_index(right_neighbor), 0))

            hopping_term = Op(operators, -tunneling)
            hubbard_model += hopping_term
            hubbard_model += hermitian_conjugated(hopping_term)

        # Add transition to neighbor below.
        if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2):
            if spinless:
                # Add Coulomb term.
                operator_1 = number_operator(
                    n_spin_orbitals, site, parity=parity) - coulomb_shift
                operator_2 = number_operator(
                    n_spin_orbitals, bottom_neighbor, parity=parity) \
                    - coulomb_shift
                hubbard_model += coulomb * operator_1 * operator_2

                # Add hopping term.
                operators = ((site, 1), (bottom_neighbor, 0))

            else:
                # Add hopping term.
                operators = ((up_index(site), 1), (up_index(bottom_neighbor),
                                                   0))
                hopping_term = Op(operators, -tunneling)
                hubbard_model += hopping_term
                hubbard_model += hermitian_conjugated(hopping_term)

                operators = ((down_index(site), 1),
                             (down_index(bottom_neighbor), 0))

            hopping_term = Op(operators, -tunneling)
            hubbard_model += hopping_term
            hubbard_model += hermitian_conjugated(hopping_term)

    return hubbard_model
示例#21
0
 def test_transm_number(self):
     n = number_operator(self.n_qubits, 3)
     n_jw = jordan_wigner(n)
     self.assertEqual(n_jw.terms[((3, 'Z'), )], -0.5)
     self.assertEqual(n_jw.terms[()], 0.5)
     self.assertEqual(len(n_jw.terms), 2)
示例#22
0
 def test_fermion_number_reversed(self):
     n_term_rev2 = FermionOperator(((2, 0), (2, 1)))
     number_op2 = number_operator(3, 2)
     expected = FermionOperator(()) - number_op2
     self.assertTrue(normal_ordered(n_term_rev2) == expected)
示例#23
0
 def test_is_molecular_term_number(self):
     op = number_operator(n_orbitals=5, orbital=3)
     self.assertTrue(op.is_molecular_term())
示例#24
0
def mean_field_dwave(x_dimension,
                     y_dimension,
                     tunneling,
                     sc_gap,
                     chemical_potential=0.,
                     periodic=True,
                     up_map=up_index,
                     down_map=down_index):
    """Return symbolic representation of a BCS mean-field d-wave Hamiltonian.

    The Hamiltonians of this model live on a grid of dimensions
    `x_dimension` x `y_dimension`.
    The grid can have periodic boundary conditions or not.
    Each site on the grid can have an "up" fermion and a "down" fermion.
    Therefore, there are a total of `2N` spin-orbitals,
    where `N = x_dimension * y_dimension` is the number of sites.

    The Hamiltonian for this model has the form

    .. math::

        \\begin{align}
        H = &- t \sum_{\langle i,j \\rangle} \sum_\sigma
                (a^\dagger_{i, \sigma} a_{j, \sigma} +
                 a^\dagger_{j, \sigma} a_{i, \sigma})
            - \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma}
            \\\\
            &- \sum_{\langle i,j \\rangle} \Delta_{ij}
              (a^\dagger_{i, \\uparrow} a^\dagger_{j, \downarrow} -
               a^\dagger_{i, \downarrow} a^\dagger_{j, \\uparrow} +
               a_{j, \downarrow} a_{i, \\uparrow} -
               a_{j, \\uparrow} a_{i, \downarrow})
        \\end{align}

    where

        - The indices :math:`\langle i, j \\rangle` run over pairs
          :math:`i` and :math:`j` of sites that are connected to each other
          in the grid
        - :math:`\sigma \in \\{\\uparrow, \downarrow\\}` is the spin
        - :math:`t` is the tunneling amplitude
        - :math:`\Delta_{ij}` is equal to :math:`+\Delta/2` for
          horizontal edges and :math:`-\Delta/2` for vertical edges,
          where :math:`\Delta` is the superconducting gap.
        - :math:`\mu` is the chemical potential

    Args:
        x_dimension (int): The width of the grid.
        y_dimension (int): The height of the grid.
        tunneling (float): The tunneling amplitude :math:`t`.
        sc_gap (float): The superconducting gap :math:`\Delta`
        chemical_potential (float, optional): The chemical potential
            :math:`\mu` at each site. Default value is 0.
        periodic (bool, optional): If True, add periodic boundary conditions.
            Default is True.

    Returns:
        mean_field_dwave_model: An instance of the FermionOperator class.
    """
    # Initialize fermion operator class.
    n_sites = x_dimension * y_dimension
    n_spin_orbitals = 2 * n_sites
    mean_field_dwave_model = FermionOperator()

    # Loop through sites and add terms.
    for site in range(n_sites):
        # Add chemical potential
        mean_field_dwave_model += number_operator(n_spin_orbitals,
                                                  up_map(site),
                                                  -chemical_potential)
        mean_field_dwave_model += number_operator(n_spin_orbitals,
                                                  down_map(site),
                                                  -chemical_potential)

        # Index coupled orbitals.
        right_neighbor = site + 1
        bottom_neighbor = site + x_dimension
        # Account for periodic boundaries.
        if periodic:
            if (x_dimension > 2) and ((site + 1) % x_dimension == 0):
                right_neighbor -= x_dimension
            if (y_dimension > 2) and (site + x_dimension + 1 > n_sites):
                bottom_neighbor -= x_dimension * y_dimension

        # Add transition to neighbor on right
        if (site + 1) % x_dimension or (periodic and x_dimension > 2):
            # Add spin-up hopping term.
            operators = ((up_map(site), 1), (up_map(right_neighbor), 0))
            hopping_term = FermionOperator(operators, -tunneling)
            mean_field_dwave_model += hopping_term
            mean_field_dwave_model += hermitian_conjugated(hopping_term)
            # Add spin-down hopping term
            operators = ((down_map(site), 1), (down_map(right_neighbor), 0))
            hopping_term = FermionOperator(operators, -tunneling)
            mean_field_dwave_model += hopping_term
            mean_field_dwave_model += hermitian_conjugated(hopping_term)

            # Add pairing term
            operators = ((up_map(site), 1), (down_map(right_neighbor), 1))
            pairing_term = FermionOperator(operators, sc_gap / 2.)
            operators = ((down_map(site), 1), (up_map(right_neighbor), 1))
            pairing_term += FermionOperator(operators, -sc_gap / 2.)
            mean_field_dwave_model -= pairing_term
            mean_field_dwave_model -= hermitian_conjugated(pairing_term)

        # Add transition to neighbor below.
        if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2):
            # Add spin-up hopping term.
            operators = ((up_map(site), 1), (up_map(bottom_neighbor), 0))
            hopping_term = FermionOperator(operators, -tunneling)
            mean_field_dwave_model += hopping_term
            mean_field_dwave_model += hermitian_conjugated(hopping_term)
            # Add spin-down hopping term
            operators = ((down_map(site), 1), (down_map(bottom_neighbor), 0))
            hopping_term = FermionOperator(operators, -tunneling)
            mean_field_dwave_model += hopping_term
            mean_field_dwave_model += hermitian_conjugated(hopping_term)

            # Add pairing term
            operators = ((up_map(site), 1), (down_map(bottom_neighbor), 1))
            pairing_term = FermionOperator(operators, -sc_gap / 2.)
            operators = ((down_map(site), 1), (up_map(bottom_neighbor), 1))
            pairing_term += FermionOperator(operators, sc_gap / 2.)
            mean_field_dwave_model -= pairing_term
            mean_field_dwave_model -= hermitian_conjugated(pairing_term)
    # Return.
    return mean_field_dwave_model
def bose_hubbard(x_dimension,
                 y_dimension,
                 tunneling,
                 interaction,
                 chemical_potential=0.,
                 dipole=0.,
                 periodic=True):
    r"""Return symbolic representation of a Bose-Hubbard Hamiltonian.

    In this model, bosons move around on a lattice, and the
    energy of the model depends on where the bosons are.

    The lattice is described by a 2D grid, with dimensions
    `x_dimension` x `y_dimension`. It is also possible to specify
    if the grid has periodic boundary conditions or not.

    The Hamiltonian for the Bose-Hubbard model has the form

    .. math::

        H = - t \sum_{\langle i, j \rangle} (b_i^\dagger b_j + b_j^\dagger b_i)
         + V \sum_{\langle i, j \rangle} b_i^\dagger b_i b_j^\dagger b_j
         + \frac{U}{2} \sum_i b_i^\dagger b_i (b_i^\dagger b_i - 1)
         - \mu \sum_i b_i^\dagger b_i.

    where

        - The indices :math:`\langle i, j \rangle` run over pairs
          :math:`i` and :math:`j` of nodes that are connected to each other
          in the grid
        - :math:`t` is the tunneling amplitude
        - :math:`U` is the on-site interaction potential
        - :math:`\mu` is the chemical potential
        - :math:`V` is the dipole or nearest-neighbour interaction potential

    Args:
        x_dimension (int): The width of the grid.
        y_dimension (int): The height of the grid.
        tunneling (float): The tunneling amplitude :math:`t`.
        interaction (float): The attractive local interaction
            strength :math:`U`.
        chemical_potential (float, optional): The chemical potential
            :math:`\mu` at each site. Default value is 0.
        periodic (bool, optional): If True, add periodic boundary conditions.
            Default is True.
        dipole (float): The attractive dipole interaction strength :math:`V`.

    Returns:
        bose_hubbard_model: An instance of the BosonOperator class.
    """

    # Initialize operator.
    n_sites = x_dimension * y_dimension
    hubbard_model = BosonOperator()

    # Loop through sites and add terms.
    for site in range(n_sites):

        # Get indices of right and bottom neighbors
        right_neighbor = _right_neighbor(site, x_dimension, y_dimension,
                                         periodic)
        bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension,
                                           periodic)

        # Avoid double-counting edges when one of the dimensions is 2
        # and the system is periodic
        if x_dimension == 2 and periodic and site % 2 == 1:
            right_neighbor = None
        if y_dimension == 2 and periodic and site >= x_dimension:
            bottom_neighbor = None

        # Add terms that couple with neighbors to the right and bottom.
        if right_neighbor is not None:
            # Add hopping term
            hubbard_model += _hopping_term(site,
                                           right_neighbor,
                                           -tunneling,
                                           bosonic=True)
            # Add local Coulomb interaction term
            hubbard_model += _coulomb_interaction_term(
                n_sites,
                site,
                right_neighbor,
                dipole,
                particle_hole_symmetry=False,
                bosonic=True)
        if bottom_neighbor is not None:
            # Add hopping term
            hubbard_model += _hopping_term(site,
                                           bottom_neighbor,
                                           -tunneling,
                                           bosonic=True)
            # Add local Coulomb interaction term
            hubbard_model += _coulomb_interaction_term(
                n_sites,
                site,
                bottom_neighbor,
                dipole,
                particle_hole_symmetry=False,
                bosonic=True)

        # Add on-site interaction.
        hubbard_model += (
            number_operator(n_sites, site, 0.5 * interaction, parity=1) *
            (number_operator(n_sites, site, parity=1) - BosonOperator(())))

        # Add chemical potential.
        hubbard_model += number_operator(n_sites,
                                         site,
                                         -chemical_potential,
                                         parity=1)

    return hubbard_model
 def test_number_operator_site(self):
     op = number_operator(3, 2, 1j)
     self.assertTrue(op.isclose(FermionOperator(((2, 1), (2, 0))) * 1j))