Beispiel #1
0
    def do_make_molecule(self, *args, **kwargs) -> MolecularData:

        energy = self.compute_energy(method="hf", *args, **kwargs)
        wfn = self.logs['hf'].wfn

        molecule = MolecularData(**self.parameters.molecular_data_param)
        if wfn.nirrep() != 1:
            wfn = wfn.c1_deep_copy(wfn.basisset())

        molecule.one_body_integrals = self.compute_one_body_integrals(
            ref_wfn=wfn)
        if "two_body_ordering" not in kwargs:
            molecule.two_body_integrals = self.compute_two_body_integrals(
                ref_wfn=wfn)
        else:
            molecule.two_body_integrals = self.compute_two_body_integrals(
                ref_wfn=wfn, ordering=kwargs["two_body_ordering"])
        molecule.hf_energy = energy
        molecule.nuclear_repulsion = wfn.variables(
        )['NUCLEAR REPULSION ENERGY']
        molecule.canonical_orbitals = numpy.asarray(wfn.Ca())
        molecule.overlap_integrals = numpy.asarray(wfn.S())
        molecule.n_orbitals = molecule.canonical_orbitals.shape[0]
        molecule.n_qubits = 2 * molecule.n_orbitals
        molecule.orbital_energies = numpy.asarray(wfn.epsilon_a())
        molecule.fock_matrix = numpy.asarray(wfn.Fa())
        molecule.save()
        return molecule
Beispiel #2
0
def generate_molecular_hamiltonian_mod(guess, geometry, basis, multiplicity,
                                       charge=0, n_active_electrons=None,
                                       n_active_orbitals=None):
    """Function
    Old subroutine to get molecular hamiltonian by using pyscf.

    Author(s): Takashi Tsuchimochi
    """

    # Run electronic structure calculations
    molecule, pyscf_mol \
            = run_pyscf_mod(guess, n_active_orbitals, n_active_electrons,
                            MolecularData(geometry, basis, multiplicity, charge,
                                          data_directory=cf.input_dir))

    # Freeze core orbitals and truncate to active space
    if n_active_electrons is None:
        n_core_orbitals = 0
        occupied_indices = None
    else:
        n_core_orbitals = (molecule.n_electrons-n_active_electrons)//2
        occupied_indices = list(range(n_core_orbitals))

    if n_active_orbitals is None:
        active_indices = None
    else:
        active_indices = list(range(n_core_orbitals,
                                    n_core_orbitals+n_active_orbitals))

    return molecule.get_molecular_hamiltonian(occupied_indices=occupied_indices,
                                              active_indices=active_indices)
Beispiel #3
0
def test_rhf_min():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)

    overlap = molecule.overlap_integrals
    mo_obi = molecule.one_body_integrals
    mo_tbi = molecule.two_body_integrals
    rotation_mat = molecule.canonical_orbitals.T.dot(overlap)
    obi = general_basis_change(mo_obi, rotation_mat, (1, 0))
    tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0))
    hff = HartreeFockFunctional(one_body_integrals=obi,
                                two_body_integrals=tbi,
                                overlap=overlap,
                                n_electrons=molecule.n_electrons,
                                model='rhf',
                                nuclear_repulsion=molecule.nuclear_repulsion)
    result = rhf_minimization(hff)
    assert isinstance(result, OptimizeResult)

    result2 = rhf_minimization(hff,
                               initial_guess=np.array([0]),
                               sp_options={
                                   'maxiter': 100,
                                   'disp': False
                               })
    assert isinstance(result2, OptimizeResult)
    assert np.isclose(result2.fun, result.fun)
Beispiel #4
0
def vqe_fixed(vqe_strategy, vqe_tomography, vqe_method):
    """
    Initialize a VQE experiment with a custom hamiltonian
    given as constant input
    """

    _vqe = None

    if vqe_strategy == "custom_program":
        custom_ham = PauliSum([PauliTerm(*x) for x in HAMILTONIAN])
        _vqe = VQEexperiment(hamiltonian=custom_ham,
                             method=vqe_method,
                             strategy=vqe_strategy,
                             parametric=False,
                             tomography=vqe_tomography,
                             shotN=NSHOTS_INT)
    elif vqe_strategy == "UCCSD":
        cwd = os.path.abspath(os.path.dirname(__file__))
        fname = os.path.join(cwd, "resources", "H2.hdf5")
        molecule = MolecularData(filename=fname)
        _vqe = VQEexperiment(molecule=molecule,
                             method=vqe_method,
                             strategy=vqe_strategy,
                             parametric=False,
                             tomography=vqe_tomography,
                             shotN=NSHOTS_INT)
    return _vqe
Beispiel #5
0
def test_table_two_particle(name, core, active, v_op_exp):
    r"""Test the FermionOperator built by the function `two_particle` of the `obs` module"""

    hf_data = MolecularData(filename=os.path.join(ref_dir, name))

    v_op = qchem.two_particle(hf_data.two_body_integrals, core=core, active=active)

    assert v_op.terms == v_op_exp
