From f417107c5deb0d70290c079aa8971a6b14d62733 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 17 Mar 2020 09:23:31 +0100 Subject: [PATCH 01/28] Implement FCIDumpDriver The FCIDumpDriver provides the means to construct a QMolecule instance out of a FCIDump file. It is split into three parts: - the `driver` itself which provides the normal run() method - a separate `parser` implementation which handles FCIDump reading - an additional `dumper` implementation which allows FCIDump writing --- .pylintdict | 20 +- qiskit/chemistry/drivers/__init__.py | 2 + qiskit/chemistry/drivers/fcidumpd/__init__.py | 19 ++ qiskit/chemistry/drivers/fcidumpd/dumper.py | 105 ++++++++ .../drivers/fcidumpd/fcidumpdriver.py | 90 +++++++ qiskit/chemistry/drivers/fcidumpd/parser.py | 224 ++++++++++++++++++ 6 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 qiskit/chemistry/drivers/fcidumpd/__init__.py create mode 100644 qiskit/chemistry/drivers/fcidumpd/dumper.py create mode 100644 qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py create mode 100644 qiskit/chemistry/drivers/fcidumpd/parser.py diff --git a/.pylintdict b/.pylintdict index 291cb12a96..9c2c1e49a5 100644 --- a/.pylintdict +++ b/.pylintdict @@ -140,6 +140,7 @@ eigenstate eigrange eigs eigvecs +einact einsum els endif @@ -159,6 +160,7 @@ excitations expr factorizers factr +fcidump fcompiler fermionic FermionicOperator @@ -205,16 +207,22 @@ hartree hassidim hb hhl +hijs +hijkls hk https +ia iadd +iajb idx ifdef ifortvars ign ignis ij +ijkl ijkm +ik ikmj imag implicants @@ -224,6 +232,7 @@ indvar init initializer initio +inline innerproduct inreg instantiations @@ -243,12 +252,16 @@ iteratively izaac jac jacobian +jb +ji +jl jordan jozsa jt jth jw kaicher +ket killoran kingma kitaev @@ -265,6 +278,7 @@ lhs lih lijh lin +lk lmin lnot loglikelihood @@ -317,6 +331,7 @@ msq multiclass multinomial multiprocess +namelist nan narray nasdaq @@ -326,6 +341,7 @@ ndarray ndarray's negoro nelder +nelec neq nevals newtons's @@ -338,6 +354,7 @@ nn noancilla noint noqa +norb norbs nosignatures np @@ -358,6 +375,7 @@ optim optimizer's optimizers org +outpath overfit params parentname @@ -581,4 +599,4 @@ zzz ucc uccd UCCS -vir \ No newline at end of file +vir diff --git a/qiskit/chemistry/drivers/__init__.py b/qiskit/chemistry/drivers/__init__.py index a8a9dc3e67..a838d4b500 100644 --- a/qiskit/chemistry/drivers/__init__.py +++ b/qiskit/chemistry/drivers/__init__.py @@ -78,6 +78,7 @@ """ from ._basedriver import BaseDriver, UnitsType, HFMethodType +from .fcidumpd import FCIDumpDriver from .gaussiand import GaussianDriver from .hdf5d import HDF5Driver from .psi4d import PSI4Driver @@ -87,6 +88,7 @@ __all__ = ['BaseDriver', 'UnitsType', 'HFMethodType', + 'FCIDumpDriver', 'GaussianDriver', 'HDF5Driver', 'PSI4Driver', diff --git a/qiskit/chemistry/drivers/fcidumpd/__init__.py b/qiskit/chemistry/drivers/fcidumpd/__init__.py new file mode 100644 index 0000000000..28295c0f5d --- /dev/null +++ b/qiskit/chemistry/drivers/fcidumpd/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" FCIDump package """ + +from .fcidumpdriver import FCIDumpDriver + +__all__ = ['FCIDumpDriver'] diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py new file mode 100644 index 0000000000..af43f03962 --- /dev/null +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" FCIDump dumper """ + +from io import TextIOWrapper +import itertools +import numpy as np + + +def dump(norb: int, nelec: int, hijs: list, hijkls: list, einact: float, outpath: str) -> None: + """ Generates a FCIDump output. + Args: + norb (int): number of orbitals + nelec (int): number of electrons + hijs (tuple): pair of alpha and beta 1-electron integrals. The latter may be None. + hijkls (tuple): triplet of alpha/alpha, beta/alpha and beta/beta 2-electron + integrals. The latter two may be None. + einact (float): inactive energy + outpath (str): path to the output file + """ + hij, hij_b = hijs + hijkl, hijkl_ba, hijkl_bb = hijkls + # assert that either all beta variables are None or all of them are not + assert all([h is None for h in [hij_b, hijkl_ba, hijkl_bb]]) \ + or all([h is not None for h in [hij_b, hijkl_ba, hijkl_bb]]) + assert norb == hij.shape[0] == hijkl.shape[0] + mos = range(norb) + with open(outpath, 'w') as outfile: + # print header + outfile.write('&FCI NORB={:4d},NELEC={:4d},\n/\n'.format(norb, nelec)) + # append 2e integrals + _dump_2e_ints(hijkl, mos, outfile) + if hijkl_ba is not None: + _dump_2e_ints(hijkl_ba.transpose(), mos, outfile, beta=1) + if hijkl_bb is not None: + _dump_2e_ints(hijkl_bb, mos, outfile, beta=2) + # append 1e integrals + _dump_1e_ints(hij, mos, outfile) + if hij_b is not None: + _dump_1e_ints(hij_b, mos, outfile, beta=True) + # append inactive energy + _write_to_outfile(outfile, einact, (0, 0, 0, 0)) + + +def _dump_1e_ints(hij: list, mos: list, outfile: TextIOWrapper, beta: bool = False) -> None: + idx_offset = 1 if not beta else 1+len(mos) + hij_elements = set() + for i, j in itertools.product(mos, repeat=2): + if i == j: + _write_to_outfile(outfile, hij[i][j], (i+idx_offset, j+idx_offset, 0, 0)) + continue + if (j, i) in hij_elements and np.isclose(hij[i][j], hij[j][i]): + continue + _write_to_outfile(outfile, hij[i][j], (i+idx_offset, j+idx_offset, 0, 0)) + hij_elements.add((i, j)) + + +def _dump_2e_ints(hijkl: list, mos: list, outfile: TextIOWrapper, beta: int = 0) -> None: + idx_offsets = [1, 1] + for b in range(beta): + idx_offsets[1-b] += len(mos) + hijkl_elements = set() + # pylint: disable=invalid-name + for elem in itertools.product(mos, repeat=4): + if np.isclose(hijkl[elem], 0.0, atol=1e-14): + continue + if len(set(elem)) == 1: + _write_to_outfile(outfile, hijkl[elem], (*[e+idx_offsets[0] for e in elem[:2]], + *[e+idx_offsets[1] for e in elem[2:]])) + continue + if beta != 1 and elem[::-1] in hijkl_elements and \ + np.isclose(hijkl[elem], hijkl[elem[::-1]]): + continue + bra_perms = set(itertools.permutations(elem[:2])) + ket_perms = set(itertools.permutations(elem[2:])) + if beta == 1: + permutations = itertools.product(bra_perms, ket_perms) + else: + permutations = itertools.chain( + itertools.product(bra_perms, ket_perms), + itertools.product(ket_perms, bra_perms) + ) + for perm in {e1 + e2 for e1, e2 in permutations}: + if perm in hijkl_elements and np.isclose(hijkl[elem], hijkl[perm]): + break + else: + _write_to_outfile(outfile, hijkl[elem], (*[e+idx_offsets[0] for e in elem[:2]], + *[e+idx_offsets[1] for e in elem[2:]])) + hijkl_elements.add(elem) + + +def _write_to_outfile(outfile, value, indices): + outfile.write('{:23.16E}{:4d}{:4d}{:4d}{:4d}\n'.format(value, *indices)) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py new file mode 100644 index 0000000000..609856eb1b --- /dev/null +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" FCIDump Driver """ + +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry import QiskitChemistryError, QMolecule +from qiskit.chemistry.drivers.fcidumpd.dumper import dump +from qiskit.chemistry.drivers.fcidumpd.parser import parse + + +class FCIDumpDriver(BaseDriver): + """ + Python implementation of an FCIDump driver. + + The FCIDump format is partially defined in Knowles1989. + + Knowles1989: + Peter J. Knowles, Nicholas C. Handy, + A determinant based full configuration interaction program, + Computer Physics Communications, Volume 54, Issue 1, 1989, Pages 75-83, + ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. + """ + + def __init__(self, fcidump_input: str) -> None: + """ + Initializer + + Args: + fcidump_input: path to the FCIDump file + + Raises: + QiskitChemistryError: invalid input + """ + super().__init__() + + if not isinstance(fcidump_input, str): + raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(fcidump_input)) + self._fcidump_input = fcidump_input + + def run(self) -> QMolecule: + """ + Constructs a QMolecule instance out of a FCIDump file. + + Returns: + QMolecule: a QMolecule instance populated with a minimal set of required data + """ + fcidump_data = parse(self._fcidump_input) + + q_mol = QMolecule() + + q_mol.hf_energy = fcidump_data.get('ecore', float('NaN')) + q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) + # TODO: NELEC is inconclusive in the case of a non-singlet spin system + q_mol.num_beta = fcidump_data.get('NELEC', float('NaN')) // 2 + q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta + + q_mol.mo_onee_ints = fcidump_data.get('hij', None) + q_mol.mo_onee_ints_b = fcidump_data.get('hij_b', None) + q_mol.mo_eri_ints = fcidump_data.get('hijkl', None) + q_mol.mo_eri_ints_bb = fcidump_data.get('hijkl_bb', None) + q_mol.mo_eri_ints_ba = fcidump_data.get('hijkl_ba', None) + + return q_mol + + @staticmethod + def dump(q_mol: QMolecule, outpath: str) -> None: + """ + Convenience method to produce an FCIDump output file + + Args: + q_mol (QMolecule): QMolecule data to be dumped. It is assumed that the HF energy stored + in this QMolecule instance contains the inactive core energy. + outpath (str): path to the output file + """ + dump(q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, + (q_mol.mo_onee_ints, q_mol.mo_onee_ints_b), + (q_mol.mo_eri_ints, q_mol.mo_eri_ints_ba, q_mol.mo_eri_ints_bb), + q_mol.hf_energy, outpath) diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py new file mode 100644 index 0000000000..867802ce7e --- /dev/null +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" FCIDump parser """ + +import itertools +import re +import numpy as np + +from qiskit.chemistry import QiskitChemistryError + + +def parse(fcidump: str) -> dict: + """ Parses a FCIDump output. + Args: + fcidump: path to the FCIDump file + Raises: + QiskitChemistryError: missing file or invalid input + Returns: + dict: dictionary for storing the parsed data + """ + fcidump_str = None + try: + with open(fcidump, 'r') as file: + fcidump_str = file.read() + except OSError: + raise QiskitChemistryError("Input file '{}' cannot be read!".format(fcidump)) + + output = {} + + # FCIDump starts with a Fortran namelist of meta data + namelist_end = re.search('(/|&END)', fcidump_str) + metadata = fcidump_str[:namelist_end.start(0)] + metadata = ' '.join(metadata.split()) # replace duplicate whitespace and newlines + # we know what elements to look for so we don't get too fancy with the parsing + # pattern explanation: + # .*? any text + # (*|*), match either part of this group followed by a comma + # [-+]? match up to a single - or + + # \d*.\d+ number format + pattern = r'.*?([-+]?\d*\.\d+|[-+]?\d+),' + # we parse the values in the order in which they are listed in Knowles1989 + _norb = re.search('NORB'+pattern, metadata) + norb = int(_norb.groups()[0]) + output['NORB'] = norb + _nelec = re.search('NELEC'+pattern, metadata) + output['NELEC'] = int(_nelec.groups()[0]) + # the rest of these values may occur and are set to their defaults otherwise + _ms2 = re.search('MS2'+pattern, metadata) + output['MS2'] = int(_ms2.groups()[0]) if _ms2 else 0 + _isym = re.search('ISYM'+pattern, metadata) + output['ISYM'] = int(_isym.groups()[0]) if _isym else 1 + # ORBSYM holds a list, thus it requires a little different treatment + _orbsym = re.search(r'ORBSYM.*?'+r'(\d+),'*norb, metadata) + output['ORBSYM'] = [int(s) for s in _orbsym.groups()] if _orbsym \ + else [1 for s in range(norb)] + _iprtim = re.search('IPRTIM'+pattern, metadata) + output['IPRTIM'] = int(_iprtim.groups()[0]) if _iprtim else -1 + _int = re.search('INT'+pattern, metadata) + output['INT'] = int(_int.groups()[0]) if _int else 5 + _memory = re.search('MEMORY'+pattern, metadata) + output['MEMORY'] = int(_memory.groups()[0]) if _memory else 10000 + _core = re.search('CORE'+pattern, metadata) + output['CORE'] = float(_core.groups()[0]) if _core else 0.0 + _maxit = re.search('MAXIT'+pattern, metadata) + output['MAXIT'] = int(_maxit.groups()[0]) if _maxit else 25 + _thr = re.search('THR'+pattern, metadata) + output['THR'] = float(_thr.groups()[0]) if _thr else 1e-5 + _thrres = re.search('THRRES'+pattern, metadata) + output['THRRES'] = float(_thrres.groups()[0]) if _thrres else 0.1 + _nroot = re.search('NROOT'+pattern, metadata) + output['NROOT'] = int(_nroot.groups()[0]) if _nroot else 1 + + # If the FCIDump file resulted from an unrestricted spin calculation the indices will label spin + # rather than molecular orbitals. This means, that a line must exist which encodes the + # coefficient for the spin orbital with index (norb*2, norb*2). By checking for such a line we + # can distinguish between unrestricted and restricted FCIDump files. + _uhf = bool(re.search(r'.*(\s+{}\s+{}\s+0\s+0)'.format(norb*2, norb*2), + fcidump_str[namelist_end.start(0):])) + + # the rest of the FCIDump will hold lines of the form x i a j b + # a few cases have to be treated differently: + # i, a, j and b are all zero: x is the core energy + # TODO: a, j and b are all zero: x is the energy of the i-th MO (often not supported) + # j and b are both zero: x is the 1e-integral between i and a (x = ) + # otherwise: x is the Coulomb integral ( x = (ia|jb) ) + hij = np.zeros((norb, norb)) + hij_elements = set(itertools.product(range(norb), repeat=2)) + hijkl = np.zeros((norb, norb, norb, norb)) + hijkl_elements = set(itertools.product(range(norb), repeat=4)) + hij_b = hijkl_ab = hijkl_ba = hijkl_bb = None + hij_b_elements = hijkl_ab_elements = hijkl_ba_elements = hijkl_bb_elements = set() + if _uhf: + beta_range = [n+norb for n in range(norb)] + hij_b = np.zeros((norb, norb)) + hij_b_elements = set(itertools.product(beta_range, repeat=2)) + hijkl_ab = np.zeros((norb, norb, norb, norb)) + hijkl_ba = np.zeros((norb, norb, norb, norb)) + hijkl_bb = np.zeros((norb, norb, norb, norb)) + hijkl_ab_elements = set(itertools.product( + range(norb), range(norb), beta_range, beta_range + )) + hijkl_ba_elements = set(itertools.product( + beta_range, beta_range, range(norb), range(norb) + )) + hijkl_bb_elements = set(itertools.product(beta_range, repeat=4)) + orbital_data = fcidump_str[namelist_end.end(0):].split('\n') + for orbital in orbital_data: + if not orbital: + continue + x = float(orbital.split()[0]) + # Note: differing naming than ijkl due to E741 and this iajb is inline with this: + # https://hande.readthedocs.io/en/latest/manual/integrals.html#fcidump-format + i, a, j, b = [int(i) for i in orbital.split()[1:]] # pylint: disable=invalid-name + if i == a == j == b == 0: + output['ecore'] = x + elif j == b == 0: + try: + hij_elements.remove((i-1, a-1)) + hij[i-1][a-1] = x + except KeyError: + if _uhf: + hij_b_elements.remove((i-1, a-1)) + hij_b[i-1-norb][a-1-norb] = x + else: + raise QiskitChemistryError() + else: + try: + hijkl_elements.remove((i-1, a-1, j-1, b-1)) + hijkl[i-1][a-1][j-1][b-1] = x + except KeyError: + if _uhf: + try: + hijkl_ab_elements.remove((i-1, a-1, j-1, b-1)) + hijkl_ab[i-1][a-1][j-1-norb][b-1-norb] = x + except KeyError: + try: + hijkl_ba_elements.remove((i-1, a-1, j-1, b-1)) + hijkl_ba[i-1-norb][a-1-norb][j-1][b-1] = x + except KeyError: + hijkl_bb_elements.remove((i-1, a-1, j-1, b-1)) + hijkl_bb[i-1-norb][a-1-norb][j-1-norb][b-1-norb] = x + else: + raise QiskitChemistryError() + + # iterate over still empty elements in 1-electron matrix and populate with symmetric ones + # if any elements are not populated these will be zero + _permute_1e_ints(hij, hij_elements, norb) + + if _uhf: + # do the same for beta spin + _permute_1e_ints(hij_b, hij_b_elements, norb, beta=True) + + # do the same of the 2-electron 4D matrix + _permute_2e_ints(hijkl, hijkl_elements, norb) + + if _uhf: + # do the same for beta spin + _permute_2e_ints(hijkl_bb, hijkl_bb_elements, norb, beta=2) + _permute_2e_ints(hijkl_ab, hijkl_ab_elements, norb, beta=1) + _permute_2e_ints(hijkl_ba, hijkl_ba_elements, norb, beta=1) + + # assert that EITHER hijkl_ab OR hijkl_ba were given + assert np.allclose(hijkl_ab, 0.0) != np.allclose(hijkl_ba, 0.0) + + if np.allclose(hijkl_ba, 0.0): + hijkl_ba = hijkl_ab.transpose() + + output['hij'] = hij + output['hij_b'] = hij_b + output['hijkl'] = hijkl + output['hijkl_ba'] = hijkl_ba + output['hijkl_bb'] = hijkl_bb + + return output + + +def _permute_1e_ints(hij: list, elements: set, norb: int, beta: bool = False) -> None: + for elem in elements.copy(): + shifted = tuple(e-(beta * norb) for e in elem) + hij[shifted] = hij[shifted[::-1]] + elements.remove(elem) + + +def _permute_2e_ints(hijkl: list, elements: set, norb: int, beta: bool = False) -> None: + for elem in elements.copy(): + shifted = tuple([e-((e >= norb) * norb)for e in elem]) + # initially look for "transposed" element if spins are equal + if beta != 1 and elem[::-1] not in elements: + hijkl[shifted] = hijkl[shifted[::-1]] + elements.remove(elem) + continue + # then look at permutations of indices within the bra and ket respectively + bra_perms = set(itertools.permutations(elem[:2])) + ket_perms = set(itertools.permutations(elem[2:])) + if beta == 1: + # generally (ij|ab) != (ab|ij) + # thus, the possible permutations are much less when the spins differ + permutations = itertools.product(bra_perms, ket_perms) + else: + # ( ij | kl ) gives { ( ij | kl ), ( ij | lk ), ( ji | kl ), ( ji | lk ) } + # AND { ( kl | ij ), ( kl | ji ), ( lk | ij ), ( lk | ji ) } + # BUT NOT ( ik | jl ) etc. + permutations = itertools.chain( + itertools.product(bra_perms, ket_perms), + itertools.product(ket_perms, bra_perms) + ) + for perm in {e1 + e2 for e1, e2 in permutations}: + if perm in elements: + continue + hijkl[shifted] = hijkl[tuple([e-((e >= norb) * norb) for e in perm])] + elements.remove(elem) + break From 2bc03138d17a81f23a9c489e40cc3c65bd244d53 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 17 Mar 2020 10:55:27 +0100 Subject: [PATCH 02/28] Add FCIDumpDriver unittests --- .pylintdict | 1 + test/chemistry/test_driver_fcidump.py | 146 ++++++++++++++++++ .../chemistry/test_driver_fcidump_rhf.fcidump | 12 ++ .../chemistry/test_driver_fcidump_uhf.fcidump | 24 +++ 4 files changed, 183 insertions(+) create mode 100644 test/chemistry/test_driver_fcidump.py create mode 100644 test/chemistry/test_driver_fcidump_rhf.fcidump create mode 100644 test/chemistry/test_driver_fcidump_uhf.fcidump diff --git a/.pylintdict b/.pylintdict index 9c2c1e49a5..032527b107 100644 --- a/.pylintdict +++ b/.pylintdict @@ -367,6 +367,7 @@ objval occ oe ok +onee oneee online onwards diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py new file mode 100644 index 0000000000..0031333a02 --- /dev/null +++ b/test/chemistry/test_driver_fcidump.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Driver FCIDump """ + +import unittest +from abc import ABC, abstractmethod +from test.chemistry import QiskitChemistryTestCase +import numpy as np +from qiskit.chemistry.drivers import FCIDumpDriver + + +class BaseTestDriverFCIDump(ABC): + """FCIDump Driver base test class using H2 @ 0.735, sto3g. + + In contrast to the other driver tests this one does *not* derive from TestDriver because the + interface is fundamentally different. + Similar to the HDF5Driver there is also no TestDriverMethodsFCIDump class for the same reason. + """ + + def __init__(self): + self.log = None + self.qmolecule = None + + @abstractmethod + def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): + """ asset Almost Equal """ + raise Exception('Abstract method') + + @abstractmethod + def assertEqual(self, first, second, msg=None): + """ assert equal """ + raise Exception('Abstract method') + + @abstractmethod + def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): + """ assert Sequence Equal """ + raise Exception('Abstract method') + + def test_driver_inactive_energy(self): + """ driver inactive energy test """ + self.log.debug('QMolecule inactive energy is {}'.format(self.qmolecule.hf_energy)) + self.assertAlmostEqual(self.qmolecule.hf_energy, 0.7199, places=3) + + def test_driver_num_orbitals(self): + """ driver num orbitals test """ + self.log.debug('QMolecule Number of orbitals is {}'.format(self.qmolecule.num_orbitals)) + self.assertEqual(self.qmolecule.num_orbitals, 2) + + def test_driver_num_alpha(self): + """ driver num alpha test """ + self.log.debug('QMolecule Number of alpha electrons is {}'.format(self.qmolecule.num_alpha)) + self.assertEqual(self.qmolecule.num_alpha, 1) + + def test_driver_num_beta(self): + """ driver num beta test """ + self.log.debug('QMolecule Number of beta electrons is {}'.format(self.qmolecule.num_beta)) + self.assertEqual(self.qmolecule.num_beta, 1) + + def _test_driver_mo_onee_ints(self, mo_onee): + self.assertEqual(mo_onee.shape, (2, 2)) + np.testing.assert_array_almost_equal(np.absolute(mo_onee), + [[1.2563, 0.0], [0.0, 0.4719]], decimal=4) + + def _test_driver_mo_eri_ints(self, mo_eri): + self.assertEqual(mo_eri.shape, (2, 2, 2, 2)) + np.testing.assert_array_almost_equal(np.absolute(mo_eri), + [[[[0.6757, 0.0], [0.0, 0.6646]], + [[0.0, 0.1809], [0.1809, 0.0]]], + [[[0.0, 0.1809], [0.1809, 0.0]], + [[0.6646, 0.0], [0.0, 0.6986]]]], decimal=4) + + +class TestDriverFCIDumpRHF(QiskitChemistryTestCase, BaseTestDriverFCIDump): + """RHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_rhf.fcidump')) + self.qmolecule = driver.run() + + def test_driver_mo_onee_ints(self): + """ driver mo onee ints test """ + self.log.debug('QMolecule MO one electron integrals are {}'.format( + self.qmolecule.mo_onee_ints)) + self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints) + + def test_driver_mo_eri_ints(self): + """ driver mo eri ints test """ + self.log.debug('QMolecule MO two electron integrals are {}'.format( + self.qmolecule.mo_eri_ints)) + self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints) + + +class TestDriverFCIDumpUHF(QiskitChemistryTestCase, BaseTestDriverFCIDump): + """UHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_uhf.fcidump')) + self.qmolecule = driver.run() + + def test_driver_mo_onee_ints(self): + """ driver alpha mo onee ints test """ + self.log.debug('QMolecule MO alpha one electron integrals are {}'.format( + self.qmolecule.mo_onee_ints)) + self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints) + + def test_driver_mo_onee_b_ints(self): + """ driver beta mo onee ints test """ + self.log.debug('QMolecule MO beta one electron integrals are {}'.format( + self.qmolecule.mo_onee_ints_b)) + self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints_b) + + def test_driver_mo_eri_ints(self): + """ driver alpha-alpha mo eri ints test """ + self.log.debug('QMolecule MO alpha-alpha two electron integrals are {}'.format( + self.qmolecule.mo_eri_ints)) + self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints) + + def test_driver_mo_eri_ints_ba(self): + """ driver beta-alpha mo eri ints test """ + self.log.debug('QMolecule MO beta-alpha two electron integrals are {}'.format( + self.qmolecule.mo_eri_ints_ba)) + self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints_ba) + + def test_driver_mo_eri_ints_bb(self): + """ driver beta-beta mo eri ints test """ + self.log.debug('QMolecule MO beta-beta two electron integrals are {}'.format( + self.qmolecule.mo_eri_ints_bb)) + self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints_bb) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_fcidump_rhf.fcidump b/test/chemistry/test_driver_fcidump_rhf.fcidump new file mode 100644 index 0000000000..973cf4b89a --- /dev/null +++ b/test/chemistry/test_driver_fcidump_rhf.fcidump @@ -0,0 +1,12 @@ +&FCI NORB= 2,NELEC= 2,MS2= 0, + ORBSYM=1,1, + ISYM=0, + / + 6.7571015480351648E-01 1 1 1 1 + 6.6458173025529665E-01 1 1 2 2 + 1.8093119978423133E-01 1 2 1 2 + 6.9857372273201834E-01 2 2 2 2 +-1.2563390730032502E+00 1 1 0 0 +-2.3575299028703285E-16 1 2 0 0 +-4.7189600728114062E-01 2 2 0 0 + 7.1996899444897966E-01 0 0 0 0 diff --git a/test/chemistry/test_driver_fcidump_uhf.fcidump b/test/chemistry/test_driver_fcidump_uhf.fcidump new file mode 100644 index 0000000000..fbc670ab45 --- /dev/null +++ b/test/chemistry/test_driver_fcidump_uhf.fcidump @@ -0,0 +1,24 @@ +&FCI NORB= 2,NELEC= 2,MS2= 0, + ORBSYM=1,1, + ISYM=0, + / + 6.7571015480351648E-01 1 1 1 1 + 6.6458173025529665E-01 1 1 2 2 + 1.8093119978423133E-01 1 2 1 2 + 6.9857372273201834E-01 2 2 2 2 + 6.7571015480351682E-01 1 1 3 3 + 6.6458173025529654E-01 1 1 4 4 +-1.8093119978423133E-01 1 2 3 4 + 6.6458173025529710E-01 2 2 3 3 + 6.9857372273201834E-01 2 2 4 4 + 6.7571015480351704E-01 3 3 3 3 + 6.6458173025529732E-01 3 3 4 4 + 1.8093119978423153E-01 3 4 3 4 + 6.9857372273201845E-01 4 4 4 4 +-1.2563390730032502E+00 1 1 0 0 +-2.3575299028703285E-16 1 2 0 0 +-4.7189600728114062E-01 2 2 0 0 +-1.2563390730032507E+00 3 3 0 0 +-6.7113779463559231E-17 3 4 0 0 +-4.7189600728114073E-01 4 4 0 0 + 7.1996899444897966E-01 0 0 0 0 From daf337b2c8574eb9fb14dc1c38c4c0b020e8aca7 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 17 Mar 2020 10:56:14 +0100 Subject: [PATCH 03/28] Fix typo and remove from pylintdict --- .pylintdict | 1 - test/chemistry/test_driver.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.pylintdict b/.pylintdict index 032527b107..86a982eb2e 100644 --- a/.pylintdict +++ b/.pylintdict @@ -368,7 +368,6 @@ occ oe ok onee -oneee online onwards oplus diff --git a/test/chemistry/test_driver.py b/test/chemistry/test_driver.py index a7a11dc6e8..bb8ff312c3 100644 --- a/test/chemistry/test_driver.py +++ b/test/chemistry/test_driver.py @@ -106,7 +106,7 @@ def test_driver_orbital_energies(self): [-0.5806, 0.6763], decimal=4) def test_driver_mo_onee_ints(self): - """ driver mo oneee ints test """ + """ driver mo onee ints test """ self.log.debug('QMolecule MO one electron integrals {}'.format(self.qmolecule.mo_onee_ints)) self.assertEqual(self.qmolecule.mo_onee_ints.shape, (2, 2)) np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_onee_ints), From 7bd03b797d6d0fd8f3d5596f27c0fad32640161e Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 17 Mar 2020 13:07:53 +0100 Subject: [PATCH 04/28] Fix copyright year --- qiskit/chemistry/drivers/fcidumpd/dumper.py | 2 +- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 2 +- qiskit/chemistry/drivers/fcidumpd/parser.py | 2 +- test/chemistry/test_driver.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index af43f03962..a3cfa40e05 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 609856eb1b..43f8de33d5 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 867802ce7e..8d1bfc3656 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/test/chemistry/test_driver.py b/test/chemistry/test_driver.py index bb8ff312c3..f120c50ab8 100644 --- a/test/chemistry/test_driver.py +++ b/test/chemistry/test_driver.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory From c35162fcb19d04d161056c07bef87e40b6426333 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 17 Mar 2020 13:24:36 +0100 Subject: [PATCH 05/28] Fix copyright year - part 2 --- qiskit/chemistry/drivers/__init__.py | 2 +- qiskit/chemistry/drivers/fcidumpd/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/drivers/__init__.py b/qiskit/chemistry/drivers/__init__.py index a838d4b500..2e7580657a 100644 --- a/qiskit/chemistry/drivers/__init__.py +++ b/qiskit/chemistry/drivers/__init__.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/__init__.py b/qiskit/chemistry/drivers/fcidumpd/__init__.py index 28295c0f5d..394858e4ef 100644 --- a/qiskit/chemistry/drivers/fcidumpd/__init__.py +++ b/qiskit/chemistry/drivers/fcidumpd/__init__.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019. +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory From 7054e55126323cb2feea8b00206943d751ceeb32 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 08:33:22 +0100 Subject: [PATCH 06/28] Finally, make copyright correct. The copyright should include: , In the case were these are the same, it is sufficient to state it once. --- qiskit/chemistry/drivers/fcidumpd/__init__.py | 2 +- qiskit/chemistry/drivers/fcidumpd/dumper.py | 2 +- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 2 +- qiskit/chemistry/drivers/fcidumpd/parser.py | 2 +- test/chemistry/test_driver_fcidump.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/__init__.py b/qiskit/chemistry/drivers/fcidumpd/__init__.py index 394858e4ef..4bd6a72e74 100644 --- a/qiskit/chemistry/drivers/fcidumpd/__init__.py +++ b/qiskit/chemistry/drivers/fcidumpd/__init__.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index a3cfa40e05..6ede4957d8 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 43f8de33d5..ca1c56c6a9 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 8d1bfc3656..69a9f27a6c 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py index 0031333a02..075ea38306 100644 --- a/test/chemistry/test_driver_fcidump.py +++ b/test/chemistry/test_driver_fcidump.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory From e42ddd44ced837de23e49b0ba3ae5ec5bf16ac8d Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 09:29:53 +0100 Subject: [PATCH 07/28] Refactor FCIDump parsing unittests Instead of relying on a hard-coded RHF and UHF testcase for H2, we are now testing against the FCIDump files for H2, LiH and OH. For the latter two cases the numpy reference arrays are provided as compressed archive files. --- test/chemistry/test_driver_fcidump.py | 145 ++-- ...fcidump => test_driver_fcidump_h2.fcidump} | 0 .../chemistry/test_driver_fcidump_lih.fcidump | 128 ++++ test/chemistry/test_driver_fcidump_lih.npz | Bin 0 -> 1986 bytes test/chemistry/test_driver_fcidump_oh.fcidump | 625 ++++++++++++++++++ test/chemistry/test_driver_fcidump_oh.npz | Bin 0 -> 8008 bytes .../chemistry/test_driver_fcidump_uhf.fcidump | 24 - 7 files changed, 846 insertions(+), 76 deletions(-) rename test/chemistry/{test_driver_fcidump_rhf.fcidump => test_driver_fcidump_h2.fcidump} (100%) create mode 100644 test/chemistry/test_driver_fcidump_lih.fcidump create mode 100644 test/chemistry/test_driver_fcidump_lih.npz create mode 100644 test/chemistry/test_driver_fcidump_oh.fcidump create mode 100644 test/chemistry/test_driver_fcidump_oh.npz delete mode 100644 test/chemistry/test_driver_fcidump_uhf.fcidump diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py index 075ea38306..fa9abe47e0 100644 --- a/test/chemistry/test_driver_fcidump.py +++ b/test/chemistry/test_driver_fcidump.py @@ -32,6 +32,15 @@ class BaseTestDriverFCIDump(ABC): def __init__(self): self.log = None self.qmolecule = None + self.hf_energy = None + self.num_orbitals = None + self.num_alpha = None + self.num_beta = None + self.mo_onee = None + self.mo_onee_b = None + self.mo_eri = None + self.mo_eri_ba = None + self.mo_eri_bb = None @abstractmethod def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): @@ -51,95 +60,127 @@ def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): def test_driver_inactive_energy(self): """ driver inactive energy test """ self.log.debug('QMolecule inactive energy is {}'.format(self.qmolecule.hf_energy)) - self.assertAlmostEqual(self.qmolecule.hf_energy, 0.7199, places=3) + self.assertAlmostEqual(self.qmolecule.hf_energy, self.hf_energy, places=3) def test_driver_num_orbitals(self): """ driver num orbitals test """ self.log.debug('QMolecule Number of orbitals is {}'.format(self.qmolecule.num_orbitals)) - self.assertEqual(self.qmolecule.num_orbitals, 2) + self.assertEqual(self.qmolecule.num_orbitals, self.num_orbitals) def test_driver_num_alpha(self): """ driver num alpha test """ self.log.debug('QMolecule Number of alpha electrons is {}'.format(self.qmolecule.num_alpha)) - self.assertEqual(self.qmolecule.num_alpha, 1) + self.assertEqual(self.qmolecule.num_alpha, self.num_alpha) def test_driver_num_beta(self): """ driver num beta test """ self.log.debug('QMolecule Number of beta electrons is {}'.format(self.qmolecule.num_beta)) - self.assertEqual(self.qmolecule.num_beta, 1) - - def _test_driver_mo_onee_ints(self, mo_onee): - self.assertEqual(mo_onee.shape, (2, 2)) - np.testing.assert_array_almost_equal(np.absolute(mo_onee), - [[1.2563, 0.0], [0.0, 0.4719]], decimal=4) - - def _test_driver_mo_eri_ints(self, mo_eri): - self.assertEqual(mo_eri.shape, (2, 2, 2, 2)) - np.testing.assert_array_almost_equal(np.absolute(mo_eri), - [[[[0.6757, 0.0], [0.0, 0.6646]], - [[0.0, 0.1809], [0.1809, 0.0]]], - [[[0.0, 0.1809], [0.1809, 0.0]], - [[0.6646, 0.0], [0.0, 0.6986]]]], decimal=4) - - -class TestDriverFCIDumpRHF(QiskitChemistryTestCase, BaseTestDriverFCIDump): - """RHF FCIDump Driver tests.""" - - def setUp(self): - super().setUp() - driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_rhf.fcidump')) - self.qmolecule = driver.run() - - def test_driver_mo_onee_ints(self): - """ driver mo onee ints test """ - self.log.debug('QMolecule MO one electron integrals are {}'.format( - self.qmolecule.mo_onee_ints)) - self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints) - - def test_driver_mo_eri_ints(self): - """ driver mo eri ints test """ - self.log.debug('QMolecule MO two electron integrals are {}'.format( - self.qmolecule.mo_eri_ints)) - self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints) - - -class TestDriverFCIDumpUHF(QiskitChemistryTestCase, BaseTestDriverFCIDump): - """UHF FCIDump Driver tests.""" - - def setUp(self): - super().setUp() - driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_uhf.fcidump')) - self.qmolecule = driver.run() + self.assertEqual(self.qmolecule.num_beta, self.num_beta) def test_driver_mo_onee_ints(self): """ driver alpha mo onee ints test """ self.log.debug('QMolecule MO alpha one electron integrals are {}'.format( self.qmolecule.mo_onee_ints)) - self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints) + self.assertEqual(self.qmolecule.mo_onee_ints.shape, self.mo_onee.shape) + np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_onee_ints), + np.absolute(self.mo_onee), decimal=4) def test_driver_mo_onee_b_ints(self): """ driver beta mo onee ints test """ + if self.mo_onee_b is None: + return self.log.debug('QMolecule MO beta one electron integrals are {}'.format( self.qmolecule.mo_onee_ints_b)) - self._test_driver_mo_onee_ints(self.qmolecule.mo_onee_ints_b) + self.assertEqual(self.qmolecule.mo_onee_ints_b.shape, self.mo_onee_b.shape) + np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_onee_ints_b), + np.absolute(self.mo_onee_b), decimal=4) def test_driver_mo_eri_ints(self): """ driver alpha-alpha mo eri ints test """ self.log.debug('QMolecule MO alpha-alpha two electron integrals are {}'.format( self.qmolecule.mo_eri_ints)) - self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints) + self.assertEqual(self.qmolecule.mo_eri_ints.shape, self.mo_eri.shape) + np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_eri_ints), + np.absolute(self.mo_eri), decimal=4) def test_driver_mo_eri_ints_ba(self): """ driver beta-alpha mo eri ints test """ + if self.mo_eri_ba is None: + return self.log.debug('QMolecule MO beta-alpha two electron integrals are {}'.format( self.qmolecule.mo_eri_ints_ba)) - self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints_ba) + self.assertEqual(self.qmolecule.mo_eri_ints_ba.shape, self.mo_eri_ba.shape) + np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_eri_ints_ba), + np.absolute(self.mo_eri_ba), decimal=4) def test_driver_mo_eri_ints_bb(self): """ driver beta-beta mo eri ints test """ + if self.mo_eri_bb is None: + return self.log.debug('QMolecule MO beta-beta two electron integrals are {}'.format( self.qmolecule.mo_eri_ints_bb)) - self._test_driver_mo_eri_ints(self.qmolecule.mo_eri_ints_bb) + self.assertEqual(self.qmolecule.mo_eri_ints_bb.shape, self.mo_eri_bb.shape) + np.testing.assert_array_almost_equal(np.absolute(self.qmolecule.mo_eri_ints_bb), + np.absolute(self.mo_eri_bb), decimal=4) + + +class TestDriverFCIDumpH2(QiskitChemistryTestCase, BaseTestDriverFCIDump): + """RHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + self.hf_energy = 0.7199 + self.num_orbitals = 2 + self.num_alpha = 1 + self.num_beta = 1 + self.mo_onee = np.array([[1.2563, 0.0], [0.0, 0.4719]]) + self.mo_onee_b = None + self.mo_eri = np.array([[[[0.6757, 0.0], [0.0, 0.6646]], + [[0.0, 0.1809], [0.1809, 0.0]]], + [[[0.0, 0.1809], [0.1809, 0.0]], + [[0.6646, 0.0], [0.0, 0.6986]]]]) + self.mo_eri_ba = None + self.mo_eri_bb = None + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_h2.fcidump')) + self.qmolecule = driver.run() + + +class TestDriverFCIDumpLiH(QiskitChemistryTestCase, BaseTestDriverFCIDump): + """RHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + self.hf_energy = 0.9924 + self.num_orbitals = 6 + self.num_alpha = 2 + self.num_beta = 2 + loaded = np.load(self.get_resource_path('test_driver_fcidump_lih.npz')) + self.mo_onee = loaded['mo_onee'] + self.mo_onee_b = None + self.mo_eri = loaded['mo_eri'] + self.mo_eri_ba = None + self.mo_eri_bb = None + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_lih.fcidump')) + self.qmolecule = driver.run() + + +class TestDriverFCIDumpOH(QiskitChemistryTestCase, BaseTestDriverFCIDump): + """RHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + self.hf_energy = 11.3412 + self.num_orbitals = 6 + self.num_alpha = 5 + self.num_beta = 4 + loaded = np.load(self.get_resource_path('test_driver_fcidump_oh.npz')) + self.mo_onee = loaded['mo_onee'] + self.mo_onee_b = loaded['mo_onee_b'] + self.mo_eri = loaded['mo_eri'] + self.mo_eri_ba = loaded['mo_eri_ba'] + self.mo_eri_bb = loaded['mo_eri_bb'] + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_oh.fcidump')) + self.qmolecule = driver.run() if __name__ == '__main__': diff --git a/test/chemistry/test_driver_fcidump_rhf.fcidump b/test/chemistry/test_driver_fcidump_h2.fcidump similarity index 100% rename from test/chemistry/test_driver_fcidump_rhf.fcidump rename to test/chemistry/test_driver_fcidump_h2.fcidump diff --git a/test/chemistry/test_driver_fcidump_lih.fcidump b/test/chemistry/test_driver_fcidump_lih.fcidump new file mode 100644 index 0000000000..02f77b8063 --- /dev/null +++ b/test/chemistry/test_driver_fcidump_lih.fcidump @@ -0,0 +1,128 @@ +&FCI NORB= 6,NELEC= 4,MS2= 0, + ORBSYM=1,1,1,1,1,1, + ISYM=0, + / + 1.6586546376776659E+00 1 1 1 1 +-1.1185240211600889E-01 1 1 1 2 + 1.3848665625981069E-01 1 1 1 3 + 1.1480489585912616E-12 1 1 1 4 + 5.3156150905632020E-02 1 1 1 6 + 3.6672307192919318E-01 1 1 2 2 +-1.3490862041788740E-02 1 1 2 3 +-4.1444544917724473E-02 1 1 2 6 + 3.9560609300166527E-01 1 1 3 3 +-1.7570190757927876E-02 1 1 3 6 + 3.9615805756229605E-01 1 1 4 4 + 3.9615805756231426E-01 1 1 5 5 + 3.6156268752540510E-01 1 1 6 6 + 1.3361925233149283E-02 1 2 1 2 +-1.1220132228289466E-02 1 2 1 3 +-8.9193406150758145E-03 1 2 1 6 + 6.1864696170519356E-03 1 2 2 2 + 3.3624105319467082E-03 1 2 2 3 + 4.6968249080026094E-03 1 2 2 6 +-1.1076181824135573E-02 1 2 3 3 + 3.6636805364300491E-03 1 2 3 6 +-4.3826190559740179E-03 1 2 4 4 +-4.3826190559700749E-03 1 2 5 5 + 3.2605442674869881E-03 1 2 6 6 + 2.1650293378449925E-02 1 3 1 3 + 2.3597082406981952E-03 1 3 1 6 + 1.5927544903074058E-02 1 3 2 2 + 1.9457359831490838E-04 1 3 2 3 +-5.7994711121253336E-04 1 3 2 6 +-1.8364753300464771E-03 1 3 3 3 + 4.4001136995770957E-03 1 3 3 6 + 5.0103349844383832E-03 1 3 4 4 + 5.0103349844262289E-03 1 3 5 5 + 1.1416837042889703E-02 1 3 6 6 + 9.8198689221677370E-03 1 4 1 4 + 7.5164446293970412E-03 1 4 2 4 +-1.0272658234905148E-02 1 4 3 4 +-6.1418191132375502E-03 1 4 4 6 + 9.8198689221912529E-03 1 5 1 5 + 7.5164446294140033E-03 1 5 2 5 +-1.0272658234932364E-02 1 5 3 5 +-6.1418191132447050E-03 1 5 5 6 + 8.5520281395617993E-03 1 6 1 6 +-6.8305742787635928E-03 1 6 2 2 +-1.7070775019968380E-03 1 6 2 3 + 1.2089575141012997E-04 1 6 2 6 + 1.0473652589391213E-02 1 6 3 3 +-4.2963064292415630E-03 1 6 3 6 + 6.0733702880809708E-04 1 6 4 4 + 6.0733702880693037E-04 1 6 5 5 +-3.0763611785068596E-03 1 6 6 6 + 4.8696923615603571E-01 2 2 2 2 + 4.8673192906504369E-02 2 2 2 3 + 1.2682585153426007E-01 2 2 2 6 + 2.2207684582544082E-01 2 2 3 3 + 5.1725041788584156E-02 2 2 3 6 + 2.6931057460912416E-01 2 2 4 4 + 2.6931057460913743E-01 2 2 5 5 + 4.5344862228143717E-01 2 2 6 6 + 1.3074357366741222E-02 2 3 2 3 + 3.4532278168798614E-02 2 3 2 6 +-7.6521522213230837E-03 2 3 3 3 + 9.5005232735582892E-03 2 3 3 6 +-5.7112650518622958E-03 2 3 4 4 +-5.7112650518572963E-03 2 3 5 5 + 4.3501724977949845E-02 2 3 6 6 + 2.3612689364016230E-02 2 4 2 4 +-1.9660010050143773E-02 2 4 3 4 +-1.9736571778768752E-02 2 4 4 6 + 2.3612689364026114E-02 2 5 2 5 +-1.9660010050139950E-02 2 5 3 5 +-1.9736571778750635E-02 2 5 5 6 + 1.2383163324182184E-01 2 6 2 6 +-1.1902300947385589E-02 2 6 3 3 + 3.1858316517801188E-02 2 6 3 6 +-1.5864225587806421E-02 2 6 4 4 +-1.5864225587796446E-02 2 6 5 5 + 1.3454832317431481E-01 2 6 6 6 + 3.3745516239657347E-01 3 3 3 3 +-3.6097312553601379E-02 3 3 3 6 + 2.8072389691079030E-01 3 3 4 4 + 2.8072389691079774E-01 3 3 5 5 + 2.3939716830479155E-01 3 3 6 6 + 4.1574401040021657E-02 3 4 3 4 + 1.4171696707401834E-02 3 4 4 6 + 4.1574401040012310E-02 3 5 3 5 + 1.4171696707380650E-02 3 5 5 6 + 2.6484660219304429E-02 3 6 3 6 +-2.1453620360574097E-03 3 6 4 4 +-2.1453620360655104E-03 3 6 5 5 + 4.4525329121800961E-02 3 6 6 6 + 3.1126171165226496E-01 4 4 4 4 +-3.3192265569307155E-07 4 4 4 5 + 2.7803357200249446E-01 4 4 5 5 + 2.6678403197166090E-01 4 4 6 6 + 1.6765218621834339E-02 4 5 4 5 + 3.3192265588819713E-07 4 5 5 5 + 1.9844263796503305E-02 4 6 4 6 + 3.1126171165226457E-01 5 5 5 5 + 2.6678403197164940E-01 5 5 6 6 + 1.9844263796465179E-02 5 6 5 6 + 4.5318537347316701E-01 6 6 6 6 +-4.7274928345697500E+00 1 1 0 0 + 1.0566592300600668E-01 1 2 0 0 +-1.6697933722736052E-01 1 3 0 0 +-1.1477850544437628E-12 1 4 0 0 + 2.9699404559664083E-17 1 5 0 0 +-3.4798155871967530E-02 1 6 0 0 +-1.4926089003683276E+00 2 2 0 0 +-3.2911492320197279E-02 2 3 0 0 +-1.0062808512802502E-15 2 4 0 0 + 5.5865269181193328E-17 2 5 0 0 +-5.2856194853614598E-02 2 6 0 0 +-1.1246113418866224E+00 3 3 0 0 +-3.6185603973485621E-16 3 4 0 0 + 2.8998241944651825E-17 3 5 0 0 +-3.1417780899706871E-02 3 6 0 0 +-1.1347793078137312E+00 4 4 0 0 +-1.0151507269873706E-16 4 5 0 0 +-2.1038003802663802E-16 4 6 0 0 +-1.1347793078136172E+00 5 5 0 0 +-8.9271852787184578E-18 5 6 0 0 +-9.5059645051377517E-01 6 6 0 0 + 9.9239946791345224E-01 0 0 0 0 diff --git a/test/chemistry/test_driver_fcidump_lih.npz b/test/chemistry/test_driver_fcidump_lih.npz new file mode 100644 index 0000000000000000000000000000000000000000..5287032ddd629cc3cbb1697f30621a12f2f99d3f GIT binary patch literal 1986 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 literal 0 HcmV?d00001 diff --git a/test/chemistry/test_driver_fcidump_uhf.fcidump b/test/chemistry/test_driver_fcidump_uhf.fcidump deleted file mode 100644 index fbc670ab45..0000000000 --- a/test/chemistry/test_driver_fcidump_uhf.fcidump +++ /dev/null @@ -1,24 +0,0 @@ -&FCI NORB= 2,NELEC= 2,MS2= 0, - ORBSYM=1,1, - ISYM=0, - / - 6.7571015480351648E-01 1 1 1 1 - 6.6458173025529665E-01 1 1 2 2 - 1.8093119978423133E-01 1 2 1 2 - 6.9857372273201834E-01 2 2 2 2 - 6.7571015480351682E-01 1 1 3 3 - 6.6458173025529654E-01 1 1 4 4 --1.8093119978423133E-01 1 2 3 4 - 6.6458173025529710E-01 2 2 3 3 - 6.9857372273201834E-01 2 2 4 4 - 6.7571015480351704E-01 3 3 3 3 - 6.6458173025529732E-01 3 3 4 4 - 1.8093119978423153E-01 3 4 3 4 - 6.9857372273201845E-01 4 4 4 4 --1.2563390730032502E+00 1 1 0 0 --2.3575299028703285E-16 1 2 0 0 --4.7189600728114062E-01 2 2 0 0 --1.2563390730032507E+00 3 3 0 0 --6.7113779463559231E-17 3 4 0 0 --4.7189600728114073E-01 4 4 0 0 - 7.1996899444897966E-01 0 0 0 0 From 9e2d1d3c4bddbee48e0cf0445a04bc435da5e05f Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 09:40:00 +0100 Subject: [PATCH 08/28] Store inactive energy in nuclear repulsion energy instead of HF energy. --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 4 ++-- test/chemistry/test_driver_fcidump.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index ca1c56c6a9..fee362719f 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -60,7 +60,7 @@ def run(self) -> QMolecule: q_mol = QMolecule() - q_mol.hf_energy = fcidump_data.get('ecore', float('NaN')) + q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) # TODO: NELEC is inconclusive in the case of a non-singlet spin system q_mol.num_beta = fcidump_data.get('NELEC', float('NaN')) // 2 @@ -87,4 +87,4 @@ def dump(q_mol: QMolecule, outpath: str) -> None: dump(q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, (q_mol.mo_onee_ints, q_mol.mo_onee_ints_b), (q_mol.mo_eri_ints, q_mol.mo_eri_ints_ba, q_mol.mo_eri_ints_bb), - q_mol.hf_energy, outpath) + q_mol.nuclear_repulsion_energy, outpath) diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py index fa9abe47e0..09c9951a00 100644 --- a/test/chemistry/test_driver_fcidump.py +++ b/test/chemistry/test_driver_fcidump.py @@ -32,7 +32,7 @@ class BaseTestDriverFCIDump(ABC): def __init__(self): self.log = None self.qmolecule = None - self.hf_energy = None + self.nuclear_repulsion_energy = None self.num_orbitals = None self.num_alpha = None self.num_beta = None @@ -59,8 +59,10 @@ def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): def test_driver_inactive_energy(self): """ driver inactive energy test """ - self.log.debug('QMolecule inactive energy is {}'.format(self.qmolecule.hf_energy)) - self.assertAlmostEqual(self.qmolecule.hf_energy, self.hf_energy, places=3) + self.log.debug('QMolecule inactive energy is {}'.format( + self.qmolecule.nuclear_repulsion_energy)) + self.assertAlmostEqual(self.qmolecule.nuclear_repulsion_energy, + self.nuclear_repulsion_energy, places=3) def test_driver_num_orbitals(self): """ driver num orbitals test """ @@ -129,7 +131,7 @@ class TestDriverFCIDumpH2(QiskitChemistryTestCase, BaseTestDriverFCIDump): def setUp(self): super().setUp() - self.hf_energy = 0.7199 + self.nuclear_repulsion_energy = 0.7199 self.num_orbitals = 2 self.num_alpha = 1 self.num_beta = 1 @@ -150,7 +152,7 @@ class TestDriverFCIDumpLiH(QiskitChemistryTestCase, BaseTestDriverFCIDump): def setUp(self): super().setUp() - self.hf_energy = 0.9924 + self.nuclear_repulsion_energy = 0.9924 self.num_orbitals = 6 self.num_alpha = 2 self.num_beta = 2 @@ -169,7 +171,7 @@ class TestDriverFCIDumpOH(QiskitChemistryTestCase, BaseTestDriverFCIDump): def setUp(self): super().setUp() - self.hf_energy = 11.3412 + self.nuclear_repulsion_energy = 11.3412 self.num_orbitals = 6 self.num_alpha = 5 self.num_beta = 4 From 7a6e6035e275f77007b56ebde768a6c1a0bca03b Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 09:40:57 +0100 Subject: [PATCH 09/28] Add FCIDumpDriver unittest for stack integration --- test/chemistry/test_driver_methods_fcidump.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/chemistry/test_driver_methods_fcidump.py diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py new file mode 100644 index 0000000000..85f485b40d --- /dev/null +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Driver Methods FCIDump """ + +from test.chemistry.test_driver_methods import TestDriverMethods +from qiskit.chemistry.drivers import FCIDumpDriver + + +class TestDriverMethodsFCIDump(TestDriverMethods): + """ Driver Methods FCIDump tests """ + + def test_lih(self): + """ LiH test """ + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_lih.fcidump')) + result = self._run_driver(driver, freeze_core=False) + self._assert_energy(result, 'lih') + + def test_oh(self): + """ OH test """ + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_oh.fcidump')) + result = self._run_driver(driver, freeze_core=False) + self._assert_energy(result, 'oh') From 3ccaf792f70de8d8f78fa901d5c9a8063647fea2 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 10:02:17 +0100 Subject: [PATCH 10/28] Check for num_atoms in QMolecule.core_orbitals() In the example of a `freeze_core` Hamiltonian this method is called to return the list of core orbitals. When working with the FCIDumpDriver it may be the case that `num_atoms` is not set. Thus, we check for this case and return in empty list instead. Unittests have been added which assert that a warning is logged. --- qiskit/chemistry/qmolecule.py | 3 +++ test/chemistry/test_driver_methods_fcidump.py | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/qiskit/chemistry/qmolecule.py b/qiskit/chemistry/qmolecule.py index b4a6322763..dc448a6336 100644 --- a/qiskit/chemistry/qmolecule.py +++ b/qiskit/chemistry/qmolecule.py @@ -139,6 +139,9 @@ def Z(self, natom): # pylint: disable=invalid-name @property def core_orbitals(self): """ returns core orbitals """ + if self.num_atoms is None: + logger.warning("Missing molecule information! Returning empty core orbital list.") + return [] count = 0 for i in range(self.num_atoms): z = self.Z(i) diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index 85f485b40d..48d24e35fb 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -32,3 +32,23 @@ def test_oh(self): driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_oh.fcidump')) result = self._run_driver(driver, freeze_core=False) self._assert_energy(result, 'oh') + + def test_lih_freeze_core(self): + """ LiH freeze core test """ + with self.assertLogs('qiskit.chemistry', level='WARNING') as log: + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_lih.fcidump')) + result = self._run_driver(driver, freeze_core=True) + self._assert_energy(result, 'lih') + warning = 'WARNING:qiskit.chemistry.qmolecule:' + \ + 'Missing molecule information! Returning empty core orbital list.' + self.assertIn(warning, log.output) + + def test_oh_freeze_core(self): + """ OH freeze core test """ + with self.assertLogs('qiskit.chemistry', level='WARNING') as log: + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_oh.fcidump')) + result = self._run_driver(driver, freeze_core=True) + self._assert_energy(result, 'oh') + warning = 'WARNING:qiskit.chemistry.qmolecule:' + \ + 'Missing molecule information! Returning empty core orbital list.' + self.assertIn(warning, log.output) From 38bdcc0a6a23d3d621ebb52a8ec0b1db4e0f65b4 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 10:13:23 +0100 Subject: [PATCH 11/28] Add optional num_particles argument Allow the user to manually specify the number of particles. --- .../drivers/fcidumpd/fcidumpdriver.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index fee362719f..e14fb812c0 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -33,12 +33,15 @@ class FCIDumpDriver(BaseDriver): ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. """ - def __init__(self, fcidump_input: str) -> None: + def __init__(self, fcidump_input: str, num_particles: list = None) -> None: """ Initializer Args: fcidump_input: path to the FCIDump file + num_particles (optional): Allows to specify the number of alpha and beta particles + explicitly. If None, the electrons are distributed evenly into the alpha and beta + states (preferring alpha in the case of an off number of particles). Raises: QiskitChemistryError: invalid input @@ -49,6 +52,11 @@ def __init__(self, fcidump_input: str) -> None: raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(fcidump_input)) self._fcidump_input = fcidump_input + if num_particles is not None and not isinstance(num_particles, list) \ + and len(num_particles) != 2: + raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(num_particles)) + self.num_particles = num_particles + def run(self) -> QMolecule: """ Constructs a QMolecule instance out of a FCIDump file. @@ -62,9 +70,11 @@ def run(self) -> QMolecule: q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) - # TODO: NELEC is inconclusive in the case of a non-singlet spin system - q_mol.num_beta = fcidump_data.get('NELEC', float('NaN')) // 2 - q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta + if self.num_particles is None: + q_mol.num_beta = fcidump_data.get('NELEC', float('NaN')) // 2 + q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta + else: + q_mol.num_alpha, q_mol.num_beta = self.num_particles q_mol.mo_onee_ints = fcidump_data.get('hij', None) q_mol.mo_onee_ints_b = fcidump_data.get('hij_b', None) From c0354d30f44ac4d9852db06bd8257864314261de Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 10:23:22 +0100 Subject: [PATCH 12/28] Add optional atoms argument Allow the user to manually specify the molecule information. This permits frozen core Hamiltonians to be created from FCIDumpDriver-generated QMolecule instances. --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 13 ++++++++++++- test/chemistry/test_driver_methods_fcidump.py | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index e14fb812c0..918a04c97b 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -33,7 +33,7 @@ class FCIDumpDriver(BaseDriver): ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. """ - def __init__(self, fcidump_input: str, num_particles: list = None) -> None: + def __init__(self, fcidump_input: str, num_particles: list = None, atoms: list = None) -> None: """ Initializer @@ -42,6 +42,9 @@ def __init__(self, fcidump_input: str, num_particles: list = None) -> None: num_particles (optional): Allows to specify the number of alpha and beta particles explicitly. If None, the electrons are distributed evenly into the alpha and beta states (preferring alpha in the case of an off number of particles). + atoms (optional): Allows to specify the atom list of the molecule. If it is provided, + the created QMolecule instance will permit frozen core Hamiltonians. This list must + consist of valid atom symbols. Raises: QiskitChemistryError: invalid input @@ -57,6 +60,11 @@ def __init__(self, fcidump_input: str, num_particles: list = None) -> None: raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(num_particles)) self.num_particles = num_particles + if atoms is not None and not isinstance(atoms, list) \ + and not all([sym in QMolecule.symbols for sym in atoms]): + raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(atoms)) + self.atoms = atoms + def run(self) -> QMolecule: """ Constructs a QMolecule instance out of a FCIDump file. @@ -75,6 +83,9 @@ def run(self) -> QMolecule: q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta else: q_mol.num_alpha, q_mol.num_beta = self.num_particles + if self.atoms is not None: + q_mol.num_atoms = len(self.atoms) + q_mol.atom_symbol = self.atoms q_mol.mo_onee_ints = fcidump_data.get('hij', None) q_mol.mo_onee_ints_b = fcidump_data.get('hij_b', None) diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index 48d24e35fb..a00364d5a0 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -52,3 +52,17 @@ def test_oh_freeze_core(self): warning = 'WARNING:qiskit.chemistry.qmolecule:' + \ 'Missing molecule information! Returning empty core orbital list.' self.assertIn(warning, log.output) + + def test_lih_with_atoms(self): + """ LiH with num_atoms test """ + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_lih.fcidump'), + atoms=['Li', 'H']) + result = self._run_driver(driver, freeze_core=True) + self._assert_energy(result, 'lih') + + def test_oh_with_atoms(self): + """ OH with num_atoms test """ + driver = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_oh.fcidump'), + atoms=['O', 'H']) + result = self._run_driver(driver, freeze_core=True) + self._assert_energy(result, 'oh') From bf6d182830e2a2cc1412335f1adec96def19ebb0 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 11:47:10 +0100 Subject: [PATCH 13/28] Update FCIDumpDriver unittest documentation --- test/chemistry/test_driver_fcidump.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py index 09c9951a00..a3206de610 100644 --- a/test/chemistry/test_driver_fcidump.py +++ b/test/chemistry/test_driver_fcidump.py @@ -22,11 +22,10 @@ class BaseTestDriverFCIDump(ABC): - """FCIDump Driver base test class using H2 @ 0.735, sto3g. + """FCIDump Driver base test class. In contrast to the other driver tests this one does *not* derive from TestDriver because the interface is fundamentally different. - Similar to the HDF5Driver there is also no TestDriverMethodsFCIDump class for the same reason. """ def __init__(self): From ae119499160dace33803bd0f68e2fb43e8022cf2 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 11:40:44 +0100 Subject: [PATCH 14/28] Add MS2, ISYM and ORBSYM optional arguments --- .pylintdict | 2 ++ qiskit/chemistry/drivers/fcidumpd/dumper.py | 28 +++++++++++++------ .../drivers/fcidumpd/fcidumpdriver.py | 13 ++++++--- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.pylintdict b/.pylintdict index 86a982eb2e..f17aab9c3c 100644 --- a/.pylintdict +++ b/.pylintdict @@ -248,6 +248,7 @@ ising isinstance iso isub +isym iteratively izaac jac @@ -374,6 +375,7 @@ oplus optim optimizer's optimizers +orbsym org outpath overfit diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index 6ede4957d8..a3959b1f1c 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -19,16 +19,20 @@ import numpy as np -def dump(norb: int, nelec: int, hijs: list, hijkls: list, einact: float, outpath: str) -> None: +def dump(outpath: str, norb: int, nelec: int, hijs: list, hijkls: list, einact: float, + ms2: int = 0, orbsym: list = None, isym: int = 1) -> None: """ Generates a FCIDump output. Args: - norb (int): number of orbitals - nelec (int): number of electrons - hijs (tuple): pair of alpha and beta 1-electron integrals. The latter may be None. - hijkls (tuple): triplet of alpha/alpha, beta/alpha and beta/beta 2-electron - integrals. The latter two may be None. - einact (float): inactive energy - outpath (str): path to the output file + outpath: path to the output file + norb: number of orbitals + nelec: number of electrons + hijs: pair of alpha and beta 1-electron integrals. The latter may be None. + hijkls: triplet of alpha/alpha, beta/alpha and beta/beta 2-electron integrals. The + latter two may be None. + einact: inactive energy + ms2 (optional): 2*S where S is the spin quantum number. Defaults to 0. + orbsym (optional): list of spatial symmetries of the orbitals + isym (optional): spatial symmetry of the wave function. Defaults to 1. """ hij, hij_b = hijs hijkl, hijkl_ba, hijkl_bb = hijkls @@ -39,7 +43,13 @@ def dump(norb: int, nelec: int, hijs: list, hijkls: list, einact: float, outpath mos = range(norb) with open(outpath, 'w') as outfile: # print header - outfile.write('&FCI NORB={:4d},NELEC={:4d},\n/\n'.format(norb, nelec)) + outfile.write('&FCI NORB={:4d},NELEC={:4d},MS2={:4d}\n'.format(norb, nelec, ms2)) + if orbsym is None: + outfile.write(' ORBSYM=' + '1,'*norb + '\n') + else: + assert len(orbsym) == norb + outfile.write(' ORBSYM=' + ','.join(orbsym) + '\n') + outfile.write(' ISYM={:d},\n/&END\n'.format(isym)) # append 2e integrals _dump_2e_ints(hijkl, mos, outfile) if hijkl_ba is not None: diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 918a04c97b..74e60e28b7 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -96,16 +96,21 @@ def run(self) -> QMolecule: return q_mol @staticmethod - def dump(q_mol: QMolecule, outpath: str) -> None: + def dump(q_mol: QMolecule, outpath: str, + ms2: int = 0, orbsym: list = None, isym: int = 1) -> None: """ Convenience method to produce an FCIDump output file Args: + outpath (str): path to the output file q_mol (QMolecule): QMolecule data to be dumped. It is assumed that the HF energy stored in this QMolecule instance contains the inactive core energy. - outpath (str): path to the output file + ms2 (optional): 2*S where S is the spin quantum number. Defaults to 0. + orbsym (optional): list of spatial symmetries of the orbitals + isym (optional): spatial symmetry of the wave function. Defaults to 1. """ - dump(q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, + dump(outpath, + q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, (q_mol.mo_onee_ints, q_mol.mo_onee_ints_b), (q_mol.mo_eri_ints, q_mol.mo_eri_ints_ba, q_mol.mo_eri_ints_bb), - q_mol.nuclear_repulsion_energy, outpath) + q_mol.nuclear_repulsion_energy, ms2, orbsym, isym) From c00e8b34054f60b9aa77e69c75fb08d9a2def85a Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 11:47:31 +0100 Subject: [PATCH 15/28] Add unittest for dumping RHF H2 QMolecule instance --- test/chemistry/test_driver_fcidump_dumper.py | 133 +++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 test/chemistry/test_driver_fcidump_dumper.py diff --git a/test/chemistry/test_driver_fcidump_dumper.py b/test/chemistry/test_driver_fcidump_dumper.py new file mode 100644 index 0000000000..f2d746d33a --- /dev/null +++ b/test/chemistry/test_driver_fcidump_dumper.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Driver FCIDump Dumping """ + +import tempfile +import unittest +from abc import ABC, abstractmethod +from test.chemistry import QiskitChemistryTestCase +import numpy as np +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.drivers import FCIDumpDriver, PySCFDriver, UnitsType + + +class BaseTestDriverFCIDumpDumper(ABC): + """FCIDump Driver dumping base test class.""" + + def __init__(self): + self.log = None + self.dumped = None + self.core_energy = None + self.num_orbitals = None + self.num_electrons = None + self.spin_number = None + self.wf_symmetry = None + self.orb_symmetries = None + self.mo_onee = None + self.mo_eri = None + + @abstractmethod + def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): + """ asset Almost Equal """ + raise Exception('Abstract method') + + @abstractmethod + def assertEqual(self, first, second, msg=None): + """ assert equal """ + raise Exception('Abstract method') + + @abstractmethod + def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): + """ assert Sequence Equal """ + raise Exception('Abstract method') + + def test_dumped_inactive_energy(self): + """ dumped inactive energy test """ + self.log.debug('Dumped inactive energy is {:g}'.format(self.dumped['ECORE'])) + self.assertAlmostEqual(self.dumped['ECORE'], self.core_energy, places=3) + + def test_dumped_num_orbitals(self): + """ dumped number of orbitals test """ + self.log.debug('Dumped number of orbitals is {:d}'.format(self.dumped['NORB'])) + self.assertEqual(self.dumped['NORB'], self.num_orbitals) + + def test_dumped_num_electrons(self): + """ dumped number of electrons test """ + self.log.debug('Dumped number of electrons is {:d}'.format(self.dumped['NELEC'])) + self.assertEqual(self.dumped['NELEC'], self.num_electrons) + + def test_dumped_spin_number(self): + """ dumped spin number test """ + self.log.debug('Dumped spin number is {:d}'.format(self.dumped['MS2'])) + self.assertEqual(self.dumped['MS2'], self.spin_number) + + def test_dumped_wave_function_sym(self): + """ dumped wave function symmetry test """ + self.log.debug('Dumped wave function symmetry is {:d}'.format(self.dumped['ISYM'])) + self.assertEqual(self.dumped['ISYM'], self.wf_symmetry) + + def test_dumped_orbital_syms(self): + """ dumped orbital symmetries test """ + self.log.debug('Dumped orbital symmetries is %s', self.dumped['ORBSYM']) + self.assertEqual(self.dumped['ORBSYM'], self.orb_symmetries) + + def test_dumped_h1(self): + """ dumped h1 integrals test """ + self.log.debug('Dumped h1 integrals are %s', self.dumped['H1']) + np.testing.assert_array_almost_equal(np.absolute(self.dumped['H1']), + np.absolute(self.mo_onee), decimal=4) + + def test_dumped_h2(self): + """ dumped h2 integrals test """ + self.log.debug('Dumped h2 integrals are %s', self.dumped['H2']) + np.testing.assert_array_almost_equal(np.absolute(self.dumped['H2']), + np.absolute(self.mo_eri), decimal=4) + + +class TestDriverFCIDumpDumpH2(QiskitChemistryTestCase, BaseTestDriverFCIDumpDumper): + """RHF FCIDump Driver tests.""" + + def setUp(self): + super().setUp() + self.core_energy = 0.7199 + self.num_orbitals = 2 + self.num_electrons = 2 + self.spin_number = 0 + self.wf_symmetry = 1 + self.orb_symmetries = [1, 1] + self.mo_onee = [[1.2563, 0.0], [0.0, 0.4719]] + self.mo_eri = [0.6757, 0.0, 0.1809, 0.6646, 0.0, 0.6986] + try: + driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + + qmolecule = driver.run() + + dump = tempfile.NamedTemporaryFile() + FCIDumpDriver.dump(qmolecule, dump.name) + + from pyscf.tools import fcidump as pyscf_fcidump # pylint: disable=import-outside-toplevel + self.dumped = pyscf_fcidump.read(dump.name) + + dump.close() + + +if __name__ == '__main__': + unittest.main() From afa6d72e218b29d0356d4e1c4140f83d6ca96940 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 11:58:49 +0100 Subject: [PATCH 16/28] Use MS2 (spin quantum number) properly Instead of the optional `num_particles` spin quantum number from the FCIDump (MS2) can be used. This gives the multiplicity directly. --- .../drivers/fcidumpd/fcidumpdriver.py | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 74e60e28b7..9501afa7cb 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -33,15 +33,12 @@ class FCIDumpDriver(BaseDriver): ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. """ - def __init__(self, fcidump_input: str, num_particles: list = None, atoms: list = None) -> None: + def __init__(self, fcidump_input: str, atoms: list = None) -> None: """ Initializer Args: fcidump_input: path to the FCIDump file - num_particles (optional): Allows to specify the number of alpha and beta particles - explicitly. If None, the electrons are distributed evenly into the alpha and beta - states (preferring alpha in the case of an off number of particles). atoms (optional): Allows to specify the atom list of the molecule. If it is provided, the created QMolecule instance will permit frozen core Hamiltonians. This list must consist of valid atom symbols. @@ -55,11 +52,6 @@ def __init__(self, fcidump_input: str, num_particles: list = None, atoms: list = raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(fcidump_input)) self._fcidump_input = fcidump_input - if num_particles is not None and not isinstance(num_particles, list) \ - and len(num_particles) != 2: - raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(num_particles)) - self.num_particles = num_particles - if atoms is not None and not isinstance(atoms, list) \ and not all([sym in QMolecule.symbols for sym in atoms]): raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(atoms)) @@ -78,11 +70,9 @@ def run(self) -> QMolecule: q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) - if self.num_particles is None: - q_mol.num_beta = fcidump_data.get('NELEC', float('NaN')) // 2 - q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta - else: - q_mol.num_alpha, q_mol.num_beta = self.num_particles + q_mol.multiplicity = fcidump_data.get('MS2', 0) + 1 + q_mol.num_beta = (fcidump_data.get('NELEC', float('NaN')) - (q_mol.multiplicity - 1)) // 2 + q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta if self.atoms is not None: q_mol.num_atoms = len(self.atoms) q_mol.atom_symbol = self.atoms @@ -96,8 +86,7 @@ def run(self) -> QMolecule: return q_mol @staticmethod - def dump(q_mol: QMolecule, outpath: str, - ms2: int = 0, orbsym: list = None, isym: int = 1) -> None: + def dump(q_mol: QMolecule, outpath: str, orbsym: list = None, isym: int = 1) -> None: """ Convenience method to produce an FCIDump output file @@ -105,7 +94,6 @@ def dump(q_mol: QMolecule, outpath: str, outpath (str): path to the output file q_mol (QMolecule): QMolecule data to be dumped. It is assumed that the HF energy stored in this QMolecule instance contains the inactive core energy. - ms2 (optional): 2*S where S is the spin quantum number. Defaults to 0. orbsym (optional): list of spatial symmetries of the orbitals isym (optional): spatial symmetry of the wave function. Defaults to 1. """ @@ -113,4 +101,4 @@ def dump(q_mol: QMolecule, outpath: str, q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, (q_mol.mo_onee_ints, q_mol.mo_onee_ints_b), (q_mol.mo_eri_ints, q_mol.mo_eri_ints_ba, q_mol.mo_eri_ints_bb), - q_mol.nuclear_repulsion_energy, ms2, orbsym, isym) + q_mol.nuclear_repulsion_energy, ms2=q_mol.multiplicity - 1, orbsym=orbsym, isym=isym) From 87969caca24eb36b3e1f6b151a4d822e2ceab0c0 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 13:34:37 +0100 Subject: [PATCH 17/28] Update copyright --- qiskit/chemistry/qmolecule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/qmolecule.py b/qiskit/chemistry/qmolecule.py index dc448a6336..e209df787b 100644 --- a/qiskit/chemistry/qmolecule.py +++ b/qiskit/chemistry/qmolecule.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019 +# (C) Copyright IBM 2018, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory From 5346c6b8e56e8869f0789deb176553acb002920a Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 13:44:46 +0100 Subject: [PATCH 18/28] Ignore possible MO energy values This will prevent the parsing from failing if the MO energies are in fact present. --- qiskit/chemistry/drivers/fcidumpd/dumper.py | 1 + qiskit/chemistry/drivers/fcidumpd/parser.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index a3959b1f1c..442f264455 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -60,6 +60,7 @@ def dump(outpath: str, norb: int, nelec: int, hijs: list, hijkls: list, einact: _dump_1e_ints(hij, mos, outfile) if hij_b is not None: _dump_1e_ints(hij_b, mos, outfile, beta=True) + # TODO append MO energies (last three indices are 0) # append inactive energy _write_to_outfile(outfile, einact, (0, 0, 0, 0)) diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 69a9f27a6c..9216644ca2 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -125,6 +125,9 @@ def parse(fcidump: str) -> dict: i, a, j, b = [int(i) for i in orbital.split()[1:]] # pylint: disable=invalid-name if i == a == j == b == 0: output['ecore'] = x + elif a == j == b == 0: + # TODO: x is the energy of the i-th MO + continue elif j == b == 0: try: hij_elements.remove((i-1, a-1)) From 6f2f11849b4aecf81cb78285c99a802e6cfbc0af Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 16:47:17 +0100 Subject: [PATCH 19/28] Remove too specific pylintdict exceptions --- .pylintdict | 10 ---------- qiskit/chemistry/drivers/fcidumpd/parser.py | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.pylintdict b/.pylintdict index f17aab9c3c..7d314a76ee 100644 --- a/.pylintdict +++ b/.pylintdict @@ -207,22 +207,16 @@ hartree hassidim hb hhl -hijs -hijkls hk https -ia iadd -iajb idx ifdef ifortvars ign ignis ij -ijkl ijkm -ik ikmj imag implicants @@ -253,9 +247,6 @@ iteratively izaac jac jacobian -jb -ji -jl jordan jozsa jt @@ -279,7 +270,6 @@ lhs lih lijh lin -lk lmin lnot loglikelihood diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 9216644ca2..731e5b71be 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -22,6 +22,7 @@ def parse(fcidump: str) -> dict: + # pylint: disable=wrong-spelling-in-comment """ Parses a FCIDump output. Args: fcidump: path to the FCIDump file @@ -197,6 +198,7 @@ def _permute_1e_ints(hij: list, elements: set, norb: int, beta: bool = False) -> def _permute_2e_ints(hijkl: list, elements: set, norb: int, beta: bool = False) -> None: + # pylint: disable=wrong-spelling-in-comment for elem in elements.copy(): shifted = tuple([e-((e >= norb) * norb)for e in elem]) # initially look for "transposed" element if spins are equal From 8f0f1eab7bffedf45bb19c783c81f67d6906b5b0 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 17:25:45 +0100 Subject: [PATCH 20/28] Improve error messages --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 6 ++++-- qiskit/chemistry/drivers/fcidumpd/parser.py | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 9501afa7cb..4d905a0907 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -49,12 +49,14 @@ def __init__(self, fcidump_input: str, atoms: list = None) -> None: super().__init__() if not isinstance(fcidump_input, str): - raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(fcidump_input)) + raise QiskitChemistryError( + "The fcidump_input must be str, not '{}'".format(fcidump_input)) self._fcidump_input = fcidump_input if atoms is not None and not isinstance(atoms, list) \ and not all([sym in QMolecule.symbols for sym in atoms]): - raise QiskitChemistryError("Invalid input for FCIDumpDriver '{}'".format(atoms)) + raise QiskitChemistryError( + "The atoms must be a list of valid atomic symbols, not '{}'".format(atoms)) self.atoms = atoms def run(self) -> QMolecule: diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 731e5b71be..9fa4c1df25 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -138,7 +138,8 @@ def parse(fcidump: str) -> dict: hij_b_elements.remove((i-1, a-1)) hij_b[i-1-norb][a-1-norb] = x else: - raise QiskitChemistryError() + raise QiskitChemistryError("Unkown 1-electron integral indices encountered in \ + '{}'".format((i, a))) else: try: hijkl_elements.remove((i-1, a-1, j-1, b-1)) @@ -156,7 +157,8 @@ def parse(fcidump: str) -> dict: hijkl_bb_elements.remove((i-1, a-1, j-1, b-1)) hijkl_bb[i-1-norb][a-1-norb][j-1-norb][b-1-norb] = x else: - raise QiskitChemistryError() + raise QiskitChemistryError("Unkown 2-electron integral indices encountered in \ + '{}'".format((i, a, j, b))) # iterate over still empty elements in 1-electron matrix and populate with symmetric ones # if any elements are not populated these will be zero @@ -176,7 +178,9 @@ def parse(fcidump: str) -> dict: _permute_2e_ints(hijkl_ba, hijkl_ba_elements, norb, beta=1) # assert that EITHER hijkl_ab OR hijkl_ba were given - assert np.allclose(hijkl_ab, 0.0) != np.allclose(hijkl_ba, 0.0) + if np.allclose(hijkl_ab, 0.0) == np.allclose(hijkl_ba, 0.0): + raise QiskitChemistryError("Encountered mixed sets of indices for the 2-electron \ + integrals. Either alpha/beta or beta/alpha matrix should be specified.") if np.allclose(hijkl_ba, 0.0): hijkl_ba = hijkl_ab.transpose() From 09e799c324fd18e4508ffb322b3d24040ad56b3f Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 17:26:46 +0100 Subject: [PATCH 21/28] Minor code improvements --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 6 +++--- qiskit/chemistry/drivers/fcidumpd/parser.py | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 4d905a0907..fae0cfad10 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -16,8 +16,8 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry import QiskitChemistryError, QMolecule -from qiskit.chemistry.drivers.fcidumpd.dumper import dump -from qiskit.chemistry.drivers.fcidumpd.parser import parse +from .dumper import dump +from .parser import parse class FCIDumpDriver(BaseDriver): @@ -53,7 +53,7 @@ def __init__(self, fcidump_input: str, atoms: list = None) -> None: "The fcidump_input must be str, not '{}'".format(fcidump_input)) self._fcidump_input = fcidump_input - if atoms is not None and not isinstance(atoms, list) \ + if atoms and not isinstance(atoms, list) \ and not all([sym in QMolecule.symbols for sym in atoms]): raise QiskitChemistryError( "The atoms must be a list of valid atomic symbols, not '{}'".format(atoms)) diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 9fa4c1df25..5aaeb2bcc3 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -31,7 +31,6 @@ def parse(fcidump: str) -> dict: Returns: dict: dictionary for storing the parsed data """ - fcidump_str = None try: with open(fcidump, 'r') as file: fcidump_str = file.read() @@ -64,8 +63,7 @@ def parse(fcidump: str) -> dict: output['ISYM'] = int(_isym.groups()[0]) if _isym else 1 # ORBSYM holds a list, thus it requires a little different treatment _orbsym = re.search(r'ORBSYM.*?'+r'(\d+),'*norb, metadata) - output['ORBSYM'] = [int(s) for s in _orbsym.groups()] if _orbsym \ - else [1 for s in range(norb)] + output['ORBSYM'] = [int(s) for s in _orbsym.groups()] if _orbsym else [1] * len(norb) _iprtim = re.search('IPRTIM'+pattern, metadata) output['IPRTIM'] = int(_iprtim.groups()[0]) if _iprtim else -1 _int = re.search('INT'+pattern, metadata) @@ -204,7 +202,7 @@ def _permute_1e_ints(hij: list, elements: set, norb: int, beta: bool = False) -> def _permute_2e_ints(hijkl: list, elements: set, norb: int, beta: bool = False) -> None: # pylint: disable=wrong-spelling-in-comment for elem in elements.copy(): - shifted = tuple([e-((e >= norb) * norb)for e in elem]) + shifted = tuple(e-((e >= norb) * norb) for e in elem) # initially look for "transposed" element if spins are equal if beta != 1 and elem[::-1] not in elements: hijkl[shifted] = hijkl[shifted[::-1]] From e7d27da1eb7671e2ef31b794a95e258c29c62e12 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 17:30:10 +0100 Subject: [PATCH 22/28] Fix docstrings and type hints. --- qiskit/chemistry/drivers/fcidumpd/__init__.py | 5 +- qiskit/chemistry/drivers/fcidumpd/dumper.py | 40 ++++++++------ .../drivers/fcidumpd/fcidumpdriver.py | 52 +++++++++---------- qiskit/chemistry/drivers/fcidumpd/parser.py | 21 ++++---- qiskit/chemistry/qmolecule.py | 8 ++- 5 files changed, 70 insertions(+), 56 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/__init__.py b/qiskit/chemistry/drivers/fcidumpd/__init__.py index 4bd6a72e74..be203f3048 100644 --- a/qiskit/chemistry/drivers/fcidumpd/__init__.py +++ b/qiskit/chemistry/drivers/fcidumpd/__init__.py @@ -12,7 +12,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" FCIDump package """ +"""FCIDump driver package. + +Contains tools to parse and dump FCIDump files. +""" from .fcidumpdriver import FCIDumpDriver diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index 442f264455..dce59fa992 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -12,27 +12,31 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" FCIDump dumper """ +"""FCIDump dumper.""" +from typing import List, Optional from io import TextIOWrapper import itertools import numpy as np -def dump(outpath: str, norb: int, nelec: int, hijs: list, hijkls: list, einact: float, - ms2: int = 0, orbsym: list = None, isym: int = 1) -> None: - """ Generates a FCIDump output. - Args: - outpath: path to the output file - norb: number of orbitals - nelec: number of electrons - hijs: pair of alpha and beta 1-electron integrals. The latter may be None. - hijkls: triplet of alpha/alpha, beta/alpha and beta/beta 2-electron integrals. The +def dump(outpath: str, norb: int, nelec: int, hijs: List[float], hijkls: List[float], einact: float, + ms2: Optional[int] = 0, orbsym: Optional[List[int]] = None, isym: Optional[int] = 1 + ) -> None: + # pylint: disable=wrong-spelling-in-docstring + """Generates a FCIDump output. + + Args: + outpath: Path to the output file. + norb: The number of orbitals. + nelec: The number of electrons. + hijs: The pair of alpha and beta 1-electron integrals. The latter may be None. + hijkls: The triplet of alpha/alpha, beta/alpha and beta/beta 2-electron integrals. The latter two may be None. - einact: inactive energy - ms2 (optional): 2*S where S is the spin quantum number. Defaults to 0. - orbsym (optional): list of spatial symmetries of the orbitals - isym (optional): spatial symmetry of the wave function. Defaults to 1. + einact: The inactive energy. + ms2: 2*S, where S is the spin quantum number. + orbsym: A list of spatial symmetries of the orbitals. + isym: The spatial symmetry of the wave function. """ hij, hij_b = hijs hijkl, hijkl_ba, hijkl_bb = hijkls @@ -65,7 +69,8 @@ def dump(outpath: str, norb: int, nelec: int, hijs: list, hijkls: list, einact: _write_to_outfile(outfile, einact, (0, 0, 0, 0)) -def _dump_1e_ints(hij: list, mos: list, outfile: TextIOWrapper, beta: bool = False) -> None: +def _dump_1e_ints(hij: List[float], mos: List[int], outfile: TextIOWrapper, + beta: Optional[bool] = False) -> None: idx_offset = 1 if not beta else 1+len(mos) hij_elements = set() for i, j in itertools.product(mos, repeat=2): @@ -78,7 +83,8 @@ def _dump_1e_ints(hij: list, mos: list, outfile: TextIOWrapper, beta: bool = Fal hij_elements.add((i, j)) -def _dump_2e_ints(hijkl: list, mos: list, outfile: TextIOWrapper, beta: int = 0) -> None: +def _dump_2e_ints(hijkl: List[float], mos: List[int], outfile: TextIOWrapper, + beta: Optional[int] = 0) -> None: idx_offsets = [1, 1] for b in range(beta): idx_offsets[1-b] += len(mos) @@ -112,5 +118,5 @@ def _dump_2e_ints(hijkl: list, mos: list, outfile: TextIOWrapper, beta: int = 0) hijkl_elements.add(elem) -def _write_to_outfile(outfile, value, indices): +def _write_to_outfile(outfile: str, value: float, indices: List[int]): outfile.write('{:23.16E}{:4d}{:4d}{:4d}{:4d}\n'.format(value, *indices)) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index fae0cfad10..2a1d6e4e2a 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -12,8 +12,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" FCIDump Driver """ +"""FCIDump Driver.""" +from typing import List, Optional from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry import QiskitChemistryError, QMolecule from .dumper import dump @@ -21,30 +22,28 @@ class FCIDumpDriver(BaseDriver): - """ - Python implementation of an FCIDump driver. + """Python implementation of an FCIDump driver. The FCIDump format is partially defined in Knowles1989. - Knowles1989: - Peter J. Knowles, Nicholas C. Handy, - A determinant based full configuration interaction program, - Computer Physics Communications, Volume 54, Issue 1, 1989, Pages 75-83, - ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. + References: + Knowles1989: Peter J. Knowles, Nicholas C. Handy, + A determinant based full configuration interaction program, + Computer Physics Communications, Volume 54, Issue 1, 1989, Pages 75-83, + ISSN 0010-4655, https://doi.org/10.1016/0010-4655(89)90033-7. """ - def __init__(self, fcidump_input: str, atoms: list = None) -> None: + def __init__(self, fcidump_input: str, atoms: Optional[List[str]] = None) -> None: """ - Initializer - Args: - fcidump_input: path to the FCIDump file - atoms (optional): Allows to specify the atom list of the molecule. If it is provided, - the created QMolecule instance will permit frozen core Hamiltonians. This list must - consist of valid atom symbols. + fcidump_input: Path to the FCIDump file. + atoms: Allows to specify the atom list of the molecule. If it is provided, the created + QMolecule instance will permit frozen core Hamiltonians. This list must consist of + valid atom symbols. Raises: - QiskitChemistryError: invalid input + QiskitChemistryError: If ``fcidump_input`` is not a string or if ``atoms`` is not a list + of valid atomic symbols as specified in ``QMolecule``. """ super().__init__() @@ -60,11 +59,10 @@ def __init__(self, fcidump_input: str, atoms: list = None) -> None: self.atoms = atoms def run(self) -> QMolecule: - """ - Constructs a QMolecule instance out of a FCIDump file. + """Constructs a QMolecule instance out of a FCIDump file. Returns: - QMolecule: a QMolecule instance populated with a minimal set of required data + A QMolecule instance populated with a minimal set of required data. """ fcidump_data = parse(self._fcidump_input) @@ -88,16 +86,16 @@ def run(self) -> QMolecule: return q_mol @staticmethod - def dump(q_mol: QMolecule, outpath: str, orbsym: list = None, isym: int = 1) -> None: - """ - Convenience method to produce an FCIDump output file + def dump(q_mol: QMolecule, outpath: str, orbsym: Optional[List[int]] = None, + isym: Optional[int] = 1) -> None: + """Convenience method to produce an FCIDump output file. Args: - outpath (str): path to the output file - q_mol (QMolecule): QMolecule data to be dumped. It is assumed that the HF energy stored - in this QMolecule instance contains the inactive core energy. - orbsym (optional): list of spatial symmetries of the orbitals - isym (optional): spatial symmetry of the wave function. Defaults to 1. + outpath: Path to the output file. + q_mol: QMolecule data to be dumped. It is assumed that the nuclear_repulsion_energy in + this QMolecule instance contains the inactive core energy. + orbsym: A list of spatial symmetries of the orbitals. + isym: The spatial symmetry of the wave function. """ dump(outpath, q_mol.num_orbitals, q_mol.num_alpha + q_mol.num_beta, diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 5aaeb2bcc3..4e03390d7d 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -12,8 +12,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" FCIDump parser """ +"""FCIDump parser.""" +from typing import Any, Dict import itertools import re import numpy as np @@ -21,15 +22,17 @@ from qiskit.chemistry import QiskitChemistryError -def parse(fcidump: str) -> dict: +def parse(fcidump: str) -> Dict[str, Any]: # pylint: disable=wrong-spelling-in-comment - """ Parses a FCIDump output. - Args: - fcidump: path to the FCIDump file - Raises: - QiskitChemistryError: missing file or invalid input - Returns: - dict: dictionary for storing the parsed data + """Parses a FCIDump output. + + Args: + fcidump: Path to the FCIDump file. + Raises: + QiskitChemistryError: If the input file cannot be found, if wrong integral indices are + encountered, or if the alpha/beta or beta/alpha 2-electron integrals are mixed. + Returns: + A dictionary storing the parsed data. """ try: with open(fcidump, 'r') as file: diff --git a/qiskit/chemistry/qmolecule.py b/qiskit/chemistry/qmolecule.py index e209df787b..b46f654d3b 100644 --- a/qiskit/chemistry/qmolecule.py +++ b/qiskit/chemistry/qmolecule.py @@ -14,6 +14,7 @@ """ QMolecule """ +from typing import List import os import logging import tempfile @@ -137,8 +138,11 @@ def Z(self, natom): # pylint: disable=invalid-name return QMolecule.symbols.index(self.atom_symbol[natom].lower().capitalize()) @property - def core_orbitals(self): - """ returns core orbitals """ + def core_orbitals(self) -> List[int]: + """ + Returns: + A list of core orbital indices. + """ if self.num_atoms is None: logger.warning("Missing molecule information! Returning empty core orbital list.") return [] From 1be8c67a20d016b43002e2cb982c6492cff60b89 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 17:51:30 +0100 Subject: [PATCH 23/28] Ensure QMolecule.log() works with FCIDumpDriver Also adds unittests. --- .../drivers/fcidumpd/fcidumpdriver.py | 3 +++ test/chemistry/test_driver_methods_fcidump.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 2a1d6e4e2a..22616a0a58 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -68,14 +68,17 @@ def run(self) -> QMolecule: q_mol = QMolecule() + q_mol.hf_energy = float('NaN') # ensures QMolecule.log() works q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) q_mol.multiplicity = fcidump_data.get('MS2', 0) + 1 + q_mol.charge = 0 # ensures QMolecule.log() works q_mol.num_beta = (fcidump_data.get('NELEC', float('NaN')) - (q_mol.multiplicity - 1)) // 2 q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta if self.atoms is not None: q_mol.num_atoms = len(self.atoms) q_mol.atom_symbol = self.atoms + q_mol.atom_xyz = [[float('NaN')] * 3] * len(self.atoms) # ensures QMolecule.log() works q_mol.mo_onee_ints = fcidump_data.get('hij', None) q_mol.mo_onee_ints_b = fcidump_data.get('hij_b', None) diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index a00364d5a0..8edc46f3f4 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -14,6 +14,7 @@ """ Test Driver Methods FCIDump """ +from test.chemistry import QiskitChemistryTestCase from test.chemistry.test_driver_methods import TestDriverMethods from qiskit.chemistry.drivers import FCIDumpDriver @@ -66,3 +67,20 @@ def test_oh_with_atoms(self): atoms=['O', 'H']) result = self._run_driver(driver, freeze_core=True) self._assert_energy(result, 'oh') + + +class TestFCIDumpDriverQMolecule(QiskitChemistryTestCase): + """QMolecule FCIDumpDriver tests.""" + + def test_qmolecule_log(self): + """Test QMolecule log function.""" + qmolecule = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_h2.fcidump')).run() + with self.assertLogs('qiskit.chemistry', level='DEBUG') as _: + qmolecule.log() + + def test_qmolecule_log_with_atoms(self): + """Test QMolecule log function.""" + qmolecule = FCIDumpDriver(self.get_resource_path('test_driver_fcidump_h2.fcidump'), + atoms=['H', 'H']).run() + with self.assertLogs('qiskit.chemistry', level='DEBUG') as _: + qmolecule.log() From 06be9e0de0466a23e36fbb54de82477d5f03e327 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 18 Mar 2020 17:54:15 +0100 Subject: [PATCH 24/28] Use typing types instead of builtins --- qiskit/chemistry/drivers/fcidumpd/parser.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 4e03390d7d..aed4f5161c 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -14,7 +14,7 @@ """FCIDump parser.""" -from typing import Any, Dict +from typing import Any, Dict, List, Optional, Set import itertools import re import numpy as np @@ -195,14 +195,16 @@ def parse(fcidump: str) -> Dict[str, Any]: return output -def _permute_1e_ints(hij: list, elements: set, norb: int, beta: bool = False) -> None: +def _permute_1e_ints(hij: List[float], elements: Set[int], norb: int, + beta: Optional[bool] = False) -> None: for elem in elements.copy(): shifted = tuple(e-(beta * norb) for e in elem) hij[shifted] = hij[shifted[::-1]] elements.remove(elem) -def _permute_2e_ints(hijkl: list, elements: set, norb: int, beta: bool = False) -> None: +def _permute_2e_ints(hijkl: List[float], elements: Set[int], norb: int, + beta: Optional[bool] = False) -> None: # pylint: disable=wrong-spelling-in-comment for elem in elements.copy(): shifted = tuple(e-((e >= norb) * norb) for e in elem) From 54db50386de50be8f6603d0cf6d8c50bc71856aa Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 19 Mar 2020 09:45:20 +0100 Subject: [PATCH 25/28] Fix typo --- test/chemistry/test_driver.py | 2 +- test/chemistry/test_driver_fcidump.py | 2 +- test/chemistry/test_driver_fcidump_dumper.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/chemistry/test_driver.py b/test/chemistry/test_driver.py index f120c50ab8..40b7048061 100644 --- a/test/chemistry/test_driver.py +++ b/test/chemistry/test_driver.py @@ -27,7 +27,7 @@ def __init__(self): @abstractmethod def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): - """ asset Almost Equal """ + """ assert Almost Equal """ raise Exception('Abstract method') @abstractmethod diff --git a/test/chemistry/test_driver_fcidump.py b/test/chemistry/test_driver_fcidump.py index a3206de610..a3eb9c62e6 100644 --- a/test/chemistry/test_driver_fcidump.py +++ b/test/chemistry/test_driver_fcidump.py @@ -43,7 +43,7 @@ def __init__(self): @abstractmethod def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): - """ asset Almost Equal """ + """ assert Almost Equal """ raise Exception('Abstract method') @abstractmethod diff --git a/test/chemistry/test_driver_fcidump_dumper.py b/test/chemistry/test_driver_fcidump_dumper.py index f2d746d33a..6bf75ff126 100644 --- a/test/chemistry/test_driver_fcidump_dumper.py +++ b/test/chemistry/test_driver_fcidump_dumper.py @@ -40,7 +40,7 @@ def __init__(self): @abstractmethod def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): - """ asset Almost Equal """ + """ assert Almost Equal """ raise Exception('Abstract method') @abstractmethod From 4e84910dcf7e041b2c267d0585730f994229f264 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 19 Mar 2020 09:49:01 +0100 Subject: [PATCH 26/28] Remove Optional type hint where not applicable --- qiskit/chemistry/drivers/fcidumpd/dumper.py | 6 +++--- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 2 +- qiskit/chemistry/drivers/fcidumpd/parser.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/dumper.py b/qiskit/chemistry/drivers/fcidumpd/dumper.py index dce59fa992..7c8e29bc31 100644 --- a/qiskit/chemistry/drivers/fcidumpd/dumper.py +++ b/qiskit/chemistry/drivers/fcidumpd/dumper.py @@ -21,7 +21,7 @@ def dump(outpath: str, norb: int, nelec: int, hijs: List[float], hijkls: List[float], einact: float, - ms2: Optional[int] = 0, orbsym: Optional[List[int]] = None, isym: Optional[int] = 1 + ms2: int = 0, orbsym: Optional[List[int]] = None, isym: int = 1 ) -> None: # pylint: disable=wrong-spelling-in-docstring """Generates a FCIDump output. @@ -70,7 +70,7 @@ def dump(outpath: str, norb: int, nelec: int, hijs: List[float], hijkls: List[fl def _dump_1e_ints(hij: List[float], mos: List[int], outfile: TextIOWrapper, - beta: Optional[bool] = False) -> None: + beta: bool = False) -> None: idx_offset = 1 if not beta else 1+len(mos) hij_elements = set() for i, j in itertools.product(mos, repeat=2): @@ -84,7 +84,7 @@ def _dump_1e_ints(hij: List[float], mos: List[int], outfile: TextIOWrapper, def _dump_2e_ints(hijkl: List[float], mos: List[int], outfile: TextIOWrapper, - beta: Optional[int] = 0) -> None: + beta: int = 0) -> None: idx_offsets = [1, 1] for b in range(beta): idx_offsets[1-b] += len(mos) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index 22616a0a58..fd3bb80e61 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -90,7 +90,7 @@ def run(self) -> QMolecule: @staticmethod def dump(q_mol: QMolecule, outpath: str, orbsym: Optional[List[int]] = None, - isym: Optional[int] = 1) -> None: + isym: int = 1) -> None: """Convenience method to produce an FCIDump output file. Args: diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index aed4f5161c..2d48e7c2e2 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -14,7 +14,7 @@ """FCIDump parser.""" -from typing import Any, Dict, List, Optional, Set +from typing import Any, Dict, List, Set import itertools import re import numpy as np @@ -196,7 +196,7 @@ def parse(fcidump: str) -> Dict[str, Any]: def _permute_1e_ints(hij: List[float], elements: Set[int], norb: int, - beta: Optional[bool] = False) -> None: + beta: bool = False) -> None: for elem in elements.copy(): shifted = tuple(e-(beta * norb) for e in elem) hij[shifted] = hij[shifted[::-1]] @@ -204,7 +204,7 @@ def _permute_1e_ints(hij: List[float], elements: Set[int], norb: int, def _permute_2e_ints(hijkl: List[float], elements: Set[int], norb: int, - beta: Optional[bool] = False) -> None: + beta: bool = False) -> None: # pylint: disable=wrong-spelling-in-comment for elem in elements.copy(): shifted = tuple(e-((e >= norb) * norb) for e in elem) From 11968d08e599eae4f4d467e345751f250a9a5443 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 19 Mar 2020 15:00:02 +0100 Subject: [PATCH 27/28] Raise error if required field is missing in FCIDump --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 6 +++--- qiskit/chemistry/drivers/fcidumpd/parser.py | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index fd3bb80e61..b7fcb3a0a6 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -70,11 +70,11 @@ def run(self) -> QMolecule: q_mol.hf_energy = float('NaN') # ensures QMolecule.log() works q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) - q_mol.num_orbitals = fcidump_data.get('NORB', float('NaN')) + q_mol.num_orbitals = fcidump_data.get('NORB') q_mol.multiplicity = fcidump_data.get('MS2', 0) + 1 q_mol.charge = 0 # ensures QMolecule.log() works - q_mol.num_beta = (fcidump_data.get('NELEC', float('NaN')) - (q_mol.multiplicity - 1)) // 2 - q_mol.num_alpha = fcidump_data.get('NELEC', float('NaN')) - q_mol.num_beta + q_mol.num_beta = (fcidump_data.get('NELEC') - (q_mol.multiplicity - 1)) // 2 + q_mol.num_alpha = fcidump_data.get('NELEC') - q_mol.num_beta if self.atoms is not None: q_mol.num_atoms = len(self.atoms) q_mol.atom_symbol = self.atoms diff --git a/qiskit/chemistry/drivers/fcidumpd/parser.py b/qiskit/chemistry/drivers/fcidumpd/parser.py index 2d48e7c2e2..df7f6d73a7 100644 --- a/qiskit/chemistry/drivers/fcidumpd/parser.py +++ b/qiskit/chemistry/drivers/fcidumpd/parser.py @@ -29,8 +29,9 @@ def parse(fcidump: str) -> Dict[str, Any]: Args: fcidump: Path to the FCIDump file. Raises: - QiskitChemistryError: If the input file cannot be found, if wrong integral indices are - encountered, or if the alpha/beta or beta/alpha 2-electron integrals are mixed. + QiskitChemistryError: If the input file cannot be found, if a required field in the FCIDump + file is missing, if wrong integral indices are encountered, or if the alpha/beta or + beta/alpha 2-electron integrals are mixed. Returns: A dictionary storing the parsed data. """ @@ -55,9 +56,13 @@ def parse(fcidump: str) -> Dict[str, Any]: pattern = r'.*?([-+]?\d*\.\d+|[-+]?\d+),' # we parse the values in the order in which they are listed in Knowles1989 _norb = re.search('NORB'+pattern, metadata) + if _norb is None: + raise QiskitChemistryError("The required NORB entry of the FCIDump format is missing!") norb = int(_norb.groups()[0]) output['NORB'] = norb _nelec = re.search('NELEC'+pattern, metadata) + if _nelec is None: + raise QiskitChemistryError("The required NELEC entry of the FCIDump format is missing!") output['NELEC'] = int(_nelec.groups()[0]) # the rest of these values may occur and are set to their defaults otherwise _ms2 = re.search('MS2'+pattern, metadata) From b50036a47170b70cf55bb8d351048d480e36bd09 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 19 Mar 2020 15:00:50 +0100 Subject: [PATCH 28/28] Revert to None as default and check in QMolecule.log() --- qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py | 3 +-- qiskit/chemistry/qmolecule.py | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py index b7fcb3a0a6..f74a2af4ce 100644 --- a/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py +++ b/qiskit/chemistry/drivers/fcidumpd/fcidumpdriver.py @@ -68,8 +68,7 @@ def run(self) -> QMolecule: q_mol = QMolecule() - q_mol.hf_energy = float('NaN') # ensures QMolecule.log() works - q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', float('NaN')) + q_mol.nuclear_repulsion_energy = fcidump_data.get('ecore', None) q_mol.num_orbitals = fcidump_data.get('NORB') q_mol.multiplicity = fcidump_data.get('MS2', 0) + 1 q_mol.charge = 0 # ensures QMolecule.log() works diff --git a/qiskit/chemistry/qmolecule.py b/qiskit/chemistry/qmolecule.py index b46f654d3b..fe505d8398 100644 --- a/qiskit/chemistry/qmolecule.py +++ b/qiskit/chemistry/qmolecule.py @@ -559,8 +559,9 @@ def log(self): logger.info("Computed Hartree-Fock energy: %s", self.hf_energy) logger.info("Nuclear repulsion energy: %s", self.nuclear_repulsion_energy) - logger.info("One and two electron Hartree-Fock energy: %s", - self.hf_energy - self.nuclear_repulsion_energy) + if None not in (self.hf_energy, self.nuclear_repulsion_energy): + logger.info("One and two electron Hartree-Fock energy: %s", + self.hf_energy - self.nuclear_repulsion_energy) logger.info("Number of orbitals is %s", self.num_orbitals) logger.info("%s alpha and %s beta electrons", self.num_alpha, self.num_beta) logger.info("Molecule comprises %s atoms and in xyz format is ::", self.num_atoms)