biotite 1.1.0__cp313-cp313-win_amd64.whl → 1.3.0__cp313-cp313-win_amd64.whl

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.

Potentially problematic release.


This version of biotite might be problematic. Click here for more details.

Files changed (160) hide show
  1. biotite/application/application.py +3 -3
  2. biotite/application/autodock/app.py +1 -1
  3. biotite/application/blast/webapp.py +1 -1
  4. biotite/application/clustalo/app.py +1 -1
  5. biotite/application/localapp.py +2 -2
  6. biotite/application/msaapp.py +10 -10
  7. biotite/application/muscle/app3.py +3 -3
  8. biotite/application/muscle/app5.py +3 -3
  9. biotite/application/sra/app.py +0 -5
  10. biotite/application/util.py +21 -1
  11. biotite/application/viennarna/rnaalifold.py +8 -8
  12. biotite/application/viennarna/rnaplot.py +10 -8
  13. biotite/application/viennarna/util.py +1 -1
  14. biotite/application/webapp.py +1 -1
  15. biotite/database/afdb/__init__.py +12 -0
  16. biotite/database/afdb/download.py +191 -0
  17. biotite/database/entrez/dbnames.py +10 -0
  18. biotite/database/entrez/download.py +9 -10
  19. biotite/database/entrez/key.py +1 -1
  20. biotite/database/entrez/query.py +5 -4
  21. biotite/database/pubchem/download.py +6 -6
  22. biotite/database/pubchem/error.py +10 -0
  23. biotite/database/pubchem/query.py +12 -23
  24. biotite/database/rcsb/download.py +3 -2
  25. biotite/database/rcsb/query.py +2 -3
  26. biotite/database/uniprot/check.py +2 -2
  27. biotite/database/uniprot/download.py +2 -5
  28. biotite/database/uniprot/query.py +3 -4
  29. biotite/file.py +14 -2
  30. biotite/interface/__init__.py +19 -0
  31. biotite/interface/openmm/__init__.py +20 -0
  32. biotite/interface/openmm/state.py +93 -0
  33. biotite/interface/openmm/system.py +227 -0
  34. biotite/interface/pymol/__init__.py +201 -0
  35. biotite/interface/pymol/cgo.py +346 -0
  36. biotite/interface/pymol/convert.py +185 -0
  37. biotite/interface/pymol/display.py +267 -0
  38. biotite/interface/pymol/object.py +1226 -0
  39. biotite/interface/pymol/shapes.py +178 -0
  40. biotite/interface/pymol/startup.py +169 -0
  41. biotite/interface/rdkit/__init__.py +19 -0
  42. biotite/interface/rdkit/mol.py +490 -0
  43. biotite/interface/version.py +94 -0
  44. biotite/interface/warning.py +19 -0
  45. biotite/sequence/align/__init__.py +0 -4
  46. biotite/sequence/align/alignment.py +33 -11
  47. biotite/sequence/align/banded.cp313-win_amd64.pyd +0 -0
  48. biotite/sequence/align/banded.pyx +22 -22
  49. biotite/sequence/align/cigar.py +2 -2
  50. biotite/sequence/align/kmeralphabet.cp313-win_amd64.pyd +0 -0
  51. biotite/sequence/align/kmeralphabet.pyx +2 -2
  52. biotite/sequence/align/kmersimilarity.cp313-win_amd64.pyd +0 -0
  53. biotite/sequence/align/kmertable.cp313-win_amd64.pyd +0 -0
  54. biotite/sequence/align/kmertable.pyx +6 -6
  55. biotite/sequence/align/localgapped.cp313-win_amd64.pyd +0 -0
  56. biotite/sequence/align/localgapped.pyx +47 -47
  57. biotite/sequence/align/localungapped.cp313-win_amd64.pyd +0 -0
  58. biotite/sequence/align/localungapped.pyx +10 -10
  59. biotite/sequence/align/matrix.py +12 -3
  60. biotite/sequence/align/multiple.cp313-win_amd64.pyd +0 -0
  61. biotite/sequence/align/multiple.pyx +1 -2
  62. biotite/sequence/align/pairwise.cp313-win_amd64.pyd +0 -0
  63. biotite/sequence/align/pairwise.pyx +37 -39
  64. biotite/sequence/align/permutation.cp313-win_amd64.pyd +0 -0
  65. biotite/sequence/align/selector.cp313-win_amd64.pyd +0 -0
  66. biotite/sequence/align/selector.pyx +2 -2
  67. biotite/sequence/align/statistics.py +1 -1
  68. biotite/sequence/align/tracetable.cp313-win_amd64.pyd +0 -0
  69. biotite/sequence/alphabet.py +2 -2
  70. biotite/sequence/annotation.py +19 -13
  71. biotite/sequence/codec.cp313-win_amd64.pyd +0 -0
  72. biotite/sequence/codon.py +1 -2
  73. biotite/sequence/graphics/alignment.py +25 -39
  74. biotite/sequence/graphics/dendrogram.py +4 -2
  75. biotite/sequence/graphics/features.py +2 -2
  76. biotite/sequence/graphics/logo.py +10 -12
  77. biotite/sequence/io/fasta/convert.py +1 -2
  78. biotite/sequence/io/fasta/file.py +1 -1
  79. biotite/sequence/io/fastq/file.py +3 -3
  80. biotite/sequence/io/genbank/file.py +3 -3
  81. biotite/sequence/io/genbank/sequence.py +2 -0
  82. biotite/sequence/io/gff/convert.py +1 -1
  83. biotite/sequence/io/gff/file.py +1 -2
  84. biotite/sequence/phylo/nj.cp313-win_amd64.pyd +0 -0
  85. biotite/sequence/phylo/tree.cp313-win_amd64.pyd +0 -0
  86. biotite/sequence/phylo/upgma.cp313-win_amd64.pyd +0 -0
  87. biotite/sequence/profile.py +19 -25
  88. biotite/sequence/search.py +0 -1
  89. biotite/sequence/seqtypes.py +12 -5
  90. biotite/sequence/sequence.py +1 -2
  91. biotite/structure/__init__.py +2 -0
  92. biotite/structure/alphabet/i3d.py +1 -2
  93. biotite/structure/alphabet/pb.py +1 -2
  94. biotite/structure/alphabet/unkerasify.py +8 -2
  95. biotite/structure/atoms.py +35 -27
  96. biotite/structure/basepairs.py +39 -40
  97. biotite/structure/bonds.cp313-win_amd64.pyd +0 -0
  98. biotite/structure/bonds.pyx +8 -5
  99. biotite/structure/box.py +159 -23
  100. biotite/structure/celllist.cp313-win_amd64.pyd +0 -0
  101. biotite/structure/celllist.pyx +83 -68
  102. biotite/structure/chains.py +17 -55
  103. biotite/structure/charges.cp313-win_amd64.pyd +0 -0
  104. biotite/structure/compare.py +420 -13
  105. biotite/structure/density.py +1 -1
  106. biotite/structure/dotbracket.py +31 -32
  107. biotite/structure/filter.py +8 -8
  108. biotite/structure/geometry.py +15 -15
  109. biotite/structure/graphics/rna.py +19 -16
  110. biotite/structure/hbond.py +18 -21
  111. biotite/structure/info/atoms.py +11 -2
  112. biotite/structure/info/ccd.py +0 -2
  113. biotite/structure/info/components.bcif +0 -0
  114. biotite/structure/info/groups.py +0 -3
  115. biotite/structure/info/misc.py +0 -1
  116. biotite/structure/info/radii.py +92 -22
  117. biotite/structure/info/standardize.py +1 -2
  118. biotite/structure/integrity.py +4 -6
  119. biotite/structure/io/general.py +2 -2
  120. biotite/structure/io/gro/file.py +8 -9
  121. biotite/structure/io/mol/convert.py +1 -1
  122. biotite/structure/io/mol/ctab.py +33 -28
  123. biotite/structure/io/mol/mol.py +1 -1
  124. biotite/structure/io/mol/sdf.py +39 -13
  125. biotite/structure/io/pdb/convert.py +86 -5
  126. biotite/structure/io/pdb/file.py +90 -24
  127. biotite/structure/io/pdb/hybrid36.cp313-win_amd64.pyd +0 -0
  128. biotite/structure/io/pdbqt/file.py +4 -4
  129. biotite/structure/io/pdbx/bcif.py +22 -7
  130. biotite/structure/io/pdbx/cif.py +20 -7
  131. biotite/structure/io/pdbx/component.py +6 -0
  132. biotite/structure/io/pdbx/compress.py +71 -34
  133. biotite/structure/io/pdbx/convert.py +429 -77
  134. biotite/structure/io/pdbx/encoding.cp313-win_amd64.pyd +0 -0
  135. biotite/structure/io/pdbx/encoding.pyx +39 -23
  136. biotite/structure/io/trajfile.py +9 -6
  137. biotite/structure/io/util.py +38 -0
  138. biotite/structure/mechanics.py +0 -1
  139. biotite/structure/molecules.py +0 -15
  140. biotite/structure/pseudoknots.py +13 -19
  141. biotite/structure/repair.py +2 -4
  142. biotite/structure/residues.py +20 -48
  143. biotite/structure/rings.py +335 -0
  144. biotite/structure/sasa.cp313-win_amd64.pyd +0 -0
  145. biotite/structure/sasa.pyx +30 -30
  146. biotite/structure/segments.py +123 -9
  147. biotite/structure/sequence.py +0 -1
  148. biotite/structure/spacegroups.json +1567 -0
  149. biotite/structure/spacegroups.license +26 -0
  150. biotite/structure/sse.py +0 -2
  151. biotite/structure/superimpose.py +75 -253
  152. biotite/structure/tm.py +581 -0
  153. biotite/structure/transform.py +232 -26
  154. biotite/structure/util.py +3 -3
  155. biotite/version.py +9 -4
  156. biotite/visualize.py +111 -1
  157. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/METADATA +8 -36
  158. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/RECORD +160 -138
  159. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/WHEEL +1 -1
  160. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/licenses/LICENSE.rst +0 -0
@@ -0,0 +1,26 @@
1
+ The transformations in 'spacegroups.json' are adapted from
2
+ 'molstar/src/mol-math/geometry/spacegroup/tables.ts' from the Mol* project
3
+ (https://github.com/molstar/molstar) governed by the following license:
4
+
5
+
6
+ The MIT License
7
+
8
+ Copyright (c) 2017 - now, Mol* contributors
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
biotite/structure/sse.py CHANGED
@@ -48,7 +48,6 @@ def annotate_sse(atom_array):
48
48
  Non-peptide residues are also allowed and obtain a ``''``
49
49
  SSE.
50
50
 
51
-
52
51
  Returns
53
52
  -------
54
53
  sse : ndarray
@@ -81,7 +80,6 @@ def annotate_sse(atom_array):
81
80
  >>> print(sse)
82
81
  ['c' 'a' 'a' 'a' 'a' 'a' 'a' 'a' 'a' 'c' 'c' 'c' 'c' 'c' 'c' 'c' 'c' 'c'
83
82
  'c' 'c']
84
-
85
83
  """
86
84
  residue_starts = get_residue_starts(atom_array)
87
85
  # Sort CA coord into the coord array at the respective residue index
@@ -12,200 +12,21 @@ __all__ = [
12
12
  "superimpose",
13
13
  "superimpose_homologs",
14
14
  "superimpose_without_outliers",
15
- "AffineTransformation",
16
15
  ]
17
16
 
18
17
 
19
18
  import numpy as np
20
- from biotite.sequence.align import SubstitutionMatrix, align_optimal, get_codes
19
+ from biotite.sequence.align.alignment import get_codes, remove_gaps
20
+ from biotite.sequence.align.matrix import SubstitutionMatrix
21
+ from biotite.sequence.align.pairwise import align_optimal
21
22
  from biotite.sequence.alphabet import common_alphabet
22
23
  from biotite.sequence.seqtypes import ProteinSequence
23
24
  from biotite.structure.atoms import coord
25
+ from biotite.structure.chains import chain_iter
24
26
  from biotite.structure.filter import filter_amino_acids, filter_nucleotides
25
27
  from biotite.structure.geometry import centroid, distance
26
28
  from biotite.structure.sequence import to_sequence
27
-
28
-
29
- class AffineTransformation:
30
- """
31
- An affine transformation, consisting of translations and a rotation.
32
-
33
- Parameters
34
- ----------
35
- center_translation : ndarray, shape=(3,) or shape=(m,3), dtype=float
36
- The translation vector for moving the centroid into the
37
- origin.
38
- rotation : ndarray, shape=(3,3) or shape=(m,3,3), dtype=float
39
- The rotation matrix.
40
- target_translation : ndarray, shape=(m,3), dtype=float
41
- The translation vector for moving the structure onto the
42
- fixed one.
43
-
44
- Attributes
45
- ----------
46
- center_translation, rotation, target_translation : ndarray
47
- Same as the parameters.
48
- The dimensions are always expanded to *(m,3)* or *(m,3,3)*,
49
- respectively.
50
- """
51
-
52
- def __init__(self, center_translation, rotation, target_translation):
53
- self.center_translation = _expand_dims(center_translation, 2)
54
- self.rotation = _expand_dims(rotation, 3)
55
- self.target_translation = _expand_dims(target_translation, 2)
56
-
57
- def apply(self, atoms):
58
- """
59
- Apply this transformation on the given structure.
60
-
61
- Parameters
62
- ----------
63
- atoms : AtomArray or AtomArrayStack or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
64
- The structure to apply the transformation on.
65
-
66
- Returns
67
- -------
68
- transformed : AtomArray or AtomArrayStack or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
69
- A copy of the `atoms` structure,
70
- with transformations applied.
71
- Only coordinates are returned, if coordinates were given in
72
- `atoms`.
73
-
74
- Examples
75
- --------
76
-
77
- >>> coord = np.arange(15).reshape(5,3)
78
- >>> print(coord)
79
- [[ 0 1 2]
80
- [ 3 4 5]
81
- [ 6 7 8]
82
- [ 9 10 11]
83
- [12 13 14]]
84
- >>> # Rotates 90 degrees around the z-axis
85
- >>> transform = AffineTransformation(
86
- ... center_translation=np.array([0,0,0]),
87
- ... rotation=np.array([
88
- ... [0, -1, 0],
89
- ... [1, 0, 0],
90
- ... [0, 0, 1]
91
- ... ]),
92
- ... target_translation=np.array([0,0,0])
93
- ... )
94
- >>> print(transform.apply(coord))
95
- [[ -1. 0. 2.]
96
- [ -4. 3. 5.]
97
- [ -7. 6. 8.]
98
- [-10. 9. 11.]
99
- [-13. 12. 14.]]
100
-
101
- """
102
- mobile_coord = coord(atoms)
103
- original_shape = mobile_coord.shape
104
- mobile_coord = _reshape_to_3d(mobile_coord)
105
- if mobile_coord.shape[0] != self.rotation.shape[0]:
106
- raise IndexError(
107
- f"Number of transformations is {self.rotation.shape[0]}, "
108
- f"but number of structure models is {mobile_coord.shape[0]}"
109
- )
110
-
111
- superimposed_coord = mobile_coord.copy()
112
- superimposed_coord += self.center_translation[:, np.newaxis, :]
113
- superimposed_coord = _multi_matmul(self.rotation, superimposed_coord)
114
- superimposed_coord += self.target_translation[:, np.newaxis, :]
115
-
116
- superimposed_coord = superimposed_coord.reshape(original_shape)
117
- if isinstance(atoms, np.ndarray):
118
- return superimposed_coord
119
- else:
120
- superimposed = atoms.copy()
121
- superimposed.coord = superimposed_coord
122
- return superimposed
123
-
124
- def as_matrix(self):
125
- """
126
- Get the translations and rotation as a combined 4x4
127
- transformation matrix.
128
-
129
- Multiplying this matrix with coordinates in the form
130
- *(x, y, z, 1)* will apply the same transformation as
131
- :meth:`apply()` to coordinates in the form *(x, y, z)*.
132
-
133
- Returns
134
- -------
135
- transformation_matrix : ndarray, shape=(m,4,4), dtype=float
136
- The transformation matrix.
137
- *m* is the number of models in the transformation.
138
-
139
- Examples
140
- --------
141
-
142
- >>> coord = np.arange(15).reshape(5,3)
143
- >>> print(coord)
144
- [[ 0 1 2]
145
- [ 3 4 5]
146
- [ 6 7 8]
147
- [ 9 10 11]
148
- [12 13 14]]
149
- >>> # Rotates 90 degrees around the z-axis
150
- >>> transform = AffineTransformation(
151
- ... center_translation=np.array([0,0,0]),
152
- ... rotation=np.array([
153
- ... [0, -1, 0],
154
- ... [1, 0, 0],
155
- ... [0, 0, 1]
156
- ... ]),
157
- ... target_translation=np.array([0,0,0])
158
- ... )
159
- >>> print(transform.apply(coord))
160
- [[ -1. 0. 2.]
161
- [ -4. 3. 5.]
162
- [ -7. 6. 8.]
163
- [-10. 9. 11.]
164
- [-13. 12. 14.]]
165
- >>> # Use a 4x4 matrix for transformation as alternative
166
- >>> coord_4 = np.concatenate([coord, np.ones((len(coord), 1))], axis=-1)
167
- >>> print(coord_4)
168
- [[ 0. 1. 2. 1.]
169
- [ 3. 4. 5. 1.]
170
- [ 6. 7. 8. 1.]
171
- [ 9. 10. 11. 1.]
172
- [12. 13. 14. 1.]]
173
- >>> print((transform.as_matrix()[0] @ coord_4.T).T)
174
- [[ -1. 0. 2. 1.]
175
- [ -4. 3. 5. 1.]
176
- [ -7. 6. 8. 1.]
177
- [-10. 9. 11. 1.]
178
- [-13. 12. 14. 1.]]
179
-
180
- """
181
- n_models = self.rotation.shape[0]
182
- rotation_mat = _3d_identity(n_models, 4)
183
- rotation_mat[:, :3, :3] = self.rotation
184
- center_translation_mat = _3d_identity(n_models, 4)
185
- center_translation_mat[:, :3, 3] = self.center_translation
186
- target_translation_mat = _3d_identity(n_models, 4)
187
- target_translation_mat[:, :3, 3] = self.target_translation
188
- return target_translation_mat @ rotation_mat @ center_translation_mat
189
-
190
-
191
- def _expand_dims(array, n_dims):
192
- """
193
- Expand the dimensions of an `ndarray` to a certain number of
194
- dimensions.
195
- """
196
- while array.ndim < n_dims:
197
- array = array[np.newaxis, ...]
198
- return array
199
-
200
-
201
- def _3d_identity(m, n):
202
- """
203
- Create an array of *m* identity matrices of shape *(n, n)*
204
- """
205
- matrices = np.zeros((m, n, n), dtype=float)
206
- indices = np.arange(n)
207
- matrices[:, indices, indices] = 1
208
- return matrices
29
+ from biotite.structure.transform import AffineTransformation
209
30
 
210
31
 
211
32
  def superimpose(fixed, mobile, atom_mask=None):
@@ -222,7 +43,7 @@ def superimpose(fixed, mobile, atom_mask=None):
222
43
  fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
223
44
  The fixed structure(s).
224
45
  Alternatively coordinates can be given.
225
- mobile: AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
46
+ mobile : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
226
47
  The structure(s) which is/are superimposed on the `fixed`
227
48
  structure.
228
49
  Each atom at index *i* in `mobile` must correspond the
@@ -231,7 +52,7 @@ def superimpose(fixed, mobile, atom_mask=None):
231
52
  :class:`AtomArrayStack` objects, they must have the same
232
53
  number of models.
233
54
  Alternatively coordinates can be given.
234
- atom_mask: ndarray, dtype=bool, optional
55
+ atom_mask : ndarray, dtype=bool, optional
235
56
  If given, only the atoms covered by this boolean mask will be
236
57
  considered for superimposition.
237
58
  This means that the algorithm will minimize the RMSD based
@@ -253,8 +74,8 @@ def superimpose(fixed, mobile, atom_mask=None):
253
74
 
254
75
  See Also
255
76
  --------
256
- superimpose_without_outliers : Superimposition with outlier removal
257
- superimpose_homologs : Superimposition of homologous structures
77
+ superimpose_without_outliers : Superimposition with outlier removal.
78
+ superimpose_homologs : Superimposition of homologous structures.
258
79
 
259
80
  Notes
260
81
  -----
@@ -340,13 +161,12 @@ def superimpose_without_outliers(
340
161
  the remaining atoms (called *anchors*) again until no outlier
341
162
  remains.
342
163
 
343
-
344
164
  Parameters
345
165
  ----------
346
166
  fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
347
167
  The fixed structure(s).
348
168
  Alternatively coordinates can be given.
349
- mobile: AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
169
+ mobile : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
350
170
  The structure(s) which is/are superimposed on the `fixed`
351
171
  structure.
352
172
  Each atom at index *i* in `mobile` must correspond the
@@ -389,8 +209,8 @@ def superimpose_without_outliers(
389
209
 
390
210
  See Also
391
211
  --------
392
- superimpose : Superimposition without outlier removal
393
- superimpose_homologs : Superimposition of homologous structures
212
+ superimpose : Superimposition without outlier removal.
213
+ superimpose_homologs : Superimposition of homologous structures.
394
214
 
395
215
  Notes
396
216
  -----
@@ -464,10 +284,16 @@ def superimpose_without_outliers(
464
284
 
465
285
 
466
286
  def superimpose_homologs(
467
- fixed, mobile, substitution_matrix=None, gap_penalty=-10, min_anchors=3, **kwargs
287
+ fixed,
288
+ mobile,
289
+ substitution_matrix=None,
290
+ gap_penalty=-10,
291
+ min_anchors=3,
292
+ terminal_penalty=False,
293
+ **kwargs,
468
294
  ):
469
295
  r"""
470
- Superimpose one protein or nucleotide chain onto another one,
296
+ Superimpose a protein or nucleotide structure onto another one,
471
297
  considering sequence differences and conformational outliers.
472
298
 
473
299
  The method finds corresponding residues by sequence alignment and
@@ -480,11 +306,11 @@ def superimpose_homologs(
480
306
  ----------
481
307
  fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n)
482
308
  The fixed structure(s).
483
- Must comprise a single chain.
484
309
  mobile : AtomArray, shape(n,) or AtomArrayStack, shape(m,n)
485
- The structure(s) which is/are superimposed on the `fixed`
486
- structure.
487
- Must comprise a single chain.
310
+ The structure(s) which is/are superimposed on the `fixed` structure.
311
+ Must contain the same number of chains as `fixed` with corresponding chains
312
+ being in the same order.
313
+ The specific chain IDs can be different.
488
314
  substitution_matrix : str or SubstitutionMatrix, optional
489
315
  The (name of the) substitution matrix used for sequence
490
316
  alignment.
@@ -504,6 +330,9 @@ def superimpose_homologs(
504
330
  `mobile` in this fallback case, an exception is raised.
505
331
  Furthermore, the outlier removal is stopped, if less than
506
332
  `min_anchors` anchors would be left.
333
+ terminal_penalty : bool, optional
334
+ If set to true, gap penalties are applied to terminal gaps in the sequence
335
+ alignment.
507
336
  **kwargs
508
337
  Additional parameters for
509
338
  :func:`superimpose_without_outliers()`.
@@ -525,8 +354,9 @@ def superimpose_homologs(
525
354
 
526
355
  See Also
527
356
  --------
528
- superimpose : Superimposition without outlier removal
529
- superimpose_without_outliers : Internally used for outlier removal
357
+ superimpose : Superimposition without outlier removal.
358
+ superimpose_without_outliers : Internally used for outlier removal.
359
+ superimpose_structural_homologs : Better suited for low sequence similarity.
530
360
 
531
361
  Notes
532
362
  -----
@@ -540,7 +370,7 @@ def superimpose_homologs(
540
370
  or len(mobile_anchor_indices) < min_anchors
541
371
  ):
542
372
  raise ValueError(
543
- "Structures have too few CA atoms for required number of anchors"
373
+ "Structures have too few backbone atoms for required number of anchors"
544
374
  )
545
375
 
546
376
  anchor_indices = _find_matching_anchors(
@@ -548,18 +378,19 @@ def superimpose_homologs(
548
378
  mobile[..., mobile_anchor_indices],
549
379
  substitution_matrix,
550
380
  gap_penalty,
381
+ terminal_penalty,
551
382
  )
552
383
  if len(anchor_indices) < min_anchors:
553
384
  # Fallback: Match all backbone anchors
554
385
  if len(fixed_anchor_indices) != len(mobile_anchor_indices):
555
386
  raise ValueError(
556
387
  "Tried fallback due to low anchor number, "
557
- "but number of CA atoms does not match"
388
+ "but number of backbone atoms does not match"
558
389
  )
559
390
  fixed_anchor_indices = fixed_anchor_indices
560
391
  mobile_anchor_indices = mobile_anchor_indices
561
392
  else:
562
- # The anchor indices point to the CA atoms
393
+ # The anchor indices point to the backbone atoms
563
394
  # -> get the corresponding indices for the whole structure
564
395
  fixed_anchor_indices = fixed_anchor_indices[anchor_indices[:, 0]]
565
396
  mobile_anchor_indices = mobile_anchor_indices[anchor_indices[:, 1]]
@@ -613,16 +444,6 @@ def _get_rotation_matrices(fixed, mobile):
613
444
  return matrices
614
445
 
615
446
 
616
- def _multi_matmul(matrices, vectors):
617
- """
618
- Calculate the matrix multiplication of m matrices
619
- with m x n vectors.
620
- """
621
- return np.transpose(
622
- np.matmul(matrices, np.transpose(vectors, axes=(0, 2, 1))), axes=(0, 2, 1)
623
- )
624
-
625
-
626
447
  def _get_backbone_anchor_indices(atoms):
627
448
  """
628
449
  Select one representative anchor atom for each amino acid and
@@ -639,51 +460,52 @@ def _find_matching_anchors(
639
460
  mobile_anchors_atoms,
640
461
  substitution_matrix,
641
462
  gap_penalty,
463
+ terminal_penalty,
642
464
  ):
643
465
  """
644
466
  Find corresponding residues using pairwise sequence alignment.
645
467
  """
646
- fixed_seq = _to_sequence(fixed_anchor_atoms)
647
- mobile_seq = _to_sequence(mobile_anchors_atoms)
648
- common_alph = common_alphabet([fixed_seq.alphabet, mobile_seq.alphabet])
649
- if common_alph is None:
650
- raise ValueError("Cannot superimpose peptides with nucleic acids")
651
-
652
- if substitution_matrix is None:
653
- if isinstance(fixed_seq, ProteinSequence):
654
- substitution_matrix = SubstitutionMatrix.std_protein_matrix()
655
- else:
656
- substitution_matrix = SubstitutionMatrix.std_nucleotide_matrix()
657
- elif isinstance(substitution_matrix, str):
658
- substitution_matrix = SubstitutionMatrix(
659
- common_alph, common_alph, substitution_matrix
660
- )
661
- score_matrix = substitution_matrix.score_matrix()
468
+ anchor_list = []
469
+ fixed_seq_offset = 0
470
+ mobile_seq_offset = 0
471
+ for fixed_chain, mobile_chain in zip(
472
+ chain_iter(fixed_anchor_atoms), chain_iter(mobile_anchors_atoms), strict=True
473
+ ):
474
+ # The input is a single chain -> expect a single sequence
475
+ fixed_seq = to_sequence(fixed_chain, allow_hetero=True)[0][0]
476
+ mobile_seq = to_sequence(mobile_chain, allow_hetero=True)[0][0]
477
+
478
+ common_alph = common_alphabet([fixed_seq.alphabet, mobile_seq.alphabet])
479
+ if common_alph is None:
480
+ raise ValueError("Cannot superimpose peptides with nucleic acids")
481
+ if substitution_matrix is None:
482
+ if isinstance(fixed_seq, ProteinSequence):
483
+ substitution_matrix = SubstitutionMatrix.std_protein_matrix()
484
+ else:
485
+ substitution_matrix = SubstitutionMatrix.std_nucleotide_matrix()
486
+ elif isinstance(substitution_matrix, str):
487
+ substitution_matrix = SubstitutionMatrix(
488
+ common_alph, common_alph, substitution_matrix
489
+ )
662
490
 
663
- alignment = align_optimal(
664
- fixed_seq,
665
- mobile_seq,
666
- substitution_matrix,
667
- gap_penalty,
668
- terminal_penalty=False,
669
- max_number=1,
670
- )[0]
671
- alignment_codes = get_codes(alignment)
672
- anchor_mask = (
673
- # Anchors must be similar amino acids
674
- (score_matrix[alignment_codes[0], alignment_codes[1]] > 0)
491
+ alignment = align_optimal(
492
+ fixed_seq,
493
+ mobile_seq,
494
+ substitution_matrix,
495
+ gap_penalty,
496
+ terminal_penalty=terminal_penalty,
497
+ max_number=1,
498
+ )[0]
675
499
  # Cannot anchor gaps
676
- & (alignment_codes[0] != -1)
677
- & (alignment_codes[1] != -1)
678
- )
679
- anchors = alignment.trace[anchor_mask]
680
- return anchors
500
+ alignment = remove_gaps(alignment)
501
+ ali_codes = get_codes(alignment)
502
+ score_matrix = substitution_matrix.score_matrix()
503
+ # Anchors must be similar amino acids
504
+ anchors = alignment.trace[score_matrix[ali_codes[0], ali_codes[1]] > 0]
681
505
 
506
+ anchors += fixed_seq_offset, mobile_seq_offset
507
+ fixed_seq_offset += len(fixed_seq)
508
+ mobile_seq_offset += len(mobile_seq)
509
+ anchor_list.append(anchors)
682
510
 
683
- def _to_sequence(atoms):
684
- sequences, _ = to_sequence(atoms, allow_hetero=True)
685
- if len(sequences) == 0:
686
- raise ValueError("Structure does not contain any amino acids or nucleotides")
687
- if len(sequences) > 1:
688
- raise ValueError("Structure contains multiple chains, but only one is allowed")
689
- return sequences[0]
511
+ return np.concatenate(anchor_list, axis=0)