예제 #1
0
    def _populate_driver_result_metadata(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        # pylint: disable=import-error
        from pyscf import __version__ as pyscf_version

        cfg = [
            f"atom={self._atom}",
            f"unit={self._unit.value}",
            f"charge={self._charge}",
            f"spin={self._spin}",
            f"basis={self._basis}",
            f"method={self.method.value}",
            f"conv_tol={self._conv_tol}",
            f"max_cycle={self._max_cycle}",
            f"init_guess={self._init_guess}",
            f"max_memory={self._max_memory}",
        ]

        if self.method.value.lower() in ("rks", "roks", "uks"):
            cfg.extend([
                f"xc_functional={self._xc_functional}",
                f"xcf_library={self._xcf_library}",
            ])

        driver_result.add_property(
            DriverMetadata("PYSCF", pyscf_version, "\n".join(cfg + [""])))
예제 #2
0
    def dump(
        driver_result: ElectronicStructureDriverResult,
        outpath: str,
        orbsym: Optional[List[str]] = None,
        isym: int = 1,
    ) -> None:
        """Convenience method to produce an FCIDump output file.

        Args:
            outpath: Path to the output file.
            driver_result: The ElectronicStructureDriverResult to be dumped. It is assumed that the
                nuclear_repulsion_energy contains the inactive core energy in its ElectronicEnergy
                property.
            orbsym: A list of spatial symmetries of the orbitals.
            isym: The spatial symmetry of the wave function.
        """
        particle_number = cast(ParticleNumber, driver_result.get_property(ParticleNumber))
        electronic_energy = cast(ElectronicEnergy, driver_result.get_property(ElectronicEnergy))
        one_body_integrals = electronic_energy.get_electronic_integral(ElectronicBasis.MO, 1)
        two_body_integrals = electronic_energy.get_electronic_integral(ElectronicBasis.MO, 2)
        dump(
            outpath,
            particle_number.num_spin_orbitals // 2,
            particle_number.num_alpha + particle_number.num_beta,
            one_body_integrals._matrices,  # type: ignore
            two_body_integrals._matrices[0:3],  # type: ignore
            electronic_energy.nuclear_repulsion_energy,
            ms2=driver_result.molecule.multiplicity - 1,
            orbsym=orbsym,
            isym=isym,
        )
예제 #3
0
    def _populate_driver_result_basis_transform(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        # pylint: disable=import-error
        from pyscf.tools import dump_mat

        mo_coeff, mo_coeff_b = self._extract_mo_data("mo_coeff",
                                                     array_dimension=3)

        if logger.isEnabledFor(logging.DEBUG):
            # Add some more to PySCF output...
            # First analyze() which prints extra information about MO energy and occupation
            self._mol.stdout.write("\n")
            self._calc.analyze()
            # Now labelled orbitals for contributions to the MOs for s,p,d etc of each atom
            self._mol.stdout.write("\n\n--- Alpha Molecular Orbitals ---\n\n")
            dump_mat.dump_mo(self._mol, mo_coeff, digits=7, start=1)
            if mo_coeff_b is not None:
                self._mol.stdout.write("\n--- Beta Molecular Orbitals ---\n\n")
                dump_mat.dump_mo(self._mol, mo_coeff_b, digits=7, start=1)
            self._mol.stdout.flush()

        driver_result.add_property(
            ElectronicBasisTransform(
                ElectronicBasis.AO,
                ElectronicBasis.MO,
                mo_coeff,
                mo_coeff_b,
            ))
예제 #4
0
 def _populate_driver_result_particle_number(
         self, driver_result: ElectronicStructureDriverResult) -> None:
     driver_result.add_property(
         ParticleNumber(
             num_spin_orbitals=self._nmo * 2,
             num_particles=(self._mol.nup(), self._mol.ndown()),
         ))
예제 #5
0
    def _populate_driver_result_particle_number(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        mo_occ, mo_occ_b = self._extract_mo_data("mo_occ")

        driver_result.add_property(
            ParticleNumber(
                num_spin_orbitals=self._mol.nao * 2,
                num_particles=(self._mol.nelec[0], self._mol.nelec[1]),
                occupation=mo_occ,
                occupation_beta=mo_occ_b,
            ))
예제 #6
0
    def run(self) -> ElectronicStructureDriverResult:
        atoms = self._atoms
        charge = self._charge
        multiplicity = self._multiplicity
        units = self._units
        basis = self.basis
        method = self.method

        q_mol = compute_integrals(
            atoms=atoms,
            units=units.value,
            charge=charge,
            multiplicity=multiplicity,
            basis=basis.value,
            method=method.value,
            tol=self._tol,
            maxiters=self._maxiters,
        )

        q_mol.origin_driver_name = "PYQUANTE"
        cfg = [
            f"atoms={atoms}",
            f"units={units.value}",
            f"charge={charge}",
            f"multiplicity={multiplicity}",
            f"basis={basis.value}",
            f"method={method.value}",
            f"tol={self._tol}",
            f"maxiters={self._maxiters}",
            "",
        ]
        q_mol.origin_driver_config = "\n".join(cfg)

        return ElectronicStructureDriverResult.from_legacy_driver_result(q_mol)
예제 #7
0
    def run(self) -> ElectronicStructureDriverResult:
        cfg = self._config
        while not cfg.endswith("\n\n"):
            cfg += "\n"

        logger.debug(
            "User supplied configuration raw: '%s'",
            cfg.replace("\r", "\\r").replace("\n", "\\n"),
        )
        logger.debug("User supplied configuration\n%s", cfg)

        # To the Gaussian section of the input file passed here as section string
        # add line '# Symm=NoInt output=(matrix,i4labels,mo2el) tran=full'
        # NB: Line above needs to be added in right context, i.e after any lines
        #     beginning with % along with any others that start with #
        # append at end the name of the MatrixElement file to be written

        file, fname = tempfile.mkstemp(suffix=".mat")
        os.close(file)

        cfg = GaussianDriver._augment_config(fname, cfg)
        logger.debug("Augmented control information:\n%s", cfg)

        run_g16(cfg)

        q_mol = GaussianDriver._parse_matrix_file(fname)
        try:
            os.remove(fname)
        except Exception:  # pylint: disable=broad-except
            logger.warning("Failed to remove MatrixElement file %s", fname)

        q_mol.origin_driver_name = "GAUSSIAN"
        q_mol.origin_driver_config = cfg
        return ElectronicStructureDriverResult.from_legacy_driver_result(q_mol)
예제 #8
0
    def convert(self, replace: bool = False) -> None:
        """Converts a legacy QMolecule HDF5 file into the new Property-framework.

        Args:
            replace: if True, will replace the original HDF5 file. Otherwise `_new.hdf5` will be
                used as a suffix.

        Raises:
            LookupError: file not found.
        """
        hdf5_file = self._get_path()

        warnings.filterwarnings("ignore", category=DeprecationWarning)
        q_mol = QMolecule(hdf5_file)
        warnings.filterwarnings("default", category=DeprecationWarning)
        q_mol.load()

        new_hdf5_file = hdf5_file
        if not replace:
            new_hdf5_file = hdf5_file.with_name(
                str(hdf5_file.stem) + "_new.hdf5")

        warnings.filterwarnings("ignore", category=DeprecationWarning)
        driver_result = ElectronicStructureDriverResult.from_legacy_driver_result(
            q_mol)
        warnings.filterwarnings("default", category=DeprecationWarning)
        save_to_hdf5(driver_result, str(new_hdf5_file), replace=replace)
예제 #9
0
    def _populate_driver_result_metadata(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        cfg = [
            f"atoms={self.atoms}",
            f"units={self.units.value}",
            f"charge={self.charge}",
            f"multiplicity={self.multiplicity}",
            f"basis={self.basis.value}",
            f"method={self.method.value}",
            f"tol={self._tol}",
            f"maxiters={self._maxiters}",
            "",
        ]

        driver_result.add_property(
            DriverMetadata("PYQUANTE", "?", "\n".join(cfg)))
예제 #10
0
    def _populate_driver_result_electronic_energy(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        # pylint: disable=import-error
        from pyquante2 import onee_integrals
        from pyquante2.ints.integrals import twoe_integrals

        basis_transform = driver_result.get_property(ElectronicBasisTransform)

        integrals = onee_integrals(self._bfs, self._mol)

        hij = integrals.T + integrals.V
        hijkl = twoe_integrals(self._bfs)

        one_body_ao = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                 (hij, None))

        two_body_ao = TwoBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (hijkl.transform(np.identity(self._nmo)), None, None, None),
        )

        one_body_mo = one_body_ao.transform_basis(basis_transform)
        two_body_mo = two_body_ao.transform_basis(basis_transform)

        electronic_energy = ElectronicEnergy(
            [one_body_ao, two_body_ao, one_body_mo, two_body_mo],
            nuclear_repulsion_energy=self._mol.nuclear_repulsion(),
            reference_energy=self._calc.energy,
        )

        if hasattr(self._calc, "orbe"):
            orbs_energy = self._calc.orbe
            orbs_energy_b = None
        else:
            orbs_energy = self._calc.orbea
            orbs_energy_b = self._calc.orbeb

        orbital_energies = ((orbs_energy, orbs_energy_b)
                            if orbs_energy_b is not None else orbs_energy)
        electronic_energy.orbital_energies = np.asarray(orbital_energies)

        electronic_energy.kinetic = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (integrals.T, None))
        electronic_energy.overlap = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (integrals.S, None))

        driver_result.add_property(electronic_energy)
예제 #11
0
    def _populate_driver_result_electronic_dipole_moment(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        basis_transform = driver_result.get_property(ElectronicBasisTransform)

        self._mol.set_common_orig((0, 0, 0))
        ao_dip = self._mol.intor_symmetric("int1e_r", comp=3)

        d_m = self._calc.make_rdm1(self._calc.mo_coeff, self._calc.mo_occ)

        if not (isinstance(d_m, np.ndarray) and d_m.ndim == 2):
            d_m = d_m[0] + d_m[1]

        elec_dip = np.negative(np.einsum("xij,ji->x", ao_dip, d_m).real)
        elec_dip = np.round(elec_dip, decimals=8)
        nucl_dip = np.einsum("i,ix->x", self._mol.atom_charges(),
                             self._mol.atom_coords())
        nucl_dip = np.round(nucl_dip, decimals=8)

        logger.info("HF Electronic dipole moment: %s", elec_dip)
        logger.info("Nuclear dipole moment: %s", nucl_dip)
        logger.info("Total dipole moment: %s", nucl_dip + elec_dip)

        x_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (ao_dip[0], None))
        y_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (ao_dip[1], None))
        z_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (ao_dip[2], None))

        x_dipole = DipoleMoment(
            "x", [x_dip_ints,
                  x_dip_ints.transform_basis(basis_transform)])
        y_dipole = DipoleMoment(
            "y", [y_dip_ints,
                  y_dip_ints.transform_basis(basis_transform)])
        z_dipole = DipoleMoment(
            "z", [z_dip_ints,
                  z_dip_ints.transform_basis(basis_transform)])

        driver_result.add_property(
            ElectronicDipoleMoment(
                [x_dipole, y_dipole, z_dipole],
                nuclear_dipole_moment=nucl_dip,
                reverse_dipole_sign=True,
            ))
예제 #12
0
    def _populate_driver_result_basis_transform(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        if hasattr(self._calc, "orbs"):
            mo_coeff = self._calc.orbs
            mo_coeff_b = None
        else:
            mo_coeff = self._calc.orbsa
            mo_coeff_b = self._calc.orbsb

        self._nmo = len(mo_coeff)

        driver_result.add_property(
            ElectronicBasisTransform(
                ElectronicBasis.AO,
                ElectronicBasis.MO,
                mo_coeff,
                mo_coeff_b,
            ))
    def second_q_ops(self) -> ListOrDictType[SecondQuantizedOp]:
        """Returns the second quantized operators associated with this Property.

        If the arguments are returned as a `list`, the operators are in the following order: the
        Hamiltonian operator, total particle number operator, total angular momentum operator, total
        magnetization operator, and (if available) x, y, z dipole operators.

        The actual return-type is determined by `qiskit_nature.settings.dict_aux_operators`.

        Returns:
            A `list` or `dict` of `SecondQuantizedOp` objects.
        """
        driver_result = self.driver.run()

        if self._legacy_driver:
            self._molecule_data = cast(QMolecule, driver_result)
            self._grouped_property = ElectronicStructureDriverResult.from_legacy_driver_result(
                self._molecule_data)

            if self._legacy_transform:
                self._molecule_data_transformed = self._transform(
                    self._molecule_data)
                self._grouped_property_transformed = (
                    ElectronicStructureDriverResult.from_legacy_driver_result(
                        self._molecule_data_transformed))

            else:
                if not self.transformers:
                    # if no transformers are supplied, we can still provide
                    # `molecule_data_transformed` as a copy of `molecule_data`
                    self._molecule_data_transformed = self._molecule_data
                self._grouped_property_transformed = self._transform(
                    self._grouped_property)

        else:
            self._grouped_property = driver_result
            self._grouped_property_transformed = self._transform(
                self._grouped_property)

        second_quantized_ops = self._grouped_property_transformed.second_q_ops(
        )

        return second_quantized_ops
예제 #14
0
    def _populate_driver_result_molecule(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        geometry: List[Tuple[str, List[float]]] = []
        for atom in self._mol.atoms:
            atuple = atom.atuple()
            geometry.append(
                (PERIODIC_TABLE[atuple[0]], [a * BOHR for a in atuple[1:]]))

        driver_result.molecule = Molecule(geometry,
                                          multiplicity=self._mol.multiplicity,
                                          charge=self._mol.charge)
예제 #15
0
    def _populate_driver_result_electronic_energy(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        # pylint: disable=import-error
        from pyscf import gto

        basis_transform = driver_result.get_property(ElectronicBasisTransform)

        one_body_ao = OneBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (self._calc.get_hcore(), None),
        )

        two_body_ao = TwoBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (self._mol.intor("int2e", aosym=1), None, None, None),
        )

        one_body_mo = one_body_ao.transform_basis(basis_transform)
        two_body_mo = two_body_ao.transform_basis(basis_transform)

        electronic_energy = ElectronicEnergy(
            [one_body_ao, two_body_ao, one_body_mo, two_body_mo],
            nuclear_repulsion_energy=gto.mole.energy_nuc(self._mol),
            reference_energy=self._calc.e_tot,
        )

        electronic_energy.kinetic = OneBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (self._mol.intor_symmetric("int1e_kin"), None),
        )
        electronic_energy.overlap = OneBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (self._calc.get_ovlp(), None),
        )

        orbs_energy, orbs_energy_b = self._extract_mo_data("mo_energy")
        orbital_energies = ((orbs_energy, orbs_energy_b)
                            if orbs_energy_b is not None else orbs_energy)
        electronic_energy.orbital_energies = np.asarray(orbital_energies)

        driver_result.add_property(electronic_energy)
