Skip to content

Commit

Permalink
Refactor FCIDump integration into qiskit_nature.second_q.formats (#799)
Browse files Browse the repository at this point in the history
* Refactor FCIDump integration into qiskit_nature.second_q.formats

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* update translator

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update test/second_q/formats/fcidump/test_fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update test/second_q/formats/fcidump/test_fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update test/second_q/formats/fcidump/test_methods_fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update test/second_q/formats/fcidump/test_methods_fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* enable unit test

* Apply suggestions from code review

* fix energy name

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/formats/fcidump/fcidump.py

Co-authored-by: Max Rossmannek <[email protected]>

* change _parse to return FCIDump

Co-authored-by: Max Rossmannek <[email protected]>
  • Loading branch information
manoelmarques and mrossinek authored Sep 14, 2022
1 parent d1b5e07 commit aaa41f7
Show file tree
Hide file tree
Showing 17 changed files with 1,863 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ test/**/*.log
test/**/*.pdf
test/**/*.prof
test/**/*.npz
#Allow
!test/second_q/formats/fcidump/*.npz

# Translations
*.mo
Expand Down
7 changes: 7 additions & 0 deletions docs/apidocs/qiskit_nature.second_q.formats.fcidump.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FCIDump
==============================

.. automodule:: qiskit_nature.second_q.formats.fcidump
:no-members:
:no-inherited-members:
:no-special-members:
1 change: 1 addition & 0 deletions qiskit_nature/second_q/formats/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
.. autosummary::
:toctree:
fcidump
qcschema
"""
32 changes: 32 additions & 0 deletions qiskit_nature/second_q/formats/fcidump/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2022.
#
# 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.

"""
The FCIDump (:mod:`qiskit_nature.second_q.formats.fcidump`)
=============================================================
Contains tools to parse and dump FCIDump files.
.. currentmodule:: qiskit_nature.second_q.formats.fcidump
.. autosummary::
:toctree: ../stubs/
:nosignatures:
FCIDump
"""

from .fcidump import FCIDump

__all__ = ["FCIDump"]
90 changes: 90 additions & 0 deletions qiskit_nature/second_q/formats/fcidump/dumper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2022.
#
# 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 typing import List, Union, TextIO, Tuple, Iterator, Any
import itertools
import numpy as np


def _dump_1e_ints(
hij: np.ndarray,
mos: Union[range, List[int]],
outfile: TextIO,
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: np.ndarray, mos: Union[range, List[int]], outfile: TextIO, beta: int = 0
) -> None:
idx_offsets = [1, 1]
for b in range(beta):
idx_offsets[1 - b] += len(mos)
hijkl_elements = set()
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:]))
permutations: Iterator[Any]
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: TextIO, value: float, indices: Tuple):
outfile.write(f"{value:23.16E}{indices[0]:4d}{indices[1]:4d}{indices[2]:4d}{indices[3]:4d}\n")
121 changes: 121 additions & 0 deletions qiskit_nature/second_q/formats/fcidump/fcidump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2022.
#
# 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"""

from __future__ import annotations

from typing import List
from dataclasses import dataclass
from pathlib import Path
import numpy as np

from qiskit_nature import QiskitNatureError


from .dumper import _dump_1e_ints, _dump_2e_ints, _write_to_outfile


@dataclass
class FCIDump:
"""
Qiskit Nature dataclass for representing the FCIDump format.
The FCIDump format is partially defined in Knowles1989.
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.
"""

hij: np.ndarray
"""The alpha 1-electron integrals."""
hijkl: np.ndarray
"""The alpha/alpha 2-electron integrals."""
hij_b: np.ndarray | None
"""The beta 1-electron integrals."""
hijkl_ba: np.ndarray | None
"""The beta/alpha 2-electron integrals."""
hijkl_bb: np.ndarray | None
"""The beta/beta 2-electron integrals."""
multiplicity: int
"""The multiplicity."""
num_electrons: int
"""The number of electrons."""
num_orbitals: int
"""The number of orbitals."""
constant_energy: float | None
"""The constant energy comprising (for example) the nuclear repulsion energy
and inactive energies."""
orbsym: List[str] | None
"""A list of spatial symmetries of the orbitals."""
isym: int
"""The spatial symmetry of the wave function."""

@classmethod
def from_file(cls, fcidump: str | Path) -> FCIDump:
"""Constructs an FCIDump object from a file."""
# pylint: disable=cyclic-import
from .parser import _parse

return _parse(fcidump if isinstance(fcidump, Path) else Path(fcidump))

def to_file(self, fcidump: str | Path) -> None:
"""Dumps an FCIDump object to a file.
Args:
fcidump: Path to the output file.
Raises:
QiskitNatureError: invalid number of orbitals.
QiskitNatureError: not all beta-spin related matrices are either None or not None.
"""
outpath = fcidump if isinstance(fcidump, Path) else Path(fcidump)
# either all beta variables are None or all of them are not
if not all(h is None for h in [self.hij_b, self.hijkl_ba, self.hijkl_bb]) and not all(
h is not None for h in [self.hij_b, self.hijkl_ba, self.hijkl_bb]
):
raise QiskitNatureError("Invalid beta variables.")
norb = self.num_orbitals
nelec = self.num_electrons
einact = self.constant_energy
ms2 = self.multiplicity - 1
if norb != self.hij.shape[0] or norb != self.hijkl.shape[0]:
raise QiskitNatureError(
f"Invalid number of orbitals {norb} {self.hij.shape[0]} {self.hijkl.shape[0]}"
)

mos = range(norb)
with outpath.open("w", encoding="utf8") as outfile:
# print header
outfile.write(f"&FCI NORB={norb:4d},NELEC={nelec:4d},MS2={ms2:4d}\n")
if self.orbsym is None:
outfile.write(" ORBSYM=" + "1," * norb + "\n")
else:
if len(self.orbsym) != norb:
raise QiskitNatureError(f"Invalid number of orbitals {norb} {len(self.orbsym)}")
outfile.write(" ORBSYM=" + ",".join(self.orbsym) + "\n")
outfile.write(f" ISYM={self.isym:d},\n&END\n")
# append 2e integrals
_dump_2e_ints(self.hijkl, mos, outfile)
if self.hijkl_ba is not None:
_dump_2e_ints(self.hijkl_ba.transpose(), mos, outfile)
if self.hijkl_bb is not None:
_dump_2e_ints(self.hijkl_bb, mos, outfile)
# append 1e integrals
_dump_1e_ints(self.hij, mos, outfile)
if self.hij_b is not None:
_dump_1e_ints(self.hij_b, mos, outfile)
# TODO append MO energies (last three indices are 0)
# append inactive energy
_write_to_outfile(outfile, einact, (0, 0, 0, 0))
Loading

0 comments on commit aaa41f7

Please sign in to comment.