Skip to content

Commit

Permalink
WIP: HDF5 integration into vibrational properties
Browse files Browse the repository at this point in the history
  • Loading branch information
mrossinek committed Jan 7, 2022
1 parent dccbf68 commit 1c89a2b
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 10 deletions.
4 changes: 2 additions & 2 deletions qiskit_nature/properties/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ def import_and_build_from_hdf5(h5py_group: h5py.Group):
yield instance

@classmethod
@abstractmethod
def from_hdf5(cls, h5py_group: h5py.Group):
"""TODO."""
# TODO: un-comment once all sub-classes actually implement this
# raise NotImplementedError()
raise NotImplementedError()


class PseudoProperty(Property, ABC):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def to_hdf5(self, parent: h5py.Group):
"""TODO."""
super().to_hdf5(parent)
group = parent.require_group(self.name)
print(self.name)

group.attrs["basis"] = self._basis.name
group.attrs["threshold"] = self._threshold
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
from abc import ABC, abstractmethod
from typing import List, Optional

import h5py

class VibrationalBasis(ABC):
from qiskit_nature.properties import PseudoProperty


class VibrationalBasis(PseudoProperty, ABC):
"""The Vibrational basis base class.
This class defines the interface which any vibrational basis must implement. A basis must be
Expand All @@ -34,6 +38,7 @@ def __init__(
num_modals_per_mode: the number of modals to be used for each mode.
threshold: the threshold value below which an integral coefficient gets neglected.
"""
super().__init__(self.__class__.__name__)
self._num_modals_per_mode = num_modals_per_mode
self._threshold = threshold

Expand All @@ -47,6 +52,19 @@ def __str__(self) -> str:
string += [f"\tModals: {self._num_modals_per_mode}"]
return "\n".join(string)

def to_hdf5(self, parent: h5py.Group):
"""TODO."""
super().to_hdf5(parent)
group = parent.require_group(self.name)

group.attrs["threshold"] = self._threshold
group.create_dataset("num_modals_per_mode", data=self.num_modals_per_mode)

@classmethod
def from_hdf5(cls, h5py_group: h5py.Group) -> "VibrationalBasis":
"""TODO."""
return cls(h5py_group["num_modals_per_mode"][...], h5py_group.attrs.get("threshold", None))

