{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Adaptive PDE discretizations on Cartesian grids\n", "## Volume : Algorithmic tools\n", "## Part : Automatic differentiation\n", "## Chapter : Known bugs and incompatibilities" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The techniques of automatic differentiation technique play an essential role in the notebooks presented in this repository. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[**Summary**](Summary.ipynb) of volume Algorithmic tools, this series of notebooks.\n", "\n", "[**Main summary**](../Summary.ipynb) of the Adaptive Grid Discretizations \n", "\tbook of notebooks, including the other volumes.\n", "\n", "# Table of contents\n", " * [1 Matrix multiplication and inversion](#1-Matrix-multiplication-and-inversion)\n", " * [2. In place modifications and aliasing](#2.-In-place-modifications-and-aliasing)\n", " * [2.1 Aliasing of the AD information](#2.1-Aliasing-of-the-AD-information)\n", " * [2.2 Non writeable AD information](#2.2-Non-writeable-AD-information)\n", " * [3. CPU/GPU generic programming](#3.-CPU/GPU-generic-programming)\n", "\n", "\n", "\n", "**Acknowledgement.** Some of the experiments presented in these notebooks are part of \n", "ongoing research with Ludovic Métivier and Da Chen.\n", "\n", "Copyright Jean-Marie Mirebeau, Centre Borelli, ENS Paris-Saclay, CNRS, University Paris-Saclay" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Importing the required libraries" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.182340Z", "iopub.status.busy": "2024-04-30T08:46:36.182051Z", "iopub.status.idle": "2024-04-30T08:46:36.190784Z", "shell.execute_reply": "2024-04-30T08:46:36.190361Z" } }, "outputs": [], "source": [ "import sys; sys.path.insert(0,\"..\") # Allow importing agd from parent directory\n", "#from Miscellaneous import TocTools; TocTools.displayTOC('ADBugs','Algo')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.193434Z", "iopub.status.busy": "2024-04-30T08:46:36.193218Z", "iopub.status.idle": "2024-04-30T08:46:36.458728Z", "shell.execute_reply": "2024-04-30T08:46:36.458308Z" } }, "outputs": [], "source": [ "import numpy as np\n", "import scipy.sparse.linalg\n", "from matplotlib import pyplot as plt" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.460542Z", "iopub.status.busy": "2024-04-30T08:46:36.460390Z", "iopub.status.idle": "2024-04-30T08:46:36.464876Z", "shell.execute_reply": "2024-04-30T08:46:36.464573Z" } }, "outputs": [], "source": [ "import agd.AutomaticDifferentiation as ad" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.466315Z", "iopub.status.busy": "2024-04-30T08:46:36.466231Z", "iopub.status.idle": "2024-04-30T08:46:36.468015Z", "shell.execute_reply": "2024-04-30T08:46:36.467783Z" } }, "outputs": [], "source": [ "def reload_packages():\n", " from Miscellaneous.rreload import rreload\n", " global ad\n", " ad, = rreload([ad],rootdir='..',verbose=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 Matrix multiplication and inversion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Please use the `ad.apply_linear_mapping` and `ad.apply_linear_inverse` functions in combination with `np.dot`, or scipy solve functions." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.469414Z", "iopub.status.busy": "2024-04-30T08:46:36.469335Z", "iopub.status.idle": "2024-04-30T08:46:36.471577Z", "shell.execute_reply": "2024-04-30T08:46:36.471352Z" } }, "outputs": [], "source": [ "v = ad.Dense.denseAD( np.random.standard_normal((4,)),np.random.standard_normal((4,4)))\n", "m0 = np.random.standard_normal((4,4))\n", "m1 = scipy.sparse.coo_matrix( ([1.,2.,3.,4.,5.],([0,2,1,2,3],[0,1,2,2,3]))).tocsr()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.472888Z", "iopub.status.busy": "2024-04-30T08:46:36.472806Z", "iopub.status.idle": "2024-04-30T08:46:36.474387Z", "shell.execute_reply": "2024-04-30T08:46:36.474144Z" } }, "outputs": [], "source": [ "# Fails\n", "#print(\"np.dot looses AD:\",np.dot(m0,v))\n", "#print(\"scipy '*' looses AD:\",m1*v) " ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.475749Z", "iopub.status.busy": "2024-04-30T08:46:36.475668Z", "iopub.status.idle": "2024-04-30T08:46:36.478026Z", "shell.execute_reply": "2024-04-30T08:46:36.477752Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "np.dot with AD:\n", " denseAD([-3.04188743 -1.81059366 -4.60168428 -0.402255 ],\n", "[[-2.18883126 -1.92773839 3.22406318 3.43328336]\n", " [-1.16890347 -0.462756 1.82295919 2.66970312]\n", " [-2.75635892 -3.19868113 1.28256549 3.17149694]\n", " [-0.1224066 0.03960797 -0.308438 0.39619431]])\n", "scipy '*' with AD:\n", " denseAD([ 0.7291558 5.45572649 9.19406824 -2.45149384],\n", "[[ 0.60006067 0.66252011 0.93092156 -0.2395021 ]\n", " [ 2.49177526 5.05030758 -0.55925942 -0.93759285]\n", " [ 5.41915601 7.88410999 -3.17199694 -4.56263608]\n", " [-3.14881496 -5.70942489 -0.69430255 -1.26786441]])\n" ] } ], "source": [ "print(\"np.dot with AD:\\n\",ad.apply_linear_mapping(m0,v))\n", "print(\"scipy '*' with AD:\\n\",ad.apply_linear_mapping(m1,v))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.479385Z", "iopub.status.busy": "2024-04-30T08:46:36.479301Z", "iopub.status.idle": "2024-04-30T08:46:36.481425Z", "shell.execute_reply": "2024-04-30T08:46:36.481192Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "scipy solve with AD :\n", " denseAD([ 0.7291558 0.26936566 0.31996104 -0.09805975],\n", "[[ 0.60006067 0.66252011 0.93092156 -0.2395021 ]\n", " [-0.28363379 0.45826241 0.71556267 0.94790528]\n", " [ 0.34946483 0.19172776 -0.40438629 -0.55208538]\n", " [-0.1259526 -0.228377 -0.0277721 -0.05071458]])\n" ] } ], "source": [ "print(\"scipy solve with AD :\\n\",ad.apply_linear_inverse(scipy.sparse.linalg.spsolve,m1,v))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. In place modifications and aliasing\n", "\n", "The AD information often consists of very large arrays. In order to save time and memory, this information is not systematically copied and/or stored fully. It can take the form of a broadcasted array, or of an alias to another array. In that case a copy is necessary to enable modifications." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Aliasing of the AD information\n", "\n", "When an operation leaves the AD information untouched, an alias is used. This can lead to bugs if in place modifications are used afterward." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.482798Z", "iopub.status.busy": "2024-04-30T08:46:36.482717Z", "iopub.status.idle": "2024-04-30T08:46:36.484337Z", "shell.execute_reply": "2024-04-30T08:46:36.484107Z" } }, "outputs": [], "source": [ "x=ad.Dense.identity(constant=np.array([1.,2.]))\n", "y=x+1 # Only affects the value, not the AD information" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.485590Z", "iopub.status.busy": "2024-04-30T08:46:36.485512Z", "iopub.status.idle": "2024-04-30T08:46:36.487207Z", "shell.execute_reply": "2024-04-30T08:46:36.486981Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Values are distinct : False\n", "AD information is shared : True\n" ] } ], "source": [ "print(\"Values are distinct :\", x.value is y.value)\n", "print(\"AD information is shared :\", y.coef is x.coef)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A modification of the aliased variable will impact the original one." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.488573Z", "iopub.status.busy": "2024-04-30T08:46:36.488478Z", "iopub.status.idle": "2024-04-30T08:46:36.490467Z", "shell.execute_reply": "2024-04-30T08:46:36.490206Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "denseAD(1.0,[1. 0.])\n", "Caution ! Shared AD information is affected : denseAD(1.0,[2. 0.])\n" ] } ], "source": [ "print(x[0])\n", "y[0]*=2\n", "print(\"Caution ! Shared AD information is affected :\", x[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avoid this effect by making a copy." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.491861Z", "iopub.status.busy": "2024-04-30T08:46:36.491761Z", "iopub.status.idle": "2024-04-30T08:46:36.493678Z", "shell.execute_reply": "2024-04-30T08:46:36.493459Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "AD information is distinct : False\n" ] } ], "source": [ "x=ad.Dense.identity(constant=np.array([1.,2.]))\n", "y=(x+1).copy()\n", "print(\"AD information is distinct :\", y.coef is x.coef)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that a similar effect arises with the `-` binary operator, but not with `*`or `/`. That is because the latter modify the AD information, which therefore must be copied anyway." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.495201Z", "iopub.status.busy": "2024-04-30T08:46:36.495101Z", "iopub.status.idle": "2024-04-30T08:46:36.497174Z", "shell.execute_reply": "2024-04-30T08:46:36.496946Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "AD information is shared : True\n", "AD information is distinct : False\n", "AD information is distinct : False\n" ] } ], "source": [ "x=ad.Dense.identity(constant=np.array([1.,2.]))\n", "print(\"AD information is shared :\", (x-1).coef is x.coef)\n", "print(\"AD information is distinct :\", (x*2).coef is x.coef)\n", "print(\"AD information is distinct :\", (x/2).coef is x.coef)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 Non writeable AD information\n", "\n", "When creating an dense AD variable, the coefficients may be non writeable (e.g. broadcasted) arrays." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.498500Z", "iopub.status.busy": "2024-04-30T08:46:36.498402Z", "iopub.status.idle": "2024-04-30T08:46:36.500226Z", "shell.execute_reply": "2024-04-30T08:46:36.500019Z" } }, "outputs": [], "source": [ "x=ad.Dense.identity(constant=np.array([[1.,2.],[3.,4.]]),shape_bound=(2,))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.501541Z", "iopub.status.busy": "2024-04-30T08:46:36.501438Z", "iopub.status.idle": "2024-04-30T08:46:36.504376Z", "shell.execute_reply": "2024-04-30T08:46:36.504113Z" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.coef.flags.writeable" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.505750Z", "iopub.status.busy": "2024-04-30T08:46:36.505656Z", "iopub.status.idle": "2024-04-30T08:46:36.507180Z", "shell.execute_reply": "2024-04-30T08:46:36.506921Z" } }, "outputs": [], "source": [ "# x+=1 # Fails because non-writeable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Make a copy to solve the issue." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.508566Z", "iopub.status.busy": "2024-04-30T08:46:36.508483Z", "iopub.status.idle": "2024-04-30T08:46:36.510098Z", "shell.execute_reply": "2024-04-30T08:46:36.509877Z" } }, "outputs": [], "source": [ "y=x.copy()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.511379Z", "iopub.status.busy": "2024-04-30T08:46:36.511299Z", "iopub.status.idle": "2024-04-30T08:46:36.513249Z", "shell.execute_reply": "2024-04-30T08:46:36.513010Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.coef.flags.writeable" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.514568Z", "iopub.status.busy": "2024-04-30T08:46:36.514488Z", "iopub.status.idle": "2024-04-30T08:46:36.516018Z", "shell.execute_reply": "2024-04-30T08:46:36.515793Z" } }, "outputs": [], "source": [ "y+=1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. CPU/GPU generic programming\n", "\n", "The agd library allows CPU/GPU generic programming to some extent. Here are the guidelines to make this approach work.\n", "\n", "*Make a copy of the numpy array module*" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.517324Z", "iopub.status.busy": "2024-04-30T08:46:36.517245Z", "iopub.status.idle": "2024-04-30T08:46:36.518907Z", "shell.execute_reply": "2024-04-30T08:46:36.518597Z" } }, "outputs": [], "source": [ "xp = np " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Activate GPU acceleration.*\n", "If uncommented, the following line will replace the module xp with np, and modify its other arguments in a custom manner intended for easy interaction." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.520245Z", "iopub.status.busy": "2024-04-30T08:46:36.520168Z", "iopub.status.idle": "2024-04-30T08:46:36.521646Z", "shell.execute_reply": "2024-04-30T08:46:36.521422Z" }, "tags": [ "GPU_config" ] }, "outputs": [], "source": [ "#xp,plt = map(ad.cupy_friendly,[xp,plt])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Create basic arrays using `xp`.* Basic arrays are arrays of zeros, of ones, arange, linspace, etc" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.522941Z", "iopub.status.busy": "2024-04-30T08:46:36.522843Z", "iopub.status.idle": "2024-04-30T08:46:36.524465Z", "shell.execute_reply": "2024-04-30T08:46:36.524247Z" } }, "outputs": [], "source": [ "x = xp.linspace(0,2*np.pi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Use numpy's overloading mechanisms.* These mechanisms will dispatch the function calls to cupy, or to the AutomaticDifferentiation module of the agd library, depending on the data type (array from numpy, cupy, or ad)." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.525814Z", "iopub.status.busy": "2024-04-30T08:46:36.525730Z", "iopub.status.idle": "2024-04-30T08:46:36.527323Z", "shell.execute_reply": "2024-04-30T08:46:36.527105Z" } }, "outputs": [], "source": [ "y = np.sin(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Use `ad.asarray` and `ad.array`.* Stacking arrays using np.array will not work for AD or cupy arrays." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.528630Z", "iopub.status.busy": "2024-04-30T08:46:36.528556Z", "iopub.status.idle": "2024-04-30T08:46:36.530162Z", "shell.execute_reply": "2024-04-30T08:46:36.529945Z" } }, "outputs": [], "source": [ "xy = ad.array([x,y])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Use functions that accept both numpy and cupy arrays.* Or modify them for that purpose, as we did for the member functions of the pyplot module using the `ad.cupy_friendly` function." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.531509Z", "iopub.status.busy": "2024-04-30T08:46:36.531436Z", "iopub.status.idle": "2024-04-30T08:46:36.593579Z", "shell.execute_reply": "2024-04-30T08:46:36.593308Z" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.plot(*xy);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*If needed, convert cupy array's to numpy arrays.* Using the `get` member function of the `cupy.ndarray` class, or using `ad.cupy_generic.cupy_get` (which leaves non-cupy variables unchanged)." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2024-04-30T08:46:36.595141Z", "iopub.status.busy": "2024-04-30T08:46:36.595035Z", "iopub.status.idle": "2024-04-30T08:46:36.597328Z", "shell.execute_reply": "2024-04-30T08:46:36.597059Z" } }, "outputs": [ { "data": { "text/plain": [ "numpy.ndarray" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(ad.cupy_generic.cupy_get(y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "celltoolbar": "Format de la Cellule Texte Brut", "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.10.8" }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }