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