Beispiel #6
0
def decompose(hf_file, mapping="jordan_wigner", core=None, active=None):
    r"""Decomposes the molecular Hamiltonian into a linear combination of Pauli operators using
    OpenFermion tools.

    This function uses OpenFermion functions to build the second-quantized electronic Hamiltonian
    of the molecule and map it to the Pauli basis using the Jordan-Wigner or Bravyi-Kitaev
    transformation.

    Args:
        hf_file (str): absolute path to the hdf5-formatted file with the
            Hartree-Fock electronic structure
        mapping (str): Specifies the transformation to map the fermionic Hamiltonian to the
            Pauli basis. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``.
        core (list): indices of core orbitals, i.e., the orbitals that are
            not correlated in the many-body wave function
        active (list): indices of active orbitals, i.e., the orbitals used to
            build the correlated many-body wave function

    Returns:
        QubitOperator: an instance of OpenFermion's ``QubitOperator``

    **Example**

    >>> decompose('./pyscf/sto-3g/h2', mapping='bravyi_kitaev')
    (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] +
    (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] +
    (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] +
    (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] +
    (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] +
    (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] +
    (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] +
    (-0.2427428049645989+0j) [Z2]
    """

    # loading HF data from the hdf5 file
    molecule = MolecularData(filename=hf_file.strip())

    # getting the terms entering the second-quantized Hamiltonian
    terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=core, active_indices=active
    )

    # generating the fermionic Hamiltonian
    fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian)

    mapping = mapping.strip().lower()

    if mapping not in ("jordan_wigner", "bravyi_kitaev"):
        raise TypeError(
            "The '{}' transformation is not available. \n "
            "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.".format(mapping)
        )

    # fermionic-to-qubit transformation of the Hamiltonian
    if mapping == "bravyi_kitaev":
        return bravyi_kitaev(fermionic_hamiltonian)

    return jordan_wigner(fermionic_hamiltonian)
Beispiel #7
0
def test_table_one_particle(core, active, t_op_exp):
    r"""Test the correctness of the FermionOperator built by the `'one_particle'` function
    of the `obs` module"""

    hf = MolecularData(filename=os.path.join(ref_dir, "h2o_psi4"))

    t_op = qchem.one_particle(hf.one_body_integrals, core=core, active=active)

    assert t_op.terms == t_op_exp
Beispiel #8
0
def test_gradient_lih():
    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)

    overlap = molecule.overlap_integrals
    mo_obi = molecule.one_body_integrals
    mo_tbi = molecule.two_body_integrals
    rotation_mat = molecule.canonical_orbitals.T.dot(overlap)
    obi = general_basis_change(mo_obi, rotation_mat, (1, 0))
    tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0))

    hff = HartreeFockFunctional(one_body_integrals=obi,
                                two_body_integrals=tbi,
                                overlap=overlap,
                                n_electrons=molecule.n_electrons,
                                model='rhf',
                                nuclear_repulsion=molecule.nuclear_repulsion)

    params = np.random.randn(hff.nocc * hff.nvirt)
    u = sp.linalg.expm(
        rhf_params_to_matrix(params,
                             hff.num_orbitals,
                             occ=hff.occ,
                             virt=hff.virt))
    grad_dim = hff.nocc * hff.nvirt
    initial_opdm = np.diag([1] * hff.nocc + [0] * hff.nvirt)
    final_opdm = u.dot(initial_opdm).dot(u.conj().T)
    grad = hff.rhf_global_gradient(params, final_opdm)

    # get finite difference gradient
    finite_diff_grad = np.zeros(grad_dim)
    epsilon = 0.0001
    for i in range(grad_dim):
        params_epsilon = params.copy()
        params_epsilon[i] += epsilon
        u = sp.linalg.expm(
            rhf_params_to_matrix(params_epsilon,
                                 hff.num_orbitals,
                                 occ=hff.occ,
                                 virt=hff.virt))
        tfinal_opdm = u.dot(initial_opdm).dot(u.conj().T)
        energy_plus_epsilon = hff.energy_from_rhf_opdm(tfinal_opdm)

        params_epsilon[i] -= 2 * epsilon
        u = sp.linalg.expm(
            rhf_params_to_matrix(params_epsilon,
                                 hff.num_orbitals,
                                 occ=hff.occ,
                                 virt=hff.virt))
        tfinal_opdm = u.dot(initial_opdm).dot(u.conj().T)
        energy_minus_epsilon = hff.energy_from_rhf_opdm(tfinal_opdm)

        finite_diff_grad[i] = (energy_plus_epsilon -
                               energy_minus_epsilon) / (2 * epsilon)

    assert np.allclose(finite_diff_grad, grad, atol=epsilon)
def build_lih_moleculardata() -> MolecularData:
    """Returns LiH molecular data."""
    geometry = [("Li", (0.0, 0.0, 0.0)), ("H", (0.0, 0.0, 1.45))]
    basis = "sto-3g"
    multiplicity = 1
    filename = os.path.join(
        fud.__file__.replace("__init__.py", ""),
        "H1-Li1_sto-3g_singlet_1.45.hdf5",
    )
    molecule = MolecularData(geometry, basis, multiplicity, filename=filename)
    molecule.load()
    return molecule
Beispiel #10
0
def test_generate_hamiltonian():
    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)
    mo_obi = molecule.one_body_integrals
    mo_tbi = molecule.two_body_integrals
    mol_ham = generate_hamiltonian(mo_obi, mo_tbi, constant=0)
    assert isinstance(mol_ham, InteractionOperator)
    assert np.allclose(mol_ham.one_body_tensor[::2, ::2], mo_obi)
    assert np.allclose(mol_ham.one_body_tensor[1::2, 1::2], mo_obi)
    assert np.allclose(mol_ham.two_body_tensor[::2, ::2, ::2, ::2],
                       0.5 * mo_tbi)
    assert np.allclose(mol_ham.two_body_tensor[1::2, 1::2, 1::2, 1::2],
                       0.5 * mo_tbi)
def test_inconsistent_active_spaces(mol_name, act_electrons, act_orbitals,
                                    message_match):
    r"""Test that an error is raised if an inconsistent active space is generated"""

    molecule = MolecularData(filename=os.path.join(ref_dir, mol_name))

    with pytest.raises(ValueError, match=message_match):
        qchem.active_space(
            molecule.n_electrons,
            molecule.n_orbitals,
            mult=molecule.multiplicity,
            active_electrons=act_electrons,
            active_orbitals=act_orbitals,
        )
Beispiel #12
0
def generate_molecular_hamiltonian(
        geometry,
        basis,
        multiplicity,
        charge=0,
        n_active_electrons=None,
        n_active_orbitals=None):
    """Generate a molecular Hamiltonian with the given properties.

    Args:
        geometry: A list of tuples giving the coordinates of each atom.
            An example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))].
            Distances in angstrom. Use atomic symbols to
            specify atoms.
        basis: A string giving the basis set. An example is 'cc-pvtz'.
            Only optional if loading from file.
        multiplicity: An integer giving the spin multiplicity.
        charge: An integer giving the charge.
        n_active_electrons: An optional integer specifying the number of
            electrons desired in the active space.
        n_active_orbitals: An optional integer specifying the number of
            spatial orbitals desired in the active space.

    Returns:
        The Hamiltonian as an InteractionOperator.
    """

    # Run electronic structure calculations
    molecule = run_pyscf(
            MolecularData(geometry, basis, multiplicity, charge)
    )

    # Freeze core orbitals and truncate to active space
    if n_active_electrons is None:
        n_core_orbitals = 0
        occupied_indices = None
    else:
        n_core_orbitals = (molecule.n_electrons - n_active_electrons) // 2
        occupied_indices = list(range(n_core_orbitals))

    if n_active_orbitals is None:
        active_indices = None
    else:
        active_indices = list(range(n_core_orbitals,
                                    n_core_orbitals + n_active_orbitals))

    return molecule.get_molecular_hamiltonian(
            occupied_indices=occupied_indices,
            active_indices=active_indices)
    def _generate_molecule(self, p: IParameter) -> MolecularData:
        """Produce molecule that can be used by the hamiltonian.
        Using a singlet state with S = 0 to specify we are looking for the lowest singlet energy state.
        multiplicity = 2S + 1
        """
        geometry = [('H', (0., 0., 0.)), ('H', (0., 0., p['r0']))]
        basis = 'sto-3g'
        multiplicity = 1
        charge = 0
        description = str(p['r0'])

        # Change to whatever directory you want
        cwd = os.getcwd()
        data_directory = cwd+'/mol_data'

        if not os.path.exists(data_directory):
            os.mkdir(data_directory)

        filename = data_directory+'/H2_'+description

        run_scf = 1
        run_mp2 = 1
        run_cisd = 1
        run_ccsd = 1
        run_fci = 1
        delete_input = False
        delete_output = False
        verbose = False

        molecule = MolecularData(
            geometry,
            basis,
            multiplicity,
            description=description,
            filename=filename)

        if os.path.exists('{}.hdf5'.format(filename)):
            molecule.load()

        else:
            molecule = run_psi4(molecule,
                                verbose=verbose,
                                run_scf=run_scf,
                                run_mp2=run_mp2,
                                run_cisd=run_cisd,
                                run_ccsd=run_ccsd,
                                run_fci=run_fci)
        # print(f'Reference (Full Configuration Energy: {molecule.fci_energy})')
        return molecule
def build_h4square_moleculardata() -> MolecularData:
    """Returns H4 molecular data."""
    geometry = [
        ("H", [0.5, 0.5, 0]),
        ("H", [0.5, -0.5, 0]),
        ("H", [-0.5, 0.5, 0]),
        ("H", [-0.5, -0.5, 0]),
    ]
    basis = "sto-3g"
    multiplicity = 1
    filename = os.path.join(fud.__file__.replace("__init__.py", ""),
                            "H4_sto-3g_singlet.hdf5")
    molecule = MolecularData(geometry, basis, multiplicity, filename=filename)
    molecule.load()
    return molecule
def test_active_spaces(mol_name, act_electrons, act_orbitals, core_ref,
                       active_ref):
    r"""Test the correctness of the generated active spaces"""

    molecule = MolecularData(filename=os.path.join(ref_dir, mol_name))

    core, active = qchem.active_space(
        molecule.n_electrons,
        molecule.n_orbitals,
        mult=molecule.multiplicity,
        active_electrons=act_electrons,
        active_orbitals=act_orbitals,
    )

    assert core == core_ref
    assert active == active_ref
Beispiel #16
0
def get_molecule(element_names, bond_len):
    geometry = [[element_names[0], [0, 0, 0]],
                [element_names[1], [0, 0, bond_len]]]
    basis = 'sto-3g'
    multiplicity = 1
    description = '{:.2}'.format(bond_len)
    data_directory = DATA_DIRECTORY

    molecule = MolecularData(geometry,
                             basis,
                             multiplicity,
                             description=description,
                             data_directory=data_directory)
    molecule.load()

    return molecule
Beispiel #17
0
def test_d2_to_g2():
    # this should test for correctness
    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)
    molecule.load()
    opdm = molecule.fci_one_rdm
    tpdm = molecule.fci_two_rdm
    phdm = map_two_pdm_to_particle_hole_dm(tpdm, opdm)

    db = tpdm_to_phdm_mapping(molecule.n_qubits)
    topdm = Tensor(tensor=opdm, name='ck')
    ttpdm = Tensor(tensor=np.einsum('ijlk', tpdm), name='cckk')
    tphdm = Tensor(tensor=np.einsum('ijlk', phdm), name='ckck')
    mt = MultiTensor(tensors=[topdm, ttpdm, tphdm], dual_basis=db)
    A, _, b = mt.synthesize_dual_basis()
    vec_rdms = mt.vectorize_tensors()
    assert np.isclose(np.linalg.norm(A.dot(vec_rdms) - b), 0)
Beispiel #18
0
def test_hf_calculations(package, tmpdir, psi4_support, tol):
    r"""Test the correctness of the HF calculation"""

    if package == "Psi4" and not psi4_support:
        pytest.skip("Skipped, no Psi4 support")

    n_atoms = 2
    n_electrons = 2
    n_orbitals = 2
    hf_energy = -1.1173490350703152
    orbital_energies = np.array([-0.59546347, 0.71416528])

    one_body_integrals = np.array([[-1.27785300e00, 1.11022302e-16],
                                   [0.00000000e00, -4.48299698e-01]])

    two_body_integrals = np.array([
        [
            [[6.82389533e-01, 0.00000000e00], [6.93889390e-17,
                                               1.79000576e-01]],
            [[4.16333634e-17, 1.79000576e-01], [6.70732778e-01,
                                                0.00000000e00]],
        ],
        [
            [[5.55111512e-17, 6.70732778e-01],
             [1.79000576e-01, 1.11022302e-16]],
            [[1.79000576e-01, 0.00000000e00], [2.77555756e-17,
                                               7.05105632e-01]],
        ],
    ])

    fullpath = qchem.meanfield(symbols,
                               coordinates,
                               name=name,
                               package=package,
                               outpath=tmpdir.strpath)

    molecule = MolecularData(filename=fullpath)

    assert molecule.n_atoms == n_atoms
    assert molecule.n_electrons == n_electrons
    assert molecule.n_orbitals == n_orbitals
    assert np.allclose(molecule.hf_energy, hf_energy, **tol)
    assert np.allclose(molecule.orbital_energies, orbital_energies, **tol)
    assert np.allclose(molecule.one_body_integrals, one_body_integrals, **tol)
    assert np.allclose(molecule.two_body_integrals, two_body_integrals, **tol)
    def test_moleculardata_to_restricted_hamiltonian(self):
        """
        Convert an OpenFermion MolecularData object to a
        fqe.RestrictedHamiltonian
        """
        h1e, h2e, _ = build_lih_data('energy')
        # dummy geometry
        geometry = [['Li', [0, 0, 0], ['H', [0, 0, 1.4]]]]
        charge = 0
        multiplicity = 1
        molecule = MolecularData(geometry=geometry,
                                 basis='sto-3g',
                                 charge=charge,
                                 multiplicity=multiplicity)
        molecule.one_body_integrals = h1e
        molecule.two_body_integrals = numpy.einsum('ijlk', -2 * h2e)
        molecule.nuclear_repulsion = 0

        restricted_ham = openfermion_utils.molecular_data_to_restricted_fqe_op(
            molecule=molecule)
        h1e_test, h2e_test = restricted_ham.tensors()
        self.assertTrue(numpy.allclose(h1e_test, h1e))
        self.assertTrue(numpy.allclose(h2e_test, h2e))
Beispiel #20
0
def test_rhf_func_generator():
    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)

    overlap = molecule.overlap_integrals
    mo_obi = molecule.one_body_integrals
    mo_tbi = molecule.two_body_integrals
    rotation_mat = molecule.canonical_orbitals.T.dot(overlap)
    obi = general_basis_change(mo_obi, rotation_mat, (1, 0))
    tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0))

    hff = HartreeFockFunctional(one_body_integrals=obi,
                                two_body_integrals=tbi,
                                overlap=overlap,
                                n_electrons=molecule.n_electrons,
                                model='rhf',
                                nuclear_repulsion=molecule.nuclear_repulsion)
    unitary, energy, gradient = rhf_func_generator(hff)
    assert isinstance(unitary, Callable)
    assert isinstance(energy, Callable)
    assert isinstance(gradient, Callable)

    params = np.random.randn(hff.nocc * hff.nvirt)
    u = unitary(params)
    assert np.allclose(u.conj().T.dot(u), np.eye(hff.num_orbitals))
    assert isinstance(energy(params), float)
    assert isinstance(gradient(params), np.ndarray)

    _, _, _, opdm_func = rhf_func_generator(hff, get_opdm_func=True)
    assert isinstance(opdm_func, Callable)
    assert isinstance(opdm_func(params), np.ndarray)
    assert np.isclose(opdm_func(params).shape[0], hff.num_orbitals)

    _, energy, _ = rhf_func_generator(hff,
                                      init_occ_vec=np.array([1, 1, 1, 1, 0,
                                                             0]))
    assert isinstance(energy(params), float)
Beispiel #21
0
)
print("                             VQD")
print(
    " --------------------------------------------------------------------------"
)

molecules = []
SymbolicAnsatz = None

for point in [3.0]:  #range(1, n_points + 1):
    bond_length = point  #bond_length_interval * float(point) + 0.2
    geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., bond_length))]

    MolecularMeta = {}
    molecule = MolecularData(geometry,
                             basis,
                             multiplicity,
                             description=str(round(bond_length, 2)))
    molecule = run_pyscf(molecule, run_scf=run_scf)

    # Basic information
    jim = molecule.name
    print("===== Molecule {} =====".format(jim))
    MolecularMeta['Name'] = jim
    MolecularMeta['Geo'] = bond_length

    # Hamiltonian
    ham_qubit = jordan_wigner(molecule.get_molecular_hamiltonian())
    ham_qubit.compress()  # Now in QubitOperator form
    ham_sparse = qubit_operator_sparse(ham_qubit, n_qubits=molecule.n_qubits)

    print(' HF:', molecule.hf_energy)
