Skip to content

Commit 917b3a8

Browse files
agave233Fairly
authored andcommitted
add giant
1 parent 7c6e780 commit 917b3a8

11 files changed

Lines changed: 1846 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## GIANT-Paddle
2+
3+
### Dependencies
4+
- python >= 3.8
5+
- paddlepaddle >= 2.1.0
6+
- pgl >= 2.1.4
7+
- openbabel == 3.1.1 (optional, only for preprocessing)
8+
9+
### Datasets
10+
The PDBbind dataset can be downloaded [here](http://pdbbind-cn.org).
11+
12+
The CSAR-HiQ dataset can be downloaded [here](http://www.csardock.org).
13+
14+
You may need to use the [UCSF Chimera tool](https://www.cgl.ucsf.edu/chimera/) to convert the PDB-format files into MOL2-format files for feature extraction at first.
15+
16+
The downloaded dataset should be preprocessed to obtain features and spatial coordinates:
17+
```
18+
python preprocess_pdbbind.py --data_path_core YOUR_DATASET_PATH --data_path_refined YOUR_DATASET_PATH --dataset_name pdbbind2016 --output_path YOUR_OUTPUT_PATH --cutoff 5
19+
```
20+
The parameter cutoff is the threshold of cutoff distance between atoms.
21+
22+
### How to run
23+
To train the model, you can run this command:
24+
```
25+
python train.py --cuda YOUR_DEVICE --model_dir MODEL_PATH_TO_SAVE --dataset pdbbind2016 --cut_dist 5 --num_angle 6
26+
```

apps/drug_target_interaction/giant/data/.gitkeep

Whitespace-only changes.
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Dataset code for protein-ligand complexe interaction graph construction.
16+
"""
17+
18+
import os
19+
import numpy as np
20+
import paddle
21+
import pgl
22+
import pickle
23+
from pgl.utils.data import Dataset as BaseDataset
24+
from pgl.utils.data import Dataloader
25+
from scipy.spatial import distance
26+
from scipy.sparse import coo_matrix
27+
from utils import cos_formula
28+
from tqdm import tqdm
29+
30+
prot_atom_ids = [6, 7, 8, 16]
31+
drug_atom_ids = [6, 7, 8, 9, 15, 16, 17, 35, 53]
32+
pair_ids = [(i, j) for i in prot_atom_ids for j in drug_atom_ids]
33+
34+
class ComplexDataset(BaseDataset):
35+
def __init__(self, data_path, dataset, cut_dist, num_angle, save_file=True):
36+
self.data_path = data_path
37+
self.dataset = dataset
38+
self.cut_dist = cut_dist
39+
self.num_angle = num_angle
40+
self.save_file = save_file
41+
42+
self.labels = []
43+
self.a2a_graphs = []
44+
self.b2a_graphs = []
45+
self.b2b_graphs_list = []
46+
self.mark_graphs_list = []
47+
self.inter_feats_list = []
48+
self.bond_types_list = []
49+
self.type_count_list = []
50+
51+
self.load_data()
52+
53+
54+
def __len__(self):
55+
""" Return the number of graphs. """
56+
return len(self.labels)
57+
58+
def __getitem__(self, idx):
59+
""" Return graphs and label. """
60+
return self.a2a_graphs[idx], self.b2a_graphs[idx], self.b2b_graphs_list[idx], self.mark_graphs_list[idx],\
61+
self.inter_feats_list[idx], self.bond_types_list[idx], self.type_count_list[idx], self.labels[idx]
62+
63+
def has_cache(self):
64+
""" Check cache file."""
65+
self.graph_path = f'{self.data_path}/{self.dataset}_{int(self.cut_dist)}_{self.num_angle}_pgl_graph.pkl'
66+
return os.path.exists(self.graph_path)
67+
68+
def save(self):
69+
""" Save the generated graphs. """
70+
print('Saving processed complex data...')
71+
graphs = [self.a2a_graphs, self.b2a_graphs, self.b2b_graphs_list, self.mark_graphs_list]
72+
global_feat = [self.inter_feats_list, self.bond_types_list, self.type_count_list]
73+
with open(self.graph_path, 'wb') as f:
74+
pickle.dump((graphs, global_feat, self.labels), f)
75+
76+
def load(self):
77+
""" Load the generated graphs. """
78+
print('Loading processed complex data...')
79+
with open(self.graph_path, 'rb') as f:
80+
graphs, global_feat, labels = pickle.load(f)
81+
return graphs, global_feat, labels
82+
83+
def build_graph(self, mol):
84+
num_atoms_d, coords, features, atoms, inter_feats = mol
85+
86+
##################################################
87+
# prepare distance matrix and interaction matrix #
88+
##################################################
89+
dist_mat = distance.cdist(coords, coords, 'euclidean')
90+
np.fill_diagonal(dist_mat, np.inf)
91+
inter_feats = np.array([inter_feats])
92+
inter_feats = inter_feats / inter_feats.sum()
93+
94+
############################
95+
# build atom to atom graph #
96+
############################
97+
num_atoms = len(coords)
98+
dist_graph_base = dist_mat.copy()
99+
dist_feat = dist_graph_base[dist_graph_base < self.cut_dist].reshape(-1,1)
100+
dist_graph_base[dist_graph_base >= self.cut_dist] = 0.
101+
atom_graph = coo_matrix(dist_graph_base)
102+
a2a_edges = list(zip(atom_graph.row, atom_graph.col))
103+
a2a_graph = pgl.Graph(a2a_edges, num_nodes=num_atoms, node_feat={"feat": features}, edge_feat={"dist": dist_feat})
104+
105+
######################
106+
# prepare bond nodes #
107+
######################
108+
indices = []
109+
bond_pair_atom_types = []
110+
inter_bonds = []
111+
for i in range(num_atoms):
112+
for j in range(num_atoms):
113+
a = dist_mat[i, j]
114+
if a < self.cut_dist:
115+
at_i, at_j = atoms[i], atoms[j]
116+
if i < num_atoms_d and j >= num_atoms_d and (at_j, at_i) in pair_ids:
117+
bond_pair_atom_types += [pair_ids.index((at_j, at_i))]
118+
elif i >= num_atoms_d and j < num_atoms_d and (at_i, at_j) in pair_ids:
119+
bond_pair_atom_types += [pair_ids.index((at_i, at_j))]
120+
else:
121+
bond_pair_atom_types += [-1]
122+
inter_bonds.append(len(indices))
123+
indices.append([i, j])
124+
125+
############################
126+
# build bond to atom graph #
127+
############################
128+
num_bonds = len(indices)
129+
assignment_b2a = np.zeros((num_bonds, num_atoms), dtype=np.int64) # Maybe need too much memory
130+
assignment_a2b = np.zeros((num_atoms, num_bonds), dtype=np.int64) # Maybe need too much memory
131+
for i, idx in enumerate(indices):
132+
assignment_b2a[i, idx[1]] = 1
133+
assignment_a2b[idx[0], i] = 1
134+
135+
b2a_graph = coo_matrix(assignment_b2a)
136+
b2a_edges = list(zip(b2a_graph.row, b2a_graph.col))
137+
b2a_graph = pgl.BiGraph(b2a_edges, src_num_nodes=num_bonds, dst_num_nodes=num_atoms)
138+
139+
############################
140+
# build bond to bond graph #
141+
############################
142+
bond_graph_base = assignment_b2a @ assignment_a2b
143+
np.fill_diagonal(bond_graph_base, 0) # eliminate self connections
144+
bond_graph_base[range(num_bonds), [indices.index([x[1],x[0]]) for x in indices]] = 0
145+
x, y = np.where(bond_graph_base.T > 0)
146+
num_edges = len(x)
147+
148+
# calculate angle
149+
angle_feat = np.zeros_like(x, dtype=np.float32)
150+
for i in range(num_edges):
151+
body1 = indices[y[i]]
152+
body2 = indices[x[i]]
153+
a = dist_mat[body1[0], body1[1]]
154+
b = dist_mat[body2[0], body2[1]]
155+
c = dist_mat[body1[0], body2[1]]
156+
if a == 0 or b == 0:
157+
print(body1, body2)
158+
print('One distance is zero.')
159+
angle_feat[i] = 0.
160+
return None, None
161+
# exit(-1)
162+
else:
163+
angle_feat[i] = cos_formula(a, b, c)
164+
165+
# angle domain divisions
166+
unit = 180.0 / self.num_angle
167+
angle_index = (np.rad2deg(angle_feat) / unit).astype('int64')
168+
angle_index = np.clip(angle_index, 0, self.num_angle - 1)
169+
170+
# multiple bond-to-bond graphs based on angle domains
171+
b2b_edges_list = [[] for _ in range(self.num_angle)]
172+
b2b_angle_list = [[] for _ in range(self.num_angle)]
173+
for i, (ind, radian) in enumerate(zip(angle_index, angle_feat)):
174+
b2b_edges_list[ind].append((y[i], x[i]))
175+
b2b_angle_list[ind].append(radian)
176+
177+
##############################################
178+
# build bond-bond graph with dihedral angles #
179+
##############################################
180+
indices = np.array(indices)
181+
# b2b_graph_list = [[] for _ in range(self.num_angle)]
182+
b2b_graph_list = []
183+
for ind in range(self.num_angle):
184+
dst_ids_list = [edge[1] for edge in b2b_edges_list[ind]]
185+
src_ids_list = [edge[0] for edge in b2b_edges_list[ind]]
186+
dihedral_angles, pair_inds = calcu_angle_in_one_space(dst_ids_list, src_ids_list, coords, indices)
187+
b2b_graph = pgl.Graph(b2b_edges_list[ind], num_nodes=num_bonds,
188+
edge_feat={"angle": np.array(dihedral_angles).reshape(-1,1),
189+
"pairs": np.array(pair_inds)})
190+
b2b_graph_list.append(b2b_graph)
191+
192+
#########################################
193+
# build index for inter-molecular bonds #
194+
#########################################
195+
bond_types = bond_pair_atom_types
196+
type_count = [0 for _ in range(len(pair_ids))]
197+
for type_i in bond_types:
198+
if type_i != -1:
199+
type_count[type_i] += 1
200+
201+
bond_types = np.array(bond_types)
202+
type_count = np.array(type_count)
203+
204+
# mark inter-molecular edges
205+
b_inter_edges = list(zip(inter_bonds, [0 for _ in range(len(inter_bonds))]))
206+
b_inter_graph = pgl.Graph(b_inter_edges, num_nodes=num_bonds)
207+
208+
################################
209+
# build atom to molecule graph #
210+
################################
211+
a2ligand_edges = [(x, 0) for x in range(num_atoms_d)]
212+
a2ligand_graph = pgl.Graph(a2ligand_edges, num_nodes=num_atoms)
213+
a2protein_edges = [(x, 0) for x in range(num_atoms_d, num_atoms)]
214+
a2protein_graph = pgl.Graph(a2protein_edges, num_nodes=num_atoms)
215+
216+
mark_graph_list = [b_inter_graph, a2ligand_graph, a2protein_graph]
217+
graphs = a2a_graph, b2a_graph, b2b_graph_list, mark_graph_list
218+
global_feat = inter_feats, bond_types, type_count
219+
return graphs, global_feat
220+
221+
def load_data(self):
222+
""" Generate complex interaction graphs. """
223+
if self.has_cache():
224+
graphs, global_feat, labels = self.load()
225+
self.a2a_graphs, self.b2a_graphs, self.b2b_graphs_list, self.mark_graphs_list = graphs
226+
self.inter_feats_list, self.bond_types_list, self.type_count_list = global_feat
227+
self.labels = labels
228+
else:
229+
print('Processing raw protein-ligand complex data...')
230+
file_name = os.path.join(self.data_path, "{0}.pkl".format(self.dataset))
231+
with open(file_name, 'rb') as f:
232+
data_mols, data_Y = pickle.load(f)
233+
234+
for mol, y in tqdm(zip(data_mols, data_Y)):
235+
graphs, global_feat = self.build_graph(mol)
236+
if graphs is None:
237+
continue
238+
self.a2a_graphs.append(graphs[0])
239+
self.b2a_graphs.append(graphs[1])
240+
self.b2b_graphs_list.append(graphs[2])
241+
self.mark_graphs_list.append(graphs[3])
242+
243+
self.inter_feats_list.append(global_feat[0])
244+
self.bond_types_list.append(global_feat[1])
245+
self.type_count_list.append(global_feat[2])
246+
self.labels.append(y)
247+
248+
self.labels = np.array(self.labels).reshape(-1, 1)
249+
# self.labels = np.array(data_Y).reshape(-1, 1)
250+
if self.save_file:
251+
self.save()
252+
253+
def _compute_phi_angle(vec_n, vec_neighbors):
254+
thete_list, ind_list = [], []
255+
vec_neigbors_proj = vec_neighbors - vec_n * (np.dot(vec_neighbors, vec_n) / np.dot(vec_n, vec_n)).reshape(-1,1)
256+
num_neighbors = len(vec_neigbors_proj)
257+
error_i = np.where((np.abs(vec_neigbors_proj).sum(1)) == 0)[0]
258+
if len(error_i) > 1:
259+
return [360.0/num_neighbors] * num_neighbors, [i for i in range(num_neighbors)]
260+
if len(error_i) > 0:
261+
vec_neigbors_proj[error_i[0]] = -1*vec_neigbors_proj.mean(0)
262+
if num_neighbors == 1:
263+
return [360], [0]
264+
for i in range(num_neighbors):
265+
vec = vec_neigbors_proj[i]
266+
vec_neigbors_proj_ = np.array([vec_neigbors_proj[j] for j in range(num_neighbors) if j != i])
267+
cross_prod = np.cross(vec, vec_neigbors_proj_)
268+
cosv = np.dot(vec_neigbors_proj_, vec) / (np.linalg.norm(vec) * np.sqrt(np.sum(vec_neigbors_proj_*vec_neigbors_proj_,1)))
269+
cosv = np.clip(cosv, -1, 1)
270+
theta = np.rad2deg(np.arccos(cosv))
271+
theta = np.where(np.sum(vec_n * cross_prod, 1) > 1e-8, theta, 360 - theta)
272+
try:
273+
assert not np.isnan(theta.min())
274+
except:
275+
print(vec_neigbors_proj)
276+
exit(-1)
277+
thete_list += [theta.min()]
278+
ind_list += [theta.argmin()]
279+
return thete_list, ind_list
280+
281+
def _build_edge_vector(coords, origin, body):
282+
if body[0] == origin:
283+
return coords[body[1]] - coords[body[0]]
284+
if body[1] == origin:
285+
return coords[body[0]] - coords[body[1]]
286+
287+
def calcu_angle_in_one_space(edge_x, edge_y, coords, indices):
288+
last = 0
289+
count = []
290+
phi_angle = []
291+
pair_neighbor = []
292+
for i in range(1, len(edge_x)+1):
293+
if i == len(edge_x) or edge_x[i] != edge_x[last]:
294+
body_n = indices[edge_x[last]]
295+
body_neigbhbors = indices[edge_y[last:i]]
296+
if len(body_neigbhbors)>1:
297+
origin = list(set(body_neigbhbors[1])&set(body_neigbhbors[0]))[0]
298+
else:
299+
origin = list(set(body_n)&set(body_neigbhbors[0]))[0]
300+
vec_n = _build_edge_vector(coords, origin, body_n)
301+
vec_neighbors = np.array([_build_edge_vector(coords, origin, body_i) for body_i in body_neigbhbors])
302+
angle, inds = _compute_phi_angle(vec_n, vec_neighbors)
303+
inds = [edge_y[last:i][j] for j in inds]
304+
phi_angle += angle
305+
pair_neighbor += inds
306+
count += [i - last]
307+
last = i
308+
return phi_angle, pair_neighbor
309+
310+
def collate_fn(batch):
311+
a2a_gs, b2a_gs, b2b_gs_l, mark_gs_l, feats, types, counts, labels = map(list, zip(*batch))
312+
313+
a2a_g = pgl.Graph.batch(a2a_gs).tensor()
314+
b2a_g = pgl.BiGraph.batch(b2a_gs).tensor()
315+
# print([[g[i].num_edges for g in b2b_gs_l] for i in range(len(b2b_gs_l[0]))])
316+
b2b_gl = [pgl.Graph.batch([g[i] for g in b2b_gs_l]).tensor() for i in range(len(b2b_gs_l[0]))]
317+
mark_gl = [pgl.Graph.batch([g[i] for g in mark_gs_l]).tensor() for i in range(len(mark_gs_l[0]))]
318+
feats = paddle.concat([paddle.to_tensor(f, dtype='float32') for f in feats])
319+
types = paddle.concat([paddle.to_tensor(t) for t in types])
320+
counts = paddle.stack([paddle.to_tensor(c) for c in counts], axis=1)
321+
labels = paddle.to_tensor(np.array(labels), dtype='float32')
322+
323+
return a2a_g, b2a_g, b2b_gl, mark_gl, feats, types, counts, labels
324+
325+
326+
if __name__ == "__main__":
327+
complex_data = ComplexDataset("./data/", "pdbbind2016_train", 5, 6)
328+
loader = Dataloader(complex_data,
329+
batch_size=32,
330+
shuffle=False,
331+
num_workers=1,
332+
collate_fn=collate_fn)
333+
cc = 0
334+
for batch in loader:
335+
a2a_g, b2a_g, b2b_gl, mark_gs_l, feats, types, counts, labels = batch
336+
# print(labels)
337+
cc += 1
338+
if cc == 2:
339+
break

0 commit comments

Comments
 (0)