From 4e85133680e5f87db7eb29a7712eb32423932740 Mon Sep 17 00:00:00 2001 From: saeed Date: Wed, 29 Apr 2026 10:38:18 +0200 Subject: [PATCH 1/5] Add pyproject.toml and simplify setup.py to always build from .pyx --- pyproject.toml | 3 ++ setup.py | 112 +++++++++++++------------------------------------ 2 files changed, 31 insertions(+), 84 deletions(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..20612d7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "numpy", "cython"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index b52cf8b..d8d529f 100644 --- a/setup.py +++ b/setup.py @@ -11,84 +11,40 @@ """ from setuptools import setup, find_packages from distutils.extension import Extension +from Cython.Build import cythonize import os.path -try: - from Cython.Distutils import build_ext -except ImportError: - use_cython = False -else: - use_cython = True - class numpy_include(os.PathLike): - """Defers import of numpy until install_requires is through""" - def __str__(self): - import numpy - return numpy.get_include() - - def __fspath__(self): - return str(self) - - -if os.path.isfile("pyspike/cython/cython_add.c") and \ - os.path.isfile("pyspike/cython/cython_get_tau.c") and \ - os.path.isfile("pyspike/cython/cython_profiles.c") and \ - os.path.isfile("pyspike/cython/cython_distances.c") and \ - os.path.isfile("pyspike/cython/cython_directionality.c") and \ - os.path.isfile("pyspike/cython/cython_simulated_annealing.c"): - use_c = True -else: - use_c = False - -if not use_cython and not use_c: - print('Cython not installed. Programs will be slow.') - # Ans = input('Abort? (Y/N)\n') - # if len(Ans)>0 and (Ans[0]=='Y' or Ans[0]=='y'): - # print("\nAborting\n") - # raise RuntimeError('User termination') + """Defers import of numpy until install_requires is through""" + def __str__(self): + import numpy + return numpy.get_include() + + def __fspath__(self): + return str(self) + + +ext_modules = cythonize([ + Extension("pyspike.cython.cython_add", + ["pyspike/cython/cython_add.pyx"]), + Extension("pyspike.cython.cython_get_tau", + ["pyspike/cython/cython_get_tau.pyx"]), + Extension("pyspike.cython.cython_profiles", + ["pyspike/cython/cython_profiles.pyx"]), + Extension("pyspike.cython.cython_distances", + ["pyspike/cython/cython_distances.pyx"]), + Extension("pyspike.cython.cython_directionality", + ["pyspike/cython/cython_directionality.pyx"]), + Extension("pyspike.cython.cython_simulated_annealing", + ["pyspike/cython/cython_simulated_annealing.pyx"]) +]) -cmdclass = {} -ext_modules = [] - -if use_cython: # Cython is available, compile .pyx -> .c - ext_modules += [ - Extension("pyspike.cython.cython_add", - ["pyspike/cython/cython_add.pyx"]), - Extension("pyspike.cython.cython_get_tau", - ["pyspike/cython/cython_get_tau.pyx"]), - Extension("pyspike.cython.cython_profiles", - ["pyspike/cython/cython_profiles.pyx"]), - Extension("pyspike.cython.cython_distances", - ["pyspike/cython/cython_distances.pyx"]), - Extension("pyspike.cython.cython_directionality", - ["pyspike/cython/cython_directionality.pyx"]), - Extension("pyspike.cython.cython_simulated_annealing", - ["pyspike/cython/cython_simulated_annealing.pyx"]) - ] - cmdclass.update({'build_ext': build_ext}) -elif use_c: # c files are there, compile to binaries - ext_modules += [ - Extension("pyspike.cython.cython_add", - ["pyspike/cython/cython_add.c"]), - Extension("pyspike.cython.cython_get_tau", - ["pyspike/cython/cython_get_tau.c"]), - Extension("pyspike.cython.cython_profiles", - ["pyspike/cython/cython_profiles.c"]), - Extension("pyspike.cython.cython_distances", - ["pyspike/cython/cython_distances.c"]), - Extension("pyspike.cython.cython_directionality", - ["pyspike/cython/cython_directionality.c"]), - Extension("pyspike.cython.cython_simulated_annealing", - ["pyspike/cython/cython_simulated_annealing.c"]) - ] -# neither cython nor c files available -> automatic fall-back to python backend setup( name='pyspike', packages=find_packages(exclude=['doc', 'test*']), version='0.8.0', - cmdclass=cmdclass, ext_modules=ext_modules, include_dirs=[numpy_include()], description='A Python library for the numerical analysis of spike\ @@ -98,34 +54,22 @@ def __fspath__(self): license='BSD', url='https://github.com/mariomulansky/PySpike', install_requires=['numpy'], - keywords=['data analysis', 'spike', 'neuroscience'], # arbitrary keywords + keywords=['data analysis', 'spike', 'neuroscience'], classifiers=[ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable 'Development Status :: 4 - Beta', - - # Indicate who your project is intended for 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Information Analysis', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], package_data={ - 'pyspike': ['cython/cython_add.c', - 'cython/cython_profiles.c', - 'cython/cython_get_tau.c', - 'cython/cython_distances.c', - 'cython/cython_directionality.c', - 'cython/cython_simulated_annealing.c'], 'test': ['Spike_testdata.txt'] } -) +) \ No newline at end of file From eb6cda8c56bcf8be90ac7b6f907009c8c3067e6a Mon Sep 17 00:00:00 2001 From: saeed Date: Wed, 29 Apr 2026 10:38:52 +0200 Subject: [PATCH 2/5] Replace pkg_resources with importlib.metadata --- pyspike/__init__.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/pyspike/__init__.py b/pyspike/__init__.py index 4e31120..b68f09b 100644 --- a/pyspike/__init__.py +++ b/pyspike/__init__.py @@ -37,22 +37,13 @@ # define the __version__ following # http://stackoverflow.com/questions/17583443 -from pkg_resources import get_distribution, DistributionNotFound -import os.path +from importlib.metadata import version, PackageNotFoundError try: - _dist = get_distribution('pyspike') - # Normalize case for Windows systems - dist_loc = os.path.normcase(_dist.location) - here = os.path.normcase(__file__) - if not here.startswith(os.path.join(dist_loc, 'pyspike')): - # not installed, but there is another version that *is* - raise DistributionNotFound -except DistributionNotFound: + __version__ = version('pyspike') +except PackageNotFoundError: __version__ = 'Please install this project with setup.py' -else: - __version__ = _dist.version - + disable_backend_warning = False def NoCythonWarn(): From 0b7e51e54f6a63112cbc7f8b04d55f5671f2245b Mon Sep 17 00:00:00 2001 From: saeed Date: Wed, 29 Apr 2026 10:43:01 +0200 Subject: [PATCH 3/5] Improve distance_matrix example with raster plot and unified time interval [0, 1000] --- examples/distance_matrix.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/distance_matrix.py b/examples/distance_matrix.py index 0e4d825..9677ce1 100644 --- a/examples/distance_matrix.py +++ b/examples/distance_matrix.py @@ -12,7 +12,7 @@ from __future__ import print_function import matplotlib.pyplot as plt - +import numpy as np import pyspike as spk # first load the data, interval ending time = 4000, start=0 (default) @@ -20,8 +20,15 @@ print(len(spike_trains)) +# plot the spike times +plt.figure() +for (i, spike_train) in enumerate(spike_trains): + plt.scatter(spike_train, i*np.ones_like(spike_train), marker='|',color='black') +plt.title("raster plot") + + plt.figure() -isi_distance = spk.isi_distance_matrix(spike_trains) +isi_distance = spk.isi_distance_matrix(spike_trains,interval=(0, 1000)) plt.imshow(isi_distance, interpolation='none') plt.title("ISI-distance") @@ -31,8 +38,8 @@ plt.title("SPIKE-distance, T=0-1000") plt.figure() -spike_sync = spk.spike_sync_matrix(spike_trains, interval=(2000, 4000)) +spike_sync = spk.spike_sync_matrix(spike_trains, interval=(0, 1000)) plt.imshow(spike_sync, interpolation='none') -plt.title("SPIKE-Sync, T=2000-4000") +plt.title("SPIKE-Sync, T=0-1000") plt.show() From 616309e0b6d61b00201e47c4bcb560c22e5e24ae Mon Sep 17 00:00:00 2001 From: saeed Date: Wed, 29 Apr 2026 10:44:19 +0200 Subject: [PATCH 4/5] Update .gitignore to exclude build artifacts --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 04fef3d..944ed0b 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,7 @@ out/ # C files are always generated by Cython *.c *.C +test-env/ +*.egg-info/ +__pycache__/ +*.so From ddc85f7b77f4fb4753bd335e5ffff97562e154d4 Mon Sep 17 00:00:00 2001 From: saeed Date: Wed, 29 Apr 2026 10:55:15 +0200 Subject: [PATCH 5/5] Fix invalid escape sequences in docstrings for Python 3.12 --- pyspike/PieceWiseConstFunc.py | 2 +- pyspike/PieceWiseLinFunc.py | 2 +- pyspike/spike_sync.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyspike/PieceWiseConstFunc.py b/pyspike/PieceWiseConstFunc.py index 450e59a..dade4d0 100644 --- a/pyspike/PieceWiseConstFunc.py +++ b/pyspike/PieceWiseConstFunc.py @@ -157,7 +157,7 @@ def integral(self, interval=None): return a def avrg(self, interval=None): - """ Computes the average of the piece-wise const function: + r""" Computes the average of the piece-wise const function: :math:`a = 1/T \int_0^T f(x) dx` where T is the length of the interval. :param interval: averaging interval given as a pair of floats, a diff --git a/pyspike/PieceWiseLinFunc.py b/pyspike/PieceWiseLinFunc.py index 1195e9a..cb4fcd4 100644 --- a/pyspike/PieceWiseLinFunc.py +++ b/pyspike/PieceWiseLinFunc.py @@ -193,7 +193,7 @@ def intermediate_value(x0, x1, y0, y1, x): return integral def avrg(self, interval=None): - """ Computes the average of the piece-wise linear function: + r""" Computes the average of the piece-wise linear function: :math:`a = 1/T \int_0^T f(x) dx` where T is the interval length. :param interval: averaging interval given as a pair of floats, a diff --git a/pyspike/spike_sync.py b/pyspike/spike_sync.py index 50bb98a..dfa896a 100644 --- a/pyspike/spike_sync.py +++ b/pyspike/spike_sync.py @@ -165,7 +165,7 @@ def _spike_sync_values(spike_train1, spike_train2, interval, max_tau, **kwargs): # spike_sync ############################################################ def spike_sync(*args, **kwargs): - """ Computes the spike synchronization value of the given spike + r""" Computes the spike synchronization value of the given spike trains. The spike synchronization value is the computed as the total number of coincidences divided by the total number of spikes: