polynet 1.0.1__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 (149) hide show
  1. polynet-1.0.1/LICENSE +21 -0
  2. polynet-1.0.1/PKG-INFO +320 -0
  3. polynet-1.0.1/README.md +279 -0
  4. polynet-1.0.1/polynet/__init__.py +8 -0
  5. polynet-1.0.1/polynet/app/Welcome_to_PolyNet.py +82 -0
  6. polynet-1.0.1/polynet/app/components/experiments.py +22 -0
  7. polynet-1.0.1/polynet/app/components/figure_export.py +87 -0
  8. polynet-1.0.1/polynet/app/components/forms/analyse_results.py +825 -0
  9. polynet-1.0.1/polynet/app/components/forms/applicability_domain.py +108 -0
  10. polynet-1.0.1/polynet/app/components/forms/create_experiment.py +501 -0
  11. polynet-1.0.1/polynet/app/components/forms/explain_model.py +684 -0
  12. polynet-1.0.1/polynet/app/components/forms/explain_tml.py +481 -0
  13. polynet-1.0.1/polynet/app/components/forms/explanation_plots.py +441 -0
  14. polynet-1.0.1/polynet/app/components/forms/fragment_plots.py +187 -0
  15. polynet-1.0.1/polynet/app/components/forms/plot_customiser.py +102 -0
  16. polynet-1.0.1/polynet/app/components/forms/representation.py +816 -0
  17. polynet-1.0.1/polynet/app/components/forms/search_grid.py +158 -0
  18. polynet-1.0.1/polynet/app/components/forms/train_models.py +1180 -0
  19. polynet-1.0.1/polynet/app/components/graph_display.py +197 -0
  20. polynet-1.0.1/polynet/app/components/optional_dependencies.py +50 -0
  21. polynet-1.0.1/polynet/app/components/plots.py +238 -0
  22. polynet-1.0.1/polynet/app/options/allowable_sets.py +3 -0
  23. polynet-1.0.1/polynet/app/options/file_paths.py +80 -0
  24. polynet-1.0.1/polynet/app/options/plot.py +21 -0
  25. polynet-1.0.1/polynet/app/options/state_keys.py +331 -0
  26. polynet-1.0.1/polynet/app/pages/1_Create_Experiment.py +108 -0
  27. polynet-1.0.1/polynet/app/pages/2_Representation.py +202 -0
  28. polynet-1.0.1/polynet/app/pages/3_Train_Models.py +560 -0
  29. polynet-1.0.1/polynet/app/pages/4_Predict.py +264 -0
  30. polynet-1.0.1/polynet/app/pages/5_Explain_Models.py +295 -0
  31. polynet-1.0.1/polynet/app/pages/6_Analyse_Results.py +203 -0
  32. polynet-1.0.1/polynet/app/services/cache.py +47 -0
  33. polynet-1.0.1/polynet/app/services/configurations.py +9 -0
  34. polynet-1.0.1/polynet/app/services/experiments.py +3 -0
  35. polynet-1.0.1/polynet/app/services/explain_model.py +282 -0
  36. polynet-1.0.1/polynet/app/services/explain_source.py +143 -0
  37. polynet-1.0.1/polynet/app/services/explain_tml.py +212 -0
  38. polynet-1.0.1/polynet/app/services/explanation_results.py +345 -0
  39. polynet-1.0.1/polynet/app/services/graph_viz.py +494 -0
  40. polynet-1.0.1/polynet/app/services/model_training.py +19 -0
  41. polynet-1.0.1/polynet/app/services/predict_model.py +3 -0
  42. polynet-1.0.1/polynet/app/services/progress.py +85 -0
  43. polynet-1.0.1/polynet/app/services/reruns.py +274 -0
  44. polynet-1.0.1/polynet/app/services/workload.py +54 -0
  45. polynet-1.0.1/polynet/app/static/polynet.png +0 -0
  46. polynet-1.0.1/polynet/app/utils/__init__.py +13 -0
  47. polynet-1.0.1/polynet/applicability/__init__.py +27 -0
  48. polynet-1.0.1/polynet/applicability/assess.py +458 -0
  49. polynet-1.0.1/polynet/applicability/calibration.py +111 -0
  50. polynet-1.0.1/polynet/applicability/domain.py +166 -0
  51. polynet-1.0.1/polynet/applicability/reference.py +159 -0
  52. polynet-1.0.1/polynet/applicability/similarity.py +140 -0
  53. polynet-1.0.1/polynet/cli.py +146 -0
  54. polynet-1.0.1/polynet/config/_loader.py +447 -0
  55. polynet-1.0.1/polynet/config/column_names.py +154 -0
  56. polynet-1.0.1/polynet/config/constants.py +64 -0
  57. polynet-1.0.1/polynet/config/display_names.py +148 -0
  58. polynet-1.0.1/polynet/config/enums.py +501 -0
  59. polynet-1.0.1/polynet/config/experiment.py +306 -0
  60. polynet-1.0.1/polynet/config/from_app.py +279 -0
  61. polynet-1.0.1/polynet/config/from_yaml.py +145 -0
  62. polynet-1.0.1/polynet/config/io.py +57 -0
  63. polynet-1.0.1/polynet/config/paths.py +379 -0
  64. polynet-1.0.1/polynet/config/schemas/__init__.py +47 -0
  65. polynet-1.0.1/polynet/config/schemas/applicability_domain.py +88 -0
  66. polynet-1.0.1/polynet/config/schemas/base.py +170 -0
  67. polynet-1.0.1/polynet/config/schemas/data.py +129 -0
  68. polynet-1.0.1/polynet/config/schemas/explainability.py +128 -0
  69. polynet-1.0.1/polynet/config/schemas/feature_preprocessing.py +101 -0
  70. polynet-1.0.1/polynet/config/schemas/fingerprints.py +174 -0
  71. polynet-1.0.1/polynet/config/schemas/general.py +47 -0
  72. polynet-1.0.1/polynet/config/schemas/plotting.py +59 -0
  73. polynet-1.0.1/polynet/config/schemas/representation.py +165 -0
  74. polynet-1.0.1/polynet/config/schemas/split_data.py +229 -0
  75. polynet-1.0.1/polynet/config/schemas/target_preprocessing.py +43 -0
  76. polynet-1.0.1/polynet/config/schemas/tml_explainability.py +116 -0
  77. polynet-1.0.1/polynet/config/schemas/training.py +473 -0
  78. polynet-1.0.1/polynet/config/search_grid.py +420 -0
  79. polynet-1.0.1/polynet/data/__init__.py +24 -0
  80. polynet-1.0.1/polynet/data/benchmarks/fluorine_mri_snr.csv +419 -0
  81. polynet-1.0.1/polynet/data/creator.py +193 -0
  82. polynet-1.0.1/polynet/data/feature_transformer.py +344 -0
  83. polynet-1.0.1/polynet/data/loader.py +245 -0
  84. polynet-1.0.1/polynet/data/preprocessing.py +406 -0
  85. polynet-1.0.1/polynet/data/sampling.py +134 -0
  86. polynet-1.0.1/polynet/data/structures.py +206 -0
  87. polynet-1.0.1/polynet/experiment/__init__.py +13 -0
  88. polynet-1.0.1/polynet/experiment/manager.py +50 -0
  89. polynet-1.0.1/polynet/explainability/__init__.py +124 -0
  90. polynet-1.0.1/polynet/explainability/attributions.py +37 -0
  91. polynet-1.0.1/polynet/explainability/embeddings.py +121 -0
  92. polynet-1.0.1/polynet/explainability/explain.py +959 -0
  93. polynet-1.0.1/polynet/explainability/fragment_figures.py +224 -0
  94. polynet-1.0.1/polynet/explainability/masking.py +635 -0
  95. polynet-1.0.1/polynet/explainability/selection.py +177 -0
  96. polynet-1.0.1/polynet/explainability/shap_explain.py +1359 -0
  97. polynet-1.0.1/polynet/explainability/visualization.py +850 -0
  98. polynet-1.0.1/polynet/factories/__init__.py +51 -0
  99. polynet-1.0.1/polynet/factories/dataloader.py +571 -0
  100. polynet-1.0.1/polynet/factories/loss.py +102 -0
  101. polynet-1.0.1/polynet/factories/network.py +187 -0
  102. polynet-1.0.1/polynet/factories/optimizer.py +218 -0
  103. polynet-1.0.1/polynet/featurizer/__init__.py +45 -0
  104. polynet-1.0.1/polynet/featurizer/allowable_sets.py +82 -0
  105. polynet-1.0.1/polynet/featurizer/descriptors.py +838 -0
  106. polynet-1.0.1/polynet/featurizer/graph.py +208 -0
  107. polynet-1.0.1/polynet/featurizer/pmx.py +450 -0
  108. polynet-1.0.1/polynet/featurizer/polymer_graph.py +508 -0
  109. polynet-1.0.1/polynet/featurizer/selection.py +278 -0
  110. polynet-1.0.1/polynet/inference/__init__.py +25 -0
  111. polynet-1.0.1/polynet/inference/ensemble.py +108 -0
  112. polynet-1.0.1/polynet/inference/gnn.py +157 -0
  113. polynet-1.0.1/polynet/inference/predict.py +154 -0
  114. polynet-1.0.1/polynet/inference/tml.py +155 -0
  115. polynet-1.0.1/polynet/inference/utils.py +108 -0
  116. polynet-1.0.1/polynet/models/__init__.py +66 -0
  117. polynet-1.0.1/polynet/models/base.py +665 -0
  118. polynet-1.0.1/polynet/models/gnn/__init__.py +40 -0
  119. polynet-1.0.1/polynet/models/gnn/cgcnn.py +248 -0
  120. polynet-1.0.1/polynet/models/gnn/cggnn.py +12 -0
  121. polynet-1.0.1/polynet/models/gnn/gat.py +161 -0
  122. polynet-1.0.1/polynet/models/gnn/gcn.py +211 -0
  123. polynet-1.0.1/polynet/models/gnn/graphsage.py +210 -0
  124. polynet-1.0.1/polynet/models/gnn/mpnn.py +230 -0
  125. polynet-1.0.1/polynet/models/gnn/transformer.py +163 -0
  126. polynet-1.0.1/polynet/models/persistence.py +237 -0
  127. polynet-1.0.1/polynet/pipeline/__init__.py +53 -0
  128. polynet-1.0.1/polynet/pipeline/runner.py +777 -0
  129. polynet-1.0.1/polynet/pipeline/self_check.py +348 -0
  130. polynet-1.0.1/polynet/pipeline/stages.py +1550 -0
  131. polynet-1.0.1/polynet/plotting/data_analysis.py +75 -0
  132. polynet-1.0.1/polynet/training/__init__.py +34 -0
  133. polynet-1.0.1/polynet/training/cv.py +55 -0
  134. polynet-1.0.1/polynet/training/evaluate.py +199 -0
  135. polynet-1.0.1/polynet/training/gnn.py +698 -0
  136. polynet-1.0.1/polynet/training/hyperopt.py +617 -0
  137. polynet-1.0.1/polynet/training/metrics.py +276 -0
  138. polynet-1.0.1/polynet/training/tml.py +477 -0
  139. polynet-1.0.1/polynet/utils/__init__.py +104 -0
  140. polynet-1.0.1/polynet/utils/chem_utils.py +383 -0
  141. polynet-1.0.1/polynet/utils/data_preprocessing.py +127 -0
  142. polynet-1.0.1/polynet/utils/graph_analysis.py +222 -0
  143. polynet-1.0.1/polynet/utils/optional_dependencies.py +91 -0
  144. polynet-1.0.1/polynet/utils/statistical_analysis.py +303 -0
  145. polynet-1.0.1/polynet/utils/validation.py +211 -0
  146. polynet-1.0.1/polynet/visualization/__init__.py +35 -0
  147. polynet-1.0.1/polynet/visualization/plots.py +480 -0
  148. polynet-1.0.1/polynet/visualization/utils.py +34 -0
  149. polynet-1.0.1/pyproject.toml +90 -0
