synkit 0.0.4__tar.gz → 0.0.6__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 (191) hide show
  1. synkit-0.0.6/.github/workflows/build-doc.yml +34 -0
  2. {synkit-0.0.4 → synkit-0.0.6}/.gitignore +4 -0
  3. synkit-0.0.6/Data/Figure/synkit.png +0 -0
  4. synkit-0.0.6/Makefile +20 -0
  5. {synkit-0.0.4 → synkit-0.0.6}/PKG-INFO +2 -2
  6. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Fingerprint/test_fp_calculator.py +9 -14
  7. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Cluster/test_graph_morphism.py +21 -1
  8. synkit-0.0.6/Test/Graph/Context/test_hier_context.py +173 -0
  9. synkit-0.0.6/Test/Graph/Context/test_radius_expand.py +108 -0
  10. {synkit-0.0.4/Test/Graph → synkit-0.0.6/Test/Graph/Hydrogen}/test_graph_hydrogen.py +1 -1
  11. synkit-0.0.6/Test/Graph/Hydrogen/test_hcomplete.py +77 -0
  12. synkit-0.0.6/doc/conf.py +30 -0
  13. synkit-0.0.6/doc/getting_started.rst +66 -0
  14. synkit-0.0.6/doc/index.rst +23 -0
  15. synkit-0.0.6/doc/references.rst +113 -0
  16. synkit-0.0.6/make.bat +35 -0
  17. {synkit-0.0.4 → synkit-0.0.6}/pyproject.toml +2 -3
  18. {synkit-0.0.4 → synkit-0.0.6}/requirements.txt +2 -3
  19. synkit-0.0.6/synkit/Chem/Fingerprint/fp_calculator.py +158 -0
  20. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Fingerprint/smiles_featurizer.py +3 -3
  21. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Molecule/standardize.py +15 -9
  22. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Cluster/batch_cluster.py +2 -2
  23. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Cluster/graph_cluster.py +1 -1
  24. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Cluster/graph_morphism.py +131 -0
  25. synkit-0.0.6/synkit/Graph/Context/hier_context.py +229 -0
  26. synkit-0.0.6/synkit/Graph/Context/radius_expand.py +246 -0
  27. synkit-0.0.4/synkit/Graph/hydrogen.py → synkit-0.0.6/synkit/Graph/Hyrogen/_misc.py +167 -3
  28. synkit-0.0.6/synkit/Graph/Hyrogen/hcomplete.py +357 -0
  29. synkit-0.0.6/synkit/Graph/Hyrogen/hextend.py +170 -0
  30. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/normalize_aam.py +1 -1
  31. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/chem_converter.py +1 -1
  32. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/debug.py +2 -1
  33. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/nx_to_gml.py +1 -1
  34. synkit-0.0.6/synkit/Rule/__init__.py +0 -0
  35. synkit-0.0.6/synkit/Rule/compose_rule.py +210 -0
  36. synkit-0.0.6/synkit/Rule/longest_path.py +95 -0
  37. synkit-0.0.6/synkit/Rule/prune_templates.py +77 -0
  38. synkit-0.0.6/synkit/Rule/rule_mapping.py +314 -0
  39. synkit-0.0.6/synkit/Rule/seq_comp.py +72 -0
  40. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/strip_rule.py +1 -1
  41. synkit-0.0.6/synkit/Utils/__init__.py +0 -0
  42. synkit-0.0.6/synkit/Vis/__init__.py +0 -0
  43. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/graph_visualizer.py +2 -1
  44. synkit-0.0.6/synkit/__init__.py +0 -0
  45. synkit-0.0.4/synkit/Chem/Fingerprint/fp_calculator.py +0 -108
  46. synkit-0.0.4/synkit/_misc.py +0 -181
  47. {synkit-0.0.4 → synkit-0.0.6}/.github/workflows/publish-package.yml +0 -0
  48. {synkit-0.0.4 → synkit-0.0.6}/.github/workflows/test-and-lint.yml +0 -0
  49. {synkit-0.0.4 → synkit-0.0.6}/CHANGELOG.md +0 -0
  50. {synkit-0.0.4 → synkit-0.0.6}/Data/Benchmark/conversion_time.json.gz +0 -0
  51. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  52. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  53. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  54. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  55. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/mech.json.gz +0 -0
  56. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/para_rule.json.gz +0 -0
  57. {synkit-0.0.4 → synkit-0.0.6}/Data/Testcase/para_rule_retro.json.gz +0 -0
  58. {synkit-0.0.4 → synkit-0.0.6}/Data/smart.json.gz +0 -0
  59. {synkit-0.0.4 → synkit-0.0.6}/LICENSE +0 -0
  60. {synkit-0.0.4 → synkit-0.0.6}/README.md +0 -0
  61. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Fingerprint/__init__.py +0 -0
  62. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  63. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  64. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Molecule/__init__.py +0 -0
  65. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Molecule/test_standardize.py +0 -0
  66. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/__init__.py +0 -0
  67. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  68. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_cleanning.py +0 -0
  69. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_deionize.py +0 -0
  70. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  71. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_neutralize.py +0 -0
  72. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_rsmi_utils.py +0 -0
  73. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_standardize.py +0 -0
  74. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  75. {synkit-0.0.4 → synkit-0.0.6}/Test/Chem/__init__.py +0 -0
  76. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Cluster/__init__.py +0 -0
  77. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Cluster/test_batch_cluster.py +0 -0
  78. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Cluster/test_graph_cluster.py +0 -0
  79. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Cluster/test_rule_morphism.py +0 -0
  80. {synkit-0.0.4/Test/Graph/Feature → synkit-0.0.6/Test/Graph/Context}/__init__.py +0 -0
  81. {synkit-0.0.4/Test/Graph/ITS → synkit-0.0.6/Test/Graph/Feature}/__init__.py +0 -0
  82. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  83. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_graph_fps.py +0 -0
  84. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_graph_signature.py +0 -0
  85. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_hash_fps.py +0 -0
  86. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  87. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/Feature/test_path_fps.py +0 -0
  88. {synkit-0.0.4/Test/Graph → synkit-0.0.6/Test/Graph/Hydrogen}/__init__.py +0 -0
  89. {synkit-0.0.4/Test/IO → synkit-0.0.6/Test/Graph/ITS}/__init__.py +0 -0
  90. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/ITS/test_aam_validator.py +0 -0
  91. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/ITS/test_its_construction.py +0 -0
  92. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/ITS/test_its_expand.py +0 -0
  93. {synkit-0.0.4 → synkit-0.0.6}/Test/Graph/ITS/test_normalize_aam.py +0 -0
  94. {synkit-0.0.4/Test/Reactor → synkit-0.0.6/Test/Graph}/__init__.py +0 -0
  95. {synkit-0.0.4/Test/Rule → synkit-0.0.6/Test/IO}/__init__.py +0 -0
  96. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_chemical_converter.py +0 -0
  97. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_dg_to_gml.py +0 -0
  98. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_gml_to_nx.py +0 -0
  99. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_graph_to_mol.py +0 -0
  100. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_mol_to_graph.py +0 -0
  101. {synkit-0.0.4 → synkit-0.0.6}/Test/IO/test_nx_to_gml.py +0 -0
  102. {synkit-0.0.4/Test/Vis → synkit-0.0.6/Test/Reactor}/__init__.py +0 -0
  103. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_core_engine.py +0 -0
  104. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_crn.py +0 -0
  105. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_multi_steps.py +0 -0
  106. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_path_finder.py +0 -0
  107. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_reactor_engine.py +0 -0
  108. {synkit-0.0.4 → synkit-0.0.6}/Test/Reactor/test_reactor_utils.py +0 -0
  109. {synkit-0.0.4/Test → synkit-0.0.6/Test/Rule}/__init__.py +0 -0
  110. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_molecule_rule.py +0 -0
  111. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_reactor_rule.py +0 -0
  112. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_retro_reactor.py +0 -0
  113. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_rule_compose.py +0 -0
  114. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_rule_rbl.py +0 -0
  115. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_rule_utils.py +0 -0
  116. {synkit-0.0.4 → synkit-0.0.6}/Test/Rule/test_valance_constrain.py +0 -0
  117. {synkit-0.0.4/synkit/Chem/Fingerprint → synkit-0.0.6/Test/Vis}/__init__.py +0 -0
  118. {synkit-0.0.4 → synkit-0.0.6}/Test/Vis/test_dpo_vis.py +0 -0
  119. {synkit-0.0.4 → synkit-0.0.6}/Test/Vis/test_embedding.py +0 -0
  120. {synkit-0.0.4/synkit/Chem/Molecule → synkit-0.0.6/Test}/__init__.py +0 -0
  121. {synkit-0.0.4 → synkit-0.0.6}/lint.sh +0 -0
  122. {synkit-0.0.4 → synkit-0.0.6}/pytest.sh +0 -0
  123. {synkit-0.0.4/synkit/Chem/Reaction → synkit-0.0.6/synkit/Chem/Fingerprint}/__init__.py +0 -0
  124. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  125. {synkit-0.0.4/synkit/Graph/Cluster → synkit-0.0.6/synkit/Chem/Molecule}/__init__.py +0 -0
  126. {synkit-0.0.4/synkit/Graph/Feature → synkit-0.0.6/synkit/Chem/Reaction}/__init__.py +0 -0
  127. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/balance_check.py +0 -0
  128. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/cleanning.py +0 -0
  129. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/deionize.py +0 -0
  130. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/fix_aam.py +0 -0
  131. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/neutralize.py +0 -0
  132. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/rsmi_utils.py +0 -0
  133. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/standardize.py +0 -0
  134. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/Reaction/tautomerize.py +0 -0
  135. {synkit-0.0.4 → synkit-0.0.6}/synkit/Chem/utils.py +0 -0
  136. {synkit-0.0.4/synkit/IO → synkit-0.0.6/synkit/Graph/Cluster}/__init__.py +0 -0
  137. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Cluster/rule_morphism.py +0 -0
  138. {synkit-0.0.4/synkit/Metrics → synkit-0.0.6/synkit/Graph/Context}/__init__.py +0 -0
  139. {synkit-0.0.4/synkit/Reactor → synkit-0.0.6/synkit/Graph/Feature}/__init__.py +0 -0
  140. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  141. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/graph_fps.py +0 -0
  142. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/graph_signature.py +0 -0
  143. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/hash_fps.py +0 -0
  144. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/morgan_fps.py +0 -0
  145. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/path_fps.py +0 -0
  146. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/Feature/wl_hash.py +0 -0
  147. {synkit-0.0.4/synkit/Rule → synkit-0.0.6/synkit/Graph/Hyrogen}/__init__.py +0 -0
  148. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/aam_utils.py +0 -0
  149. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/aam_validator.py +0 -0
  150. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/its_builder.py +0 -0
  151. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/its_construction.py +0 -0
  152. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/its_decompose.py +0 -0
  153. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/ITS/its_expand.py +0 -0
  154. {synkit-0.0.4 → synkit-0.0.6}/synkit/Graph/__init.py +0 -0
  155. {synkit-0.0.4/synkit/Utils → synkit-0.0.6/synkit/IO}/__init__.py +0 -0
  156. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/data_io.py +0 -0
  157. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/data_process.py +0 -0
  158. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/dg_to_gml.py +0 -0
  159. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/gml_to_nx.py +0 -0
  160. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/graph_to_mol.py +0 -0
  161. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/mol_to_graph.py +0 -0
  162. {synkit-0.0.4 → synkit-0.0.6}/synkit/IO/smiles_to_id.py +0 -0
  163. {synkit-0.0.4/synkit/Vis → synkit-0.0.6/synkit/Metrics}/__init__.py +0 -0
  164. {synkit-0.0.4 → synkit-0.0.6}/synkit/Metrics/_base.py +0 -0
  165. {synkit-0.0.4 → synkit-0.0.6}/synkit/Metrics/_plot.py +0 -0
  166. {synkit-0.0.4 → synkit-0.0.6}/synkit/Metrics/_ranking.py +0 -0
  167. {synkit-0.0.4/synkit → synkit-0.0.6/synkit/Reactor}/__init__.py +0 -0
  168. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/core_engine.py +0 -0
  169. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/crn.py +0 -0
  170. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/dcrn.py +0 -0
  171. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/multi_steps.py +0 -0
  172. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/path_finder.py +0 -0
  173. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/reactor_engine.py +0 -0
  174. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/reactor_utils.py +0 -0
  175. {synkit-0.0.4 → synkit-0.0.6}/synkit/Reactor/single_predictor.py +0 -0
  176. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/MaxValence.json.gz +0 -0
  177. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/molecule_rule.py +0 -0
  178. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/reactor_rule.py +0 -0
  179. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/retro_reactor.py +0 -0
  180. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/rule_compose.py +0 -0
  181. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/rule_rbl.py +0 -0
  182. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/rule_utils.py +0 -0
  183. {synkit-0.0.4 → synkit-0.0.6}/synkit/Rule/valence_constrain.py +0 -0
  184. {synkit-0.0.4 → synkit-0.0.6}/synkit/Utils/utils.py +0 -0
  185. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/chemical_graph_visualizer.py +0 -0
  186. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/chemical_reaction_visualizer.py +0 -0
  187. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/chemical_space.py +0 -0
  188. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/dpo_vis.py +0 -0
  189. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/embedding.py +0 -0
  190. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/pdf_writer.py +0 -0
  191. {synkit-0.0.4 → synkit-0.0.6}/synkit/Vis/rsmi_to_fig.py +0 -0
