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.
Files changed (37) hide show
  1. {pxmeter-0.1.2/pxmeter.egg-info → pxmeter-0.1.4}/PKG-INFO +1 -1
  2. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/calc_metric.py +11 -0
  3. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/run_config.py +4 -0
  4. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/struct.py +32 -0
  5. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/mapping.py +20 -7
  6. pxmeter-0.1.4/pxmeter/metrics/clashes.py +88 -0
  7. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/chain.py +105 -9
  8. {pxmeter-0.1.2 → pxmeter-0.1.4/pxmeter.egg-info}/PKG-INFO +1 -1
  9. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/SOURCES.txt +1 -0
  10. {pxmeter-0.1.2 → pxmeter-0.1.4}/setup.py +1 -1
  11. {pxmeter-0.1.2 → pxmeter-0.1.4}/LICENSE +0 -0
  12. {pxmeter-0.1.2 → pxmeter-0.1.4}/MANIFEST.in +0 -0
  13. {pxmeter-0.1.2 → pxmeter-0.1.4}/README.md +0 -0
  14. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/__init__.py +0 -0
  15. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/cli.py +0 -0
  16. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/__init__.py +0 -0
  17. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/configs/data_config.py +0 -0
  18. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/constants.py +0 -0
  19. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/__init__.py +0 -0
  20. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/ccd.py +0 -0
  21. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/parser.py +0 -0
  22. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/utils.py +0 -0
  23. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/data/writer.py +0 -0
  24. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/eval.py +0 -0
  25. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/__init__.py +0 -0
  26. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/lddt_metrics.py +0 -0
  27. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/rmsd.py +0 -0
  28. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/metrics/rmsd_metrics.py +0 -0
  29. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/__init__.py +0 -0
  30. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/permutation/atom.py +0 -0
  31. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter/utils.py +0 -0
  32. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/dependency_links.txt +0 -0
  33. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/entry_points.txt +0 -0
  34. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/requires.txt +0 -0
  35. {pxmeter-0.1.2 → pxmeter-0.1.4}/pxmeter.egg-info/top_level.txt +0 -0
  36. {pxmeter-0.1.2 → pxmeter-0.1.4}/requirements.txt +0 -0
  37. {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.2
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(np.argmin(dist_matrix), dist_matrix.shape)
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
- dist_matrix[min_pos[0], :] = np.inf
191
- dist_matrix[:, min_pos[1]] = np.inf
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 (may become struct2 after swap)
216
- chain_ids2 (list[str]): Chain IDs from second structure (may become struct1 after swap)
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: [M, 3])
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.2
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
@@ -26,6 +26,7 @@ pxmeter/data/struct.py
26
26
  pxmeter/data/utils.py
27
27
  pxmeter/data/writer.py
28
28
  pxmeter/metrics/__init__.py
29
+ pxmeter/metrics/clashes.py
29
30
  pxmeter/metrics/lddt_metrics.py
30
31
  pxmeter/metrics/rmsd.py
31
32
  pxmeter/metrics/rmsd_metrics.py
@@ -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.2",
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