Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast linear 2d interpolation #48

Merged
merged 68 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
7667602
add fast parallel linear 2d interpolation
alexeybelkov Dec 15, 2023
c2ac951
Easier compilation
vovaf709 Dec 18, 2023
f68f495
version
vovaf709 Dec 18, 2023
8f6d996
Unclude into mkdocs
vovaf709 Dec 18, 2023
1845c8d
Fix
vovaf709 Dec 18, 2023
7fbc824
Fix?
vovaf709 Dec 18, 2023
c9fca00
Fix?
vovaf709 Dec 18, 2023
5bd2ee2
Fix?
vovaf709 Dec 18, 2023
a2d92cd
Fix?
vovaf709 Dec 19, 2023
3a6a804
Fix?
vovaf709 Dec 19, 2023
2f8e599
Fix for Windows
vovaf709 Dec 19, 2023
f0f82ee
ls
vovaf709 Dec 19, 2023
6c368da
Fix?
vovaf709 Dec 19, 2023
575cd81
quotes
vovaf709 Dec 19, 2023
1c46c46
Fix?
vovaf709 Dec 19, 2023
8bfc459
Better manifest & old install.sh
vovaf709 Dec 19, 2023
87fbfbf
stacklevel
vovaf709 Nov 27, 2023
4f79c2d
g++-11
vovaf709 Dec 19, 2023
d6d730d
Fix
vovaf709 Dec 19, 2023
ba31716
ls
vovaf709 Dec 19, 2023
0ca56a4
g++-11 for linux
vovaf709 Dec 19, 2023
cabc9b8
Fix
vovaf709 Dec 19, 2023
fbcaa76
sudo :ok_boomer:
vovaf709 Dec 19, 2023
c8d8e6e
Fix
vovaf709 Dec 19, 2023
633f939
which
vovaf709 Dec 19, 2023
7ec06f3
Fix
vovaf709 Dec 19, 2023
f6dbca5
Fix
vovaf709 Dec 19, 2023
7bf52e5
Fix
vovaf709 Dec 19, 2023
6ce64de
Back to default
vovaf709 Dec 19, 2023
e4988f9
Fix
vovaf709 Dec 19, 2023
65ae5aa
ls
vovaf709 Dec 20, 2023
a8d39f0
Another try to use g++-11
vovaf709 Dec 20, 2023
3aa420a
Try g++ installer
vovaf709 Dec 20, 2023
1aa4e85
dolla
vovaf709 Dec 20, 2023
959b442
Maybe fix
vovaf709 Dec 20, 2023
ce3d7b7
Maybe ist about py36
vovaf709 Dec 21, 2023
d63069e
Try ln
vovaf709 Dec 21, 2023
6bfa222
sudo
vovaf709 Dec 21, 2023
806b939
py39 ok, what about py36?
vovaf709 Dec 21, 2023
b1ff9e7
Seems like its working
vovaf709 Dec 21, 2023
3567f1c
add predefined triangulation, -Ofast compilation
alexeybelkov Dec 22, 2023
a47c6d3
add test draft and test data
alexeybelkov Dec 24, 2023
a08a23f
error handling :monkax:
alexeybelkov Dec 24, 2023
069e164
Compressed test data, ablate `n_jobs` in tests
vovaf709 Dec 24, 2023
a03f989
Get rid of warnings
vovaf709 Dec 24, 2023
e0e4ec2
`deli` in reqs
vovaf709 Dec 24, 2023
7305046
Account for old numpy
vovaf709 Dec 24, 2023
d3533ff
beauty
vovaf709 Dec 24, 2023
997db2a
Fix for py36
vovaf709 Dec 24, 2023
a0cd64b
mkdocs-friendly docstrings
vovaf709 Dec 24, 2023
3fce9c7
Manage `num_threads`
vovaf709 Dec 24, 2023
4bd851d
Add interp2d benchmark
vovaf709 Dec 24, 2023
7014acc
Fix
vovaf709 Dec 24, 2023
aa04268
Not needed
vovaf709 Dec 25, 2023
dff0601
More tests
vovaf709 Dec 25, 2023
b9d4045
Update readme
vovaf709 Dec 25, 2023
0150e70
Fix some comments
vovaf709 Dec 25, 2023
28cf83c
some comments fixed
alexeybelkov Dec 25, 2023
1b7578d
more comments fixed
alexeybelkov Dec 25, 2023
b5c453a
Fix
vovaf709 Dec 26, 2023
af824fa
microfix
vovaf709 Dec 26, 2023
ebb5e1c
comments fixed
alexeybelkov Dec 26, 2023
45ccac9
authors :cool-doge:
alexeybelkov Dec 26, 2023
7a4f4ff
licenses :monkax:
alexeybelkov Dec 26, 2023
57c42b4
TypeError fix
alexeybelkov Dec 26, 2023
89b208a
microcommit
alexeybelkov Dec 26, 2023
bf46aa6
test fixes
alexeybelkov Dec 26, 2023
9ba891e
No need for `TypeError`
vovaf709 Dec 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
BasedOnStyle: Google
IndentWidth: 4
AccessModifierOffset: -4
ColumnLimit: 100
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
DerivePointerAlignment: true
KeepEmptyLinesAtTheStartOfBlocks: true
SortIncludes: false
47 changes: 47 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
Checks: '-*,cppcoreguidelines-avoid-goto,cppcoreguidelines-pro-type-const-cast, google-readability-casting, google-runtime-int, modernize-replace-random-shuffle, modernize-use-nullptr, readability-braces-around-statements, readability-container-size-empty, readability-redundant-control-flow, readability-redundant-string-init, readability-identifier-naming, google-build-using-namespace'
HeaderFilterRegex: '\.h$'
WarningsAsErrors: '*'
CheckOptions:
- key: readability-identifier-naming.NamespaceCase
value: lower_case
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.TypedefCase
value: CamelCase
- key: readability-identifier-naming.TypeAliasCase
value: CamelCase
- key: readability-identifier-naming.PrivateMemberSuffix
value: '_'
- key: readability-identifier-naming.StructCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: CamelCase
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberCase
value: lower_case
- key: readability-identifier-naming.ParameterCase
value: lower_case
- key: readability-identifier-naming.GlobalConstantPrefix
value: k
- key: readability-identifier-naming.GlobalConstantCase
value: CamelCase
- key: readability-identifier-naming.StaticConstantPrefix
value: k
- key: readability-identifier-naming.StaticConstantCase
value: CamelCase
- key: readability-identifier-naming.ConstexprVariableCase
value: CamelCase
- key: readability-identifier-naming.ConstexprVariablePrefix
value: k
- key: google-runtime-int.TypeSuffix
value: _t
- key: readability-identifier-naming.TypeTemplateParameterCase
value: CamelCase
- key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp
value: expr-type
- key: readability-identifier-naming.FunctionIgnoredRegexp
value: ^(begin|end)$
- key: readability-identifier-naming.TypeAliasIgnoredRegexp
value: ^(iterator_category|value_type|difference_type|pointer|reference|iterator_type|iterator_concept)$
20 changes: 19 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,26 @@ jobs:
run: |
brew install llvm libomp
echo $PATH
ln -sf /usr/local/bin/gcc-12 /usr/local/bin/gcc
ln -sf /usr/local/bin/gcc-11 /usr/local/bin/gcc
ln -sf /usr/local/bin/g++-11 /usr/local/bin/g++
ls /usr/local/bin/gcc*
ls /usr/local/bin/g++*
gcc --version
g++ --version
- name: Install g++-11 for ubuntu
if: matrix.os == 'ubuntu-20.04'
id: install_cc
uses: rlalik/setup-cpp-compiler@master
with:
compiler: g++-11
- name: Check compilers for ubuntu
if: matrix.os == 'ubuntu-20.04'
run: |
ls /usr/bin/gcc*
ls /usr/bin/g++*
sudo ln -sf /usr/bin/gcc-11 /usr/bin/gcc
sudo ln -sf /usr/bin/g++-11 /usr/bin/g++
g++ --version
gcc --version
- name: Build wheels
run: |
Expand Down
20 changes: 19 additions & 1 deletion .github/workflows/test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,26 @@ jobs:
run: |
brew install llvm libomp
echo $PATH
ln -sf /usr/local/bin/gcc-12 /usr/local/bin/gcc
ln -sf /usr/local/bin/gcc-11 /usr/local/bin/gcc
ln -sf /usr/local/bin/g++-11 /usr/local/bin/g++
ls /usr/local/bin/gcc*
ls /usr/local/bin/g++*
gcc --version
g++ --version
- name: Install g++-11 for ubuntu
if: matrix.os == 'ubuntu-20.04'
id: install_cc
uses: rlalik/setup-cpp-compiler@master
with:
compiler: g++-11
- name: Check compilers for ubuntu
if: matrix.os == 'ubuntu-20.04'
run: |
ls /usr/bin/gcc*
ls /usr/bin/g++*
sudo ln -sf /usr/bin/gcc-11 /usr/bin/gcc
sudo ln -sf /usr/bin/g++-11 /usr/bin/g++
g++ --version
gcc --version
- name: Build wheels
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ imops/src/_fast*.pyx
*.egg-info/
.asv/
dist/
*.so
.vscode/
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ include requirements.txt
include pyproject.toml
include _build_utils.py
recursive-include imops *.py
recursive-include imops/cpp *.h *.hpp *.cpp
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ Works faster only for `ndim<=4, dtype=float32 or float64 (and bool-int16-32-64 i
from imops import interp1d # same as `scipy.interpolate.interp1d`
```
Works faster only for `ndim<=3, dtype=float32 or float64, order=1`