예제 #16
0
    def _populate_driver_result_molecule(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        coords = self._mol.atom_coords(unit="Angstrom")
        geometry = [(self._mol.atom_pure_symbol(i), list(xyz))
                    for i, xyz in enumerate(coords)]

        driver_result.molecule = Molecule(
            geometry,
            multiplicity=self._spin + 1,
            charge=self._charge,
            masses=list(self._mol.atom_mass_list()),
        )
예제 #17
0
    def run(self) -> ElectronicStructureDriverResult:
        """Returns an ElectronicStructureDriverResult instance out of a FCIDump file."""
        fcidump_data = parse(self._fcidump_input)

        hij = fcidump_data.get("hij", None)
        hij_b = fcidump_data.get("hij_b", None)
        hijkl = fcidump_data.get("hijkl", None)
        hijkl_ba = fcidump_data.get("hijkl_ba", None)
        hijkl_bb = fcidump_data.get("hijkl_bb", None)

        multiplicity = fcidump_data.get("MS2", 0) + 1
        num_beta = (fcidump_data.get("NELEC") - (multiplicity - 1)) // 2
        num_alpha = fcidump_data.get("NELEC") - num_beta

        particle_number = ParticleNumber(
            num_spin_orbitals=fcidump_data.get("NORB") * 2,
            num_particles=(num_alpha, num_beta),
        )

        electronic_energy = ElectronicEnergy(
            [
                OneBodyElectronicIntegrals(ElectronicBasis.MO, (hij, hij_b)),
                TwoBodyElectronicIntegrals(ElectronicBasis.MO, (hijkl, hijkl_ba, hijkl_bb, None)),
            ],
            nuclear_repulsion_energy=fcidump_data.get("ecore", None),
        )

        driver_result = ElectronicStructureDriverResult()
        driver_result.add_property(electronic_energy)
        driver_result.add_property(particle_number)

        return driver_result
예제 #18
0
 def test_from_hdf5(self):
     """Test from_hdf5."""
     with h5py.File(
             self.get_resource_path(
                 "electronic_structure_driver_result.hdf5",
                 "properties/second_quantization/electronic/resources",
             ),
             "r",
     ) as file:
         for group in file.values():
             prop = ElectronicStructureDriverResult.from_hdf5(group)
             for inner_prop in iter(prop):
                 expected = self.expected.get_property(type(inner_prop))
                 self.assertEqual(inner_prop, expected)
예제 #19
0
 def setUp(self):
     super().setUp()
     hdf5_file = self.get_resource_path(
         "test_driver_hdf5_legacy.hdf5",
         "drivers/second_quantization/hdf5d")
     # Using QMolecule directly here to avoid the deprecation on HDF5Driver.run method
     # to be triggered and let it be handled on the method test_convert
     # Those deprecation messages are shown only once and this one could prevent
     # the test_convert one to show if called first.
     molecule = QMolecule(hdf5_file)
     molecule.load()
     warnings.filterwarnings("ignore", category=DeprecationWarning)
     self.driver_result = ElectronicStructureDriverResult.from_legacy_driver_result(
         molecule)
     warnings.filterwarnings("default", category=DeprecationWarning)
 def setUp(self):
     super().setUp()
     self.good_check = GaussianDriver.check_installed
     GaussianDriver.check_installed = _check_installed
     # We can now create a driver without the installed (check valid) test failing
     # and create a qmolecule from the saved output matrix file. This will test the
     # parsing of it into the qmolecule is correct.
     g16 = GaussianDriver()
     matfile = self.get_resource_path(
         "test_driver_gaussian_from_mat.mat",
         "drivers/second_quantization/gaussiand")
     try:
         q_mol = g16._parse_matrix_file(matfile)
         self.driver_result = ElectronicStructureDriverResult.from_legacy_driver_result(
             q_mol)
     except QiskitNatureError:
         self.tearDown()
         self.skipTest("GAUSSIAN qcmatrixio not found")
예제 #21
0
    def run(self) -> GroupedSecondQuantizedProperty:
        """
        Returns:
            GroupedSecondQuantizedProperty re-constructed from the HDF5 file.

        Raises:
            LookupError: file not found.
            QiskitNatureError: if the HDF5 file did not contain a GroupedSecondQuantizedProperty.
        """
        hdf5_file = self._get_path()

        legacy_hdf5_file = False

        with h5py.File(hdf5_file, "r") as file:
            if "origin_driver" in file.keys():
                legacy_hdf5_file = True
                warn_deprecated(
                    "0.4.0",
                    DeprecatedType.METHOD,
                    "HDF5Driver.run with legacy HDF5 file",
                    additional_msg=
                    (". Your HDF5 file contains the legacy QMolecule object! You should consider "
                     "converting it to the new property framework. See also HDF5Driver.convert"
                     ),
                )

        if legacy_hdf5_file:
            warnings.filterwarnings("ignore", category=DeprecationWarning)
            try:
                molecule = QMolecule(hdf5_file)
                molecule.load()
                return ElectronicStructureDriverResult.from_legacy_driver_result(
                    molecule)
            finally:
                warnings.filterwarnings("default", category=DeprecationWarning)

        driver_result = load_from_hdf5(str(hdf5_file))

        if not isinstance(driver_result, GroupedSecondQuantizedProperty):
            raise QiskitNatureError(
                f"Expected a GroupedSecondQuantizedProperty but found a {type(driver_result)} "
                "object instead.")

        return driver_result
예제 #22
0
    def _construct_driver_result(self) -> ElectronicStructureDriverResult:
        driver_result = ElectronicStructureDriverResult()

        self._populate_driver_result_molecule(driver_result)
        self._populate_driver_result_metadata(driver_result)
        self._populate_driver_result_basis_transform(driver_result)
        self._populate_driver_result_particle_number(driver_result)
        self._populate_driver_result_electronic_energy(driver_result)

        # TODO: once https://github.com/Qiskit/qiskit-nature/issues/312 is fixed we can stop adding
        # these properties by default.
        # if not settings.dict_aux_operators:
        driver_result.add_property(AngularMomentum(self._nmo * 2))
        driver_result.add_property(Magnetization(self._nmo * 2))

        return driver_result
예제 #23
0
    def run(self) -> ElectronicStructureDriverResult:
        """
        Returns:
            ElectronicStructureDriverResult re-constructed from the HDF5 file.

        Raises:
            LookupError: file not found.
        """
        hdf5_file = self._hdf5_input
        if self.work_path is not None and not os.path.isabs(hdf5_file):
            hdf5_file = os.path.abspath(os.path.join(self.work_path,
                                                     hdf5_file))

        if not os.path.isfile(hdf5_file):
            raise LookupError(f"HDF5 file not found: {hdf5_file}")

        warnings.filterwarnings("ignore", category=DeprecationWarning)
        molecule = QMolecule(hdf5_file)
        warnings.filterwarnings("default", category=DeprecationWarning)
        molecule.load()
        return ElectronicStructureDriverResult.from_legacy_driver_result(
            molecule)
예제 #24
0
    def _construct_driver_result(self) -> ElectronicStructureDriverResult:
        # NOTE: under Python 3.6, pylint appears to be unable to properly identify this case of
        # nested abstract classes (cf. https://github.com/Qiskit/qiskit-nature/runs/3245395353).
        # However, since the tests pass I am adding an exception for this specific case.
        # pylint: disable=abstract-class-instantiated
        driver_result = ElectronicStructureDriverResult()

        self._populate_driver_result_molecule(driver_result)
        self._populate_driver_result_metadata(driver_result)
        self._populate_driver_result_basis_transform(driver_result)
        self._populate_driver_result_particle_number(driver_result)
        self._populate_driver_result_electronic_energy(driver_result)
        self._populate_driver_result_electronic_dipole_moment(driver_result)

        # TODO: once https://github.com/Qiskit/qiskit-terra/issues/6772 is resolved, we no longer
        # _have_ to add these properties. However, until then the interpret method relies on indices
        # of the aux_operators which are incorrect if these properties are not added.
        driver_result.add_property(AngularMomentum(self._mol.nao * 2))
        driver_result.add_property(Magnetization(self._mol.nao * 2))

        return driver_result
예제 #25
0
    def run(self) -> ElectronicStructureDriverResult:
        """Returns an ElectronicStructureDriverResult instance out of a FCIDump file."""
        fcidump_data = parse(self._fcidump_input)

        hij = fcidump_data.get("hij", None)
        hij_b = fcidump_data.get("hij_b", None)
        hijkl = fcidump_data.get("hijkl", None)
        hijkl_ba = fcidump_data.get("hijkl_ba", None)
        hijkl_bb = fcidump_data.get("hijkl_bb", None)

        multiplicity = fcidump_data.get("MS2", 0) + 1
        num_beta = (fcidump_data.get("NELEC") - (multiplicity - 1)) // 2
        num_alpha = fcidump_data.get("NELEC") - num_beta

        particle_number = ParticleNumber(
            num_spin_orbitals=fcidump_data.get("NORB") * 2,
            num_particles=(num_alpha, num_beta),
        )

        electronic_energy = ElectronicEnergy(
            [
                OneBodyElectronicIntegrals(ElectronicBasis.MO, (hij, hij_b)),
                TwoBodyElectronicIntegrals(ElectronicBasis.MO, (hijkl, hijkl_ba, hijkl_bb, None)),
            ],
            nuclear_repulsion_energy=fcidump_data.get("ecore", None),
        )

        # NOTE: under Python 3.6, pylint appears to be unable to properly identify this case of
        # nested abstract classes (cf. https://github.com/Qiskit/qiskit-nature/runs/3245395353).
        # However, since the tests pass I am adding an exception for this specific case.
        # pylint: disable=abstract-class-instantiated
        driver_result = ElectronicStructureDriverResult()
        driver_result.add_property(electronic_energy)
        driver_result.add_property(particle_number)

        return driver_result
예제 #26
0
    def test_tuple_num_electrons_with_manual_orbitals(self):
        """Regression test against https://github.com/Qiskit/qiskit-nature/issues/434."""
        driver = HDF5Driver(hdf5_input=self.get_resource_path(
            "H2_631g.hdf5", "transformers/second_quantization/electronic"))
        driver_result = driver.run()

        trafo = ActiveSpaceTransformer(
            num_electrons=(1, 1),
            num_molecular_orbitals=2,
            active_orbitals=[0, 1],
        )
        driver_result_reduced = trafo.transform(driver_result)

        expected = ElectronicStructureDriverResult()
        expected.add_property(
            ElectronicEnergy(
                [
                    OneBodyElectronicIntegrals(
                        ElectronicBasis.MO,
                        (np.asarray([[-1.24943841, 0.0], [0.0, -0.547816138]
                                     ]), None),
                    ),
                    TwoBodyElectronicIntegrals(
                        ElectronicBasis.MO,
                        (
                            np.asarray([
                                [
                                    [[0.652098466, 0.0], [0.0, 0.433536565]],
                                    [[0.0, 0.0794483182], [0.0794483182, 0.0]],
                                ],
                                [
                                    [[0.0, 0.0794483182], [0.0794483182, 0.0]],
                                    [[0.433536565, 0.0], [0.0, 0.385524695]],
                                ],
                            ]),
                            None,
                            None,
                            None,
                        ),
                    ),
                ],
                energy_shift={"ActiveSpaceTransformer": 0.0},
            ))
        expected.add_property(
            ElectronicDipoleMoment([
                DipoleMoment(
                    "x",
                    [
                        OneBodyElectronicIntegrals(ElectronicBasis.MO,
                                                   (np.zeros((2, 2)), None))
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
                DipoleMoment(
                    "y",
                    [
                        OneBodyElectronicIntegrals(ElectronicBasis.MO,
                                                   (np.zeros((2, 2)), None))
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
                DipoleMoment(
                    "z",
                    [
                        OneBodyElectronicIntegrals(
                            ElectronicBasis.MO,
                            (
                                np.asarray([[0.69447435, -1.01418298],
                                            [-1.01418298, 0.69447435]]),
                                None,
                            ),
                        )
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
            ]))

        self.assertDriverResult(driver_result_reduced, expected)
    def transform(
        self, grouped_property: GroupedSecondQuantizedProperty
    ) -> GroupedElectronicProperty:
        """Reduces the given `GroupedElectronicProperty` to a given active space.

        Args:
            grouped_property: the `GroupedElectronicProperty` to be transformed.

        Returns:
            A new `GroupedElectronicProperty` instance.

        Raises:
            QiskitNatureError: If the provided `GroupedElectronicProperty` does not contain a
                               `ParticleNumber` or `ElectronicBasisTransform` instance, if more
                               electrons or orbitals are requested than are available, or if the
                               number of selected active orbital indices does not match
                               `num_molecular_orbitals`.
        """
        if not isinstance(grouped_property, GroupedElectronicProperty):
            raise QiskitNatureError(
                "Only `GroupedElectronicProperty` objects can be transformed by this Transformer, "
                f"not objects of type, {type(grouped_property)}."
            )

        particle_number = grouped_property.get_property(ParticleNumber)
        if particle_number is None:
            raise QiskitNatureError(
                "The provided `GroupedElectronicProperty` does not contain a `ParticleNumber` "
                "property, which is required by this transformer!"
            )
        particle_number = cast(ParticleNumber, particle_number)

        electronic_basis_transform = grouped_property.get_property(ElectronicBasisTransform)
        if electronic_basis_transform is None:
            raise QiskitNatureError(
                "The provided `GroupedElectronicProperty` does not contain an "
                "`ElectronicBasisTransform` property, which is required by this transformer!"
            )
        electronic_basis_transform = cast(ElectronicBasisTransform, electronic_basis_transform)

        # get molecular orbital occupation numbers
        occupation_alpha = particle_number.occupation_alpha
        occupation_beta = particle_number.occupation_beta
        self._mo_occ_total = occupation_alpha + occupation_beta

        # determine the active space
        self._active_orbs_indices, inactive_orbs_idxs = self._determine_active_space(
            grouped_property
        )

        # get molecular orbital coefficients
        coeff_alpha = electronic_basis_transform.coeff_alpha
        coeff_beta = electronic_basis_transform.coeff_beta

        # initialize size-reducing basis transformation
        self._transform_active = ElectronicBasisTransform(
            ElectronicBasis.AO,
            ElectronicBasis.MO,
            coeff_alpha[:, self._active_orbs_indices],
            coeff_beta[:, self._active_orbs_indices],
        )

        # compute inactive density matrix
        def _inactive_density(mo_occ, mo_coeff):
            return np.dot(
                mo_coeff[:, inactive_orbs_idxs] * mo_occ[inactive_orbs_idxs],
                np.transpose(mo_coeff[:, inactive_orbs_idxs]),
            )

        self._density_inactive = OneBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (
                _inactive_density(occupation_alpha, coeff_alpha),
                _inactive_density(occupation_beta, coeff_beta),
            ),
        )

        # construct new GroupedElectronicProperty
        grouped_property_transformed = ElectronicStructureDriverResult()
        grouped_property_transformed = self._transform_property(grouped_property)  # type: ignore
        grouped_property_transformed.molecule = (
            grouped_property.molecule  # type: ignore[attr-defined]
        )

        return grouped_property_transformed
예제 #28
0
    def run(self) -> ElectronicStructureDriverResult:
        cfg = self._config

        psi4d_directory = Path(__file__).resolve().parent
        template_file = psi4d_directory.joinpath("_template.txt")
        qiskit_nature_directory = psi4d_directory.parent.parent

        warnings.filterwarnings("ignore", category=DeprecationWarning)
        molecule = QMolecule()
        warnings.filterwarnings("default", category=DeprecationWarning)

        input_text = [cfg]
        input_text += ["import sys"]
        syspath = (
            "['"
            + qiskit_nature_directory.as_posix()
            + "','"
            + "','".join(Path(p).as_posix() for p in sys.path)
            + "']"
        )

        input_text += ["sys.path = " + syspath + " + sys.path"]
        input_text += ["import warnings"]
        input_text += ["from qiskit_nature.drivers.qmolecule import QMolecule"]
        input_text += ["warnings.filterwarnings('ignore', category=DeprecationWarning)"]
        input_text += [f'_q_molecule = QMolecule("{Path(molecule.filename).as_posix()}")']
        input_text += ["warnings.filterwarnings('default', category=DeprecationWarning)"]

        with open(template_file, "r", encoding="utf8") as file:
            input_text += [line.strip("\n") for line in file.readlines()]

        file_fd, input_file = tempfile.mkstemp(suffix=".inp")
        os.close(file_fd)
        with open(input_file, "w", encoding="utf8") as stream:
            stream.write("\n".join(input_text))

        file_fd, output_file = tempfile.mkstemp(suffix=".out")
        os.close(file_fd)
        try:
            PSI4Driver._run_psi4(input_file, output_file)
            if logger.isEnabledFor(logging.DEBUG):
                with open(output_file, "r", encoding="utf8") as file:
                    logger.debug("PSI4 output file:\n%s", file.read())
        finally:
            run_directory = os.getcwd()
            for local_file in os.listdir(run_directory):
                if local_file.endswith(".clean"):
                    os.remove(run_directory + "/" + local_file)
            try:
                os.remove("timer.dat")
            except Exception:  # pylint: disable=broad-except
                pass

            try:
                os.remove(input_file)
            except Exception:  # pylint: disable=broad-except
                pass

            try:
                os.remove(output_file)
            except Exception:  # pylint: disable=broad-except
                pass

        warnings.filterwarnings("ignore", category=DeprecationWarning)
        _q_molecule = QMolecule(molecule.filename)
        warnings.filterwarnings("default", category=DeprecationWarning)
        _q_molecule.load()
        # remove internal file
        _q_molecule.remove_file()
        _q_molecule.origin_driver_name = "PSI4"
        _q_molecule.origin_driver_config = cfg
        return ElectronicStructureDriverResult.from_legacy_driver_result(_q_molecule)
예제 #29
0
    def _parse_matrix_file(
            fname: str,
            useao2e: bool = False) -> ElectronicStructureDriverResult:
        """
        get_driver_class is used here because the discovery routine will load all the gaussian
        binary dependencies, if not loaded already. It won't work without it.
        """
        try:
            # add gauopen to sys.path so that binaries can be loaded
            gauopen_directory = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), "gauopen")
            if gauopen_directory not in sys.path:
                sys.path.insert(0, gauopen_directory)
            # pylint: disable=import-outside-toplevel
            from .gauopen.QCMatEl import MatEl
        except ImportError as mnfe:
            msg = ((
                "qcmatrixio extension not found. "
                "See Gaussian driver readme to build qcmatrixio.F using f2py")
                   if mnfe.name == "qcmatrixio" else str(mnfe))

            logger.info(msg)
            raise QiskitNatureError(msg) from mnfe

        mel = MatEl(file=fname)
        logger.debug("MatrixElement file:\n%s", mel)

        driver_result = ElectronicStructureDriverResult()

        # molecule
        coords = np.reshape(mel.c, (len(mel.ian), 3))
        geometry: list[tuple[str, list[float]]] = []
        for atom, xyz in zip(mel.ian, coords):
            geometry.append((PERIODIC_TABLE[atom], BOHR * xyz))

        driver_result.molecule = Molecule(
            geometry,
            multiplicity=mel.multip,
            charge=mel.icharg,
        )

        # driver metadata
        driver_result.add_property(DriverMetadata("GAUSSIAN", mel.gversion,
                                                  ""))

        # basis transform
        moc = GaussianDriver._get_matrix(mel, "ALPHA MO COEFFICIENTS")
        moc_b = GaussianDriver._get_matrix(mel, "BETA MO COEFFICIENTS")
        if np.array_equal(moc, moc_b):
            logger.debug(
                "ALPHA and BETA MO COEFFS identical, keeping only ALPHA")
            moc_b = None

        nmo = moc.shape[0]

        basis_transform = ElectronicBasisTransform(ElectronicBasis.AO,
                                                   ElectronicBasis.MO, moc,
                                                   moc_b)
        driver_result.add_property(basis_transform)

        # particle number
        num_alpha = (mel.ne + mel.multip - 1) // 2
        num_beta = (mel.ne - mel.multip + 1) // 2

        driver_result.add_property(
            ParticleNumber(num_spin_orbitals=nmo * 2,
                           num_particles=(num_alpha, num_beta)))

        # electronic energy
        hcore = GaussianDriver._get_matrix(mel, "CORE HAMILTONIAN ALPHA")
        logger.debug("CORE HAMILTONIAN ALPHA %s", hcore.shape)
        hcore_b = GaussianDriver._get_matrix(mel, "CORE HAMILTONIAN BETA")
        if np.array_equal(hcore, hcore_b):
            # From Gaussian interfacing documentation: "The two core Hamiltonians are identical
            # unless a Fermi contact perturbation has been applied."
            logger.debug(
                "CORE HAMILTONIAN ALPHA and BETA identical, keeping only ALPHA"
            )
            hcore_b = None
        logger.debug(
            "CORE HAMILTONIAN BETA %s",
            "- Not present" if hcore_b is None else hcore_b.shape,
        )
        one_body_ao = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                 (hcore, hcore_b))
        one_body_mo = one_body_ao.transform_basis(basis_transform)

        eri = GaussianDriver._get_matrix(mel, "REGULAR 2E INTEGRALS")
        logger.debug("REGULAR 2E INTEGRALS %s", eri.shape)
        if moc_b is None and mel.matlist.get("BB MO 2E INTEGRALS") is not None:
            # It seems that when using ROHF, where alpha and beta coeffs are
            # the same, that integrals
            # for BB and BA are included in the output, as well as just AA
            # that would have been expected
            # Using these fails to give the right answer (is ok for UHF).
            # So in this case we revert to
            # using 2 electron ints in atomic basis from the output and
            # converting them ourselves.
            useao2e = True
            logger.info(
                "Identical A and B coeffs but BB ints are present - using regular 2E ints instead"
            )
        two_body_ao = TwoBodyElectronicIntegrals(ElectronicBasis.AO,
                                                 (eri, None, None, None))
        two_body_mo: TwoBodyElectronicIntegrals
        if useao2e:
            # eri are 2-body in AO. We can convert to MO via the ElectronicBasisTransform but using
            # ints in MO already, as in the else here, is better
            two_body_mo = two_body_ao.transform_basis(basis_transform)
        else:
            # These are in MO basis but by default will be reduced in size by frozen core default so
            # to use them we need to add Window=Full above when we augment the config
            mohijkl = GaussianDriver._get_matrix(mel, "AA MO 2E INTEGRALS")
            logger.debug("AA MO 2E INTEGRALS %s", mohijkl.shape)
            mohijkl_bb = GaussianDriver._get_matrix(mel, "BB MO 2E INTEGRALS")
            logger.debug(
                "BB MO 2E INTEGRALS %s",
                "- Not present" if mohijkl_bb is None else mohijkl_bb.shape,
            )
            mohijkl_ba = GaussianDriver._get_matrix(mel, "BA MO 2E INTEGRALS")
            logger.debug(
                "BA MO 2E INTEGRALS %s",
                "- Not present" if mohijkl_ba is None else mohijkl_ba.shape,
            )
            two_body_mo = TwoBodyElectronicIntegrals(
                ElectronicBasis.MO, (mohijkl, mohijkl_ba, mohijkl_bb, None))

        electronic_energy = ElectronicEnergy(
            [one_body_ao, two_body_ao, one_body_mo, two_body_mo],
            nuclear_repulsion_energy=mel.scalar("ENUCREP"),
            reference_energy=mel.scalar("ETOTAL"),
        )

        kinetic = GaussianDriver._get_matrix(mel, "KINETIC ENERGY")
        logger.debug("KINETIC ENERGY %s", kinetic.shape)
        electronic_energy.kinetic = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (kinetic, None))

        overlap = GaussianDriver._get_matrix(mel, "OVERLAP")
        logger.debug("OVERLAP %s", overlap.shape)
        electronic_energy.overlap = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (overlap, None))

        orbs_energy = GaussianDriver._get_matrix(mel, "ALPHA ORBITAL ENERGIES")
        logger.debug("ORBITAL ENERGIES %s", overlap.shape)
        orbs_energy_b = GaussianDriver._get_matrix(mel,
                                                   "BETA ORBITAL ENERGIES")
        logger.debug("BETA ORBITAL ENERGIES %s", overlap.shape)
        orbital_energies = (
            orbs_energy, orbs_energy_b) if moc_b is not None else orbs_energy
        electronic_energy.orbital_energies = np.asarray(orbital_energies)

        driver_result.add_property(electronic_energy)

        # dipole moment
        dipints = GaussianDriver._get_matrix(mel, "DIPOLE INTEGRALS")
        dipints = np.einsum("ijk->kji", dipints)

        x_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (dipints[0], None))
        y_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (dipints[1], None))
        z_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                (dipints[2], None))

        x_dipole = DipoleMoment(
            "x", [x_dip_ints,
                  x_dip_ints.transform_basis(basis_transform)])
        y_dipole = DipoleMoment(
            "y", [y_dip_ints,
                  y_dip_ints.transform_basis(basis_transform)])
        z_dipole = DipoleMoment(
            "z", [z_dip_ints,
                  z_dip_ints.transform_basis(basis_transform)])

        nucl_dip = np.einsum("i,ix->x", mel.ian, coords)
        nucl_dip = np.round(nucl_dip, decimals=8)

        driver_result.add_property(
            ElectronicDipoleMoment(
                [x_dipole, y_dipole, z_dipole],
                nuclear_dipole_moment=nucl_dip,
                reverse_dipole_sign=True,
            ))

        # extra properties
        # TODO: once https://github.com/Qiskit/qiskit-nature/issues/312 is fixed we can stop adding
        # these properties by default.
        # if not settings.dict_aux_operators:
        driver_result.add_property(AngularMomentum(nmo * 2))
        driver_result.add_property(Magnetization(nmo * 2))

        return driver_result
예제 #30
0
    def test_arbitrary_active_orbitals(self):
        """Test manual selection of active orbital indices."""
        driver = HDF5Driver(hdf5_input=self.get_resource_path(
            "H2_631g.hdf5", "transformers/second_quantization/electronic"))
        driver_result = driver.run()

        trafo = ActiveSpaceTransformer(num_electrons=2,
                                       num_molecular_orbitals=2,
                                       active_orbitals=[0, 2])
        driver_result_reduced = trafo.transform(driver_result)

        expected = ElectronicStructureDriverResult()
        expected.add_property(
            ElectronicEnergy(
                [
                    OneBodyElectronicIntegrals(
                        ElectronicBasis.MO,
                        (
                            np.asarray([[-1.24943841, -0.16790838],
                                        [-0.16790838, -0.18307469]]),
                            None,
                        ),
                    ),
                    TwoBodyElectronicIntegrals(
                        ElectronicBasis.MO,
                        (
                            np.asarray([
                                [
                                    [[0.65209847, 0.16790822],
                                     [0.16790822, 0.53250905]],
                                    [[0.16790822, 0.10962908],
                                     [0.10962908, 0.11981429]],
                                ],
                                [
                                    [[0.16790822, 0.10962908],
                                     [0.10962908, 0.11981429]],
                                    [[0.53250905, 0.11981429],
                                     [0.11981429, 0.46345617]],
                                ],
                            ]),
                            None,
                            None,
                            None,
                        ),
                    ),
                ],
                energy_shift={"ActiveSpaceTransformer": 0.0},
            ))
        expected.add_property(
            ElectronicDipoleMoment([
                DipoleMoment(
                    "x",
                    [
                        OneBodyElectronicIntegrals(ElectronicBasis.MO,
                                                   (np.zeros((2, 2)), None))
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
                DipoleMoment(
                    "y",
                    [
                        OneBodyElectronicIntegrals(ElectronicBasis.MO,
                                                   (np.zeros((2, 2)), None))
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
                DipoleMoment(
                    "z",
                    [
                        OneBodyElectronicIntegrals(
                            ElectronicBasis.MO,
                            (np.asarray([[0.69447435, 0.0], [0.0, 0.69447435]
                                         ]), None),
                        )
                    ],
                    shift={"ActiveSpaceTransformer": 0.0},
                ),
            ]))
        self.assertDriverResult(driver_result_reduced, expected)