Skip to content

Commit

Permalink
API: Make odlpp an external library
Browse files Browse the repository at this point in the history
* Makes odlpp a plugin
* Changes all tight cuplings to odlpp to be loose cuplings, thus forcing interface changes in many places.
* `FnBase` and `NtuplesBase` type spaces can now be added to ODL by external packages through the `odl.space` hook. This allows users to add new implementations to the odl spaces.
* Changed `Rn` etc syntax to be `rn`, added `impl` parameter. Now any `rn` type space can be created using `odl.rn(3, impl)`
* In line with the above, all numpy spaces are now named `NumpyFn` etc.
  • Loading branch information
adler-j committed Jun 29, 2016
1 parent b8b4eb4 commit 6d7de6c
Show file tree
Hide file tree
Showing 52 changed files with 1,298 additions and 4,197 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "odlpp"]
path = odlpp
url = https://github.com/odlgroup/odlpp.git
[submodule "doc/sphinxext"]
path = doc/sphinxext
url = https://github.com/odlgroup/numpydoc
19 changes: 16 additions & 3 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,10 @@

import pytest
import odl
from odl.space.cu_ntuples import CUDA_AVAILABLE
from odl.trafos.wavelet import PYWAVELETS_AVAILABLE

collect_ignore = ['setup.py', 'run_tests.py']

if not CUDA_AVAILABLE:
collect_ignore.append('odl/space/cu_ntuples.py')
if not PYWAVELETS_AVAILABLE:
collect_ignore.append('odl/trafos/wavelet.py')

Expand All @@ -43,6 +40,22 @@ def pytest_addoption(parser):


# reusable fixtures
fn_impl_params = odl.FN_IMPLS.keys()
fn_impl_ids = [" impl='{}' ".format(p) for p in fn_impl_params]


@pytest.fixture(scope="module", ids=fn_impl_ids, params=fn_impl_params)
def fn_impl(request):
return request.param

ntuples_impl_params = odl.NTUPLES_IMPLS.keys()
ntuples_impl_ids = [" impl='{}' ".format(p) for p in ntuples_impl_params]


@pytest.fixture(scope="module", ids=ntuples_impl_ids, params=ntuples_impl_params)
def ntuples_impl(request):
return request.param

ufunc_params = [ufunc for ufunc in odl.util.ufuncs.UFUNCS]
ufunc_ids = [' ufunc={} '.format(p[0]) for p in ufunc_params]

Expand Down
23 changes: 23 additions & 0 deletions doc/source/dev/extend.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#############
Extending ODL
#############

ODL is written to be easy to extend with new functionality and classes, and new content is welcome. With that said, not everything fits inside the main library and some ideas are better done as *extension packages*. This may give your package more freedom and allows a faster development cycle and help keep ODL from overflowing with content.

There are several ways to extend ODL, some will be listed below.

Adding Fn spaces
----------------
The abstract spaces `FnBase` and `NtuplesBase` are the workhorses of the ODL space machinery. They are used in both the discrete :math:`R^n` case, as well as data representation for discretized function spaces such as :math:`L^2([0, 1])` in the `DiscretizedSpace` class. These are in general created through the `rn` and `uniform_discr` functions who take an ``impl`` parameter, allowing users to select the backend to use.

In the core ODL package, there is only a single backend available: `NumpyFn`/`NumpyNtuples`, given by ``impl='numpy'``, which is the default choice. Users can add CUDA support by installing the add-on library odlcuda_, which contains the additional spaces ``CudaFn``/``CudaNtuples``. By using the `rn`/`uniform_discr` functions, users can then seamlessly change the backend of their spaces.

As an advanced user, you may need to add additional spaces of this type that can be used inside ODL, perhaps to add MPI_ support. There are a few steps to do this:

* Create a new library with a ``setuptools`` installer.
* Add the spaces that you want to add to the library. The space needs to inherit from `NtuplesBase` or `FnBase`, respectively, and implement all of the abstract methods in those spaces. See the spaces for further information on the specific methods that need to be implemented.
* Add the methods ``ntuples_impls()`` and ``fn_impls()`` to a file ``odl_plugin.py`` in your library. These should return a ``dict`` mapping names to implementations.
* Add the following to your library's ``setup.py`` setup call: ``entry_points={'odl.space': ['odl_cuda = odlcuda.odl_plugin']``, where you replace ``odlcuda`` with the name of your plugin.

.. _odlcuda: https://github.com/odlgroup/odlcuda
.. _MPI: https://en.wikipedia.org/wiki/Message_Passing_Interface
18 changes: 18 additions & 0 deletions doc/source/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@
Release Notes
#############

ODL 0.3.0 Release Notes (2016-06-29)
====================================

This release marks the removal of odlpp form the core library. It has instead been moved to a separate library, odlcuda. To enable cuda backends for the odl spaces, an entry point ``'odl.space'`` has been added where external libraries can hook in to add `FnBase` and `NtuplesBase` type spaces.

New features
------------
- Added entry point ``'odl.space'``
- Add fixtures ``'fn_impl'`` and ``'ntuple_impl'`` to the test config ``conf.py``. These can now be accessed from any test.
- Allow creation of general spaces using the ``fn``, ``cn`` and ``rn`` methods. This functions now take an ``impl`` parameter which defaults to ``'numpy'`` but with odlcuda installed it may also be set to ``'cuda'``. The old numpy specific ``Fn``, ``Cn`` and ``Rn`` functions have been removed.

Changes
-------
- Moved all CUDA specfic code out of the library into ``odlcuda``. This means that ``cu_ntuples.py`` and related files have been removed.
- rename ``ntuples.py`` to ``npy_ntuples.py``.
- Added ``Numpy`` to the numy based spaces. They are now named ``NumpyFn`` and ``NumpyNtuples``.
- Prepended ``npy_`` to all methods specific to ``ntuples`` such as weightings.

ODL 0.2.4 Release Notes (2016-06-28)
====================================

Expand Down
11 changes: 5 additions & 6 deletions examples/diagnostics/diagonstics_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from future import standard_library
standard_library.install_aliases()

# Internal
import odl

print('\n\n TESTING FOR Lp SPACE \n\n')
Expand All @@ -32,18 +31,18 @@

print('\n\n TESTING FOR Rn SPACE \n\n')

spc = odl.Rn(10)
spc = odl.rn(10)
odl.diagnostics.SpaceTest(spc).run_tests()


print('\n\n TESTING FOR Cn SPACE \n\n')

spc = odl.Cn(10)
spc = odl.cn(10)
odl.diagnostics.SpaceTest(spc).run_tests()


if odl.CUDA_AVAILABLE:
print('\n\n TESTING FOR CudaRn SPACE \n\n')
if 'cuda' in odl.FN_IMPLS:
print('\n\n TESTING FOR CUDA Rn SPACE \n\n')

spc = odl.CudaRn(10)
spc = odl.rn(10, impl='cuda')
odl.diagnostics.SpaceTest(spc, eps=0.0001).run_tests()
82 changes: 0 additions & 82 deletions examples/space/cudaspace_speedtest.py

This file was deleted.

26 changes: 13 additions & 13 deletions examples/space/simple_rn.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,23 @@ def asarray(self, *args):
return self.data(*args)

r5 = SimpleRn(5)
odl.diagnostics.SpaceTest(r5).run_tests()
#odl.diagnostics.SpaceTest(r5).run_tests()

# Do some tests to compare
n = 10**7
iterations = 10

# Perform some benchmarks with Rn adn CudaRn
opt_spc = odl.Rn(n)
opt_spc = odl.rn(n)
simple_spc = SimpleRn(n)

x, y, z = np.random.rand(n), np.random.rand(n), np.random.rand(n)
ox, oy, oz = (opt_spc.element(x.copy()), opt_spc.element(y.copy()),
opt_spc.element(z.copy()))
sx, sy, sz = (simple_spc.element(x.copy()), simple_spc.element(y.copy()),
simple_spc.element(z.copy()))
if odl.CUDA_AVAILABLE:
cu_spc = odl.CudaRn(n)
if 'cuda' in odl.FN_IMPLS:
cu_spc = odl.rn(n, impl='cuda')
cx, cy, cz = (cu_spc.element(x.copy()), cu_spc.element(y.copy()),
cu_spc.element(z.copy()))

Expand All @@ -114,13 +114,13 @@ def asarray(self, *args):
simple_spc.lincomb(2.13, sx, 3.14, sy, out=sz)
print("result: {}".format(sz[1:5]))

with Timer("Rn"):
with Timer("odl numpy"):
for _ in range(iterations):
opt_spc.lincomb(2.13, ox, 3.14, oy, out=oz)
print("result: {}".format(oz[1:5]))

if odl.CUDA_AVAILABLE:
with Timer("CudaRn"):
if 'cuda' in odl.FN_IMPLS:
with Timer("odl cuda"):
for _ in range(iterations):
cu_spc.lincomb(2.13, cx, 3.14, cy, out=cz)
print("result: {}".format(cz[1:5]))
Expand All @@ -132,13 +132,13 @@ def asarray(self, *args):
result = sz.norm()
print("result: {}".format(result))

with Timer("Rn"):
with Timer("odl numpy"):
for _ in range(iterations):
result = oz.norm()
print("result: {}".format(result))

if odl.CUDA_AVAILABLE:
with Timer("CudaRn"):
if 'cuda' in odl.FN_IMPLS:
with Timer("odl cuda"):
for _ in range(iterations):
result = cz.norm()
print("result: {}".format(result))
Expand All @@ -150,13 +150,13 @@ def asarray(self, *args):
result = sz.inner(sx)
print("result: {}".format(result))

with Timer("Rn"):
with Timer("odl numpy"):
for _ in range(iterations):
result = oz.inner(ox)
print("result: {}".format(result))

if odl.CUDA_AVAILABLE:
with Timer("CudaRn"):
if 'cuda' in odl.FN_IMPLS:
with Timer("odl cuda"):
for _ in range(iterations):
result = cz.inner(cx)
print("result: {}".format(result))
2 changes: 1 addition & 1 deletion odl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from __future__ import absolute_import

__version__ = '0.2.4'
__version__ = '0.3.0'
__all__ = ('diagnostics', 'discr', 'operator', 'set', 'space', 'solvers',
'tomo', 'trafos', 'util', 'phantom')

Expand Down
4 changes: 2 additions & 2 deletions odl/diagnostics/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,6 @@ def __repr__(self):


if __name__ == '__main__':
from odl import Rn, uniform_discr
SpaceTest(Rn(10)).run_tests()
from odl import rn, uniform_discr
SpaceTest(rn(10)).run_tests()
SpaceTest(uniform_discr([0, 0], [1, 1], [5, 5])).run_tests()
26 changes: 13 additions & 13 deletions odl/discr/discr_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,11 @@ def _call(self, func, out=None, **kwargs):
Partition the rectangle by a tensor grid:
>>> from odl import TensorGrid, Rectangle, RectPartition, Rn
>>> rect = Rectangle([1, 3], [2, 5])
>>> grid = TensorGrid([1, 2], [3, 4, 5])
>>> partition = RectPartition(rect, grid)
>>> rn = Rn(grid.size)
>>> import odl
>>> rect = odl.Rectangle([1, 3], [2, 5])
>>> grid = odl.TensorGrid([1, 2], [3, 4, 5])
>>> partition = odl.RectPartition(rect, grid)
>>> rn = odl.rn(grid.size)
Finally create the operator and test it on a function:
Expand All @@ -255,18 +255,18 @@ def _call(self, func, out=None, **kwargs):
... # Properly vectorized function
>>> func_elem = funcset.element(lambda x: x[0] - x[1])
>>> coll_op(func_elem)
Rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
>>> coll_op(lambda x: x[0] - x[1]) # Works directly
Rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
>>> out = Rn(6).element()
rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
>>> out = odl.rn(6).element()
>>> coll_op(func_elem, out=out) # In-place
Rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
rn(6).element([-2.0, -3.0, -4.0, -1.0, -2.0, -3.0])
Fortran ordering:
>>> coll_op = PointCollocation(funcset, partition, rn, order='F')
>>> coll_op(func_elem)
Rn(6).element([-2.0, -1.0, -3.0, -2.0, -4.0, -3.0])
rn(6).element([-2.0, -1.0, -3.0, -2.0, -4.0, -3.0])
"""
mesh = self.grid.meshgrid
if out is None:
Expand Down Expand Up @@ -362,7 +362,7 @@ def _call(self, x, out=None):
Parameters
----------
x : `NtuplesVector`
x : `NtuplesBaseVector`
The array of values to be interpolated
out : `FunctionSetVector`, optional
Vector in which to store the interpolator
Expand Down Expand Up @@ -402,13 +402,13 @@ def _call(self, x, out=None):
Partitioning the domain uniformly with no nodes on the boundary
(will shift the grid points):
>>> from odl import uniform_partition_fromintv, Ntuples
>>> from odl import uniform_partition_fromintv, ntuples
>>> part = uniform_partition_fromintv(rect, [4, 2],
... nodes_on_bdry=False)
>>> part.grid.coord_vectors
(array([ 0.125, 0.375, 0.625, 0.875]), array([ 0.25, 0.75]))
>>> dspace = Ntuples(part.size, dtype='U1')
>>> dspace = ntuples(part.size, dtype='U1')
Now we initialize the operator and test it with some points:
Expand Down
Loading

0 comments on commit 6d7de6c

Please sign in to comment.