Skip to content

Commit

Permalink
Merge branch 'master' into fista-second-term
Browse files Browse the repository at this point in the history
  • Loading branch information
MargaretDuff authored Dec 20, 2023
2 parents 364d66e + 5fa7a49 commit 8c451c1
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 106 deletions.
46 changes: 24 additions & 22 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
# GitHub Actions

There is a single github action file with multiple jobs, which builds both the conda package and documentation, and optionally publishes the documentation: [conda_and_docs_build](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/conda_and_docs_build.yml)
There is a single github action file with multiple jobs, which builds both the conda package and documentation, and optionally publishes the documentation: [build](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/build.yml)

## Building the Conda Package: conda_build job
This github action builds and tests the conda package, by using the [conda-package-publish-action](https://github.com/paskino/conda-package-publish-action)
## Building the Conda Package: conda job

This github action builds and tests the conda package, by using the [conda-package-publish-action](https://github.com/TomographicImaging/conda-package-publish-action)

When pushing to master or creating an [annotated tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging), *all* variants are built and tested.

When opening or modifying a pull request to master, a single variant is built and tested. This variant is for linux with `python=3.7` and `numpy=1.18`.
When opening or modifying a pull request to master, a single variant is built and tested. This variant is for linux with `python=3.9` and `numpy=1.22`.

The action does not publish to conda, instead this is done by jenkins. This is because github-actions do not have a GPU.

It looks for conda-build dependencies in the channels listed [here](https://github.com/TomographicImaging/CIL/blob/f66a2e94fc65efb47889338025e85b805406b376/.github/workflows/conda_and_docs_build.yml#L55). If you add any new dependencies, the appropriate channels need to be added to this line.
It looks for conda-build dependencies in the channels listed [here](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/build.yml#L49). If you add any new dependencies, the appropriate channels need to be added to this line.

An artifact of the resulting tar.bz2 file is made available in the 'Summary' section of the action. It is called `cil-package`. This is used by the **docs_build** job. It can be found by going to the ‘Actions’ tab, and selecting the appropriate run of `.github/workflows/conda_and_docs_build.yml`, or by clicking on the tick on the action in the "All checks have passed/failed" section of a PR. When viewing the summary for the run of the action, there is an `Artifact` section at the bottom of the page. Clicking on `cil-package` allows you to download a zip folder containing the tar.bz2 file.
An artifact of the resulting tar.bz2 file is made available in the 'Summary' section of the action. It is called `cil-package`. This is used by the **docs** job. It can be found by going to the ‘Actions’ tab, and selecting the appropriate run of `.github/workflows/build.yml`, or by clicking on the tick on the action in the "All checks have passed/failed" section of a PR. When viewing the summary for the run of the action, there is an `Artifact` section at the bottom of the page. Clicking on `cil-package` allows you to download a zip folder containing the tar.bz2 file.

## Building/Publishing Documentation: docs_build and docs_publish jobs
## Building/Publishing Documentation: docs job

This github action builds and optionally publishes the documentation located in [docs/source](https://github.com/TomographicImaging/CIL/tree/master/docs/source). To do this it uses a forked version of the [build-sphinx-action](https://github.com/lauramurgatroyd/build-sphinx-action)
This github action builds and optionally publishes the documentation located in [docs/source](https://github.com/TomographicImaging/CIL/tree/master/docs/source). To do this it uses a forked version of the [build-sphinx-action](https://github.com/lauramurgatroyd/build-sphinx-action).

1. [docs_build](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/docs_build_and_publish.yml#L12):
- creates a miniconda environment from [docs_environment.yml](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/docs/docs_environment.yml)
- installs cil into the miniconda environment, using the tar.bz2 artifact (cil-package) created in the **conda_build** job
- builds the documentation with sphinx
- uses upload-artifact to upload the html files: HTMLDocumentation which may then be used by **docs_publish**. These can alos be downloaded to view.
The [docs](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/build.yml#L59) job:

2. [docs_publish](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/docs_build_and_publish.yml#L40):
- uses download-artifact to retrieve the built html files
- pushes the html files to the `nightly` folder on the gh-pages branch
- creates a miniconda environment from [docs_environment.yml](https://github.com/TomographicImaging/CIL/blob/master/.github/workflows/docs/docs_environment.yml)
- installs cil into the miniconda environment, using the tar.bz2 artifact (cil-package) created in the **conda** job
- builds the documentation with sphinx
- uses upload-artifact to upload the html files: `HTMLDocumentation`, which can be downloaded to view
- pushes the html files to the `nightly` folder on the gh-pages branch

If opening or modifying a pull request to master, `docs_build` is run, but not `publish`.
If pushing to master or tagging, the documentation is built *and* published (both the `docs_build` and `docs_publish` jobs are run).
If opening or modifying a pull request to master, `docs` is run, but the final gh-pages step is skipped.
If pushing to master or tagging, the documentation is built *and* published to gh-pages.

### Viewing Built Documentation
The `docs_build` job builds the documentation and uploads it as an artifact, in a folder named `DocumentationHTML`.
This can be found by going to the ‘Actions’ tab, and selecting the appropriate run of `.github/workflows/conda_and_docs_build.yml`, or by clicking on the tick on the action in the "All checks have passed/failed" section of a PR.

The `docs` job builds the documentation and uploads it as an artifact, in a folder named `DocumentationHTML`.
This can be found by going to the ‘Actions’ tab, and selecting the appropriate run of `.github/workflows/build.yml`, or by clicking on the tick on the action in the "All checks have passed/failed" section of a PR.

When viewing the `Summary` for the run of the action, there is an `Artifact` section at the bottom of the page.
Clicking on `DocumentationHTML` allows you to download a zip folder containing the built html files. This allows you to preview the documentation site before it is published.

### Publication of the Documentation

The documentation is hosted on the [github site](https://tomographicimaging.github.io/CIL/) associated with the repository.
This is built from the [gh-pages branch](https://github.com/TomographicImaging/CIL/tree/gh-pages).
This is built from the [gh-pages branch](https://github.com/TomographicImaging/CIL/tree/gh-pages).

If you are an admin of the CIL repository you are able to see the settings for the site by going to `Settings->Pages`.

To publish the documentation, the publish job of the gh-action pushes the documentation changes to the `gh-pages` branch.
Any push to this branch automatically updates the github site.

### Initial Setup of the Docs Site & Action

To get the action to work I first had to:

1. [Create a gh-pages branch](https://gist.github.com/ramnathv/2227408) - note this only worked in bash, not windows command line.
2. [Set the source](https://github.com/TomographicImaging/CIL/settings/pages) for our github pages to be the gh-pages branch.

I followed the examples on the [sphinx build action page](https://github.com/marketplace/actions/sphinx-build), specifically this [example workflow](https://github.com/ammaraskar/sphinx-action-test/blob/master/.github/workflows/default.yml)

Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2021 United Kingdom Research and Innovation
# Copyright 2021 The University of Manchester

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Authors:
# CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt
name: conda_and_docs_build
name: build
on:
release:
types: [published]
push:
branches: [master]
tags:
- '**'
tags: ['**']
paths-ignore:
- 'CHANGELOG.md'
- 'CITATION.cff'
Expand All @@ -41,7 +36,7 @@ on:
- 'NOTICE.txt'
- 'README.md'
jobs:
conda_build:
conda:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
Expand All @@ -56,54 +51,44 @@ jobs:
convert_osx: false
test_pyver: 3.9
test_npver: 1.22
- name: Upload artifact of the conda package.
- name: Upload artifact of the conda package
uses: actions/upload-artifact@v3
with:
name: cil-package
path: recipe/linux-64/cil*
docs_build:
needs: conda_build
docs:
needs: conda
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: ls
- name: Download artifact of the conda package.
- name: Download artifact of the conda package
uses: actions/download-artifact@v3
with:
name: cil-package
path: 'conda_package'
- uses: conda-incubator/setup-miniconda@v2
path: conda_package
- uses: conda-incubator/setup-miniconda@v3
with:
python-version: 3.9
- uses: lauramurgatroyd/build-sphinx-action@v0.1.4
with:
DOCS_PATH: docs
CONDA_BUILD_ENV_FILEPATH: docs/docs_environment.yml
ARTIFACT_NAME: DocumentationHTML
PACKAGE_FOLDER_PATH: conda_package
PACKAGE_NAME: cil
PACKAGE_CONDA_CHANNELS: conda-forge -c intel -c ccpi
BUILD_SUBDIR_NAME: nightly
docs_path: docs
conda_build_env_filepath: docs/docs_environment.yml
artifact_name: DocumentationHTML
package_folder_path: conda_package
package_name: cil
package_conda_channels: conda-forge -c intel -c ccpi
build_subdir_name: nightly
python_version: 3.9
docs_publish:
needs: docs_build
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download artifact of the html output.
uses: actions/download-artifact@v3
with:
name: DocumentationHTML
path: docs/build
- name: redirect to nightly
run: >
sed 's#DESTINATION#https://tomographicimaging.github.io/CIL/nightly/#g'
docs/.redirect-template.html
> docs/build/index.html
- name: Push changes
if: github.ref == 'refs/heads/master'
uses: casperdcl/push-dir@v1
with:
message: Update documentation
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
- ZeroOperator no longer relies on the default of allocate
- Bug fix in SIRF TotalVariation unit tests with warm_start
- Allow show2D to be used with 3D `DataContainer` instances
- Update documentation for symmetrised gradient
- Added documentation for CompositionOperator and SumOperator


* 23.1.0
- Fix bug in IndicatorBox proximal_conjugate
Expand Down
106 changes: 87 additions & 19 deletions Wrappers/Python/cil/optimisation/operators/Operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,28 @@ def dot_test(operator, domain_init=None, range_init=None, tolerance=1e-6, **kwar
Evaluates if the following equivalence holds
.. math::
Ax\times y = y \times A^Tx
:param operator: operator to test the dot_test
:param range_init: optional initialisation container in the operator range
:param domain_init: optional initialisation container in the operator domain
:param seed: Seed random generator
:type : int, default = 1
:param tolerance: Check if the following expression is below the tolerance
Parameters
----------
operator:
operator to test the dot_test
range_init:
optional initialisation container in the operator range
domain_init:
optional initialisation container in the operator domain
seed: int, default = 1
Seed random generator
tolerance:float, default 1e-6
Check if the following expression is below the tolerance
.. math::
|Ax\times y - y \times A^Tx|/(\|A\|\|x\|\|y\| + 1e-12) < tolerance
:type : float, default 1e-6
:returns: boolean, True if the test is passed.
Returns
-------
boolean, True if the test is passed.
'''

seed = kwargs.get('seed', 1)
Expand Down Expand Up @@ -373,16 +383,15 @@ class ScaledOperator(Operator):
For the rest it behaves like the operator it holds.
Parameters
----------
operator: a Operator or LinearOperator
scalar: a scalar multiplier
-----------
operator: a `Operator` or `LinearOperator`
scalar: Number
a scalar multiplier
Example
-------
The scaled operator behaves like the following:
--------
The scaled operator behaves like the following:
.. code-block:: python
Expand Down Expand Up @@ -441,9 +450,23 @@ def is_linear(self):
###############################################################################

class SumOperator(Operator):
"""Sums two operators.
For example, `SumOperator(left, right).direct(x)` is equivalent to `left.direct(x)+right.direct(x)`
Parameters
----------
operator1: `Operator`
The first `Operator` in the sum
operator2: `Operator`
The second `Operator` in the sum
Note
----
Both operators must have the same domain and range.
"""
def __init__(self, operator1, operator2):

self.operator1 = operator1
self.operator2 = operator2

Expand All @@ -459,15 +482,32 @@ def __init__(self, operator1, operator2):
range_geometry=self.operator1.range_geometry())

def direct(self, x, out=None):

r"""Calls the sum operator
Parameters
----------
x: DataContainer or BlockDataContainer
Element in the domain of the SumOperator
out: DataContainer or BlockDataContainer, default None
If out is not None the output of the SumOperator will be filled in out, otherwise a new object is instantiated and returned.
"""
if out is None:
return self.operator1.direct(x) + self.operator2.direct(x)
else:
self.operator1.direct(x, out=out)
out.add(self.operator2.direct(x), out=out)

def adjoint(self, x, out=None):

r"""Calls the adjoint of the sum operator
Parameters
----------
x: DataContainer or BlockDataContainer
Element in the range of the SumOperator
out: DataContainer or BlockDataContainer, default None
If out is not None the output of the adjoint of the SumOperator will be filled in out, otherwise a new object is instantiated and returned.
"""

if self.linear_flag:
if out is None:
return self.operator1.adjoint(x) + self.operator2.adjoint(x)
Expand All @@ -481,6 +521,7 @@ def is_linear(self):
return self.linear_flag

def calculate_norm(self):
'''Returns the norm of the SumOperator'''
if self.is_linear():
return LinearOperator.calculate_norm(self)

Expand All @@ -492,7 +533,16 @@ def calculate_norm(self):


class CompositionOperator(Operator):
"""Composes one or more operators.
For example, `CompositionOperator(left, right).direct(x)` is equivalent to `left.direct(right.direct(x))`
Parameters
----------
args: `Operator`s
Operators to be composed. As in mathematical notation, the operators will be applied right to left
"""
def __init__(self, *operators, **kwargs):

# get a reference to the operators
Expand All @@ -519,6 +569,15 @@ def __init__(self, *operators, **kwargs):

def direct(self, x, out=None):

"""Calls the composition operator
Parameters
----------
x: DataContainer or BlockDataContainer
Element in the domain of the CompositionOperator
out: DataContainer or BlockDataContainer, default None
If out is not None the output of the CompositionOperator will be filled in out, otherwise a new object is instantiated and returned.
"""
if out is None:
# return self.operator1.direct(self.operator2.direct(x))
# return functools.reduce(lambda X,operator: operator.direct(X),
Expand Down Expand Up @@ -565,7 +624,16 @@ def direct(self, x, out=None):
out.fill(step)

def adjoint(self, x, out=None):

"""Calls the adjoint of the composition operator
Parameters
----------
x: DataContainer or BlockDataContainer
Element in the range of the CompositionOperator
out: DataContainer or BlockDataContainer, default None
If out is not None the output of the adjoint of the CompositionOperator will be filled in out, otherwise a new object is instantiated and returned.
"""

if self.linear_flag:

if out is not None:
Expand Down
Loading

0 comments on commit 8c451c1

Please sign in to comment.