From 385be5bf817c6bab82ed18f8dba20ad27ce22fd9 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 15:42:42 -0700 Subject: [PATCH 01/21] Start new sparse module with to_dense() implementation --- jax_cosmo/sparse.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 jax_cosmo/sparse.py diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py new file mode 100644 index 0000000..5daea00 --- /dev/null +++ b/jax_cosmo/sparse.py @@ -0,0 +1,32 @@ +"""Support for sparse matrices composed of square blocks that are individually diagonal. + +The motivating example is a Gaussian covariance matrix computed in angular_cl. +The sparse matrix is represented as a 3D array of shape (ny, nx, ndiag) composed +of ny x nx square blocks of size ndiag x ndiag. The vector at [ny, nx] is the +diagonal of the corresponding block. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import jax.numpy as np +from jax import jit +from jax import vmap + + +@jit +def to_dense(sparse): + """Convert a sparse matrix to its dense equivalent. + + Parameters + ---------- + sparse : array + 3D array of shape (ny, nx, ndiag) of block diagonal elements. + + Returns + ------- + array + 2D array of shape (ny * ndiag, nx * ndiag) with the same dtype + as the input array. + """ + return np.vstack(vmap(lambda row: np.hstack(vmap(np.diag)(row)))(sparse)) From 175800dd13ba222600edec7616e26d60db7a1668 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 15:51:46 -0700 Subject: [PATCH 02/21] Add to_dense() unit test --- tests/test_sparse.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_sparse.py diff --git a/tests/test_sparse.py b/tests/test_sparse.py new file mode 100644 index 0000000..c0ced73 --- /dev/null +++ b/tests/test_sparse.py @@ -0,0 +1,18 @@ +import jax.numpy as jnp +import numpy as numpy +from numpy.testing import assert_allclose, assert_array_equal + +from jax_cosmo.sparse import * + + +def test_to_dense(): + X_sparse = jnp.array([[[1,2,3], [4,5,6], [-1,-2,-3]], [[1,2,3], [-4,-5,-6], [7,8,9]]]) + X_dense = to_dense(X_sparse) + X_answer = jnp.array( + [[ 1, 0, 0, 4, 0, 0, -1, 0, 0], + [ 0, 2, 0, 0, 5, 0, 0, -2, 0], + [ 0, 0, 3, 0, 0, 6, 0, 0, -3], + [ 1, 0, 0, -4, 0, 0, 7, 0, 0], + [ 0, 2, 0, 0, -5, 0, 0, 8, 0], + [ 0, 0, 3, 0, 0, -6, 0, 0, 9]]) + assert_array_equal(X_dense, X_answer) From 58539de21794e52ebc3f40a5b9316883c5c5d131 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 15:52:30 -0700 Subject: [PATCH 03/21] black fmt --- tests/test_sparse.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index c0ced73..033735d 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -6,13 +6,18 @@ def test_to_dense(): - X_sparse = jnp.array([[[1,2,3], [4,5,6], [-1,-2,-3]], [[1,2,3], [-4,-5,-6], [7,8,9]]]) + X_sparse = jnp.array( + [[[1, 2, 3], [4, 5, 6], [-1, -2, -3]], [[1, 2, 3], [-4, -5, -6], [7, 8, 9]]] + ) X_dense = to_dense(X_sparse) X_answer = jnp.array( - [[ 1, 0, 0, 4, 0, 0, -1, 0, 0], - [ 0, 2, 0, 0, 5, 0, 0, -2, 0], - [ 0, 0, 3, 0, 0, 6, 0, 0, -3], - [ 1, 0, 0, -4, 0, 0, 7, 0, 0], - [ 0, 2, 0, 0, -5, 0, 0, 8, 0], - [ 0, 0, 3, 0, 0, -6, 0, 0, 9]]) + [ + [1, 0, 0, 4, 0, 0, -1, 0, 0], + [0, 2, 0, 0, 5, 0, 0, -2, 0], + [0, 0, 3, 0, 0, 6, 0, 0, -3], + [1, 0, 0, -4, 0, 0, 7, 0, 0], + [0, 2, 0, 0, -5, 0, 0, 8, 0], + [0, 0, 3, 0, 0, -6, 0, 0, 9], + ] + ) assert_array_equal(X_dense, X_answer) From b4a0b5267f9c03b963265763420aec3836907f62 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 16:36:23 -0700 Subject: [PATCH 04/21] Implement sparse inv --- jax_cosmo/sparse.py | 35 +++++++++++++++++++++++++++++++++++ tests/test_sparse.py | 18 +++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 5daea00..71fdee4 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -14,6 +14,17 @@ from jax import vmap +def check_sparse(sparse, square=False): + """Check for a valid sparse matrix. + """ + sparse = np.array(sparse) + if sparse.ndim != 3: + raise ValueError("Expected 3D array of sparse diagonals.") + if square and (sparse.shape[0] != sparse.shape[1]): + raise ValueError("Can only invert a square matrix.") + return sparse + + @jit def to_dense(sparse): """Convert a sparse matrix to its dense equivalent. @@ -29,4 +40,28 @@ def to_dense(sparse): 2D array of shape (ny * ndiag, nx * ndiag) with the same dtype as the input array. """ + sparse = check_sparse(sparse) return np.vstack(vmap(lambda row: np.hstack(vmap(np.diag)(row)))(sparse)) + + +@jit +def inv(sparse): + """Calculate the inverse of a square matrix in sparse format. + + We currently assume that the matrix is invertible and you should not + trust the answer unless you know this is true (because jax.numpy.linalg.inv + has this behavior). + + Parameters + ---------- + sparse : array + 3D array of shape (n, n, ndiag) of block diagonal elements. + + Returns + ------- + array + 3D array of shape (n, n, ndiag) of block diagonal elements + representing the inverse matrix. + """ + sparse = check_sparse(sparse, square=True) + return np.transpose(np.linalg.inv(np.transpose(sparse, (2, 0, 1))), (1, 2, 0)) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 033735d..37bda16 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -1,6 +1,6 @@ import jax.numpy as jnp import numpy as numpy -from numpy.testing import assert_allclose, assert_array_equal +from numpy.testing import assert_allclose, assert_array_equal, assert_raises from jax_cosmo.sparse import * @@ -21,3 +21,19 @@ def test_to_dense(): ] ) assert_array_equal(X_dense, X_answer) + + with assert_raises(ValueError): + to_dense([1, 2, 3]) + + with assert_raises(ValueError): + to_dense(jnp.ones((2, 3, 4, 5))) + + +def test_inv(): + X_sparse = jnp.array([[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [2.0, 2.0]]]) + X_inv_sparse = inv(X_sparse) + X_answer = jnp.array([[[2.0, 2.0], [-1.0, -1.0]], [[-1.0, -1.0], [1.0, 1.0]]]) + assert_allclose(X_inv_sparse, X_answer) + + with assert_raises(ValueError): + inv(jnp.ones((2, 3, 4))) From 681406128e0337b93921bcf7964d80fc85c5abda Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 16:56:34 -0700 Subject: [PATCH 05/21] Implement sparse vecdot --- jax_cosmo/sparse.py | 28 +++++++++++++++++++++++++++- tests/test_sparse.py | 13 +++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 71fdee4..08ca24c 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -17,7 +17,7 @@ def check_sparse(sparse, square=False): """Check for a valid sparse matrix. """ - sparse = np.array(sparse) + sparse = np.asarray(sparse) if sparse.ndim != 3: raise ValueError("Expected 3D array of sparse diagonals.") if square and (sparse.shape[0] != sparse.shape[1]): @@ -65,3 +65,29 @@ def inv(sparse): """ sparse = check_sparse(sparse, square=True) return np.transpose(np.linalg.inv(np.transpose(sparse, (2, 0, 1))), (1, 2, 0)) + + +@jit +def vecdot(sparse, vec): + """Multiply a sparse matrix by a vector. + + Parameters + ---------- + sparse : array + 3D array of shape (ny, nx, ndiag) of block diagonal elements. + vec : array + 1D array of shape (nx). + + Returns + ------- + array + 1D array of shape (ny). + """ + sparse = check_sparse(sparse) + vec = np.asarray(vec) + if vec.ndim != 1 or sparse.shape[1] * sparse.shape[2] != vec.size: + raise ValueError("Vector has the wrong shape for this sparse matrix.") + return vmap( + lambda row, vec: np.sum(vmap(np.multiply)(row, vec.reshape(row.shape)), axis=0), + in_axes=(0, None), + )(sparse, vec).reshape(-1) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 37bda16..3c182a2 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -37,3 +37,16 @@ def test_inv(): with assert_raises(ValueError): inv(jnp.ones((2, 3, 4))) + + +def test_vecdot(): + X_sparse = [ + [[1, 2, 3], [4, 5, 6], [-1, -2, -3]], + [[1, 2, 3], [-4, -5, -6], [7, 8, 9]], + ] + y_in = [1, 0.1, -1, 2, 0.2, -2, 3, 0.3, -3] + y_out = to_dense(X_sparse).dot(jnp.array(y_in)) + assert_allclose(y_out, vecdot(X_sparse, y_in)) + + with assert_raises(ValueError): + vecdot(X_sparse, jnp.ones(5)) From 7b146f0b62d618f2050917558896d7e8ec3aa6b5 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 18:44:24 -0700 Subject: [PATCH 06/21] Add optional sparse cov output --- jax_cosmo/angular_cl.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/jax_cosmo/angular_cl.py b/jax_cosmo/angular_cl.py index 63db4dd..7e2db91 100644 --- a/jax_cosmo/angular_cl.py +++ b/jax_cosmo/angular_cl.py @@ -122,14 +122,19 @@ def get_noise_cl(inds): return lax.map(get_noise_cl, cl_index) -def gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky=0.25): +def gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky=0.25, sparse=True): """ Computes a Gaussian covariance for the angular cls of the provided probes + Set sparse True to return a sparse matrix representation that uses a factor + of n_ell less memory and is compatible with the linear algebra operations + in :mod:`jax_cosmo.sparse`. + return_cls: (returns covariance) """ ell = np.atleast_1d(ell) n_ell = len(ell) + one = 1.0 if sparse else np.eye(n_ell) # Adding noise to auto-spectra cl_obs = cl_signal + cl_noise @@ -139,20 +144,27 @@ def gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky=0.25): norm = (2 * ell + 1) * np.gradient(ell) * f_sky # Retrieve ordering for blocks of the covariance matrix - cov_blocks = np.array(_get_cov_blocks_ordering(probes)) + cov_blocks = np.array(jax_cosmo.angular_cl._get_cov_blocks_ordering(probes)) def get_cov_block(inds): a, b, c, d = inds cov = (cl_obs[a] * cl_obs[b] + cl_obs[c] * cl_obs[d]) / norm - return cov * np.eye(n_ell) + return cov * one + # Return a sparse representation of the matrix containing only the diagonals + # for each of the n_cls x n_cls blocks of size n_ell x n_ell. + # We could compress this further using the symmetry of the blocks, but + # it is easier to invert this matrix with this redundancy included. cov_mat = lax.map(get_cov_block, cov_blocks) # Reshape covariance matrix into proper matrix - cov_mat = cov_mat.reshape((n_cls, n_cls, n_ell, n_ell)) - cov_mat = cov_mat.transpose(axes=(0, 2, 1, 3)).reshape( - (n_ell * n_cls, n_ell * n_cls) - ) + if sparse: + cov_mat = cov_mat.reshape((n_cls, n_cls, n_ell)) + else: + cov_mat = cov_mat.reshape((n_cls, n_cls, n_ell, n_ell)) + cov_mat = cov_mat.transpose(axes=(0, 2, 1, 3)).reshape( + (n_ell * n_cls, n_ell * n_cls) + ) return cov_mat @@ -163,6 +175,7 @@ def gaussian_cl_covariance_and_mean( transfer_fn=tklib.Eisenstein_Hu, nonlinear_fn=power.halofit, f_sky=0.25, + sparse=False, ): """ Computes a Gaussian covariance for the angular cls of the provided probes @@ -179,6 +192,6 @@ def gaussian_cl_covariance_and_mean( cl_noise = noise_cl(ell, probes) # retrieve the covariance - cov_mat = gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky) + cov_mat = gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky, sparse) return cl_signal.flatten(), cov_mat From ad3bc0dfdc967eecb6ffd05bbe0e02b5e56d85f8 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Tue, 21 Jul 2020 19:01:39 -0700 Subject: [PATCH 07/21] Add sparse cov unit test --- jax_cosmo/angular_cl.py | 2 +- tests/test_angular_cl.py | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/jax_cosmo/angular_cl.py b/jax_cosmo/angular_cl.py index 7e2db91..0510778 100644 --- a/jax_cosmo/angular_cl.py +++ b/jax_cosmo/angular_cl.py @@ -144,7 +144,7 @@ def gaussian_cl_covariance(ell, probes, cl_signal, cl_noise, f_sky=0.25, sparse= norm = (2 * ell + 1) * np.gradient(ell) * f_sky # Retrieve ordering for blocks of the covariance matrix - cov_blocks = np.array(jax_cosmo.angular_cl._get_cov_blocks_ordering(probes)) + cov_blocks = np.array(_get_cov_blocks_ordering(probes)) def get_cov_block(inds): a, b, c, d = inds diff --git a/tests/test_angular_cl.py b/tests/test_angular_cl.py index 96a2704..31ef99b 100644 --- a/tests/test_angular_cl.py +++ b/tests/test_angular_cl.py @@ -1,15 +1,16 @@ import jax.numpy as jnp import numpy as np import pyccl as ccl -from numpy.testing import assert_allclose +from numpy.testing import assert_allclose, assert_array_equal import jax_cosmo.background as bkgrd from jax_cosmo import Cosmology from jax_cosmo import probes -from jax_cosmo.angular_cl import angular_cl +from jax_cosmo.angular_cl import angular_cl, gaussian_cl_covariance from jax_cosmo.bias import constant_linear_bias from jax_cosmo.bias import inverse_growth_linear_bias from jax_cosmo.redshift import smail_nz +from jax_cosmo.sparse import to_dense def test_lensing_cl(): @@ -143,3 +144,18 @@ def test_clustering_cl(): cl_jax = angular_cl(cosmo_jax, ell, [tracer_jax]) assert_allclose(cl_ccl, cl_jax[0], rtol=1e-2) + + +def test_sparse_cov(): + n_ell = 25 + ell = jnp.logspace(1, 3, n_ell) + nz1 = smail_nz(1.0, 2.0, 1.0) + nz2 = smail_nz(1.0, 2.0, 0.5) + n_cls = 3 + P = [probes.NumberCounts([nz1, nz2], constant_linear_bias(1.0))] + cl_signal = jnp.ones((n_cls, n_ell)) + cl_noise = jnp.ones_like(cl_signal) + cov_dense = gaussian_cl_covariance(ell, P, cl_signal, cl_noise, sparse=False) + cov_sparse = gaussian_cl_covariance(ell, P, cl_signal, cl_noise, sparse=True) + assert cov_sparse.shape == (n_cls, n_cls, n_ell) + assert_array_equal(to_dense(cov_sparse), cov_dense) From 1fb5172f74dded50a7cc0f76f85e92cf3c5acacf Mon Sep 17 00:00:00 2001 From: EiffL Date: Wed, 22 Jul 2020 11:40:46 +0200 Subject: [PATCH 08/21] Forcing formatting --- tests/test_sparse.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 3c182a2..1ef23f4 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -1,6 +1,8 @@ import jax.numpy as jnp import numpy as numpy -from numpy.testing import assert_allclose, assert_array_equal, assert_raises +from numpy.testing import assert_allclose +from numpy.testing import assert_array_equal +from numpy.testing import assert_raises from jax_cosmo.sparse import * From 269465ae8ef8ea51ef7a103f492c23ad13ffb79b Mon Sep 17 00:00:00 2001 From: EiffL Date: Wed, 22 Jul 2020 11:41:20 +0200 Subject: [PATCH 09/21] Formmatting --- tests/test_angular_cl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_angular_cl.py b/tests/test_angular_cl.py index 31ef99b..7390407 100644 --- a/tests/test_angular_cl.py +++ b/tests/test_angular_cl.py @@ -1,12 +1,14 @@ import jax.numpy as jnp import numpy as np import pyccl as ccl -from numpy.testing import assert_allclose, assert_array_equal +from numpy.testing import assert_allclose +from numpy.testing import assert_array_equal import jax_cosmo.background as bkgrd from jax_cosmo import Cosmology from jax_cosmo import probes -from jax_cosmo.angular_cl import angular_cl, gaussian_cl_covariance +from jax_cosmo.angular_cl import angular_cl +from jax_cosmo.angular_cl import gaussian_cl_covariance from jax_cosmo.bias import constant_linear_bias from jax_cosmo.bias import inverse_growth_linear_bias from jax_cosmo.redshift import smail_nz From f37c5fa95f7355e308d9eee7f59aebb5e391e4cf Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Wed, 22 Jul 2020 06:33:03 -0700 Subject: [PATCH 10/21] More details on style and pre-commit --- design.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/design.md b/design.md index fb0211d..f1b5a40 100644 --- a/design.md +++ b/design.md @@ -150,9 +150,10 @@ with code styling again. Here are the steps to follow: - Install `black` and `pre-commit`: ```bash $ pip install --user black pre-commit reorder_python_imports +$ pre-commit install ``` -`pre-commit` will be tasked with automatically running `black` formatting -whenever you commit some code. +`pre-commit` will be tasked with automatically running `black` and `reorder_python_imports` formatting +whenever you commit some code. The import guidelines are documented [here](https://github.com/asottile/reorder_python_imports#what-does-it-do). - Manually running black formatting: ```bash @@ -160,7 +161,7 @@ $ black . ``` from the root directory. -- Automatically running black at each commit: You actually have nothing +- Automatically running `black` and `reorder_python_imports` at each commit: You actually have nothing else to do. If pre-commit is installed it will happen automatically for you. From 54c594edb36bd7121f0c66eeb4e26280cdebed7f Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Wed, 22 Jul 2020 12:39:15 -0700 Subject: [PATCH 11/21] Implement sparse matmul --- jax_cosmo/sparse.py | 33 +++++++++++++++++++++++++++++++++ tests/test_sparse.py | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 08ca24c..077c0e7 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -91,3 +91,36 @@ def vecdot(sparse, vec): lambda row, vec: np.sum(vmap(np.multiply)(row, vec.reshape(row.shape)), axis=0), in_axes=(0, None), )(sparse, vec).reshape(-1) + + +@jit +def matmul(sparse1, sparse2): + """Multiply sparse matrices and return a sparse result. + + Parameters + ---------- + sparse1 : array + 3D array of shape (a, b, ndiag) of block diagonal elements. + sparse2 : array + 3D array of shape (b, c, ndiag) of block diagonal elements. + + Returns + ------- + array + 3D array of shape (a, c, ndiag) of block diagonal elements. + """ + sparse1 = check_sparse(sparse1) + sparse2 = check_sparse(sparse2) + if sparse1.shape[1] != sparse2.shape[0]: + raise ValueError("Matrix shapes are not compatible for multiplication.") + return vmap( + # Sparse multiply row @ col + vmap( + # Sparse multiply blocks B1 and B2 + lambda B1, B2: np.sum(np.multiply(B1, B2), axis=0), + (0, None), + 0, + ), + (None, 1), + 1, + )(sparse1, sparse2) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 1ef23f4..79cb843 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -52,3 +52,13 @@ def test_vecdot(): with assert_raises(ValueError): vecdot(X_sparse, jnp.ones(5)) + + +def test_matmul(): + X1 = [[[1.0, 2, 3], [4, 5, 6], [0, -1, 0]], [[-4, -5, -6], [3, 2, 1], [1, 0, 1]]] + X2 = [ + [[1.0, 2, 3], [4, 5, 6]], + [[-4, -5, -6], [3, 2, 1]], + [[-4, -5, -6], [3, 2, 1]], + ] + assert_allclose(to_dense(matmul(X1, X2)), to_dense(X1) @ to_dense(X2)) From 4ae94af1f9a001b73d5211ccd31bb64ca87b9c4a Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Wed, 22 Jul 2020 17:31:18 -0700 Subject: [PATCH 12/21] Implement sparse determinant --- jax_cosmo/sparse.py | 40 ++++++++++++++++++++++++++++++++++++++++ tests/test_sparse.py | 9 +++++++++ 2 files changed, 49 insertions(+) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 077c0e7..bcf17a7 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -9,6 +9,8 @@ from __future__ import division from __future__ import print_function +import functools + import jax.numpy as np from jax import jit from jax import vmap @@ -124,3 +126,41 @@ def matmul(sparse1, sparse2): (None, 1), 1, )(sparse1, sparse2) + + +# We split the determinant calculation for a matrix with N x N blocks +# into n pieces that can be evaluated in parallel, following eqn (2.2) +# of https://arxiv.org/abs/1112.4379. First build a helper function +# to calculate one piece indexed by 0 <= k < N: +@functools.partial(jit, static_argnums=(1, 2, 3)) +def _block_det(sparse, k, N, P): + u = sparse[k : k + 1, k + 1 : N, 0:P] + S = sparse[k + 1 : N, k + 1 : N, 0:P] + v = sparse[k + 1 : N, k : k + 1, 0:P] + Sinv_v = matmul(inv(S), v) + return np.product(sparse[k, k] - matmul(u, Sinv_v)) + + +@jit +def det(sparse): + """Calculate the determinant of a sparse matrix. + + Parameters + ---------- + sparse : array + 3D array of shape (ny, nx, ndiag) of block diagonal elements. + + Returns + ------- + float + Determinant result. + """ + sparse = check_sparse(sparse, square=True) + N, _, P = sparse.shape + result = np.product(sparse[-1, -1]) + # The individual blocks can be calculated in any order so there + # should be a better way to express this using lax.map but I + # can't get it to work without "concretization" errors. + for i in range(N - 1): + result *= _block_det(sparse, i, N, P) + return result diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 79cb843..75d3233 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -62,3 +62,12 @@ def test_matmul(): [[-4, -5, -6], [3, 2, 1]], ] assert_allclose(to_dense(matmul(X1, X2)), to_dense(X1) @ to_dense(X2)) + + +def test_det(): + X = [ + [[1, 2, 3], [4, 5, 6], [-1, 7, -2]], + [[1, 2, 3], [-4, -5, -6], [2, -3, 9]], + [[7, 8, 9], [5, -4, 6], [-3, -2, -1]], + ] + assert_allclose(det(X), np.linalg.det(to_dense(X)), rtol=1e-6) From 2981c204c9cf0063178a25bf8bcd7a70816a80b4 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Thu, 23 Jul 2020 13:23:43 -0700 Subject: [PATCH 13/21] Flesh out *_dot_* functions and implement dot(A,B) front end --- jax_cosmo/sparse.py | 206 ++++++++++++++++++++++++++++++++++++------- tests/test_sparse.py | 41 ++++----- 2 files changed, 194 insertions(+), 53 deletions(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index bcf17a7..30e566b 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -3,7 +3,25 @@ The motivating example is a Gaussian covariance matrix computed in angular_cl. The sparse matrix is represented as a 3D array of shape (ny, nx, ndiag) composed of ny x nx square blocks of size ndiag x ndiag. The vector at [ny, nx] is the -diagonal of the corresponding block. +diagonal of the corresponding block. The memory savings is a factor of ndiag +and most algorithms are sped up by a comparable factor. + +We do not assume that the corresponding dense matrix is square or symmetric, even +though a covariance has these properties, since this streamlines the implementation +for a relatively small (factor of 2) increase in memory. + +This sparse format is not one of those currently supported by scipy.sparse. +The scipy.sparse dia format has a similar memory efficiency but does not take +advantage of the block structure we exploit here for efficient operations. + +For dot products involving a sparse matrix, use :func:`dot` to automatically +select the correct jit-compiled algorithm, with some input validation. You +can also use the lower-level algorithms (with no input validation) directly: + - :fun:`sparse_dot_vec` + - :fun:`sparse_dot_dense` + - :fun:`vec_dot_sparse` + - :fun:`dense_dot_sparse` + - :fun:`sparse_dot_sparse` """ from __future__ import absolute_import from __future__ import division @@ -16,6 +34,12 @@ from jax import vmap +def is_sparse(sparse): + """Test if the input is interpretable as a sparse matrix. + """ + return np.asarray(sparse).ndim == 3 + + def check_sparse(sparse, square=False): """Check for a valid sparse matrix. """ @@ -23,7 +47,7 @@ def check_sparse(sparse, square=False): if sparse.ndim != 3: raise ValueError("Expected 3D array of sparse diagonals.") if square and (sparse.shape[0] != sparse.shape[1]): - raise ValueError("Can only invert a square matrix.") + raise ValueError("Expected a square matrix.") return sparse @@ -34,12 +58,12 @@ def to_dense(sparse): Parameters ---------- sparse : array - 3D array of shape (ny, nx, ndiag) of block diagonal elements. + 3D array of shape (a, b, ndiag) of block diagonal elements. Returns ------- array - 2D array of shape (ny * ndiag, nx * ndiag) with the same dtype + 2D array of shape (a * ndiag, b * ndiag) with the same dtype as the input array. """ sparse = check_sparse(sparse) @@ -47,48 +71,77 @@ def to_dense(sparse): @jit -def inv(sparse): - """Calculate the inverse of a square matrix in sparse format. +def dot(A, B): + """Calculate A @ B where A and B are either sparse or dense. - We currently assume that the matrix is invertible and you should not - trust the answer unless you know this is true (because jax.numpy.linalg.inv - has this behavior). + Checks the inputs and calls the appropriate *_dot_* specialized + jit-compiled method defined below. Input types are identified + by their array dimension: 1 = vector, 2 = dense matrix, + 3 = sparse matrix. + + Returns a dense 1D or 2D array except where A and B are both + sparse matrices, when the result is also a sparse matrix. Parameters ---------- - sparse : array - 3D array of shape (n, n, ndiag) of block diagonal elements. + A : array + Left hand matrix or vector to multiply. + B : array + Right-hand marix or vector to multiply. Returns ------- array - 3D array of shape (n, n, ndiag) of block diagonal elements - representing the inverse matrix. + Result of A @ B as a 1 (vector), 2 (dense matrix), or 3 (sparse + matrix) dimensional array. """ - sparse = check_sparse(sparse, square=True) - return np.transpose(np.linalg.inv(np.transpose(sparse, (2, 0, 1))), (1, 2, 0)) + A = np.asarray(A) + B = np.asarray(B) + if is_sparse(A): + Acols = A.shape[1] * A.shape[2] + else: + if A.ndim < 1 or A.ndim > 2: + raise ValueError(f"A has invalid dimension {A.ndim} (expected 1 or 2).") + Acols = A.shape[-1] + if is_sparse(B): + Brows = B.shape[0] * B.shape[2] + else: + if B.ndim < 1 or B.ndim > 2: + raise ValueError(f"B has invalid dimension {B.ndim} (expected 1 or 2).") + Brows = B.shape[0] + if Acols != Brows: + raise ValueError( + f"Shapes of A {A.shape} and B {B.shape} not compatible for dot product." + ) + + if is_sparse(A): + if is_sparse(B): + return sparse_dot_sparse(A, B) + else: + return sparse_dot_vec(A, B) if B.ndim == 1 else sparse_dot_dense(A, B) + else: + return vec_dot_sparse(A, B) if A.ndim == 1 else dense_dot_sparse(A, B) @jit -def vecdot(sparse, vec): - """Multiply a sparse matrix by a vector. +def sparse_dot_vec(sparse, vec): + """Calculate M @ v where M is a sparse matrix. + + Inputs must be jax numpy arrays. No error checking is performed. + Use :func:`dot` for a more convenient front-end with error checking. Parameters ---------- sparse : array - 3D array of shape (ny, nx, ndiag) of block diagonal elements. + 3D array of shape (a, b, ndiag) of block diagonal elements. vec : array - 1D array of shape (nx). + 1D array of shape (b * ndiag). Returns ------- array - 1D array of shape (ny). + 1D array of shape (a * ndiag). """ - sparse = check_sparse(sparse) - vec = np.asarray(vec) - if vec.ndim != 1 or sparse.shape[1] * sparse.shape[2] != vec.size: - raise ValueError("Vector has the wrong shape for this sparse matrix.") return vmap( lambda row, vec: np.sum(vmap(np.multiply)(row, vec.reshape(row.shape)), axis=0), in_axes=(0, None), @@ -96,8 +149,80 @@ def vecdot(sparse, vec): @jit -def matmul(sparse1, sparse2): - """Multiply sparse matrices and return a sparse result. +def sparse_dot_dense(sparse, dense): + """Calculate A @ B where A is sparse and B is dense and return dense. + + Inputs must be jax numpy arrays. No error checking is performed. + Use :func:`dot` for a more convenient front-end with error checking. + + Parameters + ---------- + sparse : array + 3D array of shape (a, b, ndiag) of block diagonal elements. + dense : array + 2D array of shape (b * ndiag, c). + + Returns + ------- + array + 2D array of shape (a * ndiag, c). + """ + return vmap(sparse_dot_vec, (None, 1), 1)(sparse, dense) + + +@jit +def vec_dot_sparse(vec, sparse): + """Calculate vec @ M where M is a sparse matrix. + + Inputs must be jax numpy arrays. No error checking is performed. + Use :func:`dot` for a more convenient front-end with error checking. + + Parameters + ---------- + vec : array + 1D array of shape (a * ndiag). + sparse : array + 3D array of shape (a, b, ndiag) of block diagonal elements. + + Returns + ------- + array + 1D array of shape (b * ndiag). + """ + return vmap( + lambda vec, col: np.sum(vmap(np.multiply)(vec.reshape(col.shape), col), axis=0), + in_axes=(None, 1), + )(vec, sparse).reshape(-1) + + +@jit +def dense_dot_sparse(dense, sparse): + """Calculate A @ B where A is dense and B is sparse and return dense. + + Inputs must be jax numpy arrays. No error checking is performed. + Use :func:`dot` for a more convenient front-end with error checking. + + Parameters + ---------- + dense : array + 2D array of shape (a * ndiag, b * ndiag). + sparse : array + 3D array of shape (b, c, ndiag) of block diagonal elements. + + Returns + ------- + array + 2D array of shape (a * ndiag, c * ndiag). + """ + return vmap(vec_dot_sparse, (0, None), 0)(dense, sparse) + + +@jit +def sparse_dot_sparse(sparse1, sparse2): + """Calculate A @ B where A and B are both sparse and return sparse. + + Inputs must be jax numpy arrays. No error checking is performed. + Use :func:`dot` for a more convenient front-end with error checking. Parameters ---------- @@ -111,10 +236,6 @@ def matmul(sparse1, sparse2): array 3D array of shape (a, c, ndiag) of block diagonal elements. """ - sparse1 = check_sparse(sparse1) - sparse2 = check_sparse(sparse2) - if sparse1.shape[1] != sparse2.shape[0]: - raise ValueError("Matrix shapes are not compatible for multiplication.") return vmap( # Sparse multiply row @ col vmap( @@ -128,6 +249,29 @@ def matmul(sparse1, sparse2): )(sparse1, sparse2) +@jit +def inv(sparse): + """Calculate the inverse of a square matrix in sparse format. + + We currently assume that the matrix is invertible and you should not + trust the answer unless you know this is true (because jax.numpy.linalg.inv + has this behavior). + + Parameters + ---------- + sparse : array + 3D array of shape (n, n, ndiag) of block diagonal elements. + + Returns + ------- + array + 3D array of shape (n, n, ndiag) of block diagonal elements + representing the inverse matrix. + """ + sparse = check_sparse(sparse, square=True) + return np.transpose(np.linalg.inv(np.transpose(sparse, (2, 0, 1))), (1, 2, 0)) + + # We split the determinant calculation for a matrix with N x N blocks # into n pieces that can be evaluated in parallel, following eqn (2.2) # of https://arxiv.org/abs/1112.4379. First build a helper function @@ -145,6 +289,8 @@ def _block_det(sparse, k, N, P): def det(sparse): """Calculate the determinant of a sparse matrix. + Based on equation (2.2) of https://arxiv.org/abs/1112.4379 + Parameters ---------- sparse : array diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 75d3233..e04ec5f 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -31,6 +31,24 @@ def test_to_dense(): to_dense(jnp.ones((2, 3, 4, 5))) +def test_dot(): + X1 = [[[1.0, 2], [3, 4], [5, 6]], [[4, 5], [6, 7], [8, 9]]] + X2 = [[[1.0, -2], [3, -4]], [[5, 4], [6, -7]], [[5, 6], [9, 8]]] + X1d = to_dense(X1) + X2d = to_dense(X2) + v1 = np.arange(6) + v2 = np.arange(4) + + assert_allclose(X2d @ v2, dot(X2, v2)) + assert_allclose(X1d @ v1, dot(X1, v1)) + assert_allclose(v2 @ X1d, dot(v2, X1)) + assert_allclose(v1 @ X2d, dot(v1, X2)) + assert_allclose(X1d @ X2d, dot(X1, X2d)) + assert_allclose(X1d @ X2d, dot(X1d, X2)) + assert_allclose(X1d @ X2d, to_dense(dot(X1, X2))) + assert_allclose(X2d @ X1d, to_dense(dot(X2, X1))) + + def test_inv(): X_sparse = jnp.array([[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [2.0, 2.0]]]) X_inv_sparse = inv(X_sparse) @@ -41,29 +59,6 @@ def test_inv(): inv(jnp.ones((2, 3, 4))) -def test_vecdot(): - X_sparse = [ - [[1, 2, 3], [4, 5, 6], [-1, -2, -3]], - [[1, 2, 3], [-4, -5, -6], [7, 8, 9]], - ] - y_in = [1, 0.1, -1, 2, 0.2, -2, 3, 0.3, -3] - y_out = to_dense(X_sparse).dot(jnp.array(y_in)) - assert_allclose(y_out, vecdot(X_sparse, y_in)) - - with assert_raises(ValueError): - vecdot(X_sparse, jnp.ones(5)) - - -def test_matmul(): - X1 = [[[1.0, 2, 3], [4, 5, 6], [0, -1, 0]], [[-4, -5, -6], [3, 2, 1], [1, 0, 1]]] - X2 = [ - [[1.0, 2, 3], [4, 5, 6]], - [[-4, -5, -6], [3, 2, 1]], - [[-4, -5, -6], [3, 2, 1]], - ] - assert_allclose(to_dense(matmul(X1, X2)), to_dense(X1) @ to_dense(X2)) - - def test_det(): X = [ [[1, 2, 3], [4, 5, 6], [-1, 7, -2]], From d8eb78866d6ccd6950f0c5da8de800b9507f2750 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Thu, 23 Jul 2020 13:29:22 -0700 Subject: [PATCH 14/21] Update docstring --- jax_cosmo/angular_cl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jax_cosmo/angular_cl.py b/jax_cosmo/angular_cl.py index 0510778..6918189 100644 --- a/jax_cosmo/angular_cl.py +++ b/jax_cosmo/angular_cl.py @@ -180,6 +180,10 @@ def gaussian_cl_covariance_and_mean( """ Computes a Gaussian covariance for the angular cls of the provided probes + Set sparse True to return a sparse matrix representation that uses a factor + of n_ell less memory and is compatible with the linear algebra operations + in :mod:`jax_cosmo.sparse`. + return_cls: (returns signal + noise cl, covariance) """ ell = np.atleast_1d(ell) From 22a1f66af327f6ecd09633a38580091f440b06cd Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Thu, 23 Jul 2020 14:10:18 -0700 Subject: [PATCH 15/21] Fix unit test --- jax_cosmo/sparse.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 30e566b..e270f1b 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -249,6 +249,35 @@ def sparse_dot_sparse(sparse1, sparse2): )(sparse1, sparse2) +@jit +def bilinear(X, Y, Z): + """Calculate the bilinear form X @ Y @ Z where B is sparse. + + Inputs must be jax numpy arrays. No error checking is performed. + + Parameters + ---------- + X : array + 2D array of shape (a, b * ndiag) with dense matrix elements. + Y : array + 3D array of shape (b, c, ndiag) with sparse matrix elements. + Z : array + 2D array of shape (c * ndiag, d) with dense matrix elements. + + Returns + ------- + array + 2D array of shape (a, d) with dense matrix elements. + """ + return vmap( + vmap( + lambda row, sparse, col: np.dot(row, sparse_dot_vec(sparse, col)), + (None, None, 1), + ), + (0, None, None), + )(X, Y, Z) + + @jit def inv(sparse): """Calculate the inverse of a square matrix in sparse format. @@ -281,8 +310,8 @@ def _block_det(sparse, k, N, P): u = sparse[k : k + 1, k + 1 : N, 0:P] S = sparse[k + 1 : N, k + 1 : N, 0:P] v = sparse[k + 1 : N, k : k + 1, 0:P] - Sinv_v = matmul(inv(S), v) - return np.product(sparse[k, k] - matmul(u, Sinv_v)) + Sinv_v = sparse_dot_sparse(inv(S), v) + return np.product(sparse[k, k] - sparse_dot_sparse(u, Sinv_v)) @jit From 0f323a11a35c02955c565a17ff8ad3aaa10fe6e5 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Thu, 23 Jul 2020 14:26:18 -0700 Subject: [PATCH 16/21] Implement bilinear form (dense, sparse, dense) --- jax_cosmo/sparse.py | 88 +++++++++++++++++++++++++++----------------- tests/test_sparse.py | 13 +++++++ 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index e270f1b..33bdba2 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -15,13 +15,17 @@ advantage of the block structure we exploit here for efficient operations. For dot products involving a sparse matrix, use :func:`dot` to automatically -select the correct jit-compiled algorithm, with some input validation. You -can also use the lower-level algorithms (with no input validation) directly: +select the correct jit-compiled algorithm, with some input validation. All +pairs of vector, dense matrix and at least one sparse matrix are +supported. The special bilinear form (dense, sparse, dense) is also supported. + +You can also use the lower-level algorithms (with no input validation) directly: - :fun:`sparse_dot_vec` - :fun:`sparse_dot_dense` - :fun:`vec_dot_sparse` - :fun:`dense_dot_sparse` - :fun:`sparse_dot_sparse` + - :fun:`dense_dot_sparse_dot_dense` """ from __future__ import absolute_import from __future__ import division @@ -71,8 +75,12 @@ def to_dense(sparse): @jit -def dot(A, B): - """Calculate A @ B where A and B are either sparse or dense. +def dot(*args): + """Calculate A @ B where at least one of A or B is sparse. + + All combinations of vector, dense matrix and at least one + sparse matrix are supported. The bilinear form A @ B @ C is + also supported where A and C are dense and B is sparse. Checks the inputs and calls the appropriate *_dot_* specialized jit-compiled method defined below. Input types are identified @@ -84,43 +92,55 @@ def dot(A, B): Parameters ---------- - A : array - Left hand matrix or vector to multiply. - B : array - Right-hand marix or vector to multiply. + args + 2 or 3 arrays to multiply. Returns ------- array - Result of A @ B as a 1 (vector), 2 (dense matrix), or 3 (sparse - matrix) dimensional array. + Result of A @ B or A @ B @ C. """ - A = np.asarray(A) - B = np.asarray(B) - if is_sparse(A): - Acols = A.shape[1] * A.shape[2] - else: - if A.ndim < 1 or A.ndim > 2: - raise ValueError(f"A has invalid dimension {A.ndim} (expected 1 or 2).") - Acols = A.shape[-1] - if is_sparse(B): - Brows = B.shape[0] * B.shape[2] - else: - if B.ndim < 1 or B.ndim > 2: - raise ValueError(f"B has invalid dimension {B.ndim} (expected 1 or 2).") - Brows = B.shape[0] - if Acols != Brows: - raise ValueError( - f"Shapes of A {A.shape} and B {B.shape} not compatible for dot product." - ) - - if is_sparse(A): + if len(args) == 2: + A, B = args + A = np.asarray(A) + B = np.asarray(B) + if is_sparse(A): + Acols = A.shape[1] * A.shape[2] + else: + if A.ndim < 1 or A.ndim > 2: + raise ValueError(f"A has invalid dimension {A.ndim} (expected 1 or 2).") + Acols = A.shape[-1] if is_sparse(B): - return sparse_dot_sparse(A, B) + Brows = B.shape[0] * B.shape[2] + else: + if B.ndim < 1 or B.ndim > 2: + raise ValueError(f"B has invalid dimension {B.ndim} (expected 1 or 2).") + Brows = B.shape[0] + if Acols != Brows: + raise ValueError( + f"Shapes of A {A.shape} and B {B.shape} not compatible for dot product." + ) + if is_sparse(A): + if is_sparse(B): + return sparse_dot_sparse(A, B) + else: + return sparse_dot_vec(A, B) if B.ndim == 1 else sparse_dot_dense(A, B) else: - return sparse_dot_vec(A, B) if B.ndim == 1 else sparse_dot_dense(A, B) + return vec_dot_sparse(A, B) if A.ndim == 1 else dense_dot_sparse(A, B) + elif len(args) == 3: + A, B, C = args + if A.ndim != 2 or B.ndim != 3 or C.ndim != 2: + raise ValueError("Can only handle dense @ sparse @ dense bilinear form.") + if ( + A.shape[1] != B.shape[0] * B.shape[2] + or B.shape[1] * B.shape[2] != C.shape[0] + ): + raise ValueError( + "Shapes of A {A.shape}, B {B.shape}, C {C.shape} not compatible for dot product." + ) + return dense_dot_sparse_dot_dense(A, B, C) else: - return vec_dot_sparse(A, B) if A.ndim == 1 else dense_dot_sparse(A, B) + raise ValueError(f"Expected 2 or 3 input arrays but got {len(args)}.") @jit @@ -250,7 +270,7 @@ def sparse_dot_sparse(sparse1, sparse2): @jit -def bilinear(X, Y, Z): +def dense_dot_sparse_dot_dense(X, Y, Z): """Calculate the bilinear form X @ Y @ Z where B is sparse. Inputs must be jax numpy arrays. No error checking is performed. diff --git a/tests/test_sparse.py b/tests/test_sparse.py index e04ec5f..848ba85 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -49,6 +49,19 @@ def test_dot(): assert_allclose(X2d @ X1d, to_dense(dot(X2, X1))) +def test_bilinear(): + X1 = [[[1.0, 2], [3, 4], [5, 6]], [[4, 5], [6, 7], [8, 9]]] + X2 = [[[1.0, -2], [3, -4]], [[5, 4], [6, -7]], [[5, 6], [9, 8]]] + X1d = to_dense(X1) + X2d = to_dense(X2) + X12 = dot(X2, X1) + X21 = dot(X1, X2) + X12d = X2d @ X1d + X21d = X1d @ X2d + assert_allclose(X1d @ X12d @ X2d, dot(X1d, X12, X2d)) + assert_allclose(X2d @ X21d @ X1d, dot(X2d, X21, X1d)) + + def test_inv(): X_sparse = jnp.array([[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [2.0, 2.0]]]) X_inv_sparse = inv(X_sparse) From ede457b5429129db45ccf61b6d684b6a801e72cb Mon Sep 17 00:00:00 2001 From: EiffL Date: Sat, 25 Jul 2020 18:25:05 +0200 Subject: [PATCH 17/21] Modifies the example notebook to use sparse covariances --- docs/notebooks/jax-cosmo-intro.ipynb | 2284 +++++++++++++------------- jax_cosmo/__init__.py | 1 + jax_cosmo/scipy/interpolate.py | 2 +- 3 files changed, 1146 insertions(+), 1141 deletions(-) diff --git a/docs/notebooks/jax-cosmo-intro.ipynb b/docs/notebooks/jax-cosmo-intro.ipynb index fdaf43c..cdc9a08 100644 --- a/docs/notebooks/jax-cosmo-intro.ipynb +++ b/docs/notebooks/jax-cosmo-intro.ipynb @@ -1,1198 +1,1202 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "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.8.2" - }, - "colab": { - "name": "jax-cosmo-intro.ipynb", - "provenance": [], - "toc_visible": true, - "include_colab_link": true - }, - "accelerator": "GPU" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lpIJcb3tcFkC", - "colab_type": "text" - }, - "source": [ - "# Introduction to jax-cosmo\n", - "\n", - "Authors:\n", - " - [@EiffL](https://github.com/EiffL) (Francois Lanusse)\n", - "\n", - "### Overview\n", - "\n", - "`jax-cosmo` brings the power of automatic differentiation and XLA execution\n", - "to cosmological computations, all the while preserving the readability and human\n", - "friendliness of Python / NumPy.\n", - "\n", - "This is made possible by the [JAX](https://jax.readthedocs.io/en/latest/index.html) framework, which can be summarised as JAX = NumPy + autograd + GPU/TPU. We\n", - "encourage the interested reader to follow this [introduction to JAX](https://jax.readthedocs.io/en/latest/notebooks/quickstart.html) but it will not be necessary to follow this notebook.\n", - "\n", - "\n", - "### Learning objectives\n", - "\n", - "In this short introduction we will cover:\n", - " - How to define computations of **2pt functions**\n", - " - How to execute these computations on **GPU** (spoiler alert, you actually don't need to do anything, it happens automatically)\n", - " - How to **take derivatives** of any quantities by automatic differentation\n", - " - And finally, how to piece all of this together for efficient and reliable **Fisher matrices**.\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Dlb7kXPYEf6Z", - "colab_type": "text" - }, - "source": [ - "## Installing and importing jax-cosmo\n", - "\n", - "One of the important aspects of `jax-cosmo` is that it is entirely Python-based\n", - "so it can trivially be installed without compiling or downloading any third-party tools.\n", - "\n", - "Here is how to install the current release on your system:" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "yZWz-yxPcG6q", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 51 - }, - "outputId": "b315e257-1cb3-4654-c8ff-2b319ab27b13" - }, - "source": [ - "# Installing jax-cosmo\n", - "!pip install --quiet jax-cosmo" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "\u001b[?25l\r\u001b[K |█▌ | 10kB 28.3MB/s eta 0:00:01\r\u001b[K |███ | 20kB 3.0MB/s eta 0:00:01\r\u001b[K |████▍ | 30kB 4.0MB/s eta 0:00:01\r\u001b[K |█████▉ | 40kB 4.3MB/s eta 0:00:01\r\u001b[K |███████▎ | 51kB 3.5MB/s eta 0:00:01\r\u001b[K |████████▊ | 61kB 3.9MB/s eta 0:00:01\r\u001b[K |██████████▏ | 71kB 4.3MB/s eta 0:00:01\r\u001b[K |███████████▋ | 81kB 4.5MB/s eta 0:00:01\r\u001b[K |█████████████ | 92kB 4.9MB/s eta 0:00:01\r\u001b[K |██████████████▌ | 102kB 4.8MB/s eta 0:00:01\r\u001b[K |████████████████ | 112kB 4.8MB/s eta 0:00:01\r\u001b[K |█████████████████▌ | 122kB 4.8MB/s eta 0:00:01\r\u001b[K |███████████████████ | 133kB 4.8MB/s eta 0:00:01\r\u001b[K |████████████████████▍ | 143kB 4.8MB/s eta 0:00:01\r\u001b[K |█████████████████████▉ | 153kB 4.8MB/s eta 0:00:01\r\u001b[K |███████████████████████▎ | 163kB 4.8MB/s eta 0:00:01\r\u001b[K |████████████████████████▊ | 174kB 4.8MB/s eta 0:00:01\r\u001b[K |██████████████████████████▏ | 184kB 4.8MB/s eta 0:00:01\r\u001b[K |███████████████████████████▋ | 194kB 4.8MB/s eta 0:00:01\r\u001b[K |█████████████████████████████ | 204kB 4.8MB/s eta 0:00:01\r\u001b[K |██████████████████████████████▌ | 215kB 4.8MB/s eta 0:00:01\r\u001b[K |████████████████████████████████| 225kB 4.8MB/s \n", - "\u001b[?25h Building wheel for jax-cosmo (setup.py) ... \u001b[?25l\u001b[?25hdone\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xvIGKcbXFEFO", - "colab_type": "text" - }, - "source": [ - "For efficient computation on GPU (if you have one), you might want to make sure that JAX itself is installed with the proper GPU-enabled backend. See [here](https://github.com/google/jax#installation) for more instructions.\n", - "\n", - "Now that `jax-cosmo` is installed, let's import it along with JAX tools:" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "AZkSj6XNcFkE", - "colab_type": "code", - "outputId": "6a325574-7540-4d62-bbfc-fcfaf00f009d", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "%pylab inline\n", - "import jax\n", - "import jax_cosmo as jc\n", - "import jax.numpy as np" - ], - "execution_count": 2, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Populating the interactive namespace from numpy and matplotlib\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bKuyf8bzFmSR", - "colab_type": "text" - }, - "source": [ - "**Note that we import the JAX version of NumPy here**. That's all that you have to do, any numpy functions you will use afterwards will be JAX-accelerated and differentiable.\n", - "\n", - "And for the purpose of this tutorial we also define a few plotting functions in the cell bellow, please run it." - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "8yvBIf1mm_h-", - "colab_type": "code", - "cellView": "form", - "colab": {} - }, - "source": [ - "#@title Defining some plotting functions [run me]\n", - "\n", - "import matplotlib.pyplot as plt\n", - "from matplotlib.patches import Ellipse\n", - "\n", - "def plot_contours(fisher, pos, nstd=1., ax=None, **kwargs):\n", - " \"\"\"\n", - " Plot 2D parameter contours given a Hessian matrix of the likelihood\n", - " \"\"\"\n", - " \n", - " def eigsorted(cov):\n", - " vals, vecs = linalg.eigh(cov)\n", - " order = vals.argsort()[::-1]\n", - " return vals[order], vecs[:, order]\n", - "\n", - " mat = fisher\n", - " cov = np.linalg.inv(mat)\n", - " sigma_marg = lambda i: np.sqrt(cov[i, i])\n", - "\n", - " if ax is None:\n", - " ax = plt.gca()\n", - "\n", - " vals, vecs = eigsorted(cov)\n", - " theta = degrees(np.arctan2(*vecs[:, 0][::-1]))\n", - "\n", - " # Width and height are \"full\" widths, not radius\n", - " width, height = 2 * nstd * sqrt(vals)\n", - " ellip = Ellipse(xy=pos, width=width,\n", - " height=height, angle=theta, **kwargs)\n", - "\n", - " ax.add_artist(ellip)\n", - " sz = max(width, height)\n", - " s1 = 1.5*nstd*sigma_marg(0)\n", - " s2 = 1.5*nstd*sigma_marg(1)\n", - " ax.set_xlim(pos[0] - s1, pos[0] + s1)\n", - " ax.set_ylim(pos[1] - s2, pos[1] + s2)\n", - " plt.draw()\n", - " return ellip" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nXjimh6KGFWm", - "colab_type": "text" - }, - "source": [ - "## Defining a Cosmology and computing background quantities\n", - "\n", - "We'll beginning with the basics, let's define a cosmology:\n" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "R0wxmnuBG9EC", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# Create a cosmology with default parameters\n", - "cosmo = jc.Planck15()" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "by_0gcYKG9Ag", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# Alternatively we can override some of the defaults\n", - "cosmo_modified = jc.Planck15(h=0.7)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "d-VI1BFuI3w1", - "colab_type": "code", - "outputId": "8ed049c5-20bc-4874-87a2-db3e4ed49a4e", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "# Parameters can be easily accessed from the cosmology object\n", - "cosmo.h" - ], - "execution_count": 6, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "0.6774" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 6 - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8RhqkfHjHgTT", - "colab_type": "text" - }, - "source": [ - "All background quantities can be computed from the `jax_cosmo.background` module, they typically take the cosmology as first argument, and a scale factor\n", - "argument if they are not constant." - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "bdcm_oReG89o", - "colab_type": "code", - "outputId": "07e4ff00-3bfb-4bfd-bc61-70350a062435", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 403 - } - }, - "source": [ - "# Let's define a range of scale factors\n", - "a = np.linspace(0.01, 1.)\n", - "\n", - "# And compute the comoving distance for these scale factors \n", - "chi = jc.background.radial_comoving_distance(cosmo, a)\n", - "\n", - "# We can now plot the results:\n", - "plot(a, chi)\n", - "xlabel(r'scale factor $a$')\n", - "ylabel(r'radial comoving distance $\\chi$');" - ], - "execution_count": 7, - "outputs": [ - { - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" - ], - "name": "stderr" - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "z30Karo4Jdnw", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# Not sure what are the units of the comoving distance? just ask:\n", - "jc.background.radial_comoving_distance?" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yihFIALbJ24Q", - "colab_type": "text" - }, - "source": [ - "## Defining redshift distributions\n", - "\n", - "On our path to computing Fisher matrices, we need to be able to express redshift distrbutions. In `jax-cosmo` n(z) are parametrized functions which can\n", - "be found in the `jax_cosmo.redshift` module. \n", - "\n", - "For the purpose of this tutorial, let's see how to define a Smail type distribution:\n", - "$$ n(z) = z^a \\exp(- (z/z_0)^b) $$\n", - "which depends on 3 parameters:" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "2D7ouxvVIR7M", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# You can inspect the documentation to see the \n", - "# meaning of these positional arguments\n", - "nz1 = jc.redshift.smail_nz(1., 2., 1.)\n", - "nz2 = jc.redshift.smail_nz(1., 2., 0.5)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "Ef2oNlQ7Lmdi", - "colab_type": "code", - "outputId": "799bb7a6-1e67-45d8-dfd3-ff3b27ce6f81", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 281 - } - }, - "source": [ - "# And let's plot it\n", - "z = np.linspace(0,5,256)\n", - "\n", - "# Redshift distributions are callable, and they return the normalized distribution\n", - "plot(z, nz1(z), label='z0=1.')\n", - "plot(z, nz2(z), label='z0=0.5')\n", - "legend();\n", - "xlabel('Redshift $z$');" - ], - "execution_count": 10, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "0eG0GXjCLmhz", - "colab_type": "code", - "outputId": "283348ed-0a18-45b4-a584-a58db0a72c39", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "# We can check that the nz is properly normalized\n", - "jc.scipy.integrate.romb(nz1, 0., 5.)" - ], - "execution_count": 11, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "DeviceArray(1.0000004, dtype=float32)" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 11 - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ZUYVlhKkMLpl", - "colab_type": "text" - }, - "source": [ - "Nice :-D " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "PGCY4irsNI9B", - "colab_type": "text" - }, - "source": [ - "## Defining probes and computing angular $C_\\ell$\n", - "\n", - "Let's now move on to define lensing and clustering probes using these two n(z).\n", - "In `jax-cosmo` a probe/tracer of a given type, i.e. lensing, contains a series of parameters, like redshift distributions, or galaxy bias. Probes are hosted in\n", - "the `jax_cosmo.probes` module.\n", - "\n", - "$C_\\ell$ computations will then take as argument a list of probes and will compute all auto- and cross- correlations between all redshift bins of all probes. " - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "-YUfaBhzNINW", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# First we define a list of redshift bins\n", - "nzs = [nz1, nz2]" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "R3qUxP9wO6fH", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# And now we define 2 probes \n", - "probes = [ jc.probes.WeakLensing(nzs, sigma_e=0.26), \n", - " jc.probes.NumberCounts(nzs, jc.bias.constant_linear_bias(1.)) ]" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "t40aS024QFHx", - "colab_type": "text" - }, - "source": [ - "Given these probes, we can now compute tomographic angular power spectra for these probes using the `angular_cl` tools hosted in the `jax_cosmo.angular_cl` module. For now, all computations are done under the Limber approximation." - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "QWedY8i6cFkw", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 139 - }, - "outputId": "d8b34187-8daf-4218-84a1-e6093a5868f2" - }, - "source": [ - "# Let's define a range of \\ell\n", - "ell = np.logspace(1,3)\n", - "\n", - "# And compute the data vector\n", - "cls = jc.angular_cl.angular_cl(cosmo, ell, probes)" - ], - "execution_count": 14, - "outputs": [ - { - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" - ], - "name": "stderr" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "VSKlZxxARxYO", - "colab_type": "code", - "outputId": "3d39a4d7-165e-428d-d2a8-d482353d2064", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "# Let's check the shape of these Cls\n", - "cls.shape" - ], - "execution_count": 15, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "(10, 50)" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 15 - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "X-Vnim-cSQSh", - "colab_type": "text" - }, - "source": [ - "We see that we have obtained 10 spectra, each of them of size 50, which is the length of the $\\ell$ vector. They are ordered first by probe, then by redshift bin. So the first cl is the lensing auto-spectrum of the first bin" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "-Xc458aidYL8", - "colab_type": "code", - "outputId": "960b3f8d-8bb4-4018-f45d-869de305ca19", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 303 - } - }, - "source": [ - "# This is for instance the first bin auto-spectrum \n", - "loglog(ell, cls[0])\n", - "ylabel(r'$C_\\ell$')\n", - "xlabel(r'$\\ell$');\n", - "title(r'Angular $C_\\ell$');" - ], - "execution_count": 16, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Ri-QjcD8UckV", - "colab_type": "text" - }, - "source": [ - "In addition to the data vector, we can also compute the covariance matrix using the tools from that module. Here is an example:" - ] + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lpIJcb3tcFkC" + }, + "source": [ + "# Introduction to jax-cosmo\n", + "\n", + "Authors:\n", + " - [@EiffL](https://github.com/EiffL) (Francois Lanusse)\n", + "\n", + "### Overview\n", + "\n", + "`jax-cosmo` brings the power of automatic differentiation and XLA execution\n", + "to cosmological computations, all the while preserving the readability and human\n", + "friendliness of Python / NumPy.\n", + "\n", + "This is made possible by the [JAX](https://jax.readthedocs.io/en/latest/index.html) framework, which can be summarised as JAX = NumPy + autograd + GPU/TPU. We\n", + "encourage the interested reader to follow this [introduction to JAX](https://jax.readthedocs.io/en/latest/notebooks/quickstart.html) but it will not be necessary to follow this notebook.\n", + "\n", + "\n", + "### Learning objectives\n", + "\n", + "In this short introduction we will cover:\n", + " - How to define computations of **2pt functions**\n", + " - How to execute these computations on **GPU** (spoiler alert, you actually don't need to do anything, it happens automatically)\n", + " - How to **take derivatives** of any quantities by automatic differentation\n", + " - And finally, how to piece all of this together for efficient and reliable **Fisher matrices**.\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Dlb7kXPYEf6Z" + }, + "source": [ + "## Installing and importing jax-cosmo\n", + "\n", + "One of the important aspects of `jax-cosmo` is that it is entirely Python-based\n", + "so it can trivially be installed without compiling or downloading any third-party tools.\n", + "\n", + "Here is how to install the current release on your system:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 51 }, + "colab_type": "code", + "id": "yZWz-yxPcG6q", + "outputId": "b315e257-1cb3-4654-c8ff-2b319ab27b13" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "zIdQSRgkUYC7", - "colab_type": "code", - "colab": {} - }, - "source": [ - "mu, cov = jc.angular_cl.gaussian_cl_covariance_and_mean(cosmo, ell, probes);" - ], - "execution_count": 0, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[?25l\r", + "\u001b[K |█▌ | 10kB 28.3MB/s eta 0:00:01\r", + "\u001b[K |███ | 20kB 3.0MB/s eta 0:00:01\r", + "\u001b[K |████▍ | 30kB 4.0MB/s eta 0:00:01\r", + "\u001b[K |█████▉ | 40kB 4.3MB/s eta 0:00:01\r", + "\u001b[K |███████▎ | 51kB 3.5MB/s eta 0:00:01\r", + "\u001b[K |████████▊ | 61kB 3.9MB/s eta 0:00:01\r", + "\u001b[K |██████████▏ | 71kB 4.3MB/s eta 0:00:01\r", + "\u001b[K |███████████▋ | 81kB 4.5MB/s eta 0:00:01\r", + "\u001b[K |█████████████ | 92kB 4.9MB/s eta 0:00:01\r", + "\u001b[K |██████████████▌ | 102kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |████████████████ | 112kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |█████████████████▌ | 122kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |███████████████████ | 133kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |████████████████████▍ | 143kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |█████████████████████▉ | 153kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |███████████████████████▎ | 163kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |████████████████████████▊ | 174kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |██████████████████████████▏ | 184kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |███████████████████████████▋ | 194kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |█████████████████████████████ | 204kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |██████████████████████████████▌ | 215kB 4.8MB/s eta 0:00:01\r", + "\u001b[K |████████████████████████████████| 225kB 4.8MB/s \n", + "\u001b[?25h Building wheel for jax-cosmo (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + ] + } + ], + "source": [ + "# Installing jax-cosmo\n", + "!pip install --quiet jax-cosmo" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xvIGKcbXFEFO" + }, + "source": [ + "For efficient computation on GPU (if you have one), you might want to make sure that JAX itself is installed with the proper GPU-enabled backend. See [here](https://github.com/google/jax#installation) for more instructions.\n", + "\n", + "Now that `jax-cosmo` is installed, let's import it along with JAX tools:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "AZkSj6XNcFkE", + "outputId": "6a325574-7540-4d62-bbfc-fcfaf00f009d" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "yGd3NelNVZpj", - "colab_type": "text" - }, - "source": [ - "The data vector from this function is in a flattened shape so that it can be multiplied by the covariance matrix easily." - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%pylab inline\n", + "import jax\n", + "import jax_cosmo as jc\n", + "import jax.numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bKuyf8bzFmSR" + }, + "source": [ + "**Note that we import the JAX version of NumPy here**. That's all that you have to do, any numpy functions you will use afterwards will be JAX-accelerated and differentiable.\n", + "\n", + "And for the purpose of this tutorial we also define a few plotting functions in the cell bellow, please run it." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "8yvBIf1mm_h-" + }, + "outputs": [], + "source": [ + "#@title Defining some plotting functions [run me]\n", + "\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.patches import Ellipse\n", + "\n", + "def plot_contours(fisher, pos, nstd=1., ax=None, **kwargs):\n", + " \"\"\"\n", + " Plot 2D parameter contours given a Hessian matrix of the likelihood\n", + " \"\"\"\n", + " \n", + " def eigsorted(cov):\n", + " vals, vecs = linalg.eigh(cov)\n", + " order = vals.argsort()[::-1]\n", + " return vals[order], vecs[:, order]\n", + "\n", + " mat = fisher\n", + " cov = np.linalg.inv(mat)\n", + " sigma_marg = lambda i: np.sqrt(cov[i, i])\n", + "\n", + " if ax is None:\n", + " ax = plt.gca()\n", + "\n", + " vals, vecs = eigsorted(cov)\n", + " theta = degrees(np.arctan2(*vecs[:, 0][::-1]))\n", + "\n", + " # Width and height are \"full\" widths, not radius\n", + " width, height = 2 * nstd * sqrt(vals)\n", + " ellip = Ellipse(xy=pos, width=width,\n", + " height=height, angle=theta, **kwargs)\n", + "\n", + " ax.add_artist(ellip)\n", + " sz = max(width, height)\n", + " s1 = 1.5*nstd*sigma_marg(0)\n", + " s2 = 1.5*nstd*sigma_marg(1)\n", + " ax.set_xlim(pos[0] - s1, pos[0] + s1)\n", + " ax.set_ylim(pos[1] - s2, pos[1] + s2)\n", + " plt.draw()\n", + " return ellip" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nXjimh6KGFWm" + }, + "source": [ + "## Defining a Cosmology and computing background quantities\n", + "\n", + "We'll beginning with the basics, let's define a cosmology:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "R0wxmnuBG9EC" + }, + "outputs": [], + "source": [ + "# Create a cosmology with default parameters\n", + "cosmo = jc.Planck15()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "by_0gcYKG9Ag" + }, + "outputs": [], + "source": [ + "# Alternatively we can override some of the defaults\n", + "cosmo_modified = jc.Planck15(h=0.7)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "d-VI1BFuI3w1", + "outputId": "8ed049c5-20bc-4874-87a2-db3e4ed49a4e" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "WX5lmHsRVXIh", - "colab_type": "code", - "outputId": "64a404cf-9269-4e8b-ff67-3de6eb3ba183", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 265 - } - }, - "source": [ - "semilogy(mu);" - ], - "execution_count": 18, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } + "data": { + "text/plain": [ + "0.6774" ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Parameters can be easily accessed from the cosmology object\n", + "cosmo.h" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8RhqkfHjHgTT" + }, + "source": [ + "All background quantities can be computed from the `jax_cosmo.background` module, they typically take the cosmology as first argument, and a scale factor\n", + "argument if they are not constant." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 403 }, + "colab_type": "code", + "id": "bdcm_oReG89o", + "outputId": "07e4ff00-3bfb-4bfd-bc61-70350a062435" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "KLdw1eSvVXE3", - "colab_type": "code", - "outputId": "cc8fc33a-ccb2-47a8-8e3b-cf4fdc4d9eb1", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 595 - } - }, - "source": [ - "figure(figsize=(10,10))\n", - "imshow(np.log10(cov+1e-11),cmap='gist_stern');" - ], - "execution_count": 19, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkcAAAJCCAYAAADKjmNEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nO3df5Ddd33f+9dnd7WWZQH2yhhjC8dxJMo4NBDXVzaE4RIorVW4MZ2huYBU3AypJ7mNnSbpBNLJvUDm/mhyZ0Jq5zYdJnDjxGoJQ5tCaFSGC3ScSYkUQRJ+mAZtKD9ssIW9/iVkeb06n/vHnnU/dR0sy2f3u+fjx2PmOzrnu0fW++vv2d3nfr/fc7bUWgMAwKqZoQcAANhMxBEAQEMcAQA0xBEAQEMcAQA0xBEAQGNd4qiUck0p5S9KKYullHesx78BALAeyqTf56iUMpvky0lem+SOJH+S5M211tsn+g8BAKyD9ThytCfJYq31K7XW5SQfSHLtOvw7AAATN7cO/82Lk3yjuX9Hkqu+218opXibbgDYpGaTnBp6iPVxT631uY9fOdgF2aWU60spR0opR4aaAQB4cqeSLJQuX8P1tSdauR5Hju5M8oLm/s7xuv9GrfW9Sd6bOHIEAJvdUh0NPcKGWY8M/JMku0sp31tKmU/ypiQfWYd/BwBg4iZ+5KjWulJK+akkH8vqacr311q/OOl/BwBgPUz8pfxnNITTagDAxvtMrfXKx6/s8uoqAIAzJY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABriCACgIY4AABri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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" + ] }, { - "cell_type": "markdown", - "metadata": { - "id": "hN5jA8ogp7Bb", - "colab_type": "text" - }, - "source": [ - "## Where the wild things are: Automatic Differentiation\n", - "\n", - "Now that we know how to compute various quantities, we can move on to the amazing part, computing gradients automatically by autodiff. As an example, we\n", - "will demonstrate how to analytically **compute Fisher matrices, without finite differences.** But gradients are usefull for a wide range of other applications.\n", - "\n", - "\n", - "We begin by defining a Gaussian likelihood function for the data vector we have \n", - "obtained at the previous step. And we make this likelihood function depend on an array of parameters, `Omega_c`, `sigma_8`.\n", - " \n", - "\n" + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Let's define a range of scale factors\n", + "a = np.linspace(0.01, 1.)\n", + "\n", + "# And compute the comoving distance for these scale factors \n", + "chi = jc.background.radial_comoving_distance(cosmo, a)\n", + "\n", + "# We can now plot the results:\n", + "plot(a, chi)\n", + "xlabel(r'scale factor $a$')\n", + "ylabel(r'radial comoving distance $\\chi$');" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "z30Karo4Jdnw" + }, + "outputs": [], + "source": [ + "# Not sure what are the units of the comoving distance? just ask:\n", + "jc.background.radial_comoving_distance?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yihFIALbJ24Q" + }, + "source": [ + "## Defining redshift distributions\n", + "\n", + "On our path to computing Fisher matrices, we need to be able to express redshift distrbutions. In `jax-cosmo` n(z) are parametrized functions which can\n", + "be found in the `jax_cosmo.redshift` module. \n", + "\n", + "For the purpose of this tutorial, let's see how to define a Smail type distribution:\n", + "$$ n(z) = z^a \\exp(- (z/z_0)^b) $$\n", + "which depends on 3 parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2D7ouxvVIR7M" + }, + "outputs": [], + "source": [ + "# You can inspect the documentation to see the \n", + "# meaning of these positional arguments\n", + "nz1 = jc.redshift.smail_nz(1., 2., 1.)\n", + "nz2 = jc.redshift.smail_nz(1., 2., 0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 281 }, + "colab_type": "code", + "id": "Ef2oNlQ7Lmdi", + "outputId": "799bb7a6-1e67-45d8-dfd3-ff3b27ce6f81" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "QUBA8ajicFk4", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# Let's define a parameter vector for Omega_cdm, sigma8, which we initialize \n", - "# at the fiducial cosmology used to produce the data vector.\n", - "data = mu;\n", - "params = np.array([cosmo.Omega_c, cosmo.sigma8])\n", - "\n", - "# Note the `jit` decorator for just in time compilation, this makes your code\n", - "# run fast on GPU :-)\n", - "@jax.jit\n", - "def likelihood(p):\n", - " # Create a new cosmology at these parameters\n", - " cosmo = jc.Planck15(Omega_c=p[0], sigma8=p[1])\n", - "\n", - " # Compute mean and covariance of angular Cls\n", - " m, C = jc.angular_cl.gaussian_cl_covariance_and_mean(cosmo, ell, probes)\n", - "\n", - " # Return likelihood value assuming constant covariance, so we stop the gradient\n", - " # at the level of the precision matrix, and we will not include the logdet term\n", - " # in the likelihood\n", - " P = jax.lax.stop_gradient(np.linalg.inv(C))\n", - " r = data - m\n", - " return -0.5 * (r.T @ P @ r)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "4Us1pbt1dt-h", - "colab_type": "code", - "outputId": "42bfcaff-0ed7-457f-95ce-108d1d8462eb", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 51 - } - }, - "source": [ - "# Computing the likelihood at our fiducial params, we should get 0 since we don't\n", - "# have the normalization term\n", - "print(likelihood(params))\n", - "%timeit likelihood(params).block_until_ready()" - ], - "execution_count": 21, - "outputs": [ - { - "output_type": "stream", - "text": [ - "-2.5765703e-09\n", - "10 loops, best of 3: 40.5 ms per loop\n" - ], - "name": "stdout" - } + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# And let's plot it\n", + "z = np.linspace(0,5,256)\n", + "\n", + "# Redshift distributions are callable, and they return the normalized distribution\n", + "plot(z, nz1(z), label='z0=1.')\n", + "plot(z, nz2(z), label='z0=0.5')\n", + "legend();\n", + "xlabel('Redshift $z$');" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "0eG0GXjCLmhz", + "outputId": "283348ed-0a18-45b4-a584-a58db0a72c39" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "EmJfTrVSySAW", - "colab_type": "text" - }, - "source": [ - "This is an illustration of evaluating the full likelihood. Note that because we \n", - "used the `@jax.jit` decorator on the likelihood, this code is being compiled to \n", - "and XLA expression that runs automatically on the GPU if it's available. \n", - "\n", - "\n", - "But now that we have a likelihood function of the parameters, we can manipulate\n", - "it with JAX, and in particular take the second derivative of this likelihood \n", - "with respect to the input cosmological parameters. This Hessian, is just minus \n", - "the Fisher matrix when everything is nice and Gaussian around the fiducial comology.\n", - "\n", - "\n", - "So this mean, by JAX automaticatic differentiation, we can analytically derive\n", - "the Fisher matrix in just one line:\n" + "data": { + "text/plain": [ + "DeviceArray(1.0000004, dtype=float32)" ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# We can check that the nz is properly normalized\n", + "jc.scipy.integrate.romb(nz1, 0., 5.)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ZUYVlhKkMLpl" + }, + "source": [ + "Nice :-D " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "PGCY4irsNI9B" + }, + "source": [ + "## Defining probes and computing angular $C_\\ell$\n", + "\n", + "Let's now move on to define lensing and clustering probes using these two n(z).\n", + "In `jax-cosmo` a probe/tracer of a given type, i.e. lensing, contains a series of parameters, like redshift distributions, or galaxy bias. Probes are hosted in\n", + "the `jax_cosmo.probes` module.\n", + "\n", + "$C_\\ell$ computations will then take as argument a list of probes and will compute all auto- and cross- correlations between all redshift bins of all probes. " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-YUfaBhzNINW" + }, + "outputs": [], + "source": [ + "# First we define a list of redshift bins\n", + "nzs = [nz1, nz2]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "R3qUxP9wO6fH" + }, + "outputs": [], + "source": [ + "# And now we define 2 probes \n", + "probes = [ jc.probes.WeakLensing(nzs, sigma_e=0.26), \n", + " jc.probes.NumberCounts(nzs, jc.bias.constant_linear_bias(1.)) ]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "t40aS024QFHx" + }, + "source": [ + "Given these probes, we can now compute tomographic angular power spectra for these probes using the `angular_cl` tools hosted in the `jax_cosmo.angular_cl` module. For now, all computations are done under the Limber approximation." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 139 }, + "colab_type": "code", + "id": "QWedY8i6cFkw", + "outputId": "d8b34187-8daf-4218-84a1-e6093a5868f2" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "V9vX2W1UyRhm", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 139 - }, - "outputId": "e5985d95-374b-4150-8b28-e16218ab9d45" - }, - "source": [ - "# Compile a function that computes the Hessian of the likelihood\n", - "hessian_loglik = jax.jit(jax.hessian(likelihood))\n", - "\n", - "# Evalauate the Hessian at fiductial cosmology to retrieve Fisher matrix\n", - "F = - hessian_loglik(params)" - ], - "execution_count": 22, - "outputs": [ - { - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" - ], - "name": "stderr" - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" + ] + } + ], + "source": [ + "# Let's define a range of \\ell\n", + "ell = np.logspace(1,3)\n", + "\n", + "# And compute the data vector\n", + "cls = jc.angular_cl.angular_cl(cosmo, ell, probes)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "VSKlZxxARxYO", + "outputId": "3d39a4d7-165e-428d-d2a8-d482353d2064" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "_Vvm8-IpB4rf", - "colab_type": "text" - }, - "source": [ - "What we are doing on the line above is taking the Hessian of the likelihood function, and evaluating at the fiducial cosmology. We surround the whole thing \n", - "with a `jit` instruction so that the function gets compiled and evaluated in one\n", - "block in the GPU.\n", - "\n", - "Compiling the function is not instantaneous, but once compiled, it becomes fast but the evaluation is:" + "data": { + "text/plain": [ + "(10, 50)" ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Let's check the shape of these Cls\n", + "cls.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "X-Vnim-cSQSh" + }, + "source": [ + "We see that we have obtained 10 spectra, each of them of size 50, which is the length of the $\\ell$ vector. They are ordered first by probe, then by redshift bin. So the first cl is the lensing auto-spectrum of the first bin" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 303 }, + "colab_type": "code", + "id": "-Xc458aidYL8", + "outputId": "960b3f8d-8bb4-4018-f45d-869de305ca19" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "NgrRoxsSB3UZ", - "colab_type": "code", - "outputId": "ec070fd3-1f46-449c-e5c5-bca82ccae07d", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "%timeit hessian_loglik(params).block_until_ready()" - ], - "execution_count": 23, - "outputs": [ - { - "output_type": "stream", - "text": [ - "1 loop, best of 3: 270 ms per loop\n" - ], - "name": "stdout" - } + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] - }, + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# This is for instance the first bin auto-spectrum \n", + "loglog(ell, cls[0])\n", + "ylabel(r'$C_\\ell$')\n", + "xlabel(r'$\\ell$');\n", + "title(r'Angular $C_\\ell$');" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ri-QjcD8UckV" + }, + "source": [ + "In addition to the data vector, we can also compute the covariance matrix using the tools from that module. Here is an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zIdQSRgkUYC7" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "ZqXezv82EnxE", - "colab_type": "text" - }, - "source": [ - "And best of all: **No derivatives were harmed by finite differences in the computation of this Fisher!**\n", - "\n", - "We can now try to plot it:" - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" + ] + } + ], + "source": [ + "mu, cov = jc.angular_cl.gaussian_cl_covariance_and_mean(cosmo, ell, probes, sparse=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yGd3NelNVZpj" + }, + "source": [ + "The data vector from this function is in a flattened shape so that it can be multiplied by the covariance matrix easily." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 }, + "colab_type": "code", + "id": "WX5lmHsRVXIh", + "outputId": "64a404cf-9269-4e8b-ff67-3de6eb3ba183" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "pmTdQeeXk8qB", - "colab_type": "code", - "outputId": "3ac0f9a9-3dc5-4dd4-b58b-fa6a6d8e1291", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 299 - } - }, - "source": [ - "# We can now plot contours obtained with this \n", - "plot_contours(F, params, fill=False);\n", - "xlabel('Omega_m')\n", - "ylabel('sigma8')" - ], - "execution_count": 25, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(14.5, 0.5, 'sigma8')" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 25 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "semilogy(mu);" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 595 }, + "colab_type": "code", + "id": "KLdw1eSvVXE3", + "outputId": "cc8fc33a-ccb2-47a8-8e3b-cf4fdc4d9eb1" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "dEXC2lIlE5IN", - "colab_type": "text" - }, - "source": [ - "And just to reinforce this point and demonstrate further audodiff magic, let's try to derive the same matrix differently, using the usual formula for constant\n", - "covariance:\n", - "\n", - "$$ F_{\\alpha, \\beta} = \\sum_{i,j} \\frac{d \\mu_i}{d \\theta_\\alpha} C^{-1}_{i,j} \\frac{d \\mu_j}{d \\theta_\\beta} $$\n", - "\n", - "What we need in this expression, is the covariance matrix, which we already have\n", - "and the Jacobian of the mean with respect to parameters. Normally you would need to use finite differencing, but luckily we can get that easily with JAX:" + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "figure(figsize=(10,10))\n", + "imshow(np.log10(jc.sparse.to_dense(cov)+1e-11),cmap='gist_stern');" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hN5jA8ogp7Bb" + }, + "source": [ + "## Where the wild things are: Automatic Differentiation\n", + "\n", + "Now that we know how to compute various quantities, we can move on to the amazing part, computing gradients automatically by autodiff. As an example, we\n", + "will demonstrate how to analytically **compute Fisher matrices, without finite differences.** But gradients are usefull for a wide range of other applications.\n", + "\n", + "\n", + "We begin by defining a Gaussian likelihood function for the data vector we have \n", + "obtained at the previous step. And we make this likelihood function depend on an array of parameters, `Omega_c`, `sigma_8`.\n", + " \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "QUBA8ajicFk4" + }, + "outputs": [], + "source": [ + "# Let's define a parameter vector for Omega_cdm, sigma8, which we initialize \n", + "# at the fiducial cosmology used to produce the data vector.\n", + "data = mu;\n", + "params = np.array([cosmo.Omega_c, cosmo.sigma8])\n", + "\n", + "# Note the `jit` decorator for just in time compilation, this makes your code\n", + "# run fast on GPU :-)\n", + "@jax.jit\n", + "def likelihood(p):\n", + " # Create a new cosmology at these parameters\n", + " cosmo = jc.Planck15(Omega_c=p[0], sigma8=p[1])\n", + "\n", + " # Compute mean and covariance of angular Cls\n", + " m, C = jc.angular_cl.gaussian_cl_covariance_and_mean(cosmo, ell, probes, sparse=True)\n", + "\n", + " # Return likelihood value assuming constant covariance, so we stop the gradient\n", + " # at the level of the precision matrix, and we will not include the logdet term\n", + " # in the likelihood\n", + " P = jc.sparse.inv(jax.lax.stop_gradient(C))\n", + " r = data - m\n", + " return -0.5 * r.T @ jc.sparse.sparse_dot_vec(P, r)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 51 }, + "colab_type": "code", + "id": "4Us1pbt1dt-h", + "outputId": "42bfcaff-0ed7-457f-95ce-108d1d8462eb" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "WKn4COsdlKfs", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# We define a parameter dependent function that computes the mean\n", - "def mean_fn(p):\n", - " cosmo = jc.Planck15(Omega_c=p[0], sigma8=p[1])\n", - " # Compute signal vector\n", - " m = jc.angular_cl.angular_cl(cosmo, ell, probes)\n", - " return m.flatten() # We want it in 1d to operate against the covariance matrix" - ], - "execution_count": 0, - "outputs": [] + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", + "/home/francois/.local/lib/python3.8/site-packages/jax/lax/lax.py:5591: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", + " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" + ] }, { - "cell_type": "code", - "metadata": { - "id": "Be381gp6Gjqx", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# We compute it's jacobian with JAX, and we JIT it for efficiency\n", - "jac_mean = jax.jit(jax.jacfwd(mean_fn))" - ], - "execution_count": 0, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "-3.5111292e-09\n", + "29.1 ms ± 676 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" + ] + } + ], + "source": [ + "# Computing the likelihood at our fiducial params, we should get 0 since we don't\n", + "# have the normalization term\n", + "print(likelihood(params))\n", + "%timeit likelihood(params).block_until_ready()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EmJfTrVSySAW" + }, + "source": [ + "This is an illustration of evaluating the full likelihood. Note that because we \n", + "used the `@jax.jit` decorator on the likelihood, this code is being compiled to \n", + "and XLA expression that runs automatically on the GPU if it's available. \n", + "\n", + "\n", + "But now that we have a likelihood function of the parameters, we can manipulate\n", + "it with JAX, and in particular take the second derivative of this likelihood \n", + "with respect to the input cosmological parameters. This Hessian, is just minus \n", + "the Fisher matrix when everything is nice and Gaussian around the fiducial comology.\n", + "\n", + "\n", + "So this mean, by JAX automaticatic differentiation, we can analytically derive\n", + "the Fisher matrix in just one line:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 139 }, - { - "cell_type": "code", - "metadata": { - "id": "t3kVMfEaGyuJ", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 139 - }, - "outputId": "339ec1c1-4f47-43e9-f692-9c9070f5f0a2" - }, - "source": [ - "# We can now evaluate the jacobian at the fiducial cosmology\n", - "dmu = jac_mean(params)" - ], - "execution_count": 28, - "outputs": [ - { - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in asarray is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype float64 requested in array is not available, and will be truncated to dtype float32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n", - "/usr/local/lib/python3.6/dist-packages/jax/lax/lax.py:5222: UserWarning: Explicitly requested dtype requested in astype is not available, and will be truncated to dtype int32. To enable more dtypes, set the jax_enable_x64 configuration option or the JAX_ENABLE_X64 shell environment variable. See https://github.com/google/jax#current-gotchas for more.\n", - " warnings.warn(msg.format(dtype, fun_name , truncated_dtype))\n" - ], - "name": "stderr" - } - ] + "colab_type": "code", + "id": "V9vX2W1UyRhm", + "outputId": "e5985d95-374b-4150-8b28-e16218ab9d45" + }, + "outputs": [], + "source": [ + "# Compile a function that computes the Hessian of the likelihood\n", + "hessian_loglik = jax.jit(jax.hessian(likelihood))\n", + "\n", + "# Evalauate the Hessian at fiductial cosmology to retrieve Fisher matrix\n", + "F = - hessian_loglik(params)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_Vvm8-IpB4rf" + }, + "source": [ + "What we are doing on the line above is taking the Hessian of the likelihood function, and evaluating at the fiducial cosmology. We surround the whole thing \n", + "with a `jit` instruction so that the function gets compiled and evaluated in one\n", + "block in the GPU.\n", + "\n", + "Compiling the function is not instantaneous, but once compiled, it becomes fast but the evaluation is:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "NgrRoxsSB3UZ", + "outputId": "ec070fd3-1f46-449c-e5c5-bca82ccae07d" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "H6uzzV-jHnNe", - "colab_type": "code", - "outputId": "ed61a0df-5f6f-485b-ebbc-33ddaaa15c20", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "dmu.shape" - ], - "execution_count": 29, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "(500, 2)" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 29 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "479 ms ± 9.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%timeit hessian_loglik(params).block_until_ready()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ZqXezv82EnxE" + }, + "source": [ + "And best of all: **No derivatives were harmed by finite differences in the computation of this Fisher!**\n", + "\n", + "We can now try to plot it:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 299 }, + "colab_type": "code", + "id": "pmTdQeeXk8qB", + "outputId": "3ac0f9a9-3dc5-4dd4-b58b-fa6a6d8e1291" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "X9ZDB3RtHFnG", - "colab_type": "code", - "outputId": "07f53328-fb3a-4ead-bdaf-d6528136a8aa", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - } - }, - "source": [ - "# For fun, we can alsi time it\n", - "%timeit jac_mean(params).block_until_ready()" - ], - "execution_count": 30, - "outputs": [ - { - "output_type": "stream", - "text": [ - "10 loops, best of 3: 31.6 ms per loop\n" - ], - "name": "stdout" - } + "data": { + "text/plain": [ + "Text(8.125, 0.5, 'sigma8')" ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" }, { - "cell_type": "markdown", - "metadata": { - "id": "ej3RdeaeHWy6", - "colab_type": "text" - }, - "source": [ - "Getting these gradients is the same order of time than evaluating the forward function!" + "data": { + "image/png": "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Y/zijTfeoHTp0vTu3dvtUvIdCxXjUzVq1OCnn35iy5YtXH/99TbOYoJOSkoKL730Eu+++y5nX3rQgIWK8YOIiAjmzp1Lp06daNKkCZ988onbJRnjE6rKAw88wMMPP0z16tXdLidfslAxfhESEsIjjzzCvHnzeO655+jduzdHjx51uyxjLsmECRPYsWMHjz32mNul5FsWKsavYmNjWblyJWFhYcTGxrJ8+XK3SzLmovzwww8MHz6c6dOnExYW5nY5+ZaFivG74sWLM378eF5++WXatm3Lyy+/TEZGhttlGZNnv/76K126dOHjjz8mJibG7XLyNQsVc9l06tSJpKQkvvrqK2655RZ27tzpdknGnNfBgwf529/+xgsvvMDNN9/sdjn5noWKuawqV67MwoULufnmm7nuuut49dVXOX36tNtlGZOjtLQ0OnbsyB133MHAgQPdLicgWKiYyy40NJQnn3ySZcuWkZCQwHXXXUdiYqLbZRlzFlXl3nvvJSIigldeecXtcgKGhYpxTbVq1fj666/55z//SdeuXenXrx8HDhxwuyxjsr4xv379eqZMmWJXIL4AFirGVSJC586d2bBhA6VLl6ZOnTq8//77ZGZmul2aKaBOnDjBXXfdxW+//caCBQsoXry42yUFFAsVky+UKlWKUaNGMW/ePMaPH098fDxr1651uyxTwBw8eJBbb72VkiVLMmfOHEqUKOF2SQHHQsXkKw0aNODHH3+kd+/etGrVigEDBthZYuaySE1NJT4+niZNmvDhhx/ad1EukoWKyXdCQkIYNGgQycnJXHnllcTGxvKPf/yDvXv3ul2aCVIbN26kefPm9O3bl9dee81ukX0J7F/O5FsRERG8+OKLbNiwARGhdu3aPPnkkxw6dMjt0kyQUFU++ugjrr/+el544QUeffRRt0sKeBYqJt+rWLEio0eP5ueff2b//v3UqFGDl156iWPHjrldmglghw8f5u9//zsvvfQS3377Lb169XK7pKBgoWICRuXKlRk/fjxLlixh7dq1VKtWjTFjxnDy5Em3SzMB5ocffqB+/fqULVuWpKQkYmNj3S4paFiomIBTo0YNPvnkE+bNm0dCQgLXXHMNzz77rI25mPNKS0vjiSeeoFu3brz99tuMHTuWYsWKuV1WULFQMQGrfv36fPHFF3z//ffs3buXWrVq0bdvX9asWeN2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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# We can now plot contours obtained with this \n", + "plot_contours(F, params, fill=False);\n", + "xlabel('Omega_m')\n", + "ylabel('sigma8')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dEXC2lIlE5IN" + }, + "source": [ + "And just to reinforce this point and demonstrate further audodiff magic, let's try to derive the same matrix differently, using the usual formula for constant\n", + "covariance:\n", + "\n", + "$$ F_{\\alpha, \\beta} = \\sum_{i,j} \\frac{d \\mu_i}{d \\theta_\\alpha} C^{-1}_{i,j} \\frac{d \\mu_j}{d \\theta_\\beta} $$\n", + "\n", + "What we need in this expression, is the covariance matrix, which we already have\n", + "and the Jacobian of the mean with respect to parameters. Normally you would need to use finite differencing, but luckily we can get that easily with JAX:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "WKn4COsdlKfs" + }, + "outputs": [], + "source": [ + "# We define a parameter dependent function that computes the mean\n", + "def mean_fn(p):\n", + " cosmo = jc.Planck15(Omega_c=p[0], sigma8=p[1])\n", + " # Compute signal vector\n", + " m = jc.angular_cl.angular_cl(cosmo, ell, probes)\n", + " return m.flatten() # We want it in 1d to operate against the covariance matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Be381gp6Gjqx" + }, + "outputs": [], + "source": [ + "# We compute it's jacobian with JAX, and we JIT it for efficiency\n", + "jac_mean = jax.jit(jax.jacfwd(mean_fn))" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 139 }, - { - "cell_type": "code", - "metadata": { - "id": "F3UMqqdLHQX7", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# Now we can compose the Fisher matrix:\n", - "F_2 = np.einsum('ia, ij, jb', dmu, np.linalg.inv(cov), dmu)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "t3kVMfEaGyuJ", + "outputId": "339ec1c1-4f47-43e9-f692-9c9070f5f0a2" + }, + "outputs": [], + "source": [ + "# We can now evaluate the jacobian at the fiducial cosmology\n", + "dmu = jac_mean(params)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "H6uzzV-jHnNe", + "outputId": "ed61a0df-5f6f-485b-ebbc-33ddaaa15c20" + }, + "outputs": [ { - "cell_type": "code", - "metadata": { - "id": "zUv4GmcVH1z8", - "colab_type": "code", - "outputId": "4b7fb3e2-3271-4492-f781-45c205c2e57c", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 282 - } - }, - "source": [ - "# We can now plot contours obtained with this \n", - "plot_contours(F, params, fill=False,color='black',lw=4);\n", - "plot_contours(F_2, params, fill=False, color='red', lw=4, linestyle='dashed');\n", - "xlabel('Omega_m')\n", - "ylabel('sigma8');" - ], - "execution_count": 32, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [], - "needs_background": "light" - } - } + "data": { + "text/plain": [ + "(500, 2)" ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dmu.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, + "colab_type": "code", + "id": "X9ZDB3RtHFnG", + "outputId": "07f53328-fb3a-4ead-bdaf-d6528136a8aa" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "51gfhl9cIzMC", - "colab_type": "text" - }, - "source": [ - "The red dashed is our second derivation of the Fisher matrix using the jacobian, the black contour underneath is our first derivation simply taking the Hessian of the likelihood.\n", - "\n", - "They agree perfectly, and they should, because they are both analytically computed." - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "56.5 ms ± 601 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" + ] + } + ], + "source": [ + "# For fun, we can alsi time it\n", + "%timeit jac_mean(params).block_until_ready()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ej3RdeaeHWy6" + }, + "source": [ + "Getting these gradients is the same order of time than evaluating the forward function!" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "F3UMqqdLHQX7" + }, + "outputs": [], + "source": [ + "# Now we can compose the Fisher matrix:\n", + "F_2 = jc.sparse.dot(dmu.T, jc.sparse.inv(cov), dmu)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 282 }, + "colab_type": "code", + "id": "zUv4GmcVH1z8", + "outputId": "4b7fb3e2-3271-4492-f781-45c205c2e57c" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "JrpDmbNfJUJ4", - "colab_type": "text" - }, - "source": [ - "## Conclusions and going further\n", - "\n", - "We have covered some of the most important points of `jax-cosmo`, feel free to \n", - "go through the [design document](https://github.com/DifferentiableUniverseInitiative/jax_cosmo/blob/master/design.md) for background and further explanations of how things work. You can also follow this [JAX document](https://jax.readthedocs.io/en/latest/notebooks/Common_Gotchas_in_JAX.html) to go deeper into JAX.\n", - "\n", - "\n", - "`jax-cosmo` is still very young and lacks many features, but hopefuly this notebook demonstrates the power of automatic differentiation, and given that the entire code is in simple Python, feel free to contribute missing features that would be necessary for your work ;-) " + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZUAAAEICAYAAACXo2mmAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy86wFpkAAAACXBIWXMAAAsTAAALEwEAmpwYAAA8u0lEQVR4nO3dd3xUVfr48c+TDgQIJQEklEhHQEpApCvqD10VCyqsDdeyuq4VdV1Rv4r69bu21bUtqCyWVUDUFRV12RWkS0KvgdBCTyAQSCCkPb8/5oIzkwkEmOGmPO/X674y99wyzyFhnrn3nHuOqCrGGGNMMIS5HYAxxpiqw5KKMcaYoLGkYowxJmgsqRhjjAkaSyrGGGOCxpKKMcaYoIkI5clFZAjwBhAOvK+q/+e3vTnwIRDn7PO4qk4TkQbAFKAnMEFV/+jsXxuY7XWKROATVX1QREYCLwPbnW1vqer7x4uvYcOG2rJly9OqozHGVDeLFi3ao6rxgbaFLKmISDjwNnAxsA1IEZGpqrraa7cngcmq+q6IdASmAS2BfOApoJOzAKCqB4GuXu+xCPjS63yTjiag8mjZsiWpqaknWTNjjKneRGRLWdtCefurF5CuqhtVtQCYCAz120eBOs7rusAOAFXNU9U5eJJLQCLSFkjA98rFGGOMi0KZVJoCW73Wtzll3p4BbhKRbXiuUu47ifMPx3Nl4j0kwLUislxEpohIs0AHichdIpIqIqlZWVkn8XbGGGNOxO2G+hF42kwSgcuAj0WkvDENBz7zWv8GaKmqXYDpeNpqSlHVcaqarKrJ8fEBbwkaY4w5RaFMKtsB76uFRH5tRD/qdmAygKrOB2KAhic6sYicC0So6qKjZaq6V1WPOKvvAz1OPXRjjDGnIpRJJQVoIyJJIhKF58piqt8+GcBgABHpgCeplOee1Ah8r1IQkSZeq1cCa04xbmOMMacoZL2/VLVIRP4I/Iinu/B4VV0lImOAVFWdCowC3hORh/A02o882kYiIpvxNOJHichVwCVePceux3O7zNv9InIlUARkAyNDVTdjjDGBSXUe+j45OVmtS7ExxpwcEVmkqsmBtrndUG+MMaYKsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4ImpElFRIaISJqIpIvI4wG2NxeRGSKyRESWi8hlTnkDpzxXRN7y2r+2iCz1WvaIyOvOtmgRmeS81y8i0jKUdTPGGFNaRKhOLCLhwNvAxcA2IEVEpqrqaq/dngQmq+q7ItIRmAa0BPKBp4BOzgKAqh4Eunq9xyLgS2f1dmCfqrYWkeHAX4AbQlM7U5WpKjk5ORw4cIDCwkJ040ZKcnIozs/3LIcPU3LkCCVHjlDs/AyvUYPIevWIqleP6Pr1qREfT42GDal51llExcQgIm5Xy5gzImRJBegFpKvqRgARmQgMBbyTigJ1nNd1gR0AqpoHzBGR1mWdXETaAgnAbKdoKPCM83oK8JaIiKpqUGpjKr3D2dlsnTGDA2vXkr91K0Xbt8Pu3URkZ5MBvFq3LpmZmWRmZlJQUHDsuHnA+af4ns2AXRER1KpVi1q1ahEbG0ujRo24KCqKAQcOIImJRLVqRe127Whw7rnEd+5MRExMEGprjDtCmVSaAlu91rcB5/nt8wzwbxG5D6gFXHQS5x8OTPJKGsfeT1WLRCQHaADs8T5IRO4C7gJo3rz5SbydqSz2b9rE1n//m/0LFlC8ciU1MzJolJ1Ns6Ii2pZxzGJnCaTwNGLJBYqKisjJySEnJweAdevW0R8YBJCS4rN/MbAzLIzsGjU4WKcO+YmJhHfqRL0+fWh2ySXUtb9ZU8GFMqmUxwhggqq+KiLnAx+LSCdVLSnHscOBm0/2DVV1HDAOIDk52a5iKrnt27cze/Zs5s6dS/qyZXw4dy4JJSXEneR5Eo6z7XSSSl4Z5U3LKA8HmpSU0CQvD/LyYOdOT+L5xz8A2BUWxpZ69TjcogUR3bvT8MILaXnppcTExZ1GlMYETyiTynY8V/9HJTpl3m4HhgCo6nwRiQEaApnHO7GInAtEqOqiAO+3TUQi8NxO23taNTAVipaUsHn6dJbMn8/XmzYxe/ZsNm3a5LNPeb6NBOKfVGrWrEm9evWIjo5m7969rM/PpygsjOKwMIrDwykOC6MkPJyS8HA0LIywoiKiCgqIKiwkpqiImOJiIlXR8HAoKir1fomnGGfjkhLi9+4lfO9eWLwY3n+fIiA9OppdiYlo3740veEGkoYMQcKsc6c580KZVFKANiKShOcDfzjwW799MoDBwAQR6QDEAFnlOPcI4DO/sqnArcB8YBjwk7WnVH4bv/+ere+9R9TChbTauZOkkhJWAx+Vsf8aoPFxzrc1IoI9sbEcrlOHwnr10Ph4ws86i6jEROZfdhmNzjqLhIQEatWqFZT4C4GCggJyc3PJy8sjJyeHnTt3It9+y8xVqwjbsYOYvXupc/AgDfLziS/Hn2y433oE0PrIEVpv2AAbNsBHH7FXhPRGjVjz29/S/rrr6N69O1FRUUGpkzHHI6H83HW6CL+O5//BeFV9QUTGAKmqOtXp8fUeEIun0f4xVf23c+xmPI34UcB+4JKjPcdEZCNwmaqu9XqvGOBjoBuQDQw/2kmgLMnJyZqamhq8CpvTpiUlbPjmG7a9/jpN58+nzZEjpfbZj6exLNBVydvAHcCW6Gj2NGzIkaQkIrt0oUG/fjS/+GJqNmwY0vhP15EDB8hcupTs5cvJXbWKwhUriNm0ifg9e2heUEDkSZ6vHbAOiImJ4bzzzqNfv37HltjY2BDUwFQHIrJIVZMDbqvOX+YtqVQMWlLCus8/Z+ebb9I8JYWzvXpeleVcYDkQGRlJz5496d+/P71796ZT8+a07NixSvagKjx0iK0//cTuGTM4snAhNdLTaZqVRWJxccD9M4FGZZzr7vBwbo+LI+/CC2n1wAMk9u0bsrhN1WNJpQyWVNyV/vXXbHvxRZIWLaJFgHaHQHKBtPr1WXLddbQePpzzzjuPGjVqhDbQCi4nI4ONU6aQM20asUuX0nbvXurgeYDr2jKO+R6nMdOxLiaGHd270+DWW+k4ciThdqvMHIcllTJYUjnzCgoK+PLLL3n33XcZM2sWA0+wfxGwrH59DvbrR8K119L2+uur5FVIMBUXFJD+1VcsWbKEf23ezOzZs9mxY8ex7bF4+tlHl3F8lghrW7Ui4qqr6PTQQ9Q+66wzEbapRCyplMGSypmza9cu3n77bcaNG0dmpqdz3/XApAD7FgDL4uPJ/81v6Pj44zRo1+5MhlrlqCpbtmxhzpw5zJkzh8Kvv+aDXbvKdewRYHHTpoTfdhvd/vxnImvWDG2wplKwpFIGSyqhlz51Ku9MmsTbU6b4PKUOEImn+19jPB9eSxs1ovDKK+n05z8Tl5TkQrTVg5aUsHHaNLa+8w715s6l04EDpXqUBZIlwqKePUl49126detmQ89UY8dLKm4//GiqqHVffMH+P/yBXpmZNMJz9eGvEJjQrh39L7iAzqNHc17iqT69YU6GhIXR6vLLaXX55QBkr1/PmtdeQ777jnO2bqVuGcfFq7J54UIu7dGDc845h1tvvZUbb7yRs+z2mPFiVyp2pRJUu5cuZd0NN9Bn3bpj337343kqNddZr127Nrfeeiv33HMPHTt2dCVOE1jhoUOsfPddcj79lLOXL6e5XweKPngeBDsqLCyMiy66iFt/+1uuvuIKatSvf0bjNe6w219lsKQSPHmZmaTccAM9Z84k0GODDwDfJCXx4IMPctttt1G7du0zHaI5SVpSwsr33mPfG29w7po1ZAFtytj3GmCsCCv79qXj22+T0KXLGYzUnGnHSyo2joM5LcUFBcweOZKDTZowqIyEsrJWLYY98QTr16/n/vvvt4RSSUhYGJ1//3sGrF5N9L59pL/2Gpdffjnh4aVbYG4BGqoyaM4c4s49lzmtWpE2KVA3DFPV2ZWKXamcsmVvvUWNRx+lbX5+wO0bo6LIeuQRej33nI1DVYVkZmby2Wef8eGHH7JkyRIa4pmzItDT/kvi4ij4wx/o+eyzhEVYE25VYbe/ymBJ5dQU5OYy7+KLGbBgQcBL3T0irLr+evqMH29dUKu4FStWMOuFF7hx8mTijvNZsjkyki1Dh9LjzTeJbXy80dlMZWC3v0zQbPz+e9IbNWJQgIRyGJh5/vlEbt7MwIkTLaFUA507d+beiROJ2LGDn4cNY3Nk4NHJWhYWMnDKFIrOOosZF1zAge3+A5abqsKSiikXVWXs2LFsuPxyOh46VGr7nKQksufNY9C8eTaRVDUU27gxAz//nOaHDvHLE0+wpIz5XeJUuWDmTAqaNWPmFVdwODv7zAZqQs6SijmhrKwsrrrqKu6++27uLCkhx2vbzrAwFr/0Ev02bqTp+ac66a6pKsIiIjjvhRfotm8faRMnMufsswM+o9RQlUHffssTrVrx97//vdSDsabysqRijmvBggV07tyZqVOnArAFuNfZNr9pU2LWraP7o4+6Fp+puNrdcAP9Nmxg35IlzOzXj2y/J/DTgbf27+eee+6hQ4cOfPzxxxSXMeKyqTwsqZgyfffdd1x44YXs3r3bp/zrWrX44bHH6J2RQb1WrVyKzlQWjbp2ZdDs2URkZDBz0CAOOOVP4xkwFGDjxo3ccsstdOnShS+//JLq3IGosrOkYgL6+e67uefKKzl8+LBPee/evVm6bBlD/vIX6yZsTkqdxEQGzZhB4dq1fDdgAP+KLj1O8urVq7n52muZ3aCBPedSSdmngvGhJSXMHDKEgWPH8l1JCXFe2x5//HFmz55NK7s6MaehQbt2/Obnn0nfuJF77rmHCL/nV+4HBuzbR+vhw/m5e3cOeg3bbyo+SyrmmJKiImZ1786gH38EoDMwFagpwrvvvsuLL75Y6gPAmFN11lln8c4775CWlsbNN9+MiFAXeMzZHg4MXLKE3GbNmD9qFFoSaAJpU9FYUjEAFOXnM791awYuW+ZT3guY9txz3H333e4EZqq8s88+m48++ogVK1bwTNeu1PPb3qSkhPNfe43URo3Y8tNPrsRoys+SigFgbq9e9N2yxacsB1jz+usMHD3anaBMtXLOOefw4JIlLHzySbYFGF+s5549JAwezMzBgzly4ECAM5iKwJKK4edhwxi4YoVP2e6wMHZNmkTXBx5wKSpTXfV67jnq7djBzN69KfTbVgMY9NNP7GjYkGVvvulGeOYELKlUcynPPku/L77wKdseHk7BjBm0u/56l6Iy1V2thAQGzZ/Plq+/Zmnd0tOGJRUW0un++5nZvz+Ffj0UjbssqVRj66ZMof0zz/hMJXsQyJs4kWYDBrgVljHHtL7ySs7NzmbOnXeS5ffwZDgwaM4c1iYksHXWLHcCNKVYUqmmMpcvp9bw4XjPbFIMrP2f/6HtsGFuhWVMKRIWRr9x44hYv55ZHTqU2t45N5cVgwfz6aefuhCd8RfSpCIiQ0QkTUTSReTxANubi8gMEVkiIstF5DKnvIFTnisib/kdEyUi40RknYisFZFrnfKRIpIlIkud5Y5Q1q0yKzx0iMw+fWjqNyTGnGuvpeczz7gTlDEnUK9VKwasXs2iF19kt9eDt9nA74uKuPHGG7nllls4ePCge0Ga0CUVEQkH3gYuBToCI0TEf0LyJ4HJqtoNGA6845TnA08BjwQ49WggU1XbOuf92WvbJFXt6izvB682Vcvc666jU16eT9nPnTszcMoUlyIypvx6PP444StXsjAhAYA7gW3Oto8//phu3bqxcOFC1+Kr7kJ5pdILSFfVjapaAEwEhvrto0Ad53VdPBPIoap5qjoHT3Lx9zvgRWe/ElXdE4rgq6otqan0mjbNpyylYUP62n9CU4k07NCBnjt38q/77uM7v+FeNmzYQN++fXnllVdsDDEXhDKpNAW2eq1vc8q8PQPcJCLbgGnAfcc7oYjEOS+fE5HFIvK5iDTy2uVa5zbaFBFpVsY57hKRVBFJzcrKOonqVH6qyu+ffJJrgE1O2T4RWs6cSURMjJuhGXPSJCyMq/72N1JSUjjnnHN8thUVFfGPRx9lRrt25O/f706A1ZTbDfUjgAmqmghcBnwsIseLKQJIBOapandgPvCKs+0boKWqdgGmAx8GOoGqjlPVZFVNjo+PD1Y9KoXPPvuMH3/8kR+BTsDLwIqRI4n3+w9pTGXSuXNnUlJSuPfee4+VNcAzxNCF69eTnpjI7qVL3Qqv2gllUtkOeF8tJDpl3m4HJgOo6nwgBmh4nHPuBQ4BXzrrnwPdneP3quoRp/x9oMfpBF/VZGdn8+CDDx5bPwRMGzSI/h984FpMxgRLjRo1eOutt5g6dSoJcXFMAY4Oe9opL4+SHj1Y889/uhlitRHKpJICtBGRJBGJwtMQP9VvnwxgMICIdMCTVMq8J6WeG6TfAIOcosHAauf4Jl67XgmsOf0qVB1/+tOf8L7dFx0dzdixYxG/vv/GVGZXXHEF87/5huZRUT7lTUpKaHbTTaQ8+6xLkVUjqhqyBc8trXXABmC0UzYGuNJ53RGYCywDlgKXeB27GU9vwVw87TEdnfIWwCxgOfBfoLlT/iKwyjnXDKD9ieLr0aOHVgdbtmzR8PBwxdMxQgEdM2aM22EZEzL7N2/WlAYNVMFnKQT9+cYb3Q6v0gNStYzPVdFq3DsiOTlZU1NT3Q4j5N4YOZJHP/zw2DhKbdu2ZcWKFUT5fZszpiopys9nbp8+DFyypNS2mb17M3DuXJto7hSJyCJVTQ60zf5Fq7i8zExu/egjtuB5KCgeeOSRRyyhmCovIiaGgYsX8/OwYRT7bRu0YAGzunWzOVpCwJJKFbfogQeIU6UJ8BywTISbRoxwOyxjzpiBn39O6hNPcMi/fPlyZvXoYYklyCypVGElRUU0/fJLn7K088+nRmysSxEZ447zXniBzRMmlBqUcuDSpczq2dMSSxBZUqnCFv/f/9GqoODYeiHQ7vXXXYvHGDd1vPVW9n/xBXv8E8vixfx83nmWWILEkkoVVvz22z7rC1u0oEnPni5FY4z72lx9NdmTJ7PXL7G0SE1lzEMP2bAuQWBJpYoqys+nw65dPmV1nnjCpWiMqTjaDhvGnokTyXYSywZgIPDM3/7G6NGjLbGcJksqVdTaTz45NlInQJYIne6w2QCMAWh3/fVk/vOfLAoPZxC/DlL44osv8vzzz7sYWeVnSaWK2jNpks/6+sRE65NvjJf2I0bAggXkxsX5lD/99NN8/vnn7gRVBdinTBVVZ9Ein/WiQYPcCcSYCqxHcjLTp0+nbt26PuW33norSwI8NGlOzJJKFXQ4O5uO+/b5lLUYOdKdYIyp4JKTk/n222+JjIw8VhZz+DDp/fqRtWqVi5FVTpZUqqC1//gH3rOjbAsPp7ldqRhTpn79+vHOO56JZzsAC4HrDh1iR58+FOTmuhpbZWNJpQrK8ZvZcdPZZ1t7ijEncMcdd/DiiBEsAFo7ZeceOMCC5GR7huUk2CdNFRSekeGzXtK9u0uRGFO5PDJ+POn16vmUDUhLY9YNN7gUUeVjSaUKit2zx2e9Zvv2LkViTOUSERNDy19+YUtEhE953ylTWDF2rEtRVS6WVKqgt2JieAx4G/gWiOvVy+WIjKk86rdpQ+GUKRz0KosAYu+7j8PZ2W6FVWlYUqmCJh44wMvAH4ErgIS+fV2OyJjKpfXQoaz2G4EiqbCQX4YMcSmiysOSShVz5MgRDh36dZDv8PBw6tSpc5wjjDGBnPfCC8zyu3XcPyWFlR984FJElYMllSpmn9/zKfXq1bN56I05RV2nT2d7ePix9XAg5t57OXLggHtBVXCWVKqYnJwcn3X/J4WNMeVXJzGRnc8+61PW+sgR5l96qUsRVXzHTSoicraIjBeR50UkVkTeE5GVIvK5iLQ8QzGak1CjRg2f9fz8fJciMaZqSB49mtlt2viU9Zs3jzWffOJSRBXbia5UJgApQC6wAFgLXAr8AIwPaWTmlBy9MokFmgJNrLeKMaet848/ssvrAeIIQO66i5KiIveCqqBOlFRqq+q7qvp/QB1VfVVVt6rqB0C9ExxrXFC7Vi2OAAeBbUDK4cMU2dWKMaclLimJDL/eYImHD/Nvm0m1lBMllRIRaSsiPYGaIpIMICKt8bRZmQomLCKCw35lB7ZuDbivMab8ej33HPOaNSMP+AvQEnh4/HiKi4vdDayCOVFSeQz4BvgIuAr4s4ikA/OAp0IbmjlVB8N98/1BSyrGBEWjTz6htQiPA3uBNWvWMMlv7qLq7rhJRVX/q6rtVLWDqs5R1WuB3kATVf36RCcXkSEikiYi6SLyeIDtzUVkhogsEZHlInKZU97AKc8Vkbf8jokSkXEisk5E1orItU55tIhMct7rl+rckSDPawhvgEM7drgUiTFVS6sBA7jk5pt9yp599lmKrG3lmHJ3KRaRTiJyPXAZcKOI3HKC/cPxjBRyKdARGCEiHf12exKYrKrdgOHAO055Pp4roUcCnHo0kKmqbZ3z/uyU3w7sU9XWwF/xXKFWS4ejo33WczdudCkSY6qep556inCvuwHr1q3j008/dTGiiqVcSUVE/gd401kuAF4CrjzBYb2AdFXdqKoFwERgqN8+CsemUq8L7ABQ1TxVnYMnufj7HfCis1+Jqh4dPXEo8KHzegowWKrpU38HExJ81vNmz3YpEmOqntatW3Prrbf6lI0ZM4bCwkKXIqpYynulMgwYDOxS1duAc/EkgeNpCnjfzN/mlHl7BrhJRLYB04D7jndCEYlzXj4nIoud52Ua+b+fqhYBOUCDE8RYJanfAJI1Vq50KRJjqqYnn3ySCGckYwG6bNjAz48+6m5QFUR5k8phVS0BikSkDpAJNAvC+48AJqhqIp7bah+LyPFiigASgXmq2h2YD7xyMm8oIneJSKqIpGZlZZ1q3BVaoyuu8FlPysy0SYaMCaKkpCR+d9ttXAEsAr4EEsbbo3tQ/qSS6lwlvIfn33Axng/049mOb+JJdMq83Q5MBlDV+UAM0PA459wLHMLzOwT4HDg6A9Wx9xORCDxXUnv9T6Cq41Q1WVWT4+PjT1CFyqnV0KEc8lpPKClhZ0qKa/EYUxU9cdNNTAW6OetdDh4kY+ZMFyOqGMqVVFT1D6q6X1X/DlwM3OrcBjueFKCNiCSJSBSehvipfvtk4Lmthoh0wJNUyrx8UFXF08V5kFM0GFjtvJ4KHL3ROQz4ydm/2omIiWG938jEWyZPdikaY6qmFgMGsLx2bZ+yjc8/71I0FcfJ9P7qIiJX4rkyaC0i1xxvf6dd44/Aj8AaPL28VonIGOc8AKOAO0VkGfAZMPJoIhCRzcBrwEgR2ebVc+xPwDMishy42TkHwAdAA+c5moeBUl2Yq5N9fmMVHbHGemOCbv/ll/usN589u9rfapbyfJkXkfFAF2AVcPRfTFX1dyGMLeSSk5M1NTXV7TBCYt4DD9Dnb387tr66Zk065uW5GJExVU/2+vXEtm1LlFfZ6g8/pOMtx33iotITkUWqmhxoW3mvVHo77RC3quptzlKpE0pVd/Ytt+D9fanjoUNs+uEH1+Ixpiqq36YNi5s08SnL+utfXYqmYihvUpkf4MFFU4E17tGDZXFxx9ZLgOVvv+1aPMZUVXrjjT7rHZctq9aDuJY3qXyEJ7GkOcOprHDaNEwFlnvVVazE0wjVAnh49Wqqad8FY0Km2+jReE+NF69K2mefuRaP28qbVD7A0yg+BLgCuNz5aSqwLq+9RnJUFC/hefJ048aNzJ9/op7gxpiTERMXx+qmvs9175k2zaVo3FfepJKlqlNVdZOqbjm6hDQyc9rq1qvHlUN9R8b5xGarMybojnTt6rMesWiRO4FUAOVNKktE5FMRGSEi1xxdQhqZCYqbbrrJZ33SpEkUFBS4FI0xVVO9//f/fNbP2rbNpUjcV96kUgM4AlyC57bX0VtgpoIbMmQI9evXP7aenZ3NZ9X4fq8xodDquuvwnqorqbCQ/Zs2uRaPm8r7RP1tARbrUlwJREVFMWLEiGPrkcDKRx+l8NChsg8yxpyU2MaNSY+J8SnbMHGiS9G4q7xD3/8twPKciPgPZW8qoFGjRhETHs7vgHXAy1lZLLj3XrfDMqZK2d2ihc/6gf/+16VI3FXe218xQFdgvbN0wTNA5O0i8npIIjNBk5SUxJedO/MBnnm1AVp+8gkFubkuRmVM1XLkwgsZB9wBdAb+XvdEs4NUTeVNKl2AC1T1TVV9E7gIaA9cjaedxVRwXd55B+/m+WZFRSy4+27X4jGmyrn6an6P5/mLlcCe/fvdjccl5U0q9YBYr/VaQH1VLcbTgG8quKbnn8/8Tp18ylpNnMiRAwdcisiYqiXOawQLgJycnMA7VnHlTSovAUtF5B8iMgFYArwsIrWA/4QqOBNcbceP95mfuWlxMQvuuMO1eIypSur63e7ab1cqZVPVD4A+wL+Ar4B+qvq+M5e8zaFZSTTp2ZNfzj3Xp6zzlClkLrcRd4w5Xf5XKpZUAhCR9s7P7kATPHPAbwUaO2Wmkmk/YQKHvdbrq7Lx0kur/RwQxpyuQFcq1XGsvYgTbH8YuAt41avM+1/pwqBHZEKqUdeuzLz0UgZ9//2xst47djBv1Cj6VPMhu405HdHR0XSOjqbmkSPEAXHFxeRlZxPboIHboZ1Rx71SUdW7nJfvAkNV9QJgBpADPBLi2EyI9PvyS1bVquVT1vaNN9izZo1LERlTNfynsJAFwA/AROBgNXyqvrwN9U+q6gER6Yfn6uR9PInGVEIRMTFEffyxTxfjhqqsGzLEtZiMqQpi/G4j16hmVylQ/qRydFib3wDvqep34DODpqlk2lx9NfMGD/Yp65ORwS9//rNLERlTuRUeOkQdr/USoE6zZm6F45ryJpXtIjIWuAGYJiLRJ3GsqaD6Tp3Kmho1fMpavPQSWVu3uhSRMZVXzhbf2UByRAiLOFGzddVT3sRwPfAj8P9UdT9QH7CuxJVcZM2ahE2YQKGzfhC4u6SEYTfdZMPjG3OSslet8lnfXw0TCpT/OZVDqvqlqq531neq6r9DG5o5E9pdfz1zBwxgI54Hkb4GZs2axX333Vctu0Mac6r2L1vms55du7ZLkbjLbmEZ+n7/PQ/368dKr7Jx48bx7rvWF8OY8jrs13vyUMOGLkXiLksqhsiaNXn/q69ISkryKX/ggQeYMWOGS1EZU8mkpfmsFicmuhSIuyypGAAaNmzI1KlTiY39ddzQoqIirh82jM0pKS5GZkzlkLhunc96zb59XYrEXSFNKiIyRETSRCRdRB4PsL25iMwQkSUislxELnPKGzjluSLylt8xM51zLnWWBKd8pIhkeZXbSIknqVOnTnzyySfH1msDH2RnU9i/Pwd37HAvMGMquJ2pqbTy6txSBLS/666yD6jCQpZURCQceBu4FOgIjBCRjn67PQlMVtVuwHDgHac8H3iKsp/av1FVuzpLplf5JK/y94NWmWpk6NChPPfcc7QC5gNXAm2OHCGtSxfyq+kAecacyMYPPvBZXxMbSx27/RV0vYB0Vd2oqgV4Ri3wn35Y4djzQnWBHQDO6MdzwGekdnOGjB49mrFJSZzjVZa8dy8r2ra1+VeMCaDkP74zgOz1Gw28OgllUmmKZ0Tjo7Y5Zd6eAW4SkW3ANOC+cp77H84trqdERLzKr3Vuo00RkYCPsorIXSKSKiKpWVlZ5Xy76kVE6L1gQakHI3tmZbGsTRtLLMZ40ZISkvzG+Kp7zTUuReM+txvqRwATVDURuAz4WEROFNONqtoZ6O8sNzvl3wAtVbULMB34MNDBqjpOVZNVNTk+Pj4olaiKaiUk0DAlhfToaJ/yXpmZLG3Xzua3N8axYd484oqLj60fBjr87nfuBeSyUCaV7YD31UKiU+btdmAygKrOB2KA43buVtXtzs+DwKd4brOhqntV9ejUxu8DPU4z/mov/pxzqJuayoYo32Heztu1i8Vt21J46JBLkRlTcbz71VfUA/oCTwPfNmtGjN+EXdVJKJNKCtBGRJJEJApPQ/xUv30ygMEAItIBT1Ip856UiESISEPndSRwOXie2RORJl67XgnYOO5BEN+pE7G//MKmyEif8t47d7KoTRtLLKZaO3jwIO+//z5FwDzgOSD32WddjspdIUsqqloE/BHPmGFr8PTyWiUiY0TkSme3UcCdIrIM+AwYqc7YICKyGXgNGCki25yeY9HAjyKyHFiK58rnPedc94vIKudc9wMjQ1W36qZR167UWLCgdGLZsYNUa7w31diECRM44PX3Hx8fz4gRI1yMyH1Sncd3Sk5O1tTUVLfDqDR2pqZypE8fWhYW+pQvrVuXlkuWEOf3RL4xVVlJSQnt2rUjPT39WNnTTz/Ns9XgSkVEFqlqcqBtbjfUm0qkSXIy0XPnssVv9NWuOTnsbd+erXPnuhSZMWfetGnTfBJKZGQk99xzj4sRVQyWVMxJadKzJ5Fz5pS6FaYFBQy5+mpSbEgXUw1oSQlbHnqI1l5lI0aMoHHjxq7FVFFYUjEn7azzzqPuqlUsq+N5bnUPnmETVmdlMWDAACZNmuRqfMaE2oI//Yl709NJw9P76EI8A7AaSyrmFNVv04Z2W7bwc4sWXAkcvQmQn5/P8OHDeeqppyjxm6/bmKogJyODpNdeAzwfoFcAr8TF0b17d1fjqigsqZhTFhMXR/+NG+n3aOlJQJ9//nmGDRvGgexsFyIzJnSWXXYZjb2+MBUAtSdMcC2eisaSijktYWFhvPTSS4wfP54ov4ckM7/6ir2NG7PKb7A9YyqrFWPHMsBv2uB5ffvSeqj/sIbVlyUVExS33XYbM2bMICEhAfCMDvpPIKmwkHZ33MHMSy6h2Oa9N5VYQW4uMX7tJpsiI+n97bcuRVQxWVIxQdOnTx9SUlLoeu65jAVaOOURwKDp01neqBE7rXeYqaTmXXopbY4c8Snb/+KL1XpIlkAsqZigat68OfNmz6Zx586ltnXbv5+Y885jwWOPuRCZMadu3v33M2jOHJ+y2a1b023UKJciqrgsqZigq1G7NgOXL2fhU0+xx2dmAqinSu+XX2ZWhw4c2rPHpQiNKb8V48bR/c03fcqyRDhn2jSXIqrYLKmYkOk1ZgzFixezuH79UtsGrF3LzqZNWTFunAuRGVM+W2fNovHddxPjVVYA7HjjDeq3aeNWWBWaJRUTUo26dqXr7t3M/M1v8G+mb1VQQOff/55ZHTqwb8MGV+Izpiw5GRkcueQS4v3GR1x4552ce1955xOsfiypmJALi4hg0LffsuHjj0sN7wKeq5biNm2Yc8cdqD0waSqAwoIC1vboQWu/hvmZffvSz66uj8uSijljOtx0E/EZGcxu27bUtoaq9PjgA67r3ZtVfs8BGHMm5eXlMfSqqxi9Zw/7vcrnNWvGgJkzXYqq8rCkYs6o2MaN6Z+WxpJXXmGj38OSLwBfpKTQtWtX/vSnP5GXl+dOkKba2rNnD4MHD+b777/nv8D5wEZgZa1adFu6lDC/EbpNaZZUjCu6jRpF06wsZl50EYeBNOBlZ1tRUREvvfQSHTt25KuvvqI6z/ljzpwtW7bQr18/fvnll2Nla4EbWrSg0YIF1AjQ4cSUZknFuCa6Th0GTZ9O1owZvNOnT6mG/IyMDK655hpGtW/PkldfdSVGUz2sWL6cPn36kJaW5lPetWtXvlmwgPhOnVyKrPKxpGJc13zQIF6fM4cvvviCxMREn21RwIPr1tHtkUdYXL8+K99/350gTZW17M03Ke7WjeIdO3zKL7zwQn7++WebI+UkWVIxFYKIcM0117B69WoefvhhwsPDAfg90NzZp/u+fXS6805+adKEdVOmuBarqRqK8vOZOXgw59x/P11LSvgBqONsu/7665k2bRp16tQ53ilMAJZUTIVSu3ZtXn31VZYuXco1v/kNTwTY57xdu2h93XXMbdmSzdOnn/EYTeW3ddYsVickMOinnzja9N4V+Bdw/z338NlnnxEdHe1afJWZJRVTIXXq1Ikvvv2WPe+/z+J69UptDwP6btlC4iWXMKt9ezb+8MOZD9JUOlpSwpy776buwIF0OXiw1Ha56CJef+stwsLso/FU2b+cqdA63X473bOzWfLKK6yIjS21PQIYkJbG2ZdeysJGjVj88sv2AKUJaP+mTcxv2ZJ+Y8fif1MrS4SFTz3FoOnTEUsop8X+9Uyl0G3UKDrl5LDwySdJi4kJuE+vzEy6P/YY62rVYubdd5Ofn3+GozQVkZaUsOCxx8hr04Y+W7eW2p4SHw/Ll9NrzBgXoqt6LKmYSkPCwuj13HO0OXiQeffdF3DIF4B2+fksHjuWFi1a8Oyzz5KZmXmGIzUVxcoPPmBFXBy9X36ZpsXFPtsOAz9fdx3Ju3ZZl+EgCmlSEZEhIpImIuki8niA7c1FZIaILBGR5SJymVPewCnPFZG3/I6Z6ZxzqbMkOOXRIjLJea9fRKRlKOtm3BMWEUGfv/2NxP37mffHP7KqVi2f7cXA34DMzEyeeeYZmjdvzu23386yZctcideceRnz5jGveXM63XFHwLaTtTVqsP3rrxk4ebLd7go2VQ3JAoQDG4Cz8TxusAzo6LfPOOAe53VHYLPzuhbQD7gbeMvvmJlAcoD3+wPwd+f1cGDSiWLs0aOHmsqvpLhYl73zjs5r2lSLQD8HpYzlz82b68wrr9TMFSvcDtuEQHZ2to4aNUrbR0bqEVD1W4pAZyQna35OjtuhVmpAqpbxuRrKFN0LSFfVjapaAEwEhvrto/zaNbwusANAVfNUdQ5wMjfFhwIfOq+nAINF/GaIMlWShIXR5Z57OH/bNnb8/DPrRo6kdu3aAff9bUYGA6dOpV7nzqQkJDDvoYfI37//zAZsgm7//v385S9/oXXr1rz66qusLSzkLb99FiYksOnrrxmUkkK0PX8SOmVlm9NdgGHA+17rN1P6qqMJsALYBuwDevhtHxngmJnOMUuBpwBxylcCiV77bQAaBojrLiAVSG3evHmwE7ipIHJycvSvf/2rJiUlHbtK6Rbgm6uC7gf9uX17Xfb221pSXOx26OYkZMyerQ/fd5/GxsaWuiqtB7oXdE2NGrr45ZfdDrVKwaUrlfIYAUxQ1UTgMuBjETlRTDeqamegv7PcfDJvqKrjVDVZVZPj4+NPKWhT8dWpU4cHH3yQ9evX88UXXzBkyBBuLWPfunjmdOly771kREczc+BA1vzzn9Y1uYLSkhKWvPYa8xMTadK/P7vefJPc3NxS+9VKTGT2iy/S9sABuj3yiAuRVk+hTCrbgWZe64lOmbfbgckAqjofiAEaHu+kqrrd+XkQ+BTPbTaf9xORCDyfFXtPqwam0gsPD+eaa67h+++/54ZFi5j5m9+w/jhPSrcoKmLQrFl0uOkmdkVGMqtDBxY+/TSH9uw5g1GbQHIyMvh52DA21qhBt1GjOH/7diKAUX77xcbG8sILL5CWlsbQxx+34erPsFAmlRSgjYgkiUgUnsbzqX77ZACDAUSkA56kklXWCUUkQkQaOq8jgcvx3PbCOffRL6PDgJ+cyzRjAGjcvTuDvv2W1ocOkTZxIjO7dyfrOM1uTUpKGLB2Lb2ee477zjqLK664grFjx7J9u/93IxMq+zdtYs6dd/JL48bEtGjBwC++oFWB73jW3YELgEaNGvH888+zefNmnnjiCWrWrOlKzNWdhPJz1+ki/DqenmDjVfUFERmD537cVBHpCLwHxOK5D/qYqv7bOXYznkb8KGA/cAmwBZgFRDrn/A/wsKoWi0gM8DHQDcgGhqvqxuPFl5ycrKmpqUGts6lcCg8dYulLL1E4fjzdt24l0GOVJUACvpe93bt35/LLL+fS7t3pOnAgMXFxZyTe6mBvWhqrX3yRmO++o+uePQR+GulX6dHRpN97Lxf87//aeF1niIgsUtXkgNuq85d5SyrGW86WLSx/9lkif/iBTjt3cnRQmLl4+rcH8iFwA5BWuzbZHTpQ85JLaH3LLdRv0+aMxFxVbNu2jZUvvkj8xImcm53NiW5YFQKpiYnUGDWKc++/3541OcMsqZTBkoopy5GcHFa+9Ra5kyYxbft2XsrODrjf0Qex/KVHR7MjKYmwgQNp8dvfktivn33wOVSVtLQ0Zs+efWzZvHkzD+C5rXE8WyIi2NS/P+1feYXG3bufgWhNIJZUymBJxZSHqrJy5Uq++eYbvvnmG3755RdUlbMo3fOkLDvDwtjasCGHWrcmKjmZxkOG0Pzii4moBo3IRfn5rJs8mcwvvyRm4UJez8tj0oEDpfZrhqeR1d/GqCgyevWiyX330XbYMEvOFYAllTJYUjGnIjMzk++++44tkydz+3/+Q7OiopM+x0JgYEwMHTp0KLW0TkoiqkaN4AceYkcOHGDL9OnsnTuXI0uXErVhAw0yM2men493bV4HHirjHPOB3sD66Gi2n38+iQ8+SKsrrrBEUsFYUimDJRUTDDtTUtj0yScUzphBo/R02h4+fMJule/heQo34PmAkvBwsmNiOBgXR0HDhmjTpkS2bEls+/bEnXMOjbp3P6OdA1SV3Nxcdu/eTWZmJrt37KDBV19Rsn49NTIyaJSdTbPCQsLLca5FgP+nUVRUFD179uT2Tp0YeOWVnH3ZZSGohQmW4yWVqn/tbUyINenZkyY9ex5bz8nIYP3HH5P7ww/UXbmSdvv349+5dXkZ52oINAYoLuasvDzIy4Pt2yHAYJh7RdgbHU1+ZCQFUVGsbNKEn7p1o1atWtSqVYvY2NhjrzutWEFEVBRaUEBJfj5aUOBZCguhoAD8foYfOEB0Tg6LIiP5v/BwMjMzS00lcBgC9pY7ka7AWbGxdO7bl/79+9O/f3969epFTBlTGpjKxZKKMUFWt3lzkkePhtGjASjIzSVt2jSyfvqJotRUam/aRDpAgMb/DifxPg1UaZCfD86H/fK9e/l45cqA+xbACbvmBrKbwO0cR7e1KOd5skTY0KQJR3r1IuHaa9ly3XVEWPffKsmSijEhFhUbS7vrr6fd9dcfK/se2L17N2vWrPFZOi5aBPv2ndL7lB6oxCOSU0so4Hk+pyyZlE4q28LD2RUXR16zZkjHjtTt3ZumF11EfIcO2KBI1YMlFWNc0qhRIxo1asSgQYN8yg9nZ5O5dCn7Vqwgb906CjdtQnbsIDori9icHBocPkxCSUmp9ou8Mt6n9CTM5eedVGJiYmjUqBEJCQkkJCSQvmcPeRERRHbuTP2+fWl20UUkNm5M4mm8n6n8rKHeGupNJVSUn0/WypXsT0ujYN8+CrKzya5Th1316pGbm0teXh55eXnk5uai2dlcO3MmUlyMhodTEhGBOgtHf0ZFQWTksSW8fn2imjWjxtlnU+vCC0lISCA2NhabTcKANdQbU+VExMTQJDmZJskB/18b4xrr/G2MMSZoLKkYY4wJGksqxhhjgsaSijHGmKCxpGKMMSZoLKkYY4wJGksqxhhjgsaSijHGmKCxpGKMMSZoLKkYY4wJGksqxhhjgsaSijHGmKCxpGKMMSZoLKkYY4wJmpAmFREZIiJpIpIuIo8H2N5cRGaIyBIRWS4ilznlDZzyXBF5q4xzTxWRlV7rz4jIdhFZ6iyXha5mxhhjAgnZfCoiEg68DVwMbANSRGSqqq722u1JYLKqvisiHYFpQEsgH3gK6OQs/ue+hsCzp/5VVV8JakWMMcaUWyivVHoB6aq6UVULgInAUL99FKjjvK4L7ABQ1TxVnYMnufgQkVjgYeD5UAVujDHm1IQyqTQFtnqtb3PKvD0D3CQi2/BcpdxXjvM+B7wKHAqw7Y/ObbTxIlIv0MEicpeIpIpIalZWVjnezhhjTHm53VA/ApigqonAZcDHIlJmTCLSFWilql8F2Pwu0AroCuzEk3hKUdVxqpqsqsnx8fGnGb4xxhhvoUwq24FmXuuJTpm324HJAKo6H4gBGh7nnOcDySKyGZgDtBWRmc7xu1W1WFVLgPfw3H4zxhhzBoUyqaQAbUQkSUSigOHAVL99MoDBACLSAU9SKfOelKq+q6pnqWpLoB+wTlUHOcc38dr1amBl6TMYY4wJpZD1/lLVIhH5I/AjEA6MV9VVIjIGSFXVqcAo4D0ReQhPo/1IVVUA52qkDhAlIlcBl/j1HPP3knN7TIHNwO9DUjFjjDFlEuczvFpKTk7W1NRUt8MwxphKRUQWqWpyoG1uN9QbY4ypQiypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCRpLKsYYY4LGkooxxpigsaRijDEmaCypGGOMCZpqPUyLiGQBW0L4Fg2BPSE8/5lSFephdag4qkI9qnsdWqhqwLlDqnVSCTURSS1rfJzKpCrUw+pQcVSFelgdyma3v4wxxgSNJRVjjDFBY0kltMa5HUCQVIV6WB0qjqpQD6tDGaxNxRhjTNDYlYoxxpigsaRijDEmaCypnAQRGSIiaSKSLiKPB9j+sIisFpHlIvJfEWnhta1YRJY6y1SvchGRF0RknYisEZH7K2EdZnuV7xCRf1XCOgwWkcVO+RwRaR3KOoSwHhc69VgpIh+KSEQFrkNzEfm383e/WkRaOuVJIvKLc85JIhJVCevwR+d8KiINQxl/iOvxT+ecK0VkvIhEnjAQVbWlHAsQDmwAzgaigGVAR799LgBqOq/vASZ5bcst47y3AR8BYc56QmWrg9/xXwC3VLY6AOuADs7rPwATKtvfE54viVuBts76GOD2ClyHmcDFzutYr/0mA8Od138H7qmEdegGtAQ2Aw1D+bcU4npcBoizfFae34VdqZRfLyBdVTeqagEwERjqvYOqzlDVQ87qAiCxHOe9BxijqiXOOTKDGLO/UNUBABGpA1wI/Cs44QYUqjooUMd5XRfYEaR4yxKKejQAClR1nbM+Hbg2iDH7O+U6iEhHIEJVpzv75arqIRERPH9DU5xjPgSuqkx1cF4vUdXNIYzbX6jqMU0dwELK8X/Jkkr5NcXzLfCobU5ZWW4HvvdajxGRVBFZICJXeZW3Am5wtn0vIm2CFnFpoarDUVcB/1XVA6cb6HGEqg53ANNEZBtwM/B/QYq3LKGoxx4gQkSOPiU9DGgWpHgDOZ06tAX2i8iXIrJERF4WkXA8iXG/qhaV85ynKxR1cENI6+Hc9roZ+OFEgYT0fmt1JSI3AcnAQK/iFqq6XUTOBn4SkRWqugGIBvJVNVlErgHGA/3PfNS+TrIOR40A3j+TcR7PSdbhIeAyVf1FRB4FXsOTaFx3MvUQkeHAX0UkGvg3UOxCyKUEqEMEnr/zbkAGMAkYCXztRnzlcRJ1+MCN+MrrFOvxDjBLVWef6Px2pVJ+2/H91pfolPkQkYuA0cCVqnrkaLmqbnd+bsRz/7Kbs2kb8KXz+iugS7AD9xKqOuA0RvYCvgtF4F6CXgcRiQfOVdVfnN0mAX1CEv2vQvK7UNX5qtpfVXsBs/C0FYXK6dRhG7DUuV1ThOeWaXdgLxDn1cEg4DmDKBR1cEPI6iEi/wPEAw+XK5KTbRCqrguebL4RSOLXhrBz/PbphqexrI1feT0g2nndEFiP04iG5zbL75zXg4CUylYHp+xu4MPK+HtwzrmHXxu4bwe+qGz1cNYTnJ/RwH+BCytoHcKd/eOd9X8A9zqvP8e3of4Pla0OXvts5sw01Ifqd3EHMA+oUe5YQl3ZqrTg6QmxzvnFjHbKxuDJ+gD/AXYDS51lqlPeB1jh/OJW4NUjB4jD8+1+BTAfzzfmSlUHZ/tMYEgl/j1c7bVtJnB2Ja3Hy8AaIA14sKLWwdl2MbDcqcMEIMopPxtPo3A6ngQTXQnrcD+eK4AiPJ0+3q+kv4si53xHj3n6RHHYMC3GGGOCxtpUjDHGBI0lFWOMMUFjScUYY0zQWFIxxhgTNJZUjDHGBI0lFWOMMUFjScWYUyQiiSLytYisF5ENIvJGqIdpN6ais6RizClwRtP9EviXqrbBMyhfLPCCq4EZ4zJLKsacmgvxDAT6DwBVLcYzKOXvROQPIvIvEZkuIpudCZsedkaAXSAi9QFEpJWI/CAii8Qz0Vl7r/IFIrJCRJ4XkVynPNaZXGmxs21oGbEhIi1FZK2ITBDPBHD/FJGLRGSuc2XVK+T/QqZasqRizKk5B1jkXaCeIf8z8IzD1Am4BuiJ5+rlkKp2wzMUzy3OIeOA+1S1B/AInpFgAd4A3lDVzniG+jgqH7haVbvjmXDpVeeKqSytgVeB9s7yW6Cf815PnEKdjTkhG/remNCYoaoHgYMikgN845SvALqISCyeMbw+98oL0c7P8/l1YqpPgVec1wL8r4gMAErwzJfRCNhVRgybVHUFgIiswjPXjYrICjyzEhoTdJZUjDk1q/FMgnWMM/NlczyD8B3x2lTitV6C5/9dGJ7JqLqexHveiGcI8h6qWigim4GY4+x/ohiMCTq7/WXMqfkvUFNEbgFwZsp7Fc8Ir4eOcxxw7FbZJhG5zjleRORcZ/MCfp0GeLjXYXWBTCehXAC0CEZFjAkmSyrGnAL1DO99NXCdiKzHM+R4PifXVnEjcLuILANW8euc4g8CD4vIcjztIjlO+T+BZOf21S3A2tOthzHBZkPfG1PBiEhN4LDT/jEcGKGqZfb0MqYisfuqxlQ8PYC3nJ5d+4HfuRuOMeVnVyrGVGIi0gBP+46/waq690zHY4wlFWOMMUFjDfXGGGOCxpKKMcaYoLGkYowxJmgsqRhjjAma/w/1nQTGYMe5pgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } - ] -} \ No newline at end of file + ], + "source": [ + "# We can now plot contours obtained with this \n", + "plot_contours(F, params, fill=False,color='black',lw=4);\n", + "plot_contours(F_2, params, fill=False, color='red', lw=4, linestyle='dashed');\n", + "xlabel('Omega_m')\n", + "ylabel('sigma8');" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "51gfhl9cIzMC" + }, + "source": [ + "The red dashed is our second derivation of the Fisher matrix using the jacobian, the black contour underneath is our first derivation simply taking the Hessian of the likelihood.\n", + "\n", + "They agree perfectly, and they should, because they are both analytically computed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JrpDmbNfJUJ4" + }, + "source": [ + "## Conclusions and going further\n", + "\n", + "We have covered some of the most important points of `jax-cosmo`, feel free to \n", + "go through the [design document](https://github.com/DifferentiableUniverseInitiative/jax_cosmo/blob/master/design.md) for background and further explanations of how things work. You can also follow this [JAX document](https://jax.readthedocs.io/en/latest/notebooks/Common_Gotchas_in_JAX.html) to go deeper into JAX.\n", + "\n", + "\n", + "`jax-cosmo` is still very young and lacks many features, but hopefuly this notebook demonstrates the power of automatic differentiation, and given that the entire code is in simple Python, feel free to contribute missing features that would be necessary for your work ;-) " + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "include_colab_link": true, + "name": "jax-cosmo-intro.ipynb", + "provenance": [], + "toc_visible": true + }, + "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.8.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/jax_cosmo/__init__.py b/jax_cosmo/__init__.py index ea39c8b..b12f3c7 100644 --- a/jax_cosmo/__init__.py +++ b/jax_cosmo/__init__.py @@ -22,3 +22,4 @@ import jax_cosmo.transfer as transfer from jax_cosmo.core import * from jax_cosmo.parameters import * +import jax_cosmo.sparse as sparse diff --git a/jax_cosmo/scipy/interpolate.py b/jax_cosmo/scipy/interpolate.py index bc67a78..a1cf860 100644 --- a/jax_cosmo/scipy/interpolate.py +++ b/jax_cosmo/scipy/interpolate.py @@ -28,7 +28,7 @@ def interp(x, xp, fp): ind = np.argmin((x - xp) ** 2) # Perform linear interpolation - ind = np.clip(ind, 1, len(xp) - 2) + ind = np.clip(ind.astype("float32"), 1, len(xp) - 2).astype("int32") xi = xp[ind] # Figure out if we are on the right or the left of nearest From ced8914d048147050be059317d3cbaeb6cfb0fb4 Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Sat, 25 Jul 2020 15:15:33 -0700 Subject: [PATCH 18/21] Re-implement sparse det in terms of slogdet --- jax_cosmo/sparse.py | 43 +++++++++++++++++++++++++++++++++++-------- tests/test_sparse.py | 14 +++++++++----- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/jax_cosmo/sparse.py b/jax_cosmo/sparse.py index 33bdba2..e69df7d 100644 --- a/jax_cosmo/sparse.py +++ b/jax_cosmo/sparse.py @@ -331,12 +331,15 @@ def _block_det(sparse, k, N, P): S = sparse[k + 1 : N, k + 1 : N, 0:P] v = sparse[k + 1 : N, k : k + 1, 0:P] Sinv_v = sparse_dot_sparse(inv(S), v) - return np.product(sparse[k, k] - sparse_dot_sparse(u, Sinv_v)) + M = sparse[k, k] - sparse_dot_sparse(u, Sinv_v) + sign = np.product(np.sign(M)) + logdet = np.sum(np.log(np.abs(M))) + return sign, logdet @jit -def det(sparse): - """Calculate the determinant of a sparse matrix. +def slogdet(sparse): + """Calculate the log(determinant) of a sparse matrix. Based on equation (2.2) of https://arxiv.org/abs/1112.4379 @@ -347,15 +350,39 @@ def det(sparse): Returns ------- - float - Determinant result. + tuple + Tuple (sign, logdet) such that sign * exp(logdet) is the + determinant. If the determinant is zero, logdet = -inf. """ sparse = check_sparse(sparse, square=True) N, _, P = sparse.shape - result = np.product(sparse[-1, -1]) + sign = np.product(np.sign(sparse[-1, -1])) + logdet = np.sum(np.log(np.abs(sparse[-1, -1]))) # The individual blocks can be calculated in any order so there # should be a better way to express this using lax.map but I # can't get it to work without "concretization" errors. for i in range(N - 1): - result *= _block_det(sparse, i, N, P) - return result + s, ld = _block_det(sparse, i, N, P) + sign *= s + logdet += ld + return sign, logdet + + +@jit +def det(sparse): + """Calculate the determinant of a sparse matrix. + + Uses :func:`slogdet`. + + Parameters + ---------- + sparse : array + 3D array of shape (ny, nx, ndiag) of block diagonal elements. + + Returns + ------- + float + Determinant result. + """ + sign, logdet = slogdet(sparse) + return sign * np.exp(logdet) diff --git a/tests/test_sparse.py b/tests/test_sparse.py index 848ba85..3fe213d 100644 --- a/tests/test_sparse.py +++ b/tests/test_sparse.py @@ -73,9 +73,13 @@ def test_inv(): def test_det(): - X = [ - [[1, 2, 3], [4, 5, 6], [-1, 7, -2]], - [[1, 2, 3], [-4, -5, -6], [2, -3, 9]], - [[7, 8, 9], [5, -4, 6], [-3, -2, -1]], - ] + X = np.array( + [ + [[1, 2, 3], [4, 5, 6], [-1, 7, -2]], + [[1, 2, 3], [-4, -5, -6], [2, -3, 9]], + [[7, 8, 9], [5, -4, 6], [-3, -2, -1]], + ] + ) + assert_array_equal(-det(-X), det(X)) + assert_array_equal(det(0.0 * X), 0.0) assert_allclose(det(X), np.linalg.det(to_dense(X)), rtol=1e-6) From f58da80ba4bc4a9ff886f183dc2511e9128ffb6d Mon Sep 17 00:00:00 2001 From: David Kirkby Date: Sat, 25 Jul 2020 17:24:13 -0700 Subject: [PATCH 19/21] Fix import order --- jax_cosmo/likelihood.py | 32 ++++++++++++++++++++++---------- tests/test_likelihood.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 tests/test_likelihood.py diff --git a/jax_cosmo/likelihood.py b/jax_cosmo/likelihood.py index 5ad295a..d96a176 100644 --- a/jax_cosmo/likelihood.py +++ b/jax_cosmo/likelihood.py @@ -6,27 +6,39 @@ import jax.numpy as np import jax.scipy as sp +import jax_cosmo.sparse as sparse from jax_cosmo.angular_cl import gaussian_cl_covariance def gaussian_log_likelihood(data, mu, C, constant_cov=True, inverse_method="inverse"): """ Computes the likelihood for some cl + + If the covariance C is sparse (according to :meth:`jax_cosmo.sparse.is_sparse`) + use sparse inverse and determinant algorithms (and ignore ``inverse_method``). """ # Computes residuals r = mu - data - # TODO: check what is the fastest and works the best between cholesky+solve - # and just inversion - if inverse_method == "inverse": - y = np.dot(np.linalg.inv(C), r) - elif inverse_method == "cholesky": - y = sp.linalg.cho_solve(sp.linalg.cho_factor(C, lower=True), r) + if sparse.is_sparse(C): + r = r.reshape(-1, 1) + rT_Cinv_r = sparse.dot(r.T, sparse.inv(C), r)[0, 0] else: - raise NotImplementedError + # TODO: check what is the fastest and works the best between cholesky+solve + # and just inversion + if inverse_method == "inverse": + y = np.dot(np.linalg.inv(C), r) + elif inverse_method == "cholesky": + y = sp.linalg.cho_solve(sp.linalg.cho_factor(C, lower=True), r) + else: + raise NotImplementedError + rT_Cinv_r = r.dot(y) if constant_cov: - return -0.5 * r.dot(y) + return -0.5 * rT_Cinv_r else: - _, logdet = np.linalg.slogdet(C) - return -0.5 * r.dot(y) - 0.5 * logdet + if sparse.is_sparse(C): + _, logdet = sparse.slogdet(C) + else: + _, logdet = np.linalg.slogdet(C) + return -0.5 * (rT_Cinv_r - logdet) diff --git a/tests/test_likelihood.py b/tests/test_likelihood.py new file mode 100644 index 0000000..9a950f8 --- /dev/null +++ b/tests/test_likelihood.py @@ -0,0 +1,33 @@ +import jax.numpy as jnp +from numpy.testing import assert_allclose +from numpy.testing import assert_array_equal + +from jax_cosmo import Planck15 +from jax_cosmo import probes +from jax_cosmo.angular_cl import gaussian_cl_covariance_and_mean +from jax_cosmo.bias import constant_linear_bias +from jax_cosmo.likelihood import gaussian_log_likelihood +from jax_cosmo.redshift import smail_nz +from jax_cosmo.sparse import to_dense + + +def test_gaussian_log_likelihood(): + n_ell = 5 + ell = jnp.logspace(1, 3, n_ell) + nz1 = smail_nz(1.0, 2.0, 1.0) + nz2 = smail_nz(1.0, 2.0, 0.5) + n_cls = 3 + P = [probes.NumberCounts([nz1, nz2], constant_linear_bias(1.0))] + cosmo = Planck15() + mu, cov_sparse = gaussian_cl_covariance_and_mean(cosmo, ell, P, sparse=True) + cov_dense = to_dense(cov_sparse) + data = 1.1 * mu + for constant_cov in (True, False): + loglike_sparse = gaussian_log_likelihood( + data, mu, cov_sparse, constant_cov=constant_cov + ) + for method in "inverse", "cholesky": + loglike_dense = gaussian_log_likelihood( + data, mu, cov_dense, constant_cov=constant_cov, inverse_method=method + ) + assert_allclose(loglike_sparse, loglike_dense, rtol=1e-6) From 95da132f85524f396f28f5f11ccc633ebcc83238 Mon Sep 17 00:00:00 2001 From: Francois Date: Sun, 26 Jul 2020 14:47:05 +0200 Subject: [PATCH 20/21] Adds a little bit more documentation to likelihood --- jax_cosmo/likelihood.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/jax_cosmo/likelihood.py b/jax_cosmo/likelihood.py index d96a176..27ebc4c 100644 --- a/jax_cosmo/likelihood.py +++ b/jax_cosmo/likelihood.py @@ -12,10 +12,31 @@ def gaussian_log_likelihood(data, mu, C, constant_cov=True, inverse_method="inverse"): """ - Computes the likelihood for some cl + Computes the log likelihood for a given data vector under a multivariate + Gaussian distribution. If the covariance C is sparse (according to :meth:`jax_cosmo.sparse.is_sparse`) use sparse inverse and determinant algorithms (and ignore ``inverse_method``). + + Parameters + ---------- + data: array_like + Data vector, with shape [N]. + + mu: array_like, 1d + Mean of the Gaussian likelihood, with shape [N]. + + C: array_like or sparse matrix + Covariance of Gaussian likelihood with shape [N,N] + + constant_cov: boolean + Whether to include the log determinant of the covariance matrix in the + likelihood. If `constant_cov` is true, the log determinant is ignored + (default: True) + + inverse_method: string + Methods for computing the precision matrix. Either "inverse", "cholesky". + Note that this option is ignored when the covariance is sparse. (default: "inverse") """ # Computes residuals r = mu - data From 5fc49b69539d84d914ffadad3a01f1981a955b3a Mon Sep 17 00:00:00 2001 From: Francois Date: Sun, 26 Jul 2020 14:48:05 +0200 Subject: [PATCH 21/21] removes previously introduced jax fix, now fixed upstream --- jax_cosmo/scipy/interpolate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jax_cosmo/scipy/interpolate.py b/jax_cosmo/scipy/interpolate.py index a1cf860..bc67a78 100644 --- a/jax_cosmo/scipy/interpolate.py +++ b/jax_cosmo/scipy/interpolate.py @@ -28,7 +28,7 @@ def interp(x, xp, fp): ind = np.argmin((x - xp) ** 2) # Perform linear interpolation - ind = np.clip(ind.astype("float32"), 1, len(xp) - 2).astype("int32") + ind = np.clip(ind, 1, len(xp) - 2) xi = xp[ind] # Figure out if we are on the right or the left of nearest