chython 3.0.1__tar.gz → 3.0.2__tar.gz

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 (453) hide show
  1. {chython-3.0.1/chython.egg-info → chython-3.0.2}/PKG-INFO +1 -1
  2. {chython-3.0.1 → chython-3.0.2}/chython/core/_ml.pxi +77 -75
  3. {chython-3.0.1 → chython-3.0.2}/chython/core/reaction.py +25 -9
  4. {chython-3.0.1 → chython-3.0.2}/chython/core/test/modeling_view_corpus.py +2 -2
  5. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_ml_reaction_transition.py +131 -10
  6. chython-3.0.2/chython/core/test/test_modeling_view_frozen.py +139 -0
  7. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_container.py +34 -8
  8. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_enumerate.py +32 -0
  9. {chython-3.0.1 → chython-3.0.2/chython.egg-info}/PKG-INFO +1 -1
  10. {chython-3.0.1 → chython-3.0.2}/pyproject.toml +1 -1
  11. chython-3.0.1/chython/core/test/test_modeling_view_frozen.py +0 -86
  12. {chython-3.0.1 → chython-3.0.2}/LICENSE +0 -0
  13. {chython-3.0.1 → chython-3.0.2}/MANIFEST.in +0 -0
  14. {chython-3.0.1 → chython-3.0.2}/README.md +0 -0
  15. {chython-3.0.1 → chython-3.0.2}/build_inchi.py +0 -0
  16. {chython-3.0.1 → chython-3.0.2}/chython/__init__.py +0 -0
  17. {chython-3.0.1 → chython-3.0.2}/chython/_functions.py +0 -0
  18. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/__init__.py +0 -0
  19. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_abbreviations.py +0 -0
  20. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_canonicalize.py +0 -0
  21. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_counts.py +0 -0
  22. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_crippen.py +0 -0
  23. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_hydrogens.py +0 -0
  24. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_implicit.py +0 -0
  25. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_isomers.py +0 -0
  26. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_maccs.py +0 -0
  27. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_perceive.py +0 -0
  28. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_pharmacophore.py +0 -0
  29. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_protomers.py +0 -0
  30. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_qed.py +0 -0
  31. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_residues.py +0 -0
  32. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_resonance.py +0 -0
  33. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_salts.py +0 -0
  34. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_saturate.py +0 -0
  35. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_smarts.py +0 -0
  36. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_standardize.py +0 -0
  37. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_tables.py +0 -0
  38. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/_tpsa.py +0 -0
  39. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/abbreviations.tsv +0 -0
  40. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/acids.tsv +0 -0
  41. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/covalent_radii.tsv +0 -0
  42. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/crippen.tsv +0 -0
  43. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/hbond.tsv +0 -0
  44. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/maccs.tsv +0 -0
  45. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/maccs_corpus.tsv +0 -0
  46. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/pharmacophore.tsv +0 -0
  47. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/qed_alerts.tsv +0 -0
  48. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/residues.tsv +0 -0
  49. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/resonance.tsv +0 -0
  50. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/rotatable.tsv +0 -0
  51. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/salts.tsv +0 -0
  52. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/standardize_groups.tsv +0 -0
  53. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/standardize_metals.tsv +0 -0
  54. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/sybyl_types.tsv +0 -0
  55. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/tables/tpsa.tsv +0 -0
  56. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/__init__.py +0 -0
  57. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/_corpus.py +0 -0
  58. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/_oracle.py +0 -0
  59. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/gen_standardize_rules.py +0 -0
  60. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_abbreviations.py +0 -0
  61. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_acids_tsv.py +0 -0
  62. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_canonicalize.py +0 -0
  63. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_counts.py +0 -0
  64. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_covalent_radii_tsv.py +0 -0
  65. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_crippen.py +0 -0
  66. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_crippen_tsv.py +0 -0
  67. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_dependency_direction.py +0 -0
  68. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_featurizer_injection.py +0 -0
  69. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_featurizer_tables_lazy.py +0 -0
  70. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_isomers.py +0 -0
  71. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_maccs.py +0 -0
  72. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_maccs_corpus.py +0 -0
  73. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_maccs_tsv.py +0 -0
  74. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_perceive.py +0 -0
  75. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_pharmacophore.py +0 -0
  76. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_protomers.py +0 -0
  77. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_qed.py +0 -0
  78. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_qed_alerts_tsv.py +0 -0
  79. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_reaction_hydrogen_repair.py +0 -0
  80. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_reaction_passes.py +0 -0
  81. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_residues.py +0 -0
  82. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_resonance.py +0 -0
  83. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_resonance_tsv.py +0 -0
  84. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_salts.py +0 -0
  85. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_saturate.py +0 -0
  86. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_smarts.py +0 -0
  87. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_differential.py +0 -0
  88. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_groups_port.py +0 -0
  89. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_overvalent_nitrogen.py +0 -0
  90. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_rules_examples.py +0 -0
  91. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_rules_merges.py +0 -0
  92. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_standardize_rules_tsv.py +0 -0
  93. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_thiele_is_single_purpose.py +0 -0
  94. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_tpsa.py +0 -0
  95. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_tpsa_tsv.py +0 -0
  96. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_valence_report.py +0 -0
  97. {chython-3.0.1 → chython-3.0.2}/chython/chemistry/test/test_z_translation.py +0 -0
  98. {chython-3.0.1 → chython-3.0.2}/chython/core/RULES.md +0 -0
  99. {chython-3.0.1 → chython-3.0.2}/chython/core/__init__.py +0 -0
  100. {chython-3.0.1 → chython-3.0.2}/chython/core/_canonical.pxi +0 -0
  101. {chython-3.0.1 → chython-3.0.2}/chython/core/_core.pyx +0 -0
  102. {chython-3.0.1 → chython-3.0.2}/chython/core/_descriptors.pxi +0 -0
  103. {chython-3.0.1 → chython-3.0.2}/chython/core/_elements.pxi +0 -0
  104. {chython-3.0.1 → chython-3.0.2}/chython/core/_facade.py +0 -0
  105. {chython-3.0.1 → chython-3.0.2}/chython/core/_features.pxi +0 -0
  106. {chython-3.0.1 → chython-3.0.2}/chython/core/_fingerprints.pxi +0 -0
  107. {chython-3.0.1 → chython-3.0.2}/chython/core/_hydrogens.pxi +0 -0
  108. {chython-3.0.1 → chython-3.0.2}/chython/core/_inchi.pxi +0 -0
  109. {chython-3.0.1 → chython-3.0.2}/chython/core/_isomorphism.pxi +0 -0
  110. {chython-3.0.1 → chython-3.0.2}/chython/core/_kekule.pxi +0 -0
  111. {chython-3.0.1 → chython-3.0.2}/chython/core/_log.py +0 -0
  112. {chython-3.0.1 → chython-3.0.2}/chython/core/_molecule_arena.pxi +0 -0
  113. {chython-3.0.1 → chython-3.0.2}/chython/core/_molecule_container.pxi +0 -0
  114. {chython-3.0.1 → chython-3.0.2}/chython/core/_molecule_topology.pxi +0 -0
  115. {chython-3.0.1 → chython-3.0.2}/chython/core/_molecule_views.pxi +0 -0
  116. {chython-3.0.1 → chython-3.0.2}/chython/core/_morgan.pxi +0 -0
  117. {chython-3.0.1 → chython-3.0.2}/chython/core/_pach.pxi +0 -0
  118. {chython-3.0.1 → chython-3.0.2}/chython/core/_pach3.pxi +0 -0
  119. {chython-3.0.1 → chython-3.0.2}/chython/core/_query_arena.pxi +0 -0
  120. {chython-3.0.1 → chython-3.0.2}/chython/core/_query_boxes.pxi +0 -0
  121. {chython-3.0.1 → chython-3.0.2}/chython/core/_query_container.pxi +0 -0
  122. {chython-3.0.1 → chython-3.0.2}/chython/core/_query_seal.pxi +0 -0
  123. {chython-3.0.1 → chython-3.0.2}/chython/core/_reaction_passes.py +0 -0
  124. {chython-3.0.1 → chython-3.0.2}/chython/core/_rings.pxi +0 -0
  125. {chython-3.0.1 → chython-3.0.2}/chython/core/_smarts_read.pxi +0 -0
  126. {chython-3.0.1 → chython-3.0.2}/chython/core/_smiles_read.pxi +0 -0
  127. {chython-3.0.1 → chython-3.0.2}/chython/core/_smiles_write.pxi +0 -0
  128. {chython-3.0.1 → chython-3.0.2}/chython/core/_smirks_patch.pxi +0 -0
  129. {chython-3.0.1 → chython-3.0.2}/chython/core/_smirks_read.pxi +0 -0
  130. {chython-3.0.1 → chython-3.0.2}/chython/core/_sssr.pxi +0 -0
  131. {chython-3.0.1 → chython-3.0.2}/chython/core/_stereo.pxi +0 -0
  132. {chython-3.0.1 → chython-3.0.2}/chython/core/_thiele.pxi +0 -0
  133. {chython-3.0.1 → chython-3.0.2}/chython/core/_valence.pxi +0 -0
  134. {chython-3.0.1 → chython-3.0.2}/chython/core/elements.tsv +0 -0
  135. {chython-3.0.1 → chython-3.0.2}/chython/core/isotopes.tsv +0 -0
  136. {chython-3.0.1 → chython-3.0.2}/chython/core/test/__init__.py +0 -0
  137. {chython-3.0.1 → chython-3.0.2}/chython/core/test/arena_v4_corpus.bin.gz +0 -0
  138. {chython-3.0.1 → chython-3.0.2}/chython/core/test/bench_ml.py +0 -0
  139. {chython-3.0.1 → chython-3.0.2}/chython/core/test/chytorch_oracle.py +0 -0
  140. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_element_tables.py +0 -0
  141. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_modeling_view_corpus.py +0 -0
  142. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_pach3_corpus.py +0 -0
  143. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_reaction_pach_corpus.py +0 -0
  144. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_v3_fixtures.py +0 -0
  145. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_v4_fixtures.py +0 -0
  146. {chython-3.0.1 → chython-3.0.2}/chython/core/test/gen_valence_rules.py +0 -0
  147. {chython-3.0.1 → chython-3.0.2}/chython/core/test/modeling_view_corpus.json.gz +0 -0
  148. {chython-3.0.1 → chython-3.0.2}/chython/core/test/oracle.py +0 -0
  149. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach3_corpus.py +0 -0
  150. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_corpus.py +0 -0
  151. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_v0_corpus.bin.gz +0 -0
  152. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_v0_native_corpus.bin.gz +0 -0
  153. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_v2_corpus.bin.gz +0 -0
  154. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_v3_corpus.bin.gz +0 -0
  155. {chython-3.0.1 → chython-3.0.2}/chython/core/test/pach_v4_corpus.bin.gz +0 -0
  156. {chython-3.0.1 → chython-3.0.2}/chython/core/test/reaction_pach_corpus.py +0 -0
  157. {chython-3.0.1 → chython-3.0.2}/chython/core/test/reaction_pach_v2_corpus.bin.gz +0 -0
  158. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_aggregates.py +0 -0
  159. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_alternative_spellings.py +0 -0
  160. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_apply_scratch_probe.py +0 -0
  161. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_arena_f60.py +0 -0
  162. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_arena_identity.py +0 -0
  163. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_arena_v3_compat.py +0 -0
  164. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_arena_v4_compat.py +0 -0
  165. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_aromatic_storage.py +0 -0
  166. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_canonical.py +0 -0
  167. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_canonical_mirror.py +0 -0
  168. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_cip_storage.py +0 -0
  169. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_clean_isotopes_and_coordinate_bonds.py +0 -0
  170. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_clean_stereo.py +0 -0
  171. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_conformers.py +0 -0
  172. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_container_log.py +0 -0
  173. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_copy_caches.py +0 -0
  174. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_derive.py +0 -0
  175. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_descriptors.py +0 -0
  176. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_element_tables.py +0 -0
  177. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_facade.py +0 -0
  178. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_features.py +0 -0
  179. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_featurizer_injection.py +0 -0
  180. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_fingerprints.py +0 -0
  181. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_geometry.py +0 -0
  182. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_h_unknown.py +0 -0
  183. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_hydrogens.py +0 -0
  184. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_inchi.py +0 -0
  185. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_interop_injection.py +0 -0
  186. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_isomorphism.py +0 -0
  187. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_kekule.py +0 -0
  188. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_log.py +0 -0
  189. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_magic.py +0 -0
  190. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_meta.py +0 -0
  191. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_ml_encoding.py +0 -0
  192. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_ml_state_view.py +0 -0
  193. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_ml_transition_view.py +0 -0
  194. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_ml_unpack_differential.py +0 -0
  195. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_molecule.py +0 -0
  196. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_morgan.py +0 -0
  197. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_no_chython_two_imports.py +0 -0
  198. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_oracle.py +0 -0
  199. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_pach.py +0 -0
  200. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_pach3.py +0 -0
  201. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_pack.py +0 -0
  202. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_query.py +0 -0
  203. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_edit.py +0 -0
  204. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_query.py +0 -0
  205. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_semantics.py +0 -0
  206. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_serialisation.py +0 -0
  207. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_smirks.py +0 -0
  208. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_r_storage.py +0 -0
  209. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_identity.py +0 -0
  210. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_pach.py +0 -0
  211. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_passes.py +0 -0
  212. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_patch_stereo.py +0 -0
  213. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_reaction_smiles.py +0 -0
  214. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_rings.py +0 -0
  215. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_rings_c60.py +0 -0
  216. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_set_element.py +0 -0
  217. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_sgroups.py +0 -0
  218. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smarts_read.py +0 -0
  219. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_r.py +0 -0
  220. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_read.py +0 -0
  221. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_roundtrip.py +0 -0
  222. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write.py +0 -0
  223. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_aromatic.py +0 -0
  224. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_cis_trans.py +0 -0
  225. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_detached.py +0 -0
  226. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_differential.py +0 -0
  227. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_h_unknown.py +0 -0
  228. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_stereo.py +0 -0
  229. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smiles_write_sticky.py +0 -0
  230. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smirks_filter.py +0 -0
  231. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smirks_patch.py +0 -0
  232. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smirks_read.py +0 -0
  233. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smirks_report.py +0 -0
  234. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_smirks_stereo.py +0 -0
  235. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_acceptance.py +0 -0
  236. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_parity.py +0 -0
  237. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_perception.py +0 -0
  238. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_query.py +0 -0
  239. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_units.py +0 -0
  240. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_stereo_v2_differential.py +0 -0
  241. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_structure.py +0 -0
  242. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_thiele.py +0 -0
  243. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_title.py +0 -0
  244. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_topology.py +0 -0
  245. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_union_stereo.py +0 -0
  246. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_valence.py +0 -0
  247. {chython-3.0.1 → chython-3.0.2}/chython/core/test/test_view_surface.py +0 -0
  248. {chython-3.0.1 → chython-3.0.2}/chython/core/test/v3_fixtures.py +0 -0
  249. {chython-3.0.1 → chython-3.0.2}/chython/core/test/v4_fixtures.py +0 -0
  250. {chython-3.0.1 → chython-3.0.2}/chython/core/valence_rules.tsv +0 -0
  251. {chython-3.0.1 → chython-3.0.2}/chython/core/wedge.py +0 -0
  252. {chython-3.0.1 → chython-3.0.2}/chython/depict/__init__.py +0 -0
  253. {chython-3.0.1 → chython-3.0.2}/chython/depict/_config.py +0 -0
  254. {chython-3.0.1 → chython-3.0.2}/chython/depict/_hooks.py +0 -0
  255. {chython-3.0.1 → chython-3.0.2}/chython/depict/bonds.py +0 -0
  256. {chython-3.0.1 → chython-3.0.2}/chython/depict/colorbar.py +0 -0
  257. {chython-3.0.1 → chython-3.0.2}/chython/depict/colormap.py +0 -0
  258. {chython-3.0.1 → chython-3.0.2}/chython/depict/field.py +0 -0
  259. {chython-3.0.1 → chython-3.0.2}/chython/depict/figure.py +0 -0
  260. {chython-3.0.1 → chython-3.0.2}/chython/depict/label.py +0 -0
  261. {chython-3.0.1 → chython-3.0.2}/chython/depict/layout/__init__.py +0 -0
  262. {chython-3.0.1 → chython-3.0.2}/chython/depict/layout/clean2d.js +0 -0
  263. {chython-3.0.1 → chython-3.0.2}/chython/depict/layout/molecule.py +0 -0
  264. {chython-3.0.1 → chython-3.0.2}/chython/depict/layout/reaction.py +0 -0
  265. {chython-3.0.1 → chython-3.0.2}/chython/depict/metrics/__init__.py +0 -0
  266. {chython-3.0.1 → chython-3.0.2}/chython/depict/metrics/helvetica.tsv +0 -0
  267. {chython-3.0.1 → chython-3.0.2}/chython/depict/metrics/times.tsv +0 -0
  268. {chython-3.0.1 → chython-3.0.2}/chython/depict/overlay.py +0 -0
  269. {chython-3.0.1 → chython-3.0.2}/chython/depict/render/__init__.py +0 -0
  270. {chython-3.0.1 → chython-3.0.2}/chython/depict/render/svg.py +0 -0
  271. {chython-3.0.1 → chython-3.0.2}/chython/depict/scene.py +0 -0
  272. {chython-3.0.1 → chython-3.0.2}/chython/depict/style.py +0 -0
  273. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/__init__.py +0 -0
  274. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_bonds.py +0 -0
  275. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_clean2d.py +0 -0
  276. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_colorbar.py +0 -0
  277. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_colormap.py +0 -0
  278. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_field.py +0 -0
  279. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_figure.py +0 -0
  280. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_label.py +0 -0
  281. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_metrics.py +0 -0
  282. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_overlay.py +0 -0
  283. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_package.py +0 -0
  284. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_r_atom.py +0 -0
  285. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_scene.py +0 -0
  286. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_style.py +0 -0
  287. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_svg.py +0 -0
  288. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_wedge_draw.py +0 -0
  289. {chython-3.0.1 → chython-3.0.2}/chython/depict/test/test_x3dom.py +0 -0
  290. {chython-3.0.1 → chython-3.0.2}/chython/depict/wedge.py +0 -0
  291. {chython-3.0.1 → chython-3.0.2}/chython/depict/x3dom.py +0 -0
  292. {chython-3.0.1 → chython-3.0.2}/chython/exceptions.py +0 -0
  293. {chython-3.0.1 → chython-3.0.2}/chython/formats/__init__.py +0 -0
  294. {chython-3.0.1 → chython-3.0.2}/chython/formats/_text.py +0 -0
  295. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/CTFILE.md +0 -0
  296. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/__init__.py +0 -0
  297. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_ctab.py +0 -0
  298. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_errors.py +0 -0
  299. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_facade.py +0 -0
  300. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_hydrogens.py +0 -0
  301. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_rdf.py +0 -0
  302. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_rxn.py +0 -0
  303. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_sdf.py +0 -0
  304. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_sgroup.py +0 -0
  305. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_stream.py +0 -0
  306. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_tokens.py +0 -0
  307. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_v2000.py +0 -0
  308. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/_v3000.py +0 -0
  309. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/__init__.py +0 -0
  310. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/conftest.py +0 -0
  311. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_aromatic.py +0 -0
  312. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_container_log.py +0 -0
  313. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_data_labels.py +0 -0
  314. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_facade.py +0 -0
  315. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_fidelity.py +0 -0
  316. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_hydrogens.py +0 -0
  317. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_r_atom.py +0 -0
  318. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_rdf.py +0 -0
  319. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_rxn.py +0 -0
  320. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_sdf.py +0 -0
  321. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_sgroup.py +0 -0
  322. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_stream.py +0 -0
  323. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_tokens.py +0 -0
  324. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_v2000.py +0 -0
  325. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_v3000.py +0 -0
  326. {chython-3.0.1 → chython-3.0.2}/chython/formats/ctfile/test/test_wedge.py +0 -0
  327. {chython-3.0.1 → chython-3.0.2}/chython/formats/mol2.py +0 -0
  328. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/__init__.py +0 -0
  329. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/_builder.py +0 -0
  330. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/_legacy.py +0 -0
  331. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/_mmcif.py +0 -0
  332. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/_records.py +0 -0
  333. {chython-3.0.1 → chython-3.0.2}/chython/formats/pdb/_star.py +0 -0
  334. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/__init__.py +0 -0
  335. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/conftest.py +0 -0
  336. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/oracles.py +0 -0
  337. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_conformance.py +0 -0
  338. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_done_when.py +0 -0
  339. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_isolation.py +0 -0
  340. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_log_prefix.py +0 -0
  341. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_mmcif.py +0 -0
  342. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_mol2.py +0 -0
  343. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_no_review_bookkeeping.py +0 -0
  344. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_oracles.py +0 -0
  345. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_pdb.py +0 -0
  346. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_pdb_builder.py +0 -0
  347. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_perceive_agreement.py +0 -0
  348. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_read_only_facades.py +0 -0
  349. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_xyz.py +0 -0
  350. {chython-3.0.1 → chython-3.0.2}/chython/formats/test/test_xyz_builder.py +0 -0
  351. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/__init__.py +0 -0
  352. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_cml.py +0 -0
  353. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_dialect.py +0 -0
  354. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_errors.py +0 -0
  355. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_facade.py +0 -0
  356. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_mrv.py +0 -0
  357. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/_tree.py +0 -0
  358. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/__init__.py +0 -0
  359. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/conftest.py +0 -0
  360. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_cml.py +0 -0
  361. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_container_log.py +0 -0
  362. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_dialect.py +0 -0
  363. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_equivalence.py +0 -0
  364. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_facade.py +0 -0
  365. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_mrv.py +0 -0
  366. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_mrv_census.py +0 -0
  367. {chython-3.0.1 → chython-3.0.2}/chython/formats/xml/test/test_tree.py +0 -0
  368. {chython-3.0.1 → chython-3.0.2}/chython/formats/xyz.py +0 -0
  369. {chython-3.0.1 → chython-3.0.2}/chython/interop/__init__.py +0 -0
  370. {chython-3.0.1 → chython-3.0.2}/chython/interop/_cdk.py +0 -0
  371. {chython-3.0.1 → chython-3.0.2}/chython/interop/_cdpkit.py +0 -0
  372. {chython-3.0.1 → chython-3.0.2}/chython/interop/_indigo.py +0 -0
  373. {chython-3.0.1 → chython-3.0.2}/chython/interop/_iupac.py +0 -0
  374. {chython-3.0.1 → chython-3.0.2}/chython/interop/_java.py +0 -0
  375. {chython-3.0.1 → chython-3.0.2}/chython/interop/_openbabel.py +0 -0
  376. {chython-3.0.1 → chython-3.0.2}/chython/interop/_pandas.py +0 -0
  377. {chython-3.0.1 → chython-3.0.2}/chython/interop/_rdkit.py +0 -0
  378. {chython-3.0.1 → chython-3.0.2}/chython/interop/_records.py +0 -0
  379. {chython-3.0.1 → chython-3.0.2}/chython/interop/_stereo.py +0 -0
  380. {chython-3.0.1 → chython-3.0.2}/chython/interop/config.py +0 -0
  381. {chython-3.0.1 → chython-3.0.2}/chython/interop/conformers.py +0 -0
  382. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/__init__.py +0 -0
  383. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/conftest.py +0 -0
  384. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_cdk.py +0 -0
  385. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_cdpkit.py +0 -0
  386. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_config.py +0 -0
  387. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_conformers.py +0 -0
  388. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_coordinate_honesty.py +0 -0
  389. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_dispatch.py +0 -0
  390. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_indigo.py +0 -0
  391. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_iupac.py +0 -0
  392. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_log_delivery.py +0 -0
  393. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_openbabel.py +0 -0
  394. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_pandas.py +0 -0
  395. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_rdkit.py +0 -0
  396. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_stereo.py +0 -0
  397. {chython-3.0.1 → chython-3.0.2}/chython/interop/test/test_v2_oracle.py +0 -0
  398. {chython-3.0.1 → chython-3.0.2}/chython/reactions/__init__.py +0 -0
  399. {chython-3.0.1 → chython-3.0.2}/chython/reactions/_enumerate.py +0 -0
  400. {chython-3.0.1 → chython-3.0.2}/chython/reactions/_numbering.py +0 -0
  401. {chython-3.0.1 → chython-3.0.2}/chython/reactions/_reconstruct.py +0 -0
  402. {chython-3.0.1 → chython-3.0.2}/chython/reactions/_stickers.py +0 -0
  403. {chython-3.0.1 → chython-3.0.2}/chython/reactions/_tables.py +0 -0
  404. {chython-3.0.1 → chython-3.0.2}/chython/reactions/attention/__init__.py +0 -0
  405. {chython-3.0.1 → chython-3.0.2}/chython/reactions/attention/_assign.py +0 -0
  406. {chython-3.0.1 → chython-3.0.2}/chython/reactions/attention/_encode.py +0 -0
  407. {chython-3.0.1 → chython-3.0.2}/chython/reactions/attention/_session.py +0 -0
  408. {chython-3.0.1 → chython-3.0.2}/chython/reactions/tables/functional.tsv +0 -0
  409. {chython-3.0.1 → chython-3.0.2}/chython/reactions/tables/protective.tsv +0 -0
  410. {chython-3.0.1 → chython-3.0.2}/chython/reactions/tables/reactions.tsv +0 -0
  411. {chython-3.0.1 → chython-3.0.2}/chython/reactions/tables/roles.tsv +0 -0
  412. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/__init__.py +0 -0
  413. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/_frozen_ids.py +0 -0
  414. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/gen_corpus_glossary.py +0 -0
  415. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/golden_subset.smi +0 -0
  416. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_attention.py +0 -0
  417. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_attention_assign.py +0 -0
  418. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_attention_encode.py +0 -0
  419. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_attention_isolation.py +0 -0
  420. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_corpus_glossary.py +0 -0
  421. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_dependency_direction.py +0 -0
  422. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_functional.py +0 -0
  423. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_id_stability.py +0 -0
  424. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_numbering.py +0 -0
  425. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_probes.py +0 -0
  426. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_protective.py +0 -0
  427. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_reconstruct.py +0 -0
  428. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_roles.py +0 -0
  429. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_stickers.py +0 -0
  430. {chython-3.0.1 → chython-3.0.2}/chython/reactions/test/test_tables.py +0 -0
  431. {chython-3.0.1 → chython-3.0.2}/chython/test/__init__.py +0 -0
  432. {chython-3.0.1 → chython-3.0.2}/chython/test/test_code_hygiene.py +0 -0
  433. {chython-3.0.1 → chython-3.0.2}/chython/test/test_container_methods.py +0 -0
  434. {chython-3.0.1 → chython-3.0.2}/chython/test/test_doc_figures.py +0 -0
  435. {chython-3.0.1 → chython-3.0.2}/chython/test/test_doc_references.py +0 -0
  436. {chython-3.0.1 → chython-3.0.2}/chython/test/test_doc_samples.py +0 -0
  437. {chython-3.0.1 → chython-3.0.2}/chython/test/test_facade_names.py +0 -0
  438. {chython-3.0.1 → chython-3.0.2}/chython/test/test_hydrogen_parity.py +0 -0
  439. {chython-3.0.1 → chython-3.0.2}/chython/test/test_libinchi_staging.py +0 -0
  440. {chython-3.0.1 → chython-3.0.2}/chython/test/test_log_records.py +0 -0
  441. {chython-3.0.1 → chython-3.0.2}/chython/test/test_optional_numpy.py +0 -0
  442. {chython-3.0.1 → chython-3.0.2}/chython/test/test_packaging.py +0 -0
  443. {chython-3.0.1 → chython-3.0.2}/chython/test/test_performance.py +0 -0
  444. {chython-3.0.1 → chython-3.0.2}/chython/test/test_r_atom_integration.py +0 -0
  445. {chython-3.0.1 → chython-3.0.2}/chython/test/test_release_build.py +0 -0
  446. {chython-3.0.1 → chython-3.0.2}/chython/test/test_stereo_bluebook.py +0 -0
  447. {chython-3.0.1 → chython-3.0.2}/chython/test/test_v2_boundary.py +0 -0
  448. {chython-3.0.1 → chython-3.0.2}/chython.egg-info/SOURCES.txt +0 -0
  449. {chython-3.0.1 → chython-3.0.2}/chython.egg-info/dependency_links.txt +0 -0
  450. {chython-3.0.1 → chython-3.0.2}/chython.egg-info/requires.txt +0 -0
  451. {chython-3.0.1 → chython-3.0.2}/chython.egg-info/top_level.txt +0 -0
  452. {chython-3.0.1 → chython-3.0.2}/setup.cfg +0 -0
  453. {chython-3.0.1 → chython-3.0.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chython
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Library for processing molecules and reactions in python way
5
5
  Author-email: Ramil Nugmanov <nougmanoff@protonmail.com>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -683,23 +683,41 @@ def mol_transition_view(MoleculeContainer molecule not None, TensorEncoding enco
683
683
  DEF ML_NO_INDEX = 0xffffffff
684
684
 
685
685
 
686
- # `uidx` is `map_number -> union index`, valid only for touched slots. `rseen` / `pseen` mark which
687
- # side already claimed a map number. `keep` is indexed by running ordinal over all atoms of both
688
- # sides (left first, then right); the atom passes write 1 for an atom that took a union slot and 0 for
689
- # one that was unmapped or rejected; the bond passes read it for both endpoints. `head` / `link` are a
690
- # per-atom singly linked list over bond slots, keyed on the lower union index of the pair. `side`
691
- # codes: 1 = reactant-only, 2 = product-only, 3 = both. The uidx table is max map number + 1, at most
692
- # 10000 slots -- initialized slot by slot, not memset, because a three-atom record must not pay for all.
686
+ # `uidx` is `map_number -> union index`, valid only for touched slots, and it is what PAIRS the two
687
+ # sides -- an unmapped atom therefore takes a union row without an entry in it. `rseen` / `pseen` mark
688
+ # which side already claimed a map number. `urow` is indexed by running ordinal over all atoms of both
689
+ # sides (left first, then right) and holds the union row the atom took, or `ML_NO_INDEX` for a rejected
690
+ # collision; the bond passes read it for both endpoints, which is why they need no map number of their
691
+ # own. `head` / `link` are a per-atom singly linked list over bond slots, keyed on the lower union index
692
+ # of the pair. `side` codes: 1 = reactant-only, 2 = product-only, 3 = both. The uidx table is max map
693
+ # number + 1, at most 10000 slots -- initialized slot by slot, not memset, because a three-atom record
694
+ # must not pay for all.
693
695
  cdef struct ml_merge_t:
694
696
  uint32_t *uidx
695
697
  uint8_t *rseen
696
698
  uint8_t *pseen
697
699
  uint8_t *side # 1 = reactant-only, 2 = product-only, 3 = both
698
- uint8_t *keep # 1 = atom took a union slot, 0 = unmapped or collided
700
+ uint32_t *urow # union row per atom ordinal; ML_NO_INDEX = a rejected collision
699
701
  uint32_t *head
700
702
  uint32_t *link
701
703
 
702
704
 
705
+ cdef inline void _ml_union_row(ml_union_t *g, ml_merge_t *merge, uint32_t row, atom_t *at,
706
+ uint32_t mn, uint8_t side) noexcept nogil:
707
+ """One union row from one atom, on the side that atom came from.
708
+
709
+ `h_after` is written equal to `h_before`, which a paired product atom then overwrites. An atom on
710
+ one side only keeps it -- the convention `ReactionModelingView.states` states -- and an unmapped
711
+ atom is one of those, the record having said nothing that would pair it.
712
+ """
713
+ merge.side[row] = side
714
+ merge.head[row] = ML_NO_INDEX
715
+ g.element[row] = at.element
716
+ g.h_before[row] = at.hydrogens & 0x0f
717
+ g.h_after[row] = at.hydrogens & 0x0f
718
+ g.map_number[row] = <uint16_t> mn
719
+
720
+
703
721
  cdef void _ml_union_degrees(ml_union_t *union_graph, ml_merge_t *merge) noexcept nogil:
704
722
  """Per-side heavy degree over the union, by the rule `ReactionModelingView.states` states.
705
723
 
@@ -744,9 +762,16 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
744
762
  AGENTS ARE EXCLUDED BY NOT BEING PASSED. The caller hands over the two sides it wants unioned, so
745
763
  there is no side-kind test inside the kernel to keep in step with the container's notion of one.
746
764
 
747
- A BADLY MAPPED RECORD IS REPORTED. An unmapped atom is counted and left out; a map number claimed
748
- twice on one side is listed in `collisions` and the first claim keeps the slot. Refusals live at
749
- the answer boundary and this is not one.
765
+ AN UNMAPPED ATOM IS PLACED ON THE SIDE IT CAME FROM, as reactant-only or product-only, and counted
766
+ in `unmapped`. Left out, it takes the degree of every neighbour that stayed down with it: an aryl
767
+ bromide whose bromine carries no number reads two heavy neighbours before the coupling and three
768
+ after, so every aryl halide of one ring gives the same transition state. The price is that an atom
769
+ the record leaves bare on BOTH sides is two rows rather than one, nothing in the record pairing
770
+ them, and `unmapped` is what a consumer that cannot accept that reads.
771
+
772
+ A BADLY MAPPED RECORD IS REPORTED. A map number claimed twice on one side is listed in `collisions`
773
+ and the first claim keeps the row; the second contributes nothing, since a union cannot hold two
774
+ atoms at one key. Refusals live at the answer boundary and this is not one.
750
775
  """
751
776
  require_numpy()
752
777
  if encoding is None:
@@ -787,7 +812,7 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
787
812
  + align8(<size_t> slots) # rseen
788
813
  + align8(<size_t> slots) # pseen
789
814
  + align8(<size_t> cap_atoms) # side
790
- + align8(<size_t> cap_atoms) # keep
815
+ + align8(<size_t> cap_atoms * sizeof(uint32_t)) # urow
791
816
  + align8(<size_t> cap_atoms * sizeof(uint32_t)) # head
792
817
  + align8(<size_t> cap_bonds * sizeof(uint32_t)) # link
793
818
  + <size_t> 4 * cap_atoms # element + h_before + n_before + h_after
@@ -806,7 +831,7 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
806
831
  merge.rseen = <uint8_t *> cursor; cursor += align8(<size_t> slots)
807
832
  merge.pseen = <uint8_t *> cursor; cursor += align8(<size_t> slots)
808
833
  merge.side = <uint8_t *> cursor; cursor += align8(<size_t> cap_atoms)
809
- merge.keep = <uint8_t *> cursor; cursor += align8(<size_t> cap_atoms)
834
+ merge.urow = <uint32_t *> cursor; cursor += align8(<size_t> cap_atoms * sizeof(uint32_t))
810
835
  merge.head = <uint32_t *> cursor; cursor += align8(<size_t> cap_atoms * sizeof(uint32_t))
811
836
  merge.link = <uint32_t *> cursor; cursor += align8(<size_t> cap_bonds * sizeof(uint32_t))
812
837
  union_graph.element = <uint8_t *> cursor; cursor += cap_atoms
@@ -826,7 +851,7 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
826
851
  cdef uint32_t n_union = 0, n_bonds = 0
827
852
  cdef uint32_t unmapped_r = 0, unmapped_p = 0
828
853
  cdef list collisions_r = [], collisions_p = []
829
- cdef uint32_t k, mn, mm, n, m, e, base, right_base
854
+ cdef uint32_t k, mn, n, m, e, base, right_base
830
855
  cdef uint32_t edge_to
831
856
  cdef uint8_t order
832
857
  try:
@@ -853,26 +878,20 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
853
878
  for i in range(n_mol):
854
879
  at = atoms + i
855
880
  mn = at.map_number
856
- if not mn:
881
+ if mn:
882
+ if merge.rseen[mn]:
883
+ collisions_r.append(mn)
884
+ merge.urow[base + i] = ML_NO_INDEX
885
+ continue
886
+ merge.rseen[mn] = 1
887
+ merge.uidx[mn] = n_union
888
+ else:
857
889
  unmapped_r += 1
858
- merge.keep[base + i] = 0
859
- continue
860
- if merge.rseen[mn]:
861
- collisions_r.append(mn)
862
- merge.keep[base + i] = 0
863
- continue
864
- merge.rseen[mn] = 1
865
- merge.uidx[mn] = n_union
866
- merge.side[n_union] = 1
867
- merge.head[n_union] = ML_NO_INDEX
868
- merge.keep[base + i] = 1
869
- union_graph.element[n_union] = at.element
870
- union_graph.h_before[n_union] = at.hydrogens & 0x0f
871
- union_graph.h_after[n_union] = at.hydrogens & 0x0f
872
- union_graph.map_number[n_union] = <uint16_t> mn
890
+ _ml_union_row(&union_graph, &merge, n_union, at, mn, 1)
891
+ merge.urow[base + i] = n_union
873
892
  n_union += 1
874
893
  base += n_mol
875
- right_base = base # starting ordinal for product atoms in the keep array
894
+ right_base = base # starting ordinal for product atoms in the urow array
876
895
 
877
896
  # product atoms: a known map number reads its after state onto the existing row, a new one
878
897
  # appends -- which is what keeps a component contiguous in the union order
@@ -883,54 +902,42 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
883
902
  for i in range(n_mol):
884
903
  at = atoms + i
885
904
  mn = at.map_number
886
- if not mn:
887
- unmapped_p += 1
888
- merge.keep[base + i] = 0
889
- continue
890
- if merge.pseen[mn]:
891
- collisions_p.append(mn)
892
- merge.keep[base + i] = 0
893
- continue
894
- merge.pseen[mn] = 1
895
- if merge.rseen[mn]:
896
- n = merge.uidx[mn]
897
- merge.side[n] = 3
898
- union_graph.h_after[n] = at.hydrogens & 0x0f
899
- merge.keep[base + i] = 1
900
- else:
905
+ if mn:
906
+ if merge.pseen[mn]:
907
+ collisions_p.append(mn)
908
+ merge.urow[base + i] = ML_NO_INDEX
909
+ continue
910
+ merge.pseen[mn] = 1
911
+ if merge.rseen[mn]:
912
+ n = merge.uidx[mn]
913
+ merge.side[n] = 3
914
+ union_graph.h_after[n] = at.hydrogens & 0x0f
915
+ merge.urow[base + i] = n
916
+ continue
901
917
  merge.uidx[mn] = n_union
902
- merge.side[n_union] = 2
903
- merge.head[n_union] = ML_NO_INDEX
904
- merge.keep[base + i] = 1
905
- union_graph.element[n_union] = at.element
906
- union_graph.h_before[n_union] = at.hydrogens & 0x0f
907
- union_graph.h_after[n_union] = at.hydrogens & 0x0f
908
- union_graph.map_number[n_union] = <uint16_t> mn
909
- n_union += 1
918
+ else:
919
+ unmapped_p += 1
920
+ _ml_union_row(&union_graph, &merge, n_union, at, mn, 2)
921
+ merge.urow[base + i] = n_union
922
+ n_union += 1
910
923
  base += n_mol
911
924
 
912
- # reactant bonds, both endpoints mapped and kept: appended in reactant order, `n < m`.
913
- # The `n >= m` guard also stops a bond between two atoms sharing one map number from
914
- # becoming a self-loop -- that is load-bearing, not incidental.
925
+ # reactant bonds, both endpoints holding a union row: appended in reactant order, `n < m`, so
926
+ # the `n >= m` guard is what visits each bond once.
915
927
  base = 0
916
928
  for mol in left:
917
929
  structure = mol._structure
918
- atoms = structure.atoms()
919
930
  ptr = csr_ptr(structure)
920
931
  edges = csr_edges(structure)
921
932
  n_mol = structure.header.atom_count
922
933
  for i in range(n_mol):
923
- if not merge.keep[base + i]:
934
+ n = merge.urow[base + i]
935
+ if n == ML_NO_INDEX:
924
936
  continue
925
- mn = atoms[i].map_number
926
- n = merge.uidx[mn]
927
937
  for k in range(ptr[i], ptr[i + 1]):
928
938
  edge_to = edges[k].to
929
- if not merge.keep[base + edge_to]:
930
- continue
931
- mm = atoms[edge_to].map_number
932
- m = merge.uidx[mm]
933
- if n >= m:
939
+ m = merge.urow[base + edge_to]
940
+ if m == ML_NO_INDEX or n >= m:
934
941
  continue
935
942
  union_graph.bond_i[n_bonds] = n
936
943
  union_graph.bond_j[n_bonds] = m
@@ -945,22 +952,17 @@ def reaction_transition_view(reactants, products, TensorEncoding encoding=None):
945
952
  base = right_base
946
953
  for mol in right:
947
954
  structure = mol._structure
948
- atoms = structure.atoms()
949
955
  ptr = csr_ptr(structure)
950
956
  edges = csr_edges(structure)
951
957
  n_mol = structure.header.atom_count
952
958
  for i in range(n_mol):
953
- if not merge.keep[base + i]:
959
+ n = merge.urow[base + i]
960
+ if n == ML_NO_INDEX:
954
961
  continue
955
- mn = atoms[i].map_number
956
- n = merge.uidx[mn]
957
962
  for k in range(ptr[i], ptr[i + 1]):
958
963
  edge_to = edges[k].to
959
- if not merge.keep[base + edge_to]:
960
- continue
961
- mm = atoms[edge_to].map_number
962
- m = merge.uidx[mm]
963
- if n >= m:
964
+ m = merge.urow[base + edge_to]
965
+ if m == ML_NO_INDEX or n >= m:
964
966
  continue
965
967
  order = edges[k].order
966
968
  e = merge.head[n]
@@ -202,8 +202,8 @@ class ReactionModelingView:
202
202
  number is the only thing that identifies "the same atom" across the arrow -- keying by atom number
203
203
  works only where a reader makes the two coincide.
204
204
 
205
- IT REPORTS WHAT IT COULD NOT PLACE instead of producing an undefined union. Calling unmapped
206
- atoms and colliding map numbers "undefined results" means, for a training set, silent corruption at
205
+ IT REPORTS WHAT THE RECORD DID NOT STATE instead of leaving it undefined. Calling unmapped atoms
206
+ and colliding map numbers "undefined results" means, for a training set, silent corruption at
207
207
  whatever rate the corpus happens to contain. Here they land in :attr:`unmapped` and
208
208
  :attr:`collisions`, both empty for a well-mapped reaction, so a pipeline can decide -- and a
209
209
  refusal, if one is wanted, happens at the pipeline's boundary rather than inside a container that
@@ -219,7 +219,11 @@ class ReactionModelingView:
219
219
 
220
220
  @property
221
221
  def states(self) -> dict[int, tuple]:
222
- """`{map_number: (element, h_before, n_before, h_after, n_after)}`.
222
+ """`{map_number: (element, h_before, n_before, h_after, n_after)}`, in union order.
223
+
224
+ A NEGATIVE KEY IS AN ATOM THE RECORD DOES NOT NUMBER, allocated `-1` onwards in that order. It
225
+ is placed like any other atom, as reactant-only or product-only; the negative key says the
226
+ pairing is this view's reading of an absence rather than something the record stated.
223
227
 
224
228
  `element` is the atomic number. `h_*` are IMPLICIT hydrogen counts per side and `n_*` are
225
229
  heavy-atom neighbour counts per side -- the per-side hydrogen count being the thing the
@@ -253,9 +257,11 @@ class ReactionModelingView:
253
257
  def unmapped(self) -> dict[str, int]:
254
258
  """How many atoms carried no map number, per side: `{'reactants': n, 'products': n}`.
255
259
 
256
- Not an error and not silently dropped. An unmapped atom cannot be placed in a union keyed by
257
- map number, so it is counted and left out, and a consumer that requires a fully mapped
258
- reaction checks this rather than discovering the hole in its loss curve.
260
+ Not an error and not a refusal. Each is placed on the side it came from and keyed negatively in
261
+ :attr:`states`, so no neighbour of one loses a degree; what the count is for is the atom the
262
+ record leaves bare on BOTH sides, which becomes two rows because nothing pairs them. A consumer
263
+ that requires a fully mapped reaction checks this rather than discovering the hole in its loss
264
+ curve.
259
265
  """
260
266
  return self._unmapped
261
267
 
@@ -577,18 +583,28 @@ class ReactionContainer:
577
583
  that is also a drawing. Stated rather than assumed, because it is the kind of choice a model
578
584
  silently trains around.
579
585
 
586
+ AN UNMAPPED ATOM IS KEYED NEGATIVELY, `-1` onwards in union order, and still counted in
587
+ `unmapped`. It holds a union row like any other atom -- on the side it came from -- so the key
588
+ is only the identity handle a dict needs: 0 would put every such atom on one entry, and a
589
+ positive one would be indistinguishable from a number the record stated.
590
+
580
591
  A COLLIDING MAP NUMBER KEEPS ITS FIRST CLAIM. The second atom to claim a number is counted in
581
592
  `collisions` and contributes no state and no bond; a union cannot hold two atoms at one key.
582
593
  """
583
594
  view = reaction_transition_view(self._reactants, self._products,
584
595
  TensorEncoding(unknown_h=H_UNKNOWN))
585
- map_numbers = view.map_numbers.tolist()
596
+ keys, bare = [], 0
597
+ for mn in view.map_numbers.tolist():
598
+ if not mn:
599
+ bare -= 1
600
+ mn = bare
601
+ keys.append(mn)
586
602
  states = {mn: (int(view.elements[i]), int(view.h_before[i]), int(view.n_before[i]),
587
603
  int(view.h_after[i]), int(view.n_after[i]))
588
- for i, mn in enumerate(map_numbers)}
604
+ for i, mn in enumerate(keys)}
589
605
  union = {}
590
606
  for (i, j), before, after in zip(view.bonds.tolist(), view.bond_before, view.bond_after):
591
- a, b = map_numbers[i], map_numbers[j]
607
+ a, b = keys[i], keys[j]
592
608
  union[(a, b) if a < b else (b, a)] = (int(before), int(after))
593
609
  return ReactionModelingView(states, union, view.unmapped, view.collisions)
594
610
 
@@ -60,7 +60,7 @@ SMILES = {
60
60
  # the mirror: 5..8 exist only on the product side
61
61
  'tert_butyl_esterification': '[CH3:1][C:2](=[O:3])[OH:9]'
62
62
  '>>[CH3:1][C:2](=[O:3])[O:4][C:5]([CH3:6])([CH3:7])[CH3:8].[OH2:9]',
63
- # one unmapped atom on each side, counted and left out of the union
63
+ # two unmapped atoms on each side, counted and placed on the side they came from
64
64
  'esterification_partly_mapped': '[CH3:1][C:2](=[O:3])[OH:4].CO'
65
65
  '>>[CH3:1][C:2](=[O:3])[O:4]C.O',
66
66
  # two atoms sharing map number 1 on the reactant side: the union merges them and says so
@@ -74,7 +74,7 @@ SMILES = {
74
74
  # a salt on the product side: two molecules, one of them a single mapped ion
75
75
  'saponification': '[CH3:1][C:2](=[O:3])[O:4][CH3:5].[OH-:6].[Na+:7]'
76
76
  '>>[CH3:1][C:2](=[O:3])[O-:4].[Na+:7].[CH3:5][OH:6]',
77
- # no mapping at all: every atom is unmapped, the union is empty, and that is a record
77
+ # no mapping at all: every atom is on one side only, so nothing is conserved, and that is a record
78
78
  'unmapped_hydrogenation': 'C=C.[H][H]>>CC',
79
79
  # an empty product side, which is a record and not an error
80
80
  'empty_products': '[CH3:1][CH3:2]>>',
@@ -40,6 +40,13 @@ def _bonds_by_map(view):
40
40
  for (i, j), b, a in zip(view.bonds.tolist(), view.bond_before, view.bond_after)}
41
41
 
42
42
 
43
+ def _columns(view):
44
+ """Every column but `map_numbers`: what the record states, apart from how it numbers it."""
45
+ return (view.elements.tolist(), view.h_before.tolist(), view.h_after.tolist(),
46
+ view.n_before.tolist(), view.n_after.tolist(), view.bonds.tolist(),
47
+ view.bond_before.tolist(), view.bond_after.tolist())
48
+
49
+
43
50
  def test_a_fully_mapped_substitution_reports_both_sides_of_every_atom():
44
51
  view = read_reaction_smiles('[CH3:1][Br:2].[OH-:3]>>[CH3:1][OH:3].[Br-:2]').transition_view()
45
52
  assert _by_map(view) == {
@@ -112,10 +119,36 @@ def test_an_agent_contributes_no_atom_and_no_bond():
112
119
  assert sorted(view.map_numbers.tolist()) == [1, 2, 3, 4, 5, 6]
113
120
 
114
121
 
115
- def test_an_unmapped_atom_is_counted_and_left_out_of_the_union():
122
+ def test_an_unmapped_atom_is_counted_and_placed_on_the_side_it_came_from():
123
+ """A record that names one atom still has a whole transition state.
124
+
125
+ `[CH3:1]Br>>[CH3:1]O` states the carbon and nothing else. The bromine is reactant-only and the
126
+ oxygen product-only, so the C-Br bond breaks, the C-O bond forms, and the carbon counts one heavy
127
+ neighbour on each side rather than none. `map_numbers` carries 0 for a row the record does not
128
+ number: the column is the record's own numbering and this view invents no entry in it.
129
+ """
130
+ view = read_reaction_smiles('[CH3:1]Br>>[CH3:1]O').transition_view()
131
+ assert view.map_numbers.tolist() == [1, 0, 0]
132
+ assert view.elements.tolist() == [6, 35, 8]
133
+ assert view.n_before.tolist() == [1, 1, 0]
134
+ assert view.n_after.tolist() == [1, 0, 1]
135
+ assert view.bonds.tolist() == [[0, 1], [0, 2]]
136
+ assert view.bond_before.tolist() == [1, 0]
137
+ assert view.bond_after.tolist() == [0, 1]
138
+ assert view.unmapped == {'reactants': 1, 'products': 1}
139
+
140
+
141
+ def test_an_unmapped_atom_on_both_sides_is_two_rows_and_not_one():
142
+ """The price of placing them, and the reason `unmapped` is still reported.
143
+
144
+ Methanol's carbon and oxygen are unmapped on both sides of this esterification. Nothing in the
145
+ record says the left pair and the right pair are the same two atoms, so the union holds four rows:
146
+ the reactant methanol leaves and the product methyl arrives. A consumer that cannot accept that
147
+ reads `unmapped` and refuses the record.
148
+ """
116
149
  view = read_reaction_smiles('[CH3:1][C:2](=[O:3])[OH:4].CO'
117
150
  '>>[CH3:1][C:2](=[O:3])[O:4]C.O').transition_view()
118
- assert sorted(view.map_numbers.tolist()) == [1, 2, 3, 4]
151
+ assert view.map_numbers.tolist() == [1, 2, 3, 4, 0, 0, 0, 0]
119
152
  assert view.unmapped == {'reactants': 2, 'products': 2}
120
153
 
121
154
 
@@ -125,21 +158,109 @@ def test_a_colliding_map_number_is_reported_and_not_refused():
125
158
  assert view.map_numbers.tolist() == [1, 2, 3]
126
159
 
127
160
 
128
- def test_a_bond_whose_endpoint_is_unmapped_is_not_a_union_bond():
129
- """Half a bond cannot be placed in the union, and inventing an endpoint would be worse."""
161
+ def test_a_bond_to_an_unmapped_atom_is_a_union_bond():
162
+ """Both endpoints have a row, so the bond has one: the union is over atoms, not over numbers."""
130
163
  view = read_reaction_smiles('[CH3:1]CO>>[CH3:1]C=O').transition_view()
131
- assert view.bonds.shape == (0, 2)
132
- assert view.map_numbers.tolist() == [1]
164
+ assert view.map_numbers.tolist() == [1, 0, 0, 0, 0]
165
+ assert view.bonds.tolist() == [[0, 1], [1, 2], [0, 3], [3, 4]]
166
+ assert view.bond_before.tolist() == [1, 1, 0, 0]
167
+ assert view.bond_after.tolist() == [0, 0, 1, 2]
168
+
133
169
 
170
+ def test_a_reaction_with_no_mapping_at_all_is_every_atom_on_one_side():
171
+ """No number anywhere is a record, and the union it gives is the two sides side by side.
134
172
 
135
- def test_a_reaction_with_no_mapping_at_all_gives_an_empty_union():
173
+ Nothing is conserved, because nothing in the record says anything is. `unmapped` counts every
174
+ atom, which is the signal a pipeline refuses on.
175
+ """
136
176
  view = read_reaction_smiles('C=C.[H][H]>>CC').transition_view()
137
- assert view.elements.shape == (0,)
138
- assert view.bonds.shape == (0, 2)
139
- assert view.distances.shape == (0, 0)
177
+ assert view.map_numbers.tolist() == [0] * 6
178
+ assert view.elements.tolist() == [6, 6, 1, 1, 6, 6]
179
+ assert view.bond_before.tolist() == [2, 1, 0]
180
+ assert view.bond_after.tolist() == [0, 0, 1]
140
181
  assert view.unmapped == {'reactants': 4, 'products': 2}
141
182
 
142
183
 
184
+ def test_a_changing_atom_moves_its_hydrogens_and_its_bond_order():
185
+ """`C-OH >> C=O`, the change that needs no atom to leave or arrive.
186
+
187
+ Both atoms of the reaction centre lose a hydrogen and the bond between them goes single to double.
188
+ No degree moves and `unmapped` is zero: a change is a statement about one atom on two sides, so it
189
+ takes a map number to state at all.
190
+ """
191
+ view = read_reaction_smiles('[CH3:1][CH2:2][OH:3]>>[CH3:1][CH:2]=[O:3]').transition_view()
192
+ assert view.h_before.tolist() == [3, 2, 1]
193
+ assert view.h_after.tolist() == [3, 1, 0]
194
+ assert view.n_before.tolist() == [1, 2, 1]
195
+ assert view.n_after.tolist() == [1, 2, 1], 'nothing left and nothing arrived'
196
+ assert _bonds_by_map(view) == {(1, 2): (1, 1), (2, 3): (1, 2)}
197
+ assert view.unmapped == {'reactants': 0, 'products': 0}
198
+
199
+
200
+ def test_a_leaving_atom_reads_the_same_numbered_or_not():
201
+ """Being on one side only and being unnumbered are two different facts about an atom.
202
+
203
+ The bromine is reactant-only either way; the number changes its key and nothing else. `unmapped`
204
+ is the only column that separates the two records, which is what makes it the signal to refuse on.
205
+ """
206
+ numbered = read_reaction_smiles('[CH3:1][Br:2]>>[CH4:1]').transition_view()
207
+ bare = read_reaction_smiles('[CH3:1]Br>>[CH4:1]').transition_view()
208
+ assert _columns(numbered) == _columns(bare)
209
+ assert numbered.map_numbers.tolist() == [1, 2]
210
+ assert bare.map_numbers.tolist() == [1, 0]
211
+ assert numbered.unmapped == {'reactants': 0, 'products': 0}
212
+ assert bare.unmapped == {'reactants': 1, 'products': 0}
213
+ assert bare.n_before.tolist() == [1, 1], 'the carbon has the bromine before'
214
+ assert bare.n_after.tolist() == [0, 0], 'and the bromine left alone, so it counts nobody after'
215
+ assert bare.bond_before.tolist() == [1] and bare.bond_after.tolist() == [0]
216
+
217
+
218
+ def test_an_arriving_atom_reads_the_same_numbered_or_not():
219
+ """The mirror: a product-only atom, numbered or not, is one row whose bonds read `(0, order)`."""
220
+ numbered = read_reaction_smiles('[CH4:1]>>[CH3:1][Br:2]').transition_view()
221
+ bare = read_reaction_smiles('[CH4:1]>>[CH3:1]Br').transition_view()
222
+ assert _columns(numbered) == _columns(bare)
223
+ assert bare.map_numbers.tolist() == [1, 0]
224
+ assert bare.unmapped == {'reactants': 0, 'products': 1}
225
+ assert bare.h_before.tolist() == [4, 0] and bare.h_after.tolist() == [3, 0]
226
+ assert bare.n_before.tolist() == [0, 0] and bare.n_after.tolist() == [1, 1]
227
+ assert bare.bond_before.tolist() == [0] and bare.bond_after.tolist() == [1]
228
+
229
+
230
+ def test_one_record_that_leaves_arrives_and_changes_at_once():
231
+ """Every branch in one union: two changing atoms, a leaving one, an arriving one, four moved bonds.
232
+
233
+ Maps 2 and 3 are the `C-OH >> C=O` pair. Map 4 (bromine) is reactant-only. Map 6 (water oxygen)
234
+ is product-only. Map 5 is on both sides and loses its only neighbour, so it gains a hydrogen and
235
+ drops to a degree of 0 -- a paired atom reads the departure of an unpaired one.
236
+ """
237
+ view = read_reaction_smiles('[CH3:1][CH2:2][OH:3].[Br:4][CH3:5]'
238
+ '>>[CH3:1][CH:2]=[O:3].[CH4:5].[OH2:6]').transition_view()
239
+ assert view.map_numbers.tolist() == [1, 2, 3, 4, 5, 6]
240
+ assert view.elements.tolist() == [6, 6, 8, 35, 6, 8]
241
+ assert view.h_before.tolist() == [3, 2, 1, 0, 3, 2]
242
+ assert view.h_after.tolist() == [3, 1, 0, 0, 4, 2]
243
+ assert view.n_before.tolist() == [1, 2, 1, 1, 1, 0]
244
+ assert view.n_after.tolist() == [1, 2, 1, 0, 0, 0]
245
+ assert _bonds_by_map(view) == {(1, 2): (1, 1), (2, 3): (1, 2), (4, 5): (1, 0)}
246
+ assert view.unmapped == {'reactants': 0, 'products': 0}
247
+
248
+
249
+ def test_the_same_record_reads_the_same_where_it_numbers_nothing():
250
+ """The record above with its bromine and its water unnumbered: one union, two keyings.
251
+
252
+ The leaving atom and the arriving one lose their numbers, and every column but `map_numbers` and
253
+ `unmapped` is unchanged -- including the degree of map 5, which the bromine's row is what preserves.
254
+ """
255
+ numbered = read_reaction_smiles('[CH3:1][CH2:2][OH:3].[Br:4][CH3:5]'
256
+ '>>[CH3:1][CH:2]=[O:3].[CH4:5].[OH2:6]').transition_view()
257
+ bare = read_reaction_smiles('[CH3:1][CH2:2][OH:3].Br[CH3:5]'
258
+ '>>[CH3:1][CH:2]=[O:3].[CH4:5].O').transition_view()
259
+ assert _columns(numbered) == _columns(bare)
260
+ assert bare.map_numbers.tolist() == [1, 2, 3, 0, 5, 0]
261
+ assert bare.unmapped == {'reactants': 1, 'products': 1}
262
+
263
+
143
264
  def test_an_empty_product_side_is_a_record():
144
265
  """Both atoms are reactant-only; they leave together, so each counts the other after."""
145
266
  view = read_reaction_smiles('[CH3:1][CH3:2]>>').transition_view()