Skip to content

Commit efe6445

Browse files
authored
Merge pull request #27 from DeepRank/align
Align
2 parents 9a5c1a7 + 9938878 commit efe6445

7 files changed

Lines changed: 3433 additions & 219 deletions

File tree

pdb2sql/StructureSimilarity.py

Lines changed: 18 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import numpy as np
33
from .pdb2sqlcore import pdb2sql
44
from .interface import interface
5+
from .superpose import get_trans_vect, get_rotation_matrix, superpose_selection
6+
57
from . import transform
68
import os
79
import pickle
@@ -134,27 +136,8 @@ def compute_lrmsd_fast(self, lzone=None, method='svd', check=True):
134136
xyz_ref_long, xyz_ref_short = self.get_xyz_zone_backbone(
135137
self.ref, resData, return_not_in_zone=True)
136138

137-
# get the translation so that both A chains are centered
138-
tr_decoy = self.get_trans_vect(xyz_decoy_long)
139-
tr_ref = self.get_trans_vect(xyz_ref_long)
140-
141-
# translate everything for 1
142-
xyz_decoy_short += tr_decoy
143-
xyz_decoy_long += tr_decoy
144-
145-
# translate everuthing for 2
146-
xyz_ref_short += tr_ref
147-
xyz_ref_long += tr_ref
148-
149-
# get the ideal rotation matrix
150-
# to superimpose the A chains
151-
U = self.get_rotation_matrix(
152-
xyz_decoy_long, xyz_ref_long, method=method)
153-
154-
# rotate the entire fragment
155-
156-
xyz_decoy_short = transform.rotate(
157-
xyz_decoy_short, U, center=self.origin)
139+
xyz_decoy_short = superpose_selection(
140+
xyz_decoy_short, xyz_decoy_long, xyz_ref_long, method)
158141

159142
# compute the RMSD
160143
return self.get_rmsd(xyz_decoy_short, xyz_ref_short)
@@ -281,24 +264,9 @@ def compute_irmsd_fast(
281264
xyz_contact_decoy = self.get_xyz_zone_backbone(self.decoy, resData)
282265
xyz_contact_ref = self.get_xyz_zone_backbone(self.ref, resData)
283266

284-
# get the translation so that both A chains are centered
285-
tr_decoy = self.get_trans_vect(xyz_contact_decoy)
286-
tr_ref = self.get_trans_vect(xyz_contact_ref)
287-
288-
# translate everything
289-
xyz_contact_decoy += tr_decoy
290-
xyz_contact_ref += tr_ref
291-
292-
# get the ideal rotation matrix
293-
# to superimpose the A chains
294-
U = self.get_rotation_matrix(
295-
xyz_contact_decoy,
296-
xyz_contact_ref,
297-
method=method)
298-
299-
# rotate the entire fragment
300-
xyz_contact_decoy = transform.rotate(
301-
xyz_contact_decoy, U, center=self.origin)
267+
# superpose the fragments
268+
xyz_contact_decoy = superpose_selection(xyz_contact_decoy,
269+
xyz_contact_decoy, xyz_contact_ref, method)
302270

303271
# return the RMSD
304272
return self.get_rmsd(xyz_contact_decoy, xyz_contact_ref)
@@ -559,8 +527,8 @@ def compute_lrmsd_pdb2sql(self, exportpath=None, method='svd'):
559527
xyz_ref_short = xyz_ref_A
560528

561529
# get the translation so that both A chains are centered
562-
tr_decoy = self.get_trans_vect(xyz_decoy_long)
563-
tr_ref = self.get_trans_vect(xyz_ref_long)
530+
tr_decoy = get_trans_vect(xyz_decoy_long)
531+
tr_ref = get_trans_vect(xyz_ref_long)
564532

565533
# translate everything for 1
566534
xyz_decoy_short += tr_decoy
@@ -572,7 +540,7 @@ def compute_lrmsd_pdb2sql(self, exportpath=None, method='svd'):
572540

573541
# get the ideal rotation matrix
574542
# to superimpose the A chains
575-
U = self.get_rotation_matrix(
543+
U = get_rotation_matrix(
576544
xyz_decoy_long, xyz_ref_long, method=method)
577545

578546
# rotate the entire fragment
@@ -756,23 +724,23 @@ def compute_irmsd_pdb2sql(
756724
'Error in i-rmsd: only one chain represented in one chain')
757725

758726
# get the translation so that both A chains are centered
759-
tr_decoy = self.get_trans_vect(xyz_contact_decoy)
760-
tr_ref = self.get_trans_vect(xyz_contact_ref)
727+
tr_decoy = get_trans_vect(xyz_contact_decoy)
728+
tr_ref = get_trans_vect(xyz_contact_ref)
761729

762730
# translate everything
763731
xyz_contact_decoy += tr_decoy
764732
xyz_contact_ref += tr_ref
765733

766734
# get the ideql rotation matrix
767735
# to superimpose the A chains
768-
U = self.get_rotation_matrix(
769-
xyz_contact_decoy,
770-
xyz_contact_ref,
771-
method=method)
736+
rot_mat = get_rotation_matrix(
737+
xyz_contact_decoy,
738+
xyz_contact_ref,
739+
method=method)
772740

773741
# rotate the entire fragment
774742
xyz_contact_decoy = transform.rotate(
775-
xyz_contact_decoy, U, center=self.origin)
743+
xyz_contact_decoy, rot_mat, center=self.origin)
776744

777745
# compute the RMSD
778746
irmsd = self.get_rmsd(xyz_contact_decoy, xyz_contact_ref)
@@ -1185,7 +1153,7 @@ def compute_clashes(pdb):
11851153

11861154
##########################################################################
11871155
#
1188-
# ROUTINES TO ACTUALY ALIGN THE MOLECULES
1156+
# ROUTINES TO ACTUALY SUPERPOSE THE MOLECULES
11891157
#
11901158
##########################################################################
11911159

@@ -1205,170 +1173,3 @@ def get_rmsd(P, Q):
12051173
"""
12061174
n = len(P)
12071175
return round(np.sqrt(1. / n * np.sum((P - Q)**2)), 3)
1208-
1209-
# compute the translation vector to center a set of points
1210-
@staticmethod
1211-
def get_trans_vect(P):
1212-
"""Get the translationv vector to the origin.
1213-
1214-
Args:
1215-
P (np.array(nx3)): position of the points in the molecule
1216-
1217-
Returns:
1218-
float: minus mean value of the xyz columns
1219-
"""
1220-
return -np.mean(P, 0)
1221-
1222-
# main switch for the rotation matrix
1223-
# add new methods here if necessary
1224-
def get_rotation_matrix(self, P, Q, method='svd'):
1225-
1226-
# get the matrix with Kabsh method
1227-
if method.lower() == 'svd':
1228-
mat = self.get_rotation_matrix_Kabsh(P, Q)
1229-
1230-
# or with the quaternion method
1231-
elif method.lower() == 'quaternion':
1232-
mat = self.get_rotation_matrix_quaternion(P, Q)
1233-
1234-
else:
1235-
raise ValueError(
1236-
f'{method} is not a valid method for rmsd alignement. '
1237-
f'Options are svd or quaternions')
1238-
1239-
return mat
1240-
1241-
# get the rotation matrix via a SVD
1242-
# decomposition of the correlation matrix
1243-
@staticmethod
1244-
def get_rotation_matrix_Kabsh(P, Q):
1245-
"""Get the rotation matrix to aligh two point clouds.
1246-
1247-
The method is based on th Kabsh approach
1248-
https://cnx.org/contents/HV-RsdwL@23/Molecular-Distance-Measures
1249-
1250-
Args:
1251-
P (np.array): xyz of the first point cloud
1252-
Q (np.array): xyz of the second point cloud
1253-
1254-
Returns:
1255-
np.array: rotation matrix
1256-
1257-
Raises:
1258-
ValueError: matrix have different sizes
1259-
"""
1260-
pshape = P.shape
1261-
qshape = Q.shape
1262-
1263-
if pshape[0] == qshape[0]:
1264-
npts = pshape[0]
1265-
else:
1266-
raise ValueError("Matrix don't have the same number of points",
1267-
P.shape, Q.shape)
1268-
1269-
p0, q0 = np.abs(np.mean(P, 0)), np.abs(np.mean(Q, 0))
1270-
eps = 1E-6
1271-
if any(p0 > eps) or any(q0 > eps):
1272-
raise ValueError('You must center the fragment first', p0, q0)
1273-
1274-
# form the covariance matrix
1275-
A = np.dot(P.T, Q) / npts
1276-
1277-
# SVD the matrix
1278-
V, _, W = np.linalg.svd(A)
1279-
1280-
# the W matrix returned here is
1281-
# already its transpose
1282-
# https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.linalg.svd.html
1283-
W = W.T
1284-
1285-
# determinant
1286-
d = np.linalg.det(np.dot(W, V.T))
1287-
1288-
# form the U matrix
1289-
Id = np.eye(3)
1290-
if d < 0:
1291-
Id[2, 2] = -1
1292-
1293-
U = np.dot(W, np.dot(Id, V.T))
1294-
1295-
return U
1296-
1297-
# get the rotation amtrix via the quaternion approach
1298-
# doesn't work great so far
1299-
@staticmethod
1300-
def get_rotation_matrix_quaternion(P, Q):
1301-
"""Get the rotation matrix to aligh two point clouds.
1302-
1303-
The method is based on the quaternion approach
1304-
http://www.ams.stonybrook.edu/~coutsias/papers/rmsd17.pdf
1305-
1306-
Args:
1307-
P (np.array): xyz of the first point cloud
1308-
Q (np.array): xyz of the second point cloud
1309-
1310-
Returns:
1311-
np.array: rotation matrix
1312-
1313-
Raises:
1314-
ValueError: matrix have different sizes
1315-
"""
1316-
pshape = P.shape
1317-
qshape = Q.shape
1318-
1319-
if pshape[0] != qshape[0]:
1320-
raise ValueError("Matrix don't have the same number of points",
1321-
P.shape, Q.shape)
1322-
1323-
p0, q0 = np.abs(np.mean(P, 0)), np.abs(np.mean(Q, 0))
1324-
eps = 1E-6
1325-
if any(p0 > eps) or any(q0 > eps):
1326-
raise ValueError('You must center the fragment first', p0, q0)
1327-
1328-
# form the correlation matrix
1329-
R = np.dot(P.T, Q)
1330-
1331-
# form the F matrix (eq. 10 of ref[1])
1332-
F = np.zeros((4, 4))
1333-
1334-
F[0, 0] = np.trace(R)
1335-
F[0, 1] = R[1, 2] - R[2, 1]
1336-
F[0, 2] = R[2, 0] - R[0, 2]
1337-
F[0, 3] = R[0, 1] - R[1, 0]
1338-
1339-
F[1, 0] = R[1, 2] - R[2, 1]
1340-
F[1, 1] = R[0, 0] - R[1, 1] - R[2, 2]
1341-
F[1, 2] = R[0, 1] + R[1, 0]
1342-
F[1, 3] = R[0, 2] + R[2, 0]
1343-
1344-
F[2, 0] = R[2, 0] - R[0, 2]
1345-
F[2, 1] = R[0, 1] + R[1, 0]
1346-
F[2, 2] = -R[0, 0] + R[1, 1] - R[2, 2]
1347-
F[2, 3] = R[1, 2] + R[2, 1]
1348-
1349-
F[3, 0] = R[0, 1] - R[1, 0]
1350-
F[3, 1] = R[0, 2] + R[2, 0]
1351-
F[3, 2] = R[1, 2] + R[2, 1]
1352-
F[3, 3] = -R[0, 0] - R[1, 1] + R[2, 2]
1353-
1354-
# diagonalize it
1355-
l, U = np.linalg.eig(F)
1356-
1357-
# extract the eigenvect of the highest eigenvalues
1358-
indmax = np.argmax(l)
1359-
q0, q1, q2, q3 = U[:, indmax]
1360-
1361-
# form the rotation matrix (eq. 33 ref[1])
1362-
U = np.zeros((3, 3))
1363-
1364-
U[0, 0] = q0**2 + q1**2 - q2**2 - q3**2
1365-
U[0, 1] = 2 * (q1 * q2 - q0 * q3)
1366-
U[0, 2] = 2 * (q1 * q3 + q0 * q2)
1367-
U[1, 0] = 2 * (q1 * q2 + q0 * q3)
1368-
U[1, 1] = q0**2 - q1**2 + q2**2 - q3**2
1369-
U[1, 2] = 2 * (q2 * q3 - q0 * q1)
1370-
U[2, 0] = 2 * (q1 * q3 - q0 * q2)
1371-
U[2, 1] = 2 * (q2 * q3 + q0 * q1)
1372-
U[2, 2] = q0**2 - q1**2 - q2**2 + q3**2
1373-
1374-
return U

0 commit comments

Comments
 (0)