diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab9decf --- /dev/null +++ b/.gitignore @@ -0,0 +1,140 @@ +curvelops/_version.py + +# Documentation +docssrc/build +docssrc/source/api/generated +docssrc/source/gallery + +# Editors +.*.sw[po] +.vscode/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/Makefile b/Makefile index 7e40e39..0309eca 100644 --- a/Makefile +++ b/Makefile @@ -34,16 +34,6 @@ tests: make pytestcheck $(PYTEST) tests -doc: - cd docs && rm -rf build && sphinx-apidoc -f -M -o source/ ../curvelops && make html && cd - - # Add -P to sphinx-apidoc to include private files - -watchdoc: - while inotifywait -q -r curvelops/ -e create,delete,modify; do { make doc; }; done - -servedoc: - $(PYTHON) -m http.server --directory docs/build/html/ - lint: flake8 docs/ curvelops/ tests/ @@ -52,3 +42,27 @@ typeannot: coverage: coverage run -m pytest && coverage xml && coverage html && $(PYTHON) -m http.server --directory htmlcov/ + +watchdoc: + make doc && while inotifywait -q -r curvelops/ docssrc/source/ -e create,delete,modify; do { make docupdate; }; done + +servedoc: + $(PYTHON) -m http.server --directory docssrc/build/html/ + +doc: + # Add after rm: sphinx-apidoc -f -M -o source/ ../curvelops + # Use -O to include private files + cd docssrc && rm -rf source/api/generated && rm -rf source/gallery &&\ + rm -rf source/tutorials && rm -rf source/examples &&\ + rm -rf build && make html && cd .. + +docupdate: + cd docssrc && make html && cd .. + +docgithub: + cd docssrc && make github && cd .. + +docpush: + git checkout gh-pages && git merge main && cd docssrc && make github &&\ + cd ../docs && git add . && git commit -m "Updated documentation" &&\ + git push origin gh-pages && git checkout main diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/source/curvelops.plot.rst b/docs/source/curvelops.plot.rst deleted file mode 100644 index 58c2835..0000000 --- a/docs/source/curvelops.plot.rst +++ /dev/null @@ -1,7 +0,0 @@ -curvelops.plot package -====================== - -.. automodule:: curvelops.plot - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/curvelops.rst b/docs/source/curvelops.rst deleted file mode 100644 index 9013086..0000000 --- a/docs/source/curvelops.rst +++ /dev/null @@ -1,44 +0,0 @@ -curvelops package -================= - -.. automodule:: curvelops - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - curvelops.plot - curvelops.typing - curvelops.utils - -Submodules ----------- - -curvelops.curvelops module --------------------------- - -.. automodule:: curvelops.curvelops - :members: - :undoc-members: - :show-inheritance: - -curvelops.fdct2d\_wrapper module --------------------------------- - -.. automodule:: curvelops.fdct2d_wrapper - :members: - :undoc-members: - :show-inheritance: - -curvelops.fdct3d\_wrapper module --------------------------------- - -.. automodule:: curvelops.fdct3d_wrapper - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/curvelops.typing.rst b/docs/source/curvelops.typing.rst deleted file mode 100644 index 624f1da..0000000 --- a/docs/source/curvelops.typing.rst +++ /dev/null @@ -1,7 +0,0 @@ -curvelops.typing package -======================== - -.. automodule:: curvelops.typing - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/curvelops.utils.rst b/docs/source/curvelops.utils.rst deleted file mode 100644 index 6d0b104..0000000 --- a/docs/source/curvelops.utils.rst +++ /dev/null @@ -1,7 +0,0 @@ -curvelops.utils package -======================= - -.. automodule:: curvelops.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index f320720..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. curvelops documentation master file, created by - sphinx-quickstart on Sun Nov 15 14:04:06 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to curvelops's documentation! -===================================== - -Python wrapper for `CurveLab `_'s 2D and 3D curvelet transforms. It uses the `PyLops `_ design framework to provide the forward and inverse curvelet transforms as matrix-free linear operations. Check out `some examples `_ or view the documentation below. - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - modules.rst - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/source/modules.rst b/docs/source/modules.rst deleted file mode 100644 index a6edbce..0000000 --- a/docs/source/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -curvelops -========= - -.. toctree:: - :maxdepth: 4 - - curvelops diff --git a/docs/Makefile b/docssrc/Makefile similarity index 79% rename from docs/Makefile rename to docssrc/Makefile index 69fe55e..02ef6d8 100644 --- a/docs/Makefile +++ b/docssrc/Makefile @@ -1,9 +1,13 @@ # Minimal makefile for Sphinx documentation # +# Disable numba +# export NUMBA_DISABLE_JIT=1 + # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build +SPHINXPROJ = curvelops SOURCEDIR = source BUILDDIR = build @@ -16,4 +20,9 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +# Make for github pages +github: + @make html + @cp -a build/html/. ../docs diff --git a/docs/source/conf.py b/docssrc/source/conf.py similarity index 81% rename from docs/source/conf.py rename to docssrc/source/conf.py index 8f5db2b..852b0a6 100644 --- a/docs/source/conf.py +++ b/docssrc/source/conf.py @@ -16,6 +16,8 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) +from sphinx_gallery.sorting import ExampleTitleSortKey + from curvelops import __version__ as version release = version @@ -43,6 +45,8 @@ "sphinx.ext.ifconfig", "sphinx.ext.viewcode", "sphinx.ext.intersphinx", + "sphinx_gallery.gen_gallery", + "sphinx_copybutton", ] # intersphinx configuration @@ -85,7 +89,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = "pydata_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -191,3 +195,29 @@ # -- Extension configuration ------------------------------------------------- autodoc_typehints = "none" + +sphinx_gallery_conf = { + "examples_dirs": "../../examples", # path to your example scripts + "gallery_dirs": "gallery", # path to where to save gallery generated output + "filename_pattern": r"\.py", + # Remove the "Download all examples" button from the top level gallery + "download_all_examples": False, + # Sort gallery example by file name instead of number of lines (default) + "within_subsection_order": ExampleTitleSortKey, + # directory where function granular galleries are stored + "backreferences_dir": "api/generated/backreferences", + # Modules for which function level galleries are created. + "doc_module": "curvelops", + # Insert links to documentation of objects in the examples + "reference_url": {"curvelops": None}, +} +# Always show the source code that generates a plot +plot_include_source = True +plot_formats = ["png"] +# Sphinx project configuration +templates_path = ["_templates"] +exclude_patterns = ["_build", "**.ipynb_checkpoints", "**.ipynb", "**.md5"] +source_suffix = ".rst" + +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " +copybutton_prompt_is_regexp = True diff --git a/docssrc/source/contributing.rst b/docssrc/source/contributing.rst new file mode 100644 index 0000000..3c6c3ce --- /dev/null +++ b/docssrc/source/contributing.rst @@ -0,0 +1,71 @@ +============ +Contributing +============ + +Contributions are welcome! Please submit your pull-request, issue or comment +in the `GitHub repo `__. You are also +welcome to join the `PyLops slack channel `__. + +Installation for developers +=========================== + +Developers should clone the +`main `__ branch of the +repository and install the dev requiments: + +.. code-block:: console + + $ git clone https://github.com/PyLops/curvelops + $ git remote add upstream https://github.com/PyLops/curvelops + $ make dev-install + +They should then follow the same instructions in the :ref:`Requirements` +section. We recommend installing dependencies into a separate environment. +Finally, they can install Curvelops with + +.. code-block:: console + + $ python3 -m pip install -e . + +Developers should also install `pre-commit `__ hooks with + +.. code-block:: console + + $ pre-commit install + + +Developer workflow +================== + +Developers should start from a fresh copy of main with + +.. code-block:: console + + $ git checkout main + $ git pull upstream main + +Before you start making changes, create a new branch with + +.. code-block:: console + + $ git checkout -b patch-some-cool-feature + +After implementing your cool feature (including tests 🤩), commit your changes +to kick-off the pre-commit hooks. These will reject and "fix" your code by +running the proper hooks. At this point, the user must check the changes and +then stage them before trying to commit again. + +Once changes are committed, we encourage developers to lint, check types and +build/check documentation with: + +.. code-block:: console + + $ make tests + $ make lint + $ make typeannot + $ make coverage + $ make doc + $ make servedoc + +Once everything is in order, and your code has been pushed to GitHub, +navigate to https://github.com/PyLops/curvelops and submit your PR! diff --git a/docssrc/source/index.rst b/docssrc/source/index.rst new file mode 100644 index 0000000..7eacc7e --- /dev/null +++ b/docssrc/source/index.rst @@ -0,0 +1,38 @@ +.. curvelops documentation master file, created by + sphinx-quickstart on Sun Nov 15 14:04:06 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +======== +Overview +======== + +Curvelops is part of the PyLops ecossystem, an open-source Python library +focused on providing a backend-agnostic, idiomatic, matrix-free library of +linear operators and related computations. Curvelops provides 2D and 3D +Curvelet transforms via `CurveLab `__. + +Visit :ref:`Installation` and then get started with the +`Gallery `__ or browse the +:ref:`API`. + + +.. attention:: + `CurveLab `__ is a proprietary library which must be + sourced independently by the user. It is free for academic use. This package + contains no CurveLab code apart from function calls. + +.. note:: + All CurveLab rights are reserved to Emmanuel Candes, Laurent Demanet, David + Donoho and Lexing Ying. PyLops and Curvelops are not affiliated with + CurveLab or its authors in any way. + +.. toctree:: + :maxdepth: 1 + :hidden: + + self + installation.rst + modules.rst + gallery/index.rst + contributing.rst diff --git a/docssrc/source/installation.rst b/docssrc/source/installation.rst new file mode 100644 index 0000000..fd690cc --- /dev/null +++ b/docssrc/source/installation.rst @@ -0,0 +1,69 @@ +.. _installation: + +============ +Installation +============ + +.. _requirements: + +Requirements +============ + +Installing Curvelops requires the following external components: + +* `FFTW `_ 2.1.5 +* `CurveLab `_ >= 2.0.2 + +Both of these packages must be installed manually. + +Installing FFTW +--------------- +Download and install with: + + +.. code-block:: console + + $ wget https://www.fftw.org/fftw-2.1.5.tar.gz + $ tar xvzf fftw-2.1.5.tar.gz + $ mkdir -p /home/$USER/opt/ + $ mv fftw-2.1.5/ /home/$USER/opt/ + $ cd /home/$USER/opt/fftw-2.1.5/ + $ ./configure --with-pic --prefix=/home/$USER/opt/fftw-2.1.5 --with-gcc=$(which gcc) + +The ``--prefix`` and ``--with-gcc`` are optional and determine where it will +install FFTW and where to find the GCC compiler, respectively. We recommend +using the same compiler for FFTW and CurveLab. + +Installing CurveLab +------------------- +After downloading the latest version of CurveLab, run + +.. code-block:: console + + $ tar xvzf CurveLab-2.1.3.tar.gz + $ mkdir -p /home/$USER/opt/ + $ mv CurveLab-2.1.3/ /home/$USER/opt/ + $ cd /home/$USER/opt/CurveLab-2.1.3/ + $ cp makefile.opt makefile.opt.bak + +In the file ``makefile.opt`` set ``FFTW_DIR``, ``CC`` and ``CXX`` variables. +We recommend setting ``FFTW_DIR=/home/$USER/opt/fftw-2.1.5`` +(or whatever directory was used in the ``--prefix`` option above), the output +of ``which gcc`` in CC (or whatever compiler was used in ``--with-gcc``), and +the ouput of ``which g++`` (or whatever C++ compiler is the equivalent of +the selected ``CC`` compiler). + +Installing Curvelops +==================== + +Once FFTW and CurveLab are installed, install Curvelops with: + +.. code-block:: console + + $ export FFTW=/path/to/fftw-2.1.5 + $ export FDCT=/path/to/CurveLab-2.1.3 + $ python3 -m pip install git+https://github.com/PyLops/curvelops@0.21 + +The ``FFTW`` variable is the same as ``FFTW_DIR`` as provided in the CurveLab +installation. The ``FDCT`` variable points to the root of the CurveLab +installation. diff --git a/docssrc/source/modules.rst b/docssrc/source/modules.rst new file mode 100644 index 0000000..a4573f4 --- /dev/null +++ b/docssrc/source/modules.rst @@ -0,0 +1,37 @@ +.. _API: + +=== +API +=== + +curvelops +--------- + +.. automodule:: curvelops.curvelops + :members: + :undoc-members: + :show-inheritance: + +plot +---- + +.. automodule:: curvelops.plot + :members: + :undoc-members: + :show-inheritance: + +typing +------ + +.. automodule:: curvelops.typing + :members: + :undoc-members: + :show-inheritance: + +utils +----- + +.. automodule:: curvelops.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/static/demo.png b/docssrc/source/static/demo.png similarity index 100% rename from docs/source/static/demo.png rename to docssrc/source/static/demo.png diff --git a/docs/source/static/logo.png b/docssrc/source/static/logo.png similarity index 100% rename from docs/source/static/logo.png rename to docssrc/source/static/logo.png diff --git a/docs/source/static/reconstruction.png b/docssrc/source/static/reconstruction.png similarity index 100% rename from docs/source/static/reconstruction.png rename to docssrc/source/static/reconstruction.png diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 0000000..86cb316 --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,4 @@ +Gallery +------- + +Below is a gallery of examples using curvelops. diff --git a/examples/plot_single_curvelet.py b/examples/plot_single_curvelet.py new file mode 100644 index 0000000..27a5abe --- /dev/null +++ b/examples/plot_single_curvelet.py @@ -0,0 +1,95 @@ +r""" +Single Curvelet +=============== +This example shows a single curvelet coefficient. +""" + +# %% +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.ticker import MultipleLocator +from mpl_toolkits.axes_grid1 import make_axes_locatable + +from curvelops import FDCT2D + + +# %% +def create_colorbar(im, ax): + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.1) + cb = ax.get_figure().colorbar(im, cax=cax, orientation="vertical") + return cax, cb + + +# %% [markdown] +# ### Setup + +# %% +m = 512 +n = 512 +x = np.zeros((m, n)) +DCT = FDCT2D(x.shape) + +# %% [markdown] +# ### Curvelet Domain + +# %% +y = DCT * x + +# Convert to a curvelet struct indexed by +# [scale, wedge (angle), x, y] +y_reshape = DCT.struct(y) + +# %% +# Select single curvelet +s = 4 +w = 0 +a, b = y_reshape[s][w].shape +normalization = np.sqrt(y_reshape[s][w].size) +y_reshape[s][w][a // 2, b // 2] = 1 * normalization +y_reshape[s][w + len(y_reshape[s]) // 2][a // 2, b // 2] = -1j * normalization + +y = DCT.vect(y_reshape) + +# %% +# Perform adjoint transform and reshape +x = DCT.H @ y + +# %% +# F-K domain +x_fk = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(x), norm="ortho")) + +# %% +# Visualize +vmin, vmax = 0.8 * np.array([-1, 1]) * np.abs(np.max(x)) +fig, ax = plt.subplots(2, 2, figsize=(8, 8), sharex="row", sharey="row") + +im = ax[0, 0].imshow(x.real.T, cmap="gray", vmin=vmin, vmax=vmax) +create_colorbar(im, ax[0, 0]) + +im = ax[0, 1].imshow(x.imag.T, cmap="gray", vmin=vmin, vmax=vmax) +create_colorbar(im, ax[0, 1]) + +im = ax[1, 0].imshow(np.abs(x_fk).T, cmap="turbo", vmin=0) +create_colorbar(im, ax[1, 0]) + +mask = np.abs(x_fk) > 0.01 * np.abs(x_fk).max() +im = ax[1, 1].imshow( + (mask * np.angle(x_fk, deg=True)).T, + cmap="twilight_shifted", + vmin=-180, + vmax=180, +) +cax, cb = create_colorbar(im, ax[1, 1]) +cax.get_yaxis().set_major_locator(MultipleLocator(45)) + + +ax[0, 0].set( + xlim=(m // 2 - 50, m // 2 + 50), + ylim=(n // 2 - 50, n // 2 + 50), + title="Space domain (Real) magnified", +) +ax[0, 1].set(title="Space domain (Imag) magnified") +ax[1, 0].set(title="Frequency domain (Abs)") +ax[1, 1].set(title="Frequency domain (Phase)") +fig.tight_layout() diff --git a/requirements-dev.txt b/requirements-dev.txt index 78ffc97..a09a9fd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,9 @@ setuptools_scm pytest # Docs require Sphinx -sphinx-rtd-theme +pydata-sphinx-theme +sphinx-gallery +sphinx-copybutton # Dev requires pre-commit # Lint