From 131dcf7c273ba68c2fb8384d1c3fff1a60712bb4 Mon Sep 17 00:00:00 2001 From: degiacom Date: Wed, 29 May 2024 14:52:52 +0100 Subject: [PATCH] update doc --- build/lib/molearn/__init__.py | 19 + build/lib/molearn/analysis/GUI.py | 625 ++++++++++ build/lib/molearn/analysis/__init__.py | 19 + build/lib/molearn/analysis/analyser.py | 524 ++++++++ build/lib/molearn/analysis/path.py | 253 ++++ build/lib/molearn/data/__init__.py | 15 + build/lib/molearn/data/pdb_data.py | 145 +++ build/lib/molearn/loss_functions/__init__.py | 15 + .../molearn/loss_functions/openmm_thread.py | 382 ++++++ .../loss_functions/torch_protein_energy.py | 309 +++++ .../torch_protein_energy_utils.py | 1093 +++++++++++++++++ build/lib/molearn/models/__init__.py | 12 + build/lib/molearn/models/foldingnet.py | 261 ++++ build/lib/molearn/models/leaky_autoencoder.py | 178 +++ build/lib/molearn/models/old_autoencoder.py | 216 ++++ build/lib/molearn/scoring/__init__.py | 15 + build/lib/molearn/scoring/dope_score.py | 137 +++ .../lib/molearn/scoring/ramachandran_score.py | 99 ++ build/lib/molearn/trainers/__init__.py | 16 + build/lib/molearn/trainers/molearn_trainer.py | 356 ++++++ .../lib/molearn/trainers/sinkhorn_trainer.py | 264 ++++ build/lib/molearn/utils.py | 54 + dist/molearn-1.0.0-py3.9.egg | Bin 0 -> 133161 bytes docs/build/.buildinfo | 2 +- docs/build/.doctrees/FAQ.doctree | Bin 0 -> 9984 bytes docs/build/.doctrees/analysis.doctree | Bin 112832 -> 110105 bytes docs/build/.doctrees/data.doctree | Bin 41407 -> 40386 bytes docs/build/.doctrees/environment.pickle | Bin 861286 -> 187570 bytes docs/build/.doctrees/index.doctree | Bin 6959 -> 6900 bytes docs/build/.doctrees/loss_functions.doctree | Bin 73275 -> 73224 bytes docs/build/.doctrees/models.doctree | Bin 19244 -> 17924 bytes docs/build/.doctrees/scoring.doctree | Bin 40762 -> 28716 bytes docs/build/.doctrees/trainers.doctree | Bin 191217 -> 189689 bytes docs/build/FAQ.html | 155 +++ docs/build/_modules/index.html | 31 +- docs/build/_modules/molearn/analysis/GUI.html | 30 +- .../_modules/molearn/analysis/analyser.html | 135 +- .../build/_modules/molearn/analysis/path.html | 40 +- .../build/_modules/molearn/data/pdb_data.html | 81 +- .../molearn/loss_functions/openmm_thread.html | 114 +- .../molearn/models/CNN_autoencoder.html | 30 +- .../_modules/molearn/models/foldingnet.html | 39 +- docs/build/_modules/molearn/scoring.html | 107 ++ .../_modules/molearn/scoring/dope_score.html | 58 +- .../trainers/openmm_physics_trainer.html | 94 +- .../trainers/torch_physics_trainer.html | 52 +- .../_modules/molearn/trainers/trainer.html | 161 ++- docs/build/_sources/FAQ.rst.txt | 69 ++ docs/build/_sources/models.rst.txt | 14 +- docs/build/_static/basic.css | 55 +- docs/build/_static/doctools.js | 132 +- docs/build/_static/documentation_options.js | 7 +- docs/build/_static/language_data.js | 2 +- docs/build/_static/searchtools.js | 109 +- docs/build/_static/sphinxdoc.css | 8 +- docs/build/analysis.html | 122 +- docs/build/data.html | 78 +- docs/build/genindex.html | 35 +- docs/build/index.html | 80 +- docs/build/loss_functions.html | 101 +- docs/build/models.html | 67 +- docs/build/objects.inv | Bin 1295 -> 1263 bytes docs/build/py-modindex.html | 25 +- docs/build/scoring.html | 126 +- docs/build/search.html | 25 +- docs/build/searchindex.js | 2 +- docs/build/trainers.html | 130 +- docs/source/models.rst | 14 +- src/molearn.egg-info/PKG-INFO | 53 + src/molearn.egg-info/SOURCES.txt | 28 + src/molearn.egg-info/dependency_links.txt | 1 + src/molearn.egg-info/top_level.txt | 1 + 72 files changed, 6240 insertions(+), 1180 deletions(-) create mode 100644 build/lib/molearn/__init__.py create mode 100644 build/lib/molearn/analysis/GUI.py create mode 100644 build/lib/molearn/analysis/__init__.py create mode 100644 build/lib/molearn/analysis/analyser.py create mode 100644 build/lib/molearn/analysis/path.py create mode 100644 build/lib/molearn/data/__init__.py create mode 100644 build/lib/molearn/data/pdb_data.py create mode 100644 build/lib/molearn/loss_functions/__init__.py create mode 100644 build/lib/molearn/loss_functions/openmm_thread.py create mode 100644 build/lib/molearn/loss_functions/torch_protein_energy.py create mode 100644 build/lib/molearn/loss_functions/torch_protein_energy_utils.py create mode 100644 build/lib/molearn/models/__init__.py create mode 100644 build/lib/molearn/models/foldingnet.py create mode 100644 build/lib/molearn/models/leaky_autoencoder.py create mode 100644 build/lib/molearn/models/old_autoencoder.py create mode 100644 build/lib/molearn/scoring/__init__.py create mode 100644 build/lib/molearn/scoring/dope_score.py create mode 100644 build/lib/molearn/scoring/ramachandran_score.py create mode 100644 build/lib/molearn/trainers/__init__.py create mode 100644 build/lib/molearn/trainers/molearn_trainer.py create mode 100644 build/lib/molearn/trainers/sinkhorn_trainer.py create mode 100644 build/lib/molearn/utils.py create mode 100644 dist/molearn-1.0.0-py3.9.egg create mode 100644 docs/build/.doctrees/FAQ.doctree create mode 100644 docs/build/FAQ.html create mode 100644 docs/build/_modules/molearn/scoring.html create mode 100644 docs/build/_sources/FAQ.rst.txt create mode 100644 src/molearn.egg-info/PKG-INFO create mode 100644 src/molearn.egg-info/SOURCES.txt create mode 100644 src/molearn.egg-info/dependency_links.txt create mode 100644 src/molearn.egg-info/top_level.txt diff --git a/build/lib/molearn/__init__.py b/build/lib/molearn/__init__.py new file mode 100644 index 0000000..f263f2d --- /dev/null +++ b/build/lib/molearn/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +__author__ = "V. K. Ramaswamy, S. C. Musson, C. G. Willcocks, M. T. Degiacomi" +__version__ = '1.1' +__date__ = '$Date: 2022-10-26 $' + + +from .data import PDBData +from .trainers import OpenMM_Physics_Trainer \ No newline at end of file diff --git a/build/lib/molearn/analysis/GUI.py b/build/lib/molearn/analysis/GUI.py new file mode 100644 index 0000000..76848ed --- /dev/null +++ b/build/lib/molearn/analysis/GUI.py @@ -0,0 +1,625 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +# +# Author: Matteo Degiacomi + +import time +import pickle +from IPython import display + +import numpy as np +import MDAnalysis as mda + +import warnings +warnings.filterwarnings("ignore") + +from ipywidgets import Layout +from ipywidgets import widgets +from tkinter import Tk, filedialog +import plotly.graph_objects as go +import nglview as nv + +from ..utils import as_numpy +from .analyser import MolearnAnalysis +from .path import oversample, get_path_aggregate + + +class MolearnGUI(object): + + def __init__(self, MA=[]): + + if not isinstance(MA, MolearnAnalysis) and MA != []: + raise Exception(f'Expecting an MolearnAnalysis instance, {type(MA)} found') + else: + self.MA = MA + + self.waypoints = [] # collection of all saved waypoints + self.samples = [] # collection of all calculated sampling points + + self.run() + + + def update_trails(self): + + try: + crd = self.get_samples(self.mybox.value, int(self.samplebox.value), self.drop_path.value) + self.samples = crd.copy() + except: + self.button_pdb.disabled = False + return + + # update latent space plot + if self.samples == []: + self.latent.data[3].x = self.waypoints[:, 0] + self.latent.data[3].y = self.waypoints[:, 1] + else: + self.latent.data[3].x = self.samples[:, 0] + self.latent.data[3].y = self.samples[:, 1] + + self.latent.update() + + + def on_click(self, trace, points, selector): + ''' + control display of training set + ''' + + if len(points.xs) == 0: + return + + # add new waypoint to list + pt = np.array([[points.xs[0], points.ys[0]]]) + if len(self.waypoints) == 0: + self.waypoints = pt + else: + self.waypoints = np.concatenate((self.waypoints, pt)) + + # update textbox (triggering update of 3D representation) + try: + pt = self.waypoints.flatten().round(decimals=4).astype(str) + #pt = np.array([self.latent.data[3].x, self.latent.data[3].y]).T.flatten().round(decimals=4).astype(str) + self.mybox.value = " ".join(pt) + except: + return + + self.update_trails() + + + def get_samples(self, mybox, samplebox, path): + + if path == "A*": + use_path = True + else: + use_path = False + + try: + crd = np.array(mybox.split()).astype(float) + crd = crd.reshape((int(len(crd)/2), 2)) + except Exception: + raise Exception("Cannot define sampling points") + return + + if use_path: + # connect points via A* + try: + landscape = self.latent.data[0].z + crd = get_path_aggregate(crd, landscape.T, self.MA.xvals, self.MA.yvals) + except Exception as e: + raise Exception(f"Cannot define sampling points: path finding failed. {e})") + return + + else: + # connect points via straight line + try: + crd = oversample(crd, pts=int(samplebox)) + except Exception as e: + raise Exception(f"Cannot define sampling points: oversample failed. {e}") + return + + return crd + + + def interact_3D(self, mybox, samplebox, path): + ''' + generate and display proteins according to latent space trail + ''' + + try: + crd = self.get_samples(mybox, samplebox, path) + self.samples = crd.copy() + crd = crd.reshape((1, len(crd), 2)) + except: + self.button_pdb.disabled = True + return + + if crd.shape[1] == 0: + self.button_pdb.disabled = True + return + + # generate structures along path + t = time.time() + gen = self.MA.generate(crd) + print(f'{crd.shape[1]} struct. in {time.time()-t:.4f} sec.') + + # display generated structures + self.mymol.load_new(gen) + view = nv.show_mdanalysis(self.mymol) + view.add_representation("spacefill") + #view.add_representation("cartoon") + display.display(view) + + self.button_pdb.disabled = False + + + def drop_background_event(self, change): + ''' + control colouring style of latent space surface + ''' + + if change.new == "drift": + try: + data = self.MA.surf_z.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + elif change.new == "RMSD": + try: + data = self.MA.surf_c.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + + elif change.new == "target RMSD": + try: + data = self.MA.surf_target.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + + elif change.new == "DOPE_unrefined": + try: + data = self.MA.surf_dope_unrefined.T + except: + return + + self.block0.children[2].readout_format = 'd' + + elif change.new == "DOPE_refined": + try: + data = self.MA.surf_dope_refined.T + except: + return + + self.block0.children[2].readout_format = 'd' + + elif change.new == "ramachandran_favored": + try: + data = self.MA.surf_ramachandran_favored.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + elif change.new == "ramachandran_allowed": + try: + data = self.MA.surf_ramachandran_allowed.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + elif change.new == "ramachandran_outliers": + try: + data = self.MA.surf_ramachandran_outliers.T + except: + return + + self.block0.children[2].readout_format = '.1f' + + elif "custom" in change.new: + mykey = change.new.split(":")[1] + try: + data = self.MA.custom_data[mykey].T + except Exception: + return + + if np.abs(np.max(data) - np.min(data)) < 100: + self.block0.children[2].readout_format = '.1f' + else: + self.block0.children[2].readout_format = 'd' + + self.latent.data[0].z = data + + # step below necessary to avoid situations whereby temporarily min>max + try: + self.latent.data[0].zmin = np.min(data) + self.latent.data[0].zmax = np.max(data) + self.block0.children[2].min = np.min(data) + self.block0.children[2].max = np.max(data) + except: + self.latent.data[0].zmax = np.max(data) + self.latent.data[0].zmin = np.min(data) + self.block0.children[2].max = np.max(data) + self.block0.children[2].min = np.min(data) + + self.block0.children[2].value = (np.min(data), np.max(data)) + + self.update_trails() + + + def drop_path_event(self, change): + ''' + control way paths are looked for + ''' + if change.new == "A*": + self.block0.children[5].disabled = True + else: + self.block0.children[5].disabled = False + + self.update_trails() + + + def check_training_event(self, change): + ''' + control display of training set + ''' + state_choice = change.new + self.latent.data[1].visible = state_choice + self.latent.update() + + + def check_test_event(self, change): + ''' + control display of test set + ''' + state_choice = change.new + self.latent.data[2].visible = state_choice + self.latent.update() + + + def range_slider_event(self, change): + ''' + update surface colouring upon manipulation of range slider + ''' + self.latent.data[0].zmin = change.new[0] + self.latent.data[0].zmax = change.new[1] + self.latent.update() + + + + def trail_update_event(self, change): + ''' + update trails (waypoints and way they are connected) + ''' + + try: + crd = np.array(self.mybox.value.split()).astype(float) + crd = crd.reshape((int(len(crd)/2), 2)) + except: + self.button_pdb.disabled = False + return + + self.waypoints = crd.copy() + + self.update_trails() + + + def button_pdb_event(self, check): + ''' + save PDB file corresponding to the interpolation shown in the 3D view + ''' + + root = Tk() + root.withdraw() # Hide the main window. + root.call('wm', 'attributes', '.', '-topmost', True) # Raise the root to the top of all windows. + fname = filedialog.asksaveasfilename(defaultextension="pdb", filetypes=[("PDB file", "pdb")]) + + if fname == "": + return + + crd = self.get_samples(self.mybox.value, self.samplebox.value, self.drop_path.value) + self.samples = crd.copy() + crd = crd.reshape((1, len(crd), 2)) + + if crd.shape[1] == 0: + return + + gen = self.MA.generate(crd) + self.mymol.load_new(gen) + protein = self.mymol.select_atoms("all") + + with mda.Writer(fname, protein.n_atoms) as W: + for ts in self.mymol.trajectory: + W.write(protein) + + + def button_save_state_event(self, check): + ''' + save class state + ''' + + root = Tk() + root.withdraw() # Hide the main window. + root.call('wm', 'attributes', '.', '-topmost', True) # Raise the root to the top of all windows. + fname = filedialog.asksaveasfilename(defaultextension="p", filetypes=[("pickle file", "p")]) + + if fname == "": + return + + pickle.dump([self.MA, self.waypoints], open( fname, "wb" ) ) + + + def button_load_state_event(self, check): + ''' + load class state + ''' + + root = Tk() + root.withdraw() # Hide the main window. + root.call('wm', 'attributes', '.', '-topmost', True) # Raise the root to the top of all windows. + fname = filedialog.askopenfilename(defaultextension="p", filetypes=[("picke file", "p")]) + + if fname == "": + return + + try: + self.MA, self.waypoints = pickle.load( open( fname, "rb" ) ) + self.run() + except Exception as e: + raise Exception(f"Cannot load state file. {e}") + + ##################################################### + + def run(self): + + # create an MDAnalysis instance of input protein (for viewing purposes) + if hasattr(self.MA, "mol"): + self.MA.mol.write_pdb("tmp.pdb", conformations=[0]) + self.mymol = mda.Universe('tmp.pdb') + + ### MENU ITEMS ### + + # surface representation menu + options = [] + if hasattr(self.MA, "surf_z"): + options.append("drift") + if hasattr(self.MA, "surf_c"): + options.append("RMSD") + if hasattr(self.MA, "surf_dope_unrefined"): + options.append("DOPE_unrefined") + if hasattr(self.MA, "surf_dope_refined"): + options.append("DOPE_refined") + if hasattr(self.MA, "surf_target"): + options.append("target RMSD") + if hasattr(self.MA, "surf_ramachandran_favored"): + options.append("ramachandran_favored") + if hasattr(self.MA, "surf_ramachandran_allowed"): + options.append("ramachandran_allowed") + if hasattr(self.MA, "surf_ramachandran_outliers"): + options.append("ramachandran_outliers") + if hasattr(self.MA, "custom_data"): + for k in list(self.MA.custom_data): + options.append(f'custom:{k}') + + if len(options) == 0: + options.append("none") + + self.drop_background = widgets.Dropdown( + options=options, + value=options[0], + description='Surf.:', + layout=Layout(flex='1 1 0%', width='auto')) + + if "none" in options: + self.drop_background.disabled = True + + self.drop_background.observe(self.drop_background_event, names='value') + + # dropdown menu describing pathfinding method + self.drop_path = widgets.Dropdown( + options=["Euclidean", "A*"], + value="Euclidean", + description='Path:', + layout=Layout(flex='1 1 0%', width='auto')) + + self.drop_path.observe(self.drop_path_event, names='value') + + + # training set visualisation menu + self.check_training = widgets.Checkbox( + value=False, + description='show training', + disabled=False, + indent=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.check_training.observe(self.check_training_event, names='value') + + # test set visualisation menu + self.check_test = widgets.Checkbox( + value=False, + description='show test', + disabled=False, + indent=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.check_test.observe(self.check_test_event, names='value') + + # text box holding current coordinates + self.mybox = widgets.Textarea(placeholder='coordinates', + description='crds:', + disabled=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.mybox.observe(self.trail_update_event, names='value') + + # text box holding number of sampling points + self.samplebox = widgets.Text(value='10', + description='sampling:', + disabled=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.samplebox.observe(self.trail_update_event, names='value') + + + # button to save PDB file + self.button_pdb = widgets.Button( + description='Save PDB', + disabled=True, layout=Layout(flex='1 1 0%', width='auto')) + + self.button_pdb.on_click(self.button_pdb_event) + + + # button to save state file + self.button_save_state = widgets.Button( + description= 'Save state', + disabled=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.button_save_state.on_click(self.button_save_state_event) + + + # button to load state file + self.button_load_state = widgets.Button( + description= 'Load state', + disabled=False, layout=Layout(flex='1 1 0%', width='auto')) + + self.button_load_state.on_click(self.button_load_state_event) + + + # latent space range slider + self.range_slider = widgets.FloatRangeSlider( + description='cmap range:', + disabled=True, + continuous_update=False, + orientation='horizontal', + readout=True, + readout_format='.1f', layout=Layout(flex='1 1 0%', width='auto')) + + self.range_slider.observe(self.range_slider_event, names='value') + + + if self.MA == []: + self.button_save_state.disabled = True + self.button_pdb.disabled = True + + if self.waypoints == []: + self.button_pdb.disabled = True + + + ### LATENT SPACE REPRESENTATION ### + + # coloured background + if "drift" in options: + sc = self.MA.surf_z + elif "target RMSD" in options: + sc = self.MA.surf_target + elif "DOPE_unrefined" in options: + sc = self.MA.surf_dope_unrefined + elif "DOPE_refined" in options: + sc = self.MA.surf_dope_refined + elif "ramachandran_favored" in options: + sc = self.MA.surf_ramachandran_favored + elif "ramachandran_allowed" in options: + sc = self.MA.surf_ramachandran_allowed + elif "ramachandran_outliers" in options: + sc = self.MA.surf_ramachandran_outliers + elif len(options)>0: + if "custom" in options[0]: + label = options[0].split(":")[1] + sc = self.MA.custom_data[label] + else: + sc = [] + else: + sc = [] + + if len(sc)>0: + plot1 = go.Heatmap(x=self.MA.xvals, y=self.MA.yvals, z=sc.T, zmin=np.min(sc), zmax=np.max(sc), + colorscale='viridis', name="latent_space") + else: + + if self.MA: + xvals, yvals = self.MA._get_sampling_ranges(50) + else: + xvals = np.linspace(0, 1, 10) + yvals = np.linspace(0, 1, 10) + + surf_empty = np.zeros((len(xvals), len(yvals))) + plot1 = go.Heatmap(x=xvals, y=yvals, z=surf_empty, opacity=0.0, showscale=False, name="latent_space") + + # training set + if hasattr(self.MA, "training_set_z"): + color = "white" if len(sc)>0 else "black" + plot2 = go.Scatter(x=as_numpy(self.MA.training_set_z)[:, 0].flatten(), + y=as_numpy(self.MA.training_set_z)[:, 1].flatten(), + showlegend=False, opacity=0.9, mode="markers", + marker=dict(color=color, size=5), name="training", visible=False) + else: + plot2 = go.Scatter(x=[], y=[]) + self.check_training.disabled = True + + # test set + if hasattr(self.MA, "test_set_z"): + plot3 = go.Scatter(x=as_numpy(self.MA.test_set_z)[:, 0].flatten(), + y=as_numpy(self.MA.test_set_z)[:, 1].flatten(), + showlegend=False, opacity=0.9, mode="markers", + marker=dict(color='silver', size=5), name="test", visible=False) + else: + plot3 = go.Scatter(x=[], y=[]) + self.check_test.disabled = True + + # path + plot4 = go.Scatter(x=np.array([]), y=np.array([]), + showlegend=False, opacity=0.9, mode = 'lines+markers', + marker=dict(color='red', size=4)) + + self.latent = go.FigureWidget([plot1, plot2, plot3, plot4]) + self.latent.update_layout(xaxis_title="latent vector 1", yaxis_title="latent vector 2", + autosize=True, width=400, height=350, margin=dict(l=75, r=0, t=25, b=0)) + self.latent.update_xaxes(showspikes=False) + self.latent.update_yaxes(showspikes=False) + + if len(sc)>0: + self.range_slider.value = (np.min(sc), np.max(sc)) + self.range_slider.min = np.min(sc) + self.range_slider.max = np.max(sc) + self.range_slider.step = (np.max(sc)-np.min(sc))/100.0 + self.range_slider.disabled = False + + # 3D protein representation (triggered by update of textbox, sampling box, or pathfinding method) + self.protein = widgets.interactive_output(self.interact_3D, {'mybox': self.mybox, 'samplebox': self.samplebox, 'path': self.drop_path}) + + + ### WIDGETS ARRANGEMENT ### + + self.block0 = widgets.VBox([self.check_training, self.check_test, self.range_slider, + self.drop_background, self.drop_path, self.samplebox, self.mybox, + self.button_pdb, self.button_save_state, self.button_load_state], + layout=Layout(flex='1 1 2', width='auto', border="solid")) + + self.block1 = widgets.VBox([self.latent], layout=Layout(flex='1 1 auto', width='auto')) + + # make all items displayed clickable + for item in self.latent.data: + item.on_click(self.on_click) + + self.block2 = widgets.VBox([self.protein], layout=Layout(flex='1 5 auto', width='auto')) + + self.scene = widgets.HBox([self.block0, self.block1, self.block2]) + self.scene.layout.align_items = 'center' + + if len(self.waypoints) > 0: + self.mybox.value = " ".join(self.waypoints.flatten().astype(str)) + + display.clear_output(wait=True) + display.display(self.scene) + diff --git a/build/lib/molearn/analysis/__init__.py b/build/lib/molearn/analysis/__init__.py new file mode 100644 index 0000000..7d29ca1 --- /dev/null +++ b/build/lib/molearn/analysis/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +__author__ = "V. K. Ramaswamy, S. C. Musson, C. G. Willcocks, M. T. Degiacomi" +__version__ = '1.1' +__date__ = '$Date: 2022-10-26 $' + +from .analyser import MolearnAnalysis, as_numpy +from .GUI import MolearnGUI +from .path import oversample, get_path, get_path_aggregate diff --git a/build/lib/molearn/analysis/analyser.py b/build/lib/molearn/analysis/analyser.py new file mode 100644 index 0000000..e67f2a7 --- /dev/null +++ b/build/lib/molearn/analysis/analyser.py @@ -0,0 +1,524 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +# +# Author: Matteo Degiacomi + +from copy import deepcopy +import numpy as np +import torch.optim + +import modeller +from modeller import * +from modeller.scripts import complete_pdb + +from ..scoring import Parallel_DOPE_Score, Parallel_Ramachandran_Score +from ..data import PDBData + +from ..utils import as_numpy + +import warnings +warnings.filterwarnings("ignore") + + +class MolearnAnalysis(object): + + def __init__(self): + pass + + def set_train_data(self, data, atomselect="*"): + if isinstance(data, str) and data.endswith('.pdb'): + d = PDBData() + d.import_pdb(data) + d.atomselect(atomselect) + d.prepare_dataset() + self._training_set = d.dataset.float() + self.meanval = d.mean + self.stdval = d.std + self.atoms = d.atoms + self.mol = d.frame() + elif isinstance(data, PDBData): + self._training_set = data.dataset.float() + self.meanval = data.mean + self.stdval = data.std + self.atoms = data.atoms + self.mol = data.frame() + else: + raise NotImplementedError('No other data typethan PDBData has been implemented for this method') + + def set_network(self, network): + self.network = network + self.network.eval() + self.device = next(network.parameters()).device + + + def set_test_data(self, data, use_training_parameters=False): + if isinstance(data, str) and data.endswith('.pdb'): + d = PDBData() + d.import_pdb(data) + d.atomselect(self.atoms) + if use_training_parameters: + d.std = self.stdval + d.mean = self.meanval + d.prepare_dataset() + self._test_set = d.dataset.float() + elif isinstance(data, PDBData): + self._test_set = data.dataset.float() + if self._test_set.shape[2] != self.training_set.shape[2]: + raise Exception(f'number of d.o.f. differs: training set has {self.training_set.shape[2]}, test set has {self._test_set.shape[2]}') + + def num_trainable_params(self): + + return sum(p.numel() for p in self.network.parameters() if p.requires_grad) + + @property + def training_set(self): + return self._training_set.clone() + + + @property + def training_set_z(self): + if not hasattr(self, '_training_set_z'): + with torch.no_grad(): + self._training_set_z = self.network.encode(self.training_set.to(self.device)) + return self._training_set_z.clone() + + @property + def test_set(self): + return self._test_set.data + @property + def training_set_decoded(self): + if not hasattr(self, '_training_set_decoded'): + with torch.no_grad(): + self._training_set_decoded = self.network.decode(self.training_set_z.to(self.device))[:,:,:self.training_set.shape[2]] + return self._training_set_decoded.clone() + + @property + def test_set_z(self): + if not hasattr(self, '_test_set_z'): + with torch.no_grad(): + self._test_set_z = self.network.encode(self.test_set.to(self.device)) + return self._test_set_z.clone() + + @property + def test_set_decoded(self): + if not hasattr(self, '_test_set_decoded'): + with torch.no_grad(): + self._test_set_decoded = self.network.decode(self.test_set_z.to(self.device))[:,:,:self.test_set.shape[2]] + return self._test_set_decoded.clone() + + + def get_error(self, dataset="", z=None, decoded=None, align=False): + ''' + Calculate the reconstruction error of a dataset encoded and decoded by a trained neural network + ''' + + if dataset == "" or dataset =="training_set": + dataset = self.training_set + z = self.training_set_z + decoded = self.training_set_decoded + elif dataset == "test_set": + dataset = self.test_set + z = self.test_set_z + decoded = self.test_set_decoded + elif z is not None: + if decoded is None: + with torch.no_grad(): + decoded = self.network.decode(z)[:,:,:dataset.shape[2]] + else: + with torch.no_grad(): + z = self.network.encode(dataset.float()) + decoded = self.network.decode(z)[:,:,:dataset.shape[2]] + + err = [] + for i in range(dataset.shape[0]): + crd_ref = as_numpy(dataset[i].permute(1,0).unsqueeze(0))*self.stdval + self.meanval + crd_mdl = as_numpy(decoded[i].permute(1,0).unsqueeze(0))[:, :dataset.shape[2]]*self.stdval + self.meanval #clip the padding of models + + if align: # use Molecule Biobox class to calculate RMSD + self.mol.coordinates = deepcopy(crd_ref) + self.mol.set_current(0) + self.mol.add_xyz(crd_mdl[0]) + rmsd = self.mol.rmsd(0, 1) + else: + rmsd = np.sqrt(np.sum((crd_ref.flatten()-crd_mdl.flatten())**2)/crd_mdl.shape[1]) # Cartesian L2 norm + + err.append(rmsd) + + return np.array(err) + + + def get_dope(self, dataset="", z=None, decoded = None, refined=True): + + if dataset == "" or dataset == "training_set": + dataset = self.training_set + z = self.training_set_z + decoded = self.training_set_decoded + elif dataset == "test_set": + dataset = self.test_set + z = self.test_set_z + decoded = self.test_set_decoded + elif z is not None: + if decoded is None: + with torch.no_grad(): + decoded = self.network.decode(z)[:,:,:dataset.shape[2]] + else: + with torch.no_grad(): + z = self.network.encode(dataset.float()) + decoded = self.network.decode(z)[:,:,:dataset.shape[2]] + + + dope_dataset = self.get_all_dope_score(dataset) + dope_decoded = self.get_all_dope_score(decoded) + + if refined: + return dope_dataset, dope_decoded + else: + return (dope_dataset,), (dope_decoded,) + + def get_ramachandran(self, dataset="", z = None, decoded = None): + if dataset == "" or dataset == "training_set": + dataset = self.training_set + z = self.training_set_z + decoded = self.training_set_decoded + elif dataset == "test_set": + dataset = self.test_set + z = self.test_set_z + decoded = self.test_set_decoded + elif z is not None: + if decoded is None: + with torch.no_grad(): + decoded = self.network.decode(z)[:,:,:dataset.shape[2]] + else: + with torch.no_grad(): + z = self.network.encode(dataset.float()) + decoded = self.network.decode(z)[:, :, :dataset.shape[2]] + + ramachandran_dataset = self.get_all_ramachandran_score(dataset) + ramachandran_decoded = self.get_all_ramachandran_score(decoded) + return ramachandran_dataset, ramachandran_decoded + + + def _get_sampling_ranges(self, samples): + + bx = (np.max(as_numpy(self.training_set_z)[:, 0]) - np.min(as_numpy(self.training_set_z)[:, 0]))*0.1 # 10% margins on x-axis + by = (np.max(as_numpy(self.training_set_z)[:, 1]) - np.min(as_numpy(self.training_set_z)[:, 1]))*0.1 # 10% margins on y-axis + xvals = np.linspace(np.min(as_numpy(self.training_set_z)[:, 0])-bx, np.max(as_numpy(self.training_set_z)[:, 0])+bx, samples) + yvals = np.linspace(np.min(as_numpy(self.training_set_z)[:, 1])-by, np.max(as_numpy(self.training_set_z)[:, 1])+by, samples) + + return xvals, yvals + + + def scan_error_from_target(self, target, samples=50): + ''' + experimental function, creating a coloured landscape of RMSD vs single target structure + target should be a Tensor of a single protein stucture loaded via load_test + ''' + + target = target.numpy().flatten()*self.stdval + self.meanval + + self.xvals, self.yvals = self._get_sampling_ranges(samples) + surf_compare = np.zeros((len(self.xvals), len(self.yvals))) + + with torch.no_grad(): + + for x, i in enumerate(self.xvals): + for y, j in enumerate(self.yvals): + + # take latent space coordinate (1) and decode it (2) + z = torch.tensor([[[i,j]]]).float() + s = self.network.decode(z)[:,:,:self.training_set.shape[2]]*self.stdval + self.meanval + + surf_compare[x,y] = np.sum((s.numpy().flatten()-target)**2) + + self.surf_target = np.sqrt(surf_compare/len(target)) + + return self.surf_target, self.xvals, self.yvals + + + def scan_error(self, samples = 50): + ''' + grid sample the latent space on a samples x samples grid (50 x 50 by default). + Boundaries are defined by training set projections extrema, plus/minus 10% + ''' + + if hasattr(self, "surf_z"): + if samples == len(self.surf_z): + return self.surf_z, self.surf_c, self.xvals, self.yvals + + self.xvals, self.yvals = self._get_sampling_ranges(samples) + surf_z = np.zeros((len(self.xvals), len(self.yvals))) # L2 norms in latent space ("drift") + surf_c = np.zeros((len(self.xvals), len(self.yvals))) # L2 norms in Cartesian space + + with torch.no_grad(): + + for x, i in enumerate(self.xvals): + for y, j in enumerate(self.yvals): + + # take latent space coordinate (1) and decode it (2) + z1 = torch.tensor([[[i,j]]]).float() + s1 = self.network.decode(z1)[:,:,:self.training_set.shape[2]] + + # take the decoded structure, re-encode it (3) and then decode it (4) + z2 = self.network.encode(s1) + s2 = self.network.decode(z2)[:,:,:self.training_set.shape[2]] + + surf_z[x,y] = np.sum((z2.numpy().flatten()-z1.numpy().flatten())**2) # Latent space L2, i.e. (1) vs (3) + surf_c[x,y] = np.sum((s2.numpy().flatten()-s1.numpy().flatten())**2) # Cartesian L2, i.e. (2) vs (4) + + self.surf_c = np.sqrt(surf_c) + self.surf_z = np.sqrt(surf_z) + + return self.surf_z, self.surf_c, self.xvals, self.yvals + + + def _ramachandran_score(self, frame, processes=-1): + ''' + returns multiprocessing AsyncResult + AsyncResult.get() will return the result + ''' + if not hasattr(self, 'ramachandran_score_class'): + self.ramachandran_score_class = Parallel_Ramachandran_Score(self.mol, processes=processes) #Parallel_Ramachandran_Score(self.mol) + assert len(frame.shape) == 2, f'We wanted 2D data but got {len(frame.shape)} dimensions' + if frame.shape[0] == 3: + f = frame.permute(1,0) + else: + assert frame.shape[1] == 3 + f = frame + if isinstance(f, torch.Tensor): + f = f.data.cpu().numpy() + + return self.ramachandran_score_class.get_score(f*self.stdval) + + + def _dope_score(self, frame, refine = True, processes=-1): + ''' + returns multiprocessing AsyncResult + AsyncResult.get() will return the result + ''' + if not hasattr(self, 'dope_score_class'): + self.dope_score_class = Parallel_DOPE_Score(self.mol, processes=processes) + + assert len(frame.shape) == 2, f'We wanted 2D data but got {len(frame.shape)} dimensions' + if frame.shape[0] == 3: + f = frame.permute(1,0) + else: + assert frame.shape[1] ==3 + f = frame + if isinstance(f,torch.Tensor): + f = f.data.cpu().numpy() + + return self.dope_score_class.get_score(f*self.stdval, refine=refine) + + + def get_all_ramachandran_score(self, tensor): + ''' + applies _ramachandran_score to an array of data + ''' + results = [] + for f in tensor: + results.append(self._ramachandran_score(f)) + results = np.array([r.get() for r in results]) + return results + + def get_all_dope_score(self, tensor, refine = True): + ''' + applies _dope_score to an array of data + ''' + results = [] + for f in tensor: + results.append(self._dope_score(f, refine = refine)) + results = np.array([r.get() for r in results]) + if refine: + return results[:,0], results[:,1] + return results + + def reference_dope_score(self, frame): + ''' + give a numpy array with shape [1, N, 3], already scaled to the correct size + ''' + self.mol.coordinates = deepcopy(frame) + self.mol.write_pdb('tmp.pdb') + env = Environ() + env.libs.topology.read(file='$(LIB)/top_heav.lib') + env.libs.parameters.read(file='$(LIB)/par.lib') + mdl = complete_pdb(env, 'tmp.pdb') + atmsel = Selection(mdl.chains[0]) + score = atmsel.assess_dope() + return score + + + def scan_dope(self, samples = 50, refine = True, processes = -1): + + if hasattr(self, "surf_dope_refined") and hasattr(self, "surf_dope_unrefined"): + if samples == len(self.surf_dope_refined) and samples == len(self.surf_dope_unrefined): + return self.surf_dope_unrefined, self.surf_dope_refined, self.xvals, self.yvals + + self.xvals, self.yvals = self._get_sampling_ranges(samples) + + X, Y = torch.meshgrid(torch.tensor(self.xvals), torch.tensor(self.yvals)) + z_in = torch.stack((X,Y), dim=2).view(samples*samples, 1, 2, 1).float() + + #surf_dope = np.zeros((len(self.xvals)*len(self.yvals),)) + results = [] + with torch.no_grad(): + for i, j in enumerate(z_in): + structure = self.network.decode(j)[:,:,:self.training_set.shape[2]] + results.append(self._dope_score(structure[0], refine=refine, processes=processes)) + + results = np.array([r.get() for r in results]) + + if refine: + self.surf_dope_unrefined = results[:,0].reshape(len(self.xvals), len(self.yvals)) + self.surf_dope_refined = results[:, 1].reshape(len(self.xvals), len(self.yvals)) + return self.surf_dope_unrefined, self.surf_dope_refined, self.xvals, self.yvals + else: + self.surf_dope_unrefined = results.reshape(len(self.xvals), len(self.yvals)) + return self.surf_dope_unrefined, self.xvals, self.yvals + + + def scan_all(self, samples = 50, processes = -1, rmsd_from = None): + self.xvals, self.yvals = self._get_sampling_ranges(samples) + X, Y = torch.meshgrid(torch.tensor(self.xvals), torch.tensor(self.yvals)) + z_in = torch.stack((X,Y), dim=2).view(samples*samples, 1, 2, 1).float() + z_size = len(z_in) + dope = [] + rama = [] + error_z = [] + error_c = [] + rmsd_structures = {} + rmsd = {} + if rmsd_from is not None: + if not isinstance(rmsd_from, dict): + rmsd_from = dict(name_not_set = rmsd_from) + for key, value in rmsd_from.items(): + rmsd_structures[key] = value.float().view(1,3,-1).to(self.device) + rmsd[key] = [] + + + with torch.no_grad(): + for i,j in enumerate(z_in): + if i%100==0: + print(f'on same {i} out of {z_size}, {100*i/z_size}%') + + structure = self.network.decode(j.to(self.device))[:,:,:self.training_set.shape[2]] + dope.append(self._dope_score(structure[0], refine=True, processes=processes)) + rama.append(self._ramachandran_score(structure[0], processes=processes)) + + for key, value in rmsd_structures.items(): + rmsd_value = (((structure-value)*self.stdval)**2).sum(dim=1).mean().cpu().item() + rmsd[key].append(rmsd_value) + + z2 = self.network.encode(structure) + structure2 = self.network.decode(z2)[:,:,:self.training_set.shape[2]] + error_z.append(np.sum((z2.cpu().numpy().flatten()-j.numpy().flatten())**2)) # Latent space L2, i.e. (1) vs (3) + error_c.append(np.sum((structure2.cpu().numpy().flatten()-structure.cpu().numpy().flatten())**2)) # Cartesian L2, i.e. (2) vs (4) + + + print('finish dope') + dope = np.array([r.get() for r in dope]) + print('finish rama') + rama = np.array([r.get() for r in rama]) + error_z = np.sqrt(np.array(error_z)) + error_c = np.sqrt(np.array(error_c)) + + self.surf_c = error_c.reshape(samples, samples) + self.surf_z = error_z.reshape(samples, samples) + self.surf_dope_unrefined = dope[:,0].reshape(samples, samples) + self.surf_dope_refined = dope[:,1].reshape(samples, samples) + self.surf_ramachandran_favored = rama[:,0].reshape(samples, samples) + self.surf_ramachandran_allowed = rama[:,1].reshape(samples, samples) + self.surf_ramachandran_outliers = rama[:,2].reshape(samples, samples) + self.surf_ramachandran_total = rama[:,3].reshape(samples, samples) + + rmsd_surfs = {} + for key, value in rmsd.items(): + surf_rmsd = np.array(value).reshape(samples, samples) + setattr(self, f'surf_rmsd_from_{key}', surf_rmsd) + rmsd_surfs[f'landscape_rmsd_from_{key}'] = surf_rmsd + + return dict( + landscape_err_latent=self.surf_z, + landscape_err_3d=self.surf_c, + landscape_dope_unrefined = self.surf_dope_unrefined, + landscape_dope_refined = self.surf_dope_refined, + landscape_ramachandran_favored = self.surf_ramachandran_favored, + landscape_ramachandran_allowed = self.surf_ramachandran_allowed, + landscape_ramachandran_outlier = self.surf_ramachandran_outliers, + landscape_ramachandran_total = self.surf_ramachandran_total, + xvals = self.xvals, + yvals = self.yvals, + **rmsd_surfs, + ) + + + def scan_ramachandran(self, samples = 50, processes = -1): + if hasattr(self, "surf_ramachandran"): + if samples == len(self.surf_ramachandran): + return self.surf_ramachandran_favored, self.surf_ramachandran_allowed, self.surf_ramachandran_outliers + self.xvals, self.yvals = self._get_sampling_ranges(samples) + X, Y = torch.meshgrid(torch.tensor(self.xvals), torch.tensor(self.yvals)) + z_in = torch.stack((X,Y), dim=2).view(samples*samples,1,2,1).float() + + results = [] + with torch.no_grad(): + for i,j in enumerate(z_in): + structure = self.network.decode(j)[:,:,:self.training_set.shape[2]] + results.append(self._ramachandran_score(structure[0], processes=processes)) + results = np.array([r.get() for r in results]) + self.surf_ramachandran_favored = results[:,0].reshape(len(self.xvals), len(self.yvals)) + self.surf_ramachandran_allowed = results[:,1].reshape(len(self.xvals), len(self.yvals)) + self.surf_ramachandran_outliers = results[:,2].reshape(len(self.xvals), len(self.yvals)) + self.surf_ramachandran_total = results[:,3].reshape(len(self.xvals), len(self.yvals)) + + return self.surf_ramachandran_favored, self.xvals, self.yvals + + + def scan_custom(self, fct, params, label, samples = 50): + ''' + param f: function taking atomic coordinates as input, an optional list of parameters. Returns a single value. + param params: parameters to be passed to function f + param label: name of the dataset generated by this function scan + param samples: sampling of grid sampling + returns: grid scanning of latent space according to provided function, x, and y grid axes + ''' + + if hasattr(self, "custom_data"): + if label in list(self.custom_data) and samples == len(self.custom_data[label]): + return self.custom_data[label], self.xvals, self.yvals + else: + self.custom_data = {} + + self.xvals, self.yvals = self._get_sampling_ranges(samples) + X, Y = torch.meshgrid(torch.tensor(self.xvals), torch.tensor(self.yvals)) + z_in = torch.stack((X,Y), dim=2).view(samples*samples,1,2,1).float() + + results = [] + with torch.no_grad(): + for i, j in enumerate(z_in): + + structure = self.network.decode(j)[:,:,:self.training_set.shape[2]].numpy().transpose(0, 2, 1) + results.append(fct(structure*self.stdval + self.meanval, *params)) + + results = np.array(results) + self.custom_data[label] = results.reshape(len(self.xvals), len(self.yvals)) + + return self.custom_data[label], self.xvals, self.yvals + + + def generate(self, crd): + ''' + generate a collection of protein conformations, given (Nx2) coordinates in the latent space + ''' + with torch.no_grad(): + z = torch.tensor(crd.transpose(1, 2, 0)).float() + s = self.network.decode(z)[:, :, :self.training_set.shape[2]].numpy().transpose(0, 2, 1) + + return s*self.stdval + self.meanval diff --git a/build/lib/molearn/analysis/path.py b/build/lib/molearn/analysis/path.py new file mode 100644 index 0000000..72856f9 --- /dev/null +++ b/build/lib/molearn/analysis/path.py @@ -0,0 +1,253 @@ +import heapq +import numpy as np + +class PriorityQueue(object): + ''' + Queue for shortest path algorithms. + ''' + + def __init__(self): + self.elements = [] + + def empty(self): + ''' + clear priority queue + ''' + return len(self.elements) == 0 + + def put(self, item, priority): + ''' + add element in priority queue + :param item: item to add in queue + :param priority: item's priority + ''' + heapq.heappush(self.elements, (priority, item)) + + def get(self): + ''' + returns pop top priority element from queue + ''' + return heapq.heappop(self.elements)[1] + + +def _heuristic(pt1, pt2, graph, euclidean=True): + ''' + : returns penalty associated with the distance between points. + ''' + + if not euclidean: + pts = oversample(np.array([pt1, pt2]), 1000).round().astype(int) + pts2 = np.vstack({tuple(e) for e in pts}) + h = 0 + for p in pts2: + h += graph[p[0], p[1]] + + else: + h = np.sum(np.dot(pt2-pt1, pt2-pt1)) + + return h + + +def _neighbors(idx, gridshape, flattened=True): + ''' + return coordinates of gridpoints adjacent to a given point in a grid + : param idx : index of point in a grid. Can be either a flattened index or a 2D coordinate. + : param gridshape : tuple defining grid shape + : flattened : if False, return 2D coordinates, flattened index otherwise (default) + ''' + + try: + if type(idx) != int: + idx = np.unravel_index(idx, gridshape) + elif len(idx) != 2: + raise Exception("Expecting 2D coordinates") + except: + raise Exception("idx should be either integer or an iterable") + + # generate neighbour list + n = [] + for x in range(idx[0]-1, idx[0]+2, 1): + for y in range(idx[1]-1, idx[1]+2, 1): + + if x==idx[0] and y==idx[1]: + continue + + # apply boundary conditions + if x>=gridshape[0] or y>=gridshape[1]: + continue + + if x<0 or y<0: + continue + + if flattened: + n.append(np.ravel_multi_index(np.array([x, y]), gridshape)) + else: + n.append([x, y]) + + return np.array(n) + + +def _cost(pt, graph): + ''' + evaluate cost of moving onto a grid cell + ''' + # separate function for clarity, and in case in the future we want to alter this + return graph[pt] + + +def _astar(start_2d, goal_2d, in_graph, euclidean=True): + ''' + A* algorithm, find path connecting two points in a landscape. + :param start : starting point + :param goal : end point + :param in_graph : 2D landscape + :returns connectivity dictionary, total path cost (same type as graph) + ''' + + graph = in_graph.copy() + graph -= np.min(graph) + graphshape = graph.shape + + start = np.ravel_multi_index(start_2d, graphshape) + goal = np.ravel_multi_index(goal_2d, graphshape) + + frontier = PriorityQueue() + frontier.put(start, 0) + came_from = {} + cost_so_far = {} + came_from[start] = start + cost_so_far[start] = 0 + + while not frontier.empty(): + current = frontier.get() + + if current == goal: + break + + for thenext in _neighbors(current, graphshape, True): + + thenext_2d = np.unravel_index(thenext, graphshape) + new_cost = cost_so_far[current] + _cost(thenext_2d, graph) + + if (thenext not in cost_so_far) or (new_cost < cost_so_far[thenext]): + cost_so_far[thenext] = new_cost + + h = _heuristic(goal_2d, thenext_2d, graph, euclidean) + priority = new_cost + h + frontier.put(thenext, priority) + came_from[thenext] = current + + return came_from, cost_so_far + + +def get_path(idx_start, idx_end, landscape, xvals, yvals, smooth=3): + ''' + Find shortest path between two points on a weighted grid + : param idx_start : index on a 2D grid, as start point for a path + : param idx_end : index on a 2D grid, as end point for a path + : param landscape : 2D grid + : param xvals : x-axis values, to yield actual coordinates + : param yvals : y-axis values, to yield actual coordinates + : param smooth : size of kernel for running average (must be >=1, default 3) + : returns array of 2D coordinates each with an associated value on lanscape + ''' + + if type(smooth) != int or smooth<1: + raise Exception("Smooth parameter should be an integer number >=1") + + # get raw A* data + mypath, mycost = _astar(idx_start, idx_end, landscape) + + # extract path and cost + cnt = 0 + coords = [] + score = [] + idx_flat = np.ravel_multi_index(idx_end, landscape.shape) + while cnt<1000: #safeguad for (unlikely) unfinished paths + + if idx_flat == mypath[idx_flat]: + break + + idx_flat = mypath[idx_flat] + crd = np.unravel_index(idx_flat, landscape.shape) + coords.append([xvals[crd[0]], yvals[crd[1]]]) + score.append(landscape[crd[0], crd[1]]) + + cnt += 1 + + if smooth == 1: + return np.array(coords)[::-1], np.array(score)[::-1] + + else: + + traj = np.array(coords)[::-1] + x_ave = np.convolve(traj[:, 0], np.ones(smooth), 'valid') / smooth + y_ave = np.convolve(traj[:, 1], np.ones(smooth), 'valid') / smooth + traj_smooth = np.array([x_ave, y_ave]).T + + traj_smooth = np.concatenate((np.array([traj[0]]), traj_smooth, np.array([traj[-1]]))) + return traj_smooth, np.array(score)[::-1] + + + +def _get_point_index(crd, xvals, yvals): + ''' + Extract index (of 2D surface) closest to a given real value coordinate + : param crd : coordinate + : param xvals : x-axis of surface + : param yvals : y-axis of surface + : returns 1D array with x,y coordinates + ''' + + my_x = np.argmin(np.abs(xvals - crd[0])) + my_y = np.argmin(np.abs(yvals - crd[1])) + return np.array([my_x, my_y]) + + +def get_path_aggregate(crd, landscape, xvals, yvals, input_is_index=False): + ''' + Create a chain of shortest paths via give waypoints + : param crd : waypoints coordinates (Nx2 array) + : param landscape : 2D grid + : param xvals : x-axis values, to yield actual coordinates + : param yvals : y-axis values, to yield actual coordinates + : param input_is_index: if False (default), assume crd contains actual coordinates, graph indexing otherwise + : returns array of 2D coordinates each with an associated value on lanscape + ''' + + if len(crd)<2: + return crd + + crd2 = [] + for i in range(1, len(crd)): + + if not input_is_index: + idx_start = _get_point_index(crd[i-1], xvals, yvals) + idx_end = _get_point_index(crd[i], xvals, yvals) + else: + idx_start = crd[i-1] + idx_end = crd[i] + + crdtmp = get_path(idx_start, idx_end, landscape, xvals, yvals)[0] + crd2.extend(list(crdtmp)) + + crd = np.array(crd2) + return crd + + +def oversample(crd, pts=10): + ''' + Add extra equally spaced points between a list of points + : param crd : Nx2 numpy array + : param pts : int number of extra points to add in each interval + : returns Mx2 numpy array, with M>=N. + ''' + pts += 1 + steps = np.linspace(1./pts, 1, pts) + pts = [crd[0]] + for i in range(1, crd.shape[0]): + for j in steps: + newpt = crd[i-1] + (crd[i]-crd[i-1])*j + pts.append(newpt) + + return np.array(pts) \ No newline at end of file diff --git a/build/lib/molearn/data/__init__.py b/build/lib/molearn/data/__init__.py new file mode 100644 index 0000000..9845e5c --- /dev/null +++ b/build/lib/molearn/data/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2022 Samuel C. Musson +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# Molightning is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molightning ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +""" +loaders holds classes for loading data +""" +from .pdb_data import PDBData + diff --git a/build/lib/molearn/data/pdb_data.py b/build/lib/molearn/data/pdb_data.py new file mode 100644 index 0000000..ef2e2d7 --- /dev/null +++ b/build/lib/molearn/data/pdb_data.py @@ -0,0 +1,145 @@ +import numpy as np +import torch +from copy import deepcopy +import biobox as bb +#import os + +class PDBData: + def __init__(self): + pass + + def import_pdb(self, filename): + if not hasattr(self, '_mol'): + self._mol = bb.Molecule() + self._mol.import_pdb(filename) + if not hasattr(self, 'filename'): + self.filename = [] + self.filename.append(filename) + + def fix_terminal(self): + ot1 = np.where(self._mol.data['name']=='OT1')[0] + ot2 = np.where(self._mol.data['name']=='OT2')[0] + oxt = np.where(self._mol.data['name']=='OXT')[0] + resids = [] + if len(ot1)!=0 and len(ot2)!=0: + self._mol.data.loc[ot1,'name']='O' + if len(ot1)!=0: + for i in ot1: + resids.append((self._mol.data['resid'][i], self._mol.data['resname'][i])) + if len(oxt)!=0: + for i in oxt: + resids.append((self._mol.data['resid'][i], self._mol.data['resname'][i])) + + #for resid, resname in resids: + #resname = self._mol.data['resname'][resid] + #if len(resname)==3: + # self._mol.data.loc[self._mol.data.resid.eq(resid), 'resname']=f'C{resname}' + def atomselect(self, atoms, ignore_atoms=[]): + if atoms == "*": + _atoms = list(np.unique(self._mol.data["name"].values)) + for to_remove in ignore_atoms: + if to_remove in _atoms: + _atoms.remove(to_remove) + elif atoms == "no_hydrogen": + _atoms = self.atoms #list(np.unique(self._mol.data["name"].values)) #all the atoms + _plain_atoms = [] + for a in _atoms: + if a in self._mol.knowledge['atomtype']: + _plain_atoms.append(self._mol.knowledge['atomtype'][a]) + elif a[:-1] in self._mol.knowledge['atomtype']: + _plain_atoms.append(self._mol.knowledge['atomtype'][a[:-1]]) + else: + _plain_atoms.append(self._mol.knowledge['atomtype'][a]) # if above failed just raise the keyerror + _atoms = [atom for atom, element in zip(_atoms, _plain_atoms) if element != 'H'] + else: + _atoms = [_a for _a in atoms if _a not in ignore_atoms] + + _, self._idxs = self._mol.atomselect("*", "*", _atoms, get_index=True) + self._mol = self._mol.get_subset(self._idxs) + + def prepare_dataset(self, ): + if not hasattr(self, 'dataset'): + assert hasattr(self, '_mol'), 'You need to call import_pdb before preparing the dataset' + self.dataset = self._mol.coordinates.copy() + + if not hasattr(self, 'std'): + self.std = self.dataset.std() + if not hasattr(self, 'mean'): + self.mean = self.dataset.mean() + self.dataset -= self.mean + self.dataset /= self.std + self.dataset = torch.from_numpy(self.dataset).float() + self.dataset = self.dataset.permute(0,2,1) + print(f'Dataset.shape: {self.dataset.shape}') + print(f'mean: {str(self.mean)}, std: {str(self.std)}') + + def get_atominfo(self): + if not hasattr(self, 'atominfo'): + assert hasattr(self, '_mol'), 'You need to call import_pdb before getting atom info' + self.atominfo = self._mol.get_data(columns=['name', 'resname', 'resid']) + return self.atominfo + + def frame(self): + M = bb.Molecule() + M.coordinates = self._mol.coordinates[[0]] + M.data = self._mol.data + M.data['index'] = np.arange(self._mol.coordinates.shape[1]) + M.current = 0 + M.points = M.coordinates.view()[M.current] + M.properties['center'] = M.get_center() + return deepcopy(M) + + def get_dataloader(self, batch_size, validation_split=0.1, pin_memory=True, dataset_sample_size=-1, manual_seed=None,shuffle=True, sampler=None): + if not hasattr(self, 'dataset'): + self.prepare_dataset() + valid_size = int(len(self.dataset)*validation_split) + train_size = len(self.dataset) - valid_size + dataset = torch.utils.data.TensorDataset(self.dataset.float()) + if manual_seed is not None: + self.train_dataset, self.valid_dataset = torch.utils.data.random_split(dataset, [train_size, valid_size], generator=torch.Generator().manual_seed(manual_seed)) + self.train_dataloader = torch.utils.data.DataLoader(self.train_dataset, batch_size=batch_size, pin_memory=pin_memory, + sampler=torch.utils.data.RandomSampler(self.train_dataset,generator=torch.Generator().manual_seed(manual_seed))) + else: + self.train_dataset, self.valid_dataset = torch.utils.data.random_split(dataset, [train_size, valid_size]) + self.train_dataloader = torch.utils.data.DataLoader(self.train_dataset, batch_size=batch_size, pin_memory=pin_memory, shuffle=True) + self.valid_dataloader = torch.utils.data.DataLoader(self.valid_dataset, batch_size=batch_size, pin_memory=pin_memory,shuffle=True) + return self.train_dataloader, self.valid_dataloader + + def get_datasets(self, validation_split=0.1, valid_size=None, train_size=None, manual_seed = None): + ''' + returns torch.Tensor for training and validation structures. + ''' + if not hasattr(self, 'dataset'): + self.prepare_dataset() + dataset = self.dataset.float() + if train_size is None: + _valid_size = int(len(self.dataset)*validation_split) + _train_size = len(self.dataset) - _valid_size + else: + _train_size = train_size + if valid_size is None: + _valid_size = validation_split*_train_size + else: + _valid_size = valid_size + from torch import randperm + if manual_seed is not None: + indices = randperm(len(self.dataset), generator = torch.Generator().manual_seed(manual_seed)) + else: + indices = randperm(len(self.dataset)) + train_dataset = dataset[indices[:_train_size]] + valid_dataset = dataset[indices[_train_size:_train_size+_valid_size]] + return train_dataset, valid_dataset + + + + @property + def atoms(self): + return list(np.unique(self._mol.data["name"].values)) #all the atoms + + @property + def mol(self): + return self.frame() + + + + diff --git a/build/lib/molearn/loss_functions/__init__.py b/build/lib/molearn/loss_functions/__init__.py new file mode 100644 index 0000000..c169ea6 --- /dev/null +++ b/build/lib/molearn/loss_functions/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2022 Samuel C. Musson +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# Molightning is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molightning ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +""" +loss_functions holds the classes for calculating OpenMM energy, and backup methods for calculating energy with just pytorch +""" +from .openmm_thread import openmm_energy, OpenmmPluginScore, OpenMMPluginScoreSoftForceField +from .torch_protein_energy import TorchProteinEnergy diff --git a/build/lib/molearn/loss_functions/openmm_thread.py b/build/lib/molearn/loss_functions/openmm_thread.py new file mode 100644 index 0000000..c545754 --- /dev/null +++ b/build/lib/molearn/loss_functions/openmm_thread.py @@ -0,0 +1,382 @@ +import os +#from openmm.unit import kelvin, picosecond +try: + from openmm import Platform + from openmm.app import ForceField, PDBFile, Simulation #, OBC2 + from openmm.app import element as elem + import openmm + from openmm.app.forcefield import _createResidueSignature + from openmm.app.internal import compiled + from torchexposedintegratorplugin import TorchExposedIntegrator +except ImportError as e: + import warnings + warnings.warn(f'{e}. Will not be able to use openmm.') + +import torch +import numpy as np +#from math import ceil + + +class ModifiedForceField(ForceField): + def __init__(self, *args, alternative_residue_names = None, **kwargs): + super().__init__(*args, **kwargs) + if isinstance(alternative_residue_names, dict): + self._alternative_residue_names = alternative_residue_names + else: + self._alternative_residue_names = {'HIS':'HIE'} + + def _getResidueTemplateMatches(self, res, bondedToAtom, templateSignatures=None, ignoreExternalBonds=False, ignoreExtraParticles=False): + """Return the templates that match a residue, or None if none are found. + + Parameters + ---------- + res : Topology.Residue + The residue for which template matches are to be retrieved. + bondedToAtom : list of set of int + bondedToAtom[i] is the set of atoms bonded to atom index i + + Returns + ------- + template : _TemplateData + The matching forcefield residue template, or None if no matches are found. + matches : list + a list specifying which atom of the template each atom of the residue + corresponds to, or None if it does not match the template + + """ + template = None + matches = None + for matcher in self._templateMatchers: + template = matcher(self, res, bondedToAtom, ignoreExternalBonds, ignoreExtraParticles) + if template is not None: + match = compiled.matchResidueToTemplate(res, template, bondedToAtom, ignoreExternalBonds, ignoreExtraParticles) + if match is None: + raise ValueError('A custom template matcher returned a template for residue %d (%s), but it does not match the residue.' % (res.index, res.name)) + return [template, match] + if templateSignatures is None: + templateSignatures = self._templateSignatures + signature = _createResidueSignature([atom.element for atom in res.atoms()]) + if signature in templateSignatures: + allMatches = [] + for t in templateSignatures[signature]: + match = compiled.matchResidueToTemplate(res, t, bondedToAtom, ignoreExternalBonds, ignoreExtraParticles) + if match is not None: + allMatches.append((t, match)) + if len(allMatches) == 1: + template = allMatches[0][0] + matches = allMatches[0][1] + elif len(allMatches) > 1: + for i, (t, m) in enumerate(allMatches): + name = self._alternative_residue_names.get(res.name, res.name) + if name==t.name.split('-')[0]: + template = t + matches = m + return [template, matches] + elif 'N'+name==t.name.split('-')[0]: + print(f'{str(res)}, {res.index}, is a being set as a N terminal residue') + template = t + matches = m + return [template, matches] + elif 'C'+name==t.name.split('-')[0]: + print(f'{str(res)} is a being set as a C terminal residue') + template = t + matches = m + return [template, matches] + print(f'multpile for {t.name}') + # We found multiple matches. This is OK if and only if they assign identical types and parameters to all atoms. + t1, m1 = allMatches[0] + + for t2, m2 in allMatches[1:]: + if not t1.areParametersIdentical(t2, m1, m2): + raise Exception('Multiple non-identical matching templates found for residue %d (%s): %s.' % (res.index+1, res.name, ', '.join(match[0].name for match in allMatches))) + template = allMatches[0][0] + matches = allMatches[0][1] + return [template, matches] + +class OpenmmPluginScore(): + ''' + This will use the new Openmm Plugin to calculate forces and energy. The intention is that this will be fast enough to be able to calculate forces and energy during training. + NB. The current torchintegratorplugin only supports float on GPU and double on CPU. + ''' + + def __init__(self, mol=None, xml_file = ['amber14-all.xml'], platform = 'CUDA', remove_NB=False, alternative_residue_names = dict(HIS='HIE', HSE='HIE'), atoms=['CA', 'C', 'N', + 'CB', + 'O'], + soft=False): + ''' + :param mol: (biobox.Molecule, default None) If pldataloader is not given, then a biobox object will be taken from this parameter. If neither are given then an error will be thrown. + :param data_dir: (string, default None) if pldataloader is not given then this will be used to find files such as 'variants.npy' + :param xml_file: (string, default: "amber14-all.xml") xml parameter file + :param platform: (string, default 'CUDA') either 'CUDA' or 'Reference'. + :param remove_NB: (bool, default False) remove NonbondedForce, CustomGBForce, CMMotionRemover else just remove CustomGBForce + ''' + self.mol = mol + for key, value in alternative_residue_names.items(): + #self.mol.data.loc[:,'resname'][self.mol.data['resname']==key]=value + self.mol.data.loc[self.mol.data['resname']==key,'resname']=value + #self.mol.data.loc[lambda df: df['resname']==key, key]=value + tmp_file = f'tmp{np.random.randint(1e10)}.pdb' + self.atoms = atoms + self.mol.write_pdb(tmp_file) + self.pdb = PDBFile(tmp_file) + if soft: + print('attempting soft forcefield') + from pdbfixer import PDBFixer + f = PDBFixer(tmp_file) + self.forcefield = f._createForceField(self.pdb.topology, False) + self.system = self.forcefield.createSystem(self.pdb.topology) + else: + if isinstance(xml_file,str): + self.forcefield = ModifiedForceField(xml_file, alternative_residue_names = alternative_residue_names) + elif len(xml_file)>0: + self.forcefield = ModifiedForceField(*xml_file, alternative_residue_names = alternative_residue_names) + else: + raise ValueError(f'xml_file: {xml_file} needs to be a str or a list of str') + + if atoms == 'no_hydrogen': + self.ignore_hydrogen() + else: + self.atomselect(atoms) + #save pdb and reload in modeller + templates, unique_unmatched_residues = self.forcefield.generateTemplatesForUnmatchedResidues(self.pdb.topology) + self.system = self.forcefield.createSystem(self.pdb.topology) + if remove_NB: + forces = self.system.getForces() + for idx in reversed(range(len(forces))): + force = forces[idx] + if isinstance(force, (#openmm.PeriodicTorsionForce, + openmm.CustomGBForce, + openmm.NonbondedForce, + openmm.CMMotionRemover)): + self.system.removeForce(idx) + else: + forces = self.system.getForces() + for idx in reversed(range(len(forces))): + force = forces[idx] + if isinstance(force, openmm.CustomGBForce): + self.system.removeForce(idx) + + + self.integrator = TorchExposedIntegrator() + self.platform = Platform.getPlatformByName(platform) + self.simulation = Simulation(self.pdb.topology, self.system, self.integrator, self.platform) + if platform == 'CUDA': + self.platform.setPropertyValue(self.simulation.context, 'Precision', 'single') + self.n_particles = self.simulation.context.getSystem().getNumParticles() + self.simulation.context.setPositions(self.pdb.positions) + self.get_score = self.get_energy + print(self.simulation.context.getState(getEnergy=True).getPotentialEnergy()._value) + os.remove(tmp_file) + + def ignore_hydrogen(self): + #ignore = ['ASH', 'LYN', 'GLH', 'HID', 'HIP', 'CYM', ] + ignore = [] + for name, template in self.forcefield._templates.items(): + if name in ignore: + continue + patchData = ForceField._PatchData(name+'_remove_h', 1) + + for atom in template.atoms: + if atom.element is elem.hydrogen: + if atom.name not in patchData.allAtomNames: + patchData.allAtomNames.add(atom.name) + atomDescription = ForceField._PatchAtomData(atom.name) + patchData.deletedAtoms.append(atomDescription) + else: + raise ValueError() + for bond in template.bonds: + atom1 = template.atoms[bond[0]] + atom2 = template.atoms[bond[1]] + if atom1.element is elem.hydrogen or atom2.element is elem.hydrogen: + a1 = ForceField._PatchAtomData(atom1.name) + a2 = ForceField._PatchAtomData(atom2.name) + patchData.deletedBonds.append((a1, a2)) + self.forcefield.registerTemplatePatch(name, name+'_remove_h', 0) + self.forcefield.registerPatch(patchData) + + def atomselect(self, atoms): + for name, template in self.forcefield._templates.items(): + patchData = ForceField._PatchData(name+'_leave_only_'+'_'.join(atoms), 1) + + for atom in template.atoms: + if atom.name not in atoms: + if atom.name not in patchData.allAtomNames: + patchData.allAtomNames.add(atom.name) + atomDescription = ForceField._PatchAtomData(atom.name) + patchData.deletedAtoms.append(atomDescription) + else: + raise ValueError() + + for bond in template.bonds: + atom1 = template.atoms[bond[0]] + atom2 = template.atoms[bond[1]] + if atom1.name not in atoms or atom2.name not in atoms: + a1 = ForceField._PatchAtomData(atom1.name) + a2 = ForceField._PatchAtomData(atom2.name) + patchData.deletedBonds.append((a1, a2)) + self.forcefield.registerTemplatePatch(name, name+'_leave_only_'+'_'.join(atoms), 0) + self.forcefield.registerPatch(patchData) + + + def get_energy(self, pos_ptr, force_ptr, energy_ptr, n_particles, batch_size): + ''' + :param pos_ptr: tensor.data_ptr() + :param force_ptr: tensor.data_ptr() + :param energy_ptr: tensor.data_ptr() + :param n_particles: int + :param batch_size: int + ''' + assert n_particles == self.n_particles + torch.cuda.synchronize() + self.integrator.torchMultiStructureE(pos_ptr, force_ptr, energy_ptr, n_particles, batch_size) + return True + + def execute(self, x): + ''' + :param x: torch tensor shape [b, N, 3]. dtype=float. device = gpu + ''' + force = torch.zeros_like(x) + energy = torch.zeros(x.shape[0], device = torch.device('cpu'), dtype=torch.double) + self.get_energy(x.data_ptr(), force.data_ptr(), energy.data_ptr(), x.shape[1], x.shape[0]) + return force, energy + + +class OpenmmTorchEnergyMinimizer(OpenmmPluginScore): + def minimize(self, x, maxIterations=10, threshold=10000): + minimized_x = torch.empty_like(x) + for i,s in enumerate(x.unsqueeze(1)): + h = 0.01 + force, energy = self.execute(s) + abs_max = 1/(force.abs().max()) + for j in range(maxIterations): + new_s = s - force*abs_max*h + new_force, new_energy = self.execute(new_s) + if new_energyj + bloss = (((v.norm(dim=2)-self.bond_para[:,0])**2)*self.bond_para[:,1]).sum() + v1 = v[:,self.ij_jk[0]]*self.angle_mask[0].view(1,-1,1) + v2 = v[:,self.ij_jk[1]]*self.angle_mask[1].view(1,-1,1) + xyz=torch.sum(v1*v2, dim=2) / (torch.norm(v1, dim=2) * torch.norm(v2, dim=2)) + theta = torch.acos(torch.clamp(xyz, min=-0.999999, max=0.999999)) + aloss = (((theta-self.angle_para[:,0])**2)*self.angle_para[:,1]).sum() + + u1 = v[:,self.ij_jk_kl[0]]*self.torsion_mask[0].view(1, -1, 1) + u2 = v[:,self.ij_jk_kl[1]]*self.torsion_mask[1].view(1, -1, 1) + u3 = v[:,self.ij_jk_kl[2]]*self.torsion_mask[2].view(1, -1, 1) + u12=torch.cross(u1,u2) + u23=torch.cross(u2,u3) + t3=torch.atan2(u2.norm(dim=2)*((u1*u23).sum(dim=2)),(u12*u23).sum(dim=2)) + p = self.torsion_para + tloss=((p[:,1]/p[:,0])*(1+torch.cos((p[:,3]*t3.unsqueeze(2))-p[:,2]))).sum() + bs = x.shape[0] + return bloss/bs, aloss/bs, tloss/bs + + def _cdist_nb_full(self, x, cutoff=9.0, mask=False): + dmat = torch.cdist(x.permute(0,2,1),x.permute(0,2,1)) + dmat6 = (self._warp_domain(dmat, 1.9)**6) + LJpB = self.vdw_B/dmat6 + LJpA = self.vdw_A/(dmat6**2) + Cp = (self.q1q2/self._warp_domain(dmat, 0.4)) + return torch.nansum(LJpA-LJpB+Cp) + + def _cdist_nb(self, x, cutoff=9.0, mask=False): + dmat = torch.cdist(x.permute(0,2,1),x.permute(0,2,1)) + LJp = self.vdw_A/(self._warp_domain(dmat, 1.9)**12) + Cp = (self.q1q2/self._warp_domain(dmat, 0.4)) + return torch.nansum(LJp+Cp) + + def _warp_domain(self,x,k): + return torch.nn.functional.elu(x-k, 1.0)+k + + def _conv_bond_loss(self, x): + #x shape[B, 3, N] + loss=torch.tensor(0.0).float().to(self.device) + for i, weight in enumerate(self.b_weights): + y = torch.nn.functional.conv1d(x, weight.view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight)-2)) + loss+=(self.b_force[i]*((y.norm(dim=1)-self.b_equil[i])**2)).sum() + return loss + + def _conv_angle_loss(self, x): + #x shape[X, 3, N] + loss=torch.tensor(0.0).float().to(self.device) + for i, weight in enumerate(self.a_weights): + v1 = torch.nn.functional.conv1d(x, weight[0].view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight[0])-3)) + v2 = torch.nn.functional.conv1d(x, weight[1].view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight[1])-3)) + xyz=torch.sum(v1*v2, dim=1) / (torch.norm(v1, dim=1) * torch.norm(v2, dim=1)) + theta = torch.acos(torch.clamp(xyz, min=-0.999999, max=0.999999)) + energy = (self.a_force[i]*((theta-self.a_equil[i])**2)).sum(dim=0)[self.a_masks[i]].sum() + loss+=energy + return loss + + def _conv_torsion_loss(self, x): + #x shape[X, 3, N] + loss=torch.tensor(0.0).float().to(self.device) + for i, weight in enumerate(self.t_weights): + b1 = torch.nn.functional.conv1d(x, weight[0].view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight[0])-4))#i-j + b2 = torch.nn.functional.conv1d(x, weight[1].view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight[1])-4))#j-k + b3 = torch.nn.functional.conv1d(x, weight[2].view(1,1,-1).repeat(3,1,1).to(self.device), groups=3, padding=(len(weight[2])-4))#l-k + c32=torch.cross(b3,b2) + c12=torch.cross(b1,b2) + torsion=torch.atan2((b2*torch.cross(c32,c12)).sum(dim=1), + b2.norm(dim=1)*((c12*c32).sum(dim=1))) + p = self.t_para[i,:,:,:].unsqueeze(0) + loss+=((p[:,:,1]/p[:,:,0])*(1+torch.cos((p[:,:,3]*torsion.unsqueeze(2))-p[:,:,2]))).sum() + return loss + + def _roll_bond_angle_torsion_loss(self, x): + #x.shape [5,3,2145] + bloss = torch.tensor(0.0).float().to(self.device) + aloss = torch.tensor(0.0).float().to(self.device) + tloss = torch.tensor(0.0).float().to(self.device) + v = [] + for i, diff in enumerate(self.brdiff): + v.append(x-x.roll(-diff,2)) + bloss+=(((v[-1].norm(dim=1)-self.br_equil[i])**2)*self.br_force[i]).sum() + + for i, diff in enumerate(self.ardiff): + v1 = self.arsign[i][0]*(v[diff[0]].roll(-self.arroll[i][0],2)) + v2 = self.arsign[i][1]*(v[diff[1]].roll(-self.arroll[i][1],2)) + xyz=torch.sum(v1*v2, dim=1) / (torch.norm(v1, dim=1) * torch.norm(v2, dim=1)) + theta = torch.acos(torch.clamp(xyz, min=-0.999999, max=0.999999)) + energy=(self.ar_force[i]*((theta-self.ar_equil[i])**2)) + sum_e = energy.sum() + aloss+=(sum_e) + + for i, diff in enumerate(self.trdiff): + b1 = self.trsign[i][0]*(v[diff[0]].roll(-self.trroll[i][0],2)) + b2 = self.trsign[i][1]*(v[diff[1]].roll(-self.trroll[i][1],2)) + b3 = self.trsign[i][2]*(v[diff[2]].roll(-self.trroll[i][2],2)) + c32=torch.cross(b3,b2) + c12=torch.cross(b1,b2) + torsion=torch.atan2((b2*torch.cross(c32,c12)).sum(dim=1), + b2.norm(dim=1)*((c12*c32).sum(dim=1))) + p = self.tr_para[i].unsqueeze(0) + tloss+=( ((p[:,:,1]/p[:,:,0])*(1+torch.cos((p[:,:,3]*torsion.unsqueeze(2))-p[:,:,2]))).sum()) + return bloss,aloss,tloss + diff --git a/build/lib/molearn/loss_functions/torch_protein_energy_utils.py b/build/lib/molearn/loss_functions/torch_protein_energy_utils.py new file mode 100644 index 0000000..393fc05 --- /dev/null +++ b/build/lib/molearn/loss_functions/torch_protein_energy_utils.py @@ -0,0 +1,1093 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +import numpy as np +import torch +import time +from copy import deepcopy +import biobox +import os +import pkg_resources + + +def read_lib_file(file_name, amber_atoms, atom_charge, connectivity): + try: + f_location = pkg_resources.resource_filename('molearn', 'parameters') + f_in = open(f'{f_location}/{file_name}') + print('File %s opened' % file_name) + except Exception as ex: + raise Exception('ERROR: file %s not found!' % file_name) + + + lines = f_in.readlines() + depth = 0 + indexs = {} + for tline in lines: + if tline.split()==['!!index', 'array', 'str']: + depth+=1 + for line in lines[depth:]: + if line[0]!=' ': + break + contents = line.split() + if len(contents)!=1 and len(contents[0])!=5: + break + res=contents[0] + if res[0]=='"' and res[-1] =='"': + amber_atoms[res[1:-1]]={} + atom_charge[res[1:-1]]={} + indexs[res[1:-1]]={} + connectivity[res[1:-1]]={} + else: + raise Exception(('I was expecting something of the form' + +'"XXX" but got %s instead' % res)) + depth+=1 + break + depth+=1 + for i, tline in enumerate(lines): + entry, res, unit_atoms, unit_connectivity = tline[0:7],tline[7:10], tline[10:22], tline[10:29] + if entry=='!entry.' and unit_atoms=='.unit.atoms ': + depth=i+1 + for line in lines[depth:]: + if line[ 0]!=' ': + break + contents = line.split() + if len(contents)<3 and len(contents[0])>4 and len(contents[1])>4: + break + pdb_name, amber_name,_,_,_,index,element_number,charge = contents + if (pdb_name[0]=='"' and pdb_name[-1]=='"' + and amber_name[0]=='"' and amber_name[-1]=='"'): + amber_atoms[res][contents[0][1:-1]] = contents[1][1:-1] + atom_charge[res][amber_name[1:-1]] = float(charge) + #indexs[res][amber_name[1:-1]] = int(index) + indexs[res][int(index)] = pdb_name[1:-1] + connectivity[res][pdb_name[1:-1]] = [] + else: + raise Exception(('I was expecting something of the form' + +'"XXX" but got %s instead' % res)) + elif entry=='!entry.' and unit_connectivity=='.unit.connectivity ': + depth=i+1 + for line in lines[depth:]: + if line[0]!=' ': + break + contents = line.split() + if len(contents)!=3: + break + a1,a2,flag = contents + connectivity[res][indexs[res][int(a1)]].append(indexs[res][int(a2)]) + connectivity[res][indexs[res][int(a2)]].append(indexs[res][int(a1)]) + +def get_amber_parameters(order=False, radians=True): + + file_names =('amino12.lib', + 'parm10.dat', + 'frcmod.ff14SB') + + #amber19 is dangerous because they've replaced parameters with cmap + ###### pdb atom names to amber atom names using amino19.lib ###### + amber_atoms = {} # knowledge[res][pdb_atom] = amber_atom + atom_mass = {} + atom_polarizability = {} + bond_force = {} + bond_equil = {} + angle_force = {} + angle_equil = {} + torsion_factor = {} + torsion_barrier = {} + torsion_phase = {} + torsion_period = {} + improper_factor = {} + improper_barrier = {} + improper_phase = {} + improper_period = {} + + other_parameters = {} + other_parameters['vdw_potential_well_depth']={} + other_parameters['H_bond_10_12_parameters']={} + other_parameters['equivalences']={} + other_parameters['charge']={} + other_parameters['connectivity']={} + read_lib_file(file_names[0],amber_atoms,other_parameters['charge'], other_parameters['connectivity']) + + try: + f_location = pkg_resources.resource_filename('molearn', 'parameters') + f_in = open(f'{f_location}/{file_names[1]}') + print('File %s opened' % file_names[1]) + except Exception as ex: + raise Exception('ERROR: file %s not found!' % file_names[1]) + + #section 1 title + line = f_in.readline() + print(line) + + amber_card_type_2(f_in, atom_mass, atom_polarizability) + amber_card_type_3(f_in) + amber_card_type_4(f_in, bond_force, bond_equil) + amber_card_type_5(f_in, angle_force, angle_equil) + amber_card_type_6(f_in, torsion_factor, torsion_barrier, torsion_phase, torsion_period) + amber_card_type_7(f_in, improper_factor, + improper_barrier, improper_phase, improper_period) + amber_card_type_8(f_in, other_parameters) + amber_card_type_9(f_in, other_parameters) + for line in f_in: + if len(line.split())>1: + if line.split()[1]=='RE': + amber_card_type_10B(f_in, other_parameters) + elif line[0:3]=='END': + print('parameters loaded') + f_in.close() + + #open frcmod file, should be identifcal format but missing any or all cards + try: + f_location = pkg_resources.resource_filename('molearn', 'parameters') + f_in = open(f'{f_location}/{file_names[2]}') + print('File %s opened' % file_names[2]) + except Exception as ex: + raise Exception('ERROR: file %s not found!' % file_names[2]) + + + #section 1 title + line = f_in.readline() + print(line) + + for line in f_in: + if line[:4]=='MASS': + amber_card_type_2(f_in, atom_mass, atom_polarizability) + if line[:4]=='BOND': + amber_card_type_4(f_in, bond_force, bond_equil) + if line[:4]=='ANGL': + amber_card_type_5(f_in, angle_force, angle_equil) + if line[:4]=='DIHE': + amber_card_type_6(f_in, torsion_factor, torsion_barrier, torsion_phase, torsion_period) + if line[:4]=='IMPR': + amber_card_type_7(f_in, improper_factor, + improper_barrier, improper_phase, improper_period) + if line[:4]=='HBON': + amber_card_type_8(f_in, other_parameters) + if line[:4]=='NONB': + amber_card_type_10B(f_in, other_parameters) + if line[:4]=='CMAP': + print('Yeah, Im not bothering to implement cmap') + elif line[0:3]=='END': + print('parameters loaded') + f_in.close() + + if radians: + for angle in angle_equil: + angle_equil[angle]=np.deg2rad(angle_equil[angle]) + for torsion in torsion_phase: + torsion_phase[torsion]=list(np.deg2rad(torsion_phase[torsion])) + + return (amber_atoms, atom_mass, atom_polarizability, bond_force, bond_equil, + angle_force, angle_equil, torsion_factor, torsion_barrier, torsion_phase, + torsion_period, improper_factor, improper_barrier, improper_phase, + improper_period, other_parameters) + +def amber_card_type_2(f_in, atom_mass, atom_polarizability): + + #section 2 input for atom symbols and masses + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom = line[0:2].strip() + contents = line[2:24].split() + if len(contents)==2: + mass, polarizability = float(contents[0]),float(contents[1]) + atom_mass[atom]=mass + atom_polarizability[atom]=polarizability + elif len(contents)==1: # sometimes a polarizability is not listed + mass = float(contents[0]) + atom_mass[atom]=mass + atom_polarizability[atom]=polarizability + else: + raise Exception('Should be 2A, X, F10.2, F10.2, comments but got %s' % line) + +def amber_card_type_3(f_in): + #section 3 input for atom symbols that are hydrophilic + line = f_in.readline() + +def amber_card_type_4(f_in, bond_force, bond_equil, order=False): + #section 4 bond length paramters + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom1 = line[0:2]. strip() + atom2 = line[3:5].strip() + if order: + bond = tuple(sorted((atom1, atom2))) # put in alphabetical order + else: + bond = (atom1, atom2) + contents = line[5:25].split() + if len(contents)!=2: + raise Exception('Expected 2 floats but got %s' % line[6:26]) + force_constant, equil_length = float(contents[0]), float(contents[1]) + bond_force[bond]=force_constant + bond_equil[bond]=equil_length + #this should throw an error if there are not + +def amber_card_type_5(f_in, angle_force, angle_equil, order=False): + #section 5 + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom1 = line[0:2].strip() + atom2 = line[3:5].strip() + atom3 = line[6:8].strip() + if order: + sorted13 = sorted((atom1, atom3)) + angle = (sorted13[0], atom2, sorted13[1]) + # I want it sorted alphabetically by 1-3 atoms + else: + angle=(atom1, atom2, atom3) + contents = line[8:28].split() + if len(contents)!=2: + raise Exception('Expected 2 floats but got %s' % line[6:26]) + force_constant, equil_angle = float(contents[0]), float(contents[1]) + angle_force[angle]=force_constant + angle_equil[angle]=equil_angle + +def amber_card_type_6(f_in, torsion_factor, torsion_barrier, torsion_phase, + torsion_period, order=False): + #secti on 6 torsion / proper dihedral + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom1 = line[0:2].strip() + atom2 = line[3:5].strip() + atom3 = line[6:8].strip() + atom4 = line[9:11].strip() + if order: + sort23 = sorted([(atom2, atom1), (atom3, atom4)], key=lambda x: x[0]) + torsion = tuple( (sort23[0][1], sort23[0][0], sort23[1][0], sort23[1][1]) ) + else: + torsion = (atom1, atom2, atom3, atom4) + contents = line[11:55].split() + if len(contents)!=4: + raise Exception('I wanted four values here?') + #the actual torsion potential is (barrier/factor)*(1+cos(period*phi-phase)) + if torsion in torsion_period: + if torsion_period[torsion][-1]>0: + torsion_factor[torsion] = [int(contents[0]) ] + torsion_barrier[torsion] = [float(contents[1])] + torsion_phase[torsion] = [float(contents[2])] + torsion_period[torsion] = [float(contents[3])] + elif torsion_period[torsion][-1]<0: + torsion_factor[torsion].append( int(contents[0])) + torsion_barrier[torsion].append(float(contents[1])) + torsion_phase[torsion].append( float(contents[2])) + torsion_period[torsion].append( float(contents[3])) + else: + torsion_factor[torsion] = [int(contents[0]) ] + torsion_barrier[torsion] = [float(contents[1])] + torsion_phase[torsion] = [float(contents[2])] + torsion_period[torsion] = [float(contents[3])] + +def amber_card_type_7(f_in, improper_factor, improper_barrier, + improper_phase, improper_period, order=False): + #section 7 improper dihedrals + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom1 = line[0:2].strip() + atom2 = line[3:5].strip() + atom3 = line[6:8].strip() + atom4 = line[9:11].strip() + if order: + sort23 = sorted([(atom2, atom1), (atom3, atom4)], key=lambda x: x[0]) + torsion = tuple( (sort23[0][1], sort23[0][0], sort23[1][0], sort23[1][1]) ) + else: + torsion = (atom1, atom2, atom3, atom4) + contents = line[11:55].split() + if len(contents)==3: + improper_barrier[torsion] = float(contents[0]) + improper_phase[torsion] = float(contents[1]) + improper_period[torsion] = float(contents[2]) + elif len(contents)==4: + raise Exception('This seems allowed in the doc but doesnt appear in reality') + improper_factor[torsion] = int(contents[0]) + improper_barrier[torsion] = float(contents[1]) + improper_phase[torsion] = float(contents[2]) + improper_period[torsion] = float(contents[3]) + #the actual torsion potential is (barrier/factor)*(1+cos(period*phi-phase)) + #it seems improper potential don't divide by the factor + +def amber_card_type_8(f_in, other_parameters, order=False): + #section 8 H-bond 10-12 potential parameters + for line in f_in: + if line=='\n' or line.strip()=='': + break + atom1 = line[2:4].strip() + atom2 = line[6:8].strip() + if order: + pair = tuple(sorted((atom1, atom2))) + else: + pair = (atom1, atom2) + contents = line[8:].split() + other_parameters['H_bond_10_12_parameters'][pair]=contents + +def amber_card_type_9(f_in, other_parameters): + #section 9 equi valencing atom symbols for non-bonded 6-12 potential parameters + for line in f_in: + if line=='\n' or line.strip()=='': + break + contents = line.split() + other_parameters['equivalences'][contents[0]]=contents + +def amber_card_type_10B(f_in, other_parameters): + #section 10 6-12 potential parameters + for line in f_in: + if line== '\n' or line.strip()=='': + break + contents = line.split() + other_parameters['vdw_potential_well_depth'][contents[0]] = [float(i) for i in contents[1:3]] + +def get_convolutions(dataset, pdb_atom_names, + atom_label=('set','string')[0], + perform_checks=True, + v=5, + order=False, + return_type=['mask','idxs'][1], + absolute_torsion_period=True, + NB=('matrix',)[0], + fix_terminal=True, + fix_charmm_residues=True, + fix_slice_method=False, + fix_h=False, + alt_vdw = [], + permitivity=1.0 + ): + ''' + ##INPUTS## + + dataset: one frame of a trajectory of shape [3, N] + + pdb_atom_names: should be an array of shape [N,2] + pdb_atom_names[:,0] is the pdb_atom_names and + pdb_atom_names[:,1] is the residue names + + atom_label: (default, 'set') deprecated and broken for anything other than 'set' + + perform_checks: No longer works so has been removed + + v: (default, 5) atom_selection version, bonds are determined by interatomic + distance, with v=2. v=1 shouldn't be used except in specific cirumstances. v=5 is using connectivity from amber parameters. + + order: (bool, default false) are atoms ordered, I think I've fixed this so that + it shouldn't matter either way but keep as False. + + return_type: (option now removed) + + + ##OUTPUTS## + convolution output shape N* will be N-(conv length -1)+padding + + bond_masks, b_equil, b_force: shape [number of convolutions, N*] + + bond_weights: shape[number of convolutions, conv_size] + + angle_masks, a_equil, a_force: shape [number of convolutions, conv_size] + + angle_weights: shape[number of convolutions, 2, conv_size] + + torsion_masks: shape[number of convolution, 3, conv_size] + + t_para: shape[num of convs, N*, 4, max number torsion parameters ] + + tornsion_weigths: shape [number of convolutions, 3, conv_size] + + ''' + + + #get amber parameters + (amber_atoms, atom_mass, atom_polarizability, bond_force, bond_equil, + angle_force, angle_equil, torsion_factor, torsion_barrier, torsion_phase, + torsion_period, improper_factor, improper_barrier, improper_phase, + improper_period, other_parameters) = get_amber_parameters() + if fix_terminal: + pdb_atom_names[pdb_atom_names[:,0]=='OXT',0]='O' + if fix_charmm_residues: + pdb_atom_names[pdb_atom_names[:,1]=='HSD',1]='HID' + pdb_atom_names[pdb_atom_names[:,1]=='HSE',1]='HIE' + for i in np.unique(pdb_atom_names[:,2]): + res_mask = pdb_atom_names[:,2]==i + if (pdb_atom_names[res_mask, 1]=='HIS').all(): # if a HIS residue + if (pdb_atom_names[res_mask, 0]=='HD1').any() and (pdb_atom_names[res_mask, 0]=='HE2').any(): + pdb_atom_names[res_mask, 1]='HIP' + elif (pdb_atom_names[res_mask, 0]=='HD1').any(): + pdb_atom_names[res_mask, 1]='HID' + elif (pdb_atom_names[res_mask, 0]=='HE2').any(): + pdb_atom_names[res_mask, 1]='HIE' + #if any HIS are remaining it doesn't matter which because the H is dealt with above + pdb_atom_names[pdb_atom_names[:,1]=='HIS',1]='HIE' + if fix_h: + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='MET'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='MET'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='ASN'),0]='HB3' + pdb_atom_names[pdb_atom_names[:,0]=='HN',0]='H' + pdb_atom_names[pdb_atom_names[:,0]=='1HD2',0]='HD21' + pdb_atom_names[pdb_atom_names[:,0]=='2HD2',0]='HD22' + pdb_atom_names[pdb_atom_names[:,0]=='1HG2',0]='HG21' + pdb_atom_names[pdb_atom_names[:,0]=='2HG2',0]='HG22' + pdb_atom_names[pdb_atom_names[:,0]=='3HG2',0]='HG23' + pdb_atom_names[pdb_atom_names[:,0]=='3HG1',0]='HG13' + pdb_atom_names[pdb_atom_names[:,0]=='1HG1',0]='HG11' + pdb_atom_names[pdb_atom_names[:,0]=='2HG1',0]='HG12' + pdb_atom_names[pdb_atom_names[:,0]=='1HD1',0]='HD11' + pdb_atom_names[pdb_atom_names[:,0]=='2HD1',0]='HD12' + pdb_atom_names[pdb_atom_names[:,0]=='3HD1',0]='HD13' + pdb_atom_names[pdb_atom_names[:,0]=='3HD2',0]='HD23' + pdb_atom_names[pdb_atom_names[:,0]=='1HH1',0]='HH11' + pdb_atom_names[pdb_atom_names[:,0]=='2HH1',0]='HH12' + pdb_atom_names[pdb_atom_names[:,0]=='1HH2',0]='HH21' + pdb_atom_names[pdb_atom_names[:,0]=='2HH2',0]='HH22' + pdb_atom_names[pdb_atom_names[:,0]=='1HE2',0]='HE21' + pdb_atom_names[pdb_atom_names[:,0]=='2HE2',0]='HE22' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG11', pdb_atom_names[:,1]=='ILE'),0]='HG13' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='CD', pdb_atom_names[:,1]=='ILE'),0]='CD1' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD1', pdb_atom_names[:,1]=='ILE'),0]='HD11' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD2', pdb_atom_names[:,1]=='ILE'),0]='HD12' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD3', pdb_atom_names[:,1]=='ILE'),0]='HD13' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='PHE'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='GLU'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='GLU'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='LEU'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='ARG'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='ARG'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD1', pdb_atom_names[:,1]=='ARG'),0]='HD3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='ASP'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HA1', pdb_atom_names[:,1]=='GLY'),0]='HA3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='LYS'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='LYS'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD1', pdb_atom_names[:,1]=='LYS'),0]='HD3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HE1', pdb_atom_names[:,1]=='LYS'),0]='HE3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='TYR'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='HIP'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='SER'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='SER'),0]='HG' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='PRO'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='PRO'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HD1', pdb_atom_names[:,1]=='PRO'),0]='HD3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='LEU'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='GLN'),0]='HB3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HG1', pdb_atom_names[:,1]=='GLN'),0]='HG3' + pdb_atom_names[np.logical_and(pdb_atom_names[:,0]=='HB1', pdb_atom_names[:,1]=='TRP'),0]='HB3' + #writes termini as H because we haven't loaded in termini parameters + atom_names = [[amber_atoms[res][atom],res, resid] if atom not in ['H2', 'H3'] else [amber_atoms[res]['H'], res, resid] for atom, res, resid in pdb_atom_names ] + p_atom_names = [[atom,res, resid] if atom not in ['H2', 'H3'] else ['H', res, resid] for atom, res, resid in pdb_atom_names ] + + #atom_names = [[amber_atoms[res][atom],res] for atom, res, resid in pdb_atom_names ] + atom_charges=[other_parameters['charge'][res][atom] for atom, res, resid in atom_names] + if NB == 'matrix': + equiv_t=other_parameters['equivalences'] + vdw_para = other_parameters['vdw_potential_well_depth'] + #switch these around so that values point to key + equiv = {} + for i in equiv_t.keys(): + j = equiv_t[i] + for k in j: + equiv[k]=i + atom_R = torch.tensor([vdw_para[equiv.get(atom,atom)][0] for atom, res, resid in atom_names]) #radius + atom_e = torch.tensor([vdw_para[equiv.get(atom,atom)][1] for atom, res, resid in atom_names]) #welldepth + + print('Determining bonds') + version = v # method of selecting bonded atoms + N = dataset.shape[1] #2145 + + cmat=(torch.nn.functional.pdist((dataset).permute(1,0))).cpu().numpy() + if version == 1: + bond_idxs=np.argpartition(cmat, (N-1,N)) + #this will work for any non cyclic monomeric protein + #that in mind will break if enough proline atoms to make a cycle are selected + bond_idxs, u = bond_idxs[:N-1], bond_idxs[N-1] + if cmat[u]-cmat[bond_idxs[-1]]<0.25: + raise Exception("WARNING: May not have correctly selected the bonded distances: value " + +(cmat[u]-cmat[bond_idxs[-1]])+ + "should be roughly between 0.42 and 0.57 (>0.25)" )# should be 0.42-0.57 + version+=1 #try version 2 instead + mid = cmat[bond_idxs[-1]]+((cmat[u]-cmat[bond_idxs[-1]])/2) #mid point + full_mask = (cmatcdist).triu(diagonal=1).numpy()) + remove = np.where(np.abs(j-i)>30) + max_bond_dist[i[remove],j[remove]]=0.0 + max_bond_dist = max_bond_dist.numpy() + else: + max_bond_dist = (0.6*(atom_R.view(1,-1)+atom_R.view(-1,1))).cpu().numpy() + max_bond_dist = max_bond_dist[np.where(np.triu(np.ones((N,N)),k=1))] + full_mask = np.greater(max_bond_dist,cmat) + bond_idxs = np.where(full_mask)[0] # for some reason returns tuple with one array + if version == 4: + #fix_hydrogens = [[atom, res, resid] for atom, res, resid in pdb_atom_names if atom in ['H2', 'H3']] + connectivity = other_parameters['connectivity'] + bond_types = [] + bond_idxs = [] + #tracker = [[]]*N doesn't work because of mutability + tracker = [[] for i in range(N)] + current_resid = -9999 + current_atoms = [] + for i1, (atom1, res, resid) in enumerate(p_atom_names): + assert atom1 in connectivity[res] + for atom2, i2 in current_atoms: + if resid !=current_resid:# and atom2 == 'C': + if atom2 != 'C': + continue + elif not (atom2 in connectivity[res][atom1] and atom1 in connectivity[res][atom2]): + continue + # if not (atom2 in connectivity[res][atom1] and atom1 in connectivity[res][atom2]): + # if resid != current_resid and atom2 == 'C': + # current_resid = resid + # current_atoms = [] + # else: + # continue + if atom1=='N' and atom2=='CA': + continue + tracker[i1].append(i2) + tracker[i2].append(i1) + if atom_label=='set': + if order: + names = tuple(sorted((atom_names[i2][0], atom_names[i1][0]))) + else: + names = tuple((atom_names[i2][0], atom_names[i1][0])) + bond_types.append(names) + bond_idxs.append([i2,i1]) + if resid !=current_resid:# and atom2 == 'C': + current_resid = resid + current_atoms = [] + current_atoms.append([atom1,i1]) + if version ==5: + connectivity = other_parameters['connectivity'] + bond_types = [] + bond_idxs = [] + tracker = [[] for i in range(N)] + current_resid = -9999 + current_atoms = [] + previous_atoms = [] + for i1, (atom1, res, resid) in enumerate(p_atom_names): + assert atom1 in connectivity[res] + if resid!=current_resid: + previous_atoms = deepcopy(current_atoms) + current_atoms = [] + current_resid=resid + if atom1=='N': + for atom2, i2 in previous_atoms: + if atom2=='C': + tracker[i1].append(i2) + tracker[i2].append(i1) + bond_types.append(tuple((atom_names[i2][0], atom_names[i1][0]))) + bond_idxs.append([i2,i1]) + + for atom2, i2 in current_atoms: + if atom2 in connectivity[res][atom1] and atom1 in connectivity[res][atom2]: + tracker[i1].append(i2) + tracker[i2].append(i1) + names = tuple((atom_names[i2][0], atom_names[i1][0])) + bond_types.append(names) + bond_idxs.append([i2,i1]) + current_atoms.append([atom1,i1]) + if version <4: + all_bond_idxs = np.sort(bond_idxs) + + bond_types = [] + bond_idxs = [] + tracker = [[]] # this will keep track of some of the bonds to help work out the angles + atom1=0 + atom2=1 + counter = 0 #index of the distance N,N+1 + for bond in all_bond_idxs: + if bond < counter+(N-atom1-1): + atom2 = atom1+bond-counter+1 # 0-0+1 + tracker[-1].append(atom2) # + while bond > counter+(N-atom1-2): + counter+=(N-atom1-1) + atom1 +=1 + tracker.append([]) + if bond < counter+(N-atom1-1): + atom2 = atom1+bond-counter+1 + tracker[-1].append(atom2) + if atom_label=='string': #string of atom labels, doesn't handle Proline alternate ordering + bond_types.append(atom_names[atom1][0]+'_'+atom_names[atom2][0]) + bond_idxs.append([atom1, atom2]) + elif atom_label=='set': #set of atom labels + if order: + names = tuple(sorted((atom_names[atom1][0], atom_names[atom2][0]))) + else: + names = (atom_names[atom1][0], atom_names[atom2][0]) + bond_types.append(names) + bond_idxs.append([atom1, atom2]) + + while len(tracker)atom1 prevents duplicates later ) + if version < 4: + for atom1, atom1_bonds in enumerate(deepcopy(tracker)): # for _, [] in enum [[]] + for atom2 in atom1_bonds: # for _ in [] + tracker[atom2].append(atom1) + # find every angle and add it + for atom1, atom1_bonds in enumerate(tracker): + for atom2 in atom1_bonds: + for atom3 in tracker[atom2]: + if atom3>atom1: #each angle will only be counter once + if order: + sort13 = sorted([ (atom_names[atom1][0], atom1), (atom_names[atom3][0], atom3) ], key=lambda x: x[0]) + names = tuple( (sort13[0][0], atom_names[atom2][0], sort13[1][0]) ) + + angle_types.append(names) + angle_idxs.append([sort13[0][1], atom2, sort13[1][1]]) + else: + angle_types.append((atom_names[atom1][0], atom_names[atom2][0], + atom_names[atom3][0])) + angle_idxs.append([atom1, atom2, atom3]) + if atom3 != atom1: + for atom4 in tracker[atom3]: + if atom4>atom1 and atom2!=atom4:# each torsion will be counter once + #torsions are done based on the 2 3 atoms, so sort 23 + if order: + sort23 = sorted([ (atom_names[atom2][0], atom2, atom_names[atom1][0], atom1), + (atom_names[atom3][0], atom3, atom_names[atom4][0], atom4) ], key=lambda x: x[0]) + names = tuple( (sort23[0][2], sort23[0][0], sort23[1][0], sort23[1][2]) ) + torsion_types.append(names) + torsion_idxs.append([sort23[0][3], sort23[0][1], sort23[1][1], sort23[1][3]]) + else: + torsion_types.append((atom_names[atom1][0], atom_names[atom2][0], + atom_names[atom3][0], atom_names[atom4][0])) + torsion_idxs.append([atom1, atom2, atom3, atom4]) + bond_14_idxs.append([atom1,atom4]) + #currently have bond_types, angle_types, and torsion_typs + idxs + bond_idxs = np.array(bond_idxs) + angle_idxs = np.array(angle_idxs) + torsion_idxs = np.array(torsion_idxs) + bond_max_conv = (bond_idxs.max(axis=1)-bond_idxs.min(axis=1)).max()+1 + if bond_max_conv<3 and fix_slice_method: + bond_max_conv=3 + angle_max_conv = (angle_idxs.max(axis=1)-angle_idxs.min(axis=1)).max()+1 + if angle_max_conv<5 and fix_slice_method: + angle_max_conv=5 + torsion_max_conv = (torsion_idxs.max(axis=1)-torsion_idxs.min(axis=1)).max()+1 + if torsion_max_conv<7 and fix_slice_method: + torsion_max_conv=7 + #there is a problem where i accidentally index [padding-3] so if (len -4) < 3 we index -1 which breaks things + #it shouldn't affect anything to say the max conv is greater than 6 + #this little bit just turns the 'types' list into equivalent parameters + #key error if you don't have the parameter + bond_para= np.array([ [bond_equil[bond], bond_force[bond]] if bond in bond_equil + else [bond_equil[(bond[1],bond[0])], bond_force[(bond[1], bond[0])]] + for bond in bond_types]) + angle_para=np.array([ [angle_equil[angle], angle_force[angle]] if angle in angle_equil + else [angle_equil[(angle[2],angle[1],angle[0])], angle_force[(angle[2], angle[1], angle[0])]] + for angle in angle_types]) + torsion_para=[] + t_unique=list(set(torsion_types)) + t_unique_para = {} + max_para = 0 + for torsion in t_unique: + torsion_b = (torsion[3],torsion[2],torsion[1],torsion[0]) + torsion_xx = ('X', torsion[2], torsion[1], 'X') + torsion_xb = ('X', torsion[1], torsion[2], 'X') + if torsion in torsion_barrier: + max_para = max(max_para, len(torsion_barrier[torsion])) + t_unique_para[torsion]= [torsion_factor[torsion], torsion_barrier[torsion], + torsion_phase[torsion], torsion_period[torsion]] + elif torsion_b in torsion_barrier: + max_para = max(max_para, len(torsion_barrier[torsion_b])) + t_unique_para[torsion]= [torsion_factor[torsion_b], torsion_barrier[torsion_b], + torsion_phase[torsion_b], torsion_period[torsion_b]] + elif torsion_xx in torsion_barrier: + max_para = max(max_para, len(torsion_barrier[torsion_xx])) + t_unique_para[torsion]= [torsion_factor[torsion_xx], torsion_barrier[torsion_xx], + torsion_phase[torsion_xx], torsion_period[torsion_xx]] + elif torsion_xb in torsion_barrier: + max_para = max(max_para, len(torsion_barrier[torsion_xb])) + t_unique_para[torsion]= [torsion_factor[torsion_xb], torsion_barrier[torsion_xb], + torsion_phase[torsion_xb], torsion_period[torsion_xb]] + else: + print('ERROR: Torsion %s cannot be found in torsion_barrier and will not be included'% torsion) + torsion_para = np.zeros((len(torsion_types),4,max_para)) + #we don't want barrier/factor to return nan so set factor to 1 by default + torsion_para[:,0,:]=1.0 + for i,torsion in enumerate(torsion_types): + para = t_unique_para[torsion] + torsion_para[i,:,:len(para[0])]=para + ##### make phase positive ##### + if absolute_torsion_period: + torsion_para[:,3,:] = np.abs(torsion_para[:,3,:]) + + + + ############################### bonds ################################# + + bond_masks = np.zeros((bond_max_conv-1, N-(bond_max_conv-1) + 2*(bond_max_conv-2)),dtype=np.bool) + bond_conv = (bond_idxs.max(axis=1)-bond_idxs.min(axis=1))-1 + + bond_weights = [] + b_equil = np.zeros(bond_masks.shape) + b_force = np.zeros(bond_masks.shape) + for i in range(bond_max_conv-1): + weight = [0.0]*bond_max_conv + weight[0]=1.0 + weight[i+1]=-1.0 + bond_weights.append(weight) + mask_index=bond_idxs.min(axis=1)[bond_conv==i]+bond_max_conv-2 + bond_masks[i, mask_index]=True + b_equil[i, mask_index]=bond_para[bond_conv==i,0] + b_force[i, mask_index] =bond_para[bond_conv==i,1] + + ############################### angles ################################# + + angle_conv = (angle_idxs-angle_idxs.min(axis=1).reshape(-1,1))#relative positions of atoms + angle_conv = np.where((angle_conv[:,0] amber atom names and residues + padded_atom_names = np.array([[amber_atoms[res][atom],res, resid] if atom is not None else [atom, res, resid] for atom, res, resid in pdb_atom_names]) + unpadded_atom_names = [[amber_atoms[res][atom],res, resid] for atom, res, resid in pdb_atom_names if atom is not None] + padded_atom_charges = np.array([other_parameters['charge'][res][atom] if atom is not None else np.nan for atom, res, _ in padded_atom_names]) + if padded_atom_names.shape != dataset.shape: # just a little check + raise Exception('996 padded_atom_names!=dataset.shape') + atom_names = padded_atom_names + atom_charges = padded_atom_charges + print('Determining bonds') + + + connect = other_parameters['connectivity'] + #connectivity = [[]]*N # careful with mutability + connectivity = [[] for i in range(N)] + current_resid = -9999 + current_atoms = [] + for i1, (atom1, res, resid) in enumerate(padded_atom_names): + if atom1 is None: + continue + if resid!= current_resid: + current_resid = resid + current_atoms = [] + assert atom1 in connect[res] + for atom2, i2 in current_atoms: + if atom2 in connect[res][atom1] and atom1 in connect[res][atom2]: + connectivity[i1].append(i2) + connectivity[i2].append(i1) + # cmat = torch.cdist(dataset, dataset) #[R*M,3 ]-> [R*M, R*M] + # #1.643 was max bond distance in MurD test, 2.129 was the smallest nonbonded distance + # #can't say what the best solution is but somewhere in the middle will probably be okay + # all_bond_mask = (cmat<(1.643+2.1269)/2).triu(diagonal=1) # [R*M,R*M] + # bond_idxs = all_bond_mask.nonzero() # [B x 2] + # #name_set = set(atom_names[:,0]) +# +# connectivity = [[] for i in range(N)] # this will keep track of some of the bonds to help work out the angles +# for i,j in bond_idxs: +# connectivity[i].append(j) +# connectivity[j].append(i) +# ##################### Angles/1-3 ##################### + print('Determining angles') + + bond_idxs_ = [] + angle_idxs = [] + torsion_idxs = [] + bond_14_idxs = [] + + bond_para = [] + angle_para = [] + torsion_para_ = [] + + for atom1, atom2_list in enumerate(connectivity): + for atom2 in atom2_list: + a1, a2 = atom_names[atom1][0], atom_names[atom2][0] + if atom1 < atom2: #stops any pair of atoms being selected twice + bond_idxs_.append([atom1, atom2]) + for b in [(a1,a2), (a2,a1)]: + if b in bond_equil: + bond_para.append([bond_equil[b], bond_force[b]]) + break # break prevents any bond from beind added twice + else: + raise Exception('No associated bond parameter') + + for atom3 in connectivity[atom2]: + a3 = atom_names[atom3][0] + if atom3 > atom1: #each angle will only be counter once + angle_idxs.append([atom1,atom2,atom3]) + for a in [(a1,a2,a3), (a3,a2,a1)]: + if a in angle_equil: + angle_para.append([angle_equil[a], angle_force[a]]) + break + else: + raise Exception('No associated angle parameter') + if atom3 != atom1: #don't go back to same atom + for atom4 in connectivity[atom3]: + if atom4 > atom1 and atom2!=atom4: + torsion_idxs.append([atom1, atom2, atom3, atom4]) + bond_14_idxs.append([atom1, atom4]) + a4 = atom_names[atom4][0] + for t in [(a1,a2,a3,a4),(a4,a3,a2,a1),('X',a2,a3,'X'),('X',a3,a2,'X')]: + if t in torsion_barrier: + torsion_para_.append(torch.tensor([ + torsion_factor[t], + torsion_barrier[t], + torsion_phase[t], + torsion_period[t]])) + break #each torsion only counter once + else: + raise Exception('No associated torsion parameter') + + bond_idxs_ = torch.as_tensor(bond_idxs_) + angle_idxs = torch.tensor(angle_idxs) + torsion_idxs = torch.tensor(torsion_idxs) + bond_14_idxs = torch.tensor(bond_14_idxs) + bond_para = torch.tensor(bond_para) + angle_para = torch.tensor(angle_para) + max_number_torsion_para = max([tf.shape[1] for tf in torsion_para_]) + torsion_para = torch.zeros(torsion_idxs.shape[0],4,max_number_torsion_para) + torsion_para[:,0,:]=1.0 + for i,tf in enumerate(torsion_para_): + torsion_para[i,:,0:tf.shape[1]]=tf + if absolute_torsion_period: + torsion_para[:,3,:] = np.abs(torsion_para[:,3,:]) + ###### Gather based potential ###### + #currently for data [B, R*M, 3] or [B, 3, N] + aij0 = bond_idxs.reshape(-1,2,1).eq(angle_idxs[:,(0,1)].view(-1,2,1).permute(2,1,0)).all(dim=1) + aij1 = bond_idxs.reshape(-1,2,1).eq(angle_idxs[:,(1,0)].view(-1,2,1).permute(2,1,0)).all(dim=1) + ajk0 = bond_idxs.reshape(-1,2,1).eq(angle_idxs[:,(1,2)].view(-1,2,1).permute(2,1,0)).all(dim=1) + ajk1 = bond_idxs.reshape(-1,2,1).eq(angle_idxs[:,(2,1)].view(-1,2,1).permute(2,1,0)).all(dim=1) + ij_jk = torch.stack([torch.where((aij0+aij1).T)[1], torch.where((ajk0+ajk1).T)[1]]) + aij_ = aij1.float()-aij0.float() #sign change needed for loss_function equation + ajk_ = ajk0.float()-ajk1.float() + angle_mask = torch.stack([aij_.sum(dim=0), ajk_.sum(dim=0)]) + + #following are [N_bonds, N_torsions] arrays comparing if the ij or jk are the same + ij0 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(0,1)].view(-1,2,1).permute(2,1,0)).all(dim=1) + ij1 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(1,0)].view(-1,2,1).permute(2,1,0)).all(dim=1) + jk0 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(1,2)].view(-1,2,1).permute(2,1,0)).all(dim=1) + jk1 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(2,1)].view(-1,2,1).permute(2,1,0)).all(dim=1) + kl0 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(2,3)].view(-1,2,1).permute(2,1,0)).all(dim=1) + kl1 = bond_idxs.reshape(-1,2,1).eq(torsion_idxs[:,(3,2)].view(-1,2,1).permute(2,1,0)).all(dim=1) + ij_jk_kl = torch.stack([torch.where((ij0+ij1).T)[1], + torch.where((jk0+jk1).T)[1], + torch.where((kl0+kl1).T)[1]]) + ij_ = ij0.float()-ij1.float() + jk_ = jk0.float()-jk1.float() + kl_ = kl0.float()-kl1.float() + torsion_mask = torch.stack([ij_.sum(dim=0), jk_.sum(dim=0), kl_.sum(dim=0)]) + + #j-i i->j + #i-j j->i reverse + #k-j j->k + #j-k k->j reverse + #l-k k->l + #k-l l->k reverse + + + if NB=='matrix': + equiv_t=other_parameters['equivalences'] + vdw_para = other_parameters['vdw_potential_well_depth'] + #switch these around so that values point to key + equiv = {} + for i in equiv_t.keys(): + j = equiv_t[i] + for k in j: + equiv[k]=i + atom_R = torch.tensor([vdw_para[equiv.get(i,i)][0] if i is not None else np.nan for i, j, k in atom_names]) #radius + atom_e = torch.tensor([vdw_para[equiv.get(i,i)][1] if i is not None else np.nan for i, j, k in atom_names]) #welldepth + #cdist is easier to work with than pdist, batch pdist doesn't seem to exist too + vdw_R = 0.5*torch.cdist(atom_R.view(-1,1), -atom_R.view(-1, 1)).triu(diagonal=1) + vdw_e = (atom_e.view(1,-1)*atom_e.view(-1, 1)).triu(diagonal=1).sqrt() + #set 1-2, and 1-3 distances to 0.0 + vdw_R[list(bond_idxs.T)]=0.0 + vdw_e[list(bond_idxs.T)]=0.0 + vdw_R[list(angle_idxs[:,(0,2)].T)]=0.0 + vdw_e[list(angle_idxs[:,(0,2)].T)]=0.0 + if correct_1_4: + # sum A/R**12 - B/R**6; A = e* (R**12); B = 2*e *(R**6) + # therefore scale vdw by setting e /= 2.0 + #vdw_R[list(torsion_idxs[:,(0,3)].T)]/=2.0 + vdw_e[list(torsion_idxs[:,(0,3)].T)]/=2.0 + else: + vdw_R[list(torsion_idxs[:,(0,3)].T)]=0.0 + vdw_e[list(torsion_idxs[:,(0,3)].T)]=0.0 + vdw_R[torch.isnan(vdw_R)]=0.0 + vdw_e[torch.isnan(vdw_e)]=0.0 + + #partial charges are given as fragments of electron charge. + #Can convert coulomb energy into kcal/mol by multiplying with 332.05. + #therofore multiply q by sqrt(332.05)=18.22 + e_=permitivity #permittivity + atom_charges=torch.tensor(atom_charges) + q1q2=(atom_charges.view(1,-1)*atom_charges.view(-1,1)/e_).triu(diagonal=1) #Aij=bi*bj + q1q2[list(bond_idxs.T)]=0.0 + q1q2[list(angle_idxs[:,(0,2)].T)]=0.0 + if correct_1_4: + q1q2[list(torsion_idxs[:,(0,3)].T)]/=1.2 + else: + q1q2[list(torsion_idxs[:,(0,3)].T)]=0.0 + #1-4 are should be included but scaled + return ( + bond_idxs, bond_para, + angle_idxs, angle_para, angle_mask, ij_jk, + torsion_idxs, torsion_para, torsion_mask, ij_jk_kl, + vdw_R, vdw_e, + q1q2, + ) + return ( + bond_idxs, bond_para, + angle_idxs, angle_para, angle_mask, ij_jk, + torsion_idxs, torsion_para, torsion_mask, ij_jk_kl, + ) + +if __name__ == '__main__': + import sys + import os + sys.path.insert(0, os.path.abspath('../')) + import biobox + + diff --git a/build/lib/molearn/models/__init__.py b/build/lib/molearn/models/__init__.py new file mode 100644 index 0000000..e0a1422 --- /dev/null +++ b/build/lib/molearn/models/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2021 Venkata K. Ramaswamy, Samuel C. Musson, Chris G. Willcocks, Matteo T. Degiacomi +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# molearn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molearn ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + diff --git a/build/lib/molearn/models/foldingnet.py b/build/lib/molearn/models/foldingnet.py new file mode 100644 index 0000000..71b711f --- /dev/null +++ b/build/lib/molearn/models/foldingnet.py @@ -0,0 +1,261 @@ +import torch +import biobox as bb +from torch import nn +import torch.nn.functional as F + +def index_points(point_clouds, index): + """ + Given a batch of tensor and index, select sub-tensor. + Input: + points: input points data, [B, N, C] + idx: sample index data, [B, N, k] + Return: + new_points:, indexed points data, [B, N, k, C] + """ + device = point_clouds.device + batch_size = point_clouds.shape[0] + view_shape = list(index.shape) + view_shape[1:] = [1] * (len(view_shape) - 1) + repeat_shape = list(index.shape) + repeat_shape[0] = 1 + batch_indices = torch.arange(batch_size, dtype=torch.long, device=device).view(view_shape).repeat(repeat_shape) + new_points = point_clouds[batch_indices, index, :] + return new_points + + +def knn(x, k): + """ + K nearest neighborhood. + Parameters + ---------- + x: a tensor with size of (B, C, N) + k: the number of nearest neighborhoods + + Returns + ------- + idx: indices of the k nearest neighborhoods with size of (B, N, k) + """ + inner = -2 * torch.matmul(x.transpose(2, 1), x) # (B, N, N) + xx = torch.sum(x ** 2, dim=1, keepdim=True) # (B, 1, N) + pairwise_distance = -xx - inner - xx.transpose(2, 1) # (B, 1, N), (B, N, N), (B, N, 1) -> (B, N, N) + + idx = pairwise_distance.topk(k=k, dim=-1)[1] # (B, N, k) + return idx + + +class GraphLayer(nn.Module): + """ + Graph layer. + in_channel: it depends on the input of this network. + out_channel: given by ourselves. + """ + def __init__(self, in_channel, out_channel, k=16): + super(GraphLayer, self).__init__() + self.k = k + self.conv = nn.Conv1d(in_channel, out_channel, 1) + self.bn = nn.BatchNorm1d(out_channel) + + def forward(self, x): + """ + Parameters + ---------- + x: tensor with size of (B, C, N) + """ + # KNN + knn_idx = knn(x, k=self.k) # (B, N, k) + knn_x = index_points(x.permute(0, 2, 1), knn_idx) # (B, N, k, C) + + # Local Max Pooling + x = torch.max(knn_x, dim=2)[0].permute(0, 2, 1) # (B, N, C) + + # Feature Map + x = F.relu(self.bn(self.conv(x))) + return x + + +class Encoder(nn.Module): + """ + Graph based encoder. + """ + def __init__(self, **kwargs): + super(Encoder, self).__init__() + + self.conv1 = nn.Conv1d(12, 64, 1) + self.conv2 = nn.Conv1d(64, 64, 1) + self.conv3 = nn.Conv1d(64, 64, 1) + + self.bn1 = nn.BatchNorm1d(64) + self.bn2 = nn.BatchNorm1d(64) + self.bn3 = nn.BatchNorm1d(64) + + self.graph_layer1 = GraphLayer(in_channel=64, out_channel=128, k=16) + self.graph_layer2 = GraphLayer(in_channel=128, out_channel=1024, k=16) + + self.conv4 = nn.Conv1d(1024, 512, 1) + self.bn4 = nn.BatchNorm1d(512) + self.conv5 = nn.Conv1d(512, 2,1) + + def forward(self, x): + b, c, n = x.size() + + # get the covariances, reshape and concatenate with x + knn_idx = knn(x, k=16) + knn_x = index_points(x.permute(0, 2, 1), knn_idx) # (B, N, 16, 3) + mean = torch.mean(knn_x, dim=2, keepdim=True) + knn_x = knn_x - mean + covariances = torch.matmul(knn_x.transpose(2, 3), knn_x).view(b, n, -1).permute(0, 2, 1) + x = torch.cat([x, covariances], dim=1) # (B, 12, N) + + # three layer MLP + x = F.relu(self.bn1(self.conv1(x))) + x = F.relu(self.bn2(self.conv2(x))) + x = F.relu(self.bn3(self.conv3(x))) + + + # two consecutive graph layers + x = self.graph_layer1(x) + x = self.graph_layer2(x) + + x = self.bn4(self.conv4(x)) + + x = torch.max(x, dim=-1)[0].unsqueeze(-1) + + x = self.conv5(x) + return x + + +class FoldingLayer(nn.Module): + """ + The folding operation of FoldingNet + """ + + def __init__(self, in_channel: int, out_channels: list): + super(FoldingLayer, self).__init__() + + layers = [] + for oc in out_channels[:-1]: + conv = nn.Conv1d(in_channel, oc, 3,1,1) + bn = nn.BatchNorm1d(oc) + active = nn.ReLU(inplace=True) + layers.extend([conv, bn, active]) + in_channel = oc + out_layer = nn.Conv1d(in_channel, out_channels[-1], 3,1,1) + layers.append(out_layer) + + self.layers = nn.Sequential(*layers) + + def forward(self, *args): + """ + Parameters + ---------- + grids: reshaped 2D grids or intermediam reconstructed point clouds + """ + # concatenate + #try: + # x = torch.cat([*args], dim=1) + #except: + # for arg in args: + # print(arg.shape) + # raise + x = torch.cat([*args], dim=1) + # shared mlp + x = self.layers(x) + + return x + +class Decoder_Layer(nn.Module): + """ + Decoder Module of FoldingNet + """ + + def __init__(self, in_points, out_points, in_channel, out_channel,**kwargs): + super(Decoder_Layer, self).__init__() + + # Sample the grids in 2D space + #xx = np.linspace(-0.3, 0.3, 45, dtype=np.float32) + #yy = np.linspace(-0.3, 0.3, 45, dtype=np.float32) + #self.grid = np.meshgrid(xx, yy) # (2, 45, 45) + self.out_points = out_points + self.grid = torch.linspace(-0.5, 0.5, out_points).view(1,-1) + # reshape + #self.grid = torch.Tensor(self.grid).view(2, -1) # (2, 45, 45) -> (2, 45 * 45) + assert out_points%in_points==0 + self.m = out_points//in_points + + self.fold1 = FoldingLayer(in_channel + 1, [512, 512, out_channel]) + self.fold2 = FoldingLayer(in_channel + out_channel+1, [512, 512, out_channel]) + + def forward(self, x): + """ + x: (B, C) + """ + batch_size = x.shape[0] + + # repeat grid for batch operation + grid = self.grid.to(x.device) # (2, 45 * 45) + grid = grid.unsqueeze(0).repeat(batch_size, 1, 1) # (B, 2, 45 * 45) + + # repeat codewords + x = x.repeat_interleave(self.m, dim=-1) # (B, 512, 45 * 45) + + # two folding operations + recon1 = self.fold1(grid,x) + recon2 = recon1+self.fold2(grid,x, recon1) + + return recon2 +class Decoder(nn.Module): + """ + Decoder Module of FoldingNet + """ + + def __init__(self, out_points, in_channel=2, **kwargs): + super(Decoder, self).__init__() + + # Sample the grids in 2D space + #xx = np.linspace(-0.3, 0.3, 45, dtype=np.float32) + #yy = np.linspace(-0.3, 0.3, 45, dtype=np.float32) + #self.grid = np.meshgrid(xx, yy) # (2, 45, 45) + start_out = (out_points//128) +1 + + + self.out_points = out_points + + self.layer1 = Decoder_Layer(1, start_out, in_channel,3*128) + self.layer2 = Decoder_Layer(start_out, start_out*8, 3*128, 3*16) + self.layer3 = Decoder_Layer(start_out*8, start_out*32, 3*16, 3*4) + self.layer4 = Decoder_Layer(start_out*32,start_out*128,3*4, 3) + + def forward(self, x): + """ + x: (B, C) + """ + x = x.view(-1, 2, 1) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + return x + + +class AutoEncoder(nn.Module): + def __init__(self, *args, **kwargs): + super().__init__() + + self.encoder = Encoder(*args, **kwargs) + self.decoder = Decoder(*args, **kwargs) + + def encode(self, x): + return self.encoder(x) + + def decode(self, x): + return self.decoder(x) + + def forward(self, x): + x = self.encoder(x) + x = self.decoder(x) + return x + + +if __name__=='__main__': + print('Nothing to see here') diff --git a/build/lib/molearn/models/leaky_autoencoder.py b/build/lib/molearn/models/leaky_autoencoder.py new file mode 100644 index 0000000..ca561dc --- /dev/null +++ b/build/lib/molearn/models/leaky_autoencoder.py @@ -0,0 +1,178 @@ +# Copyright (c) 2022 Samuel C. Musson +# +# Molightning is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# Molightning is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molightning ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +import torch +from torch import nn +import torch.nn.utils.spectral_norm as spectral_norm + + + +class _SingleResidualBlock(nn.Module): + def __init__(self, f, sn=False, gn=False, num_groups=8, bias=False): + super().__init__() + conv_block = [ + spectral_norm(nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f) if gn else nn.BatchNorm1d(f), + nn.LeakyReLU(inplace=True)] + self.conv_block = nn.Sequential(*conv_block) + + def forward(self, x): + return x + self.conv_block(x) + +class _ResidualBlock(nn.Module): + def __init__(self, f, hidden_channels=None, sn=False, gn=False, num_groups=8, bias=False): + if hidden_channels == None: + f2 = f + else: + f2 = hidden_channels + super().__init__() + conv_block = [ + spectral_norm(nn.Conv1d(f,f2, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f2, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f2) if gn else nn.BatchNorm1d(f2), + nn.LeakyReLU(inplace=True), + spectral_norm(nn.Conv1d(f2,f, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f) if gn else nn.BatchNorm1d(f), + nn.LeakyReLU(inplace=True)] + self.conv_block = nn.Sequential(*conv_block) + def forward(self, x): + return x + self.conv_block(x) + + +class _BottleneckResidualBlock(nn.Module): + def __init__(self, f,): + pass + +Residual_Block_Modules = {'SingleResidualBlock':_SingleResidualBlock, + 'ResidualBlock':_ResidualBlock, + 'BottleneckResidualBlock': _BottleneckResidualBlock + } +class ToLatentDimensions(nn.Module): + def __init__(self, latent_size=2): + super().__init__() + self.latent_size = latent_size + + def forward(self, x): + z = torch.nn.functional.adaptive_avg_pool2d(x, output_size=(self.latent_size,1)) + z = torch.sigmoid(z) + return z + +class FromLatentDimensions(nn.Module): + def __init__(self, latent_size=2, init_n=26, sn=False): + super().__init__() + self.f = spectral_norm(nn.Linear(latent_size, latent_size*init_n)) if sn else nn.Linear(latent_size, latent_size*init_n) + self.latent_size = latent_size + self.init_n=init_n + + def forward(self, x): + x = x.view(x.size(0), self.latent_size) + x = self.f(x) + x = x.view(x.size(0), self.latent_size, self.init_n) + return x + +class Encoder(nn.Module): + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False, starting_dimensions=3,residual_block='SingleResidualBlock'): + super().__init__() + ResidualBlock = Residual_Block_Modules[residual_block] + + sn = use_spectral_norm # rename for brevity + # encoder block + eb = nn.ModuleList() + eb.append(spectral_norm(nn.Conv1d(starting_dimensions, init_z, 4, 2, 1, bias=bias)) if sn else nn.Conv1d(3, init_z, 4, 2, 1, bias=bias)) + eb.append(nn.GroupNorm(num_groups, init_z) if use_group_norm else nn.BatchNorm1d(init_z)) + eb.append(nn.LeakyReLU(inplace=True)) + for j in range(r): + eb.append(ResidualBlock(int(init_z), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + for i in range(depth): + eb.append(spectral_norm(nn.Conv1d(int(init_z*m**i), int(init_z*m**(i+1)), 4, 2, 1, bias=bias)) if sn else \ + nn.Conv1d(int(init_z*m**i), int(init_z*m**(i+1)), 4, 2, 1, bias=bias)) + eb.append(nn.GroupNorm(num_groups, int(init_z*m**(i+1))) if use_group_norm else nn.BatchNorm1d(int(init_z*m**(i+1)))) + eb.append(nn.LeakyReLU(inplace=True)) + for j in range(r): + eb.append(ResidualBlock(int(init_z*m**(i+1)), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups)) + eb.append(spectral_norm(nn.Conv1d(int(init_z*m**depth), latent_z, 4, 2, 1, bias=bias)) if sn else \ + nn.Conv1d(int(init_z*m**depth), latent_z, 4, 2, 1, bias=bias)) + for j in range(r): + eb.append(ResidualBlock(latent_z, sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups)) + eb.append(ToLatentDimensions(latent_size=latent_z)) + self.model = nn.Sequential(*eb) + + def forward(self, x): + #expect input of shape [B,3,N] + return self.model(x) + +class Decoder(nn.Module): + + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False, last_dimensions=3, residual_block='SingleResidualBlock'): + super().__init__() + ResidualBlock = Residual_Block_Modules[residual_block] + sn = use_spectral_norm # rename for brevity + self.latent_z = latent_z + # decoder block + db = nn.ModuleList() + db.append(FromLatentDimensions(latent_size=latent_z, init_n=init_n, sn=use_spectral_norm)) + db.append(spectral_norm(nn.ConvTranspose1d(latent_z, int(init_z*m**(depth)), 4, 2, 1, bias=bias)) if sn else \ + nn.ConvTranspose1d(latent_z, int(init_z*m**(depth)), 4, 2, 1, bias=bias)) + db.append(nn.GroupNorm(num_groups, int(init_z*m**(depth))) if use_group_norm else nn.BatchNorm1d(int(init_z*m**(depth)))) + db.append(nn.LeakyReLU(inplace=True)) + for j in range(r): + db.append(ResidualBlock(int(init_z*m**(depth)), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + for i in reversed(range(depth)): + db.append(spectral_norm(nn.ConvTranspose1d(int(init_z*m**(i+1)), int(init_z*m**i), 4, 2, 1, bias=bias)) if sn else \ + nn.ConvTranspose1d(int(init_z*m**(i+1)), int(init_z*m**i), 4, 2, 1, bias=bias)) + db.append(nn.GroupNorm(num_groups, int(init_z*m**i)) if use_group_norm else nn.BatchNorm1d(int(init_z*m**i))) + db.append(nn.LeakyReLU(inplace=True)) + for j in range(r): + db.append(ResidualBlock(int(init_z*m**i), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + db.append(spectral_norm(nn.ConvTranspose1d(int(init_z), 3, 4, 2, 1)) if sn else \ + nn.ConvTranspose1d(int(init_z), 3, 4, 2, 1)) + for j in range(r): + db.append(ResidualBlock(last_dimensions, sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + self.model = nn.Sequential(*db) + + def forward(self, x): + #in lightning, forward defines the prediction/inference actions + #expect input of shape [B,Latent_size,1] + return self.model(x.view(x.size(0), self.latent_z,1)) + +class Autoencoder(nn.Module): + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False): + super().__init__() + self.encoder = Encoder(init_z, latent_z, depth, m, r, use_spectral_norm, use_group_norm, num_groups, init_n,bias=bias) + self.decoder = Decoder(init_z, latent_z, depth, m, r, use_spectral_norm, use_group_norm, num_groups, init_n,bias=bias) + + def forward(self, x): + return self.decoder(self.encoder(x))[:,:,:x.shape[2]] + + def encode(self, x): + return self.encoder(x) + def decode(self, x): + return self.decoder(x) + +class NewAutoencoder(nn.Module): + def __init__(self, kwargs=None, encoder_kwargs=None, decoder_kwargs=None): + super().__init__() + self.encoder = Encoder(**dict(kwargs, **encoder_kwargs)) + self.decoder = Decoder(**dict(kwargs, **decoder_kwargs)) + + def forward(self, x): + return self.decoder(self.encoder(x))[:,:,:x.shape[2]] + + def encode(self, x): + return self.encoder(x) + def decode(self, x): + return self.decoder(x) + + diff --git a/build/lib/molearn/models/old_autoencoder.py b/build/lib/molearn/models/old_autoencoder.py new file mode 100644 index 0000000..e23122a --- /dev/null +++ b/build/lib/molearn/models/old_autoencoder.py @@ -0,0 +1,216 @@ +# Copyright (c) 2022 Samuel C. Musson +# +# Molightning is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# Molightning is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molightning ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +import torch +from torch import nn +import torch.nn.utils.spectral_norm as spectral_norm + + + +class _SingleResidualBlock(nn.Module): + def __init__(self, f, sn=False, gn=False, num_groups=8, bias=False): + super().__init__() + conv_block = [ + spectral_norm(nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f) if gn else nn.BatchNorm1d(f), + nn.ReLU(inplace=True)] + self.conv_block = nn.Sequential(*conv_block) + + def forward(self, x): + return x + self.conv_block(x) + +class _ResidualBlock(nn.Module): + def __init__(self, f, hidden_channels=None, sn=False, gn=False, num_groups=8, bias=False): + if hidden_channels == None: + f2 = f + else: + f2 = hidden_channels + super().__init__() + conv_block = [ + spectral_norm(nn.Conv1d(f,f2, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f2, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f2) if gn else nn.BatchNorm1d(f2), + nn.ReLU(inplace=True), + spectral_norm(nn.Conv1d(f2,f, 3, stride=1, padding=1, bias=bias)) if sn else \ + nn.Conv1d(f,f, 3, stride=1, padding=1, bias=bias), + nn.GroupNorm(num_groups,f) if gn else nn.BatchNorm1d(f), + nn.ReLU(inplace=True)] + self.conv_block = nn.Sequential(*conv_block) + def forward(self, x): + return x + self.conv_block(x) + + +class _BottleneckResidualBlock(nn.Module): + def __init__(self, f,): + pass + +Residual_Block_Modules = {'SingleResidualBlock':_SingleResidualBlock, + 'ResidualBlock':_ResidualBlock, + 'BottleneckResidualBlock': _BottleneckResidualBlock + } +class ToLatentDimensions(nn.Module): + def __init__(self, latent_size=2): + super().__init__() + self.latent_size = latent_size + + def forward(self, x): + z = torch.nn.functional.adaptive_avg_pool2d(x, output_size=(self.latent_size,1)) + z = torch.sigmoid(z) + return z + +class FromLatentDimensions(nn.Module): + def __init__(self, latent_size=2, init_n=26, sn=False): + super().__init__() + self.f = spectral_norm(nn.Linear(latent_size, latent_size*init_n)) if sn else nn.Linear(latent_size, latent_size*init_n) + self.latent_size = latent_size + self.init_n=init_n + + def forward(self, x): + x = x.view(x.size(0), self.latent_size) + x = self.f(x) + x = x.view(x.size(0), self.latent_size, self.init_n) + return x + +class Encoder(nn.Module): + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False, starting_dimensions=3,residual_block='SingleResidualBlock'): + super().__init__() + ResidualBlock = Residual_Block_Modules[residual_block] + + sn = use_spectral_norm # rename for brevity + # encoder block + eb = nn.ModuleList() + eb.append(spectral_norm(nn.Conv1d(starting_dimensions, init_z, 4, 2, 1, bias=bias)) if sn else nn.Conv1d(3, init_z, 4, 2, 1, bias=bias)) + eb.append(nn.GroupNorm(num_groups, init_z) if use_group_norm else nn.BatchNorm1d(init_z)) + eb.append(nn.ReLU(inplace=True)) + for j in range(r): + eb.append(ResidualBlock(int(init_z), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + for i in range(depth): + eb.append(spectral_norm(nn.Conv1d(int(init_z*m**i), int(init_z*m**(i+1)), 4, 2, 1, bias=bias)) if sn else \ + nn.Conv1d(int(init_z*m**i), int(init_z*m**(i+1)), 4, 2, 1, bias=bias)) + eb.append(nn.GroupNorm(num_groups, int(init_z*m**(i+1))) if use_group_norm else nn.BatchNorm1d(int(init_z*m**(i+1)))) + eb.append(nn.ReLU(inplace=True)) + for j in range(r): + eb.append(ResidualBlock(int(init_z*m**(i+1)), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups)) + eb.append(spectral_norm(nn.Conv1d(int(init_z*m**depth), latent_z, 4, 2, 1, bias=bias)) if sn else \ + nn.Conv1d(int(init_z*m**depth), latent_z, 4, 2, 1, bias=bias)) + for j in range(r): + eb.append(ResidualBlock(latent_z, sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups)) + eb.append(ToLatentDimensions(latent_size=latent_z)) + self.model = nn.Sequential(*eb) + + def forward(self, x): + #expect input of shape [B,3,N] + return self.model(x) + +class Decoder(nn.Module): + + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False, last_dimensions=3, residual_block='SingleResidualBlock'): + super().__init__() + ResidualBlock = Residual_Block_Modules[residual_block] + sn = use_spectral_norm # rename for brevity + self.latent_z = latent_z + # decoder block + db = nn.ModuleList() + db.append(FromLatentDimensions(latent_size=latent_z, init_n=init_n, sn=use_spectral_norm)) + db.append(spectral_norm(nn.ConvTranspose1d(latent_z, int(init_z*m**(depth)), 4, 2, 1, bias=bias)) if sn else \ + nn.ConvTranspose1d(latent_z, int(init_z*m**(depth)), 4, 2, 1, bias=bias)) + db.append(nn.GroupNorm(num_groups, int(init_z*m**(depth))) if use_group_norm else nn.BatchNorm1d(int(init_z*m**(depth)))) + db.append(nn.ReLU(inplace=True)) + for j in range(r): + db.append(ResidualBlock(int(init_z*m**(depth)), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + for i in reversed(range(depth)): + db.append(spectral_norm(nn.ConvTranspose1d(int(init_z*m**(i+1)), int(init_z*m**i), 4, 2, 1, bias=bias)) if sn else \ + nn.ConvTranspose1d(int(init_z*m**(i+1)), int(init_z*m**i), 4, 2, 1, bias=bias)) + db.append(nn.GroupNorm(num_groups, int(init_z*m**i)) if use_group_norm else nn.BatchNorm1d(int(init_z*m**i))) + db.append(nn.ReLU(inplace=True)) + for j in range(r): + db.append(ResidualBlock(int(init_z*m**i), sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + db.append(spectral_norm(nn.ConvTranspose1d(int(init_z), 3, 4, 2, 1)) if sn else \ + nn.ConvTranspose1d(int(init_z), 3, 4, 2, 1)) + for j in range(r): + db.append(ResidualBlock(last_dimensions, sn=use_spectral_norm, gn=use_group_norm, num_groups=num_groups,bias=bias)) + self.model = nn.Sequential(*db) + + def forward(self, x): + #in lightning, forward defines the prediction/inference actions + #expect input of shape [B,Latent_size,1] + return self.model(x.view(x.size(0), self.latent_z,1)) + +class Autoencoder(nn.Module): + def __init__(self, init_z=32, latent_z=2, depth=4, m=2.0, r=2, use_spectral_norm=False,use_group_norm=False, num_groups=8, + init_n=26, bias=False, residual_block = 'SingleResidualBlock'): + super().__init__() + self.encoder = Encoder(init_z, latent_z, depth, m, r, use_spectral_norm, use_group_norm, num_groups, init_n,bias=bias,residual_block=residual_block) + self.decoder = Decoder(init_z, latent_z, depth, m, r, use_spectral_norm, use_group_norm, num_groups, init_n,bias=bias, residual_block=residual_block) + + def forward(self, x): + return self.decoder(self.encoder(x))[:,:,:x.shape[2]] + + def encode(self, x): + return self.encoder(x) + def decode(self, x): + return self.decoder(x) + +class NewAutoencoder(nn.Module): + def __init__(self, kwargs=None, encoder_kwargs=None, decoder_kwargs=None): + super().__init__() + self.encoder = Encoder(**dict(kwargs, **encoder_kwargs)) + self.decoder = Decoder(**dict(kwargs, **decoder_kwargs)) + + def forward(self, x): + return self.decoder(self.encoder(x))[:,:,:x.shape[2]] + + def encode(self, x): + return self.encoder(x) + def decode(self, x): + return self.decoder(x) + + +class ToDimension(nn.Module): + def __init__(self, in_dimension, out_dimension, n_hidden=1, hidden_width=128, bias=False, **kwargs): + super().__init__() + modulelist = nn.ModuleList() + modulelist.append(spectral_norm(nn.Linear(in_dimension, hidden_width, bias=bias))) + modulelist.append(nn.BatchNorm1d(hidden_width)) + modulelist.append(nn.ReLU()) + for i in range(n_hidden): + modulelist.append(spectral_norm(nn.Linear(hidden_width, hidden_width, bias=bias))) + modulelist.append(nn.BatchNorm1d(hidden_width)) + modulelist.append(nn.ReLU()) + modulelist.append(spectral_norm(nn.Linear(hidden_width, out_dimension))) + modulelist.append(nn.BatchNorm1d(out_dimension)) + + #modulelist.append(self.sigmoid(output)) + self.model = nn.Sequential(*modulelist) + + def forward(self, x): + x = x.view(x.size(0), -1) + x = self.model(x) + x = x.view(x.size(0),-1,1) + return x + + + +class AutoencoderFullyConnectedTo2D(Autoencoder): + def __init__(self, **kwargs): + super().__init__(**kwargs) + #out shape is [B, N, 1] + self.to2d = ToDimension(in_dimension = kwargs['latent_z'], out_dimension=2, **kwargs) + self.from2d = ToDimension(in_dimension=2, out_dimension=kwargs['latent_z'], **kwargs) + + def encode2d(self,x): + return self.to2d(x) + + def decode2d(self, x): + return self.from2d(x) + diff --git a/build/lib/molearn/scoring/__init__.py b/build/lib/molearn/scoring/__init__.py new file mode 100644 index 0000000..d39f1ee --- /dev/null +++ b/build/lib/molearn/scoring/__init__.py @@ -0,0 +1,15 @@ +""" +Scoring holds the classes for calculating DOPE, Ramachandran and OPENMM energy +""" +try: + from .dope_score import Parallel_DOPE_Score, DOPE_Score +except ImportError as e: + import warnings + warnings.warn(f"{e}. Modeller is probably not installed.") + + +try: + from .ramachandran_score import Parallel_Ramachandran_Score, Ramachandran_Score +except Exception as e: + import warnings + warnings.warn(f"{e}. Will not be able to calculate Ramachandran score.") \ No newline at end of file diff --git a/build/lib/molearn/scoring/dope_score.py b/build/lib/molearn/scoring/dope_score.py new file mode 100644 index 0000000..e34bb0c --- /dev/null +++ b/build/lib/molearn/scoring/dope_score.py @@ -0,0 +1,137 @@ +import numpy as np +from copy import deepcopy + +from ..utils import ShutUp, cpu_count, random_string + +import modeller +from modeller import * +from modeller.scripts import complete_pdb +from modeller.optimizers import ConjugateGradients + +from multiprocessing import Pool, Event, get_context +import os + +class DOPE_Score: + + def __init__(self, mol): + + #set residues names with protonated histidines back to generic HIS name (needed by DOPE score function) + testH = mol.data["resname"].values + testH[testH == "HIE"] = "HIS" + testH[testH == "HID"] = "HIS" + _mol = deepcopy(mol) + _mol.data["resname"] = testH + + alternate_residue_names = dict(CSS=('CYX',)) + atoms = ' '.join(list(_mol.data['name'].unique())) + #tmp_file = f'tmp{np.random.randint(1e10)}.pdb' + tmp_file = f'tmp{random_string()}.pdb' + _mol.write_pdb(tmp_file, conformations=[0]) + log.level(0,0,0,0,0) + env = environ() + env.libs.topology.read(file='$(LIB)/top_heav.lib') + env.libs.parameters.read(file='$(LIB)/par.lib') + self.fast_mdl = complete_pdb(env, tmp_file) + self.fast_fs = selection(self.fast_mdl.chains[0]) + self.fast_ss = self.fast_fs.only_atom_types(atoms) + atom_residue = _mol.get_data(columns=['name', 'resname', 'resid']) + atom_order = [] + first_index = next(iter(self.fast_ss)).residue.index + offset = atom_residue[0,2]-first_index + for i, j in enumerate(self.fast_ss): + if i < len(atom_residue): + for j_residue_name in alternate_residue_names.get(j.residue.name, (j.residue.name,)): + if [j.name, j_residue_name, j.residue.index+offset] == list(atom_residue[i]): + atom_order.append(i) + else: + where_arg = (atom_residue==(np.array([j.name, j_residue_name, j.residue.index+offset], dtype=object))).all(axis=1) + where = np.where(where_arg)[0] + atom_order.append(int(where)) + self.fast_atom_order = atom_order + # check fast dope atoms + for i,j in enumerate(self.fast_ss): + if i 0: + processes = min(processes, cpu_count()) + else: + processes = cpu_count() + + self.mol = deepcopy(mol) + score = DOPE_Score + ctx = get_context('spawn') + self.pool = ctx.Pool(processes=processes, initializer=set_global_score, + initargs=(score, dict(mol=mol)), + **kwargs, + ) + self.process_function = process_dope + + def __reduce__(self): + return (self.__class__, (self.mol,)) + + def get_score(self, coords, **kwargs): + ''' + :param coords: # shape (1, N, 3) numpy array + ''' + #is copy necessary? + return self.pool.apply_async(self.process_function, (coords.copy(), kwargs)) + diff --git a/build/lib/molearn/scoring/ramachandran_score.py b/build/lib/molearn/scoring/ramachandran_score.py new file mode 100644 index 0000000..99758e8 --- /dev/null +++ b/build/lib/molearn/scoring/ramachandran_score.py @@ -0,0 +1,99 @@ +import numpy as np +from copy import deepcopy +from multiprocessing import Pool, Event, get_context +from scipy.spatial.distance import cdist + +from iotbx.data_manager import DataManager +from mmtbx.validation.ramalyze import ramalyze +from scitbx.array_family import flex + +from ..utils import cpu_count, random_string +import os + +class Ramachandran_Score(): + def __init__(self, mol, threshold=1e-3): + tmp_file = f'rama_tmp{random_string()}.pdb' + mol.write_pdb(tmp_file)#'rama_tmp.pdb') + filename = tmp_file#'rama_tmp.pdb' + self.mol = mol + self.dm = DataManager(datatypes = ['model']) + self.dm.process_model_file(filename) + self.model = self.dm.get_model(filename) + self.score = ramalyze(self.model.get_hierarchy()) # get score to see if this works + self.shape = self.model.get_sites_cart().as_numpy_array().shape + + #tests + x = self.mol.coordinates[0] + m = self.model.get_sites_cart().as_numpy_array() + assert m.shape == x.shape + self.idxs = np.where(cdist(m, x)threshold)) + os.remove(tmp_file) + + def get_score(self, coords, as_ratio = False): + ''' + Given coords (corresponding to self.mol) will calculate Ramachandran scores using cctbux ramalyze module + :param coords: numpy array (shape (N, 3)) + :returns: (favored, allowed, outliers, total) + + ''' + assert coords.shape == self.shape + self.model.set_sites_cart(flex.vec3_double(coords[self.idxs].astype(np.double))) + self.score = ramalyze(self.model.get_hierarchy()) + nf = self.score.n_favored + na = self.score.n_allowed + no = self.score.n_outliers + nt = self.score.n_total + if as_ratio: + return nf/nt, na/nt, no/nt + else: + return nf, na, no, nt + + + +def set_global_score(score, kwargs): + ''' + make score a global variable + ''' + global worker_ramachandran_score + worker_ramachandran_score = score(**kwargs)#mol = mol, data_dir=data_dir, **kwargs) + +def process_ramachandran(coords, kwargs): + ''' + ramachandran worker + ''' + return worker_ramachandran_score.get_score(coords,**kwargs) + +class Parallel_Ramachandran_Score(): + + def __init__(self, mol, processes=-1): + + # set a number of processes as user desires, capped on number of CPUs + if processes > 0: + processes = min(processes, cpu_count()) + else: + processes = cpu_count() + + self.mol = deepcopy(mol) + score = Ramachandran_Score + ctx = get_context('spawn') + + self.pool = ctx.Pool(processes=processes, initializer=set_global_score, + initargs=(score, dict(mol=mol)), + ) + self.process_function = process_ramachandran + + def __reduce__(self): + return (self.__class__, (self.mol,)) + + + def get_score(self, coords,**kwargs): + ''' + :param coords: # shape (1, N, 3) numpy array + ''' + #is copy necessary? + return self.pool.apply_async(self.process_function, (coords.copy(), kwargs)) + + + diff --git a/build/lib/molearn/trainers/__init__.py b/build/lib/molearn/trainers/__init__.py new file mode 100644 index 0000000..cd332c5 --- /dev/null +++ b/build/lib/molearn/trainers/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2022 Samuel C. Musson +# +# Molearn is free software ; +# you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; +# either version 2 of the License, or (at your option) any later version. +# Molightning is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with molightning ; +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +""" +trainers holds classes for training networks +""" + +from .molearn_trainer import * +from .sinkhorn_trainer import * diff --git a/build/lib/molearn/trainers/molearn_trainer.py b/build/lib/molearn/trainers/molearn_trainer.py new file mode 100644 index 0000000..036fc96 --- /dev/null +++ b/build/lib/molearn/trainers/molearn_trainer.py @@ -0,0 +1,356 @@ +import sys +import os +import glob +import shutil +import numpy as np +import time +import torch +import biobox as bb +import csv +import molearn +from molearn.models.foldingnet import AutoEncoder as Net +from molearn.loss_functions import TorchProteinEnergy +from molearn.data import PDBData +from molearn.loss_functions import openmm_energy +import warnings +from decimal import Decimal +import json + +class TrainingFailure(Exception): + pass + +class Molearn_Trainer(): + def __init__(self, device = None, log_filename = 'log_file.dat'): + if not device: + self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') + else: + self.device = device + print(f'device: {self.device}') + self.best = None + self.best_name = None + self.epoch = 0 + self.scheduler = None + self.verbose = True + self.log_filename = 'default_log_filename.json' + self.scheduler_key = None + + def get_network_summary(self,): + def get_parameters(trainable_only, model): + return sum(p.numel() for p in model.parameters() if (p.requires_grad and trainable_only)) + + return dict( + encoder_trainable = get_parameters(True, self.autoencoder.encoder), + encoder_total = get_parameters(False, self.autoencoder.encoder), + decoder_trainable = get_parameters(True, self.autoencoder.decoder), + decoder_total = get_parameters(False, self.autoencoder.decoder), + autoencoder_trainable = get_parameters(True, self.autoencoder), + autoencoder_total = get_parameters(False, self.autoencoder), + ) + + def set_autoencoder(self, autoencoder, **kwargs): + self.autoencoder = autoencoder(**kwargs).to(self.device) + self._autoencoder_kwargs = kwargs + + def set_dataloader(self, train_dataloader=None, valid_dataloader=None): + if train_dataloader is not None: + self.train_dataloader = train_dataloader + if valid_dataloader is not None: + self.valid_dataloader = valid_dataloader + + def set_data(self, data, **kwargs): + if isinstance(data, PDBData): + self.set_dataloader(*data.get_dataloader(**kwargs)) + else: + raise NotImplementedError('Have not implemented this method to use any data other than PDBData yet') + self.std = data.std + self.mean = data.mean + self.mol = data.mol + self._data = data + + + def prepare_optimiser(self, lr = 1e-3, weight_decay = 0.0001, **optimiser_kwargs): + self.optimiser = torch.optim.AdamW(self.autoencoder.parameters(), lr=lr, weight_decay = weight_decay, **optimiser_kwargs) + + def log(self, log_dict, verbose=None): + dump = json.dumps(log_dict) + if verbose or self.verbose: + print(dump) + with open(self.log_filename, 'a') as f: + f.write(dump+'\n') + + def scheduler_step(self, logs): + pass + + def run(self, max_epochs=1600, log_filename = None, log_folder=None, checkpoint_frequency=1, checkpoint_folder='checkpoints', allow_n_failures=10, verbose=None): + if log_filename is not None: + self.log_filename = log_filename + if log_folder is not None: + if not os.path.exists(log_folder): + os.mkdir(log_folder) + self.log_filename = log_folder+'/'+self.log_filename + if verbose is not None: + self.verbose = verbose + + for attempt in range(allow_n_failures): + try: + for epoch in range(self.epoch, max_epochs): + time1 = time.time() + logs = self.train_epoch(epoch) + time2 = time.time() + with torch.no_grad(): + logs.update(self.valid_epoch(epoch)) + time3 = time.time() + self.scheduler_step(logs) + if self.best is None or self.best > logs['valid_loss']: + self.checkpoint(epoch, logs, checkpoint_folder) + elif epoch%checkpoint_frequency==0: + self.checkpoint(epoch, logs, checkpoint_folder) + time4 = time.time() + logs.update(epoch = epoch, + train_seconds=time2-time1, + valid_seconds=time3-time2, + checkpoint_seconds= time4-time3, + total_seconds=time4-time1) + self.log(logs) + if np.isnan(logs['valid_loss']) or np.isnan(logs['train_loss']): + raise TrainingFailure('nan received, failing') + self.epoch+= 1 + except TrainingFailure: + if attempt==(allow_n_failures-1): + failure_message = f'Training Failure due to Nan in attempt {attempt}, end now/n' + self.log({'Failure':failure_message}) + raise TrainingFailure('nan received, failing') + failure_message = f'Training Failure due to Nan in attempt {attempt}, try again from best/n' + self.log({'Failure':failure_message}) + if hasattr(self, 'best'): + self.load_checkpoint('best', checkpoint_folder) + else: + break + + + def train_epoch(self,epoch): + self.autoencoder.train() + N = 0 + results = {} + for i, batch in enumerate(self.train_dataloader): + batch = batch[0].to(self.device) + self.optimiser.zero_grad() + train_result = self.train_step(batch) + train_result['loss'].backward() + self.optimiser.step() + if i == 0: + results = {key:value.item()*len(batch) for key, value in train_result.items()} + else: + for key in train_result.keys(): + results[key] += train_result[key].item()*len(batch) + N+=len(batch) + return {f'train_{key}': results[key]/N for key in results.keys()} + + def train_step(self, batch): + results = self.common_step(batch) + results['loss'] = results['mse_loss'] + return results + + def common_step(self, batch): + self._internal = {} + encoded = self.autoencoder.encode(batch) + self._internal['encoded'] = encoded + decoded = self.autoencoder.decode(encoded)[:,:,:batch.size(2)] + self._internal['decoded'] = decoded + return dict(mse_loss = ((batch-decoded)**2).mean()) + + + def valid_epoch(self,epoch): + self.autoencoder.eval() + N = 0 + results = {} + for i, batch in enumerate(self.valid_dataloader): + batch = batch[0].to(self.device) + valid_result = self.valid_step(batch) + if i == 0: + results = {key:value.item()*len(batch) for key, value in valid_result.items()} + else: + for key in valid_result.keys(): + results[key] += valid_result[key].item()*len(batch) + N+=len(batch) + return {f'valid_{key}': results[key]/N for key in results.keys()} + + def valid_step(self, batch): + results = self.common_step(batch) + results['loss'] = results['mse_loss'] + return results + + def learning_rate_sweep(self, max_lr=100, min_lr=1e-5, number_of_iterations=1000, checkpoint_folder='checkpoint_sweep',train_on='mse_loss', save=['loss', 'mse_loss']): + self.autoencoder.train() + def cycle(iterable): + while True: + for i in iterable: + yield i + init_loss = 0.0 + values = [] + data = iter(cycle(self.train_dataloader)) + for i in range(number_of_iterations): + lr = min_lr*((max_lr/min_lr)**(i/number_of_iterations)) + self.update_optimiser_hyperparameters(lr=lr) + batch = next(data)[0].to(self.device).float() + + self.optimiser.zero_grad() + result = self.train_step(batch) + #result['loss']/=len(batch) + result[train_on].backward() + self.optimiser.step() + values.append((lr,)+tuple((result[name].item() for name in save))) + #print(i,lr, result['loss'].item()) + if i==0: + init_loss = result[train_on].item() + #if result[train_on].item()>1e6*init_loss: + # break + values = np.array(values) + print('min value ', values[np.nanargmin(values[:,1])]) + return values + + def update_optimiser_hyperparameters(self, **kwargs): + for g in self.optimiser.param_groups: + for key, value in kwargs.items(): + g[key] = value + + def checkpoint(self, epoch, valid_logs, checkpoint_folder, loss_key='valid_loss'): + valid_loss = valid_logs[loss_key] + if not os.path.exists(checkpoint_folder): + os.mkdir(checkpoint_folder) + torch.save({'epoch':epoch, + 'model_state_dict': self.autoencoder.state_dict(), + 'optimizer_state_dict': self.optimiser.state_dict(), + 'loss': valid_loss, + 'network_kwargs': self._autoencoder_kwargs, + 'atoms': self._data.atoms, + 'std': self.std, + 'mean': self.mean}, + f'{checkpoint_folder}/last.ckpt') + + if self.best is None or self.best > valid_loss: + filename = f'{checkpoint_folder}/checkpoint_epoch{epoch}_loss{valid_loss}.ckpt' + shutil.copyfile(f'{checkpoint_folder}/last.ckpt', filename) + if self.best is not None: + os.remove(self.best_name) + self.best_name = filename + self.best_epoch = epoch + self.best = valid_loss + + def load_checkpoint(self, checkpoint_name, checkpoint_folder, load_optimiser=True): + if checkpoint_name=='best': + if self.best_name is not None: + _name = self.best_name + else: + ckpts = glob.glob(checkpoint_folder+'/checkpoint_*') + indexs = [x.rfind('loss') for x in ckpts] + losses = [float(x[y+4:-5]) for x,y in zip(ckpts, indexs)] + _name = ckpts[np.argmin(losses)] + elif checkpoint_name =='last': + _name = f'{checkpoint_folder}/last.ckpt' + else: + _name = f'{checkpoint_folder}/{checkpoint_name}' + checkpoint = torch.load(_name) + if not hasattr(self, 'autoencoder'): + raise NotImplementedError('self.autoencoder does not exist, I have no way of knowing what network you want to load checkoint weights into yet, please set the network first') + + self.autoencoder.load_state_dict(checkpoint['model_state_dict']) + if load_optimiser: + if not hasattr(self, 'optimiser'): + raise NotImplementedError('self.optimiser does not exist, I have no way of knowing what optimiser you previously used, please set it first.') + self.optimiser.load_state_dict(checkpoint['optimizer_state_dict']) + epoch = checkpoint['epoch'] + self.epoch = epoch+1 + + +class Molearn_Physics_Trainer(Molearn_Trainer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def prepare_physics(self, physics_scaling_factor=0.1): + self.psf = physics_scaling_factor + self.physics_loss = TorchProteinEnergy(self._data.dataset[0]*self.std, pdb_atom_names = self._data.get_atominfo(), device = self.device, method = 'roll') + + def common_physics_step(self, batch, latent, mse_loss): + alpha = torch.rand(int(len(batch)//2), 1, 1).type_as(latent) + latent_interpolated = (1-alpha)*latent[:-1:2] + alpha*latent[1::2] + generated = self.autoencoder.decode(latent_interpolated)[:,:,:batch.size(2)] + bond, angle, torsion = self.physics_loss._roll_bond_angle_torsion_loss(generated*self.std) + n = len(generated) + bond/=n + angle/=n + torsion/=n + _all = torch.tensor([bond, angle, torsion]) + _all[_all.isinf()]=1e35 + total_physics = _all.nansum() + #total_physics = torch.nansum(torch.tensor([bond ,angle ,torsion])) + + return {'physics_loss':total_physics, 'bond_energy':bond, 'angle_energy':angle, 'torsion_energy':torsion} + + + def train_step(self, batch): + results = self.common_step(batch) + results.update(self.common_physics_step(batch, self._internal['encoded'], results['mse_loss'])) + with torch.no_grad(): + scale = self.psf*results['mse_loss']/(results['physics_loss']+1e-5) + final_loss = results['mse_loss']+scale*results['physics_loss'] + results['loss'] = final_loss + return results + + def valid_step(self, batch): + results = self.common_step(batch) + results.update(self.common_physics_step(batch, self._internal['encoded'], results['mse_loss'])) + scale = self.psf*results['mse_loss']/(results['physics_loss']+1e-5) + final_loss = results['mse_loss']+scale*results['physics_loss'] + results['loss'] = final_loss + return results + +class OpenMM_Physics_Trainer(Molearn_Trainer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def prepare_physics(self, physics_scaling_factor=0.1, clamp_threshold = 10000, clamp=False, start_physics_at=0, **kwargs): + self.start_physics_at = start_physics_at + self.psf = physics_scaling_factor + if clamp: + clamp_kwargs = dict(max=clamp_threshold, min = -clamp_threshold) + else: + clamp_kwargs = None + self.physics_loss = openmm_energy(self.mol, self.std, clamp=clamp_kwargs, platform = 'CUDA' if self.device == torch.device('cuda') else 'Reference', atoms = self._data.atoms, **kwargs) + + + def common_physics_step(self, batch, latent): + alpha = torch.rand(int(len(batch)//2), 1, 1).type_as(latent) + latent_interpolated = (1-alpha)*latent[:-1:2] + alpha*latent[1::2] + + generated = self.autoencoder.decode(latent_interpolated)[:,:,:batch.size(2)] + self._internal['generated'] = generated + energy = self.physics_loss(generated) + energy[energy.isinf()]=1e35 + energy = torch.clamp(energy, max=1e34) + energy = energy.nanmean() + + return {'physics_loss':energy}#a if not energy.isinf() else torch.tensor(0.0)} + + def train_step(self, batch): + results = self.common_step(batch) + results.update(self.common_physics_step(batch, self._internal['encoded'])) + with torch.no_grad(): + scale = (self.psf*results['mse_loss'])/(results['physics_loss'] +1e-5) + final_loss = results['mse_loss']+scale*results['physics_loss'] + results['loss'] = final_loss + return results + + def valid_step(self, batch): + results = self.common_step(batch) + results.update(self.common_physics_step(batch, self._internal['encoded'])) + scale = (self.psf*results['mse_loss'])/(results['physics_loss'] +1e-5) + final_loss = results['mse_loss']+scale*results['physics_loss'] + results['loss'] = final_loss + return results + + + + +if __name__=='__main__': + pass diff --git a/build/lib/molearn/trainers/sinkhorn_trainer.py b/build/lib/molearn/trainers/sinkhorn_trainer.py new file mode 100644 index 0000000..82042b2 --- /dev/null +++ b/build/lib/molearn/trainers/sinkhorn_trainer.py @@ -0,0 +1,264 @@ +import sys +import os +import glob +import numpy as np +import torch +from molearn.loss_functions import openmm_energy +from molearn.data import PDBData +import json +import biobox as bb +from time import time +try: + from geomloss import SamplesLoss +except ImportError as e: + import warnings + warnings.warn(f'{e}. Will not be able to use sinkhorn because geomloss is not installed.') + +import shutil +from copy import deepcopy + +class TrainingFailure(Exception): + pass +class Sinkhorn_Trainer(): + def __init__(self, device = None, latent_dim=2, log_filename = 'default_log_file.dat'): + if not device: + self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') + else: + self.device = device + print(f'device: {self.device}') + self.best = None + self.best_checkpoint_filename = None + self.step = 0 + self.verbose = True + self.log_filename = log_filename + self.latent_dim = latent_dim + self.sinkhorn = SamplesLoss(loss = 'sinkhorn', p=2, blur=0.05) + self.save_time = time() + + + def prepare_physics(self, physics_scaling_factor=0.1, clamp_threshold = 10000, clamp=False, start_physics_at=0, **kwargs): + self.start_physics_at = start_physics_at + self.psf = physics_scaling_factor + if clamp: + clamp_kwargs = dict(max=clamp_threshold, min = -clamp_threshold) + else: + clamp_kwargs = None + self.physics_loss = openmm_energy(self.mol, self.std, clamp=clamp_kwargs, platform = 'CUDA' if self.device == torch.device('cuda') else 'Reference', atoms = self._data.atoms, **kwargs) + + def get_network_summary(self,): + def get_parameters(trainable_only, model): + return sum(p.numel() for p in model.parameters() if (p.requires_grad and trainable_only)) + + return dict( + decoder_trainable = get_parameters(True, self.decoder), + decoder_total = get_parameters(False, self.decoder), + ) + + + def set_data(self, data,*args, **kwargs): + if isinstance(data, PDBData): + train_data, valid_data = data.get_datasets(*args, **kwargs) + self.train_data, self.valid_data = train_data.to(self.device), valid_data.to(self.device) + else: + raise NotImplementedError('Have not implemented this method to use any data other than PDBData yet') + self.std = data.std + self.mean = data.mean + self.mol = data.mol + self._data = data + + def set_dataloader(self, data, *args, **kwargs): + if isinstance(data, PDBData): + train_dataloader, valid_dataloader = data.get_dataloader(*args, **kwargs) + def cycle(iterable): + while True: + for x in iterable: + yield x + self.train_iterator = iter(cycle(train_dataloader)) + self.valid_iterator = iter(cycle(valid_dataloader)) + else: + raise NotImplementedError('Have not implemented this method to use any data other than PDBData yet') + self.std = data.std + self.mean = data.mean + self.mol = data.mol + self._data = data + + + def get_adam_opt(self, *args, **kwargs): + self.opt = torch.optim.AdamW(self.decoder.parameters(), *args, **kwargs) + + def log(self, log_dict, verbose=None): + dump = json.dumps(log_dict) + if verbose or self.verbose: + print(dump) + with open(self.log_filename, 'a') as f: + f.write(dump+'\n') + + + def run(self, steps=100, validate_every=10, log_filename = None, checkpoint_frequency=1, checkpoint_folder='checkpoints', verbose=None): + if log_filename is not None: + self.log_filename = log_filename + if verbose is not None: + self.verbose = verbose + start_step = self.step + finish_step = start_step+steps + number_of_validations = 0 + while self.step valid_logs['valid_loss']: + self.checkpoint(valid_logs, checkpoint_folder) + elif number_of_validations%checkpoint_frequency==0: + self.checkpoint(valid_logs, checkpoint_folder) + time4 = time() + logs = {'step':self.step, **train_logs, **valid_logs, + 'Memory': memory, + 'train_seconds':time2-time1, + 'valid_seconds':time3-time2, + 'checkpoint_seconds': time4-time3, + 'total_seconds':time4-time1} + self.log(logs) + if np.isnan(logs['valid_loss']) or np.isnan(logs['train_loss']): + raise TrainingFailure('nan received, failing') + + def training_n_steps(self, steps): + self.decoder.train() + results = {} + for i in range(steps): + self.opt.zero_grad() + train_result = self.train_step() + train_result['loss'].backward() + self.opt.step() + self.step+=1 + if i == 0: + results = {key:value.item() for key, value in train_result.items()} + else: + for key in train_result.keys(): + results[key] += train_result[key].item() + return {f'train_{key}': results[key]/steps for key in results.keys()} + + + + def validation_one_step(self): + self.decoder.eval() + result = self.valid_step() + return {f'valid_{key}': result[key].item() for key in result.keys()} + + def train_step(self): + data = self.train_data + z = torch.randn(data.shape[0], self.latent_dim,1).to(self.device) + structures = self.decoder(z)[:,:,:data.shape[2]] + loss = self.sinkhorn(structures.reshape(structures.size(0),-1), data.reshape(data.size(0),-1)) + return dict(loss=loss) + + def valid_step(self): + with torch.no_grad(): + data = self.valid_data + z = torch.randn(data.shape[0], self.latent_dim).to(self.device) + structures = self.decoder(z)[:,:,:data.shape[2]] + loss = self.sinkhorn(structures.reshape(structures.size(0),-1), data.reshape(data.size(0),-1)) + energy = self.physics_loss(structures) + energy[energy.isinf()]=1e35 + energy = torch.clamp(energy, max=1e34) + energy = energy.nanmean() + + z_0 = torch.zeros_like(z).requires_grad_() + structures_0 = self.decoder(z_0)[:,:,:data.shape[2]] + inner_loss = ((structures_0-data)**2).sum(1).mean() + encoded = -torch.autograd.grad(inner_loss, [z_0], create_graph=True, retain_graph=True)[0] + with torch.no_grad(): + structures_encoded = self.decoder(encoded)[:,:,:data.shape[2]] + se = (structures_encoded-data)**2 + mse_loss = se.mean() + rmsd = se.sum(1).mean().sqrt()*self.std + if time()-self.save_time>120.: + coords = [] + with torch.no_grad(): + z1_index, z2_index = self.get_extrema() + z1 = encoded[z1_index].unsqueeze(0) + z2 = encoded[z2_index].unsqueeze(0) + frames = 20 + ts = torch.linspace(0,1,frames).to(self.device).unsqueeze(-1) + #from IPython import embed + #embed(headre='valid') + zinterp =(1-ts)*z1 + ts*z2 + if zinterp.shape == (2,frames): + zinterp = zinterp.permute(1,0) + interp_structures = self.decoder(zinterp)[:,:,:data.shape[2]] + mol = deepcopy(self.mol) + mol.coordinates = (interp_structures.permute(0,2,1)*self.std).detach().cpu().numpy() + mol.write_pdb('sample_interp.pdb') + self.save_time = time() + + + return dict(loss=loss, physics_loss=energy,mse_loss=mse_loss, rmsd=rmsd) + + + + def update_optimiser_hyperparameters(self, **kwargs): + for g in self.opt.param_groups: + for key, value in kwargs.items(): + g[key] = value + + def checkpoint(self, valid_logs, checkpoint_folder, loss_key='valid_loss'): + valid_loss = valid_logs[loss_key] + if not os.path.exists(checkpoint_folder): + os.mkdir(checkpoint_folder) + torch.save({'step':self.step, + 'model_state_dict': self.decoder.state_dict(), + 'optimizer_state_dict': self.opt.state_dict(), + 'loss': valid_loss, + 'network_kwargs': self._decoder_kwargs, + 'atoms': self._data.atoms, + 'std': self.std, + 'mean': self.mean}, + f'{checkpoint_folder}/last.ckpt') + + if self.best is None or self.best > valid_loss: + filename = f'{checkpoint_folder}/checkpoint_step{self.step}_loss{valid_loss}.ckpt' + shutil.copyfile(f'{checkpoint_folder}/last.ckpt', filename) + if self.best is not None: + os.remove(self.best_checkpoint_filename) + self.best_checkpoint_filename = filename + self.best_step = self.step + self.best = valid_loss + + def load_checkpoint(self, checkpoint_name, checkpoint_folder, load_optimiser=True): + if checkpoint_name=='best': + if self.best_checkpoint_filename is not None: + _name = self.best_checkpoint_filename + else: + ckpts = glob.glob(checkpoint_folder+'/checkpoint_step*') + indexs = [x.rfind('loss') for x in ckpts] + losses = [float(x[y+4:-5]) for x,y in zip(ckpts, indexs)] + _name = ckpts[np.argmin(losses)] + elif checkpoint_name =='last': + _name = f'{checkpoint_folder}/last.ckpt' + else: + _name = f'{checkpoint_folder}/{checkpoint_name}' + checkpoint = torch.load(_name) + if not hasattr(self, 'decoder'): + self.get_network(decoder_kwargs=checkpoint['network_kwargs']) + + self.decoder.load_state_dict(checkpoint['model_state_dict']) + if load_optimiser: + if not hasattr(self, 'opt'): + self.get_adam_opt(dict(lr=1e-3)) + self.opt.load_state_dict(checkpoint['optimizer_state_dict']) + step = checkpoint['step'] + self.step = step + + def get_extrema(self, ): + #self.train_data [B, 3, N] + if hasattr(self, '_extrema'): + return self._extrema + a = self.valid_data + B = a.shape[0] + with torch.no_grad(): + m = ((a.repeat_interleave(B,dim=0)-a.repeat(B,1,1))**2).sum(1).mean(-1).argmax() + self._extrema = (m//B, m%B) + return self._extrema diff --git a/build/lib/molearn/utils.py b/build/lib/molearn/utils.py new file mode 100644 index 0000000..1e9feae --- /dev/null +++ b/build/lib/molearn/utils.py @@ -0,0 +1,54 @@ +import os, sys +import numpy as np +import torch +import random +import string + +def random_string(length=32): + ''' + generate a random string of arbitrary characters. Useful to generate temporary file names. + + :param length: length of random string + ''' + return ''.join([random.choice(string.ascii_letters) + for n in range(length)]) + + +def as_numpy(tensor): + if isinstance(tensor, torch.Tensor): + return tensor.data.cpu().numpy() + elif isinstance(tensor, np.ndarray): + return tensor + else: + return np.array(tensor) + + +def cpu_count(): + """ detect the number of available CPU """ + if hasattr(os, "sysconf"): + if "SC_NPROCESSORS_ONLN" in list(os.sysconf_names): + # Linux & Unix + ncpus = os.sysconf("SC_NPROCESSORS_ONLN") + if isinstance(ncpus, int) and ncpus > 0: + return ncpus + else: + # OSX + return int(os.popen2("sysctl -n hw.ncpu")[1].read()) + + # Windows + if "NUMBER_OF_PROCESSORS" in list(os.environ): + ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]); + if ncpus > 0: + return ncpus + + return 1 + + +class ShutUp(object): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + + def __exit__(self, *args): + sys.stdout.close() + sys.stdout = self._stdout \ No newline at end of file diff --git a/dist/molearn-1.0.0-py3.9.egg b/dist/molearn-1.0.0-py3.9.egg new file mode 100644 index 0000000000000000000000000000000000000000..6c070a8c91ffd557ec75e2cf8a057aa618de77ac GIT binary patch literal 133161 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 literal 0 HcmV?d00001 diff --git a/docs/build/.buildinfo b/docs/build/.buildinfo index 0e86985..607acb2 100644 --- a/docs/build/.buildinfo +++ b/docs/build/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: fd34726b2b2ff5441545b10d48d8c981 +config: 3856312510fb3627aa0e121350e09621 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/.doctrees/FAQ.doctree b/docs/build/.doctrees/FAQ.doctree new file mode 100644 index 0000000000000000000000000000000000000000..e88271476dc93984794cf827fe7987aa758f308b GIT binary patch literal 9984 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 JES@;={{bEXZc6|F literal 112832 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 diff --git a/docs/build/.doctrees/data.doctree b/docs/build/.doctrees/data.doctree index cb3babe68e5d026b489c8fbd436422d1c9d47caa..61ddebbcf180088f253d73e17dadb09bfd06609e 100644 GIT binary patch literal 40386 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 diff --git a/docs/build/.doctrees/environment.pickle b/docs/build/.doctrees/environment.pickle index ed1bb0071b9bf2436924b1c97f1e2a5e5f740eb5..c2fbdf21b06ac788dec708f7f255c343a981da25 100644 GIT binary patch delta 24542 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 DSUI|e literal 861286 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
  • modules |
  • - + @@ -40,9 +43,9 @@

    All modules for which code is available

  • molearn.loss_functions.openmm_thread
  • molearn.models.CNN_autoencoder
  • molearn.models.foldingnet
  • -
  • molearn.scoring.dope_score
  • -
  • molearn.scoring.ramachandran_score
  • -
  • molearn.trainers.openmm_physics_trainer
  • +
  • molearn.scoring
  • +
  • molearn.trainers.openmm_physics_trainer
  • molearn.trainers.torch_physics_trainer
  • molearn.trainers.trainer
  • @@ -76,13 +79,13 @@

    Navigation

  • modules |
  • - + \ No newline at end of file diff --git a/docs/build/_modules/molearn/analysis/GUI.html b/docs/build/_modules/molearn/analysis/GUI.html index e51c40e..fddc48b 100644 --- a/docs/build/_modules/molearn/analysis/GUI.html +++ b/docs/build/_modules/molearn/analysis/GUI.html @@ -1,15 +1,18 @@ + - + - molearn.analysis.GUI — molearn 2.0.1 documentation - - - - - + molearn.analysis.GUI — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -68,9 +71,7 @@

    Source code for molearn.analysis.GUI

     from ..utils import as_numpy
     
     
    -
    -[docs] -class MolearnGUI: +
    [docs]class MolearnGUI: ''' This class produces an interactive visualisation for data stored in a :func:`MolearnAnalysis <molearn.analysis.MolearnAnalysis>` object, @@ -574,7 +575,6 @@

    Source code for molearn.analysis.GUI

     
             display.clear_output(wait=True)
             display.display(self.scene)
    -
    @@ -606,14 +606,14 @@

    Navigation

  • modules |
  • - + \ No newline at end of file diff --git a/docs/build/_modules/molearn/analysis/analyser.html b/docs/build/_modules/molearn/analysis/analyser.html index 6236727..1e31a69 100644 --- a/docs/build/_modules/molearn/analysis/analyser.html +++ b/docs/build/_modules/molearn/analysis/analyser.html @@ -1,15 +1,18 @@ + - + - molearn.analysis.analyser — molearn 2.0.1 documentation - - - - - + molearn.analysis.analyser — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -75,9 +78,7 @@

    Source code for molearn.analysis.analyser

     warnings.filterwarnings("ignore")
     
     
    -
    -[docs] -class MolearnAnalysis: +
    [docs]class MolearnAnalysis: ''' This class provides methods dedicated to the quality analysis of a trained model. @@ -91,9 +92,7 @@

    Source code for molearn.analysis.analyser

             self.batch_size = 1
             self.processes = 1
     
    -
    -[docs] - def set_network(self, network): +
    [docs] def set_network(self, network): ''' :param network: a trained neural network defined in :func:`molearn.models <molearn.models>` ''' @@ -101,19 +100,13 @@

    Source code for molearn.analysis.analyser

             self.network.eval()
             self.device = next(network.parameters()).device
    - -
    -[docs] - def get_dataset(self, key): +
    [docs] def get_dataset(self, key): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` ''' return self._datasets[key]
    - -
    -[docs] - def set_dataset(self, key, data, atomselect="*"): +
    [docs] def set_dataset(self, key, data, atomselect="*"): ''' :param data: :func:`PDBData <molearn.data.PDBData>` object containing atomic coordinates :param str key: label to be associated with data @@ -143,10 +136,7 @@

    Source code for molearn.analysis.analyser

             if not hasattr(self, 'shape'):
                 self.shape = (_data.dataset.shape[1], _data.dataset.shape[2])
    - -
    -[docs] - def get_encoded(self, key): +
    [docs] def get_encoded(self, key): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` :return: array containing the encoding in latent space of dataset associated with key @@ -167,19 +157,13 @@

    Source code for molearn.analysis.analyser

                     
             return self._encoded[key]
    - -
    -[docs] - def set_encoded(self, key, coords): +
    [docs] def set_encoded(self, key, coords): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` ''' self._encoded[key] = torch.tensor(coords).float()
    - -
    -[docs] - def get_decoded(self, key): +
    [docs] def get_decoded(self, key): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` ''' @@ -193,28 +177,19 @@

    Source code for molearn.analysis.analyser

                     self._decoded[key] = decoded
             return self._decoded[key]
    - -
    -[docs] - def set_decoded(self, key, structures): +
    [docs] def set_decoded(self, key, structures): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` ''' self._decoded[key] = structures
    - -
    -[docs] - def num_trainable_params(self): +
    [docs] def num_trainable_params(self): ''' :return: number of trainable parameters in the neural network previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_network>` ''' return sum(p.numel() for p in self.network.parameters() if p.requires_grad)
    - -
    -[docs] - def get_error(self, key, align=True): +
    [docs] def get_error(self, key, align=True): ''' Calculate the reconstruction error of a dataset encoded and decoded by a trained neural network. @@ -244,10 +219,7 @@

    Source code for molearn.analysis.analyser

     
             return np.array(err)
    - -
    -[docs] - def get_dope(self, key, refine=True, **kwargs): +
    [docs] def get_dope(self, key, refine=True, **kwargs): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` :param bool refine: if True, refine structures before calculating DOPE score @@ -262,10 +234,7 @@

    Source code for molearn.analysis.analyser

             return dict(dataset_dope=dope_dataset, 
                         decoded_dope=dope_decoded)
    - -
    -[docs] - def get_ramachandran(self, key): +
    [docs] def get_ramachandran(self, key): ''' :param str key: key pointing to a dataset previously loaded with :func:`set_dataset <molearn.analysis.MolearnAnalysis.set_dataset>` ''' @@ -277,10 +246,7 @@

    Source code for molearn.analysis.analyser

             ramachandran.update({f'decoded_{key}':value for key, value in self.get_all_ramachandran_score(decoded).items()})
             return ramachandran
    - -
    -[docs] - def setup_grid(self, samples=64, bounds_from=None, bounds=None, padding=0.1): +
    [docs] def setup_grid(self, samples=64, bounds_from=None, bounds=None, padding=0.1): ''' Define a NxN point grid regularly sampling the latent space. @@ -308,7 +274,6 @@

    Source code for molearn.analysis.analyser

             
             return key
    - def _get_bounds(self, bounds_from, exclude=['grid', 'grid_decoded']): ''' :param bounds_from: keys of datasets to be considered for identification of boundaries in latent space @@ -335,9 +300,7 @@

    Source code for molearn.analysis.analyser

             xmax, ymax = max(xmax), max(ymax)
             return xmin, xmax, ymin, ymax
     
    -
    -[docs] - def scan_error_from_target(self, key, index=None, align=True): +
    [docs] def scan_error_from_target(self, key, index=None, align=True): ''' Calculate landscape of RMSD vs single target structure. Target should be previously loaded datset containing a single conformation. @@ -373,10 +336,7 @@

    Source code for molearn.analysis.analyser

                 
             return self.surfaces[s_key], self.xvals, self.yvals
    - -
    -[docs] - def scan_error(self, s_key='Network_RMSD', z_key='Network_z_drift'): +
    [docs] def scan_error(self, s_key='Network_RMSD', z_key='Network_z_drift'): ''' Calculate RMSD and z-drift on a grid sampling the latent space. Requires a grid system to be defined via a prior call to :func:`set_dataset <molearn.analysis.MolearnAnalysis.setup_grid>`. @@ -407,7 +367,6 @@

    Source code for molearn.analysis.analyser

                 
             return self.surfaces[s_key], self.surfaces[z_key], self.xvals, self.yvals
    - def _ramachandran_score(self, frame): ''' returns multiprocessing AsyncResult @@ -447,9 +406,7 @@

    Source code for molearn.analysis.analyser

     
             return self.dope_score_class.get_score(f*self.stdval, refine=refine, **kwargs)
     
    -
    -[docs] - def get_all_ramachandran_score(self, tensor): +
    [docs] def get_all_ramachandran_score(self, tensor): ''' Calculate Ramachandran score of an ensemble of atomic conrdinates. @@ -467,10 +424,7 @@

    Source code for molearn.analysis.analyser

                 rama['total'].append(total)
             return {key:np.array(value) for key, value in rama.items()}       
    - -
    -[docs] - def get_all_dope_score(self, tensor, refine=True): +
    [docs] def get_all_dope_score(self, tensor, refine=True): ''' Calculate DOPE score of an ensemble of atom coordinates. @@ -483,10 +437,7 @@

    Source code for molearn.analysis.analyser

             results = np.array([r.get() for r in tqdm(results, desc='Calc Dope')])
             return results
    - -
    -[docs] - def reference_dope_score(self, frame): +
    [docs] def reference_dope_score(self, frame): ''' :param numpy.array frame: array with shape [1, N, 3] with Cartesian coordinates of atoms :return: DOPE score @@ -501,10 +452,7 @@

    Source code for molearn.analysis.analyser

             score = atmsel.assess_dope()
             return score
    - -
    -[docs] - def scan_dope(self, key=None, refine=True, **kwargs): +
    [docs] def scan_dope(self, key=None, refine=True, **kwargs): ''' Calculate DOPE score on a grid sampling the latent space. Requires a grid system to be defined via a prior call to :func:`set_dataset <molearn.analysis.MolearnAnalysis.setup_grid>`. @@ -535,10 +483,7 @@

    Source code for molearn.analysis.analyser

                 
             return self.surfaces[key], self.xvals, self.yvals
    - -
    -[docs] - def scan_ramachandran(self): +
    [docs] def scan_ramachandran(self): ''' Calculate Ramachandran scores on a grid sampling the latent space. Requires a grid system to be defined via a prior call to :func:`set_dataset <molearn.analysis.MolearnAnalysis.setup_grid>`. @@ -557,11 +502,8 @@

    Source code for molearn.analysis.analyser

                     self.surfaces[keys[key]] = value
     
             return self.surfaces['Ramachandran_favored'], self.xvals, self.yvals
    - -
    -[docs] - def scan_custom(self, fct, params, key): +
    [docs] def scan_custom(self, fct, params, key): ''' Generate a surface coloured as a function of a user-defined function. @@ -581,10 +523,7 @@

    Source code for molearn.analysis.analyser

             
             return self.surfaces[key], self.xvals, self.yvals
    - -
    -[docs] - def generate(self, crd): +
    [docs] def generate(self, crd): ''' Generate a collection of protein conformations, given coordinates in the latent space. @@ -598,10 +537,8 @@

    Source code for molearn.analysis.analyser

     
             return s*self.stdval + self.meanval
    - def __getstate__(self): return {key:value for key, value in dict(self.__dict__).items() if key not in ['dope_score_class', 'ramachandran_score_class']}
    -
    @@ -633,14 +570,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/analysis/path.html b/docs/build/_modules/molearn/analysis/path.html index 2516674..fc13e94 100644 --- a/docs/build/_modules/molearn/analysis/path.html +++ b/docs/build/_modules/molearn/analysis/path.html @@ -1,15 +1,18 @@ + - + - molearn.analysis.path — molearn 2.0.1 documentation - - - - - + molearn.analysis.path — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -191,9 +194,7 @@

    Source code for molearn.analysis.path

         return came_from, cost_so_far
     
     
    -
    -[docs] -def get_path(idx_start, idx_end, landscape, xvals, yvals, smooth=3): +
    [docs]def get_path(idx_start, idx_end, landscape, xvals, yvals, smooth=3): ''' Find shortest path between two points on a weighted grid @@ -245,7 +246,6 @@

    Source code for molearn.analysis.path

             return traj_smooth, np.array(score)[::-1]
    - def _get_point_index(crd, xvals, yvals): ''' Extract index (of 2D surface) closest to a given real value coordinate @@ -261,9 +261,7 @@

    Source code for molearn.analysis.path

         return np.array([my_x, my_y])
     
     
    -
    -[docs] -def get_path_aggregate(crd, landscape, xvals, yvals, input_is_index=False): +
    [docs]def get_path_aggregate(crd, landscape, xvals, yvals, input_is_index=False): ''' Create a chain of shortest paths via give waypoints @@ -295,10 +293,7 @@

    Source code for molearn.analysis.path

         return crd
    - -
    -[docs] -def oversample(crd, pts=10): +
    [docs]def oversample(crd, pts=10): ''' Add extra equally spaced points between a list of points. @@ -316,7 +311,6 @@

    Source code for molearn.analysis.path

                 pts.append(newpt)
     
         return np.array(pts)
    -
    @@ -348,14 +342,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/data/pdb_data.html b/docs/build/_modules/molearn/data/pdb_data.html index 1e1a70d..cc838b7 100644 --- a/docs/build/_modules/molearn/data/pdb_data.html +++ b/docs/build/_modules/molearn/data/pdb_data.html @@ -1,15 +1,18 @@ + - + - molearn.data.pdb_data — molearn 2.0.1 documentation - - - - - + molearn.data.pdb_data — molearn 2.0.4 documentation + + + + + + + @@ -19,10 +22,7 @@

    Navigation

  • index
  • -
  • - modules |
  • - + @@ -40,9 +40,7 @@

    Source code for molearn.data.pdb_data

     import biobox as bb
     
     
    -
    -[docs] -class PDBData: +
    [docs]class PDBData: def __init__(self, filename=None, fix_terminal=False, atoms=None): ''' @@ -63,9 +61,7 @@

    Source code for molearn.data.pdb_data

             if atoms is not None:
                 self.atomselect(atoms=atoms)
     
    -
    -[docs] - def import_pdb(self, filename): +
    [docs] def import_pdb(self, filename): ''' Load multiPDB file. This command can be called multiple times to load many datasets, if these feature the same number of atoms @@ -79,10 +75,7 @@

    Source code for molearn.data.pdb_data

                 self.filename = []
             self.filename.append(filename)
    - -
    -[docs] - def fix_terminal(self): +
    [docs] def fix_terminal(self): ''' Rename OT1 N-terminal Oxygen to O if terminal oxygens are named OT1 and OT2 otherwise no oxygen will be selected during an atomselect using atoms = ['CA', 'C','N','O','CB']. No template will be found for terminal residue in openmm_loss. Alternative solution is to use atoms = ['CA', 'C', 'N', 'O', 'CB', 'OT1']. instead. ''' @@ -91,10 +84,7 @@

    Source code for molearn.data.pdb_data

             if len(ot1)!=0 and len(ot2)!=0:
                 self._mol.data.loc[ot1,'name']='O'
    - -
    -[docs] - def atomselect(self, atoms, ignore_atoms=[]): +
    [docs] def atomselect(self, atoms, ignore_atoms=[]): ''' From all imported PDBs, extract only atoms of interest. :func:`import_pdb <molearn.data.PDBData.import_pdb>` must have been called at least once, either at class instantiation or as a separate call. @@ -127,10 +117,7 @@

    Source code for molearn.data.pdb_data

             _, self._idxs = self._mol.atomselect("*", "*", _atoms, get_index=True)
             self._mol = self._mol.get_subset(self._idxs)
    - -
    -[docs] - def prepare_dataset(self): +
    [docs] def prepare_dataset(self): ''' Once all datasets have been loaded, normalise data and convert into `torch.Tensor` (ready for training) ''' @@ -149,10 +136,7 @@

    Source code for molearn.data.pdb_data

             print(f'Dataset.shape: {self.dataset.shape}')
             print(f'mean: {str(self.mean)}, std: {str(self.std)}')
    - -
    -[docs] - def get_atominfo(self): +
    [docs] def get_atominfo(self): ''' generate list of all atoms in dataset, where every line contains [atom name, residue name, resid] ''' @@ -161,10 +145,7 @@

    Source code for molearn.data.pdb_data

                 self.atominfo = self._mol.get_data(columns=['name', 'resname', 'resid'])
             return self.atominfo
    - -
    -[docs] - def frame(self): +
    [docs] def frame(self): ''' return `biobox.Molecule` object with loaded data ''' @@ -177,10 +158,7 @@

    Source code for molearn.data.pdb_data

             M.properties['center'] = M.get_center()
             return deepcopy(M)
    - -
    -[docs] - def get_dataloader(self, batch_size, validation_split=0.1, pin_memory=True, dataset_sample_size=-1, manual_seed=None, shuffle=True, sampler=None): +
    [docs] def get_dataloader(self, batch_size, validation_split=0.1, pin_memory=True, dataset_sample_size=-1, manual_seed=None, shuffle=True, sampler=None): ''' :param batch_size: :param validation_split: @@ -206,11 +184,8 @@

    Source code for molearn.data.pdb_data

                 self.train_dataloader = torch.utils.data.DataLoader(self.train_dataset, batch_size=batch_size, pin_memory=pin_memory, shuffle=True)
             self.valid_dataloader = torch.utils.data.DataLoader(self.valid_dataset, batch_size=batch_size, pin_memory=pin_memory,shuffle=True)
             return self.train_dataloader, self.valid_dataloader
    - -
    -[docs] - def split(self, *args, **kwargs): +
    [docs] def split(self, *args, **kwargs): ''' Split :func:`PDBData <molearn.data.PDBData>` into two other :func:`PDBData <molearn.data.PDBData>` objects corresponding to train and valid sets. @@ -232,10 +207,7 @@

    Source code for molearn.data.pdb_data

             valid.dataset = valid_dataset
             return train, valid
    - -
    -[docs] - def get_datasets(self, validation_split=0.1, valid_size=None, train_size=None, manual_seed=None): +
    [docs] def get_datasets(self, validation_split=0.1, valid_size=None, train_size=None, manual_seed=None): ''' Create a training and validation set from the imported data @@ -268,7 +240,6 @@

    Source code for molearn.data.pdb_data

             valid_dataset = dataset[indices[_train_size:_train_size+_valid_size]]
             return train_dataset, valid_dataset
    - @property def atoms(self): return list(np.unique(self._mol.data["name"].values)) # all the atoms @@ -276,7 +247,6 @@

    Source code for molearn.data.pdb_data

         @property
         def mol(self):
             return self.frame()
    -
    @@ -305,17 +275,14 @@

    Navigation

  • index
  • -
  • - modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/loss_functions/openmm_thread.html b/docs/build/_modules/molearn/loss_functions/openmm_thread.html index ac7c462..a4e429c 100644 --- a/docs/build/_modules/molearn/loss_functions/openmm_thread.html +++ b/docs/build/_modules/molearn/loss_functions/openmm_thread.html @@ -1,15 +1,18 @@ + - + - molearn.loss_functions.openmm_thread — molearn 2.0.1 documentation - - - - - + molearn.loss_functions.openmm_thread — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -36,26 +39,20 @@

    Navigation

    Source code for molearn.loss_functions.openmm_thread

     import os
     
    -try:
    -    from openmm import Platform
    -    from openmm.app import ForceField, PDBFile, Simulation
    -    from openmm.app import element as elem
    -    import openmm
    -    from openmm.app.forcefield import _createResidueSignature
    -    from openmm.app.internal import compiled
    -    from torchexposedintegratorplugin import TorchExposedIntegrator
    -except ImportError as e:
    -    import warnings
    -    warnings.warn(f'{e}. Will not be able to use openmm.')
    +from openmm import Platform
    +from openmm.app import ForceField, PDBFile, Simulation
    +from openmm.app import element as elem
    +import openmm
    +from openmm.app.forcefield import _createResidueSignature
    +from openmm.app.internal import compiled
    +from torchexposedintegratorplugin import TorchExposedIntegrator
         
     import torch
     import numpy as np
     from copy import deepcopy
     
     
    -
    -[docs] -class ModifiedForceField(ForceField): +
    [docs]class ModifiedForceField(ForceField): def __init__(self, *args, alternative_residue_names=None, **kwargs): ''' @@ -130,10 +127,7 @@

    Source code for molearn.loss_functions.openmm_thread

    return [template, matches]
    - -
    -[docs] -class OpenmmPluginScore(): +
    [docs]class OpenmmPluginScore(): ''' This will use the new OpenMM Plugin to calculate forces and energy. The intention is that this will be fast enough to be able to calculate forces and energy during training. N.B.: The current torchintegratorplugin only supports float on GPU and double on CPU. @@ -263,9 +257,7 @@

    Source code for molearn.loss_functions.openmm_thread

    self.forcefield.registerTemplatePatch(name, name+'_leave_only_'+'_'.join(atoms), 0) self.forcefield.registerPatch(patchData) -
    -[docs] - def get_energy(self, pos_ptr, force_ptr, energy_ptr, n_particles, batch_size): +
    [docs] def get_energy(self, pos_ptr, force_ptr, energy_ptr, n_particles, batch_size): ''' :param pos_ptr: tensor.data_ptr() :param force_ptr: tensor.data_ptr() @@ -278,24 +270,17 @@

    Source code for molearn.loss_functions.openmm_thread

    self.integrator.torchMultiStructureE(pos_ptr, force_ptr, energy_ptr, n_particles, batch_size) return True
    - -
    -[docs] - def execute(self, x): +
    [docs] def execute(self, x): ''' :param `torch.Tensor` x: shape [b, N, 3]. dtype=float. device = gpu ''' force = torch.zeros_like(x) energy = torch.zeros(x.shape[0], device=torch.device('cpu'), dtype=torch.double) self.get_energy(x.data_ptr(), force.data_ptr(), energy.data_ptr(), x.shape[1], x.shape[0]) - return force, energy
    -
    + return force, energy
    - -
    -[docs] -class OpenmmTorchEnergyMinimizer(OpenmmPluginScore): +
    [docs]class OpenmmTorchEnergyMinimizer(OpenmmPluginScore): def minimize(self, x, maxIterations=10, threshold=10000): minimized_x = torch.empty_like(x) @@ -318,10 +303,7 @@

    Source code for molearn.loss_functions.openmm_thread

    return minimized_x
    - -
    -[docs] -class OpenMMPluginScoreSoftForceField(OpenmmPluginScore): +
    [docs]class OpenMMPluginScoreSoftForceField(OpenmmPluginScore): def __init__(self, mol=None, platform='CUDA', atoms=['CA','C','N','CB','O']): self.mol = mol @@ -344,14 +326,9 @@

    Source code for molearn.loss_functions.openmm_thread

    print(self.simulation.context.getState(getEnergy=True).getPotentialEnergy()._value)
    +
    [docs]class openmm_energy_function(torch.autograd.Function): -
    -[docs] -class openmm_energy_function(torch.autograd.Function): - -
    -[docs] - @staticmethod +
    [docs] @staticmethod def forward(ctx, plugin, x): ''' :param plugin: OpenmmPluginScore instance @@ -376,25 +353,16 @@

    Source code for molearn.loss_functions.openmm_thread

    energy = energy.float().to(x.device) return energy
    - -
    -[docs] - @staticmethod +
    [docs] @staticmethod def backward(ctx, grad_output): force = ctx.saved_tensors[0] # force shape [B, N, 3] # embed(header='23 openmm_loss_function') - return None, -force*grad_output.view(-1,1,1)
    -
    + return None, -force*grad_output.view(-1,1,1)
    +
    [docs]class openmm_clamped_energy_function(torch.autograd.Function): -
    -[docs] -class openmm_clamped_energy_function(torch.autograd.Function): - -
    -[docs] - @staticmethod +
    [docs] @staticmethod def forward(ctx, plugin, x, clamp): ''' :param plugin: OpenmmPluginScore instance @@ -419,20 +387,13 @@

    Source code for molearn.loss_functions.openmm_thread

    energy = energy.float().to(x.device) return energy
    - -
    -[docs] - @staticmethod +
    [docs] @staticmethod def backward(ctx, grad_output): force = ctx.saved_tensors[0] - return None, -force*grad_output.view(-1, 1, 1), None
    -
    - + return None, -force*grad_output.view(-1, 1, 1), None
    -
    -[docs] -class openmm_energy(torch.nn.Module): +
    [docs]class openmm_energy(torch.nn.Module): def __init__(self, mol, std, clamp=None, **kwargs): super().__init__() @@ -461,7 +422,6 @@

    Source code for molearn.loss_functions.openmm_thread

    _x = (x*self.std).permute(0, 2, 1).contiguous() energy = openmm_clamped_energy_function.apply(self.openmmplugin, _x, self.clamp) return energy
    -
    @@ -493,14 +453,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/models/CNN_autoencoder.html b/docs/build/_modules/molearn/models/CNN_autoencoder.html index bd8b707..2158c15 100644 --- a/docs/build/_modules/molearn/models/CNN_autoencoder.html +++ b/docs/build/_modules/molearn/models/CNN_autoencoder.html @@ -1,15 +1,18 @@ + - + - molearn.models.CNN_autoencoder — molearn 2.0.1 documentation - - - - - + molearn.models.CNN_autoencoder — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -89,9 +92,7 @@

    Source code for molearn.models.CNN_autoencoder

    return x -

    -[docs] -class Autoencoder(nn.Module): +
    [docs]class Autoencoder(nn.Module): ''' This is the autoencoder used in our `Ramaswamy 2021 paper <https://journals.aps.org/prx/abstract/10.1103/PhysRevX.11.011052>`_. It is largely superseded by :func:`molearn.models.foldingnet.AutoEncoder`. @@ -153,7 +154,6 @@

    Source code for molearn.models.CNN_autoencoder

    for m in self.decoder: x = m(x) return x

    -
    @@ -185,14 +185,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/models/foldingnet.html b/docs/build/_modules/molearn/models/foldingnet.html index 9a380a6..a99f12c 100644 --- a/docs/build/_modules/molearn/models/foldingnet.html +++ b/docs/build/_modules/molearn/models/foldingnet.html @@ -1,15 +1,18 @@ + - + - molearn.models.foldingnet — molearn 2.0.1 documentation - - - - - + molearn.models.foldingnet — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -259,7 +262,7 @@

    Source code for molearn.models.foldingnet

             '''
             x: (B, C)
             '''
    -        x = x.view(-1, 2, 1)
    +        x = x.view(-1, self.latent_dimension, 1)
             x = self.layer1(x)
             x = self.layer2(x)
             x = self.layer3(x)
    @@ -268,9 +271,7 @@ 

    Source code for molearn.models.foldingnet

             return x
     
     
    -
    -[docs] -class AutoEncoder(nn.Module): +
    [docs]class AutoEncoder(nn.Module): ''' Autoencoder architecture derived from FoldingNet. ''' @@ -287,14 +288,10 @@

    Source code for molearn.models.foldingnet

         def decode(self, x):
             return self.decoder(x)
     
    -
    -[docs] - def forward(self, x): +
    [docs] def forward(self, x): x = self.encoder(x) x = self.decoder(x) - return x
    -
    - + return x
    if __name__=='__main__': @@ -330,14 +327,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/scoring.html b/docs/build/_modules/molearn/scoring.html new file mode 100644 index 0000000..2817963 --- /dev/null +++ b/docs/build/_modules/molearn/scoring.html @@ -0,0 +1,107 @@ + + + + + + + + molearn.scoring — molearn 2.0.4 documentation + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for molearn.scoring

    +"""
    +`Scoring` holds classes for calculating DOPE and Ramachandran scores.
    +"""
    +class RaiseErrorOnInit:
    +    module = 'unknown module is creating an ImportError'
    +    def __init__(self,*args, **kwargs):
    +        raise ImportError(f'{self.module}. Therefore {self.__class__.__name__} can not be used')
    +try:
    +    from .dope_score import Parallel_DOPE_Score, DOPE_Score
    +except ImportError as e:
    +    import warnings
    +    warnings.warn(f"{e}. Modeller is probably not installed.")
    +    class DOPE_Score(RaiseErrorOnInit):
    +        module = e
    +    class Parallel_DOPE_Score(RaiseErrorOnInit):
    +        module = e
    +
    +try:
    +    from .ramachandran_score import Parallel_Ramachandran_Score, Ramachandran_Score
    +except Exception as e:
    +
    [docs] class Parallel_Ramachandran_Score(RaiseErrorOnInit): + module = e
    +
    [docs] class Ramachandran_Score(RaiseErrorOnInit): + module = e
    + import warnings + warnings.warn(f"{e}. Will not be able to calculate Ramachandran score.") +
    + +
    +
    +
    +
    + +
    +
    + + + + \ No newline at end of file diff --git a/docs/build/_modules/molearn/scoring/dope_score.html b/docs/build/_modules/molearn/scoring/dope_score.html index 7c51821..e4e3b43 100644 --- a/docs/build/_modules/molearn/scoring/dope_score.html +++ b/docs/build/_modules/molearn/scoring/dope_score.html @@ -1,15 +1,18 @@ + - + - molearn.scoring.dope_score — molearn 2.0.1 documentation - - - - - + molearn.scoring.dope_score — molearn 2.0.4 documentation + + + + + + + @@ -22,8 +25,9 @@

    Navigation

  • modules |
  • - - + + +
    @@ -51,9 +55,7 @@

    Source code for molearn.scoring.dope_score

     import os
         
     
    -
    -[docs] -class DOPE_Score: +
    [docs]class DOPE_Score: ''' This class contains methods to calculate dope without saving to save and load PDB files for every structure. Atoms in a biobox coordinate tensor are mapped to the coordinates in the modeller model directly. ''' @@ -111,9 +113,7 @@

    Source code for molearn.scoring.dope_score

             self.cg = ConjugateGradients()
             os.remove(tmp_file)
     
    -
    -[docs] - def get_dope(self, frame, refine=False): +
    [docs] def get_dope(self, frame, refine=False): ''' Get the dope score. Injects coordinates into modeller and uses `mdl.build(build_method='INTERNAL_COORDINATES', initialize_xyz=False)` to reconstruct missing atoms. If a error is thrown by modeller or at any stage, we just return a fixed large value of 1e10. @@ -147,11 +147,8 @@

    Source code for molearn.scoring.dope_score

                 return dope_score
             except Exception:
                 return 1e10
    - -
    -[docs] - def get_all_dope(self, coords, refine=False): +
    [docs] def get_all_dope(self, coords, refine=False): ''' Expect a array of frames. return array of DOPE score value. @@ -176,9 +173,7 @@

    Source code for molearn.scoring.dope_score

                     
                 dope_scores.append(self.fast_fs.assess_dope())
                 
    -        return np.array(dope_scores)
    -
    - + return np.array(dope_scores)
    def set_global_score(score, kwargs): @@ -199,9 +194,7 @@

    Source code for molearn.scoring.dope_score

         return worker_dope_score.get_dope(coords,**kwargs)
     
     
    -
    -[docs] -class Parallel_DOPE_Score: +
    [docs]class Parallel_DOPE_Score: ''' a multiprocessing class to get modeller DOPE scores. A typical use case would looke like:: @@ -239,17 +232,13 @@

    Source code for molearn.scoring.dope_score

         def __reduce__(self):
             return (self.__class__, (self.mol, self.processes))
     
    -
    -[docs] - def get_score(self, coords, **kwargs): +
    [docs] def get_score(self, coords, **kwargs): ''' :param np.array coords: # shape (N, 3) numpy array ''' # is copy necessary? - return self.pool.apply_async(self.process_function, (coords.copy(), kwargs))
    -
    - + return self.pool.apply_async(self.process_function, (coords.copy(), kwargs))
    @@ -281,14 +270,15 @@

    Navigation

  • modules |
  • - + +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/trainers/openmm_physics_trainer.html b/docs/build/_modules/molearn/trainers/openmm_physics_trainer.html index 288c404..c1f8ee1 100644 --- a/docs/build/_modules/molearn/trainers/openmm_physics_trainer.html +++ b/docs/build/_modules/molearn/trainers/openmm_physics_trainer.html @@ -1,15 +1,18 @@ + - + - molearn.trainers.openmm_physics_trainer — molearn 2.0.1 documentation - - - - - + molearn.trainers.openmm_physics_trainer — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -37,11 +40,32 @@

    Source code for molearn.trainers.openmm_physics_trainer

    import torch from molearn.loss_functions import openmm_energy from .trainer import Trainer - - -
    -[docs] -class OpenMM_Physics_Trainer(Trainer): +import os + + +soft_xml_script='''\ +<ForceField> + <Script> +import openmm as mm +nb = mm.CustomNonbondedForce('C/((r/0.2)^4+1)') +nb.addGlobalParameter('C', 1.0) +sys.addForce(nb) +for i in range(sys.getNumParticles()): + nb.addParticle([]) +exclusions = set() +for bond in data.bonds: + exclusions.add((min(bond.atom1, bond.atom2), max(bond.atom1, bond.atom2))) +for angle in data.angles: + exclusions.add((min(angle[0], angle[2]), max(angle[0], angle[2]))) +for a1, a2 in exclusions: + nb.addExclusion(a1, a2) + </Script> +</ForceField> +''' + + + +
    [docs]class OpenMM_Physics_Trainer(Trainer): ''' OpenMM_Physics_Trainer subclasses Trainer and replaces the valid_step and train_step. An extra 'physics_loss' is calculated using OpenMM and the forces are inserted into backwards pass. @@ -50,10 +74,8 @@

    Source code for molearn.trainers.openmm_physics_trainer

    ''' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - -
    -[docs] - def prepare_physics(self, physics_scaling_factor=0.1, clamp_threshold=1e8, clamp=False, start_physics_at=0, **kwargs): + +
    [docs] def prepare_physics(self, physics_scaling_factor=0.1, clamp_threshold=1e8, clamp=False, start_physics_at=0, xml_file = None, soft_NB = True, **kwargs): ''' Create ``self.physics_loss`` object from :func:`loss_functions.openmm_energy <molearn.loss_functions.openmm_energy>` Needs ``self.mol``, ``self.std``, and ``self._data.atoms`` to have been set with :func:`Trainer.set_data<molearn.trainer.Trainer.set_data>` @@ -65,18 +87,26 @@

    Source code for molearn.trainers.openmm_physics_trainer

    :param \*\*kwargs: All aditional kwargs will be passed to :func:`openmm_energy <molearn.loss_functions.openmm_energy>` ''' + if xml_file is None and soft_NB==True: + print('using soft nonbonded forces by default') + from molearn.utils import random_string + tmp_filename = f'soft_nonbonded_{random_string()}.xml' + with open(tmp_filename, 'w') as f: + f.write(soft_xml_script) + xml_file = ['amber14-all.xml', tmp_filename] + kwargs['remove_NB'] = True + elif xml_file is None: + xml_file = ['amber14-all.xml'] self.start_physics_at = start_physics_at self.psf = physics_scaling_factor if clamp: clamp_kwargs = dict(max=clamp_threshold, min=-clamp_threshold) else: clamp_kwargs = None - self.physics_loss = openmm_energy(self.mol, self.std, clamp=clamp_kwargs, platform='CUDA' if self.device == torch.device('cuda') else 'Reference', atoms=self._data.atoms, **kwargs)
    - + self.physics_loss = openmm_energy(self.mol, self.std, clamp=clamp_kwargs, platform='CUDA' if self.device == torch.device('cuda') else 'Reference', atoms=self._data.atoms, xml_file = xml_file, **kwargs) + os.remove(tmp_filename)
    -
    -[docs] - def common_physics_step(self, batch, latent): +
    [docs] def common_physics_step(self, batch, latent): ''' Called from both :func:`train_step <molearn.trainers.OpenMM_Physics_Trainer.train_step>` and :func:`valid_step <molearn.trainers.OpenMM_Physics_Trainer.valid_step>`. Takes random interpolations between adjacent samples latent vectors. These are decoded (decoded structures saved as ``self._internal['generated'] = generated if needed elsewhere) and the energy terms calculated with ``self.physics_loss``. @@ -96,10 +126,7 @@

    Source code for molearn.trainers.openmm_physics_trainer

    return {'physics_loss':energy} # a if not energy.isinf() else torch.tensor(0.0)}
    - -
    -[docs] - def train_step(self, batch): +
    [docs] def train_step(self, batch): ''' This method overrides :func:`Trainer.train_step <molearn.trainers.Trainer.train_step>` and adds an additional 'Physics_loss' term. @@ -121,10 +148,7 @@

    Source code for molearn.trainers.openmm_physics_trainer

    results['loss'] = final_loss return results
    - -
    -[docs] - def valid_step(self, batch): +
    [docs] def valid_step(self, batch): ''' This method overrides :func:`Trainer.valid_step <molearn.trainers.Trainer.valid_step>` and adds an additional 'Physics_loss' term. @@ -143,9 +167,7 @@

    Source code for molearn.trainers.openmm_physics_trainer

    # scale = (self.psf*results['mse_loss'])/(results['physics_loss'] +1e-5) final_loss = torch.log(results['mse_loss'])+self.psf*torch.log(results['physics_loss']) results['loss'] = final_loss - return results
    -
    - + return results
    if __name__=='__main__': @@ -181,14 +203,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/trainers/torch_physics_trainer.html b/docs/build/_modules/molearn/trainers/torch_physics_trainer.html index 6a30ff5..cfa6c5e 100644 --- a/docs/build/_modules/molearn/trainers/torch_physics_trainer.html +++ b/docs/build/_modules/molearn/trainers/torch_physics_trainer.html @@ -1,15 +1,18 @@ + - + - molearn.trainers.torch_physics_trainer — molearn 2.0.1 documentation - - - - - + molearn.trainers.torch_physics_trainer — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -39,9 +42,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    from .trainer import Trainer -
    -[docs] -class Torch_Physics_Trainer(Trainer): +
    [docs]class Torch_Physics_Trainer(Trainer): ''' Torch_Physics_Trainer subclasses Trainer and replaces the valid_step and train_step. An extra 'physics_loss' (bonds, angles, and torsions) is calculated using pytorch. @@ -50,9 +51,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) -
    -[docs] - def prepare_physics(self, physics_scaling_factor=0.1): +
    [docs] def prepare_physics(self, physics_scaling_factor=0.1): ''' Create ``self.physics_loss`` object from :func:`loss_functions.TorchProteinEnergy <molearn.loss_functions.TorchProteinEnergy>` Needs ``self.std``, ``self._data`` to have been set with :func:`Trainer.set_data <molearn.trainer.Trainer.set_data>` @@ -61,10 +60,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    self.psf = physics_scaling_factor self.physics_loss = TorchProteinEnergy(self._data.dataset[0]*self.std, pdb_atom_names=self._data.get_atominfo(), device=self.device, method='roll')
    - -
    -[docs] - def common_physics_step(self, batch, latent): +
    [docs] def common_physics_step(self, batch, latent): ''' Called from both :func:`train_step <molearn.trainers.Torch_Physics_Trainer.train_step>` and :func:`valid_step <molearn.trainers.Torch_Physics_Trainer.valid_step>`. Takes random interpolations between adjacent samples latent vectors. These are decoded (decoded structures saved as ``self._internal['generated'] = generated if needed elsewhere) and the energy terms calculated with ``self.physics_loss``. @@ -87,10 +83,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    return {'physics_loss':total_physics, 'bond_energy':bond, 'angle_energy':angle, 'torsion_energy':torsion}
    - -
    -[docs] - def train_step(self, batch): +
    [docs] def train_step(self, batch): ''' This method overrides :func:`Trainer.train_step <molearn.trainers.Trainer.train_step>` and adds an additional 'Physics_loss' term. @@ -111,10 +104,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    results['loss'] = final_loss return results
    - -
    -[docs] - def valid_step(self, batch): +
    [docs] def valid_step(self, batch): ''' This method overrides :func:`Trainer.valid_step <molearn.trainers.Trainer.valid_step>` and adds an additional 'Physics_loss' term. @@ -132,9 +122,7 @@

    Source code for molearn.trainers.torch_physics_trainer

    # scale = self.psf*results['mse_loss']/(results['physics_loss']+1e-5) final_loss = torch.log(results['mse_loss'])+self.psf*torch.log(results['physics_loss']) results['loss'] = final_loss - return results
    -
    - + return results
    if __name__=='__main__': @@ -170,14 +158,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_modules/molearn/trainers/trainer.html b/docs/build/_modules/molearn/trainers/trainer.html index 54ac44a..cf48ebd 100644 --- a/docs/build/_modules/molearn/trainers/trainer.html +++ b/docs/build/_modules/molearn/trainers/trainer.html @@ -1,15 +1,18 @@ + - + - molearn.trainers.trainer — molearn 2.0.1 documentation - - - - - + molearn.trainers.trainer — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation

  • modules |
  • - + @@ -48,9 +51,7 @@

    Source code for molearn.trainers.trainer

         pass
     
     
    -
    -[docs] -class Trainer: +
    [docs]class Trainer: ''' Trainer class that defines a number of useful methods for training an autoencoder. @@ -87,9 +88,7 @@

    Source code for molearn.trainers.trainer

             self.log_filename = 'default_log_filename.json'
             self.scheduler_key = None
     
    -
    -[docs] - def get_network_summary(self): +
    [docs] def get_network_summary(self): ''' returns a dictionary containing information about the size of the autoencoder. ''' @@ -104,10 +103,7 @@

    Source code for molearn.trainers.trainer

                 autoencoder_trainable=get_parameters(True, self.autoencoder),
                 autoencoder_total=get_parameters(False, self.autoencoder))
    - -
    -[docs] - def set_autoencoder(self, autoencoder, **kwargs): +
    [docs] def set_autoencoder(self, autoencoder, **kwargs): ''' :param autoencoder: (:func:`autoencoder <molearn.models>`,) torch network class that implements ``autoencoder.encode``, and ``autoencoder.decode``. Please pass the class not the instance :param \*\*kwargs: any other kwargs given to this method will be used to initialise the network ``self.autoencoder = autoencoder(**kwargs)`` @@ -118,10 +114,7 @@

    Source code for molearn.trainers.trainer

                 self.autoencoder = autoencoder.to(self.device)
             self._autoencoder_kwargs = kwargs
    - -
    -[docs] - def set_dataloader(self, train_dataloader=None, valid_dataloader=None): +
    [docs] def set_dataloader(self, train_dataloader=None, valid_dataloader=None): ''' :param torch.DataLoader train_dataloader: Alternatively set using ``trainer.train_dataloader = dataloader`` :param torch.DataLoader valid_dataloader: Alternatively set using ``trainer.valid_dataloader = dataloader`` @@ -131,10 +124,7 @@

    Source code for molearn.trainers.trainer

             if valid_dataloader is not None:
                 self.valid_dataloader = valid_dataloader
    - -
    -[docs] - def set_data(self, data, **kwargs): +
    [docs] def set_data(self, data, **kwargs): ''' Sets up internal variables and gives trainer access to dataloaders. ``self.train_dataloader``, ``self.valid_dataloader``, ``self.std``, ``self.mean``, ``self.mol`` will all be obtained from this object. @@ -152,10 +142,7 @@

    Source code for molearn.trainers.trainer

             self.mol = data.mol
             self._data = data
    - -
    -[docs] - def prepare_optimiser(self, lr=1e-3, weight_decay=0.0001, **optimiser_kwargs): +
    [docs] def prepare_optimiser(self, lr=1e-3, weight_decay=0.0001, **optimiser_kwargs): ''' The Default optimiser is ``AdamW`` and is saved in ``self.optimiser``. With no optional arguments this function is the same as doing: @@ -167,10 +154,7 @@

    Source code for molearn.trainers.trainer

             '''
             self.optimiser = torch.optim.AdamW(self.autoencoder.parameters(), lr=lr, weight_decay=weight_decay, **optimiser_kwargs)
    - -
    -[docs] - def log(self, log_dict, verbose=None): +
    [docs] def log(self, log_dict, verbose=None): ''' Then contents of log_dict are dumped using ``json.dumps(log_dict)`` and printed and/or appended to ``self.log_filename`` This function is called from :func:`self.run <molearn.trainers.Trainer.run>` @@ -185,10 +169,7 @@

    Source code for molearn.trainers.trainer

             with open(self.log_filename, 'a') as f:
                 f.write(dump+'\n')
    - -
    -[docs] - def scheduler_step(self, logs): +
    [docs] def scheduler_step(self, logs): ''' This function does nothing. It is called after :func:`self.valid_epoch <molearn.trainers.Trainer.valid_epoch>` in :func:`Trainer.run() <molearn.trainers.Trainer.run>` and before :func:`checkpointing <molearn.trainers.Trainer.checkpoint>`. It is designed to be overridden if you wish to use a scheduler. @@ -196,10 +177,18 @@

    Source code for molearn.trainers.trainer

             '''
             pass
    + def prepare_logs(self, log_filename, log_folder=None): + self.log_filename = log_filename + if log_folder is not None: + if not os.path.exists(log_folder): + os.mkdir(log_folder) + if hasattr(self, "_repeat") and self._repeat >0: + self.log_filename = f'{log_folder}/{self._repeat}_{self.log_filename}' + else: + self.log_filename = f'{log_folder}/{self.log_filename}' + -
    -[docs] - def run(self, max_epochs=100, log_filename=None, log_folder=None, checkpoint_frequency=1, checkpoint_folder='checkpoint_folder', allow_n_failures=10, verbose=None): +
    [docs] def run(self, max_epochs=100, log_filename=None, log_folder=None, checkpoint_frequency=1, checkpoint_folder='checkpoint_folder', allow_n_failures=10, verbose=None, allow_grad_in_valid=False): ''' Calls the following in a loop: @@ -219,12 +208,14 @@

    Source code for molearn.trainers.trainer

             :param bool verbose: (default: None) set trainer.verbose. If True, the epoch logs will be printed as well as written to log_filename 
     
             '''
    -        if log_filename is not None:
    -            self.log_filename = log_filename
    -            if log_folder is not None:
    -                if not os.path.exists(log_folder):
    -                    os.mkdir(log_folder)
    -                self.log_filename = log_folder+'/'+self.log_filename
    +        self.get_repeat(checkpoint_folder)
    +        self.prepare_logs(log_filename if log_filename is not None else self.log_filename, log_folder)
    +        #if log_filename is not None:
    +        #    self.log_filename = log_filename
    +        #    if log_folder is not None:
    +        #        if not os.path.exists(log_folder):
    +        #            os.mkdir(log_folder)
    +        #        self.log_filename = log_folder+'/'+self.log_filename
             if verbose is not None:
                 self.verbose = verbose
     
    @@ -234,8 +225,11 @@ 

    Source code for molearn.trainers.trainer

                         time1 = time.time()
                         logs = self.train_epoch(epoch)
                         time2 = time.time()
    -                    with torch.no_grad():
    +                    if allow_grad_in_valid:
                             logs.update(self.valid_epoch(epoch))
    +                    else:
    +                        with torch.no_grad():
    +                            logs.update(self.valid_epoch(epoch))
                         time3 = time.time()
                         self.scheduler_step(logs)
                         if self.best is None or self.best > logs['valid_loss']:
    @@ -264,10 +258,7 @@ 

    Source code for molearn.trainers.trainer

                 else:
                     break
    - -
    -[docs] - def train_epoch(self,epoch): +
    [docs] def train_epoch(self,epoch): ''' Train one epoch. Called once an epoch from :func:`trainer.run <molearn.trainers.Trainer.run>` This method performs the following functions: @@ -302,10 +293,7 @@

    Source code for molearn.trainers.trainer

                 N+=len(batch)
             return {f'train_{key}': results[key]/N for key in results.keys()}
    - -
    -[docs] - def train_step(self, batch): +
    [docs] def train_step(self, batch): ''' Called from :func:`Trainer.train_epoch <molearn.trainers.Trainer.train_epoch>`. @@ -317,10 +305,7 @@

    Source code for molearn.trainers.trainer

             results['loss'] = results['mse_loss']
             return results
    - -
    -[docs] - def common_step(self, batch): +
    [docs] def common_step(self, batch): ''' Called from both train_step and valid_step. Calculates the mean squared error loss for self.autoencoder. @@ -337,10 +322,7 @@

    Source code for molearn.trainers.trainer

             self._internal['decoded'] = decoded
             return dict(mse_loss=((batch-decoded)**2).mean())
    - -
    -[docs] - def valid_epoch(self, epoch): +
    [docs] def valid_epoch(self, epoch): ''' Called once an epoch from :func:`trainer.run <molearn.trainers.Trainer.run>` within a no_grad context. This method performs the following functions: @@ -368,10 +350,7 @@

    Source code for molearn.trainers.trainer

                 N+=len(batch)
             return {f'valid_{key}': results[key]/N for key in results.keys()}
    - -
    -[docs] - def valid_step(self, batch): +
    [docs] def valid_step(self, batch): ''' Called from :func:`Trainer.valid_epoch<molearn.trainer.Trainer.valid_epoch>` on every mini-batch. @@ -383,10 +362,7 @@

    Source code for molearn.trainers.trainer

             results['loss'] = results['mse_loss']
             return results
    - -
    -[docs] - def learning_rate_sweep(self, max_lr=100, min_lr=1e-5, number_of_iterations=1000, checkpoint_folder='checkpoint_sweep', train_on='mse_loss', save=['loss', 'mse_loss']): +
    [docs] def learning_rate_sweep(self, max_lr=100, min_lr=1e-5, number_of_iterations=1000, checkpoint_folder='checkpoint_sweep', train_on='mse_loss', save=['loss', 'mse_loss']): ''' Deprecated method. Performs a sweep of learning rate between ``max_lr`` and ``min_lr`` over ``number_of_iterations``. @@ -429,10 +405,7 @@

    Source code for molearn.trainers.trainer

             print('min value ', values[np.nanargmin(values[:,1])])
             return values
    - -
    -[docs] - def update_optimiser_hyperparameters(self, **kwargs): +
    [docs] def update_optimiser_hyperparameters(self, **kwargs): ''' Update optimeser hyperparameter e.g. ``trainer.update_optimiser_hyperparameters(lr = 1e3)`` @@ -442,10 +415,7 @@

    Source code for molearn.trainers.trainer

                 for key, value in kwargs.items():
                     g[key] = value
    - -
    -[docs] - def checkpoint(self, epoch, valid_logs, checkpoint_folder, loss_key='valid_loss'): +
    [docs] def checkpoint(self, epoch, valid_logs, checkpoint_folder, loss_key='valid_loss'): ''' Checkpoint the current network. The checkpoint will be saved as ``'last.ckpt'``. If valid_logs[loss_key] is better than self.best then this checkpoint will replace self.best and ``'last.ckpt'`` will be renamed to ``f'{checkpoint_folder}/checkpoint_epoch{epoch}_loss{valid_loss}.ckpt'`` and the former best (filename saved as ``self.best_name``) will be deleted @@ -466,21 +436,18 @@

    Source code for molearn.trainers.trainer

                         'atoms': self._data.atoms,
                         'std': self.std,
                         'mean': self.mean},
    -                   f'{checkpoint_folder}/last.ckpt')
    +                   f'{checkpoint_folder}/last{f"_{self._repeat}" if self._repeat > 0 else ""}.ckpt')
     
             if self.best is None or self.best > valid_loss:
    -            filename = f'{checkpoint_folder}/checkpoint_epoch{epoch}_loss{valid_loss}.ckpt'
    -            shutil.copyfile(f'{checkpoint_folder}/last.ckpt', filename)
    +            filename = f'{checkpoint_folder}/checkpoint{f"_{self._repeat}" if self._repeat>0 else ""}_epoch{epoch}_loss{valid_loss}.ckpt'
    +            shutil.copyfile(f'{checkpoint_folder}/last{f"_{self._repeat}" if self._repeat>0 else ""}.ckpt', filename)
                 if self.best is not None:
                     os.remove(self.best_name)
                 self.best_name = filename
                 self.best_epoch = epoch
                 self.best = valid_loss
    - -
    -[docs] - def load_checkpoint(self, checkpoint_name='best', checkpoint_folder='', load_optimiser=True): +
    [docs] def load_checkpoint(self, checkpoint_name='best', checkpoint_folder='', load_optimiser=True): ''' Load checkpoint. @@ -511,7 +478,19 @@

    Source code for molearn.trainers.trainer

                 self.optimiser.load_state_dict(checkpoint['optimizer_state_dict'])
             epoch = checkpoint['epoch']
             self.epoch = epoch+1
    -
    + + def get_repeat(self, checkpoint_folder): + if not os.path.exists(checkpoint_folder): + os.mkdir(checkpoint_folder) + if not hasattr(self, '_repeat'): + self._repeat = 0 + for i in range(1000): + if not os.path.exists(checkpoint_folder+f'/last{f"_{self._repeat}" if self._repeat>0 else ""}.ckpt'): + break#os.mkdir(checkpoint_folder) + else: + self._repeat += 1 + else: + raise Exception('Something went wrong, you surely havnt done 1000 repeats?')
    @@ -548,14 +527,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/_sources/FAQ.rst.txt b/docs/build/_sources/FAQ.rst.txt new file mode 100644 index 0000000..d302b0a --- /dev/null +++ b/docs/build/_sources/FAQ.rst.txt @@ -0,0 +1,69 @@ +########################## +Frequently Asked Questions +########################## + + +I cannot install openmmtorchplugin +---------------------------------- + +openmmtorchplugin depends on conda-forge builds of pyTorch and OpenMM. +Due to this dependency, Windows cannot be supported. + +Installation can be carried out via terminal with conda-forge: + +.. code:: + + conda install -c conda-forge openmmtorchplugin + + +The following Python versions are supported: 3.8, 3.9, 3.10, 3.11. + +If you run into any issue, either at installation or runtime, ensure you have a +plugin version >=1.1.3, as previous ones have known compatibility with OpenMM. +The easiest way to ensure the most up to date version of molearn and the +openmmtorchplugin are installed, is to run a fresh install in a new conda +environment: + +.. code:: + + conda create --name test_env python=3.10 + conda install -n test_env -c conda-forge openmmtorchplugin molearn + + +openmmtorchplugin is built with cuda_compiler_version=11.2 in conda-forge CI tools. +This has been successfully tested on Ubuntu machines running with the driver +version 525.105.17 (see nvidia-smi output). + +The Nvidia website tabulates minimum driver versions required and version compatibility: +`NVIDIA CUDA Toolkit Minimum driver versions `_ + + +I get an IndexError when I try loading a multiPDB +------------------------------------------------- + +This is likely an error thrown by MDAnalysis. Typically this happens when +attempting to load a multiPDB file saved with software like VMD, which uses a +different syntax to indicate the end of a conformer in the file. A way to get +around this, is to re-save the file in a format MDAnalysis can parse, e.g., by +loading and re-saving the file via biobox. + +.. code-block:: + + import biobox as bb + M = bb.Molecule(filename) + M.write_pdb(newfilename) + + + +The GUI freezes when I use it/does not work as expected +------------------------------------------------------- + +This is usually caused by an issue with packages handling communications between the GUI and Jupyter, `see here `_. +Currently, a workaround is to use older versions of `tornado`. +In Python 3.10, the following packages have been observed to yield correct behaviour: + +.. code:: + + ipywidgets=8.0.7 + nglview=3.0.6 + tornado=6.1 diff --git a/docs/build/_sources/models.rst.txt b/docs/build/_sources/models.rst.txt index 7bcbc5a..c59cf24 100644 --- a/docs/build/_sources/models.rst.txt +++ b/docs/build/_sources/models.rst.txt @@ -1,17 +1,9 @@ -FoldingNet Model ----------------- - -Import from `molearn.models.foldingnet` +Models +------ .. autoclass:: molearn.models.foldingnet.AutoEncoder :members: - -CNN model ---------- - -import from `molearn.models.CNN_autoencoder` - .. autoclass:: molearn.models.CNN_autoencoder.Autoencoder - :members: \ No newline at end of file + :members: diff --git a/docs/build/_static/basic.css b/docs/build/_static/basic.css index 30fee9d..0889677 100644 --- a/docs/build/_static/basic.css +++ b/docs/build/_static/basic.css @@ -4,7 +4,7 @@ * * Sphinx stylesheet -- basic theme. * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -237,10 +237,6 @@ a.headerlink { visibility: hidden; } -a:visited { - color: #551A8B; -} - h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, @@ -328,17 +324,17 @@ aside.sidebar { p.sidebar-title { font-weight: bold; } - nav.contents, aside.topic, + div.admonition, div.topic, blockquote { clear: left; } /* -- topics ---------------------------------------------------------------- */ - nav.contents, aside.topic, + div.topic { border: 1px solid #ccc; padding: 7px; @@ -379,6 +375,7 @@ div.sidebar > :last-child, aside.sidebar > :last-child, nav.contents > :last-child, aside.topic > :last-child, + div.topic > :last-child, div.admonition > :last-child { margin-bottom: 0; @@ -388,6 +385,7 @@ div.sidebar::after, aside.sidebar::after, nav.contents::after, aside.topic::after, + div.topic::after, div.admonition::after, blockquote::after { @@ -613,6 +611,25 @@ ul.simple p { margin-bottom: 0; } +/* Docutils 0.17 and older (footnotes & citations) */ +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +/* Docutils 0.18+ (footnotes & citations) */ aside.footnote > span, div.citation > span { float: left; @@ -637,6 +654,8 @@ div.citation > p:last-of-type:after { clear: both; } +/* Footnotes & citations ends */ + dl.field-list { display: grid; grid-template-columns: fit-content(30%) auto; @@ -649,6 +668,10 @@ dl.field-list > dt { padding-right: 5px; } +dl.field-list > dt:after { + content: ":"; +} + dl.field-list > dd { padding-left: 0.5em; margin-top: 0em; @@ -674,16 +697,6 @@ dd { margin-left: 30px; } -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - dl > dd:last-child, dl > dd:last-child > :last-child { margin-bottom: 0; @@ -752,14 +765,6 @@ abbr, acronym { cursor: help; } -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - /* -- code displays --------------------------------------------------------- */ pre { diff --git a/docs/build/_static/doctools.js b/docs/build/_static/doctools.js index d06a71d..c3db08d 100644 --- a/docs/build/_static/doctools.js +++ b/docs/build/_static/doctools.js @@ -4,19 +4,12 @@ * * Base JavaScript utilities for all Sphinx HTML documentation. * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ "use strict"; -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - const _ready = (callback) => { if (document.readyState !== "loading") { callback(); @@ -25,11 +18,73 @@ const _ready = (callback) => { } }; +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + parent.insertBefore( + span, + parent.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + /** * Small JavaScript module for the documentation. */ const Documentation = { init: () => { + Documentation.highlightSearchWords(); Documentation.initDomainIndexTable(); Documentation.initOnKeyListeners(); }, @@ -71,6 +126,51 @@ const Documentation = { Documentation.LOCALE = catalog.locale; }, + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords: () => { + const highlight = + new URLSearchParams(window.location.search).get("highlight") || ""; + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + const url = new URL(window.location); + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + }, + /** * helper function to focus on search bar */ @@ -110,11 +210,15 @@ const Documentation = { ) return; + const blacklistedElements = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", + ]); document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; + if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements + if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys if (!event.shiftKey) { switch (event.key) { @@ -136,6 +240,10 @@ const Documentation = { event.preventDefault(); } break; + case "Escape": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.hideSearchWords(); + event.preventDefault(); } } diff --git a/docs/build/_static/documentation_options.js b/docs/build/_static/documentation_options.js index 15dd1bd..03253ca 100644 --- a/docs/build/_static/documentation_options.js +++ b/docs/build/_static/documentation_options.js @@ -1,5 +1,6 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '2.0.1', +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '2.0.4', LANGUAGE: 'en', COLLAPSE_INDEX: false, BUILDER: 'html', @@ -9,5 +10,5 @@ const DOCUMENTATION_OPTIONS = { SOURCELINK_SUFFIX: '.txt', NAVIGATION_WITH_KEYS: false, SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, + ENABLE_SEARCH_SHORTCUTS: false, }; \ No newline at end of file diff --git a/docs/build/_static/language_data.js b/docs/build/_static/language_data.js index 250f566..2e22b06 100644 --- a/docs/build/_static/language_data.js +++ b/docs/build/_static/language_data.js @@ -5,7 +5,7 @@ * This script contains the language-specific data used by searchtools.js, * namely the list of stopwords, stemmer, scorer and splitter. * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ diff --git a/docs/build/_static/searchtools.js b/docs/build/_static/searchtools.js index 7918c3f..ac4d586 100644 --- a/docs/build/_static/searchtools.js +++ b/docs/build/_static/searchtools.js @@ -4,7 +4,7 @@ * * Sphinx JavaScript utilities for the full-text search. * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -57,14 +57,14 @@ const _removeChildren = (element) => { const _escapeRegExp = (string) => string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string -const _displayItem = (item, searchTerms, highlightTerms) => { +const _displayItem = (item, highlightTerms, searchTerms) => { const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - const [docName, title, anchor, descr, score, _filename] = item; + const [docName, title, anchor, descr] = item; let listItem = document.createElement("li"); let requestUrl; @@ -75,35 +75,29 @@ const _displayItem = (item, searchTerms, highlightTerms) => { if (dirname.match(/\/index\/$/)) dirname = dirname.substring(0, dirname.length - 6); else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; + requestUrl = docUrlRoot + dirname; linkUrl = requestUrl; } else { // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; + requestUrl = docUrlRoot + docName + docFileSuffix; linkUrl = docName + docLinkSuffix; } + const params = new URLSearchParams(); + params.set("highlight", [...highlightTerms].join(" ")); let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; + linkEl.href = linkUrl + "?" + params.toString() + anchor; linkEl.innerHTML = title; - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = + if (descr) + listItem.appendChild(document.createElement("span")).innerText = " (" + descr + ")"; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } else if (showSearchSummary) fetch(requestUrl) .then((responseData) => responseData.text()) .then((data) => { if (data) listItem.appendChild( - Search.makeSearchSummary(data, searchTerms) + Search.makeSearchSummary(data, searchTerms, highlightTerms) ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); }); Search.output.appendChild(listItem); }; @@ -122,15 +116,15 @@ const _finishSearch = (resultCount) => { const _displayNextItem = ( results, resultCount, - searchTerms, highlightTerms, + searchTerms ) => { // results left, load the summary and display it // this is intended to be dynamic (don't sub resultsCount) if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); + _displayItem(results.pop(), highlightTerms, searchTerms); setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + () => _displayNextItem(results, resultCount, highlightTerms, searchTerms), 5 ); } @@ -161,8 +155,10 @@ const Search = { _pulse_status: -1, htmlToText: (htmlString) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const htmlElement = document + .createRange() + .createContextualFragment(htmlString); + _removeChildren(htmlElement.querySelectorAll(".headerlink")); const docContent = htmlElement.querySelector('[role="main"]'); if (docContent !== undefined) return docContent.textContent; console.warn( @@ -243,12 +239,6 @@ const Search = { * execute search (requires search index to be loaded) */ query: (query) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - // stem the search terms and add them to the correct list const stemmer = new Stemmer(); const searchTerms = new Set(); @@ -276,10 +266,6 @@ const Search = { } }); - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - // console.debug("SEARCH: searching for:"); // console.info("required: ", [...searchTerms]); // console.info("excluded: ", [...excludedTerms]); @@ -288,40 +274,6 @@ const Search = { let results = []; _removeChildren(document.getElementById("search-progress")); - const queryLower = query.toLowerCase(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - let score = Math.round(100 * queryLower.length / title.length) - results.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id] of foundEntries) { - let score = Math.round(100 * queryLower.length / entry.length) - results.push([ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - // lookup as object objectTerms.forEach((term) => results.push(...Search.performObjectSearch(term, objectTerms)) @@ -368,7 +320,7 @@ const Search = { // console.info("search results:", Search.lastresults); // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); + _displayNextItem(results, results.length, highlightTerms, searchTerms); }, /** @@ -449,8 +401,8 @@ const Search = { // prepare search const terms = Search._index.terms; const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; const docNames = Search._index.docnames; + const filenames = Search._index.filenames; const titles = Search._index.titles; const scoreMap = new Map(); @@ -547,15 +499,16 @@ const Search = { /** * helper function to return a node containing the * search summary for a given text. keywords is a list - * of stemmed words. + * of stemmed words, highlightWords is the list of normal, unstemmed + * words. the first one is used to find the occurrence, the + * latter for highlighting it. */ - makeSearchSummary: (htmlText, keywords) => { - const text = Search.htmlToText(htmlText); + makeSearchSummary: (htmlText, keywords, highlightWords) => { + const text = Search.htmlToText(htmlText).toLowerCase(); if (text === "") return null; - const textLower = text.toLowerCase(); const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) + .map((k) => text.indexOf(k.toLowerCase())) .filter((i) => i > -1) .slice(-1)[0]; const startWithContext = Math.max(actualStartPosition - 120, 0); @@ -563,9 +516,13 @@ const Search = { const top = startWithContext === 0 ? "" : "..."; const tail = startWithContext + 240 < text.length ? "..." : ""; - let summary = document.createElement("p"); + let summary = document.createElement("div"); summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + summary.innerText = top + text.substr(startWithContext, 240).trim() + tail; + + highlightWords.forEach((highlightWord) => + _highlightText(summary, highlightWord, "highlighted") + ); return summary; }, diff --git a/docs/build/_static/sphinxdoc.css b/docs/build/_static/sphinxdoc.css index 1e9ffe0..f922226 100644 --- a/docs/build/_static/sphinxdoc.css +++ b/docs/build/_static/sphinxdoc.css @@ -5,7 +5,7 @@ * Sphinx stylesheet -- sphinxdoc theme. Originally created by * Armin Ronacher for Werkzeug. * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -151,10 +151,6 @@ a:hover { color: #2491CF; } -a:visited { - color: #551A8B; -} - div.body a { text-decoration: underline; } @@ -269,9 +265,9 @@ div.quotebar { padding: 2px 7px; border: 1px solid #ccc; } - nav.contents, aside.topic, + div.topic { background-color: #f8f8f8; } diff --git a/docs/build/analysis.html b/docs/build/analysis.html index 5e6941c..369edd4 100644 --- a/docs/build/analysis.html +++ b/docs/build/analysis.html @@ -1,16 +1,19 @@ + - + - - - Analysis — molearn 2.0.1 documentation - - - - - + + + Analysis — molearn 2.0.4 documentation + + + + + + + @@ -31,7 +34,7 @@

    Navigation

  • previous |
  • - +
    @@ -42,15 +45,15 @@

    Navigation

    -

    Analysis

    +

    Analysis

    -class MolearnAnalysis[source]
    +class MolearnAnalysis[source]

    This class provides methods dedicated to the quality analysis of a trained model.

    -generate(crd)[source]
    +generate(crd)[source]

    Generate a collection of protein conformations, given coordinates in the latent space.

    Parameters:
    @@ -64,7 +67,7 @@

    Analysis
    -get_all_dope_score(tensor, refine=True)[source]
    +get_all_dope_score(tensor, refine=True)[source]

    Calculate DOPE score of an ensemble of atom coordinates.

    Parameters:
    @@ -78,7 +81,7 @@

    Analysis
    -get_all_ramachandran_score(tensor)[source]
    +get_all_ramachandran_score(tensor)[source]

    Calculate Ramachandran score of an ensemble of atomic conrdinates.

    Parameters:
    @@ -89,7 +92,7 @@

    Analysis
    -get_dataset(key)[source]
    +get_dataset(key)[source]
    Parameters:

    key (str) – key pointing to a dataset previously loaded with set_dataset

    @@ -99,7 +102,7 @@

    Analysis
    -get_decoded(key)[source]
    +get_decoded(key)[source]
    Parameters:

    key (str) – key pointing to a dataset previously loaded with set_dataset

    @@ -109,7 +112,7 @@

    Analysis
    -get_dope(key, refine=True, **kwargs)[source]
    +get_dope(key, refine=True, **kwargs)[source]
    Parameters:

    @@ -157,14 +160,14 @@

    G

  • get_decoded() (MolearnAnalysis method)
  • + + - @@ -443,13 +440,13 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/build/index.html b/docs/build/index.html index 55a26aa..3d1066d 100644 --- a/docs/build/index.html +++ b/docs/build/index.html @@ -1,16 +1,19 @@ + - + - + - Welcome to molearn’s documentation! — molearn 2.0.1 documentation - - - - - + Welcome to molearn’s documentation! — molearn 2.0.4 documentation + + + + + + + @@ -27,7 +30,7 @@

    Navigation

  • next |
  • - +
    @@ -38,7 +41,7 @@

    Navigation

    -

    Welcome to molearn’s documentation!

    +

    Welcome to molearn’s documentation!

    molearn is a Python package streamlining the implementation of machine learning models dedicated to the generation of protein conformations from example data obtained via experiment or molecular simulation.

    @@ -48,49 +51,12 @@

    Welcome to molearn’s documentation!

    Contents:

    \ No newline at end of file diff --git a/docs/build/loss_functions.html b/docs/build/loss_functions.html index 72c7bd3..f5681b7 100644 --- a/docs/build/loss_functions.html +++ b/docs/build/loss_functions.html @@ -1,16 +1,19 @@ + - + - + - Loss Functions — molearn 2.0.1 documentation - - - - - + Loss Functions — molearn 2.0.4 documentation + + + + + + + @@ -31,7 +34,7 @@

    Navigation

  • previous |
  • - +
    @@ -42,10 +45,10 @@

    Navigation

    -

    Loss Functions

    +

    Loss Functions

    -class ModifiedForceField(*args, alternative_residue_names=None, **kwargs)[source]
    +class ModifiedForceField(*args, alternative_residue_names=None, **kwargs)[source]

    Takes all *args and **kwargs of openmm.app.ForceField, plus an optional parameter described here.

    Parameters:
    @@ -56,7 +59,7 @@

    Navigation

    -class OpenMMPluginScoreSoftForceField(mol=None, platform='CUDA', atoms=['CA', 'C', 'N', 'CB', 'O'])[source]
    +class OpenMMPluginScoreSoftForceField(mol=None, platform='CUDA', atoms=['CA', 'C', 'N', 'CB', 'O'])[source]
    Parameters:
      @@ -74,7 +77,7 @@

      Navigation

      -class OpenmmPluginScore(mol=None, xml_file=['amber14-all.xml'], platform='CUDA', remove_NB=False, alternative_residue_names={'HIS': 'HIE', 'HSE': 'HIE'}, atoms=['CA', 'C', 'N', 'CB', 'O'], soft=False)[source]
      +class OpenmmPluginScore(mol=None, xml_file=['amber14-all.xml'], platform='CUDA', remove_NB=False, alternative_residue_names={'HIS': 'HIE', 'HSE': 'HIE'}, atoms=['CA', 'C', 'N', 'CB', 'O'], soft=False)[source]

      This will use the new OpenMM Plugin to calculate forces and energy. The intention is that this will be fast enough to be able to calculate forces and energy during training. N.B.: The current torchintegratorplugin only supports float on GPU and double on CPU.

      @@ -92,7 +95,7 @@

      Navigation

      -execute(x)[source]
      +execute(x)[source]
      Parameters:

      x (torch.Tensor) – shape [b, N, 3]. dtype=float. device = gpu

      @@ -102,7 +105,7 @@

      Navigation

      -get_energy(pos_ptr, force_ptr, energy_ptr, n_particles, batch_size)[source]
      +get_energy(pos_ptr, force_ptr, energy_ptr, n_particles, batch_size)[source]
      Parameters:
        @@ -120,7 +123,7 @@

        Navigation

        -class OpenmmTorchEnergyMinimizer(mol=None, xml_file=['amber14-all.xml'], platform='CUDA', remove_NB=False, alternative_residue_names={'HIS': 'HIE', 'HSE': 'HIE'}, atoms=['CA', 'C', 'N', 'CB', 'O'], soft=False)[source]
        +class OpenmmTorchEnergyMinimizer(mol=None, xml_file=['amber14-all.xml'], platform='CUDA', remove_NB=False, alternative_residue_names={'HIS': 'HIE', 'HSE': 'HIE'}, atoms=['CA', 'C', 'N', 'CB', 'O'], soft=False)[source]
        Parameters:
          @@ -138,13 +141,13 @@

          Navigation

          -class openmm_clamped_energy_function(*args, **kwargs)[source]
          +class openmm_clamped_energy_function(*args, **kwargs)[source]
          -static backward(ctx, grad_output)[source]
          -

          Defines a formula for differentiating the operation with backward mode -automatic differentiation (alias to the vjp function).

          -

          This function is to be overridden by all subclasses.

          +static backward(ctx, grad_output)[source] +

          Define a formula for differentiating the operation with backward mode automatic differentiation.

          +

          This function is to be overridden by all subclasses. +(Defining this function is equivalent to defining the vjp function.)

          It must accept a context ctx as the first argument, followed by as many outputs as the forward() returned (None will be passed in for non tensor outputs of the forward function), @@ -157,13 +160,13 @@

          Navigation

          pass. It also has an attribute ctx.needs_input_grad as a tuple of booleans representing whether each input needs gradient. E.g., backward() will have ctx.needs_input_grad[0] = True if the -first input to forward() needs gradient computated w.r.t. the +first input to forward() needs gradient computed w.r.t. the output.

          -static forward(ctx, plugin, x, clamp)[source]
          +static forward(ctx, plugin, x, clamp)[source]
          Parameters:
            @@ -181,19 +184,19 @@

            Navigation

            -class openmm_energy(mol, std, clamp=None, **kwargs)[source]
            -

            Initializes internal Module state, shared by both nn.Module and ScriptModule.

            +class openmm_energy(mol, std, clamp=None, **kwargs)[source] +

            Initialize internal Module state, shared by both nn.Module and ScriptModule.

            -class openmm_energy_function(*args, **kwargs)[source]
            +class openmm_energy_function(*args, **kwargs)[source]
            -static backward(ctx, grad_output)[source]
            -

            Defines a formula for differentiating the operation with backward mode -automatic differentiation (alias to the vjp function).

            -

            This function is to be overridden by all subclasses.

            +static backward(ctx, grad_output)[source] +

            Define a formula for differentiating the operation with backward mode automatic differentiation.

            +

            This function is to be overridden by all subclasses. +(Defining this function is equivalent to defining the vjp function.)

            It must accept a context ctx as the first argument, followed by as many outputs as the forward() returned (None will be passed in for non tensor outputs of the forward function), @@ -206,13 +209,13 @@

            Navigation

            pass. It also has an attribute ctx.needs_input_grad as a tuple of booleans representing whether each input needs gradient. E.g., backward() will have ctx.needs_input_grad[0] = True if the -first input to forward() needs gradient computated w.r.t. the +first input to forward() needs gradient computed w.r.t. the output.

            -static forward(ctx, plugin, x)[source]
            +static forward(ctx, plugin, x)[source]
            Parameters:
              @@ -237,34 +240,6 @@

              Navigation

    @@ -42,10 +45,10 @@

    Navigation

    -

    Scoring

    +

    Scoring

    -class DOPE_Score(mol)[source]
    +class DOPE_Score(mol)[source]

    This class contains methods to calculate dope without saving to save and load PDB files for every structure. Atoms in a biobox coordinate tensor are mapped to the coordinates in the modeller model directly.

    Parameters:
    @@ -54,7 +57,7 @@

    Scoring

    -get_all_dope(coords, refine=False)[source]
    +get_all_dope(coords, refine=False)[source]

    Expect a array of frames. return array of DOPE score value.

    Parameters:
    @@ -74,7 +77,7 @@

    Scoring
    -get_dope(frame, refine=False)[source]
    +get_dope(frame, refine=False)[source]

    Get the dope score. Injects coordinates into modeller and uses mdl.build(build_method=’INTERNAL_COORDINATES’, initialize_xyz=False) to reconstruct missing atoms. If a error is thrown by modeller or at any stage, we just return a fixed large value of 1e10.

    @@ -97,7 +100,7 @@

    Scoring
    -class Parallel_DOPE_Score(mol, processes=-1, context='spawn', **kwargs)[source]
    +class Parallel_DOPE_Score(mol, processes=- 1, context='spawn', **kwargs)[source]

    a multiprocessing class to get modeller DOPE scores. A typical use case would looke like:

    score_class = Parallel_DOPE_Score(mol, **kwargs)
    @@ -120,7 +123,7 @@ 

    Scoring

    -get_score(coords, **kwargs)[source]
    +get_score(coords, **kwargs)[source]
    Parameters:

    coords (np.array) – # shape (N, 3) numpy array

    @@ -132,66 +135,13 @@

    Scoring
    -class Ramachandran_Score(mol, threshold=0.001)[source]
    -

    This class contains methods that use iotbx/mmtbx to calulate the quality of phi and psi values in a protein.

    -
    -
    Parameters:
    -
      -
    • mol (biobox.Molecule) – One example frame to gain access to the topology. Mol will also be used to save a temporary pdb file that will be reloaded to create the initial iotbx Model.

    • -
    • threshold (float) – (default: 1e-3) Threshold used to determine similarity between biobox.molecule coordinates and iotbx model coordinates. Determine that iotbx model was created successfully.

    • -
    -
    -
    -
    -
    -get_score(coords, as_ratio=False)[source]
    -

    Given coords (corresponding to self.mol) will calculate Ramachandran scores using cctbux ramalyze module -Returns the counts of number of torsion angles that fall within favored, allowed, and outlier regions and finally the total number of torsion angles analysed. -:param numpy.ndarray coords: shape (N, 3) -:returns: (favored, allowed, outliers, total) -:rtype: tuple of ints

    -
    - -
    +class Ramachandran_Score(*args, **kwargs)[source] +

    -class Parallel_Ramachandran_Score(mol, processes=-1)[source]
    -

    A multiprocessing class to get Ramachandran scores. -A typical use case would looke like:

    -
    score_class = Parallel_Ramachandran_Score(mol, **kwargs)
    -results = []
    -for frame in coordinates_array:
    -    results.append(score_class.get_score(frame))
    -    # Ramachandran scores will be calculated asynchronously in background
    -...
    -# to retrieve the results
    -results = np.array([r.get() for r in results])
    -favored = results[:,0]
    -allowed = results[:,1]
    -outliers = results[:,2]
    -total = results[:,3]
    -
    -
    -
    -
    Parameters:
    -
      -
    • mol (biobox.Molecule) – biobox melucel containing one example fram of the protein to be analysed. This will be passed to Ramachandran_Score instances in each thread.

    • -
    • processes (int) – (default: -1) Number of processes argument to pass to multiprocessing.pool. This controls the number of therads created.

    • -
    -
    -
    -
    -
    -get_score(coords, **kwargs)[source]
    -
    -
    Parameters:
    -

    coords – # shape (N, 3) numpy array

    -
    -
    -
    - -
    +class Parallel_Ramachandran_Score(*args, **kwargs)[source] +

    @@ -202,32 +152,6 @@

    Scoring

    \ No newline at end of file diff --git a/docs/build/searchindex.js b/docs/build/searchindex.js index 785455c..e1d29ba 100644 --- a/docs/build/searchindex.js +++ b/docs/build/searchindex.js @@ -1 +1 @@ -Search.setIndex({"docnames": ["FAQ", "analysis", "data", "index", "loss_functions", "models", "scoring", "trainers"], "filenames": ["FAQ.rst", "analysis.rst", "data.rst", "index.rst", "loss_functions.rst", "models.rst", "scoring.rst", "trainers.rst"], "titles": ["Frequently Asked Questions", "Analysis", "Data Loading", "Welcome to molearn\u2019s documentation!", "Loss Functions", "FoldingNet Model", "Scoring", "Trainers"], "terms": {"thi": [0, 1, 2, 3, 4, 5, 6, 7], "like": [0, 6], "error": [0, 1, 4, 6, 7], "thrown": [0, 4, 6], "mdanalysi": 0, "typic": [0, 6, 7], "happen": [0, 7], "attempt": 0, "file": [0, 2, 4, 6, 7], "save": [0, 1, 4, 6, 7], "softwar": 0, "vmd": 0, "which": [0, 7], "differ": [0, 7], "syntax": 0, "indic": [0, 1], "end": [0, 1], "conform": [0, 1, 3], "A": [0, 6, 7], "wai": 0, "around": [0, 1], "re": 0, "format": 0, "can": [0, 2, 4, 7], "pars": 0, "e": [0, 4, 7], "g": [0, 4, 7], "via": [0, 1, 3, 7], "biobox": [0, 2, 4, 6, 7], "import": [0, 2, 5, 7], "bb": 0, "m": [0, 1, 5], "molecul": [0, 2, 4, 6, 7], "filenam": [0, 2, 7], "write_pdb": 0, "newfilenam": 0, "depend": 0, "conda": 0, "forg": 0, "build": [0, 1, 3, 6], "pytorch": [0, 7], "openmm": [0, 4, 7], "due": 0, "window": 0, "support": [0, 4], "carri": 0, "out": [0, 7], "termin": [0, 2], "c": [0, 2, 4], "follow": [0, 4, 7], "python": [0, 3], "version": 0, "ar": [0, 1, 2, 4, 6, 7], "3": [0, 1, 4, 6, 7], "8": [0, 7], "9": 0, "10": [0, 1, 7], "11": 0, "If": [0, 1, 2, 4, 6, 7], "you": [0, 4, 7], "run": [0, 1, 5, 7], "ani": [0, 4, 6, 7], "issu": 0, "either": [0, 1, 2], "runtim": 0, "ensur": 0, "have": [0, 2, 4, 7], "plugin": [0, 4], "1": [0, 1, 2, 5, 6, 7], "previou": 0, "ones": 0, "known": 0, "compat": 0, "easiest": 0, "most": 0, "up": [0, 7], "date": 0, "molearn": [0, 1, 5, 7], "fresh": 0, "new": [0, 4, 7], "environ": 0, "creat": [0, 1, 2, 6, 7], "name": [0, 1, 2], "test_env": 0, "n": [0, 1, 2, 4, 6], "built": 0, "cuda_compiler_vers": 0, "2": [0, 6, 7], "ci": 0, "tool": [0, 3], "ha": [0, 4, 7], "been": [0, 2, 7], "successfulli": [0, 6], "test": 0, "ubuntu": 0, "machin": [0, 3], "driver": 0, "525": 0, "105": 0, "17": 0, "see": [0, 3, 7], "nvidia": 0, "smi": 0, "output": [0, 1, 4, 7], "websit": 0, "tabul": 0, "minimum": 0, "requir": [0, 1, 4, 7], "cuda": [0, 4, 7], "toolkit": 0, "usual": [0, 7], "caus": 0, "packag": [0, 3, 7], "handl": [0, 3], "commun": 0, "between": [0, 1, 5, 6, 7], "jupyt": [0, 1], "here": [0, 4], "current": [0, 4, 7], "workaround": 0, "older": 0, "tornado": 0, "In": 0, "observ": 0, "yield": [0, 1], "correct": 0, "behaviour": 0, "ipywidget": 0, "0": [0, 1, 2, 4, 5, 6, 7], "7": 0, "nglview": 0, "6": 0, "class": [1, 2, 4, 5, 6, 7], "molearnanalysi": [1, 3], "sourc": [1, 2, 4, 5, 6, 7], "provid": [1, 2, 3], "method": [1, 6, 7], "dedic": [1, 3], "qualiti": [1, 6], "train": [1, 2, 3, 4, 7], "model": [1, 3, 6], "gener": [1, 2, 3, 7], "crd": 1, "collect": 1, "protein": [1, 3, 6, 7], "given": [1, 4, 6, 7], "coordin": [1, 6], "latent": [1, 5, 7], "space": [1, 5], "paramet": [1, 2, 4, 5, 6, 7], "numpi": [1, 6, 7], "arrai": [1, 6, 7], "nx2": 1, "return": [1, 2, 4, 6, 7], "cartesian": 1, "nxmx3": 1, "where": [1, 2, 7], "i": [1, 2, 3, 4, 5, 6, 7], "number": [1, 2, 4, 5, 6, 7], "atom": [1, 2, 4, 6, 7], "get_all_dope_scor": 1, "tensor": [1, 2, 4, 6, 7], "refin": [1, 6], "true": [1, 2, 4, 6, 7], "calcul": [1, 4, 6, 7], "dope": [1, 6], "score": [1, 3, 7], "an": [1, 2, 3, 4, 7], "ensembl": [1, 3], "bool": [1, 4, 6, 7], "input": [1, 4], "structur": [1, 2, 6, 7], "after": [1, 2, 7], "get_all_ramachandran_scor": 1, "ramachandran": [1, 6], "conrdin": 1, "get_dataset": [1, 2], "kei": [1, 7], "str": [1, 2, 4, 7], "point": 1, "dataset": [1, 2, 7], "previous": 1, "load": [1, 3, 6, 7], "set_dataset": 1, "get_decod": 1, "get_dop": [1, 6], "kwarg": [1, 2, 4, 5, 6, 7], "befor": [1, 2, 7], "dictionari": [1, 7], "contain": [1, 2, 6, 7], "its": 1, "decod": [1, 7], "counterpart": 1, "get_encod": 1, "encod": [1, 7], "associ": [1, 7], "get_error": 1, "align": 1, "reconstruct": [1, 6], "neural": 1, "network": [1, 7], "rmsd": 1, "find": [1, 7], "optim": [1, 7], "1d": 1, "get_ramachandran": 1, "num_trainable_param": 1, "trainabl": 1, "reference_dope_scor": 1, "frame": [1, 2, 6, 7], "shape": [1, 4, 6, 7], "scan_custom": 1, "fct": 1, "param": [1, 6, 7], "surfac": 1, "colour": 1, "function": [1, 3, 5, 7], "user": 1, "defin": [1, 3, 4, 5, 7], "take": [1, 4, 5, 7], "option": [1, 4, 7], "list": [1, 2, 7], "singl": [1, 7], "valu": [1, 4, 6, 7], "pass": [1, 4, 5, 6, 7], "f": [1, 7], "need": [1, 4, 5, 7], "empti": 1, "scan": 1, "nxn": 1, "evalu": 1, "accord": 1, "x": [1, 4, 5], "axi": 1, "y": 1, "scan_dop": 1, "none": [1, 2, 4, 5, 7], "grid": 1, "sampl": [1, 7], "system": 1, "prior": 1, "call": [1, 2, 5, 7], "label": 1, "unrefin": 1, "default": [1, 6, 7], "dope_unrefin": 1, "dope_refin": 1, "energi": [1, 4, 7], "minimis": 1, "scan_error": 1, "s_kei": 1, "network_rmsd": 1, "z_kei": 1, "network_z_drift": 1, "z": 1, "drift": 1, "scan_error_from_target": 1, "index": [1, 3], "landscap": 1, "v": 1, "target": 1, "should": [1, 4, 5, 7], "datset": 1, "int": [1, 4, 5, 6, 7], "select": [1, 2], "from": [1, 2, 3, 4, 5, 7], "multipl": [1, 2, 7], "scan_ramachandran": 1, "four": 1, "memori": 1, "ramachandran_favor": 1, "ramachandran_allow": 1, "ramachandran_outli": 1, "ramachandran_tot": 1, "ramachandran_favour": 1, "ratio": [1, 2], "residu": [1, 2, 5], "favour": 1, "data": [1, 3, 7], "atomselect": [1, 2], "pdbdata": [1, 2, 3, 7], "object": [1, 2, 4, 7], "all": [1, 2, 4, 5, 7], "set_decod": 1, "set_encod": 1, "coord": [1, 6], "set_network": 1, "setup_grid": 1, "64": 1, "bounds_from": 1, "bound": 1, "pad": 1, "regularli": 1, "size": [1, 4, 7], "": 1, "us": [1, 2, 3, 4, 5, 6, 7], "refer": [1, 4], "string": [1, 2], "tupl": [1, 4, 6], "xmin": 1, "xmax": 1, "ymin": 1, "ymax": 1, "float": [1, 4, 5, 6, 7], "extra": [1, 7], "boundari": 1, "condit": 1, "dimens": [1, 5], "molearngui": [1, 3], "ma": 1, "produc": [1, 7], "interact": 1, "visualis": 1, "store": 1, "viewabl": 1, "within": [1, 5, 6, 7], "notebook": 1, "instanc": [1, 4, 5, 6, 7], "gui": [1, 3], "get_path": [1, 3], "idx_start": 1, "idx_end": 1, "xval": 1, "yval": 1, "smooth": 1, "shortest": 1, "path": [1, 2], "two": [1, 2], "weight": [1, 7], "2d": 1, "start": [1, 7], "actual": 1, "kernel": 1, "averag": [1, 7], "must": [1, 2, 4, 7], "each": [1, 2, 4, 6, 7], "lanscap": 1, "get_path_aggreg": [1, 3], "input_is_index": 1, "fals": [1, 2, 4, 6, 7], "chain": 1, "give": [1, 7], "waypoint": 1, "assum": [1, 7], "graph": 1, "otherwis": [1, 2], "oversampl": [1, 3], "pt": 1, "add": [1, 7], "equal": 1, "interv": 1, "mx2": 1, "fix_termin": 2, "enabl": 2, "manipul": 2, "multi": 2, "pdb": [2, 6, 7], "suitabl": 2, "import_pdb": 2, "ignore_atom": 2, "extract": [2, 7], "onli": [2, 4], "interest": 2, "least": 2, "onc": [2, 7], "instanti": 2, "separ": 2, "no_hydrogen": 2, "renam": [2, 7], "ot1": 2, "oxygen": 2, "o": [2, 4], "ot2": 2, "dure": [2, 4, 6, 7], "ca": [2, 4], "cb": [2, 4], "No": 2, "templat": 2, "found": 2, "openmm_loss": 2, "altern": [2, 7], "solut": 2, "instead": [2, 5], "get_atominfo": 2, "everi": [2, 5, 6, 7], "line": 2, "resid": 2, "get_dataload": [2, 7], "batch_siz": [2, 4, 7], "validation_split": 2, "pin_memori": 2, "dataset_sample_s": 2, "manual_se": 2, "shuffl": 2, "sampler": 2, "torch": [2, 4, 7], "util": 2, "dataload": [2, 7], "set": [2, 7], "valid": [2, 7], "valid_s": 2, "train_siz": 2, "randomli": 2, "assign": 2, "specifi": 2, "spefici": 2, "seed": 2, "initialis": [2, 6, 7], "random": [2, 7], "split": 2, "replic": 2, "specif": 2, "multipdb": [2, 3], "command": 2, "time": [2, 7], "mani": [2, 4, 7], "featur": 2, "same": [2, 7], "prepare_dataset": 2, "normalis": [2, 7], "convert": 2, "readi": 2, "arg": [2, 4, 5, 7], "other": [2, 7], "correspond": [2, 4, 6, 7], "manual": 2, "streamlin": 3, "implement": [3, 7], "learn": [3, 7], "exampl": [3, 6, 7], "obtain": [3, 7], "experi": 3, "molecular": 3, "simul": 3, "loss": [3, 7], "against": 3, "protocol": [3, 7], "analys": [3, 6], "result": [3, 6, 7], "modifiedforcefield": [3, 4], "openmmpluginscoresoftforcefield": [3, 4], "openmmpluginscor": [3, 4], "openmmtorchenergyminim": [3, 4], "openmm_clamped_energy_funct": [3, 4], "openmm_energi": [3, 4, 7], "openmm_energy_funct": [3, 4], "foldingnet": 3, "autoencod": [3, 5, 7], "cnn": 3, "trainer": 3, "openmm_physics_train": [3, 7], "torch_physics_train": [3, 7], "dope_scor": [3, 6], "parallel_dope_scor": [3, 6], "ramachandran_scor": [3, 6], "parallel_ramachandran_scor": [3, 6], "analysi": 3, "frequent": 3, "ask": 3, "question": 3, "get": [3, 6, 7], "indexerror": 3, "when": [3, 7], "try": [3, 7], "cannot": 3, "instal": 3, "openmmtorchplugin": 3, "The": [3, 4, 7], "freez": 3, "doe": [3, 7], "work": 3, "expect": [3, 6], "pleas": [3, 7], "github": 3, "page": 3, "instruct": 3, "script": 3, "modul": [3, 4, 5, 6, 7], "search": [3, 7], "alternative_residue_nam": 4, "app": 4, "forcefield": 4, "plu": 4, "describ": 4, "dict": [4, 7], "alias": 4, "resnam": 4, "hi": 4, "hie": 4, "mol": [4, 6, 7], "platform": 4, "pldataload": 4, "taken": 4, "neither": 4, "xml_file": 4, "xml": 4, "remove_nb": 4, "remov": 4, "nonbondedforc": 4, "customgbforc": 4, "cmmotionremov": 4, "els": [4, 7], "just": [4, 6], "soft": 4, "amber14": 4, "hse": 4, "forc": [4, 7], "intent": 4, "fast": 4, "enough": 4, "abl": 4, "b": [4, 6], "torchintegratorplugin": 4, "gpu": 4, "doubl": 4, "cpu": 4, "execut": 4, "dtype": 4, "devic": [4, 7], "get_energi": 4, "pos_ptr": 4, "force_ptr": 4, "energy_ptr": 4, "n_particl": 4, "data_ptr": 4, "particl": 4, "batch": [4, 7], "static": 4, "backward": [4, 7], "ctx": 4, "grad_output": 4, "formula": 4, "differenti": 4, "oper": [4, 7], "mode": [4, 7], "automat": [4, 7], "alia": 4, "vjp": 4, "overridden": [4, 5, 7], "subclass": [4, 5, 7], "It": [4, 5, 7], "accept": 4, "context": [4, 6, 7], "first": [4, 5], "argument": [4, 6, 7], "forward": [4, 5], "non": 4, "were": 4, "gradient": [4, 6, 7], "w": 4, "r": [4, 5, 6], "t": 4, "grad": 4, "retriev": [4, 6, 7], "also": [4, 6, 7], "attribut": 4, "needs_input_grad": 4, "boolean": 4, "repres": 4, "whether": [4, 7], "comput": [4, 5, 7], "clamp": [4, 7], "std": [4, 7], "initi": [4, 5, 6], "intern": [4, 5, 7], "state": [4, 5, 7], "share": [4, 5], "both": [4, 5, 7], "nn": [4, 5, 7], "scriptmodul": [4, 5], "architectur": 5, "deriv": 5, "perform": [5, 7], "although": 5, "recip": 5, "one": [5, 6, 7], "afterward": 5, "sinc": 5, "former": [5, 7], "care": 5, "regist": 5, "hook": 5, "while": 5, "latter": 5, "silent": 5, "ignor": 5, "them": [5, 7], "cnn_autoencod": 5, "init_z": 5, "32": 5, "latent_z": 5, "depth": 5, "4": 5, "5": [5, 7], "droprat": 5, "our": 5, "ramaswami": 5, "2021": 5, "paper": 5, "larg": [5, 6], "supersed": 5, "channel": 5, "layer": 5, "scale": [5, 7], "factor": [5, 7], "dictat": 5, "subsequ": 5, "block": [5, 7], "dropout": 5, "rate": [5, 7], "without": 6, "map": 6, "directli": 6, "One": [6, 7], "gain": 6, "access": [6, 7], "topologi": 6, "temporari": [6, 7], "reload": 6, "get_all_dop": 6, "ndarrai": [6, 7], "relax": 6, "maximum": [6, 7], "50": 6, "step": [6, 7], "conjug": 6, "descent": 6, "type": [6, 7], "np": 6, "inject": 6, "mdl": 6, "build_method": 6, "internal_coordin": 6, "initialize_xyz": 6, "miss": 6, "stage": 6, "we": [6, 7], "fix": 6, "1e10": 6, "conjugategradi": 6, "simpli": [6, 7], "process": 6, "spawn": 6, "multiprocess": 6, "case": [6, 7], "would": 6, "look": 6, "score_class": 6, "coordinates_arrai": 6, "append": [6, 7], "get_scor": 6, "asynchron": 6, "background": 6, "thread": 6, "pool": 6, "control": [6, 7], "addit": [6, 7], "multiproces": 6, "threshold": 6, "001": [6, 7], "iotbx": 6, "mmtbx": 6, "calul": 6, "phi": 6, "psi": 6, "1e": [6, 7], "determin": [6, 7], "similar": 6, "wa": 6, "as_ratio": 6, "self": [6, 7], "cctbux": 6, "ramalyz": 6, "count": 6, "torsion": [6, 7], "angl": [6, 7], "fall": 6, "favor": 6, "allow": 6, "outlier": 6, "region": 6, "final": [6, 7], "total": 6, "rtype": 6, "melucel": 6, "fram": 6, "therad": 6, "log_filenam": 7, "log_fil": 7, "dat": 7, "base": 7, "variabl": 7, "set_autoencod": 7, "_autoencoder_kwarg": 7, "checkpoint": 7, "under": 7, "optimis": 7, "epoch": 7, "best": 7, "best_nam": 7, "standard": 7, "deviat": 7, "unscal": 7, "being": 7, "mai": 7, "thirdparti": 7, "train_dataload": 7, "valid_dataload": 7, "_data": 7, "set_data": 7, "determinin": 7, "is_avail": 7, "default_log_filenam": 7, "json": 7, "log": 7, "valid_log": 7, "checkpoint_fold": 7, "loss_kei": 7, "valid_loss": 7, "last": 7, "ckpt": 7, "better": 7, "than": 7, "replac": 7, "checkpoint_epoch": 7, "_loss": 7, "delet": 7, "folder": 7, "common_step": 7, "train_step": 7, "valid_step": 7, "mean": 7, "squar": 7, "_intern": 7, "respect": 7, "wish": 7, "elsewher": 7, "mini": 7, "To": 7, "recov": 7, "origin": 7, "mse_loss": 7, "get_network_summari": 7, "inform": 7, "about": 7, "learning_rate_sweep": 7, "max_lr": 7, "100": 7, "min_lr": 7, "05": 7, "number_of_iter": 7, "1000": 7, "checkpoint_sweep": 7, "train_on": 7, "deprec": 7, "sweep": 7, "over": 7, "good": 7, "cycl": 7, "polici": 7, "what": 7, "len": 7, "min": 7, "iter": 7, "nan": 7, "word": 7, "load_checkpoint": 7, "checkpoint_nam": 7, "load_optimis": 7, "begin": 7, "checkpoint_": 7, "charact": 7, "lowest": 7, "whithin": 7, "log_dict": 7, "verbos": 7, "Then": 7, "content": 7, "dump": 7, "print": 7, "prepare_optimis": 7, "lr": 7, "weight_decai": 7, "0001": 7, "optimiser_kwarg": 7, "adamw": 7, "With": 7, "do": 7, "adaww": 7, "onto": 7, "max_epoch": 7, "log_fold": 7, "checkpoint_frequ": 7, "allow_n_failur": 7, "loop": 7, "train_epoch": 7, "valid_epoch": 7, "scheduler_step": 7, "until": 7, "match": 7, "alreadi": 7, "exist": 7, "directori": 7, "frequenc": 7, "lower": 7, "divis": 7, "how": 7, "restart": 7, "except": 7, "rais": 7, "continu": 7, "well": 7, "written": 7, "noth": 7, "design": 7, "schedul": 7, "set_dataload": 7, "zero": 7, "zero_grad": 7, "keyword": 7, "updat": 7, "aggreg": 7, "train_": 7, "prepend": 7, "howev": 7, "These": 7, "entri": 7, "update_optimiser_hyperparamet": 7, "optimes": 7, "hyperparamet": 7, "1e3": 7, "pair": 7, "insert": 7, "no_grad": 7, "eval": 7, "valid_": 7, "physics_loss": 7, "prepare_phys": 7, "common_physics_step": 7, "interpol": 7, "adjac": 7, "vector": 7, "term": 7, "n_atom": 7, "physics_scaling_factor": 7, "clamp_threshold": 7, "100000000": 7, "start_physics_at": 7, "loss_funct": 7, "psf": 7, "As": 7, "yet": 7, "unus": 7, "adit": 7, "overrid": 7, "physic": 7, "sum": 7, "mathemat": 7, "cancel": 7, "becaus": 7, "manag": 7, "essenti": 7, "arbitari": 7, "exactli": 7, "proport": 7, "ration": 7, "final_loss": 7, "super": 7, "bond": 7, "func": 7, "torchproteinenergi": 7, "rel": 7}, "objects": {"molearn.analysis": [[1, 0, 1, "", "MolearnAnalysis"], [1, 0, 1, "", "MolearnGUI"], [1, 2, 0, "-", "path"]], "molearn.analysis.MolearnAnalysis": [[1, 1, 1, "", "generate"], [1, 1, 1, "", "get_all_dope_score"], [1, 1, 1, "", "get_all_ramachandran_score"], [1, 1, 1, "", "get_dataset"], [1, 1, 1, "", "get_decoded"], [1, 1, 1, "", "get_dope"], [1, 1, 1, "", "get_encoded"], [1, 1, 1, "", "get_error"], [1, 1, 1, "", "get_ramachandran"], [1, 1, 1, "", "num_trainable_params"], [1, 1, 1, "", "reference_dope_score"], [1, 1, 1, "", "scan_custom"], [1, 1, 1, "", "scan_dope"], [1, 1, 1, "", "scan_error"], [1, 1, 1, "", "scan_error_from_target"], [1, 1, 1, "", "scan_ramachandran"], [1, 1, 1, "", "set_dataset"], [1, 1, 1, "", "set_decoded"], [1, 1, 1, "", "set_encoded"], [1, 1, 1, "", "set_network"], [1, 1, 1, "", "setup_grid"]], "molearn.analysis.path": [[1, 3, 1, "", "get_path"], [1, 3, 1, "", "get_path_aggregate"], [1, 3, 1, "", "oversample"]], "molearn.data": [[2, 0, 1, "", "PDBData"]], "molearn.data.PDBData": [[2, 1, 1, "", "atomselect"], [2, 1, 1, "", "fix_terminal"], [2, 1, 1, "", "frame"], [2, 1, 1, "", "get_atominfo"], [2, 1, 1, "", "get_dataloader"], [2, 1, 1, "", "get_datasets"], [2, 1, 1, "", "import_pdb"], [2, 1, 1, "", "prepare_dataset"], [2, 1, 1, "", "split"]], "molearn.loss_functions": [[4, 2, 0, "-", "openmm_thread"], [4, 2, 0, "-", "torch_protein_energy"]], "molearn.loss_functions.openmm_thread": [[4, 0, 1, "", "ModifiedForceField"], [4, 0, 1, "", "OpenMMPluginScoreSoftForceField"], [4, 0, 1, "", "OpenmmPluginScore"], [4, 0, 1, "", "OpenmmTorchEnergyMinimizer"], [4, 0, 1, "", "openmm_clamped_energy_function"], [4, 0, 1, "", "openmm_energy"], [4, 0, 1, "", "openmm_energy_function"]], "molearn.loss_functions.openmm_thread.OpenmmPluginScore": [[4, 1, 1, "", "execute"], [4, 1, 1, "", "get_energy"]], "molearn.loss_functions.openmm_thread.openmm_clamped_energy_function": [[4, 1, 1, "", "backward"], [4, 1, 1, "", "forward"]], "molearn.loss_functions.openmm_thread.openmm_energy_function": [[4, 1, 1, "", "backward"], [4, 1, 1, "", "forward"]], "molearn.models.CNN_autoencoder": [[5, 0, 1, "", "Autoencoder"]], "molearn.models.foldingnet": [[5, 0, 1, "", "AutoEncoder"]], "molearn.models.foldingnet.AutoEncoder": [[5, 1, 1, "", "forward"]], "molearn.scoring": [[6, 0, 1, "", "DOPE_Score"], [6, 0, 1, "", "Parallel_DOPE_Score"], [6, 0, 1, "", "Parallel_Ramachandran_Score"], [6, 0, 1, "", "Ramachandran_Score"]], "molearn.scoring.DOPE_Score": [[6, 1, 1, "", "get_all_dope"], [6, 1, 1, "", "get_dope"]], "molearn.scoring.Parallel_DOPE_Score": [[6, 1, 1, "", "get_score"]], "molearn.scoring.Parallel_Ramachandran_Score": [[6, 1, 1, "", "get_score"]], "molearn.scoring.Ramachandran_Score": [[6, 1, 1, "", "get_score"]], "molearn.trainers": [[7, 0, 1, "", "OpenMM_Physics_Trainer"], [7, 0, 1, "", "Torch_Physics_Trainer"], [7, 0, 1, "", "Trainer"]], "molearn.trainers.OpenMM_Physics_Trainer": [[7, 1, 1, "", "common_physics_step"], [7, 1, 1, "", "prepare_physics"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "valid_step"]], "molearn.trainers.Torch_Physics_Trainer": [[7, 1, 1, "", "common_physics_step"], [7, 1, 1, "", "prepare_physics"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "valid_step"]], "molearn.trainers.Trainer": [[7, 1, 1, "", "checkpoint"], [7, 1, 1, "", "common_step"], [7, 1, 1, "", "get_network_summary"], [7, 1, 1, "", "learning_rate_sweep"], [7, 1, 1, "", "load_checkpoint"], [7, 1, 1, "", "log"], [7, 1, 1, "", "prepare_optimiser"], [7, 1, 1, "", "run"], [7, 1, 1, "", "scheduler_step"], [7, 1, 1, "", "set_autoencoder"], [7, 1, 1, "", "set_data"], [7, 1, 1, "", "set_dataloader"], [7, 1, 1, "", "train_epoch"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "update_optimiser_hyperparameters"], [7, 1, 1, "", "valid_epoch"], [7, 1, 1, "", "valid_step"]]}, "objtypes": {"0": "py:class", "1": "py:method", "2": "py:module", "3": "py:function"}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "method", "Python method"], "2": ["py", "module", "Python module"], "3": ["py", "function", "Python function"]}, "titleterms": {"frequent": 0, "ask": 0, "question": 0, "i": 0, "get": 0, "an": 0, "indexerror": 0, "when": 0, "try": 0, "load": [0, 2], "multipdb": 0, "cannot": 0, "instal": 0, "openmmtorchplugin": 0, "The": 0, "gui": 0, "freez": 0, "us": 0, "doe": 0, "work": 0, "expect": 0, "analysi": 1, "data": 2, "welcom": 3, "molearn": 3, "": 3, "document": 3, "content": 3, "indic": 3, "tabl": 3, "loss": 4, "function": 4, "foldingnet": 5, "model": 5, "cnn": 5, "score": 6, "trainer": 7}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "sphinx": 60}, "alltitles": {"Analysis": [[1, "analysis"]], "Data Loading": [[2, "data-loading"]], "Welcome to molearn\u2019s documentation!": [[3, "welcome-to-molearn-s-documentation"]], "Contents:": [[3, null]], "Indices and tables": [[3, "indices-and-tables"]], "Loss Functions": [[4, "module-molearn.loss_functions.openmm_thread"]], "FoldingNet Model": [[5, "foldingnet-model"]], "CNN model": [[5, "cnn-model"]], "Scoring": [[6, "scoring"]], "Trainers": [[7, "trainers"]], "Frequently Asked Questions": [[0, "frequently-asked-questions"]], "I cannot install openmmtorchplugin": [[0, "i-cannot-install-openmmtorchplugin"]], "I get an IndexError when I try loading a multiPDB": [[0, "i-get-an-indexerror-when-i-try-loading-a-multipdb"]], "The GUI freezes when I use it/does not work as expected": [[0, "the-gui-freezes-when-i-use-it-does-not-work-as-expected"]]}, "indexentries": {}}) \ No newline at end of file +Search.setIndex({"docnames": ["FAQ", "analysis", "data", "index", "loss_functions", "models", "scoring", "trainers"], "filenames": ["FAQ.rst", "analysis.rst", "data.rst", "index.rst", "loss_functions.rst", "models.rst", "scoring.rst", "trainers.rst"], "titles": ["Frequently Asked Questions", "Analysis", "Data Loading", "Welcome to molearn\u2019s documentation!", "Loss Functions", "Models", "Scoring", "Trainers"], "terms": {"depend": 0, "conda": 0, "forg": 0, "build": [0, 1, 3, 6], "pytorch": [0, 7], "openmm": [0, 4, 7], "due": 0, "thi": [0, 1, 2, 3, 4, 5, 6, 7], "window": 0, "support": [0, 4], "can": [0, 2, 4, 7], "carri": 0, "out": [0, 7], "via": [0, 1, 3, 7], "termin": [0, 2], "c": [0, 2, 4], "follow": [0, 4, 7], "python": [0, 3], "version": 0, "ar": [0, 1, 2, 4, 6, 7], "3": [0, 1, 4, 6, 7], "8": [0, 7], "9": 0, "10": [0, 1, 7], "11": 0, "If": [0, 1, 2, 4, 6, 7], "you": [0, 4, 7], "run": [0, 1, 5, 7], "ani": [0, 4, 6, 7], "issu": 0, "either": [0, 1, 2], "runtim": 0, "ensur": 0, "have": [0, 2, 4, 7], "plugin": [0, 4], "1": [0, 1, 2, 5, 6, 7], "previou": 0, "ones": 0, "known": 0, "compat": 0, "easiest": 0, "wai": 0, "most": 0, "up": [0, 7], "date": 0, "molearn": [0, 1, 5, 7], "fresh": 0, "new": [0, 4, 7], "environ": 0, "creat": [0, 1, 2, 6, 7], "name": [0, 1, 2], "test_env": 0, "n": [0, 1, 2, 4, 6], "built": 0, "cuda_compiler_vers": 0, "2": [0, 7], "ci": 0, "tool": [0, 3], "ha": [0, 4, 7], "been": [0, 2, 7], "successfulli": 0, "test": 0, "ubuntu": 0, "machin": [0, 3], "driver": 0, "525": 0, "105": 0, "17": 0, "see": [0, 3, 7], "nvidia": 0, "smi": 0, "output": [0, 1, 4, 7], "websit": 0, "tabul": 0, "minimum": 0, "requir": [0, 1, 4, 7], "cuda": [0, 4, 7], "toolkit": 0, "like": [0, 6], "error": [0, 1, 4, 6, 7], "thrown": [0, 4, 6], "mdanalysi": 0, "typic": [0, 6, 7], "happen": [0, 7], "attempt": 0, "file": [0, 2, 4, 6, 7], "save": [0, 1, 4, 6, 7], "softwar": 0, "vmd": 0, "which": [0, 7], "differ": [0, 7], "syntax": 0, "indic": [0, 1], "end": [0, 1], "conform": [0, 1, 3], "A": [0, 6, 7], "around": [0, 1], "re": 0, "format": 0, "pars": 0, "e": [0, 4, 7], "g": [0, 4, 7], "biobox": [0, 2, 4, 6, 7], "import": [0, 2, 7], "bb": 0, "m": [0, 1, 5], "molecul": [0, 2, 4, 6, 7], "filenam": [0, 2, 7], "write_pdb": 0, "newfilenam": 0, "usual": [0, 7], "caus": 0, "packag": [0, 3, 7], "handl": [0, 3], "commun": 0, "between": [0, 1, 5, 7], "jupyt": [0, 1], "here": [0, 4], "current": [0, 4, 7], "workaround": 0, "older": 0, "tornado": 0, "In": 0, "observ": 0, "yield": [0, 1], "correct": 0, "behaviour": 0, "ipywidget": 0, "0": [0, 1, 2, 4, 5, 7], "7": 0, "nglview": 0, "6": 0, "streamlin": 3, "implement": [3, 7], "learn": [3, 7], "model": [1, 3, 6], "dedic": [1, 3], "gener": [1, 2, 3, 7], "protein": [1, 3, 6, 7], "from": [1, 2, 3, 4, 5, 7], "exampl": [3, 6, 7], "data": [1, 3, 7], "obtain": [3, 7], "experi": 3, "molecular": 3, "simul": 3, "provid": [1, 2, 3], "load": [1, 3, 6, 7], "ensembl": [1, 3], "defin": [1, 3, 4, 5, 7], "loss": [3, 7], "function": [1, 3, 5, 7], "train": [1, 2, 3, 4, 7], "against": 3, "protocol": [3, 7], "analys": [3, 6], "result": [3, 6, 7], "foldingnet": 5, "cnn": [], "trainer": 3, "score": [1, 3, 7], "analysi": 3, "frequent": 3, "ask": 3, "question": 3, "i": 3, "cannot": 3, "instal": 3, "openmmtorchplugin": 3, "get": [3, 6, 7], "an": [1, 2, 3, 4, 7], "indexerror": 3, "when": [3, 7], "try": [3, 7], "multipdb": [2, 3], "The": [3, 4, 7], "gui": [1, 3], "freez": 3, "us": [1, 2, 3, 4, 5, 6, 7], "doe": [3, 7], "work": 3, "expect": [3, 6], "pleas": [3, 7], "github": 3, "page": 3, "instruct": 3, "script": 3, "index": [1, 3], "modul": [3, 4, 5, 7], "search": [3, 7], "cnn_autoencod": [], "class": [1, 2, 4, 5, 6, 7], "pdbdata": [1, 2, 7], "none": [1, 2, 4, 5, 7], "fix_termin": 2, "fals": [1, 2, 4, 6, 7], "atom": [1, 2, 4, 6, 7], "sourc": [1, 2, 4, 5, 6, 7], "object": [1, 2, 4, 7], "enabl": 2, "manipul": 2, "multi": 2, "pdb": [2, 6, 7], "dataset": [1, 2, 7], "suitabl": 2, "paramet": [1, 2, 4, 5, 6, 7], "str": [1, 2, 4, 7], "list": [1, 2, 7], "string": [1, 2], "import_pdb": 2, "call": [1, 2, 5, 7], "each": [1, 2, 4, 6, 7], "true": [1, 2, 4, 6, 7], "after": [1, 2, 7], "befor": [1, 2, 7], "atomselect": [1, 2], "ignore_atom": 2, "all": [1, 2, 4, 5, 7], "extract": [2, 7], "onli": [2, 4], "interest": 2, "must": [1, 2, 4, 7], "least": 2, "onc": [2, 7], "instanti": 2, "separ": 2, "no_hydrogen": 2, "renam": [2, 7], "ot1": 2, "oxygen": 2, "o": [2, 4], "ot2": 2, "otherwis": [1, 2], "select": [1, 2], "dure": [2, 4, 6, 7], "ca": [2, 4], "cb": [2, 4], "No": 2, "templat": 2, "found": 2, "residu": [1, 2, 5], "openmm_loss": 2, "altern": [2, 7], "solut": 2, "instead": [2, 5], "frame": [1, 2, 6, 7], "return": [1, 2, 4, 6, 7], "get_atominfo": 2, "where": [1, 2, 7], "everi": [2, 5, 6, 7], "line": 2, "contain": [1, 2, 6, 7], "resid": 2, "get_dataload": [2, 7], "batch_siz": [2, 4, 7], "validation_split": 2, "pin_memori": 2, "dataset_sample_s": 2, "manual_se": 2, "shuffl": 2, "sampler": 2, "torch": [2, 4, 7], "util": 2, "dataload": [2, 7], "set": [2, 7], "valid": [2, 7], "get_dataset": [1, 2], "valid_s": 2, "train_siz": 2, "ratio": [1, 2], "randomli": 2, "assign": 2, "specifi": 2, "number": [1, 2, 4, 5, 6, 7], "structur": [1, 2, 6, 7], "spefici": 2, "seed": 2, "initialis": [2, 6, 7], "random": [2, 7], "split": 2, "replic": 2, "specif": 2, "two": [1, 2], "tensor": [1, 2, 4, 6, 7], "command": 2, "multipl": [1, 2, 7], "time": [2, 7], "mani": [2, 4, 7], "featur": 2, "same": [2, 7], "path": [1, 2], "prepare_dataset": 2, "normalis": [2, 7], "convert": 2, "readi": 2, "arg": [2, 4, 5, 6, 7], "kwarg": [1, 2, 4, 5, 6, 7], "other": [2, 7], "correspond": [2, 4, 7], "manual": 2, "modifiedforcefield": 4, "alternative_residue_nam": 4, "take": [1, 4, 5, 7], "app": 4, "forcefield": 4, "plu": 4, "option": [1, 4, 7], "describ": 4, "dict": [4, 7], "alias": 4, "resnam": 4, "hi": 4, "hie": 4, "openmmpluginscoresoftforcefield": 4, "mol": [4, 6, 7], "platform": 4, "pldataload": 4, "given": [1, 4, 7], "taken": 4, "neither": 4, "xml_file": [4, 7], "xml": 4, "refer": [1, 4], "remove_nb": 4, "bool": [1, 4, 6, 7], "remov": 4, "nonbondedforc": 4, "customgbforc": 4, "cmmotionremov": 4, "els": [4, 7], "just": [4, 6], "soft": 4, "openmmpluginscor": 4, "amber14": 4, "hse": 4, "calcul": [1, 4, 6, 7], "forc": [4, 7], "energi": [1, 4, 7], "intent": 4, "fast": 4, "enough": 4, "abl": 4, "b": [4, 6], "torchintegratorplugin": 4, "float": [1, 4, 5, 6, 7], "gpu": 4, "doubl": 4, "cpu": 4, "execut": 4, "x": [1, 4, 5], "shape": [1, 4, 6, 7], "dtype": 4, "devic": [4, 7], "get_energi": 4, "pos_ptr": 4, "force_ptr": 4, "energy_ptr": 4, "n_particl": 4, "data_ptr": 4, "int": [1, 4, 5, 6, 7], "particl": 4, "batch": [4, 7], "size": [1, 4, 7], "openmmtorchenergyminim": 4, "openmm_clamped_energy_funct": 4, "static": 4, "backward": [4, 7], "ctx": 4, "grad_output": 4, "formula": 4, "differenti": 4, "oper": [4, 7], "mode": [4, 7], "automat": [4, 7], "overridden": [4, 5, 7], "subclass": [4, 5, 7], "equival": 4, "vjp": 4, "It": [4, 5, 7], "accept": 4, "context": [4, 6, 7], "first": [4, 5], "argument": [4, 6, 7], "forward": [4, 5], "pass": [1, 4, 5, 6, 7], "non": 4, "should": [1, 4, 5, 7], "were": 4, "input": [1, 4], "gradient": [4, 6, 7], "w": 4, "r": [4, 5, 6], "t": 4, "valu": [1, 4, 6, 7], "grad": 4, "retriev": [4, 6, 7], "also": [4, 6, 7], "attribut": 4, "needs_input_grad": 4, "tupl": [1, 4], "boolean": 4, "repres": 4, "whether": [4, 7], "need": [1, 4, 5, 7], "comput": [4, 5, 7], "clamp": [4, 7], "instanc": [1, 4, 5, 6, 7], "openmm_energi": [4, 7], "std": [4, 7], "initi": [4, 5, 6], "intern": [4, 5, 7], "state": [4, 5, 7], "share": [4, 5], "both": [4, 5, 7], "nn": [4, 5, 7], "scriptmodul": [4, 5], "openmm_energy_funct": 4, "autoencod": [5, 7], "architectur": 5, "deriv": 5, "perform": [5, 7], "although": 5, "recip": 5, "within": [1, 5, 7], "one": [5, 6, 7], "afterward": 5, "sinc": 5, "former": [5, 7], "care": 5, "regist": 5, "hook": 5, "while": 5, "latter": 5, "silent": 5, "ignor": 5, "them": [5, 7], "init_z": 5, "32": 5, "latent_z": 5, "depth": 5, "4": 5, "5": [5, 7], "droprat": 5, "our": 5, "ramaswami": 5, "2021": 5, "paper": 5, "larg": [5, 6], "supersed": 5, "channel": 5, "layer": 5, "latent": [1, 5, 7], "space": [1, 5], "dimens": [1, 5], "scale": [5, 7], "factor": [5, 7], "dictat": 5, "subsequ": 5, "block": [5, 7], "dropout": 5, "rate": [5, 7], "dope_scor": 6, "method": [1, 6, 7], "dope": [1, 6], "without": 6, "coordin": [1, 6], "map": 6, "directli": 6, "One": [6, 7], "gain": 6, "access": [6, 7], "topolog": 6, "temporari": [6, 7], "reload": 6, "get_all_dop": 6, "coord": [1, 6], "refin": [1, 6], "arrai": [1, 6, 7], "numpi": [1, 6, 7], "ndarrai": [6, 7], "default": [1, 6, 7], "relax": 6, "maximum": [6, 7], "50": 6, "step": [6, 7], "conjug": 6, "descent": 6, "type": [6, 7], "np": 6, "get_dop": [1, 6], "inject": 6, "mdl": 6, "build_method": 6, "internal_coordin": 6, "initialize_xyz": 6, "reconstruct": [1, 6], "miss": 6, "stage": 6, "we": [6, 7], "fix": 6, "1e10": 6, "conjugategradi": 6, "simpli": [6, 7], "parallel_dope_scor": 6, "process": 6, "spawn": 6, "multiprocess": 6, "case": [6, 7], "would": 6, "look": 6, "score_class": 6, "coordinates_arrai": 6, "append": [6, 7], "get_scor": 6, "asynchron": 6, "background": 6, "thread": 6, "pool": 6, "control": [6, 7], "addit": [6, 7], "multiproces": 6, "ramachandran_scor": 6, "parallel_ramachandran_scor": 6, "log_filenam": 7, "log_fil": 7, "dat": 7, "base": 7, "variabl": 7, "network": [1, 7], "encod": [1, 7], "decod": [1, 7], "weight": [1, 7], "associ": [1, 7], "set_autoencod": 7, "_autoencoder_kwarg": 7, "checkpoint": 7, "under": 7, "kei": [1, 7], "optimis": 7, "optim": [1, 7], "self": 7, "epoch": 7, "best": 7, "best_nam": 7, "standard": 7, "deviat": 7, "unscal": 7, "produc": [1, 7], "singl": [1, 7], "being": 7, "mai": 7, "thirdparti": 7, "train_dataload": 7, "valid_dataload": 7, "_data": 7, "set_data": 7, "determinin": 7, "is_avail": 7, "default_log_filenam": 7, "json": 7, "log": 7, "valid_log": 7, "checkpoint_fold": 7, "loss_kei": 7, "valid_loss": 7, "last": 7, "ckpt": 7, "better": 7, "than": 7, "replac": 7, "f": [1, 7], "checkpoint_epoch": 7, "_loss": 7, "delet": 7, "dictionari": [1, 7], "folder": 7, "common_step": 7, "train_step": 7, "valid_step": 7, "mean": 7, "squar": 7, "_intern": 7, "respect": 7, "wish": 7, "elsewher": 7, "mini": 7, "To": 7, "recov": 7, "origin": 7, "mse_loss": 7, "get_network_summari": 7, "inform": 7, "about": 7, "learning_rate_sweep": 7, "max_lr": 7, "100": 7, "min_lr": 7, "1e": 7, "05": 7, "number_of_iter": 7, "1000": 7, "checkpoint_sweep": 7, "train_on": 7, "deprec": 7, "sweep": 7, "over": 7, "find": [1, 7], "good": 7, "cycl": 7, "polici": 7, "final": 7, "start": [1, 7], "what": 7, "len": 7, "min": 7, "iter": 7, "nan": 7, "word": 7, "load_checkpoint": 7, "checkpoint_nam": 7, "load_optimis": 7, "begin": 7, "checkpoint_": 7, "assum": [1, 7], "charact": 7, "lowest": 7, "whithin": 7, "log_dict": 7, "verbos": 7, "Then": 7, "content": 7, "dump": 7, "print": 7, "prepare_optimis": 7, "lr": 7, "001": 7, "weight_decai": 7, "0001": 7, "optimiser_kwarg": 7, "adamw": 7, "With": 7, "do": 7, "adaww": 7, "onto": 7, "max_epoch": 7, "log_fold": 7, "checkpoint_frequ": 7, "allow_n_failur": 7, "allow_grad_in_valid": 7, "loop": 7, "train_epoch": 7, "valid_epoch": 7, "scheduler_step": 7, "until": 7, "match": 7, "alreadi": 7, "exist": 7, "directori": 7, "frequenc": 7, "lower": 7, "divis": 7, "how": 7, "restart": 7, "except": 7, "rais": 7, "continu": 7, "well": 7, "written": 7, "noth": 7, "design": 7, "schedul": 7, "give": [1, 7], "param": [1, 7], "set_dataload": 7, "zero": 7, "zero_grad": 7, "determin": 7, "keyword": 7, "updat": 7, "aggreg": 7, "averag": [1, 7], "train_": 7, "prepend": 7, "howev": 7, "These": 7, "entri": 7, "update_optimiser_hyperparamet": 7, "optimes": 7, "hyperparamet": 7, "1e3": 7, "pair": 7, "insert": 7, "no_grad": 7, "eval": 7, "valid_": 7, "openmm_physics_train": 7, "extra": [1, 7], "physics_loss": 7, "prepare_phys": 7, "common_physics_step": 7, "interpol": 7, "adjac": 7, "sampl": [1, 7], "vector": 7, "term": 7, "n_atom": 7, "physics_scaling_factor": 7, "clamp_threshold": 7, "100000000": 7, "start_physics_at": 7, "soft_nb": 7, "loss_funct": 7, "psf": 7, "As": 7, "yet": 7, "unus": 7, "adit": 7, "overrid": 7, "add": [1, 7], "physic": 7, "sum": 7, "mathemat": 7, "cancel": 7, "becaus": 7, "manag": 7, "essenti": 7, "arbitari": 7, "exactli": 7, "proport": 7, "ration": 7, "final_loss": 7, "super": 7, "torch_physics_train": 7, "bond": 7, "angl": 7, "torsion": 7, "func": 7, "torchproteinenergi": 7, "rel": 7, "molearnanalysi": 1, "qualiti": 1, "crd": 1, "collect": 1, "nx2": 1, "cartesian": 1, "nxmx3": 1, "get_all_dope_scor": 1, "get_all_ramachandran_scor": 1, "ramachandran": 1, "conrdin": 1, "point": 1, "previous": 1, "set_dataset": 1, "get_decod": 1, "its": 1, "counterpart": 1, "get_encod": 1, "get_error": 1, "align": 1, "neural": 1, "rmsd": 1, "1d": 1, "get_ramachandran": 1, "num_trainable_param": 1, "trainabl": 1, "reference_dope_scor": 1, "scan_custom": 1, "fct": 1, "surfac": 1, "colour": 1, "user": 1, "empti": 1, "scan": 1, "nxn": 1, "evalu": 1, "accord": 1, "axi": 1, "y": 1, "scan_dop": 1, "grid": 1, "system": 1, "prior": 1, "label": 1, "unrefin": 1, "dope_unrefin": 1, "dope_refin": 1, "minimis": 1, "scan_error": 1, "s_kei": 1, "network_rmsd": 1, "z_kei": 1, "network_z_drift": 1, "z": 1, "drift": 1, "scan_error_from_target": 1, "landscap": 1, "vs": 1, "target": 1, "datset": 1, "scan_ramachandran": 1, "four": 1, "memori": 1, "ramachandran_favor": 1, "ramachandran_allow": 1, "ramachandran_outli": 1, "ramachandran_tot": 1, "ramachandran_favour": 1, "favour": 1, "set_decod": 1, "set_encod": 1, "set_network": 1, "setup_grid": 1, "64": 1, "bounds_from": 1, "bound": 1, "pad": 1, "regularli": 1, "s": 1, "xmin": 1, "xmax": 1, "ymin": 1, "ymax": 1, "boundari": 1, "condit": 1, "molearngui": 1, "ma": 1, "interact": 1, "visualis": 1, "store": 1, "viewabl": 1, "notebook": 1, "get_path": 1, "idx_start": 1, "idx_end": 1, "xval": 1, "yval": 1, "smooth": 1, "shortest": 1, "2d": 1, "actual": 1, "kernel": 1, "lanscap": 1, "get_path_aggreg": 1, "input_is_index": 1, "chain": 1, "waypoint": 1, "graph": 1, "oversampl": 1, "pt": 1, "equal": 1, "interv": 1, "mx2": 1}, "objects": {"molearn.analysis": [[1, 0, 1, "", "MolearnAnalysis"], [1, 0, 1, "", "MolearnGUI"], [1, 2, 0, "-", "path"]], "molearn.analysis.MolearnAnalysis": [[1, 1, 1, "", "generate"], [1, 1, 1, "", "get_all_dope_score"], [1, 1, 1, "", "get_all_ramachandran_score"], [1, 1, 1, "", "get_dataset"], [1, 1, 1, "", "get_decoded"], [1, 1, 1, "", "get_dope"], [1, 1, 1, "", "get_encoded"], [1, 1, 1, "", "get_error"], [1, 1, 1, "", "get_ramachandran"], [1, 1, 1, "", "num_trainable_params"], [1, 1, 1, "", "reference_dope_score"], [1, 1, 1, "", "scan_custom"], [1, 1, 1, "", "scan_dope"], [1, 1, 1, "", "scan_error"], [1, 1, 1, "", "scan_error_from_target"], [1, 1, 1, "", "scan_ramachandran"], [1, 1, 1, "", "set_dataset"], [1, 1, 1, "", "set_decoded"], [1, 1, 1, "", "set_encoded"], [1, 1, 1, "", "set_network"], [1, 1, 1, "", "setup_grid"]], "molearn.analysis.path": [[1, 3, 1, "", "get_path"], [1, 3, 1, "", "get_path_aggregate"], [1, 3, 1, "", "oversample"]], "molearn.data": [[2, 0, 1, "", "PDBData"]], "molearn.data.PDBData": [[2, 1, 1, "", "atomselect"], [2, 1, 1, "", "fix_terminal"], [2, 1, 1, "", "frame"], [2, 1, 1, "", "get_atominfo"], [2, 1, 1, "", "get_dataloader"], [2, 1, 1, "", "get_datasets"], [2, 1, 1, "", "import_pdb"], [2, 1, 1, "", "prepare_dataset"], [2, 1, 1, "", "split"]], "molearn.loss_functions": [[4, 2, 0, "-", "openmm_thread"], [4, 2, 0, "-", "torch_protein_energy"]], "molearn.loss_functions.openmm_thread": [[4, 0, 1, "", "ModifiedForceField"], [4, 0, 1, "", "OpenMMPluginScoreSoftForceField"], [4, 0, 1, "", "OpenmmPluginScore"], [4, 0, 1, "", "OpenmmTorchEnergyMinimizer"], [4, 0, 1, "", "openmm_clamped_energy_function"], [4, 0, 1, "", "openmm_energy"], [4, 0, 1, "", "openmm_energy_function"]], "molearn.loss_functions.openmm_thread.OpenmmPluginScore": [[4, 1, 1, "", "execute"], [4, 1, 1, "", "get_energy"]], "molearn.loss_functions.openmm_thread.openmm_clamped_energy_function": [[4, 1, 1, "", "backward"], [4, 1, 1, "", "forward"]], "molearn.loss_functions.openmm_thread.openmm_energy_function": [[4, 1, 1, "", "backward"], [4, 1, 1, "", "forward"]], "molearn.models.CNN_autoencoder": [[5, 0, 1, "", "Autoencoder"]], "molearn.models.foldingnet": [[5, 0, 1, "", "AutoEncoder"]], "molearn.models.foldingnet.AutoEncoder": [[5, 1, 1, "", "forward"]], "molearn.scoring": [[6, 0, 1, "", "DOPE_Score"], [6, 0, 1, "", "Parallel_DOPE_Score"], [6, 0, 1, "", "Parallel_Ramachandran_Score"], [6, 0, 1, "", "Ramachandran_Score"]], "molearn.scoring.DOPE_Score": [[6, 1, 1, "", "get_all_dope"], [6, 1, 1, "", "get_dope"]], "molearn.scoring.Parallel_DOPE_Score": [[6, 1, 1, "", "get_score"]], "molearn.trainers": [[7, 0, 1, "", "OpenMM_Physics_Trainer"], [7, 0, 1, "", "Torch_Physics_Trainer"], [7, 0, 1, "", "Trainer"]], "molearn.trainers.OpenMM_Physics_Trainer": [[7, 1, 1, "", "common_physics_step"], [7, 1, 1, "", "prepare_physics"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "valid_step"]], "molearn.trainers.Torch_Physics_Trainer": [[7, 1, 1, "", "common_physics_step"], [7, 1, 1, "", "prepare_physics"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "valid_step"]], "molearn.trainers.Trainer": [[7, 1, 1, "", "checkpoint"], [7, 1, 1, "", "common_step"], [7, 1, 1, "", "get_network_summary"], [7, 1, 1, "", "learning_rate_sweep"], [7, 1, 1, "", "load_checkpoint"], [7, 1, 1, "", "log"], [7, 1, 1, "", "prepare_optimiser"], [7, 1, 1, "", "run"], [7, 1, 1, "", "scheduler_step"], [7, 1, 1, "", "set_autoencoder"], [7, 1, 1, "", "set_data"], [7, 1, 1, "", "set_dataloader"], [7, 1, 1, "", "train_epoch"], [7, 1, 1, "", "train_step"], [7, 1, 1, "", "update_optimiser_hyperparameters"], [7, 1, 1, "", "valid_epoch"], [7, 1, 1, "", "valid_step"]]}, "objtypes": {"0": "py:class", "1": "py:method", "2": "py:module", "3": "py:function"}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "method", "Python method"], "2": ["py", "module", "Python module"], "3": ["py", "function", "Python function"]}, "titleterms": {"frequent": 0, "ask": 0, "question": 0, "i": 0, "cannot": 0, "instal": 0, "openmmtorchplugin": 0, "get": 0, "an": 0, "indexerror": 0, "when": 0, "try": 0, "load": [0, 2], "multipdb": 0, "The": 0, "gui": 0, "freez": 0, "us": 0, "doe": 0, "work": 0, "expect": 0, "analysi": 1, "data": 2, "welcom": 3, "molearn": 3, "s": 3, "document": 3, "content": 3, "indic": 3, "tabl": 3, "loss": 4, "function": 4, "foldingnet": [], "model": 5, "cnn": [], "score": 6, "trainer": 7}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "sphinx": 56}}) \ No newline at end of file diff --git a/docs/build/trainers.html b/docs/build/trainers.html index b53445d..2852384 100644 --- a/docs/build/trainers.html +++ b/docs/build/trainers.html @@ -1,16 +1,19 @@ + - + - - - Trainers — molearn 2.0.1 documentation - - - - - + + + Trainers — molearn 2.0.4 documentation + + + + + + + @@ -31,7 +34,7 @@

    Navigation

  • previous |
  • - +
    @@ -42,10 +45,10 @@

    Navigation

    -

    Trainers

    +

    Trainers

    -class Trainer(device=None, log_filename='log_file.dat')[source]
    +class Trainer(device=None, log_filename='log_file.dat')[source]

    Bases: object

    Trainer class that defines a number of useful methods for training an autoencoder.

    @@ -74,7 +77,7 @@

    Trainers

    -checkpoint(epoch, valid_logs, checkpoint_folder, loss_key='valid_loss')[source]
    +checkpoint(epoch, valid_logs, checkpoint_folder, loss_key='valid_loss')[source]

    Checkpoint the current network. The checkpoint will be saved as 'last.ckpt'. If valid_logs[loss_key] is better than self.best then this checkpoint will replace self.best and 'last.ckpt' will be renamed to f'{checkpoint_folder}/checkpoint_epoch{epoch}_loss{valid_loss}.ckpt' and the former best (filename saved as self.best_name) will be deleted

    @@ -91,7 +94,7 @@

    Trainers
    -common_step(batch)[source]
    +common_step(batch)[source]

    Called from both train_step and valid_step. Calculates the mean squared error loss for self.autoencoder. Encoded and decoded frames are saved in self._internal under keys encoded and decoded respectively should you wish to use them elsewhere.

    @@ -110,13 +113,13 @@

    Trainers
    -get_network_summary()[source]
    +get_network_summary()[source]

    returns a dictionary containing information about the size of the autoencoder.

    -learning_rate_sweep(max_lr=100, min_lr=1e-05, number_of_iterations=1000, checkpoint_folder='checkpoint_sweep', train_on='mse_loss', save=['loss', 'mse_loss'])[source]
    +learning_rate_sweep(max_lr=100, min_lr=1e-05, number_of_iterations=1000, checkpoint_folder='checkpoint_sweep', train_on='mse_loss', save=['loss', 'mse_loss'])[source]

    Deprecated method. Performs a sweep of learning rate between max_lr and min_lr over number_of_iterations. See Finding Good Learning Rate and The One Cycle Policy

    @@ -141,7 +144,7 @@

    Trainers
    -load_checkpoint(checkpoint_name='best', checkpoint_folder='', load_optimiser=True)[source]
    +load_checkpoint(checkpoint_name='best', checkpoint_folder='', load_optimiser=True)[source]

    Load checkpoint.

    Parameters:
    @@ -156,7 +159,7 @@

    Trainers
    -log(log_dict, verbose=None)[source]
    +log(log_dict, verbose=None)[source]

    Then contents of log_dict are dumped using json.dumps(log_dict) and printed and/or appended to self.log_filename This function is called from self.run

    @@ -171,7 +174,7 @@

    Trainers
    -prepare_optimiser(lr=0.001, weight_decay=0.0001, **optimiser_kwargs)[source]
    +prepare_optimiser(lr=0.001, weight_decay=0.0001, **optimiser_kwargs)[source]

    The Default optimiser is AdamW and is saved in self.optimiser. With no optional arguments this function is the same as doing: trainer.optimiser = torch.optim.AdawW(self.autoencoder.parameters(), lr=1e-3, weight_decay = 0.0001)

    @@ -188,7 +191,7 @@

    Trainers
    -run(max_epochs=100, log_filename=None, log_folder=None, checkpoint_frequency=1, checkpoint_folder='checkpoint_folder', allow_n_failures=10, verbose=None)[source]
    +run(max_epochs=100, log_filename=None, log_folder=None, checkpoint_frequency=1, checkpoint_folder='checkpoint_folder', allow_n_failures=10, verbose=None, allow_grad_in_valid=False)[source]

    Calls the following in a loop:

  • UjHWvW3K=Z5o`YKJY_ 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 nKNma!Fa3+*>39C6@bowT#PpO@v(ZgDjUy_(J s#hamr3#KMD1;{O(+#wv!)x(xwT2fG2GR1H6ec{=RjC(d$i&nA%08)=R3IG5A diff --git a/docs/build/.doctrees/loss_functions.doctree b/docs/build/.doctrees/loss_functions.doctree index acdcf932243a32e38b6873a1e1fa79f119f70efd..6791507489e355df5943acd858feaa079ad83e66 100644 GIT binary patch literal 73224 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 literal 73275 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 J`n$7K`yaYyar^)P literal 40762 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 C_K!RO literal 191217 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 Yo~Ub%$as*VtRQX*&>Uw3rmLU+Kf8!Ax&QzG diff --git a/docs/build/FAQ.html b/docs/build/FAQ.html new file mode 100644 index 0000000..806b584 --- /dev/null +++ b/docs/build/FAQ.html @@ -0,0 +1,155 @@ + + + + + + + + + Frequently Asked Questions — molearn 2.0.4 documentation + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +

    Frequently Asked Questions

    +
    +

    I cannot install openmmtorchplugin

    +

    openmmtorchplugin depends on conda-forge builds of pyTorch and OpenMM. +Due to this dependency, Windows cannot be supported.

    +

    Installation can be carried out via terminal with conda-forge:

    +
    conda install -c conda-forge openmmtorchplugin
    +
    +
    +

    The following Python versions are supported: 3.8, 3.9, 3.10, 3.11.

    +

    If you run into any issue, either at installation or runtime, ensure you have a +plugin version >=1.1.3, as previous ones have known compatibility with OpenMM. +The easiest way to ensure the most up to date version of molearn and the +openmmtorchplugin are installed, is to run a fresh install in a new conda +environment:

    +
    conda create --name test_env python=3.10
    +conda install -n test_env -c conda-forge openmmtorchplugin molearn
    +
    +
    +

    openmmtorchplugin is built with cuda_compiler_version=11.2 in conda-forge CI tools. +This has been successfully tested on Ubuntu machines running with the driver +version 525.105.17 (see nvidia-smi output).

    +

    The Nvidia website tabulates minimum driver versions required and version compatibility: +NVIDIA CUDA Toolkit Minimum driver versions

    +
    +
    +

    I get an IndexError when I try loading a multiPDB

    +

    This is likely an error thrown by MDAnalysis. Typically this happens when +attempting to load a multiPDB file saved with software like VMD, which uses a +different syntax to indicate the end of a conformer in the file. A way to get +around this, is to re-save the file in a format MDAnalysis can parse, e.g., by +loading and re-saving the file via biobox.

    +
    import biobox as bb
    +M = bb.Molecule(filename)
    +M.write_pdb(newfilename)
    +
    +
    +
    +
    +

    The GUI freezes when I use it/does not work as expected

    +

    This is usually caused by an issue with packages handling communications between the GUI and Jupyter, see here. +Currently, a workaround is to use older versions of tornado. +In Python 3.10, the following packages have been observed to yield correct behaviour:

    +
    ipywidgets=8.0.7
    +nglview=3.0.6
    +tornado=6.1
    +
    +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    + + + + \ No newline at end of file diff --git a/docs/build/_modules/index.html b/docs/build/_modules/index.html index b495099..93a3bcc 100644 --- a/docs/build/_modules/index.html +++ b/docs/build/_modules/index.html @@ -1,15 +1,18 @@ + - + - Overview: module code — molearn 2.0.1 documentation - - - - - + Overview: module code — molearn 2.0.4 documentation + + + + + + + @@ -22,7 +25,7 @@

    Navigation