synkit 0.0.1__tar.gz → 0.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 (152) hide show
  1. {synkit-0.0.1 → synkit-0.0.2}/.gitignore +1 -0
  2. synkit-0.0.2/Data/Testcase/Compose/SingleRule/R0/0.gml +17 -0
  3. synkit-0.0.2/Data/Testcase/Compose/SingleRule/R0/1.gml +17 -0
  4. synkit-0.0.2/Data/Testcase/Compose/SingleRule/R0/2.gml +17 -0
  5. {synkit-0.0.1 → synkit-0.0.2}/PKG-INFO +1 -1
  6. synkit-0.0.2/Test/Chem/Reaction/test_rsmi_utils.py +82 -0
  7. synkit-0.0.2/Test/Graph/Cluster/test_batch_cluster.py +109 -0
  8. synkit-0.0.2/Test/Graph/Cluster/test_graph_cluster.py +136 -0
  9. synkit-0.0.2/Test/Graph/Cluster/test_morphism.py +117 -0
  10. synkit-0.0.2/Test/ITS/test_its_expand.py +19 -0
  11. {synkit-0.0.1 → synkit-0.0.2}/Test/ITS/test_normalize_aam.py +3 -1
  12. synkit-0.0.2/Test/Reactor/test_multi_steps.py +73 -0
  13. synkit-0.0.2/Test/Reactor/test_reactor_engine.py +209 -0
  14. synkit-0.0.2/Test/Reactor/test_reactor_utils.py +92 -0
  15. synkit-0.0.2/Test/Rule/test_molecule_rule.py +139 -0
  16. synkit-0.0.2/Test/Rule/test_reactor_rule.py +58 -0
  17. synkit-0.0.2/Test/Rule/test_rule_compose.py +53 -0
  18. synkit-0.0.2/Test/Rule/test_rule_rbl.py +50 -0
  19. synkit-0.0.2/Test/Rule/test_rule_utils.py +186 -0
  20. synkit-0.0.2/Test/Rule/test_valance_constrain.py +102 -0
  21. {synkit-0.0.1 → synkit-0.0.2}/lint.sh +1 -1
  22. {synkit-0.0.1 → synkit-0.0.2}/pyproject.toml +1 -1
  23. synkit-0.0.2/synkit/Chem/Reaction/rsmi_utils.py +100 -0
  24. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/standardize.py +4 -2
  25. synkit-0.0.2/synkit/Graph/Cluster/batch_cluster.py +222 -0
  26. synkit-0.0.2/synkit/Graph/Cluster/graph_cluster.py +163 -0
  27. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/chem_converter.py +39 -5
  28. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/dg_to_gml.py +4 -2
  29. synkit-0.0.2/synkit/ITS/its_expand.py +89 -0
  30. {synkit-0.0.1 → synkit-0.0.2}/synkit/ITS/normalize_aam.py +2 -2
  31. synkit-0.0.2/synkit/Metrics/_base.py +50 -0
  32. synkit-0.0.2/synkit/Metrics/_plot.py +122 -0
  33. synkit-0.0.2/synkit/Metrics/_ranking.py +174 -0
  34. synkit-0.0.2/synkit/Reactor/__init__.py +0 -0
  35. {synkit-0.0.1 → synkit-0.0.2}/synkit/Reactor/core_engine.py +8 -14
  36. synkit-0.0.2/synkit/Reactor/multi_steps.py +135 -0
  37. synkit-0.0.2/synkit/Reactor/reactor_engine.py +227 -0
  38. synkit-0.0.2/synkit/Reactor/reactor_utils.py +350 -0
  39. synkit-0.0.2/synkit/Rule/MaxValence.json.gz +1 -0
  40. synkit-0.0.2/synkit/Rule/__init__.py +0 -0
  41. synkit-0.0.2/synkit/Rule/molecule_rule.py +117 -0
  42. synkit-0.0.2/synkit/Rule/reactor_rule.py +85 -0
  43. synkit-0.0.2/synkit/Rule/rule_compose.py +230 -0
  44. synkit-0.0.2/synkit/Rule/rule_rbl.py +78 -0
  45. synkit-0.0.1/synkit/IO/parse_rule.py → synkit-0.0.2/synkit/Rule/rule_utils.py +19 -0
  46. synkit-0.0.2/synkit/Rule/strip_rule.py +94 -0
  47. synkit-0.0.2/synkit/Rule/valence_constrain.py +105 -0
  48. synkit-0.0.2/synkit/Vis/__init__.py +0 -0
  49. synkit-0.0.2/synkit/__init__.py +0 -0
  50. synkit-0.0.1/Test/Reactor/test_multi_step.py +0 -50
  51. synkit-0.0.1/Test/Reactor/test_multiple_step_aam.py +0 -67
  52. synkit-0.0.1/Test/Reactor/test_reagent.py +0 -58
  53. synkit-0.0.1/synkit/ITS/partial_expand.py +0 -170
  54. synkit-0.0.1/synkit/Reactor/inference.py +0 -73
  55. synkit-0.0.1/synkit/Reactor/multi_step.py +0 -227
  56. synkit-0.0.1/synkit/Reactor/multi_step_aam.py +0 -82
  57. synkit-0.0.1/synkit/Reactor/reagent.py +0 -95
  58. synkit-0.0.1/synkit/Reactor/rule_apply.py +0 -81
  59. {synkit-0.0.1 → synkit-0.0.2}/.github/workflows/publish-package.yml +0 -0
  60. {synkit-0.0.1 → synkit-0.0.2}/.github/workflows/test-and-lint.yml +0 -0
  61. {synkit-0.0.1 → synkit-0.0.2}/CHANGELOG.md +0 -0
  62. {synkit-0.0.1 → synkit-0.0.2}/Data/Figure/synkit.png +0 -0
  63. /synkit-0.0.1/Test/Chem/Fingerprint/__init__.py → /synkit-0.0.2/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  64. {synkit-0.0.1 → synkit-0.0.2}/Data/Testcase/mech.json.gz +0 -0
  65. {synkit-0.0.1 → synkit-0.0.2}/LICENSE +0 -0
  66. {synkit-0.0.1 → synkit-0.0.2}/README.md +0 -0
  67. {synkit-0.0.1/Test/Chem/Molecule → synkit-0.0.2/Test/Chem/Fingerprint}/__init__.py +0 -0
  68. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  69. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  70. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  71. {synkit-0.0.1/Test/Chem/Reaction → synkit-0.0.2/Test/Chem/Molecule}/__init__.py +0 -0
  72. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Molecule/test_standardize.py +0 -0
  73. {synkit-0.0.1/Test/Chem → synkit-0.0.2/Test/Chem/Reaction}/__init__.py +0 -0
  74. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  75. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_cleanning.py +0 -0
  76. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_deionize.py +0 -0
  77. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_neutralize.py +0 -0
  78. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_reagent.py +0 -0
  79. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_standardize.py +0 -0
  80. {synkit-0.0.1 → synkit-0.0.2}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  81. {synkit-0.0.1/Test/Graph/Cluster → synkit-0.0.2/Test/Chem}/__init__.py +0 -0
  82. {synkit-0.0.1/Test/Graph/Feature → synkit-0.0.2/Test/Graph/Cluster}/__init__.py +0 -0
  83. {synkit-0.0.1/Test/Graph → synkit-0.0.2/Test/Graph/Feature}/__init__.py +0 -0
  84. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  85. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_graph_fps.py +0 -0
  86. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_graph_signature.py +0 -0
  87. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_hash_fps.py +0 -0
  88. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  89. {synkit-0.0.1 → synkit-0.0.2}/Test/Graph/Feature/test_path_fps.py +0 -0
  90. {synkit-0.0.1/Test/IO → synkit-0.0.2/Test/Graph}/__init__.py +0 -0
  91. {synkit-0.0.1/Test/ITS → synkit-0.0.2/Test/IO}/__init__.py +0 -0
  92. {synkit-0.0.1 → synkit-0.0.2}/Test/IO/test_chemical_conversion.py +0 -0
  93. {synkit-0.0.1 → synkit-0.0.2}/Test/IO/test_gml_to_nx.py +0 -0
  94. {synkit-0.0.1 → synkit-0.0.2}/Test/IO/test_graph_to_mol.py +0 -0
  95. {synkit-0.0.1 → synkit-0.0.2}/Test/IO/test_mol_to_graph.py +0 -0
  96. {synkit-0.0.1 → synkit-0.0.2}/Test/IO/test_nx_to_gml.py +0 -0
  97. {synkit-0.0.1/Test/Reactor → synkit-0.0.2/Test/ITS}/__init__.py +0 -0
  98. {synkit-0.0.1 → synkit-0.0.2}/Test/ITS/test_aam_validator.py +0 -0
  99. {synkit-0.0.1 → synkit-0.0.2}/Test/ITS/test_its_construction.py +0 -0
  100. {synkit-0.0.1/Test/Vis → synkit-0.0.2/Test/Reactor}/__init__.py +0 -0
  101. {synkit-0.0.1 → synkit-0.0.2}/Test/Reactor/test_core_engine.py +0 -0
  102. {synkit-0.0.1/Test → synkit-0.0.2/Test/Rule}/__init__.py +0 -0
  103. {synkit-0.0.1/synkit/Chem/Fingerprint → synkit-0.0.2/Test/Vis}/__init__.py +0 -0
  104. {synkit-0.0.1 → synkit-0.0.2}/Test/Vis/test_embedding.py +0 -0
  105. {synkit-0.0.1/synkit/Chem/Molecule → synkit-0.0.2/Test}/__init__.py +0 -0
  106. {synkit-0.0.1 → synkit-0.0.2}/pytest.sh +0 -0
  107. {synkit-0.0.1 → synkit-0.0.2}/requirements.txt +0 -0
  108. {synkit-0.0.1/synkit/Chem/Reaction → synkit-0.0.2/synkit/Chem/Fingerprint}/__init__.py +0 -0
  109. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  110. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  111. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  112. {synkit-0.0.1/synkit/Graph/Cluster → synkit-0.0.2/synkit/Chem/Molecule}/__init__.py +0 -0
  113. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Molecule/standardize.py +0 -0
  114. {synkit-0.0.1/synkit/Graph/Feature → synkit-0.0.2/synkit/Chem/Reaction}/__init__.py +0 -0
  115. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/balance_check.py +0 -0
  116. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/cleanning.py +0 -0
  117. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/deionize.py +0 -0
  118. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/neutralize.py +0 -0
  119. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/reagent.py +0 -0
  120. {synkit-0.0.1 → synkit-0.0.2}/synkit/Chem/Reaction/tautomerize.py +0 -0
  121. {synkit-0.0.1/synkit/IO → synkit-0.0.2/synkit/Graph/Cluster}/__init__.py +0 -0
  122. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Cluster/morphism.py +0 -0
  123. {synkit-0.0.1/synkit/Reactor → synkit-0.0.2/synkit/Graph/Feature}/__init__.py +0 -0
  124. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  125. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/graph_fps.py +0 -0
  126. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/graph_signature.py +0 -0
  127. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/hash_fps.py +0 -0
  128. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/morgan_fps.py +0 -0
  129. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/Feature/path_fps.py +0 -0
  130. {synkit-0.0.1 → synkit-0.0.2}/synkit/Graph/__init.py +0 -0
  131. {synkit-0.0.1/synkit/Vis → synkit-0.0.2/synkit/IO}/__init__.py +0 -0
  132. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/data_io.py +0 -0
  133. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/data_process.py +0 -0
  134. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/debug.py +0 -0
  135. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/gml_to_nx.py +0 -0
  136. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/graph_to_mol.py +0 -0
  137. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/mol_to_graph.py +0 -0
  138. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/nx_to_gml.py +0 -0
  139. {synkit-0.0.1 → synkit-0.0.2}/synkit/IO/smiles_to_id.py +0 -0
  140. {synkit-0.0.1 → synkit-0.0.2}/synkit/ITS/_misc.py +0 -0
  141. {synkit-0.0.1 → synkit-0.0.2}/synkit/ITS/aam_validator.py +0 -0
  142. {synkit-0.0.1 → synkit-0.0.2}/synkit/ITS/its_builder.py +0 -0
  143. {synkit-0.0.1 → synkit-0.0.2}/synkit/ITS/its_construction.py +0 -0
  144. {synkit-0.0.1/synkit → synkit-0.0.2/synkit/Metrics}/__init__.py +0 -0
  145. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/chemical_graph_visualizer.py +0 -0
  146. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/chemical_reaction_visualizer.py +0 -0
  147. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/chemical_space.py +0 -0
  148. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/embedding.py +0 -0
  149. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/graph_visualizer.py +0 -0
  150. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/pdf_writer.py +0 -0
  151. {synkit-0.0.1 → synkit-0.0.2}/synkit/Vis/rsmi_to_fig.py +0 -0
  152. {synkit-0.0.1 → synkit-0.0.2}/synkit/_misc.py +0 -0
@@ -8,3 +8,4 @@ test_mod.py
8
8
  test_format.py
9
9
  *dev_zone
10
10
  *.pkl.gz
11
+ dev_scripts/*
@@ -0,0 +1,17 @@
1
+ rule [
2
+ ruleID "0"
3
+ left [
4
+ edge [ source 1 target 2 label "-" ]
5
+ edge [ source 3 target 4 label "-" ]
6
+ ]
7
+ context [
8
+ node [ id 1 label "H" ]
9
+ node [ id 2 label "N" ]
10
+ node [ id 3 label "C" ]
11
+ node [ id 4 label "Br" ]
12
+ ]
13
+ right [
14
+ edge [ source 1 target 4 label "-" ]
15
+ edge [ source 2 target 3 label "-" ]
16
+ ]
17
+ ]
@@ -0,0 +1,17 @@
1
+ rule [
2
+ ruleID "1"
3
+ left [
4
+ edge [ source 1 target 2 label "-" ]
5
+ edge [ source 3 target 4 label "-" ]
6
+ ]
7
+ context [
8
+ node [ id 1 label "Br" ]
9
+ node [ id 2 label "C" ]
10
+ node [ id 3 label "O" ]
11
+ node [ id 4 label "H" ]
12
+ ]
13
+ right [
14
+ edge [ source 1 target 4 label "-" ]
15
+ edge [ source 2 target 3 label "-" ]
16
+ ]
17
+ ]
@@ -0,0 +1,17 @@
1
+ rule [
2
+ ruleID "2"
3
+ left [
4
+ edge [ source 1 target 2 label "-" ]
5
+ edge [ source 3 target 4 label "-" ]
6
+ ]
7
+ context [
8
+ node [ id 1 label "C" ]
9
+ node [ id 2 label "Cl" ]
10
+ node [ id 3 label "N" ]
11
+ node [ id 4 label "H" ]
12
+ ]
13
+ right [
14
+ edge [ source 1 target 3 label "-" ]
15
+ edge [ source 2 target 4 label "-" ]
16
+ ]
17
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Utility for reaction modeling using graph grammar
5
5
  Project-URL: homepage, https://github.com/TieuLongPhan/SynKit
6
6
  Project-URL: source, https://github.com/TieuLongPhan/SynKit
@@ -0,0 +1,82 @@
1
+ import unittest
2
+ from synkit.Chem.Reaction.rsmi_utils import (
3
+ remove_common_reagents,
4
+ reverse_reaction,
5
+ remove_duplicates,
6
+ merge_reaction,
7
+ find_longest_fragment,
8
+ )
9
+
10
+
11
+ class TestChemicalReactions(unittest.TestCase):
12
+
13
+ def test_remove_common_reagents_no_common(self):
14
+ reaction = "A.B.C>>D.E.F"
15
+ expected_result = "A.B.C>>D.E.F"
16
+ result = remove_common_reagents(reaction)
17
+ self.assertEqual(result, expected_result)
18
+
19
+ def test_remove_common_reagents_with_common(self):
20
+ reaction = "A.B.C>>A.D.E"
21
+ expected_result = "B.C>>D.E"
22
+ result = remove_common_reagents(reaction)
23
+ self.assertEqual(result, expected_result)
24
+
25
+ def test_remove_common_reagents_all_common(self):
26
+ reaction = "A.B.C>>A.B.C"
27
+ expected_result = ">>"
28
+ result = remove_common_reagents(reaction)
29
+ self.assertEqual(result, expected_result)
30
+
31
+ def test_remove_duplicates(self):
32
+ input_list = ["CC", "C", "CC", "CCO", "C"]
33
+ expected_result = ["CC", "C", "CCO"]
34
+ result = remove_duplicates(input_list)
35
+ self.assertEqual(result, expected_result)
36
+
37
+ def test_reverse_reaction(self):
38
+ reaction_smiles = "C=C.O>>CCO"
39
+ expected_result = "CCO>>C=C.O"
40
+ result = reverse_reaction(reaction_smiles)
41
+ self.assertEqual(result, expected_result)
42
+
43
+ def test_merge_reaction(self):
44
+ rsmi_1 = "CCC(=O)OC.O>>CO.CCOC(=O)O"
45
+ rsmi_2 = "CCC(=O)O.CCO>>O.CCOC(=O)CC"
46
+ expected_result = "CCC(=O)OC.O.CCC(=O)O.CCO>>CO.CCOC(=O)O.O.CCOC(=O)CC"
47
+ result = merge_reaction(rsmi_1, rsmi_2)
48
+ self.assertEqual(result, expected_result)
49
+
50
+ def test_find_longest_fragment(self):
51
+ input_list = ["CCOC(=O)O", "O"]
52
+ expected_result = "CCOC(=O)O"
53
+ result = find_longest_fragment(input_list)
54
+ self.assertEqual(result, expected_result)
55
+
56
+ # Additional robustness for empty inputs or specific edge cases.
57
+ def test_remove_duplicates_empty(self):
58
+ input_list = []
59
+ expected_result = []
60
+ result = remove_duplicates(input_list)
61
+ self.assertEqual(result, expected_result)
62
+
63
+ def test_reverse_reaction_empty(self):
64
+ reaction_smiles = ">>"
65
+ expected_result = ">>"
66
+ result = reverse_reaction(reaction_smiles)
67
+ self.assertEqual(result, expected_result)
68
+
69
+ def test_merge_reaction_empty(self):
70
+ rsmi_1 = ">>"
71
+ rsmi_2 = ">>"
72
+ result = merge_reaction(rsmi_1, rsmi_2)
73
+ self.assertIsNone(result)
74
+
75
+ def test_find_longest_fragment_empty(self):
76
+ input_list = []
77
+ result = find_longest_fragment(input_list)
78
+ self.assertIsNone(result)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ unittest.main()
@@ -0,0 +1,109 @@
1
+ import time
2
+ import unittest
3
+ from synkit.IO.data_io import load_from_pickle
4
+ from synkit.Graph.Feature.graph_signature import GraphSignature
5
+ from synkit.Graph.Cluster.batch_cluster import BatchCluster
6
+
7
+
8
+ class TestBatchCluster(unittest.TestCase):
9
+ @classmethod
10
+ def setUpClass(cls):
11
+ cls.graphs = load_from_pickle("Data/Testcase/graph.pkl.gz")
12
+ cls.templates = None
13
+ for value in cls.graphs:
14
+ value["rc_sig"] = GraphSignature(value["RC"]).create_graph_signature()
15
+ value["its_sig"] = GraphSignature(value["ITS"]).create_graph_signature()
16
+
17
+ def test_initialization(self):
18
+ """Test initialization and verify if the attributes are set correctly."""
19
+ cluster = BatchCluster(["element", "charge"], ["*", 0], "bond_order")
20
+ self.assertEqual(cluster.nodeLabelNames, ["element", "charge"])
21
+ self.assertEqual(cluster.nodeLabelDefault, ["*", 0])
22
+ self.assertEqual(cluster.edgeAttribute, "bond_order")
23
+
24
+ def test_initialization_failure(self):
25
+ """Test initialization failure when lengths of node labels and defaults do not match."""
26
+ with self.assertRaises(ValueError):
27
+ BatchCluster(["element"], ["*", 0, 1], "bond_order")
28
+
29
+ def test_batch_dicts(self):
30
+ """Test the batching function to split data correctly."""
31
+ batch_cluster = BatchCluster(["element", "charge"], ["*", 0], "bond_order")
32
+ input_list = [{"id": i} for i in range(10)]
33
+ batches = batch_cluster.batch_dicts(input_list, 3)
34
+ self.assertEqual(len(batches), 4)
35
+ self.assertEqual(len(batches[0]), 3)
36
+ self.assertEqual(len(batches[-1]), 1)
37
+
38
+ def test_lib_check_functionality(self):
39
+ """Test the lib_check method using directly comparable results."""
40
+ cluster = BatchCluster()
41
+ batch_1 = self.graphs[:50]
42
+ batch_2 = self.graphs[50:]
43
+ _, templates = cluster.fit(batch_1, None, "RC", "rc_sig")
44
+ for entry in batch_2:
45
+ _, templates = cluster.lib_check(entry, templates, "RC", "rc_sig")
46
+ self.assertEqual(len(templates), 30)
47
+
48
+ def test_cluster_integration(self):
49
+ """Test the cluster method to ensure it processes data entries correctly."""
50
+ cluster = BatchCluster()
51
+ expected_template_count = 30
52
+ _, updated_templates = cluster.cluster(self.graphs, [], "RC", "rc_sig")
53
+
54
+ self.assertEqual(
55
+ len(updated_templates),
56
+ expected_template_count,
57
+ f"Failed: expected {expected_template_count} templates, got {len(updated_templates)}",
58
+ )
59
+
60
+ def test_fit(self):
61
+ cluster = BatchCluster()
62
+ batch_sizes = [None, 10]
63
+ expected_template_count = 30
64
+
65
+ for batch_size in batch_sizes:
66
+ start_time = time.time()
67
+ _, updated_templates = cluster.fit(
68
+ self.graphs, self.templates, "RC", "rc_sig", batch_size=batch_size
69
+ )
70
+ elapsed_time = time.time() - start_time
71
+
72
+ self.assertEqual(
73
+ len(updated_templates),
74
+ expected_template_count,
75
+ f"Failed for batch_size={batch_size}: expected "
76
+ + f"{expected_template_count} templates, got {len(updated_templates)}",
77
+ )
78
+ print(
79
+ f"Test for batch_size={batch_size} completed in {elapsed_time:.2f} seconds."
80
+ )
81
+
82
+ def test_fit_gml(self):
83
+ cluster = BatchCluster()
84
+ batch_sizes = [None, 10]
85
+ expected_template_count = (
86
+ 30 # Assuming this is the expected number of templates after processing
87
+ )
88
+
89
+ for batch_size in batch_sizes:
90
+ start_time = time.time()
91
+ _, updated_templates = cluster.fit(
92
+ self.graphs, self.templates, "RC", "rc_sig", batch_size=batch_size
93
+ )
94
+ elapsed_time = time.time() - start_time
95
+
96
+ self.assertEqual(
97
+ len(updated_templates),
98
+ expected_template_count,
99
+ f"Failed for batch_size={batch_size}: expected"
100
+ + f" {expected_template_count} templates, got {len(updated_templates)}",
101
+ )
102
+ print(
103
+ f"Test for batch_size={batch_size} completed in {elapsed_time:.2f} seconds."
104
+ )
105
+
106
+
107
+ # To run the tests
108
+ if __name__ == "__main__":
109
+ unittest.main()
@@ -0,0 +1,136 @@
1
+ import time
2
+ import unittest
3
+ from synkit.IO.data_io import load_from_pickle
4
+ from synkit.Graph.Cluster.graph_cluster import GraphCluster
5
+ from synkit.Graph.Feature.graph_descriptors import GraphDescriptor
6
+
7
+
8
+ class TestRCCluster(unittest.TestCase):
9
+
10
+ @classmethod
11
+ def setUpClass(cls):
12
+ # Load data once for all tests
13
+ cls.graphs = load_from_pickle("Data/Testcase/graph.pkl.gz")
14
+ for value in cls.graphs:
15
+ value = GraphDescriptor.get_descriptors(value)
16
+ cls.clusterer = GraphCluster()
17
+
18
+ def test_initialization(self):
19
+ """Test the initialization and configuration of the RCCluster."""
20
+ self.assertIsInstance(self.clusterer.nodeLabelNames, list)
21
+ self.assertEqual(self.clusterer.edgeAttribute, "order")
22
+ self.assertEqual(
23
+ len(self.clusterer.nodeLabelNames), len(self.clusterer.nodeLabelDefault)
24
+ )
25
+
26
+ def test_auto_cluster(self):
27
+ """Test the auto_cluster method functionality."""
28
+ rc = [value["RC"] for value in self.graphs]
29
+ cycles = [value["cycle"] for value in self.graphs]
30
+ signature = [value["signature_rc"] for value in self.graphs]
31
+ atom_count = [value["atom_count"] for value in self.graphs]
32
+ for att in [None, cycles, signature, atom_count]:
33
+ clusters, graph_to_cluster = self.clusterer.iterative_cluster(
34
+ rc,
35
+ att,
36
+ nodeMatch=self.clusterer.nodeMatch,
37
+ edgeMatch=self.clusterer.edgeMatch,
38
+ )
39
+ self.assertIsInstance(clusters, list)
40
+ self.assertIsInstance(graph_to_cluster, dict)
41
+ self.assertEqual(len(clusters), 30)
42
+
43
+ def test_auto_cluster_wrong_isomorphism(self):
44
+ rc = [value["RC"] for value in self.graphs]
45
+ cycles = [value["cycle"] for value in self.graphs]
46
+ signature = [value["signature_rc"] for value in self.graphs]
47
+ atom_count = [value["atom_count"] for value in self.graphs]
48
+
49
+ # cluster all
50
+ clusters, _ = self.clusterer.iterative_cluster(
51
+ rc, None, nodeMatch=None, edgeMatch=None
52
+ )
53
+ self.assertEqual(len(clusters), 8) # wrong value
54
+
55
+ # cluster with cycle
56
+ clusters, _ = self.clusterer.iterative_cluster(
57
+ rc, cycles, nodeMatch=None, edgeMatch=None
58
+ )
59
+ self.assertEqual(len(clusters), 8) # wrong value
60
+
61
+ # cluster with atom_count
62
+ clusters, _ = self.clusterer.iterative_cluster(
63
+ rc, atom_count, nodeMatch=None, edgeMatch=None
64
+ )
65
+ self.assertEqual(len(clusters), 27) # wrong value but almost correct
66
+
67
+ # cluster with signature
68
+ clusters, _ = self.clusterer.iterative_cluster(
69
+ rc, signature, nodeMatch=None, edgeMatch=None
70
+ )
71
+ self.assertEqual(len(clusters), 30) # correct by some magic. No proof for this
72
+
73
+ def test_fit(self):
74
+ """Test the fit method to ensure it correctly updates data entries with cluster indices."""
75
+
76
+ clustered_data = self.clusterer.fit(
77
+ self.graphs, rule_key="RC", attribute_key="atom_count"
78
+ )
79
+ max_class = 0
80
+ for item in clustered_data:
81
+ print(item["class"])
82
+ max_class = item["class"] if item["class"] >= max_class else max_class
83
+ # print(max_class)
84
+ self.assertIn("class", item)
85
+ self.assertEqual(max_class, 29) # 30 classes start from 0 so max is 29
86
+
87
+ def test_fit_gml(self):
88
+ """Test the fit method to ensure it correctly updates data entries with cluster indices."""
89
+
90
+ clustered_data = self.clusterer.fit(
91
+ self.graphs, rule_key="rc", attribute_key="atom_count"
92
+ )
93
+ max_class = 0
94
+ for item in clustered_data:
95
+ print(item["class"])
96
+ max_class = item["class"] if item["class"] >= max_class else max_class
97
+ # print(max_class)
98
+ self.assertIn("class", item)
99
+ self.assertEqual(max_class, 29) # 30 classes start from 0 so max is 29
100
+
101
+ def test_fit_time_compare(self):
102
+ attributes = {
103
+ "None": None,
104
+ "Cycles": "cycle",
105
+ "Signature": "signature_rc",
106
+ "Atom_count": "atom_count",
107
+ }
108
+
109
+ results = {}
110
+ for name, attr in attributes.items():
111
+ start_time = time.time()
112
+ clustered_data = self.clusterer.fit(
113
+ self.graphs, rule_key="RC", attribute_key=attr
114
+ )
115
+ elapsed_time = time.time() - start_time
116
+
117
+ # Optionally print out class information or verify correctness
118
+ max_class = max(item["class"] for item in clustered_data if "class" in item)
119
+
120
+ results[name] = elapsed_time
121
+
122
+ # Basic verification that 'class' is assigned and max class is as expected
123
+ self.assertTrue(all("class" in item for item in clustered_data))
124
+ self.assertEqual(
125
+ max_class, 29
126
+ ) # Ensure the maximum class index is as expected
127
+
128
+ # Compare results to check which attribute took the least/most time
129
+ min_time_attr = min(results, key=results.get)
130
+ max_time_attr = max(results, key=results.get)
131
+ self.assertIn(min_time_attr, ["Atom_count", "Signature"])
132
+ self.assertIn(max_time_attr, ["None", "Cycles"])
133
+
134
+
135
+ if __name__ == "__main__":
136
+ unittest.main()
@@ -0,0 +1,117 @@
1
+ import unittest
2
+ from synkit.IO.data_io import load_from_pickle
3
+ from synkit.Graph.Cluster.morphism import graph_isomorphism, rule_isomorphism
4
+
5
+
6
+ class TestRule(unittest.TestCase):
7
+
8
+ def setUp(self):
9
+ self.graphs = load_from_pickle("Data/Testcase/graph.pkl.gz")
10
+
11
+ self.small = """rule [
12
+ ruleID "Small"
13
+ left [
14
+ node [ id 1 label "H" ]
15
+ node [ id 2 label "O" ]
16
+ edge [ source 1 target 2 label "-" ]
17
+ ]
18
+ right [
19
+ node [ id 1 label "H+" ]
20
+ node [ id 2 label "O-" ]
21
+ ]
22
+ ]"""
23
+ self.large = """rule [
24
+ ruleID "Large"
25
+ left [
26
+ node [ id 1 label "H" ]
27
+ node [ id 2 label "O" ]
28
+ edge [ source 1 target 2 label "-" ]
29
+ ]
30
+ context [
31
+ node [ id 3 label "C" ]
32
+ edge [ source 2 target 3 label "-" ]
33
+ ]
34
+ right [
35
+ node [ id 1 label "H+" ]
36
+ node [ id 2 label "O-" ]
37
+ ]
38
+ ]"""
39
+
40
+ self.gml = (
41
+ "rule [\n"
42
+ ' ruleID "rule"\n'
43
+ " left [\n"
44
+ ' edge [ source 1 target 4 label "-" ]\n'
45
+ ' edge [ source 1 target 2 label "-" ]\n'
46
+ ' edge [ source 2 target 3 label "-" ]\n'
47
+ " ]\n"
48
+ " context [\n"
49
+ ' node [ id 1 label "C" ]\n'
50
+ ' node [ id 4 label "H" ]\n'
51
+ ' node [ id 2 label "C" ]\n'
52
+ ' node [ id 3 label "O" ]\n'
53
+ " ]\n"
54
+ " right [\n"
55
+ ' edge [ source 1 target 2 label "=" ]\n'
56
+ ' edge [ source 4 target 3 label "-" ]\n'
57
+ " ]\n"
58
+ "]"
59
+ )
60
+
61
+ self.gml_h = (
62
+ "rule [\n"
63
+ ' ruleID "rule"\n'
64
+ " left [\n"
65
+ ' edge [ source 1 target 2 label "-" ]\n'
66
+ ' edge [ source 1 target 3 label "-" ]\n'
67
+ ' edge [ source 3 target 4 label "-" ]\n'
68
+ " ]\n"
69
+ " context [\n"
70
+ ' node [ id 1 label "C" ]\n'
71
+ ' node [ id 2 label "H" ]\n'
72
+ ' node [ id 3 label "C" ]\n'
73
+ ' node [ id 4 label "O" ]\n'
74
+ ' node [ id 5 label "H" ]\n'
75
+ ' node [ id 6 label "H" ]\n'
76
+ ' node [ id 7 label "H" ]\n'
77
+ ' node [ id 8 label "H" ]\n'
78
+ ' node [ id 9 label "H" ]\n'
79
+ ' edge [ source 1 target 5 label "-" ]\n'
80
+ ' edge [ source 1 target 6 label "-" ]\n'
81
+ ' edge [ source 3 target 7 label "-" ]\n'
82
+ ' edge [ source 3 target 8 label "-" ]\n'
83
+ ' edge [ source 4 target 9 label "-" ]\n'
84
+ " ]\n"
85
+ " right [\n"
86
+ ' edge [ source 1 target 3 label "=" ]\n'
87
+ ' edge [ source 2 target 4 label "-" ]\n'
88
+ " ]\n"
89
+ "]"
90
+ )
91
+
92
+ def test_graph_isomorphism_true(self):
93
+ result = graph_isomorphism(
94
+ self.graphs[0]["RC"], self.graphs[3]["RC"], use_defaults=True
95
+ )
96
+ self.assertTrue(result)
97
+
98
+ def test_graph_isomorphism_false(self):
99
+ result = graph_isomorphism(
100
+ self.graphs[0]["RC"], self.graphs[1]["RC"], use_defaults=True
101
+ )
102
+ self.assertFalse(result)
103
+
104
+ def test_rule_isomorphism_isomorphism(self):
105
+ self.assertTrue(rule_isomorphism(self.small, self.small))
106
+ self.assertTrue(rule_isomorphism(self.large, self.large))
107
+ self.assertFalse(rule_isomorphism(self.small, self.large))
108
+
109
+ def test_rule_isomorphism_monomorphism(self):
110
+ # small is a subgraph of large
111
+ self.assertTrue(rule_isomorphism(self.small, self.large, "monomorphism"))
112
+ # large is not a subgraph of small
113
+ self.assertFalse(rule_isomorphism(self.large, self.small, "monomorphism"))
114
+
115
+
116
+ if __name__ == "__main__":
117
+ unittest.main()
@@ -0,0 +1,19 @@
1
+ import unittest
2
+ from synkit.ITS.aam_validator import AAMValidator
3
+ from synkit.ITS.its_expand import ITSExpand
4
+
5
+
6
+ class TestPartialExpand(unittest.TestCase):
7
+
8
+ def test_expand_aam_with_its(self):
9
+ input_rsmi = "CC[CH2:3][Cl:1].[NH2:2][H:4]>>CC[CH2:3][NH2:2].[Cl:1][H:4]"
10
+ output_rsmi = ITSExpand.expand_aam_with_its(input_rsmi, use_G=True)
11
+ expected_rsmi = (
12
+ "[CH3:1][CH2:2][CH2:3][Cl:4].[NH2:5][H:6]"
13
+ + ">>[CH3:1][CH2:2][CH2:3][NH2:5].[Cl:4][H:6]"
14
+ )
15
+ self.assertTrue(AAMValidator.smiles_check(output_rsmi, expected_rsmi, "ITS"))
16
+
17
+
18
+ if __name__ == "__main__":
19
+ unittest.main()
@@ -1,6 +1,7 @@
1
1
  import unittest
2
2
  import networkx as nx
3
3
  from synkit.ITS.normalize_aam import NormalizeAAM
4
+ from synkit.ITS.aam_validator import AAMValidator
4
5
 
5
6
 
6
7
  class TestNormalizeAAM(unittest.TestCase):
@@ -55,7 +56,8 @@ class TestNormalizeAAM(unittest.TestCase):
55
56
  + "([CH3:4])[H:6])[H:5]"
56
57
  )
57
58
  result = self.normalizer.fit(input_rsmi)
58
- self.assertEqual(result, expected_output)
59
+ print(result)
60
+ self.assertTrue(AAMValidator().smiles_check(result, expected_output))
59
61
 
60
62
 
61
63
  # Run the unittest
@@ -0,0 +1,73 @@
1
+ import unittest
2
+ from synkit.IO.data_io import load_database
3
+ from synkit.Chem.Reaction.standardize import Standardize
4
+ from synkit.IO.chem_converter import smart_to_gml
5
+ from synkit.Reactor.reactor_utils import _add_reagent, _find_all_paths
6
+ from synkit.Reactor.multi_steps import MultiSteps
7
+
8
+
9
+ class TestMultiStep(unittest.TestCase):
10
+ def setUp(self) -> None:
11
+ smarts = [
12
+ "[CH2:4]([CH:5]=[O:6])[H:7]>>[CH2:4]=[CH:5][O:6][H:7]",
13
+ (
14
+ "[CH2:2]=[O:3].[CH2:4]=[CH:5][O:6][H:7]>>[CH2:2]([O:3][H:7])[CH2:4]"
15
+ + "[CH:5]=[O:6]"
16
+ ),
17
+ "[CH2:4]([CH:5]=[O:6])[H:8]>>[CH2:4]=[CH:5][O:6][H:8]",
18
+ (
19
+ "[CH2:2]([OH:3])[CH:4]=[CH:5][O:6][H:8]>>[CH2:2]=[CH:4][CH:5]=[O:6]"
20
+ + ".[OH:3][H:8]"
21
+ ),
22
+ ]
23
+ self.gml = [smart_to_gml(value) for value in smarts]
24
+ self.order = [0, 1, 0, -1]
25
+ self.rsmi = "CC=O.CC=O.CCC=O>>CC=O.CC=C(C)C=O.O"
26
+ self.data = load_database("Data/Testcase/mech.json.gz")
27
+ self.rsmi_2 = Standardize().fit(self.data[0]["reaction"])
28
+
29
+ def test_perform_multi_step_reaction(self):
30
+ results, _ = MultiSteps._process(self.gml, self.order, self.rsmi)
31
+ self.assertEqual(len(results), 4)
32
+
33
+ def test_get_aam(self):
34
+ test = self.data[0]["mechanisms"][2]
35
+ rule = [
36
+ smart_to_gml(value["smart_string"], core=True, explicit_hydrogen=True)
37
+ for value in test["steps"]
38
+ ]
39
+ order = list(range(len(rule)))
40
+ rsmi = _add_reagent(self.rsmi, [test["cat"]])
41
+
42
+ results, reaction_tree = MultiSteps._process(rule, order, rsmi)
43
+ target_products = sorted(rsmi.split(">>")[1].split("."))
44
+ max_depth = len(results)
45
+ all_paths = _find_all_paths(reaction_tree, target_products, rsmi, max_depth)
46
+ real_path = all_paths[0][1:] # remove the original
47
+ all_steps = MultiSteps._get_aam(real_path, rule, order)
48
+ self.assertTrue(
49
+ all(m is not None for m in all_steps),
50
+ "All mechanism steps should have valid mappings",
51
+ )
52
+
53
+ self.assertEqual(len(all_steps), len(test["steps"]))
54
+
55
+ def test_multi_steps(self):
56
+ test = self.data[0]["mechanisms"][2]
57
+ rule = [
58
+ smart_to_gml(value["smart_string"], core=True, explicit_hydrogen=True)
59
+ for value in test["steps"]
60
+ ]
61
+ order = list(range(len(rule)))
62
+ all_steps = MultiSteps().multi_step(self.rsmi, rule, order, test["cat"])
63
+ print(all_steps)
64
+ self.assertTrue(
65
+ all(m is not None for m in all_steps),
66
+ "All mechanism steps should have valid mappings",
67
+ )
68
+
69
+ self.assertEqual(len(all_steps), len(test["steps"]))
70
+
71
+
72
+ if __name__ == "__main__":
73
+ unittest.main()