AlexSychovUN commited on
Commit
956b371
·
1 Parent(s): 71c437c

Added files

Browse files
GNN_classification/Dataset_Preparation.py CHANGED
@@ -5,7 +5,7 @@ from rdkit import Chem, rdBase
5
  from torch_geometric.data import Data
6
  from torch.utils.data import Dataset
7
 
8
- rdBase.DisableLog('rdApp.*')
9
 
10
 
11
  def one_of_k_encoding(x, allowable_set):
@@ -16,35 +16,86 @@ def one_of_k_encoding(x, allowable_set):
16
 
17
 
18
  def get_atom_features(atom):
19
- symbols_list = ['C', 'N', 'O', 'S', 'F', 'Si', 'P', 'Cl', 'Br', 'Mg', 'Na', 'Ca', 'Fe', 'As', 'Al', 'I', 'B', 'V', 'K', 'Tl', 'Yb', 'Sb', 'Sn', 'Ag', 'Pd', 'Co', 'Se', 'Ti', 'Zn', 'H', 'Li', 'Ge', 'Cu', 'Au', 'Ni', 'Cd', 'In', 'Mn', 'Zr', 'Cr', 'Pt', 'Hg', 'Pb', 'Unknown']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  degrees_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
21
  numhs_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
22
  implicit_valences_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
23
  return np.array(
24
  # Type of atom (Symbol)
25
- one_of_k_encoding(atom.GetSymbol(), symbols_list) +
 
26
  # Number of neighbours (Degree)
27
- one_of_k_encoding(atom.GetDegree(), degrees_list) +
 
28
  # Number of hydrogen atoms (Implicit Hs) - bond donors
29
- one_of_k_encoding(atom.GetTotalNumHs(), numhs_list) +
 
30
  # Valence - chemical potential
31
- one_of_k_encoding(atom.GetImplicitValence(), implicit_valences_list) +
 
32
  # Hybridization - so important for 3d structure, sp2 - Trigonal planar, sp3 - Tetrahedral
33
- one_of_k_encoding(atom.GetHybridization(), [
34
- Chem.rdchem.HybridizationType.SP,
35
- Chem.rdchem.HybridizationType.SP2,
36
- Chem.rdchem.HybridizationType.SP3,
37
- Chem.rdchem.HybridizationType.SP3D,
38
- Chem.rdchem.HybridizationType.SP3D2,
39
- 'other']) +
 
 
 
 
 
40
  # Aromaticity (Boolean)
41
  [atom.GetIsAromatic()]
42
-
43
-
44
  )
45
 
46
 
47
-
48
  class SmilesDataset(Dataset):
49
  def __init__(self, dataframe):
50
  self.data = dataframe
@@ -58,13 +109,13 @@ class SmilesDataset(Dataset):
58
  label = row["label"]
59
 
60
  mol = Chem.MolFromSmiles(smiles)
61
- if mol is None: return None
 
62
 
63
  # Nodes
64
  atom_features = [get_atom_features(atom) for atom in mol.GetAtoms()]
65
  x = torch.tensor(np.array(atom_features), dtype=torch.float)
66
 
67
-
68
  # Edges
69
  edge_indexes = []
70
  for bond in mol.GetBonds():
@@ -78,7 +129,6 @@ class SmilesDataset(Dataset):
78
 
79
  edge_index = torch.tensor(edge_indexes, dtype=torch.long).t().contiguous()
80
 
81
-
82
  # Label
83
  y = torch.tensor([label], dtype=torch.long)
84
  return Data(x=x, edge_index=edge_index, y=y)
@@ -98,8 +148,5 @@ if __name__ == "__main__":
98
  train_dataset = SmilesDataset(train_dataset)
99
  test_dataset = SmilesDataset(test_dataset)
100
 
101
-
102
  print(len(train_dataset))
103
  print(len(test_dataset))
104
-
105
-
 
5
  from torch_geometric.data import Data
6
  from torch.utils.data import Dataset
7
 
8
+ rdBase.DisableLog("rdApp.*")
9
 
10
 
11
  def one_of_k_encoding(x, allowable_set):
 
16
 
17
 
18
  def get_atom_features(atom):
19
+ symbols_list = [
20
+ "C",
21
+ "N",
22
+ "O",
23
+ "S",
24
+ "F",
25
+ "Si",
26
+ "P",
27
+ "Cl",
28
+ "Br",
29
+ "Mg",
30
+ "Na",
31
+ "Ca",
32
+ "Fe",
33
+ "As",
34
+ "Al",
35
+ "I",
36
+ "B",
37
+ "V",
38
+ "K",
39
+ "Tl",
40
+ "Yb",
41
+ "Sb",
42
+ "Sn",
43
+ "Ag",
44
+ "Pd",
45
+ "Co",
46
+ "Se",
47
+ "Ti",
48
+ "Zn",
49
+ "H",
50
+ "Li",
51
+ "Ge",
52
+ "Cu",
53
+ "Au",
54
+ "Ni",
55
+ "Cd",
56
+ "In",
57
+ "Mn",
58
+ "Zr",
59
+ "Cr",
60
+ "Pt",
61
+ "Hg",
62
+ "Pb",
63
+ "Unknown",
64
+ ]
65
  degrees_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
66
  numhs_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
67
  implicit_valences_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
68
  return np.array(
69
  # Type of atom (Symbol)
70
+ one_of_k_encoding(atom.GetSymbol(), symbols_list)
71
+ +
72
  # Number of neighbours (Degree)
73
+ one_of_k_encoding(atom.GetDegree(), degrees_list)
74
+ +
75
  # Number of hydrogen atoms (Implicit Hs) - bond donors
76
+ one_of_k_encoding(atom.GetTotalNumHs(), numhs_list)
77
+ +
78
  # Valence - chemical potential
79
+ one_of_k_encoding(atom.GetImplicitValence(), implicit_valences_list)
80
+ +
81
  # Hybridization - so important for 3d structure, sp2 - Trigonal planar, sp3 - Tetrahedral
82
+ one_of_k_encoding(
83
+ atom.GetHybridization(),
84
+ [
85
+ Chem.rdchem.HybridizationType.SP,
86
+ Chem.rdchem.HybridizationType.SP2,
87
+ Chem.rdchem.HybridizationType.SP3,
88
+ Chem.rdchem.HybridizationType.SP3D,
89
+ Chem.rdchem.HybridizationType.SP3D2,
90
+ "other",
91
+ ],
92
+ )
93
+ +
94
  # Aromaticity (Boolean)
95
  [atom.GetIsAromatic()]
 
 
96
  )
97
 
98
 
 
99
  class SmilesDataset(Dataset):
100
  def __init__(self, dataframe):
101
  self.data = dataframe
 
109
  label = row["label"]
110
 
111
  mol = Chem.MolFromSmiles(smiles)
112
+ if mol is None:
113
+ return None
114
 
115
  # Nodes
116
  atom_features = [get_atom_features(atom) for atom in mol.GetAtoms()]
117
  x = torch.tensor(np.array(atom_features), dtype=torch.float)
118
 
 
119
  # Edges
120
  edge_indexes = []
121
  for bond in mol.GetBonds():
 
129
 
130
  edge_index = torch.tensor(edge_indexes, dtype=torch.long).t().contiguous()
131
 
 
132
  # Label
133
  y = torch.tensor([label], dtype=torch.long)
134
  return Data(x=x, edge_index=edge_index, y=y)
 
148
  train_dataset = SmilesDataset(train_dataset)
149
  test_dataset = SmilesDataset(test_dataset)
150
 
 
151
  print(len(train_dataset))
152
  print(len(test_dataset))
 
 
GNN_classification/model.py CHANGED
@@ -1,13 +1,9 @@
1
- import torch
2
  import torch.nn as nn
3
  import torch.nn.functional as F
4
- import pandas as pd
5
- from rdkit import Chem
6
 
7
  from torch_geometric.nn import GCNConv, global_mean_pool
8
- from torch_geometric.data import Data
9
- from torch_geometric.loader import DataLoader
10
- from torch.utils.data import Dataset
11
 
12
  class GNNClassifier(nn.Module):
13
  def __init__(self, input_dim, output_dim, hidden_channels):
@@ -18,7 +14,7 @@ class GNNClassifier(nn.Module):
18
  self.conv2 = GCNConv(hidden_channels, hidden_channels)
19
  self.conv3 = GCNConv(hidden_channels, hidden_channels)
20
 
21
- self.lin = nn.Linear(hidden_channels, output_dim) # classification task 0 or 1
22
 
23
  def forward(self, x, edge_index, batch):
24
  x = self.conv1(x, edge_index)
@@ -28,8 +24,8 @@ class GNNClassifier(nn.Module):
28
  x = self.conv3(x, edge_index)
29
 
30
  # Averaging nodes and got the molecula vector
31
- x = global_mean_pool(x, batch) # [batch_size, hidden_channels]
32
 
33
  x = F.dropout(x, p=0.5, training=self.training)
34
  x = self.lin(x)
35
- return x
 
 
1
  import torch.nn as nn
2
  import torch.nn.functional as F
3
+
 
4
 
5
  from torch_geometric.nn import GCNConv, global_mean_pool
6
+
 
 
7
 
8
  class GNNClassifier(nn.Module):
9
  def __init__(self, input_dim, output_dim, hidden_channels):
 
14
  self.conv2 = GCNConv(hidden_channels, hidden_channels)
15
  self.conv3 = GCNConv(hidden_channels, hidden_channels)
16
 
17
+ self.lin = nn.Linear(hidden_channels, output_dim) # classification task 0 or 1
18
 
19
  def forward(self, x, edge_index, batch):
20
  x = self.conv1(x, edge_index)
 
24
  x = self.conv3(x, edge_index)
25
 
26
  # Averaging nodes and got the molecula vector
27
+ x = global_mean_pool(x, batch) # [batch_size, hidden_channels]
28
 
29
  x = F.dropout(x, p=0.5, training=self.training)
30
  x = self.lin(x)
31
+ return x
GNN_classification/training.py CHANGED
@@ -8,9 +8,10 @@ from torch_geometric.loader import DataLoader
8
  from Dataset_Preparation import SmilesDataset
9
  from model import GNNClassifier
10
 
11
- DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
12
  print(DEVICE)
13
 
 
14
  def train(model, loader, optimizer, criterion):
15
  model.train()
16
  total_loss = 0
@@ -73,9 +74,9 @@ if __name__ == "__main__":
73
  train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
74
  test_loader = DataLoader(test_dataset, batch_size=32, shuffle=True)
75
 
76
-
77
-
78
- model = GNNClassifier(input_dim=num_node_features, output_dim=num_classes, hidden_channels=16).to(DEVICE)
79
 
80
  optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
81
  criterion = torch.nn.CrossEntropyLoss()
 
8
  from Dataset_Preparation import SmilesDataset
9
  from model import GNNClassifier
10
 
11
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12
  print(DEVICE)
13
 
14
+
15
  def train(model, loader, optimizer, criterion):
16
  model.train()
17
  total_loss = 0
 
74
  train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
75
  test_loader = DataLoader(test_dataset, batch_size=32, shuffle=True)
76
 
77
+ model = GNNClassifier(
78
+ input_dim=num_node_features, output_dim=num_classes, hidden_channels=16
79
+ ).to(DEVICE)
80
 
81
  optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
82
  criterion = torch.nn.CrossEntropyLoss()
requirements.txt CHANGED
@@ -1,4 +1,8 @@
 
 
 
1
  pandas
 
2
  rdkit
3
  biopython
4
- torch
 
1
+ torch
2
+
3
+ numpy
4
  pandas
