Commit 80fd5456 authored by Philipp Arras's avatar Philipp Arras
Browse files

Merge branch 'more_docs' into 'NIFTy_8'

Move custom nonlinearities to html docs

See merge request !668
parents dd8e0f42 33974bdb
Pipeline #107405 passed with stages
in 35 minutes and 22 seconds
......@@ -2,6 +2,8 @@
git_version.py
docs/source/user/getting_started_0.rst
docs/source/user/custom_nonlinearities.rst
docs/source/user/getting_started_4_CorrelatedFields.rst
# custom
*.txt
......
......@@ -152,8 +152,3 @@ run_meanfield:
stage: demo_runs
script:
- python3 demos/parametric_variational_inference.py
run_nonlinearity_guide:
stage: demo_runs
script:
- python3 demos/custom_nonlinearities.py
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright(C) 2021 Max-Planck-Society
# Author: Philipp Arras
import numpy as np
import nifty8 as ift
# In NIFTy, users can add hand-crafted point-wise nonlinearities that are then
# available for `Field`, `MultiField`, `Linearization` and `Operator`. This
# guide illustrates how this is done.
# Suppose that we would like to use the point-wise function f(x) = x*exp(x) in
# an operator chain. This function is called "myptw" in the following. We
# introduce this function to NIFTy by implementing two functions.
# First, one that takes a `numpy.ndarray` as an input, applies the point-wise
# mapping and returns the result as a `numpy.ndarray` of the same shape.
# Second, a function that takes a `numpy.ndarray` as an input and returns two
# `numpy.ndarray`s: the application of the nonlinearity (same as before) and
# the derivative.
def func(x):
return x*np.exp(x)
def func_and_derv(x):
expx = np.exp(x)
return x*expx, (1+x)*expx
# These two functions are then added to the NIFTy-internal dictionary that
# contains all implemented point-wise nonlinearities.
ift.pointwise.ptw_dict["myptw"] = func, func_and_derv
# This allows us to apply this non-linearity on `Field`s, ...
dom = ift.UnstructuredDomain(10)
fld = ift.from_random(dom)
fld = ift.full(dom, 2.)
a = fld.ptw("myptw")
b = ift.makeField(dom, func(fld.val))
ift.extra.assert_allclose(a, b)
# `MultiField`s, ...
mdom = ift.makeDomain({"bar": ift.UnstructuredDomain(10)})
mfld = ift.from_random(mdom)
a = mfld.ptw("myptw")
b = ift.makeField(mdom, {"bar": func(mfld["bar"].val)})
ift.extra.assert_allclose(a, b)
# Linearizations (including the Jacobian), ...
# (Value)
lin = ift.Linearization.make_var(fld)
a = lin.ptw("myptw").val
b = ift.makeField(dom, func(fld.val))
ift.extra.assert_allclose(a, b)
# (Jacobian)
op_a = lin.ptw("myptw").jac
op_b = ift.makeOp(ift.makeField(dom, func_and_derv(fld.val)[1]))
testing_vector = ift.from_random(dom)
ift.extra.assert_allclose(op_a(testing_vector),
op_b(testing_vector))
# and `Operator`s.
op = ift.FieldAdapter(dom, "foo").ptw("myptw")
# We check that the gradient has been implemented correctly by comparing it to
# an approximation to the gradient by finite differences.
def check(func_name, eps=1e-7):
pos = ift.from_random(ift.UnstructuredDomain(10))
var0 = ift.Linearization.make_var(pos)
var1 = ift.Linearization.make_var(pos+eps)
df0 = (var1.ptw(func_name).val - var0.ptw(func_name).val)/eps
df1 = var0.ptw(func_name).jac(ift.full(lin.domain, 1.))
# rtol depends on how nonlinear the function is
ift.extra.assert_allclose(df0, df1, rtol=100*eps)
check("myptw")
......@@ -134,6 +134,7 @@
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import nifty8 as ift\n",
"import matplotlib.pyplot as plt\n",
......
......@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Notebook showcasing the NIFTy 6 Correlated Field model\n",
"# Showcasing the Correlated Field model\n",
"\n",
"**Skip to `Parameter Showcases` for the meat/veggies ;)**\n",
"\n",
......@@ -29,12 +29,12 @@
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import nifty8 as ift\n",
"import matplotlib.pyplot as plt\n",
"plt.rcParams['figure.dpi'] = 100\n",
"plt.style.use(\"seaborn-notebook\")\n",
"import numpy as np\n",
"ift.random.push_sseq_from_seed(43)\n",
"\n",
"n_pix = 256\n",
"x_space = ift.RGSpace(n_pix)"
......@@ -391,7 +391,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## The `offset_mean` parameter of `CorrelatedFieldMaker.make()`\n",
"## The `offset_mean` parameter of `CorrelatedFieldMaker()`\n",
"\n",
"The `offset_mean` parameter defines a global additive offset on the field realizations.\n",
"\n",
......@@ -424,7 +424,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## The `offset_std` parameters of `CorrelatedFieldMaker.make()`\n",
"## The `offset_std` parameters of `CorrelatedFieldMaker()`\n",
"\n",
"Variation of the global offset of the field are modelled as being log-normally distributed.\n",
"See `The Moment-Matched Log-Normal Distribution` above for details.\n",
......
jupyter-nbconvert --to rst --execute --ExecutePreprocessor.timeout=None docs/source/user/getting_started_0.ipynb
set -e
FOLDER=docs/source/user/
for FILE in ${FOLDER}getting_started_0 ${FOLDER}getting_started_4_CorrelatedFields ${FOLDER}custom_nonlinearities
do
if [ ! -f "${FILE}.rst" ] || [ ${FILE}.ipynb -nt ${FILE}.rst ]; then
jupyter-nbconvert --to rst --execute --ExecutePreprocessor.timeout=None ${FILE}.ipynb
fi
done
EXCLUDE="nifty8/logger.py nifty8/git_version.py"
sphinx-apidoc -e -o docs/source/mod nifty8 ${EXCLUDE}
......
......@@ -3,12 +3,19 @@ import nifty8
needs_sphinx = '3.2.0'
extensions = [
'sphinx.ext.napoleon', # Support for NumPy and Google style docstrings
'sphinx.ext.imgmath', # Render math as images
'sphinx.ext.viewcode' # Add links to highlighted source code
'sphinx.ext.napoleon', # Support for NumPy and Google style docstrings
'sphinx.ext.imgmath', # Render math as images
'sphinx.ext.viewcode', # Add links to highlighted source code
'sphinx.ext.intersphinx' # Links to other sphinx docs (mostly numpy)
]
master_doc = 'index'
intersphinx_mapping = {"numpy": ("https://numpy.org/doc/stable/", None),
#"matplotlib": ('https://matplotlib.org/stable/', None),
"ducc0": ("https://mtr.pages.mpcdf.de/ducc/", None),
"scipy": ('https://docs.scipy.org/doc/scipy/reference/', None),
}
napoleon_google_docstring = False
napoleon_numpy_docstring = True
napoleon_use_ivar = True
......
{
"cells": [
{
"cell_type": "markdown",
"id": "indonesian-dayton",
"metadata": {},
"source": [
"# Custom nonlinearities"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "duplicate-fitting",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"import nifty8 as ift"
]
},
{
"cell_type": "markdown",
"id": "democratic-lancaster",
"metadata": {},
"source": [
"In NIFTy, users can add hand-crafted point-wise nonlinearities that are then available for `Field`, `MultiField`, `Linearization` and `Operator`. This guide illustrates how this is done.\n",
"\n",
"Suppose that we would like to use the point-wise function f(x) = x*exp(x) in an operator chain. This function is called \"myptw\" in the following. We introduce this function to NIFTy by implementing two functions.\n",
"\n",
"First, one that takes a `numpy.ndarray` as an input, applies the point-wise mapping and returns the result as a `numpy.ndarray` of the same shape. Second, a function that takes a `numpy.ndarray` as an input and returns two `numpy.ndarray`s: the application of the nonlinearity (same as before) and the derivative."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "modern-spouse",
"metadata": {},
"outputs": [],
"source": [
"def func(x):\n",
" return x*np.exp(x)\n",
"\n",
"\n",
"def func_and_derv(x):\n",
" expx = np.exp(x)\n",
" return x*expx, (1+x)*expx"
]
},
{
"cell_type": "markdown",
"id": "shared-deficit",
"metadata": {},
"source": [
"These two functions are then added to the NIFTy-internal dictionary that contains all implemented point-wise nonlinearities."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "published-start",
"metadata": {},
"outputs": [],
"source": [
"ift.pointwise.ptw_dict[\"myptw\"] = func, func_and_derv"
]
},
{
"cell_type": "markdown",
"id": "living-surrey",
"metadata": {},
"source": [
"This allows us to apply this non-linearity on `Field`s, ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "incident-biotechnology",
"metadata": {},
"outputs": [],
"source": [
"dom = ift.UnstructuredDomain(10)\n",
"fld = ift.from_random(dom)\n",
"fld = ift.full(dom, 2.)\n",
"a = fld.ptw(\"myptw\")\n",
"b = ift.makeField(dom, func(fld.val))\n",
"ift.extra.assert_allclose(a, b)"
]
},
{
"cell_type": "markdown",
"id": "palestinian-librarian",
"metadata": {},
"source": [
"`MultiField`s, ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "naval-nightmare",
"metadata": {},
"outputs": [],
"source": [
"mdom = ift.makeDomain({\"bar\": ift.UnstructuredDomain(10)})\n",
"mfld = ift.from_random(mdom)\n",
"a = mfld.ptw(\"myptw\")\n",
"b = ift.makeField(mdom, {\"bar\": func(mfld[\"bar\"].val)})\n",
"ift.extra.assert_allclose(a, b)"
]
},
{
"cell_type": "markdown",
"id": "legendary-oriental",
"metadata": {},
"source": [
"`Linearization`s (including the Jacobian), ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "native-breeding",
"metadata": {},
"outputs": [],
"source": [
"lin = ift.Linearization.make_var(fld)\n",
"a = lin.ptw(\"myptw\").val\n",
"b = ift.makeField(dom, func(fld.val))\n",
"ift.extra.assert_allclose(a, b)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "crude-motorcycle",
"metadata": {},
"outputs": [],
"source": [
"op_a = lin.ptw(\"myptw\").jac\n",
"op_b = ift.makeOp(ift.makeField(dom, func_and_derv(fld.val)[1]))\n",
"testing_vector = ift.from_random(dom)\n",
"ift.extra.assert_allclose(op_a(testing_vector),\n",
" op_b(testing_vector))"
]
},
{
"cell_type": "markdown",
"id": "outdoor-juice",
"metadata": {},
"source": [
"and `Operator`s."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "retained-closer",
"metadata": {},
"outputs": [],
"source": [
"op = ift.FieldAdapter(dom, \"foo\").ptw(\"myptw\")"
]
},
{
"cell_type": "markdown",
"id": "accessory-pepper",
"metadata": {},
"source": [
"Please remember to always check that the gradient has been implemented correctly by comparing it to an approximation to the gradient by finite differences."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "close-bonus",
"metadata": {},
"outputs": [],
"source": [
"def check(func_name, eps=1e-7):\n",
" pos = ift.from_random(ift.UnstructuredDomain(10))\n",
" var0 = ift.Linearization.make_var(pos)\n",
" var1 = ift.Linearization.make_var(pos+eps)\n",
" df0 = (var1.ptw(func_name).val - var0.ptw(func_name).val)/eps\n",
" df1 = var0.ptw(func_name).jac(ift.full(lin.domain, 1.))\n",
" # rtol depends on how nonlinear the function is\n",
" ift.extra.assert_allclose(df0, df1, rtol=100*eps)\n",
"\n",
"check(\"myptw\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
../../../demos/getting_started_4_CorrelatedFields.ipynb
\ No newline at end of file
......@@ -16,4 +16,6 @@ are found in the `API reference <../mod/nifty8.html>`_.
volume
code
getting_started_0
getting_started_4_CorrelatedFields
custom_nonlinearities
citations
......@@ -16,6 +16,7 @@
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.
import numpy as np
import scipy.sparse
from .. import utilities
from ..domain_tuple import DomainTuple
......@@ -45,10 +46,10 @@ class MatrixProductOperator(EndomorphicOperator):
Parameters
----------
domain: :class:`Domain` or :class:`DomainTuple`
domain: Domain or DomainTuple
Domain of the operator.
If :class:`DomainTuple` it is assumed to have only one entry.
matrix: scipy.sparse matrix or numpy array
matrix: scipy.sparse.spmatrix or numpy.ndarray
Quadratic matrix of shape `(domain.shape, domain.shape)`
(if `not flatten`) that supports `matrix.transpose()`.
If it is not a numpy array, needs to be applicable to the val
......
......@@ -54,8 +54,38 @@ def test_MPI_equality():
@pms(not mpi, reason="requires at least two mpi tasks")
def test_MPI_synced_random_state():
ift.utilities.check_MPI_synced_random_state(comm)
with ift.random.Context(123 if master else 111):
with pytest.raises(RuntimeError):
ift.utilities.check_MPI_synced_random_state(comm)
if master:
ift.random.push_sseq_from_seed(123)
with pytest.raises(RuntimeError):
ift.utilities.check_MPI_synced_random_state(comm)
@pms(not mpi, reason="requires at least two mpi tasks")
@pmp("kl", [lambda a, b, c, d: ift.MetricGaussianKL(a, b, c, d, comm=comm),
lambda a, b, c, d: ift.GeoMetricKL(a, b, c,
ift.NewtonCG(ift.AbsDeltaEnergyController(0.1, iteration_limit=2)),
d, comm=comm)
])
@pmp("mirror", [False, True])
@pmp("n_samples", [2, 3])
def test_MPI_synced_random_state_kl_energies(kl, mirror, n_samples):
ic = ift.AbsDeltaEnergyController(0.1, iteration_limit=2)
lh = ift.GaussianEnergy(ift.full(ift.UnstructuredDomain(2), 2.)).ducktape("a")
ham = ift.StandardHamiltonian(lh, ic)
ift.utilities.check_MPI_synced_random_state(comm)
with ift.random.Context(123 if master else 111):
mean = ift.from_random(ham.domain)
with pytest.raises(RuntimeError):
kl(mean, ham, n_samples, mirror)
@pms(not mpi, reason="requires at least two mpi tasks")
@pmp("sync", [False, True])
def test_random_field_generation(sync):
with ift.random.Context(123 if master and not sync else 111):
dom = ift.UnstructuredDomain(5)
fld = ift.from_random(dom)
if sync:
ift.utilities.check_MPI_equality(fld, comm)
else:
with pytest.raises(RuntimeError):
ift.utilities.check_MPI_equality(fld, comm)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment