Skip to content

Commit

Permalink
Corrected the examples (#79)
Browse files Browse the repository at this point in the history
* renamed tempoSOAP to timeSOAP

* added getTimeSOAPSimple

* refined tests and re-executed the timeSOAP jupyter

* updated the notebooks with the examples
  • Loading branch information
Iximiel authored Apr 11, 2023
1 parent be94bcd commit 62837d7
Show file tree
Hide file tree
Showing 9 changed files with 933 additions and 771 deletions.
5 changes: 3 additions & 2 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ Until we get to a release version you may encounter different broken interface p
- `SOAPify.HDF5er.isTrajectoryGroup` now returns `False` with Datasets
- Added some simple cli interfaces
- Added examples for LENS and tSOAP and for generating hdf5 files
- Added getSOAPSettings for getting the SOAP oragnization fro a dataset
- now `createUniverseFromSlice()` returns also types
- Added `getSOAPSettings()` for getting the SOAP organization from a hdf5 dataset
- Added `getTimeSOAPSimple()` to give the user a shortcut to solve memory problem with large SOAP datasets
- Now `createUniverseFromSlice()` returns also types

## Changes since v0.0.6

Expand Down
75 changes: 39 additions & 36 deletions Examples/LENS.ipynb

Large diffs are not rendered by default.

699 changes: 0 additions & 699 deletions Examples/tempoSOAP.ipynb

This file was deleted.

726 changes: 726 additions & 0 deletions Examples/timeSOAP.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/source/Tutorials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ Tutorials and Examples
.. nbgallery::
FileHandling
TimeAnalysis
tempoSOAP
timeSOAP
LENS
1 change: 0 additions & 1 deletion docs/source/tempoSOAP.ipynb

This file was deleted.

1 change: 1 addition & 0 deletions docs/source/timeSOAP.ipynb
108 changes: 92 additions & 16 deletions src/SOAPify/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

import numpy
from numpy import ndarray
from .distances import simpleSOAPdistance
from MDAnalysis import Universe, AtomGroup
from MDAnalysis.lib.NeighborSearch import AtomNeighborSearch
import h5py

from .distances import simpleSOAPdistance
from .utils import getSOAPSettings, normalizeArray, fillSOAPVectorFromdscribe


def tempoSOAP(
def timeSOAP(
SOAPTrajectory: ndarray,
window: int = 1,
stride: int = None,
backward: bool = False,
returnDiff: bool = True,
distanceFunction: callable = simpleSOAPdistance,
) -> "tuple[ndarray, ndarray]":
"""performs the 'tempoSOAP' analysis on the given SOAP trajectory
"""performs the 'timeSOAP' analysis on the given SOAP trajectory
* Original author: Cristina Caruso
* Mantainer: Daniele Rapetti
Expand All @@ -27,14 +31,18 @@ def tempoSOAP(
stride (int):
the stride in frames between each state confrontation. **NOT IN USE**.
Defaults to None.
deltaT (int): number of frames to skip
backward (bool):
If true the soap distance is referred to the previous frame.
**NOT IN USE**. Defaulst to True.
returnDiff (bool):
If true returns also the first derivative of timeSOAP. Defaults to True.
distanceFunction (callable, optional):
the function that define the distance. Defaults to :func:`SOAPify.distances.simpleSOAPdistance`.
Returns:
tuple[numpy.ndarray,numpy.ndarray]:
- **timedSOAP** the tempoSOAP values, shape(frames-1,natoms)
- **deltaTimedSOAP** the derivatives of tempoSOAP, shape(natoms, frames-2)
- **timedSOAP** the timeSOAP values, shape(frames-1,natoms)
- **deltaTimedSOAP** the derivatives of timeSOAP, shape(natoms, frames-2)
"""
if stride is None:
stride = window
Expand All @@ -55,18 +63,20 @@ def tempoSOAP(
# )
# print(SOAPTrajectory.shape)

deltaTimedSOAP = numpy.diff(timedSOAP.T, axis=-1)
if returnDiff:
deltaTimedSOAP = numpy.diff(timedSOAP.T, axis=-1)
return timedSOAP, deltaTimedSOAP
return timedSOAP

return timedSOAP, deltaTimedSOAP


def tempoSOAPsimple(
def timeSOAPsimple(
SOAPTrajectory: ndarray,
window: int = 1,
stride: int = None,
backward: bool = False,
returnDiff: bool = True,
) -> "tuple[ndarray, ndarray]":
r"""performs the 'tempoSOAP' analysis on the given **normalized** SOAP trajectory
r"""performs the 'timeSOAP' analysis on the given **normalized** SOAP trajectory
this is optimized to use :func:`SOAPify.distances.simpleSOAPdistance`,
without calling it.
Expand Down Expand Up @@ -100,12 +110,16 @@ def tempoSOAPsimple(
stride (int):
the stride in frames between each state confrontation. **NOT IN USE**.
Defaults to None.
deltaT (int): number of frames to skip
backward (bool):
If true the soap distance is referred to the previous frame.
**NOT IN USE**. Defaulst to True.
returnDiff (bool):
If true returns also the first derivative of timeSOAP. Defaults to True.
Returns:
tuple[numpy.ndarray,numpy.ndarray]:
- **timedSOAP** the tempoSOAP values, shape(frames-1,natoms)
- **deltaTimedSOAP** the derivatives of tempoSOAP, shape(natoms, frames-2)
- **timedSOAP** the timeSOAP values, shape(frames-1,natoms)
- **deltaTimedSOAP** the derivatives of timeSOAP, shape(natoms, frames-2)
"""
if stride is None:
stride = window
Expand All @@ -122,9 +136,71 @@ def tempoSOAPsimple(
timedSOAP[frame - window] = numpy.linalg.norm(actual - prev, axis=1)
prev = actual

deltaTimedSOAP = numpy.diff(timedSOAP.T, axis=-1)
if returnDiff:
deltaTimedSOAP = numpy.diff(timedSOAP.T, axis=-1)
return timedSOAP, deltaTimedSOAP
return timedSOAP


def getTimeSOAPSimple(
soapDataset: h5py.Dataset,
window: int = 1,
stride: int = None,
backward: bool = False,
):
"""Shortcut to extract the timeSOAP from large datasets.
This function is the equivalent to:
- loading a chunk of the trajectory from a h5py.Dataset with a SOAP fingerprints trajectory
- filling the vector with :func:`SOAPify.utils.fillSOAPVectorFromdscribe`
- normalizing it with :func:`SOAPify.utils.normalizeArray`
- calculating the timeSOAP with :func:`timeSOAPsimple`
and then returning timeSOAP and the derivative
Args:
soapDataset (h5py.Dataset):
the dataset with the SOAP fingerprints
window (int):
the dimension of the windows between each state confrontations.
See :func:`timeSOAPsimple`
Defaults to 1.
stride (int):
the stride in frames between each state confrontation.
See :func:`timeSOAPsimple`
Defaults to None.
backward (bool):
If true the soap distance is referred to the previous frame.
See :func:`timeSOAPsimple` . Defaulst to True.
Returns:
tuple[numpy.ndarray,numpy.ndarray]:
- **timedSOAP** the timeSOAP values, shape(frames-1,natoms)
- **deltaTimedSOAP** the derivatives of timeSOAP, shape(natoms, frames-2)
"""
fillSettings = getSOAPSettings(soapDataset)
timedSOAP = numpy.zeros((soapDataset.shape[0] - window, soapDataset.shape[1]))
# TODO: add a check to the window

slide = 0
# this looks a lot convoluted, but it is way faster than working one atom
# at a time
for c in soapDataset.iter_chunks():
theSlice = slice(c[0].start - slide, c[0].stop, c[0].step)
outSlice = slice(c[0].start - slide, c[0].stop - 1, c[0].step)
timedSOAP[outSlice] = timeSOAPsimple(
normalizeArray(
fillSOAPVectorFromdscribe(soapDataset[theSlice], **fillSettings)
),
window=window,
stride=stride,
backward=backward,
returnDiff=False,
)
slide = 1

return timedSOAP, deltaTimedSOAP
return timedSOAP, numpy.diff(timedSOAP.T, axis=-1)


def listNeighboursAlongTrajectory(
Expand Down
87 changes: 71 additions & 16 deletions tests/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
import MDAnalysis
from .testSupport import is_sorted, fewFrameUniverse

# NB: the trueFalse fixture for the backward settings is here to anticipate the
# implementation of that feature

def test_tempoSOAP(referencesTrajectorySOAP, inputWindows):
window = inputWindows

@pytest.mark.parametrize("window", [1, 2, 5, 7, 10])
@pytest.mark.parametrize("backward", [True, False])
def test_timeSOAP(referencesTrajectorySOAP, window, backward):
stride = window
confFile, groupName = referencesTrajectorySOAP

Expand All @@ -32,9 +36,7 @@ def isInvalidCombination(dataLen, stride, window):

if isInvalidCombination(fillArgs["soapFromdscribe"].shape[0], stride, window):
with pytest.raises(ValueError) as excinfo:
analysis.tempoSOAP(
fillArgs["soapFromdscribe"], stride=stride, window=window
)
analysis.timeSOAP(fillArgs["soapFromdscribe"], stride=stride, window=window)
if stride is not None and window < stride:
assert "window must be bigger" in str(excinfo.value)
pytest.skip("Exception thrown correctly")
Expand All @@ -58,6 +60,9 @@ def isInvalidCombination(dataLen, stride, window):
x = SOAPTraj[frame, molecule, :]
y = SOAPTraj[frame - window, molecule, :]
distance = SOAPify.simpleSOAPdistance(x, y)
# TODO:
# if backward:
# else:
expectedTimedSOAP[
frame - window, molecule
] = distance # fill the matrix (each molecule for each frame)
Expand All @@ -67,8 +72,8 @@ def isInvalidCombination(dataLen, stride, window):
derivative = numpy.diff(expectedTimedSOAP[:, molecule])
expectedDeltaTimedSOAP.append(derivative)

timedSOAP, deltaTimedSOAP = analysis.tempoSOAP(
SOAPTraj, stride=stride, window=window
timedSOAP, deltaTimedSOAP = analysis.timeSOAP(
SOAPTraj, stride=stride, window=window, backward=backward
)
# print(timedSOAP, expectedTimedSOAP)
print(deltaTimedSOAP.shape, timedSOAP.shape)
Expand All @@ -81,8 +86,9 @@ def isInvalidCombination(dataLen, stride, window):
assert_array_almost_equal(deltaTimedSOAP, expectedDeltaTimedSOAP)


def test_tempoSOAPsimple(referencesTrajectorySOAP, inputWindows):
window = inputWindows
@pytest.mark.parametrize("window", [1, 2, 5, 7, 10])
@pytest.mark.parametrize("backward", [True, False])
def test_timeSOAPsimple(referencesTrajectorySOAP, window, backward):
stride = window
confFile, groupName = referencesTrajectorySOAP

Expand All @@ -104,9 +110,7 @@ def isInvalidCombination(dataLen, stride, window):

if isInvalidCombination(fillArgs["soapFromdscribe"].shape[0], stride, window):
with pytest.raises(ValueError) as excinfo:
analysis.tempoSOAP(
fillArgs["soapFromdscribe"], stride=stride, window=window
)
analysis.timeSOAP(fillArgs["soapFromdscribe"], stride=stride, window=window)
if stride is not None and window < stride:
assert "window must be bigger" in str(excinfo.value)
pytest.skip("Exception thrown correctly")
Expand All @@ -120,12 +124,63 @@ def isInvalidCombination(dataLen, stride, window):

SOAPTraj = SOAPify.normalizeArray(SOAPTraj)

expectedTimedSOAP, expectedDeltaTimedSOAP = analysis.tempoSOAP(
SOAPTraj, stride=stride, window=window
expectedTimedSOAP, expectedDeltaTimedSOAP = analysis.timeSOAP(
SOAPTraj, stride=stride, window=window, backward=backward
)
timedSOAP, deltaTimedSOAP = analysis.tempoSOAPsimple(
SOAPTraj, stride=stride, window=window
timedSOAP, deltaTimedSOAP = analysis.timeSOAPsimple(
SOAPTraj, stride=stride, window=window, backward=backward
)
print(timedSOAP, expectedTimedSOAP)
assert_array_almost_equal(timedSOAP, expectedTimedSOAP)

assert_array_almost_equal(deltaTimedSOAP, expectedDeltaTimedSOAP)


@pytest.mark.parametrize("window", [1, 2, 5, 7, 10])
@pytest.mark.parametrize("backward", [True, False])
def test_getTimeSOAPsimple(referencesTrajectorySOAP, window, backward):
stride = window
confFile, groupName = referencesTrajectorySOAP

with h5py.File(confFile, "r") as f:
t = f[f"/SOAP/{groupName}"]
fillArgs = {
"soapFromdscribe": t[:],
"lMax": t.attrs["l_max"],
"nMax": t.attrs["n_max"],
}
fillArgs["atomTypes"], fillArgs["atomicSlices"] = SOAPify.getSlicesFromAttrs(
t.attrs
)

def isInvalidCombination(dataLen, stride, window):
return (stride is not None and window < stride) or (
(stride is not None and stride >= dataLen) or window >= dataLen
)

if isInvalidCombination(fillArgs["soapFromdscribe"].shape[0], stride, window):
with pytest.raises(ValueError) as excinfo:
analysis.timeSOAP(fillArgs["soapFromdscribe"], stride=stride, window=window)
if stride is not None and window < stride:
assert "window must be bigger" in str(excinfo.value)
pytest.skip("Exception thrown correctly")
if (
stride is not None and stride >= fillArgs["soapFromdscribe"].shape[0]
) or window >= fillArgs["soapFromdscribe"].shape[0]:
assert "window must be smaller" in str(excinfo.value)
pytest.skip("Exception thrown correctly")

SOAPTraj = SOAPify.fillSOAPVectorFromdscribe(**fillArgs)

SOAPTraj = SOAPify.normalizeArray(SOAPTraj)

expectedTimedSOAP, expectedDeltaTimedSOAP = analysis.timeSOAPsimple(
SOAPTraj, stride=stride, window=window, backward=backward
)
with h5py.File(confFile, "r") as f:
timedSOAP, deltaTimedSOAP = analysis.getTimeSOAPSimple(
f[f"/SOAP/{groupName}"], stride=stride, window=window, backward=backward
)
print(timedSOAP, expectedTimedSOAP)
assert_array_almost_equal(timedSOAP, expectedTimedSOAP)

Expand Down

0 comments on commit 62837d7

Please sign in to comment.