polynet-1.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025–2026 Eduardo Aguilar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
polynet-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: polynet
3
+ Version: 1.0.1
4
+ Summary: A Python package for polymer informatics: property prediction with graph neural networks and traditional machine learning
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: polymer informatics,graph neural networks,machine learning,explainability
8
+ Author: Eduardo Aguilar-Bejarano
9
+ Author-email: eduardo.aguilar-bejarano@nottingham.ac.uk
10
+ Requires-Python: >=3.11,<3.12
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
14
+ Requires-Dist: astartes (>=1.3.3,<2.0.0)
15
+ Requires-Dist: imbalanced-learn (>=0.14.0,<0.15.0)
16
+ Requires-Dist: matplotlib (>=3.10.3,<4.0.0)
17
+ Requires-Dist: numpy (>=2.4.4,<3.0.0)
18
+ Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
19
+ Requires-Dist: pandas (>=2.2.3,<3.0.0)
20
+ Requires-Dist: plotly (>=6.1.1,<7.0.0)
21
+ Requires-Dist: polymetrix (>=0.2.0,<0.3.0)
22
+ Requires-Dist: pydantic (>=2.12.3,<3.0.0)
23
+ Requires-Dist: ray (>=2.51.0,<3.0.0)
24
+ Requires-Dist: rdkit (>=2025.3.2,<2026.0.0)
25
+ Requires-Dist: scikit-learn (>=1.6.1,<2.0.0)
26
+ Requires-Dist: scipy (>=1.15.3,<2.0.0)
27
+ Requires-Dist: seaborn (>=0.13.2,<0.14.0)
28
+ Requires-Dist: sentence-transformers (>=5.2.3,<6.0.0)
29
+ Requires-Dist: shap (>=0.51.0,<0.52.0)
30
+ Requires-Dist: statsmodels (>=0.14.5,<0.15.0)
31
+ Requires-Dist: streamlit (>=1.45.1,<2.0.0)
32
+ Requires-Dist: torch (==2.2.0) ; sys_platform == "darwin" and platform_machine == "x86_64"
33
+ Requires-Dist: torch (>=2.5.1,<3.0.0) ; sys_platform != "darwin" or platform_machine != "x86_64"
34
+ Requires-Dist: torch-geometric (==2.7)
35
+ Requires-Dist: xgboost (>=3.0.2,<4.0.0)
36
+ Project-URL: Changelog, https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/CHANGELOG.md
37
+ Project-URL: Documentation, https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/tree/main/docs
38
+ Project-URL: Repository, https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet
39
+ Description-Content-Type: text/markdown
40
+
41
+ # PolyNet
42
+
43
+ [![Python](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/)
44
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/LICENSE)
45
+ [![PyTorch](https://img.shields.io/badge/PyTorch-2.x-ee4c2c?logo=pytorch&logoColor=white)](https://pytorch.org/)
46
+ [![PyG](https://img.shields.io/badge/PyTorch%20Geometric-enabled-3C2179)](https://pytorch-geometric.readthedocs.io/)
47
+ [![RDKit](https://img.shields.io/badge/RDKit-supported-informational)](https://www.rdkit.org/)
48
+ [![Tests](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/actions/workflows/tests.yml/badge.svg)](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/actions/workflows/tests.yml)
49
+ [![DOI](https://zenodo.org/badge/927135580.svg)](https://doi.org/10.5281/zenodo.23302984)
50
+ [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
51
+ [![Poetry](https://img.shields.io/badge/dependency%20management-poetry-60A5FA)](https://python-poetry.org/)
52
+ [![Status](https://img.shields.io/badge/status-active%20development-brightgreen)]()
53
+
54
+ <p align="center">
55
+ <img src="https://raw.githubusercontent.com/Biomaterials-for-Medical-Devices-AI/PolyNet/main/polynet/app/static/polynet.png" alt="PolyNet logo" width="400"/>
56
+ </p>
57
+
58
+ **PolyNet** is a Python library for polymer property prediction using graph neural networks (GNNs) and traditional machine learning (TML). It provides a complete, configurable pipeline — from raw SMILES strings to trained models, evaluation metrics, result plots, and atom-level explainability — designed for polymer informatics research.
59
+
60
+ The library is split into two layers:
61
+
62
+ - **Core package (`polynet`)** — pipeline stages, model training/inference, featurisers, config schemas, and path/IO helpers. No Streamlit dependency; importable from any Python script or notebook.
63
+ - **Optional GUI (`polynet.app`)** — a Streamlit web application that wraps the same core stages. Install only when you want an interactive interface.
64
+
65
+ Both entry points call the same underlying functions and produce identical results.
66
+
67
+ ---
68
+
69
+ ## Overview
70
+
71
+ Predicting polymer properties from chemical structure is challenging because polymers are repeating units of one or more monomers combined in varying ratios. PolyNet handles this by representing each polymer as a graph where atoms are nodes, bonds are edges, and per-monomer molar ratios (normalised to sum to 1 across the participating monomers) are encoded as node attributes. This graph is fed into GNN architectures that learn structure–property relationships directly from the molecular graph.
72
+
73
+ PolyNet also supports traditional ML workflows using molecular descriptor vectors (RDKit descriptors, PolyBERT fingerprints, and others), enabling direct comparison between GNN and TML approaches on the same dataset and splits.
74
+
75
+ ---
76
+
77
+ ## Features
78
+
79
+ - **Six GNN architectures** — GCN, GAT, CGCNN, MPNN, GraphSAGE, TransformerGNN — each with regression and classification variants
80
+ - **Traditional ML** — Random Forest, XGBoost, SVM, Logistic Regression, Linear Regression, with optional automatic hyperparameter search (`RandomizedSearchCV` scored by shuffled, user-set k-fold cross-validation)
81
+ - **Molecular fingerprints** — Morgan and RDKit count fingerprints with configurable size and Morgan radius (defaults: 2048 bins, radius 3), alongside RDKit descriptors, PolyBERT and PolyMetriX
82
+ - **Multi-monomer polymers** — handles copolymers with any number of monomers and molar ratios; ratios are normalised per polymer to sum to 1 across the participating monomers (any scale works — fractions, percentages, or arbitrary ratios such as 1:1:2), and ratio-0 monomers are excluded from the graph entirely so homopolymers written as `(monomer, 100/0)` are invariant to the empty SMILES column
83
+ - **Per-monomer pooling** — `PerMonomerPooling` weighting mode pools each monomer's nodes separately and combines them as `Σ wᵢ·pool(monomerᵢ)`, removing the atom-count bias that arises when weighting before pooling on mixed-size copolymers (wD-MPNN-style weighted-mean pooling is also supported)
84
+ - **PSMILES attachment-point handling** — opt-in `IsAttachmentPoint` atom feature strips `*` (wildcard) atoms from the graph and flags the atoms that were attached to them, replacing dangling pseudo-element nodes with an explicit boolean marker
85
+ - **PolyMetriX descriptors** — polymer-aware chemical descriptors (molecular weight, ring counts, TPSA, etc.) computed on the full repeat unit, side chain, or backbone, with configurable aggregation; three modes — `side_chain`, `backbone`, and `polymer` — can be used in any combination
86
+ - **Automatic HPO** — Ray Tune for GNNs with configurable split strategy (cross-validation, holdout, repeated holdout; ASHA early stopping for holdout-based strategies); configurable number of sampled configurations (default 50 for GNNs and traditional ML) and custom search grids — in YAML or the GUI — that override individual parameters of the default grids; results cached per search settings and saved with the experiment
87
+ - **Configurable GNN training** — optimiser (Adam, SGD, RMSprop, Adadelta, Adagrad), learning-rate scheduler (ReduceLROnPlateau, step, multi-step, exponential) and regression loss (RMSE, MSE, MAE), applied identically to final training and HPO
88
+ - **Data splitting with astartes** — train/val/test splits drawn with an [astartes](https://github.com/JacksonBurns/astartes) sampler: random (default), Kennard–Stone, SPXY, k-means or target-property, optionally stratified by class; fingerprint samplers use a ratio-weighted polymer fingerprint (Morgan, RDKit or polyBERT) whose type and settings are chosen for splitting only, and the sampling settings are saved with the experiment
89
+ - **Repeated random splits and ensembles** — configurable number of train/val/test splits (`n_bootstrap_iterations`); predictions on new data are combined across the splits into ensembles per GNN architecture, across all GNNs, and per TML model × representation, reported as mean ± standard deviation (regression) or majority vote with vote fraction (classification), with metrics for each ensemble
90
+ - **Structure validation** — SMILES/PSMILES are validated and canonicalised the same way in the GUI, the CLI and when predicting new data; invalid or missing structures and duplicated sample IDs are reported with examples
91
+ - **Target variable scaling** — six scaling strategies for regression targets (StandardScaler, MinMaxScaler, RobustScaler, Log₁₀, Log(1+y), or none); scaler is fit on the training set only and automatically inverse-transformed before metrics and plots so all results are reported in the original target units
92
+ - **Polymer descriptor fusion** — user-supplied experimental or computed polymer-level features (e.g. molecular weight, chain length) can be concatenated to vectorial descriptor representations and, for GNN models, fused into the graph embedding after pooling so the FFN receives both learned and given features; for GNNs they are scaled with the pipeline's feature scaler, fitted on the training split
93
+ - **GNN fragment-level explainability** — chemistry-masking attribution (substructure masking; Wu et al., *Nat. Commun.* 14, 2585 (2023), [doi:10.1038/s41467-023-38192-3](https://doi.org/10.1038/s41467-023-38192-3)): each fragment's importance is measured as the change in prediction when that fragment is masked from the graph pooling step; fragments come from BRICS or Murcko-scaffold fragmentation and attributions are aggregated per fragment; supports global distribution plots (ridge, bar, strip) and per-molecule attribution heatmaps
94
+ - **TML SHAP explainability** — SHAP-based attribution for all traditional ML models using auto-selected explainers (`TreeExplainer` for RF/XGBoost, `LinearExplainer` for linear models, `KernelExplainer` for SVM); global summaries and per-instance plots are rendered with the native `shap` package (beeswarm / bar / violin and waterfall / force / bar); SHAP values are cached to CSV and reused across runs
95
+ - **Configurable explanation display** — view local explanations averaged across the model ensemble or as one plot per model × molecule, with selectable attribution normalisation (local / per-model / global / none)
96
+ - **Statistical model comparison** — pairwise McNemar (classification) and Wilcoxon signed-rank tests on absolute errors (regression) on the Analyse Results page; metric-level comparison across repeated splits with Wilcoxon or the Nadeau–Bengio corrected resampled t-test; multiple-comparison correction (Holm-Bonferroni, Bonferroni, Benjamini-Hochberg)
97
+ - **Publication-quality plots** — parity plots, ROC curves, confusion matrices, learning curves, and attribution heatmaps
98
+ - **Single-command pipeline** — YAML config file drives the entire workflow; no code changes between experiments
99
+ - **GUI-independent core** — all pipeline stages are importable from `polynet` without Streamlit
100
+
101
+ ---
102
+
103
+ ## Installation
104
+
105
+ PolyNet requires **Python 3.11** and uses [Poetry](https://python-poetry.org/) for dependency management.
106
+
107
+ ### Step 1 — Clone the repository
108
+
109
+ ```bash
110
+ git clone https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet.git
111
+ cd PolyNet
112
+ ```
113
+
114
+ ### Step 2 — Create a Python 3.11 environment
115
+
116
+ Choose either option.
117
+
118
+ **Option A — conda**
119
+
120
+ ```bash
121
+ conda create -n polynet python=3.11
122
+ conda activate polynet
123
+ ```
124
+
125
+ **Option B — venv**
126
+
127
+ ```bash
128
+ python3.11 -m venv .venv
129
+ source .venv/bin/activate # macOS / Linux
130
+ .venv\Scripts\activate # Windows
131
+ ```
132
+
133
+ ### Step 3 — Install Poetry
134
+
135
+ With the environment active, install Poetry via pip:
136
+
137
+ ```bash
138
+ pip install poetry
139
+ ```
140
+
141
+ Then tell Poetry to use the active environment rather than creating its own:
142
+
143
+ ```bash
144
+ poetry config virtualenvs.create false --local
145
+ ```
146
+
147
+ ### Step 4 — Install the project
148
+
149
+ ```bash
150
+ poetry install
151
+ ```
152
+
153
+ Poetry reads `pyproject.toml` and `poetry.lock` and installs all dependencies into the active environment.
154
+
155
+ ### PSMILES support
156
+
157
+ PolyNet canonicalises PSMILES (polymer SMILES with `*` attachment points) with
158
+ [canonicalize-psmiles](https://github.com/kuennethgroup/canonicalize_psmiles) from the
159
+ Kuenneth group. It is distributed by Georgia Tech Research Corporation under its own
160
+ licence, which allows **non-commercial use only**, and is not on PyPI, so it is installed
161
+ separately from PolyNet's MIT-licensed code:
162
+
163
+ - `poetry install` (above) installs it.
164
+ - In the GUI, loading PSMILES data offers an **Install canonicalize-psmiles** button
165
+ after you accept its licence.
166
+ - From a terminal: `polynet install-psmiles`.
167
+
168
+ Datasets written as plain SMILES do not need it.
169
+
170
+ ### Verifying the installation
171
+
172
+ ```bash
173
+ polynet check
174
+ ```
175
+
176
+ This runs the whole pipeline on a small synthetic dataset (about a minute) and prints a PASS / FAIL table.
177
+
178
+ ### Common Poetry commands
179
+
180
+ | Command | Description |
181
+ |---|---|
182
+ | `poetry install` | Install all dependencies from `poetry.lock` |
183
+ | `poetry add <package>` | Add a new dependency |
184
+ | `poetry update` | Update all dependencies within the version constraints |
185
+ | `poetry run python <script>` | Run a script without manually activating the environment |
186
+
187
+ **Dependencies** (managed by Poetry): PyTorch, PyTorch Geometric, RDKit, scikit-learn, XGBoost, imbalanced-learn, Ray Tune, Captum, Streamlit, Pydantic, matplotlib, seaborn, scipy, statsmodels, and more — see `pyproject.toml` for the full list.
188
+
189
+ ---
190
+
191
+ ## Quick Start
192
+
193
+ ### CLI
194
+
195
+ **1. Edit the config file** to point at your data and choose your models. The template
196
+ `configs/experiment.yaml` runs out of the box on the built-in Tg benchmark
197
+ (`benchmark_dataset: "curated_tg"`, downloaded on first use); to use your own data,
198
+ replace it with `data_path`:
199
+
200
+ ```yaml
201
+ # configs/experiment.yaml
202
+ experiment:
203
+ name: "tg_prediction"
204
+ output_dir: "results/tg_prediction"
205
+ random_seed: 42
206
+
207
+ data:
208
+ data_path: "data/polymers.csv"
209
+ id_col: "polymer_id"
210
+ target_variable_col: "Tg"
211
+ target_variable_name: "Glass Transition Temperature"
212
+ problem_type: "regression"
213
+ smiles_cols:
214
+ - "monomer1_smiles"
215
+ - "monomer2_smiles"
216
+ ```
217
+
218
+ **2. Run the pipeline:**
219
+
220
+ ```bash
221
+ polynet run --config configs/experiment.yaml
222
+ ```
223
+
224
+ All outputs are written to the directory specified by `experiment.output_dir` in the config. `polynet run --help` lists the flags (e.g. `--epochs`, `--no-gnn`, `--predict-data`).
225
+
226
+ ### Streamlit GUI
227
+
228
+ ```bash
229
+ polynet
230
+ ```
231
+
232
+ This works from any directory once the package is installed (`polynet gui` is the same). Any `streamlit run` option is forwarded, so `polynet --server.port 8502` changes the port. When developing PolyNet, run `polynet --server.fileWatcherType auto` so the app reloads on save (see [Development](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/development.md#running-the-gui-while-developing)). From the repository root, the explicit command also works:
233
+
234
+ ```bash
235
+ streamlit run polynet/app/Welcome_to_PolyNet.py
236
+ ```
237
+
238
+ ### Python API
239
+
240
+ The core stages can be called directly from any script or notebook:
241
+
242
+ ```python
243
+ from polynet.pipeline import (
244
+ build_graph_dataset,
245
+ compute_descriptors,
246
+ compute_data_splits,
247
+ train_gnn,
248
+ run_gnn_inference,
249
+ train_tml,
250
+ run_tml_inference,
251
+ compute_metrics,
252
+ predict_external,
253
+ )
254
+ from polynet.config.io import save_options, load_options
255
+ from polynet.config.paths import polynet_experiments_base_dir, model_dir
256
+ from polynet.experiment.manager import get_experiments, create_experiment
257
+ from polynet.models.persistence import load_models_from_experiment
258
+ from polynet.inference.predict import predict_unseen_tml, predict_unseen_gnn
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Data Format
264
+
265
+ PolyNet expects a CSV file with one row per polymer sample:
266
+
267
+ | Column | Required | Description |
268
+ |--------|----------|-------------|
269
+ | `polymer_id` | Recommended | Unique identifier (used as sample index) |
270
+ | `monomer1_smiles` | Yes | SMILES string for the first monomer |
271
+ | `monomer2_smiles` | Yes (copolymers) | SMILES string for the second monomer |
272
+ | `ratio_1` | No | Molar ratio of monomer 1. Omit to treat monomers as equally weighted |
273
+ | `ratio_2` | No | Molar ratio of monomer 2 |
274
+ | `target` | Yes | Property to predict (numeric for regression, integer class for classification) |
275
+
276
+ Copolymers with any number of monomers are supported by adding one SMILES column and one molar-ratio column per monomer and listing them in the config. Ratios are normalised per polymer to sum to 1 across the participating monomers, so they need not sum to 1 or 100; a ratio of 0 excludes that monomer. The target column is optional when predicting on unseen data.
277
+
278
+ ---
279
+
280
+ ## Documentation
281
+
282
+ Full documentation lives in [`docs/`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/README.md):
283
+
284
+ - **[Configuration reference](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/configuration.md)** — every YAML section, including automatic HPO
285
+ - **[Descriptors](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/descriptors.md)** — polymer descriptor fusion and the PolyMetriX integration
286
+ - **[Explainability](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/explainability.md)** — GNN masking & TML SHAP attribution, normalisation, averaged vs per-model display, and statistical model comparison
287
+ - **[Running the pipeline & outputs](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/running-and-outputs.md)** — CLI flags, predicting on external data, target scaling, and the output directory layout
288
+ - **[API reference](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/api-reference.md)** — project structure and the public Python API
289
+ - **[GUI & architecture](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/gui-and-architecture.md)** — the Streamlit application and the core-vs-GUI design
290
+ - **[Development](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/development.md)** — running the test suite and extending PolyNet
291
+
292
+ ---
293
+
294
+ ## Team
295
+ - [Eduardo Aguilar](https://edaguilarb.github.io./) (Chemist, Data Scientist, Research Software Engineer)
296
+ - [David Winkler](https://scholar.google.com/citations?user=P8DvrTgAAAAJ&hl=en) (Professor, Cheminformatician)
297
+ - [Grazziela Figueredo](https://scholar.google.com/citations?user=DXNNUcUAAAAJ&hl=en) (Associate Professor, Data Scientist, Product Owner, Principal Investigator)
298
+
299
+
300
+ ## Citing PolyNet
301
+
302
+ If you use PolyNet, please cite the software (GitHub's "Cite this repository" button reads [`CITATION.cff`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/CITATION.cff)):
303
+
304
+ ```bibtex
305
+ @software{polynet,
306
+ author = {Aguilar-Bejarano, Eduardo and Alexander, Morgan and Winkler, David and Figueredo, Grazziela},
307
+ title = {PolyNet: A Python Package for Polymer Informatics},
308
+ version = {1.0.1},
309
+ year = {2026},
310
+ doi = {10.5281/zenodo.23302984},
311
+ url = {https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet}
312
+ }
313
+ ```
314
+
315
+ This DOI always resolves to the latest release. To cite the exact version you used,
316
+ take its DOI from the [Zenodo record](https://doi.org/10.5281/zenodo.23302984)
317
+ (1.0.0: [10.5281/zenodo.23302985](https://doi.org/10.5281/zenodo.23302985)).
318
+
319
+ Changes between versions are listed in [`CHANGELOG.md`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/CHANGELOG.md).
320
+
@@ -0,0 +1,279 @@
1
+ # PolyNet
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/LICENSE)
5
+ [![PyTorch](https://img.shields.io/badge/PyTorch-2.x-ee4c2c?logo=pytorch&logoColor=white)](https://pytorch.org/)
6
+ [![PyG](https://img.shields.io/badge/PyTorch%20Geometric-enabled-3C2179)](https://pytorch-geometric.readthedocs.io/)
7
+ [![RDKit](https://img.shields.io/badge/RDKit-supported-informational)](https://www.rdkit.org/)
8
+ [![Tests](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/actions/workflows/tests.yml/badge.svg)](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/actions/workflows/tests.yml)
9
+ [![DOI](https://zenodo.org/badge/927135580.svg)](https://doi.org/10.5281/zenodo.23302984)
10
+ [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
11
+ [![Poetry](https://img.shields.io/badge/dependency%20management-poetry-60A5FA)](https://python-poetry.org/)
12
+ [![Status](https://img.shields.io/badge/status-active%20development-brightgreen)]()
13
+
14
+ <p align="center">
15
+ <img src="https://raw.githubusercontent.com/Biomaterials-for-Medical-Devices-AI/PolyNet/main/polynet/app/static/polynet.png" alt="PolyNet logo" width="400"/>
16
+ </p>
17
+
18
+ **PolyNet** is a Python library for polymer property prediction using graph neural networks (GNNs) and traditional machine learning (TML). It provides a complete, configurable pipeline — from raw SMILES strings to trained models, evaluation metrics, result plots, and atom-level explainability — designed for polymer informatics research.
19
+
20
+ The library is split into two layers:
21
+
22
+ - **Core package (`polynet`)** — pipeline stages, model training/inference, featurisers, config schemas, and path/IO helpers. No Streamlit dependency; importable from any Python script or notebook.
23
+ - **Optional GUI (`polynet.app`)** — a Streamlit web application that wraps the same core stages. Install only when you want an interactive interface.
24
+
25
+ Both entry points call the same underlying functions and produce identical results.
26
+
27
+ ---
28
+
29
+ ## Overview
30
+
31
+ Predicting polymer properties from chemical structure is challenging because polymers are repeating units of one or more monomers combined in varying ratios. PolyNet handles this by representing each polymer as a graph where atoms are nodes, bonds are edges, and per-monomer molar ratios (normalised to sum to 1 across the participating monomers) are encoded as node attributes. This graph is fed into GNN architectures that learn structure–property relationships directly from the molecular graph.
32
+
33
+ PolyNet also supports traditional ML workflows using molecular descriptor vectors (RDKit descriptors, PolyBERT fingerprints, and others), enabling direct comparison between GNN and TML approaches on the same dataset and splits.
34
+
35
+ ---
36
+
37
+ ## Features
38
+
39
+ - **Six GNN architectures** — GCN, GAT, CGCNN, MPNN, GraphSAGE, TransformerGNN — each with regression and classification variants
40
+ - **Traditional ML** — Random Forest, XGBoost, SVM, Logistic Regression, Linear Regression, with optional automatic hyperparameter search (`RandomizedSearchCV` scored by shuffled, user-set k-fold cross-validation)
41
+ - **Molecular fingerprints** — Morgan and RDKit count fingerprints with configurable size and Morgan radius (defaults: 2048 bins, radius 3), alongside RDKit descriptors, PolyBERT and PolyMetriX
42
+ - **Multi-monomer polymers** — handles copolymers with any number of monomers and molar ratios; ratios are normalised per polymer to sum to 1 across the participating monomers (any scale works — fractions, percentages, or arbitrary ratios such as 1:1:2), and ratio-0 monomers are excluded from the graph entirely so homopolymers written as `(monomer, 100/0)` are invariant to the empty SMILES column
43
+ - **Per-monomer pooling** — `PerMonomerPooling` weighting mode pools each monomer's nodes separately and combines them as `Σ wᵢ·pool(monomerᵢ)`, removing the atom-count bias that arises when weighting before pooling on mixed-size copolymers (wD-MPNN-style weighted-mean pooling is also supported)
44
+ - **PSMILES attachment-point handling** — opt-in `IsAttachmentPoint` atom feature strips `*` (wildcard) atoms from the graph and flags the atoms that were attached to them, replacing dangling pseudo-element nodes with an explicit boolean marker
45
+ - **PolyMetriX descriptors** — polymer-aware chemical descriptors (molecular weight, ring counts, TPSA, etc.) computed on the full repeat unit, side chain, or backbone, with configurable aggregation; three modes — `side_chain`, `backbone`, and `polymer` — can be used in any combination
46
+ - **Automatic HPO** — Ray Tune for GNNs with configurable split strategy (cross-validation, holdout, repeated holdout; ASHA early stopping for holdout-based strategies); configurable number of sampled configurations (default 50 for GNNs and traditional ML) and custom search grids — in YAML or the GUI — that override individual parameters of the default grids; results cached per search settings and saved with the experiment
47
+ - **Configurable GNN training** — optimiser (Adam, SGD, RMSprop, Adadelta, Adagrad), learning-rate scheduler (ReduceLROnPlateau, step, multi-step, exponential) and regression loss (RMSE, MSE, MAE), applied identically to final training and HPO
48
+ - **Data splitting with astartes** — train/val/test splits drawn with an [astartes](https://github.com/JacksonBurns/astartes) sampler: random (default), Kennard–Stone, SPXY, k-means or target-property, optionally stratified by class; fingerprint samplers use a ratio-weighted polymer fingerprint (Morgan, RDKit or polyBERT) whose type and settings are chosen for splitting only, and the sampling settings are saved with the experiment
49
+ - **Repeated random splits and ensembles** — configurable number of train/val/test splits (`n_bootstrap_iterations`); predictions on new data are combined across the splits into ensembles per GNN architecture, across all GNNs, and per TML model × representation, reported as mean ± standard deviation (regression) or majority vote with vote fraction (classification), with metrics for each ensemble
50
+ - **Structure validation** — SMILES/PSMILES are validated and canonicalised the same way in the GUI, the CLI and when predicting new data; invalid or missing structures and duplicated sample IDs are reported with examples
51
+ - **Target variable scaling** — six scaling strategies for regression targets (StandardScaler, MinMaxScaler, RobustScaler, Log₁₀, Log(1+y), or none); scaler is fit on the training set only and automatically inverse-transformed before metrics and plots so all results are reported in the original target units
52
+ - **Polymer descriptor fusion** — user-supplied experimental or computed polymer-level features (e.g. molecular weight, chain length) can be concatenated to vectorial descriptor representations and, for GNN models, fused into the graph embedding after pooling so the FFN receives both learned and given features; for GNNs they are scaled with the pipeline's feature scaler, fitted on the training split
53
+ - **GNN fragment-level explainability** — chemistry-masking attribution (substructure masking; Wu et al., *Nat. Commun.* 14, 2585 (2023), [doi:10.1038/s41467-023-38192-3](https://doi.org/10.1038/s41467-023-38192-3)): each fragment's importance is measured as the change in prediction when that fragment is masked from the graph pooling step; fragments come from BRICS or Murcko-scaffold fragmentation and attributions are aggregated per fragment; supports global distribution plots (ridge, bar, strip) and per-molecule attribution heatmaps
54
+ - **TML SHAP explainability** — SHAP-based attribution for all traditional ML models using auto-selected explainers (`TreeExplainer` for RF/XGBoost, `LinearExplainer` for linear models, `KernelExplainer` for SVM); global summaries and per-instance plots are rendered with the native `shap` package (beeswarm / bar / violin and waterfall / force / bar); SHAP values are cached to CSV and reused across runs
55
+ - **Configurable explanation display** — view local explanations averaged across the model ensemble or as one plot per model × molecule, with selectable attribution normalisation (local / per-model / global / none)
56
+ - **Statistical model comparison** — pairwise McNemar (classification) and Wilcoxon signed-rank tests on absolute errors (regression) on the Analyse Results page; metric-level comparison across repeated splits with Wilcoxon or the Nadeau–Bengio corrected resampled t-test; multiple-comparison correction (Holm-Bonferroni, Bonferroni, Benjamini-Hochberg)
57
+ - **Publication-quality plots** — parity plots, ROC curves, confusion matrices, learning curves, and attribution heatmaps
58
+ - **Single-command pipeline** — YAML config file drives the entire workflow; no code changes between experiments
59
+ - **GUI-independent core** — all pipeline stages are importable from `polynet` without Streamlit
60
+
61
+ ---
62
+
63
+ ## Installation
64
+
65
+ PolyNet requires **Python 3.11** and uses [Poetry](https://python-poetry.org/) for dependency management.
66
+
67
+ ### Step 1 — Clone the repository
68
+
69
+ ```bash
70
+ git clone https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet.git
71
+ cd PolyNet
72
+ ```
73
+
74
+ ### Step 2 — Create a Python 3.11 environment
75
+
76
+ Choose either option.
77
+
78
+ **Option A — conda**
79
+
80
+ ```bash
81
+ conda create -n polynet python=3.11
82
+ conda activate polynet
83
+ ```
84
+
85
+ **Option B — venv**
86
+
87
+ ```bash
88
+ python3.11 -m venv .venv
89
+ source .venv/bin/activate # macOS / Linux
90
+ .venv\Scripts\activate # Windows
91
+ ```
92
+
93
+ ### Step 3 — Install Poetry
94
+
95
+ With the environment active, install Poetry via pip:
96
+
97
+ ```bash
98
+ pip install poetry
99
+ ```
100
+
101
+ Then tell Poetry to use the active environment rather than creating its own:
102
+
103
+ ```bash
104
+ poetry config virtualenvs.create false --local
105
+ ```
106
+
107
+ ### Step 4 — Install the project
108
+
109
+ ```bash
110
+ poetry install
111
+ ```
112
+
113
+ Poetry reads `pyproject.toml` and `poetry.lock` and installs all dependencies into the active environment.
114
+
115
+ ### PSMILES support
116
+
117
+ PolyNet canonicalises PSMILES (polymer SMILES with `*` attachment points) with
118
+ [canonicalize-psmiles](https://github.com/kuennethgroup/canonicalize_psmiles) from the
119
+ Kuenneth group. It is distributed by Georgia Tech Research Corporation under its own
120
+ licence, which allows **non-commercial use only**, and is not on PyPI, so it is installed
121
+ separately from PolyNet's MIT-licensed code:
122
+
123
+ - `poetry install` (above) installs it.
124
+ - In the GUI, loading PSMILES data offers an **Install canonicalize-psmiles** button
125
+ after you accept its licence.
126
+ - From a terminal: `polynet install-psmiles`.
127
+
128
+ Datasets written as plain SMILES do not need it.
129
+
130
+ ### Verifying the installation
131
+
132
+ ```bash
133
+ polynet check
134
+ ```
135
+
136
+ This runs the whole pipeline on a small synthetic dataset (about a minute) and prints a PASS / FAIL table.
137
+
138
+ ### Common Poetry commands
139
+
140
+ | Command | Description |
141
+ |---|---|
142
+ | `poetry install` | Install all dependencies from `poetry.lock` |
143
+ | `poetry add <package>` | Add a new dependency |
144
+ | `poetry update` | Update all dependencies within the version constraints |
145
+ | `poetry run python <script>` | Run a script without manually activating the environment |
146
+
147
+ **Dependencies** (managed by Poetry): PyTorch, PyTorch Geometric, RDKit, scikit-learn, XGBoost, imbalanced-learn, Ray Tune, Captum, Streamlit, Pydantic, matplotlib, seaborn, scipy, statsmodels, and more — see `pyproject.toml` for the full list.
148
+
149
+ ---
150
+
151
+ ## Quick Start
152
+
153
+ ### CLI
154
+
155
+ **1. Edit the config file** to point at your data and choose your models. The template
156
+ `configs/experiment.yaml` runs out of the box on the built-in Tg benchmark
157
+ (`benchmark_dataset: "curated_tg"`, downloaded on first use); to use your own data,
158
+ replace it with `data_path`:
159
+
160
+ ```yaml
161
+ # configs/experiment.yaml
162
+ experiment:
163
+ name: "tg_prediction"
164
+ output_dir: "results/tg_prediction"
165
+ random_seed: 42
166
+
167
+ data:
168
+ data_path: "data/polymers.csv"
169
+ id_col: "polymer_id"
170
+ target_variable_col: "Tg"
171
+ target_variable_name: "Glass Transition Temperature"
172
+ problem_type: "regression"
173
+ smiles_cols:
174
+ - "monomer1_smiles"
175
+ - "monomer2_smiles"
176
+ ```
177
+
178
+ **2. Run the pipeline:**
179
+
180
+ ```bash
181
+ polynet run --config configs/experiment.yaml
182
+ ```
183
+
184
+ All outputs are written to the directory specified by `experiment.output_dir` in the config. `polynet run --help` lists the flags (e.g. `--epochs`, `--no-gnn`, `--predict-data`).
185
+
186
+ ### Streamlit GUI
187
+
188
+ ```bash
189
+ polynet
190
+ ```
191
+
192
+ This works from any directory once the package is installed (`polynet gui` is the same). Any `streamlit run` option is forwarded, so `polynet --server.port 8502` changes the port. When developing PolyNet, run `polynet --server.fileWatcherType auto` so the app reloads on save (see [Development](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/development.md#running-the-gui-while-developing)). From the repository root, the explicit command also works:
193
+
194
+ ```bash
195
+ streamlit run polynet/app/Welcome_to_PolyNet.py
196
+ ```
197
+
198
+ ### Python API
199
+
200
+ The core stages can be called directly from any script or notebook:
201
+
202
+ ```python
203
+ from polynet.pipeline import (
204
+ build_graph_dataset,
205
+ compute_descriptors,
206
+ compute_data_splits,
207
+ train_gnn,
208
+ run_gnn_inference,
209
+ train_tml,
210
+ run_tml_inference,
211
+ compute_metrics,
212
+ predict_external,
213
+ )
214
+ from polynet.config.io import save_options, load_options
215
+ from polynet.config.paths import polynet_experiments_base_dir, model_dir
216
+ from polynet.experiment.manager import get_experiments, create_experiment
217
+ from polynet.models.persistence import load_models_from_experiment
218
+ from polynet.inference.predict import predict_unseen_tml, predict_unseen_gnn
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Data Format
224
+
225
+ PolyNet expects a CSV file with one row per polymer sample:
226
+
227
+ | Column | Required | Description |
228
+ |--------|----------|-------------|
229
+ | `polymer_id` | Recommended | Unique identifier (used as sample index) |
230
+ | `monomer1_smiles` | Yes | SMILES string for the first monomer |
231
+ | `monomer2_smiles` | Yes (copolymers) | SMILES string for the second monomer |
232
+ | `ratio_1` | No | Molar ratio of monomer 1. Omit to treat monomers as equally weighted |
233
+ | `ratio_2` | No | Molar ratio of monomer 2 |
234
+ | `target` | Yes | Property to predict (numeric for regression, integer class for classification) |
235
+
236
+ Copolymers with any number of monomers are supported by adding one SMILES column and one molar-ratio column per monomer and listing them in the config. Ratios are normalised per polymer to sum to 1 across the participating monomers, so they need not sum to 1 or 100; a ratio of 0 excludes that monomer. The target column is optional when predicting on unseen data.
237
+
238
+ ---
239
+
240
+ ## Documentation
241
+
242
+ Full documentation lives in [`docs/`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/README.md):
243
+
244
+ - **[Configuration reference](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/configuration.md)** — every YAML section, including automatic HPO
245
+ - **[Descriptors](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/descriptors.md)** — polymer descriptor fusion and the PolyMetriX integration
246
+ - **[Explainability](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/explainability.md)** — GNN masking & TML SHAP attribution, normalisation, averaged vs per-model display, and statistical model comparison
247
+ - **[Running the pipeline & outputs](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/running-and-outputs.md)** — CLI flags, predicting on external data, target scaling, and the output directory layout
248
+ - **[API reference](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/api-reference.md)** — project structure and the public Python API
249
+ - **[GUI & architecture](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/gui-and-architecture.md)** — the Streamlit application and the core-vs-GUI design
250
+ - **[Development](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/docs/development.md)** — running the test suite and extending PolyNet
251
+
252
+ ---
253
+
254
+ ## Team
255
+ - [Eduardo Aguilar](https://edaguilarb.github.io./) (Chemist, Data Scientist, Research Software Engineer)
256
+ - [David Winkler](https://scholar.google.com/citations?user=P8DvrTgAAAAJ&hl=en) (Professor, Cheminformatician)
257
+ - [Grazziela Figueredo](https://scholar.google.com/citations?user=DXNNUcUAAAAJ&hl=en) (Associate Professor, Data Scientist, Product Owner, Principal Investigator)
258
+
259
+
260
+ ## Citing PolyNet
261
+
262
+ If you use PolyNet, please cite the software (GitHub's "Cite this repository" button reads [`CITATION.cff`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/CITATION.cff)):
263
+
264
+ ```bibtex
265
+ @software{polynet,
266
+ author = {Aguilar-Bejarano, Eduardo and Alexander, Morgan and Winkler, David and Figueredo, Grazziela},
267
+ title = {PolyNet: A Python Package for Polymer Informatics},
268
+ version = {1.0.1},
269
+ year = {2026},
270
+ doi = {10.5281/zenodo.23302984},
271
+ url = {https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet}
272
+ }
273
+ ```
274
+
275
+ This DOI always resolves to the latest release. To cite the exact version you used,
276
+ take its DOI from the [Zenodo record](https://doi.org/10.5281/zenodo.23302984)
277
+ (1.0.0: [10.5281/zenodo.23302985](https://doi.org/10.5281/zenodo.23302985)).
278
+
279
+ Changes between versions are listed in [`CHANGELOG.md`](https://github.com/Biomaterials-for-Medical-Devices-AI/PolyNet/blob/main/CHANGELOG.md).
@@ -0,0 +1,8 @@
1
+ """PolyNet: polymer property prediction with GNNs and traditional ML."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("polynet")
7
+ except PackageNotFoundError: # running from a source tree that is not installed
8
+ __version__ = "unknown"