diff --git a/1-setup.ipynb b/1-setup.ipynb deleted file mode 100644 index 4569e36..0000000 --- a/1-setup.ipynb +++ /dev/null @@ -1,391 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "2c160c9c-a445-493e-9948-7ba507c606fb", - "metadata": {}, - "source": [ - "# Running bash commands from your notebook\n", - "\n", - "First, let's install all the dependencies. \n", - "\n", - "You can directly run bash commands in your notebook, by either prefixing your commands with an exclamation mark `!`:\n", - "```ipython\n", - "[1] !echo \"this is a bash command\"\n", - "this is a bash command\n", - "\n", - "[2] !ls\n", - "/home/user/git_repos/FNO_workshop\n", - "```\n", - "\n", - "or by starting your cell with the `%%bash` ipython magic. \n", - "\n", - "Let's see a simple example:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "24e20734-97e5-4295-9952-d67ac36b63a0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Couldn't find program: 'bash'\n" - ] - } - ], - "source": [ - "%%bash\n", - "\n", - "for var in hello world\n", - "do\n", - " echo ${var} \n", - "done" - ] - }, - { - "cell_type": "markdown", - "id": "5b47acb6-a558-40bf-bd76-872941fdf879", - "metadata": {}, - "source": [ - "# Installing the dependencies\n", - "\n", - "Now, let's install the dependencies." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "bcce1c3e-b4d7-44ea-8b98-bce1f04182cf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Couldn't find program: 'bash'\n" - ] - } - ], - "source": [ - "%%bash \n", - "\n", - "target_folder='./temp'\n", - "[ -d ${target_folder} ] || mkdir -p ${target_folder}\n", - "cd temp\n", - "\n", - "git clone https://github.com/tensorly/tensorly \n", - "cd tensorly\n", - "python -m pip install -e .\n", - "cd ..\n", - "\n", - "git clone https://github.com/tensorly/torch\n", - "cd torch\n", - "python -m pip install -e .\n", - "cd ..\n", - "\n", - "git clone https://github.com/NeuralOperator/neuraloperator\n", - "cd neuraloperator\n", - "python -m pip install -e ." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "e0bb548e-6e98-4fac-935e-52a8115c4aac", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting gpustat\n", - " Downloading gpustat-1.0.0.tar.gz (90 kB)\n", - "Requirement already satisfied: six>=1.7 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gpustat) (1.16.0)\n", - "Collecting nvidia-ml-py<=11.495.46,>=11.450.129\n", - " Downloading nvidia_ml_py-11.495.46-py3-none-any.whl (25 kB)\n", - "Requirement already satisfied: psutil>=5.6.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gpustat) (5.8.0)\n", - "Collecting blessed>=1.17.1\n", - " Downloading blessed-1.20.0-py2.py3-none-any.whl (58 kB)\n", - "Collecting jinxed>=1.1.0\n", - " Downloading jinxed-1.2.0-py2.py3-none-any.whl (33 kB)\n", - "Requirement already satisfied: wcwidth>=0.1.4 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from blessed>=1.17.1->gpustat) (0.2.5)\n", - "Collecting ansicon\n", - " Downloading ansicon-1.89.0-py2.py3-none-any.whl (63 kB)\n", - "Building wheels for collected packages: gpustat\n", - " Building wheel for gpustat (setup.py): started\n", - " Building wheel for gpustat (setup.py): finished with status 'done'\n", - " Created wheel for gpustat: filename=gpustat-1.0.0-py3-none-any.whl size=19886 sha256=647135e0be6c489fa67d18d54e79c7dca544dfd5496efe4d20129d52a8c8803f\n", - " Stored in directory: c:\\users\\devzh\\appdata\\local\\pip\\cache\\wheels\\1b\\ed\\14\\0d513c962b25da841c42022cb5847c2ef835902c8563b8fb01\n", - "Successfully built gpustat\n", - "Installing collected packages: ansicon, jinxed, nvidia-ml-py, blessed, gpustat\n", - "Successfully installed ansicon-1.89.0 blessed-1.20.0 gpustat-1.0.0 jinxed-1.2.0 nvidia-ml-py-11.495.46\n", - "Collecting gdown\n", - " Downloading gdown-4.6.4-py3-none-any.whl (14 kB)\n", - "Requirement already satisfied: requests[socks] in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gdown) (2.26.0)\n", - "Requirement already satisfied: beautifulsoup4 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gdown) (4.10.0)\n", - "Requirement already satisfied: filelock in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gdown) (3.3.1)\n", - "Requirement already satisfied: six in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gdown) (1.16.0)\n", - "Requirement already satisfied: tqdm in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gdown) (4.62.3)\n", - "Requirement already satisfied: soupsieve>1.2 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from beautifulsoup4->gdown) (2.2.1)\n", - "Requirement already satisfied: charset-normalizer~=2.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests[socks]->gdown) (2.0.4)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests[socks]->gdown) (1.26.7)\n", - "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests[socks]->gdown) (2021.10.8)\n", - "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests[socks]->gdown) (3.2)\n", - "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests[socks]->gdown) (1.7.1)\n", - "Requirement already satisfied: colorama in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from tqdm->gdown) (0.4.4)\n", - "Installing collected packages: gdown\n", - "Successfully installed gdown-4.6.4\n", - "Requirement already satisfied: opt-einsum in c:\\users\\devzh\\anaconda3\\lib\\site-packages (3.3.0)\n", - "Requirement already satisfied: numpy>=1.7 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from opt-einsum) (1.21.2)\n", - "Requirement already satisfied: h5py in c:\\users\\devzh\\anaconda3\\lib\\site-packages (3.6.0)\n", - "Requirement already satisfied: wandb in c:\\users\\devzh\\anaconda3\\lib\\site-packages (0.12.1)\n", - "Requirement already satisfied: ruamel.yaml in c:\\users\\devzh\\anaconda3\\lib\\site-packages (0.17.21)\n", - "Requirement already satisfied: zarr in c:\\users\\devzh\\anaconda3\\lib\\site-packages (2.14.1)\n", - "Requirement already satisfied: numpy>=1.14.5 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from h5py) (1.21.2)\n", - "Requirement already satisfied: Click!=8.0.0,>=7.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (8.0.3)\n", - "Requirement already satisfied: promise<3,>=2.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (2.3)\n", - "Requirement already satisfied: sentry-sdk>=1.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (1.3.1)\n", - "Requirement already satisfied: GitPython>=1.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (3.1.18)\n", - "Requirement already satisfied: docker-pycreds>=0.4.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (0.4.0)\n", - "Requirement already satisfied: protobuf>=3.12.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (3.17.3)\n", - "Requirement already satisfied: psutil>=5.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (5.8.0)\n", - "Requirement already satisfied: subprocess32>=3.5.3 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (3.5.4)\n", - "Requirement already satisfied: python-dateutil>=2.6.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (2.8.2)\n", - "Requirement already satisfied: shortuuid>=0.5.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (1.0.1)\n", - "Requirement already satisfied: requests<3,>=2.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (2.26.0)\n", - "Requirement already satisfied: configparser>=3.8.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (5.0.2)\n", - "Requirement already satisfied: PyYAML in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (6.0)\n", - "Requirement already satisfied: six>=1.13.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (1.16.0)\n", - "Requirement already satisfied: pathtools in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from wandb) (0.1.2)\n", - "Requirement already satisfied: ruamel.yaml.clib>=0.2.6 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from ruamel.yaml) (0.2.7)\n", - "Requirement already satisfied: asciitree in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from zarr) (0.3.3)\n", - "Requirement already satisfied: numcodecs>=0.10.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from zarr) (0.11.0)\n", - "Requirement already satisfied: fasteners in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from zarr) (0.18)\n", - "Requirement already satisfied: colorama in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from Click!=8.0.0,>=7.0->wandb) (0.4.4)\n", - "Requirement already satisfied: gitdb<5,>=4.0.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from GitPython>=1.0.0->wandb) (4.0.7)\n", - "Requirement already satisfied: smmap<5,>=3.0.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from gitdb<5,>=4.0.1->GitPython>=1.0.0->wandb) (4.0.0)\n", - "Requirement already satisfied: entrypoints in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from numcodecs>=0.10.0->zarr) (0.3)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests<3,>=2.0.0->wandb) (1.26.7)\n", - "Requirement already satisfied: charset-normalizer~=2.0.0 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests<3,>=2.0.0->wandb) (2.0.4)\n", - "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests<3,>=2.0.0->wandb) (2021.10.8)\n", - "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\devzh\\anaconda3\\lib\\site-packages (from requests<3,>=2.0.0->wandb) (3.2)\n" - ] - } - ], - "source": [ - "!pip install gpustat\n", - "!pip install gdown\n", - "!pip install opt-einsum\n", - "!pip install h5py wandb ruamel.yaml zarr" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f4ed3b9d-fffd-4d5d-852c-7dc95dad086f", - "metadata": {}, - "source": [ - "# Prepare data " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "3a2484ab-0f02-45c9-acce-cb0bbe803dbb", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import requests\n", - "import hashlib\n", - "url_dict = {\n", - " 'darcyflow-1':'https://caltech-pde-data.s3.us-west-2.amazonaws.com/piececonst_r241_N1024_smooth1.mat', \n", - " 'darcyflow-2': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/piececonst_r241_N1024_smooth2.mat', \n", - " 'Navier-Stokes': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/ns_V1e-3_N5000_T50.mat', \n", - " 'darcy-test-32': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_32.pt', \n", - " 'darcy-test-64': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_64.pt', \n", - " 'darcy-train-32': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_32.pt', \n", - " 'darcy-train-64': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_64.pt', \n", - " 'KF-Re100': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/KFvorticity_Re100_N50_T500.npy'\n", - "}\n", - "\n", - "chksum_dict = {\n", - " 'piececonst_r241_N1024_smooth1.mat': '5ab3edf67bb5fd6d49ebf308cd79ed70340106d1a18af8a8439d3e7fc8e82d21', \n", - " 'piececonst_r241_N1024_smooth2.mat': '51a818ed2e4f08752eea5d3f137f0e00271589c48297a46c641382a51eb80acf', \n", - " 'ns_V1e-3_N5000_T50.mat': '78b8d9e83d767dc7050fb8145ee7e7f11e2d18d325bff9abc7f108ec3292ee78', \n", - " 'darcy_train_64.pt': 'c05770239c91ebf093ea971e4d724008a49c9f21b5363fcf182e80499fae7fb4', \n", - " 'darcy_train_32.pt': 'b8d8095d3832ed67f55b4a8fcb1970618b4ca2c6fc91aee2fe49b9c9b2c071ae', \n", - " 'darcy_test_64.pt': '2220bb25c920109e9565a7fc07b675de16d124d563996f6e7256e2faa1fde24f', \n", - " 'darcy_test_32.pt': '65137910193a553295c26e3d8273761daa44766597f4b34cfb12299fc6e3f311', \n", - " 'KFvorticity_Re100_N50_T500.npy': '55f5af44a732a7843d631ace6384ac75c787d4fb36765b2e83ce1febb52d5463'\n", - "}\n", - "\n", - "def download_file(url, file_path):\n", - " with requests.get(url, stream=True) as r:\n", - " r.raise_for_status()\n", - " with open(file_path, 'wb') as f:\n", - " for chunk in r.iter_content(chunk_size=1024 * 1024 * 1024):\n", - " f.write(chunk)\n", - " print('Complete')\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d36ba93d", - "metadata": {}, - "source": [ - "## Download Darcy datasets" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "70b2c9d0-990d-43fd-9a80-7af9dbc8dd64", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_64.pt...\n", - "Complete\n", - "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_32.pt...\n", - "Complete\n", - "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_64.pt...\n", - "Complete\n", - "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_32.pt...\n", - "Complete\n" - ] - } - ], - "source": [ - "data_root = 'data'\n", - "darcy_dir = os.path.join(data_root, 'darcy_flow')\n", - "os.makedirs(darcy_dir, exist_ok=True)\n", - "\n", - "day1_data = ['darcy-train-64', 'darcy-train-32', 'darcy-test-64', 'darcy-test-32']\n", - "\n", - "for key in day1_data:\n", - " value = url_dict[key]\n", - " print(f'Downloading {value}...')\n", - " filename = os.path.basename(value)\n", - " save_path = os.path.join(darcy_dir, filename)\n", - " download_file(url=value, file_path=save_path)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "db98503a", - "metadata": {}, - "outputs": [], - "source": [ - "# verify data integrity\n", - "for data_file in os.listdir(darcy_dir):\n", - " data_path = os.path.join(darcy_dir, data_file)\n", - " with open(data_path, 'rb') as f:\n", - " data = f.read()\n", - " sha256 = hashlib.sha256(data).hexdigest()\n", - " if sha256 == chksum_dict[data_file]:\n", - " print(f'{data_file} verified!')\n", - " else:\n", - " print(f'{data_file} verfication failed!')" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "6a5cc551", - "metadata": {}, - "source": [ - "### Download KF datasets (2d NS)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "817f3d48", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/KFvorticity_Re100_N50_T500.npy to data\\kf\n", - "Complete\n" - ] - } - ], - "source": [ - "data_root = 'data'\n", - "kf_dir = os.path.join(data_root, 'kf')\n", - "os.makedirs(kf_dir, exist_ok=True)\n", - "\n", - "kf_data = ['KF-Re100']\n", - "for key in kf_data:\n", - " value = url_dict[key]\n", - " print(f'Downloading {value} to {kf_dir}')\n", - " filename = os.path.basename(value)\n", - " save_path = os.path.join(kf_dir, filename)\n", - " download_file(url=value, file_path=save_path)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "73acfd3d-23d4-4f02-9bc7-167438ac2de4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "KFvorticity_Re100_N50_T500.npy verified!\n" - ] - } - ], - "source": [ - "for data_file in os.listdir(kf_dir):\n", - " data_path = os.path.join(kf_dir, data_file)\n", - " with open(data_path, 'rb') as f:\n", - " data = f.read()\n", - " sha256 = hashlib.sha256(data).hexdigest()\n", - " if sha256 == chksum_dict[data_file]:\n", - " print(f'{data_file} verified!')\n", - " else:\n", - " print(f'{data_file} verfication failed!')\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - }, - "vscode": { - "interpreter": { - "hash": "95d4b27ba6bfea4a66eebe0e0159b214d32a94d313a7f4c98bd9b87f5ee37cbe" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/2-intro_FNO.ipynb b/2-intro_FNO.ipynb deleted file mode 100644 index 1f6c68b..0000000 --- a/2-intro_FNO.ipynb +++ /dev/null @@ -1,472 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "59194c45-83c9-4a77-a1b0-185eca26afd5", - "metadata": {}, - "source": [ - "# Check the dependencies " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "af7a5c4c-b3a5-4f32-aee9-55290566ff56", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "tl.__version__='0.8.0'\n", - "tltorch.__version__='0.3.0'\n", - "no.__version__='0.1.0'\n" - ] - } - ], - "source": [ - "import tensorly as tl\n", - "import tltorch\n", - "import neuralop as no\n", - "\n", - "print(f'{tl.__version__=}')\n", - "print(f'{tltorch.__version__=}')\n", - "print(f'{no.__version__=}')" - ] - }, - { - "cell_type": "markdown", - "id": "a36bb3e2-c158-497c-babe-5eead700cbf1", - "metadata": { - "tags": [] - }, - "source": [ - "# FFT and Spectral Convolution\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "4efa0d7f-e39c-496e-891d-6b34c62fbd9d", - "metadata": {}, - "outputs": [], - "source": [ - "from neuralop.models.fno_block import FactorizedSpectralConv\n", - "from neuralop.models import TFNO2d\n", - "import torch" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "2c8c3eb7-82e1-4df7-b6e6-3f34331637c4", - "metadata": {}, - "outputs": [], - "source": [ - "fourier_conv = FactorizedSpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", - " factorization=None, implementation='reconstructed')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4eaf645a-b7f5-4dcc-b8de-fb388ccc9b26", - "metadata": {}, - "outputs": [], - "source": [ - "in_data = torch.randn((2, 3, 16, 16))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "016b33e0-88a6-4215-99e0-19da4f8fd5f5", - "metadata": {}, - "outputs": [], - "source": [ - "out = fourier_conv(in_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "36d0f546-9fa9-4936-a6b6-19d7bde03639", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "torch.Size([2, 10, 16, 16])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "out.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "4936746b-5abb-4a8b-9e74-238502c65930", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "FactorizedSpectralConv(\n", - " (weight): ModuleList(\n", - " (0): ComplexDenseTensor(shape=torch.Size([3, 10, 2, 2]), rank=None)\n", - " (1): ComplexDenseTensor(shape=torch.Size([3, 10, 2, 2]), rank=None)\n", - " )\n", - ")" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fourier_conv" - ] - }, - { - "cell_type": "markdown", - "id": "a616d68d-677a-4e6f-abd5-9e631ebf7fb6", - "metadata": {}, - "source": [ - "The way the spectral convolution works is that it multiplies (complex) coefficients with (complex) weights, learned end-to-end." - ] - }, - { - "cell_type": "markdown", - "id": "0c8d9860-d43d-47f3-a6aa-c7ed4522684e", - "metadata": { - "tags": [] - }, - "source": [ - "# Tensorized Spectral Convolutions\n", - "\n", - "It is possible to express the weights of one or more layers as in factorized form, as a low-rank decomposition of the full weights.\n", - "\n", - "`neuralop` comes with support for tensorization out of the box, you can simply specify, e.g., to use a Tucker factorization, `factorization='tucker'`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "b3f919de-97c2-4f0b-bb40-8e47cd2c1e0e", - "metadata": {}, - "outputs": [], - "source": [ - "fourier_conv = FactorizedSpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", - " factorization='tucker', implementation='reconstructed')" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "91a7aa04-9cc3-4f8c-b34f-54fbc625b718", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "FactorizedSpectralConv(\n", - " (weight): ModuleList(\n", - " (0): ComplexTuckerTensor(shape=(3, 10, 2, 2), rank=(1, 5, 1, 1))\n", - " (1): ComplexTuckerTensor(shape=(3, 10, 2, 2), rank=(1, 5, 1, 1))\n", - " )\n", - ")" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fourier_conv" - ] - }, - { - "cell_type": "markdown", - "id": "f8df876d-72e1-40cd-9a86-330a57dc0e8d", - "metadata": {}, - "source": [ - "## Efficient forward pass\n", - "\n", - "When factorizing the weights, have two main options during the forward pass:\n", - "1. reconstruct the full weights and use that for the forward pass \n", - "2. contract the input directly with the factorized weights to predict the output\n", - "\n", - "When the factorized weights are small, the second option can lead to large speedups or memory reduction, particularly when coupled with checkpointing. \n", - "\n", - "In `neuralop`, you can use those simply by specifying `implementation='reconstructed'` or `implementation='factorized'`:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "a0667a6b-1efe-47e0-8908-29c5fb0cf45a", - "metadata": {}, - "outputs": [], - "source": [ - "fourier_conv = FactorizedSpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", - " factorization='tucker', implementation='factorized')" - ] - }, - { - "cell_type": "markdown", - "id": "ec3ab24a-09fe-4864-b2ed-e96b54792e9f", - "metadata": {}, - "source": [ - "# Full Tensorized Fourier Neural Operator \n", - "\n", - "The full architecture is composed of \n", - "\n", - "i) a lifting layer taking the number of input channels and lifting that to the desired number of hidden channels\n", - "ii) a number of spectral convolutions, as shown above\n", - "iii) a projection layer projecting back from the number of hidden channels to the desired number of output channels\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "d51aec17-2cf4-40c4-9452-84a4b5259db6", - "metadata": {}, - "outputs": [], - "source": [ - "tfno = TFNO2d(n_modes_height=16, n_modes_width=16, hidden_channels=16, \n", - " factorization=None, skip='linear')" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "c87127e5-d24c-4096-be3a-8872a853a132", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "TFNO2d(\n", - " (convs): FactorizedSpectralConv2d(\n", - " (weight): ModuleList(\n", - " (0): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (1): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (2): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (3): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (4): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (5): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (6): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (7): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " )\n", - " )\n", - " (fno_skips): ModuleList(\n", - " (0): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " )\n", - " (lifting): Lifting(\n", - " (fc): Conv2d(3, 16, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (projection): Projection(\n", - " (fc1): Conv2d(16, 256, kernel_size=(1, 1), stride=(1, 1))\n", - " (fc2): Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - ")" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tfno" - ] - }, - { - "cell_type": "markdown", - "id": "0e70efec-bf3c-48ac-b53a-59800055f1b9", - "metadata": {}, - "source": [ - "## Lifting layer\n", - "\n", - "Increasing the number of channels" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "1deead74-bd3d-4aa9-8d2c-cfd9ab0763d7", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Lifting(\n", - " (fc): Conv2d(3, 16, kernel_size=(1, 1), stride=(1, 1))\n", - ")" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tfno.lifting" - ] - }, - { - "cell_type": "markdown", - "id": "08844bac-9335-4ac4-afc8-f1d67c3e31bb", - "metadata": {}, - "source": [ - "## Spectral convolutions" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "f2bc28dc-1226-4ed3-b757-3c42357d276a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "FactorizedSpectralConv2d(\n", - " (weight): ModuleList(\n", - " (0): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (1): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (2): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (3): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (4): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (5): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (6): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " (7): ComplexDenseTensor(shape=torch.Size([16, 16, 8, 8]), rank=None)\n", - " )\n", - ")" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tfno.convs" - ] - }, - { - "cell_type": "markdown", - "id": "1c7d9882-13db-447d-affd-07ef17256e1c", - "metadata": {}, - "source": [ - "## Skip connections: recovering non-periodicity\n", - "\n", - "Recall the FNO architecture has skip connections: the FFT transformation will loose non-periodic information that has to be reinjected through skip connections. These skip connections also help with learning.\n", - "\n", - "![FNO_layer](./images/fourier_layer.png)\n", - "\n", - "Here, linear layer (represented by weight W in the image). We can also use Identity skip (`skip='identity'`) or soft-gated connections (`skip='soft-gating'`)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "f063e3bf-34e5-4d7f-83f9-b3522aa6430b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ModuleList(\n", - " (0): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - ")" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tfno.fno_skips" - ] - }, - { - "cell_type": "markdown", - "id": "070e930e-38b6-4d3c-b62a-3ca700294c99", - "metadata": {}, - "source": [ - "## Projection: going back to the target number of channels \n", - "\n", - "Finally, the projection layer takes the hidden dimension to projection_channels and to the actual number of output channels (here, 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "88344f47-a7e8-458e-9fbb-775804fbbaad", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Projection(\n", - " (fc1): Conv2d(16, 256, kernel_size=(1, 1), stride=(1, 1))\n", - " (fc2): Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))\n", - ")" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tfno.projection" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7aae1ab6-852c-4720-9b3b-5791c2b42872", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.15" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/2024_bootcamp_notebook.ipynb b/2024_bootcamp_notebook.ipynb new file mode 100644 index 0000000..2abfd8e --- /dev/null +++ b/2024_bootcamp_notebook.ipynb @@ -0,0 +1,1762 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5b47acb6-a558-40bf-bd76-872941fdf879", + "metadata": { + "id": "5b47acb6-a558-40bf-bd76-872941fdf879" + }, + "source": [ + "# Installing the dependencies\n", + "\n", + "Now, let's install the dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e0bb548e-6e98-4fac-935e-52a8115c4aac", + "metadata": { + "id": "e0bb548e-6e98-4fac-935e-52a8115c4aac", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "ebfac54f-998f-4f29-f455-a41d88874bb3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting tensorly\n", + " Downloading tensorly-0.8.1-py3-none-any.whl (229 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m229.7/229.7 kB\u001b[0m \u001b[31m2.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from tensorly) (1.25.2)\n", + "Requirement already satisfied: scipy in /usr/local/lib/python3.10/dist-packages (from tensorly) (1.11.4)\n", + "Installing collected packages: tensorly\n", + "Successfully installed tensorly-0.8.1\n", + "Collecting torch-harmonics\n", + " Downloading torch_harmonics-0.6.5-py3-none-any.whl (63 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m63.6/63.6 kB\u001b[0m \u001b[31m1.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (from torch-harmonics) (2.2.1+cu121)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from torch-harmonics) (1.25.2)\n", + "Requirement already satisfied: triton in /usr/local/lib/python3.10/dist-packages (from torch-harmonics) (2.2.0)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (3.13.4)\n", + "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (4.11.0)\n", + "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (1.12)\n", + "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (3.3)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (3.1.3)\n", + "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch->torch-harmonics) (2023.6.0)\n", + "Collecting nvidia-cuda-nvrtc-cu12==12.1.105 (from torch->torch-harmonics)\n", + " Using cached nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (23.7 MB)\n", + "Collecting nvidia-cuda-runtime-cu12==12.1.105 (from torch->torch-harmonics)\n", + " Using cached nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (823 kB)\n", + "Collecting nvidia-cuda-cupti-cu12==12.1.105 (from torch->torch-harmonics)\n", + " Using cached nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (14.1 MB)\n", + "Collecting nvidia-cudnn-cu12==8.9.2.26 (from torch->torch-harmonics)\n", + " Using cached nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl (731.7 MB)\n", + "Collecting nvidia-cublas-cu12==12.1.3.1 (from torch->torch-harmonics)\n", + " Using cached nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl (410.6 MB)\n", + "Collecting nvidia-cufft-cu12==11.0.2.54 (from torch->torch-harmonics)\n", + " Using cached nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl (121.6 MB)\n", + "Collecting nvidia-curand-cu12==10.3.2.106 (from torch->torch-harmonics)\n", + " Using cached nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl (56.5 MB)\n", + "Collecting nvidia-cusolver-cu12==11.4.5.107 (from torch->torch-harmonics)\n", + " Using cached nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl (124.2 MB)\n", + "Collecting nvidia-cusparse-cu12==12.1.0.106 (from torch->torch-harmonics)\n", + " Using cached nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl (196.0 MB)\n", + "Collecting nvidia-nccl-cu12==2.19.3 (from torch->torch-harmonics)\n", + " Using cached nvidia_nccl_cu12-2.19.3-py3-none-manylinux1_x86_64.whl (166.0 MB)\n", + "Collecting nvidia-nvtx-cu12==12.1.105 (from torch->torch-harmonics)\n", + " Using cached nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (99 kB)\n", + "Collecting nvidia-nvjitlink-cu12 (from nvidia-cusolver-cu12==11.4.5.107->torch->torch-harmonics)\n", + " Using cached nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (21.1 MB)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch->torch-harmonics) (2.1.5)\n", + "Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy->torch->torch-harmonics) (1.3.0)\n", + "Installing collected packages: nvidia-nvtx-cu12, nvidia-nvjitlink-cu12, nvidia-nccl-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, nvidia-cusparse-cu12, nvidia-cudnn-cu12, nvidia-cusolver-cu12, torch-harmonics\n", + "Successfully installed nvidia-cublas-cu12-12.1.3.1 nvidia-cuda-cupti-cu12-12.1.105 nvidia-cuda-nvrtc-cu12-12.1.105 nvidia-cuda-runtime-cu12-12.1.105 nvidia-cudnn-cu12-8.9.2.26 nvidia-cufft-cu12-11.0.2.54 nvidia-curand-cu12-10.3.2.106 nvidia-cusolver-cu12-11.4.5.107 nvidia-cusparse-cu12-12.1.0.106 nvidia-nccl-cu12-2.19.3 nvidia-nvjitlink-cu12-12.4.127 nvidia-nvtx-cu12-12.1.105 torch-harmonics-0.6.5\n", + "Collecting neuraloperator\n", + " Downloading neuraloperator-0.3.0-py3-none-any.whl (4.0 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.0/4.0 MB\u001b[0m \u001b[31m31.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from neuraloperator) (1.25.2)\n", + "Collecting configmypy (from neuraloperator)\n", + " Downloading configmypy-0.1.0-py3-none-any.whl (11 kB)\n", + "Requirement already satisfied: pytest in /usr/local/lib/python3.10/dist-packages (from neuraloperator) (7.4.4)\n", + "Collecting black (from neuraloperator)\n", + " Downloading black-24.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.8/1.8 MB\u001b[0m \u001b[31m55.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: tensorly in /usr/local/lib/python3.10/dist-packages (from neuraloperator) (0.8.1)\n", + "Collecting tensorly-torch (from neuraloperator)\n", + " Downloading tensorly_torch-0.4.0-py3-none-any.whl (59 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m59.1/59.1 kB\u001b[0m \u001b[31m6.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: opt-einsum in /usr/local/lib/python3.10/dist-packages (from neuraloperator) (3.3.0)\n", + "Requirement already satisfied: click>=8.0.0 in /usr/local/lib/python3.10/dist-packages (from black->neuraloperator) (8.1.7)\n", + "Collecting mypy-extensions>=0.4.3 (from black->neuraloperator)\n", + " Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)\n", + "Requirement already satisfied: packaging>=22.0 in /usr/local/lib/python3.10/dist-packages (from black->neuraloperator) (24.0)\n", + "Collecting pathspec>=0.9.0 (from black->neuraloperator)\n", + " Downloading pathspec-0.12.1-py3-none-any.whl (31 kB)\n", + "Requirement already satisfied: platformdirs>=2 in /usr/local/lib/python3.10/dist-packages (from black->neuraloperator) (4.2.0)\n", + "Requirement already satisfied: tomli>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from black->neuraloperator) (2.0.1)\n", + "Requirement already satisfied: typing-extensions>=4.0.1 in /usr/local/lib/python3.10/dist-packages (from black->neuraloperator) (4.11.0)\n", + "Collecting pytest-mock (from configmypy->neuraloperator)\n", + " Downloading pytest_mock-3.14.0-py3-none-any.whl (9.9 kB)\n", + "Collecting ruamel.yaml (from configmypy->neuraloperator)\n", + " Downloading ruamel.yaml-0.18.6-py3-none-any.whl (117 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m117.8/117.8 kB\u001b[0m \u001b[31m15.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: iniconfig in /usr/local/lib/python3.10/dist-packages (from pytest->neuraloperator) (2.0.0)\n", + "Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/local/lib/python3.10/dist-packages (from pytest->neuraloperator) (1.4.0)\n", + "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /usr/local/lib/python3.10/dist-packages (from pytest->neuraloperator) (1.2.1)\n", + "Requirement already satisfied: scipy in /usr/local/lib/python3.10/dist-packages (from tensorly->neuraloperator) (1.11.4)\n", + "Collecting nose (from tensorly-torch->neuraloperator)\n", + " Downloading nose-1.3.7-py3-none-any.whl (154 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m154.7/154.7 kB\u001b[0m \u001b[31m20.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting ruamel.yaml.clib>=0.2.7 (from ruamel.yaml->configmypy->neuraloperator)\n", + " Downloading ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (526 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m526.7/526.7 kB\u001b[0m \u001b[31m47.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: nose, ruamel.yaml.clib, pathspec, mypy-extensions, tensorly-torch, ruamel.yaml, pytest-mock, black, configmypy, neuraloperator\n", + "Successfully installed black-24.4.1 configmypy-0.1.0 mypy-extensions-1.0.0 neuraloperator-0.3.0 nose-1.3.7 pathspec-0.12.1 pytest-mock-3.14.0 ruamel.yaml-0.18.6 ruamel.yaml.clib-0.2.8 tensorly-torch-0.4.0\n", + "Collecting gpustat\n", + " Downloading gpustat-1.1.1.tar.gz (98 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m98.1/98.1 kB\u001b[0m \u001b[31m2.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting nvidia-ml-py>=11.450.129 (from gpustat)\n", + " Downloading nvidia_ml_py-12.535.133-py3-none-any.whl (37 kB)\n", + "Requirement already satisfied: psutil>=5.6.0 in /usr/local/lib/python3.10/dist-packages (from gpustat) (5.9.5)\n", + "Collecting blessed>=1.17.1 (from gpustat)\n", + " Downloading blessed-1.20.0-py2.py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.4/58.4 kB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: wcwidth>=0.1.4 in /usr/local/lib/python3.10/dist-packages (from blessed>=1.17.1->gpustat) (0.2.13)\n", + "Requirement already satisfied: six>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from blessed>=1.17.1->gpustat) (1.16.0)\n", + "Building wheels for collected packages: gpustat\n", + " Building wheel for gpustat (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for gpustat: filename=gpustat-1.1.1-py3-none-any.whl size=26532 sha256=eb0778d1f88000bcd0b5d52ae02e8eaae0b648c959bd0b27238ba2b6c0a3e66d\n", + " Stored in directory: /root/.cache/pip/wheels/ec/d7/80/a71ba3540900e1f276bcae685efd8e590c810d2108b95f1e47\n", + "Successfully built gpustat\n", + "Installing collected packages: nvidia-ml-py, blessed, gpustat\n", + "Successfully installed blessed-1.20.0 gpustat-1.1.1 nvidia-ml-py-12.535.133\n", + "Requirement already satisfied: gdown in /usr/local/lib/python3.10/dist-packages (5.1.0)\n", + "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from gdown) (4.12.3)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from gdown) (3.13.4)\n", + "Requirement already satisfied: requests[socks] in /usr/local/lib/python3.10/dist-packages (from gdown) (2.31.0)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from gdown) (4.66.2)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->gdown) (2.5)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown) (3.3.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown) (3.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown) (2.0.7)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown) (2024.2.2)\n", + "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown) (1.7.1)\n", + "Requirement already satisfied: opt-einsum in /usr/local/lib/python3.10/dist-packages (3.3.0)\n", + "Requirement already satisfied: numpy>=1.7 in /usr/local/lib/python3.10/dist-packages (from opt-einsum) (1.25.2)\n", + "Requirement already satisfied: h5py in /usr/local/lib/python3.10/dist-packages (3.9.0)\n", + "Collecting wandb\n", + " Downloading wandb-0.16.6-py3-none-any.whl (2.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.2/2.2 MB\u001b[0m \u001b[31m22.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: ruamel.yaml in /usr/local/lib/python3.10/dist-packages (0.18.6)\n", + "Collecting zarr\n", + " Downloading zarr-2.17.2-py3-none-any.whl (208 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m208.5/208.5 kB\u001b[0m \u001b[31m22.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from h5py) (1.25.2)\n", + "Requirement already satisfied: Click!=8.0.0,>=7.1 in /usr/local/lib/python3.10/dist-packages (from wandb) (8.1.7)\n", + "Collecting GitPython!=3.1.29,>=1.0.0 (from wandb)\n", + " Downloading GitPython-3.1.43-py3-none-any.whl (207 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.3/207.3 kB\u001b[0m \u001b[31m23.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: requests<3,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from wandb) (2.31.0)\n", + "Requirement already satisfied: psutil>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from wandb) (5.9.5)\n", + "Collecting sentry-sdk>=1.0.0 (from wandb)\n", + " Downloading sentry_sdk-1.45.0-py2.py3-none-any.whl (267 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m267.1/267.1 kB\u001b[0m \u001b[31m24.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting docker-pycreds>=0.4.0 (from wandb)\n", + " Downloading docker_pycreds-0.4.0-py2.py3-none-any.whl (9.0 kB)\n", + "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from wandb) (6.0.1)\n", + "Collecting setproctitle (from wandb)\n", + " Downloading setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (30 kB)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from wandb) (67.7.2)\n", + "Requirement already satisfied: appdirs>=1.4.3 in /usr/local/lib/python3.10/dist-packages (from wandb) (1.4.4)\n", + "Requirement already satisfied: protobuf!=4.21.0,<5,>=3.19.0 in /usr/local/lib/python3.10/dist-packages (from wandb) (3.20.3)\n", + "Requirement already satisfied: ruamel.yaml.clib>=0.2.7 in /usr/local/lib/python3.10/dist-packages (from ruamel.yaml) (0.2.8)\n", + "Collecting asciitree (from zarr)\n", + " Downloading asciitree-0.3.3.tar.gz (4.0 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting numcodecs>=0.10.0 (from zarr)\n", + " Downloading numcodecs-0.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.7/7.7 MB\u001b[0m \u001b[31m86.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting fasteners (from zarr)\n", + " Downloading fasteners-0.19-py3-none-any.whl (18 kB)\n", + "Requirement already satisfied: six>=1.4.0 in /usr/local/lib/python3.10/dist-packages (from docker-pycreds>=0.4.0->wandb) (1.16.0)\n", + "Collecting gitdb<5,>=4.0.1 (from GitPython!=3.1.29,>=1.0.0->wandb)\n", + " Downloading gitdb-4.0.11-py3-none-any.whl (62 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.7/62.7 kB\u001b[0m \u001b[31m8.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.0.0->wandb) (3.3.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.0.0->wandb) (3.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.0.0->wandb) (2.0.7)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.0.0->wandb) (2024.2.2)\n", + "Collecting smmap<6,>=3.0.1 (from gitdb<5,>=4.0.1->GitPython!=3.1.29,>=1.0.0->wandb)\n", + " Downloading smmap-5.0.1-py3-none-any.whl (24 kB)\n", + "Building wheels for collected packages: asciitree\n", + " Building wheel for asciitree (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for asciitree: filename=asciitree-0.3.3-py3-none-any.whl size=5034 sha256=9b360729437a21a98c813a051c4706c88963112a53052d30dc1c010cd033e45a\n", + " Stored in directory: /root/.cache/pip/wheels/7f/4e/be/1171b40f43b918087657ec57cf3b81fa1a2e027d8755baa184\n", + "Successfully built asciitree\n", + "Installing collected packages: asciitree, smmap, setproctitle, sentry-sdk, numcodecs, fasteners, docker-pycreds, zarr, gitdb, GitPython, wandb\n", + "Successfully installed GitPython-3.1.43 asciitree-0.3.3 docker-pycreds-0.4.0 fasteners-0.19 gitdb-4.0.11 numcodecs-0.12.1 sentry-sdk-1.45.0 setproctitle-1.3.3 smmap-5.0.1 wandb-0.16.6 zarr-2.17.2\n" + ] + } + ], + "source": [ + "!pip install tensorly\n", + "!pip install torch-harmonics\n", + "!pip install neuraloperator\n", + "!pip install gpustat\n", + "!pip install gdown\n", + "!pip install opt-einsum\n", + "!pip install h5py wandb ruamel.yaml zarr" + ] + }, + { + "cell_type": "markdown", + "id": "f4ed3b9d-fffd-4d5d-852c-7dc95dad086f", + "metadata": { + "id": "f4ed3b9d-fffd-4d5d-852c-7dc95dad086f" + }, + "source": [ + "# Prepare data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3a2484ab-0f02-45c9-acce-cb0bbe803dbb", + "metadata": { + "id": "3a2484ab-0f02-45c9-acce-cb0bbe803dbb" + }, + "outputs": [], + "source": [ + "import os\n", + "import requests\n", + "import hashlib\n", + "url_dict = {\n", + " 'darcyflow-1':'https://caltech-pde-data.s3.us-west-2.amazonaws.com/piececonst_r241_N1024_smooth1.mat',\n", + " 'darcyflow-2': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/piececonst_r241_N1024_smooth2.mat',\n", + " 'Navier-Stokes': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/ns_V1e-3_N5000_T50.mat',\n", + " 'darcy-test-32': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_32.pt',\n", + " 'darcy-test-64': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_64.pt',\n", + " 'darcy-train-32': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_32.pt',\n", + " 'darcy-train-64': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_64.pt',\n", + " 'KF-Re100': 'https://caltech-pde-data.s3.us-west-2.amazonaws.com/KFvorticity_Re100_N50_T500.npy'\n", + "}\n", + "\n", + "chksum_dict = {\n", + " 'piececonst_r241_N1024_smooth1.mat': '5ab3edf67bb5fd6d49ebf308cd79ed70340106d1a18af8a8439d3e7fc8e82d21',\n", + " 'piececonst_r241_N1024_smooth2.mat': '51a818ed2e4f08752eea5d3f137f0e00271589c48297a46c641382a51eb80acf',\n", + " 'ns_V1e-3_N5000_T50.mat': '78b8d9e83d767dc7050fb8145ee7e7f11e2d18d325bff9abc7f108ec3292ee78',\n", + " 'darcy_train_64.pt': 'c05770239c91ebf093ea971e4d724008a49c9f21b5363fcf182e80499fae7fb4',\n", + " 'darcy_train_32.pt': 'b8d8095d3832ed67f55b4a8fcb1970618b4ca2c6fc91aee2fe49b9c9b2c071ae',\n", + " 'darcy_test_64.pt': '2220bb25c920109e9565a7fc07b675de16d124d563996f6e7256e2faa1fde24f',\n", + " 'darcy_test_32.pt': '65137910193a553295c26e3d8273761daa44766597f4b34cfb12299fc6e3f311',\n", + " 'KFvorticity_Re100_N50_T500.npy': '55f5af44a732a7843d631ace6384ac75c787d4fb36765b2e83ce1febb52d5463'\n", + "}\n", + "\n", + "def download_file(url, file_path):\n", + " with requests.get(url, stream=True) as r:\n", + " r.raise_for_status()\n", + " with open(file_path, 'wb') as f:\n", + " for chunk in r.iter_content(chunk_size=1024 * 1024 * 1024):\n", + " f.write(chunk)\n", + " print('Complete')\n" + ] + }, + { + "cell_type": "markdown", + "id": "d36ba93d", + "metadata": { + "id": "d36ba93d" + }, + "source": [ + "## Download Darcy datasets" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "70b2c9d0-990d-43fd-9a80-7af9dbc8dd64", + "metadata": { + "id": "70b2c9d0-990d-43fd-9a80-7af9dbc8dd64", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "b5a2cfb6-0a34-44db-b466-a694c5d62c92" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_64.pt...\n", + "Complete\n", + "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_train_32.pt...\n", + "Complete\n", + "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_64.pt...\n", + "Complete\n", + "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/darcy_test_32.pt...\n", + "Complete\n" + ] + } + ], + "source": [ + "data_root = 'data'\n", + "darcy_dir = os.path.join(data_root, 'darcy_flow')\n", + "os.makedirs(darcy_dir, exist_ok=True)\n", + "\n", + "day1_data = ['darcy-train-64', 'darcy-train-32', 'darcy-test-64', 'darcy-test-32']\n", + "\n", + "for key in day1_data:\n", + " value = url_dict[key]\n", + " print(f'Downloading {value}...')\n", + " filename = os.path.basename(value)\n", + " save_path = os.path.join(darcy_dir, filename)\n", + " download_file(url=value, file_path=save_path)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "db98503a", + "metadata": { + "id": "db98503a", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "77095ee6-c06f-47b6-c95f-96a437ab165b" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "darcy_test_64.pt verified!\n", + "darcy_test_32.pt verified!\n", + "darcy_train_32.pt verified!\n", + "darcy_train_64.pt verified!\n" + ] + } + ], + "source": [ + "# verify data integrity\n", + "for data_file in os.listdir(darcy_dir):\n", + " data_path = os.path.join(darcy_dir, data_file)\n", + " with open(data_path, 'rb') as f:\n", + " data = f.read()\n", + " sha256 = hashlib.sha256(data).hexdigest()\n", + " if sha256 == chksum_dict[data_file]:\n", + " print(f'{data_file} verified!')\n", + " else:\n", + " print(f'{data_file} verfication failed!')" + ] + }, + { + "cell_type": "markdown", + "id": "6a5cc551", + "metadata": { + "id": "6a5cc551" + }, + "source": [ + "### Download KF datasets (2d NS)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "817f3d48", + "metadata": { + "id": "817f3d48", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "784cc542-ee78-4181-f2c6-e331d1857f43" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading https://caltech-pde-data.s3.us-west-2.amazonaws.com/KFvorticity_Re100_N50_T500.npy to data/kf\n", + "Complete\n" + ] + } + ], + "source": [ + "data_root = 'data'\n", + "kf_dir = os.path.join(data_root, 'kf')\n", + "os.makedirs(kf_dir, exist_ok=True)\n", + "\n", + "kf_data = ['KF-Re100']\n", + "for key in kf_data:\n", + " value = url_dict[key]\n", + " print(f'Downloading {value} to {kf_dir}')\n", + " filename = os.path.basename(value)\n", + " save_path = os.path.join(kf_dir, filename)\n", + " download_file(url=value, file_path=save_path)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "73acfd3d-23d4-4f02-9bc7-167438ac2de4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "73acfd3d-23d4-4f02-9bc7-167438ac2de4", + "outputId": "f63b45c9-5218-4f03-8041-e8d05924361a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "KFvorticity_Re100_N50_T500.npy verified!\n" + ] + } + ], + "source": [ + "for data_file in os.listdir(kf_dir):\n", + " data_path = os.path.join(kf_dir, data_file)\n", + " with open(data_path, 'rb') as f:\n", + " data = f.read()\n", + " sha256 = hashlib.sha256(data).hexdigest()\n", + " if sha256 == chksum_dict[data_file]:\n", + " print(f'{data_file} verified!')\n", + " else:\n", + " print(f'{data_file} verfication failed!')\n" + ] + }, + { + "cell_type": "markdown", + "id": "59194c45-83c9-4a77-a1b0-185eca26afd5", + "metadata": { + "id": "59194c45-83c9-4a77-a1b0-185eca26afd5" + }, + "source": [ + "# Check the dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "af7a5c4c-b3a5-4f32-aee9-55290566ff56", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "af7a5c4c-b3a5-4f32-aee9-55290566ff56", + "outputId": "deb7b49a-b7ed-49c0-98cf-40a2370b99ee" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tl.__version__='0.8.1'\n", + "no.__version__='0.3.0'\n" + ] + } + ], + "source": [ + "import tensorly as tl\n", + "import neuralop as no\n", + "\n", + "print(f'{tl.__version__=}')\n", + "# print(f'{tltorch.__version__=}')\n", + "print(f'{no.__version__=}')" + ] + }, + { + "cell_type": "markdown", + "id": "a36bb3e2-c158-497c-babe-5eead700cbf1", + "metadata": { + "id": "a36bb3e2-c158-497c-babe-5eead700cbf1", + "tags": [] + }, + "source": [ + "# FFT and Spectral Convolution\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4efa0d7f-e39c-496e-891d-6b34c62fbd9d", + "metadata": { + "id": "4efa0d7f-e39c-496e-891d-6b34c62fbd9d" + }, + "outputs": [], + "source": [ + "from neuralop.layers.spectral_convolution import SpectralConv\n", + "from neuralop.models import TFNO2d\n", + "import torch" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2c8c3eb7-82e1-4df7-b6e6-3f34331637c4", + "metadata": { + "id": "2c8c3eb7-82e1-4df7-b6e6-3f34331637c4" + }, + "outputs": [], + "source": [ + "fourier_conv = SpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", + " factorization='tucker', implementation='reconstructed')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4eaf645a-b7f5-4dcc-b8de-fb388ccc9b26", + "metadata": { + "id": "4eaf645a-b7f5-4dcc-b8de-fb388ccc9b26" + }, + "outputs": [], + "source": [ + "in_data = torch.randn((2, 3, 16, 16))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "016b33e0-88a6-4215-99e0-19da4f8fd5f5", + "metadata": { + "id": "016b33e0-88a6-4215-99e0-19da4f8fd5f5" + }, + "outputs": [], + "source": [ + "out = fourier_conv(in_data)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "36d0f546-9fa9-4936-a6b6-19d7bde03639", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "36d0f546-9fa9-4936-a6b6-19d7bde03639", + "outputId": "bc07ed63-3855-40e3-a30b-9abfef5d6252" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "torch.Size([2, 10, 16, 16])" + ] + }, + "metadata": {}, + "execution_count": 12 + } + ], + "source": [ + "out.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4936746b-5abb-4a8b-9e74-238502c65930", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4936746b-5abb-4a8b-9e74-238502c65930", + "outputId": "123c2655-5e96-46c9-9e26-50c2578736e1" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "SpectralConv(\n", + " (weight): ModuleList(\n", + " (0): ComplexTuckerTensor(shape=(3, 10, 4, 3), rank=(2, 7, 3, 2))\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 13 + } + ], + "source": [ + "fourier_conv" + ] + }, + { + "cell_type": "markdown", + "id": "a616d68d-677a-4e6f-abd5-9e631ebf7fb6", + "metadata": { + "id": "a616d68d-677a-4e6f-abd5-9e631ebf7fb6" + }, + "source": [ + "The way the spectral convolution works is that it multiplies (complex) coefficients with (complex) weights, learned end-to-end." + ] + }, + { + "cell_type": "markdown", + "id": "0c8d9860-d43d-47f3-a6aa-c7ed4522684e", + "metadata": { + "id": "0c8d9860-d43d-47f3-a6aa-c7ed4522684e", + "tags": [] + }, + "source": [ + "# Tensorized Spectral Convolutions\n", + "\n", + "It is possible to express the weights of one or more layers as in factorized form, as a low-rank decomposition of the full weights.\n", + "\n", + "`neuralop` comes with support for tensorization out of the box, you can simply specify, e.g., to use a Tucker factorization, `factorization='tucker'`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b3f919de-97c2-4f0b-bb40-8e47cd2c1e0e", + "metadata": { + "id": "b3f919de-97c2-4f0b-bb40-8e47cd2c1e0e" + }, + "outputs": [], + "source": [ + "fourier_conv = SpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", + " factorization='tucker', implementation='reconstructed')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "91a7aa04-9cc3-4f8c-b34f-54fbc625b718", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "91a7aa04-9cc3-4f8c-b34f-54fbc625b718", + "outputId": "cc0f8a05-2bb1-41f6-bc8d-56d9d5c19592" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "SpectralConv(\n", + " (weight): ModuleList(\n", + " (0): ComplexTuckerTensor(shape=(3, 10, 4, 3), rank=(2, 7, 3, 2))\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ], + "source": [ + "fourier_conv" + ] + }, + { + "cell_type": "markdown", + "id": "f8df876d-72e1-40cd-9a86-330a57dc0e8d", + "metadata": { + "id": "f8df876d-72e1-40cd-9a86-330a57dc0e8d" + }, + "source": [ + "## Efficient forward pass\n", + "\n", + "When factorizing the weights, have two main options during the forward pass:\n", + "1. reconstruct the full weights and use that for the forward pass\n", + "2. contract the input directly with the factorized weights to predict the output\n", + "\n", + "When the factorized weights are small, the second option can lead to large speedups or memory reduction, particularly when coupled with checkpointing.\n", + "\n", + "In `neuralop`, you can use those simply by specifying `implementation='reconstructed'` or `implementation='factorized'`:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "a0667a6b-1efe-47e0-8908-29c5fb0cf45a", + "metadata": { + "id": "a0667a6b-1efe-47e0-8908-29c5fb0cf45a" + }, + "outputs": [], + "source": [ + "fourier_conv = SpectralConv(in_channels=3, out_channels=10, n_modes=(4, 4),\n", + " factorization='tucker', implementation='factorized')" + ] + }, + { + "cell_type": "markdown", + "id": "ec3ab24a-09fe-4864-b2ed-e96b54792e9f", + "metadata": { + "id": "ec3ab24a-09fe-4864-b2ed-e96b54792e9f" + }, + "source": [ + "# Full Tensorized Fourier Neural Operator\n", + "\n", + "The full architecture is composed of\n", + "\n", + "i) a lifting layer taking the number of input channels and lifting that to the desired number of hidden channels\n", + "ii) a number of spectral convolutions, as shown above\n", + "iii) a projection layer projecting back from the number of hidden channels to the desired number of output channels\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d51aec17-2cf4-40c4-9452-84a4b5259db6", + "metadata": { + "id": "d51aec17-2cf4-40c4-9452-84a4b5259db6" + }, + "outputs": [], + "source": [ + "tfno = TFNO2d(n_modes_height=16, n_modes_width=16, hidden_channels=16,\n", + " factorization=None, skip='linear')" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c87127e5-d24c-4096-be3a-8872a853a132", + "metadata": { + "id": "c87127e5-d24c-4096-be3a-8872a853a132", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "fb1db21e-b9e7-4f9e-904c-b1d50c73039d" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "TFNO2d(\n", + " (fno_blocks): FNOBlocks(\n", + " (convs): SpectralConv(\n", + " (weight): ModuleList(\n", + " (0-3): 4 x ComplexDenseTensor(shape=torch.Size([16, 16, 16, 9]), rank=None)\n", + " )\n", + " )\n", + " (fno_skips): ModuleList(\n", + " (0-3): 4 x Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " )\n", + " )\n", + " (lifting): MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(3, 256, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(256, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " (projection): MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(16, 256, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 18 + } + ], + "source": [ + "tfno" + ] + }, + { + "cell_type": "markdown", + "id": "0e70efec-bf3c-48ac-b53a-59800055f1b9", + "metadata": { + "id": "0e70efec-bf3c-48ac-b53a-59800055f1b9" + }, + "source": [ + "## Lifting layer\n", + "\n", + "Increasing the number of channels" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "1deead74-bd3d-4aa9-8d2c-cfd9ab0763d7", + "metadata": { + "id": "1deead74-bd3d-4aa9-8d2c-cfd9ab0763d7", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "a676bbb8-9aff-45dc-b0e1-bc2fcd39404d" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(3, 256, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(256, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 19 + } + ], + "source": [ + "tfno.lifting" + ] + }, + { + "cell_type": "markdown", + "id": "08844bac-9335-4ac4-afc8-f1d67c3e31bb", + "metadata": { + "id": "08844bac-9335-4ac4-afc8-f1d67c3e31bb" + }, + "source": [ + "## Spectral convolutions" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f2bc28dc-1226-4ed3-b757-3c42357d276a", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "f2bc28dc-1226-4ed3-b757-3c42357d276a", + "outputId": "92f4c42f-301d-4741-ab65-9d4960877fb5" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "SpectralConv(\n", + " (weight): ModuleList(\n", + " (0-3): 4 x ComplexDenseTensor(shape=torch.Size([16, 16, 16, 9]), rank=None)\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 20 + } + ], + "source": [ + "tfno.fno_blocks.convs" + ] + }, + { + "cell_type": "markdown", + "id": "1c7d9882-13db-447d-affd-07ef17256e1c", + "metadata": { + "id": "1c7d9882-13db-447d-affd-07ef17256e1c" + }, + "source": [ + "## Skip connections: recovering non-periodicity\n", + "\n", + "Recall the FNO architecture has skip connections: the FFT transformation will loose non-periodic information that has to be reinjected through skip connections. These skip connections also help with learning.\n", + "\n", + "![FNO_layer](./images/fourier_layer.png)\n", + "\n", + "Here, linear layer (represented by weight W in the image). We can also use Identity skip (`skip='identity'`) or soft-gated connections (`skip='soft-gating'`)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f063e3bf-34e5-4d7f-83f9-b3522aa6430b", + "metadata": { + "id": "f063e3bf-34e5-4d7f-83f9-b3522aa6430b", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "8bcbf0a3-473f-4319-aac5-d6b36907537f" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "ModuleList(\n", + " (0-3): 4 x Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 21 + } + ], + "source": [ + "tfno.fno_blocks.fno_skips" + ] + }, + { + "cell_type": "markdown", + "id": "070e930e-38b6-4d3c-b62a-3ca700294c99", + "metadata": { + "id": "070e930e-38b6-4d3c-b62a-3ca700294c99" + }, + "source": [ + "## Projection: going back to the target number of channels\n", + "\n", + "Finally, the projection layer takes the hidden dimension to projection_channels and to the actual number of output channels (here, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "88344f47-a7e8-458e-9fbb-775804fbbaad", + "metadata": { + "id": "88344f47-a7e8-458e-9fbb-775804fbbaad", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "3a4e8edc-5b22-46ce-e28b-9ebc25913887" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(16, 256, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + ")" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ], + "source": [ + "tfno.projection" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "4df7dcda-a364-4255-9339-a9a09c2a5e34", + "metadata": { + "id": "4df7dcda-a364-4255-9339-a9a09c2a5e34" + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from neuralop.datasets import load_darcy_pt" + ] + }, + { + "cell_type": "markdown", + "id": "ff12d431-bde9-4eba-906b-d0faea8c49fb", + "metadata": { + "id": "ff12d431-bde9-4eba-906b-d0faea8c49fb" + }, + "source": [ + "# Load the data" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "aa9c49f5-878b-4cac-9a35-b9dc53085d11", + "metadata": { + "id": "aa9c49f5-878b-4cac-9a35-b9dc53085d11" + }, + "outputs": [], + "source": [ + "data_path=darcy_dir" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "f40e8c0a-c031-457b-863c-c728de7d1b80", + "metadata": { + "id": "f40e8c0a-c031-457b-863c-c728de7d1b80" + }, + "outputs": [], + "source": [ + "train_loader, test_loaders, data_processor = load_darcy_pt(data_path, n_train=100, n_tests=[10],\n", + " batch_size=3, test_batch_sizes=[3],\n", + " test_resolutions=[32], train_resolution=32)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "2f29d90a-4fc7-4b83-8ed4-f1bb4dce2574", + "metadata": { + "id": "2f29d90a-4fc7-4b83-8ed4-f1bb4dce2574" + }, + "outputs": [], + "source": [ + "train_dataset = train_loader.dataset" + ] + }, + { + "cell_type": "markdown", + "id": "21000189-ecac-42e2-b008-06eefa7b1710", + "metadata": { + "id": "21000189-ecac-42e2-b008-06eefa7b1710" + }, + "source": [ + "# Visualizing the data " + ] + }, + { + "cell_type": "markdown", + "id": "1cf47f09-1fb3-4667-9b04-a98b3ee8d08d", + "metadata": { + "id": "1cf47f09-1fb3-4667-9b04-a98b3ee8d08d" + }, + "source": [ + "The data is stored in a dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "b6a9aed5-6532-42ba-8131-0307460c960d", + "metadata": { + "id": "b6a9aed5-6532-42ba-8131-0307460c960d" + }, + "outputs": [], + "source": [ + "data = train_dataset[0]\n", + "x = data['x']\n", + "y = data['y']" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "9f475172-62b0-4ce3-8dce-a7d0d9dca9fb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9f475172-62b0-4ce3-8dce-a7d0d9dca9fb", + "outputId": "e705cbc7-f6c6-460f-cab9-a4ef638ac11e" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "torch.Size([1, 32, 32])" + ] + }, + "metadata": {}, + "execution_count": 28 + } + ], + "source": [ + "x.shape" + ] + }, + { + "cell_type": "markdown", + "id": "7d7947ad-f98c-414b-8a12-64270988ad1f", + "metadata": { + "id": "7d7947ad-f98c-414b-8a12-64270988ad1f" + }, + "source": [ + "`x` is of shape (1, height, width).\n", + "\n", + "After preprocessing, it becomes shape (3, height, width).\n", + "\n", + "This is because, in addition to the binary input, we appended a positional encoding, so the model knows the location of each pixel.\n", + "\n", + "Let's check the actual data:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "077ebd7d-883b-4300-b13d-ed88813a3be1", + "metadata": { + "id": "077ebd7d-883b-4300-b13d-ed88813a3be1" + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "814a044d-a52f-4cf2-aa1b-859370012af5", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 725 + }, + "id": "814a044d-a52f-4cf2-aa1b-859370012af5", + "outputId": "5f6664f9-9e53-46ca-e9a6-4760ee938362" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "torch.Size([3, 32, 32])\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAo4AAAKzCAYAAACQ6oyTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB1gklEQVR4nO3deXxU9b3/8fdkkpmELBMCISFlR0AUgV4UjMgiIIsbKEhRqyBaN6BVtFasIrhF5VdFFsHtgguI4hW5eguoCOFaQQtKUREKCkKFgKJJIJB1vr8/uJk6JJxzhkwyE3g9+ziPOud7lu98Z+bDJ9855zMuY4wRAAAAYCMm0h0AAABA/UDiCAAAAEdIHAEAAOAIiSMAAAAcIXEEAACAIySOAAAAcITEEQAAAI6QOAIAAMAREkcAAAA4QuII1KH58+fL5XJp586dUdePvn37qm/fvjU+zqmKsYiMVq1aacyYMZHuBnDKIHEEauCyyy5TgwYNdPDgweNuc80118jj8ejAgQN12DOc6jZv3qwpU6aQyAIIKxJHoAauueYaHTlyREuWLKm2/fDhw1q6dKkGDx6sRo0a6dprr9WRI0fUsmXLOu6pvffee0/vvfdeSPtE8/Opa9E2Fps3b9bUqVNJHAGEFYkjUAOXXXaZkpOTtXDhwmrbly5dqqKiIl1zzTWSJLfbrfj4eLlcrrrspiMej0cejyekfaL5+dQ1xgLAqYDEEaiBhIQEXXHFFVq5cqX2799fpX3hwoVKTk7WZZddJqn66+DWr1+vQYMGqXHjxkpISFDr1q01duzYQPvq1avlcrm0evXqoGPv3LlTLpdL8+fPD6zbtGmTxowZozZt2ig+Pl6ZmZkaO3aso6/Jj73GsVWrVnK5XNUulX2p7vm0atVKl1xyiT766CN1795d8fHxatOmjV5++eUq59y0aZP69OmjhIQENWvWTA8//LDmzZvn+FrBDz/8UL169VJiYqJSU1M1dOhQff3110HbTJkyRS6XS9u3b9eYMWOUmpoqn8+n66+/XocPH65yzFdffVXdunVTQkKC0tLSNGrUKO3evdu2LzUZi8p916xZo5tvvlmNGjVSSkqKrrvuOv38889B27pcLk2ZMqXK+X95rd/8+fN15ZVXSpIuuOCCKq9bdfLy8nT99derWbNm8nq9atq0qYYOHRr0fJYuXaqLL75YWVlZ8nq9atu2rR566CFVVFQEHatv377q1KlT4PVt0KCBTjvtNL355puSpNzcXPXo0UMJCQnq0KGDPvjgg6D9K1+zLVu2aOTIkUpJSVGjRo30hz/8QcXFxcd9DpXy8/N1++23q3nz5vJ6vTrttNP0+OOPy+/32+4LwFpspDsA1HfXXHONXnrpJb3xxhsaP358YP1PP/2kFStW6KqrrlJCQkK1++7fv18DBw5Uenq67rnnHqWmpmrnzp166623Tqgv77//vr799ltdf/31yszM1FdffaXnnntOX331ldatWxfSbNj06dN16NChoHVPPfWUNm7cqEaNGlnuu337do0YMUI33HCDRo8erf/8z//UmDFj1K1bN5155pmSpO+//z6Q1EyaNEmJiYl64YUX5PV6HfXvgw8+0JAhQ9SmTRtNmTJFR44c0cyZM9WzZ0999tlnatWqVdD2I0eOVOvWrZWTk6PPPvtML7zwgpo0aaLHH388sM0jjzyi+++/XyNHjtSNN96oH374QTNnzlTv3r31+eefKzU11VHfQh2LSuPHj1dqaqqmTJmirVu3as6cOfruu+8Cfzw41bt3b/3+97/XjBkzdO+996pjx46SFPj/6gwfPlxfffWVJkyYoFatWmn//v16//33tWvXrsBYzp8/X0lJSZo4caKSkpL04YcfavLkySosLNS0adOCjvfzzz/rkksu0ahRo3TllVdqzpw5GjVqlBYsWKDbb79dt9xyi66++mpNmzZNI0aM0O7du5WcnBx0jJEjR6pVq1bKycnRunXrNGPGDP3888/V/hFS6fDhw+rTp4++//573XzzzWrRooU+/vhjTZo0SXv37tX06dMdjyOAahgANVJeXm6aNm1qsrOzg9bPnTvXSDIrVqwIrJs3b56RZHbs2GGMMWbJkiVGkvn73/9+3OOvWrXKSDKrVq0KWr9jxw4jycybNy+w7vDhw1X2f+2114wks2bNmuP2wxhj+vTpY/r06XPcfrzxxhtGknnwwQctj9OyZcsq59u/f7/xer3mzjvvDKybMGGCcblc5vPPPw+sO3DggElLS6tyzOp07drVNGnSxBw4cCCw7h//+IeJiYkx1113XWDdAw88YCSZsWPHBu1/+eWXm0aNGgUe79y507jdbvPII48EbffFF1+Y2NjYKuuPVZOxqNy3W7duprS0NLD+iSeeMJLM0qVL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auvWrXrmmWd0/vnnB+qAStKNN96oW265RcOHD9eFF16of/zjH1qxYoUaN24cdKyuXbvK7Xbr8ccfV0FBgbxer/r166cmTZpUOe8///nPwHnPOOMMxcbGasmSJdq3b1/gBpfzzjtPDRs21OjRo/X73/9eLpdLr7zySq1efrBjxw5ddtllGjx4sNauXatXX31VV199tbp06XLcff74xz/qv//7v3XJJZcEyh4VFRXpiy++0JtvvqmdO3dWGSsAzpE4AmF0zTXX6OOPP1b37t0d3eTRp08fffrpp1q0aJH27dsnn8+n7t27a8GCBUFf0c2cOVNlZWWaO3euvF6vRo4cqWnTpqlTp05Bx1u4cKEmTJig2bNnyxijgQMHatmyZcrKygr5ufz444+SpNGjR1dpW7VqVY0Tx+bNm2vVqlX6/e9/r0cffVTp6ekaN26cEhMT9fvf/17x8fGW+w8YMEDLly/XAw88oMmTJysuLk59+vTR448/HjR2objnnnvUvn17PfXUU5o6dWqgnwMHDgxK3mrLrFmztGDBAk2ePFllZWW66qqrNGPGjKAk+ne/+5127NihF198UcuXL1evXr30/vvvV7l7PzMzU3PnzlVOTo5uuOEGVVRUaNWqVdUmjs2bN9dVV12llStX6pVXXlFsbKxOP/10vfHGGxo+fLgkqVGjRnr33Xd155136r777lPDhg3129/+Vv3799egQYNqZTxef/11TZ48Wffcc49iY2M1fvz4KvUij9WgQQPl5ubq0Ucf1eLFi/Xyyy8rJSVF7du319SpUwM34wA4MS4TyavVAeAYt99+u5599lkdOnTI8o70k8n8+fN1/fXX6+9//7vOPvvsSHcn4qZMmaKpU6fqhx9+YHYQiDJc4wggYo4cORL0+MCBA3rllVd0/vnnnzJJIwDUJ3xVDSBisrOz1bdvX3Xs2FH79u3Tiy++qMLCQt1///2R7hoAoBokjgAi5qKLLtKbb76p5557Ti6XS//xH/+hF198MajcCwAgenCNIwAAABzhGkcAAAA4QuIIAAAAR0gcAQAA4AiJIwAAABwhcQQAAIAjJI4AAABwhMQRAAAAjpA4AgAAwBESRwAAADhC4ggAAABHSBwBAADgCIkjAAAAHCFxBAAAgCMkjgAAAHCExBEAAACOkDjihMyfP18ul0s7d+6MdFcA4KRAXEV9QOKIk8LmzZs1ZcoUAi4AhAlxFdVxGWNMpDuB+qeiokJlZWXyer1yuVyR7o7efPNNXXnllVq1apX69u0b6e4AQMiIq6gPYiPdAdRPbrdbbrc70t0AgJMGcRX1AV9V44RUdy1Oq1atdMkll+ijjz5S9+7dFR8frzZt2ujll1+udt81a9bo5ptvVqNGjZSSkqLrrrtOP//8c9C2LpdLU6ZMqXL+Vq1aacyYMYHjXXnllZKkCy64QC6XSy6XS6tXr6627/v371d6err69u2rX064b9++XYmJifrNb34T+oAAQA3V57g6b948uVwuff7551XaHn30Ubndbn3//ffOBwNRi8QRYbV9+3aNGDFCF154of7yl7+oYcOGGjNmjL766qsq244fP15ff/21pkyZouuuu04LFizQsGHDFOrVE71799bvf/97SdK9996rV155Ra+88oo6duxY7fZNmjTRnDlzlJubq5kzZ0qS/H6/xowZo+TkZD3zzDMhPmsAqD31Ia6OGDFCCQkJWrBgQZW2BQsWqG/fvvrVr34VUh8QnfiqGmG1detWrVmzRr169ZIkjRw5Us2bN9e8efP0//7f/wva1uPxaOXKlYqLi5MktWzZUnfffbfeeecdXXbZZY7P2aZNG/Xq1UszZszQhRde6OhanBEjRuiqq67SpEmTNGTIEC1dulR/+9vf9Pbbb6tRo0bOnzAA1LL6EFeTk5M1bNgwvfbaa3riiScUE3N0Xurzzz/X5s2b9cc//jGEZ4xoxowjwuqMM84IBDdJSk9PV4cOHfTtt99W2famm24KBDdJuvXWWxUbG6u//vWvddLXWbNmyefzacSIEbr//vt17bXXaujQoXVybgBwqr7E1euuu0579uzRqlWrAusWLFighIQEDR8+vNbPj7pB4oiwatGiRZV1DRs2rHKNjSS1a9cu6HFSUpKaNm1aZ6Uf0tLSNGPGDG3atEk+n08zZsyok/MCQCjqS1y98MIL1bRp08DX1X6/X6+99pqGDh2q5OTkWj8/6gaJI8LqeHcEhrvqU0VFRViOs2LFCknSzz//rH/9619hOSYAhFN9iatut1tXX321/uu//kvFxcVatWqV9uzZo9/+9rdh6iGiAYkjImbbtm1Bjw8dOqS9e/eqVatWgXUNGzZUfn5+0HalpaXau3dv0LoTqXm2fPlyvfDCC7r77ruVnp6u0aNHq7y8POTjAEC0iHRcve6661RYWKh33nlHCxYsUHp6ugYNGhTycRC9SBwRMc8995zKysoCj+fMmaPy8nINGTIksK5t27Zas2ZNlf2O/cs4MTFRkqoEw+PJz8/XjTfeqO7du+vRRx/VCy+8oM8++0yPPvroCT4bAIi8SMZVSercubM6d+6sF154Qf/1X/+lUaNGKTaW+3BPJryaiJjS0lL1799fI0eO1NatW/XMM8/o/PPPD7rz78Ybb9Qtt9yi4cOH68ILL9Q//vEPrVixQo0bNw46VteuXeV2u/X444+roKBAXq9X/fr1U5MmTao99x/+8AcdOHBAH3zwgdxutwYPHqwbb7xRDz/8sIYOHaouXbrU6nMHgNoQybha6brrrtNdd90lSXxNfRJixhERM2vWLHXs2FGTJ0/W/PnzddVVV2np0qVBX4/87ne/05/+9CetWbNGd955p3bs2KH3338/8JdwpczMTM2dO1f79+/XDTfcoKuuukqbN2+u9rz//d//rZdfflmPPPKITj/99MD6J598UllZWRo9enTQX+wAUF9EKq7+0jXXXCO326327dure/fuYX+OiCx+qxp1bv78+br++uv197//XWeffXakuwMA9V40xdUff/xRTZs21eTJk3X//fdHtC8IP2YcAQBA2MyfP18VFRW69tprI90V1AKucQQAADX24YcfavPmzXrkkUc0bNiwoDu5cfIgcQQAADX24IMP6uOPP1bPnj01c+bMSHcHtYRrHAEAAOAI1zgCAADAERJHAAAAOBJ11zj6/X7t2bNHycnJJ/RzRwBObsYYHTx4UFlZWYqJ4W9fJ4irAKyEFFdNLZk1a5Zp2bKl8Xq9pnv37uaTTz5xtN/u3buNJBYWFhbLZffu3bUVvqIWcZWFhaU2FydxtVZmHF9//XVNnDhRc+fOVY8ePTR9+nQNGjRIW7dutf2pouTk5NroUhUFBQWW7T6fr076cTKwG0vUL/XlvV9XsSJahCOunq+LFKu4uujuiTlZZkO55xT1TLnK9JH+6iiu1spd1T169NA555yjWbNmSTr6NUnz5s01YcIE3XPPPZb7FhYW1sk/XHZPm69znKuFtxAiqL689wsKCpSSkhLpbtSZcMTVvhqqWBeJY60jJqKeKTdlWq2ljuJq2C8QKi0t1YYNGzRgwIB/nyQmRgMGDNDatWurbF9SUqLC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n68xY8bI5/MpNTVVo0ePVn5+fpXtqruGyuVyafz48YFzeb1enXnmmVq+fHmV/VevXq2zzz5b8fHxatu2rZ599lmuy0JUqK1Y2rt376CksXJdWlqavv7666D1oXyWPvroI51zzjlBnyWn+vbtq06dOmnDhg0677zzAjFx7ty5Vbbdv3+/brjhBmVkZCg+Pl5dunTRSy+9VGW7RYsWqVu3bkpOTlZKSorOOussPf3007Z98fv9mj59us4880zFx8crIyNDN998c5W416pVK11yySWBGJKQkKCzzjorcAnVW2+9pbPOOkvx8fHq1q2bPv/882rP9+2332rQoEFKTExUVlaWHnzwQRljTqhPxhg9/PDDatasmRo0aKALLrig2jgsSV999ZX69eunhIQENWvWTA8//LD8fn+V7Y69xnH16tVyuVx644039Mgjj6hZs2aKj49X//79tX379ir7z549W23atFFCQoK6d++u//3f/z35rps0qLF169YZt9tt7rjjjsC6UaNGmYSEBLN169bAuh07dhhJZvTo0SEdf/HixUaSWbVqVZW2L7/80ng8HnP22Webp59+2sydO9fcddddpnfv3sYYY/Ly8syDDz5oJJmbbrrJvPLKK+aVV14x33zzzXHP98ADDxhJ5qyzzjKXXnqpmTVrlvntb39rJJlrr702aNvRo0cbSWbEiBFm9uzZ5rrrrjOSzLBhwwLb7Nu3zzRs2NC0b9/eTJs2zTz//PPmz3/+s+nYsaPtc3/55ZeNy+UygwcPNjNnzjSPP/64adWqlUlNTTU7duwI6kd8fLw544wzzC233GJmz55tzjvvPCPJzJs3z2RlZZk//vGPZubMmebMM880brfbfPvtt4H9582bF3jOvXr1MjNmzDDjxo0zMTExpnfv3sbv94fcpxUrVpiYmBjTqVMn8+STT5o///nPxufzmTPPPNO0bNkysJ3f7ze9e/c2MTEx5rbbbjMzZ840/fr1M507dw70/9jX5pckmS5dupimTZuahx56yEyfPt20adPGNGjQwPz444+B7T777DPj9XpNq1atzGOPPWYeeeQRk5WVZbp06VLlmEAk1HYsrXTw4EHj8XjMTTfdFLTe6Wdp06ZNJiEhwbRo0cLk5OSYhx56yGRkZAQ+s3b69OljsrKyTJMmTcz48ePNjBkzzPnnn28kmRdffDGw3eHDh03Hjh1NXFycueOOO8yMGTNMr169jCQzffr0wHbvvfeekWT69+9vZs+ebWbPnm3Gjx9vrrzyStu+3HjjjSY2Ntb87ne/M3PnzjV/+tOfTGJiojnnnHNMaWlpYLuWLVuaDh06mKZNm5opU6aYp556yvzqV78ySUlJ5tVXXzUtWrQwjz32mHnssceMz+czp512mqmoqAjsXxmj27VrZ6699loza9Ysc8kllxhJ5v777z+hPt13331GkrnooovMrFmzzNixY01WVpZp3Lhx0Htj7969Jj093TRs2NBMmTLFTJs2zbRr1y7wev0ybvfp08f06dMn8HjVqlVGkvn1r39tunXrZp566ikzZcoU06BBA9O9e/egfj/zzDNGUuDfkIkTJ5q0tDTTtm3boGPWd/xrESaTJk0yMTExZs2aNYFE75cfbGNqJ3F86qmnjCTzww8/HHf/v//971USECuVyclll10WtP62224zksw//vEPY4wxGzduNJLMjTfeGLTdXXfdZSSZDz/80BhjzJIlS4wk8/e//93R+SsdPHjQpKammt/97ndB6/Py8ozP5wtaX5nAPvroo4F1P//8s0lISDAul8ssWrQosH7Lli1GknnggQcC6yoTx27dugUFpieeeMJIMkuXLg25T127djVNmzY1+fn5gXWVAf6XiePbb79tJJknnngisK68vDzwD4STxNHj8Zjt27cH1v3jH/8wkszMmTMD6y699FLToEED8/333wfWbdu2zcTGxpI4ImrUZiyt9NBDDxlJZuXKlUHrnX6Whg0bZuLj4813330XWLd582bjdrsdJ46SzF/+8pfAupKSEtO1a1fTpEmTQAyaPn26kWReffXVwHalpaUmOzvbJCUlmcLCQmOMMX/4wx9MSkqKKS8vD2kc/vd//9dIMgsWLAhav3z58irrW7ZsaSSZjz/+OLBuxYoVRpJJSEgIGotnn322yr9ZlTF6woQJgXV+v99cfPHFxuPxBP4Nc9qn/fv3G4/HYy6++OKgP+zvvffeKu+N22+/3Ugyn3zySWDd/v37jc/nc5w4duzY0ZSUlATWP/3000aS+eKLL4wxR1+/Ro0amXPOOceUlZUFtps/f76RROKIqkpKSsxZZ51lWrdubdLT002fPn2C3sw1YZU4ViY8L7zwQtBfd790oonjihUrgtZ//fXXRpLJyckxxhjz6KOPGklm8+bNQdvt3bvXSDJ33nmnMebfH7wHHnggKCmz89ZbbwUS0B9++CFoGThwoDnttNMC21YGpf379wcdo2vXriYpKanKa5Gamho0e1o5js8++2zQdgcPHjSxsbHm5ptvDqlPe/bsMZLMPffcU+V5nXHGGUGJ40033WRiY2PNwYMHg7Z74403HCeOF110UZXzpKSkBGZuysvLTUJCgrn66qurbHfppZeSOCJq1GYsNcaY3NxcExsba0aOHFmlLZTP0qhRo6psd9FFFzlOHGNjY82hQ4eC1s+ZM8dIMmvXrjXGGDNw4ECTmZlZJba/9tprRpJ55513jDFH44Lb7TbLli2zPfcv/f73vzc+n8/s37+/SjxLSkoKmhRo2bKlOeOMM4L2z8/PN5LMxRdfHLS+clLhl7OnlTH6lzPHxhizbNkyI8m89tprIfVp4cKFRpJZvnx50PH2799fJXFs3769Offcc6s8/8rJECeJ4y//sDfm6Dc4v5xU+Nvf/mYkmeeeey5ou7KyMtOwYcOTKnHkGscw8Xg8+s///E/t2LFDBw8e1Lx58+rkurHf/OY36tmzp2688UZlZGQE7his7tqNULVr1y7ocdu2bRUTExOoefXdd98pJiZGp512WtB2mZmZSk1N1XfffSdJ6tOnj4YPH66pU6eqcePGGjp0qObNm1flOshjbdu2TZLUr18/paenBy3vvfee9u/fH7R9fHy80tPTg9b5fD41a9asymvh8/mqvXbx2OeclJSkpk2bBp6z0z5VPvdjjydJHTp0CHr83XffqWnTplVqnB27nZUWLVpUWdewYcPAc9y/f7+OHDlS5bWSVO06IFJqM5Zu2bJFl19+uTp16qQXXnih2m3sPks//PCDjhw54uizbSUrK0uJiYlB69q3by9JQTG2Xbt2Va7R7NixY6Bdkm677Ta1b99eQ4YMUbNmzTR27Nhqr8s81rZt21RQUKAmTZpUiWeHDh2qEmOPHRufzydJat68ebXrj42xMTExatOmjeVzdtqn48XY9PR0NWzYMGhd5TgeqyYxtvIclc+xsj/HxtPY2FhHdXvrE27PDKMVK1ZIkoqLi7Vt2za1bt261s+ZkJCgNWvWaNWqVfqf//kfLV++XK+//rr69eun9957T263O2znOl7wtgvqLpdLb775ptatW6d33nlHK1as0NixY/WXv/xF69atO25R2Mrk95VXXlFmZmaV9mPvLj7ecz3eenPMBdlOhNqnuhLO5whEWm3E0t27d2vgwIHy+Xz661//quTk5Gq3q4+fpSZNmmjjxo1asWKFli1bpmXLlmnevHm67rrrqr2RppLf71eTJk20YMGCatuP/UO8rmJsKH2qK/XxfVFbSBzDZNOmTXrwwQd1/fXXa+PGjbrxxhv1xRdfBP7yqk0xMTHq37+/+vfvryeffFKPPvqo/vznP2vVqlUaMGDACf+1fmzA3r59u/x+f+Cvp5YtW8rv92vbtm2Bv4Alad++fcrPz1fLli2Djnfuuefq3HPP1SOPPKKFCxfqmmuu0aJFi3TjjTdWe/62bdtKOhoUBwwYcELPIVTbtm3TBRdcEHh86NAh7d27VxdddFFIfap87pUzlL+0devWKtuuXLlShw4dCkqij92uJpo0aaL4+Phq7wKsbh0QKbURSw8cOKCBAweqpKREK1euVNOmTU/4WOnp6UpISHD02bayZ88eFRUVBc06/vOf/5SkoBi7adMm+f3+oFnHLVu2BNoreTweXXrppbr00kvl9/t122236dlnn9X9999/3G8V2rZtqw8++EA9e/ZUQkKC476fKL/fr2+//TYwyyhVfc5O+/TLGPvLWcwffvihykxny5Yta/x62ansz/bt24P+DSkvL9fOnTvVuXPnsJ0r0viqOgzKyso0ZswYZWVl6emnn9b8+fO1b98+3XHHHVW2c1pCwqmffvqpyrquXbtKUuCr4MrAVF15FyuVpWwqzZw5U5I0ZMgQSQokU9OnTw/a7sknn5QkXXzxxZKOTuUf+1fZsX2szqBBg5SSkqJHH31UZWVlVdp/+OEHh8/Eueeeey7oXHPmzFF5eXngOTvtU9OmTdW1a1e99NJLKigoCLS///772rx5c9A+F110kcrLyzVnzpzAuoqKisB4h4Pb7daAAQP09ttva8+ePYH127dv17Jly8J2HqAmaiOWFhUV6aKLLtL333+vv/71r9V+ZRkKt9utQYMG6e2339auXbsC67/++uvATKkT5eXlQSV8SktL9eyzzyo9PV3dunWTdDQ25OXl6fXXXw/ab+bMmUpKSlKfPn0kHU2MfykmJiaQqFjF2JEjR6qiokIPPfRQtf0L9d8MJ2bNmhX4b2OMZs2apbi4OPXv3z+kPg0YMEBxcXGaOXNm0L8vx/57JB0dx3Xr1unTTz8NrPvhhx+OO6t5Is4++2w1atRIzz//vMrLywPrFyxYUO1lUfUZM45h8PDDD2vjxo1auXKlkpOT1blzZ02ePFn33XefRowYEUiwvv/+e3Xs2FGjR4+2rKP4y+NKCtSleuWVV/TRRx9JOvoLIpL04IMPas2aNbr44ovVsmVL7d+/X88884yaNWum888/X9LRv+BSU1M1d+5cJScnKzExUT169LD9+mfHjh267LLLNHjwYK1du1avvvqqrr76anXp0kWS1KVLF40ePVrPPfec8vPz1adPH3366ad66aWXNGzYsMBfXS+99JKeeeYZXX755Wrbtq0OHjyo559/XikpKYGxqU5KSormzJmja6+9Vv/xH/+hUaNGKT09Xbt27dL//M//qGfPnkFBKBxKS0vVv39/jRw5Ulu3btUzzzyj888/X5dddlnIfcrJydHFF1+s888/X2PHjtVPP/2kmTNn6swzz9ShQ4cC57z00kvVs2dP3XPPPdq5c6fOOOMMvfXWW0EJZzhMmTJF7733nnr27Klbb71VFRUVmjVrljp16sSvCiEq1EYsveaaa/Tpp59q7Nix+vrrr4NqNyYlJWnYsGEh93Pq1Klavny5evXqpdtuuy2QzJ155pnatGmTo2NkZWXp8ccf186dO9W+fXu9/vrr2rhxo5577jnFxcVJkm666SY9++yzGjNmjDZs2KBWrVrpzTff1N/+9jdNnz498HX7jTfeqJ9++kn9+vVTs2bN9N1332nmzJnq2rVr0LdBx+rTp49uvvlm5eTkaOPGjRo4cKDi4uK0bds2LV68WE8//bRGjBgR8vgcT3x8vJYvX67Ro0erR48eWrZsmf7nf/5H9957b+AraKd9Sk9P11133aWcnBxdcskluuiii/T5559r2bJlaty4cdB57777br3yyisaPHiw/vCHPygxMVHPPfdcYEY3HDwej6ZMmaIJEyaoX79+GjlypHbu3Kn58+erbdu2J1et3Mjdl3Ny2LBhg4mNjQ0qMWDM0TvvzjnnHJOVlWV+/vlnY0zoJSQkHXeptHLlSjN06FCTlZVlPB6PycrKMldddZX55z//GXSspUuXmjPOOCNQesXqDuvKO3c3b95sRowYYZKTk03Dhg3N+PHjzZEjR4K2LSsrM1OnTjWtW7c2cXFxpnnz5mbSpEmmuLg4sM1nn31m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\n" + }, + "metadata": {} + } + ], + "source": [ + "# Which sample to view\n", + "index = 10\n", + "\n", + "data = train_dataset[index]\n", + "# add a batch dimension to both x and y for preprocessor\n", + "data['x'] = data['x'].unsqueeze(0)\n", + "data['y'] = data['y'].unsqueeze(0)\n", + "\n", + "# preprocessing is normally done during training\n", + "data = data_processor.preprocess(data)\n", + "\n", + "# squeeze the batch dimension out of x and y for visualization\n", + "x = data['x'].squeeze(0)\n", + "print(x.shape)\n", + "y = data['y'].squeeze(0)\n", + "fig = plt.figure(figsize=(7, 7))\n", + "ax = fig.add_subplot(2, 2, 1)\n", + "ax.imshow(x[0], cmap='gray')\n", + "ax.set_title('input x')\n", + "ax = fig.add_subplot(2, 2, 2)\n", + "ax.imshow(y.squeeze())\n", + "ax.set_title('input y')\n", + "ax = fig.add_subplot(2, 2, 3)\n", + "ax.imshow(x[1])\n", + "ax.set_title('x: 1st pos embedding')\n", + "ax = fig.add_subplot(2, 2, 4)\n", + "ax.imshow(x[2])\n", + "ax.set_title('x: 2nd pos embedding')\n", + "fig.suptitle('Visualizing one input sample', y=0.98)\n", + "plt.tight_layout()\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "012a357f-8533-482c-823d-a4587c49e726", + "metadata": { + "id": "012a357f-8533-482c-823d-a4587c49e726" + }, + "outputs": [], + "source": [ + "import torch\n", + "import wandb\n", + "import sys\n", + "from configmypy import ConfigPipeline, YamlConfig, ArgparseConfig\n", + "from neuralop import get_model\n", + "from neuralop import Trainer\n", + "from neuralop.training import setup\n", + "from neuralop.datasets import load_darcy_pt\n", + "from neuralop.utils import get_wandb_api_key, count_model_params\n", + "from neuralop import LpLoss, H1Loss" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Downloading the configuration\n", + "\n", + "Go to: https://github.com/neuraloperator/neuraloperator/blob/main/config/darcy_config.yaml and download the config file. Make the following changes:\n", + "\n", + "\n", + "\n", + "1. Add an option called folder under the data key. The path should be the path to the darcy flow in your home directory. On colab it should just be: /content/data/darcy_flow.\n", + "2. Change the training resolution to 32 and the test resolutions to 32 and 64. It should just look like this below.\n", + "3. Under the tfno key, change the data channels to 1 as we have only the data in black and white and not RGB.\n", + "\n", + "![config-change.png](data:image/png;base64,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)\n" + ], + "metadata": { + "id": "IRWO3VHFGqZV" + }, + "id": "IRWO3VHFGqZV" + }, + { + "cell_type": "markdown", + "id": "9b6358b5-78d1-4baf-8928-6bb49b150680", + "metadata": { + "id": "9b6358b5-78d1-4baf-8928-6bb49b150680" + }, + "source": [ + "# Loading the configuration\n", + "\n", + "You can open the yaml file in config/darcy_config in the same folder as this notebook to inspect the parameters and change them." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "4503f065-4063-4a4f-b00f-06a7c3a88e27", + "metadata": { + "id": "4503f065-4063-4a4f-b00f-06a7c3a88e27" + }, + "outputs": [], + "source": [ + "# Read the configuration\n", + "config_name = 'default'\n", + "pipe = ConfigPipeline([YamlConfig('./darcy_config.yaml', config_name='default'),\n", + " ])\n", + "config = pipe.read_conf()\n", + "config_name = pipe.steps[-1].config_name" + ] + }, + { + "cell_type": "markdown", + "id": "e95d820d-9578-4ad7-80b4-05a5771f1642", + "metadata": { + "id": "e95d820d-9578-4ad7-80b4-05a5771f1642" + }, + "source": [ + "## Setup\n", + "\n", + "Here we just setup pytorch and print the configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "46066d9f-21a3-4aab-b6e1-f7f38e05f88b", + "metadata": { + "id": "46066d9f-21a3-4aab-b6e1-f7f38e05f88b" + }, + "outputs": [], + "source": [ + "# Set-up distributed communication, if using\n", + "device, is_logger = setup(config)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "26d599f9-6463-4056-9a4d-72c01d05298e", + "metadata": { + "id": "26d599f9-6463-4056-9a4d-72c01d05298e", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "bc3a1ac1-75f0-4205-b5d1-c42259122ef4" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "###############################\n", + "##### CONFIGURATION #####\n", + "###############################\n", + "\n", + "Steps:\n", + "------\n", + " (1) YamlConfig with config_file=./darcy_config.yaml, config_name=default, config_folder=.\n", + "\n", + "-------------------------------\n", + "\n", + "Configuration:\n", + "--------------\n", + "\n", + "n_params_baseline=None\n", + "verbose=True\n", + "arch=tfno2d\n", + "distributed.use_distributed=False\n", + "distributed.wireup_info=mpi\n", + "distributed.wireup_store=tcp\n", + "distributed.model_parallel_size=2\n", + "distributed.seed=666\n", + "tfno2d.data_channels=1\n", + "tfno2d.n_modes_height=16\n", + "tfno2d.n_modes_width=16\n", + "tfno2d.hidden_channels=32\n", + "tfno2d.projection_channels=64\n", + "tfno2d.n_layers=4\n", + "tfno2d.domain_padding=None\n", + "tfno2d.domain_padding_mode=one-sided\n", + "tfno2d.fft_norm=forward\n", + "tfno2d.norm=group_norm\n", + "tfno2d.skip=linear\n", + "tfno2d.implementation=factorized\n", + "tfno2d.separable=0\n", + "tfno2d.preactivation=0\n", + "tfno2d.use_mlp=1\n", + "tfno2d.mlp.expansion=0.5\n", + "tfno2d.mlp.dropout=0\n", + "tfno2d.factorization=None\n", + "tfno2d.rank=1.0\n", + "tfno2d.fixed_rank_modes=None\n", + "tfno2d.dropout=0.0\n", + "tfno2d.tensor_lasso_penalty=0.0\n", + "tfno2d.joint_factorization=False\n", + "tfno2d.fno_block_precision=full\n", + "tfno2d.stabilizer=None\n", + "opt.n_epochs=2\n", + "opt.learning_rate=0.005\n", + "opt.training_loss=h1\n", + "opt.weight_decay=0.0001\n", + "opt.amp_autocast=False\n", + "opt.scheduler_T_max=500\n", + "opt.scheduler_patience=5\n", + "opt.scheduler=StepLR\n", + "opt.step_size=60\n", + "opt.gamma=0.5\n", + "data.folder=/content/data/darcy_flow\n", + "data.batch_size=16\n", + "data.n_train=1000\n", + "data.train_resolution=32\n", + "data.n_tests=[100, 50]\n", + "data.test_resolutions=[32, 64]\n", + "data.test_batch_sizes=[16, 16]\n", + "data.positional_encoding=True\n", + "data.encode_input=True\n", + "data.encode_output=False\n", + "patching.levels=0\n", + "patching.padding=0\n", + "patching.stitching=False\n", + "wandb.log=False\n", + "wandb.name=None\n", + "wandb.group=\n", + "wandb.project=\n", + "wandb.entity=\n", + "wandb.sweep=False\n", + "wandb.log_output=True\n", + "wandb.log_test_interval=1\n", + "\n", + "###############################\n" + ] + } + ], + "source": [ + "# Make sure we only print information when needed\n", + "config.verbose = config.verbose and is_logger\n", + "\n", + "#Print config to screen\n", + "if config.verbose and is_logger:\n", + " pipe.log()\n", + " sys.stdout.flush()" + ] + }, + { + "cell_type": "markdown", + "id": "1339c794-3e1c-469b-b0a0-cf968fc1dfa1", + "metadata": { + "id": "1339c794-3e1c-469b-b0a0-cf968fc1dfa1" + }, + "source": [ + "# Loading the data\n", + "\n", + "We train in one resolution and test in several resolutions to show the zero-shot super-resolution capabilities of neural-operators." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "3515a85a-40fc-4223-9cdb-8768de37d6e2", + "metadata": { + "id": "3515a85a-40fc-4223-9cdb-8768de37d6e2", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7a6028bc-0c9a-4699-8c8c-bf53304d2b7f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Loading test db at resolution 64 with 50 samples and batch-size=16\n" + ] + } + ], + "source": [ + "# Loading the Darcy flow training set in 32x32 resolution, test set in 32x32 and 64x64 resolutions\n", + "train_loader, test_loaders, output_encoder = load_darcy_pt(\n", + " config.data.folder, train_resolution=config.data.train_resolution, n_train=config.data.n_train, batch_size=config.data.batch_size,\n", + " positional_encoding=config.data.positional_encoding,\n", + " test_resolutions=config.data.test_resolutions, n_tests=config.data.n_tests, test_batch_sizes=config.data.test_batch_sizes,\n", + " encode_input=config.data.encode_input, encode_output=config.data.encode_output,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "8109298a-aca3-45b7-a8de-c5cf4e1c210b", + "metadata": { + "id": "8109298a-aca3-45b7-a8de-c5cf4e1c210b" + }, + "source": [ + "# Creating the model and putting it on the GPU" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "db295d23-ab86-4f37-83cc-7af0a8e485ea", + "metadata": { + "id": "db295d23-ab86-4f37-83cc-7af0a8e485ea", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "3400fee1-42b7-4060-f6ab-0b9fae360f4d" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "n_params: 1199713\n" + ] + } + ], + "source": [ + "model = get_model(config)\n", + "model = model.to(device)\n", + "\n", + "#Log parameter count\n", + "if is_logger:\n", + " n_params = count_model_params(model)\n", + "\n", + " if config.verbose:\n", + " print(f'\\nn_params: {n_params}')\n", + " sys.stdout.flush()" + ] + }, + { + "cell_type": "markdown", + "id": "fec85d0a-4db4-4b1f-b599-8c2afc98520a", + "metadata": { + "id": "fec85d0a-4db4-4b1f-b599-8c2afc98520a" + }, + "source": [ + "# Create the optimizer and learning rate scheduler\n", + "\n", + "Here, we use an Adam optimizer and a learning rate schedule depending on the configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "5164537a-267b-4fda-9bcd-257dc3ac4826", + "metadata": { + "id": "5164537a-267b-4fda-9bcd-257dc3ac4826" + }, + "outputs": [], + "source": [ + "#Create the optimizer\n", + "optimizer = torch.optim.Adam(model.parameters(),\n", + " lr=config.opt.learning_rate,\n", + " weight_decay=config.opt.weight_decay)\n", + "\n", + "if config.opt.scheduler == 'ReduceLROnPlateau':\n", + " scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=config.opt.gamma, patience=config.opt.scheduler_patience, mode='min')\n", + "elif config.opt.scheduler == 'CosineAnnealingLR':\n", + " scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=config.opt.scheduler_T_max)\n", + "elif config.opt.scheduler == 'StepLR':\n", + " scheduler = torch.optim.lr_scheduler.StepLR(optimizer,\n", + " step_size=config.opt.step_size,\n", + " gamma=config.opt.gamma)\n", + "else:\n", + " raise ValueError(f'Got {config.opt.scheduler=}')" + ] + }, + { + "cell_type": "markdown", + "id": "e52a72eb-965a-4997-89a4-0cdfcbcb0a1a", + "metadata": { + "id": "e52a72eb-965a-4997-89a4-0cdfcbcb0a1a" + }, + "source": [ + "# Creating the loss\n", + "\n", + "We will optimize the Sobolev norm but also evaluate our goal: the l2 relative error" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "07a53d9d-2d06-4d36-9b46-2c7f15f29c40", + "metadata": { + "id": "07a53d9d-2d06-4d36-9b46-2c7f15f29c40" + }, + "outputs": [], + "source": [ + "# Creating the losses\n", + "l2loss = LpLoss(d=2, p=2)\n", + "h1loss = H1Loss(d=2)\n", + "if config.opt.training_loss == 'l2':\n", + " train_loss = l2loss\n", + "elif config.opt.training_loss == 'h1':\n", + " train_loss = h1loss\n", + "else:\n", + " raise ValueError(f'Got training_loss={config.opt.training_loss} but expected one of [\"l2\", \"h1\"]')\n", + "eval_losses={'h1': h1loss, 'l2': l2loss}" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "5dad660e-43e9-4f38-91f6-8427b14b8ae0", + "metadata": { + "id": "5dad660e-43e9-4f38-91f6-8427b14b8ae0", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "88714486-3734-4f29-deeb-fe1862def46c" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "### MODEL ###\n", + " TFNO2d(\n", + " (fno_blocks): FNOBlocks(\n", + " (convs): SpectralConv(\n", + " (weight): ModuleList(\n", + " (0-3): 4 x ComplexDenseTensor(shape=torch.Size([32, 32, 16, 9]), rank=None)\n", + " )\n", + " )\n", + " (fno_skips): ModuleList(\n", + " (0-3): 4 x Conv2d(32, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " )\n", + " (mlp): ModuleList(\n", + " (0-3): 4 x MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(16, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (mlp_skips): ModuleList(\n", + " (0-3): 4 x SoftGating()\n", + " )\n", + " (norm): ModuleList(\n", + " (0-7): 8 x GroupNorm(1, 32, eps=1e-05, affine=True)\n", + " )\n", + " )\n", + " (lifting): MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(1, 256, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(256, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " (projection): MLP(\n", + " (fcs): ModuleList(\n", + " (0): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " (1): Conv2d(64, 1, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + ")\n", + "\n", + "### OPTIMIZER ###\n", + " Adam (\n", + "Parameter Group 0\n", + " amsgrad: False\n", + " betas: (0.9, 0.999)\n", + " capturable: False\n", + " differentiable: False\n", + " eps: 1e-08\n", + " foreach: None\n", + " fused: None\n", + " initial_lr: 0.005\n", + " lr: 0.005\n", + " maximize: False\n", + " weight_decay: 0.0001\n", + ")\n", + "\n", + "### SCHEDULER ###\n", + " \n", + "\n", + "### LOSSES ###\n", + "\n", + " * Train: \n", + "\n", + " * Test: {'h1': , 'l2': }\n", + "\n", + "### Beginning Training...\n", + "\n" + ] + } + ], + "source": [ + "if config.verbose and is_logger:\n", + " print('\\n### MODEL ###\\n', model)\n", + " print('\\n### OPTIMIZER ###\\n', optimizer)\n", + " print('\\n### SCHEDULER ###\\n', scheduler)\n", + " print('\\n### LOSSES ###')\n", + " print(f'\\n * Train: {train_loss}')\n", + " print(f'\\n * Test: {eval_losses}')\n", + " print(f'\\n### Beginning Training...\\n')\n", + " sys.stdout.flush()" + ] + }, + { + "cell_type": "markdown", + "id": "b5967441-b8bc-4ea8-a4d9-7a5bea384cbf", + "metadata": { + "id": "b5967441-b8bc-4ea8-a4d9-7a5bea384cbf" + }, + "source": [ + "# Creating the trainer" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "a19ebfd3-8a2b-42c0-af98-7a1db2dda0f6", + "metadata": { + "id": "a19ebfd3-8a2b-42c0-af98-7a1db2dda0f6", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "b3a644be-de61-42b1-f3d5-74aa9f781ee4" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "self.override_load_to_device=False\n", + "self.overrides_loss=False\n" + ] + } + ], + "source": [ + "trainer = Trainer(model=model, n_epochs=config.opt.n_epochs,\n", + " device=device,\n", + " wandb_log=config.wandb.log,\n", + " log_test_interval=config.wandb.log_test_interval,\n", + " log_output=False,\n", + " use_distributed=config.distributed.use_distributed,\n", + " verbose=config.verbose and is_logger)" + ] + }, + { + "cell_type": "markdown", + "id": "b16a3727-313d-4219-8f8f-0cec58d74b00", + "metadata": { + "id": "b16a3727-313d-4219-8f8f-0cec58d74b00" + }, + "source": [ + "# Training the model" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "0d6e3298-99ee-4371-8bad-60e6aac03d56", + "metadata": { + "id": "0d6e3298-99ee-4371-8bad-60e6aac03d56", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "157c95e8-be98-41a8-8eea-a83a25362c86" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'64_h1': 0.6382459831237793, '64_l2': 0.4895939874649048}" + ] + }, + "metadata": {}, + "execution_count": 61 + } + ], + "source": [ + "trainer.train(train_loader, test_loaders,\n", + " optimizer,\n", + " scheduler,\n", + " regularizer=False,\n", + " training_loss=train_loss,\n", + " eval_losses=eval_losses)" + ] + }, + { + "cell_type": "markdown", + "id": "1b20be56-d200-44dc-b97b-fca021e353c8", + "metadata": { + "id": "1b20be56-d200-44dc-b97b-fca021e353c8" + }, + "source": [ + "# Follow-up questions" + ] + }, + { + "cell_type": "markdown", + "id": "9a67e1d5-4b9a-4be3-bff4-fb2a6b152f9c", + "metadata": { + "id": "9a67e1d5-4b9a-4be3-bff4-fb2a6b152f9c" + }, + "source": [ + "You can now play with the configuration and see how the performance is impacted.\n", + "\n", + "Which parameters do you think will most influence performance?\n", + "Learning rate? Learning schedule? hidden_channels? Number of training samples?\n", + "\n", + "Does your intuition match the results you are getting?" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + }, + "vscode": { + "interpreter": { + "hash": "95d4b27ba6bfea4a66eebe0e0159b214d32a94d313a7f4c98bd9b87f5ee37cbe" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/3-darcy_flow.ipynb b/3-darcy_flow.ipynb deleted file mode 100644 index 6da114d..0000000 --- a/3-darcy_flow.ipynb +++ /dev/null @@ -1,198 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "id": "4df7dcda-a364-4255-9339-a9a09c2a5e34", - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "from neuralop.datasets import load_darcy_pt" - ] - }, - { - "cell_type": "markdown", - "id": "ff12d431-bde9-4eba-906b-d0faea8c49fb", - "metadata": {}, - "source": [ - "# Load the data " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "aa9c49f5-878b-4cac-9a35-b9dc53085d11", - "metadata": {}, - "outputs": [], - "source": [ - "data_path=\"/dli/task/bootcamp/data/darcy_flow/\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f40e8c0a-c031-457b-863c-c728de7d1b80", - "metadata": {}, - "outputs": [], - "source": [ - "train_loader, test_loaders, output_encoder = load_darcy_pt(data_path, n_train=100, n_tests=[10], \n", - " batch_size=3, test_batch_sizes=[3],\n", - " test_resolutions=[32], train_resolution=32)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "2f29d90a-4fc7-4b83-8ed4-f1bb4dce2574", - "metadata": {}, - "outputs": [], - "source": [ - "train_dataset = train_loader.dataset" - ] - }, - { - "cell_type": "markdown", - "id": "21000189-ecac-42e2-b008-06eefa7b1710", - "metadata": {}, - "source": [ - "# Visualizing the data " - ] - }, - { - "cell_type": "markdown", - "id": "1cf47f09-1fb3-4667-9b04-a98b3ee8d08d", - "metadata": {}, - "source": [ - "The data is stored in a dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "b6a9aed5-6532-42ba-8131-0307460c960d", - "metadata": {}, - "outputs": [], - "source": [ - "data = train_dataset[0]\n", - "x = data['x']\n", - "y = data['y']" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "9f475172-62b0-4ce3-8dce-a7d0d9dca9fb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "torch.Size([3, 128, 128])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x.shape" - ] - }, - { - "cell_type": "markdown", - "id": "7d7947ad-f98c-414b-8a12-64270988ad1f", - "metadata": {}, - "source": [ - "`x` is of shape (3, height, width). \n", - "\n", - "This is because, in addition to the binary input, we appended a positional encoding, so the model knows the location of each pixel.\n", - "\n", - "Let's check the actual data:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "077ebd7d-883b-4300-b13d-ed88813a3be1", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "814a044d-a52f-4cf2-aa1b-859370012af5", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqIAAAKzCAYAAADFi2/NAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9e7wcRZ02/lT1zDlJIAQSQi5ySQJBBARFLnKToICLgCiL7IpcRFbxRVRkd5GIaPDVRNGF+C6iL6ABiSCioKioyarg+oISEBcFfoACAYQQgZCEXE7OdNXvj7p0VXV1T8+cmTPnJN8nn86crq6uru7pmX7m+d6YlFKCQCAQCAQCgUAYZvBeT4BAIBAIBAKBsGWCiCiBQCAQCAQCoScgIkogEAgEAoFA6AmIiBIIBAKBQCAQegIiogQCgUAgEAiEnoCIKIFAIBAIBAKhJyAiSiAQCAQCgUDoCYiIEggEAoFAIBB6AiKiBAKBQCAQCISegIgogdAFvPvd78bYsWPxyiuvFPZ53/veh3q9jhdeeAHXXXcdGGN46qmnhm2OMTz11FNgjOG6666zbe3Obc6cOZgzZ05H5zda8f73vx8zZszo9TQsHn74YcybN6/n91u3MW/ePDDGej0NAoFQAiKiBEIXcPbZZ2Pjxo248cYbo9tXr16N2267DccffzymTJmC4447Dvfccw+mTZs2zDNtjnbndtVVV+Gqq67q0qxGFy655BLcdtttvZ6GxcMPP4xLL710syeiBAJh5KPW6wkQCJsjjj32WEyfPh3f+ta3cO655+a233TTTdiwYQPOPvtsAMDkyZMxefLk4Z5mJbQ7tz333LMLsxmd2HXXXXs9BQKBQBiRIEWUQOgCkiTBmWeeifvvvx9/+tOfctsXLVqEadOm4dhjjwUQN38/8MADOP7447HDDjugv78f06dPx3HHHYdnn30WQNyMbsAYw7x58+z6X/7yF5x11lmYPXs2xo0bh9e85jU44YQTonMLEc7tzjvvBGMsurjm59A0b+b7la98BZdffjlmzpyJrbfeGgcffDB+97vf5Y57zTXXYPfdd0d/fz/23HNP3HjjjZVN3EIIXHbZZdhjjz3Q39+PHXbYAWeccYa9du4c9957byxbtgyHH344xo0bh1mzZuGLX/wihBBe3zVr1uDf/u3fMHPmTPT19eE1r3kNzj//fKxbt67pfGLzZozhvPPOww033IDXve51GDduHPbdd1/85Cc/8foZ8/IDDzyAk046Cdtssw0mTJiA0047DX//+99zY7rvu8GMGTPw/ve/H4B6P9/znvcAAI488kj73sXuI4O///3v+NCHPoSddtoJ/f39mDx5Mg499FD813/9l+2zdOlSnHjiidhxxx0xZswY7LbbbjjnnHPw4osvRs/nwQcfxHve8x5MmDABEydOxAUXXIBGo4FHH30U//AP/4Dx48djxowZuOyyy7z9zf23ePFiXHDBBZg6dSrGjh2LI444Ag888EDhObi4+eabcfDBB2OrrbbC1ltvjbe//e2V9yUQCJ0FEVECoUv4wAc+AMYYvvWtb3ntDz/8MO69916ceeaZSJIkuu+6detw9NFH44UXXsDXvvY1LF26FAsXLsTOO++MtWvXtjyX5557DpMmTcIXv/hF/PznP8fXvvY11Go1HHTQQXj00UdbGmu//fbDPffc4y3f/va3Ua/XsddeezXd3z2f73znO1i3bh3e8Y53YPXq1bbP1VdfjQ996EPYZ599cOutt+LTn/40Lr30Utx5552V5vi//tf/wic/+UkcffTRuP322/G///f/xs9//nMccsghOWK0YsUKvO9978Npp52G22+/Hcceeyzmzp2LxYsX2z7r16/HEUccgeuvvx4f+9jH8LOf/Qyf/OQncd111+Gd73wnpJTVLl6An/70p7jyyivxuc99Dj/4wQ8wceJEvPvd78YTTzyR6/vud78bu+22G77//e9j3rx5+OEPf4i3v/3tGBwcbOmYxx13HObPnw9AvRfmPTzuuOMK9zn99NPxwx/+EJ/5zGewZMkSXHvttTjqqKPw0ksv2T5//etfcfDBB+PrX/86lixZgs985jP4/e9/j8MOOyw6x1NOOQX77rsvfvCDH+CDH/wgrrjiCnziE5/Au971Lhx33HG47bbb8Na3vhWf/OQnceutt+b2/9SnPoUnnngC1157La699lo899xzmDNnTvTauZg/fz7e+973Ys8998T3vvc93HDDDVi7di0OP/xwPPzww1UvI4FA6BQkgUDoGo444gi5/fbby02bNtm2f/3Xf5UA5GOPPWbbFi1aJAHIJ598Ukop5X333ScByB/+8IeFYz/55JMSgFy0aFFuGwD52c9+tnDfRqMhN23aJGfPni0/8YlPlI4Zzi3ECy+8IGfNmiX32msvuWrVKu/cjzjiiNzYr3/962Wj0bDt9957rwQgb7rpJimllGmayqlTp8qDDjrIO87y5ctlvV6Xu+yyS+F5SSnlI488IgHIc88912v//e9/LwHIT33qU94cAcjf//73Xt8999xTvv3tb7frCxYskJxzuWzZMq/f97//fQlA3nHHHaVzOvPMM3PzBiCnTJki16xZY9tWrFghOedywYIFtu2zn/2sBOC9T1JK+Z3vfEcCkIsXL/bGjL3vu+yyizzzzDPt+i233CIByF//+tel8zbYeuut5fnnn1+pr5RSCiHk4OCgXL58uQQgf/SjH+XO5z/+4z+8fd7whjdIAPLWW2+1bYODg3Ly5MnypJNOsm2//vWvJQC53377SSGEbX/qqadkvV6X//Iv/5I7lsHTTz8ta7Wa/OhHP+ode+3atXLq1KnylFNOqXyOBAKhMyBFlEDoIs4++2y8+OKLuP322wEAjUYDixcvxuGHH47Zs2cX7rfbbrthu+22wyc/+Ul84xvfGLJS02g0MH/+fOy5557o6+tDrVZDX18fHn/8cTzyyCNtj7tu3Tocd9xx2LhxI372s59h2223bbrPcccd5ynB++yzDwBg+fLlAIBHH30UK1aswCmnnOLtt/POO+PQQw9tOv6vf/1rALCmaIMDDzwQr3vd6/DLX/7Sa586dSoOPPBAr22fffax8wGAn/zkJ9h7773xhje8AY1Gwy5vf/vbwRirrNSGOPLIIzF+/Hi7PmXKFOywww7esQ3e9773eeunnHIKarWaPd9u4sADD8R1112Hz3/+8/jd734XVThXrlyJD3/4w9hpp51Qq9VQr9exyy67AED0Hjv++OO99de97nVgjFl3FQCo1WrYbbfdotfj1FNP9SLid9llFxxyyCGl1+MXv/gFGo0GzjjjDO99HDNmDI444oi230cCgdA+iIgSCF3EySefjAkTJmDRokUAgDvuuAMvvPCCDVIqwoQJE3DXXXfhDW94Az71qU9hr732wvTp0/HZz362ZVMsAFxwwQW45JJL8K53vQs//vGP8fvf/x7Lli3Dvvvuiw0bNrR1bo1GAyeffDIee+wx3HHHHdhpp50q7Tdp0iRvvb+/HwDsPIy5d8qUKbl9Y20hzP6xKP/p06d75uTYfMyc3Ovywgsv4MEHH0S9XveW8ePHQ0qZM/dXRZVjG0ydOtVbr9VqmDRpUu58uoGbb74ZZ555Jq699locfPDBmDhxIs444wysWLECgPLJPeaYY3DrrbfiwgsvxC9/+Uvce++91vc3dj4TJ0701vv6+jBu3DiMGTMm175x48bc/uH1MG1l1+OFF14AABxwwAG59/Lmm29u+30kEAjtg6LmCYQuYuzYsXjve9+La665Bs8//zy+9a1vYfz48TZYpAyvf/3r8d3vfhdSSjz44IO47rrr8LnPfQ5jx47FRRddZB/YAwMD3n6xB/HixYtxxhlnWN9AgxdffLGSihnDhz70Ifzyl7/EHXfcgX333betMWIw5MyQBheG+FTZ//nnn8eOO+7obXvuueew/fbbtzyn7bffHmPHjs35+7rbu40VK1bgNa95jV1vNBp46aWXPDLb39+fux+A+D3RCrbffnssXLgQCxcuxNNPP43bb78dF110EVauXImf//zn+POf/4z/+Z//wXXXXYczzzzT7veXv/xlSMctQ+xeWLFiRZTcG5j36fvf/75VawkEQm9BiiiB0GWcffbZSNMUX/7yl3HHHXfgn//5nzFu3LjK+zPGsO++++KKK67Atttuiz/84Q8AlDo4ZswYPPjgg17/H/3oR9ExjPJo8NOf/hR/+9vf2jgj4NOf/jQWLVpkg1Y6ide+9rWYOnUqvve973ntTz/9NO6+++6m+7/1rW8FAC/YCACWLVuGRx55BG9729tantPxxx+Pv/71r5g0aRL233//3DIcyeq/853veOvf+9730Gg0vMwEM2bMyN0Pv/rVr/Dqq696baEK3Qp23nlnnHfeeTj66KPtvWhM5OE99n//7/9tefyquOmmm7wgseXLl+Puu+8uLaLw9re/HbVaDX/961+j7+P+++/ftfkSCIQ4SBElELqM/fffH/vssw8WLlwIKWVTszygfBKvuuoqvOtd78KsWbMgpcStt96KV155BUcffTQA9fA/7bTT8K1vfQu77ror9t13X9x7773RJPrHH388rrvuOuyxxx7YZ599cP/99+PLX/5yTjGsgltuuQVf+MIXcPLJJ2P33Xf3Ui/19/fjjW98Y8tjuuCc49JLL8U555yDk08+GR/4wAfwyiuv4NJLL8W0adPAefnv59e+9rX40Ic+hP/8z/8E5xzHHnssnnrqKVxyySXYaaed8IlPfKLlOZ1//vn4wQ9+gLe85S34xCc+gX322QdCCDz99NNYsmQJ/vVf/xUHHXRQu6dcCbfeeitqtRqOPvpoPPTQQ7jkkkuw7777er60p59+Oi655BJ85jOfwRFHHIGHH34YV155JSZMmOCNtffeewNQ2QnGjx+PMWPGYObMmVE1cfXq1TjyyCNx6qmnYo899sD48eOxbNky/PznP8dJJ50EANhjjz2w66674qKLLoKUEhMnTsSPf/xjLF26tGvXY+XKlXj3u9+ND37wg1i9ejU++9nPYsyYMZg7d27hPjNmzMDnPvc5XHzxxXjiiSfwD//wD9huu+3wwgsv4N5778VWW22FSy+9tGtzJhAIeRARJRCGAWeffTY+/vGPY88996xEWGbPno1tt90Wl112GZ577jn09fXhta99bc70+R//8R8AgMsuuwyvvvoq3vrWt+InP/lJTqH76le/inq9jgULFuDVV1/FfvvtZ9MitYqHHnoIgDJvfv/73/e27bLLLh2p1vOhD30IjDFcdtllePe7340ZM2bgoosuwo9+9CM8/fTTTff/+te/jl133RXf/OY38bWvfQ0TJkzAP/zDP2DBggWlptsibLXVVvjv//5vfPGLX8TVV1+NJ598EmPHjsXOO++Mo446algU0VtvvRXz5s3D17/+dTDGcMIJJ2DhwoXo6+uzff793/8da9aswXXXXYevfOUrOPDAA/G9730PJ554ojfWzJkzsXDhQnz1q1/FnDlzkKYpFi1alAvwAoAxY8bgoIMOwg033ICnnnoKg4OD2HnnnfHJT34SF154IQCgXq/jxz/+MT7+8Y/jnHPOQa1Ww1FHHYX/+q//ws4779yV6zF//nwsW7YMZ511FtasWYMDDzwQ3/3ud5sWD5g7dy723HNPfPWrX8VNN92EgYEBTJ06FQcccAA+/OEPd2WuBAKhGEzKNhPgEQgEwjDilVdewe677453vetduPrqq3s9nWHDvHnzcOmll+Lvf//7sPiijnTceeedOPLII3HLLbfg5JNP7vV0CATCEEGKKIFAGHFYsWIFvvCFL+DII4/EpEmTsHz5clxxxRVYu3YtPv7xj/d6egQCgUDoEIiIEgiEEYf+/n489dRTOPfcc/Hyyy9j3LhxePOb34xvfOMblao3EQgEAmF0gEzzBAKBQCAQCISegNI3EQgEAoFAIBB6AiKiBAKBQCAQCISegIgogUAgEAgEAqEnICJKIBAIBAKBQOgJiIgSCAQCgUAgEHoCIqIEAoFAIBAIhJ6AiCiBQCAQCAQCoScgIkogEAgEAoFA6AmIiBIIBAKBQCAQegIiogQCgUAgEAiEnoCIKIFAIBAIBAKhJyAiSiAQCAQCgUDoCYiIEggEAoFAIBB6AiKiBAKBQCAQCISegIgogUAgEAgEAqEnICJK6Dquu+46MMbw1FNP9XoqAID169dj3rx5uPPOO3s9FQKBQGgL9L1K2FxARJTQdRx33HG45557MG3atF5PBYD6wrz00kvpC5NAIIxa0PcqYXNBrdcTIGz+mDx5MiZPntzraRAIBMJmA/peJWwuIEWU0HXETEhz5szB3nvvjWXLluHwww/HuHHjMGvWLHzxi1+EEML2u/POO8EYw+LFi3HBBRdg6tSpGDt2LI444gg88MAD3nHmzJmDOXPm5I7//ve/HzNmzAAAPPXUU/bL+9JLLwVjDIwxvP/97y+c/4c//GGMGTMG999/v20TQuBtb3sbpkyZgueff771i0IgEAhDwGj+Xn311Vex7bbb4pxzzslte+qpp5AkCb785S+3dkEIoxZERAk9w4oVK/C+970Pp512Gm6//XYce+yxmDt3LhYvXpzr+6lPfQpPPPEErr32Wlx77bV47rnnMGfOHDzxxBMtHXPatGn4+c9/DgA4++yzcc899+Cee+7BJZdcUrjPwoUL8brXvQ6nnHIKXnnlFQCwJqjFixePGNMYgUAgjIbv1a233hof+MAH8J3vfAerV6/2tl111VXo6+vDBz7wgZbmQBi9INM8oWd46aWXcMcdd+DAAw8EABx11FG48847ceONN+KMM87w+k6ePBm33XYbGGMAgMMOOwyzZ8/GggULcM0111Q+Zn9/P970pjcBAHbccUe8+c1vbrrPmDFjcMstt+BNb3oTzjrrLJx33nn4/Oc/j0996lM4+uijKx+bQCAQuo3R8r163nnn4atf/SoWLVqE888/HwCwceNGfOtb38J73/teTJo0qfLxCaMbpIgSeoapU6faL0uDffbZB8uXL8/1PfXUU+2XJQDssssuOOSQQ/DrX/+66/MEgN122w3XXHMNfvjDH+L444/H4Ycfjnnz5g3LsQkEAqEqRsv36qxZs3D88cfjqquugpQSAHDjjTfipZdewnnnndf14xNGDoiIEnqG2C/e/v5+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4mL86Ec/wj/+8Q/XPF988QUuu+wylJeXIzc3FyUlJTj77LOTqtJ+8MEHGDt2LIqLi5GXl4eBAwfixRdfjNrmFStW4LrrrkPHjh3Rvn17TJw4Efv27UNtbS3Gjx+Po48+GmVlZbjjjjvQ1NQUsS5d13H//feje/fuyMvLw5AhQ/DXv/41Yr7NmzdjwoQJ6Ny5M3Jzc3H88cfjd7/7XcR8//znP3HeeeehXbt26NSpE2644Qbs2bMnYj4Rwfz581FRUYG8vDwMGjQIf/nLXyLmi3coaePGjbj88stRVFSEkpISTJ482fVvEgC+//57XHPNNSguLsZRRx2FCy64AF988UXCfxNEba2tYml4EgoA5eXl6Nq1K7Zt2+aarpTCLbfcgqeffhrHH3882rVrhwEDBuC1116LWMbrr7+Ok046Cbm5uejZs2eLTuupqqpC//798e677+LUU09Ffn4+unTpgnvuuSdiG7/77jvcdNNN6NKlC3JycnDMMcdgxowZaGhocM0X73sgHhHBI488gpNOOgn5+fno0KEDfvKTn0TEeqvN7733HoYPH26f5rBo0SL78xg0aBDatWuHyspKvPnmm1HXt23bNowbNw7t27dHUVERfvrTn9pFF6cXXngBw4YNQ0FBAY466iice+652LBhQ8R8ixcvxnHHHWfH4aeeeirqerdv347x48ejsLAQRUVFuPTSS1FbWxsxX7RD8z169MCYMWPw5ptvYtCgQcjPz0ffvn3xxBNPRLx/9erVGDZsGPLy8ux9+vjjj0MphS+//DJq2zKWUMp89tlnUlhYKOPGjRMRkWAwKGeddZZ07txZtm/fbs9XU1MjAGTSpEktWv7atWsFgCxatCjitTVr1ohSSi677DJ54403ZMWKFbJo0SK58sorRUSkrq5OFi1aJADkF7/4hbz33nvy3nvvybZt22Kuz5q/W7duMnnyZPnLX/4if/jDH6Rz587SrVs32b17tz3vnDlzBIBcfvnl8vrrr8tTTz0lxxxzjBQVFclnn31mz3fcccfJscceK08//bSsWrVK/ud//kemTZsmK1eujLvtK1askJycHDn99NPlhRdekDfffFOuuuqqiM/DanPPnj1l2rRpsnz5cpk3b54EAgG5/PLLZdCgQTJ79myprq6Wu+66SwDI//2//9d+v7VvunXrJqeddpr8z//8j/zpT3+Sk08+WbKzs2XNmjX2vBs3bpSioiKprKyUp556SpYvXy7Tpk0TTdNk5syZ9ny1tbXSuXNn6dKliyxatEjeeOMNueKKK6R79+4CwLXt9913nwCQa665xv68u3TpIqWlpTJixIiIdjq33XrvcccdJ/fee69UV1fLggULJDc3V66++mp7vmAwKKeddprk5eXJAw88IMuXL5dZs2ZJ7969BYDcd999cfcFUVtr61hq+de//iWapsl//Md/uKYDkB49esgpp5wiL774orzxxhtSVVUlWVlZ8q9//cue76233pJAICCnnXaaLF261I4V1t92IiNGjJCOHTtKeXm5/OY3v5Fly5bJbbfdJgDk5ptvtuc7cOCAnHjiiVJQUCC/+tWvZPny5XLPPfdIVlaWnH/++fZ8ib4H4rnuuuskOztbpk2bJm+++aY899xz0rdvXykpKZHa2tqINh933HGycOFCWbZsmYwZM0YAyKxZs6SyslKef/55eeONN+TUU0+V3Nxc+frrr+33W3GqoqJCfvazn8myZctkwYIFUlBQIAMHDpTGxkZ73vvvv1+UUjJ58mR57bXXZOnSpTJs2DApKCiQjRs32vNZcf+iiy6SV199VZ555hk59thjpVu3blJRUWHPt3//fjn++OOlqKhIfvvb39qft7W/osVTp4qKCunatauccMIJ8tRTT8myZcvkkksuEQCyatUqe76PPvpI8vLy5MQTT5QlS5bIK6+8Iueff7706NFDAEhNTU3C/ZFJmIim2AsvvCAA5KGHHpJ7771XNE2T5cuXu+b58ssvJRAIyOTJk1u07HiJ6K9+9SsBIN9///0hvT8a64/7Rz/6kWv6//7v/woAmT17toiI7N69W/Lz810BUURk69atkpubKxMmTBARkW+//db+bFqqb9++MnDgQGlqanJNHzNmjJSVlUkwGHS1+dZbb3XNd/HFFwsAWbBggWv6SSedJIMGDbLHrS+28vJyOXDggD29vr5eiouL5ZxzzrGnnXvuudK1a1epq6tzLfOWW26RvLw8+e6770RE5K677hKllHz44Yeu+UaOHOlKRHfv3i15eXkxP+9kE9H58+e73n/TTTdJXl6e6LouIiKvv/66AJBHH33UNd/cuXOZiJJntGUsFRFpamqSqqoqad++vWzdutX1GgApKSmR+vp6e1ptba1omiZz5861pw0dOjRmrEg2EQUgf/7zn13Tr7vuOtE0TbZs2SIiIo899pgAkBdffNE137x58wSA/bkk8z0QzXvvvRfxo1xEZNu2bZKfny933nlnRJs/+OADe9quXbskEAhIfn6+K+n88MMPBYD85je/sadZcSo8+X/22WcFgDzzzDMiYnx/ZGVlRcTyPXv2SGlpqYwfP15EjB8p5eXlMmjQIDvGiRj/NrKzs12J6KOPPhrz8042Ec3Ly7P3i4jxI6G4uFiuv/56e9oll1wiBQUF8u9//9ueFgwG5YQTTjgiE1Eemk+x8ePH48Ybb8TPfvYzzJ49Gz//+c8xcuRI1zwVFRVobm7GwoULW2291mH38ePH48UXX8TXX3/dasu+4oorXOPDhw9HRUUFVq5cCQB47733cODAAVx11VWu+bp164azzjrLPqRdXFyMXr164b/+67+wYMECbNiwAbquJ1z/559/jn/+8592O5qbm+3h/PPPx44dO7Bp0ybXe8aMGeMaP/744wEg4uKe448/Hlu2bIlY57hx45CXl2ePFxYW4sILL8Q777yDYDCIgwcP4q9//St+9KMfoV27dhFtOnjwIN5//30AwMqVK9GvXz8MGDDAtY4JEya4xt977z0cPHgw5uedrLFjx7rGTzzxRBw8eBA7d+4EAKxatQqA8W/F6fLLL096HURtrS1jqYjgmmuuwbvvvounnnoK3bp1i5jnzDPPRGFhoT1eUlKCzp072/Fi3759WLt2bcxYkazCwsKIv9kJEyZA13W88847AIAVK1agoKAAP/nJT1zzWTHXirGH+j3w2muvQSmFn/70p65YVlpaigEDBkT07FFWVobBgwfb48XFxejcuTNOOukklJeX29OtuBstxobHufHjxyMrK8v+Xlm2bBmam5sxceJEV5vy8vIwYsQIu02bNm3C9u3bMWHCBNeh9IqKCgwfPty1jpUrV8b8vJN10kknoXv37vZ4Xl4e+vTp49rGVatW4ayzzkKnTp3saZqmRcTcIwUT0TSYPHkympqakJWVhdtuuy0l6zzjjDPw8ssv23+4Xbt2Rf/+/Vvl4qbS0tKo03bt2gUA9mNZWVnEfOXl5fbrSin89a9/xbnnnov58+dj0KBB+MEPfoDbbrst6vmSlm+++QYAcMcddyA7O9s13HTTTQAQ0RVIcXGxazwnJyfm9IMHDya9zY2Njdi7dy927dqF5uZm/Pa3v41ok9W7gdWmXbt2xVyek/U5JTNvPB07dnSN5+bmAgAOHDhgrycrKyvisygpKUl6HUSp0BaxVERw7bXX4plnnsHixYtx0UUXRZ0v/O8IMP6WrL+j3bt3Q9f1w/57jfZ3Z73fGWNLS0sjzlns3LkzsrKy7PkO9Xvgm2++gYigpKQkIp69//77CeMrYMTSWHE3mRiblZWFjh072ttixf2TTz45ok0vvPCCK75GW160abt27Yr7eScj0b+LeOs5UmMsr5pPsX379uHKK69Enz598M033+Daa6/Fn//855Ss+6KLLsJFF12EhoYGvP/++5g7dy4mTJiAHj16YNiwYYe83GgnctfW1uLYY48FEPrD3LFjR8R827dvd/0qrKiosKsXn332GV588UXMnDkTjY2NeOyxx6Ku33r/9OnTMW7cuKjzHHfccS3YosRibXNOTg6OOuooZGdnIxAI4Morr8TNN98cdRk9e/YEYHw+sZbnZH2OseZNpj+8ZHTs2BHNzc347rvvXF8c0dZLlC5tEUutJHTRokVYuHAhfvrTnx7ysjp06AClVFJ/2/FYCVe091sxoWPHjvh//+//QURcyejOnTvR3NzsirGH8j3QqVMnKKXw7rvv2j9cnaJNO1y1tbXo0qWLPd7c3Ixdu3bZ22xt03//93/HPSKUKG6Gz/u3v/0t4XyHq2PHjnH365GGFdEUu+GGG7B161YsXboUCxcuxCuvvIIHH3wwpW3Izc3FiBEjMG/ePACwrzAMr4wl69lnn3WNr1mzBlu2bLE7WB82bBjy8/PxzDPPuOb76quvsGLFCpx99tlRl9unTx/84he/QGVlJdavXx9z/ccddxx69+6Njz76CEOGDIk6OA+htYalS5e6fsXv2bMHr776Kk4//XQEAgG0a9cOZ555JjZs2IATTzwxapusAHnmmWdi48aN+Oijj1zreO6551zjp556KvLy8mJ+3q1lxIgRAIyrUZ2WLFnSausgOlytHUtFBNdddx0WLVqE3//+97j66qsPq30FBQU45ZRTYsaKZO3ZswevvPKKa9pzzz0HTdNwxhlnAADOPvts7N27Fy+//LJrPuvK8GgxNtb3QDRjxoyBiODrr7+OGssqKyuT3p5khce5F198Ec3Nzfb3yrnnnousrCz861//ihn3AeP7oaysDM8//zxExF7eli1bsGbNGtc6zjzzzJifd2saMWIEVqxY4aok67qOP/3pT626Hr9gRTSFHn/8cTzzzDNYtGgR+vXrh379+uGWW27BXXfdhR/+8Ic45ZRTABh/IL169cKkSZOSOrfpv//7vwHA7kbjgw8+wFFHHQUA9jlD9957L7766iucffbZ6Nq1K77//nv8+te/RnZ2tp149OrVC/n5+Xj22Wdx/PHH46ijjkJ5ebnrnJ5oPvjgA1x77bW45JJLsG3bNsyYMQNdunSxD4sfffTRuOeee/Dzn/8cEydOxOWXX45du3Zh1qxZyMvLw3333QcA+Pjjj3HLLbfgkksuQe/evZGTk4MVK1bg448/xt133x23Db///e8xevRonHvuubjqqqvQpUsXfPfdd/jHP/6B9evXt/ofeCAQwMiRIzF16lTouo558+ahvr4es2bNsuf59a9/jdNOOw2nn346brzxRvTo0QN79uzB559/jldffRUrVqwAAEyZMgVPPPEELrjgAsyePRslJSV49tln8c9//tO1zg4dOuCOO+7A7NmzXZ/3zJkzW3ToKJHzzjsPP/zhDzFt2jTU19dj8ODBeO+99+wvtZZ0GUbUFtoilt52221YuHAhJk+ejMrKSvscbsBI2gYOHNjidv7nf/4nzjvvPIwcORLTpk1DMBjEvHnzUFBQgO+++y6pZXTs2BE33ngjtm7dij59+uCNN97AH//4R9x44432uYgTJ07E7373O0yaNAlffvklKisrsXr1asyZMwfnn38+zjnnHADJfQ9E88Mf/hD/5//8H1x99dX44IMPcMYZZ6CgoAA7duzA6tWrUVlZiRtvvLHFn088S5cuRVZWFkaOHImNGzfinnvuwYABA+zzKHv06IFf/vKXmDFjBr744gucd9556NChA7755hv87W9/Q0FBAWbNmgVN0/Cf//mfuPbaa/GjH/0I1113Hb7//vuocXPixIl48MEHMXHiRNx///3o3bs33njjDSxbtqxVt23GjBl49dVXcfbZZ2PGjBnIz8/HY489hn379gE4AmNs+q6TOrJ8/PHHkp+fH9GNyMGDB2Xw4MHSo0cPu7ujlnY5AiDmYHnttddk9OjR0qVLF8nJyZHOnTvL+eefL++++65rWc8//7z07dtXsrOzE14hbV2Bvnz5crnyyivl6KOPtq+O37x5c8T8jz/+uJx44omSk5MjRUVFctFFF7m62Pjmm2/kqquukr59+0pBQYEcddRRcuKJJ8qDDz4ozc3NCT+Hjz76SMaPHy+dO3eW7OxsKS0tlbPOOksee+yxiDavXbvW9V7rCkjnVYwiIpMmTZKCggJ73No38+bNk1mzZknXrl0lJydHBg4cKMuWLYtoU01NjUyePFm6dOki2dnZ8oMf/ECGDx9u9yhg+fvf/y4jR46UvLw8KS4ulmuuuUb+/Oc/R3TfpOu6zJ07V7p16yY5OTly4oknyquvviojRoxI+qr58G20PhPnlZrfffedXH311XL00UdLu3btZOTIkfL+++8LAPn1r38dcx8QtbW2iqUVFRUx46jzymoRieg+ybmM8HW98sordtzr3r27PPDAA1GvuI5mxIgR0q9fP3n77bdlyJAhkpubK2VlZfLzn/88ooeQXbt2yQ033CBlZWWSlZUlFRUVMn36dDl48KA9T7LfA7E88cQTMnToUCkoKJD8/Hzp1auXTJw40XWFvNXmaJ/NBRdcEDE9/LO0Ppt169bJhRdeKEcddZQUFhbK5ZdfLt98803E+19++WU588wzpX379pKbmysVFRXyk5/8RN566y3XfI8//rj07t1bcnJypE+fPvLEE0/IpEmTIvbtV199JT/+8Y/t9f74xz+WNWvWJH3VfLRtDI/PIiLvvvuuDB06VHJzc6W0tFR+9rOf2b0ctLRXA79TIo5aNVELLF68GFdffTXWrl1rHwahzPXcc8/hiiuuwP/+7/9GXG1KRK2vqqoK3377LT799NN0N4VSYNSoUfjyyy/x2WefpbspKcVD80QU4fnnn8fXX3+NyspKaJqG999/H//1X/+FM844g0koEdFhmjp1KgYOHIhu3brhu+++w7PPPovq6upW7bbRL5iIElGEwsJCLFmyBLNnz8a+fftQVlaGq666CrNnz05304iIfC8YDOLee+9FbW0tlFI44YQT8PTTTx9WTw1+xUPzRERERJQWab0065FHHkHPnj2Rl5eHwYMH4913301nc4iIfI9xlYj8JG2J6AsvvIApU6ZgxowZ2LBhA04//XSMHj0aW7duTVeTiIh8jXGViPwmbYfmhw4dikGDBuHRRx+1px1//PG4+OKLMXfu3HQ0iYjI1xhXichv0nKxUmNjI9atWxfRSfmoUaMi7nQQja7r2L59OwoLCyPurUtEFE5EsGfPHpSXl2ds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4+k80TCu+kOmbQ+QeduUadsDtG1c9WVFVNM0dOnSBQDQvn37jNnRlkzbpkzbHiDztinTtgeI3CZWQuNjXPWXTNseIPO2KdO2B2ibuMrSABERERGlBRNRIiIiIkoL3yaiubm5uO+++5Cbm5vuprSaTNumTNseIPO2KdO2B8jMbUqVTPzsMm2bMm17gMzbpkzbHqBtt8mXFysRERERkf/5tiJKRERERP7GRJSIiIiI0oKJKBERERGlBRNRIiIiIkoL3yaijzzyCHr27Im8vDwMHjwY7777brqblJS5c+fi5JNPRmFhITp37oyLL74YmzZtcs1z1VVXQSnlGk499dQ0tTi+mTNnRrS1tLTUfl1EMHPmTJSXlyM/Px9VVVXYuHFjGlucWI8ePSK2SSmFm2++GYD3988777yDCy+8EOXl5VBK4eWXX3a9nsw+aWhowK233opOnTqhoKAAY8eOxVdffZXCrXCLt01NTU246667UFlZiYKCApSXl2PixInYvn27axlVVVUR++2yyy5L8ZZ4G+Oqd2RabGVcZVyNxZeJ6AsvvIApU6ZgxowZ2LBhA04//XSMHj0aW7duTXfTElq1ahVuvvlmvP/++6iurkZzczNGjRqFffv2ueY777zzsGPHDnt444030tTixPr16+dq6yeffGK/Nn/+fCxYsAAPP/ww1q5di9LSUowcOdK+r7UXrV271rU91dXVAIBLLrnEnsfL+2ffvn0YMGAAHn744aivJ7NPpkyZgpdeeglLlizB6tWrsXfvXowZMwbBYDBVm+ESb5v279+P9evX45577sH69euxdOlSfPbZZxg7dmzEvNddd51rv/3+979PRfN9gXHVezIptjKuMq7GJD50yimnyA033OCa1rdvX7n77rvT1KJDt3PnTgEgq1atsqdNmjRJLrroovQ1qgXuu+8+GTBgQNTXdF2X0tJSeeCBB+xpBw8elKKiInnsscdS1MLDd/vtt0uvXr1E13UR8df+ASAvvfSSPZ7MPvn+++8lOztblixZYs/z9ddfi6Zp8uabb6as7bGEb1M0f/vb3wSAbNmyxZ42YsQIuf3229u2cT7GuOotmR5bGVcZVy2+q4g2NjZi3bp1GDVqlGv6qFGjsGbNmjS16tDV1dUBAIqLi13T3377bXTu3Bl9+vTBddddh507d6ajeUnZvHkzysvL0bNnT1x22WX44osvAAA1NTWora117avc3FyMGDHCN/uqsbERzzzzDCZPngyllD3dT/vHKZl9sm7dOjQ1NbnmKS8vR//+/X2z3+rq6qCUwtFHH+2a/uyzz6JTp07o168f7rjjDs9Wj1KNcdWbMjW2Mq4aGFcNWa3YxpT49ttvEQwGUVJS4ppeUlKC2traNLXq0IgIpk6ditNOOw39+/e3p48ePRqXXHIJKioqUFNTg3vuuQdnnXUW1q1b57k7NQwdOhRPPfUU+vTpg2+++QazZ8/G8OHDsXHjRnt/RNtXW7ZsSUdzW+zll1/G999/j6uuusqe5qf9Ey6ZfVJbW4ucnBx06NAhYh4//I0dPHgQd999NyZMmID27dvb06+44gr07NkTpaWl+PTTTzF9+nR89NFH9iHCIxnjqvf+bjM5tjKuuufxw99YW8ZV3yWiFuevKMAIPuHTvO6WW27Bxx9/jNWrV7umX3rppfbz/v37Y8iQIaioqMDrr7+OcePGpbqZcY0ePdp+XllZiWHDhqFXr1548skn7RPN/byvFi5ciNGjR6O8vNye5qf9E8uh7BM/7LempiZcdtll0HUdjzzyiOu16667zn7ev39/9O7dG0OGDMH69esxaNCgVDfVk/z8t2rJhLgKZHZsZVxt2Tzp1tZx1XeH5jt16oRAIBDxC2Lnzp0Rv0a87NZbb8Urr7yClStXomvXrnHnLSsrQ0VFBTZv3pyi1h26goICVFZWYvPmzfYVnn7dV1u2bMFbb72Fa6+9Nu58fto/yeyT0tJSNDY2Yvfu3THn8aKmpiaMHz8eNTU1qK6udv1qj2bQoEHIzs72xX5ra4yr3pcpsZVxlXE1nO8S0ZycHAwePDii7FtdXY3hw4enqVXJExHccsstWLp0KVasWIGePXsmfM+uXbuwbds2lJWVpaCFh6ehoQH/+Mc/UFZWZpfrnfuqsbERq1at8sW+WrRoETp37owLLrgg7nx+2j/J7JPBgwcjOzvbNc+OHTvw6aefena/WcFy8+bNeOutt9CxY8eE79m4cSOampp8sd/aGuOq92VKbGVcZVyNcFiXOqXJkiVLJDs7WxYuXCh///vfZcqUKVJQUCBffvllupuW0I033ihFRUXy9ttvy44dO+xh//79IiKyZ88emTZtmqxZs0Zqampk5cqVMmzYMOnSpYvU19enufWRpk2bJm+//bZ88cUX8v7778uYMWOksLDQ3hcPPPCAFBUVydKlS+WTTz6Ryy+/XMrKyjy5LU7BYFC6d+8ud911l2u6H/bPnj17ZMOGDbJhwwYBIAsWLJANGzbYVzoms09uuOEG6dq1q7z11luyfv16Oeuss2TAgAHS3NzsuW1qamqSsWPHSteuXeXDDz90/V01NDSIiMjnn38us2bNkrVr10pNTY28/vrr0rdvXxk4cGDatslrGFe9JRNjK+Mq42o0vkxERUR+97vfSUVFheTk5MigQYNc3XR4GYCow6JFi0REZP/+/TJq1Cj5wQ9+INnZ2dK9e3eZNGmSbN26Nb0Nj+HSSy+VsrIyyc7OlvLychk3bpxs3LjRfl3XdbnvvvuktLRUcnNz5YwzzpBPPvkkjS1OzrJlywSAbNq0yTXdD/tn5cqVUf+NTZo0SUSS2ycHDhyQW265RYqLiyU/P1/GjBmT1m2Mt001NTUx/65WrlwpIiJbt26VM844Q4qLiyUnJ0d69eolt912m+zatStt2+RFjKvekYmxlXGVcTUaJSKSfP2UiIiIiKh1+O4cUSIiIiLKDExEiYiIiCgtmIgSERERUVowESUiIiKitGAiSkRERERpwUSUiIiIiNKCiSgRERERpQUTUSIiIiJKCyaiRERERJQWTESJiIiIKC2YiBIRERFRWjARJSIiIqK0+P+/3HhFahDzfwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Which sample to view\n", - "index = 10\n", - "\n", - "data = train_dataset[index]\n", - "x = data['x']\n", - "y = data['y']\n", - "fig = plt.figure(figsize=(7, 7))\n", - "ax = fig.add_subplot(2, 2, 1)\n", - "ax.imshow(x[0], cmap='gray')\n", - "ax.set_title('input x')\n", - "ax = fig.add_subplot(2, 2, 2)\n", - "ax.imshow(y.squeeze())\n", - "ax.set_title('input y')\n", - "ax = fig.add_subplot(2, 2, 3)\n", - "ax.imshow(x[1])\n", - "ax.set_title('x: 1st pos embedding')\n", - "ax = fig.add_subplot(2, 2, 4)\n", - "ax.imshow(x[2])\n", - "ax.set_title('x: 2nd pos embedding')\n", - "fig.suptitle('Visualizing one input sample', y=0.98)\n", - "plt.tight_layout()\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba9e2a5c-98e7-47c0-9e24-7a8e41c657dc", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.15" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/4-training-on-Darcy-Flow.ipynb b/4-training-on-Darcy-Flow.ipynb deleted file mode 100644 index f6e1453..0000000 --- a/4-training-on-Darcy-Flow.ipynb +++ /dev/null @@ -1,681 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "012a357f-8533-482c-823d-a4587c49e726", - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import wandb\n", - "import sys\n", - "from configmypy import ConfigPipeline, YamlConfig, ArgparseConfig\n", - "from neuralop import get_model\n", - "from neuralop import Trainer\n", - "from neuralop.training import setup\n", - "from neuralop.datasets import load_darcy_pt\n", - "from neuralop.utils import get_wandb_api_key, count_params\n", - "from neuralop import LpLoss, H1Loss" - ] - }, - { - "cell_type": "markdown", - "id": "9b6358b5-78d1-4baf-8928-6bb49b150680", - "metadata": {}, - "source": [ - "# Loading the configuration\n", - "\n", - "You can open the yaml file in config/darcy_config in the same folder as this notebook to inspect the parameters and change them." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "4503f065-4063-4a4f-b00f-06a7c3a88e27", - "metadata": {}, - "outputs": [], - "source": [ - "# Read the configuration\n", - "config_name = 'default'\n", - "pipe = ConfigPipeline([YamlConfig('./darcy_config.yaml', config_name='default', config_folder='./config'),\n", - " ])\n", - "config = pipe.read_conf()\n", - "config_name = pipe.steps[-1].config_name" - ] - }, - { - "cell_type": "markdown", - "id": "e95d820d-9578-4ad7-80b4-05a5771f1642", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "Here we just setup pytorch and print the configuration" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "46066d9f-21a3-4aab-b6e1-f7f38e05f88b", - "metadata": {}, - "outputs": [], - "source": [ - "# Set-up distributed communication, if using\n", - "device, is_logger = setup(config)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "26d599f9-6463-4056-9a4d-72c01d05298e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "###############################\n", - "##### CONFIGURATION #####\n", - "###############################\n", - "\n", - "Steps:\n", - "------\n", - " (1) YamlConfig with config_file=./darcy_config.yaml, config_name=default, config_folder=./config\n", - "\n", - "-------------------------------\n", - "\n", - "Configuration:\n", - "--------------\n", - "\n", - "n_params_baseline=None\n", - "verbose=True\n", - "arch=tfno2d\n", - "distributed.use_distributed=False\n", - "tfno2d.data_channels=3\n", - "tfno2d.n_modes_height=32\n", - "tfno2d.n_modes_width=32\n", - "tfno2d.hidden_channels=64\n", - "tfno2d.projection_channels=256\n", - "tfno2d.n_layers=4\n", - "tfno2d.domain_padding=None\n", - "tfno2d.domain_padding_mode=one-sided\n", - "tfno2d.fft_norm=forward\n", - "tfno2d.norm=group_norm\n", - "tfno2d.skip=linear\n", - "tfno2d.implementation=factorized\n", - "tfno2d.separable=0\n", - "tfno2d.preactivation=0\n", - "tfno2d.use_mlp=1\n", - "tfno2d.mlp.expansion=0.5\n", - "tfno2d.mlp.dropout=0\n", - "tfno2d.factorization=None\n", - "tfno2d.rank=1.0\n", - "tfno2d.fixed_rank_modes=None\n", - "tfno2d.dropout=0.0\n", - "tfno2d.tensor_lasso_penalty=0.0\n", - "tfno2d.joint_factorization=False\n", - "opt.n_epochs=150\n", - "opt.learning_rate=0.005\n", - "opt.training_loss=h1\n", - "opt.weight_decay=0.0001\n", - "opt.amp_autocast=False\n", - "opt.scheduler_T_max=300\n", - "opt.scheduler_patience=5\n", - "opt.scheduler=CosineAnnealingLR\n", - "opt.step_size=50\n", - "opt.gamma=0.5\n", - "data.folder=/data/darcy_flow/\n", - "data.batch_size=32\n", - "data.n_train=3000\n", - "data.train_resolution=32\n", - "data.n_tests=[500, 500]\n", - "data.test_resolutions=[32, 64]\n", - "data.test_batch_sizes=[32, 32]\n", - "data.positional_encoding=True\n", - "data.encode_input=True\n", - "data.encode_output=False\n", - "patching.levels=0\n", - "patching.padding=0\n", - "patching.stitching=False\n", - "wandb.log=False\n", - "wandb.log_test_interval=1\n", - "\n", - "###############################\n" - ] - } - ], - "source": [ - "# Make sure we only print information when needed\n", - "config.verbose = config.verbose and is_logger\n", - "\n", - "#Print config to screen\n", - "if config.verbose and is_logger:\n", - " pipe.log()\n", - " sys.stdout.flush()" - ] - }, - { - "cell_type": "markdown", - "id": "1339c794-3e1c-469b-b0a0-cf968fc1dfa1", - "metadata": {}, - "source": [ - "# Loading the data \n", - "\n", - "We train in one resolution and test in several resolutions to show the zero-shot super-resolution capabilities of neural-operators. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3515a85a-40fc-4223-9cdb-8768de37d6e2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "UnitGaussianNormalizer init on 3000, reducing over [0, 1, 2, 3], samples of shape [1, 32, 32].\n", - " Mean and std of shape torch.Size([1, 1, 1]), eps=1e-05\n", - "Loading test db at resolution 64 with 500 samples and batch-size=32\n" - ] - } - ], - "source": [ - "# Loading the Darcy flow training set in 32x32 resolution, test set in 32x32 and 64x64 resolutions\n", - "train_loader, test_loaders, output_encoder = load_darcy_pt(\n", - " config.data.folder, train_resolution=config.data.train_resolution, n_train=config.data.n_train, batch_size=config.data.batch_size, \n", - " positional_encoding=config.data.positional_encoding,\n", - " test_resolutions=config.data.test_resolutions, n_tests=config.data.n_tests, test_batch_sizes=config.data.test_batch_sizes,\n", - " encode_input=config.data.encode_input, encode_output=config.data.encode_output,\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "8109298a-aca3-45b7-a8de-c5cf4e1c210b", - "metadata": {}, - "source": [ - "# Creating the model and putting it on the GPU " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "db295d23-ab86-4f37-83cc-7af0a8e485ea", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Given argument key='dropout' that is not in TFNO2d's signature.\n", - "Given argument key='tensor_lasso_penalty' that is not in TFNO2d's signature.\n", - "Keyword argument out_channels not specified for model TFNO2d, using default=1.\n", - "Keyword argument lifting_channels not specified for model TFNO2d, using default=256.\n", - "Keyword argument non_linearity not specified for model TFNO2d, using default=.\n", - "Keyword argument decomposition_kwargs not specified for model TFNO2d, using default={}.\n", - "\n", - "n_params: 16844673\n" - ] - } - ], - "source": [ - "model = get_model(config)\n", - "model = model.to(device)\n", - "\n", - "#Log parameter count\n", - "if is_logger:\n", - " n_params = count_params(model)\n", - "\n", - " if config.verbose:\n", - " print(f'\\nn_params: {n_params}')\n", - " sys.stdout.flush()" - ] - }, - { - "cell_type": "markdown", - "id": "fec85d0a-4db4-4b1f-b599-8c2afc98520a", - "metadata": {}, - "source": [ - "# Create the optimizer and learning rate scheduler\n", - "\n", - "Here, we use an Adam optimizer and a learning rate schedule depending on the configuration" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "5164537a-267b-4fda-9bcd-257dc3ac4826", - "metadata": {}, - "outputs": [], - "source": [ - "#Create the optimizer\n", - "optimizer = torch.optim.Adam(model.parameters(), \n", - " lr=config.opt.learning_rate, \n", - " weight_decay=config.opt.weight_decay)\n", - "\n", - "if config.opt.scheduler == 'ReduceLROnPlateau':\n", - " scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=config.opt.gamma, patience=config.opt.scheduler_patience, mode='min')\n", - "elif config.opt.scheduler == 'CosineAnnealingLR':\n", - " scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=config.opt.scheduler_T_max)\n", - "elif config.opt.scheduler == 'StepLR':\n", - " scheduler = torch.optim.lr_scheduler.StepLR(optimizer, \n", - " step_size=config.opt.step_size,\n", - " gamma=config.opt.gamma)\n", - "else:\n", - " raise ValueError(f'Got {config.opt.scheduler=}')" - ] - }, - { - "cell_type": "markdown", - "id": "e52a72eb-965a-4997-89a4-0cdfcbcb0a1a", - "metadata": {}, - "source": [ - "# Creating the loss\n", - "\n", - "We will optimize the Sobolev norm but also evaluate our goal: the l2 relative error" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "07a53d9d-2d06-4d36-9b46-2c7f15f29c40", - "metadata": {}, - "outputs": [], - "source": [ - "# Creating the losses\n", - "l2loss = LpLoss(d=2, p=2)\n", - "h1loss = H1Loss(d=2)\n", - "if config.opt.training_loss == 'l2':\n", - " train_loss = l2loss\n", - "elif config.opt.training_loss == 'h1':\n", - " train_loss = h1loss\n", - "else:\n", - " raise ValueError(f'Got training_loss={config.opt.training_loss} but expected one of [\"l2\", \"h1\"]')\n", - "eval_losses={'h1': h1loss, 'l2': l2loss}" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "5dad660e-43e9-4f38-91f6-8427b14b8ae0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "### MODEL ###\n", - " TFNO2d(\n", - " (convs): FactorizedSpectralConv2d(\n", - " (weight): ModuleList(\n", - " (0): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (1): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (2): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (3): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (4): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (5): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (6): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " (7): ComplexDenseTensor(shape=torch.Size([64, 64, 16, 16]), rank=None)\n", - " )\n", - " )\n", - " (fno_skips): ModuleList(\n", - " (0): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " )\n", - " (mlp): ModuleList(\n", - " (0): MLP(\n", - " (fcs): ModuleList(\n", - " (0): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " (1): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (1): MLP(\n", - " (fcs): ModuleList(\n", - " (0): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " (1): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (2): MLP(\n", - " (fcs): ModuleList(\n", - " (0): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " (1): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (3): MLP(\n", - " (fcs): ModuleList(\n", - " (0): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " (1): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (mlp_skips): ModuleList(\n", - " (0): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " )\n", - " (norm): ModuleList(\n", - " (0): GroupNorm(1, 64, eps=1e-05, affine=True)\n", - " (1): GroupNorm(1, 64, eps=1e-05, affine=True)\n", - " (2): GroupNorm(1, 64, eps=1e-05, affine=True)\n", - " (3): GroupNorm(1, 64, eps=1e-05, affine=True)\n", - " )\n", - " (lifting): Lifting(\n", - " (fc): Conv2d(3, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (projection): Projection(\n", - " (fc1): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1))\n", - " (fc2): Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - ")\n", - "\n", - "### OPTIMIZER ###\n", - " Adam (\n", - "Parameter Group 0\n", - " amsgrad: False\n", - " betas: (0.9, 0.999)\n", - " capturable: False\n", - " differentiable: False\n", - " eps: 1e-08\n", - " foreach: None\n", - " fused: False\n", - " initial_lr: 0.005\n", - " lr: 0.005\n", - " maximize: False\n", - " weight_decay: 0.0001\n", - ")\n", - "\n", - "### SCHEDULER ###\n", - " \n", - "\n", - "### LOSSES ###\n", - "\n", - " * Train: \n", - "\n", - " * Test: {'h1': , 'l2': }\n", - "\n", - "### Beginning Training...\n", - "\n" - ] - } - ], - "source": [ - "if config.verbose and is_logger:\n", - " print('\\n### MODEL ###\\n', model)\n", - " print('\\n### OPTIMIZER ###\\n', optimizer)\n", - " print('\\n### SCHEDULER ###\\n', scheduler)\n", - " print('\\n### LOSSES ###')\n", - " print(f'\\n * Train: {train_loss}')\n", - " print(f'\\n * Test: {eval_losses}')\n", - " print(f'\\n### Beginning Training...\\n')\n", - " sys.stdout.flush()" - ] - }, - { - "cell_type": "markdown", - "id": "b5967441-b8bc-4ea8-a4d9-7a5bea384cbf", - "metadata": {}, - "source": [ - "# Creating the trainer" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "a19ebfd3-8a2b-42c0-af98-7a1db2dda0f6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Training on regular inputs (no multi-grid patching).\n", - "MGPatching(self.n_patches=[1, 1], self.padding_fraction=[0, 0], self.levels=0, use_distributed=False, stitching=False)\n" - ] - } - ], - "source": [ - "trainer = Trainer(model, n_epochs=config.opt.n_epochs,\n", - " device=device,\n", - " mg_patching_levels=config.patching.levels,\n", - " mg_patching_padding=config.patching.padding,\n", - " mg_patching_stitching=config.patching.stitching,\n", - " wandb_log=config.wandb.log,\n", - " log_test_interval=config.wandb.log_test_interval,\n", - " log_output=False,\n", - " use_distributed=config.distributed.use_distributed,\n", - " verbose=config.verbose and is_logger)" - ] - }, - { - "cell_type": "markdown", - "id": "b16a3727-313d-4219-8f8f-0cec58d74b00", - "metadata": {}, - "source": [ - "# Training the model " - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "0d6e3298-99ee-4371-8bad-60e6aac03d56", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Training on 3000 samples, testing on [32, 64].\n", - "[0] time=3.03, avg_loss=7.8899, train_err=0.3945, 32_h1=0.2295, 32_l2=0.1710, 64_h1=0.2847, 64_l2=0.1733\n", - "[1] time=1.38, avg_loss=3.7664, train_err=0.1883, 32_h1=0.1646, 32_l2=0.1177, 64_h1=0.2326, 64_l2=0.1221\n", - "[2] time=1.37, avg_loss=3.1005, train_err=0.1550, 32_h1=0.1411, 32_l2=0.1027, 64_h1=0.2156, 64_l2=0.1106\n", - "[3] time=1.36, avg_loss=2.5222, train_err=0.1261, 32_h1=0.1238, 32_l2=0.0800, 64_h1=0.2026, 64_l2=0.0936\n", - "[4] time=1.36, avg_loss=2.3043, train_err=0.1152, 32_h1=0.1235, 32_l2=0.0808, 64_h1=0.1874, 64_l2=0.0858\n", - "[5] time=1.36, avg_loss=2.2108, train_err=0.1105, 32_h1=0.1332, 32_l2=0.1041, 64_h1=0.2055, 64_l2=0.1122\n", - "[6] time=1.37, avg_loss=1.9753, train_err=0.0988, 32_h1=0.1077, 32_l2=0.0720, 64_h1=0.1885, 64_l2=0.0838\n", - "[7] time=1.37, avg_loss=1.9352, train_err=0.0968, 32_h1=0.1032, 32_l2=0.0642, 64_h1=0.1847, 64_l2=0.0753\n", - "[8] time=1.36, avg_loss=1.8174, train_err=0.0909, 32_h1=0.1013, 32_l2=0.0632, 64_h1=0.1798, 64_l2=0.0763\n", - "[9] time=1.37, avg_loss=1.7847, train_err=0.0892, 32_h1=0.1053, 32_l2=0.0672, 64_h1=0.1909, 64_l2=0.0788\n", - "[10] time=1.37, avg_loss=1.6375, train_err=0.0819, 32_h1=0.0926, 32_l2=0.0513, 64_h1=0.1808, 64_l2=0.0666\n", - "[11] time=1.37, avg_loss=1.5826, train_err=0.0791, 32_h1=0.0958, 32_l2=0.0574, 64_h1=0.1810, 64_l2=0.0700\n", - "[12] time=1.36, avg_loss=1.6231, train_err=0.0812, 32_h1=0.0940, 32_l2=0.0534, 64_h1=0.1740, 64_l2=0.0636\n", - "[13] time=1.42, avg_loss=1.5427, train_err=0.0771, 32_h1=0.0937, 32_l2=0.0532, 64_h1=0.1834, 64_l2=0.0692\n", - "[14] time=1.37, avg_loss=1.4741, train_err=0.0737, 32_h1=0.0989, 32_l2=0.0623, 64_h1=0.1844, 64_l2=0.0798\n", - "[15] time=1.36, avg_loss=1.5156, train_err=0.0758, 32_h1=0.1020, 32_l2=0.0649, 64_h1=0.1844, 64_l2=0.0730\n", - "[16] time=1.37, avg_loss=1.5620, train_err=0.0781, 32_h1=0.0940, 32_l2=0.0608, 64_h1=0.1803, 64_l2=0.0747\n", - "[17] time=1.36, avg_loss=1.3939, train_err=0.0697, 32_h1=0.1018, 32_l2=0.0620, 64_h1=0.1842, 64_l2=0.0772\n", - "[18] time=1.89, avg_loss=1.4904, train_err=0.0745, 32_h1=0.1010, 32_l2=0.0704, 64_h1=0.1868, 64_l2=0.0794\n", - "[19] time=1.83, avg_loss=1.4300, train_err=0.0715, 32_h1=0.0929, 32_l2=0.0525, 64_h1=0.1784, 64_l2=0.0679\n", - "[20] time=1.84, avg_loss=1.3752, train_err=0.0688, 32_h1=0.0964, 32_l2=0.0635, 64_h1=0.1825, 64_l2=0.0694\n", - "[21] time=1.84, avg_loss=1.4671, train_err=0.0734, 32_h1=0.0911, 32_l2=0.0513, 64_h1=0.1832, 64_l2=0.0696\n", - "[22] time=1.88, avg_loss=1.3043, train_err=0.0652, 32_h1=0.0938, 32_l2=0.0538, 64_h1=0.1804, 64_l2=0.0687\n", - "[23] time=1.37, avg_loss=1.2880, train_err=0.0644, 32_h1=0.0897, 32_l2=0.0492, 64_h1=0.1824, 64_l2=0.0629\n", - "[24] time=1.37, avg_loss=1.3901, train_err=0.0695, 32_h1=0.1080, 32_l2=0.0701, 64_h1=0.1828, 64_l2=0.0785\n", - "[25] time=1.37, avg_loss=1.3788, train_err=0.0689, 32_h1=0.0878, 32_l2=0.0514, 64_h1=0.1744, 64_l2=0.0613\n", - "[26] time=1.37, avg_loss=1.3071, train_err=0.0654, 32_h1=0.0880, 32_l2=0.0489, 64_h1=0.1847, 64_l2=0.0698\n", - "[27] time=1.36, avg_loss=1.3056, train_err=0.0653, 32_h1=0.0980, 32_l2=0.0679, 64_h1=0.1828, 64_l2=0.0830\n", - "[28] time=1.37, avg_loss=1.2677, train_err=0.0634, 32_h1=0.0956, 32_l2=0.0621, 64_h1=0.1827, 64_l2=0.0692\n", - "[29] time=1.37, avg_loss=1.2611, train_err=0.0631, 32_h1=0.0913, 32_l2=0.0500, 64_h1=0.1855, 64_l2=0.0652\n", - "[30] time=1.37, avg_loss=1.1833, train_err=0.0592, 32_h1=0.0888, 32_l2=0.0512, 64_h1=0.1818, 64_l2=0.0655\n", - "[31] time=1.36, avg_loss=1.2170, train_err=0.0608, 32_h1=0.0879, 32_l2=0.0481, 64_h1=0.1758, 64_l2=0.0625\n", - "[32] time=1.36, avg_loss=1.1431, train_err=0.0572, 32_h1=0.0886, 32_l2=0.0479, 64_h1=0.1756, 64_l2=0.0594\n", - "[33] time=1.37, avg_loss=1.2162, train_err=0.0608, 32_h1=0.0923, 32_l2=0.0522, 64_h1=0.1749, 64_l2=0.0629\n", - "[34] time=1.37, avg_loss=1.1588, train_err=0.0579, 32_h1=0.0892, 32_l2=0.0526, 64_h1=0.1797, 64_l2=0.0656\n", - "[35] time=1.37, avg_loss=1.1747, train_err=0.0587, 32_h1=0.0884, 32_l2=0.0481, 64_h1=0.1829, 64_l2=0.0650\n", - "[36] time=1.36, avg_loss=1.1491, train_err=0.0575, 32_h1=0.0936, 32_l2=0.0542, 64_h1=0.1787, 64_l2=0.0672\n", - "[37] time=1.37, avg_loss=1.1532, train_err=0.0577, 32_h1=0.0950, 32_l2=0.0569, 64_h1=0.1737, 64_l2=0.0679\n", - "[38] time=1.37, avg_loss=1.2426, train_err=0.0621, 32_h1=0.0875, 32_l2=0.0488, 64_h1=0.1750, 64_l2=0.0638\n", - "[39] time=1.37, avg_loss=1.1345, train_err=0.0567, 32_h1=0.0874, 32_l2=0.0493, 64_h1=0.1780, 64_l2=0.0658\n", - "[40] time=1.36, avg_loss=1.1238, train_err=0.0562, 32_h1=0.0914, 32_l2=0.0516, 64_h1=0.1796, 64_l2=0.0662\n", - "[41] time=1.36, avg_loss=1.1093, train_err=0.0555, 32_h1=0.0855, 32_l2=0.0457, 64_h1=0.1741, 64_l2=0.0621\n", - "[42] time=1.36, avg_loss=1.0772, train_err=0.0539, 32_h1=0.0899, 32_l2=0.0523, 64_h1=0.1807, 64_l2=0.0688\n", - "[43] time=1.36, avg_loss=1.0772, train_err=0.0539, 32_h1=0.0894, 32_l2=0.0556, 64_h1=0.1769, 64_l2=0.0705\n", - "[44] time=1.36, avg_loss=1.0901, train_err=0.0545, 32_h1=0.0843, 32_l2=0.0443, 64_h1=0.1750, 64_l2=0.0589\n", - "[45] time=1.36, avg_loss=1.0783, train_err=0.0539, 32_h1=0.0874, 32_l2=0.0486, 64_h1=0.1778, 64_l2=0.0593\n", - "[46] time=1.46, avg_loss=1.0837, train_err=0.0542, 32_h1=0.0874, 32_l2=0.0482, 64_h1=0.1722, 64_l2=0.0575\n", - "[47] time=1.37, avg_loss=1.1760, train_err=0.0588, 32_h1=0.0873, 32_l2=0.0507, 64_h1=0.1706, 64_l2=0.0639\n", - "[48] time=1.37, avg_loss=1.0357, train_err=0.0518, 32_h1=0.0889, 32_l2=0.0503, 64_h1=0.1799, 64_l2=0.0663\n", - "[49] time=1.36, avg_loss=1.0873, train_err=0.0544, 32_h1=0.0846, 32_l2=0.0464, 64_h1=0.1725, 64_l2=0.0592\n", - "[50] time=1.36, avg_loss=1.0996, train_err=0.0550, 32_h1=0.0861, 32_l2=0.0461, 64_h1=0.1696, 64_l2=0.0598\n", - "[51] time=1.37, avg_loss=1.0487, train_err=0.0524, 32_h1=0.0839, 32_l2=0.0433, 64_h1=0.1752, 64_l2=0.0602\n", - "[52] time=1.37, avg_loss=1.0527, train_err=0.0526, 32_h1=0.0858, 32_l2=0.0469, 64_h1=0.1736, 64_l2=0.0588\n", - "[53] time=1.36, avg_loss=1.0138, train_err=0.0507, 32_h1=0.0854, 32_l2=0.0475, 64_h1=0.1777, 64_l2=0.0619\n", - "[54] time=1.36, avg_loss=1.0210, train_err=0.0511, 32_h1=0.0832, 32_l2=0.0431, 64_h1=0.1728, 64_l2=0.0580\n", - "[55] time=1.36, avg_loss=0.9939, train_err=0.0497, 32_h1=0.0870, 32_l2=0.0474, 64_h1=0.1755, 64_l2=0.0609\n", - "[56] time=1.37, avg_loss=1.0085, train_err=0.0504, 32_h1=0.0833, 32_l2=0.0438, 64_h1=0.1731, 64_l2=0.0603\n", - "[57] time=1.37, avg_loss=1.0132, train_err=0.0507, 32_h1=0.0842, 32_l2=0.0462, 64_h1=0.1757, 64_l2=0.0613\n", - "[58] time=1.36, avg_loss=0.9938, train_err=0.0497, 32_h1=0.0839, 32_l2=0.0439, 64_h1=0.1811, 64_l2=0.0651\n", - "[59] time=1.36, avg_loss=0.9814, train_err=0.0491, 32_h1=0.0820, 32_l2=0.0425, 64_h1=0.1728, 64_l2=0.0565\n", - "[60] time=1.36, avg_loss=0.9849, train_err=0.0492, 32_h1=0.0861, 32_l2=0.0477, 64_h1=0.1715, 64_l2=0.0616\n", - "[61] time=1.37, avg_loss=0.9787, train_err=0.0489, 32_h1=0.0844, 32_l2=0.0450, 64_h1=0.1742, 64_l2=0.0623\n", - "[62] time=1.36, avg_loss=1.0104, train_err=0.0505, 32_h1=0.0830, 32_l2=0.0437, 64_h1=0.1769, 64_l2=0.0605\n", - "[63] time=1.36, avg_loss=0.9910, train_err=0.0495, 32_h1=0.0821, 32_l2=0.0415, 64_h1=0.1742, 64_l2=0.0579\n", - "[64] time=1.36, avg_loss=0.9622, train_err=0.0481, 32_h1=0.0849, 32_l2=0.0462, 64_h1=0.1763, 64_l2=0.0608\n", - "[65] time=1.36, avg_loss=1.0191, train_err=0.0510, 32_h1=0.0823, 32_l2=0.0419, 64_h1=0.1736, 64_l2=0.0570\n", - "[66] time=1.37, avg_loss=0.9814, train_err=0.0491, 32_h1=0.0873, 32_l2=0.0492, 64_h1=0.1752, 64_l2=0.0643\n", - "[67] time=1.36, avg_loss=0.9867, train_err=0.0493, 32_h1=0.0833, 32_l2=0.0446, 64_h1=0.1698, 64_l2=0.0588\n", - "[68] time=1.36, avg_loss=0.9983, train_err=0.0499, 32_h1=0.0815, 32_l2=0.0417, 64_h1=0.1712, 64_l2=0.0590\n", - "[69] time=1.37, avg_loss=0.9956, train_err=0.0498, 32_h1=0.0836, 32_l2=0.0453, 64_h1=0.1756, 64_l2=0.0604\n", - "[70] time=1.37, avg_loss=0.9433, train_err=0.0472, 32_h1=0.0830, 32_l2=0.0432, 64_h1=0.1739, 64_l2=0.0583\n", - "[71] time=1.36, avg_loss=0.9813, train_err=0.0491, 32_h1=0.0830, 32_l2=0.0433, 64_h1=0.1691, 64_l2=0.0588\n", - "[72] time=1.36, avg_loss=0.9456, train_err=0.0473, 32_h1=0.0828, 32_l2=0.0429, 64_h1=0.1695, 64_l2=0.0599\n", - "[73] time=1.37, avg_loss=0.9099, train_err=0.0455, 32_h1=0.0835, 32_l2=0.0438, 64_h1=0.1716, 64_l2=0.0599\n", - "[74] time=1.37, avg_loss=0.9241, train_err=0.0462, 32_h1=0.0816, 32_l2=0.0419, 64_h1=0.1699, 64_l2=0.0572\n", - "[75] time=1.37, avg_loss=0.8907, train_err=0.0445, 32_h1=0.0825, 32_l2=0.0410, 64_h1=0.1772, 64_l2=0.0604\n", - "[76] time=1.36, avg_loss=0.8940, train_err=0.0447, 32_h1=0.0821, 32_l2=0.0428, 64_h1=0.1733, 64_l2=0.0588\n", - "[77] time=1.37, avg_loss=0.8958, train_err=0.0448, 32_h1=0.0828, 32_l2=0.0447, 64_h1=0.1756, 64_l2=0.0593\n", - "[78] time=1.37, avg_loss=0.9276, train_err=0.0464, 32_h1=0.0816, 32_l2=0.0424, 64_h1=0.1740, 64_l2=0.0599\n", - "[79] time=1.37, avg_loss=0.8763, train_err=0.0438, 32_h1=0.0818, 32_l2=0.0414, 64_h1=0.1715, 64_l2=0.0570\n", - "[80] time=1.36, avg_loss=0.8634, train_err=0.0432, 32_h1=0.0812, 32_l2=0.0416, 64_h1=0.1753, 64_l2=0.0614\n", - "[81] time=1.36, avg_loss=0.8450, train_err=0.0423, 32_h1=0.0832, 32_l2=0.0448, 64_h1=0.1701, 64_l2=0.0626\n", - "[82] time=1.37, avg_loss=0.8997, train_err=0.0450, 32_h1=0.0818, 32_l2=0.0419, 64_h1=0.1718, 64_l2=0.0590\n", - "[83] time=1.37, avg_loss=0.8658, train_err=0.0433, 32_h1=0.0816, 32_l2=0.0415, 64_h1=0.1703, 64_l2=0.0552\n", - "[84] time=1.37, avg_loss=0.9292, train_err=0.0465, 32_h1=0.0815, 32_l2=0.0424, 64_h1=0.1674, 64_l2=0.0580\n", - "[85] time=1.36, avg_loss=0.9417, train_err=0.0471, 32_h1=0.0825, 32_l2=0.0439, 64_h1=0.1755, 64_l2=0.0608\n", - "[86] time=1.37, avg_loss=0.8608, train_err=0.0430, 32_h1=0.0792, 32_l2=0.0392, 64_h1=0.1720, 64_l2=0.0573\n", - "[87] time=1.38, avg_loss=0.9083, train_err=0.0454, 32_h1=0.0822, 32_l2=0.0440, 64_h1=0.1693, 64_l2=0.0602\n", - "[88] time=1.57, avg_loss=0.8522, train_err=0.0426, 32_h1=0.0823, 32_l2=0.0427, 64_h1=0.1695, 64_l2=0.0571\n", - "[89] time=1.36, avg_loss=0.8273, train_err=0.0414, 32_h1=0.0813, 32_l2=0.0414, 64_h1=0.1702, 64_l2=0.0568\n", - "[90] time=1.36, avg_loss=0.8612, train_err=0.0431, 32_h1=0.0834, 32_l2=0.0468, 64_h1=0.1718, 64_l2=0.0641\n", - "[91] time=1.36, avg_loss=0.8358, train_err=0.0418, 32_h1=0.0811, 32_l2=0.0410, 64_h1=0.1678, 64_l2=0.0558\n", - "[92] time=1.37, avg_loss=0.8725, train_err=0.0436, 32_h1=0.0807, 32_l2=0.0408, 64_h1=0.1688, 64_l2=0.0557\n", - "[93] time=1.36, avg_loss=0.8163, train_err=0.0408, 32_h1=0.0804, 32_l2=0.0417, 64_h1=0.1714, 64_l2=0.0593\n", - "[94] time=1.36, avg_loss=0.8119, train_err=0.0406, 32_h1=0.0791, 32_l2=0.0393, 64_h1=0.1706, 64_l2=0.0581\n", - "[95] time=1.36, avg_loss=0.8022, train_err=0.0401, 32_h1=0.0819, 32_l2=0.0416, 64_h1=0.1697, 64_l2=0.0555\n", - "[96] time=1.37, avg_loss=0.8371, train_err=0.0419, 32_h1=0.0793, 32_l2=0.0393, 64_h1=0.1684, 64_l2=0.0570\n", - "[97] time=1.37, avg_loss=0.8227, train_err=0.0411, 32_h1=0.0800, 32_l2=0.0407, 64_h1=0.1685, 64_l2=0.0583\n", - "[98] time=1.43, avg_loss=0.8176, train_err=0.0409, 32_h1=0.0841, 32_l2=0.0471, 64_h1=0.1681, 64_l2=0.0578\n", - "[99] time=1.85, avg_loss=0.8517, train_err=0.0426, 32_h1=0.0809, 32_l2=0.0401, 64_h1=0.1726, 64_l2=0.0607\n", - "[100] time=1.85, avg_loss=0.8445, train_err=0.0422, 32_h1=0.0810, 32_l2=0.0408, 64_h1=0.1688, 64_l2=0.0558\n", - "[101] time=1.42, avg_loss=0.7962, train_err=0.0398, 32_h1=0.0796, 32_l2=0.0393, 64_h1=0.1680, 64_l2=0.0577\n", - "[102] time=1.84, avg_loss=0.7758, train_err=0.0388, 32_h1=0.0799, 32_l2=0.0398, 64_h1=0.1664, 64_l2=0.0556\n", - "[103] time=1.87, avg_loss=0.8005, train_err=0.0400, 32_h1=0.0792, 32_l2=0.0395, 64_h1=0.1688, 64_l2=0.0552\n", - "[104] time=1.43, avg_loss=0.8099, train_err=0.0405, 32_h1=0.0791, 32_l2=0.0394, 64_h1=0.1664, 64_l2=0.0535\n", - "[105] time=1.37, avg_loss=0.7828, train_err=0.0391, 32_h1=0.0815, 32_l2=0.0430, 64_h1=0.1691, 64_l2=0.0574\n", - "[106] time=1.37, avg_loss=0.7799, train_err=0.0390, 32_h1=0.0795, 32_l2=0.0393, 64_h1=0.1679, 64_l2=0.0556\n", - "[107] time=1.36, avg_loss=0.7685, train_err=0.0384, 32_h1=0.0810, 32_l2=0.0434, 64_h1=0.1725, 64_l2=0.0633\n", - "[108] time=1.36, avg_loss=0.7581, train_err=0.0379, 32_h1=0.0801, 32_l2=0.0407, 64_h1=0.1744, 64_l2=0.0574\n", - "[109] time=1.37, avg_loss=0.7415, train_err=0.0371, 32_h1=0.0782, 32_l2=0.0383, 64_h1=0.1670, 64_l2=0.0540\n", - "[110] time=1.37, avg_loss=0.7387, train_err=0.0369, 32_h1=0.0790, 32_l2=0.0392, 64_h1=0.1664, 64_l2=0.0539\n", - "[111] time=1.37, avg_loss=0.7338, train_err=0.0367, 32_h1=0.0788, 32_l2=0.0385, 64_h1=0.1694, 64_l2=0.0574\n", - "[112] time=1.36, avg_loss=0.7426, train_err=0.0371, 32_h1=0.0811, 32_l2=0.0434, 64_h1=0.1745, 64_l2=0.0593\n", - "[113] time=1.36, avg_loss=0.7849, train_err=0.0392, 32_h1=0.0817, 32_l2=0.0452, 64_h1=0.1653, 64_l2=0.0627\n", - "[114] time=1.37, avg_loss=0.7933, train_err=0.0397, 32_h1=0.0803, 32_l2=0.0409, 64_h1=0.1715, 64_l2=0.0568\n", - "[115] time=1.37, avg_loss=0.7377, train_err=0.0369, 32_h1=0.0789, 32_l2=0.0389, 64_h1=0.1688, 64_l2=0.0556\n", - "[116] time=1.37, avg_loss=0.7639, train_err=0.0382, 32_h1=0.0794, 32_l2=0.0394, 64_h1=0.1683, 64_l2=0.0574\n", - "[117] time=1.36, avg_loss=0.7515, train_err=0.0376, 32_h1=0.0785, 32_l2=0.0382, 64_h1=0.1665, 64_l2=0.0549\n", - "[118] time=1.37, avg_loss=0.7180, train_err=0.0359, 32_h1=0.0792, 32_l2=0.0394, 64_h1=0.1671, 64_l2=0.0576\n", - "[119] time=1.37, avg_loss=0.7191, train_err=0.0360, 32_h1=0.0795, 32_l2=0.0396, 64_h1=0.1672, 64_l2=0.0541\n", - "[120] time=1.37, avg_loss=0.7148, train_err=0.0357, 32_h1=0.0792, 32_l2=0.0389, 64_h1=0.1671, 64_l2=0.0575\n", - "[121] time=1.36, avg_loss=0.7012, train_err=0.0351, 32_h1=0.0795, 32_l2=0.0399, 64_h1=0.1639, 64_l2=0.0555\n", - "[122] time=1.37, avg_loss=0.6962, train_err=0.0348, 32_h1=0.0787, 32_l2=0.0388, 64_h1=0.1697, 64_l2=0.0570\n", - "[123] time=1.37, avg_loss=0.6970, train_err=0.0349, 32_h1=0.0793, 32_l2=0.0388, 64_h1=0.1693, 64_l2=0.0567\n", - "[124] time=1.37, avg_loss=0.6888, train_err=0.0344, 32_h1=0.0788, 32_l2=0.0382, 64_h1=0.1687, 64_l2=0.0570\n", - "[125] time=1.37, avg_loss=0.7060, train_err=0.0353, 32_h1=0.0799, 32_l2=0.0412, 64_h1=0.1649, 64_l2=0.0576\n", - "[126] time=1.36, avg_loss=0.6991, train_err=0.0350, 32_h1=0.0792, 32_l2=0.0393, 64_h1=0.1681, 64_l2=0.0583\n", - "[127] time=1.37, avg_loss=0.7098, train_err=0.0355, 32_h1=0.0796, 32_l2=0.0406, 64_h1=0.1641, 64_l2=0.0574\n", - "[128] time=1.37, avg_loss=0.6971, train_err=0.0349, 32_h1=0.0792, 32_l2=0.0399, 64_h1=0.1690, 64_l2=0.0588\n", - "[129] time=1.37, avg_loss=0.6810, train_err=0.0340, 32_h1=0.0793, 32_l2=0.0393, 64_h1=0.1648, 64_l2=0.0559\n", - "[130] time=1.36, avg_loss=0.6848, train_err=0.0342, 32_h1=0.0780, 32_l2=0.0378, 64_h1=0.1670, 64_l2=0.0536\n", - "[131] time=1.94, avg_loss=0.6600, train_err=0.0330, 32_h1=0.0779, 32_l2=0.0379, 64_h1=0.1661, 64_l2=0.0545\n", - "[132] time=1.87, avg_loss=0.6428, train_err=0.0321, 32_h1=0.0794, 32_l2=0.0394, 64_h1=0.1695, 64_l2=0.0588\n", - "[133] time=1.85, avg_loss=0.6532, train_err=0.0327, 32_h1=0.0789, 32_l2=0.0392, 64_h1=0.1690, 64_l2=0.0568\n", - "[134] time=1.88, avg_loss=0.6573, train_err=0.0329, 32_h1=0.0780, 32_l2=0.0376, 64_h1=0.1664, 64_l2=0.0559\n", - "[135] time=1.78, avg_loss=0.6445, train_err=0.0322, 32_h1=0.0784, 32_l2=0.0386, 64_h1=0.1644, 64_l2=0.0560\n", - "[136] time=1.82, avg_loss=0.6378, train_err=0.0319, 32_h1=0.0780, 32_l2=0.0383, 64_h1=0.1651, 64_l2=0.0527\n", - "[137] time=1.85, avg_loss=0.6550, train_err=0.0327, 32_h1=0.0792, 32_l2=0.0400, 64_h1=0.1652, 64_l2=0.0551\n", - "[138] time=1.75, avg_loss=0.6341, train_err=0.0317, 32_h1=0.0775, 32_l2=0.0376, 64_h1=0.1661, 64_l2=0.0556\n", - "[139] time=2.00, avg_loss=0.8234, train_err=0.0412, 32_h1=0.0783, 32_l2=0.0386, 64_h1=0.1654, 64_l2=0.0566\n", - "[140] time=1.97, avg_loss=0.6822, train_err=0.0341, 32_h1=0.0784, 32_l2=0.0380, 64_h1=0.1675, 64_l2=0.0558\n", - "[141] time=1.98, avg_loss=0.6332, train_err=0.0317, 32_h1=0.0778, 32_l2=0.0379, 64_h1=0.1670, 64_l2=0.0546\n", - "[142] time=1.99, avg_loss=0.6205, train_err=0.0310, 32_h1=0.0786, 32_l2=0.0394, 64_h1=0.1678, 64_l2=0.0592\n", - "[143] time=1.82, avg_loss=0.6098, train_err=0.0305, 32_h1=0.0785, 32_l2=0.0386, 64_h1=0.1676, 64_l2=0.0584\n", - "[144] time=1.38, avg_loss=0.6116, train_err=0.0306, 32_h1=0.0794, 32_l2=0.0422, 64_h1=0.1702, 64_l2=0.0591\n", - "[145] time=1.37, avg_loss=0.6018, train_err=0.0301, 32_h1=0.0776, 32_l2=0.0373, 64_h1=0.1674, 64_l2=0.0564\n", - "[146] time=1.38, avg_loss=0.6001, train_err=0.0300, 32_h1=0.0781, 32_l2=0.0386, 64_h1=0.1662, 64_l2=0.0583\n", - "[147] time=1.38, avg_loss=0.5990, train_err=0.0300, 32_h1=0.0796, 32_l2=0.0416, 64_h1=0.1679, 64_l2=0.0572\n", - "[148] time=1.38, avg_loss=0.6462, train_err=0.0323, 32_h1=0.0802, 32_l2=0.0411, 64_h1=0.1721, 64_l2=0.0580\n", - "[149] time=1.37, avg_loss=0.6152, train_err=0.0308, 32_h1=0.0777, 32_l2=0.0373, 64_h1=0.1688, 64_l2=0.0562\n" - ] - } - ], - "source": [ - "trainer.train(train_loader, test_loaders,\n", - " output_encoder,\n", - " model, \n", - " optimizer,\n", - " scheduler, \n", - " regularizer=False, \n", - " training_loss=train_loss,\n", - " eval_losses=eval_losses)" - ] - }, - { - "cell_type": "markdown", - "id": "1b20be56-d200-44dc-b97b-fca021e353c8", - "metadata": {}, - "source": [ - "# Follow-up questions" - ] - }, - { - "cell_type": "markdown", - "id": "9a67e1d5-4b9a-4be3-bff4-fb2a6b152f9c", - "metadata": {}, - "source": [ - "You can now play with the configuration and see how the performance is impacted.\n", - "\n", - "Which parameters do you think will most influence performance? \n", - "Learning rate? Learning schedule? hidden_channels? Number of training samples? \n", - "\n", - "Does your intuition match the results you are getting?" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.15" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/slides/DeepONets.pdf b/slides/DeepONets.pdf new file mode 100644 index 0000000..e25bd00 Binary files /dev/null and b/slides/DeepONets.pdf differ diff --git a/slides/NeuralOperators.pdf b/slides/NeuralOperators.pdf new file mode 100644 index 0000000..cc248f9 Binary files /dev/null and b/slides/NeuralOperators.pdf differ