From 3e60373f71d0b07786c301c0b7b90c5ab7397aa1 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sat, 14 Jan 2023 18:03:08 +0100 Subject: [PATCH 01/15] Initialize the example module. --- modopt/examples/README.rst | 6 ++ modopt/examples/__init__.py | 4 + modopt/examples/conftest.py | 39 ++++++++ .../example_lasso_forward_backward.py | 90 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 modopt/examples/README.rst create mode 100644 modopt/examples/__init__.py create mode 100644 modopt/examples/conftest.py create mode 100644 modopt/examples/example_lasso_forward_backward.py diff --git a/modopt/examples/README.rst b/modopt/examples/README.rst new file mode 100644 index 00000000..e86ef76e --- /dev/null +++ b/modopt/examples/README.rst @@ -0,0 +1,6 @@ +======================== +Examples of ModOpt usage +======================== + + +This is a collection of python scripts showing the use of ModOpt. diff --git a/modopt/examples/__init__.py b/modopt/examples/__init__.py new file mode 100644 index 00000000..16f1ff54 --- /dev/null +++ b/modopt/examples/__init__.py @@ -0,0 +1,4 @@ +"""Documented examples to show the usage of various functionnality of ModOpt. + +Also, it serves as integration test for different methods. +""" diff --git a/modopt/examples/conftest.py b/modopt/examples/conftest.py new file mode 100644 index 00000000..7bd10b14 --- /dev/null +++ b/modopt/examples/conftest.py @@ -0,0 +1,39 @@ +""" +Configuration for testing the example scripts. + +Based on: +https://stackoverflow.com/questions/56807698/how-to-run-script-as-pytest-test +""" +from pathlib import Path +import runpy +import pytest + +def pytest_collect_file(path, parent): + """Pytest hook. + + Create a collector for the given path, or None if not relevant. + The new node needs to have the specified parent as parent. + """ + p = Path(path) + if p.suffix == '.py' and 'example' in p.name: + return Script.from_parent(parent, path=p, name=p.name) + + +class Script(pytest.File): + """Script files collected by pytest.""" + + def collect(self): + """Collect the script as its own item.""" + yield ScriptItem.from_parent(self, name=self.name) + +class ScriptItem(pytest.Item): + """Item script collected by pytest.""" + + def runtest(self): + """Run the script as a test.""" + runpy.run_path(str(self.path)) + + def repr_failure(self, excinfo): + """Return only the error traceback of the script.""" + excinfo.traceback = excinfo.traceback.cut(path=self.path) + return super().repr_failure(excinfo) diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py new file mode 100644 index 00000000..37006b40 --- /dev/null +++ b/modopt/examples/example_lasso_forward_backward.py @@ -0,0 +1,90 @@ +""" +Solving the LASSO Problem with the Forward Backward Algorithm. + +This an example to show how to solve the example LASSO Problem +using the Forward-Backward Algorithm. + +In this example we are going to use: + - Modopt Operators (Linear, Gradient, Proximal) + - Modopt implementation of solvers + - Modopt Metric API. +TODO: add reference to LASSO paper. +""" + +import numpy as np +import matplotlib.pyplot as plt + +from modopt.opt.algorithms import POGM, ForwardBackward +from modopt.opt.cost import costObj +from modopt.opt.linear import LinearParent, Identity +from modopt.opt.gradient import GradBasic +from modopt.opt.proximity import SparseThreshold +from modopt.math.matrix import PowerMethod +from modopt.math.stats import mse + +# %% +# Here we create a instance of the LASSO Problem + +BETA_TRUE = np.array( + [3.0, 1.5, 0, 0, 2, 0, 0, 0] +) # 8 original values from lLASSO Paper +DIM = len(BETA_TRUE) + + +rng = np.random.default_rng() +sigma_noise = 1 +obs = 20 +# create a measurement matrix with decaying covariance matrix. +cov = 0.4 ** abs((np.arange(DIM) * np.ones((DIM, DIM))).T - np.arange(DIM)) +x = rng.multivariate_normal(np.zeros(DIM), cov, obs) + +y = x @ BETA_TRUE +y_noise = y + (sigma_noise * np.random.standard_normal(obs)) + + +# %% +# Next we create Operators for solving the problem. + +lin_op = LinearParent(lambda b: x @ b, lambda bb: x.T @ bb) # MatrixOperator could also work here. +grad_op = GradBasic(y_noise, op=lin_op.op, trans_op=lin_op.adj_op) + +prox_op = SparseThreshold(Identity(), 1, thresh_type="soft") + +# %% +# In order to get the best convergence rate, we first determine the Lipschitz constant of the gradien Operator +# + +calc_lips = PowerMethod(grad_op.trans_op_op, 8, data_type="float32", auto_run=True) +lip = calc_lips.spec_rad +print(lip) + +# %% +# Solving using FISTA algorithm +# ----------------------------- +# +# TODO: Add description/Reference of FISTA. + +cost_op_fista = costObj([grad_op, prox_op], verbose=False) + +fb_fista = ForwardBackward( + np.zeros(8), + beta_param=1 / lip, + grad=grad_op, + prox=prox_op, + cost=cost_op_fista, + metric_call_period=1, + auto_iterate=False, # Just to give us the pleasure of doing things by ourself. +) + +fb_fista.iterate() + +# %% +# After the run we can have a look at the results + +print(fb_fista.x_final) +print(mse(fb_fista.x_final, BETA_TRUE)) +assert mse(fb_fista.x_final, BETA_TRUE) < 0.1 + +# %% + +plt.plot(cost_op_fista._cost_list) From 5db5d3b8177d76f05490aa3c5414f0c48eb57cde Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sun, 15 Jan 2023 18:36:30 +0100 Subject: [PATCH 02/15] do not export the assert statements. --- modopt/examples/example_lasso_forward_backward.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py index 37006b40..3cf1eeb7 100644 --- a/modopt/examples/example_lasso_forward_backward.py +++ b/modopt/examples/example_lasso_forward_backward.py @@ -83,7 +83,10 @@ print(fb_fista.x_final) print(mse(fb_fista.x_final, BETA_TRUE)) + +# sphinx_gallery_start_ignore assert mse(fb_fista.x_final, BETA_TRUE) < 0.1 +# sphinx_gallery_end_ignore # %% From c36efcb1920664c28d34b2c632152c4cab6d94ca Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 18 Jan 2023 10:19:00 +0100 Subject: [PATCH 03/15] add matplotlib as requirement. --- .github/workflows/cd-build.yml | 4 +--- .github/workflows/ci-build.yml | 4 ++-- docs/requirements.txt | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd-build.yml b/.github/workflows/cd-build.yml index 1e49f8bc..fca9feb1 100644 --- a/.github/workflows/cd-build.yml +++ b/.github/workflows/cd-build.yml @@ -62,9 +62,7 @@ jobs: - name: Set up Conda with Python 3.8 uses: conda-incubator/setup-miniconda@v2 with: - auto-update-conda: true - python-version: 3.8 - auto-activate-base: false + python-version: "3.8" - name: Install dependencies shell: bash -l {0} diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index cdfff840..bbe6beaa 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -43,7 +43,7 @@ jobs: python -m pip install --upgrade pip python -m pip install -r develop.txt python -m pip install -r docs/requirements.txt - python -m pip install astropy scikit-image scikit-learn + python -m pip install astropy scikit-image scikit-learn matplotlib python -m pip install tensorflow>=2.4.1 python -m pip install twine python -m pip install . @@ -108,7 +108,7 @@ jobs: python --version python -m pip install --upgrade pip python -m pip install -r develop.txt - python -m pip install astropy scikit-image scikit-learn + python -m pip install astropy scikit-image scikit-learn matplotlib python -m pip install . - name: Run Tests diff --git a/docs/requirements.txt b/docs/requirements.txt index 4d2a14fb..c9e29c88 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,3 +6,4 @@ numpydoc==1.1.0 sphinx==4.3.1 sphinxcontrib-bibtex==2.4.1 sphinxawesome-theme==3.2.1 +sphinx-gallery==0.11.1 From 04f9d1f76b7ebd25f26e370494ff9b7523e313c1 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 18 Jan 2023 10:36:47 +0100 Subject: [PATCH 04/15] add support for sphinx-gallery --- docs/source/conf.py | 13 +++++++++++++ docs/source/toc.rst | 1 + modopt/examples/README.rst | 7 +++---- modopt/examples/example_lasso_forward_backward.py | 1 + 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index fb954f6d..46564b9f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -45,6 +45,7 @@ 'nbsphinx', 'nbsphinx_link', 'numpydoc', + "sphinx_gallery.gen_gallery" ] # Include module names for objects @@ -145,6 +146,18 @@ # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = True + + +# -- Options for Sphinx Gallery ---------------------------------------------- + +sphinx_gallery_conf = { + "examples_dirs": ["../../modopt/examples/"], + "filename_pattern": "/example_", + "ignore_pattern": r"/(__init__|conftest)\.py", +} + + + # -- Options for nbshpinx output ------------------------------------------ diff --git a/docs/source/toc.rst b/docs/source/toc.rst index 84a6af87..ef5753f5 100644 --- a/docs/source/toc.rst +++ b/docs/source/toc.rst @@ -25,6 +25,7 @@ plugin_example notebooks + auto_examples/index .. toctree:: :hidden: diff --git a/modopt/examples/README.rst b/modopt/examples/README.rst index e86ef76e..24935c0e 100644 --- a/modopt/examples/README.rst +++ b/modopt/examples/README.rst @@ -1,6 +1,5 @@ -======================== -Examples of ModOpt usage -======================== - +======== +Examples +======== This is a collection of python scripts showing the use of ModOpt. diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py index 3cf1eeb7..e67be682 100644 --- a/modopt/examples/example_lasso_forward_backward.py +++ b/modopt/examples/example_lasso_forward_backward.py @@ -1,5 +1,6 @@ """ Solving the LASSO Problem with the Forward Backward Algorithm. +============================================================== This an example to show how to solve the example LASSO Problem using the Forward-Backward Algorithm. From d772cd1c52b25a678f31614266933c10b867d2b1 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby <77174042+paquiteau@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:05:27 +0100 Subject: [PATCH 05/15] Update modopt/examples/README.rst Co-authored-by: Samuel Farrens --- modopt/examples/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modopt/examples/README.rst b/modopt/examples/README.rst index 24935c0e..e6ffbe27 100644 --- a/modopt/examples/README.rst +++ b/modopt/examples/README.rst @@ -2,4 +2,4 @@ Examples ======== -This is a collection of python scripts showing the use of ModOpt. +This is a collection of Python scripts demonstrating the use of ModOpt. From 482ba3330292f6941386cae32d4fe2fb602f9b75 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby <77174042+paquiteau@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:05:35 +0100 Subject: [PATCH 06/15] Update modopt/examples/__init__.py Co-authored-by: Samuel Farrens --- modopt/examples/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modopt/examples/__init__.py b/modopt/examples/__init__.py index 16f1ff54..d7e77357 100644 --- a/modopt/examples/__init__.py +++ b/modopt/examples/__init__.py @@ -1,4 +1,10 @@ -"""Documented examples to show the usage of various functionnality of ModOpt. +"""EXAMPLES. + +This module contains documented examples that demonstrate the usage of various +ModOpt tools. + +These examples also serve as integration tests for various methods. + +:Author: Pierre-Antoine Comby -Also, it serves as integration test for different methods. """ From 8e76b65c24270cef0f2414337a435c3b56e8962f Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby <77174042+paquiteau@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:05:49 +0100 Subject: [PATCH 07/15] Update modopt/examples/conftest.py Co-authored-by: Samuel Farrens --- modopt/examples/conftest.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modopt/examples/conftest.py b/modopt/examples/conftest.py index 7bd10b14..73358679 100644 --- a/modopt/examples/conftest.py +++ b/modopt/examples/conftest.py @@ -1,8 +1,15 @@ -""" -Configuration for testing the example scripts. +"""TEST CONFIGURATION. + +This module contains methods for configuring the testing of the example +scripts. +:Author: Pierre-Antoine Comby + +Notes +----- Based on: https://stackoverflow.com/questions/56807698/how-to-run-script-as-pytest-test + """ from pathlib import Path import runpy From 186608002d86dbec1c5744bb761badd1df127ee3 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby <77174042+paquiteau@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:05:56 +0100 Subject: [PATCH 08/15] Update modopt/examples/example_lasso_forward_backward.py Co-authored-by: Samuel Farrens --- modopt/examples/example_lasso_forward_backward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py index e67be682..3f10988f 100644 --- a/modopt/examples/example_lasso_forward_backward.py +++ b/modopt/examples/example_lasso_forward_backward.py @@ -2,7 +2,7 @@ Solving the LASSO Problem with the Forward Backward Algorithm. ============================================================== -This an example to show how to solve the example LASSO Problem +This an example to show how to solve an example LASSO Problem using the Forward-Backward Algorithm. In this example we are going to use: From 5ce9243ea6cce314f0ccb286e52734b379e65210 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby <77174042+paquiteau@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:06:05 +0100 Subject: [PATCH 09/15] Update modopt/examples/example_lasso_forward_backward.py Co-authored-by: Samuel Farrens --- modopt/examples/example_lasso_forward_backward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py index 3f10988f..05920b9b 100644 --- a/modopt/examples/example_lasso_forward_backward.py +++ b/modopt/examples/example_lasso_forward_backward.py @@ -52,7 +52,7 @@ prox_op = SparseThreshold(Identity(), 1, thresh_type="soft") # %% -# In order to get the best convergence rate, we first determine the Lipschitz constant of the gradien Operator +# In order to get the best convergence rate, we first determine the Lipschitz constant of the gradient Operator # calc_lips = PowerMethod(grad_op.trans_op_op, 8, data_type="float32", auto_run=True) From c3f884f927a7fcb431d8868f321389f4d234b6e2 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Fri, 20 Jan 2023 16:49:39 +0100 Subject: [PATCH 10/15] ignore auto_example folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 06dff8db..f9eaaa68 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ instance/ docs/_build/ docs/source/fortuna.* docs/source/scripts.* +docs/source/auto_examples/ docs/source/*.nblink # PyBuilder From 22e782e3797994ad09a74a36bf82a9b0d5c632c9 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Fri, 20 Jan 2023 17:35:28 +0100 Subject: [PATCH 11/15] doc formatting. --- modopt/opt/algorithms/forward_backward.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modopt/opt/algorithms/forward_backward.py b/modopt/opt/algorithms/forward_backward.py index 6923a6af..83f45e8b 100644 --- a/modopt/opt/algorithms/forward_backward.py +++ b/modopt/opt/algorithms/forward_backward.py @@ -817,9 +817,9 @@ class POGM(SetUp): Initial guess for the :math:`y` variable z : numpy.ndarray Initial guess for the :math:`z` variable - grad + grad : GradBasic Gradient operator class - prox + prox : ProximalParent Proximity operator class cost : class instance or str, optional Cost function class instance (default is ``'auto'``); Use ``'auto'`` to From 93dbb098387acf6f61e53ee25b0e3afd4e0bb3ab Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Fri, 20 Jan 2023 17:35:36 +0100 Subject: [PATCH 12/15] add pogm and basic comparison. --- .../example_lasso_forward_backward.py | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/modopt/examples/example_lasso_forward_backward.py b/modopt/examples/example_lasso_forward_backward.py index e67be682..ee469de1 100644 --- a/modopt/examples/example_lasso_forward_backward.py +++ b/modopt/examples/example_lasso_forward_backward.py @@ -1,3 +1,4 @@ +# noqa: D205 """ Solving the LASSO Problem with the Forward Backward Algorithm. ============================================================== @@ -15,7 +16,7 @@ import numpy as np import matplotlib.pyplot as plt -from modopt.opt.algorithms import POGM, ForwardBackward +from modopt.opt.algorithms import ForwardBackward, POGM from modopt.opt.cost import costObj from modopt.opt.linear import LinearParent, Identity from modopt.opt.gradient import GradBasic @@ -46,18 +47,19 @@ # %% # Next we create Operators for solving the problem. -lin_op = LinearParent(lambda b: x @ b, lambda bb: x.T @ bb) # MatrixOperator could also work here. +# MatrixOperator could also work here. +lin_op = LinearParent(lambda b: x @ b, lambda bb: x.T @ bb) grad_op = GradBasic(y_noise, op=lin_op.op, trans_op=lin_op.adj_op) prox_op = SparseThreshold(Identity(), 1, thresh_type="soft") # %% -# In order to get the best convergence rate, we first determine the Lipschitz constant of the gradien Operator -# +# In order to get the best convergence rate, we first determine the Lipschitz constant +# of the gradien Operator calc_lips = PowerMethod(grad_op.trans_op_op, 8, data_type="float32", auto_run=True) lip = calc_lips.spec_rad -print(lip) +print("lipschitz constant:", lip) # %% # Solving using FISTA algorithm @@ -83,12 +85,69 @@ # After the run we can have a look at the results print(fb_fista.x_final) -print(mse(fb_fista.x_final, BETA_TRUE)) +mse_fista = mse(fb_fista.x_final, BETA_TRUE) +plt.stem(fb_fista.x_final, label="estimation", linefmt="C0-") +plt.stem(BETA_TRUE, label="reference", linefmt="C1-") +plt.legend() +plt.title(f"FISTA Estimation MSE={mse_fista:.4f}") # sphinx_gallery_start_ignore -assert mse(fb_fista.x_final, BETA_TRUE) < 0.1 +assert mse(fb_fista.x_final, BETA_TRUE) < 1 # sphinx_gallery_end_ignore + # %% +# Solving Using the POGM Algorithm +# -------------------------------- +# +# TODO: Add description/Reference to POGM. + -plt.plot(cost_op_fista._cost_list) +cost_op_pogm = costObj([grad_op, prox_op], verbose=False) + +fb_pogm = POGM( + np.zeros(8), + np.zeros(8), + np.zeros(8), + np.zeros(8), + beta_param=1 / lip, + grad=grad_op, + prox=prox_op, + cost=cost_op_pogm, + metric_call_period=1, + auto_iterate=False, # Just to give us the pleasure of doing things by ourself. +) + +fb_pogm.iterate() + +# %% +# After the run we can have a look at the results + +print(fb_pogm.x_final) +mse_pogm = mse(fb_pogm.x_final, BETA_TRUE) + +plt.stem(fb_pogm.x_final, label="estimation", linefmt="C0-") +plt.stem(BETA_TRUE, label="reference", linefmt="C1-") +plt.legend() +plt.title(f"FISTA Estimation MSE={mse_pogm:.4f}") +# +# sphinx_gallery_start_ignore +assert mse(fb_pogm.x_final, BETA_TRUE) < 1 + +# %% +# Comparing the Two algorithms +# ---------------------------- + +plt.figure() +plt.semilogy(cost_op_fista._cost_list, label="FISTA convergence") +plt.semilogy(cost_op_pogm._cost_list, label="POGM convergence") +plt.xlabel("iterations") +plt.ylabel("Cost Function") +plt.legend() +plt.show() + + +# %% +# We can see that the two algorithm converges quickly, and POGM requires less iterations. +# However the POGM iterations are more costly, so a proper benchmark with time measurement is needed. +# Check the benchopt benchmark for more details. From a7d5a8e12c7cd271323c496fb52538d16e07f994 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 15 Mar 2023 17:34:57 +0100 Subject: [PATCH 13/15] fix: add matplotlib for the plotting in examples scripts. --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index b24bea79..656a5b39 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -43,7 +43,7 @@ jobs: python -m pip install --upgrade pip python -m pip install -r develop.txt python -m pip install -r docs/requirements.txt - python -m pip install astropy "scikit-image<0.20" scikit-learn + python -m pip install astropy "scikit-image<0.20" scikit-learn matplotlib python -m pip install tensorflow>=2.4.1 python -m pip install twine python -m pip install . From bb4d0bcb12d2ec52c6d1259826b4809d463e1657 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 15 Mar 2023 17:43:59 +0100 Subject: [PATCH 14/15] fix: add matplotlib for basic ci too. --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 656a5b39..07baf99b 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -108,7 +108,7 @@ jobs: python --version python -m pip install --upgrade pip python -m pip install -r develop.txt - python -m pip install astropy "scikit-image<0.20" scikit-learn + python -m pip install astropy "scikit-image<0.20" scikit-learn matplotlib python -m pip install . - name: Run Tests From c3b35f89190d94bffdbdeaa752926f0641873cb5 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 15 Mar 2023 17:57:28 +0100 Subject: [PATCH 15/15] ci: run pytest with xdist for faster testing --- .github/workflows/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 07baf99b..c4ba28a0 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -108,11 +108,11 @@ jobs: python --version python -m pip install --upgrade pip python -m pip install -r develop.txt - python -m pip install astropy "scikit-image<0.20" scikit-learn matplotlib + python -m pip install astropy "scikit-image<0.20" scikit-learn matplotlib python -m pip install . - name: Run Tests shell: bash -l {0} run: | export PATH=/usr/share/miniconda/bin:$PATH - python setup.py test + pytest -n 2