@abstractmethod
def eval_integral(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
from itertools import chain, cycle, permutations, product, tee
from typing import Dict, List, Optional, Tuple

import h5py
import numpy as np

from qiskit_nature import QiskitNatureError
from qiskit_nature.operators.second_quantization import VibrationalOp
from qiskit_nature.properties.property import PseudoProperty

from ..bases import VibrationalBasis


class VibrationalIntegrals(ABC):
class VibrationalIntegrals(PseudoProperty, ABC):
"""A container for arbitrary ``n-body`` vibrational integrals.
When these integrals are printed the output will be truncated based on the
Expand All @@ -52,10 +54,8 @@ def __init__(
Raises:
ValueError: if the number of body terms is less than 1.
"""
if num_body_terms < 1:
raise ValueError(
f"The number of body terms must be greater than 0, not '{num_body_terms}'."
)
super().__init__(f"{num_body_terms}Body{self.__class__.__name__}")
self._validate_num_body_terms(num_body_terms)
self._num_body_terms = num_body_terms
self._integrals = integrals
self._basis: VibrationalBasis = None
Expand Down Expand Up @@ -95,6 +95,32 @@ def __str__(self) -> str:
count += 1
return "\n".join(string)

def to_hdf5(self, parent: h5py.Group):
"""TODO."""
super().to_hdf5(parent)
group = parent.require_group(self.name)

group.attrs["num_body_terms"] = self._num_body_terms

dtype = h5py.vlen_dtype(np.dtype("int32"))
integrals_dset = group.create_dataset("integrals", (len(self.integrals),), dtype=dtype)
coeffs_dset = group.create_dataset("coefficients", (len(self.integrals),), dtype=float)

for idx, ints in enumerate(self.integrals):
coeffs_dset[idx] = ints[0]
integrals_dset[idx] = list(ints[1])

@classmethod
def from_hdf5(cls, h5py_group: h5py.Group) -> "VibrationalIntegrals":
"""TODO."""
integrals = []
for coeff, ints in zip(h5py_group["coefficients"][...], h5py_group["integrals"][...]):
integrals.append((coeff, tuple(ints)))

ret = cls(h5py_group.attrs["num_body_terms"], integrals)

return ret

@staticmethod
def set_truncation(max_num_entries: int) -> None:
"""Set the maximum number of integral values to display before truncation.
Expand All @@ -107,6 +133,14 @@ def set_truncation(max_num_entries: int) -> None:
"""
VibrationalIntegrals._truncate = max_num_entries

@staticmethod
def _validate_num_body_terms(num_body_terms: int) -> None:
"""Validates the number of body terms."""
if num_body_terms < 1:
raise ValueError(
f"The number of body terms must be greater than 0, not '{num_body_terms}'."
)

def to_basis(self) -> np.ndarray:
"""Maps the integrals into a basis which permits mapping into second-quantization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from typing import List, Optional, Tuple

import h5py

from qiskit_nature import ListOrDictType, settings
from qiskit_nature.drivers import WatsonHamiltonian
from qiskit_nature.operators.second_quantization import VibrationalOp
Expand All @@ -40,6 +42,11 @@ def __init__(
"""
super().__init__(self.__class__.__name__, basis)

@classmethod
def from_hdf5(cls, h5py_group: h5py.Group) -> "OccupiedModals":
"""TODO."""
return cls()

@classmethod
def from_legacy_driver_result(cls, result: LegacyDriverResult) -> "OccupiedModals":
"""Construct an OccupiedModals instance from a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,20 @@ class GroupedVibrationalProperty(GroupedSecondQuantizedProperty[T], VibrationalP
"""A GroupedProperty subtype containing purely vibrational properties."""

@property
def basis(self) -> VibrationalBasis:
def basis(self) -> Optional[VibrationalBasis]:
"""Returns the basis."""
return list(self._properties.values())[0].basis
for prop in self._properties.values():
if isinstance(prop, PseudoProperty):
continue
return prop.basis
return None

@basis.setter
def basis(self, basis: VibrationalBasis) -> None:
"""Sets the basis."""
for prop in self._properties.values():
if isinstance(prop, PseudoProperty):
continue
prop.basis = basis

def add_property(self, prop: Optional[T]) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from typing import cast, Dict, List, Optional, Tuple

import h5py

from qiskit_nature import ListOrDictType, settings
from qiskit_nature.drivers import WatsonHamiltonian
from qiskit_nature.operators.second_quantization import VibrationalOp
Expand Down Expand Up @@ -72,6 +74,27 @@ def __str__(self) -> str:
string += [f"\t{ints}"]
return "\n".join(string)

def to_hdf5(self, parent: h5py.Group):
"""TODO."""
super().to_hdf5(parent)
group = parent.require_group(self.name)

ints_group = group.create_group("vibrational_integrals")
for integral in self._vibrational_integrals.values():
integral.to_hdf5(ints_group)

if self.truncation_order:
group.attrs["truncation_order"] = self.truncation_order

@classmethod
def from_hdf5(cls, h5py_group: h5py.Group) -> "VibrationalEnergy":
"""TODO."""
ints = []
for int_group in h5py_group["vibrational_integrals"].values():
ints.append(VibrationalIntegrals.from_hdf5(int_group))

return cls(ints, h5py_group.attrs.get("truncation_order", None))

@classmethod
def from_legacy_driver_result(cls, result: LegacyDriverResult) -> "VibrationalEnergy":
"""Construct a VibrationalEnergy instance from a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@

from typing import cast

import h5py

from qiskit_nature import ListOrDictType, settings
from qiskit_nature.drivers import WatsonHamiltonian
from qiskit_nature.operators.second_quantization import VibrationalOp

from ..second_quantized_property import LegacyDriverResult
from .bases import VibrationalBasis
from .occupied_modals import OccupiedModals
from .vibrational_energy import VibrationalEnergy
from .types import GroupedVibrationalProperty
Expand Down Expand Up @@ -48,6 +51,37 @@ def num_modes(self, num_modes: int) -> None:
"""Sets the number of modes."""
self._num_modes = num_modes

def to_hdf5(self, parent: h5py.Group):
"""TODO."""
super().to_hdf5(parent)
group = parent.require_group(self.name)

group.attrs["num_modes"] = self.num_modes

if self.basis is not None:
self.basis.to_hdf5(group)

@classmethod
def from_hdf5(cls, h5py_group: h5py.Group) -> "VibrationalStructureDriverResult":
"""TODO."""
grouped_property = super().from_hdf5(h5py_group)
grouped_property.iterate_pseudo_properties = True

basis: VibrationalBasis = None
ret = cls()
for prop in grouped_property:
if isinstance(prop, VibrationalBasis):
basis = prop
continue
ret.add_property(prop)

ret.num_modes = h5py_group.attrs["num_modes"]

if basis is not None:
ret.basis = basis

return ret

@classmethod
def from_legacy_driver_result(
cls, result: LegacyDriverResult
Expand Down

0 comments on commit 1c89a2b

Please sign in to comment.