@@ -0,0 +1,34 @@
1
+ name: Build documentation for GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ workflow_dispatch: # Enable manual action trigger
9
+
10
+ env:
11
+ PYTHONPATH: .
12
+
13
+ jobs:
14
+ build_documentation_for_github_pages:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v3
18
+ with:
19
+ fetch-depth: 0
20
+ - uses: actions/setup-python@v4
21
+ with:
22
+ python-version: '3.11'
23
+ cache: 'pip' # caching pip dependencies
24
+ - run: pip install -r requirements.txt
25
+ - run: pip install sphinx sphinx-rtd-theme
26
+ - run: python3 -m sphinx ./doc docs
27
+ - name: publish doc
28
+ shell: bash
29
+ run: |
30
+ git config user.name "GitHub Action"
31
+ git config user.email "no@mail.tmp"
32
+ git add -f docs/
33
+ git commit -m "Doc build"
34
+ git push --force "https://github.com/TieuLongPhan/SynKit.git" `git subtree split --prefix docs/ main`:gh-pages
@@ -16,3 +16,7 @@ Data/Benchmark/its.json.gz
16
16
  Data/Benchmark/rc.json.gz
17
17
  *.log
18
18
  .coverage
19
+ dev/*
20
+ *.rdf
21
+ Data/Testcase/hydro/aam.json.gz
22
+ synkit/Chem/FG/*
Binary file
synkit-0.0.6/Makefile ADDED
@@ -0,0 +1,20 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line, and also
5
+ # from the environment for the first two.
6
+ SPHINXOPTS ?=
7
+ SPHINXBUILD ?= sphinx-build
8
+ SOURCEDIR = source
9
+ BUILDDIR = build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ # Catch-all target: route all unknown targets to Sphinx using the new
18
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19
+ %: Makefile
20
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.4
3
+ Version: 0.0.6
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
@@ -19,9 +19,9 @@ Requires-Dist: regex>=2024.11.6
19
19
  Requires-Dist: requests>=2.32.3
20
20
  Requires-Dist: scikit-learn>=1.4.0
21
21
  Requires-Dist: seaborn>=0.13.2
22
- Requires-Dist: torch>=2.2.0
23
22
  Provides-Extra: all
24
23
  Requires-Dist: numpy>=2.2.0; extra == 'all'
24
+ Requires-Dist: torch>=2.2.0; extra == 'all'
25
25
  Description-Content-Type: text/markdown
26
26
 
27
27
  # SynKit
@@ -1,13 +1,11 @@
1
1
  import unittest
2
- import pandas as pd
3
-
4
2
  from synkit.Chem.Fingerprint.fp_calculator import FPCalculator
5
3
 
6
4
 
7
5
  class TestFPCalculator(unittest.TestCase):
8
6
  def setUp(self):
9
7
  # Sample data setup
10
- self.data = pd.DataFrame(
8
+ self.data = [
11
9
  {
12
10
  "smiles": [
13
11
  (
@@ -25,39 +23,36 @@ class TestFPCalculator(unittest.TestCase):
25
23
  ],
26
24
  "ID": [1, 2, 3],
27
25
  }
28
- )
29
- self.smiles_column = "smiles"
26
+ ]
27
+ self.smiles_key = "smiles"
30
28
  self.fp_type = "drfp"
31
29
  self.n_jobs = 2
32
30
  self.verbose = 0
33
- self.save_path = None
34
31
 
35
32
  # Instantiate the FPCalculator
36
33
  self.fp_calculator = FPCalculator(
37
- data=self.data,
38
- smiles_column=self.smiles_column,
34
+ smiles_key=self.smiles_key,
39
35
  fp_type=self.fp_type,
40
36
  n_jobs=self.n_jobs,
41
37
  verbose=self.verbose,
42
- save_path=self.save_path,
43
38
  )
44
39
 
45
40
  def test_init_invalid_fp_type(self):
46
41
  with self.assertRaises(ValueError):
47
- FPCalculator(data=self.data, fp_type="invalid_type")
42
+ FPCalculator(smiles_key=self.smiles_key, fp_type="invalid_type")
48
43
 
49
44
  def test_fit_missing_column(self):
50
45
  with self.assertRaises(ValueError):
51
46
  fp_calculator = FPCalculator(
52
- data=pd.DataFrame({"not_smiles": ["C"]}), smiles_column="smiles"
47
+ smiles_key=self.smiles_key,
48
+ fp_type=self.fp_type,
53
49
  )
54
- fp_calculator.fit()
50
+ fp_calculator.dict_process({"not_smiles": ["C"]}, "smiles")
55
51
 
56
52
  def test_constructor_and_attribute_assignment(self):
57
- self.assertEqual(self.fp_calculator.smiles_column, "smiles")
53
+ self.assertEqual(self.fp_calculator.smiles_key, "smiles")
58
54
  self.assertEqual(self.fp_calculator.fp_type, "drfp")
59
55
  self.assertEqual(self.fp_calculator.n_jobs, 2)
60
- self.assertIsNone(self.fp_calculator.save_path)
61
56
 
62
57
 
63
58
  if __name__ == "__main__":
@@ -2,7 +2,12 @@ import unittest
2
2
  from synkit.IO.data_io import load_from_pickle
3
3
  from synkit.IO.chem_converter import rsmi_to_its
4
4
  from synkit.Graph.ITS.its_decompose import get_rc
5
- from synkit.Graph.Cluster.graph_morphism import graph_isomorphism, subgraph_isomorphism
5
+ from synkit.Graph.Cluster.graph_morphism import (
6
+ graph_isomorphism,
7
+ subgraph_isomorphism,
8
+ maximum_connected_common_subgraph,
9
+ heuristics_MCCS,
10
+ )
6
11
 
7
12
 
8
13
  class TestGraphMorphism(unittest.TestCase):
@@ -46,6 +51,21 @@ class TestGraphMorphism(unittest.TestCase):
46
51
  result = subgraph_isomorphism(self.rc, self.its, check_type="induced")
47
52
  self.assertFalse(result)
48
53
 
54
+ def test_maximum_connected_common_subgraph(self):
55
+ mcs = maximum_connected_common_subgraph(
56
+ self.graphs[0]["RC"], self.graphs[1]["RC"]
57
+ )
58
+ self.assertEqual(mcs.number_of_nodes(), 3)
59
+ self.assertGreater(
60
+ self.graphs[0]["RC"].number_of_nodes(), mcs.number_of_nodes()
61
+ )
62
+
63
+ def test_heuristics_MCCS(self):
64
+ graphs = [value["RC"] for value in self.graphs]
65
+ mcs = heuristics_MCCS(graphs[:3])
66
+ self.assertEqual(mcs.number_of_nodes(), 1)
67
+ self.assertGreater(graphs[0].number_of_nodes(), mcs.number_of_nodes())
68
+
49
69
 
50
70
  if __name__ == "__main__":
51
71
  unittest.main()
@@ -0,0 +1,173 @@
1
+ import unittest
2
+ from copy import deepcopy
3
+ from synkit.IO.data_io import load_from_pickle
4
+ from synkit.Graph.Context.hier_context import HierContext
5
+
6
+
7
+ class TestRuleCluster(unittest.TestCase):
8
+ def setUp(self) -> None:
9
+ # Create an instance of HierContext with max_radius=3.
10
+ self.cluster = HierContext(max_radius=3)
11
+ # Load test data from a pickle file.
12
+ # Ensure this file exists at the specified location: "Data/Testcase/graph.pkl.gz"
13
+ self.data = load_from_pickle("Data/Testcase/graph.pkl.gz")
14
+ # Verify that the loaded data is a non-empty list.
15
+ self.assertIsInstance(self.data, list)
16
+ self.assertGreater(len(self.data), 0, "Loaded test data should not be empty.")
17
+
18
+ def test_group_class(self):
19
+ """
20
+ Tests the _group_class function by grouping dictionaries based on a key.
21
+ """
22
+ data = [
23
+ {"a": 1, "value": "x"},
24
+ {"a": 2, "value": "y"},
25
+ {"a": 1, "value": "z"},
26
+ {"a": 3, "value": "w"},
27
+ ]
28
+ grouped = HierContext._group_class(data, "a")
29
+ expected = {
30
+ 1: [{"a": 1, "value": "x"}, {"a": 1, "value": "z"}],
31
+ 2: [{"a": 2, "value": "y"}],
32
+ 3: [{"a": 3, "value": "w"}],
33
+ }
34
+ self.assertEqual(
35
+ grouped,
36
+ expected,
37
+ "The grouped output does not match the expected grouping.",
38
+ )
39
+
40
+ def test_update_child_idx(self):
41
+ """
42
+ Tests the _update_child_idx function to ensure that child IDs are correctly added
43
+ based on parent–cluster relationships.
44
+ """
45
+ # Create a two-layer hierarchy:
46
+ # Layer 0: One node with 'class' = 1.
47
+ # Layer 1: Two nodes with 'class' = 2 and 3, each having Parent set to 1.
48
+ layer0 = [{"class": 1}]
49
+ layer1 = [{"class": 2, "Parent": 1}, {"class": 3, "Parent": 1}]
50
+ data = [layer0, layer1]
51
+ updated = HierContext._update_child_idx(data, cls_id="class")
52
+
53
+ # The node from layer 0 should have its "Child" field updated with the class IDs from layer 1.
54
+ self.assertIn("Child", updated[0][0])
55
+ self.assertEqual(
56
+ set(updated[0][0]["Child"]),
57
+ {2, 3},
58
+ "The 'Child' list of the parent node does not contain the expected child class IDs.",
59
+ )
60
+
61
+ def test_process(self):
62
+ """
63
+ Tests the _process function which extracts a context, computes a hash,
64
+ and clusters the data.
65
+ """
66
+ # Use the loaded test data.
67
+ data = deepcopy(self.data)
68
+ cluster_results, templates = HierContext._process(
69
+ data,
70
+ k=1,
71
+ its_key="ITS",
72
+ context_key="K",
73
+ cls_func=self.cluster.cluster,
74
+ )
75
+
76
+ # Validate that the first processed data entry has updated keys.
77
+ self.assertIn(
78
+ "R_1", cluster_results[0], "Cluster result should include the key 'R_1'."
79
+ )
80
+ self.assertIn(
81
+ "K",
82
+ cluster_results[0],
83
+ "Cluster result should include the context key 'K'.",
84
+ )
85
+ self.assertIsInstance(
86
+ cluster_results[0]["R_1"],
87
+ int,
88
+ "Cluster identifier 'R_1' should be an integer.",
89
+ )
90
+
91
+ # Check that templates are processed correctly.
92
+ # Depending on your dummy clustering behavior, update these expected values.
93
+ self.assertGreater(len(templates), 0, "Templates list should not be empty.")
94
+ self.assertEqual(
95
+ templates[0]["class"],
96
+ 0,
97
+ "Expected the first template's 'class' to be 0 after processing.",
98
+ )
99
+ # If you expect a specific number of templates from your test data, verify it.
100
+ # Here, we expect 61 templates. Change if necessary.
101
+ self.assertEqual(len(templates), 61, "Unexpected number of templates produced.")
102
+ self.assertIn("K", templates[0], "Template should contain the context key 'K'.")
103
+
104
+ def test_process_level(self):
105
+ """
106
+ Tests the _process_level function by grouping data based on parent cluster IDs,
107
+ processing each group, and then verifying that cluster keys and templates are updated.
108
+ """
109
+ # Use the loaded test data for processing at a child level.
110
+ data = deepcopy(self.data)
111
+ cluster_indices, templates = self.cluster._process_level(
112
+ data, "ITS", "K", self.cluster.cluster, 1
113
+ )
114
+
115
+ # Check that each clustered result has the "R_1" field.
116
+ self.assertIn(
117
+ "R_1",
118
+ cluster_indices[0],
119
+ "Each cluster result should have the 'R_1' field for level 1 clustering.",
120
+ )
121
+ self.assertIn(
122
+ "K", cluster_indices[0], "Each cluster result should contain the key 'K'."
123
+ )
124
+ # Validate that templates include the necessary keys.
125
+ self.assertIn("K", templates[0], "Each template should contain the key 'K'.")
126
+ self.assertIn(
127
+ "Parent",
128
+ templates[0],
129
+ "Each template should contain the key 'Parent' to link to a parent cluster.",
130
+ )
131
+
132
+ def test_fit(self):
133
+ """
134
+ Tests the fit method which processes a list of graph data entries, classifying each based on
135
+ hierarchical clustering, and verifies that cluster indices and templates are updated at multiple levels.
136
+ """
137
+ data, templates = self.cluster.fit(self.data, its_key="ITS", context_key="K")
138
+
139
+ # Check that each data entry has keys for the expected levels.
140
+ for level in range(4): # Levels 0, 1, 2, 3 (since max_radius=3)
141
+ self.assertIn(
142
+ f"R_{level}",
143
+ data[0],
144
+ f"Data entry should include the key 'R_{level}' after processing.",
145
+ )
146
+
147
+ # Check that a template list exists for each hierarchical level.
148
+ self.assertEqual(
149
+ len(templates), 4, "There should be 4 levels of templates (0, 1, 2, 3)."
150
+ )
151
+
152
+ # Validate that the first template of the parent level contains required keys.
153
+ self.assertIn(
154
+ "K",
155
+ templates[0][0],
156
+ "The first template in level 0 should contain the key 'K'.",
157
+ )
158
+ self.assertIn(
159
+ "Child",
160
+ templates[0][0],
161
+ "The first template in level 0 should contain the key 'Child' if child relationships were established.",
162
+ )
163
+ # Validate that a template in level 1 contains a Parent key.
164
+ if len(templates) > 1 and templates[1]:
165
+ self.assertIn(
166
+ "Parent",
167
+ templates[1][0],
168
+ "Templates at level 1 should contain the key 'Parent' linking to the parent cluster.",
169
+ )
170
+
171
+
172
+ if __name__ == "__main__":
173
+ unittest.main()
@@ -0,0 +1,108 @@
1
+ import unittest
2
+ import networkx as nx
3
+ from synkit.IO.data_io import load_from_pickle
4
+ from synkit.Graph.ITS.its_decompose import get_rc
5
+ from synkit.Graph.Context.radius_expand import RadiusExpand
6
+ from synkit.Graph.Cluster.graph_morphism import graph_isomorphism, subgraph_isomorphism
7
+
8
+
9
+ class TestRadiusExpand(unittest.TestCase):
10
+
11
+ def setUp(self):
12
+ self.data = load_from_pickle("Data/Testcase/graph.pkl.gz")
13
+
14
+ def test_find_unequal_order_edges(self):
15
+ # Create a graph with edges that include various 'order' and 'standard_order' attributes.
16
+ G = nx.Graph()
17
+ # Edge qualifies: unequal order and standard_order != 0.
18
+ G.add_edge(1, 2, order=(1, 2), standard_order=1)
19
+ # Edge does not qualify: equal order.
20
+ G.add_edge(2, 3, order=(1, 1), standard_order=1)
21
+ # Edge qualifies.
22
+ G.add_edge(3, 4, order=(2, 3), standard_order=1)
23
+ # Edge does not qualify because standard_order == 0.
24
+ G.add_edge(1, 3, order=(1, 2), standard_order=0)
25
+ result = set(RadiusExpand.find_unequal_order_edges(G))
26
+ expected = {1, 2, 3, 4}
27
+ self.assertEqual(result, expected)
28
+
29
+ def test_extract_subgraph(self):
30
+ # Create a test graph
31
+ G = nx.Graph()
32
+ G.add_edges_from([(1, 2), (2, 3), (3, 4)])
33
+
34
+ # Node indices for subgraph extraction
35
+ node_indices = [2, 3]
36
+
37
+ # Extract subgraph and test
38
+ subgraph = RadiusExpand.extract_subgraph(G, node_indices)
39
+ self.assertTrue(nx.is_isomorphic(subgraph, nx.Graph([(2, 3)])))
40
+
41
+ def test_longest_radius_extension(self):
42
+ # Create a graph where a chain of edges with standard_order == 0 exists.
43
+ G = nx.Graph()
44
+ G.add_edge(0, 1, standard_order=0)
45
+ G.add_edge(1, 2, standard_order=0)
46
+ # This edge is blocked because standard_order != 0.
47
+ G.add_edge(2, 3, standard_order=1)
48
+ # Compute longest extension from node 0.
49
+ path = RadiusExpand.longest_radius_extension(G, [0])
50
+ self.assertEqual(path, [0, 1, 2])
51
+
52
+ # Add an alternative branch: from node 1 to node 4.
53
+ G.add_edge(1, 4, standard_order=0)
54
+ # Now, DFS might find either [0, 1, 2] or [0, 1, 4]. Both have length 3.
55
+ path2 = RadiusExpand.longest_radius_extension(G, [0])
56
+ self.assertEqual(len(path2), 3)
57
+ self.assertTrue(set(path2).issubset(set(G.nodes())))
58
+
59
+ def test_remove_normal_edges(self):
60
+ # Build a graph with a custom attribute 'weight' and remove those edges where weight == 0.
61
+ G = nx.Graph()
62
+ G.add_edge(1, 2, weight=0)
63
+ G.add_edge(2, 3, weight=1)
64
+ G.add_edge(3, 4, weight=0)
65
+ G.add_edge(4, 5, weight=2)
66
+ modified = RadiusExpand.remove_normal_edges(G, "weight")
67
+ # Expected to keep only edges where weight is not 0: (2,3) and (4,5).
68
+ expected_edges = {(2, 3), (4, 5)}
69
+ self.assertEqual(set(modified.edges()), expected_edges)
70
+
71
+ def test_extract_k(self):
72
+ its = self.data[0]["ITS"]
73
+ rc = get_rc(its)
74
+ context = RadiusExpand.extract_k(its, 0)
75
+ self.assertTrue(graph_isomorphism(rc, context, use_defaults=True))
76
+
77
+ # Test with a positive n_knn value.
78
+ context = RadiusExpand.extract_k(its, 1)
79
+ self.assertTrue(subgraph_isomorphism(rc, context))
80
+ self.assertTrue(subgraph_isomorphism(context, its))
81
+
82
+ # Test with n_knn == -1 to automatically determine neighbor expansion.
83
+ context = RadiusExpand.extract_k(its, -1)
84
+ self.assertTrue(graph_isomorphism(its, context, use_defaults=True))
85
+
86
+ def test_context_extraction(self):
87
+ data = self.data[0]
88
+ result = RadiusExpand.context_extraction(
89
+ data, its_key="ITS", context_key="K", n_knn=1
90
+ )
91
+ self.assertIn("K", result)
92
+ self.assertTrue(subgraph_isomorphism(result["RC"], result["K"]))
93
+ self.assertTrue(subgraph_isomorphism(result["K"], result["ITS"]))
94
+
95
+ def test_paralle_context_extraction(self):
96
+ data_list = self.data[0:3]
97
+ results = RadiusExpand.paralle_context_extraction(
98
+ data_list, its_key="ITS", context_key="K", n_jobs=1, verbose=0, n_knn=1
99
+ )
100
+ self.assertEqual(len(results), 3)
101
+ for res in results:
102
+ self.assertIn("K", res)
103
+ self.assertTrue(subgraph_isomorphism(res["RC"], res["K"]))
104
+ self.assertTrue(subgraph_isomorphism(res["K"], res["ITS"]))
105
+
106
+
107
+ if __name__ == "__main__":
108
+ unittest.main()
@@ -2,7 +2,7 @@ import unittest
2
2
  from synkit.Graph.ITS.aam_validator import AAMValidator
3
3
  from synkit.Graph.Cluster.graph_morphism import graph_isomorphism
4
4
  from synkit.IO.chem_converter import rsmi_to_graph, graph_to_rsmi
5
- from synkit.Graph.hydrogen import implicit_hydrogen, explicit_hydrogen
5
+ from synkit.Graph.Hyrogen._misc import implicit_hydrogen, explicit_hydrogen
6
6
 
7
7
 
8
8
  class TestGraphH(unittest.TestCase):
@@ -0,0 +1,77 @@
1
+ import unittest
2
+ import networkx as nx
3
+ from copy import deepcopy
4
+ from synkit.IO.data_io import load_from_pickle
5
+ from synkit.Graph.ITS.its_decompose import its_decompose
6
+ from synkit.Graph.Hyrogen.hcomplete import HComplete
7
+
8
+
9
+ class TestHComplete(unittest.TestCase):
10
+
11
+ def setUp(self):
12
+ """Setup before each test."""
13
+ # Create sample graphs
14
+ self.data = load_from_pickle("./Data/Testcase/hydro/hydrogen_test.pkl.gz")
15
+
16
+ def test_process_single_graph_data_success(self):
17
+ """Test the process_single_graph_data method."""
18
+ processed_data = HComplete.process_single_graph_data(self.data[0], "ITS", "RC")
19
+ self.assertTrue(isinstance(processed_data["ITS"], nx.Graph))
20
+ self.assertTrue(isinstance(processed_data["RC"], nx.Graph))
21
+
22
+ def test_process_single_graph_data_fail(self):
23
+ """Test the process_single_graph_data method."""
24
+ processed_data = HComplete.process_single_graph_data(self.data[16], "ITS", "RC")
25
+ self.assertIsNone(processed_data["ITS"])
26
+ self.assertIsNone(processed_data["RC"])
27
+
28
+ def test_process_single_graph_data_empty_graph(self):
29
+ """Test that an empty graph results in empty ITSGraph and GraphRules."""
30
+ empty_graph_data = {
31
+ "ITS": None,
32
+ "RC": None,
33
+ }
34
+
35
+ processed_data = HComplete.process_single_graph_data(
36
+ empty_graph_data, "ITSGraph"
37
+ )
38
+
39
+ # Ensure the result is None or empty as expected for an empty graph
40
+ self.assertIsNone(processed_data["ITS"])
41
+ self.assertIsNone(processed_data["RC"])
42
+
43
+ def test_process_graph_data_parallel(self):
44
+ """Test the process_graph_data_parallel method."""
45
+ result = HComplete().process_graph_data_parallel(
46
+ self.data,
47
+ "ITS",
48
+ "RC",
49
+ n_jobs=1,
50
+ verbose=0,
51
+ )
52
+ result = [value for value in result if value["ITS"]]
53
+ # Check if the result matches the input data structure
54
+ self.assertEqual(len(result), 45) # 45 valid graphs
55
+
56
+ def test_process_multiple_hydrogens(self):
57
+ """Test the process_multiple_hydrogens method."""
58
+ graphs = deepcopy(self.data[0])
59
+ its = graphs["ITS"]
60
+ react_graph, prod_graph = its_decompose(its)
61
+
62
+ result = HComplete.process_multiple_hydrogens(
63
+ graphs,
64
+ "ITS",
65
+ "RC",
66
+ react_graph,
67
+ prod_graph,
68
+ ignore_aromaticity=False,
69
+ balance_its=True,
70
+ )
71
+
72
+ self.assertTrue(isinstance(result["ITS"], nx.Graph))
73
+ self.assertTrue(isinstance(result["RC"], nx.Graph))
74
+
75
+
76
+ if __name__ == "__main__":
77
+ unittest.main()
@@ -0,0 +1,30 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # For the full list of built-in configuration values, see the documentation:
4
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
+
6
+ # -- Project information -----------------------------------------------------
7
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
+
9
+ project = "synkit"
10
+ copyright = "2025, Tieu-Long Phan"
11
+ author = "Tieu-Long Phan"
12
+
13
+ # -- General configuration ---------------------------------------------------
14
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
15
+
16
+ extensions = [
17
+ "sphinx.ext.autodoc",
18
+ "sphinx.ext.autosectionlabel",
19
+ "sphinx.ext.githubpages",
20
+ ]
21
+
22
+ templates_path = ["_templates"]
23
+ exclude_patterns = []
24
+
25
+
26
+ # -- Options for HTML output -------------------------------------------------
27
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
28
+
29
+ html_theme = "sphinx_rtd_theme"
30
+ html_static_path = ["_static"]
@@ -0,0 +1,66 @@
1
+ .. _getting-started-synkit:
2
+
3
+ ===============
4
+ Getting Started
5
+ ===============
6
+
7
+ Welcome to the **synkit** documentation. This guide will assist you in setting up **synkit**, a versatile toolkit designed to streamline and enhance your workflows.
8
+
9
+ Introduction
10
+ ------------
11
+
12
+ **synkit** is designed to provide tools and functions that simplify complex tasks and improve productivity. It is essential for professionals looking to optimize their processes efficiently.
13
+
14
+ Installation
15
+ ============
16
+
17
+ Installing **synkit** is straightforward using pip, the Python package installer. For a more controlled and conflict-free environment, we recommend setting up **synkit** within a virtual environment.
18
+
19
+ Requirements
20
+ ------------
21
+
22
+ Before installing **synkit**, ensure you have Python 3.11 or later installed on your machine. The use of a virtual environment is recommended to avoid conflicts with other Python packages.
23
+
24
+ Creating a Virtual Environment (Optional)
25
+ -----------------------------------------
26
+
27
+ Setting up a virtual environment for **synkit** is an optional but recommended step. It isolates the installation and dependencies of **synkit** from other Python projects, which helps prevent conflicts and maintain a clean workspace.
28
+
29
+ 1. **Create a virtual environment**:
30
+
31
+ Use the following command to create a virtual environment named `synkit_env`. This step requires the `conda` package manager, which can be installed via the Anaconda distribution.
32
+
33
+ .. code-block:: bash
34
+
35
+ conda create -n synkit_env python=3.11
36
+
37
+ 2. **Activate the virtual environment**:
38
+
39
+ Once the environment is created, activate it using:
40
+
41
+ .. code-block:: bash
42
+
43
+ conda activate synkit_env
44
+
45
+ Installing **synkit** via Pip
46
+ ---------------------------------
47
+
48
+ After setting up and activating your virtual environment, install **synkit** using pip by running the following command:
49
+
50
+ .. code-block:: bash
51
+
52
+ pip install synkit
53
+
54
+ This command will download and install the latest version of **synkit** along with its required dependencies.
55
+
56
+
57
+ Further Resources
58
+ =================
59
+
60
+ For more detailed documentation, usage examples, and API guides, visit the [official **synkit** documentation](https://tieulongphan.github.io/SynKit).
61
+
62
+ Support
63
+ -------
64
+
65
+ If you encounter any issues or require assistance, please refer to the community support forums or file an issue on the [**synkit** GitHub page](https://github.com/TieuLongPhan/SynKit/issues).
66
+
@@ -0,0 +1,23 @@
1
+ .. synutility documentation master file, created by
2
+ sphinx-quickstart on Fri Oct 18 11:47:09 2024.
3
+ You can adapt this file completely to your liking, but it should at least
4
+ contain the root `toctree` directive.
5
+
6
+ Welcome to synkit documentation!
7
+ ====================================
8
+
9
+ Add your content using ``reStructuredText`` syntax. See the
10
+ `reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
11
+ documentation for details.
12
+
13
+
14
+ .. toctree::
15
+ :maxdepth: 2
16
+
17
+ getting_started
18
+ io
19
+ graph
20
+ reactor
21
+ rule
22
+ references
23
+