From 5ebbaa5859cde1686dab1b958f8211ad42e47bc9 Mon Sep 17 00:00:00 2001 From: epapoutsellis Date: Wed, 6 Nov 2024 12:25:32 +0000 Subject: [PATCH 1/6] remove tigre, ipp, ccpi reg, add astra 2.2.0 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5dc5f25bca..e0dbde6218 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,13 +9,13 @@ LABEL org.opencontainers.image.source=https://github.com/TomographicImaging/CIL LABEL org.opencontainers.image.licenses="Apache-2.0 AND BSD-3-Clause AND GPL-3.0" # CUDA-specific packages -ARG CIL_EXTRA_PACKAGES="tigre=2.6 astra-toolbox=2.1.0=cuda*" +ARG CIL_EXTRA_PACKAGES="astra-toolbox=2.2.0" # build & runtime dependencies # TODO: sync scripts/create_local_env_for_cil_development.sh, scripts/requirements-test.yml, recipe/meta.yaml (e.g. missing libstdcxx-ng _openmp_mutex pip)? # vis. https://github.com/TomographicImaging/CIL/pull/1590 COPY --chown="${NB_USER}" scripts/requirements-test.yml environment.yml # channel_priority: https://stackoverflow.com/q/58555389 -RUN sed -ri '/tigre|astra-toolbox| python /d' environment.yml \ +RUN sed -ri '/ccpi-regulariser|astra|ipp|tigre|tomophantom| python /d' environment.yml \ && for pkg in 'jupyter-server-proxy>4.1.0' $CIL_EXTRA_PACKAGES; do echo " - $pkg" >> environment.yml; done \ && conda config --env --set channel_priority strict \ && for ch in defaults nvidia ccpi https://software.repos.intel.com/python/conda conda-forge; do conda config --env --add channels $ch; done \ From 3ccf3c6ad4acd5dcf8c6f395bd64b94f1c9ce161 Mon Sep 17 00:00:00 2001 From: elma16 Date: Wed, 6 Nov 2024 13:37:35 +0000 Subject: [PATCH 2/6] macos build --- scripts/requirements-test.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/scripts/requirements-test.yml b/scripts/requirements-test.yml index 90de773a0f..928c3d7782 100644 --- a/scripts/requirements-test.yml +++ b/scripts/requirements-test.yml @@ -14,35 +14,25 @@ name: cil_dev channels: - conda-forge - - nvidia - - https://software.repos.intel.com/python/conda - defaults dependencies: # base (vis. recipe/conda_build_config.yaml) - python >=3.10 - numpy >=1.23 - ccpi::cil-data - - ccpi::tigre=2.6 - - ccpi::ccpi-regulariser=24.0.1 - - ccpi::tomophantom=2.0.0 - - astra-toolbox=2.1=cuda* + - astra-toolbox=2.2.0 + - llvm-openmp - cvxpy - python-wget - scikit-image - packaging - cmake >=3.16 - setuptools <71 - - ipp-include=2021.12 - - ipp-devel=2021.12 - - ipp=2021.12 - ipywidgets - scipy - matplotlib - h5py - pillow - - libgcc-ng # [linux] - - libstdcxx-ng # [linux] - - _openmp_mutex # [linux] - dxchange - olefile - pywavelets From e9ecc3f590837167dc2ca89566842534bda65857 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 7 Nov 2024 09:52:08 +0000 Subject: [PATCH 3/6] reintroduce standard build --- Dockerfile | 5 +++-- scripts/requirements-test.yml | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index e0dbde6218..9cc5bded63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,13 +9,14 @@ LABEL org.opencontainers.image.source=https://github.com/TomographicImaging/CIL LABEL org.opencontainers.image.licenses="Apache-2.0 AND BSD-3-Clause AND GPL-3.0" # CUDA-specific packages -ARG CIL_EXTRA_PACKAGES="astra-toolbox=2.2.0" +ARG CIL_EXCLUDE_PACKAGES="cuda" +ARG CIL_EXTRA_PACKAGES="tigre=2.6 astra-toolbox=2.1.0=cuda*" # build & runtime dependencies # TODO: sync scripts/create_local_env_for_cil_development.sh, scripts/requirements-test.yml, recipe/meta.yaml (e.g. missing libstdcxx-ng _openmp_mutex pip)? # vis. https://github.com/TomographicImaging/CIL/pull/1590 COPY --chown="${NB_USER}" scripts/requirements-test.yml environment.yml # channel_priority: https://stackoverflow.com/q/58555389 -RUN sed -ri '/ccpi-regulariser|astra|ipp|tigre|tomophantom| python /d' environment.yml \ +RUN sed -ri "/$CIL_EXCLUDE_PACKAGES| python /d" environment.yml \ && for pkg in 'jupyter-server-proxy>4.1.0' $CIL_EXTRA_PACKAGES; do echo " - $pkg" >> environment.yml; done \ && conda config --env --set channel_priority strict \ && for ch in defaults nvidia ccpi https://software.repos.intel.com/python/conda conda-forge; do conda config --env --add channels $ch; done \ diff --git a/scripts/requirements-test.yml b/scripts/requirements-test.yml index 928c3d7782..6016d67a13 100644 --- a/scripts/requirements-test.yml +++ b/scripts/requirements-test.yml @@ -14,25 +14,35 @@ name: cil_dev channels: - conda-forge + - nvidia + - https://software.repos.intel.com/python/conda - defaults dependencies: # base (vis. recipe/conda_build_config.yaml) - python >=3.10 - numpy >=1.23 - ccpi::cil-data - - astra-toolbox=2.2.0 - - llvm-openmp + - ccpi::tigre=2.6 # [cuda and not arm64] + - ccpi::ccpi-regulariser=24.0.1 # [not arm64] + - ccpi::tomophantom=2.0.0 # [not arm64] + - astra-toolbox=2.1=cuda* # [cuda and not arm64] - cvxpy - python-wget - scikit-image - packaging - cmake >=3.16 - setuptools <71 + - ipp-include=2021.12 # [not arm64] + - ipp-devel=2021.12 # [not arm64] + - ipp=2021.12 # [not arm64] - ipywidgets - scipy - matplotlib - h5py - pillow + - libgcc-ng # [linux] + - libstdcxx-ng # [linux] + - _openmp_mutex # [linux] - dxchange - olefile - pywavelets From 5a10a3129795ed0c21233ac6071f7bcc4d3051b1 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 7 Nov 2024 10:00:52 +0000 Subject: [PATCH 4/6] CI: macos --- .github/workflows/build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b2b323a39..e3cc744cf2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,19 +63,31 @@ jobs: conda env remove -n "${{ steps.reqs.outputs.envname }}" test: defaults: {run: {shell: 'bash -el {0}'}} - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }}-latest strategy: matrix: include: - python-version: '3.10' numpy-version: 1.23 + os: ubuntu + - python-version: 3.12 + numpy-version: 1.26 + os: ubuntu - python-version: 3.12 numpy-version: 1.26 + os: macos steps: - uses: actions/checkout@v4 with: {fetch-depth: 0, submodules: recursive} - name: set requirements - run: sed -ri -e '/ python /d' -e 's/(.* numpy) .*/\1=${{ matrix.numpy-version }}/' -e 's/=cuda*//' -e '/tigre/d' scripts/requirements-test.yml + run: | + case ${{ matrix.os }} in + ubuntu) + sed -ri -e '/ python /d' -e 's/(.* numpy) .*/\1=${{ matrix.numpy-version }}/' -e 's/=cuda*//' -e '/tigre/d' scripts/requirements-test.yml ;; + macos) + sed -ri '' -e '/ python /d' -e 's/(.* numpy) .*/\1=${{ matrix.numpy-version }}/' -e '/not arm64/d' -e '/linux/d' scripts/requirements-test.yml ; + echo -e " - astra-toolbox=2.2.0\n - llvm-openmp" >> scripts/requirements-test.yml ;; + esac - uses: conda-incubator/setup-miniconda@v3 with: python-version: ${{ matrix.python-version }} From 3ce7243262b16f59b108fe7daa2ccdb33e04afae Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 7 Nov 2024 10:09:50 +0000 Subject: [PATCH 5/6] debug (TODO: revert) --- .github/workflows/build.yml | 224 ------------------------------------ 1 file changed, 224 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e3cc744cf2..0ea29767f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,57 +22,12 @@ on: - 'NOTICE.txt' - 'README.md' jobs: - test-cuda: - defaults: {run: {shell: 'bash -el {0}'}} - runs-on: [self-hosted, python, cuda] - strategy: - matrix: - python-version: [3.11] - numpy-version: [1.25] - steps: - - uses: actions/checkout@v4 - with: {fetch-depth: 0, submodules: recursive} - - id: reqs - name: set requirements - run: | - envname="${GITHUB_REPOSITORY##*/}-${GITHUB_RUN_ID}.${GITHUB_RUN_NUMBER}" - echo "envname=$envname" >> $GITHUB_OUTPUT - sed -ri -e 's/^(name: ).*/\1$envname/' -e '/ python /d' -e 's/(.* numpy) .*/\1=${{ matrix.numpy-version }}/' scripts/requirements-test.yml - - uses: conda-incubator/setup-miniconda@v3 - with: - python-version: ${{ matrix.python-version }} - environment-file: scripts/requirements-test.yml - activate-environment: ${{ steps.reqs.outputs.envname }} - run-post: false - - id: build - name: build - run: | - conda activate "${{ steps.reqs.outputs.envname }}" - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" - cmake --build ./build --target install - - name: test - run: | - conda activate "${{ steps.reqs.outputs.envname }}" - TESTS_FORCE_GPU=1 python -m unittest discover -v -k tigre -k TIGRE -k astra -k ASTRA -k gpu -k GPU ./Wrappers/Python/test - - if: always() - name: Post Run conda-incubator/setup-miniconda@v3 - shell: bash - run: | - sed -i '/${{ steps.reqs.outputs.envname }}/d' ~/.profile - source ~/.profile - conda env remove -n "${{ steps.reqs.outputs.envname }}" test: defaults: {run: {shell: 'bash -el {0}'}} runs-on: ${{ matrix.os }}-latest strategy: matrix: include: - - python-version: '3.10' - numpy-version: 1.23 - os: ubuntu - - python-version: 3.12 - numpy-version: 1.26 - os: ubuntu - python-version: 3.12 numpy-version: 1.26 os: macos @@ -99,182 +54,3 @@ jobs: cmake --build ./build --target install - name: test run: python -m unittest discover -v ./Wrappers/Python/test - conda-matrix: - runs-on: ubuntu-latest - outputs: - python-version: ${{ steps.matrix.outputs.python-version }} - numpy-version: ${{ steps.matrix.outputs.numpy-version }} - steps: - - id: matrix - run: | - if ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags') }}; then - echo "python-version=['3.10', 3.11]" >> $GITHUB_OUTPUT - echo "numpy-version=[1.23, 1.24, 1.25, 1.26]" >> $GITHUB_OUTPUT - else - echo "python-version=['3.10']" >> $GITHUB_OUTPUT - echo "numpy-version=[1.23]" >> $GITHUB_OUTPUT - fi - conda: - defaults: {run: {shell: 'bash -el {0}'}} - runs-on: ubuntu-latest - needs: conda-matrix - strategy: - matrix: - python-version: ${{ fromJson(needs.conda-matrix.outputs.python-version) }} - numpy-version: ${{ fromJson(needs.conda-matrix.outputs.numpy-version) }} - include: - - python-version: 3.12 - numpy-version: 1.26 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - ref: ${{ github.event.pull_request.head.sha || github.ref }} # fix SHA - - uses: conda-incubator/setup-miniconda@v3 - with: - python-version: ${{ matrix.python-version }} - mamba-version: "1.5" - channels: conda-forge - - name: conda build & test - working-directory: recipe - run: | - conda install boa - conda mambabuild . -c conda-forge -c https://tomography.stfc.ac.uk/conda --override-channels --python=${{ matrix.python-version }} --numpy=${{ matrix.numpy-version }} --output-folder . - - uses: actions/upload-artifact@v4 - with: - name: cil-package-py${{ matrix.python-version }}-np${{ matrix.numpy-version }} - path: recipe/linux-64/cil* - - name: anaconda upload -c ccpi - if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags') - run: | - conda install anaconda-client - anaconda -v -t ${{ secrets.CCPI_CONDA_TOKEN }} upload --force --label ${{ startsWith(github.ref, 'refs/tags') && 'main' || 'dev' }} recipe/linux-64/cil* - - name: conda upload -c tomography.stfc.ac.uk/conda - if: startsWith(github.ref, 'refs/tags') - run: | - echo '${{ secrets.STFC_SSH_KEY }}' > ./key - chmod 600 ./key - rsync -e 'ssh -o StrictHostKeyChecking=no -i ./key' -P recipe/linux-64/cil* \ - '${{ secrets.STFC_SSH_HOST }}:${{ secrets.STFC_SSH_CONDA_DIR }}/linux-64/' - conda-reindex: - if: startsWith(github.ref, 'refs/tags') - needs: conda - runs-on: ubuntu-latest - steps: - - name: conda index tomography.stfc.ac.uk/conda - run: | - echo '${{ secrets.STFC_SSH_KEY }}' > ./key - chmod 600 ./key - ssh -o StrictHostKeyChecking=no -i ./key ${{ secrets.STFC_SSH_HOST }} \ - 'bash -lic "conda index --bz2 --zst --run-exports --channeldata --rss -n ccpi ${{ secrets.STFC_SSH_CONDA_DIR }}"' - docs: - defaults: {run: {shell: 'bash -el {0}', working-directory: docs}} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - ref: ${{ github.event.pull_request.head.sha || github.ref }} # fix SHA - - uses: conda-incubator/setup-miniconda@v3 - with: {python-version: 3.11} - - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.2' - bundler-cache: true - cache-version: 0 - - name: install dependencies - run: | - conda install -c conda-forge -yq conda-merge - conda-merge ../scripts/requirements-test.yml docs_environment.yml > environment.yml - conda env update -n test - conda list - - name: build cil - working-directory: . - run: | - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" - cmake --build ./build --target install - - name: checkout docs - uses: actions/checkout@v4 - with: - path: docs/build - ref: gh-pages - - id: pages - uses: actions/configure-pages@v5 - - name: update web pages (jekyll) - run: make JEKYLLOPTS="--baseurl ${{ steps.pages.outputs.base_path }}" web-deps web - env: {JEKYLL_ENV: production} - - name: update docs pages (sphinx) - run: | - docs_dir="${{ github.ref_name }}" - docs_dir="${docs_dir//\//_}" - if test "$docs_dir" = master; then docs_dir=nightly; fi - make BUILDSUBDIR="$docs_dir" dirhtml - - uses: actions/upload-artifact@v4 - with: - name: DocumentationHTML - path: docs/build - - name: Push changes - if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags') - uses: casperdcl/push-dir@v1 - with: - message: Update documentation - branch: gh-pages - dir: docs/build - nojekyll: true - docker: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - ref: ${{ github.event.pull_request.head.sha || github.ref }} # fix SHA - - uses: jlumbroso/free-disk-space@v1.3.1 - with: - docker-images: false - large-packages: false - - uses: docker/setup-buildx-action@v3 - - uses: docker/metadata-action@v5 - id: meta - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=branch - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - labels: | - org.opencontainers.image.licenses=Apache-2.0 AND BSD-3-Clause AND GPL-3.0 - - uses: docker/login-action@v3 - if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags') - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/build-push-action@v6 - with: - cache-from: type=gha - cache-to: type=gha,mode=max - context: . - load: true - tags: tomographicimaging/cil:test - - name: test - run: > - docker run --rm -v .:/CIL tomographicimaging/cil:test /bin/bash -c - 'python -m unittest discover -v /CIL/Wrappers/Python/test' - - uses: docker/build-push-action@v6 - with: - cache-from: type=gha - cache-to: type=gha,mode=max - context: . - push: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags') }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - pass: - needs: [test-cuda, test, conda, docs, docker] - runs-on: ubuntu-latest - steps: [{run: echo success}] From 9c1d3018ece70198e42f30a8355074a7564d0a92 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 7 Nov 2024 10:40:37 +0000 Subject: [PATCH 6/6] tests: relax tolerances --- Wrappers/Python/test/test_DataContainer.py | 5 +- Wrappers/Python/test/test_algorithms.py | 146 ++++++++++----------- Wrappers/Python/test/test_functions.py | 6 +- 3 files changed, 76 insertions(+), 81 deletions(-) diff --git a/Wrappers/Python/test/test_DataContainer.py b/Wrappers/Python/test/test_DataContainer.py index b1003dddd2..fa75b8c2fb 100644 --- a/Wrappers/Python/test/test_DataContainer.py +++ b/Wrappers/Python/test/test_DataContainer.py @@ -1037,13 +1037,12 @@ def test_sapyb_datacontainer_f(self): # equals to 1 + -1 = 0 out = d1.sapyb(a,d2,b) res = np.zeros_like(d1.as_array()) - np.testing.assert_array_equal(res, out.as_array()) + np.testing.assert_array_almost_equal(res, out.as_array(), decimal=7) out.fill(0) d1.sapyb(a,d2,b, out) res = np.zeros_like(d1.as_array()) - np.testing.assert_array_equal(res, out.as_array()) - + np.testing.assert_array_almost_equal(res, out.as_array(), decimal=7) def test_sapyb_scalar_f(self): # a,b scalar diff --git a/Wrappers/Python/test/test_algorithms.py b/Wrappers/Python/test/test_algorithms.py index 845b5a44ce..19aea30941 100644 --- a/Wrappers/Python/test/test_algorithms.py +++ b/Wrappers/Python/test/test_algorithms.py @@ -568,21 +568,21 @@ def setUp(self): self.alg = CGLS(initial=self.initial, operator=self.operator, data=self.data, update_objective_interval=2) - + def test_initialization_with_default_tolerance(self):#can be deprecated when tolerance is deprecated in CGLS self.assertEqual(self.alg.tolerance, 0) - + def test_initialization_with_custom_tolerance(self):#can be deprecated when tolerance is deprecated in CGLS with self.assertWarns(DeprecationWarning): alg = CGLS(initial=self.initial, operator=self.operator, data=self.data, tolerance=0.01) self.assertEqual(alg.tolerance, 0.01) - + def test_set_up(self): # Test the set_up method - + self.alg.set_up(initial=self.initial, operator=self.operator, data=self.data) - + # Check if internal variables are set up correctly self.assertNumpyArrayEqual(self.alg.x.as_array(), self.initial.as_array()) self.assertEqual(self.alg.operator, self.operator) @@ -594,18 +594,18 @@ def test_set_up(self): def test_update(self): self.alg.set_up(initial=self.mock_initial, operator=self.mock_operator, data=self.mock_data) - + self.alg.update() - + self.mock_operator.direct.assert_called_with(self.mock_data, out=self.alg.q) self.mock_operator.adjoint.assert_called_with(self.alg.r, out=self.alg.s) - + def test_convergence(self): - + self.alg.run(20, verbose=0) self.assertNumpyArrayAlmostEqual(self.alg.x.as_array(), self.data.as_array()) - + def test_should_stop_flag_false(self): #can be deprecated when tolerance is deprecated in CGLS # Mocking norms to ensure tolerance isn't reached self.alg.run(2) @@ -615,7 +615,7 @@ def test_should_stop_flag_false(self): #can be deprecated when tolerance is depr self.alg.normx = 0.1 self.assertFalse(self.alg.flag()) - + def test_should_stop_flag_true(self): #can be deprecated when tolerance is deprecated in CGLS # Mocking norms to ensure tolerance is reached self.alg.run(4) @@ -625,13 +625,13 @@ def test_should_stop_flag_true(self): #can be deprecated when tolerance is depre self.alg.normx = 10 self.assertTrue(self.alg.flag()) - + def test_tolerance_reached_immediately(self): #can be deprecated when tolerance is deprecated in CGLS alg = CGLS(initial=self.operator.domain_geometry().allocate(0), operator=self.operator, data=self.operator.domain_geometry().allocate(0)) alg.run(2) - - + + def test_update_objective(self): # Mocking squared_norm to return a finite value @@ -643,25 +643,25 @@ def test_update_objective(self): # Ensure the loss list is updated self.assertEqual(self.alg.loss, [1.0]) - + def test_update_objective_with_nan_raises_stop_iteration(self): # Mocking squared_norm to return NaN self.alg.r=MagicMock() self.alg.r.squared_norm.return_value = np.nan - + with self.assertRaises(StopIteration): self.alg.update_objective() - + def test_update(self): self.alg.gamma=4 - + self.alg.update() norm=(self.data-self.initial).squared_norm() alpha=4/norm self.assertNumpyArrayEqual(self.alg.x.as_array(), (self.initial+alpha*(self.data-self.initial)).as_array()) self.assertNumpyArrayEqual(self.alg.r.as_array(), (self.data - self.initial-alpha*(self.data-self.initial)).as_array()) beta= ((self.data - self.initial-alpha*(self.data-self.initial)).norm()**2)/4 - self.assertNumpyArrayEqual(self.alg.p.as_array(), ((self.data - self.initial-alpha*(self.data-self.initial))+beta*(self.data-self.initial)).as_array()) + self.assertNumpyArrayAlmostEqual(self.alg.p.as_array(), ((self.data - self.initial-alpha*(self.data-self.initial))+beta*(self.data-self.initial)).as_array(), decimal=7) class TestPDHG(CCPiTestClass): def test_PDHG_Denoising(self): @@ -927,7 +927,7 @@ def test_PDHG_strongly_convex_both_fconj_and_g(self): operator = IdentityOperator(ig) try: - pdhg = PDHG(f=f, g=g, operator=operator, + pdhg = PDHG(f=f, g=g, operator=operator, gamma_g=0.5, gamma_fconj=0.5) pdhg.run(verbose=0) except ValueError as err: @@ -959,12 +959,12 @@ def setUp(self): def tearDown(self): pass - + def test_set_up(self): initial = self.A2.domain_geometry().allocate(0) sirt = SIRT(initial=initial, operator=self.A2, data=self.b2, lower=0, upper=1) - + # Test if set_up correctly configures the object self.assertTrue(sirt.configured) self.assertIsNotNone(sirt.x) @@ -972,11 +972,11 @@ def test_set_up(self): self.assertIsNotNone(sirt.constraint) self.assertEqual(sirt.constraint.lower, 0) self.assertEqual(sirt.constraint.upper, 1) - + constraint = IndicatorBox(lower=0, upper=1) sirt = SIRT(initial=None, operator=self.A2, data=self.b2, constraint=constraint) - + # Test if set_up correctly configures the object with constraint self.assertTrue(sirt.configured) self.assertEqual(sirt.constraint, constraint) @@ -985,15 +985,15 @@ def test_set_up(self): with self.assertRaises(ValueError) as context: sirt = SIRT(initial=None, operator=None, data=self.b2) self.assertEqual(str(context.exception), 'You must pass an `operator` to the SIRT algorithm') - + with self.assertRaises(ValueError) as context: sirt = SIRT(initial=None, operator=self.A2, data=None) self.assertEqual(str(context.exception), 'You must pass `data` to the SIRT algorithm') - with self.assertRaises(ValueError) as context: - sirt = SIRT(initial=None, operator=None, data=None) - self.assertEqual(str(context.exception), - 'You must pass an `operator` and `data` to the SIRT algorithm') + with self.assertRaises(ValueError) as context: + sirt = SIRT(initial=None, operator=None, data=None) + self.assertEqual(str(context.exception), + 'You must pass an `operator` and `data` to the SIRT algorithm') sirt = SIRT(initial=None, operator=self.A2, data=self.b2) self.assertTrue(sirt.configured) @@ -1142,14 +1142,14 @@ def add_noise(self, sino, noise='gaussian', seed=10): else: raise ValueError('Unsupported Noise ', noise) return noisy_data - + @unittest.skipUnless(has_astra, "cil-astra not available") def test_SPDHG_vs_PDHG_implicit(self): data = dataexample.SIMPLE_PHANTOM_2D.get(size=(16, 16)) alpha = 0.05 num_subsets = 10 - - + + ig = data.geometry ig.voxel_size_x = 0.1 ig.voxel_size_y = 0.1 @@ -1167,13 +1167,13 @@ def test_SPDHG_vs_PDHG_implicit(self): # Create noisy data. noisy_data = self.add_noise(sin, noise='poisson', seed=10) - + # Create BlockOperator operator = Aop f = KullbackLeibler(b=noisy_data) g = alpha * TotalVariation(10, 1e-4, lower=0, warm_start=True) - + # % 'implicit' PDHG, preconditioned step-sizes tau_tmp = 1. sigma_tmp = 1. @@ -1217,7 +1217,7 @@ def test_SPDHG_vs_PDHG_implicit(self): def test_SPDHG_vs_PDHG_explicit(self): alpha = .05 num_subsets = 10 - + data = dataexample.SIMPLE_PHANTOM_2D.get(size=(16, 16)) ig = data.geometry @@ -1248,7 +1248,7 @@ def test_SPDHG_vs_PDHG_explicit(self): operators = list(A.operators) + [op1] K = BlockOperator(* operators ) - + # block function F = BlockFunction(*[*[KullbackLeibler(b=data_sub[i]) for i in range(subsets)] + [MixedL21Norm()]]) @@ -1358,34 +1358,34 @@ def test_CGLSEarlyStopping(self): operator = IdentityOperator(ig) alg = CGLS(initial=initial, operator=operator, data=data, update_objective_interval=2) - + #Test init cb = callbacks.CGLSEarlyStopping(epsilon = 0.1, omega = 33) self.assertEqual(cb.epsilon, 0.1) self.assertEqual(cb.omega, 33) - + #Test default values cb = callbacks.CGLSEarlyStopping() self.assertEqual(cb.epsilon, 1e-6) self.assertEqual(cb.omega, 1e6) - + #Tests it doesn't stops iterations if the norm of the current residual is not less than epsilon times the norm of the original residual alg.norms = 10 alg.norms0 = 99 callbacks.CGLSEarlyStopping(epsilon = 0.1)(alg) - + #Test it stops iterations if the norm of the current residual is less than epsilon times the norm of the original residual alg.norms = 1 alg.norms0 = 100 with self.assertRaises(StopIteration): callbacks.CGLSEarlyStopping(epsilon = 0.1)(alg) - - #Test it doesn't stop iterations if the norm of x is smaller than omega + + #Test it doesn't stop iterations if the norm of x is smaller than omega alg.norms = 10 alg.norms0 = 99 alg.x = data callbacks.CGLSEarlyStopping(epsilon = 0.1)(alg) - + #Test it stops iterations if the norm of x is larger than omega alg.norms = 10 alg.norms0 = 99 @@ -1406,8 +1406,8 @@ def test_EarlyStoppingObjectiveValue(self): alg.loss=[5, 5.001] with self.assertRaises(StopIteration): callbacks.EarlyStoppingObjectiveValue(0.1)(alg) - - + + class TestADMM(unittest.TestCase): def setUp(self): ig = ImageGeometry(2, 3, 2) @@ -1504,9 +1504,9 @@ def setUp(self): # default test data self.data = dataexample.CAMERA.get(size=(32, 32)) - - - + + + def test_init_and_set_up(self): F1= 0.5 * L2NormSquared(b=self.data) H1 = 0.1* MixedL21Norm() @@ -1518,26 +1518,26 @@ def test_init_and_set_up(self): self.assertEqual(algo_pd3o.delta,F1.L/(2.0*operator.norm()**2) ) np.testing.assert_array_equal(algo_pd3o.x.array, self.data.geometry.allocate(0).array) self.assertTrue(algo_pd3o.configured) - + algo_pd3o=PD3O(f=F1, g=G1, h=H1, operator=operator, gamma=3, delta=43.1, initial=self.data.geometry.allocate('random', seed=3)) self.assertEqual(algo_pd3o.gamma,3 ) self.assertEqual(algo_pd3o.delta,43.1 ) np.testing.assert_array_equal(algo_pd3o.x.array, self.data.geometry.allocate('random', seed=3).array) self.assertTrue(algo_pd3o.configured) - + with self.assertWarnsRegex(UserWarning,"Please use PDHG instead." ): algo_pd3o=PD3O(f=ZeroFunction(), g=G1, h=H1, operator=operator, delta=43.1) self.assertEqual(algo_pd3o.gamma,1.0/operator.norm() ) - + def test_PD3O_PDHG_denoising_1_iteration(self): # compare the TV denoising problem using - # PDHG, PD3O for 1 iteration + # PDHG, PD3O for 1 iteration # regularisation parameter - alpha = 0.1 + alpha = 0.1 - # setup PDHG denoising + # setup PDHG denoising F = alpha * MixedL21Norm() operator = GradientOperator(self.data.geometry) norm_op = operator.norm() @@ -1547,42 +1547,42 @@ def test_PD3O_PDHG_denoising_1_iteration(self): pdhg = PDHG(f=F, g=G, operator=operator, tau=tau, sigma=sigma, update_objective_interval = 100) pdhg.run(1) - # setup PD3O denoising (F=ZeroFunction) + # setup PD3O denoising (F=ZeroFunction) H = alpha * MixedL21Norm() - + F = ZeroFunction() gamma = 1./norm_op delta = 1./norm_op pd3O = PD3O(f=F, g=G, h=H, operator=operator, gamma=gamma, delta=delta, update_objective_interval = 100) - pd3O.run(1) - + pd3O.run(1) + # PD3O vs pdhg - np.testing.assert_allclose(pdhg.solution.array, pd3O.solution.array,atol=1e-2) - + np.testing.assert_allclose(pdhg.solution.array, pd3O.solution.array,atol=1e-2) + # objective values - np.testing.assert_allclose(pdhg.objective[-1], pd3O.objective[-1],atol=1e-2) - - + np.testing.assert_allclose(pdhg.objective[-1], pd3O.objective[-1],atol=1e-2) + + def test_pd3o_convergence(self): - + # pd30 convergence test using TV denoising # regularisation parameter - alpha = 0.11 + alpha = 0.11 # use TotalVariation from CIL (with Fast Gradient Projection algorithm) TV = TotalVariation(max_iteration=200) - tv_cil = TV.proximal(self.data, tau=alpha) + tv_cil = TV.proximal(self.data, tau=alpha) + - F = alpha * MixedL21Norm() operator = GradientOperator(self.data.geometry) norm_op= operator.norm() - - # setup PD3O denoising (H proximalble and G,F = 1/4 * L2NormSquared) + + # setup PD3O denoising (H proximalble and G,F = 1/4 * L2NormSquared) H = alpha * MixedL21Norm() G = 0.25 * L2NormSquared(b=self.data) F = 0.25 * L2NormSquared(b=self.data) @@ -1591,11 +1591,7 @@ def test_pd3o_convergence(self): pd3O_with_f = PD3O(f=F, g=G, h=H, operator=operator, gamma=gamma, delta=delta, update_objective_interval = 100) - pd3O_with_f.run(1000) + pd3O_with_f.run(1000) # pd30 vs fista - np.testing.assert_allclose(tv_cil.array, pd3O_with_f.solution.array,atol=1e-2) - - - - + np.testing.assert_allclose(tv_cil.array, pd3O_with_f.solution.array,atol=1e-2) diff --git a/Wrappers/Python/test/test_functions.py b/Wrappers/Python/test/test_functions.py index 8fa9c1558a..fff147d980 100644 --- a/Wrappers/Python/test/test_functions.py +++ b/Wrappers/Python/test/test_functions.py @@ -1139,7 +1139,7 @@ def soft_shrinkage_test(self, x): tau = -1. with self.assertWarns(UserWarning): ret = soft_shrinkage(-0.5 *x, tau) - np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array())) + np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array()), 2e-7, 3e-7) tau = -3j with self.assertRaises(ValueError): ret = soft_shrinkage(-0.5 *x, tau) @@ -1162,7 +1162,7 @@ def soft_shrinkage_test(self, x): np.testing.assert_allclose(ret.as_array(), -1 * np.zeros_like(x.as_array())) tau = -1.* np.ones_like(x.as_array()) ret = soft_shrinkage(-0.5 *x, tau) - np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array())) + np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array()), 2e-7, 3e-7) # tau DataContainer tau = 1. * x @@ -1182,7 +1182,7 @@ def soft_shrinkage_test(self, x): np.testing.assert_allclose(ret.as_array(), -1 * np.zeros_like(x.as_array())) tau = -1. * x ret = soft_shrinkage(-0.5 *x, tau) - np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array())) + np.testing.assert_allclose(ret.as_array(), -1.5 * np.ones_like(x.as_array()), 2e-7, 3e-7) np.testing.assert_allclose(ret.as_array().imag, np.zeros_like(ret.as_array().imag), atol=1e-6, rtol=1e-6)