Source code for deepqmc.geom.coordinate_transform
from collections.abc import Callable, Sequence
from typing import Protocol, TypeVar
import jax
import jax.numpy as jnp
from .zmatrix import ConcreteZMatrixTemplate
T = TypeVar('T', covariant=True) # noqa: N808
C = TypeVar('C')
[docs]
class CoordinateTransform(Protocol):
r"""Protocol for coordinate transformations.
A :class:`CoordinateTransform` maps Cartesian nuclear coordinates to another
(possibly lower-dimensional) coordinate representation, e.g. a set of internal
coordinates. It is used e.g. by
:class:`~deepqmc.sampling.nuclei_samplers.ConstraintNucleiSampler` to perform
sampling steps in a coordinate system other than Cartesian.
"""
[docs]
def from_cartesian(self, coords: jax.Array) -> jax.Array:
r"""Transform Cartesian nuclear coordinates to this representation.
Args:
coords (~jax.Array): Cartesian nuclear coordinates, of shape
``(n_nuc, 3)``.
Returns:
~jax.Array: the coordinates in the target representation, of shape
``(len(self),)``.
"""
...
def __len__(self) -> int:
r"""Return the number of coordinates produced by this transform."""
...
[docs]
class InvertibleCoordinateTransform(CoordinateTransform, Protocol):
r"""Protocol for invertible coordinate transformations.
In addition to :meth:`~CoordinateTransform.from_cartesian`, an
:class:`InvertibleCoordinateTransform` can also map coordinates back to
Cartesian space. This is required to apply e.g. sampled noise in the
transformed coordinate system, as done by
:class:`~deepqmc.sampling.nuclei_samplers.ConstraintNucleiSampler`.
"""
[docs]
def to_cartesian(self, coords: jax.Array) -> jax.Array:
r"""Transform coordinates in this representation back to Cartesian space.
Args:
coords (~jax.Array): coordinates in this transform's representation,
of shape ``(len(self),)``.
Returns:
~jax.Array: the corresponding Cartesian nuclear coordinates, of shape
``(n_nuc, 3)``.
"""
...
[docs]
class ZMatrixCoordinateTransform(InvertibleCoordinateTransform):
r"""Invertible coordinate transform between Cartesian coordinates and a Z matrix.
The transformed coordinates are the flattened bond lengths, angles and dihedral
angles described by ``zmatrix_template``. This transform is typically passed as
the ``coordinate_transform`` of a
:class:`~deepqmc.sampling.nuclei_samplers.ConstraintNucleiSampler`, to sample
nuclear displacements in terms of bond lengths, angles and dihedrals rather than
Cartesian coordinates.
Args:
zmatrix_template (~deepqmc.geom.zmatrix.ConcreteZMatrixTemplate): the Z
matrix template defining which atoms are connected by the bonds,
angles and dihedrals making up the Z matrix.
"""
zmatrix_template: ConcreteZMatrixTemplate
def __init__(self, zmatrix_template: ConcreteZMatrixTemplate):
self.zmatrix_template = zmatrix_template
def __len__(self) -> int:
r"""Return the number of coordinates, not the number of Z matrix lines."""
return sum(min(i, 3) for i in range(len(self.zmatrix_template)))
def to_cartesian(self, coords: jax.Array) -> jax.Array:
coords = self.zmatrix_template.clean_values(coords)
return self.zmatrix_template.concretize(coords).to_cartesian()
def from_cartesian(self, coords: jax.Array) -> jax.Array:
return self.zmatrix_template.concretize_from_cartesian(coords).value
[docs]
class CartesianCoordinateTransform(InvertibleCoordinateTransform):
r"""Identity coordinate transform operating on flattened Cartesian coordinates.
This is the default ``coordinate_transform`` used by
:class:`~deepqmc.sampling.nuclei_samplers.ConstraintNucleiSampler` when none is
specified, i.e. noise is added directly to the Cartesian nuclear coordinates.
Args:
n_atoms (int): the number of atoms (nuclei) whose coordinates are
transformed.
"""
def __init__(self, n_atoms: int):
self.n_atoms = n_atoms
def from_cartesian(self, coords: jax.Array) -> jax.Array:
return coords.reshape(self.n_atoms * 3)
def to_cartesian(self, coords: jax.Array) -> jax.Array:
return coords.reshape(self.n_atoms, 3)
def __len__(self) -> int:
return self.n_atoms * 3
[docs]
class RedundantInternalCoordinateTransform(CoordinateTransform):
r"""Coordinate transform to a (possibly redundant) set of internal coordinates.
This transform is not invertible: it merely evaluates a user-specified list of
internal-coordinate functions, e.g. :func:`~deepqmc.geom.distance`,
:func:`~deepqmc.geom.angle` or :func:`~deepqmc.geom.dihedral` partially applied
to fixed atom indices, on the Cartesian nuclear coordinates. Unlike a Z matrix,
the resulting coordinates need not have a one-to-one correspondence with the
Cartesian coordinates and may be redundant.
Args:
internal_coordinates
(~collections.abc.Sequence[~collections.abc.Callable[..., ~jax.Array]]):
a sequence of functions, each called with the Cartesian nuclear
coordinates as the keyword argument ``coords`` and returning a single
scalar internal coordinate.
"""
def __init__(self, internal_coordinates: Sequence[Callable[..., jax.Array]]):
self.internal_coordinates = internal_coordinates
def from_cartesian(self, coords: jax.Array) -> jax.Array:
return jnp.array(
[
internal_coordinate(coords=coords)
for internal_coordinate in self.internal_coordinates
]
)
def __len__(self) -> int:
return len(self.internal_coordinates)
[docs]
class GeneralCoordinateTransform(CoordinateTransform):
r"""Coordinate transform wrapping an arbitrary user-defined transform function.
Args:
n_coordinate (int): the number of coordinates returned by
``coordinate_transform_fn``.
coordinate_transform_fn (~collections.abc.Callable[[~jax.Array], ~jax.Array]):
a function mapping Cartesian nuclear coordinates to a coordinate array
of length ``n_coordinate``.
"""
def __init__(
self,
n_coordinate: int,
coordinate_transform_fn: Callable[[jax.Array], jax.Array],
):
self.len = n_coordinate
self.transform = coordinate_transform_fn
def from_cartesian(self, coords: jax.Array) -> jax.Array:
return self.transform(coords)
def __len__(self) -> int:
return self.len
[docs]
class SubsetCoordinateTransform(CoordinateTransform):
r"""Coordinate transform selecting a subset of the flattened Cartesian coordinates.
Args:
coordinate_idxs (~collections.abc.Sequence[int]): indices into the
flattened (``n_nuc * 3``) Cartesian coordinate array to select.
"""
def __init__(self, coordinate_idxs):
self.coordinate_idxs = coordinate_idxs
def from_cartesian(self, coords: jax.Array) -> jax.Array:
return coords.flatten()[self.coordinate_idxs]
def __len__(self) -> int:
return len(self.coordinate_idxs)