synkit 1.6.5__py3-none-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.
Files changed (636) hide show
  1. synkit/CRN/Benchmark/__init__.py +53 -0
  2. synkit/CRN/Benchmark/crosschecks.py +13 -0
  3. synkit/CRN/Benchmark/kegg.py +12 -0
  4. synkit/CRN/Benchmark/networks.py +13 -0
  5. synkit/CRN/Benchmark/scaling.py +13 -0
  6. synkit/CRN/Benchmark/validate.py +13 -0
  7. synkit/CRN/Construct/__init__.py +16 -0
  8. synkit/CRN/Construct/abstract.py +668 -0
  9. synkit/CRN/Construct/arity.py +36 -0
  10. synkit/CRN/Construct/builder.py +619 -0
  11. synkit/CRN/Construct/derivation.py +63 -0
  12. synkit/CRN/Construct/flattener.py +100 -0
  13. synkit/CRN/Construct/keys.py +14 -0
  14. synkit/CRN/Construct/mixtures.py +117 -0
  15. synkit/CRN/Construct/rdkit_worker.py +69 -0
  16. synkit/CRN/Construct/smiles.py +99 -0
  17. synkit/CRN/Construct/state.py +31 -0
  18. synkit/CRN/Construct/strategy.py +75 -0
  19. synkit/CRN/Construct/worker.py +42 -0
  20. synkit/CRN/IO/__init__.py +36 -0
  21. synkit/CRN/IO/sbml.py +925 -0
  22. synkit/CRN/Pathway/__init__.py +72 -0
  23. synkit/CRN/Pathway/_adapter.py +333 -0
  24. synkit/CRN/Pathway/pathfinder.py +500 -0
  25. synkit/CRN/Pathway/reachability.py +796 -0
  26. synkit/CRN/Pathway/realizability.py +1322 -0
  27. synkit/CRN/Petrinet/__init__.py +93 -0
  28. synkit/CRN/Petrinet/analyzer.py +486 -0
  29. synkit/CRN/Petrinet/minimal_semiflows.py +436 -0
  30. synkit/CRN/Petrinet/net.py +1041 -0
  31. synkit/CRN/Petrinet/persistence.py +97 -0
  32. synkit/CRN/Petrinet/semiflows.py +483 -0
  33. synkit/CRN/Petrinet/siphon_search.py +402 -0
  34. synkit/CRN/Petrinet/structure.py +286 -0
  35. synkit/CRN/Props/__init__.py +130 -0
  36. synkit/CRN/Props/_rank.py +29 -0
  37. synkit/CRN/Props/deficiency.py +953 -0
  38. synkit/CRN/Props/dynamics.py +832 -0
  39. synkit/CRN/Props/helper.py +151 -0
  40. synkit/CRN/Props/stoich.py +751 -0
  41. synkit/CRN/Props/thermo.py +584 -0
  42. synkit/CRN/Query/__init__.py +66 -0
  43. synkit/CRN/Query/cache.py +41 -0
  44. synkit/CRN/Query/kegg_api.py +99 -0
  45. synkit/CRN/Query/kegg_extract.py +714 -0
  46. synkit/CRN/Query/kegg_impute.py +707 -0
  47. synkit/CRN/Query/kegg_parse.py +480 -0
  48. synkit/CRN/Query/to_syncrn.py +290 -0
  49. synkit/CRN/README.md +234 -0
  50. synkit/CRN/Structure/__init__.py +12 -0
  51. synkit/CRN/Structure/_graph_io.py +713 -0
  52. synkit/CRN/Structure/_matrices.py +88 -0
  53. synkit/CRN/Structure/_parse.py +544 -0
  54. synkit/CRN/Structure/reaction.py +237 -0
  55. synkit/CRN/Structure/rule.py +55 -0
  56. synkit/CRN/Structure/species.py +46 -0
  57. synkit/CRN/Structure/syncrn.py +929 -0
  58. synkit/CRN/Symmetry/__init__.py +20 -0
  59. synkit/CRN/Symmetry/_common.py +1059 -0
  60. synkit/CRN/Symmetry/_ir.py +940 -0
  61. synkit/CRN/Symmetry/automorphism.py +472 -0
  62. synkit/CRN/Symmetry/canon.py +441 -0
  63. synkit/CRN/Symmetry/isomorphism.py +412 -0
  64. synkit/CRN/Symmetry/symmetry.py +61 -0
  65. synkit/CRN/Symmetry/wl_canon.py +967 -0
  66. synkit/CRN/Visualize/__init__.py +11 -0
  67. synkit/CRN/Visualize/labels.py +113 -0
  68. synkit/CRN/Visualize/layout.py +708 -0
  69. synkit/CRN/Visualize/palette.py +292 -0
  70. synkit/CRN/Visualize/validation.py +122 -0
  71. synkit/CRN/Visualize/vis.py +1152 -0
  72. synkit/CRN/__init__.py +233 -0
  73. synkit/CRN/_build.py +50 -0
  74. synkit/CRN/_native_build.py +175 -0
  75. synkit/CRN/analysis.py +629 -0
  76. synkit/CRN/backend.py +74 -0
  77. synkit/CRN/cpp/CMakeLists.txt +58 -0
  78. synkit/CRN/cpp/algebra.cpp +46 -0
  79. synkit/CRN/cpp/closure.cpp +29 -0
  80. synkit/CRN/cpp/exact.h +55 -0
  81. synkit/CRN/cpp/pathway.cpp +46 -0
  82. synkit/CRN/cpp/semiflows.cpp +209 -0
  83. synkit/CRN/cpp/structural.cpp +240 -0
  84. synkit/CRN/cpp/symmetry.cpp +45 -0
  85. synkit/CRN/cpp/write_manifest.cmake +28 -0
  86. synkit/CRN/kinds.py +137 -0
  87. synkit/CRN/lib/synkit_crn.dll +0 -0
  88. synkit/CRN/lib/synkit_crn.json +33 -0
  89. synkit/CRN/native.py +388 -0
  90. synkit/CRN/native_analysis.py +459 -0
  91. synkit/Chem/Cluster/__init__.py +0 -0
  92. synkit/Chem/Cluster/butina.py +140 -0
  93. synkit/Chem/Fingerprint/__init__.py +0 -0
  94. synkit/Chem/Fingerprint/fp_calculator.py +155 -0
  95. synkit/Chem/Fingerprint/smiles_featurizer.py +258 -0
  96. synkit/Chem/Fingerprint/transformation_fp.py +139 -0
  97. synkit/Chem/Mapper/README.rst +43 -0
  98. synkit/Chem/Mapper/__init__.py +255 -0
  99. synkit/Chem/Mapper/_compat.py +130 -0
  100. synkit/Chem/Mapper/adapters/__init__.py +1 -0
  101. synkit/Chem/Mapper/adapters/prediction.py +126 -0
  102. synkit/Chem/Mapper/adapters/template.py +412 -0
  103. synkit/Chem/Mapper/chem/__init__.py +52 -0
  104. synkit/Chem/Mapper/chem/aam.py +673 -0
  105. synkit/Chem/Mapper/chem/blind.py +160 -0
  106. synkit/Chem/Mapper/chem/its.py +309 -0
  107. synkit/Chem/Mapper/chem/smiles.py +564 -0
  108. synkit/Chem/Mapper/graph/__init__.py +66 -0
  109. synkit/Chem/Mapper/graph/automorphism.py +395 -0
  110. synkit/Chem/Mapper/graph/blockcut.py +98 -0
  111. synkit/Chem/Mapper/graph/labeled_graph.py +104 -0
  112. synkit/Chem/Mapper/graph/refine.py +176 -0
  113. synkit/Chem/Mapper/graph/synkit_adapter.py +110 -0
  114. synkit/Chem/Mapper/io/__init__.py +13 -0
  115. synkit/Chem/Mapper/io/idxmapstr.py +82 -0
  116. synkit/Chem/Mapper/mapper.py +685 -0
  117. synkit/Chem/Mapper/reaction_search.py +554 -0
  118. synkit/Chem/Mapper/reporting/__init__.py +1 -0
  119. synkit/Chem/Mapper/reporting/alternatives.py +451 -0
  120. synkit/Chem/Mapper/reporting/analysis.py +681 -0
  121. synkit/Chem/Mapper/reporting/native_analysis.py +485 -0
  122. synkit/Chem/Mapper/reporting/spectrum.py +642 -0
  123. synkit/Chem/Mapper/solvers/__init__.py +1 -0
  124. synkit/Chem/Mapper/solvers/exact/__init__.py +141 -0
  125. synkit/Chem/Mapper/solvers/exact/assignment/__init__.py +1 -0
  126. synkit/Chem/Mapper/solvers/exact/assignment/assignment_certificate.py +113 -0
  127. synkit/Chem/Mapper/solvers/exact/assignment/assignment_domains.py +162 -0
  128. synkit/Chem/Mapper/solvers/exact/assignment/assignment_verification.py +58 -0
  129. synkit/Chem/Mapper/solvers/exact/assignment/incremental_assignment.py +247 -0
  130. synkit/Chem/Mapper/solvers/exact/certificate.py +128 -0
  131. synkit/Chem/Mapper/solvers/exact/core.py +183 -0
  132. synkit/Chem/Mapper/solvers/exact/distance_search/__init__.py +1 -0
  133. synkit/Chem/Mapper/solvers/exact/distance_search/bounds.py +319 -0
  134. synkit/Chem/Mapper/solvers/exact/distance_search/exhaustive.py +179 -0
  135. synkit/Chem/Mapper/solvers/exact/distance_search/inputs.py +283 -0
  136. synkit/Chem/Mapper/solvers/exact/distance_search/records.py +242 -0
  137. synkit/Chem/Mapper/solvers/exact/distance_search/search.py +1025 -0
  138. synkit/Chem/Mapper/solvers/exact/distance_search/verify.py +378 -0
  139. synkit/Chem/Mapper/solvers/exact/edit_support.py +375 -0
  140. synkit/Chem/Mapper/solvers/exact/hybrid.py +224 -0
  141. synkit/Chem/Mapper/solvers/exact/hydrogen.py +264 -0
  142. synkit/Chem/Mapper/solvers/exact/kernel_solvers/__init__.py +1 -0
  143. synkit/Chem/Mapper/solvers/exact/kernel_solvers/branching.py +499 -0
  144. synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumerate.py +1050 -0
  145. synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumeration_result.py +30 -0
  146. synkit/Chem/Mapper/solvers/exact/kernel_solvers/kernel.py +256 -0
  147. synkit/Chem/Mapper/solvers/exact/kernel_solvers/milp.py +194 -0
  148. synkit/Chem/Mapper/solvers/exact/native/__init__.py +1 -0
  149. synkit/Chem/Mapper/solvers/exact/native/build.py +154 -0
  150. synkit/Chem/Mapper/solvers/exact/native/candidates.py +593 -0
  151. synkit/Chem/Mapper/solvers/exact/native/canonical.py +396 -0
  152. synkit/Chem/Mapper/solvers/exact/native/disk_frontier.py +415 -0
  153. synkit/Chem/Mapper/solvers/exact/native/frontier.py +390 -0
  154. synkit/Chem/Mapper/solvers/exact/native/hydrogen_quotient.py +213 -0
  155. synkit/Chem/Mapper/solvers/exact/native/its.py +528 -0
  156. synkit/Chem/Mapper/solvers/exact/native/native_distance.cpp +2803 -0
  157. synkit/Chem/Mapper/solvers/exact/native/orbit_aggregation.py +328 -0
  158. synkit/Chem/Mapper/solvers/exact/native/parallel.py +205 -0
  159. synkit/Chem/Mapper/solvers/exact/native/pmcd.py +203 -0
  160. synkit/Chem/Mapper/solvers/exact/native/runtime.py +40 -0
  161. synkit/Chem/Mapper/solvers/exact/native/search.py +294 -0
  162. synkit/Chem/Mapper/solvers/exact/native/separator.py +224 -0
  163. synkit/Chem/Mapper/solvers/exact/native_build.py +12 -0
  164. synkit/Chem/Mapper/solvers/exact/pabs/__init__.py +1 -0
  165. synkit/Chem/Mapper/solvers/exact/pabs/cost_lattice.py +43 -0
  166. synkit/Chem/Mapper/solvers/exact/pabs/factor_spectrum.py +110 -0
  167. synkit/Chem/Mapper/solvers/exact/pabs/multi_shell.py +368 -0
  168. synkit/Chem/Mapper/solvers/exact/pabs/propagation.py +679 -0
  169. synkit/Chem/Mapper/solvers/exact/pabs/propagation_bounds.py +436 -0
  170. synkit/Chem/Mapper/solvers/exact/pabs/propagation_limits.py +13 -0
  171. synkit/Chem/Mapper/solvers/exact/pabs/propagation_search.py +856 -0
  172. synkit/Chem/Mapper/solvers/exact/pabs/propagation_symmetry.py +66 -0
  173. synkit/Chem/Mapper/solvers/exact/pabs/residual_typed_bonds.py +136 -0
  174. synkit/Chem/Mapper/solvers/exact/pabs/reward_frontier.py +201 -0
  175. synkit/Chem/Mapper/solvers/exact/pabs/separator_bound.py +258 -0
  176. synkit/Chem/Mapper/solvers/exact/pabs/separator_spectrum.py +308 -0
  177. synkit/Chem/Mapper/solvers/exact/pabs/sparse_reward_bound.py +207 -0
  178. synkit/Chem/Mapper/solvers/exact/pabs/suffix_spectrum.py +299 -0
  179. synkit/Chem/Mapper/solvers/exact/search.py +55 -0
  180. synkit/Chem/Mapper/solvers/exact/seeds/__init__.py +1 -0
  181. synkit/Chem/Mapper/solvers/exact/seeds/seed.py +104 -0
  182. synkit/Chem/Mapper/solvers/exact/seeds/seed_fragments.py +148 -0
  183. synkit/Chem/Mapper/solvers/exact/seeds/seed_relaxation.py +181 -0
  184. synkit/Chem/Mapper/solvers/exact/symmetry.py +223 -0
  185. synkit/Chem/Mapper/solvers/native/__init__.py +4 -0
  186. synkit/Chem/Mapper/solvers/native/assignment.py +99 -0
  187. synkit/Chem/Mapper/solvers/native/astar.py +22 -0
  188. synkit/Chem/Mapper/solvers/native/build.py +385 -0
  189. synkit/Chem/Mapper/solvers/native/classification.py +641 -0
  190. synkit/Chem/Mapper/solvers/native/core.py +378 -0
  191. synkit/Chem/Mapper/solvers/native/cpp/CMakeLists.txt +120 -0
  192. synkit/Chem/Mapper/solvers/native/cpp/examples/map_reaction.cpp +29 -0
  193. synkit/Chem/Mapper/solvers/native/cpp/examples/slap_graph.cpp +63 -0
  194. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/assignment/assignment.h +18 -0
  195. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/astar.h +18 -0
  196. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/bound.h +33 -0
  197. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/pmcd_witness.h +16 -0
  198. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/runtime.h +130 -0
  199. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/hydrogen/hydrogen.h +73 -0
  200. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mapper.h +175 -0
  201. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mces/mces.h +9 -0
  202. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/pabs/seed.h +34 -0
  203. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/reaction/reaction.h +30 -0
  204. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/sepdp/sepdp.h +21 -0
  205. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/slap.h +72 -0
  206. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/solvers/solvers.h +16 -0
  207. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/classification.h +140 -0
  208. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/cpabs.h +33 -0
  209. synkit/Chem/Mapper/solvers/native/cpp/src/assignment/abb.cpp +302 -0
  210. synkit/Chem/Mapper/solvers/native/cpp/src/astar/CMakeLists.txt +12 -0
  211. synkit/Chem/Mapper/solvers/native/cpp/src/astar/README.md +47 -0
  212. synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar.cpp +228 -0
  213. synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar_graph.cpp +37 -0
  214. synkit/Chem/Mapper/solvers/native/cpp/src/astar/bound.cpp +212 -0
  215. synkit/Chem/Mapper/solvers/native/cpp/src/core/dispatch.cpp +272 -0
  216. synkit/Chem/Mapper/solvers/native/cpp/src/core/pmcd_witness.cpp +36 -0
  217. synkit/Chem/Mapper/solvers/native/cpp/src/hydrogen/hydrogen.cpp +380 -0
  218. synkit/Chem/Mapper/solvers/native/cpp/src/mces/README.md +57 -0
  219. synkit/Chem/Mapper/solvers/native/cpp/src/mces/mces.cpp +287 -0
  220. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/bridge.cpp +334 -0
  221. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/chemical_floor.inc +181 -0
  222. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/endpoint_symmetry.inc +104 -0
  223. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/exact_helpers.h +20 -0
  224. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_assignment_certificate.h +165 -0
  225. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_certificate.h +125 -0
  226. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_model.h +365 -0
  227. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_provider.inc +319 -0
  228. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_rounding.inc +170 -0
  229. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_test_hooks.h +342 -0
  230. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/objective_reduction.inc +137 -0
  231. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/overlap_dual.inc +351 -0
  232. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/profile_seed.inc +214 -0
  233. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/seed_heuristics.inc +640 -0
  234. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_embedding.cpp +430 -0
  235. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_root_lp.h +176 -0
  236. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/test_hooks.inc +262 -0
  237. synkit/Chem/Mapper/solvers/native/cpp/src/reaction/reaction.cpp +611 -0
  238. synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/README.md +61 -0
  239. synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/sepdp.cpp +253 -0
  240. synkit/Chem/Mapper/solvers/native/cpp/src/slap.cpp +810 -0
  241. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/formulation.h +138 -0
  242. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/maxsat.cpp +526 -0
  243. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/ortools.cpp +475 -0
  244. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/seed.h +80 -0
  245. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/weighted_cnf.h +256 -0
  246. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/README.md +43 -0
  247. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/dfs.cpp +123 -0
  248. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/edit_support.cpp +224 -0
  249. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification.cpp +553 -0
  250. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_pipeline.cpp +124 -0
  251. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_stream.h +13 -0
  252. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/cpabs.cpp +455 -0
  253. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/edge_deck.cpp +743 -0
  254. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
  255. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
  256. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naugraph.c +732 -0
  257. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.c +94 -0
  258. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.h +49 -0
  259. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.c +1771 -0
  260. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.h +133 -0
  261. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nautil.c +750 -0
  262. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naututil.h +355 -0
  263. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.c +1256 -0
  264. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.h +1544 -0
  265. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.c +1131 -0
  266. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.h +70 -0
  267. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/sorttemplates.c +580 -0
  268. synkit/Chem/Mapper/solvers/native/hydrogen.py +711 -0
  269. synkit/Chem/Mapper/solvers/native/implicit.py +506 -0
  270. synkit/Chem/Mapper/solvers/native/implicit_certificate.py +187 -0
  271. synkit/Chem/Mapper/solvers/native/implicit_preparation.py +309 -0
  272. synkit/Chem/Mapper/solvers/native/lib/synkit_mapper.dll +0 -0
  273. synkit/Chem/Mapper/solvers/native/mces.py +16 -0
  274. synkit/Chem/Mapper/solvers/native/reaction.py +179 -0
  275. synkit/Chem/Mapper/solvers/native/reaction_build.py +277 -0
  276. synkit/Chem/Mapper/solvers/native/reaction_text.py +26 -0
  277. synkit/Chem/Mapper/solvers/native/runtime.py +242 -0
  278. synkit/Chem/Mapper/solvers/native/sepdp.py +47 -0
  279. synkit/Chem/Mapper/solvers/native/slap.py +266 -0
  280. synkit/Chem/Mapper/solvers/native/solvers.py +67 -0
  281. synkit/Chem/Mapper/solvers/native/stereo.py +86 -0
  282. synkit/Chem/Mapper/solvers/native/supporting.py +34 -0
  283. synkit/Chem/Mapper/solvers/native/symmetry.py +91 -0
  284. synkit/Chem/Mapper/solvers/slap/__init__.py +29 -0
  285. synkit/Chem/Mapper/solvers/slap/lap.py +174 -0
  286. synkit/Chem/Mapper/solvers/slap/sequential.py +696 -0
  287. synkit/Chem/Mapper/validation/__init__.py +1 -0
  288. synkit/Chem/Mapper/validation/aam_validator.py +442 -0
  289. synkit/Chem/Mapper/validation/annotations.py +274 -0
  290. synkit/Chem/Mapper/validation/bonds.py +124 -0
  291. synkit/Chem/Mapper/validation/cohorts.py +72 -0
  292. synkit/Chem/Mapper/validation/identifiability.py +150 -0
  293. synkit/Chem/Mapper/validation/numerical_scope.py +36 -0
  294. synkit/Chem/Mapper/validation/orbits.py +137 -0
  295. synkit/Chem/Molecule/__init__.py +0 -0
  296. synkit/Chem/Molecule/_chirality_loci.py +165 -0
  297. synkit/Chem/Molecule/_chirality_types.py +231 -0
  298. synkit/Chem/Molecule/_helical_loci.py +167 -0
  299. synkit/Chem/Molecule/_stereo_axis_evidence.py +202 -0
  300. synkit/Chem/Molecule/_stereo_element_detection.py +193 -0
  301. synkit/Chem/Molecule/_stereo_orientation_constraints.py +511 -0
  302. synkit/Chem/Molecule/_tetrahedral_carriers.py +63 -0
  303. synkit/Chem/Molecule/atom_features.py +379 -0
  304. synkit/Chem/Molecule/chirality.py +1041 -0
  305. synkit/Chem/Molecule/cip_assignment.py +569 -0
  306. synkit/Chem/Molecule/cip_ranking.py +843 -0
  307. synkit/Chem/Molecule/coordinate_stereo.py +503 -0
  308. synkit/Chem/Molecule/derived_stereo_naming.py +165 -0
  309. synkit/Chem/Molecule/descriptors.py +452 -0
  310. synkit/Chem/Molecule/formula.py +249 -0
  311. synkit/Chem/Molecule/global_stereo.py +175 -0
  312. synkit/Chem/Molecule/graph_annotator.py +300 -0
  313. synkit/Chem/Molecule/standardize.py +817 -0
  314. synkit/Chem/Molecule/stereo_evidence.py +179 -0
  315. synkit/Chem/Molecule/stereo_perception.py +1032 -0
  316. synkit/Chem/Molecule/valence.py +123 -0
  317. synkit/Chem/Reaction/Mapper/__init__.py +44 -0
  318. synkit/Chem/Reaction/__init__.py +20 -0
  319. synkit/Chem/Reaction/aam_validator.py +5 -0
  320. synkit/Chem/Reaction/balance_check.py +138 -0
  321. synkit/Chem/Reaction/canon_rsmi.py +249 -0
  322. synkit/Chem/Reaction/cleaning.py +58 -0
  323. synkit/Chem/Reaction/deionize.py +199 -0
  324. synkit/Chem/Reaction/explicit_h_audit.py +232 -0
  325. synkit/Chem/Reaction/fix_aam.py +64 -0
  326. synkit/Chem/Reaction/neutralize.py +207 -0
  327. synkit/Chem/Reaction/radical_wildcard.py +224 -0
  328. synkit/Chem/Reaction/standardize.py +203 -0
  329. synkit/Chem/Reaction/tautomerize.py +205 -0
  330. synkit/Chem/__init__.py +1 -0
  331. synkit/Chem/utils.py +315 -0
  332. synkit/Data/__init__.py +0 -0
  333. synkit/Data/aldol.json.gz +1 -0
  334. synkit/Data/gen_partial_aam.py +147 -0
  335. synkit/Data/paracetamol.json.gz +1 -0
  336. synkit/Graph/Canon/__init__.py +16 -0
  337. synkit/Graph/Canon/_native/__init__.py +13 -0
  338. synkit/Graph/Canon/_native/build.py +93 -0
  339. synkit/Graph/Canon/_native/lib/synkit_canon.dll +0 -0
  340. synkit/Graph/Canon/_native/prepare.py +49 -0
  341. synkit/Graph/Canon/_native/runtime.py +140 -0
  342. synkit/Graph/Canon/_native/search.py +137 -0
  343. synkit/Graph/Canon/canon_algs.py +228 -0
  344. synkit/Graph/Canon/canon_graph.py +531 -0
  345. synkit/Graph/Canon/cpp/CMakeLists.txt +42 -0
  346. synkit/Graph/Canon/cpp/README.md +123 -0
  347. synkit/Graph/Canon/cpp/examples/canonical_graph.cpp +16 -0
  348. synkit/Graph/Canon/cpp/include/synkit_canon/canon.h +85 -0
  349. synkit/Graph/Canon/cpp/src/canonical.cpp +391 -0
  350. synkit/Graph/Canon/cpp/src/symmetry.h +83 -0
  351. synkit/Graph/Canon/cpp/tests/symmetry.cpp +93 -0
  352. synkit/Graph/Canon/exact.py +1020 -0
  353. synkit/Graph/Canon/exact_refinement.py +368 -0
  354. synkit/Graph/Canon/nauty.py +143 -0
  355. synkit/Graph/Context/__init__.py +0 -0
  356. synkit/Graph/Context/hier_context.py +244 -0
  357. synkit/Graph/Context/radius_expand.py +252 -0
  358. synkit/Graph/FG/__init__.py +19 -0
  359. synkit/Graph/FG/api.py +39 -0
  360. synkit/Graph/FG/audit.py +155 -0
  361. synkit/Graph/FG/catalog.py +919 -0
  362. synkit/Graph/FG/detector.py +288 -0
  363. synkit/Graph/FG/model.py +97 -0
  364. synkit/Graph/FG/recognizers.py +274 -0
  365. synkit/Graph/FG/ring_system.py +154 -0
  366. synkit/Graph/Feature/Descriptors/topology.py +857 -0
  367. synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
  368. synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +222 -0
  369. synkit/Graph/Feature/__init__.py +5 -0
  370. synkit/Graph/Feature/graph_descriptors.py +323 -0
  371. synkit/Graph/Feature/graph_fps.py +96 -0
  372. synkit/Graph/Feature/graph_signature.py +239 -0
  373. synkit/Graph/Feature/hash_fps.py +133 -0
  374. synkit/Graph/Feature/morgan_fps.py +88 -0
  375. synkit/Graph/Feature/path_fps.py +81 -0
  376. synkit/Graph/Feature/wl_hash.py +134 -0
  377. synkit/Graph/Fusion/__init__.py +85 -0
  378. synkit/Graph/Fusion/candidate.py +559 -0
  379. synkit/Graph/Fusion/constructor.py +877 -0
  380. synkit/Graph/Fusion/identity.py +184 -0
  381. synkit/Graph/Fusion/interface.py +700 -0
  382. synkit/Graph/Fusion/search.py +241 -0
  383. synkit/Graph/Hyrogen/__init__.py +6 -0
  384. synkit/Graph/Hyrogen/_misc.py +591 -0
  385. synkit/Graph/Hyrogen/hcomplete.py +995 -0
  386. synkit/Graph/Hyrogen/hcomplete_legacy.py +368 -0
  387. synkit/Graph/Hyrogen/hcompletion_algorithms.py +713 -0
  388. synkit/Graph/Hyrogen/hextend.py +570 -0
  389. synkit/Graph/Hyrogen/hextend_legacy.py +215 -0
  390. synkit/Graph/ITS/__init__.py +4 -0
  391. synkit/Graph/ITS/its_builder.py +120 -0
  392. synkit/Graph/ITS/its_construction.py +719 -0
  393. synkit/Graph/ITS/its_decompose.py +572 -0
  394. synkit/Graph/ITS/its_destruction.py +331 -0
  395. synkit/Graph/ITS/its_expand.py +1251 -0
  396. synkit/Graph/ITS/its_relabel.py +187 -0
  397. synkit/Graph/ITS/its_reverter.py +356 -0
  398. synkit/Graph/ITS/normalize_aam.py +145 -0
  399. synkit/Graph/ITS/partial_its.py +238 -0
  400. synkit/Graph/ITS/rc_extractor.py +656 -0
  401. synkit/Graph/ITS/stereo.py +74 -0
  402. synkit/Graph/MTG/__init__.py +107 -0
  403. synkit/Graph/MTG/gluing.py +451 -0
  404. synkit/Graph/MTG/history.py +928 -0
  405. synkit/Graph/MTG/inventory.py +118 -0
  406. synkit/Graph/MTG/local.py +447 -0
  407. synkit/Graph/MTG/mcs_matcher.py +246 -0
  408. synkit/Graph/MTG/mtg.py +611 -0
  409. synkit/Graph/MTG/mtg_explore.py +74 -0
  410. synkit/Graph/MTG/observation.py +209 -0
  411. synkit/Graph/MTG/process.py +577 -0
  412. synkit/Graph/MTG/queries.py +66 -0
  413. synkit/Graph/MTG/realization.py +95 -0
  414. synkit/Graph/MTG/series_parallel.py +107 -0
  415. synkit/Graph/MTG/utils.py +424 -0
  416. synkit/Graph/Matcher/__init__.py +10 -0
  417. synkit/Graph/Matcher/approx_mcs.py +1264 -0
  418. synkit/Graph/Matcher/auto_est.py +736 -0
  419. synkit/Graph/Matcher/automorphism.py +314 -0
  420. synkit/Graph/Matcher/batch_cluster.py +224 -0
  421. synkit/Graph/Matcher/dedup_matches.py +247 -0
  422. synkit/Graph/Matcher/graph_cluster.py +191 -0
  423. synkit/Graph/Matcher/graph_matcher.py +312 -0
  424. synkit/Graph/Matcher/graph_morphism.py +421 -0
  425. synkit/Graph/Matcher/mcs_matcher.py +804 -0
  426. synkit/Graph/Matcher/multi_turbo_iso.py +186 -0
  427. synkit/Graph/Matcher/orbit.py +284 -0
  428. synkit/Graph/Matcher/partial_matcher.py +694 -0
  429. synkit/Graph/Matcher/sing.py +544 -0
  430. synkit/Graph/Matcher/subgraph_matcher.py +812 -0
  431. synkit/Graph/Matcher/turbo_iso.py +272 -0
  432. synkit/Graph/Matcher/wl_sel.py +537 -0
  433. synkit/Graph/Mech/__init__.py +26 -0
  434. synkit/Graph/Mech/conversion.py +1466 -0
  435. synkit/Graph/Mech/conversion_debug.py +237 -0
  436. synkit/Graph/Mech/electron_accounting.py +200 -0
  437. synkit/Graph/Mech/lwg_editor.py +406 -0
  438. synkit/Graph/Mech/lwg_ops.py +313 -0
  439. synkit/Graph/Morphism/__init__.py +99 -0
  440. synkit/Graph/Morphism/constraints.py +460 -0
  441. synkit/Graph/Morphism/llg.py +999 -0
  442. synkit/Graph/Morphism/morphism.py +349 -0
  443. synkit/Graph/Morphism/stereo.py +258 -0
  444. synkit/Graph/Morphism/stereo_morphism.py +383 -0
  445. synkit/Graph/Morphism/stereo_morphism_helpers.py +957 -0
  446. synkit/Graph/Morphism/stereo_morphism_types.py +117 -0
  447. synkit/Graph/Stereo/__init__.py +376 -0
  448. synkit/Graph/Stereo/_descriptor_core.py +207 -0
  449. synkit/Graph/Stereo/_descriptor_serialization.py +138 -0
  450. synkit/Graph/Stereo/canonical.py +648 -0
  451. synkit/Graph/Stereo/changes.py +964 -0
  452. synkit/Graph/Stereo/composition.py +391 -0
  453. synkit/Graph/Stereo/configured.py +946 -0
  454. synkit/Graph/Stereo/couplings.py +245 -0
  455. synkit/Graph/Stereo/descriptors.py +1030 -0
  456. synkit/Graph/Stereo/enumeration.py +597 -0
  457. synkit/Graph/Stereo/extended_descriptors.py +406 -0
  458. synkit/Graph/Stereo/global_configured.py +152 -0
  459. synkit/Graph/Stereo/global_stereo.py +304 -0
  460. synkit/Graph/Stereo/identity.py +648 -0
  461. synkit/Graph/Stereo/interchange.py +496 -0
  462. synkit/Graph/Stereo/legacy.py +460 -0
  463. synkit/Graph/Stereo/matching.py +899 -0
  464. synkit/Graph/Stereo/native.py +154 -0
  465. synkit/Graph/Stereo/orbits.py +668 -0
  466. synkit/Graph/Stereo/outcomes.py +99 -0
  467. synkit/Graph/Stereo/pairwise.py +350 -0
  468. synkit/Graph/Stereo/rdkit_adapter.py +662 -0
  469. synkit/Graph/Stereo/rdkit_identity.py +118 -0
  470. synkit/Graph/Stereo/rdkit_resonance.py +140 -0
  471. synkit/Graph/Stereo/reaction_interchange.py +266 -0
  472. synkit/Graph/Stereo/semantics.py +296 -0
  473. synkit/Graph/Stereo/supports.py +257 -0
  474. synkit/Graph/Stereo/wire.py +339 -0
  475. synkit/Graph/Wildcard/__init__.py +0 -0
  476. synkit/Graph/Wildcard/fuse_graph.py +156 -0
  477. synkit/Graph/Wildcard/graph_wc.py +303 -0
  478. synkit/Graph/Wildcard/its_merge.py +857 -0
  479. synkit/Graph/Wildcard/radwc.py +114 -0
  480. synkit/Graph/Wildcard/wc_matcher.py +576 -0
  481. synkit/Graph/Wildcard/wildcard.py +225 -0
  482. synkit/Graph/__init__.py +17 -0
  483. synkit/Graph/canon_graph.py +14 -0
  484. synkit/Graph/syn_graph.py +199 -0
  485. synkit/Graph/utils.py +324 -0
  486. synkit/IO/__init__.py +4 -0
  487. synkit/IO/chem_converter.py +1059 -0
  488. synkit/IO/combinatorial/__init__.py +8 -0
  489. synkit/IO/combinatorial/gml_to_graph.py +223 -0
  490. synkit/IO/combinatorial/graph_to_gml.py +280 -0
  491. synkit/IO/combinatorial/graph_to_smarts.py +186 -0
  492. synkit/IO/combinatorial/smarts_expander.py +147 -0
  493. synkit/IO/combinatorial/smarts_generalizer.py +133 -0
  494. synkit/IO/combinatorial/smarts_to_graph.py +179 -0
  495. synkit/IO/conversion.py +42 -0
  496. synkit/IO/data_io.py +269 -0
  497. synkit/IO/data_process.py +143 -0
  498. synkit/IO/debug.py +73 -0
  499. synkit/IO/gml_to_nx.py +150 -0
  500. synkit/IO/graph_to_mol.py +199 -0
  501. synkit/IO/mol_to_graph.py +1105 -0
  502. synkit/IO/mol_to_graph_chemistry.py +545 -0
  503. synkit/IO/nx_to_gml.py +209 -0
  504. synkit/IO/smiles_to_id.py +123 -0
  505. synkit/Mechanism/__init__.py +151 -0
  506. synkit/Mechanism/adapters.py +136 -0
  507. synkit/Mechanism/audit.py +323 -0
  508. synkit/Mechanism/benchmark.py +638 -0
  509. synkit/Mechanism/compatibility.py +13 -0
  510. synkit/Mechanism/drawing.py +85 -0
  511. synkit/Mechanism/electron_models.py +725 -0
  512. synkit/Mechanism/equivalence.py +521 -0
  513. synkit/Mechanism/evidence.py +262 -0
  514. synkit/Mechanism/interchange.py +141 -0
  515. synkit/Mechanism/model.py +918 -0
  516. synkit/Mechanism/neighbor_stereo.py +312 -0
  517. synkit/Mechanism/radical_data.py +740 -0
  518. synkit/Mechanism/replay.py +986 -0
  519. synkit/Mechanism/schema.py +264 -0
  520. synkit/Mechanism/stereo_state.py +416 -0
  521. synkit/Mechanism/symbols.py +91 -0
  522. synkit/Rule/Apply/__init__.py +35 -0
  523. synkit/Rule/Apply/conditions.py +45 -0
  524. synkit/Rule/Apply/dpo.py +998 -0
  525. synkit/Rule/Apply/rule_matcher.py +218 -0
  526. synkit/Rule/Apply/validation.py +354 -0
  527. synkit/Rule/Compose/__init__.py +106 -0
  528. synkit/Rule/Compose/_identity.py +43 -0
  529. synkit/Rule/Compose/composition.py +976 -0
  530. synkit/Rule/Compose/laws.py +439 -0
  531. synkit/Rule/Compose/search.py +771 -0
  532. synkit/Rule/Compose/synrule.py +170 -0
  533. synkit/Rule/Modify/__init__.py +0 -0
  534. synkit/Rule/Modify/implict_rule.py +56 -0
  535. synkit/Rule/Modify/longest_path.py +92 -0
  536. synkit/Rule/Modify/molecule_rule.py +115 -0
  537. synkit/Rule/Modify/prune_templates.py +78 -0
  538. synkit/Rule/Modify/rule_utils.py +194 -0
  539. synkit/Rule/Modify/strip_rule.py +97 -0
  540. synkit/Rule/__init__.py +28 -0
  541. synkit/Rule/generic_stereo.py +884 -0
  542. synkit/Rule/stereo_identity.py +271 -0
  543. synkit/Rule/syn_rule.py +1090 -0
  544. synkit/Synthesis/MSR/__init__.py +0 -0
  545. synkit/Synthesis/MSR/path_finder.py +239 -0
  546. synkit/Synthesis/Metrics/__init__.py +0 -0
  547. synkit/Synthesis/Metrics/_base.py +52 -0
  548. synkit/Synthesis/Metrics/_plot.py +129 -0
  549. synkit/Synthesis/Metrics/_ranking.py +179 -0
  550. synkit/Synthesis/RBL/__init__.py +80 -0
  551. synkit/Synthesis/RBL/engine.py +1249 -0
  552. synkit/Synthesis/RBL/matching.py +652 -0
  553. synkit/Synthesis/RBL/overlap.py +336 -0
  554. synkit/Synthesis/RBL/pipeline.py +376 -0
  555. synkit/Synthesis/RBL/policy.py +139 -0
  556. synkit/Synthesis/RBL/proof.py +370 -0
  557. synkit/Synthesis/RBL/reaction.py +506 -0
  558. synkit/Synthesis/RBL/state.py +169 -0
  559. synkit/Synthesis/RBL/validation.py +1060 -0
  560. synkit/Synthesis/Reactor/__init__.py +60 -0
  561. synkit/Synthesis/Reactor/core/__init__.py +1 -0
  562. synkit/Synthesis/Reactor/core/engine.py +967 -0
  563. synkit/Synthesis/Reactor/core/product.py +621 -0
  564. synkit/Synthesis/Reactor/core/rewrite.py +561 -0
  565. synkit/Synthesis/Reactor/core/strategy.py +44 -0
  566. synkit/Synthesis/Reactor/matching/__init__.py +1 -0
  567. synkit/Synthesis/Reactor/matching/mixin.py +920 -0
  568. synkit/Synthesis/Reactor/matching/policy.py +345 -0
  569. synkit/Synthesis/Reactor/matching/symmetry.py +309 -0
  570. synkit/Synthesis/Reactor/output/__init__.py +1 -0
  571. synkit/Synthesis/Reactor/output/deduplication.py +703 -0
  572. synkit/Synthesis/Reactor/output/policy.py +16 -0
  573. synkit/Synthesis/Reactor/output/serialization.py +485 -0
  574. synkit/Synthesis/Reactor/output/structural.py +893 -0
  575. synkit/Synthesis/Reactor/stereo/__init__.py +1 -0
  576. synkit/Synthesis/Reactor/stereo/assignment.py +35 -0
  577. synkit/Synthesis/Reactor/stereo/mixin.py +780 -0
  578. synkit/Synthesis/Reactor/variants/__init__.py +1 -0
  579. synkit/Synthesis/Reactor/variants/imbalanced.py +180 -0
  580. synkit/Synthesis/Reactor/variants/partial.py +84 -0
  581. synkit/Synthesis/Reactor/workflow/__init__.py +1 -0
  582. synkit/Synthesis/Reactor/workflow/batch.py +550 -0
  583. synkit/Synthesis/Reactor/workflow/benchmark.py +143 -0
  584. synkit/Synthesis/Reactor/workflow/postprocess.py +267 -0
  585. synkit/Synthesis/Reactor/workflow/rule_filter.py +210 -0
  586. synkit/Synthesis/__init__.py +0 -0
  587. synkit/Synthesis/reactor_utils.py +316 -0
  588. synkit/Utils/__init__.py +0 -0
  589. synkit/Utils/utils.py +172 -0
  590. synkit/Vis/__init__.py +91 -0
  591. synkit/Vis/crn/__init__.py +40 -0
  592. synkit/Vis/epd/__init__.py +20 -0
  593. synkit/Vis/epd/arrows.py +425 -0
  594. synkit/Vis/epd/chem.py +197 -0
  595. synkit/Vis/epd/constants.py +83 -0
  596. synkit/Vis/epd/layout.py +283 -0
  597. synkit/Vis/epd/mapping.py +51 -0
  598. synkit/Vis/epd/models.py +136 -0
  599. synkit/Vis/epd/render.py +543 -0
  600. synkit/Vis/epd/utils.py +108 -0
  601. synkit/Vis/epd/visualizer.py +811 -0
  602. synkit/Vis/graph_visualizer.py +375 -0
  603. synkit/Vis/its/__init__.py +19 -0
  604. synkit/Vis/its/drawer.py +615 -0
  605. synkit/Vis/its_drawer.py +35 -0
  606. synkit/Vis/molecule/__init__.py +31 -0
  607. synkit/Vis/molecule/drawer.py +563 -0
  608. synkit/Vis/molecule/reaction.py +285 -0
  609. synkit/Vis/molecule_drawer.py +33 -0
  610. synkit/Vis/mtg/__init__.py +17 -0
  611. synkit/Vis/mtg/drawer.py +769 -0
  612. synkit/Vis/mtg_drawer.py +34 -0
  613. synkit/Vis/pdf_writer.py +146 -0
  614. synkit/Vis/reaction/__init__.py +19 -0
  615. synkit/Vis/reaction/rule.py +141 -0
  616. synkit/Vis/reaction/rxn.py +532 -0
  617. synkit/Vis/reaction_drawer.py +41 -0
  618. synkit/Vis/space/__init__.py +17 -0
  619. synkit/Vis/space/chemical.py +80 -0
  620. synkit/Vis/space/embedding.py +87 -0
  621. synkit/Vis/vis_synedu/Vis/__init__.py +7 -0
  622. synkit/Vis/vis_synedu/Vis/dpo.py +862 -0
  623. synkit/Vis/vis_synedu/rxn_vis.py +377 -0
  624. synkit/Vis/vis_synedu/vis.py +501 -0
  625. synkit/Vis/visual_drawer.py +215 -0
  626. synkit/Vis/visual_model.py +520 -0
  627. synkit/__init__.py +3 -0
  628. synkit/_native.py +91 -0
  629. synkit/examples.py +50 -0
  630. synkit/version.py +23 -0
  631. synkit-1.6.5.dist-info/METADATA +156 -0
  632. synkit-1.6.5.dist-info/RECORD +636 -0
  633. synkit-1.6.5.dist-info/WHEEL +4 -0
  634. synkit-1.6.5.dist-info/licenses/LICENSE +21 -0
  635. synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
  636. synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
