biotite 1.1.0__cp313-cp313-macosx_10_13_x86_64.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 (332) hide show
  1. biotite/__init__.py +18 -0
  2. biotite/application/__init__.py +69 -0
  3. biotite/application/application.py +276 -0
  4. biotite/application/autodock/__init__.py +12 -0
  5. biotite/application/autodock/app.py +500 -0
  6. biotite/application/blast/__init__.py +14 -0
  7. biotite/application/blast/alignment.py +92 -0
  8. biotite/application/blast/webapp.py +428 -0
  9. biotite/application/clustalo/__init__.py +12 -0
  10. biotite/application/clustalo/app.py +223 -0
  11. biotite/application/dssp/__init__.py +12 -0
  12. biotite/application/dssp/app.py +159 -0
  13. biotite/application/localapp.py +342 -0
  14. biotite/application/mafft/__init__.py +12 -0
  15. biotite/application/mafft/app.py +116 -0
  16. biotite/application/msaapp.py +363 -0
  17. biotite/application/muscle/__init__.py +13 -0
  18. biotite/application/muscle/app3.py +227 -0
  19. biotite/application/muscle/app5.py +163 -0
  20. biotite/application/sra/__init__.py +18 -0
  21. biotite/application/sra/app.py +452 -0
  22. biotite/application/tantan/__init__.py +12 -0
  23. biotite/application/tantan/app.py +199 -0
  24. biotite/application/util.py +57 -0
  25. biotite/application/viennarna/__init__.py +18 -0
  26. biotite/application/viennarna/rnaalifold.py +310 -0
  27. biotite/application/viennarna/rnafold.py +254 -0
  28. biotite/application/viennarna/rnaplot.py +206 -0
  29. biotite/application/viennarna/util.py +77 -0
  30. biotite/application/webapp.py +76 -0
  31. biotite/copyable.py +71 -0
  32. biotite/database/__init__.py +23 -0
  33. biotite/database/entrez/__init__.py +15 -0
  34. biotite/database/entrez/check.py +60 -0
  35. biotite/database/entrez/dbnames.py +91 -0
  36. biotite/database/entrez/download.py +229 -0
  37. biotite/database/entrez/key.py +44 -0
  38. biotite/database/entrez/query.py +262 -0
  39. biotite/database/error.py +16 -0
  40. biotite/database/pubchem/__init__.py +21 -0
  41. biotite/database/pubchem/download.py +258 -0
  42. biotite/database/pubchem/error.py +20 -0
  43. biotite/database/pubchem/query.py +830 -0
  44. biotite/database/pubchem/throttle.py +98 -0
  45. biotite/database/rcsb/__init__.py +13 -0
  46. biotite/database/rcsb/download.py +159 -0
  47. biotite/database/rcsb/query.py +964 -0
  48. biotite/database/uniprot/__init__.py +13 -0
  49. biotite/database/uniprot/check.py +40 -0
  50. biotite/database/uniprot/download.py +129 -0
  51. biotite/database/uniprot/query.py +293 -0
  52. biotite/file.py +232 -0
  53. biotite/sequence/__init__.py +84 -0
  54. biotite/sequence/align/__init__.py +203 -0
  55. biotite/sequence/align/alignment.py +680 -0
  56. biotite/sequence/align/banded.cpython-313-darwin.so +0 -0
  57. biotite/sequence/align/banded.pyx +652 -0
  58. biotite/sequence/align/buckets.py +71 -0
  59. biotite/sequence/align/cigar.py +425 -0
  60. biotite/sequence/align/kmeralphabet.cpython-313-darwin.so +0 -0
  61. biotite/sequence/align/kmeralphabet.pyx +595 -0
  62. biotite/sequence/align/kmersimilarity.cpython-313-darwin.so +0 -0
  63. biotite/sequence/align/kmersimilarity.pyx +233 -0
  64. biotite/sequence/align/kmertable.cpython-313-darwin.so +0 -0
  65. biotite/sequence/align/kmertable.pyx +3411 -0
  66. biotite/sequence/align/localgapped.cpython-313-darwin.so +0 -0
  67. biotite/sequence/align/localgapped.pyx +892 -0
  68. biotite/sequence/align/localungapped.cpython-313-darwin.so +0 -0
  69. biotite/sequence/align/localungapped.pyx +279 -0
  70. biotite/sequence/align/matrix.py +622 -0
  71. biotite/sequence/align/matrix_data/3Di.mat +24 -0
  72. biotite/sequence/align/matrix_data/BLOSUM100.mat +31 -0
  73. biotite/sequence/align/matrix_data/BLOSUM30.mat +31 -0
  74. biotite/sequence/align/matrix_data/BLOSUM35.mat +31 -0
  75. biotite/sequence/align/matrix_data/BLOSUM40.mat +31 -0
  76. biotite/sequence/align/matrix_data/BLOSUM45.mat +31 -0
  77. biotite/sequence/align/matrix_data/BLOSUM50.mat +31 -0
  78. biotite/sequence/align/matrix_data/BLOSUM50_13p.mat +25 -0
  79. biotite/sequence/align/matrix_data/BLOSUM50_14.3.mat +25 -0
  80. biotite/sequence/align/matrix_data/BLOSUM50_5.0.mat +25 -0
  81. biotite/sequence/align/matrix_data/BLOSUM55.mat +31 -0
  82. biotite/sequence/align/matrix_data/BLOSUM60.mat +31 -0
  83. biotite/sequence/align/matrix_data/BLOSUM62.mat +31 -0
  84. biotite/sequence/align/matrix_data/BLOSUM62_13p.mat +25 -0
  85. biotite/sequence/align/matrix_data/BLOSUM62_14.3.mat +25 -0
  86. biotite/sequence/align/matrix_data/BLOSUM62_5.0.mat +25 -0
  87. biotite/sequence/align/matrix_data/BLOSUM65.mat +31 -0
  88. biotite/sequence/align/matrix_data/BLOSUM70.mat +31 -0
  89. biotite/sequence/align/matrix_data/BLOSUM75.mat +31 -0
  90. biotite/sequence/align/matrix_data/BLOSUM80.mat +31 -0
  91. biotite/sequence/align/matrix_data/BLOSUM85.mat +31 -0
  92. biotite/sequence/align/matrix_data/BLOSUM90.mat +31 -0
  93. biotite/sequence/align/matrix_data/BLOSUMN.mat +31 -0
  94. biotite/sequence/align/matrix_data/CorBLOSUM49_5.0.mat +25 -0
  95. biotite/sequence/align/matrix_data/CorBLOSUM57_13p.mat +25 -0
  96. biotite/sequence/align/matrix_data/CorBLOSUM57_14.3.mat +25 -0
  97. biotite/sequence/align/matrix_data/CorBLOSUM61_5.0.mat +25 -0
  98. biotite/sequence/align/matrix_data/CorBLOSUM66_13p.mat +25 -0
  99. biotite/sequence/align/matrix_data/CorBLOSUM67_14.3.mat +25 -0
  100. biotite/sequence/align/matrix_data/DAYHOFF.mat +32 -0
  101. biotite/sequence/align/matrix_data/GONNET.mat +26 -0
  102. biotite/sequence/align/matrix_data/IDENTITY.mat +25 -0
  103. biotite/sequence/align/matrix_data/MATCH.mat +25 -0
  104. biotite/sequence/align/matrix_data/NUC.mat +25 -0
  105. biotite/sequence/align/matrix_data/PAM10.mat +34 -0
  106. biotite/sequence/align/matrix_data/PAM100.mat +34 -0
  107. biotite/sequence/align/matrix_data/PAM110.mat +34 -0
  108. biotite/sequence/align/matrix_data/PAM120.mat +34 -0
  109. biotite/sequence/align/matrix_data/PAM130.mat +34 -0
  110. biotite/sequence/align/matrix_data/PAM140.mat +34 -0
  111. biotite/sequence/align/matrix_data/PAM150.mat +34 -0
  112. biotite/sequence/align/matrix_data/PAM160.mat +34 -0
  113. biotite/sequence/align/matrix_data/PAM170.mat +34 -0
  114. biotite/sequence/align/matrix_data/PAM180.mat +34 -0
  115. biotite/sequence/align/matrix_data/PAM190.mat +34 -0
  116. biotite/sequence/align/matrix_data/PAM20.mat +34 -0
  117. biotite/sequence/align/matrix_data/PAM200.mat +34 -0
  118. biotite/sequence/align/matrix_data/PAM210.mat +34 -0
  119. biotite/sequence/align/matrix_data/PAM220.mat +34 -0
  120. biotite/sequence/align/matrix_data/PAM230.mat +34 -0
  121. biotite/sequence/align/matrix_data/PAM240.mat +34 -0
  122. biotite/sequence/align/matrix_data/PAM250.mat +34 -0
  123. biotite/sequence/align/matrix_data/PAM260.mat +34 -0
  124. biotite/sequence/align/matrix_data/PAM270.mat +34 -0
  125. biotite/sequence/align/matrix_data/PAM280.mat +34 -0
  126. biotite/sequence/align/matrix_data/PAM290.mat +34 -0
  127. biotite/sequence/align/matrix_data/PAM30.mat +34 -0
  128. biotite/sequence/align/matrix_data/PAM300.mat +34 -0
  129. biotite/sequence/align/matrix_data/PAM310.mat +34 -0
  130. biotite/sequence/align/matrix_data/PAM320.mat +34 -0
  131. biotite/sequence/align/matrix_data/PAM330.mat +34 -0
  132. biotite/sequence/align/matrix_data/PAM340.mat +34 -0
  133. biotite/sequence/align/matrix_data/PAM350.mat +34 -0
  134. biotite/sequence/align/matrix_data/PAM360.mat +34 -0
  135. biotite/sequence/align/matrix_data/PAM370.mat +34 -0
  136. biotite/sequence/align/matrix_data/PAM380.mat +34 -0
  137. biotite/sequence/align/matrix_data/PAM390.mat +34 -0
  138. biotite/sequence/align/matrix_data/PAM40.mat +34 -0
  139. biotite/sequence/align/matrix_data/PAM400.mat +34 -0
  140. biotite/sequence/align/matrix_data/PAM410.mat +34 -0
  141. biotite/sequence/align/matrix_data/PAM420.mat +34 -0
  142. biotite/sequence/align/matrix_data/PAM430.mat +34 -0
  143. biotite/sequence/align/matrix_data/PAM440.mat +34 -0
  144. biotite/sequence/align/matrix_data/PAM450.mat +34 -0
  145. biotite/sequence/align/matrix_data/PAM460.mat +34 -0
  146. biotite/sequence/align/matrix_data/PAM470.mat +34 -0
  147. biotite/sequence/align/matrix_data/PAM480.mat +34 -0
  148. biotite/sequence/align/matrix_data/PAM490.mat +34 -0
  149. biotite/sequence/align/matrix_data/PAM50.mat +34 -0
  150. biotite/sequence/align/matrix_data/PAM500.mat +34 -0
  151. biotite/sequence/align/matrix_data/PAM60.mat +34 -0
  152. biotite/sequence/align/matrix_data/PAM70.mat +34 -0
  153. biotite/sequence/align/matrix_data/PAM80.mat +34 -0
  154. biotite/sequence/align/matrix_data/PAM90.mat +34 -0
  155. biotite/sequence/align/matrix_data/PB.license +21 -0
  156. biotite/sequence/align/matrix_data/PB.mat +18 -0
  157. biotite/sequence/align/matrix_data/RBLOSUM52_5.0.mat +25 -0
  158. biotite/sequence/align/matrix_data/RBLOSUM59_13p.mat +25 -0
  159. biotite/sequence/align/matrix_data/RBLOSUM59_14.3.mat +25 -0
  160. biotite/sequence/align/matrix_data/RBLOSUM64_5.0.mat +25 -0
  161. biotite/sequence/align/matrix_data/RBLOSUM69_13p.mat +25 -0
  162. biotite/sequence/align/matrix_data/RBLOSUM69_14.3.mat +25 -0
  163. biotite/sequence/align/multiple.cpython-313-darwin.so +0 -0
  164. biotite/sequence/align/multiple.pyx +620 -0
  165. biotite/sequence/align/pairwise.cpython-313-darwin.so +0 -0
  166. biotite/sequence/align/pairwise.pyx +587 -0
  167. biotite/sequence/align/permutation.cpython-313-darwin.so +0 -0
  168. biotite/sequence/align/permutation.pyx +313 -0
  169. biotite/sequence/align/primes.txt +821 -0
  170. biotite/sequence/align/selector.cpython-313-darwin.so +0 -0
  171. biotite/sequence/align/selector.pyx +954 -0
  172. biotite/sequence/align/statistics.py +264 -0
  173. biotite/sequence/align/tracetable.cpython-313-darwin.so +0 -0
  174. biotite/sequence/align/tracetable.pxd +64 -0
  175. biotite/sequence/align/tracetable.pyx +370 -0
  176. biotite/sequence/alphabet.py +555 -0
  177. biotite/sequence/annotation.py +830 -0
  178. biotite/sequence/codec.cpython-313-darwin.so +0 -0
  179. biotite/sequence/codec.pyx +155 -0
  180. biotite/sequence/codon.py +477 -0
  181. biotite/sequence/codon_tables.txt +202 -0
  182. biotite/sequence/graphics/__init__.py +33 -0
  183. biotite/sequence/graphics/alignment.py +1115 -0
  184. biotite/sequence/graphics/color_schemes/3di_flower.json +48 -0
  185. biotite/sequence/graphics/color_schemes/autumn.json +51 -0
  186. biotite/sequence/graphics/color_schemes/blossom.json +51 -0
  187. biotite/sequence/graphics/color_schemes/clustalx_dna.json +11 -0
  188. biotite/sequence/graphics/color_schemes/clustalx_protein.json +28 -0
  189. biotite/sequence/graphics/color_schemes/flower.json +51 -0
  190. biotite/sequence/graphics/color_schemes/jalview_buried.json +31 -0
  191. biotite/sequence/graphics/color_schemes/jalview_hydrophobicity.json +31 -0
  192. biotite/sequence/graphics/color_schemes/jalview_prop_helix.json +31 -0
  193. biotite/sequence/graphics/color_schemes/jalview_prop_strand.json +31 -0
  194. biotite/sequence/graphics/color_schemes/jalview_prop_turn.json +31 -0
  195. biotite/sequence/graphics/color_schemes/jalview_taylor.json +28 -0
  196. biotite/sequence/graphics/color_schemes/jalview_zappo.json +28 -0
  197. biotite/sequence/graphics/color_schemes/ocean.json +51 -0
  198. biotite/sequence/graphics/color_schemes/pb_flower.json +40 -0
  199. biotite/sequence/graphics/color_schemes/rainbow_dna.json +11 -0
  200. biotite/sequence/graphics/color_schemes/rainbow_protein.json +30 -0
  201. biotite/sequence/graphics/color_schemes/spring.json +51 -0
  202. biotite/sequence/graphics/color_schemes/sunset.json +51 -0
  203. biotite/sequence/graphics/color_schemes/wither.json +51 -0
  204. biotite/sequence/graphics/colorschemes.py +170 -0
  205. biotite/sequence/graphics/dendrogram.py +229 -0
  206. biotite/sequence/graphics/features.py +544 -0
  207. biotite/sequence/graphics/logo.py +104 -0
  208. biotite/sequence/graphics/plasmid.py +712 -0
  209. biotite/sequence/io/__init__.py +12 -0
  210. biotite/sequence/io/fasta/__init__.py +22 -0
  211. biotite/sequence/io/fasta/convert.py +284 -0
  212. biotite/sequence/io/fasta/file.py +265 -0
  213. biotite/sequence/io/fastq/__init__.py +19 -0
  214. biotite/sequence/io/fastq/convert.py +117 -0
  215. biotite/sequence/io/fastq/file.py +507 -0
  216. biotite/sequence/io/genbank/__init__.py +17 -0
  217. biotite/sequence/io/genbank/annotation.py +269 -0
  218. biotite/sequence/io/genbank/file.py +573 -0
  219. biotite/sequence/io/genbank/metadata.py +336 -0
  220. biotite/sequence/io/genbank/sequence.py +171 -0
  221. biotite/sequence/io/general.py +201 -0
  222. biotite/sequence/io/gff/__init__.py +26 -0
  223. biotite/sequence/io/gff/convert.py +128 -0
  224. biotite/sequence/io/gff/file.py +450 -0
  225. biotite/sequence/phylo/__init__.py +36 -0
  226. biotite/sequence/phylo/nj.cpython-313-darwin.so +0 -0
  227. biotite/sequence/phylo/nj.pyx +221 -0
  228. biotite/sequence/phylo/tree.cpython-313-darwin.so +0 -0
  229. biotite/sequence/phylo/tree.pyx +1169 -0
  230. biotite/sequence/phylo/upgma.cpython-313-darwin.so +0 -0
  231. biotite/sequence/phylo/upgma.pyx +164 -0
  232. biotite/sequence/profile.py +567 -0
  233. biotite/sequence/search.py +118 -0
  234. biotite/sequence/seqtypes.py +713 -0
  235. biotite/sequence/sequence.py +374 -0
  236. biotite/setup_ccd.py +197 -0
  237. biotite/structure/__init__.py +133 -0
  238. biotite/structure/alphabet/__init__.py +25 -0
  239. biotite/structure/alphabet/encoder.py +332 -0
  240. biotite/structure/alphabet/encoder_weights_3di.kerasify +0 -0
  241. biotite/structure/alphabet/i3d.py +110 -0
  242. biotite/structure/alphabet/layers.py +86 -0
  243. biotite/structure/alphabet/pb.license +21 -0
  244. biotite/structure/alphabet/pb.py +171 -0
  245. biotite/structure/alphabet/unkerasify.py +122 -0
  246. biotite/structure/atoms.py +1554 -0
  247. biotite/structure/basepairs.py +1404 -0
  248. biotite/structure/bonds.cpython-313-darwin.so +0 -0
  249. biotite/structure/bonds.pyx +1972 -0
  250. biotite/structure/box.py +588 -0
  251. biotite/structure/celllist.cpython-313-darwin.so +0 -0
  252. biotite/structure/celllist.pyx +849 -0
  253. biotite/structure/chains.py +314 -0
  254. biotite/structure/charges.cpython-313-darwin.so +0 -0
  255. biotite/structure/charges.pyx +520 -0
  256. biotite/structure/compare.py +274 -0
  257. biotite/structure/density.py +109 -0
  258. biotite/structure/dotbracket.py +214 -0
  259. biotite/structure/error.py +39 -0
  260. biotite/structure/filter.py +590 -0
  261. biotite/structure/geometry.py +655 -0
  262. biotite/structure/graphics/__init__.py +13 -0
  263. biotite/structure/graphics/atoms.py +243 -0
  264. biotite/structure/graphics/rna.py +295 -0
  265. biotite/structure/hbond.py +428 -0
  266. biotite/structure/info/__init__.py +24 -0
  267. biotite/structure/info/atom_masses.json +121 -0
  268. biotite/structure/info/atoms.py +81 -0
  269. biotite/structure/info/bonds.py +149 -0
  270. biotite/structure/info/ccd.py +202 -0
  271. biotite/structure/info/components.bcif +0 -0
  272. biotite/structure/info/groups.py +131 -0
  273. biotite/structure/info/masses.py +121 -0
  274. biotite/structure/info/misc.py +138 -0
  275. biotite/structure/info/radii.py +197 -0
  276. biotite/structure/info/standardize.py +186 -0
  277. biotite/structure/integrity.py +215 -0
  278. biotite/structure/io/__init__.py +29 -0
  279. biotite/structure/io/dcd/__init__.py +13 -0
  280. biotite/structure/io/dcd/file.py +67 -0
  281. biotite/structure/io/general.py +243 -0
  282. biotite/structure/io/gro/__init__.py +14 -0
  283. biotite/structure/io/gro/file.py +344 -0
  284. biotite/structure/io/mol/__init__.py +20 -0
  285. biotite/structure/io/mol/convert.py +112 -0
  286. biotite/structure/io/mol/ctab.py +415 -0
  287. biotite/structure/io/mol/header.py +120 -0
  288. biotite/structure/io/mol/mol.py +149 -0
  289. biotite/structure/io/mol/sdf.py +914 -0
  290. biotite/structure/io/netcdf/__init__.py +13 -0
  291. biotite/structure/io/netcdf/file.py +64 -0
  292. biotite/structure/io/pdb/__init__.py +20 -0
  293. biotite/structure/io/pdb/convert.py +307 -0
  294. biotite/structure/io/pdb/file.py +1290 -0
  295. biotite/structure/io/pdb/hybrid36.cpython-313-darwin.so +0 -0
  296. biotite/structure/io/pdb/hybrid36.pyx +242 -0
  297. biotite/structure/io/pdbqt/__init__.py +15 -0
  298. biotite/structure/io/pdbqt/convert.py +113 -0
  299. biotite/structure/io/pdbqt/file.py +688 -0
  300. biotite/structure/io/pdbx/__init__.py +23 -0
  301. biotite/structure/io/pdbx/bcif.py +656 -0
  302. biotite/structure/io/pdbx/cif.py +1075 -0
  303. biotite/structure/io/pdbx/component.py +245 -0
  304. biotite/structure/io/pdbx/compress.py +321 -0
  305. biotite/structure/io/pdbx/convert.py +1745 -0
  306. biotite/structure/io/pdbx/encoding.cpython-313-darwin.so +0 -0
  307. biotite/structure/io/pdbx/encoding.pyx +1031 -0
  308. biotite/structure/io/trajfile.py +693 -0
  309. biotite/structure/io/trr/__init__.py +13 -0
  310. biotite/structure/io/trr/file.py +43 -0
  311. biotite/structure/io/xtc/__init__.py +13 -0
  312. biotite/structure/io/xtc/file.py +43 -0
  313. biotite/structure/mechanics.py +73 -0
  314. biotite/structure/molecules.py +352 -0
  315. biotite/structure/pseudoknots.py +628 -0
  316. biotite/structure/rdf.py +245 -0
  317. biotite/structure/repair.py +304 -0
  318. biotite/structure/residues.py +572 -0
  319. biotite/structure/sasa.cpython-313-darwin.so +0 -0
  320. biotite/structure/sasa.pyx +322 -0
  321. biotite/structure/segments.py +178 -0
  322. biotite/structure/sequence.py +111 -0
  323. biotite/structure/sse.py +308 -0
  324. biotite/structure/superimpose.py +689 -0
  325. biotite/structure/transform.py +530 -0
  326. biotite/structure/util.py +168 -0
  327. biotite/version.py +16 -0
  328. biotite/visualize.py +265 -0
  329. biotite-1.1.0.dist-info/METADATA +190 -0
  330. biotite-1.1.0.dist-info/RECORD +332 -0
  331. biotite-1.1.0.dist-info/WHEEL +4 -0
  332. biotite-1.1.0.dist-info/licenses/LICENSE.rst +30 -0
@@ -0,0 +1,689 @@
1
+ # This source code is part of the Biotite package and is distributed
2
+ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further
3
+ # information.
4
+
5
+ """
6
+ This module provides functions for structure superimposition.
7
+ """
8
+
9
+ __name__ = "biotite.structure"
10
+ __author__ = "Patrick Kunzmann, Claude J. Rogers"
11
+ __all__ = [
12
+ "superimpose",
13
+ "superimpose_homologs",
14
+ "superimpose_without_outliers",
15
+ "AffineTransformation",
16
+ ]
17
+
18
+
19
+ import numpy as np
20
+ from biotite.sequence.align import SubstitutionMatrix, align_optimal, get_codes
21
+ from biotite.sequence.alphabet import common_alphabet
22
+ from biotite.sequence.seqtypes import ProteinSequence
23
+ from biotite.structure.atoms import coord
24
+ from biotite.structure.filter import filter_amino_acids, filter_nucleotides
25
+ from biotite.structure.geometry import centroid, distance
26
+ 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
209
+
210
+
211
+ def superimpose(fixed, mobile, atom_mask=None):
212
+ """
213
+ Superimpose structures onto each other, minimizing the RMSD between
214
+ them.
215
+ :footcite:`Kabsch1976, Kabsch1978`.
216
+
217
+ More precisely, the `mobile` structure is rotated and translated onto
218
+ the `fixed` structure.
219
+
220
+ Parameters
221
+ ----------
222
+ fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
223
+ The fixed structure(s).
224
+ 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
226
+ The structure(s) which is/are superimposed on the `fixed`
227
+ structure.
228
+ Each atom at index *i* in `mobile` must correspond the
229
+ atom at index *i* in `fixed` to obtain correct results.
230
+ Furthermore, if both `fixed` and `mobile` are
231
+ :class:`AtomArrayStack` objects, they must have the same
232
+ number of models.
233
+ Alternatively coordinates can be given.
234
+ atom_mask: ndarray, dtype=bool, optional
235
+ If given, only the atoms covered by this boolean mask will be
236
+ considered for superimposition.
237
+ This means that the algorithm will minimize the RMSD based
238
+ on the covered atoms instead of all atoms.
239
+ The returned superimposed structure will contain all atoms
240
+ of the input structure, regardless of this parameter.
241
+
242
+ Returns
243
+ -------
244
+ fitted : AtomArray or AtomArrayStack or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
245
+ A copy of the `mobile` structure(s),
246
+ superimposed on the fixed structure(s).
247
+ Only coordinates are returned, if coordinates were given in
248
+ `mobile`.
249
+ transformation : AffineTransformation
250
+ The affine transformation(s) that were applied on `mobile`.
251
+ :meth:`AffineTransformation.apply()` can be used to transform
252
+ another AtomArray in the same way.
253
+
254
+ See Also
255
+ --------
256
+ superimpose_without_outliers : Superimposition with outlier removal
257
+ superimpose_homologs : Superimposition of homologous structures
258
+
259
+ Notes
260
+ -----
261
+ The `transformation` can come in handy, in case you want to
262
+ superimpose two
263
+ structures with different amount of atoms.
264
+ Often the two structures need to be filtered in order to obtain the
265
+ same size and annotation arrays.
266
+ After superimposition the transformation can be applied on the
267
+ original structure using :meth:`AffineTransformation.apply()`.
268
+
269
+ References
270
+ ----------
271
+
272
+ .. footbibliography::
273
+
274
+ Examples
275
+ --------
276
+
277
+ At first two models of a structure are taken and one of them is
278
+ randomly rotated/translated.
279
+ Consequently the RMSD is quite large:
280
+
281
+ >>> array1 = atom_array_stack[0]
282
+ >>> array2 = atom_array_stack[1]
283
+ >>> array2 = translate(array2, [1,2,3])
284
+ >>> array2 = rotate(array2, [1,2,3])
285
+ >>> print("{:.3f}".format(rmsd(array1, array2)))
286
+ 11.260
287
+
288
+ RMSD decreases after superimposition of only CA atoms:
289
+
290
+ >>> array2_fit, transformation = superimpose(
291
+ ... array1, array2, atom_mask=(array2.atom_name == "CA")
292
+ ... )
293
+ >>> print("{:.3f}".format(rmsd(array1, array2_fit)))
294
+ 1.961
295
+
296
+ RMSD is even lower when all atoms are considered in the
297
+ superimposition:
298
+
299
+ >>> array2_fit, transformation = superimpose(array1, array2)
300
+ >>> print("{:.3f}".format(rmsd(array1, array2_fit)))
301
+ 1.928
302
+ """
303
+ # Bring coordinates into the same dimensionality
304
+ mob_coord = _reshape_to_3d(coord(mobile))
305
+ fix_coord = _reshape_to_3d(coord(fixed))
306
+
307
+ if atom_mask is not None:
308
+ # Implicitly this creates array copies
309
+ mob_filtered = mob_coord[:, atom_mask, :]
310
+ fix_filtered = fix_coord[:, atom_mask, :]
311
+ else:
312
+ mob_filtered = np.copy(mob_coord)
313
+ fix_filtered = np.copy(fix_coord)
314
+
315
+ # Center coordinates at (0,0,0)
316
+ mob_centroid = centroid(mob_filtered)
317
+ fix_centroid = centroid(fix_filtered)
318
+ mob_centered_filtered = mob_filtered - mob_centroid[:, np.newaxis, :]
319
+ fix_centered_filtered = fix_filtered - fix_centroid[:, np.newaxis, :]
320
+
321
+ rotation = _get_rotation_matrices(fix_centered_filtered, mob_centered_filtered)
322
+ transform = AffineTransformation(-mob_centroid, rotation, fix_centroid)
323
+ return transform.apply(mobile), transform
324
+
325
+
326
+ def superimpose_without_outliers(
327
+ fixed,
328
+ mobile,
329
+ min_anchors=3,
330
+ max_iterations=10,
331
+ quantiles=(0.25, 0.75),
332
+ outlier_threshold=1.5,
333
+ ):
334
+ r"""
335
+ Superimpose structures onto a fixed structure, ignoring
336
+ conformational outliers.
337
+
338
+ This method iteratively superimposes the `mobile` structure onto the
339
+ `fixed` structure, removes conformational outliers and superimposes
340
+ the remaining atoms (called *anchors*) again until no outlier
341
+ remains.
342
+
343
+
344
+ Parameters
345
+ ----------
346
+ fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n) or ndarray, shape(n,), dtype=float or ndarray, shape(m,n), dtype=float
347
+ The fixed structure(s).
348
+ 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
350
+ The structure(s) which is/are superimposed on the `fixed`
351
+ structure.
352
+ Each atom at index *i* in `mobile` must correspond the
353
+ atom at index *i* in `fixed` to obtain correct results.
354
+ Furthermore, if both `fixed` and `mobile` are
355
+ :class:`AtomArrayStack` objects, they must have the same
356
+ number of models.
357
+ Alternatively coordinates can be given.
358
+ min_anchors : int, optional
359
+ The outlier removal is stopped, if less than `min_anchors`
360
+ anchors would be left.
361
+ max_iterations : int, optional
362
+ The maximum number of iterations for removing conformational
363
+ outliers.
364
+ Setting the value to 1 means that no outlier removal is
365
+ conducted.
366
+ quantiles : tuple (float, float), optional
367
+ The lower and upper quantile for the interpercentile range
368
+ (IPR).
369
+ By default the interquartile range is taken.
370
+ outlier_threshold : float, optional
371
+ The threshold for considering a conformational outlier.
372
+ The threshold is given in units of IPR.
373
+
374
+ Returns
375
+ -------
376
+ fitted : AtomArray or AtomArrayStack
377
+ A copy of the `mobile` structure(s), superimposed on the fixed
378
+ structure.
379
+ Only coordinates are returned, if coordinates were given in
380
+ `mobile`.
381
+ transform : AffineTransformation
382
+ This object contains the affine transformation(s) that were
383
+ applied on `mobile`.
384
+ :meth:`AffineTransformation.apply()` can be used to transform
385
+ another AtomArray in the same way.
386
+ anchor_indices : ndarray, shape(k,), dtype=int
387
+ The indices of the anchor atoms.
388
+ These atoms were used for the superimposition.
389
+
390
+ See Also
391
+ --------
392
+ superimpose : Superimposition without outlier removal
393
+ superimpose_homologs : Superimposition of homologous structures
394
+
395
+ Notes
396
+ -----
397
+ This method runs the following algorithm in iterations:
398
+
399
+ 1. Superimpose anchor atoms of `mobile` onto `fixed`.
400
+ 2. Calculate the squared distance :math:`d^2` between the
401
+ superimposed anchors.
402
+ 3. Remove conformational outliers from anchors based on the
403
+ following criterion:
404
+
405
+ .. math:: d^2 > P_\text{upper}(d^2) + \left( P_\text{upper}(d^2) - P_\text{lower}(d^2) \right) \cdot T
406
+
407
+ In prose this means that an anchor is considered an outlier, if
408
+ it is `outlier_threshold` :math:`T` times the interpercentile
409
+ range (IPR) above the upper percentile.
410
+ By default, this is 1.5 times the interquartile range, which is
411
+ the usual threshold to mark outliers in box plots.
412
+
413
+ In the beginning, all atoms are considered as anchors.
414
+
415
+ Considering all atoms (not only the anchors), this approach does
416
+ **not** minimize the RMSD, in contrast to :func:`superimpose()`.
417
+ The purpose of this function is to ignore outliers to decrease the
418
+ RMSD in the more conserved parts of the structure.
419
+ """
420
+ if max_iterations < 1:
421
+ raise ValueError("Maximum number of iterations must be at least 1")
422
+
423
+ # Ensure that the first quantile is smaller than the second one
424
+ quantiles = sorted(quantiles)
425
+
426
+ fixed_coord = coord(fixed)
427
+ mobile_coord = coord(mobile)
428
+ # Before refinement, all anchors are included
429
+ # 'inlier' is the opposite of 'outlier'
430
+ updated_inlier_mask = np.ones(fixed_coord.shape[-2], dtype=bool)
431
+
432
+ for _ in range(max_iterations):
433
+ # Run superimposition
434
+ inlier_mask = updated_inlier_mask
435
+ filtered_fixed_coord = fixed_coord[..., inlier_mask, :]
436
+ filtered_mobile_coord = mobile_coord[..., inlier_mask, :]
437
+ superimposed_coord, transform = superimpose(
438
+ filtered_fixed_coord, filtered_mobile_coord
439
+ )
440
+
441
+ # Find outliers
442
+ sq_dist = distance(filtered_fixed_coord, superimposed_coord) ** 2
443
+ if sq_dist.ndim == 2:
444
+ # If multiple models are superimposed,
445
+ # use the mean squared distance to determine outliers
446
+ sq_dist = np.mean(sq_dist, axis=0)
447
+ lower_quantile, upper_quantile = np.quantile(sq_dist, quantiles)
448
+ ipr = upper_quantile - lower_quantile
449
+ updated_inlier_mask = inlier_mask.copy()
450
+ # Squared distance was only calculated for the existing inliers
451
+ # -> update the mask only for these atoms
452
+ updated_inlier_mask[updated_inlier_mask] = (
453
+ sq_dist <= upper_quantile + outlier_threshold * ipr
454
+ )
455
+ if np.all(updated_inlier_mask):
456
+ # No outliers anymore -> early termination
457
+ break
458
+ if np.count_nonzero(updated_inlier_mask) < min_anchors:
459
+ # Less than min_anchors anchors would be left -> early termination
460
+ break
461
+
462
+ anchor_indices = np.where(inlier_mask)[0]
463
+ return transform.apply(mobile), transform, anchor_indices
464
+
465
+
466
+ def superimpose_homologs(
467
+ fixed, mobile, substitution_matrix=None, gap_penalty=-10, min_anchors=3, **kwargs
468
+ ):
469
+ r"""
470
+ Superimpose one protein or nucleotide chain onto another one,
471
+ considering sequence differences and conformational outliers.
472
+
473
+ The method finds corresponding residues by sequence alignment and
474
+ selects their :math:`C_{\alpha}` or :math:`P` atoms as
475
+ superimposition *anchors*.
476
+ Then iteratively the anchor atoms are superimposed and outliers are
477
+ removed.
478
+
479
+ Parameters
480
+ ----------
481
+ fixed : AtomArray, shape(n,) or AtomArrayStack, shape(m,n)
482
+ The fixed structure(s).
483
+ Must comprise a single chain.
484
+ 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.
488
+ substitution_matrix : str or SubstitutionMatrix, optional
489
+ The (name of the) substitution matrix used for sequence
490
+ alignment.
491
+ Must fit the chain type.
492
+ By default, ``"BLOSUM62"`` and ``"NUC"`` are used respectively.
493
+ Only aligned residues with a positive score are considered as
494
+ initial anchors.
495
+ gap_penalty : int or tuple of int, optional
496
+ The gap penalty for sequence alignment.
497
+ A single value indicates a linear penalty, while a tuple
498
+ indicates an affine penalty.
499
+ min_anchors : int, optional
500
+ If less than `min_anchors` anchors are found by sequence
501
+ alignment, the method ditches the alignment and matches all
502
+ anchor atoms.
503
+ If the number of anchor atoms is not equal in `fixed` and
504
+ `mobile` in this fallback case, an exception is raised.
505
+ Furthermore, the outlier removal is stopped, if less than
506
+ `min_anchors` anchors would be left.
507
+ **kwargs
508
+ Additional parameters for
509
+ :func:`superimpose_without_outliers()`.
510
+
511
+ Returns
512
+ -------
513
+ fitted : AtomArray or AtomArrayStack
514
+ A copy of the `mobile` structure(s), superimposed on the fixed
515
+ structure(s).
516
+ transform : AffineTransformation
517
+ This object contains the affine transformation(s) that were
518
+ applied on `mobile`.
519
+ :meth:`AffineTransformation.apply()` can be used to transform
520
+ another AtomArray in the same way.
521
+ fixed_anchor_indices, mobile_anchor_indices : ndarray, shape(k,), dtype=int
522
+ The indices of the anchor atoms in the fixed and mobile
523
+ structure, respectively.
524
+ These atoms were used for the superimposition.
525
+
526
+ See Also
527
+ --------
528
+ superimpose : Superimposition without outlier removal
529
+ superimpose_without_outliers : Internally used for outlier removal
530
+
531
+ Notes
532
+ -----
533
+ As this method relies on sequence alignment, it works only for
534
+ proteins/nucleic acids with decent sequence homology.
535
+ """
536
+ fixed_anchor_indices = _get_backbone_anchor_indices(fixed)
537
+ mobile_anchor_indices = _get_backbone_anchor_indices(mobile)
538
+ if (
539
+ len(fixed_anchor_indices) < min_anchors
540
+ or len(mobile_anchor_indices) < min_anchors
541
+ ):
542
+ raise ValueError(
543
+ "Structures have too few CA atoms for required number of anchors"
544
+ )
545
+
546
+ anchor_indices = _find_matching_anchors(
547
+ fixed[..., fixed_anchor_indices],
548
+ mobile[..., mobile_anchor_indices],
549
+ substitution_matrix,
550
+ gap_penalty,
551
+ )
552
+ if len(anchor_indices) < min_anchors:
553
+ # Fallback: Match all backbone anchors
554
+ if len(fixed_anchor_indices) != len(mobile_anchor_indices):
555
+ raise ValueError(
556
+ "Tried fallback due to low anchor number, "
557
+ "but number of CA atoms does not match"
558
+ )
559
+ fixed_anchor_indices = fixed_anchor_indices
560
+ mobile_anchor_indices = mobile_anchor_indices
561
+ else:
562
+ # The anchor indices point to the CA atoms
563
+ # -> get the corresponding indices for the whole structure
564
+ fixed_anchor_indices = fixed_anchor_indices[anchor_indices[:, 0]]
565
+ mobile_anchor_indices = mobile_anchor_indices[anchor_indices[:, 1]]
566
+
567
+ _, transform, selected_anchor_indices = superimpose_without_outliers(
568
+ fixed[..., fixed_anchor_indices],
569
+ mobile[..., mobile_anchor_indices],
570
+ min_anchors,
571
+ **kwargs,
572
+ )
573
+ fixed_anchor_indices = fixed_anchor_indices[selected_anchor_indices]
574
+ mobile_anchor_indices = mobile_anchor_indices[selected_anchor_indices]
575
+
576
+ return (
577
+ transform.apply(mobile),
578
+ transform,
579
+ fixed_anchor_indices,
580
+ mobile_anchor_indices,
581
+ )
582
+
583
+
584
+ def _reshape_to_3d(coord):
585
+ """
586
+ Reshape the coordinate array to 3D, if it is 2D.
587
+ """
588
+ if coord.ndim < 2:
589
+ raise ValueError("Coordinates must be at least two-dimensional")
590
+ if coord.ndim == 2:
591
+ return coord[np.newaxis, ...]
592
+ elif coord.ndim == 3:
593
+ return coord
594
+ else:
595
+ raise ValueError("Coordinates must be at most three-dimensional")
596
+
597
+
598
+ def _get_rotation_matrices(fixed, mobile):
599
+ """
600
+ Get the rotation matrices to superimpose the given mobile
601
+ coordinates into the given fixed coordinates, minimizing the RMSD.
602
+
603
+ Uses the *Kabsch* algorithm.
604
+ Both sets of coordinates must already be centered at origin.
605
+ """
606
+ # Calculate cross-covariance matrices
607
+ cov = np.sum(fixed[:, :, :, np.newaxis] * mobile[:, :, np.newaxis, :], axis=1)
608
+ v, s, w = np.linalg.svd(cov)
609
+ # Remove possibility of reflected atom coordinates
610
+ reflected_mask = np.linalg.det(v) * np.linalg.det(w) < 0
611
+ v[reflected_mask, :, -1] *= -1
612
+ matrices = np.matmul(v, w)
613
+ return matrices
614
+
615
+
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
+ def _get_backbone_anchor_indices(atoms):
627
+ """
628
+ Select one representative anchor atom for each amino acid and
629
+ nucleotide and return their indices.
630
+ """
631
+ return np.where(
632
+ ((filter_amino_acids(atoms)) & (atoms.atom_name == "CA"))
633
+ | ((filter_nucleotides(atoms)) & (atoms.atom_name == "P"))
634
+ )[0]
635
+
636
+
637
+ def _find_matching_anchors(
638
+ fixed_anchor_atoms,
639
+ mobile_anchors_atoms,
640
+ substitution_matrix,
641
+ gap_penalty,
642
+ ):
643
+ """
644
+ Find corresponding residues using pairwise sequence alignment.
645
+ """
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()
662
+
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)
675
+ # Cannot anchor gaps
676
+ & (alignment_codes[0] != -1)
677
+ & (alignment_codes[1] != -1)
678
+ )
679
+ anchors = alignment.trace[anchor_mask]
680
+ return anchors
681
+
682
+
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]