5
+
6
  rdkit
7
  biopython
8
+
visualization.ipynb CHANGED
@@ -2,180 +2,261 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 1,
6
  "id": "initial_id",
7
- "metadata": {},
 
 
 
 
 
 
 
 
 
8
  "outputs": [
9
  {
10
  "data": {
 
11
  "application/vnd.jupyter.widget-view+json": {
12
- "model_id": "ccfa267dcd6945b6be10a9cbeffb4e5e",
13
  "version_major": 2,
14
- "version_minor": 0
15
- },
16
- "text/plain": []
17
  },
18
  "metadata": {},
19
- "output_type": "display_data"
 
 
 
20
  }
21
  ],
22
- "source": [
23
- "import nglview as nv\n",
24
- "import os"
25
- ]
26
  },
27
  {
28
  "cell_type": "code",
29
- "execution_count": 2,
30
  "id": "d8d7978e-980a-400c-8c6a-5365990c8855",
31
- "metadata": {},
32
- "outputs": [],
 
 
 
 
33
  "source": [
34
  "PDBBIND_PATH = \"refined-set\""
35
- ]
 
 
36
  },
37
  {
38
  "cell_type": "code",
39
- "execution_count": 3,
40
  "id": "788a6b43-c515-45c7-bc52-341d446b1a65",
41
- "metadata": {},
42
- "outputs": [],
 
 
 
 
43
  "source": [
44
  "EXAMPLE_PDB_ID = \"1a1e\""
45
- ]
 
 
46
  },
47
  {
48
  "cell_type": "code",
49
- "execution_count": 4,
50
  "id": "e8f4bebc-845f-43e8-bc4d-ab7b649eb49c",
51
- "metadata": {},
52
- "outputs": [],
 
 
 
 
53
  "source": [
54
  "pdb_dir = os.path.join(PDBBIND_PATH, EXAMPLE_PDB_ID)"
55
- ]
 
 
56
  },
57
  {
58
  "cell_type": "code",
59
- "execution_count": 5,
60
  "id": "24b5e435-4d8f-4505-b27c-dd6317376ed4",
61
- "metadata": {},
62
- "outputs": [],
 
 
 
 
63
  "source": [
64
  "protein_file = os.path.join(pdb_dir, f\"{EXAMPLE_PDB_ID}_protein.pdb\")"
65
- ]
 
 
66
  },
67
  {
68
  "cell_type": "code",
69
- "execution_count": 6,
70
  "id": "e7fc3539-00c0-48a2-b012-c80757fa12c4",
71
- "metadata": {},
72
- "outputs": [],
 
 
 
 
73
  "source": [
74
  "ligand_file = os.path.join(pdb_dir, f\"{EXAMPLE_PDB_ID}_ligand.sdf\")"
75
- ]
 
 
76
  },
77
  {
78
  "cell_type": "code",
79
- "execution_count": 7,
80
  "id": "9a053b99-7c01-4881-b3f7-e9b39090af9d",
81
- "metadata": {},
82
- "outputs": [],
 
 
 
 
83
  "source": [
84
  "view = nv.NGLWidget()"
85
- ]
 
 
86
  },
87
  {
88
  "cell_type": "code",
89
- "execution_count": 8,
90
  "id": "df8c8e00-3ce6-41dd-b457-d9f50e318dad",
91
- "metadata": {},
92
- "outputs": [],
 
 
 
 
93
  "source": [
94
  "protein_comp = view.add_component(protein_file)"
95
- ]
 
 
96
  },
97
  {
98
  "cell_type": "code",
99
- "execution_count": 9,
100
  "id": "c191fead-fef8-4077-b787-5bf9552307b1",
101
- "metadata": {},
102
- "outputs": [],
 
 
 
 
103
  "source": [
104
  "protein_comp.clear_representations()"
105
- ]
 
 
106
  },