@@ -0,0 +1,641 @@
1
+ """Prepare exact palettes once; construct, canonically label and dedup ITS in C++."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ctypes as ct
6
+ import json
7
+ import math
8
+ import time
9
+ from dataclasses import dataclass
10
+ from functools import lru_cache
11
+ from numbers import Integral, Real
12
+ from pathlib import Path
13
+
14
+ import numpy as np
15
+
16
+ from synkit.Graph.Canon.exact import _encode_color
17
+
18
+ from ..exact.native.canonical import PackedCanonicalKey
19
+ from ...reporting.spectrum import _typed
20
+
21
+ _INT = ct.POINTER(ct.c_int32)
22
+ _LONG = ct.POINTER(ct.c_int64)
23
+
24
+
25
+ class _Problem(ct.Structure):
26
+ _fields_ = [
27
+ ("n", ct.c_int32),
28
+ ("node_pair_colors", _INT),
29
+ ("edges_r", _INT),
30
+ ("edges_p", _INT),
31
+ ("edge_states_r", ct.c_int32),
32
+ ("edge_states_p", ct.c_int32),
33
+ ("edge_pair_colors", _INT),
34
+ ("palette_size", ct.c_int32),
35
+ ("palette_json", ct.POINTER(ct.c_char_p)),
36
+ ]
37
+
38
+
39
+ class _Statistics(ct.Structure):
40
+ _fields_ = [
41
+ ("candidates", ct.c_int64),
42
+ ("classes", ct.c_int64),
43
+ ("canonical_nodes", ct.c_int64),
44
+ ("complete", ct.c_int32),
45
+ ("reason", ct.c_int32),
46
+ ("elapsed_seconds", ct.c_double),
47
+ ]
48
+
49
+
50
+ _CALLBACK = ct.CFUNCTYPE(
51
+ ct.c_int,
52
+ _INT,
53
+ ct.c_int64,
54
+ ct.c_int64,
55
+ ct.POINTER(ct.c_ubyte),
56
+ ct.c_int64,
57
+ ct.c_char_p,
58
+ ct.c_void_p,
59
+ )
60
+
61
+
62
+ @lru_cache(maxsize=8)
63
+ def _load(path):
64
+ library = ct.CDLL(str(Path(path).resolve()))
65
+ if library.synkit_mapper_classification_abi() != 1:
66
+ raise RuntimeError("Unsupported native classification ABI")
67
+ if hasattr(library, "synkit_mapper_classification_max_vertices"):
68
+ library.synkit_mapper_classification_max_vertices.argtypes = []
69
+ library.synkit_mapper_classification_max_vertices.restype = ct.c_int
70
+ library.synkit_mapper_classification_error.restype = ct.c_char_p
71
+ library.synkit_mapper_classify_batch.argtypes = [
72
+ ct.POINTER(_Problem),
73
+ ct.c_int64,
74
+ _INT,
75
+ _LONG,
76
+ ct.c_double,
77
+ ct.c_int64,
78
+ ct.c_int64,
79
+ _CALLBACK,
80
+ ct.c_void_p,
81
+ ct.POINTER(_Statistics),
82
+ ]
83
+ library.synkit_mapper_classify_batch.restype = ct.c_int
84
+ library.synkit_mapper_classify_batch_with_cancel.argtypes = [
85
+ ct.POINTER(_Problem),
86
+ ct.c_int64,
87
+ _INT,
88
+ _LONG,
89
+ ct.c_double,
90
+ ct.c_int64,
91
+ ct.c_int64,
92
+ ct.c_void_p,
93
+ _CALLBACK,
94
+ ct.c_void_p,
95
+ ct.POINTER(_Statistics),
96
+ ]
97
+ library.synkit_mapper_classify_batch_with_cancel.restype = ct.c_int
98
+ if hasattr(library, "synkit_mapper_classify_batch_updates_with_cancel"):
99
+ function = library.synkit_mapper_classify_batch_updates_with_cancel
100
+ function.argtypes = library.synkit_mapper_classify_batch_with_cancel.argtypes
101
+ function.restype = ct.c_int
102
+ if hasattr(library, "synkit_mapper_classify_batch_ex"):
103
+ function = library.synkit_mapper_classify_batch_ex
104
+ function.argtypes = [
105
+ *library.synkit_mapper_classify_batch_with_cancel.argtypes,
106
+ ct.c_int32,
107
+ ct.c_int32,
108
+ ]
109
+ function.restype = ct.c_int
110
+ return library
111
+
112
+
113
+ @dataclass(frozen=True)
114
+ class NativeITSClass:
115
+ representative: tuple[int, ...]
116
+ cost2: int
117
+ observed_mapping_count: int
118
+ key: PackedCanonicalKey
119
+ identifier: str
120
+
121
+
122
+ @dataclass(frozen=True)
123
+ class NativeClassificationResult:
124
+ classes: tuple[NativeITSClass, ...]
125
+ candidates: int
126
+ complete: bool
127
+ reason: str | None
128
+ canonical_nodes: int
129
+ elapsed_seconds: float
130
+
131
+
132
+ def _records_callback(n, classes, errors, class_callback, *, upserts=False):
133
+ positions = {}
134
+
135
+ @_CALLBACK
136
+ def receive(mapping, cost2, observed, packed, size, identifier, _):
137
+ try:
138
+ name = identifier.decode("ascii")
139
+ key = PackedCanonicalKey(ct.string_at(packed, size), identifier=name)
140
+ record = NativeITSClass(
141
+ tuple(mapping[i] for i in range(n)), cost2, observed, key, name
142
+ )
143
+ if upserts and key.parts in positions:
144
+ index = positions[key.parts]
145
+ old = classes[index]
146
+ if (
147
+ old.representative != record.representative
148
+ or old.cost2 != record.cost2
149
+ or old.identifier != record.identifier
150
+ or record.observed_mapping_count < old.observed_mapping_count
151
+ ):
152
+ raise RuntimeError("Invalid cumulative native class update")
153
+ classes[index] = record
154
+ return 0
155
+ positions[key.parts] = len(classes)
156
+ classes.append(record)
157
+ return (
158
+ int(bool(class_callback(record))) if class_callback is not None else 0
159
+ )
160
+ except BaseException as error: # noqa: BLE001 - C callback boundary.
161
+ errors.append(error)
162
+ return 1
163
+
164
+ return receive
165
+
166
+
167
+ def _classification_result(classes, statistics, seconds):
168
+ return NativeClassificationResult(
169
+ tuple(classes),
170
+ statistics.candidates,
171
+ bool(statistics.complete),
172
+ {
173
+ 0: None,
174
+ 1: "time_limit",
175
+ 2: "canonical_node_limit",
176
+ 3: "class_limit",
177
+ 4: "callback_stop",
178
+ 5: "cancelled",
179
+ }[statistics.reason],
180
+ statistics.canonical_nodes,
181
+ seconds,
182
+ )
183
+
184
+
185
+ def _cap(value, name):
186
+ if value is None:
187
+ return -1
188
+ if (
189
+ isinstance(value, bool)
190
+ or not isinstance(value, Integral)
191
+ or not 0 < value < 2**63
192
+ ):
193
+ raise ValueError(f"{name} must be positive int64 or None")
194
+ return int(value)
195
+
196
+
197
+ class NativeITSBatchClassifier:
198
+ """Exact attributed ITS classes with the existing SKC1 and class-ID contracts.
199
+
200
+ ``properties`` preserves its insertion order and pairs endpoint atom arrays.
201
+ ``edge_properties`` pairs dense endpoint attribute matrices. For E/Z, use
202
+ ``{"stereo": (reactant_stereo, product_stereo)}``; colors then exactly match
203
+ reaction_search's ``(weight_r, weight_p, stereo_r, stereo_p)``. Elements
204
+ include isotope/type information under the caller's established convention.
205
+ The C++ loop performs all per-candidate construction, exact search, hashing
206
+ and deduplication. Python receives one callback per distinct complete key.
207
+ Counts refer to supplied mappings, never inferred labeled multiplicities.
208
+ Palette preparation memoizes repeated exact typed states within this
209
+ constructor call; tokens, ordering and native certificate bytes are unchanged.
210
+ Inputs above 256 vertices require the library's explicit capacity accessor;
211
+ this implementation supports at most 512 vertices.
212
+ """
213
+
214
+ # Keep ABI validation, palettes and their ctypes buffer lifetimes together.
215
+ def __init__( # noqa: C901
216
+ self,
217
+ reactant,
218
+ product,
219
+ elements,
220
+ properties,
221
+ *,
222
+ library_path,
223
+ edge_properties=None,
224
+ identifier_mode="legacy",
225
+ ):
226
+ preparation_started = time.perf_counter()
227
+ if identifier_mode not in {"legacy", "packed"}:
228
+ raise ValueError("identifier_mode must be 'legacy' or 'packed'")
229
+ self.identifier_mode = identifier_mode
230
+ a, b = np.asarray(reactant), np.asarray(product)
231
+ self.n = n = len(elements)
232
+ if not 0 <= n <= 512 or a.shape != (n, n) or b.shape != (n, n):
233
+ raise ValueError(
234
+ "Native ITS requires square endpoints with 0..512 vertices"
235
+ )
236
+ # Old libraries retain their qualified 256-vertex limit. Only large
237
+ # inputs load early; the established <=256 preparation order is intact.
238
+ library = None
239
+ if identifier_mode == "packed":
240
+ library = _load(str(Path(library_path).resolve()))
241
+ if not all(
242
+ hasattr(library, name)
243
+ for name in (
244
+ "synkit_mapper_classify_batch_ex",
245
+ "synkit_mapper_solve_classified_ex",
246
+ )
247
+ ):
248
+ raise RuntimeError("Native library does not support packed identifiers")
249
+ if n > 256:
250
+ library = _load(str(Path(library_path).resolve()))
251
+ capacity = min(
252
+ 512,
253
+ library.synkit_mapper_classification_max_vertices()
254
+ if hasattr(library, "synkit_mapper_classification_max_vertices")
255
+ else 256,
256
+ )
257
+ if n > capacity:
258
+ raise ValueError(
259
+ f"Native ITS library supports at most {capacity} vertices"
260
+ )
261
+ for matrix in (a, b):
262
+ if (
263
+ not np.isfinite(matrix).all()
264
+ or np.any(matrix < 0)
265
+ or not np.array_equal(matrix, matrix.T)
266
+ or np.any(np.diag(matrix))
267
+ ):
268
+ raise ValueError(
269
+ "ITS endpoints must be finite nonnegative symmetric and loop-free"
270
+ )
271
+ for name, sides in properties.items():
272
+ if len(sides) != 2 or any(len(side) != n for side in sides):
273
+ raise ValueError(f"Invalid atom property {name}")
274
+ edge_properties = edge_properties or {}
275
+ for name, sides in edge_properties.items():
276
+ if len(sides) != 2 or any(np.shape(side) != (n, n) for side in sides):
277
+ raise ValueError(f"Invalid edge property {name}")
278
+ if any(
279
+ any(side[i][j] != side[j][i] for i in range(n) for j in range(n))
280
+ for side in sides
281
+ ):
282
+ raise ValueError(f"Asymmetric edge property {name}")
283
+ # Chemistry repeats a small number of exact atom states across n*n
284
+ # possible pairs. _typed preserves the established literal-type contract.
285
+ left_states = [
286
+ (
287
+ _typed(elements[i]),
288
+ tuple(_typed(sides[0][i]) for sides in properties.values()),
289
+ )
290
+ for i in range(n)
291
+ ]
292
+ right_states = [
293
+ tuple(_typed(sides[1][j]) for sides in properties.values())
294
+ for j in range(n)
295
+ ]
296
+ node_cache = {
297
+ (left, right): _encode_color(
298
+ (
299
+ left[0],
300
+ tuple(
301
+ (name, x, y) for name, x, y in zip(properties, left[1], right)
302
+ ),
303
+ (),
304
+ )
305
+ )
306
+ for left in dict.fromkeys(left_states)
307
+ for right in dict.fromkeys(right_states)
308
+ }
309
+ node_tokens = [
310
+ [node_cache[left, right] for right in right_states] for left in left_states
311
+ ]
312
+ states, matrices = [], []
313
+ for side, matrix in enumerate((a, b)):
314
+ # Intern exact tokens rather than Python equality: bool/int and
315
+ # signed floating zero may differ in the certificate contract.
316
+ # Edge attributes use their exact encoder, not repr as a cache key:
317
+ # distinct supported mappings/containers may have equal reprs.
318
+ cache, encoded_states, encoded_rows = {}, {}, []
319
+ for i in range(n):
320
+ row = []
321
+ for j in range(n):
322
+ weight = float(matrix[i, j])
323
+ attrs = tuple(
324
+ values[side][i][j] for values in edge_properties.values()
325
+ )
326
+ key = (weight.hex(), tuple(_encode_color(v) for v in attrs))
327
+ if key not in cache:
328
+ raw = (weight, *attrs)
329
+ token = _encode_color(raw)
330
+ cache[key] = token
331
+ encoded_states[token] = raw
332
+ row.append(cache[key])
333
+ encoded_rows.append(row)
334
+ palette = [encoded_states[token] for token in sorted(encoded_states)]
335
+ if not palette:
336
+ palette = [(0.0,)]
337
+ ids = {_encode_color(value): index for index, value in enumerate(palette)}
338
+ states.append(palette)
339
+ matrices.append(
340
+ np.ascontiguousarray(
341
+ [[ids[v] for v in row] for row in encoded_rows],
342
+ dtype=np.int32,
343
+ ).reshape(n, n)
344
+ )
345
+ edge_tokens = []
346
+ for left in states[0]:
347
+ row = []
348
+ for right in states[1]:
349
+ raw = (
350
+ left[0],
351
+ right[0],
352
+ *(v for pair in zip(left[1:], right[1:]) for v in pair),
353
+ )
354
+ row.append(_encode_color(raw) if left[0] or right[0] else None)
355
+ edge_tokens.append(row)
356
+ tokens = sorted(
357
+ {v for row in node_tokens for v in row}
358
+ | {v for row in edge_tokens for v in row if v is not None}
359
+ )
360
+ if len(tokens) >= 65536:
361
+ raise ValueError("Native ITS exact palette exceeds uint16")
362
+ ids = {token: index for index, token in enumerate(tokens)}
363
+ self.node_colors = np.ascontiguousarray(
364
+ [[ids[v] for v in row] for row in node_tokens], dtype=np.int32
365
+ ).reshape(n, n)
366
+ self.edge_colors = np.ascontiguousarray(
367
+ [[ids[v] + 1 if v is not None else 0 for v in row] for row in edge_tokens],
368
+ dtype=np.int32,
369
+ )
370
+ self.edge_matrices = matrices
371
+ self.palette = (ct.c_char_p * len(tokens))(
372
+ *(
373
+ json.dumps(token, separators=(",", ":")).encode("ascii")
374
+ for token in tokens
375
+ )
376
+ )
377
+ self.problem = _Problem(
378
+ n,
379
+ self.node_colors.ctypes.data_as(_INT),
380
+ matrices[0].ctypes.data_as(_INT),
381
+ matrices[1].ctypes.data_as(_INT),
382
+ len(states[0]),
383
+ len(states[1]),
384
+ self.edge_colors.ctypes.data_as(_INT),
385
+ len(tokens),
386
+ self.palette,
387
+ )
388
+ self.library = (
389
+ library if library is not None else _load(str(Path(library_path).resolve()))
390
+ )
391
+ self.preparation_seconds = time.perf_counter() - preparation_started
392
+
393
+ @classmethod
394
+ def from_endpoint_graphs(
395
+ cls,
396
+ endpoint_mols,
397
+ endpoint_graphs,
398
+ *,
399
+ library_path,
400
+ binary=False,
401
+ identifier_mode="legacy",
402
+ ):
403
+ """Prepare the exact existing reaction_search class contract, including E/Z.
404
+
405
+ Preparation still uses RDKit Python, as does the shared chemistry
406
+ frontend. Canonical classification of every subsequent candidate is
407
+ native. Objective anchors/dummies outside a molecule have empty bond
408
+ stereo, matching the historical frontend exactly.
409
+ """
410
+ from ..slap.lap import _adjacency_and_elements
411
+
412
+ a, elements = _adjacency_and_elements(endpoint_graphs[0], binary)
413
+ b, _ = _adjacency_and_elements(endpoint_graphs[1], binary)
414
+ properties = {
415
+ name: (endpoint_graphs[0].props[name], endpoint_graphs[1].props[name])
416
+ for name in ("charge", "radical", "hcount", "stereo", "dummy")
417
+ }
418
+ stereo = []
419
+ for mol in endpoint_mols:
420
+ matrix = np.full((len(elements), len(elements)), "", dtype=object)
421
+ for bond in mol.GetBonds():
422
+ i, j = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
423
+ matrix[i, j] = matrix[j, i] = str(bond.GetStereo())
424
+ stereo.append(matrix)
425
+ return cls(
426
+ a,
427
+ b,
428
+ elements,
429
+ properties,
430
+ library_path=library_path,
431
+ edge_properties={"stereo": stereo},
432
+ identifier_mode=identifier_mode,
433
+ )
434
+
435
+ def classify(
436
+ self,
437
+ mappings,
438
+ costs2=None,
439
+ *,
440
+ time_limit_seconds=None,
441
+ max_canonical_nodes=None,
442
+ max_classes=None,
443
+ class_callback=None,
444
+ cancel_token=None,
445
+ stream=False,
446
+ ):
447
+ """Classify mappings, optionally delivering each key on discovery.
448
+
449
+ ``stream=True`` uses the additive cumulative-count interface when
450
+ available, otherwise the legacy batch API. User callbacks run once
451
+ per new key; final result records merge subsequent count updates.
452
+ Counts in interrupted streaming output are observed lower bounds.
453
+ Legacy default callbacks retain their final batch-count semantics.
454
+ """
455
+ started = time.perf_counter()
456
+ values = list(mappings)
457
+ if any(len(mapping) != self.n for mapping in values):
458
+ raise ValueError("Mapping width differs from ITS size")
459
+ if any(
460
+ isinstance(v, bool) or not isinstance(v, Integral) or not 0 <= v < self.n
461
+ for mapping in values
462
+ for v in mapping
463
+ ):
464
+ raise ValueError("Mappings require valid integer vertex IDs")
465
+ permutations = np.ascontiguousarray(values, dtype=np.int32).reshape(
466
+ len(values), self.n
467
+ )
468
+ costs2 = [0] * len(values) if costs2 is None else list(costs2)
469
+ if len(costs2) != len(values) or any(
470
+ isinstance(v, bool) or not isinstance(v, Integral) or not 0 <= v < 2**63
471
+ for v in costs2
472
+ ):
473
+ raise ValueError("costs2 must contain one nonnegative int64 per mapping")
474
+ costs = np.ascontiguousarray(costs2, dtype=np.int64)
475
+ if time_limit_seconds is not None and (
476
+ isinstance(time_limit_seconds, bool)
477
+ or not isinstance(time_limit_seconds, Real)
478
+ or not math.isfinite(time_limit_seconds)
479
+ or time_limit_seconds < 0
480
+ ):
481
+ raise ValueError("time_limit_seconds must be finite and nonnegative")
482
+ classes, errors = [], []
483
+
484
+ packed_identifier = self.identifier_mode == "packed"
485
+ updates = stream and (
486
+ packed_identifier
487
+ or hasattr(self.library, "synkit_mapper_classify_batch_updates_with_cancel")
488
+ )
489
+ receive = _records_callback(
490
+ self.n, classes, errors, class_callback, upserts=updates
491
+ )
492
+ function = (
493
+ self.library.synkit_mapper_classify_batch_ex
494
+ if packed_identifier
495
+ else (
496
+ self.library.synkit_mapper_classify_batch_updates_with_cancel
497
+ if updates
498
+ else self.library.synkit_mapper_classify_batch_with_cancel
499
+ )
500
+ )
501
+
502
+ statistics = _Statistics()
503
+ status = function(
504
+ ct.byref(self.problem),
505
+ len(values),
506
+ permutations.ctypes.data_as(_INT),
507
+ costs.ctypes.data_as(_LONG),
508
+ (
509
+ -1
510
+ if time_limit_seconds is None
511
+ else max(0, time_limit_seconds - (time.perf_counter() - started))
512
+ ),
513
+ _cap(max_canonical_nodes, "max_canonical_nodes"),
514
+ _cap(max_classes, "max_classes"),
515
+ None if cancel_token is None else cancel_token.pointer,
516
+ receive,
517
+ None,
518
+ ct.byref(statistics),
519
+ *((int(updates), 1) if packed_identifier else ()),
520
+ )
521
+ if errors:
522
+ raise errors[0]
523
+ if status:
524
+ raise RuntimeError(
525
+ self.library.synkit_mapper_classification_error().decode()
526
+ )
527
+ return _classification_result(
528
+ classes, statistics, time.perf_counter() - started
529
+ )
530
+
531
+ def solve_prepared(
532
+ self,
533
+ problem,
534
+ request,
535
+ *,
536
+ max_canonical_nodes=None,
537
+ class_callback=None,
538
+ allowed_domains=None,
539
+ stream=False,
540
+ ):
541
+ """Native search and classification using the shared prepared C structs.
542
+
543
+ ``problem`` and ``request`` are ``_native.runtime.Problem/Request``.
544
+ Their input buffers must stay alive for this synchronous call. This
545
+ method copies the request and sets classify=1. The endpoint palette
546
+ preparation is reusable and is reported separately by preparation_seconds.
547
+ No intermediate mapping calls Python. Unsupported resource/backend
548
+ settings are returned explicitly in the returned common Statistics.
549
+ Return ``(NativeClassificationResult, runtime.Statistics)``.
550
+ Optional ``allowed_domains`` is a dense n*n zero/one mask passed intact
551
+ through the restricted native dispatcher. Its proof/completeness scope
552
+ remains the supplied domains; class batching does not relax them.
553
+ ``stream=True`` publishes new keys immediately and merges cumulative
554
+ count updates; user callbacks run only on first discovery. Older
555
+ libraries retain batch delivery. The default keeps legacy callbacks.
556
+ """
557
+ from .runtime import Problem, Request, Statistics
558
+
559
+ started = time.perf_counter()
560
+ if not isinstance(problem, Problem) or not isinstance(request, Request):
561
+ raise TypeError("Expected shared native Problem and Request structs")
562
+ prepared = Request.from_buffer_copy(request)
563
+ prepared.classify = 1
564
+ mask = None
565
+ prefix = [ct.byref(problem), ct.byref(prepared)]
566
+ prefix_types = [ct.POINTER(Problem), ct.POINTER(Request)]
567
+ if allowed_domains is not None:
568
+ values = np.asarray(allowed_domains)
569
+ if (
570
+ values.shape != (problem.n, problem.n)
571
+ or not np.isin(values, [0, 1]).all()
572
+ ):
573
+ raise ValueError("allowed_domains must be an n-by-n zero/one mask")
574
+ mask = np.ascontiguousarray(values, dtype=np.uint8)
575
+ mask_pointer = ct.POINTER(ct.c_uint8)
576
+ prefix.append(mask.ctypes.data_as(mask_pointer))
577
+ prefix_types.append(mask_pointer)
578
+ name = (
579
+ "synkit_mapper_solve_classified"
580
+ if mask is None
581
+ else "synkit_mapper_solve_domains_classified"
582
+ )
583
+ packed_identifier = self.identifier_mode == "packed"
584
+ updates = stream and (
585
+ packed_identifier or hasattr(self.library, name + "_updates")
586
+ )
587
+ if packed_identifier:
588
+ # The explicit interface always has a nullable mask argument.
589
+ if mask is None:
590
+ prefix.append(None)
591
+ prefix_types.append(ct.POINTER(ct.c_uint8))
592
+ function = self.library.synkit_mapper_solve_classified_ex
593
+ else:
594
+ function = getattr(self.library, name + "_updates" if updates else name)
595
+ self.delivery_mode = "incremental_discovery" if updates else "legacy_batch"
596
+ function.argtypes = [
597
+ *prefix_types,
598
+ ct.POINTER(_Problem),
599
+ ct.c_int64,
600
+ _CALLBACK,
601
+ ct.c_void_p,
602
+ ct.POINTER(Statistics),
603
+ ct.POINTER(_Statistics),
604
+ *((ct.c_int32, ct.c_int32) if packed_identifier else ()),
605
+ ]
606
+ function.restype = ct.c_int
607
+ classes, errors = [], []
608
+ receive = _records_callback(
609
+ self.n, classes, errors, class_callback, upserts=updates
610
+ )
611
+ search_statistics, class_statistics = Statistics(), _Statistics()
612
+ if prepared.seconds >= 0:
613
+ prepared.seconds = max(
614
+ 0, prepared.seconds - (time.perf_counter() - started)
615
+ )
616
+ status = function(
617
+ *prefix,
618
+ ct.byref(self.problem),
619
+ _cap(max_canonical_nodes, "max_canonical_nodes"),
620
+ receive,
621
+ None,
622
+ ct.byref(search_statistics),
623
+ ct.byref(class_statistics),
624
+ *((int(updates), 1) if packed_identifier else ()),
625
+ )
626
+ if errors:
627
+ raise errors[0]
628
+ if status:
629
+ core_error = getattr(self.library, "synkit_mapper_core_error", None)
630
+ message = b""
631
+ if core_error is not None:
632
+ core_error.restype = ct.c_char_p
633
+ message = core_error() or b""
634
+ message = message or self.library.synkit_mapper_classification_error()
635
+ raise RuntimeError((message or b"Native classified search failed").decode())
636
+ return (
637
+ _classification_result(
638
+ classes, class_statistics, class_statistics.elapsed_seconds
639
+ ),
640
+ search_statistics,
641
+ )