-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
312 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
from enum import Enum | ||
import numpy as np | ||
from scipy.optimize import minimize | ||
|
||
from oddt import random_seed | ||
from oddt.docking.MCMCUtils import MCMCUtils | ||
|
||
|
||
class OptimizationMethod(Enum): | ||
""" | ||
method of optimization (one of SIMPLEX/NO_OPTIM, NELDER_MEAD or LBFGSB) | ||
""" | ||
SIMPLEX = 1 | ||
NO_OPTIM = 1 | ||
NELDER_MEAD = 2 | ||
LBFGSB = 3 | ||
|
||
|
||
class MCMCAlgorithm(object): | ||
def __init__(self, engine, optim=OptimizationMethod.NELDER_MEAD, optim_iter=10, mc_steps=50, | ||
mut_steps=100, seed=None, ): | ||
""" | ||
Parameters | ||
---------- | ||
engine: CustomEngine | ||
engine with prepared molecules and defined scoring function | ||
optim: OptimizationMethod | ||
method of optimization (or SIMPLEX/NO_OPTIM if none) around locally chosen conformation point, | ||
must be one of the values in OptimizationMethod enumeration | ||
optim_iter: int (default=10) | ||
number of iterations for local optimization, in the scipy.optimize.minimize | ||
mc_steps: int (default=50) | ||
number of steps performed by the MCMC algorithm | ||
mut_steps: int (100) | ||
number of mutation steps (while choosing next random conformation) | ||
seed: int | ||
seed for the pseudonumber generators | ||
""" | ||
|
||
self.engine = engine | ||
self.ligand = engine.lig | ||
self.optim = optim | ||
self.optim_iter = optim_iter | ||
self.mc_steps = mc_steps | ||
self.mut_steps = mut_steps | ||
if seed: | ||
random_seed(seed) | ||
|
||
self.num_rotors = len(self.engine.rotors) | ||
|
||
self.lig_dict = self.engine.lig_dict | ||
self.mcmc_utils = MCMCUtils() | ||
|
||
def perform(self): | ||
""" | ||
performs the algorithm | ||
Returns | ||
------- | ||
conformation, score: float[], float | ||
best conformation and best score for this conformation | ||
""" | ||
|
||
x1 = self.generate_rotor_vector() | ||
c1 = self.engine.lig.mutate(x1) | ||
e1 = self.engine.score(c1) | ||
out = {'score': e1, 'conformation': c1.copy().tolist()} | ||
|
||
for _ in range(self.mc_steps): | ||
c2, x2 = self.generate_conformation(x1) | ||
e2 = self.engine.score(c2) | ||
e3, x3 = self._optimize(e2, x2) | ||
|
||
delta = e3 - e1 | ||
|
||
if delta < 0 or np.exp(-delta) > np.random.uniform(): # Metropolis criterion | ||
x1 = x3 | ||
if delta < 0: | ||
e1 = e3 | ||
conformation = self.engine.lig.mutate(x1.copy()) | ||
out = {'score': e1, 'conformation': conformation.tolist()} | ||
|
||
return out['conformation'], out['score'] | ||
|
||
def _optimize(self, e2, x2): | ||
|
||
bounds = ((-1., 1.), (-1., 1.), (-1., 1.), (-np.pi, np.pi), (-np.pi, np.pi), (-np.pi, np.pi)) | ||
for i in range(len(self.engine.rotors)): | ||
bounds += ((-np.pi, np.pi),) | ||
bounds = np.array(bounds) | ||
|
||
if self.optim == OptimizationMethod.SIMPLEX: | ||
return e2, x2 | ||
elif self.optim == OptimizationMethod.NELDER_MEAD: | ||
return self._minimize_nelder_mead(x2) | ||
elif self.optim == OptimizationMethod.LBFGSB: | ||
return self._minimize_lbfgsb(bounds, x2) | ||
return e2, x2 | ||
|
||
def _minimize_nelder_mead(self, x2): | ||
|
||
m = minimize(self.mcmc_utils.score_coords, x2, args=(self.engine, self.engine.score), | ||
method='Nelder-Mead') | ||
e3, x3 = self._extract_from_scipy_minimize(m) | ||
return e3, x3 | ||
|
||
def _extract_from_scipy_minimize(self, m): | ||
|
||
x3 = m.x | ||
x3 = self.mcmc_utils.keep_bound(x3) | ||
e3 = m.fun | ||
return e3, x3 | ||
|
||
def _minimize_lbfgsb(self, bounds, x2): | ||
|
||
m = minimize(self.mcmc_utils.score_coords, x2, method='L-BFGS-B', | ||
jac=self.mcmc_utils.score_coords_jac, | ||
args=(self.engine, self.engine.score), bounds=bounds, options={'maxiter': self.optim_iter}) | ||
e3, x3 = self._extract_from_scipy_minimize(m) | ||
return e3, x3 | ||
|
||
def generate_conformation(self, x1): | ||
|
||
for _ in range(self.mut_steps): | ||
x2 = self.mcmc_utils.rand_mutate_big(x1.copy()) | ||
c2 = self.engine.lig.mutate(x2) | ||
return c2, x2 | ||
|
||
# generate random coordinate | ||
def generate_rotor_vector(self): | ||
|
||
trans_vec = np.random.uniform(-1, 1, size=3) | ||
rot_vec = np.random.uniform(-np.pi, np.pi, size=3) | ||
rotors_vec = np.random.uniform(-np.pi, np.pi, size=self.num_rotors) | ||
return np.hstack((trans_vec, rot_vec, rotors_vec)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import numpy as np | ||
|
||
|
||
class MCMCUtils(object): | ||
def __init__(self): | ||
""" | ||
class containing MCMC utils for MCMC algorithm | ||
""" | ||
|
||
def score_coords(self, x, engine, scoring_func): | ||
""" | ||
score using given coordinates | ||
Parameters | ||
---------- | ||
x: float[] | ||
conformation | ||
engine: CustomEngine | ||
engine with prepared molecules and defined scoring function | ||
Returns | ||
------- | ||
score: float | ||
score | ||
""" | ||
c1 = engine.lig.mutate(x) | ||
return scoring_func(c1) | ||
|
||
def score_coords_jac(self, x, engine, scoring_func, step=1e-2): | ||
""" | ||
jac function for scipy.optimize.minimize | ||
Parameters | ||
---------- | ||
x: float[] | ||
conformation | ||
engine: CustomEngine | ||
engine with prepared molecules and defined scoring function | ||
step: | ||
infinitesimal change in x (for computing the gradient) | ||
Returns | ||
------- | ||
grad: float[] | ||
gradient of the scoring function | ||
""" | ||
c1 = engine.lig.mutate(x) | ||
e1 = scoring_func(c1) | ||
grad = [] | ||
for i in range(len(x)): | ||
x_g = x.copy() | ||
x_g[i] += step # if i < 3 else 1e-2 | ||
cg = engine.lig.mutate(x_g) | ||
grad.append(scoring_func(cg)) | ||
return (np.array(grad) - e1) / step | ||
|
||
def _random_angle(self, size=1): | ||
""" | ||
generation of random angles, in range from -pi to +pi | ||
Parameters | ||
---------- | ||
size: int | ||
number of random angles to generate | ||
Returns | ||
------- | ||
random: float or float[size] | ||
random angle (float) if size=1 or list of random angles (float[]) if size>1 | ||
""" | ||
if size > 1: | ||
return np.random.uniform(-np.pi, np.pi, size=size) | ||
return np.random.uniform(-np.pi, np.pi) | ||
|
||
def keep_bound(self, x): | ||
""" | ||
keep bound of conformation | ||
Parameters | ||
---------- | ||
x: float[] | ||
conformation | ||
Returns | ||
------- | ||
x: float[] | ||
bounded conformation | ||
""" | ||
x[:3] = np.clip(x[:3], -1, 1) | ||
x[3:] = np.clip(x[3:], -np.pi, np.pi) | ||
return x | ||
|
||
def rand_mutate_big(self, x): | ||
""" | ||
change elements of conformation randomly | ||
Parameters | ||
---------- | ||
x: float[] | ||
conformation | ||
Returns | ||
------- | ||
x: float[] | ||
conformation with randomly changed elements | ||
""" | ||
x = x.copy() | ||
m = np.random.randint(0, len(x) - 4) | ||
if m == 0: # do random translation | ||
x[:3] = np.random.uniform(-0.3, 0.3, size=3) | ||
elif m == 1: # do random rotation step | ||
x[3:6] = self._random_angle(size=3) | ||
else: # do random dihedral change | ||
ix = 6 + m - 2 | ||
x[ix] = self._random_angle() | ||
return x |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters