pxmeter 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pxmeter-0.1.2/pxmeter.egg-info → pxmeter-0.1.4}/PKG-INFO +1 -1
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/calc_metric.py +11 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/run_config.py +4 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/struct.py +32 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/mapping.py +20 -7
- pxmeter-0.1.4/pxmeter/metrics/clashes.py +88 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/chain.py +105 -9
- {pxmeter-0.1.2 → pxmeter-0.1.4/pxmeter.egg-info}/PKG-INFO +1 -1
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/SOURCES.txt +1 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/setup.py +1 -1
- {pxmeter-0.1.2 → pxmeter-0.1.4}/LICENSE +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/MANIFEST.in +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/README.md +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/__init__.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/cli.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/__init__.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/data_config.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/constants.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/__init__.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/ccd.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/parser.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/utils.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/writer.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/eval.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/__init__.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/lddt_metrics.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/rmsd.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/rmsd_metrics.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/__init__.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/atom.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/utils.py +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/dependency_links.txt +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/entry_points.txt +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/requires.txt +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/top_level.txt +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/requirements.txt +0 -0
- {pxmeter-0.1.2 → pxmeter-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pxmeter
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: PXMeter is a comprehensive toolkit for evaluating the quality of structures generated by biomolecular structure prediction models.
|
|
5
5
|
Author: Bytedance Inc.
|
|
6
6
|
Author-email: ai4s-bio@bytedance.com
|
|
@@ -34,6 +34,7 @@ from pxmeter.configs.run_config import RUN_CONFIG
|
|
|
34
34
|
from pxmeter.constants import IONS, LIGAND
|
|
35
35
|
from pxmeter.data.ccd import get_ccd_mol_from_chain_atom_array
|
|
36
36
|
from pxmeter.data.struct import Structure
|
|
37
|
+
from pxmeter.metrics.clashes import check_clashes_by_vdw
|
|
37
38
|
from pxmeter.metrics.lddt_metrics import LDDT
|
|
38
39
|
from pxmeter.metrics.rmsd_metrics import RMSDMetrics
|
|
39
40
|
|
|
@@ -554,6 +555,16 @@ class MetricResult:
|
|
|
554
555
|
meta_info_dict["ref_to_model_chain_mapping"] = chain_map
|
|
555
556
|
meta_info_dict["ref_chain_info"] = cls._get_chain_info(ref_struct)
|
|
556
557
|
|
|
558
|
+
# Calculate clashes
|
|
559
|
+
if metric_config.calc_clashes:
|
|
560
|
+
clashes = check_clashes_by_vdw(
|
|
561
|
+
model_struct.atom_array,
|
|
562
|
+
vdw_scale_factor=metric_config.clashes.vdw_scale_factor,
|
|
563
|
+
)
|
|
564
|
+
complex_result_dict["clashes"] = len(
|
|
565
|
+
{x for a, b in clashes for x in (a, b)}
|
|
566
|
+
)
|
|
567
|
+
|
|
557
568
|
# Calculate RMSD (if ligand and pocket specified in ref_features)
|
|
558
569
|
if metric_config.calc_rmsd and interested_lig_label_asym_id:
|
|
559
570
|
rmsd_metrics = RMSDMetrics(
|
|
@@ -23,6 +23,7 @@ RUN_CONFIG = ConfigDict(
|
|
|
23
23
|
"enumerate_all_anchors": True,
|
|
24
24
|
},
|
|
25
25
|
"metric": {
|
|
26
|
+
"calc_clashes": True,
|
|
26
27
|
"calc_lddt": True,
|
|
27
28
|
"calc_dockq": True,
|
|
28
29
|
"calc_rmsd": True,
|
|
@@ -32,6 +33,9 @@ RUN_CONFIG = ConfigDict(
|
|
|
32
33
|
"nucleotide_threshold": 30.0,
|
|
33
34
|
"non_nucleotide_threshold": 15.0,
|
|
34
35
|
},
|
|
36
|
+
"clashes": {
|
|
37
|
+
"vdw_scale_factor": 0.5,
|
|
38
|
+
},
|
|
35
39
|
},
|
|
36
40
|
}
|
|
37
41
|
)
|
|
@@ -432,6 +432,38 @@ class Structure:
|
|
|
432
432
|
chain_id_to_entity_id[chain_id] = entity_id
|
|
433
433
|
return chain_id_to_entity_id
|
|
434
434
|
|
|
435
|
+
def get_ligand_polymer_bonds(self) -> np.ndarray:
|
|
436
|
+
"""
|
|
437
|
+
Get bonds between the bonded ligand and its parent chain.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
np.ndarray: bond records between the bonded ligand and its parent chain.
|
|
441
|
+
e.g. np.array([[atom1, atom2, bond_order]...])
|
|
442
|
+
"""
|
|
443
|
+
atom_array = self.atom_array
|
|
444
|
+
bond_array = atom_array.bonds.as_array()
|
|
445
|
+
|
|
446
|
+
polymer_mask = np.isin(
|
|
447
|
+
atom_array.label_entity_id, list(self.entity_poly_type.keys())
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
lig_mask = ~polymer_mask
|
|
451
|
+
|
|
452
|
+
idx_i = bond_array[:, 0]
|
|
453
|
+
idx_j = bond_array[:, 1]
|
|
454
|
+
|
|
455
|
+
lig_polymer_bond_indices = np.where(
|
|
456
|
+
(lig_mask[idx_i] & polymer_mask[idx_j])
|
|
457
|
+
| (lig_mask[idx_j] & polymer_mask[idx_i])
|
|
458
|
+
)[0]
|
|
459
|
+
if lig_polymer_bond_indices.size == 0:
|
|
460
|
+
# no ligand-polymer bonds
|
|
461
|
+
lig_polymer_bonds = np.empty((0, 3)).astype(int)
|
|
462
|
+
else:
|
|
463
|
+
# np.array([[atom1, atom2, bond_order], ...])
|
|
464
|
+
lig_polymer_bonds = bond_array[lig_polymer_bond_indices]
|
|
465
|
+
return lig_polymer_bonds
|
|
466
|
+
|
|
435
467
|
def clean_structure(
|
|
436
468
|
self,
|
|
437
469
|
mse_to_met=True,
|
|
@@ -924,15 +924,28 @@ class MappingCIF:
|
|
|
924
924
|
# This step will be change the res_id, res_name, atom_name in self.model_struct.atom_array
|
|
925
925
|
self._align_model_lig_atom_to_ref(self.model_struct, model_to_ref_atom_mapping)
|
|
926
926
|
|
|
927
|
+
# Re-order model struct by res_id for each chain
|
|
928
|
+
order = []
|
|
929
|
+
for chain_id in np.unique(self.model_struct.uni_chain_id):
|
|
930
|
+
chain_mask = np.where(self.model_struct.uni_chain_id == chain_id)[0]
|
|
931
|
+
# Remove unmapped ligands
|
|
932
|
+
valid_chain_mask = chain_mask[
|
|
933
|
+
~(
|
|
934
|
+
(
|
|
935
|
+
(self.model_struct.atom_array.res_name[chain_mask] == ".")
|
|
936
|
+
& (self.model_struct.atom_array.atom_name[chain_mask] == ".")
|
|
937
|
+
)
|
|
938
|
+
| (
|
|
939
|
+
self.model_struct.atom_array.res_id[chain_mask] < 0
|
|
940
|
+
) # -1 for unmapped residues
|
|
941
|
+
)
|
|
942
|
+
]
|
|
943
|
+
res_ids = self.model_struct.atom_array.res_id[valid_chain_mask]
|
|
944
|
+
order.extend(valid_chain_mask[np.argsort(res_ids)])
|
|
945
|
+
|
|
927
946
|
# Remove unmapped ligand of model and reset unique atom id
|
|
928
947
|
self.model_struct = self.model_struct.select_substructure(
|
|
929
|
-
|
|
930
|
-
(
|
|
931
|
-
(self.model_struct.atom_array.res_name == ".")
|
|
932
|
-
& (self.model_struct.atom_array.atom_name == ".")
|
|
933
|
-
)
|
|
934
|
-
| (self.model_struct.atom_array.res_id < 0) # -1 for unmapped residues
|
|
935
|
-
),
|
|
948
|
+
order,
|
|
936
949
|
reset_uni_id=True,
|
|
937
950
|
)
|
|
938
951
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Copyright 2025 ByteDance and/or its affiliates.
|
|
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
|
+
from typing import Sequence
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from biotite.structure import AtomArray
|
|
19
|
+
from biotite.structure.info.radii import vdw_radius_single
|
|
20
|
+
from scipy.spatial import KDTree
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_clashes_by_vdw(
|
|
24
|
+
atom_array: AtomArray,
|
|
25
|
+
query_mask: Sequence[bool] = None,
|
|
26
|
+
vdw_scale_factor: float = 0.5,
|
|
27
|
+
) -> list[tuple[int, int]]:
|
|
28
|
+
"""
|
|
29
|
+
Check clashes between atoms in the given atom array.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
atom_array (AtomArray): The atom array to check for clashes.
|
|
33
|
+
query_mask (bool, optional): A boolean mask to select atoms to check for clashes.
|
|
34
|
+
If None, all atoms are checked.
|
|
35
|
+
vdw_scale_factor (float, optional): The scale factor to apply to the Van der Waals radii.
|
|
36
|
+
Defaults to 0.5.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
list[tuple[int, int]]: A list of tuples representing the indices of atoms that are in clash.
|
|
40
|
+
"""
|
|
41
|
+
if query_mask is None:
|
|
42
|
+
# query all atoms
|
|
43
|
+
query_mask = np.ones(len(atom_array), dtype=bool)
|
|
44
|
+
elif not np.any(query_mask):
|
|
45
|
+
# no query atoms, return empty list
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
if query_mask is None:
|
|
49
|
+
query_mask = np.ones(len(atom_array), dtype=bool)
|
|
50
|
+
|
|
51
|
+
query_idx_in_ref = np.where(query_mask)[0]
|
|
52
|
+
|
|
53
|
+
vdw_radii = np.array([vdw_radius_single(e) for e in atom_array.element])
|
|
54
|
+
query_vdw_radii = vdw_radii[query_mask]
|
|
55
|
+
|
|
56
|
+
clashes = []
|
|
57
|
+
query_tree = KDTree(atom_array.coord)
|
|
58
|
+
for query_idx, nbs_idx in enumerate(
|
|
59
|
+
query_tree.query_ball_point(atom_array.coord[query_mask], r=3.0)
|
|
60
|
+
):
|
|
61
|
+
query_bonds, _query_bond_types = atom_array.bonds.get_bonds(
|
|
62
|
+
query_idx_in_ref[query_idx]
|
|
63
|
+
)
|
|
64
|
+
query_vdw = query_vdw_radii[query_idx]
|
|
65
|
+
if query_vdw is None:
|
|
66
|
+
# undefined vdw for elem, use 1.7 as "C"
|
|
67
|
+
query_vdw = vdw_radius_single("C")
|
|
68
|
+
|
|
69
|
+
for nb_idx in nbs_idx:
|
|
70
|
+
if query_idx_in_ref[query_idx] == nb_idx:
|
|
71
|
+
# clash with self
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
if nb_idx in query_bonds:
|
|
75
|
+
# clash with bonded atoms
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
nb_vdw = vdw_radii[nb_idx]
|
|
79
|
+
if nb_vdw is None:
|
|
80
|
+
# undefined vdw for elem, use 1.7 as "C"
|
|
81
|
+
nb_vdw = vdw_radius_single("C")
|
|
82
|
+
|
|
83
|
+
dist = np.linalg.norm(
|
|
84
|
+
atom_array.coord[query_mask][query_idx] - atom_array.coord[nb_idx]
|
|
85
|
+
)
|
|
86
|
+
if dist < vdw_scale_factor * (query_vdw + nb_vdw):
|
|
87
|
+
clashes.append((query_idx_in_ref[query_idx], nb_idx))
|
|
88
|
+
return clashes
|
|
@@ -59,6 +59,71 @@ class ChainPermutation:
|
|
|
59
59
|
self.ref_chain_id_to_entity_id = self.ref_struct.get_chain_id_to_entity_id()
|
|
60
60
|
self.model_chain_id_to_entity_id = self.model_struct.get_chain_id_to_entity_id()
|
|
61
61
|
|
|
62
|
+
self.ref_and_model_mapping_ban_set = self._get_ban_set_by_lig_bonded_position()
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def find_bonded_position_for_lig_chains(
|
|
66
|
+
struct: Structure,
|
|
67
|
+
) -> dict[str, tuple[str, int]]:
|
|
68
|
+
"""
|
|
69
|
+
Find the bonded entity ID and residue ID for ligand chains.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
struct (Structure): Structure object.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
dict[str, tuple[str, int]]: Mapping of ligand chain ID to bonded
|
|
76
|
+
entity ID and residue ID.
|
|
77
|
+
"""
|
|
78
|
+
ligand_polymer_bonds = struct.get_ligand_polymer_bonds()
|
|
79
|
+
|
|
80
|
+
chain_id_to_bonded_position = {}
|
|
81
|
+
for bond in ligand_polymer_bonds:
|
|
82
|
+
atom1, atom2, _ = bond
|
|
83
|
+
if struct.atom_array.label_entity_id[atom1] not in struct.entity_poly_type:
|
|
84
|
+
# atom1 is ligand
|
|
85
|
+
lig_chain_id = struct.uni_chain_id[atom1]
|
|
86
|
+
entity_id = struct.atom_array.label_entity_id[atom2]
|
|
87
|
+
res_id = struct.atom_array.res_id[atom2]
|
|
88
|
+
else:
|
|
89
|
+
lig_chain_id = struct.uni_chain_id[atom2]
|
|
90
|
+
entity_id = struct.atom_array.label_entity_id[atom1]
|
|
91
|
+
res_id = struct.atom_array.res_id[atom1]
|
|
92
|
+
chain_id_to_bonded_position[lig_chain_id] = (entity_id, res_id)
|
|
93
|
+
return chain_id_to_bonded_position
|
|
94
|
+
|
|
95
|
+
def _get_ban_set_by_lig_bonded_position(
|
|
96
|
+
self,
|
|
97
|
+
) -> set[tuple[str, str]]:
|
|
98
|
+
ref_chain_id_to_bond_position = (
|
|
99
|
+
ChainPermutation.find_bonded_position_for_lig_chains(self.ref_struct)
|
|
100
|
+
)
|
|
101
|
+
model_chain_id_to_bond_position = (
|
|
102
|
+
ChainPermutation.find_bonded_position_for_lig_chains(self.model_struct)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
ban_set = set()
|
|
106
|
+
for ref_chain_id in np.unique(self.ref_struct.uni_chain_id):
|
|
107
|
+
ref_bonded_entity, ref_bonded_res_id = ref_chain_id_to_bond_position.get(
|
|
108
|
+
ref_chain_id, ["-1", -1]
|
|
109
|
+
)
|
|
110
|
+
mapped_model_bonded_entity = self.ref_to_model_entity_id.get(
|
|
111
|
+
ref_bonded_entity, "-1"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
for model_chain_id in np.unique(self.model_struct.uni_chain_id):
|
|
115
|
+
(
|
|
116
|
+
model_bonded_entity,
|
|
117
|
+
model_bonded_res_id,
|
|
118
|
+
) = model_chain_id_to_bond_position.get(model_chain_id, ["-1", -1])
|
|
119
|
+
|
|
120
|
+
if mapped_model_bonded_entity != "-1" and model_bonded_entity != "-1":
|
|
121
|
+
if (mapped_model_bonded_entity != model_bonded_entity) or (
|
|
122
|
+
ref_bonded_res_id != model_bonded_res_id
|
|
123
|
+
):
|
|
124
|
+
ban_set.add((ref_chain_id, model_chain_id))
|
|
125
|
+
return ban_set
|
|
126
|
+
|
|
62
127
|
def find_model_anchor_chains(self) -> str:
|
|
63
128
|
"""
|
|
64
129
|
Ref: AlphaFold3 SI Chapter 4.2. -> AlphaFold Multimer Chapter 7.3.1
|
|
@@ -180,15 +245,22 @@ class ChainPermutation:
|
|
|
180
245
|
row_indices = []
|
|
181
246
|
col_indices = []
|
|
182
247
|
|
|
248
|
+
dist_matrix_copy = dist_matrix.copy()
|
|
183
249
|
for _ in range(num_cols):
|
|
184
|
-
min_pos = np.unravel_index(
|
|
250
|
+
min_pos = np.unravel_index(
|
|
251
|
+
np.argmin(dist_matrix_copy), dist_matrix_copy.shape
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
if dist_matrix_copy[min_pos[0], min_pos[1]] == np.inf:
|
|
255
|
+
# No more valid pairs
|
|
256
|
+
break
|
|
185
257
|
|
|
186
258
|
row_indices.append(min_pos[0])
|
|
187
259
|
col_indices.append(min_pos[1])
|
|
188
260
|
|
|
189
261
|
# Set the found row and column to np.inf to ignore it
|
|
190
|
-
|
|
191
|
-
|
|
262
|
+
dist_matrix_copy[min_pos[0], :] = np.inf
|
|
263
|
+
dist_matrix_copy[:, min_pos[1]] = np.inf
|
|
192
264
|
return row_indices, col_indices
|
|
193
265
|
|
|
194
266
|
@staticmethod
|
|
@@ -199,6 +271,7 @@ class ChainPermutation:
|
|
|
199
271
|
struct2: Structure,
|
|
200
272
|
coords1: np.ndarray,
|
|
201
273
|
coords2: np.ndarray,
|
|
274
|
+
banned_chain_pairs: set[tuple[str, str]],
|
|
202
275
|
) -> dict[str, str]:
|
|
203
276
|
"""
|
|
204
277
|
Chain mapping between two structures within the same entity using
|
|
@@ -212,12 +285,13 @@ class ChainPermutation:
|
|
|
212
285
|
- Selecting the pair with minimal centroid distance
|
|
213
286
|
|
|
214
287
|
Args:
|
|
215
|
-
chain_ids1 (list[str]): Chain IDs from first structure
|
|
216
|
-
chain_ids2 (list[str]): Chain IDs from second structure
|
|
288
|
+
chain_ids1 (list[str]): Chain IDs from first structure
|
|
289
|
+
chain_ids2 (list[str]): Chain IDs from second structure
|
|
217
290
|
struct1 (Structure): First structure containing chain metadata
|
|
218
291
|
struct2 (Structure): Second structure containing chain metadata
|
|
219
292
|
coords1 (np.ndarray): Atom coordinates for struct1 (shape: [N, 3])
|
|
220
|
-
coords2 (np.ndarray): Atom coordinates for struct2 (shape: [
|
|
293
|
+
coords2 (np.ndarray): Atom coordinates for struct2 (shape: [N, 3])
|
|
294
|
+
banned_chain_pairs (set[tuple[str, str]]): Pairs of chain IDs to be banned.
|
|
221
295
|
|
|
222
296
|
Returns:
|
|
223
297
|
dict[str, str]: Mapping of chain IDs from struct1 to struct2.
|
|
@@ -227,6 +301,8 @@ class ChainPermutation:
|
|
|
227
301
|
chain_ids1, chain_ids2 = chain_ids2, chain_ids1
|
|
228
302
|
struct1, struct2 = struct2, struct1
|
|
229
303
|
coords1, coords2 = coords2, coords1
|
|
304
|
+
banned_chain_pairs = {(cid2, cid1) for cid1, cid2 in banned_chain_pairs}
|
|
305
|
+
|
|
230
306
|
swapped = True
|
|
231
307
|
else:
|
|
232
308
|
swapped = False
|
|
@@ -241,6 +317,10 @@ class ChainPermutation:
|
|
|
241
317
|
atoms1 = struct1.uni_atom_id[mask1]
|
|
242
318
|
|
|
243
319
|
for cid2 in chain_ids2:
|
|
320
|
+
if (cid1, cid2) in banned_chain_pairs:
|
|
321
|
+
# "inf" distance if the pair is banned
|
|
322
|
+
continue
|
|
323
|
+
|
|
244
324
|
mask2 = struct2.uni_chain_id == cid2
|
|
245
325
|
atoms2 = struct2.uni_atom_id[mask2]
|
|
246
326
|
|
|
@@ -258,6 +338,9 @@ class ChainPermutation:
|
|
|
258
338
|
matched_chains = {}
|
|
259
339
|
row_indices, col_indices = ChainPermutation._find_min_indices(dist_mat)
|
|
260
340
|
for row, col in zip(row_indices, col_indices):
|
|
341
|
+
if np.isinf(dist_mat[row, col]):
|
|
342
|
+
# "inf" distance if the pair is banned
|
|
343
|
+
continue
|
|
261
344
|
matched_chains[chain_ids1[row]] = chain_ids2[col]
|
|
262
345
|
|
|
263
346
|
return (
|
|
@@ -309,6 +392,7 @@ class ChainPermutation:
|
|
|
309
392
|
struct2=self.model_struct,
|
|
310
393
|
coords1=aligned_ref_coord,
|
|
311
394
|
coords2=self.model_struct.atom_array.coord,
|
|
395
|
+
banned_chain_pairs=self.ref_and_model_mapping_ban_set,
|
|
312
396
|
)
|
|
313
397
|
matched_chains.update(matched_chains_in_curr_entity)
|
|
314
398
|
return matched_chains
|
|
@@ -443,6 +527,12 @@ class ChainPermutation:
|
|
|
443
527
|
anchors = {}
|
|
444
528
|
|
|
445
529
|
for ref_anchor_chain_id in ref_anchor_candidates:
|
|
530
|
+
if (
|
|
531
|
+
ref_anchor_chain_id,
|
|
532
|
+
model_anchor_chain_id,
|
|
533
|
+
) in self.ref_and_model_mapping_ban_set:
|
|
534
|
+
continue
|
|
535
|
+
|
|
446
536
|
# Find atoms in ref chain to match atoms in model chain
|
|
447
537
|
ref_chain_mask = self.ref_struct.uni_chain_id == ref_anchor_chain_id
|
|
448
538
|
ref_anchor_coord = self.ref_struct.atom_array.coord[ref_chain_mask]
|
|
@@ -555,9 +645,6 @@ class ChainPermutation:
|
|
|
555
645
|
- Anchors used for alignment.
|
|
556
646
|
"""
|
|
557
647
|
model_anchor_chain_ids = self.find_model_anchor_chains()
|
|
558
|
-
if not self.enumerate_all_anchors:
|
|
559
|
-
# Only use the first anchor chain
|
|
560
|
-
model_anchor_chain_ids = [model_anchor_chain_ids[0]]
|
|
561
648
|
|
|
562
649
|
ref_to_model_optimal_mapping = None
|
|
563
650
|
best_rmsd = float("inf")
|
|
@@ -579,8 +666,17 @@ class ChainPermutation:
|
|
|
579
666
|
) = self.find_ref_to_model_optimal_chain_mapping(
|
|
580
667
|
model_anchor_chain_id, ref_anchor_candidates
|
|
581
668
|
)
|
|
669
|
+
|
|
670
|
+
if mapping_i is None:
|
|
671
|
+
continue
|
|
672
|
+
|
|
582
673
|
if best_rmsd_i < best_rmsd:
|
|
583
674
|
best_rmsd = best_rmsd_i
|
|
584
675
|
ref_to_model_optimal_mapping = mapping_i
|
|
585
676
|
best_anchors = anchors
|
|
677
|
+
|
|
678
|
+
if not self.enumerate_all_anchors:
|
|
679
|
+
# Only use the first valid anchor chain
|
|
680
|
+
break
|
|
681
|
+
|
|
586
682
|
return ref_to_model_optimal_mapping, best_anchors
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pxmeter
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: PXMeter is a comprehensive toolkit for evaluating the quality of structures generated by biomolecular structure prediction models.
|
|
5
5
|
Author: Bytedance Inc.
|
|
6
6
|
Author-email: ai4s-bio@bytedance.com
|
|
@@ -20,7 +20,7 @@ with open("requirements.txt") as f:
|
|
|
20
20
|
setup(
|
|
21
21
|
name="pxmeter",
|
|
22
22
|
python_requires=">=3.11",
|
|
23
|
-
version="0.1.
|
|
23
|
+
version="0.1.4",
|
|
24
24
|
description="PXMeter is a comprehensive toolkit for evaluating the quality of \
|
|
25
25
|
structures generated by biomolecular structure prediction models.",
|
|
26
26
|
author="Bytedance Inc.",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|