├── .DS_Store ├── .coveragerc ├── .gitignore ├── .vscode └── settings.json ├── AUTHORS.md ├── Examples ├── .DS_Store ├── Advection-Diffusion.ipynb ├── Burgers.ipynb ├── Burgers.py ├── KDV.ipynb ├── data │ ├── .DS_Store │ ├── Advection_diffusion.mat │ ├── burgers.npy │ └── kdv.npy └── output │ └── burgers │ └── 20190423_175345 │ ├── iteration_0 │ └── events.out.tfevents.1556034829.C02QP0AZG8WL.local │ └── iteration_1 │ └── events.out.tfevents.1556035633.C02QP0AZG8WL.local ├── LICENSE.txt ├── README.md ├── docs ├── Makefile ├── _static │ └── .gitignore ├── authors.rst ├── changelog.rst ├── conf.py ├── index.rst └── license.rst ├── setup.cfg ├── setup.py ├── src └── deepymod │ ├── .DS_Store │ ├── DeepMoD.py │ ├── PINN.py │ ├── __init__.py │ ├── graphs.py │ ├── library_functions.py │ ├── tb_setup.py │ └── utilities.py └── tests ├── conftest.py ├── tb.py └── test_skeleton.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/.DS_Store -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | branch = True 4 | source = deepymod 5 | # omit = bad_file.py 6 | 7 | [paths] 8 | source = 9 | src/ 10 | */site-packages/ 11 | 12 | [report] 13 | # Regexes for lines to exclude from consideration 14 | exclude_lines = 15 | # Have to re-enable the standard pragma 16 | pragma: no cover 17 | 18 | # Don't complain about missing debug-only code: 19 | def __repr__ 20 | if self\.debug 21 | 22 | # Don't complain if tests don't hit defensive assertion code: 23 | raise AssertionError 24 | raise NotImplementedError 25 | 26 | # Don't complain if non-runnable code isn't run: 27 | if 0: 28 | if __name__ == .__main__.: 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary and binary files 2 | *~ 3 | *.py[cod] 4 | *.so 5 | *.cfg 6 | !.isort.cfg 7 | !setup.cfg 8 | *.orig 9 | *.log 10 | *.pot 11 | __pycache__/* 12 | .cache/* 13 | .*.swp 14 | */.ipynb_checkpoints/* 15 | 16 | # Project files 17 | .ropeproject 18 | .project 19 | .pydevproject 20 | .settings 21 | .idea 22 | tags 23 | 24 | # Package files 25 | *.egg 26 | *.eggs/ 27 | .installed.cfg 28 | *.egg-info 29 | 30 | # Unittest and coverage 31 | htmlcov/* 32 | .coverage 33 | .tox 34 | junit.xml 35 | coverage.xml 36 | .pytest_cache/ 37 | 38 | # Build and docs folder/files 39 | build/* 40 | dist/* 41 | sdist/* 42 | docs/api/* 43 | docs/_rst/* 44 | docs/_build/* 45 | cover/* 46 | MANIFEST 47 | 48 | # Per-project virtualenvs 49 | .venv*/ 50 | Deepymod.code-workspace 51 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": true, 3 | "python.pythonPath": "/anaconda3/envs/deepmod/bin/python" 4 | } -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * Gert-Jan 4 | * Remy 5 | -------------------------------------------------------------------------------- /Examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/.DS_Store -------------------------------------------------------------------------------- /Examples/Advection-Diffusion.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 2D Advection-Diffusion equation" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "in this notebook we provide a simple example of the DeepMoD algorithm and apply it on the 2D advection-diffusion equation. " 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import numpy as np\n", 24 | "import pandas as pd \n", 25 | "\n", 26 | "from scipy.io import loadmat\n", 27 | "from deepymod.DeepMoD import DeepMoD\n", 28 | "from deepymod.library_functions import library_2Din_1Dout\n", 29 | "from deepymod.utilities import library_matrix_mat, print_PDE\n", 30 | "\n", 31 | "import matplotlib.pyplot as plt\n", 32 | "plt.style.use('seaborn-notebook')\n", 33 | "\n", 34 | "np.random.seed(42) # setting seed for randomisation" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Prepare the data" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "Next, we prepare the dataset." 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 2, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "data = loadmat('data/Advection_diffusion.mat')\n", 58 | "usol = np.real(data['Expression1'])\n", 59 | "usol= usol.reshape((51,51,61,4))\n", 60 | "\n", 61 | "x_v= usol[:,:,:,0]\n", 62 | "y_v = usol[:,:,:,1]\n", 63 | "t_v = usol[:,:,:,2]\n", 64 | "u_v = usol[:,:,:,3]" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "Next we plot the dataset for three different time-points" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 3, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "data": { 81 | "image/png": "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\n", 82 | "text/plain": [ 83 | "
" 84 | ] 85 | }, 86 | "metadata": { 87 | "needs_background": "light" 88 | }, 89 | "output_type": "display_data" 90 | } 91 | ], 92 | "source": [ 93 | "fig, axes = plt.subplots(ncols=3, figsize=(15, 4))\n", 94 | "\n", 95 | "im0 = axes[0].contourf(x_v[:,:,0], y_v[:,:,0], u_v[:,:,0], cmap='coolwarm')\n", 96 | "axes[0].set_xlabel('x')\n", 97 | "axes[0].set_ylabel('y')\n", 98 | "axes[0].set_title('t = 0')\n", 99 | "\n", 100 | "im1 = axes[1].contourf(x_v[:,:,10], y_v[:,:,10], u_v[:,:,10], cmap='coolwarm')\n", 101 | "axes[1].set_xlabel('x')\n", 102 | "axes[1].set_title('t = 10')\n", 103 | "\n", 104 | "im2 = axes[2].contourf(x_v[:,:,20], y_v[:,:,20], u_v[:,:,20], cmap='coolwarm')\n", 105 | "axes[2].set_xlabel('x')\n", 106 | "axes[2].set_title('t= 20')\n", 107 | "\n", 108 | "fig.colorbar(im1, ax=axes.ravel().tolist())\n", 109 | "\n", 110 | "plt.show()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "We flatten it to give it the right dimensions for feeding it to the network:" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 4, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "X = np.transpose((t_v.flatten(),x_v.flatten(), y_v.flatten()))\n", 127 | "y = u_v.reshape((u_v.size, 1))" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "We select the noise level we add to the data-set" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 5, 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "noise_level = 0.1" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 6, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "y_noisy = y + noise_level * np.std(y) * np.random.randn(y.size, 1)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "Select the number of samples:" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 14, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "number_of_samples = 2000" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 15, 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "idx = np.random.permutation(y.size)\n", 178 | "X_train = X[idx, :][:number_of_samples]\n", 179 | "y_train = y_noisy[idx, :][:number_of_samples]" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "## Configure the neural network" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "Next we define the architecture and strength of the $L_1$ penalty for the neural network.\n", 194 | "Note that in this example have three input channels in this example: {x,y,t}" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 16, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "config = {'layers': [3, 20, 20, 20, 20, 20, 1], 'lambda': 10**-6}" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "DeepMoD accepts any arbitrary library function and any options for it can be given through *the library_config*. The library function for this example accepts a maximum order for the polynomial and derivative terms. DeepMoD also needs to know the total number of terms upfront. We can calculate that by making a list of the polynomial and derivative terms and getting all the terms by feeding them into the library_matrix_mat function. Its output will be used later to print the found PDE." 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 17, 216 | "metadata": {}, 217 | "outputs": [ 218 | { 219 | "name": "stdout", 220 | "output_type": "stream", 221 | "text": [ 222 | "['1', 'u_{x}', 'u_{y}', 'u_{xx}', 'u_{yy}', 'u_{xy}', 'u', 'uu_{x}', 'uu_{y}', 'uu_{xx}', 'uu_{yy}', 'uu_{xy}']\n" 223 | ] 224 | } 225 | ], 226 | "source": [ 227 | "u = ['1', 'u']\n", 228 | "du = ['1', 'u_{x}', 'u_{y}','u_{xx}', 'u_{yy}','u_{xy}']\n", 229 | "coeffs_list = library_matrix_mat(u, du)\n", 230 | "print(coeffs_list)\n", 231 | "library_config = {'total_terms': 12, 'deriv_order': 2, 'poly_order': 1}" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "Next we set the training options. Usually we only change the maximum iterations and the grad_tol, which sets the convergence criterion;" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 18, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "train_opts = {'max_iterations': 50000, 'grad_tol':10**-7, 'learning_rate': 0.002, 'beta1': 0.99, 'beta2': 0.999, 'epsilon': 10**-8}" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "The last configuration we need to fill is the ouput_opts. It contains an output_directory and X_predict field. We've build a custom tensorboard so you can follow the progress of the run. Output_directory sets where the files are saved, then simply run \n", 255 | "\n", 256 | "`tensorboard --logdir $[OUTPUT_DIRECTORY]`\n", 257 | "\n", 258 | "in a terminal to open tensorboard. It shows the value of the coefficients, scaled coefficients and all possible costs. Note that the runs are timestamped in output_directory, so you'll have to add it! We can also use the output of tensorboard to analyze deepmod after. We show this below. The last option is X_predict. As DeepMoD also denoises the data, use this option to denoise some dataset X after DeepMoD has converged." 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": 19, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "output_opts = {'output_directory': 'output/AD/', 'X_predict': X}" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "## Run DeepMoD" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "We can now run DeepMoD using all the options we have set and the training data. We also need to specify which library function we wish to use. You can build any library you want and just pass is through this command:" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 20, 287 | "metadata": {}, 288 | "outputs": [ 289 | { 290 | "name": "stdout", 291 | "output_type": "stream", 292 | "text": [ 293 | "Epoch | Total loss | Loss gradient | MSE | PI | L1 \n", 294 | "0 [0.6593459, 0.0046862564, 0.44291213, 0.21642527, 8.5108695e-06]\n", 295 | "500 [0.017847514, 0.008489036, 0.012486025, 0.0053546075, 6.8822583e-06]\n", 296 | "1000 [0.007624976, 0.002521414, 0.005711712, 0.0019071312, 6.132989e-06]\n", 297 | "1500 [0.004391544, 0.0013907211, 0.0036640374, 0.0007229427, 4.5637894e-06]\n", 298 | "2000 [0.0031209611, 0.0008210218, 0.0028167781, 0.00030024798, 3.9351603e-06]\n", 299 | "2500 [0.002600971, 0.00036740233, 0.0024965932, 0.000101119906, 3.2578196e-06]\n", 300 | "3000 [0.0024599223, 0.00017162364, 0.0024095601, 4.7353435e-05, 3.0089411e-06]\n", 301 | "3500 [0.0024121318, 9.847269e-05, 0.0023771466, 3.2198153e-05, 2.7869635e-06]\n", 302 | "4000 [0.002388063, 5.711915e-05, 0.0023593518, 2.6148555e-05, 2.5626073e-06]\n", 303 | "4500 [0.002376226, 3.57227e-05, 0.0023507562, 2.3113762e-05, 2.3561163e-06]\n", 304 | "5000 [0.0023678131, 2.2681548e-05, 0.0023441603, 2.1471866e-05, 2.180967e-06]\n", 305 | "5500 [0.002362449, 1.5532938e-05, 0.0023393922, 2.100791e-05, 2.048895e-06]\n", 306 | "6000 [0.002358321, 8.684065e-06, 0.0023355074, 2.0839167e-05, 1.9743486e-06]\n", 307 | "6500 [0.0023554114, 7.751487e-06, 0.0023323216, 2.1148717e-05, 1.9410127e-06]\n", 308 | "7000 [0.0023501145, 2.7872125e-06, 0.0023267933, 2.1389977e-05, 1.9313827e-06]\n", 309 | "7500 [0.0023484468, 6.5981403e-06, 0.00232486, 2.1659169e-05, 1.9275808e-06]\n", 310 | "8000 [0.002347048, 1.0129937e-05, 0.0023231073, 2.2014638e-05, 1.9260629e-06]\n", 311 | "8500 [0.002342434, 3.5099063e-06, 0.0023179473, 2.2560855e-05, 1.9256497e-06]\n", 312 | "9000 [0.002342486, 1.0569085e-05, 0.0023173448, 2.3215742e-05, 1.925575e-06]\n", 313 | "9500 [0.002337382, 5.819258e-07, 0.0023116323, 2.382338e-05, 1.9262607e-06]\n", 314 | "10000 [0.0023379177, 1.2388375e-05, 0.0023116672, 2.4320207e-05, 1.9303016e-06]\n", 315 | "10500 [0.00233275, 1.9343743e-06, 0.0023061037, 2.4706795e-05, 1.9395197e-06]\n", 316 | "11000 [0.0023306545, 9.800292e-07, 0.0023038015, 2.4902762e-05, 1.950157e-06]\n", 317 | "11500 [0.0023287886, 2.9293765e-06, 0.0023017784, 2.5048952e-05, 1.961442e-06]\n", 318 | "12000 [0.0023270352, 4.268168e-06, 0.0022998995, 2.5163237e-05, 1.9726167e-06]\n", 319 | "12500 [0.0023251, 3.606108e-06, 0.0022978063, 2.5307976e-05, 1.9856598e-06]\n", 320 | "13000 [0.0023237304, 5.750065e-06, 0.0022963122, 2.542019e-05, 1.9978957e-06]\n", 321 | "13500 [0.0023236114, 9.952794e-06, 0.0022959928, 2.5607518e-05, 2.0112795e-06]\n", 322 | "14000 [0.0023217378, 9.67784e-06, 0.002293973, 2.5743515e-05, 2.021145e-06]\n", 323 | "14500 [0.0023201862, 9.021525e-06, 0.002292225, 2.5930392e-05, 2.0310474e-06]\n", 324 | "15000 [0.002316446, 9.628943e-07, 0.0022882782, 2.612785e-05, 2.0399896e-06]\n", 325 | "15500 [0.0023162956, 7.5272974e-06, 0.0022879306, 2.6318055e-05, 2.0470468e-06]\n", 326 | "16000 [0.0023166845, 1.0158696e-05, 0.0022881373, 2.6494667e-05, 2.0522932e-06]\n", 327 | "16500 [0.0023136954, 7.923965e-06, 0.0022849923, 2.6647489e-05, 2.0556208e-06]\n", 328 | "17000 [0.0023109978, 2.4149135e-06, 0.0022821485, 2.6791555e-05, 2.0577997e-06]\n", 329 | "17500 [0.0023103529, 5.778677e-06, 0.002281376, 2.6918033e-05, 2.0589232e-06]\n", 330 | "18000 [0.0023101687, 9.343914e-06, 0.0022810989, 2.7009986e-05, 2.059849e-06]\n", 331 | "18500 [0.0023093605, 1.0498733e-05, 0.0022802462, 2.7053442e-05, 2.060786e-06]\n", 332 | "19000 [0.002308857, 1.22482315e-05, 0.0022797403, 2.7054808e-05, 2.0617708e-06]\n", 333 | "19500 [0.002307788, 1.243968e-05, 0.002278693, 2.7032067e-05, 2.062929e-06]\n", 334 | "20000 [0.002307106, 1.3442545e-05, 0.0022780336, 2.7008336e-05, 2.0640564e-06]\n", 335 | "20500 [0.0023064425, 1.4437461e-05, 0.0022773785, 2.699893e-05, 2.065074e-06]\n", 336 | "21000 [0.0023049016, 1.3437283e-05, 0.0022758225, 2.7012911e-05, 2.0660295e-06]\n", 337 | "21500 [0.0023026424, 1.0202121e-05, 0.002273521, 2.7054422e-05, 2.0670198e-06]\n", 338 | "22000 [0.002300991, 8.199055e-06, 0.0022718008, 2.7122143e-05, 2.0679909e-06]\n", 339 | "22500 [0.002299947, 8.312614e-06, 0.0022706592, 2.7218768e-05, 2.0688813e-06]\n", 340 | "23000 [0.002298221, 5.1467646e-06, 0.0022688103, 2.7340784e-05, 2.0698346e-06]\n", 341 | "23500 [0.002296725, 5.596593e-07, 0.002267165, 2.7489375e-05, 2.0706298e-06]\n", 342 | "24000 [0.0022956515, 2.4465287e-06, 0.0022659148, 2.7665894e-05, 2.070814e-06]\n", 343 | "24500 [0.0022958696, 1.0439242e-05, 0.0022659216, 2.787767e-05, 2.0702669e-06]\n", 344 | "25000 [0.0022964878, 1.582569e-05, 0.0022662992, 2.8119248e-05, 2.0693174e-06]\n", 345 | "25500 [0.002293476, 1.1133378e-05, 0.0022630256, 2.8381994e-05, 2.0682223e-06]\n", 346 | "26000 [0.0022910892, 6.39179e-06, 0.0022603518, 2.8670258e-05, 2.067144e-06]\n", 347 | "26500 [0.0022925858, 1.600352e-05, 0.002261529, 2.8991271e-05, 2.0654768e-06]\n", 348 | "27000 [0.0022887997, 8.28765e-06, 0.0022574062, 2.9330484e-05, 2.0631658e-06]\n", 349 | "27500 [0.0022871455, 6.1159067e-06, 0.0022553892, 2.9695484e-05, 2.0607404e-06]\n", 350 | "28000 [0.0022883506, 1.3709325e-05, 0.0022562332, 3.0058747e-05, 2.0586044e-06]\n", 351 | "28500 [0.0022858814, 1.0170708e-05, 0.002253443, 3.0381543e-05, 2.0567252e-06]\n", 352 | "29000 [0.002283151, 3.4510276e-06, 0.0022504167, 3.06791e-05, 2.0551317e-06]\n", 353 | "29500 [0.0022851794, 1.3249159e-05, 0.0022521296, 3.099624e-05, 2.0535877e-06]\n", 354 | "30000 [0.0022807906, 6.8734346e-07, 0.0022473794, 3.1359687e-05, 2.0514863e-06]\n", 355 | "30500 [0.0022831017, 1.26354835e-05, 0.00224931, 3.1741845e-05, 2.0498355e-06]\n", 356 | "31000 [0.002278835, 1.2883075e-06, 0.0022447787, 3.200721e-05, 2.0491198e-06]\n", 357 | "31500 [0.002281174, 1.25356555e-05, 0.002246855, 3.2270287e-05, 2.048793e-06]\n", 358 | "32000 [0.0022769684, 1.3127698e-06, 0.002242324, 3.259626e-05, 2.0481336e-06]\n", 359 | "32500 [0.0022790337, 1.1846965e-05, 0.0022440415, 3.2944434e-05, 2.047683e-06]\n", 360 | "33000 [0.0022771878, 9.807295e-06, 0.0022419572, 3.318259e-05, 2.0478712e-06]\n", 361 | "33500 [0.00227459, 4.2201073e-06, 0.0022392035, 3.3338092e-05, 2.0483603e-06]\n", 362 | "34000 [0.0022766243, 1.2258887e-05, 0.0022410722, 3.350326e-05, 2.048801e-06]\n", 363 | "34500 [0.0022741558, 9.707831e-06, 0.0022384066, 3.370022e-05, 2.0490884e-06]\n", 364 | "35000 [0.0022725498, 8.918311e-06, 0.0022365688, 3.3931763e-05, 2.0491505e-06]\n", 365 | "35500 [0.0022739258, 1.4112379e-05, 0.0022377372, 3.413915e-05, 2.0493699e-06]\n", 366 | "36000 [0.0022706625, 4.465868e-06, 0.0022343956, 3.4217304e-05, 2.049575e-06]\n", 367 | "36500 [0.0022711027, 6.6976036e-06, 0.002234691, 3.4362132e-05, 2.0495681e-06]\n", 368 | "37000 [0.0022713512, 1.3028995e-05, 0.002234643, 3.465876e-05, 2.0493624e-06]\n", 369 | "37500 [0.0022681323, 5.2393366e-06, 0.0022313115, 3.4771307e-05, 2.0492687e-06]\n", 370 | "38000 [0.002270208, 8.655961e-06, 0.0022333073, 3.485182e-05, 2.0489827e-06]\n", 371 | "38500 [0.0022664117, 5.563065e-06, 0.002229221, 3.514173e-05, 2.0488346e-06]\n", 372 | "39000 [0.002267342, 8.989547e-06, 0.0022299422, 3.535083e-05, 2.0489742e-06]\n", 373 | "39500 [0.0022662615, 7.5202556e-06, 0.0022288156, 3.5396934e-05, 2.0488137e-06]\n", 374 | "40000 [0.002264405, 6.7831575e-06, 0.0022267592, 3.5596302e-05, 2.0492946e-06]\n", 375 | "40500 [0.002264356, 8.458457e-06, 0.0022263783, 3.5927722e-05, 2.0501802e-06]\n", 376 | "41000 [0.0022623332, 8.852894e-06, 0.0022241864, 3.609603e-05, 2.0508835e-06]\n", 377 | "41500 [0.0022620365, 8.574223e-06, 0.0022238279, 3.6157347e-05, 2.0512714e-06]\n", 378 | "42000 [0.002259698, 7.188082e-06, 0.0022212751, 3.637053e-05, 2.05238e-06]\n", 379 | "42500 [0.0022588482, 8.691311e-06, 0.0022200851, 3.6709018e-05, 2.0539987e-06]\n", 380 | "43000 [0.002258588, 1.1959799e-05, 0.0022195755, 3.695687e-05, 2.0555565e-06]\n", 381 | "43500 [0.002254713, 4.109491e-06, 0.002215532, 3.712458e-05, 2.0565683e-06]\n", 382 | "44000 [0.0022553033, 9.201708e-06, 0.0022159196, 3.7326266e-05, 2.0574628e-06]\n", 383 | "44500 [0.0022550628, 1.1312604e-05, 0.0022154043, 3.7600075e-05, 2.0584855e-06]\n", 384 | "45000 [0.002252887, 1.238408e-05, 0.0022129226, 3.790509e-05, 2.0594377e-06]\n", 385 | "45500 [0.0022490232, 4.865187e-06, 0.0022086774, 3.8285532e-05, 2.0602577e-06]\n", 386 | "46000 [0.0022484362, 1.04158735e-05, 0.0022076634, 3.8712347e-05, 2.060609e-06]\n", 387 | "46500 [0.0022483922, 1.48178915e-05, 0.0022071938, 3.91381e-05, 2.0602704e-06]\n", 388 | "47000 [0.0022478618, 1.3514581e-05, 0.0022062436, 3.9558745e-05, 2.0594314e-06]\n", 389 | "47500 [0.0022463528, 1.8979228e-05, 0.0022042848, 4.0010043e-05, 2.058056e-06]\n", 390 | "48000 [0.0022449195, 1.5883536e-05, 0.0022024203, 4.0442705e-05, 2.056496e-06]\n", 391 | "48500 [0.0022435542, 1.702139e-05, 0.002200617, 4.0882554e-05, 2.0547789e-06]\n", 392 | "49000 [0.002241195, 1.6843736e-05, 0.0021978277, 4.13144e-05, 2.0529303e-06]\n", 393 | "49500 [0.0022383528, 1.40340235e-05, 0.0021945734, 4.172844e-05, 2.051029e-06]\n", 394 | "Current sparse vectors:\n", 395 | "[array([[0. ],\n", 396 | " [0.23685676],\n", 397 | " [0.4574976 ],\n", 398 | " [0.41017011],\n", 399 | " [0.44168493],\n", 400 | " [0. ],\n", 401 | " [0. ],\n", 402 | " [0. ],\n", 403 | " [0. ],\n", 404 | " [0. ],\n", 405 | " [0. ],\n", 406 | " [0. ]])]\n", 407 | "Now running for the final time...\n", 408 | "Epoch | Total loss | Loss gradient | MSE | PI | L1 \n", 409 | "0 [0.089149505, 0.012092508, 0.08468605, 0.004463459, 0.0]\n", 410 | "500 [0.0025410282, 0.00015548721, 0.0023900631, 0.00015096515, 0.0]\n", 411 | "1000 [0.0023419117, 1.9292098e-05, 0.002269058, 7.285371e-05, 0.0]\n", 412 | "1500 [0.002301082, 3.938705e-06, 0.00224797, 5.3111933e-05, 0.0]\n", 413 | "2000 [0.002280426, 1.1975444e-06, 0.0022347914, 4.563465e-05, 0.0]\n", 414 | "2500 [0.0022684801, 3.8609875e-07, 0.0022258735, 4.2606498e-05, 0.0]\n", 415 | "3000 [0.00226089, 1.7553903e-07, 0.0022194157, 4.14744e-05, 0.0]\n", 416 | "3500 [0.0022555983, 1.3997989e-07, 0.0022143007, 4.129768e-05, 0.0]\n", 417 | "4000 [0.0022515599, 7.744925e-08, 0.0022099263, 4.1633644e-05, 0.0]\n", 418 | "Optimizer converged.\n" 419 | ] 420 | } 421 | ], 422 | "source": [ 423 | "sparse_vectors, denoised = DeepMoD(X_train, y_train, config, library_2Din_1Dout, library_config, train_opts, output_opts)" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "Show final result:" 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 32, 436 | "metadata": {}, 437 | "outputs": [ 438 | { 439 | "name": "stdout", 440 | "output_type": "stream", 441 | "text": [ 442 | "Inferred equation:\n", 443 | "u_t = 0.254u_{x} + 0.495u_{y} + 0.498u_{xx} + 0.522u_{yy}\n" 444 | ] 445 | } 446 | ], 447 | "source": [ 448 | "u = ['1', 'u']\n", 449 | "du = ['1', 'u_{x}', 'u_{y}','u_{xx}', 'u_{yy}','u_{xy}']\n", 450 | "coeffs_list = library_matrix_mat(u, du)\n", 451 | "\n", 452 | "print('Inferred equation:')\n", 453 | "print_PDE(sparse_vectors[0], coeffs_list, PDE_term='u_t')" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "Plot the 'Ground truth', 'Noisy' and 'Reconstructed/Denoised' solution" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 22, 466 | "metadata": {}, 467 | "outputs": [], 468 | "source": [ 469 | "X_predict = X[(X[:,0]==5.0) | (X[:,0]==9.0) | (X[:,0]==7.0), :]" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": 23, 475 | "metadata": {}, 476 | "outputs": [], 477 | "source": [ 478 | "data_dict = {'x_grid': X[:,1],'y_grid': X[:,2], 't_grid': X[:,0], 'ground_truth': np.squeeze(y), 'noisy': np.squeeze(y_noisy)}\n", 479 | "df = pd.DataFrame(data_dict)\n", 480 | "for key in df:\n", 481 | " df[key] = np.squeeze(df[key])\n", 482 | "data = pd.DataFrame(df)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 24, 488 | "metadata": {}, 489 | "outputs": [], 490 | "source": [ 491 | "data_dict_denoised = {'x_grid': X[:,1],'y_grid': X[:,2], 't_grid': X[:,0], 'denoised': np.squeeze(denoised)}\n", 492 | "df_dn = pd.DataFrame(data_dict_denoised)\n", 493 | "for key in df_dn:\n", 494 | " df_dn[key] = np.squeeze(df_dn[key])\n", 495 | "data_denoised = pd.DataFrame(df_dn)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": 31, 501 | "metadata": {}, 502 | "outputs": [ 503 | { 504 | "data": { 505 | "image/png": "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\n", 506 | "text/plain": [ 507 | "
" 508 | ] 509 | }, 510 | "metadata": { 511 | "needs_background": "light" 512 | }, 513 | "output_type": "display_data" 514 | } 515 | ], 516 | "source": [ 517 | "time1 = 7.0\n", 518 | "\n", 519 | "vmin = np.min(data[data['t_grid'] == time1]['ground_truth'])\n", 520 | "vmax = np.max(data[data['t_grid'] == time1]['ground_truth'])\n", 521 | "\n", 522 | "fig, axes = plt.subplots(ncols=3, figsize=(15, 4))\n", 523 | "\n", 524 | "im1 = axes[0].contourf(pd.pivot_table(data[data['t_grid'] == time1],index='y_grid', columns='x_grid', values='ground_truth'), cmap='coolwarm',vmin=vmin, vmax=vmax)\n", 525 | "axes[0].set_xlabel('x')\n", 526 | "axes[0].set_ylabel('y')\n", 527 | "axes[0].set_title('Ground truth')\n", 528 | "\n", 529 | "im2 = axes[1].contourf(pd.pivot_table(data[data['t_grid'] == time1],index='y_grid', columns='x_grid', values='noisy'), cmap='coolwarm', vmin=vmin, vmax=vmax)\n", 530 | "axes[1].set_xlabel('x')\n", 531 | "axes[1].set_title('Noisy')\n", 532 | "\n", 533 | "im3 = axes[2].contourf(pd.pivot_table(data_denoised[data_denoised['t_grid'] == time1],index='y_grid', columns='x_grid', values='denoised'), cmap='coolwarm', vmin=vmin, vmax=vmax)\n", 534 | "axes[2].set_xlabel('x')\n", 535 | "axes[2].set_title('Sampled')\n", 536 | "\n", 537 | "fig.colorbar(im1, ax=axes.ravel().tolist())\n", 538 | "\n", 539 | "plt.show()" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": null, 545 | "metadata": {}, 546 | "outputs": [], 547 | "source": [] 548 | } 549 | ], 550 | "metadata": { 551 | "kernelspec": { 552 | "display_name": "Python 3", 553 | "language": "python", 554 | "name": "python3" 555 | }, 556 | "language_info": { 557 | "codemirror_mode": { 558 | "name": "ipython", 559 | "version": 3 560 | }, 561 | "file_extension": ".py", 562 | "mimetype": "text/x-python", 563 | "name": "python", 564 | "nbconvert_exporter": "python", 565 | "pygments_lexer": "ipython3", 566 | "version": "3.6.8" 567 | } 568 | }, 569 | "nbformat": 4, 570 | "nbformat_minor": 2 571 | } 572 | -------------------------------------------------------------------------------- /Examples/Burgers.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | import numpy as np 3 | from deepymod.DeepMoD import DeepMoD 4 | from deepymod.library_functions import library_1D 5 | from deepymod.utilities import library_matrix_mat, print_PDE 6 | 7 | np.random.seed(42) # setting seed for randomisation 8 | 9 | # Loading data 10 | data = np.load('Examples/data/burgers.npy', allow_pickle=True).item() 11 | X = np.transpose((data['x'].flatten(), data['t'].flatten())) 12 | y = np.real(data['u']).reshape((data['u'].size, 1)) 13 | 14 | noise_level = 0.05 15 | number_of_samples = 1000 16 | 17 | y_noisy = y + noise_level * np.std(y) * np.random.randn(y.size, 1) 18 | idx = np.random.permutation(y.size) 19 | X_train = X[idx, :][:number_of_samples] 20 | y_train = y_noisy[idx, :][:number_of_samples] 21 | 22 | u = ['1', 'u', 'uˆ2'] 23 | du = ['1', 'u_{x}', 'u_{xx}', 'u_{xxx}'] 24 | coeffs_list = library_matrix_mat(u, du) 25 | 26 | # Configuring and running DeepMoD 27 | config = {'layers': [2, 20, 20, 20, 20, 20, 1], 'lambda': 10e-6} 28 | train_opts = {'max_iterations': 10000, 'grad_tol': 10**-6, 'learning_rate': 0.002, 'beta1': 0.99, 'beta2': 0.999, 'epsilon': 10**-8} 29 | library_config = {'total_terms': len(coeffs_list), 'deriv_order': 3, 'poly_order': 2} 30 | output_opts = {'output_directory': 'Examples/output/burgers/', 'X_predict': X} 31 | 32 | sparse_vectors, denoised = DeepMoD(X_train, y_train, config, library_1D, library_config, train_opts, output_opts) 33 | 34 | print('Inferred equation:') 35 | print_PDE(sparse_vectors[0], coeffs_list, PDE_term='u_t') 36 | -------------------------------------------------------------------------------- /Examples/KDV.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Korteweg-de Vries equation" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "in this notebook we provide a simple example of the DeepMoD algorithm and apply it on the Korteweg-de Vries equation. " 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Set the noise level, number of samples, architecture and strength of the $L_1$ Penalty" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import numpy as np\n", 31 | "import pandas as pd \n", 32 | "\n", 33 | "from deepymod.DeepMoD import DeepMoD\n", 34 | "from deepymod.library_functions import library_1D\n", 35 | "from deepymod.utilities import library_matrix_mat, print_PDE\n", 36 | "\n", 37 | "import matplotlib.pyplot as plt\n", 38 | "plt.style.use('seaborn-notebook')\n", 39 | "\n", 40 | "np.random.seed(42) # setting seed for randomisation" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "## Prepare the data" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "Next, we prepare the dataset." 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 2, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "Shape of grid: (512, 201)\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "data = np.load('data/kdv.npy', allow_pickle=True).item()\n", 72 | "print('Shape of grid:', data['x'].shape)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "Let's plot it to get an idea of the data:" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 3, 85 | "metadata": {}, 86 | "outputs": [ 87 | { 88 | "data": { 89 | "image/png": "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\n", 90 | "text/plain": [ 91 | "
" 92 | ] 93 | }, 94 | "metadata": { 95 | "needs_background": "light" 96 | }, 97 | "output_type": "display_data" 98 | } 99 | ], 100 | "source": [ 101 | "fig, ax = plt.subplots()\n", 102 | "im = ax.contourf(data['x'], data['t'], np.real(data['u']))\n", 103 | "ax.set_xlabel('x')\n", 104 | "ax.set_ylabel('t')\n", 105 | "fig.colorbar(mappable=im)\n", 106 | "\n", 107 | "plt.show()" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 4, 113 | "metadata": {}, 114 | "outputs": [ 115 | { 116 | "name": "stdout", 117 | "output_type": "stream", 118 | "text": [ 119 | "(102912, 2) (102912, 1)\n" 120 | ] 121 | } 122 | ], 123 | "source": [ 124 | "X = np.transpose((data['x'].flatten(), data['t'].flatten()))\n", 125 | "y = np.real(data['u']).reshape((data['u'].size, 1))\n", 126 | "\n", 127 | "print(X.shape, y.shape)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "As we can see, $X$ has 2 dimensions, $\\{x, t\\}$, while $y$ has only one, $\\{u\\}$. Always explicity set the shape (i.e. $N\\times 1$, not $N$) or you'll get errors. This dataset is noiseless, so let's add $5\\%$ noise:" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 5, 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "noise_level = 0.025\n", 144 | "y_noisy = y + noise_level * np.std(y) * np.random.randn(y.size, 1)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "The dataset is also much larger than needed, so let's hussle it and pick out a 2000 samples:" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 6, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "number_of_samples = 1000\n", 161 | "\n", 162 | "idx = np.random.permutation(y.size)\n", 163 | "X_train = X[idx, :][:number_of_samples]\n", 164 | "y_train = y_noisy[idx, :][:number_of_samples]" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "We now have a dataset which we can use. Let's plot, for a final time, the original dataset, the noisy set and the samples points:" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 7, 177 | "metadata": {}, 178 | "outputs": [ 179 | { 180 | "data": { 181 | "image/png": "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\n", 182 | "text/plain": [ 183 | "
" 184 | ] 185 | }, 186 | "metadata": { 187 | "needs_background": "light" 188 | }, 189 | "output_type": "display_data" 190 | } 191 | ], 192 | "source": [ 193 | "fig, axes = plt.subplots(ncols=3, figsize=(15, 4))\n", 194 | "\n", 195 | "im0 = axes[0].contourf(data['x'], data['t'], np.real(data['u']), cmap='coolwarm')\n", 196 | "axes[0].set_xlabel('x')\n", 197 | "axes[0].set_ylabel('t')\n", 198 | "axes[0].set_title('Ground truth')\n", 199 | "\n", 200 | "im1 = axes[1].contourf(data['x'], data['t'], y_noisy.reshape(data['x'].shape), cmap='coolwarm')\n", 201 | "axes[1].set_xlabel('x')\n", 202 | "axes[1].set_title('Noisy')\n", 203 | "\n", 204 | "sampled = np.array([y_noisy[index, 0] if index in idx[:number_of_samples] else np.nan for index in np.arange(data['x'].size)])\n", 205 | "sampled = np.rot90(sampled.reshape(data['x'].shape)) #array needs to be rotated because of imshow\n", 206 | "\n", 207 | "im2 = axes[2].imshow(sampled, aspect='auto', cmap='coolwarm')\n", 208 | "axes[2].set_xlabel('x')\n", 209 | "axes[2].set_title('Sampled')\n", 210 | "\n", 211 | "fig.colorbar(im1, ax=axes.ravel().tolist())\n", 212 | "\n", 213 | "plt.show()" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "## Configure the neural network" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "We now setup the options for DeepMoD. There's as few dictionaries to fill, starting with 'config', which sets up the the general network layers and L$_1$ penalty. We use the following:" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 8, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "config = {'layers': [2, 20, 20, 20, 20, 20, 1], 'lambda': 10e-6}" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "Next we set the training options. Usually we only change the maximum iterations and the grad_tol, which sets the convergence criterion;" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 9, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [ 252 | "train_opts = {'max_iterations': 20000, 'grad_tol':10**-7, 'learning_rate': 0.002, 'beta1': 0.99, 'beta2': 0.999, 'epsilon': 10**-8}" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "DeepMoD accepts any arbitrary library function and any options for it can be given through *the library_config*. The library function for this example accepts a maximum order for the polynomial and derivative terms. DeepMoD also needs to know the total number of terms upfront. We can calculate that by making a list of the polynomial and derivative terms and getting all the terms by feeding them into the library_matrix_mat function. Its output will be used later to print the found PDE." 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 10, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "name": "stdout", 269 | "output_type": "stream", 270 | "text": [ 271 | "['1', 'u_{x}', 'u_{xx}', 'u_{xxx}', 'u', 'uu_{x}', 'uu_{xx}', 'uu_{xxx}', 'uˆ2', 'uˆ2u_{x}', 'uˆ2u_{xx}', 'uˆ2u_{xxx}']\n" 272 | ] 273 | } 274 | ], 275 | "source": [ 276 | "u = ['1', 'u', 'uˆ2']\n", 277 | "du = ['1', 'u_{x}', 'u_{xx}', 'u_{xxx}']\n", 278 | "coeffs_list = library_matrix_mat(u, du)\n", 279 | "\n", 280 | "print(coeffs_list)\n", 281 | "\n", 282 | "library_config = {'total_terms': len(coeffs_list), 'deriv_order': 3, 'poly_order': 2}" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "The last configuration we need to fill is the ouput_opts. It contains an output_directory and X_predict field. We've build a custom tensorboard so you can follow the progress of the run. Output_directory sets where the files are saved, then simply run \n", 290 | "\n", 291 | "`tensorboard --logdir $[OUTPUT_DIRECTORY]`\n", 292 | "\n", 293 | "in a terminal to open tensorboard. It shows the value of the coefficients, scaled coefficients and all possible costs. Note that the runs are timestamped in output_directory, so you'll have to add it! We can also use the output of tensorboard to analyze deepmod after. We show this below. The last option is X_predict. As DeepMoD also denoises the data, use this option to denoise some dataset X after DeepMoD has converged." 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 11, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "output_opts = {'output_directory': 'output/burgers/', 'X_predict': X}" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "metadata": {}, 308 | "source": [ 309 | "## Run DeepMoD " 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "We can now run DeepMoD using all the options we have set and the training data. We also need to specify which library function we wish to use. You can build any library you want and just pass is through this command:" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 12, 322 | "metadata": {}, 323 | "outputs": [ 324 | { 325 | "name": "stdout", 326 | "output_type": "stream", 327 | "text": [ 328 | "Epoch | Total loss | Loss gradient | MSE | PI | L1 \n", 329 | "0 [0.59844595, 0.012143345, 0.03649929, 0.56183606, 0.00011058643]\n", 330 | "500 [0.017280467, 0.0008246411, 0.01365739, 0.003542878, 8.0197635e-05]\n", 331 | "1000 [0.007692895, 0.0003051667, 0.0063986643, 0.0011447447, 0.00014948579]\n", 332 | "1500 [0.006816086, 0.00046630114, 0.0054706945, 0.0012328458, 0.000112545706]\n", 333 | "2000 [0.0061532785, 0.00055087375, 0.0048074387, 0.0012451045, 0.00010073495]\n", 334 | "2500 [0.0056804577, 0.00043784844, 0.004504157, 0.0010848064, 9.1494236e-05]\n", 335 | "3000 [0.0053056804, 0.00047241076, 0.0041766097, 0.0010415456, 8.7525295e-05]\n", 336 | "3500 [0.0048427177, 0.00044476453, 0.0038137352, 0.00094592787, 8.3054234e-05]\n", 337 | "4000 [0.004501195, 0.00041666673, 0.003577137, 0.0008459331, 7.8125435e-05]\n", 338 | "4500 [0.004195904, 0.0004019875, 0.0033961462, 0.0007283125, 7.1445094e-05]\n", 339 | "5000 [0.0037246996, 0.00026619332, 0.0031362, 0.00052883115, 5.966869e-05]\n", 340 | "5500 [0.0033118161, 0.00024248492, 0.0029053634, 0.0003590006, 4.745204e-05]\n", 341 | "6000 [0.0029341863, 0.000178471, 0.0027038818, 0.00019577671, 3.4527606e-05]\n", 342 | "6500 [0.0021720668, 0.00015067268, 0.0020237083, 0.00012388227, 2.4476247e-05]\n", 343 | "7000 [0.0003807902, 5.909343e-05, 0.0002621233, 9.600604e-05, 2.2660875e-05]\n", 344 | "7500 [0.00016101023, 6.790641e-05, 8.68271e-05, 5.2561023e-05, 2.1622114e-05]\n", 345 | "8000 [9.191394e-05, 2.7699458e-05, 4.2765772e-05, 2.301868e-05, 2.6129494e-05]\n", 346 | "8500 [6.7884255e-05, 3.1486506e-05, 2.7285636e-05, 1.48291365e-05, 2.5769486e-05]\n", 347 | "9000 [5.8916274e-05, 8.57717e-05, 2.0975061e-05, 1.3269257e-05, 2.4671957e-05]\n", 348 | "9500 [4.7397552e-05, 2.7865803e-05, 1.4617792e-05, 8.928925e-06, 2.3850836e-05]\n", 349 | "10000 [4.5547422e-05, 7.980451e-05, 1.432358e-05, 8.779121e-06, 2.2444721e-05]\n", 350 | "10500 [4.9768027e-05, 0.00012178927, 2.0729794e-05, 6.572313e-06, 2.246592e-05]\n", 351 | "11000 [3.979064e-05, 6.0420487e-05, 1.0976486e-05, 6.603612e-06, 2.2210543e-05]\n", 352 | "11500 [3.7414506e-05, 3.7158738e-05, 9.626921e-06, 5.426596e-06, 2.2360988e-05]\n", 353 | "12000 [4.2834476e-05, 0.00011168698, 1.3175875e-05, 7.3765386e-06, 2.2282062e-05]\n", 354 | "12500 [4.1874926e-05, 0.00010290873, 1.4683081e-05, 4.4168933e-06, 2.2774953e-05]\n", 355 | "13000 [4.069874e-05, 0.00010510862, 1.19132255e-05, 6.37596e-06, 2.2409553e-05]\n", 356 | "13500 [3.5421057e-05, 5.1473093e-05, 8.581921e-06, 4.2507204e-06, 2.2588414e-05]\n", 357 | "14000 [3.8822218e-05, 9.649434e-05, 1.0812813e-05, 5.34302e-06, 2.2666385e-05]\n", 358 | "14500 [3.7364967e-05, 8.248894e-05, 1.1224907e-05, 2.9559756e-06, 2.3184082e-05]\n", 359 | "15000 [3.5189023e-05, 6.1062485e-05, 8.545973e-06, 3.6025694e-06, 2.304048e-05]\n", 360 | "15500 [3.5924353e-05, 7.356688e-05, 1.0015952e-05, 2.4854066e-06, 2.3422994e-05]\n", 361 | "16000 [3.4232224e-05, 4.9114868e-05, 8.579784e-06, 2.1740557e-06, 2.3478386e-05]\n", 362 | "16500 [3.5352714e-05, 6.71687e-05, 9.533147e-06, 2.203455e-06, 2.3616112e-05]\n", 363 | "17000 [3.4392826e-05, 4.937196e-05, 8.861496e-06, 1.8755997e-06, 2.3655728e-05]\n", 364 | "17500 [3.4903722e-05, 6.224436e-05, 9.329994e-06, 1.8362431e-06, 2.3737482e-05]\n", 365 | "18000 [3.473495e-05, 5.980847e-05, 9.208452e-06, 1.7461612e-06, 2.3780336e-05]\n", 366 | "18500 [3.3835357e-05, 4.55829e-05, 8.403357e-06, 1.6505102e-06, 2.378149e-05]\n", 367 | "19000 [3.3072087e-05, 2.2047127e-05, 7.575799e-06, 1.7385713e-06, 2.3757717e-05]\n", 368 | "19500 [3.3248314e-05, 2.7663793e-05, 7.5011953e-06, 2.055596e-06, 2.3691522e-05]\n", 369 | "Current sparse vectors:\n", 370 | "[array([[ 0. ],\n", 371 | " [ 0. ],\n", 372 | " [ 0. ],\n", 373 | " [-0.79069424],\n", 374 | " [ 0. ],\n", 375 | " [-3.89588761],\n", 376 | " [ 0. ],\n", 377 | " [ 0. ],\n", 378 | " [ 0. ],\n", 379 | " [ 0. ],\n", 380 | " [ 0. ],\n", 381 | " [ 0. ]])]\n", 382 | "Now running for the final time...\n", 383 | "Epoch | Total loss | Loss gradient | MSE | PI | L1 \n", 384 | "0 [0.21666934, 0.073488116, 0.117822, 0.09884734, 0.0]\n", 385 | "500 [0.00040882008, 0.0007653867, 0.0002719016, 0.00013691848, 0.0]\n", 386 | "1000 [0.00011373228, 0.00030619602, 4.6929195e-05, 6.680309e-05, 0.0]\n", 387 | "1500 [9.9783334e-05, 0.0002869711, 3.787739e-05, 6.190594e-05, 0.0]\n", 388 | "2000 [8.9193214e-05, 0.0002738838, 3.3205863e-05, 5.598735e-05, 0.0]\n", 389 | "2500 [7.855566e-05, 0.00025543506, 2.9475254e-05, 4.908041e-05, 0.0]\n", 390 | "3000 [6.750411e-05, 0.0002310568, 2.6110358e-05, 4.139375e-05, 0.0]\n", 391 | "3500 [5.638964e-05, 0.00020345667, 2.2780794e-05, 3.3608845e-05, 0.0]\n", 392 | "4000 [4.573988e-05, 0.00017475683, 1.953208e-05, 2.62078e-05, 0.0]\n", 393 | "4500 [3.605238e-05, 0.00014609518, 1.6552096e-05, 1.9500285e-05, 0.0]\n", 394 | "5000 [2.7725642e-05, 0.00011825412, 1.399054e-05, 1.3735103e-05, 0.0]\n", 395 | "5500 [2.0980722e-05, 9.224264e-05, 1.1889321e-05, 9.0914e-06, 0.0]\n", 396 | "6000 [1.5842088e-05, 6.900917e-05, 1.0235079e-05, 5.6070085e-06, 0.0]\n", 397 | "6500 [1.22083175e-05, 4.9143997e-05, 9.008948e-06, 3.1993693e-06, 0.0]\n", 398 | "7000 [9.870361e-06, 3.2946704e-05, 8.1700255e-06, 1.7003356e-06, 0.0]\n", 399 | "7500 [8.507063e-06, 2.0490492e-05, 7.644798e-06, 8.622656e-07, 0.0]\n", 400 | "8000 [7.793492e-06, 1.1619663e-05, 7.3527176e-06, 4.4077424e-07, 0.0]\n", 401 | "8500 [7.456933e-06, 5.838437e-06, 7.2104626e-06, 2.4647022e-07, 0.0]\n", 402 | "9000 [7.3119854e-06, 2.4408803e-06, 7.1480067e-06, 1.6397858e-07, 0.0]\n", 403 | "9500 [7.251008e-06, 7.4391244e-07, 7.121076e-06, 1.2993192e-07, 0.0]\n", 404 | "10000 [7.217891e-06, 1.4173328e-07, 7.1068075e-06, 1.1108324e-07, 0.0]\n", 405 | "10500 [7.1935133e-06, 3.74436e-08, 7.095721e-06, 9.779242e-08, 0.0]\n", 406 | "Optimizer converged.\n" 407 | ] 408 | } 409 | ], 410 | "source": [ 411 | "sparse_vectors, denoised = DeepMoD(X_train, y_train, config, library_1D, library_config, train_opts, output_opts)" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "Now that DeepMoD has converged, it has found the following equation:" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 13, 424 | "metadata": {}, 425 | "outputs": [ 426 | { 427 | "name": "stdout", 428 | "output_type": "stream", 429 | "text": [ 430 | "Inferred equation:\n", 431 | "u_t = 1.002u_{xxx} - 6.011uu_{x}\n" 432 | ] 433 | } 434 | ], 435 | "source": [ 436 | "print('Inferred equation:')\n", 437 | "print_PDE(sparse_vectors[0], coeffs_list, PDE_term='u_t')" 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "metadata": {}, 443 | "source": [ 444 | "which is correct! Let's analyse a few more results in the next section." 445 | ] 446 | }, 447 | { 448 | "cell_type": "markdown", 449 | "metadata": {}, 450 | "source": [ 451 | "## Analysis" 452 | ] 453 | }, 454 | { 455 | "cell_type": "markdown", 456 | "metadata": {}, 457 | "source": [ 458 | "Plot the 'Ground truth', 'Noisy' and 'Reconstructed/Denoised' solution" 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": 15, 464 | "metadata": {}, 465 | "outputs": [], 466 | "source": [ 467 | "data= {'x_grid': X[:, 0], 't_grid': X[:, 1], 'ground truth': y, 'noisy': y_noisy, 'inferred': denoised}\n", 468 | "for key in data:\n", 469 | " data[key] = np.squeeze(data[key])\n", 470 | "data = pd.DataFrame(data)\n", 471 | "train_idx = np.random.permutation(data['t_grid'].size)[:number_of_samples]\n", 472 | "data['train set'] = [data['noisy'][index] if index in train_idx else None for index in np.arange(data['t_grid'].size)]" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": 18, 478 | "metadata": {}, 479 | "outputs": [ 480 | { 481 | "data": { 482 | "image/png": "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\n", 483 | "text/plain": [ 484 | "
" 485 | ] 486 | }, 487 | "metadata": { 488 | "needs_background": "light" 489 | }, 490 | "output_type": "display_data" 491 | } 492 | ], 493 | "source": [ 494 | "vmin = data['ground truth'].min()\n", 495 | "vmax =data['ground truth'].max()\n", 496 | "\n", 497 | "fig, axes = plt.subplots(ncols=3, figsize=(15, 4))\n", 498 | "\n", 499 | "im0 = axes[0].contourf(data.pivot(index='t_grid', columns='x_grid', values='ground truth'), cmap='coolwarm',vmin=vmin, vmax=vmax)\n", 500 | "axes[0].set_xlabel('x')\n", 501 | "axes[0].set_ylabel('t')\n", 502 | "axes[0].set_title('Ground truth')\n", 503 | "\n", 504 | "im1 = axes[1].contourf(data.pivot(index='t_grid', columns='x_grid', values='noisy'), cmap='coolwarm', vmin=vmin, vmax=vmax)\n", 505 | "axes[1].set_xlabel('x')\n", 506 | "axes[1].set_title('Noisy')\n", 507 | "\n", 508 | "im2 = axes[2].contourf(data.pivot(index='t_grid', columns='x_grid', values='inferred'), cmap='coolwarm', vmin=vmin, vmax=vmax)\n", 509 | "axes[2].set_xlabel('x')\n", 510 | "axes[2].set_title('Sampled')\n", 511 | "\n", 512 | "fig.colorbar(im1, ax=axes.ravel().tolist())\n", 513 | "\n", 514 | "plt.show()" 515 | ] 516 | }, 517 | { 518 | "cell_type": "code", 519 | "execution_count": null, 520 | "metadata": {}, 521 | "outputs": [], 522 | "source": [] 523 | } 524 | ], 525 | "metadata": { 526 | "kernelspec": { 527 | "display_name": "Python 3", 528 | "language": "python", 529 | "name": "python3" 530 | }, 531 | "language_info": { 532 | "codemirror_mode": { 533 | "name": "ipython", 534 | "version": 3 535 | }, 536 | "file_extension": ".py", 537 | "mimetype": "text/x-python", 538 | "name": "python", 539 | "nbconvert_exporter": "python", 540 | "pygments_lexer": "ipython3", 541 | "version": "3.6.8" 542 | } 543 | }, 544 | "nbformat": 4, 545 | "nbformat_minor": 2 546 | } 547 | -------------------------------------------------------------------------------- /Examples/data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/data/.DS_Store -------------------------------------------------------------------------------- /Examples/data/Advection_diffusion.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/data/Advection_diffusion.mat -------------------------------------------------------------------------------- /Examples/data/burgers.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/data/burgers.npy -------------------------------------------------------------------------------- /Examples/data/kdv.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/data/kdv.npy -------------------------------------------------------------------------------- /Examples/output/burgers/20190423_175345/iteration_0/events.out.tfevents.1556034829.C02QP0AZG8WL.local: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/output/burgers/20190423_175345/iteration_0/events.out.tfevents.1556034829.C02QP0AZG8WL.local -------------------------------------------------------------------------------- /Examples/output/burgers/20190423_175345/iteration_1/events.out.tfevents.1556035633.C02QP0AZG8WL.local: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/Examples/output/burgers/20190423_175345/iteration_1/events.out.tfevents.1556035633.C02QP0AZG8WL.local -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Gert-Jan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeePyMoD 2 | 3 | **This implementation of DeePyMoD is no longer maintained! We switched to a PyTorch based implementation: https://github.com/PhIMaL/DeePyMoD_torch** 4 | 5 | DeepMod is a deep learning based model discovery algorithm which seeks the partial differential equation underlying a spatio-temporal data set. DeepMoD employs sparse regression on a library of basis functions and their corresponding spatial derivatives. This code is based on the paper: [arXiv:1904.09406](http://arxiv.org/abs/1904.09406) 6 | 7 | A feed-forward neural network approximates the data set and automatic differentiation is used to construct this function library and perform regression within the neural network. This construction makes it extremely robust to noise and applicable to small data sets and, contrary to other deep learning methods, does not require a training set and is impervious to overfitting. We illustrate this approach on several physical problems, such as the Burgers', Korteweg-de Vries, advection-diffusion and Keller-Segel equations. 8 | 9 | # Examples 10 | In the notebook folder we present three examples: 11 | * Burgers' equation 12 | * Korteweg-de Vries equation 13 | * 2D Advection-Diffusion equation 14 | 15 | **The Burger's example contains detailed instructions on using DeepMoD.** More examples will be uploaded soon ... 16 | 17 | # How to install 18 | We suggest placing DeePyMoD in its own conda environment. Then, to install DeePyMoD simply clone the repository either by downloading the zip or cloning it through git, 19 | 20 | `git clone https://github.com/PhIMaL/DeePyMoD` 21 | 22 | and then go to the directory and run: 23 | 24 | `python setup.py install` 25 | 26 | We haven't included a requirements.txt, so you'll have to satisfy those yourself (Numpy, matplotlib and Tensorflow 1.12/1.13 should work). 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | AUTODOCDIR = api 10 | AUTODOCBUILD = sphinx-apidoc 11 | PROJECT = DeePyMoD 12 | MODULEDIR = ../src/deepymod 13 | 14 | # User-friendly check for sphinx-build 15 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1) 16 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 17 | endif 18 | 19 | # Internal variables. 20 | PAPEROPT_a4 = -D latex_paper_size=a4 21 | PAPEROPT_letter = -D latex_paper_size=letter 22 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 23 | # the i18n builder cannot share the environment and doctrees with the others 24 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 25 | 26 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext doc-requirements 27 | 28 | help: 29 | @echo "Please use \`make ' where is one of" 30 | @echo " html to make standalone HTML files" 31 | @echo " dirhtml to make HTML files named index.html in directories" 32 | @echo " singlehtml to make a single large HTML file" 33 | @echo " pickle to make pickle files" 34 | @echo " json to make JSON files" 35 | @echo " htmlhelp to make HTML files and a HTML help project" 36 | @echo " qthelp to make HTML files and a qthelp project" 37 | @echo " devhelp to make HTML files and a Devhelp project" 38 | @echo " epub to make an epub" 39 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 40 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 41 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 42 | @echo " text to make text files" 43 | @echo " man to make manual pages" 44 | @echo " texinfo to make Texinfo files" 45 | @echo " info to make Texinfo files and run them through makeinfo" 46 | @echo " gettext to make PO message catalogs" 47 | @echo " changes to make an overview of all changed/added/deprecated items" 48 | @echo " xml to make Docutils-native XML files" 49 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 50 | @echo " linkcheck to check all external links for integrity" 51 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 52 | 53 | clean: 54 | rm -rf $(BUILDDIR)/* $(AUTODOCDIR) 55 | 56 | $(AUTODOCDIR): $(MODULEDIR) 57 | mkdir -p $@ 58 | $(AUTODOCBUILD) -f -o $@ $^ 59 | 60 | doc-requirements: $(AUTODOCDIR) 61 | 62 | html: doc-requirements 63 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 66 | 67 | dirhtml: doc-requirements 68 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 69 | @echo 70 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 71 | 72 | singlehtml: doc-requirements 73 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 74 | @echo 75 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 76 | 77 | pickle: doc-requirements 78 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 79 | @echo 80 | @echo "Build finished; now you can process the pickle files." 81 | 82 | json: doc-requirements 83 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 84 | @echo 85 | @echo "Build finished; now you can process the JSON files." 86 | 87 | htmlhelp: doc-requirements 88 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 89 | @echo 90 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 91 | ".hhp project file in $(BUILDDIR)/htmlhelp." 92 | 93 | qthelp: doc-requirements 94 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 95 | @echo 96 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 97 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 98 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(PROJECT).qhcp" 99 | @echo "To view the help file:" 100 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/$(PROJECT).qhc" 101 | 102 | devhelp: doc-requirements 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $HOME/.local/share/devhelp/$(PROJECT)" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $HOME/.local/share/devhelp/$(PROJEC)" 109 | @echo "# devhelp" 110 | 111 | epub: doc-requirements 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | patch-latex: 117 | find _build/latex -iname "*.tex" | xargs -- \ 118 | sed -i'' 's~includegraphics{~includegraphics\[keepaspectratio,max size={\\textwidth}{\\textheight}\]{~g' 119 | 120 | latex: doc-requirements 121 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 122 | $(MAKE) patch-latex 123 | @echo 124 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 125 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 126 | "(use \`make latexpdf' here to do that automatically)." 127 | 128 | latexpdf: doc-requirements 129 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 130 | $(MAKE) patch-latex 131 | @echo "Running LaTeX files through pdflatex..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | latexpdfja: doc-requirements 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through platex and dvipdfmx..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | text: doc-requirements 142 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 143 | @echo 144 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 145 | 146 | man: doc-requirements 147 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 148 | @echo 149 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 150 | 151 | texinfo: doc-requirements 152 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 153 | @echo 154 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 155 | @echo "Run \`make' in that directory to run these through makeinfo" \ 156 | "(use \`make info' here to do that automatically)." 157 | 158 | info: doc-requirements 159 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 160 | @echo "Running Texinfo files through makeinfo..." 161 | make -C $(BUILDDIR)/texinfo info 162 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 163 | 164 | gettext: doc-requirements 165 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 166 | @echo 167 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 168 | 169 | changes: doc-requirements 170 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 171 | @echo 172 | @echo "The overview file is in $(BUILDDIR)/changes." 173 | 174 | linkcheck: doc-requirements 175 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 176 | @echo 177 | @echo "Link check complete; look for any errors in the above output " \ 178 | "or in $(BUILDDIR)/linkcheck/output.txt." 179 | 180 | doctest: doc-requirements 181 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 182 | @echo "Testing of doctests in the sources finished, look at the " \ 183 | "results in $(BUILDDIR)/doctest/output.txt." 184 | 185 | xml: doc-requirements 186 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 187 | @echo 188 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 189 | 190 | pseudoxml: doc-requirements 191 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 192 | @echo 193 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 194 | -------------------------------------------------------------------------------- /docs/_static/.gitignore: -------------------------------------------------------------------------------- 1 | # Empty directory 2 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. _authors: 2 | .. include:: ../AUTHORS.rst 3 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. _changes: 2 | .. include:: ../CHANGELOG.rst 3 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is execfile()d with the current directory set to its containing dir. 4 | # 5 | # Note that not all possible configuration values are present in this 6 | # autogenerated file. 7 | # 8 | # All configuration values have a default; values that are commented out 9 | # serve to show the default. 10 | 11 | import os 12 | import sys 13 | import inspect 14 | import shutil 15 | 16 | __location__ = os.path.join(os.getcwd(), os.path.dirname( 17 | inspect.getfile(inspect.currentframe()))) 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | sys.path.insert(0, os.path.join(__location__, '../src')) 23 | 24 | # -- Run sphinx-apidoc ------------------------------------------------------ 25 | # This hack is necessary since RTD does not issue `sphinx-apidoc` before running 26 | # `sphinx-build -b html . _build/html`. See Issue: 27 | # https://github.com/rtfd/readthedocs.org/issues/1139 28 | # DON'T FORGET: Check the box "Install your project inside a virtualenv using 29 | # setup.py install" in the RTD Advanced Settings. 30 | # Additionally it helps us to avoid running apidoc manually 31 | 32 | try: # for Sphinx >= 1.7 33 | from sphinx.ext import apidoc 34 | except ImportError: 35 | from sphinx import apidoc 36 | 37 | output_dir = os.path.join(__location__, "api") 38 | module_dir = os.path.join(__location__, "../src/deepymod") 39 | try: 40 | shutil.rmtree(output_dir) 41 | except FileNotFoundError: 42 | pass 43 | 44 | try: 45 | import sphinx 46 | from pkg_resources import parse_version 47 | 48 | cmd_line_template = "sphinx-apidoc -f -o {outputdir} {moduledir}" 49 | cmd_line = cmd_line_template.format(outputdir=output_dir, moduledir=module_dir) 50 | 51 | args = cmd_line.split(" ") 52 | if parse_version(sphinx.__version__) >= parse_version('1.7'): 53 | args = args[1:] 54 | 55 | apidoc.main(args) 56 | except Exception as e: 57 | print("Running `sphinx-apidoc` failed!\n{}".format(e)) 58 | 59 | # -- General configuration ----------------------------------------------------- 60 | 61 | # If your documentation needs a minimal Sphinx version, state it here. 62 | # needs_sphinx = '1.0' 63 | 64 | # Add any Sphinx extension module names here, as strings. They can be extensions 65 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 66 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 67 | 'sphinx.ext.autosummary', 'sphinx.ext.viewcode', 'sphinx.ext.coverage', 68 | 'sphinx.ext.doctest', 'sphinx.ext.ifconfig', 'sphinx.ext.mathjax', 69 | 'sphinx.ext.napoleon'] 70 | 71 | # Add any paths that contain templates here, relative to this directory. 72 | templates_path = ['_templates'] 73 | 74 | # The suffix of source filenames. 75 | source_suffix = '.rst' 76 | 77 | # The encoding of source files. 78 | # source_encoding = 'utf-8-sig' 79 | 80 | # The master toctree document. 81 | master_doc = 'index' 82 | 83 | # General information about the project. 84 | project = u'DeePyMoD' 85 | copyright = u'2019, Gert-Jan' 86 | 87 | # The version info for the project you're documenting, acts as replacement for 88 | # |version| and |release|, also used in various other places throughout the 89 | # built documents. 90 | # 91 | # The short X.Y version. 92 | version = '' # Is set by calling `setup.py docs` 93 | # The full version, including alpha/beta/rc tags. 94 | release = '' # Is set by calling `setup.py docs` 95 | 96 | # The language for content autogenerated by Sphinx. Refer to documentation 97 | # for a list of supported languages. 98 | # language = None 99 | 100 | # There are two options for replacing |today|: either, you set today to some 101 | # non-false value, then it is used: 102 | # today = '' 103 | # Else, today_fmt is used as the format for a strftime call. 104 | # today_fmt = '%B %d, %Y' 105 | 106 | # List of patterns, relative to source directory, that match files and 107 | # directories to ignore when looking for source files. 108 | exclude_patterns = ['_build'] 109 | 110 | # The reST default role (used for this markup: `text`) to use for all documents. 111 | # default_role = None 112 | 113 | # If true, '()' will be appended to :func: etc. cross-reference text. 114 | # add_function_parentheses = True 115 | 116 | # If true, the current module name will be prepended to all description 117 | # unit titles (such as .. function::). 118 | # add_module_names = True 119 | 120 | # If true, sectionauthor and moduleauthor directives will be shown in the 121 | # output. They are ignored by default. 122 | # show_authors = False 123 | 124 | # The name of the Pygments (syntax highlighting) style to use. 125 | pygments_style = 'sphinx' 126 | 127 | # A list of ignored prefixes for module index sorting. 128 | # modindex_common_prefix = [] 129 | 130 | # If true, keep warnings as "system message" paragraphs in the built documents. 131 | # keep_warnings = False 132 | 133 | 134 | # -- Options for HTML output --------------------------------------------------- 135 | 136 | # The theme to use for HTML and HTML Help pages. See the documentation for 137 | # a list of builtin themes. 138 | html_theme = 'alabaster' 139 | 140 | # Theme options are theme-specific and customize the look and feel of a theme 141 | # further. For a list of options available for each theme, see the 142 | # documentation. 143 | html_theme_options = { 144 | 'sidebar_width': '300px', 145 | 'page_width': '1200px' 146 | } 147 | 148 | # Add any paths that contain custom themes here, relative to this directory. 149 | # html_theme_path = [] 150 | 151 | # The name for this set of Sphinx documents. If None, it defaults to 152 | # " v documentation". 153 | try: 154 | from deepymod import __version__ as version 155 | except ImportError: 156 | pass 157 | else: 158 | release = version 159 | 160 | # A shorter title for the navigation bar. Default is the same as html_title. 161 | # html_short_title = None 162 | 163 | # The name of an image file (relative to this directory) to place at the top 164 | # of the sidebar. 165 | # html_logo = "" 166 | 167 | # The name of an image file (within the static path) to use as favicon of the 168 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 169 | # pixels large. 170 | # html_favicon = None 171 | 172 | # Add any paths that contain custom static files (such as style sheets) here, 173 | # relative to this directory. They are copied after the builtin static files, 174 | # so a file named "default.css" will overwrite the builtin "default.css". 175 | html_static_path = ['_static'] 176 | 177 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 178 | # using the given strftime format. 179 | # html_last_updated_fmt = '%b %d, %Y' 180 | 181 | # If true, SmartyPants will be used to convert quotes and dashes to 182 | # typographically correct entities. 183 | # html_use_smartypants = True 184 | 185 | # Custom sidebar templates, maps document names to template names. 186 | # html_sidebars = {} 187 | 188 | # Additional templates that should be rendered to pages, maps page names to 189 | # template names. 190 | # html_additional_pages = {} 191 | 192 | # If false, no module index is generated. 193 | # html_domain_indices = True 194 | 195 | # If false, no index is generated. 196 | # html_use_index = True 197 | 198 | # If true, the index is split into individual pages for each letter. 199 | # html_split_index = False 200 | 201 | # If true, links to the reST sources are added to the pages. 202 | # html_show_sourcelink = True 203 | 204 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 205 | # html_show_sphinx = True 206 | 207 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 208 | # html_show_copyright = True 209 | 210 | # If true, an OpenSearch description file will be output, and all pages will 211 | # contain a tag referring to it. The value of this option must be the 212 | # base URL from which the finished HTML is served. 213 | # html_use_opensearch = '' 214 | 215 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 216 | # html_file_suffix = None 217 | 218 | # Output file base name for HTML help builder. 219 | htmlhelp_basename = 'deepymod-doc' 220 | 221 | 222 | # -- Options for LaTeX output -------------------------------------------------- 223 | 224 | latex_elements = { 225 | # The paper size ('letterpaper' or 'a4paper'). 226 | # 'papersize': 'letterpaper', 227 | 228 | # The font size ('10pt', '11pt' or '12pt'). 229 | # 'pointsize': '10pt', 230 | 231 | # Additional stuff for the LaTeX preamble. 232 | # 'preamble': '', 233 | } 234 | 235 | # Grouping the document tree into LaTeX files. List of tuples 236 | # (source start file, target name, title, author, documentclass [howto/manual]). 237 | latex_documents = [ 238 | ('index', 'user_guide.tex', u'DeePyMoD Documentation', 239 | u'Gert-Jan', 'manual'), 240 | ] 241 | 242 | # The name of an image file (relative to this directory) to place at the top of 243 | # the title page. 244 | # latex_logo = "" 245 | 246 | # For "manual" documents, if this is true, then toplevel headings are parts, 247 | # not chapters. 248 | # latex_use_parts = False 249 | 250 | # If true, show page references after internal links. 251 | # latex_show_pagerefs = False 252 | 253 | # If true, show URL addresses after external links. 254 | # latex_show_urls = False 255 | 256 | # Documents to append as an appendix to all manuals. 257 | # latex_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | # latex_domain_indices = True 261 | 262 | # -- External mapping ------------------------------------------------------------ 263 | python_version = '.'.join(map(str, sys.version_info[0:2])) 264 | intersphinx_mapping = { 265 | 'sphinx': ('http://www.sphinx-doc.org/en/stable', None), 266 | 'python': ('https://docs.python.org/' + python_version, None), 267 | 'matplotlib': ('https://matplotlib.org', None), 268 | 'numpy': ('https://docs.scipy.org/doc/numpy', None), 269 | 'sklearn': ('http://scikit-learn.org/stable', None), 270 | 'pandas': ('http://pandas.pydata.org/pandas-docs/stable', None), 271 | 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), 272 | } 273 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | DeePyMoD 3 | ======== 4 | 5 | This is the documentation of **DeePyMoD**. 6 | 7 | .. note:: 8 | 9 | This is the main page of your project's `Sphinx`_ documentation. 10 | It is formatted in `reStructuredText`_. Add additional pages 11 | by creating rst-files in ``docs`` and adding them to the `toctree`_ below. 12 | Use then `references`_ in order to link them from this page, e.g. 13 | :ref:`authors` and :ref:`changes`. 14 | 15 | It is also possible to refer to the documentation of other Python packages 16 | with the `Python domain syntax`_. By default you can reference the 17 | documentation of `Sphinx`_, `Python`_, `NumPy`_, `SciPy`_, `matplotlib`_, 18 | `Pandas`_, `Scikit-Learn`_. You can add more by extending the 19 | ``intersphinx_mapping`` in your Sphinx's ``conf.py``. 20 | 21 | The pretty useful extension `autodoc`_ is activated by default and lets 22 | you include documentation from docstrings. Docstrings can be written in 23 | `Google style`_ (recommended!), `NumPy style`_ and `classical style`_. 24 | 25 | 26 | Contents 27 | ======== 28 | 29 | .. toctree:: 30 | :maxdepth: 2 31 | 32 | License 33 | Authors 34 | Changelog 35 | Module Reference 36 | 37 | 38 | Indices and tables 39 | ================== 40 | 41 | * :ref:`genindex` 42 | * :ref:`modindex` 43 | * :ref:`search` 44 | 45 | .. _toctree: http://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html 46 | .. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html 47 | .. _references: http://www.sphinx-doc.org/en/stable/markup/inline.html 48 | .. _Python domain syntax: http://sphinx-doc.org/domains.html#the-python-domain 49 | .. _Sphinx: http://www.sphinx-doc.org/ 50 | .. _Python: http://docs.python.org/ 51 | .. _Numpy: http://docs.scipy.org/doc/numpy 52 | .. _SciPy: http://docs.scipy.org/doc/scipy/reference/ 53 | .. _matplotlib: https://matplotlib.org/contents.html# 54 | .. _Pandas: http://pandas.pydata.org/pandas-docs/stable 55 | .. _Scikit-Learn: http://scikit-learn.org/stable 56 | .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html 57 | .. _Google style: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings 58 | .. _NumPy style: https://numpydoc.readthedocs.io/en/latest/format.html 59 | .. _classical style: http://www.sphinx-doc.org/en/stable/domains.html#info-field-lists 60 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | .. _license: 2 | 3 | ======= 4 | License 5 | ======= 6 | 7 | .. include:: ../LICENSE.txt 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # This file is used to configure your project. 2 | # Read more about the various options under: 3 | # http://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files 4 | 5 | [metadata] 6 | name = DeePyMoD 7 | description = Add a short description here! 8 | author = Gert-Jan 9 | author-email = gert-jan.both@cri-paris.com 10 | license = mit 11 | url = https://pyscaffold.org/ 12 | long-description = file: README.rst 13 | # Change if running only on Windows, Mac or Linux (comma-separated) 14 | platforms = any 15 | # Add here all kinds of additional classifiers as defined under 16 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 17 | classifiers = 18 | Development Status :: 4 - Beta 19 | Programming Language :: Python 20 | 21 | [options] 22 | zip_safe = False 23 | packages = find: 24 | include_package_data = True 25 | package_dir = 26 | =src 27 | # DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD! 28 | setup_requires = pyscaffold>=3.1a0,<3.2a0 29 | # Add here dependencies of your project (semicolon/line-separated), e.g. 30 | # install_requires = numpy; scipy 31 | # The usage of test_requires is discouraged, see `Dependency Management` docs 32 | # tests_require = pytest; pytest-cov 33 | # Require a specific Python version, e.g. Python 2.7 or >= 3.4 34 | # python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* 35 | install_requires tensorflow=1.12.*; numpy 36 | python_requires = >= 3.4.* 37 | 38 | [options.packages.find] 39 | where = src 40 | exclude = 41 | tests 42 | 43 | [options.extras_require] 44 | # Add here additional requirements for extra features, to install with: 45 | # `pip install DeePyMoD[PDF]` like: 46 | # PDF = ReportLab; RXP 47 | # Add here test requirements (semicolon/line-separated) 48 | testing = 49 | pytest 50 | pytest-cov 51 | 52 | [options.entry_points] 53 | # Add here console scripts like: 54 | # console_scripts = 55 | # script_name = deepymod.module:function 56 | # For example: 57 | # console_scripts = 58 | # fibonacci = deepymod.skeleton:run 59 | # And any other entry points, for example: 60 | # pyscaffold.cli = 61 | # awesome = pyscaffoldext.awesome.extension:AwesomeExtension 62 | 63 | [test] 64 | # py.test options when running `python setup.py test` 65 | # addopts = --verbose 66 | extras = True 67 | 68 | [tool:pytest] 69 | # Options for py.test: 70 | # Specify command line options as you would do when invoking py.test directly. 71 | # e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml 72 | # in order to write a coverage file that can be read by Jenkins. 73 | addopts = 74 | --cov deepymod --cov-report term-missing 75 | --verbose 76 | norecursedirs = 77 | dist 78 | build 79 | .tox 80 | testpaths = tests 81 | 82 | [aliases] 83 | build = bdist_wheel 84 | release = build upload 85 | 86 | [bdist_wheel] 87 | # Use this option if your package is pure-python 88 | universal = 1 89 | 90 | [build_sphinx] 91 | source_dir = docs 92 | build_dir = docs/_build 93 | 94 | [devpi:upload] 95 | # Options for the devpi: PyPI server and packaging tool 96 | # VCS export must be deactivated since we are using setuptools-scm 97 | no-vcs = 1 98 | formats = bdist_wheel 99 | 100 | [flake8] 101 | # Some sane defaults for the code style checker flake8 102 | exclude = 103 | .tox 104 | build 105 | dist 106 | .eggs 107 | docs/conf.py 108 | 109 | [pyscaffold] 110 | # PyScaffold's parameters when the project was created. 111 | # This will be used when updating. Do not change! 112 | version = 3.1 113 | package = deepymod 114 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Setup file for deepymod. 5 | Use setup.cfg to configure your project. 6 | 7 | This file was generated with PyScaffold 3.1. 8 | PyScaffold helps you to put up the scaffold of your new Python project. 9 | Learn more under: https://pyscaffold.org/ 10 | """ 11 | import sys 12 | 13 | from pkg_resources import require, VersionConflict 14 | from setuptools import setup 15 | 16 | try: 17 | require('setuptools>=38.3') 18 | except VersionConflict: 19 | print("Error: version of setuptools is too old (<38.3)!") 20 | sys.exit(1) 21 | 22 | 23 | if __name__ == "__main__": 24 | setup(use_pyscaffold=True) 25 | -------------------------------------------------------------------------------- /src/deepymod/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhIMaL/DeePyMoD_tensorflow/8f89e0f499d9a2219b2c0d9bbf3157ab87b58b54/src/deepymod/.DS_Store -------------------------------------------------------------------------------- /src/deepymod/DeepMoD.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import copy 3 | import os 4 | from datetime import datetime 5 | 6 | from deepymod.PINN import PINN, map_to_sparse_vector, inference 7 | 8 | def DeepMoD(data, target, config, library_function, library_config, train_opts, output_opts): 9 | # Defining internal configuration 10 | internal_config = copy.deepcopy(config) 11 | 12 | # Defining initial weights, biases and coefficients for the network 13 | initial_coeffs = [np.random.rand(library_config['total_terms'], 1) * 2 - 1 for output_neuron in np.arange(config['layers'][-1])] 14 | initial_biases = [np.zeros(neurons) for neurons in config['layers'][1:]] 15 | initial_weights = [np.random.randn(input_neurons, output_neurons) * np.sqrt(1 / (input_neurons + output_neurons)) for input_neurons, output_neurons in zip(config['layers'][:-1], config['layers'][1:])] # Xavier initalization 16 | 17 | 18 | internal_config.update({'initial_coeffs': initial_coeffs, 'initial_weights': initial_weights, 'initial_biases': initial_biases}) 19 | 20 | output_opts['output_directory'] = os.path.join(output_opts['output_directory'], datetime.now().strftime("%Y%m%d_%H%M%S")) #making folder with timestamp 21 | 22 | # Run minimization procedure 23 | mask = np.ones((library_config['total_terms'], config['layers'][-1])) 24 | output_opts.update({'cycles': 0}) 25 | 26 | coeff_list, coeff_scaled_list, weights, biases = PINN(data, target, mask, internal_config, library_function, library_config, train_opts, output_opts) 27 | sparsity_pattern_list = [thresholding(coeff, mode='auto') for coeff in coeff_scaled_list] 28 | 29 | output_opts['cycles'] += 1 30 | 31 | # Updating everything else for next cycle 32 | mask[~np.transpose(np.squeeze(np.array(sparsity_pattern_list)))] = 0 33 | coeff_list_thresholded = [np.expand_dims(coeff[sparsity_pattern], axis=1) for coeff, sparsity_pattern in zip(coeff_list, sparsity_pattern_list)] 34 | internal_config.update({'initial_coeffs': coeff_list_thresholded, 'initial_weights': weights, 'initial_biases': biases}) 35 | 36 | # Printing current sparse vector to see progress 37 | print('Current sparse vectors:') 38 | print([map_to_sparse_vector(sparsity_pattern, coeff) for sparsity_pattern, coeff in zip(sparsity_pattern_list, coeff_list_thresholded)]) 39 | 40 | # Now thats it's converged, fit again but without the L1 penalty 41 | print('Now running for the final time...') 42 | internal_config['lambda'] = 0 43 | coeff_list, _, weights, biases = PINN(data, target, mask, internal_config, library_function, library_config, train_opts, output_opts) 44 | 45 | if 'X_predict' in output_opts.keys(): 46 | prediction = inference(output_opts['X_predict'], weights, biases, internal_config['layers']) 47 | return coeff_list, prediction 48 | else: 49 | return coeff_list 50 | 51 | 52 | def thresholding(vector, mode, treshold=0.0): 53 | if mode == 'auto': 54 | upper_lim, lower_lim = np.median(vector)+np.std(vector), np.median(vector) - np.std(vector) 55 | sparsity_mask = (vector <= upper_lim) & (vector >= lower_lim) 56 | else: 57 | sparsity_mask = np.abs(vector) < treshold 58 | 59 | return ~sparsity_mask 60 | -------------------------------------------------------------------------------- /src/deepymod/PINN.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | import os 4 | 5 | from deepymod.graphs import PINN_graph, inference_graph 6 | from deepymod.tb_setup import tb_setup 7 | 8 | def PINN(data, target, mask, config, library_function, library_config, train_opts, output_opts): 9 | # Defining graph, optimizer and feed_dict 10 | graph = PINN_graph(config, library_function, library_config) 11 | 12 | train_op = tf.train.AdamOptimizer(learning_rate=train_opts['learning_rate'], beta1=train_opts['beta1'], beta2=train_opts['beta2'], epsilon=train_opts['epsilon']).minimize(graph.loss) 13 | 14 | feed_dict = {graph.data_feed: data, graph.target_feed: target, graph.mask_feed: mask} 15 | 16 | # Running the fitting procedure 17 | with tf.Session() as sess: 18 | sess.run(tf.global_variables_initializer(), feed_dict=feed_dict) 19 | sess.run(graph.iterator.initializer, feed_dict=feed_dict) 20 | 21 | writer = tf.summary.FileWriter(os.path.join(output_opts['output_directory'], "iteration_" + str(output_opts['cycles']))) 22 | writer.add_graph(sess.graph) 23 | merged_summary, custom_board = tb_setup(graph, output_opts) 24 | writer.add_summary(custom_board) 25 | 26 | print('Epoch | Total loss | Loss gradient | MSE | PI | L1 ') 27 | for iteration in np.arange(train_opts['max_iterations']): 28 | sess.run(train_op) 29 | if iteration % 50 == 0: 30 | summary = sess.run(merged_summary) 31 | writer.add_summary(summary, iteration) 32 | if iteration % 500 == 0: 33 | print(iteration, sess.run([graph.loss, graph.gradloss, graph.cost_MSE, graph.cost_PI, graph.cost_L1])) 34 | if sess.run(graph.gradloss) < train_opts['grad_tol']: 35 | print('Optimizer converged.') 36 | break 37 | 38 | 39 | coeff_list = [map_to_sparse_vector(coeff_mask, coeff) for coeff_mask, coeff in zip(np.split(mask, mask.shape[1], axis=1), sess.run(graph.coeff_list))] 40 | coeff_scaled_list = [map_to_sparse_vector(coeff_mask, coeff) for coeff_mask, coeff in zip(np.split(mask, mask.shape[1], axis=1), sess.run(graph.coeff_scaled_list))] 41 | 42 | weights_biases = sess.run(tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES))[len(coeff_list):] 43 | weights = weights_biases[::2] 44 | biases = weights_biases[1::2] 45 | 46 | return coeff_list, coeff_scaled_list, weights, biases 47 | 48 | def inference(data, weights, biases, layers, batchsize=1000): 49 | graph = inference_graph(data, weights, biases, layers, batchsize=batchsize) 50 | 51 | with tf.Session() as sess: 52 | sess.run(tf.global_variables_initializer()) 53 | 54 | prediction = [sess.run(graph.prediction) for batch in np.arange(np.ceil(data.shape[0]/batchsize))] 55 | prediction = np.concatenate(prediction, axis=0) 56 | 57 | return prediction 58 | 59 | 60 | def map_to_sparse_vector(mask, coeff): 61 | sparse_vec = np.zeros_like(mask, dtype=np.float) 62 | sparse_vec[np.where(mask)[0]] = coeff 63 | 64 | return sparse_vec 65 | -------------------------------------------------------------------------------- /src/deepymod/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pkg_resources import get_distribution, DistributionNotFound 3 | 4 | try: 5 | # Change here if project is renamed and does not equal the package name 6 | dist_name = 'DeePyMoD' 7 | __version__ = get_distribution(dist_name).version 8 | except DistributionNotFound: 9 | __version__ = 'unknown' 10 | finally: 11 | del get_distribution, DistributionNotFound 12 | -------------------------------------------------------------------------------- /src/deepymod/graphs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | 4 | 5 | def PINN_graph(config, library_function, library_config): 6 | tf.reset_default_graph() 7 | # Creating datasets 8 | with tf.name_scope("Defining_variables"): 9 | data_feed = tf.placeholder(tf.float32, shape=[None, config['layers'][0]]) 10 | target_feed = tf.placeholder(tf.float32, shape=[None, config['layers'][-1]]) 11 | mask_feed = tf.placeholder(tf.int32, shape=[None, config['layers'][-1]]) 12 | 13 | lambda_L1 = tf.constant(config['lambda'], tf.float32) 14 | 15 | coeff_list = [tf.Variable(config['initial_coeffs'][output_neuron], dtype=tf.float32) for output_neuron in np.arange(config['layers'][-1])] 16 | 17 | with tf.name_scope("Data_pipeline"): 18 | mask = tf.ones([tf.size(target_feed[:, 0:1]), tf.shape(mask_feed)[0], tf.shape(mask_feed)[1]], dtype=tf.int32) * tf.expand_dims(mask_feed, axis=0) 19 | 20 | dataset = tf.data.Dataset.from_tensor_slices((data_feed, target_feed, mask)).repeat().batch(tf.shape(data_feed, out_type=tf.int64)[0]) 21 | 22 | iterator = dataset.make_initializable_iterator() 23 | data, target, sparsity_mask = iterator.get_next() 24 | 25 | # The actual network 26 | with tf.name_scope("Neural_Network"): 27 | X = data 28 | for layer in np.arange(len(config['layers'])-2): 29 | X = tf.layers.dense(X, units=config['layers'][layer+1], activation=tf.nn.tanh, kernel_initializer=tf.constant_initializer(config['initial_weights'][layer]), bias_initializer=tf.constant_initializer(config['initial_biases'][layer])) 30 | prediction = tf.layers.dense(inputs=X, units=config['layers'][-1], activation=None, kernel_initializer=tf.constant_initializer(config['initial_weights'][-1]), bias_initializer=tf.constant_initializer(config['initial_biases'][-1])) 31 | 32 | # make library according to supplied function 33 | with tf.name_scope("Creating_library"): 34 | time_deriv_list, theta = library_function(data, prediction, library_config) 35 | theta_split = [tf.dynamic_partition(theta, coeff_mask, 2)[1] for coeff_mask in tf.unstack(sparsity_mask, axis=2, num=len(coeff_list))] 36 | sparse_thetas_list = [tf.reshape(sparse_theta, [tf.shape(theta)[0], tf.size(coeff)]) for coeff, sparse_theta in zip(coeff_list, theta_split)] 37 | 38 | # Normalizing 39 | with tf.name_scope("Scaling"): 40 | scaling_time = [tf.norm(time_deriv, axis=0) for time_deriv in time_deriv_list] 41 | scaling_theta = [tf.expand_dims(tf.norm(sparse_theta, axis=0), axis=1) for sparse_theta in sparse_thetas_list] 42 | coeff_scaled_list = [coeff * (theta_scale / time_scale) for coeff, theta_scale, time_scale in zip(coeff_list, scaling_theta, scaling_time)] 43 | 44 | # Defining cost function 45 | with tf.name_scope("Cost_MSE"): 46 | MSE_costs = tf.reduce_mean(tf.square(target - prediction), axis=0) 47 | cost_MSE = tf.reduce_mean(MSE_costs) 48 | 49 | with tf.name_scope("Cost_PI"): 50 | PI_costs = [tf.reduce_mean(tf.square(tf.matmul(sparse_theta, coeff) - time_deriv)) for sparse_theta, coeff, time_deriv in zip(sparse_thetas_list, coeff_list, time_deriv_list)] 51 | cost_PI = tf.reduce_sum(PI_costs) 52 | 53 | with tf.name_scope('Cost_L1'): 54 | L1_costs = [lambda_L1 * tf.reduce_sum(tf.abs(coeff[1:, :])) for coeff in coeff_scaled_list] 55 | cost_L1 = tf.reduce_sum(L1_costs) 56 | 57 | with tf.name_scope("Total_cost"): 58 | loss = cost_MSE + cost_PI + cost_L1 59 | 60 | # graph node for gradient 61 | with tf.name_scope("GradLoss"): 62 | grad_losses = [tf.reduce_max(tf.abs(tf.gradients(loss, coeff)[0]) / (theta_scale / time_scale)) for coeff, theta_scale, time_scale in zip(coeff_list, scaling_theta, scaling_time)] 63 | gradloss = tf.reduce_max(grad_losses) 64 | 65 | return AttrDict(locals()) 66 | 67 | def inference_graph(data, weights, biases, layers, batchsize=1000): 68 | dataset = tf.data.Dataset.from_tensor_slices(data).batch(batchsize) 69 | iterator = dataset.make_one_shot_iterator() 70 | data = iterator.get_next() 71 | 72 | X = data 73 | for layer in np.arange(len(layers)-2): 74 | X = tf.layers.dense(X, units=layers[layer+1], activation=tf.nn.tanh, kernel_initializer=tf.constant_initializer(weights[layer]), bias_initializer=tf.constant_initializer(biases[layer])) 75 | prediction = tf.layers.dense(inputs=X, units=layers[-1], activation=None, kernel_initializer=tf.constant_initializer(weights[-1]), bias_initializer=tf.constant_initializer(biases[-1])) 76 | 77 | return AttrDict(locals()) 78 | 79 | class AttrDict(dict): 80 | __getattr__ = dict.__getitem__ 81 | __setattr__ = dict.__setitem__ 82 | 83 | -------------------------------------------------------------------------------- /src/deepymod/library_functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | 4 | ''' 5 | This file contains several ready-to-use library functions. 6 | ''' 7 | 8 | def library_1D(data, prediction, library_config): 9 | ''' 10 | Constructs a library graph in 1D. Library config is dictionary with required terms. 11 | ''' 12 | 13 | # Polynomial 14 | u = tf.ones_like(prediction) 15 | 16 | for order in np.arange(1, library_config['poly_order']+1): 17 | u = tf.concat((u, u[:, order-1:order]*prediction), axis=1) 18 | u = tf.expand_dims(u, axis=2) 19 | 20 | # Gradients 21 | dy = tf.gradients(prediction, data)[0] 22 | y_t = dy[:, 1:2] 23 | y_x = dy[:, 0:1] 24 | 25 | du = tf.concat((tf.ones_like(y_x), y_x), axis=1) 26 | for order in np.arange(2, library_config['deriv_order']+1): 27 | du = tf.concat((du, tf.gradients(du[:, order-1], data)[0][:, 0:1]), axis=1) 28 | du = tf.expand_dims(du, axis=1) 29 | 30 | # Bringing it together 31 | theta = tf.matmul(u, du) 32 | theta = tf.reshape(theta, [tf.shape(theta)[0], tf.size(theta[0, :, :])]) 33 | 34 | return [y_t], theta 35 | 36 | def library_2Din_1Dout(data, prediction, library_config): 37 | ''' 38 | Constructs a library graph in 1D. Library config is dictionary with required terms. 39 | ''' 40 | 41 | # Polynomial 42 | u = tf.ones_like(prediction) 43 | 44 | for order in np.arange(1, library_config['poly_order']+1): 45 | u = tf.concat((u, u[:, order-1:order]*prediction), axis=1) 46 | u = tf.expand_dims(u, axis=2) 47 | 48 | # Gradients 49 | du = tf.gradients(prediction, data)[0] 50 | u_t = du[:, 0:1] 51 | u_x = du[:, 1:2] 52 | u_y = du[:, 2:3] 53 | du2 = tf.gradients(u_x,data)[0] 54 | u_xx = du2[:, 1:2] 55 | u_xy = du2[:, 2:3] 56 | u_yy = tf.gradients(u_y,data)[0][:, 2:3] 57 | du = tf.concat((tf.ones_like(u_x), u_x, u_y , u_xx, u_yy, u_xy), axis=1) 58 | #for order in np.arange(2, library_config['deriv_order']+1): 59 | # du = tf.concat((du, tf.gradients(du[:, order-1], data)[0][:, 0:1]), axis=1) 60 | du = tf.expand_dims(du, axis=1) 61 | 62 | # Bringing it together 63 | theta = tf.matmul(u, du) 64 | theta = tf.reshape(theta, [tf.shape(theta)[0], tf.size(theta[0, :, :])]) 65 | 66 | return [u_t], theta 67 | 68 | 69 | def library_2Din_2Dout(data, prediction, library_config): 70 | 71 | #Polynomial in u 72 | u = tf.ones_like(prediction[:, 0:1]) 73 | 74 | for order in np.arange(1, library_config['poly_order']+1): 75 | u = tf.concat((u, u[:, order-1:order]*prediction[:, 0:1]), axis=1) 76 | u = tf.expand_dims(u, axis=2) 77 | 78 | print(u.shape) 79 | 80 | # Polynomial in v 81 | v = tf.ones_like(prediction[:, 1:2]) 82 | 83 | for order in np.arange(1, library_config['poly_order']+1): 84 | v = tf.concat((v, v[:, order-1:order]*prediction[:, 1:2]), axis=1) 85 | v = tf.expand_dims(v, axis=1) 86 | 87 | print(v.shape) 88 | # Calculating all cross terms 89 | uv = tf.matmul(u, v) 90 | uv = tf.reshape(uv, [tf.shape(u)[0], tf.size(uv[0, :, :])]) 91 | uv = tf.expand_dims(uv,axis=2) 92 | 93 | 94 | print(uv.shape) 95 | # Derivative in u 96 | du = tf.gradients(prediction[:, 0:1], data)[0] 97 | u_t = du[:, 0:1] 98 | u_x = du[:, 1:2] 99 | u_y = du[:, 2:3] 100 | du2 = tf.gradients(u_x, data)[0] 101 | u_xx = du2[:, 1:2] 102 | u_xy = du2[:, 2:3] 103 | u_yy = tf.gradients(u_y, data)[0][:, 2:3] 104 | du = tf.concat((u_x, u_y , u_xx, u_yy, u_xy), axis=1) 105 | print(du.shape) 106 | # Derivative in v 107 | dv = tf.gradients(prediction[:, 1:2], data)[0] 108 | v_t = dv[:, 0:1] 109 | v_x = dv[:, 1:2] 110 | v_y = dv[:, 2:3] 111 | dv2 = tf.gradients(v_x, data)[0] 112 | v_xx = dv2[:, 1:2] 113 | v_xy = dv2[:, 2:3] 114 | v_yy = tf.gradients(v_y,data)[0][:, 2:3] 115 | dv = tf.concat((v_x, v_y , v_xx, v_yy, v_xy), axis=1) 116 | print(dv.shape) 117 | # Bringing du and dv together and calculating cross terms 118 | dudv = tf.concat((tf.ones_like(v_x), du, dv), axis=1) 119 | dudv = tf.expand_dims(dudv, axis=1) 120 | theta = tf.matmul(uv, dudv) 121 | theta = tf.reshape(theta, [tf.shape(theta)[0], tf.size(theta[0, :, :])]) 122 | 123 | time_deriv = [u_t, v_t] 124 | 125 | return time_deriv, theta 126 | 127 | def library_2Din_2Dout_lim(data, prediction, library_config): 128 | 129 | #Polynomial in u 130 | u = tf.ones_like(prediction[:, 0:1]) 131 | 132 | for order in np.arange(1, library_config['poly_order']+1): 133 | u = tf.concat((u, u[:, order-1:order]*prediction[:, 0:1]), axis=1) 134 | u = tf.expand_dims(u, axis=2) 135 | 136 | 137 | # Polynomial in v 138 | v = tf.ones_like(prediction[:, 1:2]) 139 | 140 | for order in np.arange(1, library_config['poly_order']+1): 141 | v = tf.concat((v, v[:, order-1:order]*prediction[:, 1:2]), axis=1) 142 | v = tf.expand_dims(v, axis=1) 143 | 144 | 145 | # Calculating all cross terms 146 | uv = tf.matmul(u, v) 147 | uv = tf.reshape(uv, [tf.shape(u)[0], tf.size(uv[0, :, :])]) 148 | uv = tf.expand_dims(uv,axis=2) 149 | 150 | # Derivative in u 151 | du = tf.gradients(prediction[:, 0:1], data)[0] 152 | u_t = du[:, 0:1] 153 | u_x = du[:, 1:2] 154 | u_y = du[:, 2:3] 155 | du2 = tf.gradients(u_x, data)[0] 156 | u_xx = du2[:, 1:2] 157 | u_yy = tf.gradients(u_y, data)[0][:, 2:3] 158 | du = tf.concat((u_xx, u_yy), axis=1) 159 | 160 | # Derivative in v 161 | dv = tf.gradients(prediction[:, 1:2], data)[0] 162 | v_t = dv[:, 0:1] 163 | v_x = dv[:, 1:2] 164 | v_y = dv[:, 2:3] 165 | dv2 = tf.gradients(v_x, data)[0] 166 | v_xx = dv2[:, 1:2] 167 | v_yy = tf.gradients(v_y,data)[0][:, 2:3] 168 | dv = tf.concat((v_xx, v_yy), axis=1) 169 | 170 | # Bringing du and dv together and calculating cross terms 171 | dudv = tf.concat((tf.ones_like(v_x), du, dv), axis=1) 172 | dudv = tf.expand_dims(dudv, axis=1) 173 | theta = tf.matmul(uv, dudv) 174 | theta = tf.reshape(theta, [tf.shape(theta)[0], tf.size(theta[0, :, :])]) 175 | 176 | time_deriv = [u_t, v_t] 177 | 178 | return time_deriv, theta 179 | 180 | def library_1Din_2Dout_chemo(data, prediction, library_config): 181 | #Polynomial in u 182 | 183 | u = tf.ones_like(prediction[:, 0:1]) 184 | for order in np.arange(1, library_config['poly_order']+1): 185 | u = tf.concat((u, u[:, order-1:order]*prediction[:, 0:1]), axis=1) 186 | u = tf.expand_dims(u, axis=2) 187 | 188 | print("u",u.shape) 189 | 190 | # Polynomial in v 191 | 192 | v = tf.ones_like(prediction[:, 1:2]) 193 | for order in np.arange(1, library_config['poly_order']+1): 194 | v = tf.concat((v, v[:, order-1:order]*prediction[:, 1:2]), axis=1) 195 | v = tf.expand_dims(v, axis=1) 196 | 197 | print("v",v.shape) 198 | 199 | # Derivative in u 200 | du = tf.gradients(prediction[:, 0:1], data)[0] 201 | u_t = du[:, 1:2] 202 | u_x = du[:, 0:1] 203 | 204 | du2 = tf.gradients(u_x, data)[0] 205 | u_xx = du2[:, 0:1] 206 | 207 | du = tf.concat((u_x,u_xx),axis=1) 208 | print("du",du.shape) 209 | 210 | # Derivative in v 211 | dv = tf.gradients(prediction[:, 1:2], data)[0] 212 | v_t = dv[:, 1:2] 213 | v_x = dv[:, 0:1] 214 | 215 | dv2 = tf.gradients(v_x, data)[0] 216 | v_xx = dv2[:, 0:1] 217 | 218 | dv=tf.concat((v_x, v_xx),axis=1) 219 | print("dv",dv.shape) 220 | 221 | #Calculating all cross derivative terms 222 | Du = du 223 | Dv = dv 224 | 225 | Du = tf.expand_dims(Du, axis=2) 226 | Dv = tf.expand_dims(Dv, axis=1) 227 | 228 | Ddudv = tf.matmul(Du,Dv) 229 | Ddudv = tf.reshape(Ddudv, [tf.shape(Ddudv)[0], tf.size(Ddudv[0, :, :])]) 230 | 231 | # Calculating all cross terms 232 | 233 | uv = tf.matmul(u, v) 234 | uv = tf.reshape(uv, [tf.shape(u)[0], tf.size(uv[0, :, :])]) 235 | uv = tf.expand_dims(uv,axis=2) 236 | 237 | print("uv",uv.shape) 238 | print("Ddudv",Ddudv.shape) 239 | 240 | # Bringing du and dv together and calculating cross terms 241 | dudv = tf.concat((tf.ones_like(v_x), du, dv, Ddudv), axis=1) 242 | 243 | print("dudv",dudv.shape) 244 | 245 | dudv = tf.expand_dims(dudv, axis=1) 246 | theta = tf.matmul(uv, dudv) 247 | theta = tf.reshape(theta, [tf.shape(theta)[0], tf.size(theta[0, :, :])]) 248 | 249 | time_deriv = [u_t, v_t] 250 | 251 | return time_deriv, theta 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /src/deepymod/tb_setup.py: -------------------------------------------------------------------------------- 1 | from tensorboard.summary import custom_scalar_pb 2 | from tensorboard.plugins.custom_scalar import layout_pb2 3 | import tensorflow as tf 4 | import numpy as np 5 | 6 | def tb_setup(graph, output_opts): 7 | # Cost summaries 8 | [tf.summary.scalar('MSE_cost_'+str(idx), cost) for idx, cost in enumerate(tf.unstack(graph.MSE_costs))] 9 | [tf.summary.scalar('PI_cost_'+str(idx), cost) for idx, cost in enumerate(graph.PI_costs)] 10 | [tf.summary.scalar('L1_cost_'+str(idx), cost) for idx, cost in enumerate(graph.L1_costs)] 11 | tf.summary.scalar('Total_cost', graph.loss) 12 | tf.summary.scalar('Loss_Grad', graph.gradloss) 13 | 14 | # Coefficient summaries 15 | # [[tf.summary.scalar('Coeff_' + str(idx_coeff) + '_Comp_' + str(idx_comp), tf.squeeze(comp)) for idx_comp, comp in enumerate(tf.unstack(coeff, axis=0))] for idx_coeff, coeff in enumerate(tf.unstack(graph.coeff_list))] 16 | [[tf.summary.scalar('Coeff_' + str(idx_coeff) + '_Comp_' + str(idx_comp), tf.squeeze(comp)) for idx_comp, comp in enumerate(tf.unstack(coeff, axis=0))] for idx_coeff, coeff in enumerate(graph.coeff_list)] 17 | [[tf.summary.scalar('Scaled_Coeff_' + str(idx_coeff) + '_Comp_' + str(idx_comp), tf.squeeze(comp)) for idx_comp, comp in enumerate(tf.unstack(coeff, axis=0))] for idx_coeff, coeff in enumerate(graph.coeff_scaled_list)] 18 | 19 | # Merging everything and making custom board 20 | merged_summary = tf.summary.merge_all() 21 | custom_board = custom_board_generator(graph) 22 | 23 | return merged_summary, custom_board 24 | 25 | def custom_board_generator(graph): 26 | # We make the coefficient and scaled coefficient charts first because we need to do it dynamically. 27 | coeff_chart = [layout_pb2.Chart(title='Coeff_' + str(idx), multiline=layout_pb2.MultilineChartContent(tag=[r'Coeff_' + str(idx) + '_Comp_*'])) for idx in np.arange(len(graph.PI_costs))] 28 | coeff_scaled_chart = [layout_pb2.Chart(title='Scaled_Coeff_' + str(idx), multiline=layout_pb2.MultilineChartContent(tag=[r'Scaled_Coeff_' + str(idx) + '_Comp_*'])) for idx in np.arange(len(graph.PI_costs))] 29 | 30 | # Actually making the board 31 | custom_board = custom_scalar_pb( 32 | layout_pb2.Layout(category=[ 33 | layout_pb2.Category(title='Training', 34 | chart=[layout_pb2.Chart(title='MSE_Losses', multiline=layout_pb2.MultilineChartContent(tag=[r'MSE_cost_*'])), 35 | layout_pb2.Chart(title='PI_Losses', multiline=layout_pb2.MultilineChartContent(tag=[r'PI_cost_*'])), 36 | layout_pb2.Chart(title='L1_Losses', multiline=layout_pb2.MultilineChartContent(tag=[r'L1_cost_*'])), 37 | layout_pb2.Chart(title='Total_cost', multiline=layout_pb2.MultilineChartContent(tag=['Total_cost'])), 38 | layout_pb2.Chart(title='Gradloss', multiline=layout_pb2.MultilineChartContent(tag=['Loss_Grad']))\ 39 | ] 40 | ), 41 | layout_pb2.Category(title='Coefficients', chart=coeff_chart), 42 | layout_pb2.Category(title='Scaled_Coefficients', chart=coeff_scaled_chart) 43 | ])) 44 | 45 | return custom_board 46 | -------------------------------------------------------------------------------- /src/deepymod/utilities.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import tensorflow as tf 4 | import pandas as pd 5 | import sys 6 | 7 | def library_matrix_mat(u, v, latex=False): 8 | ''' 9 | Implements the matrix multiplication for strings and flattens it, 10 | mimicking how the library is made. 11 | Set latex=True to obtain latex forms. 12 | ''' 13 | comp_list = [] 14 | for u_element in u: 15 | for v_element in v: 16 | if ((u_element == '1') and ('v_element' == '1')): 17 | result = '1' 18 | elif u_element == '1': 19 | result = v_element 20 | elif v_element == '1': 21 | result = u_element 22 | else: 23 | result = u_element + v_element 24 | comp_list.append(result) 25 | if latex is True: 26 | comp_list = list(map(lambda x: '$'+x+'$', comp_list)) 27 | return comp_list 28 | 29 | 30 | def print_PDE(sparse_vector, coeffs_list, PDE_term='u_t'): 31 | ''' 32 | Prints PDE with non-zero components according to sparse_vector. 33 | Set PDE_term to different string for different equations. 34 | ''' 35 | non_zero_idx = np.nonzero(sparse_vector)[0] 36 | PDE = PDE_term + ' = ' 37 | for idx, term in enumerate(non_zero_idx): 38 | if idx != 0: 39 | if np.sign(sparse_vector[term]) == -1: 40 | PDE += ' - ' 41 | else: 42 | PDE += ' + ' 43 | PDE += '%.3f' % np.abs(sparse_vector[term]) + coeffs_list[term] 44 | print(PDE) 45 | 46 | 47 | def tensorboard_to_dataframe(event_path): 48 | data_iterator = tf.train.summary_iterator(event_path) 49 | 50 | data = [] 51 | epoch = [] 52 | tags = [] 53 | while True: 54 | try: 55 | event = data_iterator.__next__() 56 | data.append([value.simple_value for value in event.summary.value]) 57 | epoch.append(event.step) 58 | if (event.step > 0) and (len(tags) == 0): 59 | tags = [value.tag for value in event.summary.value] 60 | except: 61 | break 62 | data = np.array(data[3:]) #first three steps contain bullshit 63 | epoch = np.array(epoch[3:]) 64 | 65 | # Parsing data into a nice dataframe 66 | idx_coeffs = [] 67 | idx_coeffs_scaled = [] 68 | 69 | for idx, term in enumerate(tags): 70 | if term[:5] == 'Coeff': 71 | idx_coeffs.append(idx) 72 | 73 | elif term[:6] == 'Scaled': 74 | idx_coeffs_scaled.append(idx) 75 | 76 | coeffs = np.take(data, idx_coeffs, axis=1) 77 | coeffs_scaled = np.take(data, idx_coeffs_scaled, axis=1) 78 | 79 | df = pd.DataFrame({'coeffs': list(coeffs),'coeffs_scaled': list(coeffs_scaled), 'epoch': epoch}) 80 | 81 | for tag_idx in np.arange(5): 82 | df[str(tags[tag_idx])] = data[:, tag_idx] 83 | 84 | return df 85 | 86 | 87 | def sparse_vec_classifier(test_vec, correct_vec): 88 | non_zero_terms_test = np.nonzero(test_vec)[0] 89 | non_zero_terms_correct = np.nonzero(correct_vec)[0] 90 | 91 | if np.array_equiv(non_zero_terms_test, non_zero_terms_correct) == 1: 92 | return 1, np.nanmean(np.abs((test_vec - correct_vec)/test_vec))*100 93 | else: 94 | return 0, None 95 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Dummy conftest.py for deepymod. 5 | 6 | If you don't know what this is for, just leave it empty. 7 | Read more about conftest.py under: 8 | https://pytest.org/latest/plugins.html 9 | """ 10 | 11 | # import pytest 12 | -------------------------------------------------------------------------------- /tests/tb.py: -------------------------------------------------------------------------------- 1 | from deepymod.utilities import tensorboard_to_dataframe 2 | 3 | event_file = 'Examples/output/burgers/2019-04-23_17:17:18/iteration_0/events.out.tfevents.1556032642.C02QP0AZG8WL.local' 4 | df_tb = tensorboard_to_dataframe(event_file) 5 | 6 | print(df_tb.keys()) 7 | -------------------------------------------------------------------------------- /tests/test_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import pytest 5 | from deepymod.skeleton import fib 6 | 7 | __author__ = "Gert-Jan" 8 | __copyright__ = "Gert-Jan" 9 | __license__ = "mit" 10 | 11 | 12 | def test_fib(): 13 | assert fib(1) == 1 14 | assert fib(2) == 1 15 | assert fib(7) == 13 16 | with pytest.raises(AssertionError): 17 | fib(-10) 18 | --------------------------------------------------------------------------------