Beispiel #22
0
def molecular_hamiltonian(
    name,
    geo_file,
    charge=0,
    mult=1,
    basis="sto-3g",
    package="pyscf",
    active_electrons=None,
    active_orbitals=None,
    mapping="jordan_wigner",
    outpath=".",
    wires=None,
):  # pylint:disable=too-many-arguments
    r"""Generates the qubit Hamiltonian of a molecule.

    This function drives the construction of the second-quantized electronic Hamiltonian
    of a molecule and its transformation to the basis of Pauli matrices.

    #. The process begins by reading the file containing the geometry of the molecule.

    #. OpenFermion-PySCF or OpenFermion-Psi4 plugins are used to launch
       the Hartree-Fock (HF) calculation for the polyatomic system using the quantum
       chemistry package ``PySCF`` or ``Psi4``, respectively.

       - The net charge of the molecule can be given to simulate
         cationic/anionic systems. Also, the spin multiplicity can be input
         to determine the number of unpaired electrons occupying the HF orbitals
         as illustrated in the left panel of the figure below.

       - The basis of Gaussian-type *atomic* orbitals used to represent the *molecular* orbitals
         can be specified to go beyond the minimum basis approximation. Basis set availability
         per element can be found
         `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_

    #. An active space can be defined for a given number of *active electrons*
       occupying a reduced set of *active orbitals* in the vicinity of the frontier
       orbitals as sketched in the right panel of the figure below.

    #. Finally, the second-quantized Hamiltonian is mapped to the Pauli basis and
       converted to a PennyLane observable.

    |

    .. figure:: ../../_static/qchem/fig_mult_active_space.png
        :align: center
        :width: 90%

    |

    Args:
        name (str): name of the molecule
        geo_file (str): file containing the geometry of the molecule
        charge (int): Net charge of the molecule. If not specified a a neutral system is assumed.
        mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1`
            for :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals.
            Possible values of ``mult`` are :math:`1, 2, 3, \ldots`. If not specified,
            a closed-shell HF state is assumed.
        basis (str): Atomic basis set used to represent the molecular orbitals. Basis set
            availability per element can be found
            `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_
        package (str): quantum chemistry package (pyscf or psi4) used to solve the
            mean field electronic structure problem
        active_electrons (int): Number of active electrons. If not specified, all electrons
            are considered to be active.
        active_orbitals (int): Number of active orbitals. If not specified, all orbitals
            are considered to be active.
        mapping (str): transformation (``'jordan_wigner'`` or ``'bravyi_kitaev'``) used to
            map the fermionic Hamiltonian to the qubit Hamiltonian
        outpath (str): path to the directory containing output files
        wires (Wires, list, tuple, dict): Custom wire mapping for connecting to Pennylane ansatz.
            For types Wires/list/tuple, each item in the iterable represents a wire label
            corresponding to the qubit number equal to its index.
            For type dict, only int-keyed dict (for qubit-to-wire conversion) is accepted for
            partial mapping. If None, will use identity map.

    Returns:
        tuple[pennylane.Hamiltonian, int]: the fermionic-to-qubit transformed Hamiltonian
        and the number of qubits

    **Example**

    >>> name = "h2"
    >>> geo_file = "h2.xyz"
    >>> H, qubits = molecular_hamiltonian(name, geo_file)
    >>> print(qubits)
    4
    >>> print(H)
    (-0.04207897647782188) [I0]
    + (0.17771287465139934) [Z0]
    + (0.1777128746513993) [Z1]
    + (-0.24274280513140484) [Z2]
    + (-0.24274280513140484) [Z3]
    + (0.17059738328801055) [Z0 Z1]
    + (0.04475014401535161) [Y0 X1 X2 Y3]
    + (-0.04475014401535161) [Y0 Y1 X2 X3]
    + (-0.04475014401535161) [X0 X1 Y2 Y3]
    + (0.04475014401535161) [X0 Y1 Y2 X3]
    + (0.12293305056183801) [Z0 Z2]
    + (0.1676831945771896) [Z0 Z3]
    + (0.1676831945771896) [Z1 Z2]
    + (0.12293305056183801) [Z1 Z3]
    + (0.176276408043196) [Z2 Z3]
    """

    geometry = read_structure(geo_file, outpath)

    hf_file = meanfield(name, geometry, charge, mult, basis, package, outpath)

    molecule = MolecularData(filename=hf_file)

    core, active = active_space(
        molecule.n_electrons, molecule.n_orbitals, mult, active_electrons, active_orbitals
    )

    h_of, qubits = (decompose(hf_file, mapping, core, active), 2 * len(active))

    return convert_observable(h_of, wires=wires), qubits
Beispiel #23
0
def get_ham_from_psi4(wfn,
                      mints,
                      n_active_extract=None,
                      freeze_core_extract=False,
                      orbs=None,
                      nuclear_repulsion_energy=0):
    """Get a molecular Hamiltonian from a Psi4 calculation.

    Args:
        wfn (psi4.core.Wavefunction): Psi4 wavefunction object
        mints (psi4.core.MintsHelper): Psi4 molecular integrals helper
        n_active_extract (int): number of molecular orbitals to include in the
            saved Hamiltonian. If None, includes all orbitals, else you must provide
            active orbitals in orbs.
        freeze_core_extract (bool): whether to freeze core orbitals as doubly
            occupied in the saved Hamiltonian.
        orbs (psi4.core.Matrix): Psi4 orbitals for active space transformations. Must
            include all occupied (also core in all cases).
        nuclear_repulsion_energy (float): The ion-ion interaction energy.
    
    Returns:
        hamiltonian (openfermion.ops.InteractionOperator): the electronic
            Hamiltonian. Note that the ion-ion electrostatic energy is not
            included.
    """

    assert wfn.same_a_b_orbs(), "Extraction of Hamiltonian from wavefunction" + \
        "with different alpha and beta orbitals not yet supported :("

    # Note: code refactored to use Psi4 integral-transformation routines
    # no more storing the whole two-electron integral tensor when only an
    # active space is needed

    orbitals = wfn.Ca().to_array(dense=True)
    one_body_integrals = general_basis_change(np.asarray(mints.ao_kinetic()),
                                              orbitals, (1, 0))
    one_body_integrals += general_basis_change(
        np.asarray(mints.ao_potential()), orbitals, (1, 0))

    # Build the transformation matrices, i.e. the orbitals for which
    # we want the integrals, as Psi4.core.Matrix objects
    n_core_extract = 0
    if freeze_core_extract:
        n_core_extract = wfn.nfrzc()
    if n_active_extract is None:
        trf_mat = wfn.Ca()
        n_active_extract = wfn.nmo() - n_core_extract
    else:
        # If orbs is given, it allows us to perform the two-electron integrals
        # transformation only in the space of active orbitals. Otherwise, we
        # transform all orbitals and filter them out in the get_ham_from_integrals
        # function
        if orbs is None:
            trf_mat = wfn.Ca()
        else:
            assert (orbs.to_array(dense=True).shape[1] == n_active_extract +
                    n_core_extract)
            trf_mat = orbs

    two_body_integrals = np.asarray(
        mints.mo_eri(trf_mat, trf_mat, trf_mat, trf_mat))
    n_orbitals = trf_mat.shape[1]
    two_body_integrals.reshape(
        (n_orbitals, n_orbitals, n_orbitals, n_orbitals))
    two_body_integrals = np.einsum('psqr', two_body_integrals)

    # Truncate
    one_body_integrals[np.absolute(one_body_integrals) < EQ_TOLERANCE] = 0.
    two_body_integrals[np.absolute(two_body_integrals) < EQ_TOLERANCE] = 0.

    if n_active_extract is None and not freeze_core_extract:
        occupied_indices = None
        active_indices = None
    else:
        # Indices of occupied molecular orbitals
        occupied_indices = range(n_core_extract)

        # Indices of active molecular orbitals
        active_indices = range(n_core_extract,
                               n_core_extract + n_active_extract)

    # In order to keep the MolecularData class happy, we need a 'valid' molecule
    molecular_data = MolecularData(geometry=[('H', (0, 0, 0))],
                                   basis='',
                                   multiplicity=2)

    molecular_data.one_body_integrals = one_body_integrals
    molecular_data.two_body_integrals = two_body_integrals
    molecular_data.nuclear_repulsion = nuclear_repulsion_energy
    hamiltonian = molecular_data.get_molecular_hamiltonian(
        occupied_indices, active_indices)

    return (hamiltonian)
Beispiel #24
0
def meanfield(
    name, geometry, charge=0, mult=1, basis="sto-3g", package="pyscf", outpath="."
):  # pylint: disable=too-many-arguments
    r"""Generates a file from which the mean field electronic structure
    of the molecule can be retrieved.

    This function uses OpenFermion-PySCF and OpenFermion-Psi4 plugins to
    perform the Hartree-Fock (HF) calculation for the polyatomic system using the quantum
    chemistry packages ``PySCF`` and ``Psi4``, respectively. The mean field electronic
    structure is saved in an hdf5-formatted file in the directory
    ``os.path.join(outpath, package, basis)``.

    The charge of the molecule can be given to simulate cationic/anionic systems.
    Also, the spin multiplicity can be input to determine the number of unpaired electrons
    occupying the HF orbitals as illustrated in the figure below.

    |

    .. figure:: ../../_static/qchem/hf_references.png
        :align: center
        :width: 50%

    |

    Args:
        name (str): molecule label
        geometry (list): list containing the symbol and Cartesian coordinates for each atom
        charge (int): net charge of the system
        mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1` for
            :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals.
            Possible values for ``mult`` are :math:`1, 2, 3, \ldots`. If not specified,
            a closed-shell HF state is assumed.
        basis (str): Atomic basis set used to represent the HF orbitals. Basis set
            availability per element can be found
            `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_
        package (str): Quantum chemistry package used to solve the Hartree-Fock equations.
            Either ``'pyscf'`` or ``'psi4'`` can be used.
        outpath (str): path to output directory

    Returns:
        str: absolute path to the file containing the mean field electronic structure

    **Example**

    >>> name = 'h2'
    >>> geometry = [['H', (0.0, 0.0, -0.35)], ['H', (0.0, 0.0, 0.35)]]
    >>> meanfield(name, geometry)
    ./pyscf/sto-3g/h2
    """

    package = package.strip().lower()

    if package not in ("psi4", "pyscf"):
        error_message = (
            "Integration with quantum chemistry package '{}' is not available. \n Please set"
            " 'package' to 'pyscf' or 'psi4'.".format(package)
        )
        raise TypeError(error_message)

    package_dir = os.path.join(outpath.strip(), package)
    basis_dir = os.path.join(package_dir, basis.strip())

    if not os.path.isdir(package_dir):
        os.mkdir(package_dir)
        os.mkdir(basis_dir)
    elif not os.path.isdir(basis_dir):
        os.mkdir(basis_dir)

    path_to_file = os.path.join(basis_dir, name.strip())

    molecule = MolecularData(geometry, basis, mult, charge, filename=path_to_file)

    if package == "psi4":
        run_psi4(molecule, run_scf=1, verbose=0, tolerate_error=1)

    if package == "pyscf":
        run_pyscf(molecule, run_scf=1, verbose=0)

    return path_to_file