107
  {
108
  "cell_type": "code",
109
- "execution_count": 10,
110
  "id": "4559033a-aeda-4659-8d91-9002b5a6ecda",
111
- "metadata": {},
112
- "outputs": [],
 
 
 
 
113
  "source": [
114
  "protein_comp.add_representation('cartoon', color='blue')"
115
- ]
 
 
116
  },
117
  {
118
  "cell_type": "code",
119
- "execution_count": 11,
120
  "id": "73ea1a50-8463-40b8-a942-0c92d3e97a97",
121
- "metadata": {},
122
- "outputs": [],
 
 
 
 
123
  "source": [
124
  "ligand_comp = view.add_component(ligand_file)"
125
- ]
 
 
126
  },
127
  {
128
  "cell_type": "code",
129
- "execution_count": 12,
130
  "id": "16cdb710-1ed6-4b1d-9e6a-69b7ad61a600",
131
- "metadata": {},
132
- "outputs": [],
 
 
 
 
133
  "source": [
134
  "ligand_comp.clear_representations()"
135
- ]
 
 
136
  },
137
  {
138
  "cell_type": "code",
139
- "execution_count": 13,
140
  "id": "2193c497-f33c-4de0-86a9-6e535002fcb7",
141
- "metadata": {},
142
- "outputs": [],
 
 
 
 
143
  "source": [
144
  "ligand_comp.add_representation('ball+stick', radius=0.3)"
145
- ]
 
 
146
  },
147
  {
148
  "cell_type": "code",
149
- "execution_count": 14,
150
  "id": "b1cc7f44-a374-4400-b4ba-8f75101b21ce",
151
- "metadata": {},
 
 
 
 
 
 
 
 
152
  "outputs": [
153
  {
154
  "data": {
155
- "application/vnd.jupyter.widget-view+json": {
156
- "model_id": "6037e0edee3247a49cd586e52e64a61b",
157
- "version_major": 2,
158
- "version_minor": 0
159
- },
160
  "text/plain": [
161
  "NGLWidget()"
162
- ]
 
 
 
 
 
163
  },
164
  "metadata": {},
165
- "output_type": "display_data"
 
 
 
166
  }
167
  ],
168
- "source": [
169
- "view"
170
- ]
171
  },
172
  {
173
  "cell_type": "code",
174
- "execution_count": null,
175
  "id": "5655e465-bb44-4218-a5e3-db2c5e62cd9c",
176
- "metadata": {},
 
 
 
 
 
 
177
  "outputs": [],
178
- "source": []
179
  }
180
  ],
181
  "metadata": {
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
 
5
  "id": "initial_id",
6
+ "metadata": {
7
+ "ExecuteTime": {
8
+ "end_time": "2025-12-05T14:02:00.479196Z",
9
+ "start_time": "2025-12-05T14:02:00.003864Z"
10
+ }
11
+ },
12
+ "source": [
13
+ "import nglview as nv\n",
14
+ "import os"
15
+ ],
16
  "outputs": [
17
  {
18
  "data": {
19
+ "text/plain": [],
20
  "application/vnd.jupyter.widget-view+json": {
 
21
  "version_major": 2,
22
+ "version_minor": 0,
23
+ "model_id": "3016118bc02a458cbcb4491a27089a6a"
24
+ }
25
  },
26
  "metadata": {},
27
+ "output_type": "display_data",
28
+ "jetTransient": {
29
+ "display_id": null
30
+ }
31
  }
32
  ],
33
+ "execution_count": 1
 
 
 
34
  },
35
  {
36
  "cell_type": "code",
 
37
  "id": "d8d7978e-980a-400c-8c6a-5365990c8855",
38
+ "metadata": {
39
+ "ExecuteTime": {
40
+ "end_time": "2025-12-05T14:02:00.497753Z",
41
+ "start_time": "2025-12-05T14:02:00.493751Z"
42
+ }
43
+ },
44
  "source": [
45
  "PDBBIND_PATH = \"refined-set\""
46
+ ],
47
+ "outputs": [],
48
+ "execution_count": 2
49
  },
