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

DOC: Add interactive notebooks to pages in the "Usage Examples" section #741

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a034289
DEP: Add MyST-NB as a docs dependency
agriyakhetarpal May 7, 2024
10f8538
DOC: Remove all reST files for Usage Examples
agriyakhetarpal May 7, 2024
a40446c
DOC: Add configuration values for MyST content
agriyakhetarpal May 7, 2024
bf794d5
DOC: Use Jupytext for usage examples (DWT-IDWT)
agriyakhetarpal May 7, 2024
0024315
DOC: Use Jupytext for usage examples (extras)
agriyakhetarpal May 7, 2024
efa2ff3
DOC: Use Jupytext for usage examples (Multilevel Wavelet Transforms)
agriyakhetarpal May 7, 2024
b366030
DOC: Use Jupytext for usage examples (Signal Extension Modes)
agriyakhetarpal May 7, 2024
0054161
DOC: Use Jupytext for usage examples (Wavelet object)
agriyakhetarpal May 7, 2024
ae05c1a
DOC: Use Jupytext for usage examples (Wavelet Packets)
agriyakhetarpal May 7, 2024
93f5e04
DOC: Use Jupytext for usage examples (Wavelet Packets 2D)
agriyakhetarpal May 7, 2024
4d108b4
MAINT: Ignore Jupyter Notebook execution cache folder
agriyakhetarpal May 7, 2024
42602bb
DOC: Add `NotebookLite` directive to all notebooks
agriyakhetarpal May 7, 2024
5e1623b
DEP: Add Jupytext CLI to dependencies
agriyakhetarpal May 7, 2024
86c1c2f
DOC: Custom Sphinx extension to invoke Jupytext
agriyakhetarpal May 9, 2024
7251c60
MAINT: Ignore auto-generated notebooks
agriyakhetarpal May 9, 2024
970439b
STY, DOC: Fix issues from `prettier` and linters
agriyakhetarpal May 9, 2024
fb1a592
DOC, DEP: Add and configure `sphinx-design`
agriyakhetarpal May 10, 2024
14e998c
DOC: Enable downloads, add dropdown (Signal Extension Modes)
agriyakhetarpal May 10, 2024
a76554a
DOC: Enable downloads, add dropdown (DWT-IDWT)
agriyakhetarpal May 10, 2024
35b3c5b
DOC: Enable downloads, add dropdown (Wavelet Packets 2D)
agriyakhetarpal May 10, 2024
cf73530
DOC: Enable downloads, add dropdown (Wavelet Packets)
agriyakhetarpal May 10, 2024
ae4949a
DOC: Enable downloads, add dropdown (Wavelet object)
agriyakhetarpal May 10, 2024
ae55547
DOC: Enable downloads, add dropdown (Multilevel Wavelet Transforms)
agriyakhetarpal May 10, 2024
91a4dff
DOC: Enable downloads, add dropdown (Gotchas)
agriyakhetarpal May 10, 2024
b2c271a
DOC: Silence JupyterLite
agriyakhetarpal May 31, 2024
9f60e55
DOC: Normalise initial Markdown cell, add tags
agriyakhetarpal Jun 1, 2024
aff61d5
DEP: Add `nbformat` as a dependency
agriyakhetarpal Jun 1, 2024
6bc9c60
DOC: Clean up Markdown notebooks at build time
agriyakhetarpal Jun 1, 2024
56bf1e1
DOC: Disable frozen module debugging warnings
agriyakhetarpal Jun 1, 2024
5a331b2
API: Remove unneeded `__future__` imports
agriyakhetarpal Jun 4, 2024
21e0158
DOC: Clean up unneeded type conversions
agriyakhetarpal Jun 4, 2024
6f150b7
DOC: Fix sync for notebook conversion + cleanup
agriyakhetarpal Jun 5, 2024
1f4bd32
STY: Fix deprecated Ruff configuration
agriyakhetarpal Jun 7, 2024
07a0926
DOC: Use the `jupytext` API instead of its CLI
agriyakhetarpal Jun 7, 2024
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pip-log.txt
doc/_build/
doc/build/
.jupyterlite.doit.db
/doc/jupyter_execute/
doc/source/regression/*.ipynb

# Editors
.idea
Expand Down
76 changes: 73 additions & 3 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import datetime
import importlib.metadata
import os
from pathlib import Path

import jinja2.filters
import numpy as np
Expand All @@ -23,6 +25,43 @@
except TypeError:
pass

from sphinx.application import Sphinx

HERE = Path(__file__).parent


def convert_md_to_ipynb(app: Sphinx, *args, **kwargs):
import subprocess
import sys
print("Converting Markdown files to IPyNB...")
subprocess.check_call(
[
sys.executable,
"-m",
"jupytext",
"--to",
"ipynb",
f"{HERE / 'regression' / '*.md'}",
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved
]
)


def cleanup_ipynb(app: Sphinx, *args, **kwargs):
import nbformat

for path in (HERE / "regression").glob("*.ipynb"):
with open(path) as f:
nb = nbformat.read(f, as_version=4)
nb.cells = [cell for cell in nb.cells if "true" not in cell.metadata.get("ignore-when-converting", [])]

with open(path, "w") as f:
nbformat.write(nb, f)
print(f"Cleaned up {path}")


def setup(app):
app.connect("builder-inited", convert_md_to_ipynb)
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved
app.connect("builder-inited", cleanup_ipynb)
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand All @@ -36,6 +75,7 @@
extensions = [
'jupyterlite_sphinx',
'matplotlib.sphinxext.plot_directive',
'myst_nb',
'numpydoc',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
Expand All @@ -45,15 +85,19 @@
'sphinx.ext.mathjax',
'sphinx.ext.todo',
'sphinx_copybutton',
'sphinx_design',
'sphinx_togglebutton',

]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = {
'.rst': 'restructuredtext',
'.md': 'myst-nb',
'ipynb': None, # do not parse IPyNB files
}

# The encoding of source files.
source_encoding = 'utf-8'
Expand Down Expand Up @@ -276,7 +320,10 @@

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['substitutions.rst', ]
exclude_patterns = [
'substitutions.rst',
'regression/*.ipynb' # exclude IPyNB files from the build
]
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved

# numpydoc_show_class_members = False
numpydoc_class_members_toctree = False
Expand Down Expand Up @@ -308,3 +355,26 @@
Shall you encounter any issues, please feel free to report them on the
[PyWavelets issue tracker](https://github.com/PyWavelets/pywt/issues)."""
)

# -- Options for MyST-NB and Markdown-based content --------------------------

os.environ["PYDEVD_DISABLE_FILE_VALIDATION"] = "1"

nb_execution_mode = 'auto'
nb_execution_timeout = 60
nb_execution_allow_errors = False

nb_render_markdown_format = "myst"
render_markdown_format = "myst"

nb_remove_code_source = False
nb_remove_code_outputs = False

myst_enable_extensions = [
'amsmath',
'colon_fence',
'dollarmath',
]

# nb_execution_allow_errors = True
# nb_execution_show_tb = True
Comment on lines +376 to +377
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what to do with these configuration values, probably best to remove them since both default to True?

Suggested change
# nb_execution_allow_errors = True
# nb_execution_show_tb = True

211 changes: 211 additions & 0 deletions doc/source/regression/dwt-idwt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
---
jupytext:
text_representation:
extension: .md
format_name: myst
format_version: 1.0.0
jupytext_version: 1.16.1
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
mystnb:
execution_allow_errors: true
execution_show_tb: true
---

+++ {"ignore-when-converting": "true"}

```{eval-rst}
.. currentmodule:: pywt

.. dropdown:: 🧑‍🔬 This notebook can be executed online. Click this section to try it out! ✨
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved
:color: success

.. notebooklite:: dwt-idwt.ipynb
:width: 100%
:height: 600px
:prompt: Open notebook

.. dropdown:: Download this notebook
:color: info
:open:

Please use the following links to download this notebook in various formats:

1. :download:`Download IPyNB (IPython Notebook) <dwt-idwt.ipynb>`
2. :download:`Download Markdown Notebook (Jupytext) <dwt-idwt.md>`
```

+++

# DWT and IDWT

## Discrete Wavelet Transform

Let's do a {func}`Discrete Wavelet Transform <dwt>` of a sample data `x`
using the `db2` wavelet. It's simple..

```{code-cell}
import pywt
x = [3, 7, 1, 1, -2, 5, 4, 6]
cA, cD = pywt.dwt(x, 'db2')
```

And the approximation and details coefficients are in `cA` and `cD`
respectively:

```{code-cell}
cA
```

```{code-cell}
cD
```

## Inverse Discrete Wavelet Transform

Now let's do an opposite operation
\- {func}`Inverse Discrete Wavelet Transform <idwt>`:

```{code-cell}
pywt.idwt(cA, cD, 'db2')
```

Voilà! That's it!

## More examples

Now let's experiment with the {func}`dwt` some more. For example let's pass a
{class}`Wavelet` object instead of the wavelet name and specify signal
extension mode (the default is {ref}`symmetric <Modes.symmetric>`) for the
border effect handling:

```{code-cell}
w = pywt.Wavelet('sym3')
cA, cD = pywt.dwt(x, wavelet=w, mode='constant')
```

```{code-cell}
print(cA)
```

```{code-cell}
print(cD)
```

Note that the output coefficients arrays length depends not only on the input
data length but also on the :class:Wavelet type (particularly on its
{attr}`filters length <~Wavelet.dec_len>` that are used in the transformation).

To find out what the size of the output data will be, use the {func}`dwt_coeff_len`
function:

```{code-cell}
pywt.dwt_coeff_len(data_len=len(x), filter_len=w.dec_len, mode='symmetric')
```

```{code-cell}
pywt.dwt_coeff_len(len(x), w, 'symmetric')
```

and the length of the output is:

```{code-cell}
len(cA)
```

Looks fine. (And if you expected that the output length would be a half of the
input data length, well, that's the trade-off that allows for the perfect
reconstruction...).

The third argument of the {func}`dwt_coeff_len` is the already mentioned signal
extension mode (please refer to the PyWavelets' documentation for the
{ref}`modes <modes>` description). Currently, there are six
{ref}`extension modes <Modes>` available:

```{code-cell}
pywt.Modes.modes
```

As you see in the above example, the {ref}`periodization <Modes.periodization>`
(periodization) mode is slightly different from the others. It's aim when
doing the {func}`DWT <dwt>` transform is to output coefficients arrays that
are half of the length of the input data.

Knowing that, you should never mix the periodization mode with other modes when
doing {func}`DWT <dwt>` and {func}`IDWT <idwt>`. Otherwise, it will produce
**invalid results**:

```{code-cell}
x = [3, 7, 1, 1, -2, 5, 4, 6]

cA, cD = pywt.dwt(x, wavelet=w, mode='periodization')
print(pywt.idwt(cA, cD, 'sym3', 'symmetric')) # invalid mode
```

```{code-cell}
print(pywt.idwt(cA, cD, 'sym3', 'periodization'))
```

## Tips & tricks

### Passing `None` instead of coefficients data to {func}`idwt`

Now, we showcase some tips & tricks. Passing `None` as one of the coefficient
arrays parameters is similar to passing a _zero-filled_ array. The results are
simply the same:

```{code-cell}
print(pywt.idwt([1,2,0,1], None, 'db2', 'symmetric'))
```

```{code-cell}
print(pywt.idwt([1, 2, 0, 1], [0, 0, 0, 0], 'db2', 'symmetric'))
```

```{code-cell}
print(pywt.idwt(None, [1, 2, 0, 1], 'db2', 'symmetric'))
```

```{code-cell}
print(pywt.idwt([0, 0, 0, 0], [1, 2, 0, 1], 'db2', 'symmetric'))
```

Remember that only one argument at a time can be `None`:

```{code-cell}
---
tags: [raises-exception]
---
print(pywt.idwt(None, None, 'db2', 'symmetric'))
```

### Coefficients data size in {attr}`idwt`

When doing the {func}`IDWT <idwt>` transform, usually the coefficient arrays
must have the same size.

```{code-cell}
---
tags: [raises-exception]
---
print(pywt.idwt([1, 2, 3, 4, 5], [1, 2, 3, 4], 'db2', 'symmetric'))
```

Not every coefficient array can be used in {func}`IDWT <idwt>`. In the
following example the {func}`idwt` will fail because the input arrays are
invalid - they couldn't be created as a result of {func}`DWT <dwt>`, because
the minimal output length for dwt using `db4` wavelet and the {ref}`symmetric
<Modes.symmetric>` mode is `4`, not `3`:

```{code-cell}
---
tags: [raises-exception]
---
pywt.idwt([1,2,4], [4,1,3], 'db4', 'symmetric')
```

```{code-cell}
int(pywt.dwt_coeff_len(1, pywt.Wavelet('db4').dec_len, 'symmetric'))
```
Loading