Beispiel #25
0
def get_ham_from_psi4(
    wfn, mints, ndocc=None, nact=None, nuclear_repulsion_energy=0,
):
    """Get a molecular Hamiltonian from a Psi4 calculation.

    Args:
        wfn (psi4.core.Wavefunction): Psi4 wavefunction object
        mints (psi4.core.MintsHelper): Psi4 molecular integrals helper
        ndocc (int): number of doubly occupied molecular orbitals to
            include in the saved Hamiltonian.
        nact (int): number of active molecular orbitals to include in the
            saved Hamiltonian.
        nuclear_repulsion_energy (float): The ion-ion interaction energy.

    Returns:
        hamiltonian (openfermion.ops.InteractionOperator): the electronic
            Hamiltonian.
    """

    assert wfn.same_a_b_orbs(), (
        "Extraction of Hamiltonian from wavefunction"
        + "with different alpha and beta orbitals not yet supported :("
    )

    orbitals = wfn.Ca().to_array(dense=True)

    if nact is None and ndocc is None:
        trf_mat = orbitals
        ndocc = 0
        nact = orbitals.shape[1]
        print(
            f"Active space selection options were reset to: ndocc = {ndocc} and  nact = {nact}"
        )
    elif nact is not None and ndocc is None:
        assert nact <= orbitals.shape[1]
        ndocc = 0
        trf_mat = orbitals[:, :nact]
        print(
            f"Active space selection options were reset to: ndocc = {ndocc} and  nact = {nact}"
        )
    elif ndocc is not None and nact is None:
        assert ndocc <= orbitals.shape[1]
        nact = orbitals.shape[1] - ndocc
        trf_mat = orbitals
        print(
            f"Active space selection options were reset to: ndocc = {ndocc} and  nact = {nact}"
        )
    else:
        assert orbitals.shape[1] >= nact + ndocc
        trf_mat = orbitals[:, : nact + ndocc]

    # Note: code refactored to use Psi4 integral-transformation routines
    # no more storing the whole two-electron integral tensor when only an
    # active space is needed

    one_body_integrals = general_basis_change(
        np.asarray(mints.ao_kinetic()), orbitals, (1, 0)
    )
    one_body_integrals += general_basis_change(
        np.asarray(mints.ao_potential()), orbitals, (1, 0)
    )

    # Build the transformation matrices, i.e. the orbitals for which
    # we want the integrals, as Psi4.core.Matrix objects

    trf_mat = psi4.core.Matrix.from_array(trf_mat)

    two_body_integrals = np.asarray(mints.mo_eri(trf_mat, trf_mat, trf_mat, trf_mat))
    n_orbitals = trf_mat.shape[1]
    two_body_integrals.reshape((n_orbitals, n_orbitals, n_orbitals, n_orbitals))
    two_body_integrals = np.einsum("psqr", two_body_integrals)

    # Truncate
    one_body_integrals[np.absolute(one_body_integrals) < EQ_TOLERANCE] = 0.0
    two_body_integrals[np.absolute(two_body_integrals) < EQ_TOLERANCE] = 0.0

    occupied_indices = range(ndocc)
    active_indices = range(ndocc, ndocc + nact)

    # In order to keep the MolecularData class happy, we need a 'valid' molecule
    molecular_data = MolecularData(
        geometry=[("H", (0, 0, 0))], basis="", multiplicity=2
    )

    molecular_data.one_body_integrals = one_body_integrals
    molecular_data.two_body_integrals = two_body_integrals
    molecular_data.nuclear_repulsion = nuclear_repulsion_energy
    hamiltonian = molecular_data.get_molecular_hamiltonian(
        occupied_indices, active_indices
    )

    return hamiltonian
Beispiel #26
0
def vqe_qc(vqe_strategy, vqe_qc_backend, vqe_parametricflag, sample_molecule):
    """
    Initialize a VQE experiment with a custom hamiltonian
    given as constant input, given a QC-type backend (tomography is always set to True then)
    """

    _vqe = None
    qc = None
    vqe_cq = None

    if vqe_strategy == "custom_program":
        if vqe_qc_backend == 'Aspen-qvm':
            qc = get_qc('Aspen-4-2Q-A-qvm')
            vqe_cq = [1, 2]
        elif vqe_qc_backend == 'Aspen-pyqvm':
            qc = get_qc('Aspen-4-2Q-A-pyqvm')
            vqe_cq = [1, 2]
        elif vqe_qc_backend == 'Nq-qvm':
            qc = get_qc('2q-qvm')
        elif vqe_qc_backend == 'Nq-pyqvm':
            qc = get_qc('2q-pyqvm')

        custom_ham = PauliSum([PauliTerm(*x) for x in HAMILTONIAN])
        _vqe = VQEexperiment(hamiltonian=custom_ham,
                             qc=qc,
                             custom_qubits=vqe_cq,
                             method='QC',
                             strategy=vqe_strategy,
                             parametric=True,
                             tomography=True,
                             shotN=NSHOTS_SMALL)
    elif vqe_strategy == "HF":
        if vqe_qc_backend == 'Aspen-qvm':
            qc = get_qc('Aspen-4-2Q-A-qvm')
            vqe_cq = [1, 2]
        elif vqe_qc_backend == 'Aspen-pyqvm':
            qc = get_qc('Aspen-4-2Q-A-pyqvm')
            vqe_cq = [1, 2]
        elif vqe_qc_backend == 'Nq-qvm':
            qc = get_qc('2q-qvm')
        elif vqe_qc_backend == 'Nq-pyqvm':
            qc = get_qc('2q-pyqvm')
        cwd = os.path.abspath(os.path.dirname(__file__))
        fname = os.path.join(cwd, "resources", "H.hdf5")
        molecule = MolecularData(filename=fname)
        _vqe = VQEexperiment(molecule=molecule,
                             qc=qc,
                             custom_qubits=vqe_cq,
                             method='QC',
                             strategy=vqe_strategy,
                             parametric=vqe_parametricflag,
                             tomography=True,
                             shotN=NSHOTS_SMALL)
    elif vqe_strategy == "UCCSD":
        if vqe_qc_backend == 'Aspen-qvm':
            qc = get_qc('Aspen-4-4Q-A-qvm')
            vqe_cq = [7, 0, 1, 2]
        elif vqe_qc_backend == 'Aspen-pyqvm':
            qc = get_qc('Aspen-4-4Q-A-pyqvm')
            vqe_cq = [7, 0, 1, 2]
        elif vqe_qc_backend == 'Nq-qvm':
            qc = get_qc('4q-qvm')
        elif vqe_qc_backend == 'Nq-pyqvm':
            qc = get_qc('4q-pyqvm')

        _vqe = VQEexperiment(molecule=sample_molecule,
                             qc=qc,
                             custom_qubits=vqe_cq,
                             method='QC',
                             strategy=vqe_strategy,
                             parametric=vqe_parametricflag,
                             tomography=True,
                             shotN=NSHOTS_SMALL)
    return _vqe
Beispiel #27
0
def sample_molecule():
    cwd = os.path.abspath(os.path.dirname(__file__))
    fname = os.path.join(cwd, "resources", "H2.hdf5")
    molecule = MolecularData(filename=fname)
    return molecule
Beispiel #28
0
 def do_make_molecule(self, molecule=None) -> MolecularData:
     if molecule is None:
         molecule = MolecularData(**self.parameters.molecular_data_param)
     return run_pyscf(molecule, **self.parameters_pyscf.__dict__)
Beispiel #29
0
def meanfield(
    symbols,
    coordinates,
    name="molecule",
    charge=0,
    mult=1,
    basis="sto-3g",
    package="pyscf",
    outpath=".",
):  # pylint: disable=too-many-arguments
    r"""Generates a file from which the mean field electronic structure
    of the molecule can be retrieved.

    This function uses OpenFermion-PySCF and OpenFermion-Psi4 plugins to
    perform the Hartree-Fock (HF) calculation for the polyatomic system using the quantum
    chemistry packages ``PySCF`` and ``Psi4``, respectively. The mean field electronic
    structure is saved in an hdf5-formatted file in the directory
    ``os.path.join(outpath, package, basis)``.

    The charge of the molecule can be given to simulate cationic/anionic systems.
    Also, the spin multiplicity can be input to determine the number of unpaired electrons
    occupying the HF orbitals as illustrated in the figure below.

    |

    .. figure:: ../../_static/qchem/hf_references.png
        :align: center
        :width: 50%

    |

    Args:
        symbols (list[str]): symbols of the atomic species in the molecule
        coordinates (array[float]): 1D array with the atomic positions in Cartesian
            coordinates. The coordinates must be given in atomic units and the size of the array
            should be ``3*N`` where ``N`` is the number of atoms.
        name (str): molecule label
        charge (int): net charge of the system
        mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1` for
            :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals.
            Possible values for ``mult`` are :math:`1, 2, 3, \ldots`. If not specified,
            a closed-shell HF state is assumed.
        basis (str): Atomic basis set used to represent the HF orbitals. Basis set
            availability per element can be found
            `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_
        package (str): Quantum chemistry package used to solve the Hartree-Fock equations.
            Either ``'pyscf'`` or ``'psi4'`` can be used.
        outpath (str): path to output directory

    Returns:
        str: absolute path to the file containing the mean field electronic structure

    **Example**

    >>> symbols, coordinates = (['H', 'H'], np.array([0., 0., -0.66140414, 0., 0., 0.66140414]))
    >>> meanfield(symbols, coordinates, name="h2")
    ./pyscf/sto-3g/h2
    """

    if coordinates.size != 3 * len(symbols):
        raise ValueError(
            "The size of the array 'coordinates' has to be 3*len(symbols) = {};"
            " got 'coordinates.size' = {}".format(3 * len(symbols),
                                                  coordinates.size))

    package = package.strip().lower()

    if package not in ("psi4", "pyscf"):
        error_message = (
            "Integration with quantum chemistry package '{}' is not available. \n Please set"
            " 'package' to 'pyscf' or 'psi4'.".format(package))
        raise TypeError(error_message)

    package_dir = os.path.join(outpath.strip(), package)
    basis_dir = os.path.join(package_dir, basis.strip())

    if not os.path.isdir(package_dir):
        os.mkdir(package_dir)
        os.mkdir(basis_dir)
    elif not os.path.isdir(basis_dir):
        os.mkdir(basis_dir)

    path_to_file = os.path.join(basis_dir, name.strip())

    geometry = [[symbol,
                 tuple(coordinates[3 * i:3 * i + 3] * bohr_angs)]
                for i, symbol in enumerate(symbols)]

    molecule = MolecularData(geometry,
                             basis,
                             mult,
                             charge,
                             filename=path_to_file)

    if package == "psi4":
        run_psi4(molecule, run_scf=1, verbose=0, tolerate_error=1)

    if package == "pyscf":
        run_pyscf(molecule, run_scf=1, verbose=0)

    return path_to_file