50
  {
51
  "cell_type": "code",
 
52
  "id": "788a6b43-c515-45c7-bc52-341d446b1a65",
53
+ "metadata": {
54
+ "ExecuteTime": {
55
+ "end_time": "2025-12-05T14:02:00.510747Z",
56
+ "start_time": "2025-12-05T14:02:00.505672Z"
57
+ }
58
+ },
59
  "source": [
60
  "EXAMPLE_PDB_ID = \"1a1e\""
61
+ ],
62
+ "outputs": [],
63
+ "execution_count": 3
64
  },
65
  {
66
  "cell_type": "code",
 
67
  "id": "e8f4bebc-845f-43e8-bc4d-ab7b649eb49c",
68
+ "metadata": {
69
+ "ExecuteTime": {
70
+ "end_time": "2025-12-05T14:02:00.523669Z",
71
+ "start_time": "2025-12-05T14:02:00.518519Z"
72
+ }
73
+ },
74
  "source": [
75
  "pdb_dir = os.path.join(PDBBIND_PATH, EXAMPLE_PDB_ID)"
76
+ ],
77
+ "outputs": [],
78
+ "execution_count": 4
79
  },
80
  {
81
  "cell_type": "code",
 
82
  "id": "24b5e435-4d8f-4505-b27c-dd6317376ed4",
83
+ "metadata": {
84
+ "ExecuteTime": {
85
+ "end_time": "2025-12-05T14:02:00.570497Z",
86
+ "start_time": "2025-12-05T14:02:00.565454Z"
87
+ }
88
+ },
89
  "source": [
90
  "protein_file = os.path.join(pdb_dir, f\"{EXAMPLE_PDB_ID}_protein.pdb\")"
91
+ ],
92
+ "outputs": [],
93
+ "execution_count": 5
94
  },
95
  {
96
  "cell_type": "code",
 
97
  "id": "e7fc3539-00c0-48a2-b012-c80757fa12c4",
98
+ "metadata": {
99
+ "ExecuteTime": {
100
+ "end_time": "2025-12-05T14:02:00.584673Z",
101
+ "start_time": "2025-12-05T14:02:00.578982Z"
102
+ }
103
+ },
104
  "source": [
105
  "ligand_file = os.path.join(pdb_dir, f\"{EXAMPLE_PDB_ID}_ligand.sdf\")"
106
+ ],
107
+ "outputs": [],
108
+ "execution_count": 6
109
  },
110
  {
111
  "cell_type": "code",
 
112
  "id": "9a053b99-7c01-4881-b3f7-e9b39090af9d",
113
+ "metadata": {
114
+ "ExecuteTime": {
115
+ "end_time": "2025-12-05T14:02:00.649631Z",
116
+ "start_time": "2025-12-05T14:02:00.591897Z"
117
+ }
118
+ },
119
  "source": [
120
  "view = nv.NGLWidget()"
121
+ ],
122
+ "outputs": [],
123
+ "execution_count": 7
124
  },
125
  {
126
  "cell_type": "code",
 
127
  "id": "df8c8e00-3ce6-41dd-b457-d9f50e318dad",
128
+ "metadata": {
129
+ "ExecuteTime": {
130
+ "end_time": "2025-12-05T14:02:00.779528Z",
131
+ "start_time": "2025-12-05T14:02:00.657448Z"
132
+ }
133
+ },
134
  "source": [
135
  "protein_comp = view.add_component(protein_file)"
136
+ ],
137
+ "outputs": [],
138
+ "execution_count": 8
139
  },
140
  {
141
  "cell_type": "code",
 
142
  "id": "c191fead-fef8-4077-b787-5bf9552307b1",
143
+ "metadata": {
144
+ "ExecuteTime": {
145
+ "end_time": "2025-12-05T14:02:00.802894Z",
146
+ "start_time": "2025-12-05T14:02:00.795534Z"
147
+ }
148
+ },
149
  "source": [
150
  "protein_comp.clear_representations()"
151
+ ],
152
+ "outputs": [],
153
+ "execution_count": 9
154
  },
155
  {
156
  "cell_type": "code",
 
157
  "id": "4559033a-aeda-4659-8d91-9002b5a6ecda",
158
+ "metadata": {
159
+ "ExecuteTime": {
160
+ "end_time": "2025-12-05T14:02:00.824161Z",
161
+ "start_time": "2025-12-05T14:02:00.817622Z"
162
+ }
163
+ },
164
  "source": [
165
  "protein_comp.add_representation('cartoon', color='blue')"
166
+ ],
167
+ "outputs": [],
168
+ "execution_count": 10
169
  },
170
  {
171
  "cell_type": "code",
 
172
  "id": "73ea1a50-8463-40b8-a942-0c92d3e97a97",
173
+ "metadata": {
174
+ "ExecuteTime": {
175
+ "end_time": "2025-12-05T14:02:00.850013Z",
176
+ "start_time": "2025-12-05T14:02:00.840262Z"
177
+ }
178
+ },
179
  "source": [
180
  "ligand_comp = view.add_component(ligand_file)"
181
+ ],
182
+ "outputs": [],
183
+ "execution_count": 11
184
  },
185
  {
186
  "cell_type": "code",
 
187
  "id": "16cdb710-1ed6-4b1d-9e6a-69b7ad61a600",
188
+ "metadata": {
189
+ "ExecuteTime": {
190
+ "end_time": "2025-12-05T14:02:00.866184Z",
191
+ "start_time": "2025-12-05T14:02:00.859732Z"
192
+ }
193
+ },
194
  "source": [
195
  "ligand_comp.clear_representations()"
196
+ ],
197
+ "outputs": [],
198
+ "execution_count": 12
199
  },
200
  {
201
  "cell_type": "code",
 
202
  "id": "2193c497-f33c-4de0-86a9-6e535002fcb7",
203
+ "metadata": {
204
+ "ExecuteTime": {
205
+ "end_time": "2025-12-05T14:02:00.882846Z",
206
+ "start_time": "2025-12-05T14:02:00.876856Z"
207
+ }
208
+ },
209
  "source": [
210
  "ligand_comp.add_representation('ball+stick', radius=0.3)"
211
+ ],
212
+ "outputs": [],
213
+ "execution_count": 13
214
  },
215
  {
216
  "cell_type": "code",
 
217
  "id": "b1cc7f44-a374-4400-b4ba-8f75101b21ce",
218
+ "metadata": {
219
+ "ExecuteTime": {
220
+ "end_time": "2025-12-05T14:02:00.903573Z",
221
+ "start_time": "2025-12-05T14:02:00.897038Z"
222
+ }
223
+ },
224
+ "source": [
225
+ "view"
226
+ ],
227
  "outputs": [
228
  {
229
  "data": {
 
 
 
 
 
230
  "text/plain": [
231
  "NGLWidget()"
232
+ ],
233
+ "application/vnd.jupyter.widget-view+json": {
234
+ "version_major": 2,
235
+ "version_minor": 0,
236
+ "model_id": "028b8398377e4869a80fba4c3d5e5921"
237
+ }
238
  },
239
  "metadata": {},
240
+ "output_type": "display_data",
241
+ "jetTransient": {
242
+ "display_id": null
243
+ }
244
  }
245
  ],
246
+ "execution_count": 14
 
 
247
  },
248
  {
249
  "cell_type": "code",
 
250
  "id": "5655e465-bb44-4218-a5e3-db2c5e62cd9c",
251
+ "metadata": {
252
+ "ExecuteTime": {
253
+ "end_time": "2025-12-05T14:02:00.915090Z",
254
+ "start_time": "2025-12-05T14:02:00.912563Z"
255
+ }
256
+ },
257
+ "source": [],
258
  "outputs": [],
259
+ "execution_count": null
260
  }
261
  ],
262
  "metadata": {