Skip to content

Commit

Permalink
numpy2 compatibility issues fixed (#443)
Browse files Browse the repository at this point in the history
* Some formatting and function calls fixed
* Mdtraj removed from testing (does not support numpy2 as of yet)
* Warnings still to be cleared
  • Loading branch information
Marcello-Sega authored Dec 2, 2024
1 parent 546ad85 commit d612fb0
Show file tree
Hide file tree
Showing 20 changed files with 80 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
# The executor is the environment in which the steps below will be executed - below will use a python 3.8 container
# Change the version below to your required version of python
docker:
- image: cimg/python:3.9
- image: cimg/python:3.10
# Checkout the code as the first step. This is a dedicated CircleCI step.
# The python orb's install-packages step will install the dependencies from a Pipfile via Pipenv by default.
# Here we're making sure we use just use the system-wide pip. By default it uses the project root's requirements.txt.
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
include:
- python-version: "3.9"
numpy-version: "1.26.4"
- python-version: "3.10"
numpy-version: "2.1.3"

steps:
- uses: actions/checkout@v2
Expand All @@ -29,9 +29,9 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
python -m pip install mdtraj
#python -m pip install mdtraj # removed as long as mdtraj is not supporting numpy2
python -m pip install .
if [ -f requirements.txt ]; then pip install --upgrade -r requirements.txt; fi
if [ -f requirements.txt ]; then pip install --upgrade -r requirements.txt; fi
#- name: Lint with flake8
# run: |
# # stop the build if there are Python syntax errors or undefined names
Expand Down
8 changes: 5 additions & 3 deletions pytim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from . import observables, utilities, datafiles
from .version import __version__
import warnings
from .patches import patchNumpy, patchMDTRAJ_ReplacementTables
patchNumpy()
patchMDTRAJ_ReplacementTables()
try: # waiting for numpy2 support in mdtraj>=1.10.2
from .patches import patchMDTRAJ_ReplacementTables
patchMDTRAJ_ReplacementTables()
except:
pass

warnings.filterwarnings(
"ignore",
Expand Down
18 changes: 10 additions & 8 deletions pytim/examples/example_mdtraj.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
loaded with MDTraj (http://mdtraj.org/)
(see also the openmm interoperability)
"""
try:
import mdtraj
import pytim
from pytim.datafiles import WATER_GRO, WATER_XTC

import mdtraj
import pytim
from pytim.datafiles import WATER_GRO, WATER_XTC

t = mdtraj.load_xtc(WATER_XTC, top=WATER_GRO)
inter = pytim.ITIM(t)
for step in t[:]:
print("surface atoms: "+repr(inter.atoms.indices))
t = mdtraj.load_xtc(WATER_XTC, top=WATER_GRO)
inter = pytim.ITIM(t)
for step in t[:]:
print("surface atoms: "+repr(inter.atoms.indices))
except:
pass # for package testing, in case mdtraj is not available or has compatibility issues
3 changes: 3 additions & 0 deletions pytim/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,10 @@ def _():
>>> # mdtraj
>>> try:
... from packaging.version import Version
... import mdtraj
... if Version(mdtraj.__version__) < Version('1.10.2'): # numpy2 support
... pass
... try:
... import numpy as np
... import MDAnalysis as mda
Expand Down
13 changes: 8 additions & 5 deletions pytim/itim.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,14 @@ class ITIM(Interface):
>>> # pytim can be used also on top of mdtraj (MDAnalysis must be present,though)
>>> import mdtraj
>>> import pytim
>>> from pytim.datafiles import WATER_GRO, WATER_XTC
>>> t = mdtraj.load_xtc(WATER_XTC,top=WATER_GRO)
>>> inter = pytim.ITIM(t)
>>> try:
... import mdtraj
... import pytim
... from pytim.datafiles import WATER_GRO, WATER_XTC
... t = mdtraj.load_xtc(WATER_XTC,top=WATER_GRO)
... inter = pytim.ITIM(t)
... except (ModuleNotFoundError,ValueError): # ValueError to handle mdtraj not supporting numpy2
... pass
.. _MDAnalysis: http://www.mdanalysis.org/
Expand Down
4 changes: 2 additions & 2 deletions pytim/observables/basic_observables.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,12 @@ class Distance(Observable):
>>> u = mda.Universe(pytim.datafiles.WATER_GRO)
>>> d1 = pytim.observables.Distance().compute(u.atoms[:9],u.atoms[:9])
>>> d2 = pytim.observables.RelativePosition(spherical=True).compute(u.atoms[:9],u.atoms[:9])[:,0]
>>> np.all(np.isclose(d1,d2))
>>> all(np.isclose(d1,d2))
True
>>> d1 = pytim.observables.Distance('xy').compute(u.atoms[:9],u.atoms[:9])
>>> d2 = pytim.observables.RelativePosition('xy',spherical=True).compute(u.atoms[:9],u.atoms[:9])[:,0]
>>> np.all(np.isclose(d1,d2))
>>> all(np.isclose(d1,d2))
True
"""
Expand Down
10 changes: 5 additions & 5 deletions pytim/observables/contactangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,24 @@ class ContactAngle(object):
>>> for ts in u.trajectory[::]:
... CA.sample()
>>> # Instantaneous contact angle (last frame) by fitting a circle...
>>> np.round(CA.contact_angle,2)
>>> print(np.round(CA.contact_angle,2))
90.58
>>>
>>> # ... and using an elliptical fit:
>>> left, right = CA.contact_angles
>>> # left angle
>>> np.round(np.abs(left),2)
>>> print(np.round(np.abs(left),2))
79.95
>>> # right angle
>>> np.round(right,2)
>>> print(np.round(right,2))
83.84
>>> # Contact angles from the averaged binned statistics of
>>> # surface atoms' radial distance as a function of the azimuthal angle
>>> list(np.round(CA.mean_contact_angles,2))
[96.2, 100.68]
>>> np.around(CA.mean_contact_angles,1).tolist()
[96.2, 100.7]
"""

Expand Down
10 changes: 5 additions & 5 deletions pytim/observables/correlator.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def sample(self, group):
RuntimeError(
'Cannot compute survival probability without a reference')
sampled = self.observable.compute(group)
self.timeseries.append(list(sampled.flatten()))
self.timeseries.append(sampled.flatten().tolist())

if self.shape is None:
self.shape = sampled.shape
Expand All @@ -149,13 +149,13 @@ def _sample_intermittent(self, group):
# the residence function (1 if in the reference group, 0 otherwise)
mask = np.isin(self.reference, group)
# append the residence function to its timeseries
self.maskseries.append(list(mask))
self.maskseries.append(mask.tolist())
if self.observable is not None:
# this copies a vector of zeros with the correct shape
sampled = self.reference_obs.copy()
obs = self.observable.compute(group)
sampled[np.where(mask)] = obs
self.timeseries.append(list(sampled.flatten()))
self.timeseries.append(sampled.flatten().tolist())
else:
self.timeseries = self.maskseries
if self.shape is None:
Expand Down Expand Up @@ -249,7 +249,7 @@ def correlation(self, normalized=True, continuous=True):
>>> print (np.allclose(corr, [ c0, c1, c2, c3]))
True
>>> # check normalization
>>> np.all(vv.correlation(continuous=False) == corr/corr[0])
>>> print(np.all(vv.correlation(continuous=False) == corr/corr[0]))
True
>>> # not normalizd, continuous
>>> corr = vv.correlation(normalized=False,continuous=True)
Expand All @@ -258,7 +258,7 @@ def correlation(self, normalized=True, continuous=True):
>>> print (np.allclose(corr, [ c0, c1, c2, c3]))
True
>>> # check normalization
>>> np.all(vv.correlation(continuous=True) == corr/corr[0])
>>> print(np.all(vv.correlation(continuous=True) == corr/corr[0]))
True
"""
Expand Down
2 changes: 1 addition & 1 deletion pytim/observables/distributionfunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def sample(self, g1=None, g2=None, kargs1=None, kargs2=None):
self.count += count

box = self.universe.dimensions
self.volume += np.product(box[:3])
self.volume += np.prod(box[:3])
if self.g2 is None or len(self.g2) == 0:
self.n_normalize += len(self.g1)
else:
Expand Down
4 changes: 2 additions & 2 deletions pytim/observables/free_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ class FreeVolume(object):
>>> FV = pytim.observables.FreeVolume(u,npoints = nsamples)
>>> np.random.seed(1) # ensure reproducibility of test
>>> free, err = FV.compute()
>>> np.isclose(free,1.0-0.6802,rtol=1e-3)
>>> print(np.isclose(free,1.0-0.6802,rtol=1e-3))
True
>>> np.random.seed(1) # ensure reproducibility of test
>>> lst, _ = FV._compute()
>>> np.isclose(free,1.0-len(lst)*1.0/nsamples, rtol=1e-6)
>>> print(np.isclose(free,1.0-len(lst)*1.0/nsamples, rtol=1e-6))
True
"""
Expand Down
2 changes: 1 addition & 1 deletion pytim/observables/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def compute(self, inp, **kargs):
>>> condition = np.logical_and(u.atoms.sides==0,u.atoms.layers==1)
>>> group = u.atoms[condition]
>>> costheta, phi = biv.compute(group)
>>> np.all(np.isclose([costheta[0],phi[0]], [0.6533759236335754, 0.10778185716460659]))
>>> print(all(np.isclose([costheta[0],phi[0]], [0.6533759236335754, 0.10778185716460659])))
True
"""

Expand Down
21 changes: 11 additions & 10 deletions pytim/observables/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,18 +311,19 @@ def _():
>>> inter = pytim.ITIM(u,cluster_cut=3.5,alpha=2.5)
>>> print(inter.normal)
2
>>> np.set_printoptions(precision=8)
>>> np.random.seed(1) # for the MC normalization
>>> stdprof = pytim.observables.Profile()
>>> stdprof.sample(u.atoms)
>>> print(stdprof.get_values(binwidth=0.5)[2][:6])
[0.09229169 0.10959639 0.08075523 0.10959639 0.09805993 0.09805993]
>>> vals = stdprof.get_values(binwidth=0.5)[2]
>>> print(np.around(vals[:6],decimals=3))
[0.092 0.11 0.081 0.11 0.098 0.098]
>>> prof = pytim.observables.Profile(interface=inter)
>>> prof.sample(u.atoms)
>>> vals = prof.get_values(binwidth=0.5)[2]
>>> print(vals[len(vals)//2-3:len(vals)//2+3])
[0.07344066 0.04300743 0.02803522 inf 0. 0. ]
>>> print(np.around(vals[len(vals)//2-3:len(vals)//2+3],decimals=3))
[0.073 0.043 0.028 inf 0. 0. ]
Expand Down Expand Up @@ -353,15 +354,15 @@ def _():
>>> np.random.seed(1) # for the MC normalization
>>> stdprof = pytim.observables.Profile()
>>> stdprof.sample(u.atoms)
>>> print(stdprof.get_values(binwidth=0.5)[2][:6])
[0.09229169 0.10959639 0.08075523 0.10959639 0.09805993 0.09805993]
>>> vals = stdprof.get_values(binwidth=0.5)[2]
>>> print(np.around(vals[:6],decimals=3))
[0.092 0.11 0.081 0.11 0.098 0.098]
>>> prof = pytim.observables.Profile(interface=inter)
>>> prof.sample(u.atoms)
>>> vals = prof.get_values(binwidth=1.0)[2]
>>> print(vals[len(vals)//2-4:len(vals)//2+2])
[0.09554818 0.09796541 0.05555127 0. inf 0. ]
>>> print(np.around(vals[len(vals)//2-4:len(vals)//2+2],decimals=3))
[0.096 0.098 0.056 0. inf 0. ]
"""

Expand Down
28 changes: 10 additions & 18 deletions pytim/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,14 @@
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
from __future__ import print_function


def patchNumpy():
# this try/except block patches numpy and provides _validate_lengths
# to skimage<=1.14.1
import numpy
try:
numpy.lib.arraypad._validate_lengths
except AttributeError:
def patch_validate_lengths(ar, crop_width):
return numpy.lib.arraypad._as_pairs(crop_width, ar.ndim, as_index=True)
numpy.lib.arraypad._validate_lengths = patch_validate_lengths


def patchTrajectory(trajectory, interface):
""" Patch the MDAnalysis trajectory class
this patch makes the layer assignement being automatically
called whenever a new frame is loaded.
"""
from importlib.metadata import version
if int(version('numpy').split('.')[0])<2 : return
try:
trajectory.interface
trajectory.interface = interface
Expand Down Expand Up @@ -110,11 +99,14 @@ def patchMDTRAJ(trajectory, universe):
Example:
>>> import mdtraj
>>> import pytim
>>> from pytim.datafiles import WATER_GRO, WATER_XTC
>>> t = mdtraj.load_xtc(WATER_XTC,top=WATER_GRO)
>>> inter = pytim.ITIM(t)
>>> try:
... import mdtraj
... import pytim
... from pytim.datafiles import WATER_GRO, WATER_XTC
... t = mdtraj.load_xtc(WATER_XTC,top=WATER_GRO)
... inter = pytim.ITIM(t)
... except:
... pass
"""
Expand Down
2 changes: 1 addition & 1 deletion pytim/sanity_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def _check_universe(self, input_obj):
patchMDTRAJ(input_obj, self.interface.universe)
os.remove(_file.name)
return 'mdtraj'
except ImportError:
except (ImportError,ValueError): # ValueError to handle mdtraj not supporting numpy2
pass
try:
import os
Expand Down
2 changes: 1 addition & 1 deletion pytim/utilities_dbscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def _():
>>> print (np.sort(c2)[-2:])
[ 0 9335]
>>> print ((np.all(c1==c2), np.all(l1==l2)))
>>> print ((all(c1==c2), all(l1==l2)))
(True, True)
"""
Expand Down
4 changes: 2 additions & 2 deletions pytim/utilities_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ def polygonalArea(points):
>>> s1 = np.sin(2*np.pi/5.) ; s2 = np.sin(4*np.pi/5.)
>>> pentagon = np.array([[1,0,0],[c1,s1,0],[-c2,s2,0],[-c2,-s2,0],[c1,-s1,0]])
>>> A = 0.25 * np.sqrt(25+10*np.sqrt(5)) * 100./ (50+10*np.sqrt(5))
>>> np.isclose(pytim.utilities.polygonalArea(pentagon),A)
>>> print(np.isclose(pytim.utilities.polygonalArea(pentagon),A))
True
>>> # now let's rotate it:
>>> rotated = np.dot(EulerRotation(0,np.pi/2.,0),pentagon.T).T
>>> np.isclose(pytim.utilities.polygonalArea(rotated),A)
>>> print(np.isclose(pytim.utilities.polygonalArea(rotated),A))
True
"""
Expand Down
2 changes: 1 addition & 1 deletion requirements.testing.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mdtraj
pytest==8.1.1
codecov==2.1.13
pytest-cov==5.0.0
packaging
7 changes: 4 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
cython>=0.24.1
numpy>=1.26.4,<2.0.0
numpy>=2.1.3
scipy>=1.6.0
gsd>3.0.0
MDAnalysis>=2.7.0
setuptools
MDAnalysis>=2.8.0
PyWavelets>=0.5.2
scikit-image>=0.14.2
scikit-image>=0.24.0
sphinx>=1.4.3
matplotlib
pytest
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ def run_tests(self):
# requirements files see:
# https://packaging.python.org/en/latest/requirements.html
install_requires=[
'numpy>=1.26.4,<2.0.0', 'cython>=0.24.1','gsd>=3.0.0','MDAnalysis>=2.7.0'
'numpy>=2.1.3', 'cython>=0.24.1','gsd>=3.0.0','MDAnalysis>=2.8.0'
],

# List additional groups of dependencies here (e.g. development
# dependencies). You can install these using the following syntax,
# for example:
# $ pip install -e .[dev,test]
tests_require=['nose>=1.3.7', 'coverage'],
tests_require=['nose>=1.3.7', 'coverage', 'scikit-image'],
# If there are data files included in your packages that need to be
# installed, specify them here. If using Python 2.6 or less, then these
# have to be included in MANIFEST.in as well.
Expand Down

0 comments on commit d612fb0

Please sign in to comment.