From d1b0254eb65870ee66b5070620e88aa9ca50100e Mon Sep 17 00:00:00 2001 From: ayan-b Date: Wed, 10 Jun 2020 22:44:00 +0530 Subject: [PATCH 01/12] Commit C source --- gdist.py | 7 +++++++ gdist_c_api.cpp | 12 ++++++++++++ geodesic_library/gdist.cpp | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 gdist.py create mode 100644 gdist_c_api.cpp create mode 100644 geodesic_library/gdist.cpp diff --git a/gdist.py b/gdist.py new file mode 100644 index 00000000..83bd998c --- /dev/null +++ b/gdist.py @@ -0,0 +1,7 @@ +import ctypes + +import numpy as np + +lib = ctypes.CDLL('./gdist_c_api.so') +# build numpy arrays etc +print(lib.computeGdist(1, 1, 1, 1)) diff --git a/gdist_c_api.cpp b/gdist_c_api.cpp new file mode 100644 index 00000000..251d43e4 --- /dev/null +++ b/gdist_c_api.cpp @@ -0,0 +1,12 @@ +#include +#include + +#include "geodesic_library/geodesic_algorithm_exact.h" + +extern "C" { + double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles); +}; + +double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { + return 1.0; +} diff --git a/geodesic_library/gdist.cpp b/geodesic_library/gdist.cpp new file mode 100644 index 00000000..3e37a69e --- /dev/null +++ b/geodesic_library/gdist.cpp @@ -0,0 +1,21 @@ +/* read mesh from file and + - if one vertex is specified, for all vertices of the mesh print their distances to this vertex + - if two vertices are specified, print the shortest path between these vertices + + Danil Kirsanov, 01/2008 + Minor cleanup, 2011, SAK. +*/ +#include +#include + +#include "geodesic_algorithm_exact.h" + +extern "C" { + double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { + return computeGdistCpp(numberOfVertices, numberOfTriangles, vertices, triangles); + } +} + +double computeGdistCpp(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { + return 1.0; +} From 965339fc81837ec7aca1b9a3c9c850757780fdc4 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Thu, 2 Jul 2020 21:58:56 +0530 Subject: [PATCH 02/12] macOS & Linux done --- .gitignore | 5 ++ .travis.yml | 10 ++- gdist.py | 149 ++++++++++++++++++++++++++++++++- gdist_c_api.cpp | 166 ++++++++++++++++++++++++++++++++++--- geodesic_library/gdist.cpp | 21 ----- pyproject.toml | 2 - requirements.txt | 1 - setup.py | 24 ++---- tests/test_gdist.py | 134 +++++++++++++++--------------- 9 files changed, 386 insertions(+), 126 deletions(-) delete mode 100644 geodesic_library/gdist.cpp delete mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index 7e4bae9e..c7c4f05a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ __pycache__/ # Distribution / packaging dist/ +*.egg-info +build/ # coverage file .coverage @@ -26,7 +28,10 @@ env # Executables *.out *.exe +*.o # Shared object files *.so *.pyd +*.dylib +*.dll diff --git a/.travis.yml b/.travis.yml index 4fe99372..d8c1e3dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,20 @@ notifications: install: - pip3 install . - # - python setup.py build_ext --inplace + - python setup.py build_ext --inplace script: - - pip3 install pytest~=3.6.1 - - pytest + - pip3 install pytest pytest-cov + - pytest --cov=gdist jobs: include: - name: "Python 3.8 on Xenial Linux" language: python python: 3.8 + - name: "Python 3.7.4 on macOS" + os: osx + osx_image: xcode11.2 + language: shell - name: "Python 3.8.0 on Windows" os: windows language: shell diff --git a/gdist.py b/gdist.py index 83bd998c..8c6f3a9e 100644 --- a/gdist.py +++ b/gdist.py @@ -1,7 +1,150 @@ import ctypes +import glob +import sys import numpy as np +import scipy.sparse -lib = ctypes.CDLL('./gdist_c_api.so') -# build numpy arrays etc -print(lib.computeGdist(1, 1, 1, 1)) + +if sys.platform == 'win32': + libfile = glob.glob('build/*/gdist*.pyd')[0] + lib = ctypes.windll.LoadLibrary(libfile) +elif sys.platform == 'darwin': + try: + libfile = glob.glob('build/*/gdist*.so')[0] + except IndexError: + libfile = glob.glob('build/*/gdist*.dylib')[0] + lib = ctypes.cdll.LoadLibrary(libfile) +else: + libfile = glob.glob('build/*/gdist*.so')[0] + lib = ctypes.cdll.LoadLibrary(libfile) + +lib.compute_gdist.argtypes = [ + ctypes.c_uint, + ctypes.c_uint, + np.ctypeslib.ndpointer(dtype=np.float64), + np.ctypeslib.ndpointer(dtype=np.int32), + ctypes.c_uint, + ctypes.c_uint, + np.ctypeslib.ndpointer(dtype=np.int32), + np.ctypeslib.ndpointer(dtype=np.int32), + np.ctypeslib.ndpointer(dtype=np.float64), + ctypes.c_double, +] + +lib.compute_gdist.restype = None + +lib.local_gdist_matrix.argtypes = [ + ctypes.c_uint, + ctypes.c_uint, + np.ctypeslib.ndpointer(dtype=np.float64), + np.ctypeslib.ndpointer(dtype=np.int32), + ctypes.POINTER(ctypes.c_uint), + ctypes.c_double, +] +lib.local_gdist_matrix.restype = ctypes.POINTER(ctypes.c_double) + + +class Gdist(object): + def compute_gdist( + self, + number_of_vertices, + number_of_triangles, + vertices, + triangles, + number_of_source_indices, + number_of_target_indices, + source_indices_array, + target_indices_array, + distance_limit + ): + target_indices_size = target_indices_array.size + distance = np.empty(target_indices_size, dtype=np.float64) + lib.compute_gdist( + number_of_vertices, + number_of_triangles, + vertices, + triangles, + number_of_source_indices, + number_of_target_indices, + source_indices_array, + target_indices_array, + distance, + distance_limit + ) + return distance + + def local_gdist_matrix( + self, + number_of_vertices, + number_of_triangles, + vertices, + triangles, + max_distance, + ): + sparse_matrix_size = ctypes.c_uint(0) + data = lib.local_gdist_matrix( + number_of_vertices, + number_of_triangles, + vertices, + triangles, + ctypes.byref(sparse_matrix_size), + max_distance + ) + + data = np.fromiter(data, dtype=np.float64, + count=3 * sparse_matrix_size.value) + return data + + +def compute_gdist( + vertices, + triangles, + source_indices=None, + target_indices=None, + max_distance=1e100, +): + vertices = vertices.ravel() + triangles = triangles.ravel() + source_indices = source_indices.ravel() + target_indices = target_indices.ravel() + + g = Gdist() + distance = g.compute_gdist( + number_of_vertices=vertices.size, + number_of_triangles=triangles.size, + vertices=vertices, + triangles=triangles, + number_of_source_indices=source_indices.size, + number_of_target_indices=target_indices.size, + source_indices_array=source_indices, + target_indices_array=target_indices, + distance_limit=max_distance + ) + return np.fromiter(distance, dtype=np.float64, count=target_indices.size) + + +def local_gdist_matrix( + vertices, + triangles, + max_distance=1e100 +): + vertices = vertices.ravel() + triangles = triangles.ravel() + + g = Gdist() + data = g.local_gdist_matrix( + vertices.size, + triangles.size, + vertices, + triangles, + max_distance + ) + sizes = data.size // 3 + rows = data[:sizes] + columns = data[sizes: 2*sizes] + data = data[2*sizes:] + + return scipy.sparse.csc_matrix( + (data, (rows, columns)), shape=(vertices.size // 3, vertices.size // 3) + ) diff --git a/gdist_c_api.cpp b/gdist_c_api.cpp index 251d43e4..fd28b352 100644 --- a/gdist_c_api.cpp +++ b/gdist_c_api.cpp @@ -1,12 +1,154 @@ -#include -#include - -#include "geodesic_library/geodesic_algorithm_exact.h" - -extern "C" { - double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles); -}; - -double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { - return 1.0; -} +#include +#include +#include + +#include "geodesic_library/geodesic_algorithm_exact.h" + + +#if defined(_WIN32) +# if defined(DLL_EXPORTS) +# define DLL_EXPORT_API __declspec(dllexport) +# else +# define DLL_EXPORT_API __declspec(dllimport) +# endif +#else +# define DLL_EXPORT_API +#endif + + +void compute_gdist_impl( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + int *triangles, + unsigned number_of_source_indices, + unsigned number_of_target_indices, + unsigned *source_indices_array, + unsigned *target_indices_array, + double *distance, + double distance_limit +) { + + std::vector points (vertices, vertices + number_of_vertices); + std::vector faces (triangles, triangles + number_of_triangles); + std::vector source_indices (source_indices_array, source_indices_array + number_of_source_indices); + std::vector target_indices (target_indices_array, target_indices_array + number_of_target_indices); + + geodesic::Mesh mesh; + mesh.initialize_mesh_data(points, faces); // create internal mesh data structure including edges + + geodesic::GeodesicAlgorithmExact algorithm(&mesh); // create exact algorithm for the mesh + + std::vector all_sources, stop_points; + + for (unsigned i = 0; i < number_of_source_indices; ++i) { + all_sources.push_back(geodesic::SurfacePoint(&mesh.vertices()[source_indices[i]])); + } + + for (unsigned i = 0; i < number_of_target_indices; ++i) { + stop_points.push_back(geodesic::SurfacePoint(&mesh.vertices()[target_indices[i]])); + } + + algorithm.propagate(all_sources, distance_limit, &stop_points); + + for (unsigned i = 0; i < stop_points.size(); ++i) { + algorithm.best_source(stop_points[i], distance[i]); + } +} + +double* local_gdist_matrix_impl( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + unsigned *triangles, + unsigned *sparse_matrix_size, + double max_distance +) { + std::vector points (vertices, vertices + number_of_vertices); + std::vector faces (triangles, triangles + number_of_triangles); + + geodesic::Mesh mesh; + mesh.initialize_mesh_data(points, faces); // create internal mesh data structure including edges + geodesic::GeodesicAlgorithmExact algorithm(&mesh); // create exact algorithm for the mesh + std::vector rows_vector, columns_vector; + std::vector data_vector; + + double distance = 0; + + std::vector targets(number_of_vertices), source; + + for (unsigned i = 0; i < number_of_vertices; ++i) { + targets[i] = geodesic::SurfacePoint(&mesh.vertices()[i]); + } + for (unsigned i = 0; i < number_of_vertices / 3; ++i) { + source.push_back(geodesic::SurfacePoint(&mesh.vertices()[i])); + algorithm.propagate(source, max_distance, NULL); + source.pop_back(); + for (unsigned j = 0; j < number_of_vertices / 3; ++j) { + algorithm.best_source(targets[j], distance); + if (distance != geodesic::GEODESIC_INF && distance != 0 && distance <= max_distance) { + rows_vector.push_back(i); + columns_vector.push_back(j); + data_vector.push_back(distance); + } + } + } + + double *data; + data = new double[3 * rows_vector.size()]; + + *sparse_matrix_size = rows_vector.size(); + + std::copy(rows_vector.begin(), rows_vector.end(), data); + std::copy(columns_vector.begin(), columns_vector.end(), data + data_vector.size()); + std::copy(data_vector.begin(), data_vector.end(), data + 2 * data_vector.size()); + + return data; +} + + +extern "C" { + DLL_EXPORT_API void compute_gdist( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + int *triangles, + unsigned number_of_source_indices, + unsigned number_of_target_indices, + unsigned *source_indices_array, + unsigned *target_indices_array, + double *distance, + double distance_limit + ) { + compute_gdist_impl( + number_of_vertices, + number_of_triangles, + vertices, + triangles, + number_of_source_indices, + number_of_target_indices, + source_indices_array, + target_indices_array, + distance, + distance_limit + ); + } + + DLL_EXPORT_API double* local_gdist_matrix( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + unsigned *triangles, + unsigned *sparse_matrix_size, + double max_distance + ) { + return local_gdist_matrix_impl( + number_of_vertices, + number_of_triangles, + vertices, + triangles, + sparse_matrix_size, + max_distance + ); + } +}; diff --git a/geodesic_library/gdist.cpp b/geodesic_library/gdist.cpp deleted file mode 100644 index 3e37a69e..00000000 --- a/geodesic_library/gdist.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* read mesh from file and - - if one vertex is specified, for all vertices of the mesh print their distances to this vertex - - if two vertices are specified, print the shortest path between these vertices - - Danil Kirsanov, 01/2008 - Minor cleanup, 2011, SAK. -*/ -#include -#include - -#include "geodesic_algorithm_exact.h" - -extern "C" { - double computeGdist(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { - return computeGdistCpp(numberOfVertices, numberOfTriangles, vertices, triangles); - } -} - -double computeGdistCpp(int numberOfVertices, int numberOfTriangles, double *vertices, double *triangles) { - return 1.0; -} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 83de03ad..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel", "Cython", "numpy"] diff --git a/requirements.txt b/requirements.txt index 701f255a..6bad1038 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -cython numpy scipy diff --git a/setup.py b/setup.py index 9826b811..42eeb7df 100644 --- a/setup.py +++ b/setup.py @@ -41,34 +41,29 @@ """ import os -import numpy import shutil import setuptools -from Cython.Distutils import build_ext GEODESIC_NAME = "gdist" GEODESIC_MODULE = [ setuptools.Extension( name=GEODESIC_NAME, # Name of extension - sources=["gdist.pyx"], # Filename of Cython source - language="c++", # Cython create C++ source - # Disable assertions; one is failing geodesic_mesh.h:405 - define_macros=[('NDEBUG', 1)], - extra_compile_args=['--std=c++14'], - extra_link_args=['--std=c++14'], - include_dirs=[numpy.get_include(), "geodesic_library"], + sources=["gdist_c_api.cpp"], + language="c++", + extra_compile_args=['--std=c++11'], + extra_link_args=['--std=c++11'], ) ] INCLUDE_DIRS = [ - numpy.get_include(), # NumPy dtypes + # numpy.get_include(), # NumPy dtypes "geodesic_library", # geodesic distance, C++ library. ] TEAM = "Danil Kirsanov, Gaurav Malhotra and Stuart Knock" -INSTALL_REQUIREMENTS = ['numpy', 'scipy', 'cython'] +INSTALL_REQUIREMENTS = ['numpy', 'scipy'] with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as fd: DESCRIPTION = fd.read() @@ -76,9 +71,9 @@ setuptools.setup( name="tvb-" + GEODESIC_NAME, version='2.0.2', + scripts=['gdist.py'], ext_modules=GEODESIC_MODULE, include_dirs=INCLUDE_DIRS, - cmdclass={'build_ext': build_ext}, install_requires=INSTALL_REQUIREMENTS, description="Compute geodesic distances", long_description=DESCRIPTION, @@ -88,8 +83,3 @@ url='https://github.com/the-virtual-brain/tvb-gdist', keywords="gdist geodesic distance geo tvb" ) - -shutil.rmtree('tvb_gdist.egg-info', True) -if os.path.exists(GEODESIC_NAME + '.cpp'): - os.remove(GEODESIC_NAME + '.cpp') -shutil.rmtree('build', True) diff --git a/tests/test_gdist.py b/tests/test_gdist.py index d00a9f91..3c0472cb 100644 --- a/tests/test_gdist.py +++ b/tests/test_gdist.py @@ -1,67 +1,67 @@ -import numpy as np - -import gdist - - -class TestComputeGdist(): - def test_flat_triangular_mesh(self): - data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) - vertices = data[0:121].astype(np.float64) - triangles = data[121:].astype(np.int32) - source = np.array([1], dtype=np.int32) - target = np.array([2], dtype=np.int32) - distance = gdist.compute_gdist( - vertices, - triangles, - source_indices=source, - target_indices=target - ) - np.testing.assert_array_almost_equal(distance, [0.2]) - - def test_hedgehog_mesh(self): - data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) - vertices = data[0:300].astype(np.float64) - triangles = data[300:].astype(np.int32) - source = np.array([0], dtype=np.int32) - target = np.array([1], dtype=np.int32) - distance = gdist.compute_gdist( - vertices, - triangles, - source_indices=source, - target_indices=target - ) - np.testing.assert_array_almost_equal(distance, [1.40522]) - - -class TestLocalGdistMatrix: - def test_flat_triangular_mesh(self): - data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) - vertices = data[0:121].astype(np.float64) - triangles = data[121:].astype(np.int32) - distances = gdist.local_gdist_matrix(vertices, triangles) - epsilon = 1e-6 # the default value used in `assert_array_almost_equal` - # test if the obtained matrix is symmetric - assert (abs(distances - distances.T) > epsilon).nnz == 0 - np.testing.assert_array_almost_equal(distances.toarray()[1][0], 0.2) - # set max distance as 0.3 - distances = gdist.local_gdist_matrix(vertices, triangles, 0.3) - # test if the obtained matrix is symmetric - assert (abs(distances - distances.T) > epsilon).nnz == 0 - assert np.max(distances) <= 0.3 - - def test_hedgehog_mesh(self): - data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) - vertices = data[0:300].astype(np.float64) - triangles = data[300:].astype(np.int32) - distances = gdist.local_gdist_matrix(vertices, triangles) - epsilon = 1e-6 # the default value used in `assert_array_almost_equal` - # test if the obtained matrix is symmetric - assert (abs(distances - distances.T) > epsilon).nnz == 0 - np.testing.assert_array_almost_equal( - distances.toarray()[1][0], 1.40522 - ) - # set max distance as 1.45 - distances = gdist.local_gdist_matrix(vertices, triangles, 1.45) - # test if the obtained matrix is symmetric - assert (abs(distances - distances.T) > epsilon).nnz == 0 - assert np.max(distances) <= 1.45 +import numpy as np + +import gdist + + +class TestComputeGdist(): + def test_flat_triangular_mesh(self): + data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) + vertices = data[0:121].astype(np.float64) + triangles = data[121:].astype(np.int32) + source = np.array([1], dtype=np.int32) + target = np.array([2], dtype=np.int32) + distance = gdist.compute_gdist( + vertices, + triangles, + source_indices=source, + target_indices=target + ) + np.testing.assert_array_almost_equal(distance, [0.2]) + + def test_hedgehog_mesh(self): + data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) + vertices = data[0:300].astype(np.float64) + triangles = data[300:].astype(np.int32) + source = np.array([0], dtype=np.int32) + target = np.array([1], dtype=np.int32) + distance = gdist.compute_gdist( + vertices, + triangles, + source_indices=source, + target_indices=target + ) + np.testing.assert_array_almost_equal(distance, [1.40522]) + + +class TestLocalGdistMatrix: + def test_flat_triangular_mesh(self): + data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) + vertices = data[0:121].astype(np.float64) + triangles = data[121:].astype(np.int32) + distances = gdist.local_gdist_matrix(vertices, triangles) + epsilon = 1e-6 # the default value used in `assert_array_almost_equal` + # test if the obtained matrix is symmetric + assert (abs(distances - distances.T) > epsilon).nnz == 0 + np.testing.assert_array_almost_equal(distances.toarray()[1][0], 0.2) + # set max distance as 0.3 + distances = gdist.local_gdist_matrix(vertices, triangles, 0.3) + # test if the obtained matrix is symmetric + assert (abs(distances - distances.T) > epsilon).nnz == 0 + assert np.max(distances) <= 0.3 + + def test_hedgehog_mesh(self): + data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) + vertices = data[0:300].astype(np.float64) + triangles = data[300:].astype(np.int32) + distances = gdist.local_gdist_matrix(vertices, triangles) + epsilon = 1e-6 # the default value used in `assert_array_almost_equal` + # test if the obtained matrix is symmetric + assert (abs(distances - distances.T) > epsilon).nnz == 0 + np.testing.assert_array_almost_equal( + distances.toarray()[1][0], 1.40522 + ) + # set max distance as 1.45 + distances = gdist.local_gdist_matrix(vertices, triangles, 1.45) + # test if the obtained matrix is symmetric + assert (abs(distances - distances.T) > epsilon).nnz == 0 + assert np.max(distances) <= 1.45 From eb45128758728dc122c595f7c456ea6bdb023c49 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Tue, 23 Jun 2020 21:50:13 +0530 Subject: [PATCH 03/12] Use ctypes instead of cython --- .travis.yml | 7 +++-- create_dll.bat | 43 +++++++++++++++++++++++++++++++ gdist.py | 4 ++- gdist_c_api.cpp | 21 +++------------ gdist_c_api.h | 63 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 ++ setup.py | 23 ++++++++++------- tests/test_gdist.py | 8 +++--- 8 files changed, 137 insertions(+), 34 deletions(-) create mode 100644 create_dll.bat create mode 100644 gdist_c_api.h create mode 100644 pyproject.toml diff --git a/.travis.yml b/.travis.yml index d8c1e3dc..b362b90e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ notifications: email: false install: - - pip3 install . - python setup.py build_ext --inplace + - pip3 install . script: - pip3 install pytest pytest-cov - pytest --cov=gdist @@ -13,6 +13,8 @@ jobs: - name: "Python 3.8 on Xenial Linux" language: python python: 3.8 + before_install: pip3 install codecov + after_success: codecov - name: "Python 3.7.4 on macOS" os: osx osx_image: xcode11.2 @@ -22,8 +24,9 @@ jobs: language: shell before_install: - choco install python --version 3.8.0 - - python -m pip install --upgrade pip + - python -m pip install -U pip - pip install --user wheel + - ./create_dll.bat env: PATH=/c/Python38:/c/Python38/Scripts:$PATH - name: "Python 3.7.4 on macOS" os: osx diff --git a/create_dll.bat b/create_dll.bat new file mode 100644 index 00000000..d4108c66 --- /dev/null +++ b/create_dll.bat @@ -0,0 +1,43 @@ +echo on + +if NOT DEFINED VCINSTALLDIR ( + if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" ( + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64 + echo "USING VISUAL STUDIO 17" + ) +) + +if NOT DEFINED VCINSTALLDIR ( + if exist "C:\Program Files (x86)\Microsoft Visual Studio 15.0\VC\vcvarsall.bat" ( + call "C:\Program Files (x86)\Microsoft Visual Studio 15.0\VC\vcvarsall.bat" amd64 + echo "USING VISUAL STUDIO 15" + ) +) + +if NOT DEFINED VCINSTALLDIR ( + if exist "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" ( + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 + echo "USING VISUAL STUDIO 14" + ) +) + +if NOT DEFINED VCINSTALLDIR ( + if exist "C:\Program Files (x86)\Microsoft Visual Studio 13.0\VC\vcvarsall.bat" ( + call "C:\Program Files (x86)\Microsoft Visual Studio 13.0\VC\vcvarsall.bat" amd64 + echo "USING VISUAL STUDIO 13" + ) +) + +if NOT DEFINED VCINSTALLDIR ( + if exist "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" ( + call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 + echo "USING VISUAL STUDIO 12" + ) +) + +if NOT DEFINED VCINSTALLDIR ( + echo "No compatible visual studio found! run vcvarsall.bat first!" +) + +cl.exe /LD /DDLL_EXPORTS /DNDEBUG gdist_c_api.cpp +ls diff --git a/gdist.py b/gdist.py index 8c6f3a9e..3fc2878f 100644 --- a/gdist.py +++ b/gdist.py @@ -1,5 +1,6 @@ import ctypes import glob +import os import sys import numpy as np @@ -7,7 +8,8 @@ if sys.platform == 'win32': - libfile = glob.glob('build/*/gdist*.pyd')[0] + libfile = glob.glob('gdist_c_api.dll')[0] + libfile = os.path.abspath(libfile) lib = ctypes.windll.LoadLibrary(libfile) elif sys.platform == 'darwin': try: diff --git a/gdist_c_api.cpp b/gdist_c_api.cpp index fd28b352..493f4d48 100644 --- a/gdist_c_api.cpp +++ b/gdist_c_api.cpp @@ -1,19 +1,4 @@ -#include -#include -#include - -#include "geodesic_library/geodesic_algorithm_exact.h" - - -#if defined(_WIN32) -# if defined(DLL_EXPORTS) -# define DLL_EXPORT_API __declspec(dllexport) -# else -# define DLL_EXPORT_API __declspec(dllimport) -# endif -#else -# define DLL_EXPORT_API -#endif +#include "gdist_c_api.h" void compute_gdist_impl( @@ -108,7 +93,7 @@ double* local_gdist_matrix_impl( extern "C" { - DLL_EXPORT_API void compute_gdist( + void compute_gdist( unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, @@ -134,7 +119,7 @@ extern "C" { ); } - DLL_EXPORT_API double* local_gdist_matrix( + double* local_gdist_matrix( unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, diff --git a/gdist_c_api.h b/gdist_c_api.h new file mode 100644 index 00000000..50c0077d --- /dev/null +++ b/gdist_c_api.h @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "geodesic_library/geodesic_algorithm_exact.h" + + +#if defined(_WIN32) +# if defined(DLL_EXPORTS) +# define DLL_EXPORT_API __declspec(dllexport) +# else +# define DLL_EXPORT_API __declspec(dllimport) +# endif +#else +# define DLL_EXPORT_API +#endif + + +void compute_gdist_impl( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + int *triangles, + unsigned number_of_source_indices, + unsigned number_of_target_indices, + unsigned *source_indices_array, + unsigned *target_indices_array, + double *distance, + double distance_limit +); + +double* local_gdist_matrix_impl( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + unsigned *triangles, + unsigned *sparse_matrix_size, + double max_distance +); + +extern "C" { + DLL_EXPORT_API void compute_gdist( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + int *triangles, + unsigned number_of_source_indices, + unsigned number_of_target_indices, + unsigned *source_indices_array, + unsigned *target_indices_array, + double *distance, + double distance_limit + ); + + DLL_EXPORT_API double* local_gdist_matrix( + unsigned number_of_vertices, + unsigned number_of_triangles, + double *vertices, + unsigned *triangles, + unsigned *sparse_matrix_size, + double max_distance + ); +}; diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..8b88f611 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "numpy"] diff --git a/setup.py b/setup.py index 42eeb7df..9e2bf9fd 100644 --- a/setup.py +++ b/setup.py @@ -42,19 +42,23 @@ import os import shutil +import sys import setuptools GEODESIC_NAME = "gdist" -GEODESIC_MODULE = [ - setuptools.Extension( - name=GEODESIC_NAME, # Name of extension - sources=["gdist_c_api.cpp"], - language="c++", - extra_compile_args=['--std=c++11'], - extra_link_args=['--std=c++11'], - ) -] +GEODESIC_MODULE = [] + +if sys.platform == 'darwin' or sys.platform == 'linux': + GEODESIC_MODULE = [ + setuptools.Extension( + name=GEODESIC_NAME, # Name of extension + sources=["gdist_c_api.cpp"], + language="c++", + extra_compile_args=['--std=c++11'], + extra_link_args=['--std=c++11'], + ) + ] INCLUDE_DIRS = [ # numpy.get_include(), # NumPy dtypes @@ -72,6 +76,7 @@ name="tvb-" + GEODESIC_NAME, version='2.0.2', scripts=['gdist.py'], + py_modules=['gdist'], ext_modules=GEODESIC_MODULE, include_dirs=INCLUDE_DIRS, install_requires=INSTALL_REQUIREMENTS, diff --git a/tests/test_gdist.py b/tests/test_gdist.py index 3c0472cb..44f094f3 100644 --- a/tests/test_gdist.py +++ b/tests/test_gdist.py @@ -13,8 +13,8 @@ def test_flat_triangular_mesh(self): distance = gdist.compute_gdist( vertices, triangles, - source_indices=source, - target_indices=target + source, + target ) np.testing.assert_array_almost_equal(distance, [0.2]) @@ -27,8 +27,8 @@ def test_hedgehog_mesh(self): distance = gdist.compute_gdist( vertices, triangles, - source_indices=source, - target_indices=target + source, + target ) np.testing.assert_array_almost_equal(distance, [1.40522]) From 52d996bb4f471134aa6fa7c04d5646e66551e8e9 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Wed, 24 Jun 2020 08:24:32 +0530 Subject: [PATCH 04/12] Free memory --- gdist.py | 11 ++++++++--- gdist_c_api.cpp | 9 +++++++++ gdist_c_api.h | 4 ++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/gdist.py b/gdist.py index 3fc2878f..1d4d14eb 100644 --- a/gdist.py +++ b/gdist.py @@ -33,7 +33,6 @@ np.ctypeslib.ndpointer(dtype=np.float64), ctypes.c_double, ] - lib.compute_gdist.restype = None lib.local_gdist_matrix.argtypes = [ @@ -46,6 +45,11 @@ ] lib.local_gdist_matrix.restype = ctypes.POINTER(ctypes.c_double) +lib.free_memory.argtypes = [ + ctypes.POINTER(ctypes.c_double), +] +lib.free_memory.restype = None + class Gdist(object): def compute_gdist( @@ -94,9 +98,10 @@ def local_gdist_matrix( max_distance ) - data = np.fromiter(data, dtype=np.float64, + np_data = np.fromiter(data, dtype=np.float64, count=3 * sparse_matrix_size.value) - return data + lib.free_memory(data) + return np_data def compute_gdist( diff --git a/gdist_c_api.cpp b/gdist_c_api.cpp index 493f4d48..56358712 100644 --- a/gdist_c_api.cpp +++ b/gdist_c_api.cpp @@ -92,6 +92,11 @@ double* local_gdist_matrix_impl( } +void free_memory_impl(double *ptr) { + delete[] ptr; +} + + extern "C" { void compute_gdist( unsigned number_of_vertices, @@ -136,4 +141,8 @@ extern "C" { max_distance ); } + + void free_memory(double *ptr) { + free_memory_impl(ptr); + } }; diff --git a/gdist_c_api.h b/gdist_c_api.h index 50c0077d..dfd2a7fc 100644 --- a/gdist_c_api.h +++ b/gdist_c_api.h @@ -38,6 +38,8 @@ double* local_gdist_matrix_impl( double max_distance ); +void free_memory_impl(double *ptr); + extern "C" { DLL_EXPORT_API void compute_gdist( unsigned number_of_vertices, @@ -60,4 +62,6 @@ extern "C" { unsigned *sparse_matrix_size, double max_distance ); + + DLL_EXPORT_API void free_memory(double *ptr); }; From a05084123dec1e56574dcf9458d6a4d418544931 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Wed, 24 Jun 2020 09:35:12 +0530 Subject: [PATCH 05/12] Put generated file inside build --- create_dll.bat | 6 +++++- gdist.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/create_dll.bat b/create_dll.bat index d4108c66..75a8fd35 100644 --- a/create_dll.bat +++ b/create_dll.bat @@ -39,5 +39,9 @@ if NOT DEFINED VCINSTALLDIR ( echo "No compatible visual studio found! run vcvarsall.bat first!" ) -cl.exe /LD /DDLL_EXPORTS /DNDEBUG gdist_c_api.cpp +mkdir build\lib.win32 + +cd build\lib.win32 + +cl.exe /LD /DDLL_EXPORTS /DNDEBUG ..\..\gdist_c_api.cpp ls diff --git a/gdist.py b/gdist.py index 1d4d14eb..0ed8787e 100644 --- a/gdist.py +++ b/gdist.py @@ -8,7 +8,7 @@ if sys.platform == 'win32': - libfile = glob.glob('gdist_c_api.dll')[0] + libfile = glob.glob('build/*/gdist_c_api.dll')[0] libfile = os.path.abspath(libfile) lib = ctypes.windll.LoadLibrary(libfile) elif sys.platform == 'darwin': From a56de8f8f69b264d14ff11ec09d8cc187b3e73b4 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Wed, 24 Jun 2020 09:44:19 +0530 Subject: [PATCH 06/12] Lint fixes --- gdist.py | 23 +++++++++++++---------- setup.py | 2 -- tests/test_gdist.py | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/gdist.py b/gdist.py index 0ed8787e..8322bcd9 100644 --- a/gdist.py +++ b/gdist.py @@ -62,7 +62,7 @@ def compute_gdist( number_of_target_indices, source_indices_array, target_indices_array, - distance_limit + distance_limit, ): target_indices_size = target_indices_array.size distance = np.empty(target_indices_size, dtype=np.float64) @@ -76,7 +76,7 @@ def compute_gdist( source_indices_array, target_indices_array, distance, - distance_limit + distance_limit, ) return distance @@ -95,11 +95,14 @@ def local_gdist_matrix( vertices, triangles, ctypes.byref(sparse_matrix_size), - max_distance + max_distance, ) - np_data = np.fromiter(data, dtype=np.float64, - count=3 * sparse_matrix_size.value) + np_data = np.fromiter( + data, + dtype=np.float64, + count=3 * sparse_matrix_size.value, + ) lib.free_memory(data) return np_data @@ -126,7 +129,7 @@ def compute_gdist( number_of_target_indices=target_indices.size, source_indices_array=source_indices, target_indices_array=target_indices, - distance_limit=max_distance + distance_limit=max_distance, ) return np.fromiter(distance, dtype=np.float64, count=target_indices.size) @@ -134,7 +137,7 @@ def compute_gdist( def local_gdist_matrix( vertices, triangles, - max_distance=1e100 + max_distance=1e100, ): vertices = vertices.ravel() triangles = triangles.ravel() @@ -145,12 +148,12 @@ def local_gdist_matrix( triangles.size, vertices, triangles, - max_distance + max_distance, ) sizes = data.size // 3 rows = data[:sizes] - columns = data[sizes: 2*sizes] - data = data[2*sizes:] + columns = data[sizes: 2 * sizes] + data = data[2 * sizes:] return scipy.sparse.csc_matrix( (data, (rows, columns)), shape=(vertices.size // 3, vertices.size // 3) diff --git a/setup.py b/setup.py index 9e2bf9fd..93547a24 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,6 @@ """ import os -import shutil import sys import setuptools @@ -61,7 +60,6 @@ ] INCLUDE_DIRS = [ - # numpy.get_include(), # NumPy dtypes "geodesic_library", # geodesic distance, C++ library. ] diff --git a/tests/test_gdist.py b/tests/test_gdist.py index 44f094f3..16d180fc 100644 --- a/tests/test_gdist.py +++ b/tests/test_gdist.py @@ -39,7 +39,7 @@ def test_flat_triangular_mesh(self): vertices = data[0:121].astype(np.float64) triangles = data[121:].astype(np.int32) distances = gdist.local_gdist_matrix(vertices, triangles) - epsilon = 1e-6 # the default value used in `assert_array_almost_equal` + epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 np.testing.assert_array_almost_equal(distances.toarray()[1][0], 0.2) @@ -54,7 +54,7 @@ def test_hedgehog_mesh(self): vertices = data[0:300].astype(np.float64) triangles = data[300:].astype(np.int32) distances = gdist.local_gdist_matrix(vertices, triangles) - epsilon = 1e-6 # the default value used in `assert_array_almost_equal` + epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 np.testing.assert_array_almost_equal( From 51e28b5214054962044eb354987b4b145bfba217 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Wed, 1 Jul 2020 19:03:52 +0530 Subject: [PATCH 07/12] Test equality with stable version --- .travis.yml | 13 +- alternative_geodesic.py | 224 -------------- create_dll.bat | 2 +- gdist.py | 1 + gdist.pyx | 278 ------------------ geodesic.py | 196 ------------ .../gdist_c_api.cpp | 2 +- .../gdist_c_api.h | 2 +- geodesic_library/geodesic_algorithm_exact.h | 4 +- linux_so.sh | 5 + macos_dylib.sh | 5 + setup.py | 6 +- 12 files changed, 28 insertions(+), 710 deletions(-) delete mode 100644 alternative_geodesic.py delete mode 100644 gdist.pyx delete mode 100644 geodesic.py rename gdist_c_api.cpp => geodesic_library/gdist_c_api.cpp (95%) rename gdist_c_api.h => geodesic_library/gdist_c_api.h (96%) create mode 100644 linux_so.sh create mode 100644 macos_dylib.sh diff --git a/.travis.yml b/.travis.yml index b362b90e..58d1914e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,23 +2,28 @@ notifications: email: false install: - - python setup.py build_ext --inplace - pip3 install . script: - - pip3 install pytest pytest-cov - - pytest --cov=gdist + - pip3 install nose2 nose2-cov + - nose2 --with-coverage jobs: include: - name: "Python 3.8 on Xenial Linux" language: python python: 3.8 - before_install: pip3 install codecov + before_install: + - pip3 install codecov + - sudo chmod +x linux_so.sh + - ./linux_so.sh after_success: codecov - name: "Python 3.7.4 on macOS" os: osx osx_image: xcode11.2 language: shell + before_install: + - chmod 755 macos_dylib.sh + - ./macos_dylib.sh - name: "Python 3.8.0 on Windows" os: windows language: shell diff --git a/alternative_geodesic.py b/alternative_geodesic.py deleted file mode 100644 index 4ae05aa3..00000000 --- a/alternative_geodesic.py +++ /dev/null @@ -1,224 +0,0 @@ -# -*- coding: utf-8 -*- -# -# -# TheVirtualBrain-Framework Package. This package holds all Data Management, and -# Web-UI helpful to run brain-simulations. To use it, you also need do download -# TheVirtualBrain-Scientific Package (for simulators). See content of the -# documentation-folder for more details. See also http://www.thevirtualbrain.org -# -# (c) 2012-2020, Baycrest Centre for Geriatric Care ("Baycrest") and others -# -# This program is free software: you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the Free Software Foundation, -# either version 3 of the License, or (at your option) any later version. -# This program is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public License for more details. -# You should have received a copy of the GNU General Public License along with this -# program. If not, see . -# -# -# CITATION: -# When using The Virtual Brain for scientific publications, please cite it as follows: -# -# Paula Sanz Leon, Stuart A. Knock, M. Marmaduke Woodman, Lia Domide, -# Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) -# The Virtual Brain: a simulator of primate brain network dynamics. -# Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) -# -# - -""" -Translation of the C++ geodesic library to Python - -mw 18/10/2013 - -""" - -import time -import numpy - -# geodesic_constants_and_simple_functions.h -GEODESIC_INF = 1e100 -SMALLEST_INTERVAL_RATIO = 1e-6 - - -def cos_from_edges(a, b, c): - assert all(a > 1e-50) and all(b > 1e-50) and all(c > 1e-50) - return numpy.clip((b * b + c * c - a * a) / (2.0 * b * c), -1.0, 1.0) - - -def angle_from_edges(a, b, c): - return numpy.arccos(cos_from_edges(a, b, c)) - - -def read_mesh_from(filename): - raise NotImplemented - - -# geodesic_mesh_elements.h -class MeshElement(object): - def __init__(self): - self.adjacent_vertices = [] - self.adjacent_faces = [] - self.adjacent_edges = [] - - -class Point3D(object): - x, y, z = 0., 0., 0. - - def distance(self, v): - x, y, z = v - dx, dy, dz = self.x - x, self.y - y, self.z - z - return numpy.sqrt(dx * dx + dy * dy + dz * dz) - - def set(self, x, y, z): - self.x, self.y, self.z = 0., 0., 0. - - def iadd(self, v): - self.x += v[0] - self.y += v[1] - self.z += v[2] - - def imul(self, v): - self.x *= v - self.y *= v - self.z *= v - - -class Vertex(MeshElement, Point3D): - saddle_or_boundary = None - - -class Face(MeshElement): - corner_angles = [None] * 3 - - def opposite_edge(vertex): - raise NotImplemented - - def opposite_vertex(edge): - raise NotImplemented - - def next_edge(edge, vertex): - raise NotImplemented - - def vertex_angle(self, vertex): - for v, a in zip(self.adjacent_vertices, self.corner_angles): - if v == vertex: - return a - - -class Edge(MeshElement): - length = 0.0 - - def opposite_face(self, face): - raise NotImplemented - - def opposite_vertex(self, vertex): - raise NotImplemented - - def belongs(self, vertex): - raise NotImplemented - - def is_boundary(self): - return 1 == len(self.adjacent_faces) - - def local_coordinates(point, x, y): - raise NotImplemented - - -class SurfacePoint(Point3D): - """ - A point lying anywhere on the surface of the mesh - - """ - - def __init__(self, p3, a=0.5): - if isinstance(p3, Vertex): - self.p = p3 - elif isinstance(p3, Face): - self.set(0., 0., 0.) - [self.iadd(vi) for vi in p3.adjacent_vertices] - self.imul(1. / 3) - elif isinstance(p3, Edge): - b = 1 - a - v0 = p3.adjacent_vertices[0] - v1 = p3.adjacent_vertices[1] - self.x = b * v0.x + a * v1.x - self.y = b * v0.y + a * v1.y - self.z = b * v0.z + a * v1.z - - -class HalfEdge(object): - # ints in C++ - face_id, vertex_0, vertex_1 = None, None, None - - def __lt__(x, y): - if (x.vertex_0 == y.vertex_0): - return x.vertex_1 < y.vertex_1 - else: - return x.vertex_0 < y.vertex_0 - - def __ne__(x, y): - return x.vertex_0 != y.vertex_0 or x.vertex_1 != y.vertex_1 - - def __eq__(x, y): - return x.vertex_0 == y.vertex_0 and x.vertex_1 == y.vertex_1 - - -class SurfacePath(object): - # std::vector& path - path = [] - - def length(self): - raise NotImplemented - - def print_info_about_path(self): - raise NotImplemented - - -# geodesic_algorithm_base.h - - -class Base(object): - """ - Base algorithm, from geodesic_algorithm_base.h - - """ - - def __init__(self): - self.max_propagation_distance = 1e100 - self.mesh = None - - def propagate(self, sources, max_propagation_distance, stop_points): - raise NotImplemented - - def trace_back(self, destination, path): - raise NotImplemented - - def geodesic(self, source, destination, path): - raise NotImplemented - - def best_source(self, point, distance): - raise NotImplemented - - def print_statistics(self): - raise NotImplemented - - def set_stop_conditions(self, stop_points, stop_distance): - raise NotImplemented - - def stop_distance(self): - return self.max_propagation_distance - - -class Dijkstra(Base): - pass - - -class Subdivision(Base): - pass - - -class Exact(Base): - pass diff --git a/create_dll.bat b/create_dll.bat index 75a8fd35..aa89808d 100644 --- a/create_dll.bat +++ b/create_dll.bat @@ -43,5 +43,5 @@ mkdir build\lib.win32 cd build\lib.win32 -cl.exe /LD /DDLL_EXPORTS /DNDEBUG ..\..\gdist_c_api.cpp +cl.exe /LD /DDLL_EXPORTS ..\..\geodesic_library\gdist_c_api.cpp ls diff --git a/gdist.py b/gdist.py index 8322bcd9..a635a94e 100644 --- a/gdist.py +++ b/gdist.py @@ -150,6 +150,7 @@ def local_gdist_matrix( triangles, max_distance, ) + assert data.size % 3 == 0 sizes = data.size // 3 rows = data[:sizes] columns = data[sizes: 2 * sizes] diff --git a/gdist.pyx b/gdist.pyx deleted file mode 100644 index a2898c05..00000000 --- a/gdist.pyx +++ /dev/null @@ -1,278 +0,0 @@ -# -*- coding: utf-8 -*- -# cython: language_level=3 -# -# -# TheVirtualBrain-Framework Package. This package holds all Data Management, and -# Web-UI helpful to run brain-simulations. To use it, you also need do download -# TheVirtualBrain-Scientific Package (for simulators). See content of the -# documentation-folder for more details. See also http://www.thevirtualbrain.org -# -# (c) 2012-2020, Baycrest Centre for Geriatric Care ("Baycrest") and others -# -# This program is free software: you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the Free Software Foundation, -# either version 3 of the License, or (at your option) any later version. -# This program is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public License for more details. -# You should have received a copy of the GNU General Public License along with this -# program. If not, see . -# -# -# CITATION: -# When using The Virtual Brain for scientific publications, please cite it as follows: -# -# Paula Sanz Leon, Stuart A. Knock, M. Marmaduke Woodman, Lia Domide, -# Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) -# The Virtual Brain: a simulator of primate brain network dynamics. -# Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) -# -# - -""" -This module defines a Cython wrapper for the geodesic distance C++ library. -The algorithm (implemented in C++) extends Mitchell, Mount and Papadimitriou -(1987) and was written by Danil Kirsanov -(http://code.google.com/archive/p/geodesic/). - -The interface definitions and wrapper functions are written in Cython syntax -(http://cython.org/) and provide an API for some of the classes, functions and -constants useful for calculating the geodesic distance. - -To compile, and build gdist.so using Cython:: - - python setup.py build_ext --inplace - -.. moduleauthor:: Gaurav Malhotra -.. moduleauthor:: Stuart A. Knock - -""" - -#For compatible datatypes -import numpy -cimport numpy - -# For csc_matrix returned by local_gdist_matrix() -import scipy.sparse - -# Pre-cdef'd containers from the C++ standard library -from libcpp.vector cimport vector - -################################################################################ -############ Defininitions to access the C++ geodesic distance library ######### -################################################################################ -cdef extern from "geodesic_mesh_elements.h" namespace "geodesic": - cdef cppclass Vertex: - Vertex() - -cdef extern from "geodesic_mesh_elements.h" namespace "geodesic": - cdef cppclass SurfacePoint: - SurfacePoint() - SurfacePoint(Vertex*) - double& x() - double& y() - double& z() - -cdef extern from "geodesic_mesh.h" namespace "geodesic": - cdef cppclass Mesh: - Mesh() - void initialize_mesh_data(vector[double]&, vector[unsigned]&) - vector[Vertex]& vertices() - -cdef extern from "geodesic_algorithm_exact.h" namespace "geodesic": - cdef cppclass GeodesicAlgorithmExact: - GeodesicAlgorithmExact(Mesh*) - void propagate(vector[SurfacePoint]&, double, vector[SurfacePoint]*) - unsigned best_source(SurfacePoint&, double&) - -cdef extern from "geodesic_constants_and_simple_functions.h" namespace "geodesic": - double GEODESIC_INF -################################################################################ - - -cdef get_mesh( - numpy.ndarray[numpy.float64_t, ndim=2] vertices, - numpy.ndarray[numpy.int32_t, ndim=2] triangles, - Mesh &amesh -): - # Define C++ vectors to contain the mesh surface components. - cdef vector[double] points - cdef vector[unsigned] faces - - # Map numpy array of mesh "vertices" to C++ vector of mesh "points" - cdef numpy.float64_t coord - for coord in vertices.flatten(): - points.push_back(coord) - - # Map numpy array of mesh "triangles" to C++ vector of mesh "faces" - cdef numpy.int32_t indx - for indx in triangles.flatten(): - faces.push_back(indx) - - amesh.initialize_mesh_data(points, faces) - - -def compute_gdist(numpy.ndarray[numpy.float64_t, ndim=2] vertices, - numpy.ndarray[numpy.int32_t, ndim=2] triangles, - numpy.ndarray[numpy.int32_t, ndim=1] source_indices = None, - numpy.ndarray[numpy.int32_t, ndim=1] target_indices = None, - double max_distance = GEODESIC_INF): - """ - This is the wrapper function for computing geodesic distance between a set - of sources and targets on a mesh surface. This function accepts five - arguments: - ``vertices``: defines x,y,z coordinates of the mesh's vertices. - ``triangles``: defines faces of the mesh as index triplets into vertices. - ``source_indices``: Index of the source on the mesh. - ``target_indices``: Index of the targets on the mesh. - ``max_distance``: - and returns a numpy.ndarray((len(target_indices), ), dtype=numpy.float64) - specifying the shortest distance to the target vertices from the nearest - source vertex on the mesh. If no target_indices are provided, all vertices - of the mesh are considered as targets, however, in this case, specifying - max_distance will limit the targets to those vertices within max_distance of - a source. - - NOTE: This is the function to use when specifying localised stimuli and - parameter variations. For efficiently using the whole mesh as sources, such - as is required to represent local connectivity within the cortex, see the - local_gdist_matrix() function. - - Basic usage then looks like:: - >>> import numpy - >>> temp = numpy.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) - >>> vertices = temp[0:121].astype(numpy.float64) - >>> triangles = temp[121:321].astype(numpy.int32) - >>> src = numpy.array([1], dtype=numpy.int32) - >>> trg = numpy.array([2], dtype=numpy.int32) - >>> import gdist - >>> gdist.compute_gdist(vertices, triangles, source_indices = src, target_indices = trg) - array([ 0.2]) - - """ - - cdef Mesh amesh - get_mesh(vertices, triangles, amesh) - - # Define and create object for exact algorithm on that mesh: - cdef GeodesicAlgorithmExact *algorithm = new GeodesicAlgorithmExact(&amesh) - - # Define a C++ vector for the source vertices - cdef vector[SurfacePoint] all_sources - - # Define a C++ vector for the target vertices - cdef vector[SurfacePoint] stop_points - - # Define a NumPy array to hold the results - cdef numpy.ndarray[numpy.float64_t, ndim=1] distances - - cdef Py_ssize_t k - - if source_indices is None: #Default to 0 - source_indices = numpy.arange(0, dtype=numpy.int32) - # Map the NumPy sources of targets to a C++ vector - for k in source_indices: - all_sources.push_back(SurfacePoint(&amesh.vertices()[k])) - - if target_indices is None: - # Propagate purely on max_distance - algorithm.propagate(all_sources, max_distance, NULL) - # Make all vertices targets - # NOTE: this is inefficient in the best_source step below, but not sure - # how to avoid. - target_indices = numpy.arange(vertices.shape[0], dtype=numpy.int32) - # Map the NumPy array of targets to a C++ vector - for k in target_indices: - stop_points.push_back(SurfacePoint(&amesh.vertices()[k])) - else: - # Map the NumPy array of targets to a C++ vector - for k in target_indices: - stop_points.push_back(SurfacePoint(&amesh.vertices()[k])) - # Propagate to the specified targets - algorithm.propagate(all_sources, max_distance, &stop_points) - - distances = numpy.zeros((len(target_indices), ), dtype=numpy.float64) - for k in range(stop_points.size()): - algorithm.best_source(stop_points[k], distances[k]) - - distances[distances==GEODESIC_INF] = numpy.inf - - return distances - - -def local_gdist_matrix(numpy.ndarray[numpy.float64_t, ndim=2] vertices, - numpy.ndarray[numpy.int32_t, ndim=2] triangles, - double max_distance = GEODESIC_INF): - """ - This is the wrapper function for computing geodesic distance from every - vertex on the surface to all those within a distance ``max_distance`` of - them. The function accepts three arguments: - ``vertices``: defines x,y,z coordinates of the mesh's vertices - ``triangles``: defines faces of the mesh as index triplets into vertices. - ``max_distance``: - and returns a scipy.sparse.csc_matrix((N, N), dtype=numpy.float64), where N - is the number of vertices, specifying the shortest distance from all - vertices to all the vertices within max_distance. - - Basic usage then looks like:: - >>> import numpy - >>> temp = numpy.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) - >>> import gdist - >>> vertices = temp[0:121].astype(numpy.float64) - >>> triangles = temp[121:321].astype(numpy.int32) - >>> gdist.local_gdist_matrix(vertices, triangles, max_distance=1.0) - <121x121 sparse matrix of type '' - with 6232 stored elements in Compressed Sparse Column format> - - Runtime and guesstimated memory usage as a function of max_distance for the - reg_13 cortical surface mesh, ie containing 2**13 vertices per hemisphere. - :: - [[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], # mm - [19, 28, 49, 81, 125, 181, 248, 331, 422, 522], # s - [ 3, 13, 30, 56, 89, 129, 177, 232, 292, 358]] # MB] - - where memory is a min-guestimate given by: mem_req = nnz * 8 / 1024 / 1024 - - NOTE: The best_source loop could be sped up considerably by restricting - targets to those vertices within max_distance of the source, however, - this will first require the efficient extraction of this information - from the propgate step... - """ - - cdef Mesh amesh - get_mesh(vertices, triangles, amesh) - - # Define and create object for exact algorithm on that mesh: - cdef GeodesicAlgorithmExact *algorithm = new GeodesicAlgorithmExact(&amesh) - - cdef vector[SurfacePoint] source, targets - cdef Py_ssize_t N = vertices.shape[0] - cdef Py_ssize_t k - cdef Py_ssize_t kk - cdef numpy.float64_t distance = 0 - - # Add all vertices as targets - for k in range(N): - targets.push_back(SurfacePoint(&amesh.vertices()[k])) - - rows = [] - columns = [] - data = [] - for k in range(N): - source.push_back(SurfacePoint(&amesh.vertices()[k])) - algorithm.propagate(source, max_distance, NULL) - source.pop_back() - - for kk in range(N): #TODO: Reduce to vertices reached during propagate. - algorithm.best_source(targets[kk], distance) - - if ( - distance is not GEODESIC_INF - and distance is not 0 - and distance <= max_distance - ): - rows.append(k) - columns.append(kk) - data.append(distance) - - return scipy.sparse.csc_matrix((data, (rows, columns)), shape=(N, N)) diff --git a/geodesic.py b/geodesic.py deleted file mode 100644 index c3f452b9..00000000 --- a/geodesic.py +++ /dev/null @@ -1,196 +0,0 @@ - -""" -Translation of the C++ geodesic library to Python - -mw 18/10/2013 - -""" - -import time -import numpy - -# geodesic_constants_and_simple_functions.h -GEODESIC_INF = 1e100 -SMALLEST_INTERVAL_RATIO = 1e-6 - - -def cos_from_edges(a, b, c): - assert all(a > 1e-50) and all(b > 1e-50) and all(c > 1e-50) - return numpy.clip((b*b + c*c - a*a) / (2.0 * b * c), -1.0, 1.0) - - -def angle_from_edges(a, b, c): - return numpy.arccos(cos_from_edges(a, b, c)) - - -def read_mesh_from(filename): - raise NotImplemented - -# geodesic_mesh_elements.h - - -class MeshElement(object): - def __init__(self): - self.adjacent_vertices = [] - self.adjacent_faces = [] - self.adjacent_edges = [] - - -class Point3D(object): - x, y, z = 0., 0., 0. - - def distance(self, v): - x, y, z = v - dx, dy, dz = self.x - x, self.y - y, self.z - z - return numpy.sqrt(dx*dx + dy*dy + dz*dz) - - def set(self, x, y, z): - self.x, self.y, self.z = 0., 0., 0. - - def iadd(self, v): - self.x += v[0] - self.y += v[1] - self.z += v[2] - - def imul(self, v): - self.x *= v - self.y *= v - self.z *= v - - -class Vertex(MeshElement, Point3D): - saddle_or_boundary = None - - -class Face(MeshElement): - corner_angles = [None]*3 - - def opposite_edge(vertex): - raise NotImplemented - - def opposite_vertex(edge): - raise NotImplemented - - def next_edge(edge, vertex): - raise NotImplemented - - def vertex_angle(self, vertex): - for v, a in zip(self.adjacent_vertices, self.corner_angles): - if v == vertex: - return a - - -class Edge(MeshElement): - length = 0.0 - - def opposite_face(self, face): - raise NotImplemented - - def opposite_vertex(self, vertex): - raise NotImplemented - - def belongs(self, vertex): - raise NotImplemented - - def is_boundary(self): - return 1 == len(self.adjacent_faces) - - def local_coordinates(point, x, y): - raise NotImplemented - - -class SurfacePoint(Point3D): - """ - A point lying anywhere on the surface of the mesh - - """ - - def __init__(self, p3, a=0.5): - if isinstance(p3, Vertex): - self.p = p3 - elif isinstance(p3, Face): - self.set(0., 0., 0.) - [self.iadd(vi) for vi in p3.adjacent_vertices] - self.imul(1./3) - elif isinstance(p3, Edge): - b = 1 - a - v0 = p3.adjacent_vertices[0] - v1 = p3.adjacent_vertices[1] - self.x = b*v0.x + a*v1.x - self.y = b*v0.y + a*v1.y - self.z = b*v0.z + a*v1.z - - -class HalfEdge(object): - # ints in C++ - face_id, vertex_0, vertex_1 = None, None, None - - def __lt__(x, y): - if (x.vertex_0 == y.vertex_0): - return x.vertex_1 < y.vertex_1 - else: - return x.vertex_0 < y.vertex_0 - - def __ne__(x, y): - return x.vertex_0 != y.vertex_0 or x.vertex_1 != y.vertex_1 - - def __eq__(x, y): - return x.vertex_0 == y.vertex_0 and x.vertex_1 == y.vertex_1 - - -class SurfacePath(object): - # std::vector& path - path = [] - - def length(self): - raise NotImplemented - - def print_info_about_path(self): - raise NotImplemented - - -# geodesic_algorithm_base.h - - -class Base(object): - """ - Base algorithm, from geodesic_algorithm_base.h - - """ - - def __init__(self): - self.max_propagation_distance = 1e100 - self.mesh = None - - def propagate(self, sources, max_propagation_distance, stop_points): - raise NotImplemented - - def trace_back(self, destination, path): - raise NotImplemented - - def geodesic(self, source, destination, path): - raise NotImplemented - - def best_source(self, point, distance): - raise NotImplemented - - def print_statistics(self): - raise NotImplemented - - def set_stop_conditions(self, stop_points, stop_distance): - raise NotImplemented - - def stop_distance(self): - return self.max_propagation_distance - - -class Dijkstra(Base): - pass - - -class Subdivision(Base): - pass - - -class Exact(Base): - pass diff --git a/gdist_c_api.cpp b/geodesic_library/gdist_c_api.cpp similarity index 95% rename from gdist_c_api.cpp rename to geodesic_library/gdist_c_api.cpp index 56358712..8f0bd226 100644 --- a/gdist_c_api.cpp +++ b/geodesic_library/gdist_c_api.cpp @@ -81,7 +81,7 @@ double* local_gdist_matrix_impl( double *data; data = new double[3 * rows_vector.size()]; - + assert (data != NULL); // memory allocation should not fail *sparse_matrix_size = rows_vector.size(); std::copy(rows_vector.begin(), rows_vector.end(), data); diff --git a/gdist_c_api.h b/geodesic_library/gdist_c_api.h similarity index 96% rename from gdist_c_api.h rename to geodesic_library/gdist_c_api.h index dfd2a7fc..68447be9 100644 --- a/gdist_c_api.h +++ b/geodesic_library/gdist_c_api.h @@ -2,7 +2,7 @@ #include #include -#include "geodesic_library/geodesic_algorithm_exact.h" +#include "geodesic_algorithm_exact.h" #if defined(_WIN32) diff --git a/geodesic_library/geodesic_algorithm_exact.h b/geodesic_library/geodesic_algorithm_exact.h index fad8c417..0428bf7e 100644 --- a/geodesic_library/geodesic_algorithm_exact.h +++ b/geodesic_library/geodesic_algorithm_exact.h @@ -1140,7 +1140,7 @@ inline void GeodesicAlgorithmExact::construct_propagated_intervals(bool invert, p->min() = 0.0; //it will be changed later on - assert(p->start() < p->stop()); + // assert(p->start() < p->stop()); } } else //now we have to invert the intervals @@ -1163,7 +1163,7 @@ inline void GeodesicAlgorithmExact::construct_propagated_intervals(bool invert, p->min() = 0; - assert(p->start() < p->stop()); + // assert(p->start() < p->stop()); assert(p->start() >= 0.0); assert(p->stop() <= edge->length()); } diff --git a/linux_so.sh b/linux_so.sh new file mode 100644 index 00000000..be95d998 --- /dev/null +++ b/linux_so.sh @@ -0,0 +1,5 @@ +mkdir build +cd build +mkdir lib.linux +cd lib.linux +g++ -std=c++11 -shared -fPIC ../../geodesic_library/gdist_c_api.cpp -o gdist_c_api.so diff --git a/macos_dylib.sh b/macos_dylib.sh new file mode 100644 index 00000000..1f84f4f3 --- /dev/null +++ b/macos_dylib.sh @@ -0,0 +1,5 @@ +mkdir build +cd build +mkdir lib.macos +cd lib.macos +clang++ -std=c++11 -shared -fPIC ../../geodesic_library/gdist_c_api.cpp -o gdist_c_api.dylib diff --git a/setup.py b/setup.py index 93547a24..1a46cbf0 100644 --- a/setup.py +++ b/setup.py @@ -52,8 +52,8 @@ GEODESIC_MODULE = [ setuptools.Extension( name=GEODESIC_NAME, # Name of extension - sources=["gdist_c_api.cpp"], - language="c++", + sources=['geodesic_library/gdist_c_api.cpp'], + language='c++', extra_compile_args=['--std=c++11'], extra_link_args=['--std=c++11'], ) @@ -75,7 +75,7 @@ version='2.0.2', scripts=['gdist.py'], py_modules=['gdist'], - ext_modules=GEODESIC_MODULE, + # ext_modules=GEODESIC_MODULE, include_dirs=INCLUDE_DIRS, install_requires=INSTALL_REQUIREMENTS, description="Compute geodesic distances", From 085c469d66903bf1b10314c6e30314511cf67be2 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Thu, 2 Jul 2020 22:02:00 +0530 Subject: [PATCH 08/12] Remove excess macOS job --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 58d1914e..5e035c0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,10 +33,6 @@ jobs: - pip install --user wheel - ./create_dll.bat env: PATH=/c/Python38:/c/Python38/Scripts:$PATH - - name: "Python 3.7.4 on macOS" - os: osx - osx_image: xcode11.2 - language: shell - stage: lint language: python python: 3.8 From 1479a22aee726bef11b258f4a74b489c7fc51fa6 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Tue, 7 Jul 2020 14:09:58 +0530 Subject: [PATCH 09/12] Use pytest again --- .travis.yml | 4 ++-- create_dll.bat | 2 +- gdist.py | 14 +++++++------- geodesic_library/gdist_c_api.cpp | 4 ++-- geodesic_library/gdist_c_api.h | 4 ++-- tests/test_equality_with_stable.py | 19 +++++++++++++++++++ tests/test_gdist.py | 16 ++++++++-------- 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e035c0a..b3c67a5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ notifications: install: - pip3 install . script: - - pip3 install nose2 nose2-cov - - nose2 --with-coverage + - pip3 install pytest pytest-cov + - pytest --cov=gdist jobs: include: diff --git a/create_dll.bat b/create_dll.bat index aa89808d..7230c8c3 100644 --- a/create_dll.bat +++ b/create_dll.bat @@ -43,5 +43,5 @@ mkdir build\lib.win32 cd build\lib.win32 -cl.exe /LD /DDLL_EXPORTS ..\..\geodesic_library\gdist_c_api.cpp +cl.exe /LD /DDLL_EXPORTS /EHsc ..\..\geodesic_library\gdist_c_api.cpp ls diff --git a/gdist.py b/gdist.py index a635a94e..bc11a3f7 100644 --- a/gdist.py +++ b/gdist.py @@ -10,26 +10,26 @@ if sys.platform == 'win32': libfile = glob.glob('build/*/gdist_c_api.dll')[0] libfile = os.path.abspath(libfile) - lib = ctypes.windll.LoadLibrary(libfile) + lib = ctypes.CDLL(libfile) elif sys.platform == 'darwin': try: libfile = glob.glob('build/*/gdist*.so')[0] except IndexError: libfile = glob.glob('build/*/gdist*.dylib')[0] - lib = ctypes.cdll.LoadLibrary(libfile) + lib = ctypes.CDLL(libfile) else: libfile = glob.glob('build/*/gdist*.so')[0] - lib = ctypes.cdll.LoadLibrary(libfile) + lib = ctypes.CDLL(libfile) lib.compute_gdist.argtypes = [ ctypes.c_uint, ctypes.c_uint, np.ctypeslib.ndpointer(dtype=np.float64), - np.ctypeslib.ndpointer(dtype=np.int32), + np.ctypeslib.ndpointer(dtype=np.uint32), ctypes.c_uint, ctypes.c_uint, - np.ctypeslib.ndpointer(dtype=np.int32), - np.ctypeslib.ndpointer(dtype=np.int32), + np.ctypeslib.ndpointer(dtype=np.uint32), + np.ctypeslib.ndpointer(dtype=np.uint32), np.ctypeslib.ndpointer(dtype=np.float64), ctypes.c_double, ] @@ -39,7 +39,7 @@ ctypes.c_uint, ctypes.c_uint, np.ctypeslib.ndpointer(dtype=np.float64), - np.ctypeslib.ndpointer(dtype=np.int32), + np.ctypeslib.ndpointer(dtype=np.uint32), ctypes.POINTER(ctypes.c_uint), ctypes.c_double, ] diff --git a/geodesic_library/gdist_c_api.cpp b/geodesic_library/gdist_c_api.cpp index 8f0bd226..2bbb1923 100644 --- a/geodesic_library/gdist_c_api.cpp +++ b/geodesic_library/gdist_c_api.cpp @@ -5,7 +5,7 @@ void compute_gdist_impl( unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, - int *triangles, + unsigned *triangles, unsigned number_of_source_indices, unsigned number_of_target_indices, unsigned *source_indices_array, @@ -102,7 +102,7 @@ extern "C" { unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, - int *triangles, + unsigned *triangles, unsigned number_of_source_indices, unsigned number_of_target_indices, unsigned *source_indices_array, diff --git a/geodesic_library/gdist_c_api.h b/geodesic_library/gdist_c_api.h index 68447be9..c31f106f 100644 --- a/geodesic_library/gdist_c_api.h +++ b/geodesic_library/gdist_c_api.h @@ -20,7 +20,7 @@ void compute_gdist_impl( unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, - int *triangles, + unsigned *triangles, unsigned number_of_source_indices, unsigned number_of_target_indices, unsigned *source_indices_array, @@ -45,7 +45,7 @@ extern "C" { unsigned number_of_vertices, unsigned number_of_triangles, double *vertices, - int *triangles, + unsigned *triangles, unsigned number_of_source_indices, unsigned number_of_target_indices, unsigned *source_indices_array, diff --git a/tests/test_equality_with_stable.py b/tests/test_equality_with_stable.py index 594a6fc5..02a37370 100644 --- a/tests/test_equality_with_stable.py +++ b/tests/test_equality_with_stable.py @@ -4,6 +4,7 @@ def test_equality_with_stable(): +<<<<<<< HEAD surface_datas = ['inner_skull_642', 'outer_skull_642', 'scalp_1082'] for surface_data in surface_datas: expected = np.loadtxt( @@ -22,3 +23,21 @@ def test_equality_with_stable(): ) actual = actual.toarray() np.testing.assert_array_almost_equal(actual, expected) +======= + surface_data = 'inner_skull_642' + expected = np.loadtxt(f'data/{surface_data}/gdist_matrix.txt') + vertices = np.loadtxt( + f'data/{surface_data}/vertices.txt', + dtype=np.float64, + ) + triangles = np.loadtxt( + f'data/{surface_data}/triangles.txt', + dtype=np.uint32, + ) + actual = gdist.local_gdist_matrix( + vertices=vertices, + triangles=triangles, + ) + actual = actual.toarray() + np.testing.assert_array_almost_equal(actual, expected) +>>>>>>> ed49909... Use pytest again diff --git a/tests/test_gdist.py b/tests/test_gdist.py index 16d180fc..f32e7339 100644 --- a/tests/test_gdist.py +++ b/tests/test_gdist.py @@ -7,9 +7,9 @@ class TestComputeGdist(): def test_flat_triangular_mesh(self): data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) vertices = data[0:121].astype(np.float64) - triangles = data[121:].astype(np.int32) - source = np.array([1], dtype=np.int32) - target = np.array([2], dtype=np.int32) + triangles = data[121:].astype(np.uint32) + source = np.array([1], dtype=np.uint32) + target = np.array([2], dtype=np.uint32) distance = gdist.compute_gdist( vertices, triangles, @@ -21,9 +21,9 @@ def test_flat_triangular_mesh(self): def test_hedgehog_mesh(self): data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) vertices = data[0:300].astype(np.float64) - triangles = data[300:].astype(np.int32) - source = np.array([0], dtype=np.int32) - target = np.array([1], dtype=np.int32) + triangles = data[300:].astype(np.uint32) + source = np.array([0], dtype=np.uint32) + target = np.array([1], dtype=np.uint32) distance = gdist.compute_gdist( vertices, triangles, @@ -37,7 +37,7 @@ class TestLocalGdistMatrix: def test_flat_triangular_mesh(self): data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) vertices = data[0:121].astype(np.float64) - triangles = data[121:].astype(np.int32) + triangles = data[121:].astype(np.uint32) distances = gdist.local_gdist_matrix(vertices, triangles) epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric @@ -52,7 +52,7 @@ def test_flat_triangular_mesh(self): def test_hedgehog_mesh(self): data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) vertices = data[0:300].astype(np.float64) - triangles = data[300:].astype(np.int32) + triangles = data[300:].astype(np.uint32) distances = gdist.local_gdist_matrix(vertices, triangles) epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric From 4ca35c42aaa45eb0209b89c2f03346e9c5ffa492 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Tue, 7 Jul 2020 14:22:19 +0530 Subject: [PATCH 10/12] Back to nose2 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3c67a5b..5e035c0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ notifications: install: - pip3 install . script: - - pip3 install pytest pytest-cov - - pytest --cov=gdist + - pip3 install nose2 nose2-cov + - nose2 --with-coverage jobs: include: From e677b682398ec54d57c1989b842becb25e9dcd49 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Tue, 14 Jul 2020 22:22:09 +0530 Subject: [PATCH 11/12] Fix merge conflict --- tests/test_equality_with_stable.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tests/test_equality_with_stable.py b/tests/test_equality_with_stable.py index 02a37370..62581776 100644 --- a/tests/test_equality_with_stable.py +++ b/tests/test_equality_with_stable.py @@ -4,26 +4,6 @@ def test_equality_with_stable(): -<<<<<<< HEAD - surface_datas = ['inner_skull_642', 'outer_skull_642', 'scalp_1082'] - for surface_data in surface_datas: - expected = np.loadtxt( - f'data/surface_data/{surface_data}/gdist_matrix.txt') - vertices = np.loadtxt( - f'data/surface_data/{surface_data}/vertices.txt', - dtype=np.float64, - ) - triangles = np.loadtxt( - f'data/surface_data/{surface_data}/triangles.txt', - dtype=np.int32, - ) - actual = gdist.local_gdist_matrix( - vertices=vertices, - triangles=triangles, - ) - actual = actual.toarray() - np.testing.assert_array_almost_equal(actual, expected) -======= surface_data = 'inner_skull_642' expected = np.loadtxt(f'data/{surface_data}/gdist_matrix.txt') vertices = np.loadtxt( @@ -40,4 +20,3 @@ def test_equality_with_stable(): ) actual = actual.toarray() np.testing.assert_array_almost_equal(actual, expected) ->>>>>>> ed49909... Use pytest again From 120c1b537fef87c11dee760583125193b24fe099 Mon Sep 17 00:00:00 2001 From: ayan-b Date: Tue, 14 Jul 2020 22:27:40 +0530 Subject: [PATCH 12/12] Fix merge conflicts --- tests/test_equality_with_stable.py | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/test_equality_with_stable.py b/tests/test_equality_with_stable.py index 62581776..f8285631 100644 --- a/tests/test_equality_with_stable.py +++ b/tests/test_equality_with_stable.py @@ -4,19 +4,21 @@ def test_equality_with_stable(): - surface_data = 'inner_skull_642' - expected = np.loadtxt(f'data/{surface_data}/gdist_matrix.txt') - vertices = np.loadtxt( - f'data/{surface_data}/vertices.txt', - dtype=np.float64, - ) - triangles = np.loadtxt( - f'data/{surface_data}/triangles.txt', - dtype=np.uint32, - ) - actual = gdist.local_gdist_matrix( - vertices=vertices, - triangles=triangles, - ) - actual = actual.toarray() - np.testing.assert_array_almost_equal(actual, expected) + surface_datas = ['inner_skull_642', 'outer_skull_642', 'scalp_1082'] + for surface_data in surface_datas: + expected = np.loadtxt( + f'data/surface_data/{surface_data}/gdist_matrix.txt') + vertices = np.loadtxt( + f'data/surface_data/{surface_data}/vertices.txt', + dtype=np.float64, + ) + triangles = np.loadtxt( + f'data/surface_data/{surface_data}/triangles.txt', + dtype=np.uint32, + ) + actual = gdist.local_gdist_matrix( + vertices=vertices, + triangles=triangles, + ) + actual = actual.toarray() + np.testing.assert_array_almost_equal(actual, expected)