Repository: lanl/scico
Branch: main
Commit: 010a7714678a
Files: 335
Total size: 1.8 MB
Directory structure:
gitextract_aiioi9sl/
├── .coveragerc
├── .flake8
├── .github/
│ ├── codecov.yml
│ ├── isbin.sh
│ └── workflows/
│ ├── check_files.yml
│ ├── lint.yml
│ ├── mypy.yml
│ ├── pypi_upload.yml
│ ├── pytest_latest.yml
│ ├── pytest_macos.yml
│ ├── pytest_ubuntu.yml
│ └── test_examples.yml
├── .gitignore
├── .gitmodules
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGES.rst
├── LICENSE
├── MANIFEST.in
├── README.md
├── conftest.py
├── dev_requirements.txt
├── docs/
│ ├── Makefile
│ ├── docs_requirements.txt
│ ├── rtd_requirements.txt
│ ├── source/
│ │ ├── _static/
│ │ │ └── scico.css
│ │ ├── _templates/
│ │ │ ├── autosummary/
│ │ │ │ └── module.rst
│ │ │ ├── package.rst
│ │ │ └── sidebar/
│ │ │ └── brand.html
│ │ ├── advantages.rst
│ │ ├── api.rst
│ │ ├── classes.rst
│ │ ├── conf/
│ │ │ ├── 10-project.py
│ │ │ ├── 15-theme.py
│ │ │ ├── 20-extensions.py
│ │ │ ├── 25-napoleon.py
│ │ │ ├── 30-autodoc.py
│ │ │ ├── 40-intersphinx.py
│ │ │ ├── 45-mathjax.py
│ │ │ ├── 50-graphviz.py
│ │ │ ├── 55-nbsphinx.py
│ │ │ ├── 60-rtd.py
│ │ │ ├── 70-latex.py
│ │ │ ├── 71-texinfo.py
│ │ │ ├── 72-man_page.py
│ │ │ ├── 80-scico_numpy.py
│ │ │ ├── 81-scico_scipy.py
│ │ │ └── 85-dtype_typehints.py
│ │ ├── conf.py
│ │ ├── contributing.rst
│ │ ├── docsutil.py
│ │ ├── examples.rst
│ │ ├── include/
│ │ │ ├── blockarray.rst
│ │ │ ├── examplenotes.rst
│ │ │ ├── functional.rst
│ │ │ ├── learning.rst
│ │ │ ├── operator.rst
│ │ │ └── optimizer.rst
│ │ ├── index.rst
│ │ ├── install.rst
│ │ ├── inverse.rst
│ │ ├── notes.rst
│ │ ├── overview.rst
│ │ ├── pyfigures/
│ │ │ ├── cylindgrad.py
│ │ │ ├── polargrad.py
│ │ │ ├── spheregrad.py
│ │ │ ├── xray_2d_geom.py
│ │ │ ├── xray_3d_ang.py
│ │ │ ├── xray_3d_vec.py
│ │ │ └── xray_3d_vol.py
│ │ ├── references.bib
│ │ ├── style.rst
│ │ ├── team.rst
│ │ └── zreferences.rst
│ └── tikxfigures/
│ ├── img_align.tex
│ ├── makesvg.sh
│ ├── vol_align_xyz.tex
│ ├── vol_align_xz.tex
│ └── vol_align_yz.tex
├── examples/
│ ├── README.rst
│ ├── examples_requirements.txt
│ ├── jnb.py
│ ├── makeindex.py
│ ├── makenotebooks.py
│ ├── notebooks_requirements.txt
│ ├── removejnberr.py
│ ├── scriptcheck.sh
│ ├── scripts/
│ │ ├── README.rst
│ │ ├── ct_abel_tv_admm.py
│ │ ├── ct_abel_tv_admm_tune.py
│ │ ├── ct_astra_3d_tv_admm.py
│ │ ├── ct_astra_3d_tv_padmm.py
│ │ ├── ct_astra_noreg_pcg.py
│ │ ├── ct_astra_tv_admm.py
│ │ ├── ct_astra_weighted_tv_admm.py
│ │ ├── ct_datagen_foam2.py
│ │ ├── ct_fan_svmbir_ppp_bm3d_admm_prox.py
│ │ ├── ct_modl_train_foam2.py
│ │ ├── ct_multi_tv_admm.py
│ │ ├── ct_odp_train_foam2.py
│ │ ├── ct_projector_comparison_2d.py
│ │ ├── ct_projector_comparison_3d.py
│ │ ├── ct_svmbir_ppp_bm3d_admm_cg.py
│ │ ├── ct_svmbir_ppp_bm3d_admm_prox.py
│ │ ├── ct_svmbir_tv_multi.py
│ │ ├── ct_symcone_tv_padmm.py
│ │ ├── ct_tv_admm.py
│ │ ├── ct_unet_train_foam2.py
│ │ ├── deconv_circ_tv_admm.py
│ │ ├── deconv_datagen_bsds.py
│ │ ├── deconv_datagen_foam1.py
│ │ ├── deconv_microscopy_allchn_tv_admm.py
│ │ ├── deconv_microscopy_tv_admm.py
│ │ ├── deconv_modl_train_foam1.py
│ │ ├── deconv_odp_train_foam1.py
│ │ ├── deconv_ppp_bm3d_admm.py
│ │ ├── deconv_ppp_bm3d_apgm.py
│ │ ├── deconv_ppp_bm4d_admm.py
│ │ ├── deconv_ppp_dncnn_admm.py
│ │ ├── deconv_ppp_dncnn_padmm.py
│ │ ├── deconv_tv_admm.py
│ │ ├── deconv_tv_admm_tune.py
│ │ ├── deconv_tv_padmm.py
│ │ ├── demosaic_ppp_bm3d_admm.py
│ │ ├── denoise_approx_tv_multi.py
│ │ ├── denoise_cplx_tv_nlpadmm.py
│ │ ├── denoise_cplx_tv_pdhg.py
│ │ ├── denoise_datagen_bsds.py
│ │ ├── denoise_dncnn_train_bsds.py
│ │ ├── denoise_dncnn_universal.py
│ │ ├── denoise_l1tv_admm.py
│ │ ├── denoise_ptv_pdhg.py
│ │ ├── denoise_tv_admm.py
│ │ ├── denoise_tv_apgm.py
│ │ ├── denoise_tv_multi.py
│ │ ├── diffusercam_tv_admm.py
│ │ ├── index.rst
│ │ ├── sparsecode_apgm.py
│ │ ├── sparsecode_conv_admm.py
│ │ ├── sparsecode_conv_md_admm.py
│ │ ├── sparsecode_nn_admm.py
│ │ ├── sparsecode_nn_apgm.py
│ │ ├── sparsecode_poisson_apgm.py
│ │ ├── superres_ppp_dncnn_admm.py
│ │ ├── trace_example.py
│ │ └── video_rpca_admm.py
│ ├── updatejnbcode.py
│ └── updatejnbmd.py
├── misc/
│ ├── README.rst
│ ├── conda/
│ │ ├── README.rst
│ │ ├── install_conda.sh
│ │ └── make_conda_env.sh
│ ├── gpu/
│ │ ├── README.rst
│ │ ├── availgpu.py
│ │ └── envinfo.py
│ └── pytest/
│ ├── README.rst
│ ├── pytest_cov.sh
│ ├── pytest_fast.sh
│ └── pytest_time.sh
├── pyproject.toml
├── pytest.ini
├── requirements.txt
├── scico/
│ ├── __init__.py
│ ├── _core.py
│ ├── _version.py
│ ├── data/
│ │ └── __init__.py
│ ├── denoiser.py
│ ├── diagnostics.py
│ ├── examples.py
│ ├── flax/
│ │ ├── __init__.py
│ │ ├── _flax.py
│ │ ├── _models.py
│ │ ├── blocks.py
│ │ ├── examples/
│ │ │ ├── __init__.py
│ │ │ ├── data_generation.py
│ │ │ ├── data_preprocessing.py
│ │ │ ├── examples.py
│ │ │ └── typed_dict.py
│ │ ├── inverse.py
│ │ └── train/
│ │ ├── __init__.py
│ │ ├── apply.py
│ │ ├── checkpoints.py
│ │ ├── clu_utils.py
│ │ ├── diagnostics.py
│ │ ├── input_pipeline.py
│ │ ├── learning_rate.py
│ │ ├── losses.py
│ │ ├── spectral.py
│ │ ├── state.py
│ │ ├── steps.py
│ │ ├── trainer.py
│ │ ├── traversals.py
│ │ └── typed_dict.py
│ ├── function.py
│ ├── functional/
│ │ ├── __init__.py
│ │ ├── _denoiser.py
│ │ ├── _dist.py
│ │ ├── _functional.py
│ │ ├── _indicator.py
│ │ ├── _norm.py
│ │ ├── _proxavg.py
│ │ └── _tvnorm.py
│ ├── linop/
│ │ ├── __init__.py
│ │ ├── _circconv.py
│ │ ├── _convolve.py
│ │ ├── _dft.py
│ │ ├── _diag.py
│ │ ├── _diff.py
│ │ ├── _func.py
│ │ ├── _grad.py
│ │ ├── _linop.py
│ │ ├── _matrix.py
│ │ ├── _stack.py
│ │ ├── _util.py
│ │ ├── optics.py
│ │ └── xray/
│ │ ├── __init__.py
│ │ ├── _axitom/
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── backprojection.py
│ │ │ ├── config.py
│ │ │ ├── filtering.py
│ │ │ ├── projection.py
│ │ │ └── utilities.py
│ │ ├── _util.py
│ │ ├── _xray.py
│ │ ├── abel.py
│ │ ├── astra.py
│ │ ├── svmbir.py
│ │ └── symcone.py
│ ├── loss.py
│ ├── metric.py
│ ├── numpy/
│ │ ├── __init__.py
│ │ ├── _blockarray.py
│ │ ├── _wrapped_function_lists.py
│ │ ├── _wrappers.py
│ │ ├── fft.py
│ │ ├── linalg.py
│ │ ├── testing.py
│ │ └── util.py
│ ├── operator/
│ │ ├── __init__.py
│ │ ├── _func.py
│ │ ├── _operator.py
│ │ ├── _stack.py
│ │ └── biconvolve.py
│ ├── optimize/
│ │ ├── __init__.py
│ │ ├── _admm.py
│ │ ├── _admmaux.py
│ │ ├── _common.py
│ │ ├── _ladmm.py
│ │ ├── _padmm.py
│ │ ├── _pgm.py
│ │ ├── _pgmaux.py
│ │ ├── _primaldual.py
│ │ ├── admm.py
│ │ └── pgm.py
│ ├── plot.py
│ ├── random.py
│ ├── ray/
│ │ ├── __init__.py
│ │ └── tune.py
│ ├── scipy/
│ │ ├── __init__.py
│ │ └── special.py
│ ├── solver.py
│ ├── test/
│ │ ├── conftest.py
│ │ ├── flax/
│ │ │ ├── test_apply.py
│ │ │ ├── test_checkpoints.py
│ │ │ ├── test_clu.py
│ │ │ ├── test_examples_flax.py
│ │ │ ├── test_flax.py
│ │ │ ├── test_inv.py
│ │ │ ├── test_spectral.py
│ │ │ ├── test_steps.py
│ │ │ ├── test_train_aux.py
│ │ │ ├── test_trainer.py
│ │ │ └── test_traversal.py
│ │ ├── functional/
│ │ │ ├── prox.py
│ │ │ ├── test_composed.py
│ │ │ ├── test_denoiser_func.py
│ │ │ ├── test_funcional_core.py
│ │ │ ├── test_indicator.py
│ │ │ ├── test_loss.py
│ │ │ ├── test_misc.py
│ │ │ ├── test_norm.py
│ │ │ ├── test_proxavg.py
│ │ │ ├── test_separable.py
│ │ │ └── test_tvnorm.py
│ │ ├── linop/
│ │ │ ├── test_binop.py
│ │ │ ├── test_circconv.py
│ │ │ ├── test_conversions.py
│ │ │ ├── test_convolve.py
│ │ │ ├── test_dft.py
│ │ │ ├── test_diag.py
│ │ │ ├── test_diff.py
│ │ │ ├── test_func.py
│ │ │ ├── test_grad.py
│ │ │ ├── test_linop.py
│ │ │ ├── test_linop_stack.py
│ │ │ ├── test_linop_util.py
│ │ │ ├── test_matrix.py
│ │ │ ├── test_optics.py
│ │ │ └── xray/
│ │ │ ├── test_abel.py
│ │ │ ├── test_astra.py
│ │ │ ├── test_svmbir.py
│ │ │ ├── test_symcone.py
│ │ │ ├── test_xray_2d.py
│ │ │ ├── test_xray_3d.py
│ │ │ └── test_xray_util.py
│ │ ├── numpy/
│ │ │ ├── test_blockarray.py
│ │ │ ├── test_numpy.py
│ │ │ └── test_numpy_util.py
│ │ ├── operator/
│ │ │ ├── test_biconvolve.py
│ │ │ ├── test_op_stack.py
│ │ │ └── test_operator.py
│ │ ├── optimize/
│ │ │ ├── test_admm.py
│ │ │ ├── test_ladmm.py
│ │ │ ├── test_padmm.py
│ │ │ ├── test_pdhg.py
│ │ │ └── test_pgm.py
│ │ ├── osver.py
│ │ ├── test_core.py
│ │ ├── test_data.py
│ │ ├── test_denoiser.py
│ │ ├── test_diagnostics.py
│ │ ├── test_examples.py
│ │ ├── test_function.py
│ │ ├── test_metric.py
│ │ ├── test_random.py
│ │ ├── test_ray_tune.py
│ │ ├── test_scipy_special.py
│ │ ├── test_solver.py
│ │ ├── test_util.py
│ │ └── test_version.py
│ ├── trace.py
│ ├── typing.py
│ └── util.py
└── setup.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .coveragerc
================================================
[run]
source = scico
command_line = -m pytest
omit =
scico/test/*
scico/plot.py
scico/trace.py
scico/linop/xray/_axitom/*.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
def __repr__
================================================
FILE: .flake8
================================================
[flake8]
max-line-length = 100
ignore =
#E731: do not assign a lambda expression, use a def
E731
================================================
FILE: .github/codecov.yml
================================================
coverage:
precision: 2
round: nearest
range: "80...100"
status:
project:
default:
target: auto
threshold: 0.05%
patch: false
================================================
FILE: .github/isbin.sh
================================================
#! /bin/bash
# Determine whether files are acceptable for commit into main scico repo
size_threshold=65536
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
OS=$(uname -a | cut -d ' ' -f 1)
for f in $@; do
echo $f
case "$OS" in
Linux) size=$(stat --format "%s" $f);;
Darwin) size=$(stat -f "%z" $f);;
*) echo "Error: unsupported operating system $OS" >&2; exit 1;;
esac
# Exception on maximum size for pytest-split .test_durations file
if [ $size -gt $size_threshold ] && [ "$(basename $f)" != ".test_durations" ]; then
echo "file exceeds maximum allowable size of $size_threshold bytes"
echo "raw data and ipynb files should go in scico-data"
exit 2
fi
charset=$(file -b --mime $f | sed -e 's/.*charset=//')
if [ ! -L "$f" ] && [ "$charset" = "binary" ]; then
echo "binary files cannot be commited to the repository"
echo "raw data and ipynb files should go in scico-data"
exit 3
fi
basename=$(basename -- "$f")
ext="${basename##*.}"
if [ "$ext" = "ipynb" ]; then
echo "ipynb files cannot be commited to the repository"
echo "raw data and ipynb files should go in scico-data"
exit 4
fi
done
IFS=$SAVEIFS
exit 0
================================================
FILE: .github/workflows/check_files.yml
================================================
# Check file types and sizes
name: check files
on: [push, pull_request]
jobs:
checkfiles:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v5
- id: files
uses: Ana06/get-changed-files@v2.3.0
continue-on-error: true
- run: |
for f in ${{ steps.files.outputs.added }}; do
${GITHUB_WORKSPACE}/.github/./isbin.sh $f
done
================================================
FILE: .github/workflows/lint.yml
================================================
# Run isort and black on pushes to main and any pull requests
name: lint
on:
push:
branches:
- main
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Black code formatter
uses: psf/black@stable
with:
version: ">=24.3.0"
- name: Isort import sorter
uses: isort/isort-action@v1
- name: Pylint code analysis
run: |
pip install pylint
pylint --disable=all --enable=missing-docstring,broad-exception-raised scico
================================================
FILE: .github/workflows/mypy.yml
================================================
# Install and run mypy
name: mypy
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Install Python 3
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies
run: |
pip install mypy
- name: Run mypy
run: |
mypy --follow-imports=skip --ignore-missing-imports --exclude "(numpy|test)" scico/ scico/numpy/util.py
================================================
FILE: .github/workflows/pypi_upload.yml
================================================
# When a tag is pushed, build packages and upload to PyPI
name: pypi upload
# Trigger when tags are pushed
on:
push:
tags:
- '*'
workflow_dispatch:
jobs:
build-and-upload:
name: Upload package to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Install Python 3
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
sudo apt-get install -y libopenblas-dev
pip install -r requirements.txt
pip install -r dev_requirements.txt
pip install wheel
python setup.py sdist bdist_wheel
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
verbose: true
================================================
FILE: .github/workflows/pytest_latest.yml
================================================
# Install scico requirements and run pytest with latest jax version
name: unit tests (latest jax)
# Controls when the workflow will run
on:
# Run workflow every Sunday at midnight UTC
schedule:
- cron: "0 0 * * 0"
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
pytest-latest-jax:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Install Python 3
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install lastversion
run: |
python -m pip install --upgrade pip
pip install lastversion
- name: Install dependencies
run: |
rjaxlib=$(grep jaxlib requirements.txt | sed -e 's/jaxlib.*<=\([0-9\.]*$\)/\1/')
rjax=$(grep -E "jax[^lib]" requirements.txt | sed -e 's/jax.*<=\([0-9\.]*$\)/\1/')
ljaxlib=$(lastversion --at pip jaxlib)
ljax=$(lastversion --at pip jax)
echo jaxlib required: $rjaxlib latest: $ljaxlib
echo jax required: $rjax latest: $ljax
if [ "$rjaxlib" = "$ljaxlib" ] && [ "$rjax" = "$ljax" ]; then
echo Test is redundant: required and latest jaxlib/jax versions match
echo "TEST=cancel" >> $GITHUB_ENV
else
echo "TEST=run" >> $GITHUB_ENV
sudo apt-get install -y libopenblas-dev
pip install -r requirements.txt
pip install -r dev_requirements.txt
pip install -e .
pip install --upgrade "jax[cpu]"
fi
- name: Run tests with pytest
run: |
TEST="${{ env.TEST }}"
if [ "$TEST" = "run" ]; then
pytest
else
exit 0
fi
================================================
FILE: .github/workflows/pytest_macos.yml
================================================
# Install scico requirements and run pytest
name: unit tests (macos)
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
test:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
group: [1, 2, 3, 4, 5]
name: pytest split ${{ matrix.group }} (macos)
defaults:
run:
shell: bash -l {0}
steps:
# Check-out the repository under $GITHUB_WORKSPACE
- uses: actions/checkout@v5
with:
submodules: recursive
# Set up conda environment
- name: Set up miniconda
uses: conda-incubator/setup-miniconda@v3
with:
miniforge-version: latest
activate-environment: test-env
python-version: "3.12"
# Configure conda environment cache
- name: Set up conda environment cache
uses: actions/cache@v4
with:
path: ${{ env.CONDA }}/envs
key: conda-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('dev_requirements.txt') }}-${{ env.CACHE_NUMBER }}
env:
CACHE_NUMBER: 0 # Increase this value to force cache reset
id: cache
# Display environment details
- name: Display environment details
run: |
conda info
printenv | sort
# Install dependencies in conda environment
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
conda install -c conda-forge pytest pytest-cov
python -m pip install --upgrade pip
pip install pytest-split
pip install -r requirements.txt
pip install -r dev_requirements.txt
pip install "bm3d>=4.0.0"
pip install "bm4d>=4.0.0"
pip install "ray[tune]>=2.44"
pip install hyperopt
pip install "setuptools<82.0.0" # workaround for hyperopt 0.2.7
pip install pydantic
pip install "orbax-checkpoint>=0.5.0"
#conda install -c conda-forge "svmbir>=0.4.0"
conda install -c astra-toolbox astra-toolbox
conda install -c conda-forge pyyaml
# Install package to be tested
- name: Install package to be tested
run: pip install -e .
# Run unit tests
- name: Run main unit tests
run: |
DURATIONS_FILE=$(mktemp)
bzcat data/pytest/durations_macos.bz2 > $DURATIONS_FILE
pytest -x --level=1 --durations-path=$DURATIONS_FILE --splits=5 --group=${{ matrix.group }} --pyargs scico
================================================
FILE: .github/workflows/pytest_ubuntu.yml
================================================
# Install scico requirements and run pytest
name: unit tests (ubuntu)
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
group: [1, 2, 3, 4, 5]
name: pytest split ${{ matrix.group }} (ubuntu)
defaults:
run:
shell: bash -l {0}
steps:
# Check-out the repository under $GITHUB_WORKSPACE
- uses: actions/checkout@v5
with:
submodules: recursive
# Set up conda environment
- name: Set up miniconda
uses: conda-incubator/setup-miniconda@v3
with:
miniforge-version: latest
activate-environment: test-env
python-version: "3.12"
# Configure conda environment cache
- name: Set up conda environment cache
uses: actions/cache@v4
with:
path: ${{ env.CONDA }}/envs
key: conda-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('dev_requirements.txt') }}-${{ env.CACHE_NUMBER }}
env:
CACHE_NUMBER: 0 # Increase this value to force cache reset
id: cache
# Display environment details
- name: Display environment details
run: |
conda info
printenv | sort
# Install required system package
- name: Install required system package
run: sudo apt-get install -y libopenblas-dev
# Install dependencies in conda environment
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
conda install -c conda-forge pytest pytest-cov
python -m pip install --upgrade pip
pip install pytest-split
pip install -r requirements.txt
pip install -r dev_requirements.txt
pip install "bm4d>=4.2.2"
pip install "bm3d>=4.0.0"
pip install "ray[tune]>=2.44"
pip install hyperopt
pip install "setuptools<82.0.0" # workaround for hyperopt 0.2.7
pip install pydantic
pip install "orbax-checkpoint>=0.5.0"
conda install -c conda-forge "svmbir>=0.4.0"
conda install -c conda-forge astra-toolbox
conda install -c conda-forge pyyaml
# Install package to be tested
- name: Install package to be tested
run: pip install -e .
# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
# Run unit tests
- name: Run main unit tests
run: |
DURATIONS_FILE=$(mktemp)
bzcat data/pytest/durations_ubuntu.bz2 > $DURATIONS_FILE
pytest -x --cov --level=2 --durations-path=$DURATIONS_FILE --splits=5 --group=${{ matrix.group }} --pyargs scico
# Upload coverage data
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
include-hidden-files: true
name: coverage${{ matrix.group }}
path: ${{ github.workspace }}/.coverage
# Run doc tests
- name: Run doc tests
if: matrix.group == 1
run: |
pytest --ignore-glob="*test_*.py" --ignore=scico/linop/xray --doctest-modules scico
pytest --doctest-glob="*.rst" docs
coverage:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install deps
run: |
python -m pip install --upgrade pip
pip install coverage
- name: Download all artifacts
# Downloads coverage1, coverage2, etc.
uses: actions/download-artifact@v4
- name: Run coverage
run: |
coverage combine coverage?/.coverage
coverage report
coverage xml
- uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
env_vars: OS,PYTHON
fail_ci_if_error: false
files: coverage.xml
flags: unittests
name: codecov-umbrella
verbose: true
================================================
FILE: .github/workflows/test_examples.yml
================================================
# Install scico requirements and run short versions of example scripts
name: test examples
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allow this workflow to be run manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
name: test examples (ubuntu)
defaults:
run:
shell: bash -l {0}
steps:
# Check-out the repository under $GITHUB_WORKSPACE
- uses: actions/checkout@v5
with:
submodules: recursive
# Set up conda environment
- name: Set up miniconda
uses: conda-incubator/setup-miniconda@v3
with:
miniforge-version: latest
activate-environment: test-env
python-version: "3.12"
# Configure conda environment cache
- name: Set up conda environment cache
uses: actions/cache@v4
with:
path: ${{ env.CONDA }}/envs
key: conda-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('dev_requirements.txt') }}-${{ hashFiles('examples/examples_requirements.txt') }}-${{ env.CACHE_NUMBER }}
env:
CACHE_NUMBER: 0 # Increase this value to force cache reset
id: cache
# Display environment details
- name: Display environment details
run: |
conda info
printenv | sort
# Install required system package
- name: Install required system package
run: sudo apt-get install -y libopenblas-dev
# Install dependencies in conda environment
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
conda install -c conda-forge pytest pytest-cov
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r dev_requirements.txt
conda install -c conda-forge astra-toolbox
conda install -c conda-forge pyyaml
pip install --upgrade --force-reinstall scipy>=1.6.0 # Temporary fix for GLIBCXX_3.4.30 not found in conda forge version
pip install -r examples/examples_requirements.txt
# Install package to be tested
- name: Install package to be tested
run: pip install -e .
# Run example test
- name: Run example test
run: |
${GITHUB_WORKSPACE}/examples/scriptcheck.sh -e -d -t -g
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Editor backups
.*~
# Docs generation
docs/source/_autosummary/
docs/source/examples/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# VS Code settings
.vscode/
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# macos files
*.DS_Store
================================================
FILE: .gitmodules
================================================
[submodule "data"]
path = data
url = https://github.com/lanl/scico-data.git
================================================
FILE: .pre-commit-config.yaml
================================================
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: local
hooks:
- id: check-for-binary
name: check for binary/ipynb files
entry: .github/isbin.sh
language: script
pass_filenames: true
- id: autoflake
name: autoflake
entry: autoflake
language: python
language_version: python3
types: [python]
args: ['-i', '--remove-all-unused-imports', '--ignore-init-module-imports']
- id: isort
name: isort (python)
entry: isort
language: python
language_version: python3
types: [python]
- id: isort
name: isort (cython)
entry: isort
language: python
language_version: python3
types: [cython]
- id: black
name: black
entry: black
description: 'Black: The uncompromising Python code formatter'
language: python
language_version: python3
types: [python]
- id: pylint
name: pylint
entry: pylint
language: python
language_version: python3
types: [python]
exclude: ^(scico/test/|examples|docs)
args: ['--score=n', '--disable=all', '--enable=missing-docstring,broad-exception-raised']
================================================
FILE: .readthedocs.yaml
================================================
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Get submodules
submodules:
include: all
recursive: true
# Set the version of Python and other tools you might need
build:
os: ubuntu-24.04
tools:
python: "3.12"
jobs:
pre_build:
- mkdir -p docs/source/examples
- |
for f in data/notebooks/*; do
b=$(basename $f)
if [ ! -f "docs/source/examples/$b" ]; then
ln -s -t docs/source/examples "../../../$f"
fi
done
post_build: # unclear why this is necessary
- cp docs/source/_static/scico.css _readthedocs/html/_static
apt_packages:
- graphviz
- libopenblas-dev
# Build documentation in the docs/ directory with Sphinx
sphinx:
builder: html
configuration: docs/source/conf.py
fail_on_warning: false
# Declare the Python requirements required to build your docs
python:
install:
- requirements: docs/docs_requirements.txt
- requirements: docs/rtd_requirements.txt
================================================
FILE: CHANGES.rst
================================================
===================
SCICO Release Notes
===================
Version 0.0.8 (unreleased)
----------------------------
• Enable certain parameters of array creation functions to trigger
``BlockArray`` creation when they receive lists (currently ``device``).
• New functional ``functional.BoxIndicator``.
• Support ``jaxlib`` and ``jax`` versions 0.5.0 to 0.10.0.
• Support ``flax`` versions 0.8.0 to 0.12.7.
• Various bug fixes and minor improvements.
Version 0.0.7 (2025-12-09)
----------------------------
• New module ``scico.trace`` for tracing function/method calls.
• New generic functional ``functional.ComposedFunctional`` representing
a functional composed with an orthogonal linear operator.
• New optimizer methods ``save_state`` and ``load_state`` supporting
algorithm state checkpointing.
• New classes for creating a volume from an image by symmetry, and
for cone beam X-ray transform of a cylindrically symmetric object
in module ``linop.xray.symcone``.
• New utility functions for CT reconstruction preprocessing added in
module ``linop.xray``.
• Moved ``linop.abel`` module to ``linop.xray.abel``.
• Make ``orbax-checkpoint`` dependency optional due to absence of recent
conda-forge packages.
• Support ``jaxlib`` and ``jax`` versions 0.5.0 to 0.8.1.
• Support ``flax`` versions 0.8.0 to 0.12.0.
Version 0.0.6 (2024-10-25)
----------------------------
• Significant changes to ``linop.xray.astra`` API.
• Rename integrated 2D X-ray transform class to
``linop.xray.XRayTransform2D`` and add filtered back projection method
``fbp``.
• New integrated 3D X-ray transform via ``linop.xray.XRayTransform3D``.
• New functional ``functional.IsotropicTVNorm`` and faster implementation
of ``functional.AnisotropicTVNorm``.
• New linear operators ``linop.ProjectedGradient``, ``linop.PolarGradient``,
``linop.CylindricalGradient``, and ``linop.SphericalGradient``.
• Rename ``scico.numpy.util.parse_axes`` to
``scico.numpy.util.normalize_axes``.
• Rename ``scico.flax.save_weights`` and ``scico.flax.load_weights`` to
``scico.flax.save_variables`` and ``scico.flax.load_variables``
respectively.
• Support ``jaxlib`` and ``jax`` versions 0.4.13 to 0.4.35.
• Support ``flax`` versions 0.8.0 to 0.10.0.
Version 0.0.5 (2023-12-18)
----------------------------
• New functionals ``functional.AnisotropicTVNorm`` and
``functional.ProximalAverage`` with proximal operator approximations.
• New integrated Radon/X-ray transform ``linop.XRayTransform``.
• New operators ``operator.DiagonalStack`` and ``operator.VerticalStack``.
• Rename modules ``radon_astra`` and ``radon_svmbir`` to ``xray.astra`` and
``xray.svmbir`` respectively, and rename ``TomographicProjector`` classes
to ``XRayTransform``.
• Rename ``AbelProjector`` to ``AbelTransform``.
• Rename ``solver.ATADSolver`` to ``solver.MatrixATADSolver``.
• Rename some ``__init__`` parameters of ``linop.DiagonalStack`` and
``linop.VerticalStack``.
• Support ``jaxlib`` and ``jax`` versions 0.4.3 to 0.4.23.
• Support ``flax`` versions up to 0.7.5.
• Use ``orbax`` for checkpointing ``flax`` models.
Version 0.0.4 (2023-08-03)
----------------------------
• Add new ``Function`` class for representing array-to-array mappings with
more than one input.
• Add new methods and a function for computing Jacobian-vector products for
``Operator`` objects.
• Add new proximal ADMM solvers.
• Add new ADMM subproblem solvers for problems involving a sum-of-convolutions
operator.
• Extend support for other ML models including UNet, ODP and MoDL.
• Add functionality for training Flax-based ML models and for data generation.
• Enable diagnostics for ML training loops.
• Support ``jaxlib`` and ``jax`` versions 0.4.3 to 0.4.14.
• Change required packages and version numbers, including more recent version
for ``flax``.
• Drop support for Python 3.7.
• Add support for 3D tomographic projection with the ASTRA Toolbox.
Version 0.0.3 (2022-09-21)
----------------------------
• Change required packages and version numbers, including more recent version
requirements for ``numpy``, ``scipy``, ``svmbir``, and ``ray``.
• Package ``bm4d`` removed from main requirements list due to issue #342.
• Support ``jaxlib`` versions 0.3.0 to 0.3.15 and ``jax`` versions
0.3.0 to 0.3.17.
• Rename linear operators in ``radon_astra`` and ``radon_svmbir`` modules
to ``TomographicProjector``.
• Add support for fan beam CT in ``radon_svmbir`` module.
• Add function ``linop.linop_from_function`` for constructing linear
operators from functions.
• Enable addition operator for functionals.
• Completely new implementation of ``BlockArray`` class.
• Additional solvers in ``scico.solver``.
• New Huber norm (``HuberNorm``) and set distance functionals (``SetDistance``
and ``SquaredSetDistance``).
• New loss functions ``loss.SquaredL2AbsLoss`` and
``loss.SquaredL2SquaredAbsLoss`` for phase retrieval problems.
• Add interface to BM4D denoiser.
• Change interfaces of ``linop.FiniteDifference`` and ``linop.DFT``.
• Change filenames of some example scripts (and corresponding notebooks).
• Add support for Python 3.7.
• New ``DiagonalStack`` linear operator.
• Add support for non-linear operators to ``optimize.PDHG`` optimizer class.
• Various bug fixes.
Version 0.0.2 (2022-02-14)
----------------------------
• Additional optimization algorithms: Linearized ADMM and PDHG.
• Additional Abel transform and array slicing linear operators.
• Additional nuclear norm functional.
• New module ``scico.ray.tune`` providing a simplified interface to Ray Tune.
• Move optimization algorithms into ``optimize`` subpackage.
• Additional iteration stats columns for iterative ADMM subproblem solvers.
• Renamed "Primal Rsdl" to "Prml Rsdl" in displayed iteration stats.
• Move some functions from ``util`` and ``math`` modules to new ``array``
module.
• Bump pinned ``jaxlib`` and ``jax`` versions to 0.3.0.
Version 0.0.1 (2021-11-24)
----------------------------
• Initial release.
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2021-2025, Los Alamos National Laboratory
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: MANIFEST.in
================================================
include MANIFEST.in
include README.md
include CHANGES.rst
include LICENSE
include setup.py
include conftest.py
include pyproject.toml
include pytest.ini
include requirements.txt
include dev_requirements.txt
include docs/docs_requirements.txt
recursive-include scico *.py
recursive-include scico/data *.png *.mpk *.rst
recursive-include docs Makefile *.py *.ipynb *.rst *.bib *.css *.svg *.png *.ico
recursive-include examples *_requirements.txt *.txt *.rst *.py *.sh
recursive-include misc *.py *.sh *.rst
================================================
FILE: README.md
================================================
[](https://www.python.org/)
[](https://github.com/lanl/scico/blob/main/LICENSE)
[](https://github.com/psf/black)
[](http://scico.readthedocs.io/en/latest/?badge=latest)
[](https://doi.org/10.21105/joss.04722)\
[](https://github.com/lanl/scico/actions/workflows/lint.yml)
[](https://github.com/lanl/scico/actions/workflows/pytest_ubuntu.yml)
[](https://codecov.io/gh/lanl/scico)
[](https://www.codefactor.io/repository/github/lanl/scico/overview/main)\
[](https://badge.fury.io/py/scico)
[](https://pepy.tech/project/scico)
[](https://anaconda.org/conda-forge/scico)
[](https://anaconda.org/conda-forge/scico)\
[](https://nbviewer.jupyter.org/github/lanl/scico-data/tree/main/notebooks/index.ipynb)
[](https://mybinder.org/v2/gh/lanl/scico-data/binder?labpath=notebooks%2Findex.ipynb)
[](https://colab.research.google.com/github/lanl/scico-data/blob/colab/notebooks/index.ipynb)
# Scientific Computational Imaging Code (SCICO)
SCICO is a Python package for solving the inverse problems that arise in
scientific imaging applications. Its primary focus is providing methods
for solving ill-posed inverse problems by using an appropriate prior
model of the reconstruction space. SCICO includes a growing suite of
operators, cost functionals, regularizers, and optimization routines
that may be combined to solve a wide range of problems, and is designed
so that it is easy to add new building blocks. SCICO is built on top of
[JAX](https://github.com/google/jax), which provides features such as
automatic gradient calculation and GPU acceleration.
[Documentation](https://scico.rtfd.io/) is available online. If you use
this software for published work, please cite the corresponding [JOSS
Paper](https://doi.org/10.21105/joss.04722) (see bibtex entry
`balke-2022-scico` in `docs/source/references.bib`).
# Installation
The online documentation includes detailed
[installation instructions](https://scico.rtfd.io/en/latest/install.html).
# Usage Examples
Usage examples are available as Python scripts and Jupyter Notebooks.
Example scripts are located in `examples/scripts`. The corresponding
Jupyter Notebooks are provided in the
[scico-data](https://github.com/lanl/scico-data) submodule and symlinked
to `examples/notebooks`. They are also viewable on
[GitHub](https://github.com/lanl/scico-data/tree/main/notebooks) or
[nbviewer](https://nbviewer.jupyter.org/github/lanl/scico-data/tree/main/notebooks/index.ipynb),
and can be run online on
[binder](https://mybinder.org/v2/gh/lanl/scico-data/binder?labpath=notebooks%2Findex.ipynb)
or
[google colab](https://colab.research.google.com/github/lanl/scico-data/blob/colab/notebooks/index.ipynb).
# License
SCICO is distributed as open-source software under a BSD 3-Clause
License (see the `LICENSE` file for details).
LANL open source approval reference C20091.
\(c\) 2020-2026. Triad National Security, LLC. All rights reserved. This
program was produced under U.S. Government contract 89233218CNA000001
for Los Alamos National Laboratory (LANL), which is operated by Triad
National Security, LLC for the U.S. Department of Energy/National
Nuclear Security Administration. All rights in the program are reserved
by Triad National Security, LLC, and the U.S. Department of
Energy/National Nuclear Security Administration. The Government has
granted for itself and others acting on its behalf a nonexclusive,
paid-up, irrevocable worldwide license in this material to reproduce,
prepare derivative works, distribute copies to the public, perform
publicly and display publicly, and to permit others to do so.
================================================
FILE: conftest.py
================================================
"""
Configure pytest.
"""
import os
import numpy as np
import pytest
os.environ["RAY_ACCEL_ENV_VAR_OVERRIDE_ON_ZERO"] = "0" # suppress ray warning
try:
import ray # noqa: F401
except ImportError:
have_ray = False
else:
have_ray = True
ray.init(num_cpus=1) # call required to be here: see ray-project/ray#44087
import jax.numpy as jnp
import scico.numpy as snp
def pytest_sessionstart(session):
"""Initialize before start of test session."""
# placeholder: currently unused
def pytest_sessionfinish(session, exitstatus):
"""Clean up after end of test session."""
if have_ray:
ray.shutdown()
@pytest.fixture(autouse=True)
def add_modules(doctest_namespace):
"""Add common modules for use in docstring examples.
Necessary because `np` is used in doc strings for jax functions
(e.g. `linear_transpose`) that get pulled into `scico/__init__.py`.
Also allow `snp` and `jnp` to be used without explicitly importing.
"""
doctest_namespace["np"] = np
doctest_namespace["snp"] = snp
doctest_namespace["jnp"] = jnp
================================================
FILE: dev_requirements.txt
================================================
-r requirements.txt
pylint
pytest>=7.3.0
pytest-split
packaging
pre-commit
black>=24.3.0
isort
autoflake
================================================
FILE: docs/Makefile
================================================
# Makefile for Sphinx documentation
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = ../build/sphinx
.PHONY: help clean Makefile
# Put this first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
clean:
rm -rf $(BUILDDIR)/*
rm -f $(SOURCEDIR)/_autosummary/*
rm -f $(SOURCEDIR)/examples/*
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@mkdir -p source/examples; \
for f in ../data/notebooks/*; do \
b=$$(basename $$f) ; \
if [ ! -f "source/examples/$$b" ]; then \
echo Creating soft link for notebook $$b ; \
ln -s -t source/examples "../../$$f" ; \
fi \
done
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/docs_requirements.txt
================================================
-r ../requirements.txt
sphinx>=5.0.0
sphinxcontrib-napoleon
sphinxcontrib-bibtex
sphinx-autodoc-typehints
furo>=2024.5.6
jinja2<3.1.0 # temporary fix for jinja2/nbconvert bug
traitlets!=5.2.2 # temporary fix for ipython/traitlets#741
nbsphinx
ipython
ipython_genutils
py2jn
pygraphviz>=1.9
pandoc
docutils>=0.18
================================================
FILE: docs/rtd_requirements.txt
================================================
# nbconvert>=7.5 requires a version of pandoc that is not available
# in the readthedocs build environment
nbconvert<7.5
================================================
FILE: docs/source/_static/scico.css
================================================
/* furo theme customization */
body[data-theme="dark"] figure img {
filter: invert(100%);
}
.sidebar-drawer {
width: fit-content !important;
}
.main > .content {
min-width: 75%;
width: fit-content !important;
max-width: 80em;
}
.highlight {
background: #e9efff;
}
.sidebar-brand-text {
font-size: 1.0rem !important;
text-align: center;
padding-top: 0.5em;
}
/* Code display section */
div.doctest.highlight-default {
background-color: #f9f9f4;
}
/* Style for autosummary API docs */
[data-theme=light] dl.field-list.simple {
background-color: #f5f5f5;
border-radius: 4px;
}
[data-theme=light] dl.field-list.simple > dt.field-odd {
background-color: #f2f2f2;
border-radius: 4px;
}
[data-theme=light] dl.field-list.simple > dt.field-even {
background-color: #f2f2f2;
border-radius: 4px;
}
[data-theme=light] dl.py.data {
background-color: #fdfafa;
border-radius: 4px;
}
[data-theme=light] dl.py.data > dt {
border-radius: 4px;
}
[data-theme=light] dl.py.attribute {
background-color: #fdfafa;
border-radius: 4px;
}
[data-theme=light] dl.py.attribute > dt {
border-radius: 4px;
}
[data-theme=light] dl.py.function {
background-color: #fdfafa;
border-radius: 4px;
}
[data-theme=light] dl.py.function > dt {
border-radius: 4px;
}
[data-theme=light] dl.py.function blockquote {
background-color: #f5f5f5;
border-left: 0px;
}
[data-theme=light] dl.py.class {
background-color: #fdfafa;
border-radius: 4px;
}
[data-theme=light] dl.py.class > dt {
border-radius: 4px;
}
[data-theme=light] dl.py.method {
background-color: #f6f6f6;
border-radius: 4px;
}
[data-theme=light] dl.py.method > dt {
border-radius: 4px;
}
[data-theme=light] dl.py.property {
background-color: #f6f6f6;
border-radius: 4px;
}
[data-theme=light] dl.py.property > dt {
border-radius: 4px;
}
/* Style for figure captions */
div.figure p.caption span.caption-text,
figcaption span.caption-text {
font-size: var(--font-size--small);
margin-left: 5%;
margin-right: 5%;
display: inline-block;
text-align: justify;
}
================================================
FILE: docs/source/_templates/autosummary/module.rst
================================================
{{ fullname | escape | underline}}
.. automodule:: {{ fullname }}
{% block attributes %}
{% if attributes %}
.. rubric:: {{ _('Module Attributes') }}
.. autosummary::
{% for item in attributes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block modules %}
{% if modules %}
.. rubric:: Modules
.. autosummary::
:toctree:
:recursive:
{% for item in modules %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block functions %}
{% if functions %}
.. rubric:: {{ _('Functions') }}
.. autosummary::
{% for item in functions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block classes %}
{% if classes %}
.. rubric:: {{ _('Classes') }}
.. autosummary::
{% for item in classes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block exceptions %}
{% if exceptions %}
.. rubric:: {{ _('Exceptions') }}
.. autosummary::
{% for item in exceptions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
================================================
FILE: docs/source/_templates/package.rst
================================================
API Reference
=============
.. automodule:: {{ fullname }}
{% block modules %}
{% if modules %}
.. autosummary::
:toctree:
:recursive:
{% for item in modules %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
================================================
FILE: docs/source/_templates/sidebar/brand.html
================================================
{#-
Hi there!
You might be interested in https://pradyunsg.me/furo/customisation/sidebar/
Although if you're reading this, chances are that you're either familiar
enough with Sphinx that you know what you're doing, or landed here from that
documentation page.
Hope your day's going well. :)
-#}
{% block brand_content %}
{%- if logo_url %}
{%- endif %}
{%- if theme_light_logo and theme_dark_logo %}
{%- endif %}
{% if not theme_sidebar_hide_name %}
{{ version }}
{%- endif %}
{% endblock brand_content %}
================================================
FILE: docs/source/advantages.rst
================================================
Why SCICO?
==========
Advantages of JAX-based Design
------------------------------
The vast majority of scientific computing packages in Python are based
on `NumPy `__ and `SciPy `__.
SCICO, in contrast, is based on `JAX
`__, which provides most of the
same features, but with the addition of automatic differentiation, GPU
support, and just-in-time (JIT) compilation. (The availability of
these features in SCICO is subject to some :ref:`caveats
`.) SCICO users and developers are advised to become
familiar with the `differences between JAX and
NumPy. `_.
While recent advances in automatic differentiation have primarily been
driven by its important role in deep learning, it is also invaluable in
a functional minimization framework such as SCICO. The most obvious
advantage is allowing the use of gradient-based minimization methods
without the need for tedious mathematical derivation of an expression
for the gradient. Equally valuable, though, is the ability to
automatically compute the adjoint operator of a linear operator, the
manual derivation of which is often time-consuming.
GPU support and JIT compilation both offer the potential for significant
code acceleration, with the speed gains that can be obtained depending
on the algorithm/function to be executed. In many cases, a speed
improvement by an order of magnitude or more can be obtained by running
the same code on a GPU rather than a CPU, and similar speed gains can
sometimes also be obtained via JIT compilation.
The figure below shows timing results obtained on a compute server
with an Intel Xeon Gold 6230 CPU and NVIDIA GeForce RTX 2080 Ti
GPU. It is interesting to note that for :class:`.FiniteDifference` the
GPU provides no acceleration, while JIT provides more than an order of
magnitude of speed improvement on both CPU and GPU. For :class:`.DFT`
and :class:`.Convolve`, significant JIT acceleration is limited to the
GPU, which also provides significant acceleration over the CPU.
.. image:: /figures/jax-timing.png
:align: center
:width: 95%
:alt: Timing results for SCICO operators on CPU and GPU with and without JIT
Related Packages
----------------
Many elements of SCICO are partially available in other packages. We
briefly review them here, highlighting some of the main differences with
SCICO.
`GlobalBioIm `__
is similar in structure to SCICO (and a major inspiration for SCICO),
providing linear operators and solvers for inverse problems in imaging.
However, it is written in MATLAB and is thus not usable in a completely
free environment. It also lacks the automatic adjoint calculation and
simple GPU support offered by SCICO.
`PyLops `__ provides a linear operator
class and many built-in linear operators. These operators are compatible
with many `SciPy `__ solvers. GPU support is
provided via `CuPy `__, which has the disadvantage
that switching for a CPU to GPU requires code changes, unlike SCICO and
`JAX `__. SCICO is more focused
on computational imaging that PyLops and has several specialized
operators that PyLops does not.
`Pycsou `__, like
SCICO, is a Python project inspired by GlobalBioIm. Since it is based on
PyLops, it shares the disadvantages with respect to SCICO of that
project.
`ODL `__ provides a variety of
operators and related infrastructure for prototyping of inverse
problems. It is built on top of
`NumPy `__/`SciPy `__, and does
not support any of the advanced features of
`JAX `__.
`ProxImaL `__ is a Python
package for image optimization problems. Like SCICO and many of the
other projects listed here, problems are specified by combining objects
representing, operators, functionals, and solvers. It does not support
any of the advanced features of
`JAX `__.
`ProxMin `__ provides a set of
proximal optimization algorithms for minimizing non-smooth functionals.
It is built on top of
`NumPy `__/`SciPy `__, and does
not support any of the advanced features of
`JAX `__ (however, an open issue
suggests that `JAX `__
compatibility is planned).
`CVXPY `__ provides a flexible language for
defining optimization problems and a wide selection of solvers, but has
limited support for matrix-free methods.
Other related projects that may be of interest include:
- `ToMoBAR `__
- `CCPi-Regularisation Toolkit `__
- `SPORCO `__
- `SigPy `__
- `MIRT `__
- `BART `__
================================================
FILE: docs/source/api.rst
================================================
:orphan:
API Documentation
=================
.. autosummary::
:toctree: _autosummary
:template: package.rst
:caption: API Reference
:recursive:
scico
================================================
FILE: docs/source/classes.rst
================================================
.. _classes:
******************
Main SCICO Classes
******************
.. include:: include/blockarray.rst
.. include:: include/operator.rst
.. include:: include/functional.rst
.. include:: include/optimizer.rst
.. include:: include/learning.rst
================================================
FILE: docs/source/conf/10-project.py
================================================
from scico._version import package_version
# General information about the project.
project = "SCICO"
copyright = "2020-2026, SCICO Developers"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = package_version()
# The full version, including alpha/beta/rc tags.
release = version
================================================
FILE: docs/source/conf/15-theme.py
================================================
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
# html_theme = "python_docs_theme"
html_theme = "furo"
html_theme_options = {
"top_of_page_buttons": [],
# "sidebar_hide_name": True,
}
if html_theme == "python_docs_theme":
html_sidebars = {
"**": ["globaltoc.html", "sourcelink.html", "searchbox.html"],
}
# These folders are copied to the documentation's HTML output
html_static_path = ["_static"]
# These paths are either relative to html_static_path or fully qualified
# paths (eg. https://...)
html_css_files = [
"scico.css",
"http://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css",
]
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = "_static/logo.svg"
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = "_static/scico.ico"
================================================
FILE: docs/source/conf/20-extensions.py
================================================
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.napoleon",
"sphinx.ext.autodoc",
"sphinx_autodoc_typehints",
"sphinx.ext.autosummary",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
"sphinxcontrib.bibtex",
"sphinx.ext.inheritance_diagram",
"matplotlib.sphinxext.plot_directive",
"sphinx.ext.todo",
"nbsphinx",
]
bibtex_bibfiles = ["references.bib"]
================================================
FILE: docs/source/conf/25-napoleon.py
================================================
from sphinx.ext.napoleon.docstring import GoogleDocstring
## See
## https://github.com/sphinx-doc/sphinx/issues/2115
## https://michaelgoerz.net/notes/extending-sphinx-napoleon-docstring-sections.html
##
# first, we define new methods for any new sections and add them to the class
def parse_keys_section(self, section):
return self._format_fields("Keys", self._consume_fields())
GoogleDocstring._parse_keys_section = parse_keys_section
def parse_attributes_section(self, section):
return self._format_fields("Attributes", self._consume_fields())
GoogleDocstring._parse_attributes_section = parse_attributes_section
def parse_class_attributes_section(self, section):
return self._format_fields("Class Attributes", self._consume_fields())
GoogleDocstring._parse_class_attributes_section = parse_class_attributes_section
# we now patch the parse method to guarantee that the the above methods are
# assigned to the _section dict
def patched_parse(self):
self._sections["keys"] = self._parse_keys_section
self._sections["class attributes"] = self._parse_class_attributes_section
self._unpatched_parse()
GoogleDocstring._unpatched_parse = GoogleDocstring._parse
GoogleDocstring._parse = patched_parse
# napoleon_include_init_with_doc = True
napoleon_use_ivar = True
napoleon_use_rtype = False
# See https://github.com/sphinx-doc/sphinx/issues/9119
# napoleon_custom_sections = [("Returns", "params_style")]
================================================
FILE: docs/source/conf/30-autodoc.py
================================================
autodoc_default_options = {
"member-order": "bysource",
"inherited-members": False,
"ignore-module-all": False,
"show-inheritance": True,
"members": True,
"special-members": "__call__",
}
autodoc_docstring_signature = True
autoclass_content = "both"
# See https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_mock_imports
autodoc_mock_imports = ["astra", "svmbir", "ray"]
# See
# https://stackoverflow.com/questions/2701998#62613202
# https://github.com/JamesALeedham/Sphinx-Autosummary-Recursion
autosummary_generate = True
# See https://stackoverflow.com/questions/5599254
autoclass_content = "both"
================================================
FILE: docs/source/conf/40-intersphinx.py
================================================
# Intersphinx mapping
intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
"matplotlib": ("https://matplotlib.org/stable/", None),
"jax": ("https://docs.jax.dev/en/latest/", None),
"flax": ("https://flax.readthedocs.io/en/latest/", None),
"ray": ("https://docs.ray.io/en/latest/", None),
"svmbir": ("https://svmbir.readthedocs.io/en/latest/", None),
}
# Added timeout due to periodic scipy.org down time
# intersphinx_timeout = 30
================================================
FILE: docs/source/conf/45-mathjax.py
================================================
import os
if os.environ.get("NO_MATHJAX"):
extensions.append("sphinx.ext.imgmath")
imgmath_image_format = "svg"
else:
extensions.append("sphinx.ext.mathjax")
# To use local copy of MathJax for offline use, set MATHJAX_URI to
# file:///[path-to-mathjax-repo-root]/es5/tex-mml-chtml.js
if os.environ.get("MATHJAX_URI"):
mathjax_path = os.environ.get("MATHJAX_URI")
mathjax3_config = {
"tex": {
"macros": {
"mb": [r"\mathbf{#1}", 1],
"mbs": [r"\boldsymbol{#1}", 1],
"mbb": [r"\mathbb{#1}", 1],
"norm": [r"\lVert #1 \rVert", 1],
"abs": [r"\left| #1 \right|", 1],
"argmin": [r"\mathop{\mathrm{argmin}}"],
"sign": [r"\mathop{\mathrm{sign}}"],
"prox": [r"\mathrm{prox}"],
"det": [r"\mathrm{det}"],
"exp": [r"\mathrm{exp}"],
"loss": [r"\mathop{\mathrm{loss}}"],
"kp": [r"k_{\|}"],
"rp": [r"r_{\|}"],
}
}
}
================================================
FILE: docs/source/conf/50-graphviz.py
================================================
graphviz_output_format = "svg"
inheritance_graph_attrs = dict(rankdir="LR", fontsize=9, ratio="compress", bgcolor="transparent")
inheritance_edge_attrs = dict(
color='"#2962ffff"',
)
inheritance_node_attrs = dict(
shape="box",
fontsize=9,
height=0.4,
margin='"0.08, 0.03"',
style='"rounded,filled"',
color='"#2962ffff"',
fontcolor='"#2962ffff"',
fillcolor='"#f0f0f8b0"',
)
================================================
FILE: docs/source/conf/55-nbsphinx.py
================================================
nbsphinx_prolog = """
.. raw:: html
"""
nbsphinx_execute = "never"
================================================
FILE: docs/source/conf/60-rtd.py
================================================
import os
on_rtd = os.environ.get("READTHEDOCS") == "True"
if on_rtd:
print("Building on ReadTheDocs\n")
print(" current working directory: {}".format(os.path.abspath(os.curdir)))
print(" rootpath: %s" % rootpath)
print(" confpath: %s" % confpath)
html_static_path = []
# See https://about.readthedocs.com/blog/2024/07/addons-by-default/#how-to-opt-in-to-addons-now
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
if "html_context" not in globals():
html_context = {}
html_context["READTHEDOCS"] = True
import matplotlib
matplotlib.use("agg")
else:
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
================================================
FILE: docs/source/conf/70-latex.py
================================================
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
("index", "scico.tex", "SCICO Documentation", "The SCICO Developers", "manual"),
]
latex_engine = "xelatex"
# latex_use_xindy = False
# mathjax3_config must already be defined
latex_macros = []
for k, v in mathjax3_config["tex"]["macros"].items():
if len(v) == 1:
latex_macros.append(r"\newcommand{\%s}{%s}" % (k, v[0]))
else:
latex_macros.append(r"\newcommand{\%s}[1]{%s}" % (k, v[0]))
imgmath_latex_preamble = "\n".join(latex_macros)
latex_elements = {"preamble": "\n".join(latex_macros)}
================================================
FILE: docs/source/conf/71-texinfo.py
================================================
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
"index",
"SCICO",
"SCICO Documentation",
"SCICO Developers",
"SCICO",
"Scientific Computational Imaging COde (SCICO)",
"Miscellaneous",
),
]
================================================
FILE: docs/source/conf/72-man_page.py
================================================
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "scico", "SCICO Documentation", ["SCICO Developers"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
================================================
FILE: docs/source/conf/80-scico_numpy.py
================================================
import re
from inspect import getmembers, isfunction
# Rewrite module names for certain functions imported into scico.numpy so that they are
# included in the docs for that module. While a bit messy to do so here rather than in a
# function run via app.connect, it is necessary (for some yet to be identified reason)
# to do it here to ensure that the relevant API docs include a table of functions.
import scico.numpy
for module in (scico.numpy, scico.numpy.fft, scico.numpy.linalg, scico.numpy.testing):
for _, f in getmembers(module, isfunction):
# Rewrite module name so that function is included in docs
f.__module__ = module.__name__
f.__doc__ = re.sub(
r"^:func:`([\w_]+)` wrapped to operate",
r":obj:`jax.numpy.\1` wrapped to operate",
str(f.__doc__),
flags=re.M,
)
modname = ".".join(module.__name__.split(".")[1:])
f.__doc__ = re.sub(
r"^LAX-backend implementation of :func:`([\w_]+)`.",
r"LAX-backend implementation of :obj:`%s.\1`." % modname,
str(f.__doc__),
flags=re.M,
)
# Improve formatting of jax.numpy warning
f.__doc__ = re.sub(
r"^\*\*\* This function is not yet implemented by jax.numpy, and will "
r"raise NotImplementedError \*\*\*",
"**WARNING**: This function is not yet implemented by jax.numpy, "
" and will raise :exc:`NotImplementedError`.",
f.__doc__,
flags=re.M,
)
# Remove cross-references to section NEP35
f.__doc__ = re.sub(":ref:`NEP 35 `", "NEP 35", f.__doc__, re.M)
# Remove cross-reference to numpydoc style references section
f.__doc__ = re.sub(r" \[(\d+)\]_", "", f.__doc__, flags=re.M)
# Remove entire numpydoc references section
f.__doc__ = re.sub(r"References\n----------\n.*\n", "", f.__doc__, flags=re.DOTALL)
# Fix various docstring formatting errors
scico.numpy.testing.break_cycles.__doc__ = re.sub(
"calling gc.collect$",
"calling gc.collect.\n\n",
scico.numpy.testing.break_cycles.__doc__,
flags=re.M,
)
scico.numpy.testing.break_cycles.__doc__ = re.sub(
r" __del__\) inside", r"__del__\) inside", scico.numpy.testing.break_cycles.__doc__, flags=re.M
)
scico.numpy.testing.assert_raises_regex.__doc__ = re.sub(
r"\*args,\n.*\*\*kwargs",
"*args, **kwargs",
scico.numpy.testing.assert_raises_regex.__doc__,
flags=re.M,
)
scico.numpy.BlockArray.global_shards.__doc__ = re.sub(
r"`Shard`s", r"`Shard`\ s", scico.numpy.BlockArray.global_shards.__doc__, flags=re.M
)
================================================
FILE: docs/source/conf/81-scico_scipy.py
================================================
import re
from inspect import getmembers, isfunction
# Similar processing for scico.scipy
import scico.scipy
ssp_func = getmembers(scico.scipy.special, isfunction)
for _, f in ssp_func:
if f.__module__[0:11] == "scico.scipy" or f.__module__[0:14] == "jax._src.scipy":
# Rewrite module name so that function is included in docs
f.__module__ = "scico.scipy.special"
# Attempt to fix incorrect cross-reference
f.__doc__ = re.sub(
r"^:func:`([\w_]+)` wrapped to operate",
r":obj:`jax.scipy.special.\1` wrapped to operate",
str(f.__doc__),
flags=re.M,
)
modname = "scipy.special"
f.__doc__ = re.sub(
r"^LAX-backend implementation of :func:`([\w_]+)`.",
r"LAX-backend implementation of :obj:`%s.\1`." % modname,
str(f.__doc__),
flags=re.M,
)
# Remove cross-reference to numpydoc style references section
f.__doc__ = re.sub(r"(^|\ )\[(\d+)\]_", "", f.__doc__, flags=re.M)
# Remove entire numpydoc references section
f.__doc__ = re.sub(r"References\n----------\n.*\n", "", f.__doc__, flags=re.DOTALL)
# Remove problematic citation
f.__doc__ = re.sub(r"See \[dlmf\]_ for details.", "", f.__doc__, re.M)
f.__doc__ = re.sub(r"\[dlmf\]_", "NIST DLMF", f.__doc__, re.M)
# Fix indentation problems
if hasattr(scico.scipy.special, "sph_harm"):
scico.scipy.special.sph_harm.__doc__ = re.sub(
"^Computes the", " Computes the", scico.scipy.special.sph_harm.__doc__, flags=re.M
)
================================================
FILE: docs/source/conf/85-dtype_typehints.py
================================================
from typing import Optional, Sequence, Union # needed for typehints_formatter hack
from scico.typing import ( # needed for typehints_formatter hack
ArrayIndex,
AxisIndex,
DType,
)
# An explanation for this nasty hack, the primary purpose of which is to avoid
# the very long definition of the scico.typing.DType appearing explicitly in the
# docs. This is handled correctly by sphinx.ext.autodoc in some circumstances,
# but only when sphinx_autodoc_typehints is not included in the extension list,
# and the appearance of the type hints (e.g. whether links to definitions are
# included) seems to depend on whether "from __future__ import annotations" was
# used in the module being documented, which is not ideal from a consistency
# perspective. (It's also worth noting that sphinx.ext.autodoc provides some
# configurability for type aliases via the autodoc_type_aliases sphinx
# configuration option.) The alternative is to include sphinx_autodoc_typehints,
# which gives a consistent appearance to the type hints, but the
# autodoc_type_aliases configuration option is ignored, and type aliases are
# always expanded. This hack avoids expansion for the type aliases with the
# longest definitions by definining a custom function for formatting the
# type hints, using an option provided by sphinx_autodoc_typehints. For
# more information, see
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases
# https://github.com/tox-dev/sphinx-autodoc-typehints/issues/284
# https://github.com/tox-dev/sphinx-autodoc-typehints/blob/main/README.md
def typehints_formatter_function(annotation, config):
markup = {
DType: ":obj:`~scico.typing.DType`",
# Compound types involving DType must be added here to avoid their DType
# component being expanded in the docs.
Optional[DType]: r":obj:`~typing.Optional`\ [\ :obj:`~scico.typing.DType`\ ]",
Union[DType, Sequence[DType]]: (
r":obj:`~typing.Union`\ [\ :obj:`~scico.typing.DType`\ , "
r":obj:`~typing.Sequence`\ [\ :obj:`~scico.typing.DType`\ ]]"
),
AxisIndex: ":obj:`~scico.typing.AxisIndex`",
ArrayIndex: ":obj:`~scico.typing.ArrayIndex`",
}
if annotation in markup:
return markup[annotation]
else:
return None
typehints_formatter = typehints_formatter_function
================================================
FILE: docs/source/conf.py
================================================
# -*- coding: utf-8 -*-
import os
import sys
confpath = os.path.dirname(__file__)
sys.path.append(confpath)
rootpath = os.path.realpath(os.path.join(confpath, "..", ".."))
sys.path.append(rootpath)
from docsutil import insert_inheritance_diagram, package_classes, run_conf_files
# Process settings in files in conf directory
_vardict = run_conf_files(vardict={"confpath": confpath, "rootpath": rootpath})
for _k, _v in _vardict.items():
globals()[_k] = _v
del _vardict, _k, _v
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "5.0.0"
# The suffix of source filenames.
source_suffix = ".rst"
# The encoding of source files.
source_encoding = "utf-8"
# The master toctree document.
master_doc = "index"
# Output file base name for HTML help builder.
htmlhelp_basename = "SCICOdoc"
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["_build", "**tests**", "**README.rst", "include"]
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# Include TODOs
todo_include_todos = True
def class_inherit_diagrams(_):
# Insert inheritance diagrams for classes that have base classes
import scico
custom_parts = {"scico.ray.tune.Tuner": 4}
clslst = package_classes(scico)
for cls in clslst:
insert_inheritance_diagram(cls, parts=custom_parts)
def process_docstring(app, what, name, obj, options, lines):
# Don't show docs for inherited members in classes in scico.flax.
# This is primarily useful for silencing warnings due to problems in
# the current release of flax, but is arguably also useful in avoiding
# extensive documentation of methods that are likely to be of limited
# interest to users of the scico.flax classes.
#
# Note: this event handler currently has no effect since inclusion of
# inherited members is currently globally disabled (see
# "inherited-members" in autodoc_default_options), but is left in
# place in case a decision is ever made to revert the global setting.
#
# See https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
# for documentation of the autodoc-process-docstring event used here.
if what == "class" and "scico.flax." in name:
options["inherited-members"] = False
def setup(app):
app.connect("builder-inited", class_inherit_diagrams)
app.connect("autodoc-process-docstring", process_docstring)
================================================
FILE: docs/source/contributing.rst
================================================
.. _scico_dev_contributing:
Contributing
============
Contributions to SCICO are welcome. Before starting work, please
contact the maintainers, either via email or the GitHub issue system,
to discuss the relevance of your contribution and the most appropriate
location within the existing package structure.
.. _installing_dev:
Installing a Development Version
--------------------------------
1. Fork both the ``scico`` and ``scico-data`` repositories, creating
copies of these repositories in your own git account.
2. Make sure that you have Python 3.10 or later installed in order to
create a conda virtual environment.
3. Clone your fork from the source repo.
::
git clone --recurse-submodules git@github.com:/scico.git
4. Create a conda environment using Python 3.10 or later, e.g.:
::
conda create -n scico python=3.12
5. Activate the created conda virtual environment:
::
conda activate scico
6. Change directory to the root of the cloned repository:
::
cd scico
7. Add the ``scico`` repo as an upstream remote to sync your changes:
::
git remote add upstream https://www.github.com/lanl/scico
8. After adding the upstream, the recommended way to install SCICO and
its dependencies is via pip:
::
pip install -r requirements.txt # Installs basic requirements
pip install -r dev_requirements.txt # Installs developer requirements
pip install -r docs/docs_requirements.txt # Installs documentation requirements
pip install -e . # Installs SCICO from the current directory in editable mode
For installing dependencies related to the examples please see :ref:`example_notebooks`.
Installing these are neccessary for the successfull running of the tests.
9. The SCICO project uses the `black
`_, `isort
`_ and `pylint
`_ code formatting
utilities. It is important to set up a `pre-commit hook
`_ to ensure that any modified code passes
format check before it is committed to the development repo:
::
pre-commit install # Sets up git pre-commit hooks
It is also recommended to `pin the conda package version
`__
of `black `_ to the version
number specified in ``dev_requirements.txt``.
10. For testing see `Tests`_.
Building Documentation
----------------------
Before building the documentation, one must install the documentation
specific dependencies by running
::
pip install -r docs/docs_requirements.txt
Then, a local copy of the documentation can be built from the
respository root directory by running
::
python setup.py build_sphinx
Alternatively, one can also build the documentation by running the
following from the `docs/` directory
::
make html
Contributing Code
-----------------
- New features / bugs / documentation are *always* developed in separate branches.
- Branches should be named in the form
`/`, where ``
provides a highly condensed description of the purpose of the branch
(e.g. `address_todo`), and may include an issue number if
appropriate (e.g. `fix_223`).
A feature development workflow might look like this:
1. Follow the instructions in `Installing a Development Version`_.
2. Sync with the upstream repository:
::
git pull --rebase origin main --recurse-submodules
3. Create a branch to develop from:
::
git checkout -b /
4. Make your desired changes.
5. Run the test suite:
::
pytest
You can limit the test suite to a specific file for example:
::
pytest scico/test/test_blockarray.py
6. When you are finished making changes, create a new commit:
::
git add file1.py git add file2.py
git commit -m "A good commit message"
If you have added or modified an example script, see `Usage Examples`_.
If your contribution involves any significant new features or changes,
add a corresponding entry to the change summary for the next release
in the ``CHANGES.rst`` file.
7. Sync with the upstream repository:
::
git fetch upstream
git rebase upstream/main
8. Push your development upstream:
::
git push --set-upstream origin /
9. Create a new pull request to the ``main`` branch; see `the GitHub instructions `_.
10. The SCICO maintainers will review and merge your PR.
The SCICO project recommends the ``squash and merge`` option for merging PRs.
11. Delete the branch after it has been merged.
Adding Data
-----------
The following steps show how to add new data, ``new_data.npz``, to the
packaged data. We assume the ``scico`` repository has been cloned to
``scico/``. Note that the data is located in the ``scico-data``
submodule, which is attached to the main `scico` repository via the
directory ``scico/data`` (i.e. the ``data/`` subdirectory of the
repository root directory, *not* the ``scico/data`` subdirectory of
the repository root directory). When adding new data, both the
``scico`` and ``scico-data`` repositories must be updated and kept in
sync.
1. Create new branches in the main ``scico`` repository as well as in
the submodule corresponding to the ``scico-data`` repository (which
can be achieved by following the usual branch creation procedure
after changing the current directory to ``scico/data``).
2. Add the ``new_data.npz`` file to the appropriate subdirectory
(creating a new one if necessary) of the ``scico/data`` directory.
3. Change directory to this directory (taken to be ``scico/data/flax``
for the purposes of this example) and add/commit the new data file:
::
cd scico/data/flax
git add new_data.npz
git commit -m "Add new data file"
4. Return to the ``scico`` repository root directory, add/commit the
new data, and update submodule:
::
cd ../.. # pwd now `scico` repo root
git add data
git commit -m "Add data and update data module"
5. Push both repositories:
::
git submodule foreach --recursive 'git push' && git push
Type Checking
-------------
All code is required to pass ``mypy`` type checking.
Install ``mypy``:
::
conda install mypy
To run the type checker, execute the following from the scico repository root:
::
mypy --follow-imports=skip --ignore-missing-imports --exclude "(numpy|test)" scico/
Tests
-----
All functions and classes should have corresponding ``pytest`` unit tests.
Running Tests
^^^^^^^^^^^^^
To be able to run the tests, install ``pytest`` and, optionally,
``pytest-runner``:
::
conda install pytest pytest-runner
The tests can be run by
::
pytest
or (if ``pytest-runner`` is installed)
::
python setup.py test
from the ``scico`` repository root directory. Tests can be run in an installed
version of ``scico`` by
::
pytest --pyargs scico
When any significant changes are made to the test suite, the ``pytest-split`` test
time database files in ``data/pytest`` should be updated using
::
pytest --store-durations --durations-path data/pytest/durations_ubuntu --level 2
(for Ubuntu CI), and
::
pytest --store-durations --durations-path data/pytest/durations_macos --level 1
(for MacOS CI). These updated files should be bzipped and committed into the
``scico-data`` repository, replacing the current versions.
Test Coverage
^^^^^^^^^^^^^
Test coverage is a measure of the fraction of the package code that is
exercised by the tests. While this should not be the primary criterion
in designing tests, it is a useful tool for finding obvious areas of
omission.
To be able to check test coverage, install ``coverage``:
::
conda install coverage
A coverage report can be obtained by
::
coverage run
coverage report
Usage Examples
--------------
New usage examples should adhere to the same general structure as the
existing examples to ensure that the mechanism for automatically
generating corresponding Jupyter notebooks functions correctly. In
particular:
1. The initial lines of the script should consist of a comment block,
followed by a blank line, followed by a multiline string with an
RST heading on the first line, e.g.,
::
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the SCICO package. Details of the copyright
# and user license can be found in the 'LICENSE.txt' file distributed
# with the package.
"""
Script Title
============
Script description.
"""
2. The final line of the script is an ``input`` statement intended to
avoid the script terminating immediately, thereby closing all
figures:
::
input("\nWaiting for input to close figures and exit")
3. Citations are included using the standard `Sphinx
`__ ``:cite:`cite-key```
syntax, where ``cite-key`` is the key of an entry in
``docs/source/references.bib``.
4. Cross-references to other components of the documentation are
included using the syntax described in the `nbsphinx documentation
`__.
5. External links are included using Markdown syntax ``[link text](url)``.
6. When constructing a synthetic image/volume for use in the example,
define a global variable `N` that controls the size of the problem,
and where relevant, define a global variable `maxiter` that
controls the number of iterations of optimization algorithms such
as ADMM. Adhering to this convention allows the
``examples/scriptcheck.sh`` utility to automatically construct less
computationally expensive versions of the example scripts for
testing that they run without any errors.
Adding new examples
^^^^^^^^^^^^^^^^^^^
The following steps show how to add a new example, ``new_example.py``,
to the packaged usage examples. We assume the ``scico`` repository has
been cloned to ``scico/``.
Note that the ``.py`` scripts are included in
``scico/examples/scripts``, while the compiled Jupyter Notebooks are
located in the scico-data submodule, which is symlinked to
``scico/data``. When adding a new usage example, both the ``scico``
and ``scico-data`` repositories must be updated and kept in sync.
.. warning:: Ensure that all binary data (including raw data, images,
``.ipynb`` files) are added to ``scico-data``, not the main
``scico`` repo.
1. Create new branches in the main `scico` repository as well as in
the submodule corresponding to the `scico-data` repository (which
can be achieved by following the usual branch creation procedure
after changing the current directory to ``scico/data``).
2. Add the ``new_example.py`` script to the ``scico/examples/scripts`` directory.
3. Add the basename of the script (i.e., without the pathname; in this
case, ``new_example.py``) to the appropriate section of
``examples/scripts/index.rst``.
4. Convert your new example to a Jupyter notebook by changing
directory to the ``scico/examples`` directory and following the
instructions in ``scico/examples/README.rst``.
5. Change directory to the ``data`` directory and add/commit the new
Jupyter Notebook:
::
cd scico/data
git add notebooks/new_example.ipynb
git commit -m "Add new usage example"
6. Return to the main ``scico`` repository root directory, ensure the
``main`` branch is checked out, add/commit the new script and
updated submodule:
::
cd .. # pwd now `scico` repo root
git add data
git add examples/scripts/new_filename.py
git commit -m "Add usage example and update data module"
7. Push both repositories:
::
git submodule foreach --recursive 'git push' && git push
================================================
FILE: docs/source/docsutil.py
================================================
# -*- coding: utf-8 -*-
# Copyright (C) 2021-2023 by SCICO Developers
# All rights reserved. BSD 3-clause License.
# This file is part of the SCICO package. Details of the copyright and
# user license can be found in the 'LICENSE' file distributed with the
# package.
"""Utilities for building docs."""
import importlib
import inspect
import os
import pkgutil
import sys
from glob import glob
from runpy import run_path
def run_conf_files(vardict=None, path=None):
"""Execute Python files in conf directory.
Args:
vardict: Dictionary into which variable names should be inserted.
Defaults to empty dict.
path: Path to conf directory. Defaults to path to this module.
Returns:
A dict populated with variables defined during execution of the
configuration files.
"""
if vardict is None:
vardict = {}
if path is None:
path = os.path.dirname(__file__)
files = os.path.join(path, "conf", "*.py")
for f in sorted(glob(files)):
conf = run_path(f, init_globals=vardict)
for k, v in conf.items():
if len(k) >= 4 and k[0:2] == "__" and k[-2:] == "__": # ignore ____ variables
continue
vardict[k] = v
return vardict
def package_classes(package):
"""Get a list of classes in a package.
Return a list of qualified names of classes in the specified
package. Classes in modules with names beginning with an "_" are
omitted, as are classes whose internal module name record is not
the same as the module in which they are found (i.e. indicating
that they have been imported from elsewhere).
Args:
package: Reference to package for which classes are to be listed
(not package name string).
Returns:
A list of qualified names of classes in the specified package.
"""
classes = []
# Iterate over modules in package
for importer, modname, _ in pkgutil.walk_packages(
path=package.__path__, prefix=(package.__name__ + "."), onerror=lambda x: None
):
# Skip modules whose names begin with a "_"
if modname.split(".")[-1][0] == "_":
continue
importlib.import_module(modname)
# Iterate over module members
for name, obj in inspect.getmembers(sys.modules[modname]):
if inspect.isclass(obj):
# Get internal module name of class for comparison with working module name
try:
objmodname = getattr(sys.modules[modname], obj.__name__).__module__
except Exception:
objmodname = None
if objmodname == modname:
classes.append(modname + "." + obj.__name__)
return classes
def get_text_indentation(text, skiplines=0):
"""Compute the leading whitespace indentation in a block of text.
Args:
text: A block of text as a string.
Returns:
Indentation length.
"""
min_indent = len(text)
lines = text.splitlines()
if len(lines) > skiplines:
lines = lines[skiplines:]
else:
return None
for line in lines:
if len(line) > 0:
indent = len(line) - len(line.lstrip())
if indent < min_indent:
min_indent = indent
return min_indent
def add_text_indentation(text, indent):
"""Insert leading whitespace into a block of text.
Args:
text: A block of text as a string.
indent: Number of leading spaces to insert on each line.
Returns:
Text with additional indentation.
"""
lines = text.splitlines()
for n, line in enumerate(lines):
if len(line) > 0:
lines[n] = (" " * indent) + line
return "\n".join(lines)
def insert_inheritance_diagram(clsqname, parts=None, default_nparts=2):
"""Insert an inheritance diagram into a class docstring.
No action is taken for classes without a base clase, and for classes
without a docstring.
Args:
clsqname: Qualified name (i.e. including module name path) of class.
parts: A dict mapping qualified class names to custom values for
the ":parts:" directive.
default_nparts: Default value for the ":parts:" directive.
"""
# Extract module name and class name from qualified class name
clspth = clsqname.split(".")
modname = ".".join(clspth[0:-1])
clsname = clspth[-1]
# Get reference to class
cls = getattr(sys.modules[modname], clsname)
# Return immediately if class has no base classes
if getattr(cls, "__bases__") == (object,):
return
# Get current docstring
docstr = getattr(cls, "__doc__")
# Return immediately if class has no docstring
if docstr is None:
return
# Use class-specific parts or default parts directive value
if parts and clsqname in parts:
nparts = parts[clsqname]
else:
nparts = default_nparts
# Split docstring into individual lines
lines = docstr.splitlines()
# Return immediately if there are no lines
if not lines:
return
# Cut leading whitespace lines
n = 0
for n, line in enumerate(lines):
if line != "":
break
lines = lines[n:]
# Define inheritance diagram insertion text
idstr = f"""
.. inheritance-diagram:: {clsname}
:parts: {nparts}
"""
docstr_indent = get_text_indentation(docstr, skiplines=1)
if docstr_indent is not None and docstr_indent > 4:
idstr = add_text_indentation(idstr, docstr_indent - 4)
# Insert inheritance diagram after summary line and whitespace line following it
lines.insert(2, idstr)
# Construct new docstring and attach it to the class
extdocstr = "\n".join(lines)
setattr(cls, "__doc__", extdocstr)
================================================
FILE: docs/source/examples.rst
================================================
.. _example_notebooks:
Usage Examples
==============
.. toctree::
:maxdepth: 1
.. include:: include/examplenotes.rst
Organized by Application
------------------------
.. toctree::
:maxdepth: 1
Computed Tomography
^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_abel_tv_admm
examples/ct_abel_tv_admm_tune
examples/ct_symcone_tv_padmm
examples/ct_astra_noreg_pcg
examples/ct_astra_3d_tv_admm
examples/ct_astra_3d_tv_padmm
examples/ct_tv_admm
examples/ct_astra_tv_admm
examples/ct_multi_tv_admm
examples/ct_astra_weighted_tv_admm
examples/ct_svmbir_tv_multi
examples/ct_svmbir_ppp_bm3d_admm_cg
examples/ct_svmbir_ppp_bm3d_admm_prox
examples/ct_fan_svmbir_ppp_bm3d_admm_prox
examples/ct_modl_train_foam2
examples/ct_odp_train_foam2
examples/ct_unet_train_foam2
examples/ct_projector_comparison_2d
examples/ct_projector_comparison_3d
Deconvolution
^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/deconv_circ_tv_admm
examples/deconv_tv_admm
examples/deconv_tv_padmm
examples/deconv_tv_admm_tune
examples/deconv_microscopy_tv_admm
examples/deconv_microscopy_allchn_tv_admm
examples/deconv_ppp_bm3d_admm
examples/deconv_ppp_bm3d_apgm
examples/deconv_ppp_dncnn_admm
examples/deconv_ppp_dncnn_padmm
examples/deconv_ppp_bm4d_admm
examples/deconv_modl_train_foam1
examples/deconv_odp_train_foam1
Sparse Coding
^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/sparsecode_nn_admm
examples/sparsecode_nn_apgm
examples/sparsecode_conv_admm
examples/sparsecode_conv_md_admm
examples/sparsecode_apgm
examples/sparsecode_poisson_apgm
Miscellaneous
^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/demosaic_ppp_bm3d_admm
examples/superres_ppp_dncnn_admm
examples/denoise_l1tv_admm
examples/denoise_ptv_pdhg
examples/denoise_tv_admm
examples/denoise_tv_apgm
examples/denoise_tv_multi
examples/denoise_approx_tv_multi
examples/denoise_cplx_tv_nlpadmm
examples/denoise_cplx_tv_pdhg
examples/denoise_dncnn_universal
examples/diffusercam_tv_admm
examples/video_rpca_admm
examples/ct_datagen_foam2
examples/deconv_datagen_bsds
examples/deconv_datagen_foam1
examples/denoise_datagen_bsds
Organized by Regularization
---------------------------
.. toctree::
:maxdepth: 1
Plug and Play Priors
^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_svmbir_ppp_bm3d_admm_cg
examples/ct_svmbir_ppp_bm3d_admm_prox
examples/ct_fan_svmbir_ppp_bm3d_admm_prox
examples/deconv_ppp_bm3d_admm
examples/deconv_ppp_bm3d_apgm
examples/deconv_ppp_dncnn_admm
examples/deconv_ppp_dncnn_padmm
examples/deconv_ppp_bm4d_admm
examples/demosaic_ppp_bm3d_admm
examples/superres_ppp_dncnn_admm
Total Variation
^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_abel_tv_admm
examples/ct_abel_tv_admm_tune
examples/ct_symcone_tv_padmm
examples/ct_tv_admm
examples/ct_multi_tv_admm
examples/ct_astra_tv_admm
examples/ct_astra_3d_tv_admm
examples/ct_astra_3d_tv_padmm
examples/ct_astra_weighted_tv_admm
examples/ct_svmbir_tv_multi
examples/deconv_circ_tv_admm
examples/deconv_tv_admm
examples/deconv_tv_admm_tune
examples/deconv_tv_padmm
examples/deconv_microscopy_tv_admm
examples/deconv_microscopy_allchn_tv_admm
examples/denoise_l1tv_admm
examples/denoise_ptv_pdhg
examples/denoise_tv_admm
examples/denoise_tv_apgm
examples/denoise_tv_multi
examples/denoise_approx_tv_multi
examples/denoise_cplx_tv_nlpadmm
examples/denoise_cplx_tv_pdhg
examples/diffusercam_tv_admm
Sparsity
^^^^^^^^
.. toctree::
:maxdepth: 1
examples/diffusercam_tv_admm
examples/sparsecode_nn_admm
examples/sparsecode_nn_apgm
examples/sparsecode_conv_admm
examples/sparsecode_conv_md_admm
examples/sparsecode_apgm
examples/sparsecode_poisson_apgm
examples/video_rpca_admm
Machine Learning
^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_datagen_foam2
examples/ct_modl_train_foam2
examples/ct_odp_train_foam2
examples/ct_unet_train_foam2
examples/deconv_datagen_bsds
examples/deconv_datagen_foam1
examples/deconv_modl_train_foam1
examples/deconv_odp_train_foam1
examples/denoise_datagen_bsds
examples/denoise_dncnn_train_bsds
examples/denoise_dncnn_universal
Organized by Optimization Algorithm
-----------------------------------
.. toctree::
:maxdepth: 1
ADMM
^^^^
.. toctree::
:maxdepth: 1
examples/ct_abel_tv_admm
examples/ct_abel_tv_admm_tune
examples/ct_symcone_tv_padmm
examples/ct_astra_tv_admm
examples/ct_tv_admm
examples/ct_astra_3d_tv_admm
examples/ct_astra_weighted_tv_admm
examples/ct_multi_tv_admm
examples/ct_svmbir_tv_multi
examples/ct_svmbir_ppp_bm3d_admm_cg
examples/ct_svmbir_ppp_bm3d_admm_prox
examples/ct_fan_svmbir_ppp_bm3d_admm_prox
examples/deconv_circ_tv_admm
examples/deconv_tv_admm
examples/deconv_tv_admm_tune
examples/deconv_microscopy_tv_admm
examples/deconv_microscopy_allchn_tv_admm
examples/deconv_ppp_bm3d_admm
examples/deconv_ppp_dncnn_admm
examples/deconv_ppp_bm4d_admm
examples/diffusercam_tv_admm
examples/sparsecode_nn_admm
examples/sparsecode_conv_admm
examples/sparsecode_conv_md_admm
examples/demosaic_ppp_bm3d_admm
examples/superres_ppp_dncnn_admm
examples/denoise_l1tv_admm
examples/denoise_tv_admm
examples/denoise_tv_multi
examples/denoise_approx_tv_multi
examples/video_rpca_admm
Linearized ADMM
^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_svmbir_tv_multi
examples/denoise_tv_multi
Proximal ADMM
^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/ct_astra_3d_tv_padmm
examples/deconv_tv_padmm
examples/denoise_tv_multi
examples/deconv_ppp_dncnn_padmm
Non-linear Proximal ADMM
^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
examples/denoise_cplx_tv_nlpadmm
PDHG
^^^^
.. toctree::
:maxdepth: 1
examples/ct_svmbir_tv_multi
examples/denoise_ptv_pdhg
examples/denoise_tv_multi
examples/denoise_cplx_tv_pdhg
PGM
^^^
.. toctree::
:maxdepth: 1
examples/deconv_ppp_bm3d_apgm
examples/sparsecode_apgm
examples/sparsecode_nn_apgm
examples/sparsecode_poisson_apgm
examples/denoise_tv_apgm
examples/denoise_approx_tv_multi
PCG
^^^
.. toctree::
:maxdepth: 1
examples/ct_astra_noreg_pcg
================================================
FILE: docs/source/include/blockarray.rst
================================================
.. _blockarray_class:
BlockArray
==========
.. testsetup::
>>> import numpy as np
>>> import scico
>>> import scico.random
>>> import scico.linop
>>> import scico.numpy as snp
>>> from scico.numpy import BlockArray
The class :class:`.BlockArray` provides a way to combine arrays of
different shapes into a single object for use with other SCICO classes.
A :class:`.BlockArray` consists of a list of :class:`jax.Array` objects,
which we refer to as blocks. A :class:`.BlockArray` differs from a list in
that, whenever possible, :class:`.BlockArray` properties and methods
(including unary and binary operators like +, -, \*, ...) automatically
map along the blocks, returning another :class:`.BlockArray` or tuple as
appropriate. For example,
::
>>> x = snp.blockarray((
... [[1, 3, 7],
... [2, 2, 1]],
... [2, 4, 8]
... ))
>>> x.shape # returns tuple
((2, 3), (3,))
>>> x * 2 # returns BlockArray # doctest: +ELLIPSIS
BlockArray([...Array([[ 2, 6, 14],
[ 4, 4, 2]], dtype=...), ...Array([ 4, 8, 16], dtype=...)])
>>> y = snp.blockarray((
... [[.2],
... [.3]],
... [.4]
... ))
>>> x + y # returns BlockArray # doctest: +ELLIPSIS
BlockArray([...Array([[1.2, 3.2, 7.2],
[2.3, 2.3, 1.3]], dtype=...), ...Array([2.4, 4.4, 8.4], dtype=...)])
.. _numpy_functions_blockarray:
NumPy and SciPy Functions
-------------------------
:mod:`scico.numpy`, :mod:`scico.numpy.testing`, and
:mod:`scico.scipy.special` provide wrappers around :mod:`jax.numpy`,
:mod:`numpy.testing` and :mod:`jax.scipy.special` where many of the
functions have been extended to work with instances of :class:`.BlockArray`.
In particular:
* When a tuple of tuples is passed as the `shape`
argument to an array creation routine, a :class:`.BlockArray` is created.
* When a :class:`.BlockArray` is passed to a reduction function, the blocks are
ravelled (i.e., reshaped to be 1D) and concatenated before the reduction
is applied. This behavior may be prevented by passing the `axis`
argument, in which case the function is mapped over the blocks.
* When one or more :class:`.BlockArray` instances are passed to a mathematical
function that is not a reduction, the function is mapped over
(corresponding) blocks.
For a list of array creation routines, see
::
>>> scico.numpy.creation_routines # doctest: +ELLIPSIS
('empty', ...)
For a list of reduction functions, see
::
>>> scico.numpy.reduction_functions # doctest: +ELLIPSIS
('sum', ...)
For lists of the remaining wrapped functions, see
::
>>> scico.numpy.mathematical_functions # doctest: +ELLIPSIS
('sin', ...)
>>> scico.numpy.testing_functions # doctest: +ELLIPSIS
('testing.assert_allclose', ...)
>>> import scico.scipy
>>> scico.scipy.special.functions # doctest: +ELLIPSIS
('betainc', ...)
Note that:
* The functional and method versions of the "same" function differ in their
behavior, with the method version only applying the reduction within each
block, and the function version applying the reduction across all blocks.
For example, :func:`scico.numpy.sum` applied to a :class:`.BlockArray` with
two blocks returns a scalar value, while :meth:`.BlockArray.sum` returns a
:class:`.BlockArray` two scalar blocks.
* For example, :func:`scico.numpy.ravel` returns a fully flattened, single
:class:`jax.Array`, while :meth:`.BlockArray.ravel` returns a
:class:`.BlockArray` with ravelled blocks.
Motivating Example
------------------
The discrete differences of a two-dimensional array, :math:`\mb{x} \in
\mbb{R}^{n \times m}`, in the horizontal and vertical directions can
be represented by the arrays :math:`\mb{x}_h \in \mbb{R}^{n \times
(m-1)}` and :math:`\mb{x}_v \in \mbb{R}^{(n-1) \times m}`
respectively. While it is usually useful to consider the output of a
difference operator as a single entity, we cannot combine these two
arrays into a single array since they have different shapes. We could
vectorize each array and concatenate the resulting vectors, leading to
:math:`\mb{\bar{x}} \in \mbb{R}^{n(m-1) + m(n-1)}`, which can be
stored as a one-dimensional array, but this makes it hard to access
the individual components :math:`\mb{x}_h` and :math:`\mb{x}_v`.
Instead, we can construct a :class:`.BlockArray`, :math:`\mb{x}_B =
[\mb{x}_h, \mb{x}_v]`:
::
>>> n = 32
>>> m = 16
>>> x_h, key = scico.random.randn((n, m-1))
>>> x_v, _ = scico.random.randn((n-1, m), key=key)
# Form the blockarray
>>> x_B = snp.blockarray([x_h, x_v])
# The blockarray shape is a tuple of tuples
>>> x_B.shape
((32, 15), (31, 16))
# Each block component can be easily accessed
>>> x_B[0].shape
(32, 15)
>>> x_B[1].shape
(31, 16)
Constructing a BlockArray
-------------------------
The recommended way to construct a :class:`.BlockArray` is by using the
:func:`~scico.numpy.blockarray` function.
::
>>> import scico.numpy as snp
>>> x0, key = scico.random.randn((32, 32))
>>> x1, _ = scico.random.randn((16,), key=key)
>>> X = snp.blockarray((x0, x1))
>>> X.shape
((32, 32), (16,))
>>> X.size
(1024, 16)
>>> len(X)
2
While :func:`~scico.numpy.blockarray` will accept arguments of type
:class:`~numpy.ndarray` or :class:`~jax.Array`, arguments of type :class:`~numpy.ndarray` will be converted to :class:`~jax.Array` type.
Operating on a BlockArray
-------------------------
.. _blockarray_indexing:
Indexing
^^^^^^^^
:class:`.BlockArray` indexing works just like indexing a list.
Multiplication between BlockArray and LinearOperator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :class:`.Operator` and :class:`.LinearOperator` classes are designed
to work on instances of :class:`.BlockArray` in addition to instances of
:obj:`~jax.Array`. For example
::
>>> x, key = scico.random.randn((3, 4))
>>> A_1 = scico.linop.Identity(x.shape)
>>> A_1.shape # array -> array
((3, 4), (3, 4))
>>> A_2 = scico.linop.FiniteDifference(x.shape)
>>> A_2.shape # array -> BlockArray
(((2, 4), (3, 3)), (3, 4))
>>> diag = snp.blockarray([np.array(1.0), np.array(2.0)])
>>> A_3 = scico.linop.Diagonal(diag, input_shape=(A_2.output_shape))
>>> A_3.shape # BlockArray -> BlockArray
(((2, 4), (3, 3)), ((2, 4), (3, 3)))
================================================
FILE: docs/source/include/examplenotes.rst
================================================
.. _example_depend:
Example Dependencies
--------------------
Some examples use additional dependencies, which are listed in `examples_requirements.txt `_.
The additional requirements should be installed via pip, with the exception of ``astra-toolbox``,
which should be installed via conda:
::
conda install astra-toolbox
pip install -r examples/examples_requirements.txt # Installs other example requirements
The dependencies can also be installed individually as required.
Note that ``astra-toolbox`` should be installed on a host with one or more CUDA GPUs to ensure
that the version with GPU support is installed.
Run Time
--------
Most of these examples have been constructed with sufficiently small test problems to
allow them to run to completion within 5 minutes or less on a reasonable workstation.
Note, however, that it was not feasible to construct meaningful examples of the training
of some of the deep learning algorithms that complete within a relatively short time;
the examples "CT Training and Reconstructions with MoDL" and "CT Training and
Reconstructions with ODP" in particular are much slower, and can require multiple hours
to run on a workstation with multiple GPUs.
|
================================================
FILE: docs/source/include/functional.rst
================================================
Functionals
===========
A functional is
a mapping from :math:`\mathbb{R}^n` or :math:`\mathbb{C}^n` to :math:`\mathbb{R}`.
In SCICO, functionals are
primarily used to represent a cost to be minimized
and are represented by instances of the :class:`.Functional` class.
An instance of :class:`.Functional`, ``f``, may provide three core operations.
* Evaluation
- ``f(x)`` returns the value of the functional
evaluated at the point ``x``.
- A functional that can be evaluated
has the attribute ``f.has_eval == True``.
- Not all functionals can be evaluated: see `Plug-and-Play`_.
* Gradient
- ``f.grad(x)`` returns the gradient of the functional evaluated at ``x``.
- Gradients are calculated using JAX reverse-mode automatic differentiation,
exposed through :func:`scico.grad`.
- *Note:* The gradient of a functional ``f`` can be evaluated even if that functional is not smooth.
All that is required is that the functional can be evaluated, ``f.has_eval == True``.
However, the result may not be a valid gradient (or subgradient) for all inputs.
* Proximal operator
- ``f.prox(v, lam)`` returns the result of the scaled proximal
operator of ``f``, i.e., the proximal operator of ``lambda x:
lam * f(x)``, evaluated at the point ``v``.
- The proximal operator of a functional :math:`f : \mathbb{R}^n \to
\mathbb{R}` is the mapping :math:`\mathrm{prox}_f : \mathbb{R}^n
\to \mathbb{R}^n` defined as
.. math::
\mathrm{prox}_f (\mb{v}) = \argmin_{\mb{x}} f(\mb{x}) +
\frac{1}{2} \norm{\mb{v} - \mb{x}}_2^2\;.
Plug-and-Play
-------------
For the plug-and-play framework :cite:`sreehari-2016-plug`,
we encapsulate generic denoisers including CNNs
in :class:`.Functional` objects that **cannot be evaluated**.
The denoiser is applied via the the proximal operator.
For examples, see :ref:`example_notebooks`.
Proximal Calculus
-----------------
We support a limited subset of proximal calculus rules:
Scaled Functionals
^^^^^^^^^^^^^^^^^^
Given a scalar ``c`` and a functional ``f`` with a defined proximal method, we can
determine the proximal method of ``c * f`` as
.. math::
\begin{align}
\mathrm{prox}_{c f} (v, \lambda) &= \argmin_x \lambda (c f)(x) + \frac{1}{2} \norm{v - x}_2^2 \\
&= \argmin_x (\lambda c) f(x) + \frac{1}{2} \norm{v - x}_2^2 \\
&= \mathrm{prox}_{f} (v, c \lambda) \;.
\end{align}
Note that we have made no assumptions regarding homogeneity of ``f``;
rather, only that the proximal method of ``f`` is given
in the parameterized form :math:`\mathrm{prox}_{c f}`.
In SCICO, multiplying a :class:`.Functional` by a scalar
will return a :class:`.ScaledFunctional`.
This :class:`.ScaledFunctional` retains the ``has_eval`` and ``has_prox`` attributes
from the original :class:`.Functional`,
but the proximal method is modified to accomodate the additional scalar.
Separable Functionals
^^^^^^^^^^^^^^^^^^^^^
A separable functional :math:`f : \mathbb{C}^N \to \mathbb{R}` can be written as the sum
of functionals :math:`f_i : \mathbb{C}^{N_i} \to \mathbb{R}` with :math:`\sum_i N_i = N`. In particular,
.. math::
f(\mb{x}) = f(\mb{x}_1, \dots, \mb{x}_N) = f_1(\mb{x}_1) + \dots + f_N(\mb{x}_N) \;.
The proximal operator of a separable :math:`f` can be written
in terms of the proximal operators of the :math:`f_i`
(see Theorem 6.6 of :cite:`beck-2017-first`):
.. math::
\mathrm{prox}_f(\mb{x}, \lambda)
=
\begin{pmatrix}
\mathrm{prox}_{f_1}(\mb{x}_1, \lambda) \\
\vdots \\
\mathrm{prox}_{f_N}(\mb{x}_N, \lambda) \\
\end{pmatrix} \;.
Separable Functionals are implemented in the :class:`.SeparableFunctional` class. Separable functionals naturally accept :class:`.BlockArray` inputs and return the prox as a :class:`.BlockArray`.
Adding New Functionals
----------------------
To add a new functional,
create a class which
1. inherits from base :class:`.Functional`;
2. has ``has_eval`` and ``has_prox`` flags;
3. has ``_eval`` and ``prox`` methods, as necessary.
For example,
::
class MyFunctional(scico.functional.Functional):
has_eval = True
has_prox = True
def _eval(self, x: JaxArray) -> float:
return snp.sum(x)
def prox(self, x: JaxArray, lam : float) -> JaxArray:
return x - lam
Losses
------
In SCICO, a loss is a special type of functional
.. math::
f(\mb{x}) = \alpha l( \mb{y}, A(\mb{x}) ) \;,
where :math:`\alpha` is a scaling parameter,
:math:`l` is a functional,
:math:`\mb{y}` is a set of measurements,
and :math:`A` is an operator.
SCICO uses the class :class:`.Loss` to represent losses.
Loss functionals commonly arrise in the context of solving
inverse problems in scientific imaging,
where they are used to represent the mismatch
between predicted measurements :math:`A(\mb{x})`
and actual ones :math:`\mb{y}`.
================================================
FILE: docs/source/include/learning.rst
================================================
Learned Models
==============
In SCICO, neural network models are used to represent imaging problems and provide different modes of data-driven regularization.
The models are implemented in `Flax `_, and constitute a representative sample of frequently used networks.
FlaxMap
-------
SCICO interfaces with the implemented models via :class:`.FlaxMap`. This provides a standardized access to all trained models via the model definiton and the learned parameters. Further specialized functionality, such as learned denoisers, are built on top of :class:`.FlaxMap`. The specific models that have been implemented are described below.
DnCNN
-----
The denoiser convolutional neural network model (DnCNN) :cite:`zhang-2017-dncnn`, implemented as :class:`.DnCNNNet`, is used to denoise images that have been corrupted with additive Gaussian noise.
ODP
---
The unrolled optimization with deep priors (ODP) :cite:`diamond-2018-odp`, implemented as :class:`.ODPNet`, is used to solve inverse problems in imaging by adapting classical iterative methods into an end-to-end framework that incorporates deep networks as well as knowledge of the image formation model.
The framework aims to solve the optimization problem
.. math::
\argmin_{\mb{x}} \; f(A \mb{x}, \mb{y}) + r(\mb{x}) \;,
where :math:`A` represents a linear forward model and :math:`r` a regularization function encoding prior information, by unrolling the iterative solution method into a network where each iteration corresponds to a different stage in the ODP network. Different iterative solutions produce different unrolled optimization algorithms which, in turn, produce different ODP networks. The ones implemented in SCICO are described below.
Proximal Map
^^^^^^^^^^^^
This algorithm corresponds to solving
.. math::
:label: eq:odp_prox
\argmin_{\mb{x}} \; \alpha_k \, f(A \mb{x}, \mb{y}) + \frac{1}{2} \| \mb{x} - \mb{x}^k - \mb{x}^{k+1/2} \|_2^2 \;,
with :math:`k` corresponding to the index of the iteration, which translates to an index of the stage of the network, :math:`f(A \mb{x}, \mb{y})` a fidelity term, usually an :math:`\ell_2` norm, and :math:`\mb{x}^{k+1/2}` a regularization representing :math:`\mathrm{prox}_r (\mb{x}^k)` and usually implemented as a convolutional neural network (CNN). This proximal map representation is used when minimization problem :eq:`eq:odp_prox` can be solved in a computationally efficient manner.
:class:`.ODPProxDnBlock` uses this formulation to solve a denoising problem, which, according to :cite:`diamond-2018-odp`, can be solved by
.. math::
\mb{x}^{k+1} = (\alpha_k \, \mb{y} + \mb{x}^k + \mb{x}^{k+1/2}) \, / \, (\alpha_k + 1) \;,
where :math:`A` corresponds to the identity operator and is therefore omitted, :math:`\mb{y}` is the noisy signal, :math:`\alpha_k > 0` is a learned stage-wise parameter weighting the contribution of the fidelity term and :math:`\mb{x}^k + \mb{x}^{k+1/2}` is the regularization, usually represented by a residual CNN.
:class:`.ODPProxDblrBlock` uses this formulation to solve a deblurring problem, which, according to :cite:`diamond-2018-odp`, can be solved by
.. math::
\mb{x}^{k+1} = \mathcal{F}^{-1} \mathrm{diag} (\alpha_k | \mathcal{F}(K)|^2 + 1 )^{-1} \mathcal{F} \, (\alpha_k K^T * \mb{y} + \mb{x}^k + \mb{x}^{k+1/2}) \;,
where :math:`A` is the blurring operator, :math:`K` is the blurring kernel, :math:`\mb{y}` is the blurred signal, :math:`\mathcal{F}` is the DFT, :math:`\alpha_k > 0` is a learned stage-wise parameter weighting the contribution of the fidelity term and :math:`\mb{x}^k + \mb{x}^{k+1/2}` is the regularization represented by a residual CNN.
Gradient Descent
^^^^^^^^^^^^^^^^
When the solution of the optimization problem in :eq:`eq:odp_prox` can not be simply represented by an analytical step, a formulation based on a gradient descent iteration is preferred. This yields
.. math::
\mb{x}^{k+1} = \mb{x}^k + \mb{x}^{k+1/2} - \alpha_k \, A^T \nabla_x \, f(A \mb{x}^k, \mb{y}) \;,
where :math:`\mb{x}^{k+1/2}` represents :math:`\nabla r(\mb{x}^k)`.
:class:`.ODPGrDescBlock` uses this formulation to solve a generic problem with :math:`\ell_2` fidelity as
.. math::
\mb{x}^{k+1} = \mb{x}^k + \mb{x}^{k+1/2} - \alpha_k \, A^T (A \mb{x} - \mb{y}) \;,
with :math:`\mb{y}` the measured signal and :math:`\mb{x} + \mb{x}^{k+1/2}` a residual CNN.
MoDL
----
The model-based deep learning (MoDL) :cite:`aggarwal-2019-modl`, implemented as :class:`.MoDLNet`, is used to solve inverse problems in imaging also by adapting classical iterative methods into an end-to-end deep learning framework, but, in contrast to ODP, it solves the optimization problem
.. math::
\argmin_{\mb{x}} \; \| A \mb{x} - \mb{y}\|_2^2 + \lambda \, \| \mb{x} - \mathrm{D}_w(\mb{x})\|_2^2 \;,
by directly computing the update
.. math::
\mb{x}^{k+1} = (A^T A + \lambda \, I)^{-1} (A^T \mb{y} + \lambda \, \mb{z}^k) \;,
via conjugate gradient. The regularization :math:`\mb{z}^k = \mathrm{D}_w(\mb{x}^{k})` incorporates prior information, usually in the form of a denoiser model. In this case, the denoiser :math:`\mathrm{D}_w` is shared between all the stages of the network requiring relatively less memory than other unrolling methods. This also allows for deploying a different number of iterations in testing than the ones used in training.
================================================
FILE: docs/source/include/operator.rst
================================================
Operators
=========
An operator is a map from :math:`\mathbb{R}^n` or :math:`\mathbb{C}^n`
to :math:`\mathbb{R}^m` or :math:`\mathbb{C}^m`. In SCICO, operators
are primarily used to represent imaging systems and provide
regularization. SCICO operators are represented by instances of the
:class:`.Operator` class.
SCICO :class:`.Operator` objects extend the notion of "shape" and
"size" from the usual NumPy ``ndarray`` class. Each
:class:`.Operator` object has an ``input_shape`` and ``output_shape``;
these shapes can be either tuples or a tuple of tuples (in the case of
a :class:`.BlockArray`). The ``matrix_shape`` attribute describes the
shape of the :class:`.LinearOperator` if it were to act on vectorized,
or flattened, inputs.
For example, consider a two-dimensional array :math:`\mb{x} \in
\mathbb{R}^{n \times m}`. We compute the discrete differences of
:math:`\mb{x}` in the horizontal and vertical directions, generating
two new arrays: :math:`\mb{x}_h \in \mathbb{R}^{n \times (m-1)}` and
:math:`\mb{x}_v \in \mathbb{R}^{(n-1) \times m}`. We represent this
linear operator by :math:`\mb{A} : \mathbb{R}^{n \times m} \to
\mathbb{R}^{n \times (m-1)} \otimes \mathbb{R}^{(n-1) \times m}`. In
SCICO, this linear operator will return a :class:`.BlockArray` with
the horizontal and vertical differences stored as blocks. Letting
:math:`y = \mb{A} x`, we have ``y.shape = ((n, m-1), (n-1, m))`` and
::
A.input_shape = (n, m)
A.output_shape = ((n, m-1), (n-1, m)], (n, m))
A.shape = ( ((n, m-1), (n-1, m)), (n, m)) # (output_shape, input_shape)
A.input_size = n*m
A.output_size = n*(n-1)*m*(m-1)
A.matrix_shape = (n*(n-1)*m*(m-1), n*m) # (output_size, input_size)
Operator Calculus
-----------------
SCICO supports a variety of operator calculus rules, allowing new
operators to be defined in terms of old ones. The following table
summarizes the available operations.
+----------------+-----------------+
| Operation | Result |
+----------------+-----------------+
| ``(A+B)(x)`` | ``A(x) + B(x)`` |
+----------------+-----------------+
| ``(A-B)(x)`` | ``A(x) - B(x)`` |
+----------------+-----------------+
| ``(c * A)(x)`` | ``c * A(x)`` |
+----------------+-----------------+
| ``(A/c)(x)`` | ``A(x)/c`` |
+----------------+-----------------+
| ``(-A)(x)`` | ``-A(x)`` |
+----------------+-----------------+
| ``A(B)(x)`` | ``A(B(x))`` |
+----------------+-----------------+
| ``A(B)`` | ``Operator`` |
+----------------+-----------------+
Defining a New Operator
-----------------------
To define a new operator, pass a callable to the :class:`.Operator`
constructor:
::
A = Operator(input_shape=(32,), eval_fn = lambda x: 2 * x)
Or use subclassing:
::
>>> from scico.operator import Operator
>>> class MyOp(Operator):
...
... def _eval(self, x):
... return 2 * x
>>> A = MyOp(input_shape=(32,))
At a minimum, the ``_eval`` function must be overridden. If either
``output_shape`` or ``output_dtype`` are unspecified, they are
determined by evaluating the operator on an input of appropriate shape
and dtype.
Linear Operators
================
Linear operators are those for which
.. math::
H(a \mb{x} + b \mb{y}) = a H(\mb{x}) + b H(\mb{y}) \;.
SCICO represents linear operators as instances of the class
:class:`.LinearOperator`. While finite-dimensional linear operators
can always be associated with a matrix, it is often useful to
represent them in a matrix-free manner. Most of SCICO's linear
operators are implemented matrix-free.
Using a LinearOperator
----------------------
We implement two ways to evaluate a :class:`.LinearOperator`. The
first is using standard callable syntax: ``A(x)``. The second mimics
the NumPy matrix multiplication syntax: ``A @ x``. Both methods
perform shape and type checks to validate the input before ultimately
either calling `A._eval` or generating a new :class:`.LinearOperator`.
For linear operators that map real-valued inputs to real-valued
outputs, there are two ways to apply the adjoint: ``A.adj(y)`` and
``A.T @ y``.
For complex-valued linear operators, there are three ways to apply the
adjoint ``A.adj(y)``, ``A.H @ y``, and ``A.conj().T @ y``. Note that
in this case, ``A.T`` returns the non-conjugated transpose of the
:class:`.LinearOperator`.
While the cost of evaluating the linear operator is virtually
identical for ``A(x)`` and ``A @ x``, the ``A.H`` and ``A.conj().T``
methods are somewhat slower; especially the latter. This is because
two intermediate linear operators must be created before the function
is evaluated. Evaluating ``A.conj().T @ y`` is equivalent to:
::
def f(y):
B = A.conj() # New LinearOperator #1
C = B.T # New LinearOperator #2
return C @ y
**Note**: the speed differences between these methods vanish if
applied inside of a jit-ed function. For instance:
::
f = jax.jit(lambda x: A.conj().T @ x)
+------------------+-----------------+
| Public Method | Private Method |
+------------------+-----------------+
| ``__call__`` | ``._eval`` |
+------------------+-----------------+
| ``adj`` | ``._adj`` |
+------------------+-----------------+
| ``gram`` | ``._gram`` |
+------------------+-----------------+
The public methods perform shape and type checking to validate the
input before either calling the corresponding private method or
returning a composite LinearOperator.
Linear Operator Calculus
------------------------
SCICO supports several linear operator calculus rules.
Given
``A`` and ``B`` of class :class:`.LinearOperator` and of appropriate shape,
``x`` an array of appropriate shape,
``c`` a scalar, and
``O`` an :class:`.Operator`,
we have
+----------------+----------------------------+
| Operation | Result |
+----------------+----------------------------+
| ``(A+B)(x)`` | ``A(x) + B(x)`` |
+----------------+----------------------------+
| ``(A-B)(x)`` | ``A(x) - B(x)`` |
+----------------+----------------------------+
| ``(c * A)(x)`` | ``c * A(x)`` |
+----------------+----------------------------+
| ``(A/c)(x)`` | ``A(x)/c`` |
+----------------+----------------------------+
| ``(-A)(x)`` | ``-A(x)`` |
+----------------+----------------------------+
| ``(A@B)(x)`` | ``A@B@x`` |
+----------------+----------------------------+
| ``A @ B`` | ``ComposedLinearOperator`` |
+----------------+----------------------------+
| ``A @ O`` | ``Operator`` |
+----------------+----------------------------+
| ``O(A)`` | ``Operator`` |
+----------------+----------------------------+
Defining a New Linear Operator
------------------------------
To define a new linear operator, pass a callable to the
:class:`.LinearOperator` constructor
::
>>> from scico.linop import LinearOperator
>>> A = LinearOperator(input_shape=(32,),
... eval_fn = lambda x: 2 * x)
Or, use subclassing:
::
>>> class MyLinearOperator(LinearOperator):
... def _eval(self, x):
... return 2 * x
>>> A = MyLinearOperator(input_shape=(32,))
At a minimum, the ``_eval`` method must be overridden. If the
``_adj`` method is not overriden, the adjoint is determined using
:func:`scico.linear_adjoint`. If either ``output_shape`` or
``output_dtype`` are unspecified, they are determined by evaluating
the Operator on an input of appropriate shape and dtype.
🔪 Sharp Edges 🔪
------------------
Strict Types in Adjoint
^^^^^^^^^^^^^^^^^^^^^^^
SCICO silently promotes real types to complex types in forward
application, but enforces strict type checking in the adjoint. This
is due to the strict type-safe nature of jax adjoints.
LinearOperators from External Code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
External code may be wrapped as a subclass of :class:`.Operator` or
:class:`.LinearOperator` and used in SCICO optimization routines;
however this process can be complicated and error-prone. As a
starting point, look at the source for
:class:`.radon_svmbir.TomographicProjector` or
:class:`.radon_astra.TomographicProjector` and the JAX documentation
for the `vector-jacobian product
`_
and `custom VJP rules
`_.
================================================
FILE: docs/source/include/optimizer.rst
================================================
.. _optimizer:
Optimization Algorithms
=======================
ADMM
----
The Alternating Direction Method of Multipliers (ADMM)
:cite:`glowinski-1975-approximation` :cite:`gabay-1976-dual` is an
algorithm for minimizing problems of the form
.. math::
:label: eq:admm_prob
\argmin_{\mb{x}, \mb{z}} \; f(\mb{x}) + g(\mb{z}) \; \text{such that}
\; \acute{A} \mb{x} + \acute{B} \mb{z} = \mb{c} \;,
where :math:`f` and :math:`g` are convex (but not necessarily smooth)
functionals, :math:`\acute{A}` and :math:`\acute{B}` are linear operators,
and :math:`\mb{c}` is a constant vector. (For a thorough introduction and
overview, see :cite:`boyd-2010-distributed`.)
The SCICO ADMM solver, :class:`.ADMM`, solves problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + \sum_{i=1}^N g_i(C_i \mb{x}) \;,
where :math:`f` and the :math:`g_i` are instances of :class:`.Functional`,
and the :math:`C_i` are :class:`.LinearOperator`, by defining
.. math::
g(\mb{z}) = \sum_{i=1}^N g_i(\mb{z}_i) \qquad \mb{z}_i = C_i \mb{x}
in :eq:`eq:admm_prob`, corresponding to defining
.. math::
\acute{A} = \left( \begin{array}{c} C_0 \\ C_1 \\ C_2 \\
\vdots \end{array} \right) \quad
\acute{B} = \left( \begin{array}{cccc}
-I & 0 & 0 & \ldots \\
0 & -I & 0 & \ldots \\
0 & 0 & -I & \ldots \\
\vdots & \vdots & \vdots & \ddots
\end{array} \right) \quad
\mb{z} = \left( \begin{array}{c} \mb{z}_0 \\ \mb{z}_1 \\ \mb{z}_2 \\
\vdots \end{array} \right) \quad
\mb{c} = \left( \begin{array}{c} 0 \\ 0 \\ 0 \\
\vdots \end{array} \right) \;.
In :class:`.ADMM`, :math:`f` is a :class:`.Functional`, typically a
:class:`.Loss`, corresponding to the forward model of an imaging
problem, and the :math:`g_i` are :class:`.Functional`, typically
corresponding to a regularization term or constraint. Each of the
:math:`g_i` must have a proximal operator defined. It is also possible
to set ``f = None``, which corresponds to defining :math:`f = 0`,
i.e. the zero function.
Subproblem Solvers
^^^^^^^^^^^^^^^^^^
The most computational expensive component of the ADMM iterations is typically
the :math:`\mb{x}`-update,
.. math::
:label: eq:admm_x_step
\argmin_{\mb{x}} \; f(\mb{x}) + \sum_i \frac{\rho_i}{2}
\norm{\mb{z}^{(k)}_i - \mb{u}^{(k)}_i - C_i \mb{x}}_2^2 \;.
The available solvers for this problem are:
* :class:`.admm.GenericSubproblemSolver`
This is the default subproblem solver as it is applicable in all cases. It
it is only suitable for relatively small-scale problems as it makes use of
:func:`.solver.minimize`, which wraps :func:`scipy.optimize.minimize`.
* :class:`.admm.LinearSubproblemSolver`
This subproblem solver can be used when :math:`f` takes the form
:math:`\norm{\mb{A} \mb{x} - \mb{y}}^2_W`. It makes use of the conjugate
gradient method, and is significantly more efficient than
:class:`.admm.GenericSubproblemSolver` when it can be used.
* :class:`.admm.MatrixSubproblemSolver`
This subproblem solver can be used when :math:`f` takes the form
:math:`\norm{\mb{A} \mb{x} - \mb{y}}^2_W`, and :math:`A` and all of the
:math:`C_i` are diagonal (:class:`.Diagonal`) or matrix operators
(:class:`MatrixOperator`). It exploits a pre-computed matrix factorization
for a significantly more efficient solution than conjugate gradient.
* :class:`.admm.CircularConvolveSolver`
This subproblem solver can be used when :math:`f` takes the form
:math:`\norm{\mb{A} \mb{x} - \mb{y}}^2_W` and :math:`\mb{A}` and all
the :math:`C_i` s are circulant (i.e., diagonalized by the DFT).
* :class:`.admm.FBlockCircularConvolveSolver` and :class:`.admm.G0BlockCircularConvolveSolver`
These subproblem solvers can be used when the primary linear operator
is block-circulant (i.e. an operator with blocks that are diagonalied
by the DFT).
For more details of these solvers and how to specify them, see the API
reference page for :mod:`scico.optimize.admm`.
Proximal ADMM
-------------
Proximal ADMM :cite:`deng-2015-global` is an algorithm for solving
problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(\mb{z}) \;
\text{such that}\; A \mb{x} + B \mb{z} = \mb{c} \;,
where :math:`f` and :math:`g` are are convex (but not necessarily
smooth) functionals and :math:`A` and :math:`B` are linear
operators. Although convergence per iteration is typically somewhat
worse than that of ADMM, the iterations can be much cheaper than that
of ADMM, giving Proximal ADMM competitive time convergence
performance.
The SCICO Proximal ADMM solver, :class:`.ProximalADMM`, requires
:math:`f` and :math:`g` to be instances of :class:`.Functional`, and
to have a proximal operator defined (:meth:`.Functional.prox`), and
:math:`A` and :math:`B` are required to be an instance of
:class:`.LinearOperator`.
Non-Linear Proximal ADMM
------------------------
Non-Linear Proximal ADMM :cite:`benning-2016-preconditioned` is an
algorithm for solving problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(\mb{z}) \;
\text{such that}\; H(\mb{x}, \mb{z}) = 0 \;,
where :math:`f` and :math:`g` are are convex (but not necessarily
smooth) functionals and :math:`H` is a function of two vector variables.
The SCICO Non-Linear Proximal ADMM solver, :class:`.NonLinearPADMM`, requires
:math:`f` and :math:`g` to be instances of :class:`.Functional`, and
to have a proximal operator defined (:meth:`.Functional.prox`), and
:math:`H` is required to be an instance of :class:`.Function`.
Linearized ADMM
---------------
Linearized ADMM :cite:`yang-2012-linearized`
:cite:`parikh-2014-proximal` (Sec. 4.4.2) is an algorithm for solving
problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(C \mb{x}) \;,
where :math:`f` and :math:`g` are are convex (but not necessarily
smooth) functionals. Although convergence per iteration is typically
significantly worse than that of ADMM, the :math:`\mb{x}`-update, can
be much cheaper than that of ADMM, giving Linearized ADMM competitive
time convergence performance.
The SCICO Linearized ADMM solver, :class:`.LinearizedADMM`,
requires :math:`f` and :math:`g` to be instances of :class:`.Functional`,
and to have a proximal operator defined (:meth:`.Functional.prox`), and
:math:`C` is required to be an instance of :class:`.LinearOperator`.
PDHG
----
The Primal–Dual Hybrid Gradient (PDHG) algorithm
:cite:`esser-2010-general` :cite:`chambolle-2010-firstorder`
:cite:`pock-2011-diagonal` solves problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(C \mb{x}) \;,
where :math:`f` and :math:`g` are are convex (but not necessarily smooth)
functionals. The algorithm has similar advantages over ADMM to those of Linearized ADMM, but typically exhibits better convergence properties.
The SCICO PDHG solver, :class:`.PDHG`,
requires :math:`f` and :math:`g` to be instances of :class:`.Functional`,
and to have a proximal operator defined (:meth:`.Functional.prox`), and
:math:`C` is required to be an instance of :class:`.Operator` or :class:`.LinearOperator`.
PGM
---
The Proximal Gradient Method (PGM) :cite:`daubechies-2004-iterative`
:cite:`beck-2010-gradient` and Accelerated Proximal Gradient Method
(AcceleratedPGM) :cite:`beck-2009-fast` are algorithms for minimizing
problems of the form
.. math::
\argmin_{\mb{x}} f(\mb{x}) + g(\mb{x}) \;,
where :math:`g` is convex and :math:`f` is smooth and convex. The
corresponding SCICO solvers are :class:`.PGM` and :class:`.AcceleratedPGM`
respectively. In most cases :class:`.AcceleratedPGM` is expected to provide
faster convergence. In both of these classes, :math:`f` and :math:`g` are
both of type :class:`.Functional`, where :math:`f` must be differentiable,
and :math:`g` must have a proximal operator defined.
While ADMM provides significantly more flexibility than PGM, and often
converges faster, the latter is preferred when solving the ADMM
:math:`\mb{x}`-step is very computationally expensive, such as in the case of
:math:`f(\mb{x}) = \norm{\mb{A} \mb{x} - \mb{y}}^2_W` where :math:`A` is
large and does not have any special structure that would allow an efficient
solution of :eq:`eq:admm_x_step`.
Step Size Options
^^^^^^^^^^^^^^^^^
The step size (usually referred to in terms of its reciprocal,
:math:`L`) for the gradient descent in :class:`PGM` can be adapted via
Barzilai-Borwein methods (also called spectral methods) and iterative
line search methods.
The available step size policy classes are:
* :class:`.BBStepSize`
This implements the step size adaptation based on the Barzilai-Borwein
method :cite:`barzilai-1988-stepsize`. The step size :math:`\alpha` is
estimated as
.. math::
\mb{\Delta x} = \mb{x}_k - \mb{x}_{k-1} \; \\
\mb{\Delta g} = \nabla f(\mb{x}_k) - \nabla f (\mb{x}_{k-1}) \; \\
\alpha = \frac{\mb{\Delta x}^T \mb{\Delta g}}{\mb{\Delta g}^T
\mb{\Delta g}} \;.
Since the PGM solver uses the reciprocal of the step size, the value
:math:`L = 1 / \alpha` is returned.
* :class:`.AdaptiveBBStepSize`
This implements the adaptive Barzilai-Borwein method as introduced in
:cite:`zhou-2006-adaptive`. The adaptive step size rule computes
.. math::
\mb{\Delta x} = \mb{x}_k - \mb{x}_{k-1} \; \\
\mb{\Delta g} = \nabla f(\mb{x}_k) - \nabla f (\mb{x}_{k-1}) \; \\
\alpha^{\mathrm{BB1}} = \frac{\mb{\Delta x}^T \mb{\Delta x}}
{\mb{\Delta x}^T \mb{\Delta g}} \; \\
\alpha^{\mathrm{BB2}} = \frac{\mb{\Delta x}^T \mb{\Delta g}}
{\mb{\Delta g}^T \mb{\Delta g}} \;.
The determination of the new step size is made via the rule
.. math::
\alpha = \left\{ \begin{array}{ll} \alpha^{\mathrm{BB2}} &
\mathrm{~if~} \alpha^{\mathrm{BB2}} / \alpha^{\mathrm{BB1}}
< \kappa \; \\
\alpha^{\mathrm{BB1}} & \mathrm{~otherwise} \end{array}
\right . \;,
with :math:`\kappa \in (0, 1)`.
Since the PGM solver uses the reciprocal of the step size, the value
:math:`L = 1 / \alpha` is returned.
* :class:`.LineSearchStepSize`
This implements the line search strategy described in :cite:`beck-2009-fast`.
This strategy estimates :math:`L` such that
:math:`f(\mb{x}) \leq \hat{f}_{L}(\mb{x})` is satisfied with
:math:`\hat{f}_{L}` a quadratic approximation to :math:`f` defined as
.. math::
\hat{f}_{L}(\mb{x}, \mb{y}) = f(\mb{y}) + \nabla f(\mb{y})^H
(\mb{x} - \mb{y}) + \frac{L}{2} \left\| \mb{x} - \mb{y}
\right\|_2^2 \;,
with :math:`\mb{x}` the potential new update and :math:`\mb{y}` the
current solution or current extrapolation (if using :class:`.AcceleratedPGM`).
* :class:`.RobustLineSearchStepSize`
This implements the robust line search strategy described in
:cite:`florea-2017-robust`. This strategy estimates :math:`L` such that
:math:`f(\mb{x}) \leq \hat{f}_{L}(\mb{x})` is satisfied with
:math:`\hat{f}_{L}` a quadratic approximation to :math:`f` defined as
.. math::
\hat{f}_{L}(\mb{x}, \mb{y}) = f(\mb{y}) + \nabla f(\mb{y})^H
(\mb{x} - \mb{y}) + \frac{L}{2} \left\| \mb{x} - \mb{y} \right\|_2^2 \;,
with :math:`\mb{x}` the potential new update and :math:`\mb{y}` the
auxiliary extrapolation state. Note that this should only be used
with :class:`.AcceleratedPGM`.
For more details of these step size managers and how to specify them, see
the API reference page for :mod:`scico.optimize.pgm`.
================================================
FILE: docs/source/index.rst
================================================
SCICO Documentation
===================
.. toctree::
:maxdepth: 2
:caption: User Documentation
overview
inverse
advantages
install
classes
notes
examples
API Reference <_autosummary/scico.rst>
zreferences
.. toctree::
:maxdepth: 2
:caption: Developer Documentation
team
contributing
style
Indices
=======
* :ref:`genindex`
* :ref:`modindex`
================================================
FILE: docs/source/install.rst
================================================
.. _installing:
Installing SCICO
================
SCICO requires Python version 3.8 or later. (Version 3.12 is
recommended as it is the version under which SCICO is tested in GitHub
continuous integration, and since the most recent versions of JAX require
version 3.10 or later.) SCICO is supported on both Linux and
MacOS, but is not currently supported on Windows due to the limited
support for ``jaxlib`` on Windows. However, Windows users can use
SCICO via the `Windows Subsystem for Linux
`_ (WSL). Guides
exist for using WSL with
`CPU only `_
and with
`GPU support `_.
While not required, installation of SCICO and its dependencies within a
`Conda `_
environment is recommended.
`Scripts `_
are provided for creating a
`miniconda `_
installation and an environment including all primary SCICO dependencies
as well as dependencies for usage example, testing, and building the
documentation.
From PyPI
---------
The simplest way to install the most recent release of SCICO from
`PyPI `_ is
::
pip install scico
which will install SCICO and its primary dependencies. If the additional
dependencies for the example scripts are also desired, it can instead be
installed using
::
pip install scico[examples]
Note, however, that since the ``astra-toolbox`` package available from
PyPI is not straightforward to install (it has numerous build requirements
that are not specified as package dependencies), it is recommended to
first install this package via conda
::
conda install astra-toolbox
From conda-forge
----------------
SCICO can also be installed from `conda-forge `_
::
conda install -c conda-forge "scico>0.0.5"
where the version constraint is required to avoid installation of an old
package with broken dependencies.
Note, however, that installation from conda forge is only possible on a Linux
platform since there is no conda package for the secondary dependency
``tensorstore`` under MacOS. There are also complications on Linux platforms
with Python versions 3.9 or earlier due to the automatic installation of a
version of secondary dependency ``etils`` that does not support Python versions
earlier than 3.10. This can be rectified by
::
conda install etils=1.5.1
The most recent SCICO conda forge package also includes dependencies for
the example scripts, except for ``bm3d``, ``bm4d``, and
``colour_demosaicing``, for which conda packages are not available. These
can be installed from PyPI
::
pip install bm3d bm4d colour_demosaicing
From GitHub
-----------
The development version of SCICO can be downloaded from the `GitHub repo
`_. Note that, since the SCICO repo has
a submodule, it should be cloned via the command
::
git clone --recurse-submodules git@github.com:lanl/scico.git
Install using the commands
::
cd scico
pip install -r requirements.txt
pip install -e .
If a clone of the SCICO repository is not needed, it is simpler to
install directly using ``pip``
::
pip install git+https://github.com/lanl/scico
GPU Support
-----------
The instructions above install a CPU-only version of SCICO. To install
a version with GPU support:
1. Follow the CPU-only instructions, above
2. Install the version of jaxlib with GPU support, as described in the `JAX installation
instructions `_.
In the simplest case, the appropriate command is
::
pip install --upgrade "jax[cuda12]"
for CUDA 12, but it may be necessary to explicitly specify the
``jaxlib`` version if the most recent release is not yet supported
by SCICO (as specified in the ``requirements.txt`` file).
The script
`misc/gpu/envinfo.py `_
in the source distribution is provided as an aid to debugging GPU support
issues. The script
`misc/gpu/availgpu.py `_
can be used to automatically recommend a setting of the CUDA_VISIBLE_DEVICES
environment variable that excludes GPUs that are already in use.
Additional Dependencies
-----------------------
See :ref:`example_depend` for instructions on installing dependencies
related to the examples.
For Developers
--------------
See :ref:`scico_dev_contributing` for instructions on installing a
version of SCICO suitable for development.
================================================
FILE: docs/source/inverse.rst
================================================
Inverse Problems
================
In traditional imaging, the burden of image formation is placed on
physical components, such as a lens, with the resulting image being
taken from the sensor with minimal processing. In computational
imaging, in contrast, the burden of image formation is shared with or
shifted to computation, with the resulting image typically being very
different from the measured data. Common examples of computational
imaging include demosaicing in consumer cameras, computed tomography
and magnetic resonance imaging in medicine, and synthetic aperture
radar in remote sensing. This is an active and growing area of
research, and many of these problems have common properties that could
be supported by shared implementations of solution components.
The goal of SCICO is to provide a general research tool for
computational imaging, with a particular focus on scientific imaging
applications, which are particularly underrepresented in the existing
range of open-source packages in this area. While a number of other
packages overlap somewhat in functionality with SCICO, only a few
support execution of the same code on both CPU and GPU devices, and we
are not aware of any that support just-in-time compilation and
automatic gradient computation, which is invaluable in computational
imaging. SCICO provides all three of these valuable features (subject
to some :ref:`caveats `) by being built on top of `JAX
`__ rather than `NumPy
`__.
The remainder of this section outlines the steps involved in solving
an inverse problem, and shows how each concept maps to a component of
SCICO. More detail on the main classes involved in setting up and
solving an inverse problem can be found in :ref:`classes`.
Forward Modeling
----------------
In order to solve a computational imaging problem we need to know how
the image we wish to reconstruct, :math:`\mathbf{x}`, is related to the
data that we can measure, :math:`\mathbf{y}`. This is represented via a
model of the measurement process,
.. math:: \mathbf{y} = A(\mathbf{x}) \,.
SCICO provides the :class:`.Operator` and :class:`.LinearOperator`
classes, which may be subclassed by users, in order to implement the
forward operator, :math:`A`. It also has several built-in operators,
most of which are linear, e.g., finite convolutions, discrete Fourier
transforms, optical propagators, Abel transforms, and X-ray transforms
(the same as Radon transforms in 2D). For example,
.. code:: python
input_shape = (512, 512)
angles = np.linspace(0, 2 * np.pi, 180, endpoint=False)
channels = 512
A = scico.linop.xray.svmbir.XRayTransform(input_shape, angles, channels)
defines a tomographic projection operator.
A significant advantage of SCICO being built on top of `JAX
`__ is that the adjoints of
linear operators, which can be quite time consuming to implement even
when the operator itself is straightforward, are computed
automatically by exploiting the automatic differentation features of
`JAX `__. If :code:`A` is a
:class:`.LinearOperator`, then its adjoint is simply :code:`A.T` for
real transforms and :code:`A.H` for complex transforms. Likewise,
Jacobian-vector products can be automatically computed for non-linear
operators, allowing for simple linearization and gradient
calculations.
SCICO operators can be composed to construct new operators. (If both
operands are linear, then the result is also linear.) For example, if
:code:`A` and :code:`B` have been defined as distinct linear
operators, then
.. code:: python
C = B @ A
defines a new linear operator :code:`C` that first applies operator
:code:`A` and then applies operator :code:`B` to the result
(i.e. :math:`C = B A` in math notation). This operator algebra can be
used to build complicated forward operators from simpler building
blocks.
SCICO also handles cases where either the image we want to
reconstruct, :math:`\mb{x}`, or its measurements, :math:`\mb{y}`, do
not fit neatly into a multi-dimensional array. This is achieved via
:class:`.BlockArray` objects, which consist of a :class:`list` of
multi-dimensional array *blocks*. A :class:`.BlockArray` differs from
a :class:`list` in that, whenever possible, :class:`.BlockArray`
properties and methods (including unary and binary operators like
``+``, ``-``, ``*``, …) automatically map along the blocks, returning
another :class:`.BlockArray` or :class:`tuple` as appropriate. For
example, consider a system that measures the column sums and row sums
of an image. If the input image has shape :math:`M \times N`, the
resulting measurement will have shape :math:`M + N`, which is awkward
to represent as a multi-dimensional array. In SCICO, we can represent
this operator by
.. code:: python
input_shape = (130, 50)
H0 = scico.linop.Sum(input_shape, axis=0)
H1 = scico.linop.Sum(input_shape, axis=1)
H = scico.linop.VerticalStack((H0, H1))
The result of applying ``H`` to an image with shape ``(130, 50)`` is a
:class:`.BlockArray` with shape ``((50,), (130,))``. This result is
compatible with the rest of SCICO and may be used, e.g., as the input
of other operators.
Inverse Problem Formulation
---------------------------
In order to estimate the image from the measured data, we need to solve
an *inverse problem*. In its simplest form, the solution to such an
inverse problem can be expressed as the optimization problem
.. math:: \hat{\mb{x}} = \mathop{\mathrm{arg\,min}}_{\mb{x}} f( \mb{x} ) \,,
where :math:`\mb{x}` is the unknown image and :math:`\hat{\mb{x}}` is
the recovered image. A common choice of :math:`f` is
.. math:: f(\mb{x}) = (1/2) \| A(\mb{x}) - \mb{y} \|_2^2 \,,
where :math:`\mb{y}` is the measured data and :math:`A` is the
forward operator; in this case the minimization problem is a least
squares problem.
In SCICO, the :mod:`.functional` module provides implementations of common
functionals such as :math:`\ell_2` and :math:`\ell_1` norms. The
:mod:`.loss` module is used to implement a special type of functional
.. math:: f(\mb{x}) = \alpha l(A(\mb{x}),\mb{y}) \,,
where :math:`\alpha` is a scaling parameter and :math:`l(\cdot)` is
another functional. The SCICO :mod:`.loss` module contains a variety
of loss functionals that are commonly used in computational
imaging. For example, the squared :math:`\ell_2` loss written above
for a forward operator, :math:`A`, can be defined in SCICO using the
code:
.. code:: python
f = scico.loss.SquaredL2Loss(y=y, A=A)
The difficulty of the inverse problem depends on the amount of noise in
the measured data and the properties of the forward operator. In
particular, if :math:`A` is a linear operator, then the difficulty of
the inverse problem depends significantly on the condition number of
:math:`A`, since a large condition number implies that large changes in
:math:`\mb{x}` can correspond to small changes in
:math:`\mb{y}`, making it difficult to estimate :math:`\mb{x}`
from :math:`\mb{y}`. When there is a significant amount of
measurement noise or ill-conditioning of :math:`A`, the standard
approach to resolve the limitations in the information available from
the measured data is to introduce a *prior model* of the solution space,
which is typically achieved by adding a *regularization term* to the
data fidelity term, resulting in the optimization problem
.. math:: \hat{\mb{x}} = \mathop{\mathrm{arg\,min}}_{\mb{x}} f(\mb{x}) + g(C (\mb{x})) \,,
where the functional :math:`g(C(\cdot))` is designed to increase the
cost for solutions that are considered less likely or desirable, based
on prior knowledge of the properties of the solution space. A common
choice of :math:`g(C(\cdot))` is the total variation norm
.. math:: g(\mb{x}) = \lambda \| C \mb{x} \|_{2,1} \,,
where :math:`\lambda` is a scalar controlling the regularization
strength, :math:`C` is a linear operator that computes the spatial
gradients of its argument, and :math:`\| \cdot \|_{2,1}` denotes the
:math:`\ell_{2,1}` norm, which promotes group sparsity. Use of this
functional as a regularization term corresponds to the assumption that
the images of interest are piecewise constant. In SCICO, we can
represent this regularization functional using a built-in linear
operator and a member of the :mod:`.functional` module:
.. code:: python
C = scico.linop.FiniteDifference(A.input_shape, append=0)
λ = 1.0e-1
g = λ * scico.functional.L21Norm()
Computing the value of the regularizer then closely matches the math:
:code:`g(C(x))`.
Finally, the overall objective function needs to be optimized. One of
the primary goals of SCICO is to make the solution of such problems
accessible to application domain scientists with limited expertise in
computational imaging, providing infrastructure for solving this type of
problem efficiently, without the need for the user to implement complex
algorithms.
Solvers
-------
Once an inverse problem has been specified using the above components,
the resulting functional must be minimized in order to solve the
problem. SCICO provides a number of optimization algorithms for
addressing a wide range of problems. These optimization algorithms
belong to two distinct categories.
Basic Solvers
~~~~~~~~~~~~~
The :mod:`scico.solver` module provides a number of functions for
solving linear systems and simple optimization problems, some of which
are useful as subproblem solvers within the proximal algorithms
described in the following section. It also provides an interface to
functions in :mod:`scipy.optimize`, supporting their use with
multi-dimensional arrays and scico :class:`.Functional` objects. These
algorithms are useful both as subproblem solvers within the proximal
algorithms described below, as well as for direct solution of
higher-level problems.
For example,
.. code:: python
f = scico.loss.PoissonLoss(y=y, A=A)
method = 'BFGS' # or any method available for scipy.optimize.minimize
x0 = scico.numpy.ones(A.input_shape)
res = scico.solver.minimize(f, x0=x0, method=method)
x_hat = res.x
defines a Poisson objective function and minimizes it using the BFGS
:cite:`nocedal-2006-numerical` algorithm.
Proximal Algorithms
~~~~~~~~~~~~~~~~~~~
The :mod:`scico.optimize` sub-package provides a set of *proximal
algorithms* :cite:`parikh-2014-proximal` that have proven to be useful
for solving imaging inverse problems. The common feature of these
algorithms is their exploitation of the *proximal operator*
:cite:`beck-2017-first` (Ch. 6), of the components of the functions
that they minimize.
**ADMM** The most flexible of the proximal algorithms supported by SCICO
is the alternating direction method of multipliers (ADMM)
:cite:`glowinski-1975-approximation` :cite:`gabay-1976-dual`
:cite:`boyd-2010-distributed`, which supports solving problems of the form
.. math:: \mathop{\mathrm{arg\,min}}_{\mb{x}} \; f(\mb{x}) + \sum_{i=1}^N g_i(C_i \mb{x}) \,.
When :math:`f(\cdot)` is an instance of ``scico.loss.SquaredL2Loss``,
i.e.,
.. math:: f(\mb{x}) = (1/2) \| A \mb{x} - \mb{y} \|_2^2 \,,
for linear operator :math:`A` and constant vector :math:`\mb{y}`,
the primary computational cost of the algorithm is typically in solving
a linear system involving a weighted sum of :math:`A^\top A` and the
:math:`C_i^\top C_i`, assuming that the proximal operators of the
functionals :math:`g_i(\cdot)` can be computed efficiently. This linear
system can also be solved efficiently when :math:`A` and all of the
:math:`C_i` are either identity operators or circular convolutions.
**Proximal ADMM** Proximal ADMM :cite:`deng-2015-global` solves problems of
the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(\mb{z}) \;
\text{such that}\; A \mb{x} + B \mb{z} = \mb{c} \;,
where :math:`A` and :math:`B` are linear operators. There is also a non-linear
PADMM solver :cite:`benning-2016-preconditioned` for problems of the form
.. math::
\argmin_{\mb{x}} \; f(\mb{x}) + g(\mb{z}) \;
\text{such that}\; H(\mb{x}, \mb{z}) = 0 \;,
where :math:`H` is a function. For some problems, proximal ADMM converges
substantially faster than ADMM or linearized ADMM.
**Linearized ADMM** Linearized ADMM :cite:`yang-2012-linearized`
:cite:`parikh-2014-proximal` solves a more restricted problem form,
.. math:: \mathop{\mathrm{arg\,min}}_{\mb{x}} \; f(\mb{x}) + g(C \mb{x}) \,.
It is an effective algorithm when the proximal operators of both
:math:`f(\cdot)` and :math:`g(\cdot)` can be computed efficiently, and
has the advantage over "standard" ADMM of avoiding the need for solving
a linear system involving :math:`C^\top C`.
**PDHG** Primal–dual hybrid gradient (PDHG) :cite:`esser-2010-general`
:cite:`chambolle-2010-firstorder` :cite:`pock-2011-diagonal` solves
the same form of problem as linearized ADMM
.. math:: \mathop{\mathrm{arg\,min}}_{\mb{x}} \; f(\mb{x}) + g(C \mb{x}) \,,
but unlike the linearized ADMM implementation, both linear and
non-linear operators :math:`C` are supported. For some problems, PDHG
converges substantially faster than ADMM or linearized ADMM.
**PGM and Accelerated PGM** The proximal gradient method (PGM)
:cite:`daubechies-2004-iterative` and accelerated proximal gradient method
(APGM), which is also known as FISTA :cite:`beck-2017-first`, solve problems
of the form
.. math:: \mathop{\mathrm{arg\,min}}_{\mb{x}} \; f(\mb{x}) + g(\mb{x}) \,,
where :math:`f(\cdot)` is assumed to be differentiable, and
:math:`g(\cdot)` is assumed to have a proximal operator that can be
computed efficiently. These algorithms typically require more iterations
for convergence than ADMM, but can provide faster convergence with time
when the linear solve required by ADMM is slow to compute.
Machine Learning
----------------
While relatively simple regularization terms such as the total
variation norm can be effective when the underlying assumptions are
well matched to the data (e.g., the reconstructed images for certain
materials science applications really are approximately piecewise
constant), it is difficult to design mathematically simple
regularization terms that adequately represent the properties of the
complex data that is often encountered in practice. A widely-used
alternative framework for regularizing the solution of imaging inverse
problems is *plug-and-play priors* (PPP)
:cite:`venkatakrishnan-2013-plugandplay2` :cite:`sreehari-2016-plug`
:cite:`kamilov-2023-plugandplay`, which provides a mechanism for
exploiting image denoisers such as BM3D :cite:`dabov-2008-image` as
implicit priors. With the rise of deep learning methods, PPP provided
one of the first frameworks for applying machine learning methods to
inverse problems via the use of learned denoisers such as DnCNN
:cite:`zhang-2017-dncnn`.
SCICO supports PPP inverse problems solutions with both BM3D and DnCNN
denoisers, and provides usage examples for both choices. BM3D is more
flexible, as it includes a tunable noise level parameter, while SCICO
only includes DnCNN models trained at three different noise levels (as
in the original DnCNN paper), but DnCNN has a significant speed
advantage when GPUs are available. As an example, the following code
outline demonstrates a PPP solution, with a non-negativity constraint
and a 17-layer DnCNN denoiser as a regularizer, of an inverse problem
with measurement, :math:`\mb{y}`, and a generic linear forward
operator, :math:`A`.
.. code:: python
ρ = 0.3 # ADMM penalty parameter
maxiter = 10 # number of ADMM iterations
f = scico.loss.SquaredL2Loss(y=y, A=A)
g1 = scico.functional.DnCNN("17M")
g2 = scico.functional.NonNegativeIndicator()
C = scico.linop.Identity(A.input_shape)
solver = scico.optimize.admm.ADMM(
f=f,
g_list=[g1, g2],
C_list=[C, C],
rho_list=[ρ, ρ],
x0=A.T @ y,
maxiter=maxiter,
subproblem_solver=scico.optimize.admm.LinearSubproblemSolver(),
itstat_options={"display": True, "period": 5},
)
x_hat = solver.solve()
Example results for this type of approach applied to image deconvolution
(i.e. with forward operator, :math:`A`, as a convolution) are shown in
the figure below.
.. image:: /figures/deconv_ppp_dncnn.png
:align: center
:width: 95%
:alt: Image deconvolution via PPP with DnCNN denoiser.
|
More recently, a wider variety of frameworks have been developed for
applying deep learning methods to inverse problems, including the
application of the adjoint of the forward operator to map the
measurement to the solution space followed by an artifact removal CNN
:cite:`jin-2017-unet`, and learned networks with structures based on
the unrolling of iterative algorithms such as PPP
:cite:`monga-2021-algorithm`. A number of these methods are currently
being implemented, and will be included in a future SCICO release. It
is worth noting, however, that while some of these methods offer
superior performance to PPP, it is at the cost of having to train the
models with problem-specific data, which may be difficult to obtain,
while PPP is often able to function well with a denoiser trained on
generic image data.
================================================
FILE: docs/source/notes.rst
================================================
*****
Notes
*****
Debugging
=========
If difficulties are encountered in debugging jitted functions, jit can
be globally disabled by setting the environment variable
``JAX_DISABLE_JIT=1`` before running Python, as in
::
JAX_DISABLE_JIT=1 python test_script.py
Double Precision
================
By default, JAX enforces single-precision numbers. Double precision
can be enabled in one of two ways:
1. Setting the environment variable ``JAX_ENABLE_X64=TRUE`` before
launching Python.
2. Manually setting the ``jax_enable_x64`` flag **at program
startup**; that is, **before** importing SCICO.
::
from jax.config import config
config.update("jax_enable_x64", True)
import scico # continue as usual
For more information, see the `JAX notes on double precision `_.
Device Control
==============
Use of the CPU device can be forced even when GPUs are present by setting the
environment variable ``JAX_PLATFORM_NAME=cpu`` before running Python. This also
serves to disable the warning that older versions of JAX issued when running
on a platform without a GPU, but this should no longer be necessary for any
JAX versions supported by SCICO.
By default, JAX views a multi-core CPU as a single device. Primarily for testing
purposes, it may be useful to instruct JAX to emulate multiple CPU devices, by
setting the environment variable ``XLA_FLAGS='--xla_force_host_platform_device_count='``,
where ```` is an integer number of devices. For more detail see the relevant
`section of the JAX docs `__.
By default, JAX will preallocate a large chunk of GPU memory on startup. This
behavior can be controlled using environment variables ``XLA_PYTHON_CLIENT_PREALLOCATE``,
``XLA_PYTHON_CLIENT_MEM_FRACTION``, and ``XLA_PYTHON_CLIENT_ALLOCATOR``, as described in
the relevant `section of the JAX docs `__.
Random Number Generation
========================
JAX implements an explicit, non-stateful pseudorandom number generator (PRNG).
The user is responsible for generating a PRNG key and mutating it each time a
new random number is generated. We recommend users read the `JAX documentation
`_
for information on the design of JAX random number functionality.
In :mod:`scico.random` we provide convenient wrappers around several `jax.random
`_ routines to handle
the generation and splitting of PRNG keys.
::
# Calls to scico.random functions always return a PRNG key
# If no key is passed to the function, a new key is generated
x, key = scico.random.randn((2,))
print(x) # [ 0.19307713 -0.52678305]
# scico.random functions automatically split the PRNGkey and return
# an updated key
y, key = scico.random.randn((2,), key=key)
print(y) # [ 0.00870693 -0.04888531]
The user is responsible for passing the PRNG key to
:mod:`scico.random` functions. If no key is passed, repeated calls to
:mod:`scico.random` functions will return the same random numbers:
::
x, key = scico.random.randn((2,))
print(x) # [ 0.19307713 -0.52678305]
# No key passed, will return the same random numbers!
y, key = scico.random.randn((2,))
print(y) # [ 0.19307713 -0.52678305]
.. _non_jax_dep:
Compiled Dependency Packages
============================
The code acceleration and automatic differentiation features of JAX
are not available for some components of SCICO that are provided via
interfaces to compiled C code. When these components are used on a
platform with GPUs, the remainder of the code will run on a GPU, but
there is potential for a considerable delay due to host-GPU memory
transfers. This issue primarily affects:
Denoisers
---------
The :func:`.bm3d` and :func:`.bm4d` denoisers (and the corresponding
:class:`.BM3D` and :class:`.BM4D` pseudo-functionals) are implemented
via interfaces to the `bm3d `__ and
`bm4d `__ packages respectively. The
:class:`~.denoiser.DnCNN` denoiser (and the corresponding
:class:`~.functional.DnCNN` pseudo-functional) denoiser should be used
when the full benefits of JAX-based code are required.
Tomographic Projectors/Radon Transforms
---------------------------------------
Note that the tomographic projections that are frequently referred
to as Radon transforms are referred to as X-ray transforms in SCICO.
While the Radon transform is far more well-known than the X-ray
transform, which is the same as the Radon transform for projections
in two dimensions, these two transform differ in higher numbers of
dimensions, and it is the X-ray transform that is the appropriate
mathematical model for beam attenuation based imaging in three or
more dimensions.
SCICO includes three different implementations of X-ray transforms.
Of these, :class:`.linop.XRayTransform` is an integral component of
SCICO, while the other two depend on external packages.
The :class:`.xray.svmbir.XRayTransform` class is implemented
via an interface to the `svmbir
`__ package. The
:class:`.xray.astra.XRayTransform2D` and
:class:`.xray.astra.XRayTransform3D` classes are implemented via an
interface to the `ASTRA toolbox
`__. This toolbox does provide some
GPU acceleration support, but efficiency is expected to be lower than
JAX-based code due to host-GPU memory transfers.
Automatic Differentiation Caveats
=================================
Complex Functions
-----------------
The JAX-defined gradient of a complex-valued function is a
complex-conjugated version of the usual gradient used in mathematical
optimization and computational imaging. Minimizing a function using
the JAX convention involves taking steps in the direction of the
complex conjugated gradient.
The function :func:`scico.grad` returns the expected gradient, that
is, the conjugate of the JAX gradient. For further discussion, see
this `JAX issue `_.
As a concrete example, consider the function :math:`f(x) =
\frac{1}{2}\norm{\mb{A} \mb{x}}_2^2` where :math:`\mb{A}` is a complex
matrix. The gradient of :math:`f` is usually given :math:`(\nabla
f)(\mb{x}) = \mb{A}^H \mb{A} \mb{x}`, where :math:`\mb{A}^H` is the
conjugate transpose of :math:`\mb{A}`. Applying :func:`jax.grad` to
:math:`f` will yield :math:`(\mb{A}^H \mb{A} \mb{x})^*`, where
:math:`\cdot^*` denotes complex conjugation.
The following code demonstrates the use of :func:`jax.grad` and
:func:`scico.grad`:
::
m, n = (4, 3)
A, key = randn((m, n), dtype=np.complex64, key=None)
x, key = randn((n,), dtype=np.complex64, key=key)
def f(x):
return 0.5 * snp.linalg.norm(A @ x)**2
an_grad = A.conj().T @ A @ x # The expected gradient
np.testing.assert_allclose(jax.grad(f)(x), an_grad.conj(), rtol=1e-4)
np.testing.assert_allclose(scico.grad(f)(x), an_grad, rtol=1e-4)
Non-differentiable Functionals
------------------------------
:func:`scico.grad` can be applied to any function, but has undefined
behavior for non-differentiable functions. For non-differerentiable
functions, :func:`scico.grad` may or may not return a valid
subgradient. As an example, ``scico.grad(snp.abs)(0.) = 0``, which is
a valid subgradient. However, ``scico.grad(snp.linalg.norm)([0., 0.])
= [nan, nan]``.
Differentiable functions that are written as the composition of a
differentiable and non-differentiable function should be avoided. As
an example, :math:`f(x) = \norm{x}_2^2` can be implemented in as ``f =
lambda x: snp.linalg.norm(x)**2``. This involves first calculating the
non-squared :math:`\ell_2` norm, then squaring it. The un-squared
:math:`\ell_2` norm is not differentiable at zero. When evaluating
the gradient of ``f`` at 0, :func:`scico.grad` returns :data:`~numpy.NaN`:
::
>>> import scico
>>> import scico.numpy as snp
>>> f = lambda x: snp.linalg.norm(x)**2
>>> scico.grad(f)(snp.zeros(2, dtype=snp.float32)) # doctest: +SKIP
Array([nan, nan], dtype=float32)
This can be fixed (assuming real-valued arrays only) by defining the
squared :math:`\ell_2` norm directly as ``g = lambda x: snp.sum(x**2)``.
The gradient will work as expected:
::
>>> g = lambda x: snp.sum(x**2)
>>> scico.grad(g)(snp.zeros(2, dtype=snp.float32)) #doctest: +SKIP
Array([0., 0.], dtype=float32)
If complex-valued arrays also need to be supported, a minor modification is
necessary:
::
>>> g = lambda x: snp.sum(snp.abs(x)**2)
>>> scico.grad(g)(snp.zeros(2, dtype=snp.float32)) #doctest: +SKIP
Array([0., 0.], dtype=float32)
>>> scico.grad(g)(snp.zeros(2, dtype=snp.complex64)) #doctest: +SKIP
Array([0.-0.j, 0.-0.j], dtype=complex64)
An alternative is to define a `custom derivative rule
`_
to enforce a particular derivative convention at a point.
JAX Arrays
==========
JAX utilizes a new array type :class:`~jax.Array`, which is similar to
NumPy :class:`~numpy.ndarray`, but can be backed by CPU, GPU, or TPU
memory and is immutable.
JAX and NumPy Arrays
--------------------
SCICO and JAX functions can be applied directly to NumPy arrays
without explicit conversion to JAX arrays, but this is not
recommended, as it can result in repeated data transfers from the CPU
to GPU. Consider this toy example on a system with a GPU present:
::
x = np.random.randn(8) # Array on host
A = np.random.randn(8, 8) # Array on host
y = snp.dot(A, x) # A, x transfered to GPU
# y resides on GPU
z = y + x # x must be transfered to GPU again
The unnecessary transfer can be avoided by first converting ``A`` and ``x`` to
JAX arrays:
::
x = np.random.randn(8) # array on host
A = np.random.randn(8, 8) # array on host
x = jax.device_put(x) # transfer to GPU
A = jax.device_put(A)
y = snp.dot(A, x) # no transfer needed
z = y + x # no transfer needed
We recommend that input data be converted to JAX arrays via
:func:`jax.device_put` before calling any SCICO optimizers.
On a multi-GPU system, :func:`jax.device_put` can place data on a specific
GPU. See the `JAX notes on data placement
`_.
JAX Arrays are Immutable
------------------------
Unlike standard NumPy arrays, JAX arrays are immutable: once they have
been created, they cannot be changed. This prohibits in-place updating
of JAX arrays. JAX provides special syntax for updating individual
array elements through the `indexed update operators
`_.
================================================
FILE: docs/source/overview.rst
================================================
Overview
========
`Scientific Computational Imaging Code (SCICO)
`__ is a Python package for solving the
inverse problems that arise in scientific imaging applications. Its
primary focus is providing methods for solving ill-posed inverse
problems by using an appropriate prior model of the reconstruction
space. SCICO includes a growing suite of operators, cost functionals,
regularizers, and optimization algorithms that may be combined to
solve a wide range of problems, and is designed so that it is easy to
add new building blocks. When solving a problem, these components are
combined in a way that makes code for optimization routines look like
the pseudocode in scientific papers. SCICO is built on top of `JAX
`__ rather than `NumPy
`__, enabling GPU/TPU acceleration, just-in-time
compilation, and automatic gradient functionality, which is used to
automatically compute the adjoints of linear operators. An example of
how to solve a multi-channel tomography problem with SCICO is shown in
the figure below.
.. image:: /figures/scico-tomo-overview.png
:align: center
:width: 95%
:alt: Solving a multi-channel tomography problem with SCICO.
|
The SCICO source code is available from `GitHub
`__, and pre-built packages are
available from `PyPI `__. (Detailed
instructions for installing SCICO are available in :ref:`installing`.)
It has extensive `online documentation `__,
including :doc:`API documentation <_autosummary/scico>` and
:ref:`usage examples `, which can be run online at
`Google Colab
`__
and `binder
`__.
If you use this package for published work, please cite
:cite:`balke-2022-scico` (see bibtex entry ``balke-2022-scico`` in
`docs/source/references.bib
`_
in the source distribution).
Contributing
------------
Bug reports, feature requests, and general suggestions are welcome,
and should be submitted via the `GitHub issue system
`__. More substantial
contributions are also :ref:`welcome `.
License
-------
SCICO is distributed as open-source software under a BSD 3-Clause
License (see the `LICENSE
`__ file for
details). LANL open source approval reference C20091.
© 2020-2025. Triad National Security, LLC. All rights reserved.
This program was produced under U.S. Government contract
89233218CNA000001 for Los Alamos National Laboratory (LANL), which is
operated by Triad National Security, LLC for the U.S. Department of
Energy/National Nuclear Security Administration. All rights in the
program are reserved by Triad National Security, LLC, and the
U.S. Department of Energy/National Nuclear Security Administration.
The Government has granted for itself and others acting on its behalf
a nonexclusive, paid-up, irrevocable worldwide license in this
material to reproduce, prepare derivative works, distribute copies to
the public, perform publicly and display publicly, and to permit
others to do so.
================================================
FILE: docs/source/pyfigures/cylindgrad.py
================================================
import numpy as np
import scico.linop as scl
from scico import plot
input_shape = (7, 7, 7)
centre = (np.array(input_shape) - 1) / 2
end = np.array(input_shape) - centre
g0, g1, g2 = np.mgrid[-centre[0] : end[0], -centre[1] : end[1], -centre[2] : end[2]]
cg = scl.CylindricalGradient(input_shape=input_shape)
ang = cg.coord[0]
rad = cg.coord[1]
axi = cg.coord[2]
theta = np.arctan2(g0, g1)
clr = theta
# See https://stackoverflow.com/a/49888126
clr = (clr.ravel() - clr.min()) / np.ptp(clr)
clr = np.concatenate((clr, np.repeat(clr, 2)))
clr = plot.plt.cm.plasma(clr)
plot.plt.rcParams["savefig.transparent"] = True
fig = plot.plt.figure(figsize=(20, 6))
ax = fig.add_subplot(1, 3, 1, projection="3d")
ax.quiver(g0, g1, g2, ang[0], ang[1], ang[2], colors=clr, length=0.9)
ax.set_title("Angular local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
ax = fig.add_subplot(1, 3, 2, projection="3d")
ax.quiver(g0, g1, g2, rad[0], rad[1], rad[2], colors=clr, length=0.9)
ax.set_title("Radial local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
ax = fig.add_subplot(1, 3, 3, projection="3d")
ax.quiver(g0, g1, g2, axi[0], axi[1], axi[2], colors=clr[0], length=0.9)
ax.set_title("Axial local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
fig.tight_layout()
fig.show()
================================================
FILE: docs/source/pyfigures/polargrad.py
================================================
import numpy as np
import scico.linop as scl
from scico import plot
input_shape = (21, 21)
centre = (np.array(input_shape) - 1) / 2
end = np.array(input_shape) - centre
g0, g1 = np.mgrid[-centre[0] : end[0], -centre[1] : end[1]]
pg = scl.PolarGradient(input_shape=input_shape)
ang = pg.coord[0]
rad = pg.coord[1]
clr = (np.arctan2(ang[1], ang[0]) + np.pi) / (2 * np.pi)
plot.plt.rcParams["image.cmap"] = "plasma"
plot.plt.rcParams["savefig.transparent"] = True
fig, ax = plot.plt.subplots(nrows=1, ncols=2, figsize=(13, 6))
ax[0].quiver(g0, g1, ang[0], ang[1], clr)
ax[0].set_title("Angular local coordinate axis", fontsize=16)
ax[0].set_xlabel("$x$", fontsize=14)
ax[0].set_ylabel("$y$", fontsize=14)
ax[0].tick_params(labelsize=14)
ax[0].xaxis.set_ticks((-10, -5, 0, 5, 10))
ax[0].yaxis.set_ticks((-10, -5, 0, 5, 10))
ax[1].quiver(g0, g1, rad[0], rad[1], clr)
ax[1].set_title("Radial local coordinate axis", fontsize=16)
ax[1].set_xlabel("$x$", fontsize=14)
ax[1].set_ylabel("$y$", fontsize=14)
ax[1].tick_params(labelsize=14)
ax[1].xaxis.set_ticks((-10, -5, 0, 5, 10))
ax[1].yaxis.set_ticks((-10, -5, 0, 5, 10))
fig.tight_layout()
fig.show()
================================================
FILE: docs/source/pyfigures/spheregrad.py
================================================
import numpy as np
import scico.linop as scl
from scico import plot
input_shape = (7, 7, 7)
centre = (np.array(input_shape) - 1) / 2
end = np.array(input_shape) - centre
g0, g1, g2 = np.mgrid[-centre[0] : end[0], -centre[1] : end[1], -centre[2] : end[2]]
sg = scl.SphericalGradient(input_shape=input_shape)
azi = sg.coord[0]
pol = sg.coord[1]
rad = sg.coord[2]
theta = np.arctan2(g0, g1)
phi = np.arctan2(np.sqrt(g0**2 + g1**2), g2)
clr = theta * phi
# See https://stackoverflow.com/a/49888126
clr = (clr.ravel() - clr.min()) / np.ptp(clr)
clr = np.concatenate((clr, np.repeat(clr, 2)))
clr = plot.plt.cm.plasma(clr)
plot.plt.rcParams["savefig.transparent"] = True
fig = plot.plt.figure(figsize=(20, 6))
ax = fig.add_subplot(1, 3, 1, projection="3d")
ax.quiver(g0, g1, g2, azi[0], azi[1], azi[2], colors=clr, length=0.9)
ax.set_title("Azimuthal local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
ax = fig.add_subplot(1, 3, 2, projection="3d")
ax.quiver(g0, g1, g2, pol[0], pol[1], pol[2], colors=clr, length=0.9)
ax.set_title("Polar local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
ax = fig.add_subplot(1, 3, 3, projection="3d")
ax.quiver(g0, g1, g2, rad[0], rad[1], rad[2], colors=clr, length=0.9)
ax.set_title("Radial local coordinate axis", fontsize=18)
ax.set_xlabel("$x$", fontsize=15)
ax.set_ylabel("$y$", fontsize=15)
ax.set_zlabel("$z$", fontsize=15)
ax.tick_params(labelsize=15)
fig.tight_layout()
fig.show()
================================================
FILE: docs/source/pyfigures/xray_2d_geom.py
================================================
import numpy as np
import matplotlib as mpl
import matplotlib.patches as patches
import matplotlib.pyplot as plt
mpl.rcParams["savefig.transparent"] = True
c = 1.0 / np.sqrt(2.0)
e = 1e-2
style = "Simple, tail_width=0.5, head_width=4, head_length=8"
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(21, 7))
# all plots
for n in range(3):
ax[n].set_aspect(1.0)
ax[n].set_xlim(-1.1, 1.1)
ax[n].set_ylim(-1.1, 1.1)
ax[n].set_xticks(np.linspace(-1.0, 1.0, 5))
ax[n].set_yticks(np.linspace(-1.0, 1.0, 5))
ax[n].tick_params(axis="x", labelsize=14)
ax[n].tick_params(axis="y", labelsize=14)
ax[n].set_xlabel("axis 1", fontsize=16)
ax[n].set_ylabel("axis 0", fontsize=16)
# scico
ax[0].set_title("scico", fontsize=18)
plist = [
patches.FancyArrowPatch((-1.0, 0.0), (-0.5, 0.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch((-c, -c), (-c / 2.0, -c / 2.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch(
(
0.0,
-1.0,
),
(0.0, -0.5),
arrowstyle=style,
color="r",
),
patches.Arc((0.0, 0.0), 2.0, 2.0, theta1=180, theta2=-45.0, color="b", lw=2, ls="dotted"),
patches.FancyArrowPatch((c - e, -c - e), (c + e, -c + e), arrowstyle=style, color="b"),
]
for p in plist:
ax[0].add_patch(p)
ax[0].text(-0.88, 0.02, r"$\theta=0$", color="r", fontsize=16)
ax[0].text(-3 * c / 4 - 0.01, -3 * c / 4 - 0.1, r"$\theta=\frac{\pi}{4}$", color="r", fontsize=16)
ax[0].text(0.03, -0.8, r"$\theta=\frac{\pi}{2}$", color="r", fontsize=16)
ax[0].plot((1.0, 1.0), (-0.375, 0.375), color="orange", lw=2)
ax[0].arrow(
0.94,
0.375,
0.0,
-0.75,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[0].text(0.7, 0.0, r"$\theta=0$", color="orange", ha="left", fontsize=16)
ax[0].plot((-0.375, 0.375), (1.0, 1.0), color="orange", lw=2)
ax[0].arrow(
-0.375,
0.94,
0.75,
0.0,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[0].text(0.0, 0.82, r"$\theta=\frac{\pi}{2}$", color="orange", ha="center", fontsize=16)
# astra
ax[1].set_title("astra", fontsize=18)
plist = [
patches.FancyArrowPatch((0.0, -1.0), (0.0, -0.5), arrowstyle=style, color="r"),
patches.FancyArrowPatch((c, -c), (c / 2.0, -c / 2.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch((1.0, 0.0), (0.5, 0.0), arrowstyle=style, color="r"),
patches.Arc((0.0, 0.0), 2.0, 2.0, theta1=-90, theta2=45.0, color="b", lw=2, ls="dotted"),
patches.FancyArrowPatch((c + e, c - e), (c - e, c + e), arrowstyle=style, color="b"),
]
for p in plist:
ax[1].add_patch(p)
ax[1].text(0.02, -0.75, r"$\theta=0$", color="r", fontsize=16)
ax[1].text(3 * c / 4 + 0.01, -3 * c / 4 + 0.01, r"$\theta=\frac{\pi}{4}$", color="r", fontsize=16)
ax[1].text(0.65, 0.05, r"$\theta=\frac{\pi}{2}$", color="r", fontsize=16)
ax[1].plot((-0.375, 0.375), (1.0, 1.0), color="orange", lw=2)
ax[1].arrow(
-0.375,
0.94,
0.75,
0.0,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[1].text(0.0, 0.82, r"$\theta=0$", color="orange", ha="center", fontsize=16)
ax[1].plot((-1.0, -1.0), (-0.375, 0.375), color="orange", lw=2)
ax[1].arrow(
-0.94,
-0.375,
0.0,
0.75,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[1].text(-0.9, 0.0, r"$\theta=\frac{\pi}{2}$", color="orange", ha="left", fontsize=16)
# svmbir
ax[2].set_title("svmbir", fontsize=18)
plist = [
patches.FancyArrowPatch((-1.0, 0.0), (-0.5, 0.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch((-c, c), (-c / 2.0, c / 2.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch(
(
0.0,
1.0,
),
(0.0, 0.5),
arrowstyle=style,
color="r",
),
patches.Arc((0.0, 0.0), 2.0, 2.0, theta1=45, theta2=180, color="b", lw=2, ls="dotted"),
patches.FancyArrowPatch((c - e, c + e), (c + e, c - e), arrowstyle=style, color="b"),
]
for p in plist:
ax[2].add_patch(p)
ax[2].text(-0.88, 0.02, r"$\theta=0$", color="r", fontsize=16)
ax[2].text(-3 * c / 4 + 0.01, 3 * c / 4 + 0.01, r"$\theta=\frac{\pi}{4}$", color="r", fontsize=16)
ax[2].text(0.03, 0.75, r"$\theta=\frac{\pi}{2}$", color="r", fontsize=16)
ax[2].plot((1.0, 1.0), (-0.375, 0.375), color="orange", lw=2)
ax[2].arrow(
0.94,
0.375,
0.0,
-0.75,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[2].text(0.7, 0.0, r"$\theta=0$", color="orange", ha="left", fontsize=16)
ax[2].plot((-0.375, 0.375), (-1.0, -1.0), color="orange", lw=2)
ax[2].arrow(
0.375,
-0.94,
-0.75,
0.0,
color="orange",
lw=1.0,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax[2].text(0.0, -0.82, r"$\theta=\frac{\pi}{2}$", color="orange", ha="center", fontsize=16)
fig.tight_layout()
fig.show()
================================================
FILE: docs/source/pyfigures/xray_3d_ang.py
================================================
import numpy as np
import matplotlib as mpl
import matplotlib.patches as patches
import matplotlib.pyplot as plt
mpl.rcParams["savefig.transparent"] = True
c = 1.0 / np.sqrt(2.0)
e = 1e-2
style = "Simple, tail_width=0.5, head_width=4, head_length=8"
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))
ax.set_aspect(1.0)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_xticks(np.linspace(-1.0, 1.0, 5))
ax.set_yticks(np.linspace(-1.0, 1.0, 5))
ax.tick_params(axis="x", labelsize=12)
ax.tick_params(axis="y", labelsize=12)
ax.set_xlabel("$x$", fontsize=14)
ax.set_ylabel("$y$", fontsize=14)
plist = [
patches.FancyArrowPatch((0.0, -1.0), (0.0, -0.5), arrowstyle=style, color="r"),
patches.FancyArrowPatch((c, -c), (c / 2.0, -c / 2.0), arrowstyle=style, color="r"),
patches.FancyArrowPatch((1.0, 0.0), (0.5, 0.0), arrowstyle=style, color="r"),
patches.Arc((0.0, 0.0), 2.0, 2.0, theta1=-90, theta2=45.0, color="b", lw=2, ls="dotted"),
patches.FancyArrowPatch((c + e, c - e), (c - e, c + e), arrowstyle=style, color="b"),
]
for p in plist:
ax.add_patch(p)
ax.text(0.02, -0.75, r"$\theta=0$", color="r", fontsize=14)
ax.text(
3 * c / 4 + 0.01,
-3 * c / 4 + 0.01,
r"$\theta=\frac{\pi}{4}$",
color="r",
fontsize=14,
)
ax.text(0.65, 0.05, r"$\theta=\frac{\pi}{2}$", color="r", fontsize=14)
ax.plot((-0.375, 0.375), (1.0, 1.0), color="orange", lw=2)
ax.arrow(
-0.375,
0.94,
0.75,
0.0,
color="orange",
lw=0.5,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax.text(0.0, 0.82, r"$\theta=0$", color="orange", ha="center", fontsize=14)
ax.plot((-1.0, -1.0), (-0.375, 0.375), color="orange", lw=2)
ax.arrow(
-0.94,
-0.375,
0.0,
0.75,
color="orange",
lw=0.5,
ls="--",
head_width=0.03,
length_includes_head=True,
)
ax.text(-0.9, 0.0, r"$\theta=\frac{\pi}{2}$", color="orange", ha="left", fontsize=14)
fig.tight_layout()
fig.show()
================================================
FILE: docs/source/pyfigures/xray_3d_vec.py
================================================
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
mpl.rcParams["savefig.transparent"] = True
# See https://github.com/matplotlib/matplotlib/issues/21688
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def do_3d_projection(self, renderer=None):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
return np.min(zs)
# Define vector components
𝜃 = 10 * np.pi / 180.0 # angle in x-y plane (azimuth angle)
𝛼 = 70 * np.pi / 180.0 # angle with z axis (zenith angle)
𝛥p, 𝛥d = 0.3, 1.0
d = (-𝛥d * np.sin(𝛼) * np.sin(𝜃), 𝛥d * np.sin(𝛼) * np.cos(𝜃), 𝛥d * np.cos(𝛼))
u = (𝛥p * np.cos(𝜃), 𝛥p * np.sin(𝜃), 0.0)
v = (𝛥p * np.cos(𝛼) * np.sin(𝜃), -𝛥p * np.cos(𝛼) * np.cos(𝜃), 𝛥p * np.sin(𝛼))
# Location of text labels
d_txtpos = np.array(d) + np.array([0, 0, -0.12])
u_txtpos = np.array(d) + np.array(u) + np.array([0, 0, -0.1])
v_txtpos = np.array(d) + np.array(v) + np.array([0, 0, 0.03])
arrowstyle = "-|>,head_width=2.5,head_length=9"
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
# Set view
ax.set_aspect("equal")
ax.elev = 15
ax.azim = -50
ax.set_box_aspect(None, zoom=2)
ax.set_xlim((-1.1, 1.1))
ax.set_ylim((-1.1, 1.1))
ax.set_zlim((-1.1, 1.1))
# Disable shaded 3d axis grids
ax.set_axis_off()
# Draw central x,y,z axes and labels
axis_crds = np.array([[-1, 1], [0, 0], [0, 0]])
axis_lbls = ("$x$", "$y$", "$z$")
for k in range(3):
crd = np.roll(axis_crds, k, axis=0)
ax.add_artist(
Arrow3D(
*crd.tolist(),
lw=1.5,
ls="--",
arrowstyle=arrowstyle,
color="black",
)
)
ax.text(*(1.05 * crd[:, 1]).tolist(), axis_lbls[k], fontsize=12)
# Draw d, u, v and labels
ax.quiver(0, 0, 0, *d, arrow_length_ratio=0.08, lw=2, color="blue")
ax.quiver(*d, *u, arrow_length_ratio=0.08 / 𝛥p, lw=2, color="blue")
ax.quiver(*d, *v, arrow_length_ratio=0.08 / 𝛥p, lw=2, color="blue")
ax.text(*d_txtpos, r"$\mathbf{d}$", fontsize=12)
ax.text(*u_txtpos, r"$\mathbf{u}$", fontsize=12)
ax.text(*v_txtpos, r"$\mathbf{v}$", fontsize=12)
fig.tight_layout()
fig.subplots_adjust(-0.1, -0.06, 1, 1)
fig.show()
================================================
FILE: docs/source/pyfigures/xray_3d_vol.py
================================================
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
mpl.rcParams["savefig.transparent"] = True
# See https://github.com/matplotlib/matplotlib/issues/21688
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def do_3d_projection(self, renderer=None):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
return np.min(zs)
# Define vector components
𝜃 = 10 * np.pi / 180.0 # angle in x-y plane (azimuth angle)
𝛼 = 70 * np.pi / 180.0 # angle with z axis (zenith angle)
𝛥p, 𝛥d = 0.3, 1.0
d = (-𝛥d * np.sin(𝛼) * np.sin(𝜃), 𝛥d * np.sin(𝛼) * np.cos(𝜃), 𝛥d * np.cos(𝛼))
u = (𝛥p * np.cos(𝜃), 𝛥p * np.sin(𝜃), 0.0)
v = (𝛥p * np.cos(𝛼) * np.sin(𝜃), -𝛥p * np.cos(𝛼) * np.cos(𝜃), 𝛥p * np.sin(𝛼))
# Location of text labels
d_txtpos = np.array(d) + np.array([0, 0, -0.12])
u_txtpos = np.array(d) + np.array(u) + np.array([0, 0, -0.1])
v_txtpos = np.array(d) + np.array(v) + np.array([0, 0, 0.03])
arrowstyle = "-|>,head_width=2.5,head_length=9"
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
# Set view
ax.set_aspect("equal")
ax.elev = 40
ax.azim = -60
ax.set_box_aspect(None, zoom=1.8)
ax.set_xlim((-10.5, 10.5))
ax.set_ylim((-10.5, 10.5))
ax.set_zlim((-10.5, 10.5))
# Disable shaded 3d axis grids
ax.set_axis_off()
# Draw central x,y,z axes and labels
axis_crds = np.array([[-10, 10], [0, 0], [0, 0]])
axis_lbls = ("$x$", "$y$", "$z$")
for k in range(3):
crd = np.roll(axis_crds, k, axis=0)
ax.add_artist(
Arrow3D(
*crd.tolist(),
lw=1.5,
ls="--",
arrowstyle=arrowstyle,
color="black",
)
)
ax.text(*(1.05 * crd[:, 1]).tolist(), axis_lbls[k], fontsize=12)
wx = 4
wy = 3
wz = 2
bx = np.array([-wx, wx, wx, wx, -wx, -wx, -wx])
by = np.array([-wy, -wy, wy, wy, wy, -wy, -wy])
bz = np.array([-wz, -wz, -wz, wz, wz, wz, -wz])
ax.plot(bx, by, bz, lw=2, color="blue")
ax.plot(bx[0:3], by[0:3], -bz[0:3], lw=2, color="blue")
bx = np.array([wx, wx])
by = np.array([-wy, -wy])
bz = np.array([-wz, wz])
ax.plot(bx, by, bz, lw=2, color="blue")
bx = np.array([-wx, -wx, wx])
by = np.array([-wy, wy, wy])
bz = np.array([-wz, -wz, -wz])
ax.plot(bx, by, bz, lw=2, ls="--", color="blue")
bx = np.array([-wx, -wx])
by = np.array([wy, wy])
bz = np.array([-wz, wz])
ax.plot(bx, by, bz, lw=2, ls="--", color="blue")
fig.tight_layout()
fig.subplots_adjust(-0.1, -0.1, 1, 1.07)
fig.show()
================================================
FILE: docs/source/references.bib
================================================
@Article {aggarwal-2019-modl,
author = {Aggarwal, Hemant K. and Mani, Merry P. and Jacob,
Mathews},
journal = {IEEE Transactions on Medical Imaging},
title = {{MoDL}: Model-Based Deep Learning Architecture for
Inverse Problems},
year = 2019,
volume = 38,
number = 2,
pages = {394--405},
doi = {10.1109/TMI.2018.2865356}
}
@Article {alliney-1992-digital,
author = {Alliney, Stefano},
journal = {IEEE Transactions on Signal Processing},
title = {Digital filters as absolute norm regularizers},
year = 1992,
volume = 40,
number = 6,
pages = {1548--1562},
doi = {10.1109/78.139258},
month = Jun
}
@Article {almeida-2013-deconvolving,
author = {Almeida, Mariana S. C. and Figueiredo, M\'ario},
journal = {IEEE Transactions on Image Processing},
title = {Deconvolving Images With Unknown Boundaries Using
the Alternating Direction Method of Multipliers},
year = 2013,
month = Aug,
volume = 22,
number = 8,
pages = {3074--3086},
doi = {10.1109/TIP.2013.2258354}
}
@Article {antipa-2018-diffusercam,
author = {Nick Antipa and Grace Kuo and Reinhard Heckel and
Ben Mildenhall and Emrah Bostan and Ren Ng and Laura
Waller},
title = {{DiffuserCam}: lensless single-exposure 3{D}
imaging},
journal = {Optica},
year = 2018,
month = Jan,
volume = 5,
number = 1,
doi = {10.1364/optica.5.000001},
pages = {1--9}
}
@Article {balke-2022-scico,
author = {Thilo Balke and Fernando Davis and Cristina
Garcia-Cardona and Soumendu Majee and Michael McCann
and Luke Pfister and Brendt Wohlberg},
title = {Scientific Computational Imaging Code ({SCICO})},
journal = {Journal of Open Source Software},
year = 2022,
volume = 7,
number = 78,
pages = 4722,
doi = {10.21105/joss.04722}
}
@Article {barzilai-1988-stepsize,
author = {Jonathan Barzilai and Jonathan M. Borwein},
title = {Two-point step size gradient methods},
journal = {{IMA} Journal of Numerical Analysis},
volume = 8,
pages = {141--148},
year = 1988,
month = Jan,
doi = {10.1093/imanum/8.1.141}
}
@Article {beck-2009-fast,
title = {A Fast Iterative Shrinkage-Thresholding Algorithm
for Linear Inverse Problems},
author = {Beck, Amir and Teboulle, Marc},
journal = {SIAM Journal on Imaging Sciences},
year = 2009,
volume = 2,
number = 1,
pages = {183--202},
doi = {10.1137/080716542}
}
@Article {beck-2009-tv,
title = {Fast Gradient-Based Algorithms for Constrained Total
Variation Image Denoising and Deblurring Problems},
author = {Beck, Amir and Teboulle, Marc},
journal = {IEEE Transactions on Image Processing},
year = 2009,
month = Nov,
volume = 18,
number = 11,
pages = {2419--2434},
doi = {10.1109/TIP.2009.2028250}
}
@InCollection {beck-2010-gradient,
author = {Amir Beck and Marc Teboulle},
editor = {Daniel P. Palomar and Yonina C. Eldar},
title = {Gradient-based algorithms with applications to
signal-recovery problems},
booktitle = {Convex Optimization in Signal Processing and
Communications},
pages = {42--88},
publisher = {Cambridge University Press},
year = 2010,
doi = {10.1017/CBO9780511804458.003},
url = {http://www.math.tau.ac.il/~teboulle/papers/gradient_chapter.pdf}
}
@Software {bradbury-2018-jax,
author = {James Bradbury and Roy Frostig and Peter Hawkins and
Matthew James Johnson and Chris Leary and Dougal
Maclaurin and George Necula and Adam Paszke and Jake
Vander{P}las and Skye Wanderman-{M}ilne and Qiao
Zhang},
title = {{JAX}: composable transformations of
{P}ython+{N}um{P}y programs},
url = {http://github.com/google/jax},
version = {0.2.5},
year = 2018
}
@Book {beck-2017-first,
title = {First-order methods in optimization},
author = {Beck, Amir},
year = 2017,
publisher = {Society for Industrial and Applied Mathematics
(SIAM)},
doi = {10.1137/1.9781611974997},
isbn = 1611974984
}
@InProceedings {benning-2016-preconditioned,
title = {Preconditioned {ADMM} with nonlinear operator
constraint},
author = {Benning, Martin and Knoll, Florian and
Sch{\"o}nlieb, Carola-Bibiane and Valkonen, Tuomo},
booktitle = {IFIP Conference on System Modeling and Optimization
(CSMO) 2015},
pages = {117--126},
year = 2016,
doi = {10.1007/978-3-319-55795-3_10}
}
@Article {boyd-2010-distributed,
title = {Distributed optimization and statistical learning
via the alternating direction method of multipliers},
author = {Boyd, Stephen and Parikh, Neal and Chu, Eric and
Peleato, Borja and Eckstein, Jonathan},
journal = {Foundations and Trends in Machine Learning},
year = 2010,
volume = 3,
number = 1,
pages = {1--122},
doi = {10.1561/2200000016}
}
@Article {buzzard-2018-plug,
title = {Plug-and-play unplugged: Optimization-free
reconstruction using consensus equilibrium},
author = {Buzzard, Gregery T. and Chan, Stanley H. and
Sreehari, Suhas and Bouman, Charles A.},
journal = {SIAM Journal on Imaging Sciences},
volume = 11,
number = 3,
pages = {2001--2020},
year = 2018,
doi = {10.1137/17M1122451}
}
@Article {cai-2010-singular,
title = {A Singular Value Thresholding Algorithm for Matrix
Completion},
author = {Cai, Jian-Feng and Cand{\`e}s, Emmanuel J. and Shen,
Zuowei},
journal = {SIAM Journal on Optimization},
year = 2010,
volume = 20,
number = 4,
pages = {1956--1982},
doi = {10.1137/080738970}
}
@Article {chambolle-2010-firstorder,
author = {Antonin Chambolle and Thomas Pock},
title = {A First-Order Primal-Dual Algorithm for Convex
Problems with~Applications to Imaging},
journal = {Journal of Mathematical Imaging and Vision},
doi = {10.1007/s10851-010-0251-1},
year = 2010,
month = Dec,
volume = 40,
number = 1,
pages = {120--145}
}
@Misc {chandler-2024-closedform,
author = {Edward P. Chandler and Shirin Shoushtari and Brendt
Wohlberg and Ulugbek S. Kamilov},
title = {Closed-Form Approximation of the Total Variation
Proximal Operator},
year = 2024,
eprint = {2412.07718}
}
@Article {clinthorne-1993-preconditioning,
author = {Clinthorne, Neal H. and Pan, Tin-Su and Chiao,
Ping-Chun and Rogers, W. Leslie and Stamos, John A.},
title = {Preconditioning methods for improved convergence
rates in iterative reconstructions},
journal = {IEEE Transactions on Medical Imaging},
year = 1993,
volume = 12,
number = 1,
pages = {78--83},
month = Mar,
doi = {10.1109/42.222670}
}
@InProceedings {dabov-2008-image,
author = {Kostadin Dabov and Alessandro Foi and Vladimir
Katkovnik and Karen Egiazarian},
title = {Image restoration by sparse {3D} transform-domain
collaborative filtering},
volume = 6812,
booktitle = {Image Processing: Algorithms and Systems VI},
editor = {Jaakko T. Astola and Karen O. Egiazarian and Edward
R. Dougherty},
organization = {International Society for Optics and Photonics},
publisher = {SPIE},
pages = {62--73},
year = 2008,
month = Mar,
doi = {10.1117/12.766355}
}
@Article {daubechies-2004-iterative,
title = {An iterative thresholding algorithm for linear
inverse problems with a sparsity constraint},
author = {Daubechies, Ingrid and Defrise, Michel and De Mol,
Christine},
journal = {Communications on Pure and Applied Mathematics},
volume = 57,
number = 11,
pages = {1413--1457},
year = 2004,
doi = {10.1002/cpa.20042}
}
@Article {deng-2015-global,
author = {Wei Deng and Wotao Yin},
title = {On the Global and Linear Convergence of the
Generalized Alternating Direction Method of
Multipliers},
journal = {Journal of Scientific Computing},
year = 2015,
month = May,
volume = 66,
number = 3,
pages = {889--916},
doi = {10.1007/s10915-015-0048-x},
}
@Misc {diamond-2018-odp,
author = {Steven Diamond and Vincent Sitzmann and Felix Heide
and Gordon Wetzstein},
title = {Unrolled Optimization with Deep Priors},
year = 2018,
eprint = {1705.08041v2}
}
@Article {esser-2010-general,
author = {Ernie Esser and Xiaoqun Zhang and Tony F. Chan},
title = {A General Framework for a Class of First Order
Primal-Dual Algorithms for Convex Optimization in
Imaging Science},
journal = {SIAM Journal on Imaging Sciences},
doi = {10.1137/09076934x},
year = 2010,
month = Jan,
volume = 3,
number = 4,
pages = {1015--1046}
}
@PhDThesis {esser-2010-primal,
author = {Ernie Esser},
title = {Primal Dual Algorithms for Convex Models and
Applications to Image Restoration, Registration and
Nonlocal Inpainting},
school = {University of California Los Angeles},
year = 2010
}
@InProceedings {florea-2017-robust,
title = {A Robust {FISTA}-Like Algorithm},
author = {Mihai I. Florea and Sergiy A. Vorobyov},
booktitle = {Proceedings of the IEEE International Conference on
Acoustics, Speech and Signal Processing (ICASSP)},
year = 2017,
month = Mar,
pages = {4521--4525},
doi = {10.1109/ICASSP.2017.7953012},
location = {New Orleans, LA, USA}
}
@Article {gabay-1976-dual,
title = {A dual algorithm for the solution of nonlinear
variational problems via finite element
approximation},
author = {Gabay, Daniel and Mercier, Bertrand},
journal = {Computers \& Mathematics with Applications},
volume = 2,
number = 1,
pages = {17--40},
year = 1976,
doi = {10.1016/0898-1221(76)90003-1}
}
@Article {glowinski-1975-approximation,
title = {Sur l'approximation, par {\'e}l{\'e}ments finis
d'ordre un, et la r{\'e}solution, par
p{\'e}nalisation-dualit{\'e} d'une classe de
probl{\`e}mes de Dirichlet non lin{\'e}aires},
author = {Glowinski, Roland and Marroco, Americo},
journal = {ESAIM: Mathematical Modelling and Numerical Analysis
- Mod{\'e}lisation Math{\'e}matique et Analyse
Num{\'e}rique},
volume = 9,
number = {R2},
pages = {41--76},
year = 1975,
url = {http://eudml.org/doc/193269}
}
@Article {goldstein-2009-split,
author = {Tom Goldstein and Stanley Osher},
title = {The Split {B}regman Method for L1-Regularized
Problems},
journal = {SIAM Journal on Imaging Sciences},
volume = 2,
number = 2,
pages = {323--343},
year = 2009,
doi = {10.1137/080725891}
}
@Misc {goldstein-2014-fasta,
title = {A Field Guide to Forward-Backward Splitting with a
{FASTA} Implementation},
author = {Tom Goldstein and Christoph Studer and Richard
Baraniuk},
year = 2014,
eprint = {1411.3406},
url = {http://arxiv.org/abs/1411.3406},
}
@Book {goodman-2005-fourier,
author = {Goodman, Joseph W.},
title = {Introduction to {F}ourier Optics},
publisher = {McGraw-Hill},
year = 2005,
isbn = 9780974707723,
edition = 3
}
@Misc {hossein-2024-total,
title = {Total Variation Regularization for Tomographic
Reconstruction of Cylindrically Symmetric Objects},
author = {Maliha Hossain and Charles A. Bouman and Brendt
Wohlberg},
year = 2024,
eprint = {2406.17928}
}
@Article {hoyer-2004-nonnegative,
title = {Non-negative matrix factorization with sparseness
constraints},
author = {Patrik O. Hoyer},
journal = {Journal of Machine Learning Research},
volume = 5,
number = Nov,
pages = {1457--1469},
year = 2004,
url = {https://www.jmlr.org/papers/volume5/hoyer04a/hoyer04a.pdf}
}
@Article {huber-1964-robust,
doi = {10.1214/aoms/1177703732},
year = 1964,
month = Mar,
volume = 35,
number = 1,
pages = {73--101},
author = {Peter J. Huber},
title = {Robust Estimation of a Location Parameter},
journal = {The Annals of Mathematical Statistics}
}
@Article {jin-2017-unet,
title = {Deep Convolutional Neural Network for Inverse
Problems in Imaging},
author = {Kyong Hwan Jin and Michael T. McCann and Emmanuel
Froustey and Michael Unser},
journal = {IEEE Transactions on Image Processing},
volume = 26,
number = 9,
pages = {4509--4522},
year = 2017,
doi = {10.1109/TIP.2017.2713099}
}
@Book {kak-1988-principles,
author = {Avinash C. Kak and Malcolm Slaney},
title = {Principles of Computerized Tomographic Imaging},
publisher = {IEEE Press},
year = 1988
}
@TechReport {kamilov-2016-minimizing,
author = {Ulugbek S. Kamilov},
title = {Minimizing Isotropic Total Variation without
Subiterations},
institution = {Mitsubishi Electric Research Laboratories (MERL)},
year = 2016,
number = {TR2016-109},
month = Aug,
note = {Presented at International Traveling Workshop on
Interactions Between Sparse Models and Technology
(iTWIST) 2016},
url = {https://www.merl.com/publications/docs/TR2016-109.pdf}
}
@Article {kamilov-2016-parallel,
title = {A parallel proximal algorithm for anisotropic total
variation minimization},
author = {Ulugbek S. Kamilov},
journal = {IEEE Transactions on Image Processing},
volume = 26,
number = 2,
pages = {539--548},
year = 2016,
doi = {10.1109/tip.2016.2629449 }
}
@Article {kamilov-2017-plugandplay,
author = {Ulugbek S. Kamilov and Hassan Mansour and Brendt
Wohlberg},
title = {A Plug-and-Play Priors Approach for Solving
Nonlinear Imaging Inverse Problems},
year = 2017,
month = Dec,
journal = {IEEE Signal Processing Letters},
volume = 24,
number = 12,
doi = {10.1109/LSP.2017.2763583},
pages = {1872--1876}
}
@Article {kamilov-2023-plugandplay,
author = {Ulugbek S. Kamilov and Charles A. Bouman and Gregery
T. Buzzard and Brendt Wohlberg},
title = {Plug-and-Play Methods for Integrating Physical and
Learned Models in Computational Imaging},
journal = {IEEE Signal Processing Magazine},
year = 2023,
month = Jan,
volume = 40,
number = 1,
pages = {85--97},
doi = {10.1109/MSP.2022.3199595}
}
@Article {liu-2018-first,
author = {Jialin Liu and Cristina Garcia-Cardona and Brendt
Wohlberg and Wotao Yin},
title = {First and Second Order Methods for Online
Convolutional Dictionary Learning},
journal = {SIAM Journal on Imaging Sciences},
year = 2018,
volume = 11,
number = 2,
pages = {1589--1628},
doi = {10.1137/17M1145689},
eprint = {1709.00106}
}
@Article {lou-2018-fast,
title = {Fast {L1-L2} Minimization via a Proximal Operator},
author = {Yifei Lou and Ming Yan},
journal = {Journal of Scientific Computing},
volume = 74,
number = 2,
pages = {767--785},
year = 2018,
doi = {10.1007/s10915-017-0463-2}
}
@Article {maggioni-2012-nonlocal,
title = {Nonlocal transform-domain filter for volumetric data
denoising and reconstruction},
author = {Maggioni, Matteo and Katkovnik, Vladimir and
Egiazarian, Karen and Foi, Alessandro},
journal = {IEEE Transactions on Image Processing},
volume = 22,
number = 1,
pages = {119--133},
year = 2012,
doi = {10.1109/TIP.2012.2210725}
}
@InProceedings {makinen-2019-exact,
author = {Ymir M\"akinen and Lucio Azzari and Alessandro Foi},
booktitle = {IEEE International Conference on Image Processing
(ICIP)},
title = {Exact Transform-Domain Noise Variance for
Collaborative Filtering of Stationary Correlated
Noise},
year = 2019,
pages = {185--189},
doi = {10.1109/ICIP.2019.8802964},
month = Sep
}
@Article {menon-2007-demosaicing,
title = {Demosaicing With Directional Filtering and a
posteriori Decision},
author = {Daniele Menon and Stefano Andriani and Giancarlo
Calvagno},
journal = {IEEE Transactions on Image Processing},
year = 2007,
month = Jan,
volume = 16,
number = 1,
pages = {132--141},
doi = {10.1109/tip.2006.884928}
}
@Article {monga-2021-algorithm,
author = {Monga, Vishal and Li, Yuelong and Eldar, Yonina C.},
journal = {IEEE Signal Processing Magazine},
title = {Algorithm Unrolling: Interpretable, Efficient Deep
Learning for Signal and Image Processing},
year = 2021,
volume = 38,
number = 2,
pages = {18-44},
doi = {10.1109/MSP.2020.3016905}
}
@Book {nocedal-2006-numerical,
title = {Numerical Optimization},
author = {Jorge Nocedal and Stephen J. Wright},
year = 2006,
publisher = {Springer},
doi = {10.1007/978-0-387-40065-5},
isbn = 9780387303031
}
@Article {olufsen-2019-axitom,
title = {{AXITOM}: A {P}ython package for reconstruction of
axisymmetric tomograms acquired by a conical beam},
volume = 4,
doi = {10.21105/joss.01704},
number = 42,
journal = {Journal of Open Source Software},
author = {Olufsen, Sindre},
year = 2019,
month = oct,
pages = {1704}
}
@Book {paganin-2006-coherent,
doi = {10.1093/acprof:oso/9780198567288.001.0001},
isbn = 9780198567288,
year = 2006,
month = Jan,
publisher = {Oxford University Press},
author = {David Paganin},
title = {Coherent X-Ray Optics}
}
@Article {parikh-2014-proximal,
title = {Proximal algorithms},
author = {Parikh, Neal and Boyd, Stephen},
journal = {Foundations and Trends in optimization},
volume = 1,
number = 3,
pages = {127--239},
year = 2014,
doi = {10.1561/2400000003}
}
@InProceedings {pock-2011-diagonal,
author = {Thomas Pock and Antonin Chambolle},
title = {Diagonal preconditioning for first order primal-dual
algorithms in convex optimization},
booktitle = {Proceedings of the International Conference on
Computer Vision (ICCV)},
doi = {10.1109/iccv.2011.6126441},
pages = {1762--1769},
year = 2011,
month = Nov,
address = {Barcelona, Spain}
}
@Misc {pyabel-2022,
author = {Stephen Gibson and Daniel Hickstein and Roman
Yurchak, Mikhail Ryazanov and Dhrubajyoti Das and
Gilbert Shih},
title = {PyAbel},
howpublished = {PyAbel/PyAbel: v0.8.5},
year = 2022,
doi = {10.5281/zenodo.5888391}
}
@InProceedings {ronneberger-2015-unet,
author = {Olaf Ronneberger and Philipp Fischer and Thomas
Brox},
title = {{U}-{N}et: Convolutional Networks for Biomedical
Image Segmentation},
booktitle = {Proceedings of the 18th International Conference on
Medical Image Computing and Computer-Assisted
Intervention},
doi = {10.1007/978-3-319-24574-4_28},
volume = 9351,
pages = {234--241},
year = 2015,
month = Oct,
address = {Munich, Germany},
}
@Article {rudin-1992-nonlinear,
author = {Leonid I. Rudin and Stanley Osher and Emad Fatemi},
title = {Nonlinear total variation based noise removal
algorithms},
journal = {Physica D: Nonlinear Phenomena},
volume = 60,
number = {1--4},
pages = {259-268},
year = 1992,
doi = {10.1016/0167-2789(92)90242-F}
}
@Article {sauer-1993-local,
title = {A local update strategy for iterative reconstruction
from projections},
author = {Sauer, Ken and Bouman, Charles},
journal = {IEEE Transactions on Signal Processing},
year = 1993,
month = Feb,
number = 2,
pages = {534--548},
volume = 41,
doi = {10.1109/78.193196}
}
@Article {soulez-2016-proximity,
author = {Ferr{\'{e}}ol Soulez and {\'{E}}ric Thi{\'{e}}baut
and Antony Schutz and Andr{\'{e}} Ferrari and
Fr{\'{e}}d{\'{e}}ric Courbin and Michael Unser},
title = {Proximity operators for phase retrieval},
journal = {Applied Optics},
doi = {10.1364/ao.55.007412},
year = 2016,
month = Sep,
volume = 55,
number = 26,
pages = {7412--7421}
}
@Article {sreehari-2016-plug,
author = {Suhas Sreehari and Singanallur V. Venkatakrishnan
and Brendt Wohlberg and Gregery T. Buzzard and
Lawrence F. Drummy and Jeffrey P. Simmons and
Charles A. Bouman},
title = {Plug-and-Play Priors for Bright Field Electron
Tomography and Sparse Interpolation},
year = 2016,
month = Dec,
journal = {IEEE Transactions on Computational Imaging},
volume = 2,
number = 4,
doi = {10.1109/TCI.2016.2599778},
pages = {408--423}
}
@Misc {svmbir-2020,
author = {SVMBIR Development Team},
title = {{S}uper-{V}oxel {M}odel {B}ased {I}terative
{R}econstruction ({SVMBIR})},
howpublished = {Software library available from
\url{https://github.com/cabouman/svmbir}},
year = 2020
}
@Article {valkonen-2014-primal,
title = {A primal--dual hybrid gradient method for nonlinear
operators with applications to {MRI}},
author = {Valkonen, Tuomo},
journal = {Inverse Problems},
volume = 30,
number = 5,
pages = 055012,
year = 2014,
doi = {10.1088/0266-5611/30/5/055012}
}
@InProceedings {venkatakrishnan-2013-plugandplay2,
author = {Singanallur V. Venkatakrishnan and Charles A. Bouman
and Brendt Wohlberg},
title = {Plug-and-Play Priors for Model Based Reconstruction},
year = 2013,
month = Dec,
booktitle = {Proceedings of IEEE Global Conference on Signal and
Information Processing (GlobalSIP)},
address = {Austin, TX, USA},
doi = {10.1109/GlobalSIP.2013.6737048},
pages = {945--948}
}
@Article {voelz-2009-digital,
author = {David G. Voelz and Michael C. Roggemann},
title = {Digital Simulation of Scalar Optical Diffraction:
Revisiting Chirp Function Sampling Criteria and
Consequences},
journal = {Applied Optics},
volume = 48,
number = 32,
pages = 6132,
year = 2009,
doi = {10.1364/ao.48.006132},
}
@Book {voelz-2011-computational,
author = {Voelz, David},
title = {Computational {F}ourier optics : a {MATLAB}
tutorial},
year = 2011,
publisher = {SPIE Press},
address = {Bellingham, Wash},
isbn = 9780819482044,
}
@InProceedings {wohlberg-2014-efficient,
author = {Brendt Wohlberg},
title = {Efficient Convolutional Sparse Coding},
booktitle = {Proceedings of IEEE International Conference on
Acoustics, Speech, and Signal Processing (ICASSP)},
year = 2014,
month = May,
doi = {10.1109/ICASSP.2014.6854992},
pages = {7173--7177},
location = {Florence, Italy}
}
@Article {wohlberg-2021-psf,
author = {Brendt Wohlberg and Przemek Wozniak},
title = {PSF Estimation in Crowded Astronomical Imagery as a
Convolutional Dictionary Learning Problem},
year = 2021,
month = Feb,
journal = {IEEE Signal Processing Letters},
volume = 28,
doi = {10.1109/LSP.2021.3050706},
pages = {374--378}
}
@Article {yang-2012-linearized,
author = {Junfeng Yang and Xiaoming Yuan},
title = {Linearized augmented {L}agrangian and alternating
direction methods for nuclear norm minimization},
journal = {Mathematics of Computation},
doi = {10.1090/s0025-5718-2012-02598-1},
year = 2012,
month = Mar,
volume = 82,
number = 281,
pages = {301--329}
}
@InProceedings {yu-2013-better,
author = {Yu, Yao-Liang},
booktitle = {Advances in Neural Information Processing Systems},
editor = {C.J. Burges and L. Bottou and M. Welling and
Z. Ghahramani and K.Q. Weinberger},
title = {Better Approximation and Faster Algorithm Using the
Proximal Average},
url = {https://proceedings.neurips.cc/paper_files/paper/2013/file/49182f81e6a13cf5eaa496d51fea6406-Paper.pdf},
volume = 26,
year = 2013
}
@Article {zhang-2017-dncnn,
author = {Kai Zhang and Wangmeng Zuo and Yunjin Chen and Deyu
Meng and Lei Zhang},
title = {Beyond a {G}aussian Denoiser: Residual Learning of
Deep {CNN} for Image Denoising},
year = 2017,
month = Jul,
journal = {IEEE Transactions on Image Processing},
volume = 26,
number = 7,
doi = {10.1109/TIP.2017.2662206},
pages = {3142--3155}
}
@Article {zhang-2021-plug,
author = {Zhang, Kai and Li, Yawei and Zuo, Wangmeng and
Zhang, Lei and Van Gool, Luc and Timofte, Radu},
title = {Plug-and-Play Image Restoration With Deep Denoiser
Prior},
journal = {IEEE Transactions on Pattern Analysis and Machine
Intelligence},
year = 2022,
volume = 44,
number = 10,
doi = {10.1109/TPAMI.2021.3088914},
pages = {6360--6376}
}
@Article {zhou-2006-adaptive,
author = {Bin Zhou and Li Gao and Yu-Hong Dai},
title = {Gradient Methods with Adaptive Step-Sizes},
year = 2006,
month = Mar,
journal = {Computational Optimization and Applications},
volume = 35,
doi = {10.1007/s10589-006-6446-0},
pages = {69--86}
}
================================================
FILE: docs/source/style.rst
================================================
.. _scico_dev_style:
Style Guide
===========
Overview
--------
We adhere to `PEP8 `_ with
the exception of allowing a line length limit of 99 characters (as
opposed to 79 characters). The standard limit of 72 characters for
"flowing long blocks of text" in docstrings or comments is
retained. We use `Black `_ as our PEP-8
Formatter and `isort `_ to sort
imports. (Please set up a `pre-commit hook `_
to ensure any modified code passes format check before it is committed
to the development repo.)
We aim to incorporate `PEP 526
`_ type annotations
throughout the library. See the `Mypy
`_ type annotation `cheat
sheet `_
for usage examples. Custom types are defined in :mod:`.typing`.
Our coding conventions are based on both the `NumPy conventions
`_ and
the `Google docstring conventions
`_.
Unicode variable names are allowed for internal usage (e.g. for Greek
characters for mathematical symbols), but not as part of the public
interface for functions or methods.
Naming
------
We follow the `Google naming conventions `_:
.. list-table:: Naming Conventions
:widths: 20 20
:header-rows: 1
* - Component
- Naming Convention
* - Modules
- module_name
* - Package
- package_name
* - Class
- ClassName
* - Method
- method_name
* - Function
- function_name
* - Exception
- ExceptionName
* - Variable
- var_name
* - Parameter
- parameter_name
* - Constant
- CONSTANT_NAME
These names should be descriptive and unambiguous to avoid confusion
within the code and other modules in the future.
Example:
.. code:: Python
d = 6 # Day of the week == Saturday
if d < 5:
print("Weekday")
Here the code could be hard to follow since the name ``d`` is not
descriptive and requires extra comments to explain the code, which
would have been solved otherwise by good naming conventions.
Example:
.. code:: Python
fldln = 5 # field length
This could be improved by using the descriptive variable ``field_len``.
Things to avoid:
- Single character names except for the following special cases:
- counters or iterators (``i``, ``j``);
- `e` as an exception identifier (``Exception e``);
- `f` as a file in ``with`` statements;
- mathematical notation in which a reference to the paper or
algorithm with said notation is preferred if not clear from the
intended purpose.
- Trailing underscores unless the component is meant to be protected or private:
- protected: Use a single underscore, ``_``, for protected access; and
- pseudo-private: Use double underscores, ``__``, for
pseudo-private access via name mangling.
Displaying and Printing Strings
-------------------------------
We follow the `Google string conventions
`_. Notably,
prefer to use Python f-strings, rather than `.format` or `%`
syntax. For example:
.. code:: Python
state = "active"
print("The state is %s" % state) # Not preferred
print(f"The state is {state}") # Preferred
Imports
-------
We follow the `Google import conventions
`_. The
use of ``import`` statements should be reserved for packages and
modules only, i.e. individual classes and functions should not be
imported. The only exception to this is the typing module.
- Use ``import x`` for importing packages and modules, where x is the package or
module name.
- Use ``from x import y`` where x is the package name and y is the module name.
- Use ``from x import y as z`` if two modules named ``y`` are imported
or if ``y`` is too long of a name.
- Use ``import y as z`` when ``z`` is a standard abbreviation like
``import numpy as np``.
Variables
---------
We follow the `Google variable typing conventions
`_
which states that there are a few extra documentation and coding
practices that can be applied to variables such as:
- One may type a variables by using a ``: type`` before the function
value is assigned, e.g.,
.. code-block:: python
a: Foo = SomeDecoratedFunction()
- Avoid global variables.
- A function can refer to variables defined in enclosing functions but
cannot assign to them.
Parameters
----------
There are three important style components for parameters inspired by
the `NumPy parameter conventions
`_:
1. Typing
We use type annotations meaning we specify the types of the inputs
and outputs of any method. From the ``typing`` module we can use
more types such as ``Optional``, ``Union``, and ``Any``. For
example,
.. code-block:: python
def foo(a: str) -> str:
"""Takes an input of type string and returns a value of type string"""
...
2. Default Values
Parameters should include ``parameter_name = value`` where value is
the default for that particular parameter. If the parameter has a
type then the format is ``parameter_name: Type = value``. When
documenting parameters, if a parameter can only assume one of a
fixed set of values, those values can be listed in braces, with the
default appearing first. For example,
.. code-block:: python
"""
letters: {'A', 'B, 'C'}
Description of `letters`.
"""
3. NoneType
In Python, ``NoneType`` is a first-class type, meaning the type
itself can be passed into and returned from functions. ``None`` is
the most commonly used alias for ``NoneType``. If any of the
parameters of a function can be ``None`` then it has to be
declared. ``Optional[T]`` is preferred over ``Union[T, None]``.
For example,
.. code-block:: python
def foo(a: Optional[str], b: Optional[Union[str, int]]) -> str:
...
For documentation purposes, ``NoneType`` or ``None`` should be
written with double backticks.
Docstrings
----------
Docstrings are a way to document code within Python and it is the
first statement within a package, module, class, or function. To
generate a document with all the documentation for the code use `pydoc
`_.
Typing
~~~~~~
We follow the `NumPy parameter conventions
`_. The
following are docstring-specific usages:
- Always enclose variables in single backticks.
- For the parameter types, be as precise as possible, do not use backticks.
Modules
~~~~~~~
We follow the `Google module conventions
`_. Notably,
files must start with a docstring that describes the functionality of
the module. For example,
.. code-block:: python
"""A one-line summary of the module must be terminated by a period.
Leave a blank line and describe the module or program. Optionally
describe exported classes, functions, and/or usage examples.
Usage Example:
foo = ClassFoo()
bar = foo.FunctionBar()
""""
Functions
~~~~~~~~~
The word *function* encompasses functions, methods, or generators in
this section. The docstring should give enough information to make
calls to the function without needing to read the functions code.
We follow the `Google function conventions
`_.
Notably, functions should contain docstrings unless:
- not externally visible (the function name is prefaced with an underscore) or
- very short.
The docstring should be imperative-style ``"""Fetch rows from a
Table"""`` instead of the descriptive-style ``"""Fetches rows from a
Table"""``. If the method overrides a method from a base class then it
may use a simple docstring referencing that base class such as
``"""See base class"""``, unless the behavior is different from the
overridden method or there are extra details that need to be
documented.
| There are three sections to function docstrings:
- Args:
- List each parameter by name, and include a description for each parameter.
- Returns: (or Yield in the case of generators)
- Describe the type of the return value. If a function only
returns ``None`` then this section is not required.
- Raises:
- List all exceptions followed by a description. The name and
description should be separated by a colon followed by a space.
Example:
.. code-block:: python
def fetch_smalltable_rows(table_handle: smalltable.Table,
keys: Sequence[Union[bytes, str]],
require_all_keys: bool = False,
) -> Mapping[bytes, Tuple[str]]:
"""Fetch rows from a Smalltable.
Retrieve rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
table_handle:
An open smalltable.Table instance.
keys:
A sequence of strings representing the key of each table
row to fetch. String `keys` will be UTF-8 encoded.
require_all_keys: Optional
If `require_all_keys` is ``True`` only
rows with values set for all keys will be returned.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).
Raises:
IOError: An error occurred accessing the smalltable.
"""
Classes
~~~~~~~
We follow the `Google class conventions
`_. Classes,
like functions, should have a docstring below the definition
describing the class and the class functionality. If the class
contains public attributes, the class should have an attributes
section where each attribute is listed by name and followed by a
description, separated by a colon, like for function parameters. For
example,
| Example:
.. code:: Python
class foo:
"""One-liner describing the class.
Additional information or description for the class.
Can be multi-line
Attributes:
attr1: First attribute of the class.
attr2: Second attribute of the class.
"""
def __init__(self):
"""Should have a docstring of type function."""
pass
def method(self):
"""Should have a docstring of type: function."""
pass
Extra Sections
~~~~~~~~~~~~~~
We follow the `NumPy style guide
`_. Notably,
the following are sections that can be added to functions, modules,
classes, or method definitions.
- See Also:
- Refers to related code. Used to direct users to other modules,
functions, or classes that they may not be aware of.
- When referring to functions in the same sub-module, no prefix is
needed. Example: For ``numpy.mean`` inside the same sub-module:
.. code-block:: python
"""
See Also
--------
average: Weighted average.
"""
- For a reference to ``fft`` in another module:
.. code-block:: python
"""
See Also
--------
fft.fft2: 2-D fast discrete Fourier transform.
"""
- Notes
- Provide additional information about the code. May include
mathematical equations in LaTeX format. For example,
.. code-block:: python
"""
Notes
-----
The FFT is a fast implementation of the discrete Fourier transform:
.. math::
X(e^{j\omega } ) = x(n)e^{ - j\omega n}
"""
Math can also be used inline:
.. code-block:: python
"""
Notes
-----
The value of :math:`\omega` is larger than 5.
"""
For a list of available LaTex macros, search for "macros" in
`docs/source/conf.py `_.
- Examples:
- Uses the doctest format and is meant to showcase usage.
- If there are multiple examples include blank lines before and
after each example. For example,
.. code-block:: python
"""
Examples
--------
Necessary imports
>>> import numpy as np
Comment explaining example 1.
>>> int(np.add(1, 2))
3
Comment explaining a new example.
>>> np.add([1, 2], [3, 4])
array([4, 6])
If the example is too long then each line after the first start it
with a ``...``
>>> np.add([[1, 2], [3, 4]],
... [[5, 6], [7, 8]])
array([[ 6, 8],
[10, 12]])
"""
Comments
~~~~~~~~
There are two types of comments: *block* and *inline*. A good rule of
thumb to follow for when to include a comment in your code is *if you
have to explain it or is too hard to figure out at first glance, then
comment it*. An example of this, taken from the `Google comment
conventions
`_,
is complicated operations which most likely require a block of
comments beforehand.
.. code-block:: Python
# We use a block comment because the following code performs a
# difficult operation. Here we can explain the variables or
# what the concept of the operation does in an easier
# to understand way.
i = i & (i-1) == 0: # true if i is 0 or a power of 2 [explains the concept not the code]
If a comment consists of one or more full sentences (as is typically
the case for *block* comments), it should start with an upper case
letter and end with a period. *Inline* comments often consist of a
brief phrase which is not a full sentence, in which case they should
have a lower case initial letter and not have a terminating period.
Markup
~~~~~~
The following components require the recommended markup taken from the
`NumPy Conventions
`__.:
- Paragraphs:
Indentation is significant and indicates the indentation of the output. New
paragraphs are marked with a blank line.
- Variable, parameter, module, function, method, and class names:
Should be written between single back-ticks (e.g. \`x\`, rendered as `x`), but
note that use of `Sphinx cross-reference syntax `_ is preferred for modules (`:mod:\`module-name\`` ), functions (`:func:\`function-name\`` ), methods (`:meth:\`method-name\`` ) and classes (`:class:\`class-name\`` ).
- None, NoneType, True, and False:
Should be written between double back-ticks (e.g. \`\`None\`\`, \`\`True\`\`,
rendered as ``None``, ``True``).
- Types:
Should be written between double back-ticks (e.g. \`\`int\`\`, rendered as ``int``).
NumPy dtypes, however, should be written using cross-reference syntax, e.g.
\:attr\:\`~numpy.float32\` for :attr:`~numpy.float32`.
Other components can use \*italics\*, \*\*bold\*\*, and \`\`monospace\`\`
(respectively rendered as *italics*, **bold**, and ``monospace``) if needed, but
not for variable names, doctest code, or multi-line code.
Documentation
-------------
Documentation that is separate from code (like this page) should follow the
`IEEE Style Manual
`_.
For additional grammar and usage guidance,
refer to `The Chicago Manual of Style `_.
A few notable guidelines:
* Equations which conclude a sentence should end with a period,
e.g., "Poisson's equation is
.. math::
\Delta \varphi = f \;."
* Do not capitalize acronyms or inititalisms when defining them,
e.g., "computer-aided system engineering (CASE),"
"fast Fourier transform (FFT)."
* Avoid capitalization in text except where absolutely necessary,
e.g., "Newton’s first law."
* Use a single space after the period at the end of a sentence.
The source code (`.rst` files) for these pages does not have a hard
line-length guideline, but line breaks at or before 79 characters are
encouraged.
================================================
FILE: docs/source/team.rst
================================================
Developers
==========
Core Developers
---------------
- `Cristina Garcia Cardona `_
- `Michael McCann `_
- `Brendt Wohlberg `_
Emeritus Developers
-------------------
- `Thilo Balke `_
- `Fernando Davis `_
- `Soumendu Majee `_
- `Luke Pfister `_
Contributors
------------
- `Weijie Gan `_ (Non-blind variant of DnCNN)
- `Oleg Korobkin `_ (BlockArray improvements)
- `Andrew Leong `_ (Improvements to optics module documentation)
- `Saurav Maheshkar `_ (Improvements to pre-commit configuration)
- `Yanpeng Yuan `_ (ASTRA interface improvements)
- `Li-Ta (Ollie) Lo `_ (ASTRA interface improvements)
- `Renat Sibgatulin `_ (Docs corrections)
- `Salman Naqvi `_ (Contributions to approximate TV norm prox and proximal average implementation)
- `Eddie Chandler `_ (Contributions to approximate isotropic TV norm prox)
================================================
FILE: docs/source/zreferences.rst
================================================
References
==========
.. bibliography:: references.bib
:style: plain
================================================
FILE: docs/tikxfigures/img_align.tex
================================================
\documentclass[tikz]{standalone}
\usetikzlibrary{calc,angles,quotes}
\begin{document}
\begin{tikzpicture}[scale=2]
\footnotesize
% Define rectangle dimensions
\def\width{2} % base width
\def\aspect{1.25} % aspect ratio (height/width)
\pgfmathsetmacro{\height}{\width*\aspect}
% Rotate rectangle by 20 degrees
\begin{scope}[rotate around={-20:(0,0)}]
% Draw rectangle with bottom-left corner at origin
\draw[thick] (0,0) -| (\width,\height)
node[pos=0.25,below] {$N_1$}
node[pos=0.75,right] {$N_0$}
-| (0,0);
% Save post-rotation rectangle corners
\coordinate (BL) at (0,0);
\coordinate (BR) at (\width,0);
\coordinate (TL) at (0,\height);
\coordinate (TR) at (\width,\height);
\end{scope}
\def\liney{2.5} % top line height
\coordinate (PL) at (BL |- 0,\liney); % vertical intersection from bottom-left
\coordinate (PR) at (TR |- 0,\liney); % vertical intersection from top-right
% Horizontal line representing sensor
\draw[blue,thick] (PL) -- (PR);
% Draw verticals to meet horizontal line
\draw[blue,thick,dashed] (BL) -- (PL);
\draw[blue,thick,dashed] (TR) -- (PR);
% Double-sided arrow for width label
\draw[<->,blue,dashed] (PL) ++(0,0.15) -- ($(PR)+(0,0.15)$)
node[midway,above] {$w_0 + w_1$};
% Central vertical line through top-left corner
\draw[blue,thick,dashed] (TL |- BL) -- (TL |- 0,\liney);
% Horizontal lines with labels
\draw[blue,thick,dashed] (TR) -- (TL |- TR) node[midway,below] {$w_1 = N_1 \cos(\theta)$};
\draw[blue,thick,dashed] (BL) -- (TL |- BL) node[right,below] {$\qquad w_0 = N_0 \sin(\theta)$};
% Define intersection point with central vertical line
\coordinate (VL) at (TL |- BR);
\coordinate (HL) at (TL |- TR);
% θ between left rectangle side and central vertical line
\pic [draw, ->, "$\theta$", angle radius=30] {angle = BL--TL--VL};
% 90-θ between top rectangle side and horizontal line
\pic [draw, ->, "$90\!-\!\theta\quad\;\;$", angle radius=50] {angle = TL--TR--HL};
\end{tikzpicture}
\end{document}
================================================
FILE: docs/tikxfigures/makesvg.sh
================================================
#! /bin/bash
pdf2svg vol_align_xyz.pdf vol_align_xyz.svg
pdf2svg vol_align_xz.pdf vol_align_xz.svg
pdf2svg vol_align_yz.pdf vol_align_yz.svg
pdf2svg img_align.pdf img_align.svg
================================================
FILE: docs/tikxfigures/vol_align_xyz.tex
================================================
\documentclass{standalone}
\usepackage{tikz, tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{70}{110}
\begin{tikzpicture}[scale=5,tdplot_main_coords]
\footnotesize
\draw[thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};
\draw[thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};
\draw[thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};
\coordinate (O) at (0,0,0);
\tdplotsetcoord{P}{1}{30}{40}
\draw[-stealth,thick,color=red] (O) -- (P);
\node[draw=none,color=red] at (0.0,0.11,0.77) {$(x, y, z)$};
\draw[dashed, color=blue] (P) -- (Pxz);
\draw[dashed, color=blue] (P) -- (Pyz);
\draw[dashed, color=blue] (O) -- (Pxz);
\draw[dashed, color=blue] (O) -- (Pyz);
\draw[dashed, color=blue] (Pz) -- (Pxz);
\draw[dashed, color=blue] (Pz) -- (Pyz);
\node[draw=none,color=blue] at (0.38,0.0,0.5) {$r_x$};
\node[draw=none,color=blue] at (0.0,0.24,0.4) {$r_y$};
\tdplotsetthetaplanecoords{0}
\tdplotdrawarc[tdplot_rotated_coords,blue,dotted]{(O)}{.25}{22.7}{90}{anchor=mid east}{$\theta_x$}
\tdplotsetthetaplanecoords{90}
\tdplotdrawarc[tdplot_rotated_coords,blue,dotted]{(O)}{.25}{23}{90}{anchor=mid west}{$\theta_y$}
\end{tikzpicture}
\end{document}
================================================
FILE: docs/tikxfigures/vol_align_xz.tex
================================================
\documentclass{standalone}
\usepackage{tikz, tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{90}{0}
\begin{tikzpicture}[scale=5,tdplot_main_coords]
\footnotesize
\draw[thick,->] (0,0,0) -- (1,0,0) node[anchor=west]{$x$};
\draw[thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};
\coordinate (O) at (0,0,0);
\tdplotsetcoord{P}{1}{30}{40}
\draw[-stealth,thick,color=red] (O) -- (P) node[anchor=west]{$\!(x, z)$};
\draw[dashed, color=blue] (P) -- (Pyz);
\draw[dashed, color=blue] (P) -- (Px);
\node[draw=none,color=blue] at (0.4,0.0,-0.055) {$r_x \cos (\theta_x)$};
\node[draw=none,rotate=90,color=blue] at (-0.055,0.0,0.84) {$r_x \sin (\theta_x)$};
\tdplotsetthetaplanecoords{0}
\tdplotdrawarc[tdplot_rotated_coords,red,dotted]{(O)}{.25}{22.7}{90}{anchor=north east}{$\theta_x$}
\node[draw=none,color=red] at (0.14,0.0,0.5) {$r_x$};
\end{tikzpicture}
\end{document}
================================================
FILE: docs/tikxfigures/vol_align_yz.tex
================================================
\documentclass{standalone}
\usepackage{tikz, tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{90}{90}
\begin{tikzpicture}[scale=5,tdplot_main_coords]
\footnotesize
\draw[thick,->] (0,0,0) -- (0,1,0) node[anchor=west]{$y$};
\draw[thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};
\coordinate (O) at (0,0,0);
\tdplotsetcoord{P}{1}{30}{40}
\draw[-stealth,thick,color=red] (O) -- (P) node[anchor=west]{$\!(y, z)$};
\draw[dashed, color=blue] (P) -- (Pxz);
\draw[dashed, color=blue] (P) -- (Py);
\node[draw=none,color=blue] at (0.0,0.35,-0.055) {$r_y \cos (\theta_y)$};
\node[draw=none,rotate=90,color=blue] at (0.0,-0.055,0.84) {$r_y \sin (\theta_y)$};
\tdplotsetthetaplanecoords{90}
\tdplotdrawarc[tdplot_rotated_coords,red,dotted]{(O)}{.25}{23}{90}{anchor=north east}{$\theta_y$}
\node[draw=none,color=red] at (0.05,0.11,0.5) {$r_y$};
\end{tikzpicture}
\end{document}
================================================
FILE: examples/README.rst
================================================
SCICO Usage Examples
====================
This directory contains usage examples for the SCICO package. The primary form of these examples is the Python scripts in the directory ``scripts``. A corresponding set of Jupyter notebooks, in the directory ``notebooks``, is auto-generated from these usage example scripts.
Building Notebooks
------------------
The scripts for building Jupyter notebooks from the source example scripts are currently only supported under Linux. All scripts described below should be run from this directory, i.e. ``[repo root]/examples``.
Running on a GPU
^^^^^^^^^^^^^^^^
Since some of the examples require a considerable amount of memory (``deconv_microscopy_tv_admm.py`` and ``deconv_microscopy_allchn_tv_admm.py`` in particular), it is recommended to set the following environment variables prior to building the notebooks:
::
export XLA_PYTHON_CLIENT_ALLOCATOR=platform
export XLA_PYTHON_CLIENT_PREALLOCATE=false
Running on a CPU
^^^^^^^^^^^^^^^^
If a GPU is not available, or if the available GPU does not have sufficient memory to build the notebooks, set the environment variable
::
JAX_PLATFORM_NAME=cpu
to run on the CPU instead.
Building Specific Examples
--------------------------
To build or rebuild notebooks for specific examples, the example script names can be specified on the command line, e.g.
::
python makenotebooks.py ct_astra_pcg.py ct_astra_tv_admm.py
When rebuilding notebooks for examples that themselves make use of ``ray``
for parallelization (e.g. ``deconv_microscopy_allchn_tv_admm.py``), it is recommended to specify serial notebook execution, as in
::
python makenotebooks.py --no-ray deconv_microscopy_allchn_tv_admm.py
Building All Examples
---------------------
By default, ``makenotebooks.py`` only rebuilds notebooks that are out of date with respect to their corresponding example scripts, as determined by their respective file timestamps. However, timestamps for files retrieved from version control may not be meaningful for this purpose. To rebuild all examples, the following commands (assuming that GPUs are available) are recommended:
::
export XLA_PYTHON_CLIENT_ALLOCATOR=platform
export XLA_PYTHON_CLIENT_PREALLOCATE=false
touch scripts/*.py
python makenotebooks.py --no-ray deconv_microscopy_tv_admm.py deconv_microscopy_allchn_tv_admm.py
python makenotebooks.py
Updating Notebooks in the Repo
------------------------------
The recommended procedure for rebuilding notebooks for inclusion in the ``data`` submodule is:
1. Add and commit the modified script(s).
2. Rebuild the notebooks as described above.
2. Add and commit the updated notebooks following the submodule handling procedure described in the developer docs.
Adding a New Notebook
---------------------
The procedure for adding a adding a new notebook is:
1. Add an entry for the source file in ``scripts/index.rst``. Note that a script that is not listed in this index will not be converted into a notebook.
2. Run ``makeindex.py`` to update the example scripts README file, the notebook index file, and the examples index in the docs.
3. Build the corresponding notebook following the instructions above.
4. Add and commit the new script, the ``scripts/index.rst`` script index file, the auto-generated ``scripts/README.rst`` file and ``docs/source/examples.rst`` index file, and the new or updated notebooks and the auto-generated ``notebooks/index.ipynb`` file in the notebooks directory, following the submodule handling procedure as described in the developer docs.
Management Utilities
--------------------
A number of files in this directory assist in the mangement of the usage examples:
`examples_requirements.txt `_
Requirements file (as used by ``pip``) listing additional dependencies for running the usage example scripts.
`notebooks_requirements.txt `_
Requirements file (as used by ``pip``) listing additional dependencies for building the Jupyter notebooks from the usage example scripts.
`makenotebooks.py `_
Auto-generate Jupyter notebooks from the example scripts.
`updatejnbmd.py `_
Update markdown cells in notebooks from corresponding example scripts.
`makeindex.py `_
Auto-generate the docs example index ``docs/source/examples.rst`` from the example scripts index ``scripts/index.rst``.
`scriptcheck.sh `_
Run all example scripts with smaller problems and a reduced number of iterations as a rapid check that they are functioning correctly.
================================================
FILE: examples/examples_requirements.txt
================================================
-r ../requirements.txt
colorama
colour_demosaicing
svmbir>=0.4.0
astra-toolbox
xdesign>=0.5.5
ray[tune,train]>=2.44
hyperopt
setuptools<82.0.0 # workaround for hyperopt 0.2.7
pydantic
orbax-checkpoint>=0.5.0
bm3d>=4.0.0
bm4d>=4.2.2
================================================
FILE: examples/jnb.py
================================================
# -*- coding: utf-8 -*-
# Copyright (C) 2022-2024 by SCICO Developers
# All rights reserved. BSD 3-clause License.
# This file is part of the SCICO package. Details of the copyright and
# user license can be found in the 'LICENSE' file distributed with the
# package.
"""Support functions for manipulating Jupyter notebooks."""
import re
from timeit import default_timer as timer
import nbformat
from nbconvert.preprocessors import CellExecutionError, ExecutePreprocessor
from py2jn.tools import py_string_to_notebook, write_notebook
def py_file_to_string(src):
"""Preprocess example script file and return result as a string."""
with open(src, "r") as srcfile:
# Drop header comment
for line in srcfile:
if line[0] != "#":
break # assume first non-comment line is a newline that can be dropped
# Insert notebook plot config after last import
lines = []
import_seen = False
for line in srcfile:
line = re.sub('^r"""', '"""', line) # remove r from r"""
line = re.sub(":cite:`([^`]+)`", r'', line) # fix cite format
if import_seen:
# Once an import statement has been seen, break on encountering a line that
# is neither an import statement nor a newline, nor a component of an import
# statement extended over multiple lines, nor an os.environ statement, nor a
# ray.init statement, nor components of a try/except construction (note that
# handling of these final two cases is probably not very robust).
if not re.match(
r"(^import|^from|^\n$|^\W+[^\W]|^\)$|^os.environ|^ray.init|^try:$|^except)",
line,
):
lines.append(line)
break
else:
# Set flag indicating that an import statement has been seen once one has
# been encountered
if re.match("^import|^from .* import", line):
import_seen = True
lines.append(line)
if "plot" in "".join(lines):
# Backtrack through list of lines to find last import statement
n = 1
for line in lines[-2::-1]:
if re.match("^(import|from)", line):
break
else:
n += 1
# Insert notebook plotting config directly after last import statement
lines.insert(-n, "plot.config_notebook_plotting()\n")
# Process remainder of source file
for line in srcfile:
if re.match(r"^input\(", line): # end processing when input statement encountered
break
line = re.sub('^r"""', '"""', line) # remove r from r"""
line = re.sub(r":cite:\`([^`]+)\`", r'', line) # fix cite format
lines.append(line)
# Backtrack through list of lines to remove trailing newlines
n = 0
for line in lines[::-1]:
if re.match("^\n$", line):
n += 1
else:
break
if n > 0:
lines = lines[0:-n]
return "".join(lines)
def script_to_notebook(src, dst):
"""Convert a Python example script into a Jupyter notebook."""
s = py_file_to_string(src)
nb = py_string_to_notebook(s)
write_notebook(nb, dst)
def read_notebook(fname):
"""Read a notebook from the specified notebook file."""
try:
nb = nbformat.read(fname, as_version=4)
except (AttributeError, nbformat.reader.NotJSONError):
raise RuntimeError("Error reading notebook file %s." % fname)
return nb
def execute_notebook(fname):
"""Execute the specified notebook file."""
with open(fname) as f:
nb = nbformat.read(f, as_version=4)
ep = ExecutePreprocessor(timeout=None)
try:
t0 = timer()
out = ep.preprocess(nb)
t1 = timer()
with open(fname, "w", encoding="utf-8") as f:
nbformat.write(nb, f)
except CellExecutionError:
print(f"ERROR executing {fname}")
return False
print(f"{fname} done in {(t1 - t0):.1e} s")
return True
def notebook_executed(nbfn):
"""Determine whether the notebook at `nbfn` has been executed."""
try:
nb = nbformat.read(nbfn, as_version=4)
except (AttributeError, nbformat.reader.NotJSONError):
raise RuntimeError("Error reading notebook file %s." % pth)
cells = nb["worksheets"][0]["cells"]
for n in range(len(nb["cells"])):
if cells[n].cell_type == "code" and cells[n].execution_count is None:
return False
return True
def same_notebook_code(nb1, nb2):
"""Return ``True`` if the code cells of notebook objects `nb1` and `nb2`
are all the same.
"""
if "cells" in nb1:
nb1c = nb1["cells"]
else:
nb1c = nb1["worksheets"][0]["cells"]
if "cells" in nb2:
nb2c = nb2["cells"]
else:
nb2c = nb2["worksheets"][0]["cells"]
# Notebooks do not match if the number of cells differ
if len(nb1c) != len(nb2c):
return False
# Iterate over cells in nb1
for n in range(len(nb1c)):
# Notebooks do not match if corresponding cells have different
# types
if nb1c[n]["cell_type"] != nb2c[n]["cell_type"]:
return False
# Notebooks do not match if source of corresponding code cells
# differ
if nb1c[n]["cell_type"] == "code" and nb1c[n]["source"] != nb2c[n]["source"]:
return False
return True
def same_notebook_markdown(nb1, nb2):
"""Return ``True`` if the markdown cells of notebook objects `nb1`
and `nb2` are all the same.
"""
if "cells" in nb1:
nb1c = nb1["cells"]
else:
nb1c = nb1["worksheets"][0]["cells"]
if "cells" in nb2:
nb2c = nb2["cells"]
else:
nb2c = nb2["worksheets"][0]["cells"]
# Notebooks do not match if the number of cells differ
if len(nb1c) != len(nb2c):
return False
# Iterate over cells in nb1
for n in range(len(nb1c)):
# Notebooks do not match if corresponding cells have different
# types
if nb1c[n]["cell_type"] != nb2c[n]["cell_type"]:
return False
# Notebooks do not match if source of corresponding code cells
# differ
if nb1c[n]["cell_type"] == "markdown" and nb1c[n]["source"] != nb2c[n]["source"]:
return False
return True
def replace_markdown_cells(src, dst):
"""Overwrite markdown cells in notebook object `dst` with corresponding
cells in notebook object `src`.
"""
if "cells" in src:
srccell = src["cells"]
else:
srccell = src["worksheets"][0]["cells"]
if "cells" in dst:
dstcell = dst["cells"]
else:
dstcell = dst["worksheets"][0]["cells"]
# It is an error to attempt markdown replacement if src and dst
# have different numbers of cells
if len(srccell) != len(dstcell):
raise ValueError("Notebooks do not have the same number of cells.")
# Iterate over cells in src
for n in range(len(srccell)):
# It is an error to attempt markdown replacement if any
# corresponding pair of cells have different type
if srccell[n]["cell_type"] != dstcell[n]["cell_type"]:
raise ValueError("Cell number %d of different type in src and dst.")
# If current src cell is a markdown cell, copy the src cell to
# the dst cell
if srccell[n]["cell_type"] == "markdown":
dstcell[n]["source"] = srccell[n]["source"]
def remove_error_output(src):
"""Remove output to stderr from all cells in `src`."""
if "cells" in src:
cells = src["cells"]
else:
cells = src["worksheets"][0]["cells"]
modified = False
for c in cells:
if "outputs" in c:
dellist = []
for n, out in enumerate(c["outputs"]):
if "name" in out and out["name"] == "stderr":
dellist.append(n)
modified = True
for n in dellist[::-1]:
del c["outputs"][n]
return modified
================================================
FILE: examples/makeindex.py
================================================
#!/usr/bin/env python
# Construct an index README file and a docs example index file from
# source index file "scripts/index.rst".
# Run as
# python makeindex.py
import re
from pathlib import Path
import nbformat as nbf
import py2jn
import pypandoc
src = "scripts/index.rst"
# Make dict mapping script names to docstring header titles
titles = {}
scripts = list(Path("scripts").glob("*py"))
for s in scripts:
prevline = None
with open(s, "r") as sfile:
for line in sfile:
if line[0:3] == "===":
titles[s.name] = prevline.rstrip()
break
else:
prevline = line
# Build README in scripts directory
dst = "scripts/README.rst"
with open(dst, "w") as dstfile:
with open(src, "r") as srcfile:
for line in srcfile:
# Detect lines containing script filenames
m = re.match(r"(\s+)- ([^\s]+.py)", line)
if m:
prespace = m.group(1)
name = m.group(2)
title = titles[name]
print(
"%s`%s <%s>`_\n%s %s" % (prespace, name, name, prespace, title), file=dstfile
)
else:
print(line, end="", file=dstfile)
# Build notebooks index file in notebooks directory
dst = "notebooks/index.ipynb"
rst_text = ""
with open(src, "r") as srcfile:
for line in srcfile:
# Detect lines containing script filenames
m = re.match(r"(\s+)- ([^\s]+).py", line)
if m:
prespace = m.group(1)
name = m.group(2)
title = titles[name + ".py"]
rst_text += "%s- `%s <%s.ipynb>`_\n" % (prespace, title, name)
else:
rst_text += line
# Convert text from rst to markdown
md_format = "markdown_github+tex_math_dollars+fenced_code_attributes"
md_text = pypandoc.convert_text(rst_text, md_format, format="rst", extra_args=["--atx-headers"])
md_text = '"""' + md_text + '"""'
# Convert from python to notebook format and write notebook
nb = py2jn.py_string_to_notebook(md_text)
py2jn.tools.write_notebook(nb, dst, nbver=4)
nb = nbf.read(dst, nbf.NO_CONVERT)
nb.metadata = {"nbsphinx": {"orphan": True}}
nbf.write(nb, dst)
# Build examples index for docs
dst = "../docs/source/examples.rst"
prfx = "examples/"
with open(dst, "w") as dstfile:
print(".. _example_notebooks:\n", file=dstfile)
with open(src, "r") as srcfile:
for line in srcfile:
# Add toctree and include statements after main heading
if line[0:3] == "===":
print(line, end="", file=dstfile)
print("\n.. toctree::\n :maxdepth: 1", file=dstfile)
print("\n.. include:: include/examplenotes.rst", file=dstfile)
continue
# Detect lines containing script filenames
m = re.match(r"(\s+)- ([^\s]+).py", line)
if m:
print(" " + prfx + m.group(2), file=dstfile)
else:
print(line, end="", file=dstfile)
# Add toctree statement after section headings
if line[0:3] == line[0] * 3 and line[0] in ["=", "-", "^"]:
print("\n.. toctree::\n :maxdepth: 1", file=dstfile)
================================================
FILE: examples/makenotebooks.py
================================================
#!/usr/bin/env python
# Extract a list of Python scripts from "scripts/index.rst" and
# create/update and execute any Jupyter notebooks that are out
# of date with respect to their source Python scripts. If script
# names specified on command line, process them instead.
# Run
# python makenotebooks.py -h
# for usage details.
import argparse
import os
import re
import signal
import sys
from pathlib import Path
import psutil
from jnb import execute_notebook, script_to_notebook
examples_dir = Path(__file__).resolve().parent # absolute path to ../scico/examples/
have_ray = True
try:
import ray
except ImportError:
have_ray = False
def script_uses_ray(fname):
"""Determine whether a script uses ray."""
with open(fname, "r") as f:
text = f.read()
return bool(re.search("^import ray", text, re.MULTILINE)) or bool(
re.search("^import scico.ray", text, re.MULTILINE)
)
def script_path(sname):
"""Get script path from script name."""
return examples_dir / "scripts" / Path(sname)
def notebook_path(sname):
"""Get notebook path from script path."""
return examples_dir / "notebooks" / Path(Path(sname).stem + ".ipynb")
argparser = argparse.ArgumentParser(
description="Convert Python example scripts to Jupyter notebooks."
)
argparser.add_argument(
"--all",
action="store_true",
help="Process all notebooks, without checking timestamps. "
"Has no effect when files to process are explicitly specified.",
)
argparser.add_argument(
"--no-exec", action="store_true", help="Create/update notebooks but don't execute them."
)
argparser.add_argument(
"--no-ray",
action="store_true",
help="Execute notebooks serially, without the use of ray parallelization.",
)
argparser.add_argument(
"--verbose",
action="store_true",
help="Verbose operation.",
)
argparser.add_argument(
"--test",
action="store_true",
help="Show actions that would be taken but don't do anything.",
)
argparser.add_argument("filename", nargs="*", help="Optional Python example script filenames")
args = argparser.parse_args()
# Raise error if ray needed but not present
if not have_ray and not args.no_ray:
raise RuntimeError("The ray package is required to run this script, try --no-ray")
if args.filename:
# Script names specified on command line
scriptnames = [os.path.basename(s) for s in args.filename]
else:
# Read script names from index file
scriptnames = []
srcidx = examples_dir / "scripts" / "index.rst"
with open(srcidx, "r") as idxfile:
for line in idxfile:
m = re.match(r"(\s+)- ([^\s]+.py)", line)
if m:
scriptnames.append(m.group(2))
# Ensure list entries are unique
scriptnames = sorted(list(set(scriptnames)))
# Create list of selected scripts.
scripts = []
for s in scriptnames:
spath = script_path(s)
npath = notebook_path(s)
# If scripts specified on command line or --all flag specified, convert all scripts.
# Otherwise, only convert scripts that have a newer timestamp than their corresponding
# notebooks, or that have not previously been converted (i.e. corresponding notebook
# file does not exist).
if (
args.all
or args.filename
or not npath.is_file()
or spath.stat().st_mtime > npath.stat().st_mtime
):
# Add to the list of selected scripts
scripts.append(s)
if not scripts:
if args.verbose:
print("No scripts require conversion")
sys.exit(0)
# Display status information
if args.verbose:
print(f"Processing scripts {', '.join(scripts)}")
# Convert selected scripts to corresponding notebooks and determine which can be run in parallel
serial_scripts = []
parallel_scripts = []
for s in scripts:
spath = script_path(s)
npath = notebook_path(s)
# Determine how script should be executed
if script_uses_ray(spath):
serial_scripts.append(s)
else:
parallel_scripts.append(s)
# Make notebook file
if args.verbose or args.test:
print(f"Converting script {s} to notebook")
if not args.test:
script_to_notebook(spath, npath)
if args.no_exec:
if args.verbose:
print("Notebooks will not be executed")
sys.exit(0)
# If ray disabled or not worth using, run all serially
if args.no_ray or len(parallel_scripts) < 2:
serial_scripts.extend(parallel_scripts)
parallel_scripts = []
# Execute notebooks corresponding to serial_scripts
for s in serial_scripts:
npath = notebook_path(s)
if args.verbose or args.test:
print(f"Executing notebook corresponding to script {s}")
if not args.test:
execute_notebook(npath)
# Execute notebooks corresponding to parallel_scripts
if parallel_scripts:
if args.verbose or args.test:
print(
f"Notebooks corresponding to scripts {', '.join(parallel_scripts)} will "
"be executed in parallel"
)
# Execute notebooks in parallel using ray
nproc = len(parallel_scripts)
ray.init()
ngpu = 0
ar = ray.available_resources()
ncpu = max(int(ar["CPU"]) // nproc, 1)
if "GPU" in ar:
ngpu = max(int(ar["GPU"]) // nproc, 1)
if args.verbose or args.test:
print(f" Running on {ncpu} CPUs and {ngpu} GPUs per process")
# Function to execute each notebook with available resources suitably divided
@ray.remote(num_cpus=ncpu, num_gpus=ngpu)
def ray_run_nb(fname):
execute_notebook(fname)
if not args.test:
# Execute relevant notebooks in parallel
try:
notebooks = [notebook_path(s) for s in parallel_scripts]
objrefs = [ray_run_nb.remote(nbfile) for nbfile in notebooks]
ray.wait(objrefs, num_returns=len(objrefs))
except KeyboardInterrupt:
print("\nTerminating on keyboard interrupt")
for ref in objrefs:
ray.cancel(ref, force=True)
ray.shutdown()
# Clean up sub-processes not ended by ray.cancel
process = psutil.Process()
children = process.children(recursive=True)
for child in children:
os.kill(child.pid, signal.SIGTERM)
================================================
FILE: examples/notebooks_requirements.txt
================================================
-r examples-requirements.txt
ipykernel
ipywidgets
nbformat
nbconvert
nb_conda_kernels<=2.5.1 # 2.5.2 broken: see anaconda/nb_conda_kernels#280
psutil
py2jn
pypandoc
================================================
FILE: examples/removejnberr.py
================================================
#!/usr/bin/env python
# Remove output to stderr in notebooks. NB: use with caution!
# Run as
# python removejnberr.py
import glob
import os
from jnb import read_notebook, remove_error_output
from py2jn.tools import write_notebook
for src in glob.glob(os.path.join("notebooks", "*.ipynb")):
nb = read_notebook(src)
modflg = remove_error_output(nb)
if modflg:
print(f"Removing output to stderr from {src}")
write_notebook(nb, src)
================================================
FILE: examples/scriptcheck.sh
================================================
#!/usr/bin/env bash
# Basic test of example script functionality by running them all with
# optimization algorithms configured to use only a small number of iterations.
# Currently only supported under Linux.
SCRIPT=$(basename $0)
SCRIPTPATH=$(realpath $(dirname $0))
USAGE=$(cat <<-EOF
Usage: $SCRIPT [-h] [-d]
[-h] Display usage information
[-e] Display excerpt of error message on failure
[-d] Skip tests involving additional data downloads
[-t] Skip tests related to learned model training
[-g] Skip tests that need a GPU
EOF
)
OPTIND=1
DISPLAY_ERROR=0
SKIP_DOWNLOAD=0
SKIP_TRAINING=0
SKIP_GPU=0
while getopts ":hedtg" opt; do
case $opt in
h) echo "$USAGE"; exit 0;;
e) DISPLAY_ERROR=1;;
d) SKIP_DOWNLOAD=1;;
t) SKIP_TRAINING=1;;
g) SKIP_GPU=1;;
\?) echo "Error: invalid option -$OPTARG" >&2
echo "$USAGE" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ ! $# -eq 0 ] ; then
echo "Error: no positional arguments" >&2
echo "$USAGE" >&2
exit 2
fi
# Set environment variables and paths. This script is assumed to be run
# from its root directory.
export PYTHONPATH=$SCRIPTPATH/..
export PYTHONIOENCODING=utf-8
export MPLBACKEND=agg
export PYTHONWARNINGS=ignore:Matplotlib:UserWarning
d='/tmp/scriptcheck_'$$
mkdir -p $d
retval=0
# On SIGINT clean up temporary script directory and exit.
function cleanupexit {
rm $d/*.py
rmdir $d
exit 2
}
trap cleanupexit SIGINT
# Define regex strings.
re1="s/'maxiter' ?: ?[0-9]+/'maxiter': 2/g; "
re2="s/^maxiter ?= ?[0-9]+/maxiter = 2/g; "
re3="s/^N ?= ?[0-9]+/N = 32/g; "
re4="s/num_samples= ?[0-9]+/num_samples = 2/g; "
re5='s/\"cpu\": ?[0-9]+/\"cpu\": 1/g; '
re6="s/^downsampling_rate ?= ?[0-9]+/downsampling_rate = 12/g; "
re7="s/input\(/#input\(/g; "
re8="s/fig.show\(/#fig.show\(/g"
# Iterate over all scripts.
for f in $SCRIPTPATH/scripts/*.py; do
printf "%-50s " $(basename $f)
# Skip problem cases.
if [ $SKIP_DOWNLOAD -eq 1 ] && grep -q '_microscopy' <<< $f; then
printf "%s\n" skipped
continue
fi
if [ $SKIP_TRAINING -eq 1 ]; then
if grep -q '_datagen' <<< $f || grep -q '_train' <<< $f; then
printf "%s\n" skipped
continue
fi
fi
if [ $SKIP_GPU -eq 1 ] && grep -q '_astra_3d' <<< $f; then
printf "%s\n" skipped
continue
fi
if [ $SKIP_GPU -eq 1 ] && grep -q 'ct_projector_comparison_3d' <<< $f; then
printf "%s\n" skipped
continue
fi
# Create temporary copy of script with all algorithm maxiter values set
# to small number and final input statements commented out.
g=$d/$(basename $f)
sed -E -e "$re1$re2$re3$re4$re5$re6$re7$re8" $f > $g
# Run temporary script and print status message.
if output=$(timeout 180s python $g 2>&1); then
printf "%s\n" succeeded
else
printf "%s\n" FAILED
retval=1
if [ $DISPLAY_ERROR -eq 1 ]; then
echo "$output" | tail -8 | sed -e 's/^/ /'
fi
fi
# Remove temporary script.
rm -f $g
done
# Remove temporary script directory.
rmdir $d
exit $retval
================================================
FILE: examples/scripts/README.rst
================================================
Usage Examples
==============
Organized by Application
------------------------
Computed Tomography
^^^^^^^^^^^^^^^^^^^
`ct_abel_tv_admm.py `_
TV-Regularized Abel Inversion
`ct_abel_tv_admm_tune.py `_
Parameter Tuning for TV-Regularized Abel Inversion
`ct_symcone_tv_padmm.py `_
TV-Regularized Cone Beam CT for Symmetric Objects
`ct_astra_noreg_pcg.py `_
CT Reconstruction with CG and PCG
`ct_astra_3d_tv_admm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (ADMM Solver)
`ct_astra_3d_tv_padmm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (Proximal ADMM Solver)
`ct_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Integrated Projector)
`ct_astra_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (ASTRA Projector)
`ct_multi_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Multiple Projectors)
`ct_astra_weighted_tv_admm.py `_
TV-Regularized Low-Dose CT Reconstruction
`ct_svmbir_tv_multi.py `_
TV-Regularized CT Reconstruction (Multiple Algorithms)
`ct_svmbir_ppp_bm3d_admm_cg.py `_
PPP (with BM3D) CT Reconstruction (ADMM with CG Subproblem Solver)
`ct_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) CT Reconstruction (ADMM with Fast SVMBIR Prox)
`ct_fan_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) Fan-Beam CT Reconstruction
`ct_modl_train_foam2.py `_
CT Training and Reconstruction with MoDL
`ct_odp_train_foam2.py `_
CT Training and Reconstruction with ODP
`ct_unet_train_foam2.py `_
CT Training and Reconstructions with UNet
`ct_projector_comparison_2d.py `_
2D X-ray Transform Comparison
`ct_projector_comparison_3d.py `_
3D X-ray Transform Comparison
Deconvolution
^^^^^^^^^^^^^
`deconv_circ_tv_admm.py `_
Circulant Blur Image Deconvolution with TV Regularization
`deconv_tv_admm.py `_
Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_tv_padmm.py `_
Image Deconvolution with TV Regularization (Proximal ADMM Solver)
`deconv_tv_admm_tune.py `_
Parameter Tuning for Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_microscopy_tv_admm.py `_
Deconvolution Microscopy (Single Channel)
`deconv_microscopy_allchn_tv_admm.py `_
Deconvolution Microscopy (All Channels)
`deconv_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Deconvolution (ADMM Solver)
`deconv_ppp_bm3d_apgm.py `_
PPP (with BM3D) Image Deconvolution (APGM Solver)
`deconv_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Deconvolution (ADMM Solver)
`deconv_ppp_dncnn_padmm.py `_
PPP (with DnCNN) Image Deconvolution (Proximal ADMM Solver)
`deconv_ppp_bm4d_admm.py `_
PPP (with BM4D) Volume Deconvolution
`deconv_modl_train_foam1.py `_
Deconvolution Training and Reconstructions with MoDL
`deconv_odp_train_foam1.py `_
Deconvolution Training and Reconstructions with ODP
Sparse Coding
^^^^^^^^^^^^^
`sparsecode_nn_admm.py `_
Non-Negative Basis Pursuit DeNoising (ADMM)
`sparsecode_nn_apgm.py `_
Non-Negative Basis Pursuit DeNoising (APGM)
`sparsecode_conv_admm.py `_
Convolutional Sparse Coding (ADMM)
`sparsecode_conv_md_admm.py `_
Convolutional Sparse Coding with Mask Decoupling (ADMM)
`sparsecode_apgm.py `_
Basis Pursuit DeNoising (APGM)
`sparsecode_poisson_apgm.py `_
Non-negative Poisson Loss Reconstruction (APGM)
Miscellaneous
^^^^^^^^^^^^^
`demosaic_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Demosaicing
`superres_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Superresolution
`denoise_l1tv_admm.py `_
ℓ1 Total Variation Denoising
`denoise_ptv_pdhg.py `_
Polar Total Variation Denoising (PDHG)
`denoise_tv_admm.py `_
Total Variation Denoising (ADMM)
`denoise_tv_apgm.py `_
Total Variation Denoising with Constraint (APGM)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
`denoise_approx_tv_multi.py `_
Denoising with Approximate Total Variation Proximal Operator
`denoise_cplx_tv_nlpadmm.py `_
Complex Total Variation Denoising with NLPADMM Solver
`denoise_cplx_tv_pdhg.py `_
Complex Total Variation Denoising with PDHG Solver
`denoise_dncnn_universal.py `_
Comparison of DnCNN Variants for Image Denoising
`diffusercam_tv_admm.py `_
TV-Regularized 3D DiffuserCam Reconstruction
`video_rpca_admm.py `_
Video Decomposition via Robust PCA
`ct_datagen_foam2.py `_
CT Data Generation for NN Training
`deconv_datagen_bsds.py `_
Blurred Data Generation (Natural Images) for NN Training
`deconv_datagen_foam1.py `_
Blurred Data Generation (Foams) for NN Training
`denoise_datagen_bsds.py `_
Noisy Data Generation for NN Training
Organized by Regularization
---------------------------
Plug and Play Priors
^^^^^^^^^^^^^^^^^^^^
`ct_svmbir_ppp_bm3d_admm_cg.py `_
PPP (with BM3D) CT Reconstruction (ADMM with CG Subproblem Solver)
`ct_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) CT Reconstruction (ADMM with Fast SVMBIR Prox)
`ct_fan_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) Fan-Beam CT Reconstruction
`deconv_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Deconvolution (ADMM Solver)
`deconv_ppp_bm3d_apgm.py `_
PPP (with BM3D) Image Deconvolution (APGM Solver)
`deconv_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Deconvolution (ADMM Solver)
`deconv_ppp_dncnn_padmm.py `_
PPP (with DnCNN) Image Deconvolution (Proximal ADMM Solver)
`deconv_ppp_bm4d_admm.py `_
PPP (with BM4D) Volume Deconvolution
`demosaic_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Demosaicing
`superres_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Superresolution
Total Variation
^^^^^^^^^^^^^^^
`ct_abel_tv_admm.py `_
TV-Regularized Abel Inversion
`ct_abel_tv_admm_tune.py `_
Parameter Tuning for TV-Regularized Abel Inversion
`ct_symcone_tv_padmm.py `_
TV-Regularized Cone Beam CT for Symmetric Objects
`ct_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Integrated Projector)
`ct_multi_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Multiple Projectors)
`ct_astra_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (ASTRA Projector)
`ct_astra_3d_tv_admm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (ADMM Solver)
`ct_astra_3d_tv_padmm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (Proximal ADMM Solver)
`ct_astra_weighted_tv_admm.py `_
TV-Regularized Low-Dose CT Reconstruction
`ct_svmbir_tv_multi.py `_
TV-Regularized CT Reconstruction (Multiple Algorithms)
`deconv_circ_tv_admm.py `_
Circulant Blur Image Deconvolution with TV Regularization
`deconv_tv_admm.py `_
Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_tv_admm_tune.py `_
Parameter Tuning for Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_tv_padmm.py `_
Image Deconvolution with TV Regularization (Proximal ADMM Solver)
`deconv_microscopy_tv_admm.py `_
Deconvolution Microscopy (Single Channel)
`deconv_microscopy_allchn_tv_admm.py `_
Deconvolution Microscopy (All Channels)
`denoise_l1tv_admm.py `_
ℓ1 Total Variation Denoising
`denoise_ptv_pdhg.py `_
Polar Total Variation Denoising (PDHG)
`denoise_tv_admm.py `_
Total Variation Denoising (ADMM)
`denoise_tv_apgm.py `_
Total Variation Denoising with Constraint (APGM)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
`denoise_approx_tv_multi.py `_
Denoising with Approximate Total Variation Proximal Operator
`denoise_cplx_tv_nlpadmm.py `_
Complex Total Variation Denoising with NLPADMM Solver
`denoise_cplx_tv_pdhg.py `_
Complex Total Variation Denoising with PDHG Solver
`diffusercam_tv_admm.py `_
TV-Regularized 3D DiffuserCam Reconstruction
Sparsity
^^^^^^^^
`diffusercam_tv_admm.py `_
TV-Regularized 3D DiffuserCam Reconstruction
`sparsecode_nn_admm.py `_
Non-Negative Basis Pursuit DeNoising (ADMM)
`sparsecode_nn_apgm.py `_
Non-Negative Basis Pursuit DeNoising (APGM)
`sparsecode_conv_admm.py `_
Convolutional Sparse Coding (ADMM)
`sparsecode_conv_md_admm.py `_
Convolutional Sparse Coding with Mask Decoupling (ADMM)
`sparsecode_apgm.py `_
Basis Pursuit DeNoising (APGM)
`sparsecode_poisson_apgm.py `_
Non-negative Poisson Loss Reconstruction (APGM)
`video_rpca_admm.py `_
Video Decomposition via Robust PCA
Machine Learning
^^^^^^^^^^^^^^^^
`ct_datagen_foam2.py `_
CT Data Generation for NN Training
`ct_modl_train_foam2.py `_
CT Training and Reconstruction with MoDL
`ct_odp_train_foam2.py `_
CT Training and Reconstruction with ODP
`ct_unet_train_foam2.py `_
CT Training and Reconstructions with UNet
`deconv_datagen_bsds.py `_
Blurred Data Generation (Natural Images) for NN Training
`deconv_datagen_foam1.py `_
Blurred Data Generation (Foams) for NN Training
`deconv_modl_train_foam1.py `_
Deconvolution Training and Reconstructions with MoDL
`deconv_odp_train_foam1.py `_
Deconvolution Training and Reconstructions with ODP
`denoise_datagen_bsds.py `_
Noisy Data Generation for NN Training
`denoise_dncnn_train_bsds.py `_
Training of DnCNN for Denoising
`denoise_dncnn_universal.py `_
Comparison of DnCNN Variants for Image Denoising
Organized by Optimization Algorithm
-----------------------------------
ADMM
^^^^
`ct_abel_tv_admm.py `_
TV-Regularized Abel Inversion
`ct_abel_tv_admm_tune.py `_
Parameter Tuning for TV-Regularized Abel Inversion
`ct_symcone_tv_padmm.py `_
TV-Regularized Cone Beam CT for Symmetric Objects
`ct_astra_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (ASTRA Projector)
`ct_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Integrated Projector)
`ct_astra_3d_tv_admm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (ADMM Solver)
`ct_astra_weighted_tv_admm.py `_
TV-Regularized Low-Dose CT Reconstruction
`ct_multi_tv_admm.py `_
TV-Regularized Sparse-View CT Reconstruction (Multiple Projectors)
`ct_svmbir_tv_multi.py `_
TV-Regularized CT Reconstruction (Multiple Algorithms)
`ct_svmbir_ppp_bm3d_admm_cg.py `_
PPP (with BM3D) CT Reconstruction (ADMM with CG Subproblem Solver)
`ct_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) CT Reconstruction (ADMM with Fast SVMBIR Prox)
`ct_fan_svmbir_ppp_bm3d_admm_prox.py `_
PPP (with BM3D) Fan-Beam CT Reconstruction
`deconv_circ_tv_admm.py `_
Circulant Blur Image Deconvolution with TV Regularization
`deconv_tv_admm.py `_
Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_tv_admm_tune.py `_
Parameter Tuning for Image Deconvolution with TV Regularization (ADMM Solver)
`deconv_microscopy_tv_admm.py `_
Deconvolution Microscopy (Single Channel)
`deconv_microscopy_allchn_tv_admm.py `_
Deconvolution Microscopy (All Channels)
`deconv_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Deconvolution (ADMM Solver)
`deconv_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Deconvolution (ADMM Solver)
`deconv_ppp_bm4d_admm.py `_
PPP (with BM4D) Volume Deconvolution
`diffusercam_tv_admm.py `_
TV-Regularized 3D DiffuserCam Reconstruction
`sparsecode_nn_admm.py `_
Non-Negative Basis Pursuit DeNoising (ADMM)
`sparsecode_conv_admm.py `_
Convolutional Sparse Coding (ADMM)
`sparsecode_conv_md_admm.py `_
Convolutional Sparse Coding with Mask Decoupling (ADMM)
`demosaic_ppp_bm3d_admm.py `_
PPP (with BM3D) Image Demosaicing
`superres_ppp_dncnn_admm.py `_
PPP (with DnCNN) Image Superresolution
`denoise_l1tv_admm.py `_
ℓ1 Total Variation Denoising
`denoise_tv_admm.py `_
Total Variation Denoising (ADMM)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
`denoise_approx_tv_multi.py `_
Denoising with Approximate Total Variation Proximal Operator
`video_rpca_admm.py `_
Video Decomposition via Robust PCA
Linearized ADMM
^^^^^^^^^^^^^^^
`ct_svmbir_tv_multi.py `_
TV-Regularized CT Reconstruction (Multiple Algorithms)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
Proximal ADMM
^^^^^^^^^^^^^
`ct_astra_3d_tv_padmm.py `_
3D TV-Regularized Sparse-View CT Reconstruction (Proximal ADMM Solver)
`deconv_tv_padmm.py `_
Image Deconvolution with TV Regularization (Proximal ADMM Solver)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
`deconv_ppp_dncnn_padmm.py `_
PPP (with DnCNN) Image Deconvolution (Proximal ADMM Solver)
Non-linear Proximal ADMM
^^^^^^^^^^^^^^^^^^^^^^^^
`denoise_cplx_tv_nlpadmm.py `_
Complex Total Variation Denoising with NLPADMM Solver
PDHG
^^^^
`ct_svmbir_tv_multi.py `_
TV-Regularized CT Reconstruction (Multiple Algorithms)
`denoise_ptv_pdhg.py `_
Polar Total Variation Denoising (PDHG)
`denoise_tv_multi.py `_
Comparison of Optimization Algorithms for Total Variation Denoising
`denoise_cplx_tv_pdhg.py