### Fast 2d linear interpolation
```python
import numpy as np
from imops.interp2d import Linear2DInterpolator
n, m = 1024, 2
points = np.random.randint(low=0, high=1024, size=(n, m))
points = np.unique(points, axis=0)
x_points = points[: n // 2]
values = np.random.uniform(low=0.0, high=1.0, size=(len(x_points),))
interp_points = points[n // 2:]
num_threads = -1 # will be equal to num of CPU cores
# You can optionally pass your own triangulation as an np.array of shape [num_triangles, 3], element at (i, j) position is an index of a point from x_points
interpolator = Linear2DInterpolator(x_points, values, num_threads=num_threads, triangles=None)
# Also you can pass values to __call__ and rewrite the ones that were passed to __init__
interp_values = interpolator(interp_points, values + 1.0, fill_value=0.0)
```

### Fast binary morphology

```python
Expand Down
29 changes: 22 additions & 7 deletions _build_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
from setuptools.command.build_py import build_py


class NumpyImport(dict):
"""Hacky way to return Numpy's `include` path with lazy import."""
class LazyImport(dict):
"""Hacky way to return any module's `include` path with lazy import."""

# Must be json-serializable due to
# https://github.com/cython/cython/blob/6ad6ca0e9e7d030354b7fe7d7b56c3f6e6a4bc23/Cython/Compiler/ModuleNode.py#L773
def __init__(self):
def __init__(self, module_name):
self.module_name = module_name
return super().__init__(self, description=self.__doc__)

# Must be hashable due to
Expand All @@ -20,9 +21,10 @@ def __hash__(self):
return id(self)

def __repr__(self):
import numpy as np
scope = {}
exec(f'import {self.module_name} as module', scope)

return np.get_include()
return scope['module'].get_include()

__fspath__ = __repr__

Expand Down Expand Up @@ -56,21 +58,25 @@ def get_ext_modules():
name = 'imops'
on_windows = platform.system() == 'Windows'
args = ['/openmp' if on_windows else '-fopenmp']
cpp_args = [
'/std:c++20' if on_windows else '-std=c++2a',
'/O3' if on_windows else '-O3',
] # FIXME: account for higher gcc versions

# Cython extension and .pyx source file names must be the same to compile
# https://stackoverflow.com/questions/8024805/cython-compiled-c-extension-importerror-dynamic-module-does-not-define-init-fu
modules = ['backprojection', 'measure', 'morphology', 'numeric', 'radon', 'zoom']
modules_to_link_against_numpy_core_math_lib = ['numeric']

src_dir = Path(__file__).parent / name / 'src'
for module in modules:
src_dir = Path(__file__).parent / name / 'src'
shutil.copyfile(src_dir / f'_{module}.pyx', src_dir / f'_fast_{module}.pyx')

return [
Extension(
f'{name}.src._{prefix}{module}',
[f'{name}/src/_{prefix}{module}.pyx'],
include_dirs=[NumpyImport()],
include_dirs=[LazyImport('numpy')],
library_dirs=[NumpyLibImport()] if module in modules_to_link_against_numpy_core_math_lib else [],
libraries=['npymath'] + ['m'] * (not on_windows)
if module in modules_to_link_against_numpy_core_math_lib
Expand All @@ -84,4 +90,13 @@ def get_ext_modules():
# fallback to standard `-O2` compiled versions until https://github.com/neuro-ml/imops/issues/37 is resolved
# for prefix, additional_args in zip(['', 'fast_'], [[], ['-ffast-math']])
for prefix, additional_args in zip(['', 'fast_'], [[], []])
] + [
Extension(
f'{name}.cpp.cpp_modules',
[f'{name}/cpp/main.cpp'],
include_dirs=[LazyImport('pybind11')],
extra_compile_args=args + cpp_args,
extra_link_args=args + cpp_args,
language='c++',
)
]
3 changes: 2 additions & 1 deletion asv.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"scikit-image": [],
"numba": [],
"pip+connected-components-3d": [],
"pip+fastremap": []
"pip+fastremap": [],
"pybind11": []
},
"env_nobuild": {
"OMP_NUM_THREADS": "8",
Expand Down
41 changes: 41 additions & 0 deletions benchmarks/benchmark_interp2d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from pathlib import Path

import numpy as np
from scipy.interpolate import LinearNDInterpolator

from imops.interp2d import Linear2DInterpolator

from .common import NUMS_THREADS_TO_BENCHMARK, discard_arg, load_npy_gz


class Interp2dSuite:
params = [('C++', 'Scipy'), ('float32', 'float64'), NUMS_THREADS_TO_BENCHMARK]
param_names = ['backend', 'dtype', 'num_threads']

@discard_arg(1)
@discard_arg(-1)
def setup(self, dtype):
x = load_npy_gz(Path(__file__).parent / 'data' / 'ribs.npy.gz').astype(dtype)
add_cols = x.shape[1] // 4
distances = np.concatenate((x[..., -add_cols:], x, x[..., :add_cols]), axis=1)
self.int_points = np.transpose((np.isnan(distances)).nonzero())
self.x_points = np.transpose((~np.isnan(distances)).nonzero())
self.x_values = distances[~np.isnan(distances)]

@discard_arg(2)
def time_interp2d(self, backend, num_threads):
if backend == 'C++':
Linear2DInterpolator(self.x_points, self.x_values, num_threads=num_threads)(self.int_points, fill_value=0.0)
elif backend == 'Scipy':
LinearNDInterpolator(self.x_points, self.x_values)(self.int_points)
else:
raise NotImplementedError

@discard_arg(2)
def peakmem_interp2d(self, backend, num_threads):
if backend == 'C++':
Linear2DInterpolator(self.x_points, self.x_values, num_threads=num_threads)(self.int_points, fill_value=0.0)
elif backend == 'Scipy':
LinearNDInterpolator(self.x_points, self.x_values)(self.int_points)
else:
raise NotImplementedError
Binary file added benchmarks/data/ribs.npy.gz
Binary file not shown.
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ pip install imops[numba] # additionally install Numba backend
members:
- __call__

::: imops.interp2d.Linear2DInterpolator
options:
members:
- __call__

::: imops.morphology.binary_dilation

::: imops.morphology.binary_erosion
Expand Down
2 changes: 1 addition & 1 deletion imops/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.8.4'
__version__ = '0.8.5'
33 changes: 33 additions & 0 deletions imops/cpp/interp2d/delaunator/delaunator-header-only.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// MIT License

// Copyright (c) 2018 Volodymyr Bilonenko

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.



#pragma once

#define DELAUNATOR_HEADER_ONLY

#include "delaunator.hpp"

#include "delaunator.cpp"

#undef DELAUNATOR_HEADER_ONLY
Loading
Loading