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,648 @@
1
+ """Map-invariant identity forms for graph-level relative stereochemistry.
2
+
3
+ The structural graph remains canonicalized by :class:`GraphCanonicaliser`.
4
+ This module adds a separate stereo form whose atom references are replaced by
5
+ isomorphism-invariant colour-refinement tokens. The tokens are deliberately a
6
+ prefilter: exact stereo-aware graph isomorphism remains the equality authority
7
+ when two stereo-bearing :class:`~synkit.Graph.syn_graph.SynGraph` objects share
8
+ the same combined signature.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections import Counter
14
+ from collections.abc import Callable, Hashable, Mapping
15
+ import hashlib
16
+ from typing import Any
17
+
18
+ import networkx as nx
19
+
20
+ from .descriptors import (
21
+ AtropBondStereo,
22
+ OctahedralStereo,
23
+ PlanarBondStereo,
24
+ Reference,
25
+ SquarePlanarStereo,
26
+ StereoValue,
27
+ TetrahedralStereo,
28
+ TrigonalBipyramidalStereo,
29
+ parse_virtual_reference,
30
+ )
31
+ from .global_stereo import FrameworkStereo
32
+
33
+ StereoToken = Hashable
34
+ StereoReferenceResolver = Callable[[Reference], StereoToken]
35
+
36
+ _STEREO_TYPES = (
37
+ TetrahedralStereo,
38
+ SquarePlanarStereo,
39
+ TrigonalBipyramidalStereo,
40
+ OctahedralStereo,
41
+ PlanarBondStereo,
42
+ AtropBondStereo,
43
+ )
44
+ _ATOM_TYPES = (
45
+ TetrahedralStereo,
46
+ SquarePlanarStereo,
47
+ TrigonalBipyramidalStereo,
48
+ OctahedralStereo,
49
+ )
50
+ _TETRAHEDRAL_PERMUTATIONS = (
51
+ (0, 1, 2, 3, 4),
52
+ (0, 3, 1, 2, 4),
53
+ (0, 2, 3, 1, 4),
54
+ (0, 1, 4, 2, 3),
55
+ (0, 2, 1, 4, 3),
56
+ (0, 4, 2, 1, 3),
57
+ (0, 1, 3, 4, 2),
58
+ (0, 4, 1, 3, 2),
59
+ (0, 3, 4, 1, 2),
60
+ (0, 2, 4, 3, 1),
61
+ (0, 3, 2, 4, 1),
62
+ (0, 4, 3, 2, 1),
63
+ )
64
+ _TETRAHEDRAL_INVERSION = (0, 2, 1, 3, 4)
65
+ _NODE_IDENTITY_ATTRIBUTES = (
66
+ "element",
67
+ "charge",
68
+ "lone_pairs",
69
+ "radical",
70
+ "aromatic",
71
+ "hcount",
72
+ )
73
+ _EDGE_IDENTITY_ATTRIBUTES = ("order", "standard_order")
74
+ _NODE_IDENTITY_DEFAULTS = {
75
+ "element": "",
76
+ "charge": 0,
77
+ "lone_pairs": 0,
78
+ "radical": 0,
79
+ "aromatic": False,
80
+ "hcount": 0,
81
+ }
82
+ _EDGE_IDENTITY_DEFAULTS = {"order": 0, "standard_order": 0}
83
+
84
+
85
+ class StereoIdentityError(ValueError):
86
+ """Raised when stereo metadata cannot form a map-invariant identity."""
87
+
88
+
89
+ def _freeze(value: Any) -> Hashable:
90
+ if isinstance(value, Mapping):
91
+ return tuple(sorted((str(key), _freeze(item)) for key, item in value.items()))
92
+ if isinstance(value, (list, tuple)):
93
+ return tuple(_freeze(item) for item in value)
94
+ if isinstance(value, set):
95
+ return tuple(sorted((_freeze(item) for item in value), key=repr))
96
+ try:
97
+ hash(value)
98
+ except TypeError:
99
+ return repr(value)
100
+ return value
101
+
102
+
103
+ def _digest(value: Any) -> str:
104
+ return hashlib.sha256(repr(value).encode("utf-8")).hexdigest()[:32]
105
+
106
+
107
+ def stereo_identity_node_match(
108
+ left: Mapping[str, Any],
109
+ right: Mapping[str, Any],
110
+ ) -> bool:
111
+ """Match the stable node attributes used by SynGraph's default identity."""
112
+ return all(
113
+ _freeze(left.get(key, _NODE_IDENTITY_DEFAULTS[key]))
114
+ == _freeze(right.get(key, _NODE_IDENTITY_DEFAULTS[key]))
115
+ for key in _NODE_IDENTITY_ATTRIBUTES
116
+ )
117
+
118
+
119
+ def stereo_identity_edge_match(
120
+ left: Mapping[str, Any],
121
+ right: Mapping[str, Any],
122
+ ) -> bool:
123
+ """Match the stable edge attributes used by SynGraph's default identity."""
124
+ return all(
125
+ _freeze(left.get(key, _EDGE_IDENTITY_DEFAULTS[key]))
126
+ == _freeze(right.get(key, _EDGE_IDENTITY_DEFAULTS[key]))
127
+ for key in _EDGE_IDENTITY_ATTRIBUTES
128
+ )
129
+
130
+
131
+ def _node_seed(attributes: Mapping[str, Any]) -> tuple[Any, ...]:
132
+ return tuple(
133
+ _freeze(attributes.get(key, _NODE_IDENTITY_DEFAULTS[key]))
134
+ for key in _NODE_IDENTITY_ATTRIBUTES
135
+ )
136
+
137
+
138
+ def _edge_seed(attributes: Mapping[str, Any]) -> tuple[Any, ...]:
139
+ return tuple(
140
+ _freeze(attributes.get(key, _EDGE_IDENTITY_DEFAULTS[key]))
141
+ for key in _EDGE_IDENTITY_ATTRIBUTES
142
+ )
143
+
144
+
145
+ def _adjacent_colour_records(
146
+ graph: nx.Graph,
147
+ node: Hashable,
148
+ colours: Mapping[Hashable, str],
149
+ ) -> tuple[Any, ...]:
150
+ records: list[tuple[Any, ...]] = []
151
+
152
+ def append(direction: str, neighbor: Hashable, edge_data: Any) -> None:
153
+ if graph.is_multigraph():
154
+ for data in edge_data.values():
155
+ records.append((direction, _edge_seed(data), colours[neighbor]))
156
+ else:
157
+ records.append((direction, _edge_seed(edge_data), colours[neighbor]))
158
+
159
+ if graph.is_directed():
160
+ for neighbor, edge_data in graph.succ[node].items(): # type: ignore[attr-defined]
161
+ append("out", neighbor, edge_data)
162
+ for neighbor, edge_data in graph.pred[node].items(): # type: ignore[attr-defined]
163
+ append("in", neighbor, edge_data)
164
+ else:
165
+ for neighbor, edge_data in graph.adj[node].items():
166
+ append("edge", neighbor, edge_data)
167
+ return tuple(sorted(records, key=repr))
168
+
169
+
170
+ def structural_node_colours(graph: nx.Graph) -> Mapping[Hashable, str]:
171
+ """Return deterministic, map-independent 1-WL colours for graph nodes.
172
+
173
+ Colour refinement may collide for non-isomorphic rooted environments, but
174
+ it cannot split isomorphic ones. That one-sided guarantee is exactly what
175
+ a hash prefilter requires; exact isomorphism resolves collisions later.
176
+ """
177
+ colours = {
178
+ node: _digest(("node", _node_seed(attributes)))
179
+ for node, attributes in graph.nodes(data=True)
180
+ }
181
+ for _iteration in range(max(1, len(graph))):
182
+ colours = {
183
+ node: _digest(
184
+ (
185
+ "refine",
186
+ colours[node],
187
+ _adjacent_colour_records(graph, node, colours),
188
+ )
189
+ )
190
+ for node in graph
191
+ }
192
+ return colours
193
+
194
+
195
+ def stereo_registry_layers(
196
+ graph: nx.Graph,
197
+ ) -> Mapping[str, Mapping[str, StereoValue]]:
198
+ """Normalize flat molecule and named ITS stereo registries into layers."""
199
+ raw = graph.graph.get("stereo_descriptors", {})
200
+ if raw is None or raw == {}:
201
+ return {}
202
+ if not isinstance(raw, Mapping):
203
+ raise StereoIdentityError("stereo_descriptors must be a mapping.")
204
+ if all(isinstance(value, _STEREO_TYPES) for value in raw.values()):
205
+ return {"state": raw} # type: ignore[return-value]
206
+ if not all(isinstance(value, Mapping) for value in raw.values()):
207
+ raise StereoIdentityError(
208
+ "stereo_descriptors must be one flat registry or named registries."
209
+ )
210
+ layers: dict[str, Mapping[str, StereoValue]] = {}
211
+ for layer, registry in raw.items():
212
+ if not all(isinstance(value, _STEREO_TYPES) for value in registry.values()):
213
+ raise StereoIdentityError(
214
+ f"Stereo registry layer {layer!r} contains an invalid descriptor."
215
+ )
216
+ if registry:
217
+ layers[str(layer)] = registry
218
+ return layers
219
+
220
+
221
+ def _layer_atom_reference(
222
+ graph: nx.Graph,
223
+ node: Hashable,
224
+ layer: str,
225
+ ) -> int | None:
226
+ atom_map = graph.nodes[node].get("atom_map")
227
+ if isinstance(atom_map, tuple) and len(atom_map) == 2:
228
+ side = {"reactant": 0, "product": 1}.get(layer)
229
+ if side is not None and type(atom_map[side]) is int and atom_map[side] > 0:
230
+ return atom_map[side]
231
+ return node if type(node) is int else None
232
+ if type(atom_map) is int and atom_map > 0:
233
+ return atom_map
234
+ return node if type(node) is int else None
235
+
236
+
237
+ def _effective_stereo_side(graph: nx.Graph, layer: str) -> str | None:
238
+ if layer in {"reactant", "product"}:
239
+ return layer
240
+ projection = graph.graph.get("stereo_projection")
241
+ return projection if projection in {"reactant", "product"} else None
242
+
243
+
244
+ def _reference_nodes(
245
+ graph: nx.Graph,
246
+ layer: str,
247
+ ) -> Mapping[int, tuple[Hashable, str]]:
248
+ references: dict[int, tuple[Hashable, str]] = {}
249
+ for node in graph:
250
+ reference = _layer_atom_reference(graph, node, layer)
251
+ if reference is None:
252
+ continue
253
+ previous = references.get(reference)
254
+ target = (node, "atom")
255
+ if previous is not None and previous != target:
256
+ raise StereoIdentityError(
257
+ f"Stereo reference {reference} is ambiguous in layer {layer!r}."
258
+ )
259
+ references[reference] = target
260
+
261
+ side = _effective_stereo_side(graph, layer)
262
+ pair_field = {
263
+ "reactant": "h_pairs_left",
264
+ "product": "h_pairs_right",
265
+ }.get(side, "h_pairs")
266
+ for node, attributes in graph.nodes(data=True):
267
+ pair_maps = attributes.get("h_pair_atom_maps", {})
268
+ pair_ids = attributes.get(pair_field, ())
269
+ if not isinstance(pair_maps, Mapping) or not isinstance(
270
+ pair_ids, (list, tuple, set)
271
+ ):
272
+ continue
273
+ for pair_id in pair_ids:
274
+ reference = pair_maps.get(pair_id)
275
+ if type(reference) is not int or reference <= 0:
276
+ continue
277
+ target = (node, "H")
278
+ previous = references.get(reference)
279
+ # Generated ITS graphs can retain a virtual-H bookkeeping map
280
+ # that collides with a real mapped atom introduced by another
281
+ # component. Integer descriptor references denote the real atom;
282
+ # virtual hydrogens have their explicit ``@H:center`` form, so the
283
+ # structural atom is the unambiguous identity authority here.
284
+ if previous is not None and previous[1] == "atom":
285
+ continue
286
+ if previous is not None and previous != target:
287
+ raise StereoIdentityError(
288
+ f"Hydrogen reference {reference} is ambiguous in layer {layer!r}."
289
+ )
290
+ references[reference] = target
291
+ return references
292
+
293
+
294
+ def descriptor_relative_form(
295
+ descriptor: StereoValue,
296
+ resolve_reference: StereoReferenceResolver,
297
+ ) -> tuple[Any, ...]:
298
+ """Canonicalize one descriptor after replacing map-dependent references."""
299
+ if isinstance(descriptor, FrameworkStereo):
300
+ support = tuple(
301
+ sorted(
302
+ (resolve_reference(atom) for atom in descriptor.support_atoms),
303
+ key=repr,
304
+ )
305
+ )
306
+ frames = []
307
+ for coupled_frame in descriptor.frames:
308
+ center = resolve_reference(coupled_frame.center)
309
+ references = tuple(
310
+ resolve_reference(reference) for reference in coupled_frame.references
311
+ )
312
+ parity = (
313
+ None
314
+ if descriptor.orientation is None
315
+ else coupled_frame.relation * descriptor.orientation
316
+ )
317
+ if parity is None:
318
+ form = (center, tuple(sorted(references, key=repr)), None)
319
+ else:
320
+ frame = (center, *references)
321
+ if parity == -1:
322
+ frame = tuple(frame[index] for index in _TETRAHEDRAL_INVERSION)
323
+ forms = tuple(
324
+ tuple(frame[index] for index in permutation)
325
+ for permutation in _TETRAHEDRAL_PERMUTATIONS
326
+ )
327
+ form = (min(forms, key=repr), 1)
328
+ frames.append(form)
329
+ return (
330
+ descriptor.descriptor_class,
331
+ support,
332
+ tuple(sorted(frames, key=repr)),
333
+ descriptor.orientation is None,
334
+ )
335
+ atoms = tuple(resolve_reference(reference) for reference in descriptor.atoms)
336
+ if descriptor.parity is None:
337
+ if isinstance(descriptor, _ATOM_TYPES):
338
+ return (
339
+ descriptor.descriptor_class,
340
+ None,
341
+ atoms[0],
342
+ tuple(sorted(atoms[1:], key=repr)),
343
+ )
344
+ left = (atoms[2], tuple(sorted(atoms[:2], key=repr)))
345
+ right = (atoms[3], tuple(sorted(atoms[4:], key=repr)))
346
+ return (
347
+ descriptor.descriptor_class,
348
+ None,
349
+ tuple(sorted((left, right), key=repr)),
350
+ )
351
+
352
+ if isinstance(descriptor, TetrahedralStereo):
353
+ permutations = _TETRAHEDRAL_PERMUTATIONS
354
+ inversion = _TETRAHEDRAL_INVERSION
355
+ else:
356
+ permutations = descriptor._PERMUTATIONS # type: ignore[attr-defined]
357
+ inversion = getattr(descriptor, "_INVERSION", None)
358
+ working = atoms
359
+ parity = descriptor.parity
360
+ if parity == -1:
361
+ if inversion is None:
362
+ raise StereoIdentityError(
363
+ f"{descriptor.descriptor_class} has no defined inversion."
364
+ )
365
+ working = tuple(working[index] for index in inversion)
366
+ parity = 1
367
+ forms = tuple(
368
+ tuple(working[index] for index in permutation) for permutation in permutations
369
+ )
370
+ return (
371
+ descriptor.descriptor_class,
372
+ parity,
373
+ min(forms, key=repr),
374
+ )
375
+
376
+
377
+ def _signature_reference_resolver(
378
+ graph: nx.Graph,
379
+ layer: str,
380
+ colours: Mapping[Hashable, str],
381
+ ) -> StereoReferenceResolver:
382
+ by_reference = _reference_nodes(graph, layer)
383
+
384
+ def resolve(reference: Reference) -> StereoToken:
385
+ if type(reference) is int:
386
+ target = by_reference.get(reference)
387
+ if target is None:
388
+ raise StereoIdentityError(
389
+ f"Descriptor reference {reference} is absent from layer {layer!r}."
390
+ )
391
+ node, kind = target
392
+ if kind == "H":
393
+ return ("virtual", "H", colours[node])
394
+ return ("atom", colours[node])
395
+ virtual = parse_virtual_reference(reference)
396
+ if virtual is None:
397
+ raise StereoIdentityError(f"Invalid virtual reference: {reference!r}.")
398
+ owner_target = by_reference.get(virtual.center)
399
+ if owner_target is None or owner_target[1] != "atom":
400
+ raise StereoIdentityError(
401
+ f"Virtual-reference owner {virtual.center} is absent from {layer!r}."
402
+ )
403
+ owner = owner_target[0]
404
+ return ("virtual", virtual.kind, colours[owner])
405
+
406
+ return resolve
407
+
408
+
409
+ def stereo_identity_form(graph: nx.Graph) -> tuple[Any, ...]:
410
+ """Return the inspectable, map-invariant stereo registry form."""
411
+ layers = stereo_registry_layers(graph)
412
+ if not layers:
413
+ return ()
414
+ colours = structural_node_colours(graph)
415
+ result = []
416
+ for layer, registry in sorted(layers.items()):
417
+ resolver = _signature_reference_resolver(graph, layer, colours)
418
+ forms = tuple(
419
+ sorted(
420
+ (
421
+ descriptor_relative_form(descriptor, resolver)
422
+ for descriptor in registry.values()
423
+ ),
424
+ key=repr,
425
+ )
426
+ )
427
+ result.append((layer, forms))
428
+ return tuple(result)
429
+
430
+
431
+ def stereo_identity_signature(graph: nx.Graph) -> str | None:
432
+ """Return a compact digest of :func:`stereo_identity_form`, if present."""
433
+ form = stereo_identity_form(graph)
434
+ return _digest(("stereo", form)) if form else None
435
+
436
+
437
+ def _mapped_reference_resolver(
438
+ graph: nx.Graph,
439
+ layer: str,
440
+ node_mapping: Mapping[Hashable, Hashable] | None = None,
441
+ ) -> StereoReferenceResolver:
442
+ by_reference = _reference_nodes(graph, layer)
443
+
444
+ def resolve(reference: Reference) -> StereoToken:
445
+ if type(reference) is int:
446
+ target = by_reference.get(reference)
447
+ if target is None:
448
+ raise StereoIdentityError(
449
+ f"Descriptor reference {reference} is absent from layer {layer!r}."
450
+ )
451
+ node, kind = target
452
+ mapped_node = node_mapping.get(node, node) if node_mapping else node
453
+ if kind == "H":
454
+ return ("virtual", "H", ("node", mapped_node))
455
+ return ("node", mapped_node)
456
+ virtual = parse_virtual_reference(reference)
457
+ if virtual is None:
458
+ raise StereoIdentityError(f"Invalid virtual reference: {reference!r}.")
459
+ owner_target = by_reference.get(virtual.center)
460
+ if owner_target is None or owner_target[1] != "atom":
461
+ raise StereoIdentityError(
462
+ f"Virtual-reference owner {virtual.center} is absent from {layer!r}."
463
+ )
464
+ owner = owner_target[0]
465
+ mapped_owner = node_mapping.get(owner, owner) if node_mapping else owner
466
+ return ("virtual", virtual.kind, ("node", mapped_owner))
467
+
468
+ return resolve
469
+
470
+
471
+ def mapped_reference_resolver(
472
+ graph: nx.Graph,
473
+ layer: str,
474
+ node_mapping: Mapping[Hashable, Hashable] | None = None,
475
+ ) -> StereoReferenceResolver:
476
+ """Resolve descriptor references to node identities under one mapping.
477
+
478
+ This is the exact counterpart of the colour-based signature resolver. It
479
+ is public so rule and mechanism consumers can compare associated metadata
480
+ under the same structural mapping as the descriptor registry.
481
+ """
482
+ return _mapped_reference_resolver(graph, layer, node_mapping)
483
+
484
+
485
+ def _descriptor_matches_policy(
486
+ query: StereoValue,
487
+ candidate: StereoValue,
488
+ query_resolver: StereoReferenceResolver,
489
+ candidate_resolver: StereoReferenceResolver,
490
+ unknown_policy: str,
491
+ ) -> bool:
492
+ """Match one descriptor under an explicit query-unknown policy."""
493
+ if type(query) is not type(candidate):
494
+ return False
495
+ query_form = descriptor_relative_form(query, query_resolver)
496
+ candidate_form = descriptor_relative_form(candidate, candidate_resolver)
497
+ if unknown_policy == "either" and query.parity is not None:
498
+ inverse_form = descriptor_relative_form(query.invert(), query_resolver)
499
+ return candidate_form in {query_form, inverse_form}
500
+ if query.parity is not None or unknown_policy == "exact":
501
+ return query_form == candidate_form
502
+ unknown_candidate = type(candidate)(
503
+ candidate.atoms,
504
+ None,
505
+ candidate.provenance,
506
+ )
507
+ return query_form == descriptor_relative_form(
508
+ unknown_candidate,
509
+ candidate_resolver,
510
+ )
511
+
512
+
513
+ def _exact_layer_matches(
514
+ queries: list[StereoValue],
515
+ candidates: list[StereoValue],
516
+ query_resolver: StereoReferenceResolver,
517
+ candidate_resolver: StereoReferenceResolver,
518
+ ) -> bool:
519
+ """Compare a registry layer as a multiset of exact relative forms."""
520
+ query_forms = Counter(
521
+ descriptor_relative_form(descriptor, query_resolver) for descriptor in queries
522
+ )
523
+ candidate_forms = Counter(
524
+ descriptor_relative_form(descriptor, candidate_resolver)
525
+ for descriptor in candidates
526
+ )
527
+ return query_forms == candidate_forms
528
+
529
+
530
+ def _query_layer_matches(
531
+ queries: list[StereoValue],
532
+ candidates: list[StereoValue],
533
+ query_resolver: StereoReferenceResolver,
534
+ candidate_resolver: StereoReferenceResolver,
535
+ unknown_policy: str,
536
+ ) -> bool:
537
+ """Find a one-to-one candidate assignment for a query registry layer."""
538
+ remaining = list(candidates)
539
+ for query in queries:
540
+ match_index = next(
541
+ (
542
+ index
543
+ for index, candidate in enumerate(remaining)
544
+ if _descriptor_matches_policy(
545
+ query,
546
+ candidate,
547
+ query_resolver,
548
+ candidate_resolver,
549
+ unknown_policy,
550
+ )
551
+ ),
552
+ None,
553
+ )
554
+ if match_index is None:
555
+ return False
556
+ remaining.pop(match_index)
557
+ return True
558
+
559
+
560
+ def mapped_stereo_registries_match(
561
+ left: nx.Graph,
562
+ right: nx.Graph,
563
+ node_mapping: Mapping[Hashable, Hashable],
564
+ *,
565
+ unknown_policy: str = "exact",
566
+ ) -> bool:
567
+ """Check exact layered stereo identity under one structural node mapping."""
568
+ if unknown_policy not in {"exact", "wildcard", "either"}:
569
+ raise ValueError("unknown_policy must be 'exact', 'wildcard', or 'either'.")
570
+
571
+ try:
572
+ left_layers = stereo_registry_layers(left)
573
+ right_layers = stereo_registry_layers(right)
574
+ if set(left_layers) != set(right_layers):
575
+ return False
576
+ for layer in left_layers:
577
+ left_resolver = _mapped_reference_resolver(left, layer, node_mapping)
578
+ right_resolver = _mapped_reference_resolver(right, layer)
579
+ queries = list(left_layers[layer].values())
580
+ candidates = list(right_layers[layer].values())
581
+ if len(queries) != len(candidates):
582
+ return False
583
+ if unknown_policy == "exact":
584
+ if not _exact_layer_matches(
585
+ queries,
586
+ candidates,
587
+ left_resolver,
588
+ right_resolver,
589
+ ):
590
+ return False
591
+ continue
592
+ if not _query_layer_matches(
593
+ queries,
594
+ candidates,
595
+ left_resolver,
596
+ right_resolver,
597
+ unknown_policy,
598
+ ):
599
+ return False
600
+ except StereoIdentityError:
601
+ return False
602
+ return True
603
+
604
+
605
+ def mapped_stereo_subgraph_registries_match(
606
+ query: nx.Graph,
607
+ candidate: nx.Graph,
608
+ node_mapping: Mapping[Hashable, Hashable],
609
+ *,
610
+ unknown_policy: str = "exact",
611
+ ) -> bool:
612
+ """Check query stereo constraints under a structural subgraph mapping.
613
+
614
+ Unlike :func:`mapped_stereo_registries_match`, candidate-only descriptors
615
+ are permitted. Every query descriptor must match one distinct candidate
616
+ descriptor after its references are transported through ``node_mapping``.
617
+ This is the stereo predicate required by injective graph morphisms where
618
+ the host may contain additional atoms, components, or stereo centers.
619
+ """
620
+ if unknown_policy not in {"exact", "wildcard", "either"}:
621
+ raise ValueError("unknown_policy must be 'exact', 'wildcard', or 'either'.")
622
+
623
+ try:
624
+ query_layers = stereo_registry_layers(query)
625
+ candidate_layers = stereo_registry_layers(candidate)
626
+ for layer, query_registry in query_layers.items():
627
+ candidate_registry = candidate_layers.get(layer)
628
+ if candidate_registry is None or len(candidate_registry) < len(
629
+ query_registry
630
+ ):
631
+ return False
632
+ query_resolver = _mapped_reference_resolver(
633
+ query,
634
+ layer,
635
+ node_mapping,
636
+ )
637
+ candidate_resolver = _mapped_reference_resolver(candidate, layer)
638
+ if not _query_layer_matches(
639
+ list(query_registry.values()),
640
+ list(candidate_registry.values()),
641
+ query_resolver,
642
+ candidate_resolver,
643
+ unknown_policy,
644
+ ):
645
+ return False
646
+ except StereoIdentityError:
647
+ return False
648
+ return True