{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Transform module of `mlcolvar`\n", " \n", "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/luigibonati/mlcolvar/blob/main/docs/notebooks/tutorials/adv_transforms.ipynb)\n", "\n", "Author: Enrico Trizio" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this noteboook, we will provide an overview of the `transform` module of the library.\n", "This includes non-trainable modules based on a parent `transform` class and divided into two groups.\n", "\n", "---\n", "\n", "#### `tools`\n", "Processing tools and utilities, including, for example:\n", "- `Normalization`\n", "- `SwitchingFunctions`\n", "- `ContinuousHistogram`\n", "\n", "---\n", "#### `descriptors`\n", "Physical descriptors that can be computed from **atomic positions**, including, for example:\n", "- `PairwiseDistances`\n", "- `TorsionalAngles`\n", "- `CoordinationNumbers`\n", " \n", "Multiple descriptors can also be combined using the `MultipleDescriptors` class." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import torch\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from mlcolvar.io import load_dataframe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing descriptors with `mlcolvar`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All the descriptors classes share some general features:\n", "\n", "**Positions as input**\n", "\n", "Descriptors in `mlcolvar` take atomic positions as inputs. These can be also returned by `PLUMED` using the `POSITION` keyword for a simpler loading through colvar files.\n", "The positions can be used both as absolute coordinates (default) or as coordinates scaled on the cell's vectors. In this case, the `scaled_coords` key should be set to `True`. \n", "\n", "**Cell and PBC**\n", "\n", "Currently, PBC are implemented **only for orthorombic simulation cells**. \n", "By default, PBC are enabled, and the cell's dimensions have to be provided.\n", "\n", "The cell information can be passed in one of two ways, exclusively:\n", "- Fixed cell (e.g., NVT simulations), at initialization, using the `cell` keyword, for a fixed cell only. This mode supports torchscript of the preprocessing module and can be used with the `PLUMED` interface.\n", "- Varying cells, at runtime (e.g., NPT simulations), using the `cell` entry in the `forward` method or adding the `cell` data in the dataset used for training. At the moment, **it is not possible** to script the preprocessing module, as it is not supported in the `PLUMED` interface.\n", "\n", "**Combining descriptors**\n", "\n", "Multiple descriptors computed on the same set of positions (eventually of a diffent type) can be combined into a single transform object using `MultipleDescriptors`. For example, we can concatenate some torsional angles with some distances.\n", "\n", "Here we use alanine dipeptide as an example and we will first show how to compute the single decriptors and then how they can be combined.\n", "\n", "**Exporting models**\n", "\n", "Only fixed-cell descriptors are supported in the torchscript-based interface for PLUMED (see above).\n", "In any case, if the descriptors can be computed in `PLUMED`, to have faster inference, it is better to export the trained model without the preprocessing (e.g., setting `model.preprocessing = None` before the `model.to_torchscript` call)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# number of atoms\n", "n_atoms=10\n", "\n", "# simulation cell \n", "cell = torch.Tensor([3.0233, 3.0233, 3.0233])\n", "\n", " \n", "filenames = ['https://raw.githubusercontent.com/EnricoTrizio/committor_2.0/refs/heads/main/alanine/unbiased_sims/COLVAR_A',\n", " 'https://raw.githubusercontent.com/EnricoTrizio/committor_2.0/refs/heads/main/alanine/unbiased_sims/COLVAR_B',\n", " ] \n", "\n", "# load data\n", "dataframe = load_dataframe(file_names = filenames,\n", " create_labels = True,\n", " return_dataframe = True,\n", " start=0,\n", " stop=10000,\n", " stride=10,\n", " verbose = True)\n", "\n", "# we put the positions into a tensor\n", "positions = torch.Tensor(dataframe.filter(regex='p[1-9]\\.[abc]|p[1-2][0-9]\\.[abc]').values) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### TorsionalAngles\n", "Torsional angles can be computed using the `TorsionalAngles` class, which can return the sin, cos and angle value based on the `mode` key.\n", "\n", "Here we compute the sin and cos of the usual $\\phi$ and $\\psi$ angles of alanine." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 2])\n", "torch.Size([2000, 2])\n" ] } ], "source": [ "from mlcolvar.core.transform.descriptors import TorsionalAngles\n", "\n", "# initialize objects to compute angles\n", "# this computes the sin and cos of Phi, to return also the angle add 'angle' to the mode list.\n", "ComputePhi = TorsionalAngles(indices=[1,3,4,6],\n", " n_atoms=10,\n", " mode=['sin', 'cos'],\n", " PBC=True, \n", " cell=cell, \n", " scaled_coords=True)\n", "\n", "# this computes the sin and cos of Psi to return also the angle add 'angle' to the mode list.\n", "ComputePsi = TorsionalAngles(indices=[3,4,6,8],\n", " n_atoms=10,\n", " mode=['sin', 'cos'], \n", " PBC=True, \n", " cell=cell, \n", " scaled_coords=True)\n", "\n", "# we apply it on the input positions\n", "out_phi = ComputePhi(positions)\n", "out_psi = ComputePsi(positions)\n", "\n", "print(out_phi.shape)\n", "print(out_psi.shape)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, a list of indices can be given to compute multiple torsional angles all at once." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 4])\n", "True\n", "True\n" ] } ], "source": [ "# initialize objects to compute angles\n", "# this computes the sin and cos of Phi, to return also the angle add 'angle' to the mode list.\n", "ComputeAngles = TorsionalAngles(indices=[[1,3,4,6],\n", " [3,4,6,8]],\n", " n_atoms=10,\n", " mode=['sin', 'cos'],\n", " PBC=True, \n", " cell=cell, \n", " scaled_coords=True)\n", "\n", "# we apply it on the input positions\n", "out_angles = ComputeAngles(positions)\n", "\n", "print(out_angles.shape)\n", "\n", "# check that we got consistent results\n", "print(torch.allclose(out_phi, out_angles[:, :2]))\n", "print(torch.allclose(out_psi, out_angles[:, 2:]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PairwiseDistances\n", "Pairwise distances can be computed using the `PairwiseDistances` class, which, by default, computes all the non-duplicated distances between the given atoms. Otherwise, if we are interested in some specific distances only we can choose the corresponing atomic paris using the `slicing_pairs` keyword. " ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 45])\n", "torch.Size([2000, 3])\n" ] } ], "source": [ "from mlcolvar.core.transform.descriptors import PairwiseDistances\n", "\n", "# compute ALL the non duplicated distances within the given set of atoms\n", "# initialize object to compute all distances\n", "ComputeDistances = PairwiseDistances(n_atoms=10, \n", " PBC=True, \n", " cell=cell, \n", " scaled_coords=True)\n", "\n", "# we apply it to the positions\n", "out_dist = ComputeDistances(positions)\n", "\n", "print(out_dist.shape)\n", "\n", "# compute only some specific distances within the given set of atoms\n", "# initialize object to compute distances between atom pairs: 0:1 2:3 4:5\n", "ComputeDistances_red = PairwiseDistances(n_atoms=10, \n", " PBC=True, \n", " cell=cell, \n", " scaled_coords=True,\n", " slicing_pairs=[[0, 1],\n", " [2, 3],\n", " [4, 5],])\n", "\n", "# we apply it to the positions\n", "out_dist_red = ComputeDistances_red(positions)\n", "\n", "print(out_dist_red.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### CoordinationNumbers\n", "The coordination number of a group A of atoms with respect to those from a group B based on a cutoff radius can be computed using the `CoordinationNumbers` class.\n", "the overall behaviour of this class mimics the one of `COORDINATION` in `PLUMED`." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 2])\n" ] } ], "source": [ "from mlcolvar.core.transform.descriptors import CoordinationNumbers\n", "from mlcolvar.core.transform.tools import SwitchingFunctions\n", "\n", "# to make the coordiantion numbers derivable we need to apply a switching function for the cutoff\n", "# more details are given below\n", "# here it's important to note that the optional dmax ensures that at cutoff the switch value is zero, similarly to PLUMED\n", "switching_function = SwitchingFunctions(in_features=10*3, # n_atoms*3\n", " name='Rational',\n", " cutoff=0.1,\n", " dmax=0.1,\n", " options={'n': 6, 'm': 12}\n", " )\n", "\n", "# compute the coordination of atoms 0 and 9 with respect to the others\n", "ComputeCoord = CoordinationNumbers(group_A=[0, 9],\n", " group_B=[1,2,3,4,5,6,7,8],\n", " cutoff=0.1, \n", " n_atoms=10, \n", " PBC=True, \n", " cell=cell,\n", " mode='continuous', # there's also a discontinuous mode with a sharp cutoff that is not derivable but can be used for analysis\n", " switching_function=switching_function,\n", " dmax=0.1\n", " )\n", "\n", "# we apply it to the positions\n", "out_coord = ComputeCoord(positions)\n", "\n", "print(out_coord.shape)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Perfoming calculations with varying cell size\n", "\n", "For computing descriptors with varying cell size (e.g., NPT simulations), at initialization of the descriptor class, one must set `cell=None` (default).\n", "The cell values must be passed as arguments to the `forward` pass." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 45])\n" ] } ], "source": [ "# we make a mock varying cell list of tensors, with the same cell for all frames just for testing purposes\n", "cell_list = torch.stack([cell for _ in range(positions.shape[0])])\n", "\n", "ComputeDistances_RuntimeCell = PairwiseDistances(n_atoms=10, \n", " PBC=True, \n", " cell=None, \n", " scaled_coords=True)\n", "\n", "# we apply it to the positions\n", "out_dist_list = ComputeDistances_RuntimeCell(positions, cell=cell_list)\n", "\n", "print(out_dist_list.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Combining descriptors with `mlcolvar`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple and different descriptors acting on the same positions can be combine using the `MultipleDescriptors` class, which takes a list of descriptor objects and combines them into a single model." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 49])\n" ] } ], "source": [ "from mlcolvar.core.transform.descriptors import MultipleDescriptors\n", "\n", "# we combine the descriptors\n", "ComputeDescriptors = MultipleDescriptors(descriptors_list=[ComputePhi, # two outputs \n", " ComputePsi, # two outputs\n", " ComputeDistances # three outputs\n", " ], \n", " n_atoms=n_atoms)\n", "\n", "# we applied the combined model to the input positions\n", "out_combined = ComputeDescriptors(positions)\n", "\n", "print(out_combined.shape)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Processing tools in `mlcolvar`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transform objects in `tools` are used for general processing of data.\n", "Here we'll see two examples:\n", "- `SwitchingFunctions`, which can be used, for example, to define contacts from distances\n", "- `ContinuousHistograms`, which can be used, for example, to build differentiable CVs related to the distribution of some input. \n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### SwitchingFunctions\n", "Switching functions can be used to *switch* some input above and below a threshold and are avaialble using the `SwitchingFunction` class.\n", "\n", "For example, we can use this to turn our distances into contacts, which may be more suitable to describe the on/off nature of chemical reactions." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([2000, 45])\n" ] } ], "source": [ "from mlcolvar.core.transform.tools import SwitchingFunctions\n", "\n", "# initialize object to compute switching function\n", "ComputeSwitch = SwitchingFunctions(in_features=45,\n", " name='Rational',\n", " cutoff=0.3,\n", " options={'n': 6, 'm': 12})\n", "\n", "# we apply to the distances\n", "out_contacts = ComputeSwitch(out_dist)\n", "\n", "print(out_contacts.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ContinuousHistogram\n", "\n", "Continuous histograms can be computed using the `ContinuousHistogram` class, which allows constructing differentiable histograms using Gaussian kernels returning the values of the bins.\n", "\n", "We can test this on the distances and contacts in our system" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "image/png": "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OPfcc4v250nve/PEE0/gc5/7HIYMGYKqqircc889iEQiNo3WHuvWrcP111+PCRMmwOVyYfny5Tkfs3btWlx88cXweDw4++yz8eKLL5o7KCGhpUuXioqKCvH888+L3bt3i7vuukuMHDlStLS0pL1+w4YNwu12i0cffVTs2bNH/PrXvxaDBg0SO3futHnk1tP73nz7298WTz31lNi2bZvYu3evuP3220VlZaVoaGiweeTW0vu+pNTW1orTTjtNXHnlleKGG26wZ7A20vu+RKNRcemll4prr71WrF+/XtTW1oq1a9eK7du32zxy6+l9b1566SXh8XjESy+9JGpra8WqVavE+PHjxT333GPzyK21YsUKcd9994nXX39dABDLli3Len1NTY0YOnSoWLBggdizZ4948sknhdvtFitXrjRtTFIGqhkzZoj58+f3fK0oipgwYYKorq5Oe/23vvUtcd111/W5b+bMmeL73/++peN0gt73pr9EIiGGDx8uFi9ebNUQHWHkfUkkEuLyyy8Xf/nLX8S8efOKMlDpfV+efvppMWnSJBGLxewaomP0vjfz588XX/7yl/vct2DBAjF79mxLx+kkLYHqX//1X8XUqVP73HfzzTeLa665xrRxSFf6i8Vi2LJlC+bMmdNzX1lZGebMmYOPP/447WM+/vjjPtcDwDXXXJPx+kJl5L3pLxQKIR6PY/To0VYN03ZG35d/+7d/w9ixY/G9733PjmHazsj78sYbb2DWrFmYP38+xo0bh2nTpuGRRx6Boih2DdsWRt6byy+/HFu2bOkpD9bU1GDFihW49tprbRmzrOz4/Wv77um5tLe3Q1EUjBs3rs/948aNw759+9I+prm5Oe31zc3Nlo3TCUbem/5++ctfYsKECQO+sQqZkfdl/fr1eO6557B9+3YbRugMI+9LTU0N3nvvPdx6661YsWIFDh06hB/96EeIx+O4//777Ri2LYy8N9/+9rfR3t6OK664AkIIJBIJ/OAHP8CvfvUrO4YsrUy/f30+H8LhMIYMGZL3a0iXUZF1Fi1ahKVLl2LZsmUYPHiw08NxjN/vx2233YZnn30Wp5xyitPDkYqqqhg7diz+/Oc/45JLLsHNN9+M++67D88884zTQ3Pc2rVr8cgjj+A///M/sXXrVrz++ut466238NBDDzk9tKInXUZ1yimnwO12o6Wlpc/9LS0tOPXUU9M+5tRTT9V1faEy8t6kPPbYY1i0aBFWr16N6dOnWzlM2+l9Xw4fPowjR47g+uuv77lPVVUAQHl5Ofbv34/JkydbO2gbGPl+GT9+PAYNGgS3291z33nnnYfm5mbEYjFUVFRYOma7GHlvfvOb3+C2227DP//zPwMALrjgAgSDQdx999247777ch7+V6wy/f4dMWKEKdkUIGFGVVFRgUsuuQRr1qzpuU9VVaxZswazZs1K+5hZs2b1uR4A3n333YzXFyoj7w0APProo3jooYewcuVKXHrppXYM1VZ635cpU6Zg586d2L59e8/t7/7u73D11Vdj+/btqKqqsnP4ljHy/TJ79mwcOnSoJ3ADwIEDBzB+/PiiCVKAsfcmFAoNCEapgC5KeG9vW37/mtaWYaKlS5cKj8cjXnzxRbFnzx5x9913i5EjR4rm5mYhhBC33XabuPfee3uu37BhgygvLxePPfaY2Lt3r7j//vuLuj1dz3uzaNEiUVFRIf72t7+Jpqamnpvf73fqr2AJve9Lf8Xa9af3fTl69KgYPny4+PGPfyz2798v3nzzTTF27Fjxu9/9zqm/gmX0vjf333+/GD58uHjllVdETU2NeOedd8TkyZPFt771Laf+Cpbw+/1i27ZtYtu2bQKAePzxx8W2bdtEXV2dEEKIe++9V9x2220916fa03/xi1+IvXv3iqeeeqo02tOFEOLJJ58UEydOFBUVFWLGjBli48aNPX921VVXiXnz5vW5/tVXXxXnnnuuqKioEFOnThVvvfWWzSO2j5735owzzhAABtzuv/9++wduMb3fM70Va6ASQv/78tFHH4mZM2cKj8cjJk2aJB5++GGRSCRsHrU99Lw38XhcPPDAA2Ly5Mli8ODBoqqqSvzoRz8SXV1d9g/cQu+//37a3xmp92LevHniqquuGvCYiy66SFRUVIhJkyaJF154wdQx8TwqIiKSmnRzVERERL0xUBERkdQYqIiISGoMVEREJDUGKiIikhoDFRERSY2BioiIpMZARUREUmOgIiIiqTFQERGR1BioiIhIav8flSrG3z6UMFUAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from mlcolvar.core.transform.tools import ContinuousHistogram\n", "\n", "# we initialize the object for the histogram computation \n", "ComputeHistogram = ContinuousHistogram(in_features=45,\n", " min=0,\n", " max=1, \n", " bins=100)\n", "\n", "# we apply it to the distances\n", "out_hist_dist = ComputeHistogram(out_dist)\n", "# we apply it to the contacts\n", "out_hist_contacts = ComputeHistogram(out_contacts)\n", "\n", "# we compare the two distributions\n", "plt.figure(figsize=(5,4))\n", "plt.fill_between(np.linspace(0, 1, 100), 0, out_hist_dist.mean(axis=0), alpha=0.8)\n", "plt.fill_between(np.linspace(0, 1, 100), 0, out_hist_contacts.mean(axis=0), alpha=0.8)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sequentially apply transforms in `mlcolvar` \n", "To apply sequentially different transforms we can simply use a `torch.nn.Sequential` class." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "image/png": "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UkBi98bv9/dIRp0JXQzgvb1N+f2r27IMHS1dulQ4nU6xcOFCsW/fPnH//feLYcOGifr6eiGEEHfeead47LHHutpv3LhRpKWliZdeekns379fzJkzJ6WXp8dzb+bOnSsyMjLE3//+d1FXV9f18Xq9Vv0rGCLe+3KiVF31F+99qaqqEllZWeKBBx4QBw8eFJ9++qkYPXq0eO6556z6VzBMvPdmzpw5IisrS7z//vuirKxMfPHFF+K0004Tt9xyi1X/Cobwer1ix44dYseOHQKAePnll8WOHTtEZWWlEEKIxx57TNx5551d7TuXp//2t78V+/fvF/PmzRsYy9OFEOLVV18VJ510ksjIyBCXXHKJKCws7Pre5ZdfLmbOnNmt/bJly8QZZ5whMjIyxKRJk8Rnn31mcsTmiefenHzyyQJAj8+cOXPMD9xg8f7MfFuqJioh4r8vmzZtElOmTBFOp1Oceuqp4vnnnxfhcNjkqM0Rz70JhULi6aefFqeddprIzMwUeXl54le/+pVobW01P3ADffnllxF/Z3Tei5kzZ4rLL7+8xznf+973REZGhjj11FPFggULdI2J9aiIiEhq0j2jIiIi+jYmKiIikhoTFRERSY2JioiIpMZERUREUmOiIiIiqTFRERGR1JioiIhIakxUREQkNSYqIiKSGhMVERFJ7f8D1poYsCjyssEAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ComputeContactsHist = torch.nn.Sequential(ComputeDistances, ComputeSwitch, ComputeHistogram)\n", "\n", "# we initialize the object for the computation positions -> distances -> contacts -> histogram\n", "out_sequential = ComputeContactsHist(positions)\n", "\n", "# we compare the two distributions\n", "plt.figure(figsize=(5,4))\n", "plt.fill_between(np.linspace(0, 1, 100), 0, out_sequential.mean(axis=0), alpha=0.8)\n", "plt.fill_between(np.linspace(0, 1, 100), 0, out_hist_contacts.mean(axis=0), alpha=0.8)\n", "plt.show()\n" ] } ], "metadata": { "kernelspec": { "display_name": "graph_mlcolvar_test_2.5", "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.18" } }, "nbformat": 4, "nbformat_minor": 2 }