Skip to content

Commit

Permalink
Merge pull request #3255 from ericpre/pyodide
Browse files Browse the repository at this point in the history
HyperSpy on pyodide (and jupyterlite)
  • Loading branch information
francisco-dlp committed Nov 8, 2023
2 parents b685684 + b45ab6b commit 3aa9f7b
Show file tree
Hide file tree
Showing 39 changed files with 277 additions and 112 deletions.
75 changes: 75 additions & 0 deletions .github/workflows/build.yml
@@ -0,0 +1,75 @@
name: Build

on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
pull_request:
types: [labeled, ready_for_review, reopened]

jobs:
build:
if: ${{ contains(github.event.pull_request.labels.*.name, 'run-packaging') || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }}
name: Build
runs-on: ubuntu-latest
env:
PYTHON_VERSION: '3.11'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v4
name: Install Python
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Display version
run: |
python --version
pip --version
- name: Install pypa/build
run: |
pip install build
- name: Build a binary wheel and a source tarball
run: |
python -m build
- name: Display content dist folder
run: |
ls -shR dist/
- uses: actions/upload-artifact@v3
with:
path: ./dist/*
name: dist

test:
name: Test Packaging
needs: build
runs-on: ubuntu-latest
env:
PYTHON_VERSION: '3.11'
steps:
- uses: actions/setup-python@v4
name: Install Python
with:
python-version: ${{ env.PYTHON_VERSION }}

- uses: actions/download-artifact@v3

- name: Display content working folder
run: |
ls -R
- name: Install distribution
run: |
pip install --pre --find-links dist hyperspy[all,tests]
- name: Pip list
run: |
pip list
- name: Test distribution
run: |
pytest --pyargs hyperspy --reruns 3 -n 2
7 changes: 3 additions & 4 deletions doc/user_guide/install.rst
Expand Up @@ -172,15 +172,14 @@ use:
See the following list of selectors to select the installation of optional
dependencies required by specific functionalities:

* ``ipython`` for integration with the `ipython` terminal and parallel processing using `ipyparallel`,
* ``learning`` for some machine learning features,
* ``gui-jupyter`` to use the `Jupyter widgets <https://ipywidgets.readthedocs.io/en/stable/>`_
GUI elements,
* ``gui-traitsui`` to use the GUI elements based on `traitsui <https://docs.enthought.com/traitsui/>`_,

Check warning on line 179 in doc/user_guide/install.rst

View workflow job for this annotation

GitHub Actions / Docs (PR comments)

Block quote ends without a blank line; unexpected unindent.
* ``mrcz`` to read mrcz file,
* ``speed`` to speed up some functionalities,
* ``usid`` to read usid file,
* ``speed`` install numba and numexpr to speed up some functionalities,
* ``tests`` to install required libraries to run HyperSpy's unit tests,
* ``coverage`` to coverage statistics when running the tests,
* ``build-doc`` to install required libraries to build HyperSpy's documentation,
* ``dev`` to install all the above,
* ``all`` to install all the above except the development requirements
Expand Down
38 changes: 24 additions & 14 deletions hyperspy/_components/bleasdale.py
Expand Up @@ -24,30 +24,34 @@ class Bleasdale(Expression):

r"""Bleasdale function component.
Also called the Bleasdale-Nelder function. Originates from the description of the yield-density relationship in crop growth.
Also called the Bleasdale-Nelder function. Originates from
the description of the yield-density relationship in crop growth.
.. math::
f(x) = \left(a+b\cdot x\right)^{-1/c}
Parameters
-----------
a : Float
b : Float
c : Float
**kwargs
Extra keyword arguments are passed to the
:py:class:`~._components.expression.Expression` component.
----------
a : float, default=1.0
The value of Parameter a.
b : float, default=1.0
The value of Parameter b.
c : float, default=1.0
The value of Parameter c.
**kwargs
Extra keyword arguments are passed to
:py:class:`~.api.model.components1D.Expression`.
Note
----
For :math:`(a+b\cdot x)\leq0`, the component will be set to 0.
"""

def __init__(self, a=1., b=1., c=1., module="numexpr", **kwargs):
def __init__(self, a=1., b=1., c=1., module=None, **kwargs):
super().__init__(
expression="where((a + b * x) > 0, (a + b * x) ** (-1 / c), 0)",
expression="where((a + b * x) > 0, pow(a + b * x, -1 / c), 0)",
name="Bleasdale",
a=a,
b=b,
Expand All @@ -58,6 +62,12 @@ def __init__(self, a=1., b=1., c=1., module="numexpr", **kwargs):
linear_parameter_list=['b'],
check_parameter_linearity=False,
**kwargs)
module = self._whitelist['module'][1]
if module in ("numpy", "scipy"):
# Issue with calculating component at 0...
raise ValueError(
f"{module} is not supported for this component, use numexpr instead."
)

def grad_a(self, x):
"""
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/exponential.py
Expand Up @@ -52,7 +52,7 @@ class Exponential(Expression):
:py:class:`~._components.expression.Expression` component.
"""

def __init__(self, A=1., tau=1., module="numexpr", **kwargs):
def __init__(self, A=1., tau=1., module=None, **kwargs):
super().__init__(
expression="A * exp(-x / tau)",
name="Exponential",
Expand Down
29 changes: 22 additions & 7 deletions hyperspy/_components/expression.py
Expand Up @@ -17,6 +17,7 @@
# along with HyperSpy. If not, see <https://www.gnu.org/licenses/#GPL>.

from functools import wraps
import logging
import numpy as np
import sympy

Expand All @@ -26,6 +27,9 @@
from hyperspy.docstrings.parameters import FUNCTION_ND_DOCSTRING


_logger = logging.getLogger(__name__)


_CLASS_DOC = \
"""%s component (created with Expression).
Expand Down Expand Up @@ -87,9 +91,10 @@ class docstring.
applicable. It enables interative adjustment of the position of the
component in the model. For 2D components, a tuple must be passed
with the name of the two parameters e.g. `("x0", "y0")`.
module : {"numpy", "numexpr", "scipy"}, default "numpy"
module : {"numpy", "numexpr", "scipy", None}, default "numpy"
Module used to evaluate the function. numexpr is often faster but
it supports fewer functions and requires installing numexpr.
If None, the "numexpr" will be used if installed.
add_rotation : bool, default False
This is only relevant for 2D components. If `True` it automatically
adds `rotation_angle` parameter.
Expand All @@ -114,10 +119,10 @@ class docstring.
parameters.
check_parameter_linearity : bool
If `True`, automatically check if each parameter is linear and set
its corresponding attribute accordingly. If `False`, the default is to
its corresponding attribute accordingly. If `False`, the default is to
set all parameters, except for those who are specified in
``linear_parameter_list``.
**kwargs
Keyword arguments can be used to initialise the value of the
parameters.
Expand Down Expand Up @@ -162,6 +167,17 @@ def __init__(self, expression, name, position=None, module="numpy",
linear_parameter_list=None, check_parameter_linearity=True,
**kwargs):

if module is None:
try:
import numexpr
module = "numexpr"
except ImportError:
module = "numpy"
_logger.warning(
"Numexpr is not installed, falling back to numpy, "
"which is slower to calculate model."
)

if linear_parameter_list is None:
linear_parameter_list = []
self._add_rotation = add_rotation
Expand Down Expand Up @@ -224,13 +240,13 @@ def __init__(self, expression, name, position=None, module="numpy",
if p.name not in linear_parameter_list:
# _parsed_expr used "non public" parameter name and we
# need to use the correct parameter name by using
# _rename_pars_inv
# _rename_pars_inv
p._linear = _check_parameter_linearity(
self._parsed_expr,
self._rename_pars_inv.get(p.name, p.name)
)

def compile_function(self, module="numpy", position=False):
def compile_function(self, module, position=False):
"""
Compile the function and calculate the gradient automatically when
possible.
Expand Down Expand Up @@ -379,7 +395,7 @@ def _separate_pseudocomponents(self):
Separate an expression into a group of lambdified functions
that can compute the free parts of the expression, and a single
lambdified function that computes the fixed parts of the expression
Used by the _compute_expression_part method.
"""
expr = self._str_expression
Expand Down Expand Up @@ -455,4 +471,3 @@ def _check_parameter_linearity(expr, name):
"determined automatically.", UserWarning)
return False
return True

2 changes: 1 addition & 1 deletion hyperspy/_components/gaussian.py
Expand Up @@ -105,7 +105,7 @@ class Gaussian(Expression):
~._components.gaussianhf.GaussianHF
"""

def __init__(self, A=1., sigma=1., centre=0., module="numexpr", **kwargs):
def __init__(self, A=1., sigma=1., centre=0., module=None, **kwargs):
super().__init__(
expression="A * (1 / (sigma * sqrt(2*pi))) * exp(-(x - centre)**2 \
/ (2 * sigma**2))",
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/gaussian2d.py
Expand Up @@ -70,7 +70,7 @@ class Gaussian2D(Expression):
"""

def __init__(self, A=1., sigma_x=1., sigma_y=1., centre_x=0.,
centre_y=0, module="numexpr", **kwargs):
centre_y=0, module=None, **kwargs):
super().__init__(
expression="A * (1 / (sigma_x * sigma_y * 2 * pi)) * \
exp(-((x - centre_x) ** 2 / (2 * sigma_x ** 2) \
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/gaussianhf.py
Expand Up @@ -83,7 +83,7 @@ class GaussianHF(Expression):
"""

def __init__(self, height=1., fwhm=1., centre=0., module="numexpr",
def __init__(self, height=1., fwhm=1., centre=0., module=None,
**kwargs):
super().__init__(
expression="height * exp(-(x - centre)**2 * 4 * log(2)/fwhm**2)",
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/logistic.py
Expand Up @@ -57,7 +57,7 @@ class Logistic(Expression):
:py:class:`~._components.expression.Expression` component.
"""

def __init__(self, a=1., b=1., c=1., origin=0., module="numexpr", **kwargs):
def __init__(self, a=1., b=1., c=1., origin=0., module=None, **kwargs):
super().__init__(
expression="a / (1 + b * exp(-c * (x - origin)))",
name="Logistic",
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/lorentzian.py
Expand Up @@ -94,7 +94,7 @@ class Lorentzian(Expression):
the full-with-half-maximum and height of the distribution, respectively.
"""

def __init__(self, A=1., gamma=1., centre=0., module="numexpr", **kwargs):
def __init__(self, A=1., gamma=1., centre=0., module=None, **kwargs):
# We use `_gamma` internally to workaround the use of the `gamma`
# function in sympy
super().__init__(
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/polynomial.py
Expand Up @@ -51,7 +51,7 @@ class Polynomial(Expression):
"""

def __init__(self, order=2, module="numexpr", **kwargs):
def __init__(self, order=2, module=None, **kwargs):
if order == 0:
raise ValueError("Polynomial of order 0 is not supported.")
coeff_list = ['{}'.format(o).zfill(len(list(str(order)))) for o in
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/power_law.py
Expand Up @@ -61,7 +61,7 @@ class PowerLaw(Expression):
"""

def __init__(self, A=10e5, r=3., origin=0., left_cutoff=0.0,
module="numexpr", compute_gradients=False, **kwargs):
module=None, compute_gradients=False, **kwargs):
super().__init__(
expression="where(left_cutoff<x, A*(-origin + x)**-r, 0)",
name="PowerLaw",
Expand Down
2 changes: 1 addition & 1 deletion hyperspy/_components/rc.py
Expand Up @@ -54,7 +54,7 @@ class RC(Expression):
"""

def __init__(self, Vmax=1., V0=0., tau=1., module="numexpr", **kwargs):
def __init__(self, Vmax=1., V0=0., tau=1., module=None, **kwargs):
super().__init__(
expression="V0 + Vmax * (1 - exp(-x / tau))",
name="RC",
Expand Down

0 comments on commit 3aa9f7b

Please sign in to comment.