chython 3.3.1__tar.gz → 3.3.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.
- {chython-3.3.1/chython.egg-info → chython-3.3.2}/PKG-INFO +1 -1
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/_enumerate.py +68 -13
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/_reconstruct.py +136 -39
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_reconstruct.py +118 -0
- {chython-3.3.1 → chython-3.3.2/chython.egg-info}/PKG-INFO +1 -1
- {chython-3.3.1 → chython-3.3.2}/pyproject.toml +1 -1
- {chython-3.3.1 → chython-3.3.2}/LICENSE +0 -0
- {chython-3.3.1 → chython-3.3.2}/MANIFEST.in +0 -0
- {chython-3.3.1 → chython-3.3.2}/README.md +0 -0
- {chython-3.3.1 → chython-3.3.2}/build_inchi.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/_functions.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_abbreviations.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_canonicalize.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_counts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_crippen.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_hydrogens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_implicit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_isomers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_kekule_form.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_maccs.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_organometallics.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_perceive.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_pharmacophore.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_protomers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_qed.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_residues.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_resonance.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_salts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_saturate.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_smarts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_standardize.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_tables.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/_tpsa.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/abbreviations.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/acids.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/covalent_radii.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/crippen.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/hbond.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/maccs.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/maccs_corpus.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/pharmacophore.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/qed_alerts.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/residues.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/resonance.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/rotatable.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/salts.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/standardize_groups.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/standardize_metals.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/sybyl_types.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/tables/tpsa.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/_oracle.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/gen_standardize_rules.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_abbreviations.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_acids_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_canonicalize.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_counts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_covalent_radii_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_crippen.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_crippen_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_dependency_direction.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_featurizer_injection.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_featurizer_tables_lazy.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_isomers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_kekule_form.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_maccs.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_maccs_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_maccs_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_organometallics.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_perceive.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_pharmacophore.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_protomers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_qed.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_qed_alerts_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_reaction_hydrogen_repair.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_reaction_passes.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_residues.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_resonance.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_resonance_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_salts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_saturate.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_smarts.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_differential.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_groups_port.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_overvalent_nitrogen.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_rules_examples.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_rules_merges.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_standardize_rules_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_thiele_is_single_purpose.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_tpsa.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_tpsa_tsv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_valence_report.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/chemistry/test/test_z_translation.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/RULES.md +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_canonical.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_cip.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_core.pyx +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_descriptors.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_elements.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_features.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_fingerprints.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_hydrogens.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_inchi.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_isomorphism.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_kekule.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_log.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_ml.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_molecule_arena.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_molecule_container.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_molecule_topology.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_molecule_views.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_morgan.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_pach.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_pach3.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_query_arena.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_query_boxes.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_query_container.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_query_seal.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_reaction_passes.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_rings.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_smarts_read.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_smiles_read.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_smiles_write.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_smirks_patch.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_smirks_read.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_sssr.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_stereo.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_thiele.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/_valence.pxi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/elements.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/isotopes.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/reaction.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/arena_v4_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/bench_ml.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/chytorch_oracle.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_element_tables.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_modeling_view_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_pach3_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_reaction_pach_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_v3_fixtures.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_v4_fixtures.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/gen_valence_rules.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/modeling_view_corpus.json.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/modeling_view_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/oracle.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach3_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_bond_group_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_v0_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_v0_native_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_v2_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_v3_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/pach_v4_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/reaction_pach_corpus.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/reaction_pach_v2_corpus.bin.gz +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_aggregates.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_alternative_spellings.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_apply_scratch_probe.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_arena_f60.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_arena_identity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_arena_v3_compat.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_arena_v4_compat.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_aromatic_storage.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_canonical.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_canonical_mirror.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_cip_assign.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_cip_cases.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_cip_digraph.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_cip_ranking.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_cip_storage.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_clean_isotopes_and_coordinate_bonds.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_clean_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_conformers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_container_log.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_copy_caches.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_derive.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_descriptors.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_element_tables.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_enrich.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_features.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_featurizer_injection.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_fingerprints.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_geometry.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_h_unknown.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_hydrogens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_inchi.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_interop_injection.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_isomorphism.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_kekule.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_log.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_magic.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_meta.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_ml_encoding.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_ml_reaction_transition.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_ml_state_view.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_ml_transition_view.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_ml_unpack_differential.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_modeling_view_frozen.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_molecule.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_morgan.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_no_chython_two_imports.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_oracle.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_pach.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_pach3.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_pack.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_query.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_edit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_query.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_semantics.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_serialisation.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_smirks.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_r_storage.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_container.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_identity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_pach.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_passes.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_patch_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_reaction_smiles.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_rings.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_rings_c60.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_set_element.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_sgroups.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smarts_read.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_r.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_read.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_roundtrip.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_aromatic.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_cis_trans.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_detached.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_differential.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_h_unknown.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smiles_write_sticky.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smirks_filter.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smirks_patch.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smirks_read.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smirks_report.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_smirks_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_acceptance.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_anchor_identity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_group_storage.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_owners.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_parity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_perception.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_query.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_units.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_stereo_v2_differential.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_structure.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_thiele.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_title.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_topology.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_union_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_valence.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/test_view_surface.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/v3_fixtures.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/test/v4_fixtures.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/valence_rules.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/core/wedge.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/_config.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/_hooks.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/bonds.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/colorbar.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/colormap.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/field.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/figure.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/label.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/layout/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/layout/clean2d.js +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/layout/molecule.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/layout/reaction.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/metrics/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/metrics/helvetica.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/metrics/times.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/overlay.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/render/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/render/svg.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/scene.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/style.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_bonds.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_clean2d.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_colorbar.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_colormap.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_field.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_figure.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_label.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_metrics.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_overlay.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_package.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_r_atom.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_scene.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_style.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_svg.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_wedge_draw.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/test/test_x3dom.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/wedge.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/depict/x3dom.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/exceptions.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/_text.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/CTFILE.md +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_ctab.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_errors.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_hydrogens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_rdf.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_rxn.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_sdf.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_sgroup.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_stream.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_tokens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_v2000.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/_v3000.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/conftest.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_aromatic.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_container_log.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_data_labels.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_fidelity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_hydrogens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_r_atom.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_rdf.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_rxn.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_sdf.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_sgroup.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_stereo_label.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_stream.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_tokens.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_v2000.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_v3000.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/ctfile/test/test_wedge.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/mol2.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/_builder.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/_legacy.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/_mmcif.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/_records.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/pdb/_star.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/conftest.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/oracles.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_conformance.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_done_when.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_isolation.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_log_prefix.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_mmcif.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_mol2.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_no_review_bookkeeping.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_oracles.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_pdb.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_pdb_builder.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_perceive_agreement.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_read_only_facades.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_xyz.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/test/test_xyz_builder.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_cml.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_dialect.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_errors.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_mrv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/_tree.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/conftest.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_cml.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_container_log.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_dialect.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_equivalence.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_facade.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_mrv.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_mrv_census.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xml/test/test_tree.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/formats/xyz.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_cdk.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_cdpkit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_indigo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_iupac.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_java.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_openbabel.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_pandas.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_rdkit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_records.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/config.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/conformers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/conftest.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_cdk.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_cdpkit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_config.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_conformers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_coordinate_honesty.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_dispatch.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_indigo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_iupac.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_log_delivery.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_openbabel.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_pandas.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_rdkit.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_stereo.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/interop/test/test_v2_oracle.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/_numbering.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/_stickers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/_tables.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/attention/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/attention/_assign.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/attention/_encode.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/attention/_session.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/tables/functional.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/tables/protective.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/tables/reactions.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/tables/roles.tsv +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/_frozen_ids.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/gen_corpus_glossary.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/golden_subset.smi +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_attention.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_attention_assign.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_attention_encode.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_attention_isolation.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_corpus_glossary.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_dependency_direction.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_enumerate.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_functional.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_id_stability.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_numbering.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_probes.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_protective.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_roles.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_stickers.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/reactions/test/test_tables.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/__init__.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_code_hygiene.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_container_methods.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_doc_figures.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_doc_references.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_doc_samples.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_facade_names.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_hydrogen_parity.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_libinchi_staging.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_log_records.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_optional_numpy.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_packaging.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_performance.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_r_atom_integration.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_release_build.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_stereo_bluebook.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_v2_boundary.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython/test/test_writer_posture.py +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython.egg-info/SOURCES.txt +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython.egg-info/dependency_links.txt +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython.egg-info/requires.txt +0 -0
- {chython-3.3.1 → chython-3.3.2}/chython.egg-info/top_level.txt +0 -0
- {chython-3.3.1 → chython-3.3.2}/setup.cfg +0 -0
- {chython-3.3.1 → chython-3.3.2}/setup.py +0 -0
|
@@ -110,8 +110,66 @@ def _selected(rules: Mapping[str, tuple[ReactionRule, ...]],
|
|
|
110
110
|
return chain.from_iterable(rules.values())
|
|
111
111
|
|
|
112
112
|
|
|
113
|
+
#: `{rule id: (its groups as a set, its groups as a multiset)}`. The prefilter's left-hand side is a
|
|
114
|
+
#: constant of the row while the right-hand side is the pool's, so an enumerator walking many pools over
|
|
115
|
+
#: one corpus would otherwise rebuild every row's `Counter` once per pool.
|
|
116
|
+
_NEEDS_CACHE: dict[str, tuple[frozenset, Counter]] = {}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _needs(rule: ReactionRule) -> tuple[frozenset, Counter]:
|
|
120
|
+
"""`rule.groups` as a set and as a multiset, built once per row.
|
|
121
|
+
|
|
122
|
+
Two spellings of one requirement because the set settles most pools on its own: only a row whose
|
|
123
|
+
groups are all present at all goes on to the subtraction that also counts them.
|
|
124
|
+
"""
|
|
125
|
+
needs = _NEEDS_CACHE.get(rule.id)
|
|
126
|
+
if needs is None:
|
|
127
|
+
needs = _NEEDS_CACHE[rule.id] = (frozenset(rule.groups), Counter(rule.groups))
|
|
128
|
+
return needs
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _fitting(molecules: Sequence[MoleculeContainer], rules: Mapping[str, tuple[ReactionRule, ...]],
|
|
132
|
+
reaction: str | None = None, carried: Sequence[Mapping[str, int]] | None = None
|
|
133
|
+
) -> Iterator[ReactionRule]:
|
|
134
|
+
"""The rows whose groups this pool could satisfy. `_run`'s prefilter, and nothing after it.
|
|
135
|
+
|
|
136
|
+
Separate from `_run` because a caller may hold a test this file cannot: reconstruction knows the
|
|
137
|
+
recorded product, so it can bound what a row could BUILD from a pool before paying to build it.
|
|
138
|
+
Both filters and why each one is sound are documented on `_run`.
|
|
139
|
+
|
|
140
|
+
`carried` is the caller's own `[functional_groups(m) for m in molecules]`, positional, where it has
|
|
141
|
+
one already: a molecule's groups do not depend on what it is enumerated beside, so an enumerator
|
|
142
|
+
walking many pools over the same inputs scans each input once instead of once per pool.
|
|
143
|
+
"""
|
|
144
|
+
if carried is None:
|
|
145
|
+
carried = [functional_groups(molecule) for molecule in molecules]
|
|
146
|
+
available = Counter()
|
|
147
|
+
for groups in carried:
|
|
148
|
+
available.update(groups)
|
|
149
|
+
have = available.keys()
|
|
150
|
+
|
|
151
|
+
for rule in _selected(rules, reaction):
|
|
152
|
+
wanted, counts = _needs(rule)
|
|
153
|
+
if not wanted <= have or counts - available:
|
|
154
|
+
continue
|
|
155
|
+
if any(wanted.isdisjoint(groups) for groups in carried):
|
|
156
|
+
continue
|
|
157
|
+
yield rule
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _outcomes(rule: ReactionRule, molecules: Sequence[MoleculeContainer]
|
|
161
|
+
) -> Iterator[EnumeratedReaction]:
|
|
162
|
+
"""Every reaction `rule` produces on `molecules` that touched all of them."""
|
|
163
|
+
inputs = len(molecules)
|
|
164
|
+
for template in rule.templates:
|
|
165
|
+
for rxn in template(*molecules):
|
|
166
|
+
if len(rxn.reactants) == inputs:
|
|
167
|
+
yield EnumeratedReaction(rule.name, rxn, rule.id)
|
|
168
|
+
|
|
169
|
+
|
|
113
170
|
def _run(molecules: Sequence[MoleculeContainer], rules: Mapping[str, tuple[ReactionRule, ...]],
|
|
114
|
-
reaction: str | None = None
|
|
171
|
+
reaction: str | None = None, carried: Sequence[Mapping[str, int]] | None = None
|
|
172
|
+
) -> Iterator[EnumeratedReaction]:
|
|
115
173
|
"""The enumeration itself. One presence scan per input, then every row that fits.
|
|
116
174
|
|
|
117
175
|
Separate from `react()` only so a test can hand it a rule fixture the corpus has no row for.
|
|
@@ -122,19 +180,16 @@ def _run(molecules: Sequence[MoleculeContainer], rules: Mapping[str, tuple[React
|
|
|
122
180
|
(`len(rxn.reactants) == inputs`), so a three-molecule question is never answered by a row that
|
|
123
181
|
ignores one; an untouched COMPONENT of a touched input is different and survives, which is the salt
|
|
124
182
|
rule. Nothing between them knows how many molecules a row "expects".
|
|
125
|
-
"""
|
|
126
|
-
available = Counter()
|
|
127
|
-
for molecule in molecules:
|
|
128
|
-
available.update(functional_groups(molecule))
|
|
129
|
-
inputs = len(molecules)
|
|
130
183
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
184
|
+
THAT OUTGOING RULE IS ALSO A PREFILTER READ PER INPUT: an input carrying none of a row's groups can
|
|
185
|
+
never be one of its reactants, so a row is skipped where any single input is disjoint from it. The
|
|
186
|
+
union test cannot see this -- it asks only whether the groups are there, not whether they are spread
|
|
187
|
+
across every molecule that has to be touched.
|
|
188
|
+
|
|
189
|
+
`carried` is `_fitting`'s, and means the same thing here.
|
|
190
|
+
"""
|
|
191
|
+
for rule in _fitting(molecules, rules, reaction, carried):
|
|
192
|
+
yield from _outcomes(rule, molecules)
|
|
138
193
|
|
|
139
194
|
|
|
140
195
|
def react(molecule: MoleculeContainer, others=(), reaction: str | None = None
|
|
@@ -28,11 +28,12 @@ not; a configuration that disagrees is a fact about the record, reported and opt
|
|
|
28
28
|
never a reason to hand back no mapping. `_Options.loose` is that separation, and `_Link` is what carries
|
|
29
29
|
each rung's paired components out to it.
|
|
30
30
|
"""
|
|
31
|
-
from collections
|
|
31
|
+
from collections import Counter
|
|
32
|
+
from collections.abc import Callable, Container, Iterator, Mapping, Sequence
|
|
32
33
|
from itertools import combinations
|
|
33
34
|
from typing import NamedTuple
|
|
34
35
|
|
|
35
|
-
from ._enumerate import
|
|
36
|
+
from ._enumerate import _fitting, _outcomes, deprotect, EnumeratedReaction, functional_groups
|
|
36
37
|
from ._numbering import fast_mapping
|
|
37
38
|
from ._tables import reaction_rules
|
|
38
39
|
from ..core import (INFO, LOST, LogRecord, MoleculeContainer, REFUSED, ReactionContainer, STEREO_ABS,
|
|
@@ -106,12 +107,17 @@ def reconstruct_mapping(reaction: ReactionContainer, *, max_size_ratio: float =
|
|
|
106
107
|
# entitled to the row that actually explains it. The loose walk is what a record whose
|
|
107
108
|
# configuration explains nothing falls through to, and it reaches only records that would otherwise
|
|
108
109
|
# have come back unexplained.
|
|
110
|
+
#
|
|
111
|
+
# ONE MEMO FOR THE CALL AND BOTH WALKS SHARE IT: a rung enumerates what the inputs and the corpus
|
|
112
|
+
# allow, which is not a question about strictness, so the loose walk reads the strict walk's outcomes
|
|
113
|
+
# back rather than putting the same question again.
|
|
114
|
+
memo = {}
|
|
109
115
|
for loose in ((False, True) if stereo == 'loose' else (False,)):
|
|
110
116
|
options = _Options(max_size_ratio, min_filter_size, loose)
|
|
111
117
|
for phase in _PHASES:
|
|
112
118
|
if unbalanced and phase not in _FILTER_EXEMPT:
|
|
113
119
|
continue
|
|
114
|
-
found = list(phase(recorded, inputs, options))
|
|
120
|
+
found = list(phase(recorded, inputs, options, memo))
|
|
115
121
|
if not found:
|
|
116
122
|
continue
|
|
117
123
|
complete, links = found[0].write(recorded)
|
|
@@ -144,11 +150,29 @@ def reconstruct_mapping(reaction: ReactionContainer, *, max_size_ratio: float =
|
|
|
144
150
|
|
|
145
151
|
# --- the rungs ------------------------------------------------------------------------------------
|
|
146
152
|
|
|
147
|
-
def
|
|
153
|
+
def _once(memo: dict, key: str, compute: Callable[[], object]):
|
|
154
|
+
"""`compute()`'s value, computed on the first walk of a call and read back on the second.
|
|
155
|
+
|
|
156
|
+
WHAT A RUNG ENUMERATES IS NOT A QUESTION ABOUT STRICTNESS. `_applications` and `deprotect` take no
|
|
157
|
+
`_Options`: both walks put the same question to the corpus and get the same outcomes back, and what
|
|
158
|
+
strictness decides is which of those outcomes `_reproduces` accepts. So the walk is paid once.
|
|
159
|
+
|
|
160
|
+
`compute` materializes -- the driver exhausts every rung with `list()`, so nothing is lost -- and the
|
|
161
|
+
outcomes are held, not the generator, because a generator replays nothing. Sound only because every
|
|
162
|
+
consumer of an outcome reads it: `_reproduces` compares, `_translate` copies before it edits.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
return memo[key]
|
|
166
|
+
except KeyError:
|
|
167
|
+
got = memo[key] = compute()
|
|
168
|
+
return got
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _purification(recorded, inputs, options, memo) -> Iterator[_Explanation]:
|
|
148
172
|
"""The product went in and came out. The mapping it implies is the identity.
|
|
149
173
|
|
|
150
174
|
The one explanation whose mapping is certain, and the only label with no namespace -- no rule and no
|
|
151
|
-
table produced it.
|
|
175
|
+
table produced it. Nothing to memo: the identity test IS the strictness question.
|
|
152
176
|
"""
|
|
153
177
|
if _number_product(recorded.copy(), inputs, options.loose)[0]:
|
|
154
178
|
yield _Explanation('purification',
|
|
@@ -180,9 +204,9 @@ def _translate(reaction, sources: Sequence[MoleculeContainer]) -> list[MoleculeC
|
|
|
180
204
|
return out
|
|
181
205
|
|
|
182
206
|
|
|
183
|
-
def _react(recorded, inputs, options) -> Iterator[_Explanation]:
|
|
207
|
+
def _react(recorded, inputs, options, memo) -> Iterator[_Explanation]:
|
|
184
208
|
"""A corpus row, applied to the inputs as they arrived. The strongest evidence there is."""
|
|
185
|
-
for pool, outcome in _applications(inputs):
|
|
209
|
+
for pool, outcome in _once(memo, 'react', lambda: list(_applications(recorded, inputs))):
|
|
186
210
|
if not _reproduces(recorded, outcome.reaction.products, options.loose):
|
|
187
211
|
continue
|
|
188
212
|
sources = [*_translate(outcome.reaction, pool), *inputs]
|
|
@@ -191,42 +215,54 @@ def _react(recorded, inputs, options) -> Iterator[_Explanation]:
|
|
|
191
215
|
outcome.rule_id)
|
|
192
216
|
|
|
193
217
|
|
|
194
|
-
def _deprotect(recorded, inputs, options) -> Iterator[_Explanation]:
|
|
218
|
+
def _deprotect(recorded, inputs, options, memo) -> Iterator[_Explanation]:
|
|
195
219
|
"""An input, unmasked. The recorded product IS the recorded input minus a protecting group.
|
|
196
220
|
|
|
197
221
|
`partial=True` because incomplete cleavage is ordinary and `R-N(Boc)2 -> R-NHBoc` is a record this
|
|
198
|
-
rung must read.
|
|
222
|
+
rung must read. Largest-first, which is the order the explanations come out in.
|
|
199
223
|
"""
|
|
200
|
-
for molecule in
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
for molecule, outcome in _once(memo, 'deprotect',
|
|
225
|
+
lambda: [(m, o) for m in inputs
|
|
226
|
+
for o in deprotect(m, partial=True)]):
|
|
227
|
+
if not _reproduces(recorded, outcome.reaction.products, options.loose):
|
|
228
|
+
continue
|
|
229
|
+
sources = [*_translate(outcome.reaction, [molecule]), *inputs]
|
|
230
|
+
yield _Explanation('deprotect:%s' % '+'.join(outcome.names),
|
|
231
|
+
lambda target, s=sources, o=options: _number_product(target, s, o.loose),
|
|
232
|
+
'+'.join(outcome.rule_ids))
|
|
209
233
|
|
|
210
234
|
|
|
211
|
-
def
|
|
212
|
-
"""
|
|
235
|
+
def _strip(inputs) -> tuple[list[MoleculeContainer], set[int]]:
|
|
236
|
+
"""`inputs` with every protecting group taken off, and which members of the result a strip produced.
|
|
213
237
|
|
|
214
238
|
ONE all-stripped pass and not every raw/stripped combination, which would be exponential in the
|
|
215
239
|
number of protected inputs: a deliberate lower bound on what composition buys. A stripped form
|
|
216
240
|
keeps the stable ids it came in with, so numbering survives the composition unaided.
|
|
217
241
|
"""
|
|
218
242
|
pool = []
|
|
219
|
-
|
|
243
|
+
stripped = set()
|
|
220
244
|
for molecule in inputs:
|
|
221
245
|
outcome = next(deprotect(molecule), None)
|
|
222
246
|
if outcome is None:
|
|
223
247
|
pool.append(molecule)
|
|
224
248
|
else:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
249
|
+
for product in _translate(outcome.reaction, [molecule]):
|
|
250
|
+
stripped.add(len(pool))
|
|
251
|
+
pool.append(product)
|
|
252
|
+
return pool, stripped
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _deprotect_then_react(recorded, inputs, options, memo) -> Iterator[_Explanation]:
|
|
256
|
+
"""Strip what can be stripped, then let the corpus fire on what is left.
|
|
257
|
+
|
|
258
|
+
Only the pools a stripping reached are walked -- `stripped`, handed on as `_applications`' `required`.
|
|
259
|
+
A pool of unstripped members is one `_react` already put to the corpus, and this rung runs after it.
|
|
260
|
+
"""
|
|
261
|
+
pool, stripped = _once(memo, 'strip', lambda: _strip(inputs))
|
|
262
|
+
if not stripped:
|
|
228
263
|
return
|
|
229
|
-
for subset, outcome in
|
|
264
|
+
for subset, outcome in _once(memo, 'deprotect+react',
|
|
265
|
+
lambda: list(_applications(recorded, pool, required=stripped))):
|
|
230
266
|
if not _reproduces(recorded, outcome.reaction.products, options.loose):
|
|
231
267
|
continue
|
|
232
268
|
sources = [*_translate(outcome.reaction, subset), *pool, *inputs]
|
|
@@ -235,13 +271,13 @@ def _deprotect_then_react(recorded, inputs, options) -> Iterator[_Explanation]:
|
|
|
235
271
|
outcome.rule_id)
|
|
236
272
|
|
|
237
273
|
|
|
238
|
-
def _protect(recorded, inputs, options) -> Iterator[_Explanation]:
|
|
274
|
+
def _protect(recorded, inputs, options, memo) -> Iterator[_Explanation]:
|
|
239
275
|
"""The recorded product is a recorded input, masked.
|
|
240
276
|
|
|
241
277
|
THE WEAKEST RUNG AND SO THE LAST: an amide, an ester and a carbamate are all protecting groups as
|
|
242
278
|
well as products, so offered first this reads every acylation as a protection.
|
|
243
279
|
"""
|
|
244
|
-
for outcome in deprotect(recorded, partial=True):
|
|
280
|
+
for outcome in _once(memo, 'protect', lambda: list(deprotect(recorded, partial=True))):
|
|
245
281
|
pairs = _pair_with_inputs(outcome.reaction.products, inputs, options.loose)
|
|
246
282
|
if not pairs:
|
|
247
283
|
continue
|
|
@@ -751,31 +787,92 @@ def _anchors(owners) -> tuple:
|
|
|
751
787
|
|
|
752
788
|
# --- enumeration ----------------------------------------------------------------------------------
|
|
753
789
|
|
|
754
|
-
def
|
|
755
|
-
|
|
790
|
+
def _supplies(supply: Counter, targets: Sequence[Mapping[str, int]], rule) -> bool:
|
|
791
|
+
"""Could this pool, plus whatever the row creates, hold the atoms of SOME recorded component?
|
|
792
|
+
|
|
793
|
+
A NECESSARY CONDITION AND NOT A MATCH. `_reproduces` asks whether a component of the record IS a
|
|
794
|
+
component of what the row built, and a built component's atoms are the pool's, less what the row
|
|
795
|
+
deleted, plus what it created -- so a record component wanting more of an element than the pool
|
|
796
|
+
carries is reachable only through a created atom, and a row creates a fixed number of those. One
|
|
797
|
+
component sufficing is enough, the record being matched per component; deletions are not subtracted,
|
|
798
|
+
which only makes the bound weaker and never wrong.
|
|
799
|
+
|
|
800
|
+
THE SCREEN THE RECORDED PRODUCT AFFORDS AND THE GROUP PREFILTER CANNOT. That prefilter reads the
|
|
801
|
+
inputs alone, so it passes every row whose groups are in the pot however little the pot could build
|
|
802
|
+
-- and the rows it passes are the ones whose cost is the isomorphism search.
|
|
803
|
+
"""
|
|
804
|
+
created = max(len(template.created_atoms) for template in rule.templates)
|
|
805
|
+
for counts in targets:
|
|
806
|
+
deficit = 0
|
|
807
|
+
for element, n in counts.items():
|
|
808
|
+
short = n - supply.get(element, 0)
|
|
809
|
+
if short > 0:
|
|
810
|
+
deficit += short
|
|
811
|
+
if deficit > created:
|
|
812
|
+
break
|
|
813
|
+
else:
|
|
814
|
+
return True
|
|
815
|
+
return False
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _applications(recorded: MoleculeContainer, inputs: Sequence[MoleculeContainer],
|
|
819
|
+
rules=None, required: Container[int] | None = None
|
|
820
|
+
) -> Iterator[tuple[list[MoleculeContainer], 'EnumeratedReaction']]:
|
|
756
821
|
"""Every way a corpus row applies to a SUBSET of `inputs`, with the subset it applied to.
|
|
757
822
|
|
|
758
823
|
Subsets and not the whole list, because `_run` requires every molecule it is handed to be touched
|
|
759
824
|
while a recorded record files its base, solvent and catalyst among the inputs. Bounded by the widest
|
|
760
|
-
row's slot count, so
|
|
825
|
+
row's slot count, so the count is a polynomial in the number of inputs and not a power set -- but
|
|
826
|
+
`C(n, 1..4)` is still `n**4/24`, which is why the walk is over `reachable` and not over every input.
|
|
827
|
+
|
|
828
|
+
THE INPUTS A ROW COULD REACH, AND NOT ALL OF THEM. An input carrying none of the groups any row
|
|
829
|
+
names a slot for cannot be one of an outcome's reactants, and `_run` keeps an outcome only when every
|
|
830
|
+
molecule it was handed was touched -- so every pool containing such an input is empty by
|
|
831
|
+
construction. Enumerating them anyway lets a record's own solvents dominate its cost: a record files
|
|
832
|
+
its base and solvent among its reagents, and they are typically 2 of the 4 a median record carries.
|
|
833
|
+
|
|
834
|
+
Each input is scanned for its groups ONCE and the result handed to `_fitting`, rather than rescanned
|
|
835
|
+
per pool: a molecule's groups do not depend on what it is enumerated beside. `recorded` is here for
|
|
836
|
+
`_supplies`, which is the one filter in the walk that reads the answer rather than the inputs.
|
|
837
|
+
|
|
838
|
+
`required` is a set of indices a pool must draw at least one member from, and the caller's way of
|
|
839
|
+
saying that the pools it omits were already walked: `_deprotect_then_react` runs after `_react` in the
|
|
840
|
+
same walk, so a pool of its stripped list holding no stripped molecule IS a pool `_react` offered the
|
|
841
|
+
corpus and `_react` not having returned is that pool's verdict.
|
|
761
842
|
|
|
762
843
|
The subset comes back because the reactor's products carry the reactor's own numbering: `_translate`
|
|
763
844
|
needs the inputs, positionally, to cross back to theirs.
|
|
764
845
|
|
|
765
|
-
TODO: the bare `except` is defensive, not load-bearing --
|
|
766
|
-
the current corpus, but a row that consistently fails is invisible here. Narrow it to the
|
|
767
|
-
type once there is one.
|
|
846
|
+
TODO: the bare `except` is defensive, not load-bearing -- applying a row has not been observed to
|
|
847
|
+
raise on the current corpus, but a row that consistently fails is invisible here. Narrow it to the
|
|
848
|
+
observed type once there is one.
|
|
768
849
|
"""
|
|
769
850
|
if rules is None:
|
|
770
851
|
rules = reaction_rules()
|
|
771
|
-
widest =
|
|
772
|
-
|
|
773
|
-
for
|
|
774
|
-
for
|
|
852
|
+
widest = 0
|
|
853
|
+
slots = set()
|
|
854
|
+
for family in rules.values():
|
|
855
|
+
for rule in family:
|
|
856
|
+
if len(rule.groups) > widest:
|
|
857
|
+
widest = len(rule.groups)
|
|
858
|
+
slots.update(rule.groups)
|
|
859
|
+
carried = [functional_groups(molecule) for molecule in inputs]
|
|
860
|
+
reachable = [i for i, groups in enumerate(carried) if not slots.isdisjoint(groups)]
|
|
861
|
+
targets = [part.element_counts for part in recorded.split()]
|
|
862
|
+
for size in range(1, min(widest, len(reachable)) + 1):
|
|
863
|
+
for subset in combinations(reachable, size):
|
|
864
|
+
if required is not None and not any(i in required for i in subset):
|
|
865
|
+
continue
|
|
775
866
|
pool = [inputs[i] for i in subset]
|
|
867
|
+
supply = Counter()
|
|
868
|
+
for molecule in pool:
|
|
869
|
+
supply.update(molecule.element_counts)
|
|
776
870
|
try:
|
|
777
|
-
for
|
|
778
|
-
|
|
871
|
+
for rule in _fitting(pool, rules, carried=[carried[i] for i in subset]):
|
|
872
|
+
if not _supplies(supply, targets, rule):
|
|
873
|
+
continue
|
|
874
|
+
for outcome in _outcomes(rule, pool):
|
|
875
|
+
yield pool, outcome
|
|
779
876
|
except Exception:
|
|
780
877
|
continue
|
|
781
878
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# You should have received a copy of the GNU Lesser General Public License
|
|
17
17
|
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
18
18
|
#
|
|
19
|
+
from collections import Counter
|
|
19
20
|
from pytest import raises
|
|
20
21
|
|
|
21
22
|
from ...core import ReactionContainer, read_smiles as smiles
|
|
@@ -456,6 +457,123 @@ def test_a_spectator_input_comes_back_unmapped():
|
|
|
456
457
|
assert all(toluene.map_number_of(n) == 0 for n in toluene.atom_numbers)
|
|
457
458
|
|
|
458
459
|
|
|
460
|
+
# --- what the walk is allowed to cost -------------------------------------------------------------
|
|
461
|
+
|
|
462
|
+
#: Solvents, a drying agent and a base that carry NO group the corpus names a slot for, so no row can
|
|
463
|
+
#: reach them: dichloromethane, THF, water, DMF, dioxane, hexane. A record files its solvent among its
|
|
464
|
+
#: reagents, and the corpus median is 4 reagents, so this is the ordinary case and not a stress test.
|
|
465
|
+
_UNREACHABLE = ('ClCCl', 'C1CCOC1', 'O', 'CN(C)C=O', 'C1COCCO1', 'CCCCCC')
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _amidation(extra=()):
|
|
469
|
+
return ReactionContainer([smiles('CC(=O)O'), smiles('CCN'), *(smiles(s) for s in extra)],
|
|
470
|
+
[smiles('CC(=O)NCC')])
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def _pools(reaction):
|
|
474
|
+
"""The label, and how many pools of inputs the walk offered the corpus to earn it."""
|
|
475
|
+
from .. import _reconstruct
|
|
476
|
+
|
|
477
|
+
offered = []
|
|
478
|
+
kept = _reconstruct._fitting
|
|
479
|
+
|
|
480
|
+
def counting(molecules, *args, **kwargs):
|
|
481
|
+
offered.append(len(molecules))
|
|
482
|
+
return kept(molecules, *args, **kwargs)
|
|
483
|
+
|
|
484
|
+
_reconstruct._fitting = counting
|
|
485
|
+
try:
|
|
486
|
+
return reaction.reconstruct_mapping(), len(offered)
|
|
487
|
+
finally:
|
|
488
|
+
_reconstruct._fitting = kept
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _mapping(reaction):
|
|
492
|
+
"""The written mapping, per molecule, as the thing the padding may not perturb."""
|
|
493
|
+
return [sorted((n, m.map_number_of(n)) for n in m.atom_numbers) for m in reaction.molecules()]
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def test_inert_inputs_do_not_change_the_answer():
|
|
497
|
+
plain, padded = _amidation(), _amidation(_UNREACHABLE)
|
|
498
|
+
assert plain.reconstruct_mapping() == padded.reconstruct_mapping() == ('react:amidation',)
|
|
499
|
+
# PER ATOM, on the two molecules both records share: the acid, the amine and the amide
|
|
500
|
+
written = _mapping(padded)
|
|
501
|
+
assert _mapping(plain) == [*written[:2], written[-1]]
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def test_inert_inputs_do_not_multiply_the_work():
|
|
505
|
+
# `_applications` walks the subsets of the inputs A ROW COULD REACH, so the six inert reagents cost
|
|
506
|
+
# nothing: 3 pools either way. Walking every subset instead costs `C(n, 1..4)`, measured at 162 on
|
|
507
|
+
# this record -- and a record may file more reagents than six.
|
|
508
|
+
plain_label, plain = _pools(_amidation())
|
|
509
|
+
padded_label, padded = _pools(_amidation(_UNREACHABLE))
|
|
510
|
+
assert plain_label == padded_label == ('react:amidation',)
|
|
511
|
+
assert plain == 3
|
|
512
|
+
assert padded == plain, 'an input no row can reach must not be enumerated'
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def test_the_composed_rung_only_walks_what_the_stripping_reached():
|
|
516
|
+
# `_deprotect_then_react` runs after `_react` on the same walk, so a pool of its stripped list that
|
|
517
|
+
# holds nothing stripped is a pool `_react` already offered: here the acid alone, and the acid with
|
|
518
|
+
# the isobutylene the Boc left behind. Both rungs walk, so the count is per rung.
|
|
519
|
+
from .._reconstruct import _applications
|
|
520
|
+
|
|
521
|
+
rxn = ReactionContainer([smiles('CC(=O)O'), smiles('CC(C)(C)OC(=O)NCC')], [smiles('CC(=O)NCC')])
|
|
522
|
+
stripped, unstripped = [], []
|
|
523
|
+
for store, required in ((stripped, {1}), (unstripped, None)):
|
|
524
|
+
pool = [smiles('CC(=O)O'), smiles('CCN'), smiles('CC(C)=C')]
|
|
525
|
+
store.extend(subset for subset, _ in _applications(rxn.products[0], pool, required=required))
|
|
526
|
+
assert rxn.reconstruct_mapping() == ('deprotect+react:amidation',)
|
|
527
|
+
assert len(stripped) < len(unstripped), 'a pool the stripping never reached must not be re-walked'
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def test_the_corpus_is_walked_once_for_both_strictnesses():
|
|
531
|
+
# THE FLAT-PRODUCT RECORD, which only the loose walk explains: the strict walk enumerates every rung
|
|
532
|
+
# and accepts none of it, so the loose walk is the one that earns the label. What a rung enumerates
|
|
533
|
+
# is a question about the inputs and the corpus, not about strictness, so it is asked once.
|
|
534
|
+
from .. import _reconstruct
|
|
535
|
+
|
|
536
|
+
rxn = ReactionContainer([smiles('C[C@H](O)CC'), smiles('CC(=O)O')], [smiles('CC(CC)OC(C)=O')])
|
|
537
|
+
walks = []
|
|
538
|
+
kept = _reconstruct._applications
|
|
539
|
+
|
|
540
|
+
def counting(recorded, inputs, rules=None, required=None):
|
|
541
|
+
walks.append(required)
|
|
542
|
+
return kept(recorded, inputs, rules, required)
|
|
543
|
+
|
|
544
|
+
_reconstruct._applications = counting
|
|
545
|
+
try:
|
|
546
|
+
assert rxn.reconstruct_mapping() == ('react:esterification',)
|
|
547
|
+
finally:
|
|
548
|
+
_reconstruct._applications = kept
|
|
549
|
+
assert walks == [None], 'the corpus walk is paid once per call, not once per strictness'
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def test_the_recorded_product_bounds_what_a_row_may_build():
|
|
553
|
+
from .._reconstruct import _supplies
|
|
554
|
+
from .._tables import reaction_rules
|
|
555
|
+
|
|
556
|
+
rule = reaction_rules()['amidation'][0]
|
|
557
|
+
created = max(len(template.created_atoms) for template in rule.templates)
|
|
558
|
+
# the bound is over ELEMENTS, and one recorded component sufficing is enough
|
|
559
|
+
assert _supplies(Counter({'C': 4, 'N': 1, 'O': 1}), [{'C': 4, 'N': 1, 'O': 1}], rule)
|
|
560
|
+
assert _supplies(Counter({'C': 9}), [{'C': 40}, {'C': 4}], rule)
|
|
561
|
+
# a record wanting an element no input carries is out of reach, up to what the row creates
|
|
562
|
+
assert not _supplies(Counter({'C': 40}), [{'C': 4, 'Br': created + 1}], rule)
|
|
563
|
+
# NECESSARY AND NOT SUFFICIENT: enough atoms of the right elements says nothing about the bonds
|
|
564
|
+
assert _supplies(Counter({'C': 99}), [{'C': 4}], rule)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def test_an_unreachable_input_still_reports_as_a_spectator():
|
|
568
|
+
# the pruning is of the SEARCH, so an input it skipped is still an input: it comes back at 0 like
|
|
569
|
+
# any other spectator rather than disappearing from the record
|
|
570
|
+
rxn = _amidation(_UNREACHABLE)
|
|
571
|
+
assert rxn.reconstruct_mapping() == ('react:amidation',)
|
|
572
|
+
assert len(rxn.reactants) + len(rxn.agents) == 2 + len(_UNREACHABLE)
|
|
573
|
+
for molecule in (*rxn.reactants, *rxn.agents)[2:]:
|
|
574
|
+
assert all(molecule.map_number_of(n) == 0 for n in molecule.atom_numbers)
|
|
575
|
+
|
|
576
|
+
|
|
459
577
|
# --- stereo strictness ----------------------------------------------------------------------------
|
|
460
578
|
|
|
461
579
|
def test_a_flat_product_is_explained_loosely():
|
|
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = 'chython'
|
|
7
|
-
version = '3.3.
|
|
7
|
+
version = '3.3.2'
|
|
8
8
|
description = 'Library for processing molecules and reactions in python way'
|
|
9
9
|
authors = [{name = 'Ramil Nugmanov', email = 'nougmanoff@protonmail.com'}]
|
|
10
10
|
license = 'LGPL-3.0-or-later'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|