Saeed commited on
Commit
77dc6ef
·
verified ·
1 Parent(s): beeab0e

MLATE V3: batched optimiser, all model families, OpenRouter protocol generation

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -37
  2. README.md +75 -127
  3. app.py +633 -1012
  4. biomaterials.py +925 -123
  5. cell_lines.py +1319 -177
  6. corpus_reference.json +289 -0
  7. corpus_reference.parquet +3 -0
  8. deploy/models/classifiers/random/cell_response__adaboost.pkl +3 -0
  9. deploy/models/classifiers/random/cell_response__bagged_trees.pkl +3 -0
  10. deploy/models/classifiers/random/cell_response__bernoulli_naive_bayes.pkl +3 -0
  11. deploy/models/classifiers/random/cell_response__catboost.pkl +3 -0
  12. deploy/models/classifiers/random/cell_response__decision_tree.pkl +3 -0
  13. deploy/models/classifiers/random/cell_response__extra_tree.pkl +3 -0
  14. deploy/models/classifiers/random/cell_response__extra_trees.pkl +3 -0
  15. deploy/models/classifiers/random/cell_response__gaussian_naive_bayes.pkl +3 -0
  16. deploy/models/classifiers/random/cell_response__gradient_boosting.pkl +3 -0
  17. deploy/models/classifiers/random/cell_response__hist_gradient_boosting.pkl +3 -0
  18. deploy/models/classifiers/random/cell_response__k_nearest_neighbours.pkl +3 -0
  19. deploy/models/classifiers/random/cell_response__k_nn_distance_weighted.pkl +3 -0
  20. deploy/models/classifiers/random/cell_response__lightgbm.pkl +3 -0
  21. deploy/models/classifiers/random/cell_response__linear_discriminant.pkl +3 -0
  22. deploy/models/classifiers/random/cell_response__linear_svm.pkl +3 -0
  23. deploy/models/classifiers/random/cell_response__logistic_regression.pkl +3 -0
  24. deploy/models/classifiers/random/cell_response__logistic_regression_balanced.pkl +3 -0
  25. deploy/models/classifiers/random/cell_response__mlp_256_128.pkl +3 -0
  26. deploy/models/classifiers/random/cell_response__nearest_centroid.pkl +3 -0
  27. deploy/models/classifiers/random/cell_response__passive_aggressive.pkl +3 -0
  28. deploy/models/classifiers/random/cell_response__perceptron.pkl +3 -0
  29. deploy/models/classifiers/random/cell_response__quadratic_discriminant.pkl +3 -0
  30. deploy/models/classifiers/random/cell_response__random_forest.pkl +3 -0
  31. deploy/models/classifiers/random/cell_response__random_forest_balanced.pkl +3 -0
  32. deploy/models/classifiers/random/cell_response__ridge_classifier.pkl +3 -0
  33. deploy/models/classifiers/random/cell_response__sgd_hinge.pkl +3 -0
  34. deploy/models/classifiers/random/cell_response__soft_voting_rf_xgb_lr.pkl +3 -0
  35. deploy/models/classifiers/random/cell_response__stacking_rf_xgb_lr_lr.pkl +3 -0
  36. deploy/models/classifiers/random/cell_response__svm_polynomial.pkl +3 -0
  37. deploy/models/classifiers/random/cell_response__svm_rbf.pkl +3 -0
  38. deploy/models/classifiers/random/cell_response__xgboost.pkl +3 -0
  39. deploy/models/classifiers/random/printability__adaboost.pkl +3 -0
  40. deploy/models/classifiers/random/printability__bagged_trees.pkl +3 -0
  41. deploy/models/classifiers/random/printability__bernoulli_naive_bayes.pkl +3 -0
  42. deploy/models/classifiers/random/printability__catboost.pkl +3 -0
  43. deploy/models/classifiers/random/printability__decision_tree.pkl +3 -0
  44. deploy/models/classifiers/random/printability__extra_tree.pkl +3 -0
  45. deploy/models/classifiers/random/printability__extra_trees.pkl +3 -0
  46. deploy/models/classifiers/random/printability__gaussian_naive_bayes.pkl +3 -0
  47. deploy/models/classifiers/random/printability__gradient_boosting.pkl +3 -0
  48. deploy/models/classifiers/random/printability__hist_gradient_boosting.pkl +3 -0
  49. deploy/models/classifiers/random/printability__k_nearest_neighbours.pkl +3 -0
  50. deploy/models/classifiers/random/printability__k_nn_distance_weighted.pkl +3 -0
.gitattributes CHANGED
@@ -1,37 +1,3 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- *.cbm filter=lfs diff=lfs merge=lfs -text
37
- Dataset.xlsx filter=lfs diff=lfs merge=lfs -text
 
1
+ *.pkl filter=lfs diff=lfs merge=lfs -text
2
+ *.pt filter=lfs diff=lfs merge=lfs -text
3
+ *.parquet filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,127 +1,75 @@
1
- ---
2
- title: MLATE
3
- emoji: 🥼
4
- colorFrom: red
5
- colorTo: indigo
6
- sdk: streamlit
7
- sdk_version: 1.44.1
8
- python_version: "3.12"
9
- app_file: app.py
10
- pinned: false
11
- license: cc-by-nc-4.0
12
- short_description: ML Applications in Tissue Engineering
13
- ---
14
-
15
-
16
- # 👬 MLATE V3: Multi-Tissue Scaffold Prediction Platform
17
-
18
- **MLATE V3** is a fully integrated, machine learning-powered platform for predicting, optimizing, and generating detailed fabrication procedures for 3D-(bio)printed scaffolds in tissue engineering. This app enables researchers to input a wide range of biomaterials, cell lines, and printing parameters, and receive optimized scaffold compositions along with step-by-step printing instructions generated via Google Gemini.
19
-
20
- > 📄 *Rafieyan et al. (preprint, 2025). MLATE V3: A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering*
21
-
22
- ---
23
-
24
- ## 🚀 Features
25
-
26
- - 🔬 Predict scaffold quality based on printability and cell response
27
- - 🧪 Optimize biomaterial concentrations, cell densities, and printing parameters using Optuna
28
- - 🧠 Powered by two fine-tuned **CatBoostClassifier** models
29
- - 📋 Automatically generates fabrication protocols with Gemini API
30
- - 🔐 Enforces safe defaults and intelligent UI input validation
31
- - 🧱 Uses a real-world, curated dataset of **2847 samples** across **multiple tissues and cell lines**
32
-
33
- ---
34
-
35
- ## 📂 Dataset
36
-
37
- This project includes a publicly available dataset (`Dataset.xlsx`) containing:
38
- - 123 biomaterials
39
- - 175 cell lines
40
- - 7 printing parameters
41
- - Scaffold performance labels
42
-
43
- The dataset is available in the [Files and Versions](https://huggingface.co/spaces/your-username/your-space-name/blob/main/Dataset.xlsx) tab of this Space.
44
-
45
- You may also optionally add this to Hugging Face Datasets for broader access.
46
-
47
- ---
48
-
49
- ## ⚙️ How It Works
50
-
51
- 1. **Input**: User selects biomaterials, cell line, and printing parameters with min/max/step values
52
- 2. **Optimization**: Optuna runs 50 trials to maximize predicted scaffold quality (WSSQ)
53
- 3. **Prediction**:
54
- - Two CatBoost models are used to predict:
55
- - `Printability` (3-class)
56
- - `Cell Response` (5-class)
57
- - Probabilistic predictions are mapped to expected scores
58
- 4. **Scaffold Quality**: A weighted combination of printability and cell response
59
- 5. **Procedure Generation**: A Gemini API prompt generates custom step-by-step fabrication instructions
60
-
61
- ---
62
-
63
- ## 💻 Running Locally
64
-
65
- Clone the repo and install dependencies:
66
-
67
- ```bash
68
- git clone https://huggingface.co/spaces/your-username/MLATE-V3
69
- cd MLATE-V3
70
-
71
- # Create virtual environment (optional)
72
- python -m venv venv
73
- source venv/bin/activate # or venv\Scripts\activate on Windows
74
-
75
- # Install dependencies
76
- pip install -r requirements.txt
77
-
78
- # Set your Gemini API key
79
- export GEMINI_API_KEY=your_key_here # or set in .env
80
-
81
- # Run the app
82
- streamlit run app.py
83
- ```
84
-
85
- ---
86
-
87
- ## 📜 License
88
-
89
- This project is licensed under the **Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)**.
90
-
91
- You are free to:
92
- - Share and adapt the code
93
- - Use the dataset for academic research
94
-
95
- But:
96
- - **Commercial use is prohibited**
97
- - **Citation is required** (see below)
98
-
99
- ---
100
-
101
- ## 📚 Citation
102
-
103
- If you use MLATE V3 or its dataset in your research, please cite:
104
-
105
- > Rafieyan et al. (preprint, 2025).
106
- > *MLATE V3: A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering*
107
- > *(Preprint link to be added after publication)*
108
-
109
- BibTeX:
110
- ```bibtex
111
- @article{rafieyan2025mlate,
112
- author = {Rafieyan, Saeed and others},
113
- title = {MLATE V3: A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering},
114
- journal = {Preprint},
115
- year = {2025}
116
- }
117
- ```
118
-
119
- ---
120
-
121
- ## ⚠️ Disclaimer
122
-
123
- This tool is intended for **research and academic use only**. While we strive for accuracy, the predictions and fabrication procedures are generated using machine learning and language models and may contain errors or inconsistencies. The authors are **not responsible for any unintended consequences** arising from use of this tool in experimental or clinical settings.
124
-
125
- ---
126
-
127
- Developed by [Saeed Rafieyan](https://sraf.ir)
 
1
+ ---
2
+ title: MLATE V3
3
+ emoji: 🧬
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: streamlit
7
+ sdk_version: 1.58.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ short_description: Optimises 3D-printed and bioprinted scaffolds
12
+ ---
13
+
14
+ # MLATE V3
15
+
16
+ Predicts **printability** and **cell response** for 3D-printed and bioprinted
17
+ scaffolds, searches the formulation space for the composition and printing
18
+ conditions that maximise a combined quality score, and drafts a bench protocol
19
+ for the result.
20
+
21
+ Models were trained on 2,646 scaffold records extracted from the literature.
22
+ Predictions are decision support: they narrow the experimental search space and
23
+ do not replace experimental validation.
24
+
25
+ ## Using it
26
+
27
+ 1. Enter a range for each biomaterial you can work with, and for the printing
28
+ parameters your equipment allows. The optimiser searches inside those ranges.
29
+ 2. Choose a cell line, or `NoCellCultured` for acellular printing.
30
+ 3. Set the cell-response weight. Printability takes the remainder.
31
+ 4. Run the optimisation.
32
+ 5. Optionally generate a fabrication protocol. This step needs a free
33
+ [OpenRouter](https://openrouter.ai) key; nothing else does.
34
+
35
+ ## What the score means
36
+
37
+ WSSQ combines the two predicted outcomes through two conjunctive means, so a
38
+ scaffold cannot score well by excelling at one objective and failing the other.
39
+ Acellular formulations are scored on printability alone rather than penalised
40
+ for a biological outcome that does not apply to them.
41
+
42
+ ## Models
43
+
44
+ All three model families are offered for each target, ranked by weighted F1 on
45
+ the held-out test partition and refitted on the complete dataset. On a GPU host
46
+ the menu opens on the best model overall; on a CPU host it opens on the best
47
+ conventional classifier, because the in-context foundation models re-read all
48
+ 2,646 training records on every pass and turn a search of seconds into one of
49
+ minutes. They remain selectable, with their cost stated beside the menu.
50
+
51
+ The random-split artefacts are served, this being the interpolation regime in
52
+ which the tool is used: adjusting a concentration, substituting a cell line, or
53
+ moving a pressure within observed ranges. The study-grouped models are the
54
+ conservative estimate for an unseen laboratory and are reported in the paper.
55
+
56
+ ## Deploying your own copy
57
+
58
+ From a checkout of the repository:
59
+
60
+ ```
61
+ python 06_webapp/export_deployment.py # fit and export the artefacts
62
+ python 06_webapp/build_app_data.py # vocabularies, tables, corpus subset
63
+ python 06_webapp/deploy_to_hf.py # stage and check, no upload
64
+ python 06_webapp/deploy_to_hf.py --push # upload, after `hf auth login`
65
+ ```
66
+
67
+ `deploy_to_hf.py` assembles a self-contained tree under `deploy/space/`,
68
+ verifies that it imports and finds its models, and uploads it. Model artefacts
69
+ are not in this directory by default; they are added by that script.
70
+
71
+ ## Citation
72
+
73
+ Rafieyan *et al.*, *MLATE V3: An Open-Source Cross-Tissue AI Framework for
74
+ Data-Driven Optimization of 3D-Printed and Bioprinted Scaffolds*.
75
+ Code and dataset: https://github.com/saeedrafieyan/mlate
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,1012 +1,633 @@
1
- import os
2
- from pathlib import Path
3
- import streamlit as st
4
- import pandas as pd
5
- import joblib
6
- import optuna
7
- import numpy as np
8
-
9
- from google import genai
10
- from google.genai import types
11
-
12
- from biomaterials import BIOMATERIAL_OPTIONS
13
- from cell_lines import CELL_LINE_OPTIONS
14
-
15
- _original_number_input = st.number_input
16
-
17
- def safe_number_input(label, **kwargs):
18
- """
19
- Clamp `value` into [min_value, max_value] and warn if we had to adjust.
20
- Then call the real st.number_input with the clamped default.
21
- """
22
- min_value = kwargs.get("min_value", float("-inf"))
23
- max_value = kwargs.get("max_value", float("inf"))
24
- value = kwargs.get("value", min_value)
25
- clamped = min(max(value, min_value), max_value)
26
- if clamped != value:
27
- st.warning(
28
- f"⚠️ Default for “{label}” ({value}) was outside "
29
- f"[{min_value}, {max_value}]; using {clamped} instead."
30
- )
31
- kwargs["value"] = clamped
32
- return _original_number_input(label, **kwargs)
33
-
34
- st.number_input = safe_number_input
35
-
36
- APP_DIR = Path(__file__).resolve().parent
37
- MODEL_ROOT = APP_DIR / "models"
38
- PREPROCESSOR_DIR = MODEL_ROOT / "preprocessors"
39
-
40
- MODEL_TASKS = {
41
- "printability": {
42
- "folder": MODEL_ROOT / "printability",
43
- "prefix": "Printability_",
44
- "label_encoder": "label_encoder_printability.pkl",
45
- },
46
- "cell_response": {
47
- "folder": MODEL_ROOT / "cell response",
48
- "prefix": "Cell_Response_",
49
- "label_encoder": "label_encoder_cell_response.pkl",
50
- },
51
- }
52
-
53
- DL_MODEL_CONFIGS = {
54
- ("printability", "ResNet"): {"n_layers": 6, "hidden_dim": 302, "dropout": 0.190106, "activation_func": "relu"},
55
- ("printability", "MLP"): {"n_layers": 3, "hidden_dim": 367, "dropout": 0.169472, "activation_func": "tanh"},
56
- ("printability", "1D_CNN"): {"n_layers": 6, "hidden_dim": 287, "dropout": 0.233072, "activation_func": "relu"},
57
- ("printability", "FT_Transformer"): {"n_layers": 3, "hidden_dim": 437, "dropout": 0.324095, "activation_func": "GELU"},
58
- ("printability", "TabNet_Lite"): {"n_layers": 4, "hidden_dim": 283, "dropout": 0.309445, "activation_func": "relu"},
59
- ("printability", "NODE_Lite"): {"n_layers": 5, "hidden_dim": 289, "dropout": 0.145481, "activation_func": "SELU"},
60
- ("cell_response", "ResNet"): {"n_layers": 4, "hidden_dim": 269, "dropout": 0.229224, "activation_func": "tanh"},
61
- ("cell_response", "MLP"): {"n_layers": 5, "hidden_dim": 238, "dropout": 0.256294, "activation_func": "ELU"},
62
- ("cell_response", "1D_CNN"): {"n_layers": 6, "hidden_dim": 134, "dropout": 0.158794, "activation_func": "SiLU"},
63
- ("cell_response", "FT_Transformer"): {"n_layers": 5, "hidden_dim": 395, "dropout": 0.185155, "activation_func": "SiLU"},
64
- ("cell_response", "TabNet_Lite"): {"n_layers": 5, "hidden_dim": 342, "dropout": 0.103486, "activation_func": "SiLU"},
65
- ("cell_response", "NODE_Lite"): {"n_layers": 2, "hidden_dim": 127, "dropout": 0.328308, "activation_func": "SiLU"},
66
- }
67
-
68
- MODEL_RANKINGS = {
69
- "printability": [
70
- "HistGradientBoosting", "TabPFN 2.6", "Bagging", "GradientBoosting", "TabICL v2",
71
- "XGBoost", "LightGBM", "KNeighbors", "LabelPropagation", "ExtraTrees", "MLP",
72
- "LabelSpreading", "TabNet Lite", "MLP DL", "ResNet", "1D CNN", "DecisionTree",
73
- "NODE Lite", "LinearSVC", "LDA", "PassiveAggressive", "CalibratedClassifierCV",
74
- "LogisticRegression", "RidgeClassifier", "FT Transformer", "Perceptron",
75
- "MultinomialNB", "RadiusNeighbors", "ComplementNB", "NuSVC", "AdaBoost",
76
- "SGD", "BernoulliNB", "GaussianNB", "QDA", "RandomForest", "ExtraTreeClassifier",
77
- "DummyClassifier",
78
- ],
79
- "cell_response": [
80
- "HistGradientBoosting", "TabICL v2", "TabPFN 2.6", "XGBoost", "Bagging",
81
- "KNeighbors", "LabelPropagation", "LabelSpreading", "LightGBM", "FT Transformer",
82
- "CalibratedClassifierCV", "ResNet", "NODE Lite", "1D CNN", "MLP DL", "LDA",
83
- "NuSVC", "MultinomialNB", "LogisticRegression", "LinearSVC", "Perceptron",
84
- "QDA", "RidgeClassifier", "ExtraTrees", "ExtraTreeClassifier", "AdaBoost",
85
- "DecisionTree", "RandomForest", "GradientBoosting", "SGD", "MLP",
86
- "PassiveAggressive", "ComplementNB", "TabNet Lite", "BernoulliNB",
87
- "RadiusNeighbors", "DummyClassifier", "GaussianNB",
88
- ],
89
- }
90
-
91
- PERFORMANCE_GUIDE = {
92
- "printability": """
93
- | Rank | Model | Framework | Accuracy | F1 | AUC | MCC |
94
- |---:|---|---|---:|---:|---:|---:|
95
- | 1 | HistGradientBoosting | Machine Learning | 0.80 | 0.80 | 0.94 | 0.69 |
96
- | 2 | TabPFN 2.6 | Deep Learning / Transformer | 0.80 | 0.80 | 0.94 | 0.69 |
97
- | 3 | Bagging | Machine Learning | 0.79 | 0.79 | 0.93 | 0.67 |
98
- | 4 | GradientBoosting | Machine Learning | 0.79 | 0.79 | 0.93 | 0.66 |
99
- | 5 | TabICL v2 | Deep Learning / Transformer | 0.79 | 0.79 | 0.94 | 0.68 |
100
- | 6 | XGBoost | Machine Learning | 0.78 | 0.78 | 0.93 | 0.66 |
101
- | 7 | LightGBM | Machine Learning | 0.77 | 0.77 | 0.93 | 0.64 |
102
- | 8 | KNeighbors | Machine Learning | 0.77 | 0.77 | 0.90 | 0.64 |
103
- | 9 | LabelPropagation | Machine Learning | 0.77 | 0.77 | 0.82 | 0.64 |
104
- | 10 | ExtraTrees | Machine Learning | 0.77 | 0.77 | 0.92 | 0.63 |
105
- """,
106
- "cell_response": """
107
- | Rank | Model | Framework | Accuracy | F1 | AUC | MCC |
108
- |---:|---|---|---:|---:|---:|---:|
109
- | 1 | HistGradientBoosting | Machine Learning | 0.81 | 0.81 | 0.96 | 0.67 |
110
- | 2 | TabICL v2 | Deep Learning / Transformer | 0.79 | 0.79 | 0.97 | 0.65 |
111
- | 3 | TabPFN 2.6 | Deep Learning / Transformer | 0.78 | 0.78 | 0.96 | 0.63 |
112
- | 4 | XGBoost | Machine Learning | 0.79 | 0.77 | 0.96 | 0.65 |
113
- | 5 | Bagging | Machine Learning | 0.77 | 0.77 | 0.95 | 0.62 |
114
- | 6 | KNeighbors | Machine Learning | 0.77 | 0.77 | 0.93 | 0.61 |
115
- | 7 | LabelPropagation | Machine Learning | 0.77 | 0.77 | 0.93 | 0.61 |
116
- | 8 | LabelSpreading | Machine Learning | 0.78 | 0.76 | 0.93 | 0.61 |
117
- | 9 | LightGBM | Machine Learning | 0.78 | 0.76 | 0.96 | 0.64 |
118
- | 10 | FT Transformer | Deep Learning / Transformer | 0.76 | 0.76 | 0.94 | 0.61 |
119
- """,
120
- }
121
-
122
- PERFORMANCE_GUIDE_FULL = {
123
- "printability": """
124
- # Printability - Merged Performance Summary
125
-
126
- This file contains the aggregated and benchmarked results of traditional Machine Learning (ML) and Deep Learning / Transformer architectures for predicting **Printability**, sorted hierarchically by **F1 Score** and **Accuracy**.
127
-
128
- | Rank | Model | Framework | Accuracy | Precision | Recall | F1 | AUC | MCC | Kappa |
129
- |---:|---|---|---:|---:|---:|---:|---:|---:|---:|
130
- | 1 | HistGradientBoosting | Machine Learning | 0.80 | 0.80 | 0.80 | 0.80 | 0.94 | 0.69 | 0.69 |
131
- | 2 | TabPFN 2.6 | Deep Learning / Transformer | 0.80 | 0.81 | 0.80 | 0.80 | 0.94 | 0.69 | 0.68 |
132
- | 3 | Bagging | Machine Learning | 0.79 | 0.79 | 0.79 | 0.79 | 0.93 | 0.67 | 0.67 |
133
- | 4 | GradientBoosting | Machine Learning | 0.79 | 0.79 | 0.79 | 0.79 | 0.93 | 0.66 | 0.66 |
134
- | 5 | TabICL v2 | Deep Learning / Transformer | 0.79 | 0.81 | 0.79 | 0.79 | 0.94 | 0.68 | 0.67 |
135
- | 6 | XGBoost | Machine Learning | 0.78 | 0.79 | 0.78 | 0.78 | 0.93 | 0.66 | 0.65 |
136
- | 7 | LightGBM | Machine Learning | 0.77 | 0.78 | 0.77 | 0.77 | 0.93 | 0.64 | 0.64 |
137
- | 8 | KNeighbors | Machine Learning | 0.77 | 0.78 | 0.77 | 0.77 | 0.90 | 0.64 | 0.64 |
138
- | 9 | LabelPropagation | Machine Learning | 0.77 | 0.77 | 0.77 | 0.77 | 0.82 | 0.64 | 0.63 |
139
- | 10 | ExtraTrees | Machine Learning | 0.77 | 0.77 | 0.77 | 0.77 | 0.92 | 0.63 | 0.63 |
140
- | 11 | MLP | Machine Learning | 0.76 | 0.76 | 0.76 | 0.76 | 0.90 | 0.63 | 0.63 |
141
- | 12 | LabelSpreading | Machine Learning | 0.76 | 0.76 | 0.76 | 0.76 | 0.90 | 0.62 | 0.62 |
142
- | 13 | TabNet Lite | Deep Learning / Transformer | 0.74 | 0.74 | 0.74 | 0.74 | 0.91 | 0.59 | 0.59 |
143
- | 14 | MLP (DL) | Deep Learning / Transformer | 0.73 | 0.73 | 0.73 | 0.73 | 0.91 | 0.58 | 0.58 |
144
- | 15 | ResNet | Deep Learning / Transformer | 0.73 | 0.73 | 0.73 | 0.73 | 0.91 | 0.57 | 0.57 |
145
- | 16 | 1D CNN | Deep Learning / Transformer | 0.71 | 0.73 | 0.71 | 0.72 | 0.89 | 0.56 | 0.56 |
146
- | 17 | DecisionTree | Machine Learning | 0.71 | 0.72 | 0.71 | 0.71 | 0.89 | 0.54 | 0.54 |
147
- | 18 | NODE Lite | Deep Learning / Transformer | 0.69 | 0.70 | 0.69 | 0.70 | 0.90 | 0.52 | 0.52 |
148
- | 19 | LinearSVC | Machine Learning | 0.69 | 0.68 | 0.69 | 0.68 | 0.85 | 0.49 | 0.49 |
149
- | 20 | LDA | Machine Learning | 0.68 | 0.68 | 0.68 | 0.68 | 0.84 | 0.49 | 0.48 |
150
- | 21 | PassiveAggressive | Machine Learning | 0.69 | 0.68 | 0.69 | 0.67 | 0.85 | 0.48 | 0.48 |
151
- | 22 | CalibratedClassifierCV | Machine Learning | 0.68 | 0.68 | 0.68 | 0.67 | 0.85 | 0.48 | 0.48 |
152
- | 23 | LogisticRegression | Machine Learning | 0.68 | 0.67 | 0.68 | 0.67 | 0.85 | 0.47 | 0.47 |
153
- | 24 | RidgeClassifier | Machine Learning | 0.68 | 0.67 | 0.68 | 0.66 | 0.84 | 0.46 | 0.46 |
154
- | 25 | FT Transformer | Deep Learning / Transformer | 0.66 | 0.65 | 0.66 | 0.65 | 0.87 | 0.45 | 0.45 |
155
- | 26 | Perceptron | Machine Learning | 0.67 | 0.68 | 0.67 | 0.64 | 0.84 | 0.45 | 0.42 |
156
- | 27 | MultinomialNB | Machine Learning | 0.64 | 0.69 | 0.64 | 0.64 | 0.83 | 0.45 | 0.43 |
157
- | 28 | RadiusNeighbors | Machine Learning | 0.66 | 0.70 | 0.66 | 0.63 | 0.87 | 0.43 | 0.39 |
158
- | 29 | ComplementNB | Machine Learning | 0.63 | 0.67 | 0.63 | 0.63 | 0.82 | 0.44 | 0.43 |
159
- | 30 | NuSVC | Machine Learning | 0.60 | 0.69 | 0.60 | 0.61 | 0.84 | 0.45 | 0.42 |
160
- | 31 | AdaBoost | Machine Learning | 0.62 | 0.59 | 0.62 | 0.58 | 0.80 | 0.38 | 0.37 |
161
- | 32 | SGD | Machine Learning | 0.63 | 0.59 | 0.63 | 0.57 | 0.82 | 0.36 | 0.35 |
162
- | 33 | BernoulliNB | Machine Learning | 0.59 | 0.62 | 0.59 | 0.55 | 0.77 | 0.32 | 0.31 |
163
- | 34 | GaussianNB | Machine Learning | 0.33 | 0.70 | 0.33 | 0.36 | 0.74 | 0.25 | 0.19 |
164
- | 35 | QDA | Machine Learning | 0.52 | 0.27 | 0.52 | 0.35 | 0.80 | 0.00 | 0.00 |
165
- | 36 | RandomForest | Machine Learning | 0.52 | 0.27 | 0.52 | 0.35 | 0.78 | 0.00 | 0.00 |
166
- | 37 | ExtraTreeClassifier | Machine Learning | 0.52 | 0.27 | 0.52 | 0.35 | 0.50 | 0.00 | 0.00 |
167
- | 38 | DummyClassifier | Machine Learning | 0.52 | 0.27 | 0.52 | 0.35 | 0.50 | 0.00 | 0.00 |
168
- """,
169
- "cell_response": """
170
- # Cell Response - Merged Performance Summary
171
-
172
- This file contains the aggregated and benchmarked results of traditional Machine Learning (ML) and Deep Learning / Transformer architectures for predicting **Cell Response**, sorted hierarchically by **F1 Score** and **Accuracy**.
173
-
174
- | Rank | Model | Framework | Accuracy | Precision | Recall | F1 | AUC | MCC | Kappa |
175
- |---:|---|---|---:|---:|---:|---:|---:|---:|---:|
176
- | 1 | HistGradientBoosting | Machine Learning | 0.81 | 0.81 | 0.81 | 0.81 | 0.96 | 0.67 | 0.67 |
177
- | 2 | TabICL v2 | Deep Learning / Transformer | 0.79 | 0.79 | 0.79 | 0.79 | 0.97 | 0.65 | 0.65 |
178
- | 3 | TabPFN 2.6 | Deep Learning / Transformer | 0.78 | 0.78 | 0.78 | 0.78 | 0.96 | 0.63 | 0.63 |
179
- | 4 | XGBoost | Machine Learning | 0.79 | 0.76 | 0.79 | 0.77 | 0.96 | 0.65 | 0.64 |
180
- | 5 | Bagging | Machine Learning | 0.77 | 0.78 | 0.77 | 0.77 | 0.95 | 0.62 | 0.62 |
181
- | 6 | KNeighbors | Machine Learning | 0.77 | 0.77 | 0.77 | 0.77 | 0.93 | 0.61 | 0.61 |
182
- | 7 | LabelPropagation | Machine Learning | 0.77 | 0.77 | 0.77 | 0.77 | 0.93 | 0.61 | 0.61 |
183
- | 8 | LabelSpreading | Machine Learning | 0.78 | 0.76 | 0.78 | 0.76 | 0.93 | 0.61 | 0.61 |
184
- | 9 | LightGBM | Machine Learning | 0.78 | 0.79 | 0.78 | 0.76 | 0.96 | 0.64 | 0.63 |
185
- | 10 | FT Transformer | Deep Learning / Transformer | 0.76 | 0.79 | 0.76 | 0.76 | 0.94 | 0.61 | 0.60 |
186
- | 11 | CalibratedClassifierCV | Machine Learning | 0.76 | 0.76 | 0.76 | 0.75 | 0.94 | 0.59 | 0.59 |
187
- | 12 | ResNet | Deep Learning / Transformer | 0.76 | 0.76 | 0.76 | 0.75 | 0.94 | 0.59 | 0.59 |
188
- | 13 | NODE Lite | Deep Learning / Transformer | 0.76 | 0.75 | 0.76 | 0.75 | 0.95 | 0.59 | 0.59 |
189
- | 14 | 1D CNN | Deep Learning / Transformer | 0.75 | 0.76 | 0.75 | 0.74 | 0.95 | 0.59 | 0.59 |
190
- | 15 | MLP (DL) | Deep Learning / Transformer | 0.75 | 0.75 | 0.75 | 0.74 | 0.95 | 0.57 | 0.57 |
191
- | 16 | LDA | Machine Learning | 0.75 | 0.72 | 0.75 | 0.73 | 0.94 | 0.55 | 0.55 |
192
- | 17 | NuSVC | Machine Learning | 0.74 | 0.73 | 0.74 | 0.73 | 0.95 | 0.55 | 0.55 |
193
- | 18 | MultinomialNB | Machine Learning | 0.74 | 0.72 | 0.74 | 0.73 | 0.95 | 0.55 | 0.55 |
194
- | 19 | LogisticRegression | Machine Learning | 0.74 | 0.70 | 0.74 | 0.71 | 0.95 | 0.54 | 0.54 |
195
- | 20 | LinearSVC | Machine Learning | 0.74 | 0.71 | 0.74 | 0.70 | 0.94 | 0.53 | 0.52 |
196
- | 21 | Perceptron | Machine Learning | 0.74 | 0.68 | 0.74 | 0.70 | 0.94 | 0.53 | 0.52 |
197
- | 22 | QDA | Machine Learning | 0.74 | 0.68 | 0.74 | 0.70 | 0.94 | 0.57 | 0.55 |
198
- | 23 | RidgeClassifier | Machine Learning | 0.73 | 0.70 | 0.73 | 0.70 | 0.94 | 0.52 | 0.51 |
199
- | 24 | ExtraTrees | Machine Learning | 0.74 | 0.71 | 0.74 | 0.68 | 0.95 | 0.55 | 0.52 |
200
- | 25 | ExtraTreeClassifier | Machine Learning | 0.73 | 0.67 | 0.73 | 0.68 | 0.91 | 0.54 | 0.52 |
201
- | 26 | AdaBoost | Machine Learning | 0.74 | 0.65 | 0.74 | 0.67 | 0.91 | 0.59 | 0.55 |
202
- | 27 | DecisionTree | Machine Learning | 0.74 | 0.65 | 0.74 | 0.67 | 0.92 | 0.59 | 0.55 |
203
- | 28 | RandomForest | Machine Learning | 0.74 | 0.65 | 0.74 | 0.67 | 0.92 | 0.59 | 0.55 |
204
- | 29 | GradientBoosting | Machine Learning | 0.74 | 0.65 | 0.74 | 0.67 | 0.92 | 0.59 | 0.55 |
205
- | 30 | SGD | Machine Learning | 0.71 | 0.65 | 0.71 | 0.66 | 0.92 | 0.49 | 0.47 |
206
- | 31 | MLP | Machine Learning | 0.73 | 0.61 | 0.73 | 0.65 | 0.93 | 0.55 | 0.52 |
207
- | 32 | PassiveAggressive | Machine Learning | 0.73 | 0.65 | 0.73 | 0.65 | 0.92 | 0.52 | 0.49 |
208
- | 33 | ComplementNB | Machine Learning | 0.72 | 0.65 | 0.72 | 0.65 | 0.93 | 0.49 | 0.47 |
209
- | 34 | TabNet Lite | Deep Learning / Transformer | 0.70 | 0.67 | 0.70 | 0.65 | 0.93 | 0.48 | 0.46 |
210
- | 35 | BernoulliNB | Machine Learning | 0.71 | 0.60 | 0.71 | 0.63 | 0.93 | 0.48 | 0.45 |
211
- | 36 | RadiusNeighbors | Machine Learning | 0.60 | 0.36 | 0.60 | 0.45 | 0.50 | 0.00 | 0.00 |
212
- | 37 | DummyClassifier | Machine Learning | 0.60 | 0.36 | 0.60 | 0.45 | 0.50 | 0.00 | 0.00 |
213
- | 38 | GaussianNB | Machine Learning | 0.28 | 0.69 | 0.28 | 0.34 | 0.84 | 0.18 | 0.14 |
214
- """,
215
- }
216
-
217
- GEMINI_MODELS = [
218
- "gemini-3.5-flash",
219
- "gemini-3.1-flash-lite",
220
- "gemini-3.1-pro-preview",
221
- "gemini-3.1-flash-lite-preview",
222
- "gemini-3-flash-preview",
223
- "gemini-2.5-pro",
224
- "gemini-2.5-flash",
225
- "gemini-2.5-flash-lite",
226
- ]
227
-
228
- def scaffold_quality_combined(printability, cell_response,
229
- weight_printability=0.3, weight_cell_response=0.7):
230
- """
231
- Calculates the Weighted Scaffold Synthesis Quality (WSSQ).
232
- """
233
- if printability == 0:
234
- return 0.0
235
-
236
- # Normalization
237
- norm_p = printability / 3.0
238
-
239
- # If cell_response is 1 (minimum), avoid division by zero in harmonic mean
240
- if cell_response <= 1:
241
- return 100 * norm_p
242
-
243
- norm_c = (cell_response - 1) / 4.0
244
-
245
- # Weighted Harmonic Mean
246
- hm = (weight_printability + weight_cell_response) / (
247
- (weight_printability / norm_p) +
248
- (weight_cell_response / norm_c)
249
- )
250
-
251
- # Weighted Multiplicative Component
252
- mc = (norm_p**weight_printability) * (norm_c**weight_cell_response)
253
-
254
- return 100 * ((hm + mc) / 2.0)
255
-
256
- PRINT_PARAM_NAMES = [
257
- "Physical Crosslinking Duration (s)",
258
- "Photo Crosslinking Duration (s)",
259
- "Extrusion Pressure (kPa)",
260
- "Nozzle Movement Speed (mm/s)",
261
- "Nozzle Diameter (µm)",
262
- "Syringe Temperature (°C)",
263
- "Substrate Temperature (°C)",
264
- ]
265
-
266
- @st.cache_resource
267
- def load_prediction_preprocessors():
268
- return {
269
- "preprocessor": joblib.load(PREPROCESSOR_DIR / "preprocessor.pkl"),
270
- "feature_cols": joblib.load(PREPROCESSOR_DIR / "feature_cols.pkl"),
271
- "printability_encoder": joblib.load(PREPROCESSOR_DIR / MODEL_TASKS["printability"]["label_encoder"]),
272
- "cell_response_encoder": joblib.load(PREPROCESSOR_DIR / MODEL_TASKS["cell_response"]["label_encoder"]),
273
- }
274
-
275
- def model_display_name(path, prefix):
276
- name = path.stem.replace("_model", "")
277
- if name.startswith(prefix):
278
- name = name[len(prefix):]
279
- return f"{name} ({path.suffix.lstrip('.')})"
280
-
281
- def model_rank_key(display_name, task_key):
282
- base_name = display_name.rsplit(" (", 1)[0]
283
- rank_aliases = {
284
- "1D_CNN": "1D CNN",
285
- "FT_Transformer": "FT Transformer",
286
- "NODE_Lite": "NODE Lite",
287
- "TabNet_Lite": "TabNet Lite",
288
- "TabPFN_2.6": "TabPFN 2.6",
289
- "TabICL_v2": "TabICL v2",
290
- "ExtraTree": "ExtraTreeClassifier",
291
- }
292
- rank_name = rank_aliases.get(base_name, base_name)
293
- if base_name == "MLP" and display_name.endswith("(pth)"):
294
- rank_name = "MLP DL"
295
- ranking = MODEL_RANKINGS[task_key]
296
- rank = ranking.index(rank_name) if rank_name in ranking else len(ranking)
297
- return rank, base_name.lower(), display_name
298
-
299
- def discover_model_options(task_key):
300
- task = MODEL_TASKS[task_key]
301
- files = []
302
- for suffix in ("*.pkl", "*.joblib", "*.pth"):
303
- files.extend(task["folder"].glob(suffix))
304
- options = {model_display_name(path, task["prefix"]): str(path) for path in files}
305
- return dict(sorted(options.items(), key=lambda item: model_rank_key(item[0], task_key)))
306
-
307
- def parse_architecture(path, task_key):
308
- stem = Path(path).stem.replace("_model", "")
309
- prefix = MODEL_TASKS[task_key]["prefix"]
310
- return stem[len(prefix):] if stem.startswith(prefix) else stem
311
-
312
- def build_torch_model(architecture, input_dim, out_dim, cfg):
313
- import torch
314
- import torch.nn as nn
315
-
316
- activation_funcs = {
317
- "relu": nn.ReLU, "tanh": nn.Tanh, "GELU": nn.GELU,
318
- "SELU": nn.SELU, "ELU": nn.ELU, "SiLU": nn.SiLU,
319
- }
320
- act = activation_funcs[cfg["activation_func"]]
321
-
322
- class ResidualBlock(nn.Module):
323
- def __init__(self):
324
- super().__init__()
325
- self.linear = nn.Linear(cfg["hidden_dim"], cfg["hidden_dim"])
326
- self.bn = nn.BatchNorm1d(cfg["hidden_dim"])
327
- self.act = act()
328
- self.dropout = nn.Dropout(cfg["dropout"])
329
- def forward(self, x):
330
- return x + self.dropout(self.act(self.bn(self.linear(x))))
331
-
332
- class TissueResNet(nn.Module):
333
- def __init__(self):
334
- super().__init__()
335
- self.input_layer = nn.Sequential(
336
- nn.Linear(input_dim, cfg["hidden_dim"]),
337
- nn.BatchNorm1d(cfg["hidden_dim"]),
338
- act(),
339
- )
340
- self.blocks = nn.ModuleList([ResidualBlock() for _ in range(cfg["n_layers"])])
341
- self.output_layer = nn.Linear(cfg["hidden_dim"], out_dim)
342
- def forward(self, x):
343
- x = self.input_layer(x)
344
- for block in self.blocks:
345
- x = block(x)
346
- return self.output_layer(x)
347
-
348
- class StandardMLP(nn.Module):
349
- def __init__(self):
350
- super().__init__()
351
- layers = []
352
- in_dim = input_dim
353
- for _ in range(cfg["n_layers"]):
354
- layers.extend([
355
- nn.Linear(in_dim, cfg["hidden_dim"]),
356
- nn.BatchNorm1d(cfg["hidden_dim"]),
357
- act(),
358
- nn.Dropout(cfg["dropout"]),
359
- ])
360
- in_dim = cfg["hidden_dim"]
361
- layers.append(nn.Linear(cfg["hidden_dim"], out_dim))
362
- self.network = nn.Sequential(*layers)
363
- def forward(self, x):
364
- return self.network(x)
365
-
366
- class Tabular1DCNN(nn.Module):
367
- def __init__(self):
368
- super().__init__()
369
- layers = []
370
- in_channels = 1
371
- for _ in range(cfg["n_layers"]):
372
- layers.extend([
373
- nn.Conv1d(in_channels, cfg["hidden_dim"], kernel_size=3, padding=1),
374
- nn.BatchNorm1d(cfg["hidden_dim"]),
375
- act(),
376
- nn.Dropout(cfg["dropout"]),
377
- ])
378
- in_channels = cfg["hidden_dim"]
379
- self.conv_net = nn.Sequential(*layers)
380
- self.pool = nn.AdaptiveAvgPool1d(1)
381
- self.fc = nn.Linear(cfg["hidden_dim"], out_dim)
382
- def forward(self, x):
383
- x = self.conv_net(x.unsqueeze(1))
384
- return self.fc(self.pool(x).squeeze(2))
385
-
386
- class FTTransformer(nn.Module):
387
- def __init__(self):
388
- super().__init__()
389
- self.d_token = max(4, (cfg["hidden_dim"] // 4) * 4)
390
- self.feature_embeddings = nn.ModuleList([nn.Linear(1, self.d_token) for _ in range(input_dim)])
391
- self.cls_token = nn.Parameter(torch.randn(1, 1, self.d_token))
392
- encoder_layer = nn.TransformerEncoderLayer(
393
- d_model=self.d_token, nhead=4, dropout=cfg["dropout"], batch_first=True
394
- )
395
- self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=cfg["n_layers"])
396
- self.fc = nn.Linear(self.d_token, out_dim)
397
- def forward(self, x):
398
- batch_size = x.size(0)
399
- tokens = [self.feature_embeddings[i](x[:, i:i+1]).unsqueeze(1) for i in range(x.size(1))]
400
- x_emb = torch.cat([self.cls_token.expand(batch_size, -1, -1)] + tokens, dim=1)
401
- return self.fc(self.transformer(x_emb)[:, 0, :])
402
-
403
- class TabNetLite(nn.Module):
404
- def __init__(self):
405
- super().__init__()
406
- self.n_steps = max(1, cfg["n_layers"])
407
- self.initial_bn = nn.BatchNorm1d(input_dim)
408
- self.transformers = nn.ModuleList([
409
- nn.Sequential(
410
- nn.Linear(input_dim, cfg["hidden_dim"]),
411
- nn.BatchNorm1d(cfg["hidden_dim"]),
412
- act(),
413
- nn.Dropout(cfg["dropout"]),
414
- )
415
- for _ in range(self.n_steps)
416
- ])
417
- self.attentions = nn.ModuleList([
418
- nn.Sequential(
419
- nn.Linear(cfg["hidden_dim"], input_dim),
420
- nn.BatchNorm1d(input_dim),
421
- nn.Softmax(dim=-1),
422
- )
423
- for _ in range(self.n_steps)
424
- ])
425
- self.fc_out = nn.Linear(cfg["hidden_dim"], out_dim)
426
- def forward(self, x):
427
- x = self.initial_bn(x)
428
- out_agg, prior = 0, torch.ones_like(x)
429
- feat_rep = self.transformers[0](x)
430
- for step in range(self.n_steps):
431
- mask = self.attentions[step](feat_rep) * prior
432
- prior = prior * (1.0 - mask)
433
- feat_rep = self.transformers[step](x * mask)
434
- out_agg += feat_rep
435
- return self.fc_out(out_agg)
436
-
437
- class NeuralDecisionForest(nn.Module):
438
- def __init__(self):
439
- super().__init__()
440
- self.n_trees = max(1, cfg["hidden_dim"] // 16)
441
- self.depth = max(2, cfg["n_layers"] + 1)
442
- self.n_leaves = 2 ** self.depth
443
- self.trees = nn.ModuleList([
444
- nn.Sequential(
445
- nn.Linear(input_dim, self.n_leaves),
446
- nn.Dropout(cfg["dropout"]),
447
- nn.Softmax(dim=-1),
448
- )
449
- for _ in range(self.n_trees)
450
- ])
451
- self.leaf_weights = nn.Parameter(torch.randn(self.n_trees, self.n_leaves, out_dim))
452
- def forward(self, x):
453
- out = 0
454
- for i, tree in enumerate(self.trees):
455
- out += torch.matmul(tree(x), self.leaf_weights[i])
456
- return out / self.n_trees
457
-
458
- builders = {
459
- "ResNet": TissueResNet,
460
- "MLP": StandardMLP,
461
- "1D_CNN": Tabular1DCNN,
462
- "FT_Transformer": FTTransformer,
463
- "TabNet_Lite": TabNetLite,
464
- "NODE_Lite": NeuralDecisionForest,
465
- }
466
- return builders[architecture]()
467
-
468
- @st.cache_resource
469
- def load_prediction_model(path, task_key, input_dim, out_dim):
470
- path = Path(path)
471
- if path.suffix in {".pkl", ".joblib"}:
472
- return {"kind": "sklearn", "model": joblib.load(path)}
473
-
474
- if path.suffix == ".pth":
475
- import torch
476
- architecture = parse_architecture(path, task_key)
477
- cfg = DL_MODEL_CONFIGS.get((task_key, architecture))
478
- if cfg is None:
479
- raise ValueError(f"No architecture configuration found for {path.name}.")
480
- model = build_torch_model(architecture, input_dim, out_dim, cfg)
481
- state_dict = torch.load(path, map_location="cpu")
482
- model.load_state_dict(state_dict)
483
- model.eval()
484
- return {"kind": "torch", "model": model}
485
-
486
- raise ValueError(f"Unsupported model file: {path.name}")
487
-
488
- def softmax(values):
489
- values = np.asarray(values, dtype=float)
490
- values = values - np.max(values)
491
- exp_values = np.exp(values)
492
- return exp_values / exp_values.sum()
493
-
494
- def expected_class_value(model_bundle, x_raw, preprocessor, label_encoder):
495
- x_model = preprocessor.transform(x_raw).astype(np.float32)
496
-
497
- if model_bundle["kind"] == "torch":
498
- import torch
499
- with torch.no_grad():
500
- logits = model_bundle["model"](torch.tensor(x_model, dtype=torch.float32)).numpy()[0]
501
- probs = softmax(logits)
502
- labels = label_encoder.classes_.astype(float)
503
- return float(np.dot(probs, labels))
504
-
505
- model = model_bundle["model"]
506
- if hasattr(model, "predict_proba"):
507
- probs = model.predict_proba(x_model)[0]
508
- classes = np.asarray(model.classes_, dtype=int)
509
- labels = label_encoder.inverse_transform(classes).astype(float)
510
- return float(np.dot(probs, labels))
511
-
512
- if hasattr(model, "decision_function"):
513
- scores = np.asarray(model.decision_function(x_model)[0])
514
- if scores.ndim == 0:
515
- p_high = 1.0 / (1.0 + np.exp(-scores))
516
- probs = np.array([1.0 - p_high, p_high])
517
- else:
518
- probs = softmax(scores)
519
- classes = np.asarray(model.classes_, dtype=int)
520
- labels = label_encoder.inverse_transform(classes).astype(float)
521
- return float(np.dot(probs, labels))
522
-
523
- pred = np.asarray(model.predict(x_model), dtype=int)
524
- return float(label_encoder.inverse_transform(pred)[0])
525
-
526
- prediction_assets = load_prediction_preprocessors()
527
- preprocessor = prediction_assets["preprocessor"]
528
- feature_cols = prediction_assets["feature_cols"]
529
- label_encoder_print = prediction_assets["printability_encoder"]
530
- label_encoder_cell = prediction_assets["cell_response_encoder"]
531
-
532
- sample_for_shape = {col: 0.0 for col in feature_cols}
533
- sample_for_shape["Cell Line"] = CELL_LINE_OPTIONS[0]
534
- preprocessed_input_dim = preprocessor.transform(pd.DataFrame([sample_for_shape])[feature_cols]).shape[1]
535
-
536
- @st.dialog("Optimization Trials")
537
- def show_trial_guidance():
538
- st.markdown(
539
- """
540
- The trial count controls how many candidate scaffold settings Optuna tests before choosing the best WSSQ.
541
-
542
- **Recommended range:** 100-1000 trials.
543
-
544
- **Default:** 300 trials, which is a balanced choice for normal use.
545
-
546
- **Runtime impact:** running time grows roughly in proportion to the number of trials. Use 50-100 for a quick test, 300 for a balanced run, and 500-1000 when you want a more thorough search and can wait longer.
547
- """
548
- )
549
-
550
- @st.dialog("Weighted Synergistic Scaffold Quality (WSSQ)", width="large")
551
- def show_wssq_guidance():
552
- st.markdown(
553
- """
554
- WSSQ is the optimization score used in MLATE to combine **printability** and **cell response** into one scaffold-quality objective.
555
-
556
- WSSQ was introduced to handle two practical needs: acellular 3D-printed scaffolds, where cell response is not applicable in the same way, and bioprinted scaffolds, where biological response is central to scaffold quality.
557
-
558
- The app normalizes printability and cell response, then combines them using two components:
559
-
560
- - **Weighted harmonic mean:** rewards balanced high values and penalizes weak performance in either target.
561
- - **Weighted multiplicative component:** captures synergy between printability and cell response.
562
-
563
- The final WSSQ is scaled from 0 to 100%. If printability is 0, WSSQ is 0. If cell response is at the minimum biological-response level, the score falls back to normalized printability. Otherwise, both targets contribute according to the sidebar weights.
564
-
565
- Practically, a scaffold with excellent cell response but poor printability can score lower than a scaffold with slightly lower cell response but better balance, because WSSQ is designed to favor experimentally useful, well-balanced scaffold candidates.
566
- """
567
- )
568
-
569
- @st.dialog("Model Selection Guide", width="large")
570
- def show_model_selection_guidance():
571
- st.markdown(
572
- """
573
- Models are ranked by F1 score and accuracy. The highest-ranked available model appears first in each dropdown.
574
-
575
- Use the top-ranked models when you want the strongest benchmarked predictive performance. If a selected model fails to load because of local package-version incompatibility, choose the next ranked model in the same task until the environment is aligned with the model artifacts.
576
- """
577
- )
578
- tab_print, tab_cell = st.tabs(["Printability", "Cell Response"])
579
- with tab_print:
580
- st.markdown(PERFORMANCE_GUIDE["printability"])
581
- with st.expander("Show more"):
582
- st.markdown(PERFORMANCE_GUIDE_FULL["printability"])
583
- with tab_cell:
584
- st.markdown(PERFORMANCE_GUIDE["cell_response"])
585
- with st.expander("Show more"):
586
- st.markdown(PERFORMANCE_GUIDE_FULL["cell_response"])
587
-
588
- @st.dialog("How to Get a Gemini API Key")
589
- def show_gemini_api_key_guidance():
590
- st.markdown(
591
- """
592
- To generate a fabrication procedure, you need a Gemini API key from Google. Creating a key only takes a minute, and Google provides a free tier.
593
-
594
- 1. Open [Google AI Studio API Keys](https://aistudio.google.com/app/apikey) and sign in with your Google/Gmail account if prompted.
595
- 2. If this is your first visit, accept the terms of service and continue.
596
- 3. Click **Get API key** or **Create API key**.
597
- 4. Choose an existing Google Cloud project, or select **Create API key in new project**.
598
- 5. Copy the generated key, return to this app, and paste it into the **Gemini API Key** box.
599
-
600
- **Important:** Treat your API key like a password. Do not share it publicly or paste it into files that will be uploaded online. This app uses your key only for the current protocol generation request and does not save it.
601
- """
602
- )
603
-
604
- if 'bio_rows' not in st.session_state:
605
- st.session_state.bio_rows = [{
606
- 'mat': BIOMATERIAL_OPTIONS[0],
607
- 'min': 0.0, 'max': 10.0, 'step': 0.1
608
- }]
609
-
610
- if 'density_range' not in st.session_state:
611
- st.session_state.density_range = {'min': 0.0, 'max': 10.0, 'step': 0.1}
612
-
613
- if 'pp_ranges' not in st.session_state:
614
- st.session_state.pp_ranges = {
615
- "Physical Crosslinking Duration (s)": {'min': 0.0, 'max': 300.0, 'step': 5.0},
616
- "Photo Crosslinking Duration (s)": {'min': 0.0, 'max': 180.0, 'step': 5.0},
617
- "Extrusion Pressure (kPa)": {'min': 5.0, 'max': 200.0, 'step': 5.0},
618
- "Nozzle Movement Speed (mm/s)": {'min': 1.0, 'max': 20.0, 'step': 0.5},
619
- "Nozzle Diameter (µm)": {'min': 100.0,'max': 1000.0, 'step': 50.0},
620
- "Syringe Temperature (°C)": {'min': 4.0, 'max': 40.0, 'step': 1.0},
621
- "Substrate Temperature (°C)": {'min': 4.0, 'max': 37.0, 'step': 1.0},
622
- }
623
-
624
- # --- Sidebar UI for Weights ---
625
- st.sidebar.header("Optimization Weights")
626
- # User only controls Cell Response (0 to 100)
627
- w_cell_pct = st.sidebar.slider("Cell Response Weight (%)", min_value=0, max_value=100, value=70, step=5)
628
- # Printability is dynamically calculated and cannot be changed manually
629
- w_print_pct = 100 - w_cell_pct
630
- st.sidebar.number_input("Printability Weight (%)", value=w_print_pct, disabled=True, help="Auto-calculated to ensure sum is 100%")
631
- if st.sidebar.button("What is WSSQ?", use_container_width=True):
632
- show_wssq_guidance()
633
-
634
- # Convert back to 0.0 - 1.0 for the mathematical formula
635
- w_cell = w_cell_pct / 100.0
636
- w_print = w_print_pct / 100.0
637
-
638
- print_model_options = discover_model_options("printability")
639
- cell_model_options = discover_model_options("cell_response")
640
-
641
- if not print_model_options or not cell_model_options:
642
- st.error("No selectable prediction models were found in the models folder.")
643
- st.stop()
644
-
645
- selected_print_model_name = st.sidebar.selectbox(
646
- "Printability Model",
647
- list(print_model_options.keys()),
648
- key="printability_model_select",
649
- )
650
- selected_cell_model_name = st.sidebar.selectbox(
651
- "Cell Response Model",
652
- list(cell_model_options.keys()),
653
- key="cell_response_model_select",
654
- )
655
- if st.sidebar.button("Model Selection Guide", use_container_width=True):
656
- show_model_selection_guidance()
657
-
658
- n_trials = st.sidebar.number_input(
659
- "Optimization Trials",
660
- min_value=10,
661
- max_value=10000,
662
- value=300,
663
- step=50,
664
- help="Number of Optuna trials used when you click Optimize WSSQ.",
665
- )
666
- if st.sidebar.button("Trial Count Help", use_container_width=True):
667
- show_trial_guidance()
668
-
669
- gemini_key = st.sidebar.text_input(
670
- "Gemini API Key",
671
- value=os.getenv("GEMINI_API_KEY", ""),
672
- type="password",
673
- help="Used only when generating the LLM-based fabrication procedure.",
674
- )
675
- if st.sidebar.button("How to Get API Key", use_container_width=True):
676
- show_gemini_api_key_guidance()
677
-
678
- gemini_model = st.sidebar.selectbox(
679
- "Gemini Model",
680
- GEMINI_MODELS,
681
- index=0,
682
- key="gemini_model_select",
683
- )
684
-
685
- st.title("MLATE: Machine Learning Applications in Tissue Engineering")
686
- st.markdown(
687
- "<p style='font-size:1.2em; color:grey;'>"
688
- "A Data-driven Cross-tissue Machine Learning Framework for Inverse design of 3D (Bio)printing Scaffolds "
689
- "For more details, please refer to and cite our paper: "
690
- "<a href='https://doi.org/xxx' target='_blank'>https://doi.org/xxx</a>"
691
- "</p>",
692
- unsafe_allow_html=True
693
- )
694
-
695
- st.subheader("Biomaterials (enter range for each)")
696
- if st.button("➕ Add Biomaterial"):
697
- used = {r['mat'] for r in st.session_state.bio_rows}
698
- available = [m for m in BIOMATERIAL_OPTIONS if m not in used]
699
- if available:
700
- st.session_state.bio_rows.append({
701
- 'mat': available[0], 'min': 0.0, 'max': 10.0, 'step': 0.1
702
- })
703
- st.rerun()
704
-
705
- for i, row in enumerate(st.session_state.bio_rows):
706
- used_except_current = {
707
- r['mat'] for idx, r in enumerate(st.session_state.bio_rows) if idx != i
708
- }
709
- options = [m for m in BIOMATERIAL_OPTIONS if m not in used_except_current]
710
-
711
- c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
712
- mat = c1.selectbox(
713
- "Biomaterial", options,
714
- index=options.index(row['mat']) if row['mat'] in options else 0,
715
- key=f"bio_mat_{i}",
716
- label_visibility="collapsed",
717
- )
718
- st.session_state.bio_rows[i]['mat'] = mat
719
-
720
- mn = c2.number_input(
721
- "Min", min_value=0.0, max_value=row['max'],
722
- value=row['min'], step=row['step'], key=f"bio_min_{i}"
723
- )
724
- mx = c3.number_input(
725
- "Max", min_value=row['step'], max_value=100.0,
726
- value=max(row['max'], row['step']), step=row['step'],
727
- key=f"bio_max_{i}"
728
- )
729
- st.session_state.bio_rows[i].update(min=mn, max=mx)
730
-
731
- st.session_state.bio_rows[i]['step'] = c4.number_input(
732
- "Step", min_value=0.0,
733
- max_value=(mx - mn) if mx > mn else 0.1,
734
- value=row['step'], step=0.1, key=f"bio_step_{i}"
735
- )
736
-
737
- if c5.button("❌", key=f"rem_{i}"):
738
- st.session_state.bio_rows.pop(i)
739
- st.rerun()
740
-
741
- st.markdown("---")
742
-
743
- st.subheader("Cell Line & Density (10^6 cells/ml)")
744
- col1, col2, col3, col4 = st.columns([2,1,1,1])
745
-
746
- cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS, key="cell_line_select")
747
-
748
- if cell_line == "NoCellCultured":
749
- st.info("🧪 **Acellular 3D printing mode** – No cells will be included. Cell density is forced to 0.")
750
- st.session_state.density_range.update({'min': 0.0, 'max': 0.0, 'step': 0.0})
751
-
752
- col2.number_input("Min Density", value=0.0, disabled=True, key="cd_min")
753
- col3.number_input("Max Density", value=0.0, disabled=True, key="cd_max")
754
- col4.number_input("Step", value=0.0, disabled=True, key="cd_step")
755
- else:
756
- if st.session_state.density_range.get('max', 0) <= 0.1:
757
- st.session_state.density_range.update({'min': 1.0, 'max': 20.0, 'step': 0.5})
758
-
759
- dr = st.session_state.density_range
760
- dmin = col2.number_input(
761
- "Min Density",
762
- min_value=0.0,
763
- max_value=dr['max'],
764
- value=dr['min'],
765
- step=dr['step'],
766
- key="cd_min"
767
- )
768
- dmax = col3.number_input(
769
- "Max Density",
770
- min_value=dr['step'],
771
- max_value=1000.0,
772
- value=max(dr['max'], dr['step']),
773
- step=dr['step'],
774
- key="cd_max"
775
- )
776
- dstep = col4.number_input(
777
- "Step",
778
- min_value=0.0,
779
- max_value=(dmax - dmin) if dmax > dmin else 0.1,
780
- value=dr['step'],
781
- step=0.1,
782
- key="cd_step"
783
- )
784
- st.session_state.density_range.update({'min': dmin, 'max': dmax, 'step': dstep})
785
-
786
- st.markdown("---")
787
-
788
- st.subheader("Crosslinking Settings")
789
-
790
- col_cross1, col_cross2 = st.columns(2)
791
-
792
- disable_physical = col_cross1.checkbox(
793
- "Disable Physical Crosslinking",
794
- value=False,
795
- help="Check if you do not want physical/ionic crosslinking (e.g. CaCl₂ bath, temperature-induced)"
796
- )
797
-
798
- disable_photo = col_cross2.checkbox(
799
- "Disable Photo Crosslinking",
800
- value=False,
801
- help="Check if you do not want UV/visible light crosslinking"
802
- )
803
-
804
- st.subheader("Printing Parameters (enter range)")
805
-
806
- for name in PRINT_PARAM_NAMES:
807
- if name == "Physical Crosslinking Duration (s)" and disable_physical:
808
- st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
809
- c1, c2, c3, c4 = st.columns([2,1,1,1])
810
- c1.write(name + " (DISABLED)")
811
- c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
812
- c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
813
- c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
814
- continue
815
-
816
- elif name == "Photo Crosslinking Duration (s)" and disable_photo:
817
- st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
818
- c1, c2, c3, c4 = st.columns([2,1,1,1])
819
- c1.write(name + " (DISABLED)")
820
- c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
821
- c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
822
- c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
823
- continue
824
-
825
- pmin = st.session_state.pp_ranges[name]['min']
826
- pmax = st.session_state.pp_ranges[name]['max']
827
- pstep = st.session_state.pp_ranges[name]['step']
828
-
829
- c1, c2, c3, c4 = st.columns([2,1,1,1])
830
- c1.write(name)
831
-
832
- pmin = c2.number_input(
833
- "Min", min_value=0.0, max_value=pmax,
834
- value=pmin, step=pstep, key=f"pp_min_{name}"
835
- )
836
- pmax = c3.number_input(
837
- "Max", min_value=pstep, max_value=10000.0,
838
- value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
839
- )
840
- pstep = c4.number_input(
841
- "Step", min_value=0.0,
842
- max_value=(pmax - pmin) if pmax > pmin else 1.0,
843
- value=pstep, step=max(1e-3, pstep/10),
844
- key=f"pp_step_{name}"
845
- )
846
- st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
847
- st.markdown("---")
848
-
849
- if st.button("Optimize WSSQ"):
850
- with st.spinner("Running Optuna…"):
851
- try:
852
- model_print = load_prediction_model(
853
- print_model_options[selected_print_model_name],
854
- "printability",
855
- preprocessed_input_dim,
856
- len(label_encoder_print.classes_),
857
- )
858
- model_cell = load_prediction_model(
859
- cell_model_options[selected_cell_model_name],
860
- "cell_response",
861
- preprocessed_input_dim,
862
- len(label_encoder_cell.classes_),
863
- )
864
- except Exception as exc:
865
- st.error(
866
- "Could not load the selected prediction model. This model will only work when the "
867
- "running environment matches the saved artifact dependencies. For HistGradientBoosting, "
868
- "use numpy>=2.0 in the Python environment that runs Streamlit.\n\n"
869
- f"Details:\n{exc}"
870
- )
871
- st.stop()
872
-
873
- def objective(trial):
874
- bi_vals = {
875
- r['mat']: trial.suggest_float(
876
- f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
877
- )
878
- for r in st.session_state.bio_rows
879
- }
880
- for m in BIOMATERIAL_OPTIONS:
881
- bi_vals.setdefault(m, 0.0)
882
-
883
- cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
884
- "cell_density", dr['min'], dr['max'], step=dr['step']
885
- )
886
-
887
- pp_vals = {
888
- name: trial.suggest_float(
889
- f"pp__{name}",
890
- st.session_state.pp_ranges[name]['min'],
891
- st.session_state.pp_ranges[name]['max'],
892
- step=st.session_state.pp_ranges[name]['step']
893
- )
894
- for name in PRINT_PARAM_NAMES
895
- }
896
-
897
- feat = {**bi_vals, **pp_vals}
898
- feat["Cell Density (cells/mL)"] = cd
899
- feat["Cell Line"] = cell_line
900
-
901
- X = pd.DataFrame([feat]).reindex(columns=feature_cols, fill_value=0.0)
902
-
903
- exp_p = expected_class_value(model_print, X, preprocessor, label_encoder_print)
904
- exp_c = expected_class_value(model_cell, X, preprocessor, label_encoder_cell)
905
-
906
- np.random.seed(42)
907
- # Use dynamic weights from the sidebar sliders
908
- return scaffold_quality_combined(
909
- exp_p,
910
- exp_c,
911
- weight_printability=w_print,
912
- weight_cell_response=w_cell
913
- )
914
-
915
- sampler = optuna.samplers.TPESampler(
916
- seed=42,
917
- n_startup_trials=30,
918
- multivariate=True,
919
- group=True,
920
- consider_prior=True
921
- )
922
-
923
- study = optuna.create_study(
924
- direction="maximize",
925
- sampler=sampler,
926
- pruner=optuna.pruners.MedianPruner()
927
- )
928
- study.optimize(objective, n_trials=int(n_trials))
929
-
930
- # Store results in session state to persist after rerun
931
- st.session_state.best_params = study.best_trial.params
932
- st.session_state.best_value = study.best_trial.value
933
- st.session_state.optimized_cell_line = cell_line
934
-
935
- if 'best_params' in st.session_state:
936
- st.success(f"Best WSSQ: **{st.session_state.best_value:.3f}**")
937
- best_df = pd.Series(st.session_state.best_params, name="value") \
938
- .rename_axis("parameter") \
939
- .to_frame()
940
- st.table(best_df)
941
-
942
- st.markdown("---")
943
- st.subheader("Customize Fabrication Protocol")
944
- user_inquiry = st.text_area(
945
- "Add specific limitations, equipment, or extra requirements:",
946
- placeholder="e.g., I only have a 25G nozzle available, or I need to use a specific UV intensity of 10mW/cm²...",
947
- key="user_inquiry"
948
- )
949
-
950
- if st.button("Generate Fabrication Procedure"):
951
- if not gemini_key:
952
- st.error("Please enter your Gemini API key in the sidebar before generating a fabrication procedure.")
953
- st.stop()
954
-
955
- with st.spinner("Generating rigorous fabrication procedure…"):
956
- client = genai.Client(api_key=gemini_key)
957
-
958
- formatted_params = "\n".join([
959
- f"- {k.replace('bio__', 'Biomaterial: ').replace('pp__', 'Print Setting: ')}: {v:.2f}"
960
- for k, v in st.session_state.best_params.items()
961
- ])
962
-
963
- # Base prompt (remains unchanged)
964
- base_prompt = (
965
- f"Please act as a senior tissue engineer with 15+ years of hands-on experience in 3D bioprinting for regenerative medicine. "
966
- f"Write a **highly practical, bench-ready laboratory fabrication protocol** for fabricating a scaffold. "
967
- f"Assume the reader is an experienced experimentalist who routinely works in a tissue engineering lab.\n\n"
968
- f"**Use exactly these inputs to tailor every step:**\n"
969
- f"Target Cell Line: {st.session_state.optimized_cell_line}\n"
970
- f"Parameters:\n{formatted_params}\n\n"
971
- f"**Critical requirements for the protocol (you MUST follow all of them):**\n"
972
- f"• If Target Cell Line is 'NoCellCultured', this is **acellular 3D printing** (not bioprinting). Remove all references to cells, cell viability, cell density, and cell culturing. The final scaffold is cell-free. Change section 6 title to 'Post-Printing Incubation & Storage Instructions' and adapt its content accordingly.\n"
973
- f"• If any suggested parameter is physically unrealistic (e.g. nozzle diameter 9 µm or syringe temp 2°C) or the nozzle diameter is very small relative to the cell diameter of the target cell line (when cells are used), adjust it slightly in the protocol and explicitly note the adjustment with justification.\n"
974
- f"• Every quantity must be given in precise, measurable lab units (e.g., 2.5 mL, 1.2 % w/v, 10 mg/mL, 37 °C, 5 min, 150 rpm).\n"
975
- f"• Include exact timings, temperatures, and workflow order to protect structural fidelity (and cell viability >85 % post-print when cells are used).\n"
976
- f"• Anticipate and explicitly address common bioprinting pitfalls relevant to the given parameters (nozzle clogging, shear-induced cell death, premature gelation, filament fusion, air bubbles, etc.) and give precise mitigation steps.\n"
977
- f"• Use only reagents and equipment that are standard in tissue engineering labs; if a specific brand/model is implied by the parameters, note a common equivalent.\n"
978
- f"• Include simple quality-control checkpoints (visual inspection, live/dead staining timing when cells are used, etc.).\n\n"
979
- f"Your response must be structured **exactly** with the following sections (no extra sections, no introductory text, no summary, no conclusions):\n"
980
- f"1. Required Materials & Equipment\n"
981
- f"2. Sterilization & Safety Precautions\n"
982
- f"3. Bioink Preparation\n"
983
- f"4. 3D Bioprinting Settings & Execution\n"
984
- f"5. Post-processing & Crosslinking\n"
985
- f"6. Cell Culturing & Incubation Instructions\n"
986
- )
987
-
988
- # Append user inquiry if provided
989
- final_prompt = base_prompt
990
- if user_inquiry:
991
- final_prompt += f"\n**Additional User Constraints & Inquiries (Integrate these into the protocol):**\n{user_inquiry}"
992
-
993
- resp = client.models.generate_content(
994
- model=gemini_model,
995
- contents=final_prompt,
996
- config=types.GenerateContentConfig(
997
- system_instruction=(
998
- "You are a senior tissue engineer and expert experimentalist specializing in translating optimized bioprinting parameters into reproducible, high-viability laboratory protocols. "
999
- "Your protocols are used daily by PhD students and post-docs in regenerative medicine labs. "
1000
- "You always prioritize: (1) maximum cell viability and function, (2) structural fidelity of the printed construct, (3) workflow efficiency under sterile conditions, and (4) safety. "
1001
- "Write in clear, imperative, step-by-step language with numbered or bulleted sub-steps. "
1002
- "Never be vague — give exact volumes, times, temperatures, speeds, and concentrations. "
1003
- "Never add disclaimers or theoretical background unless explicitly asked."
1004
- ),
1005
- temperature=0.1,
1006
- top_p=0.85,
1007
- max_output_tokens=6144
1008
- )
1009
- )
1010
-
1011
- st.markdown("## Fabrication Procedure")
1012
- st.markdown(resp.text)
 
1
+ """
2
+ MLATE V3 — scaffold optimisation and protocol generation
3
+ ========================================================
4
+
5
+ streamlit run 06_webapp/app.py
6
+
7
+ A thin interface over `mlate.optimize`, `mlate.wssq` and `mlate.protocol`. The
8
+ previous release put the objective function, the WSSQ formula and the language
9
+ model call inside this file, which meant none of them could be run, tested or
10
+ reported without launching a browser. Everything scientific now lives in the
11
+ package; this file collects inputs, calls it, and displays the result.
12
+
13
+ Deployable to a Hugging Face Space: model artefacts are located relative to
14
+ this file, and the only corpus data required at run time is the trimmed
15
+ reference table that `build_app_data.py` writes beside the app.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import sys
22
+ from pathlib import Path
23
+ from types import SimpleNamespace
24
+
25
+ import joblib
26
+ import numpy as np
27
+ import pandas as pd
28
+ import streamlit as st
29
+
30
+ HERE = Path(__file__).resolve().parent
31
+ sys.path.insert(0, str(HERE.parent))
32
+ sys.path.insert(0, str(HERE))
33
+
34
+ from mlate import config as cfg # noqa: E402
35
+ from mlate import optimize as opt # noqa: E402
36
+ from mlate import protocol as proto # noqa: E402
37
+ from mlate import serving # noqa: E402
38
+ from mlate import wssq as wssq_mod # noqa: E402
39
+
40
+ from biomaterials import BIOMATERIAL_OPTIONS, BIOMATERIAL_RANGES # noqa: E402
41
+ from cell_lines import (ACELLULAR_TOKEN, CELL_DENSITY_RANGES, # noqa: E402
42
+ CELL_LINE_COUNTS, CELL_LINE_OPTIONS)
43
+ from model_performance import (PERFORMANCE_GUIDE, # noqa: E402
44
+ PERFORMANCE_GUIDE_FULL)
45
+
46
+ st.set_page_config(page_title="MLATE V3", page_icon="🧬", layout="wide")
47
+
48
+ # Deployment layout: a Space carries deploy/models next to the app, a checkout
49
+ # has it at the repository root. Both are tried so the same file runs in either.
50
+ MODEL_ROOTS = [HERE / "deploy" / "models", cfg.ROOT / "deploy" / "models"]
51
+ PRINT_PARAMS = ["Physical Crosslinking Duration (s)",
52
+ "Photo Crosslinking Duration (s)",
53
+ "Extrusion Pressure (kPa)",
54
+ "Nozzle Movement Speed (mm/s)",
55
+ "Nozzle Diameter (µm)",
56
+ "Syringe Temperature (°C)",
57
+ "Substrate Temperature (°C)"]
58
+ CROSSLINK = PRINT_PARAMS[:2]
59
+ PROTOCOL_PARAMS = PRINT_PARAMS[2:]
60
+
61
+
62
+ def models_root() -> Path | None:
63
+ for r in MODEL_ROOTS:
64
+ if (r / "classifiers").exists():
65
+ return r
66
+ return None
67
+
68
+
69
+ # ── loading ──────────────────────────────────────────────────────────────────
70
+
71
+ @st.cache_resource(show_spinner=False)
72
+ def load_preprocessor():
73
+ """The release preprocessor, fitted on every row for inference."""
74
+ root = models_root()
75
+ d = root / "preprocessors"
76
+ return (joblib.load(d / "preprocessor.pkl"),
77
+ joblib.load(d / "input_columns.pkl"))
78
+
79
+
80
+ @st.cache_resource(show_spinner=False)
81
+ def load_manifest() -> dict:
82
+ root = models_root()
83
+ p = root / "deployment_manifest.json"
84
+ return json.loads(p.read_text(encoding="utf-8")) if p.exists() else {}
85
+
86
+
87
+ @st.cache_data(show_spinner=False)
88
+ def available_models(task: str) -> list[dict]:
89
+ """
90
+ Every model offered for one target, ranked by benchmarked weighted F1.
91
+
92
+ All three families, not only the pickled classifiers: the previous release
93
+ listed the conventional models alone, which meant TabICL - the strongest
94
+ printability model on the benchmark - was exported, reported and then not
95
+ offered. Discovery costs a filename parse; an estimator is opened when it
96
+ is chosen.
97
+
98
+ Random-split artefacts only. The application serves prediction inside the
99
+ design space the corpus covers - adjusting a concentration, swapping a cell
100
+ line, moving a pressure within observed ranges - which is the interpolation
101
+ regime the random protocol estimates. The study-grouped models are the
102
+ conservative bound for an unseen laboratory and are reported in the
103
+ manuscript, but they are tuned for a harder task than the one performed
104
+ here. The foundation models are split-independent and appear under both.
105
+ """
106
+ root = models_root()
107
+ if root is None:
108
+ return []
109
+ return [{"name": st_.name, "family": st_.family, "path": str(st_.path),
110
+ "weighted_f1": st_.weighted_f1, "cost": st_.cost_hint}
111
+ for st_ in serving.discover(root, task, "random")]
112
+
113
+
114
+ def model_label(entry: dict) -> str:
115
+ """Menu text: the model, what kind it is, and what it scored."""
116
+ kind = {"ml": "", "dl": " · deep", "foundation": " · foundation"}
117
+ return (f"{entry['name']}{kind.get(entry['family'], '')} · "
118
+ f"F1 {entry['weighted_f1']:.3f}")
119
+
120
+
121
+ @st.cache_resource(show_spinner=False)
122
+ def load_model(path_str: str, family: str):
123
+ """
124
+ Open one artefact. Cached, because a foundation model re-supplies its
125
+ 2,646-row context on first use and there is no reason to pay that twice.
126
+ """
127
+ return serving.load(Path(path_str), family)
128
+
129
+
130
+ @st.cache_data(show_spinner=False)
131
+ def load_corpus() -> tuple[pd.DataFrame | None, object]:
132
+ """
133
+ The observed formulations, shipped beside the app by `build_app_data.py`.
134
+
135
+ Needed for the two checks that a prediction alone cannot make: how far the
136
+ proposed formulation sits from anything published, and which real
137
+ formulations are closest to it. Returns (None, None) if the file is absent
138
+ rather than failing, so the application still optimises; the interface then
139
+ says the check could not be performed instead of implying it passed.
140
+ """
141
+ table, meta = HERE / "corpus_reference.parquet", HERE / "corpus_reference.json"
142
+ if not (table.exists() and meta.exists()):
143
+ return None, None
144
+ groups = json.loads(meta.read_text(encoding="utf-8"))
145
+ return pd.read_parquet(table), SimpleNamespace(**groups)
146
+
147
+
148
+ @st.cache_data(show_spinner=False, ttl=3600)
149
+ def list_llms() -> tuple[list[tuple], bool]:
150
+ """
151
+ The language models on offer, checked against OpenRouter's live catalogue.
152
+
153
+ Checked rather than trusted because the catalogue turns over quickly: five
154
+ of the eleven identifiers shipped with the previous revision had been
155
+ withdrawn within weeks, and a withdrawn identifier fails only when the user
156
+ presses Generate, after an optimisation has already been paid for. Cached
157
+ for an hour so the check costs one request per session.
158
+
159
+ No key is passed. The catalogue is public, and Streamlit's cache is shared
160
+ across every session of a deployment, so a secret used as a cache key would
161
+ be a secret held on behalf of all of them.
162
+ """
163
+ return proto.available_models(None)
164
+
165
+
166
+ # ── help dialogs ───────────────────────────────────────────────────────���─────
167
+
168
+ @st.dialog("Weighted Synergistic Scaffold Quality (WSSQ)", width="large")
169
+ def show_wssq_guidance():
170
+ st.markdown(
171
+ """
172
+ WSSQ combines **printability** and **cell response** into a single score the
173
+ optimiser can maximise.
174
+
175
+ Both components are combined by two *conjunctive* means — a weighted harmonic
176
+ mean and a weighted geometric mean, averaged. Conjunctive means collapse toward
177
+ zero if either component does, so neither objective can be traded away: a
178
+ scaffold that prints perfectly but supports no cells does not score well. An
179
+ ordinary average would allow exactly that.
180
+
181
+ Two boundary rules apply. A formulation that does not extrude (printability 0)
182
+ scores 0. An **acellular** formulation has no cell response to assess, so it is
183
+ scored on printability alone rather than being penalised for a biological
184
+ outcome that does not apply to it.
185
+
186
+ The **cell-response weight** below is yours to set. Cell response carries more
187
+ than biology: pore size, porosity, interconnectivity and stiffness are not known
188
+ before fabrication and cannot be model inputs, but they strongly shape how cells
189
+ behave, so cell response acts as a proxy for them.
190
+ """)
191
+
192
+
193
+ @st.dialog("Choosing a model", width="large")
194
+ def show_model_guidance():
195
+ st.markdown(
196
+ "Ranked by weighted F1 on the held-out test partition of the "
197
+ "random split (n = 530 records per target).\n\n"
198
+ "All three families are offered: the conventional classifiers, the six "
199
+ "deep networks, and the three in-context foundation models. The "
200
+ "highest-ranked model appears first in each menu.\n\n"
201
+ "**What the choice costs.** A foundation model carries the corpus "
202
+ "rather than fitted parameters and re-reads all 2,646 records on every "
203
+ "pass, so it is slower than a conventional classifier - measured at "
204
+ "about 1 s per batch of candidates on a GPU and about 17 s without "
205
+ "one, against 80 ms. Candidates are scored in batches of 32 precisely "
206
+ "so that this is paid once per batch rather than once per candidate; a "
207
+ "150-trial search is a few seconds with a conventional model, under a "
208
+ "minute with a foundation model on a GPU, and around three minutes on "
209
+ "CPU.\n\n"
210
+ "Models are refitted on the complete dataset for deployment. If one "
211
+ "fails to load because of a local package-version mismatch, choose the "
212
+ "next in the list.")
213
+ tp, tc = st.tabs(["Printability", "Cell Response"])
214
+ with tp:
215
+ st.markdown(PERFORMANCE_GUIDE["printability"])
216
+ with st.expander("All models"):
217
+ st.markdown(PERFORMANCE_GUIDE_FULL["printability"])
218
+ with tc:
219
+ st.markdown(PERFORMANCE_GUIDE["cell_response"])
220
+ with st.expander("All models"):
221
+ st.markdown(PERFORMANCE_GUIDE_FULL["cell_response"])
222
+
223
+
224
+ @st.dialog("Optimisation trials")
225
+ def show_trial_guidance():
226
+ st.markdown(
227
+ "Each trial is one candidate formulation, scored by the two models "
228
+ "and combined into WSSQ. The search is Bayesian: it models which "
229
+ "regions of the space produce good scores and samples there, so later "
230
+ "trials are better targeted than earlier ones.\n\n"
231
+ "The first 30 trials or so explore broadly and their scores mean "
232
+ "little on their own. **100 to 300 trials** is a reasonable range; "
233
+ "more trials help most when many variables are being searched at once.")
234
+
235
+
236
+ @st.dialog("Getting an OpenRouter API key", width="large")
237
+ def show_api_key_guidance():
238
+ st.markdown(
239
+ """
240
+ Protocol generation calls a language model through **OpenRouter**, which
241
+ provides access to models from many vendors — Anthropic, OpenAI, Google, Meta,
242
+ DeepSeek, Mistral and others — through a single key. Nothing else in this
243
+ application requires a key: prediction and optimisation run entirely locally.
244
+
245
+ **To obtain a key**
246
+
247
+ 1. Go to **openrouter.ai** and create an account.
248
+ 2. Open **Keys** from the account menu and choose **Create Key**.
249
+ 3. Copy the key and paste it into the sidebar field. It is held only for this
250
+ browser session and is never stored or logged.
251
+
252
+ **Free models.** Models whose name ends in `:free` cost nothing and need no
253
+ credit. They are the right place to start, but they are shared and heavily
254
+ rate-limited: a request may be refused outright when the model is busy, and the
255
+ remedy is to wait a minute or pick another. For routine use, add a small amount
256
+ of credit under **Credits**; a protocol typically costs well under one cent on
257
+ the mid-range models.
258
+
259
+ **Choosing a model.** The menu lists a curated selection with a note on each,
260
+ filtered against OpenRouter's live catalogue so that a model withdrawn since
261
+ this release does not appear. Stronger reasoning models follow the protocol's
262
+ constraints more reliably — particularly the instruction to report an
263
+ implausible parameter rather than quietly correct it, and the instruction not
264
+ to invent supplier names or catalogue numbers.
265
+ """)
266
+
267
+
268
+ # ── sidebar ──────────────────────────────────────────────────────────────────
269
+
270
+ root = models_root()
271
+ if root is None:
272
+ st.error(
273
+ "No model artefacts found. Expected `deploy/models/` beside this file "
274
+ "or at the repository root. Run `python 06_webapp/export_deployment.py` "
275
+ "to create them.")
276
+ st.stop()
277
+
278
+ st.sidebar.header("Optimisation weights")
279
+ w_cell_pct = st.sidebar.slider(
280
+ "Cell-response weight (%)", 0, 100, 70, 5,
281
+ help="Printability weight is the remainder.")
282
+ w_print_pct = 100 - w_cell_pct
283
+ st.sidebar.number_input("Printability weight (%)", value=w_print_pct,
284
+ disabled=True)
285
+ if st.sidebar.button("What is WSSQ?", use_container_width=True):
286
+ show_wssq_guidance()
287
+
288
+ st.sidebar.header("Models")
289
+ print_models = available_models("printability")
290
+ cell_models = available_models("cell_response")
291
+ if not print_models or not cell_models:
292
+ st.error("Model artefacts are present but none could be loaded.")
293
+ st.stop()
294
+
295
+ def default_index(entries: list[dict]) -> int:
296
+ """
297
+ Which model the menu opens on.
298
+
299
+ The highest-ranked model overall when a GPU is present. On a CPU host - a
300
+ free Hugging Face Space, or most laptops - the highest-ranked model that is
301
+ not a foundation model, because an in-context model re-reads its whole
302
+ context on every pass and turns a search that takes seconds into one taking
303
+ minutes. The foundation models stay in the menu with their cost stated; the
304
+ difference is only what a first-time visitor is given before choosing.
305
+ """
306
+ if serving.device() == "cuda":
307
+ return 0
308
+ return next((i for i, e in enumerate(entries)
309
+ if e["family"] != "foundation"), 0)
310
+
311
+
312
+ print_entry = st.sidebar.selectbox(
313
+ "Printability model", print_models, format_func=model_label,
314
+ index=default_index(print_models))
315
+ cell_entry = st.sidebar.selectbox(
316
+ "Cell-response model", cell_models, format_func=model_label,
317
+ index=default_index(cell_models))
318
+ if "foundation" in (print_entry["family"], cell_entry["family"]):
319
+ st.sidebar.caption(
320
+ f"Foundation model selected - {print_entry['cost']}. These carry the "
321
+ f"corpus rather than fitted parameters and re-read it on every pass, "
322
+ f"so a search is slower than with a conventional classifier and much "
323
+ f"slower without a GPU.")
324
+ if st.sidebar.button("Model performance", use_container_width=True):
325
+ show_model_guidance()
326
+
327
+ st.sidebar.header("Search")
328
+ n_trials = st.sidebar.number_input("Optimisation trials", 20, 2000, 150, 10)
329
+ if st.sidebar.button("How many trials?", use_container_width=True):
330
+ show_trial_guidance()
331
+
332
+ st.sidebar.header("Protocol generation")
333
+ # Never prefilled from the server's own key. `type="password"` masks a value
334
+ # on screen but still sends it to the browser, so prefilling would hand the
335
+ # operator's key to every visitor of a public deployment. A key configured on
336
+ # the server remains usable - `proto.api_key` falls back to it when this field
337
+ # is empty - but it is never transmitted.
338
+ api_key_input = st.sidebar.text_input(
339
+ "OpenRouter API key", type="password",
340
+ help="Needed only for protocol generation. Held for this browser session "
341
+ "only; never stored or logged.")
342
+ if not api_key_input and proto.api_key():
343
+ st.sidebar.caption("A key is configured on the server; leave this blank "
344
+ "to use it.")
345
+ llm_models, llm_verified = list_llms()
346
+ model_labels = [f"{m} · {tier}" for m, _, tier, _ in llm_models]
347
+ llm_idx = st.sidebar.selectbox(
348
+ "Language model", range(len(model_labels)),
349
+ format_func=lambda i: model_labels[i],
350
+ index=next((i for i, m in enumerate(llm_models)
351
+ if m[0] == proto.DEFAULT_MODEL), 0))
352
+ llm_model, _vendor, _tier, llm_note = llm_models[llm_idx]
353
+ st.sidebar.caption(
354
+ llm_note if llm_verified
355
+ else f"{llm_note} \n_Availability unconfirmed: the OpenRouter "
356
+ f"catalogue could not be reached._")
357
+ if st.sidebar.button("How to get a key", use_container_width=True):
358
+ show_api_key_guidance()
359
+
360
+ # ── main ─────────────────────────────────────────────────────────────────────
361
+
362
+ st.title("MLATE: Machine Learning Applications in Tissue Engineering")
363
+ st.markdown(
364
+ "Define the ranges you can work within, and the optimiser searches them for "
365
+ "the formulation with the highest predicted scaffold quality. Predictions "
366
+ "come from models trained on 2,646 scaffold records extracted from the "
367
+ "literature, and are decision support rather than validated outcomes.")
368
+
369
+ if "bio_rows" not in st.session_state:
370
+ st.session_state.bio_rows = [
371
+ {"mat": "Alginate (%w/v)", "min": 1.0, "max": 6.0, "step": 0.5}]
372
+
373
+ st.subheader("Biomaterials")
374
+ st.caption("Give a range for each component. The optimiser searches within it.")
375
+ c1, c2 = st.columns([1, 5])
376
+ if c1.button("Add biomaterial"):
377
+ remaining = [m for m in BIOMATERIAL_OPTIONS
378
+ if m not in {r["mat"] for r in st.session_state.bio_rows}]
379
+ if remaining:
380
+ st.session_state.bio_rows.append(
381
+ {"mat": remaining[0], "min": 0.0, "max": 5.0, "step": 0.5})
382
+ if c2.button("Clear all") and st.session_state.bio_rows:
383
+ st.session_state.bio_rows = []
384
+
385
+ for i, row in enumerate(list(st.session_state.bio_rows)):
386
+ a, b, c, d, e = st.columns([4, 1.4, 1.4, 1.4, 0.8])
387
+ row["mat"] = a.selectbox("Material", BIOMATERIAL_OPTIONS,
388
+ index=BIOMATERIAL_OPTIONS.index(row["mat"])
389
+ if row["mat"] in BIOMATERIAL_OPTIONS else 0,
390
+ key=f"mat{i}", label_visibility="collapsed")
391
+ obs = BIOMATERIAL_RANGES.get(row["mat"])
392
+ row["min"] = b.number_input("min", value=float(row["min"]), step=0.1,
393
+ key=f"lo{i}")
394
+ row["max"] = c.number_input("max", value=float(row["max"]), step=0.1,
395
+ key=f"hi{i}")
396
+ row["step"] = d.number_input("step", value=float(row["step"]), step=0.1,
397
+ min_value=0.01, key=f"st{i}")
398
+ if e.button("✕", key=f"rm{i}"):
399
+ st.session_state.bio_rows.pop(i)
400
+ st.rerun()
401
+ if obs:
402
+ note = (f"observed in corpus: {obs['min']:.3g} – {obs['max']:.3g} "
403
+ f"(median {obs['median']:.3g}, n = {obs['n']})")
404
+ if row["max"] > obs["max"] or (row["min"] > 0
405
+ and row["min"] < obs["min"]):
406
+ a.caption(f":orange[{note} — your range extends beyond this]")
407
+ else:
408
+ a.caption(note)
409
+
410
+ st.markdown("---")
411
+ st.subheader("Cell line and density")
412
+ labels = [f"{c} ({CELL_LINE_COUNTS.get(c, 0)} records)"
413
+ for c in CELL_LINE_OPTIONS]
414
+ ci = st.selectbox("Cell line", range(len(CELL_LINE_OPTIONS)),
415
+ format_func=lambda i: labels[i], index=0)
416
+ cell_line = CELL_LINE_OPTIONS[ci]
417
+
418
+ density_var = None
419
+ if cell_line == ACELLULAR_TOKEN:
420
+ st.info(
421
+ "Acellular mode. Cell response does not apply, so scaffold quality is "
422
+ "scored on printability alone and the cell-response weight is ignored.")
423
+ else:
424
+ obs = CELL_DENSITY_RANGES.get(cell_line)
425
+ d1, d2, d3 = st.columns(3)
426
+ dmin = d1.number_input("Density min (×10⁶ cells/mL)",
427
+ value=float(obs["min"]) if obs else 1.0, step=0.5)
428
+ dmax = d2.number_input("Density max (×10⁶ cells/mL)",
429
+ value=float(obs["max"]) if obs else 10.0, step=0.5)
430
+ dstep = d3.number_input("Density step", value=0.5, step=0.1,
431
+ min_value=0.01)
432
+ density_var = opt.Variable(cfg.CELL_COLS[1], dmin, dmax, dstep)
433
+ if obs:
434
+ st.caption(f"observed for {cell_line}: {obs['min']:.3g} – "
435
+ f"{obs['max']:.3g} (median {obs['median']:.3g})")
436
+
437
+ st.markdown("---")
438
+ st.subheader("Crosslinking")
439
+ xl_vars = []
440
+ for p in CROSSLINK:
441
+ a, b, c = st.columns(3)
442
+ lo = a.number_input(f"{p} — min", value=0.0, step=5.0, key=f"xlo{p}")
443
+ hi = b.number_input(f"{p} — max", value=300.0, step=5.0, key=f"xhi{p}")
444
+ stp = c.number_input("step", value=5.0, step=1.0, min_value=0.01,
445
+ key=f"xst{p}")
446
+ xl_vars.append(opt.Variable(p, lo, hi, stp))
447
+
448
+ st.subheader("Printing parameters")
449
+ DEFAULTS = {"Extrusion Pressure (kPa)": (20.0, 200.0, 5.0),
450
+ "Nozzle Movement Speed (mm/s)": (1.0, 20.0, 0.5),
451
+ "Nozzle Diameter (µm)": (100.0, 600.0, 10.0),
452
+ "Syringe Temperature (°C)": (18.0, 40.0, 0.5),
453
+ "Substrate Temperature (°C)": (4.0, 40.0, 0.5)}
454
+ pp_vars = []
455
+ for p in PROTOCOL_PARAMS:
456
+ lo0, hi0, s0 = DEFAULTS[p]
457
+ a, b, c = st.columns(3)
458
+ lo = a.number_input(f"{p} — min", value=lo0, step=s0, key=f"plo{p}")
459
+ hi = b.number_input(f"{p} — max", value=hi0, step=s0, key=f"phi{p}")
460
+ stp = c.number_input("step", value=s0, step=0.1, min_value=0.01,
461
+ key=f"pst{p}")
462
+ pp_vars.append(opt.Variable(p, lo, hi, stp))
463
+
464
+ st.markdown("---")
465
+
466
+ if st.button("Optimise scaffold quality", type="primary"):
467
+ if not st.session_state.bio_rows:
468
+ st.error("Add at least one biomaterial.")
469
+ st.stop()
470
+
471
+ pre, feature_columns = load_preprocessor()
472
+ space = opt.SearchSpace(
473
+ cell_line=cell_line,
474
+ biomaterials=[opt.Variable(r["mat"], r["min"], r["max"], r["step"])
475
+ for r in st.session_state.bio_rows],
476
+ printing=xl_vars + pp_vars,
477
+ cell_density=density_var)
478
+
479
+ objective = opt.Objective(
480
+ space, pre, feature_columns,
481
+ load_model(print_entry["path"], print_entry["family"]),
482
+ load_model(cell_entry["path"], cell_entry["family"]),
483
+ print_weight=w_print_pct / 100, cell_weight=w_cell_pct / 100)
484
+
485
+ bar = st.progress(0.0, text="Searching…")
486
+
487
+ def report(done, total, best):
488
+ bar.progress(min(done / total, 1.0),
489
+ text=f"Trial {done}/{total} · best WSSQ {best:.1f}%")
490
+
491
+ best, score, study = opt.optimise(objective, n_trials=int(n_trials),
492
+ progress=report)
493
+ bar.empty()
494
+
495
+ detail = objective.evaluate(best)
496
+ corpus, groups = load_corpus()
497
+ st.session_state.update(
498
+ best_params=best, best_value=score, best_detail=detail,
499
+ opt_cell_line=cell_line,
500
+ best_distance=(None if corpus is None
501
+ else opt.distance_report(best, corpus)))
502
+
503
+ if "best_params" in st.session_state:
504
+ best = st.session_state.best_params
505
+ detail = st.session_state.best_detail
506
+
507
+ st.success(f"Best WSSQ: **{st.session_state.best_value:.1f}%**")
508
+ m1, m2, m3 = st.columns(3)
509
+ m1.metric("Expected printability",
510
+ f"{detail['expected_printability']:.2f}", help="Scale 0–3")
511
+ if st.session_state.opt_cell_line != ACELLULAR_TOKEN:
512
+ m2.metric("Expected cell response",
513
+ f"{detail['expected_cell_response']:.2f}", help="Scale 1–5")
514
+ m3.metric("Cell line", st.session_state.opt_cell_line)
515
+
516
+ st.subheader("Optimised formulation")
517
+ tidy = pd.DataFrame(
518
+ {"Parameter": list(best), "Value": [f"{v:.4g}" for v in best.values()]})
519
+ st.dataframe(tidy, use_container_width=True, hide_index=True)
520
+
521
+ st.caption(
522
+ "Predicted values, not measurements. The expected scores are "
523
+ "probability-weighted averages over the predicted class distribution, "
524
+ "so a value between two classes reflects genuine model uncertainty.")
525
+
526
+ dist = st.session_state.get("best_distance")
527
+ if dist is None:
528
+ st.info(
529
+ "The corpus reference table is not present, so how far this "
530
+ "formulation sits from published work could not be checked. Run "
531
+ "`python 06_webapp/build_app_data.py` to generate it.")
532
+ else:
533
+ d1, d2 = st.columns(2)
534
+ d1.metric("Parameters outside the observed range",
535
+ dist["n_out_of_range"],
536
+ help="Values no published study in the corpus reports. The "
537
+ "models are extrapolating for these.")
538
+ d2.metric("Distance to nearest published formulation",
539
+ f"{dist['nearest_neighbour_distance']:.3f}",
540
+ help="Scaled Euclidean distance. A formulation can sit "
541
+ "inside the observed range of every single variable and "
542
+ "still be a combination no one has attempted; this "
543
+ "number is what catches that.")
544
+ if dist["n_out_of_range"]:
545
+ with st.expander("Which parameters, and by how much"):
546
+ st.dataframe(pd.DataFrame([
547
+ {"Parameter": k, "Proposed": f"{v['value']:.4g}",
548
+ "Observed range": f"{v['observed_min']:.4g} - "
549
+ f"{v['observed_max']:.4g}"}
550
+ for k, v in dist["out_of_range"].items()]),
551
+ use_container_width=True, hide_index=True)
552
+
553
+ st.markdown("---")
554
+ st.subheader("Fabrication protocol")
555
+ user_inquiry = st.text_area(
556
+ "Constraints or equipment you must work with",
557
+ placeholder="e.g. only a 25 G nozzle is available; UV source is "
558
+ "fixed at 10 mW/cm²",
559
+ key="user_inquiry")
560
+
561
+ if st.button("Generate protocol"):
562
+ key = proto.api_key(api_key_input)
563
+ if not key:
564
+ st.error("Enter an OpenRouter API key in the sidebar first.")
565
+ st.stop()
566
+
567
+ bio = {k: v for k, v in best.items() if k in BIOMATERIAL_OPTIONS}
568
+ printing = {k: v for k, v in best.items() if k in PRINT_PARAMS}
569
+ # The neighbours and the extrapolation report are what ground the
570
+ # prompt. Passing neither leaves the template asserting that no similar
571
+ # formulation exists and that the candidate is in range, and neither
572
+ # would have been checked.
573
+ corpus, groups = load_corpus()
574
+ neighbours = (None if corpus is None else
575
+ proto.nearest_formulations(best, corpus, groups, n=3))
576
+ form = proto.Formulation(
577
+ cell_line=st.session_state.opt_cell_line,
578
+ biomaterials=bio, printing=printing,
579
+ cell_density=best.get(cfg.CELL_COLS[1]),
580
+ expected_printability=detail["expected_printability"],
581
+ expected_cell_response=detail["expected_cell_response"],
582
+ printability_proba=detail.get("printability_proba"),
583
+ cell_response_proba=detail.get("cell_response_proba"),
584
+ wssq=st.session_state.best_value,
585
+ neighbours=neighbours,
586
+ extrapolation=st.session_state.get("best_distance"))
587
+
588
+ # Streamed rather than awaited. A free model takes between thirty
589
+ # seconds and two and a half minutes to write a protocol, and a
590
+ # spinner held for that long is indistinguishable from a hang.
591
+ st.markdown("## Fabrication procedure")
592
+ status = st.empty()
593
+ stream_area = st.empty()
594
+ status.info(f"Generating with {llm_model}…")
595
+ pieces: list[str] = []
596
+
597
+ def on_chunk(piece: str) -> None:
598
+ pieces.append(piece)
599
+ stream_area.markdown("".join(pieces))
600
+
601
+ def on_retry(attempt: int, reason: str | None) -> None:
602
+ status.warning(
603
+ f"Attempt {attempt} of {proto.DEFAULT_RETRIES}: {reason}")
604
+
605
+ try:
606
+ out = proto.generate(
607
+ form, key=key, model=llm_model,
608
+ user_constraints=user_inquiry,
609
+ n_records=(len(corpus) if corpus is not None else 2646),
610
+ on_chunk=on_chunk, on_retry=on_retry)
611
+ except Exception as exc:
612
+ status.empty()
613
+ stream_area.empty()
614
+ st.error(f"Generation failed: {exc}")
615
+ st.stop()
616
+
617
+ status.empty()
618
+ stream_area.markdown(out["protocol"])
619
+ if out.get("truncated"):
620
+ st.warning(
621
+ "The model reached its output limit and the protocol is "
622
+ "incomplete. Generate again, or choose another model.")
623
+ with st.expander("Generation record"):
624
+ st.json({k: v for k, v in out.items() if k != "protocol"})
625
+ st.download_button("Download protocol (Markdown)",
626
+ out["protocol"],
627
+ file_name="mlate_protocol.md")
628
+
629
+ st.markdown("---")
630
+ st.caption(
631
+ "MLATE V3 — decision support for 3D-printed and bioprinted scaffolds. "
632
+ "Predictions narrow the experimental search space; they do not replace "
633
+ "experimental validation.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
biomaterials.py CHANGED
@@ -1,124 +1,926 @@
 
 
 
 
 
 
 
1
  BIOMATERIAL_OPTIONS = [
2
- "Alginate (%w/v)",
3
- "PVA-HA (%w/v)",
4
- "CaSO4 (%w/v)",
5
- "Na2HPO4 (%w/v)",
6
- "Gelatin (%w/v)",
7
- "GelMA (%w/v)",
8
- "laponite (%w/v)",
9
- "graphene oxide (%w/v)",
10
- "hydroxyapatite (%w/v)",
11
- "Hyaluronic_Acid (%w/v)",
12
- "hyaluronan metacrylate (%w/v)",
13
- "NorHA (%w/v)",
14
- "Fibroin/Fibrinogen (%w/v)",
15
- "Pluronic P-123 (%w/v)",
16
- "Collagen (%w/v)",
17
- "Chitosan (%w/v)",
18
- "CS-AEMA (%w/v)",
19
- "RGD (mM)",
20
- "TCP (%w/v)",
21
- "Gellan (%w/v)",
22
- "bioactive glass (%w/v)",
23
- "Nano/Methycellulose (%w/v)",
24
- "PEGTA (%w/v)",
25
- "PEGMA (%w/v)",
26
- "PEGDA (%w/v)",
27
- "Agarose (%w/v)",
28
- " hyaluronic acid+ Ph moieties (%w/v)",
29
- "matrigel (%w/v)",
30
- "CaCl2(mM)",
31
- "NaCl(mM)",
32
- "BaCl2(mM)",
33
- "SrCl2(mM)",
34
- "CaCO3 (mM)",
35
- "Genipin (%w/v)",
36
- "PVA (%wt)",
37
- "trans-glutaminase (%w/v)",
38
- "alginate lyase (U/ml)",
39
- "D-glucose (%w/v)",
40
- "PLGA (%w/v)",
41
- "vascular tissued-derived dECM (%w/v)",
42
- "PEG-8-SH (mM)",
43
- "Alginate dialdehyde (%w/v)",
44
- "Alginate sulfate (%w/v)",
45
- "RGD-modified alginate (%w/v)",
46
- "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
47
- "chondroitin sulfate methacrylate (%w/v)",
48
- "PCL (%w/v)",
49
- "alginate methacrylate (%w/v)",
50
- "HRP (U/ml)",
51
- "Pluronic F127 (%w/v)/Lutrol F127 (%w/v)",
52
- "Irgacure 2959 (%w/v)",
53
- "Eosin Y (%w/v)",
54
- "Ruthenium (mM)",
55
- "sodium persulfate (SPS) (mM)",
56
- "HEPES (mM)",
57
- "LAP (%w/v)",
58
- "glutaraldehyde (%w/v)",
59
- "PBS (M)",
60
- "glycerol (%w/v)",
61
- "cECM (%w/v)",
62
- "gel-fu(%w/v)",
63
- "Rose Bengal (%w/v)",
64
- "Vitamin B2(%w/v)",
65
- "VEGF(%w/v)",
66
- "Polypyrrole:PSS(%w/v)",
67
- "boratebioactiveglass(%w/v)",
68
- "astaxanthin(%w/v)",
69
- "PRP (%v/v)",
70
- "methacrylated collagen (%w/v)",
71
- "α-Toc (µM)",
72
- "ascorbic acid (mM)",
73
- "Liver dECM(%w/v)",
74
- "galactosylated alginate (%w/v)",
75
- "SC-PEG(%w/v)",
76
- "SFMA-L(%w/v)",
77
- "SFMA-M(%w/v)",
78
- "SFMA-H(%w/v)",
79
- "KdECMMA(%w/v)",
80
- "BA silk fibronin (%w/v)",
81
- "Carrageenan(%v)",
82
- "Carbopol ETD 2020 NF (%w/v)",
83
- "Carbopol Ultrez 10 NF(%w/v)",
84
- "Carbopol NF-980(%w/v)",
85
- "FBS (%v/v)",
86
- "MeTro (%w/v)",
87
- "Triethanolamine (%v/v)",
88
- "PEG-Fibrinogen (%w/v)",
89
- "polyethylene glycol dimethacrylate (%w/v)",
90
- "aprotinin (µg/ml)",
91
- "gold nanorod (mg/mL)",
92
- "egg white (w/v)",
93
- "1-Vinyl-2-Pyrrolidione (v/v)",
94
- "carboxyl functionalized carbon nanotubes (%w/v)",
95
- "polyHIPE (%w/v)",
96
- "β-D galactose (mM)",
97
- "hydrogen peroxide (H2O2) (%v/v)",
98
- "lactic acid v/v",
99
- "NorCol (%w/v)",
100
- "DDT (%w/v)",
101
- "ammonium persulfate (mM)",
102
- "diTyr-RGD (mM)",
103
- "PHEG-Tyr (%w/v)",
104
- "MMP2-degradable peptide (%w/v)",
105
- "KdECM (%w/v)",
106
- "EDC (mg)",
107
- "NHS (mg)",
108
- "VA086 (%w/v)",
109
- "PGS (%w/v)",
110
- "thiolated HA (%w/v)",
111
- "boron nitride nanotubes (%w/v)",
112
- "PEDOT:PSS (ul)",
113
- "KCl (mM)",
114
- "skeletal muscle ECM methacrylate (%w/v)",
115
- "PEO (%w/v)",
116
- "Carbon dots (mg/ml)",
117
- "Laminin (ug/ml)",
118
- "DF-PEG (%w/v)",
119
- "omenta ECM (%w/v)",
120
- "thrombin (unit/ml)",
121
- "Carbon nanotube (CNT) (w/v)",
122
- "Phytagel(%v)",
123
- "Laponite-XLG (%w/w)"
124
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Biomaterial vocabulary and observed concentration ranges.
3
+
4
+ GENERATED by 06_webapp/build_app_data.py - do not edit by hand.
5
+ Regenerate after any change to the dataset or the benchmark tables.
6
+ """
7
+
8
  BIOMATERIAL_OPTIONS = [
9
+ 'Alginate (%w/v)',
10
+ 'PVA-HA (%w/v)',
11
+ 'CaSO4 (%w/v)',
12
+ 'Na2HPO4 (%w/v)',
13
+ 'Gelatin (%w/v)',
14
+ 'GelMA (%w/v)',
15
+ 'laponite (%w/v)',
16
+ 'graphene oxide (%w/v)',
17
+ 'hydroxyapatite (%w/v)',
18
+ 'Hyaluronic_Acid (%w/v)',
19
+ 'hyaluronan methacrylate (%w/v)',
20
+ 'NorHA (%w/v)',
21
+ 'Fibroin/Fibrinogen (%w/v)',
22
+ 'Pluronic P-123 (%w/v)',
23
+ 'Collagen (%w/v)',
24
+ 'Chitosan (%w/v)',
25
+ 'CS-AEMA (%w/v)',
26
+ 'RGD (mM)',
27
+ 'TCP (%w/v)',
28
+ 'Gellan (%w/v)',
29
+ 'bioactive glass (%w/v)',
30
+ 'Nano/Methylcellulose (%w/v)',
31
+ 'PEGTA (%w/v)',
32
+ 'PEGMA (%w/v)',
33
+ 'PEGDA (%w/v)',
34
+ 'Agarose (%w/v)',
35
+ 'hyaluronic acid+ Ph moieties (%w/v)',
36
+ 'matrigel (%w/v)',
37
+ 'CaCl2 (mM)',
38
+ 'NaCl (mM)',
39
+ 'BaCl2 (mM)',
40
+ 'SrCl2 (mM)',
41
+ 'CaCO3 (mM)',
42
+ 'Genipin (%w/v)',
43
+ 'PVA (%wt)',
44
+ 'trans-glutaminase (%w/v)',
45
+ 'alginate lyase (U/ml)',
46
+ 'D-glucose (%w/v)',
47
+ 'PLGA (%w/v)',
48
+ 'vascular tissue-derived dECM (%w/v)',
49
+ 'PEG-8-SH (mM)',
50
+ 'Alginate dialdehyde (%w/v)',
51
+ 'Alginate sulfate (%w/v)',
52
+ 'RGD-modified alginate (%w/v)',
53
+ 'poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)',
54
+ 'chondroitin sulfate methacrylate (%w/v)',
55
+ 'PCL (%w/v)',
56
+ 'alginate methacrylate (%w/v)',
57
+ 'HRP (U/ml)',
58
+ 'Pluronic F127 / Lutrol F127 (%w/v)',
59
+ 'Irgacure 2959 (%w/v)',
60
+ 'Eosin Y (%w/v)',
61
+ 'Ruthenium (mM)',
62
+ 'sodium persulfate (SPS) (mM)',
63
+ 'HEPES (mM)',
64
+ 'LAP (%w/v)',
65
+ 'glutaraldehyde (%w/v)',
66
+ 'PBS (M)',
67
+ 'glycerol (%w/v)',
68
+ 'cECM (%w/v)',
69
+ 'gel-fu (%w/v)',
70
+ 'Rose Bengal (%w/v)',
71
+ 'Vitamin B2 (%w/v)',
72
+ 'VEGF (%w/v)',
73
+ 'Polypyrrole:PSS (%w/v)',
74
+ 'borate bioactive glass (%w/v)',
75
+ 'astaxanthin (%w/v)',
76
+ 'PRP (%v/v)',
77
+ 'methacrylated collagen (%w/v)',
78
+ 'α-Toc (µM)',
79
+ 'ascorbic acid (mM)',
80
+ 'Liver dECM (%w/v)',
81
+ 'galactosylated alginate (%w/v)',
82
+ 'SC-PEG (%w/v)',
83
+ 'SFMA-L (%w/v)',
84
+ 'SFMA-M (%w/v)',
85
+ 'SFMA-H (%w/v)',
86
+ 'KdECMMA (%w/v)',
87
+ 'BA silk fibroin (%w/v)',
88
+ 'Carrageenan (%v)',
89
+ 'Carbopol ETD 2020 NF (%w/v)',
90
+ 'Carbopol Ultrez 10 NF (%w/v)',
91
+ 'Carbopol NF-980 (%w/v)',
92
+ 'FBS (%v/v)',
93
+ 'MeTro (%w/v)',
94
+ 'Triethanolamine (%v/v)',
95
+ 'PEG-Fibrinogen (%w/v)',
96
+ 'polyethylene glycol dimethacrylate (%w/v)',
97
+ 'aprotinin (µg/ml)',
98
+ 'gold nanorod (mg/mL)',
99
+ 'egg white (w/v)',
100
+ '1-Vinyl-2-pyrrolidone (v/v)',
101
+ 'carboxyl functionalized carbon nanotubes (%w/v)',
102
+ 'polyHIPE (%w/v)',
103
+ 'β-D galactose (mM)',
104
+ 'hydrogen peroxide (H2O2) (%v/v)',
105
+ 'lactic acid (v/v)',
106
+ 'NorCol (%w/v)',
107
+ 'DTT (%w/v)',
108
+ 'ammonium persulfate (mM)',
109
+ 'diTyr-RGD (mM)',
110
+ 'PHEG-Tyr (%w/v)',
111
+ 'MMP2-degradable peptide (%w/v)',
112
+ 'KdECM (%w/v)',
113
+ 'EDC (mg)',
114
+ 'NHS (mg)',
115
+ 'VA086 (%w/v)',
116
+ 'PGS (%w/v)',
117
+ 'thiolated HA (%w/v)',
118
+ 'boron nitride nanotubes (%w/v)',
119
+ 'PEDOT:PSS (ul)',
120
+ 'KCl (mM)',
121
+ 'skeletal muscle ECM methacrylate (%w/v)',
122
+ 'PEO (%w/v)',
123
+ 'Carbon dots (mg/ml)',
124
+ 'Laminin (ug/ml)',
125
+ 'DF-PEG (%w/v)',
126
+ 'omentum ECM (%w/v)',
127
+ 'thrombin (unit/ml)',
128
+ 'Carbon nanotube (CNT) (w/v)',
129
+ 'Phytagel (%v)',
130
+ 'Laponite-XLG (%w/w)',
131
+ 'sodium carboxymethyl cellulose (mg)',
132
+ 'lysozyme amyloid nanofibrils:gold nanoparticles (mg)',
133
+ 'Lysozyme amyloid nanofibrils (mg)',
134
+ 'rGO (mg/ml)',
135
+ 'Methacrylated gellan gum (%w/v)',
136
+ 'Acetylsalicylic Acid (%w/w)',
137
+ 'PVA methacrylate (%w/v)',
138
+ 'MXene (mg/ml)',
139
+ ]
140
+
141
+ # Observed range among formulations that CONTAIN each material.
142
+ # The UI seeds its range widgets from these so a user starts
143
+ # inside the region the models were trained on; values outside
144
+ # are permitted but flagged as extrapolation.
145
+ BIOMATERIAL_RANGES = {
146
+ "Alginate (%w/v)": {
147
+ "min": 0.25,
148
+ "max": 20.0,
149
+ "median": 4.0,
150
+ "n": 1138
151
+ },
152
+ "PVA-HA (%w/v)": {
153
+ "min": 0.3,
154
+ "max": 2.5,
155
+ "median": 2.0,
156
+ "n": 15
157
+ },
158
+ "CaSO4 (%w/v)": {
159
+ "min": 0.03,
160
+ "max": 25.71,
161
+ "median": 1.0,
162
+ "n": 32
163
+ },
164
+ "Na2HPO4 (%w/v)": {
165
+ "min": 0.12,
166
+ "max": 0.15,
167
+ "median": 0.15,
168
+ "n": 9
169
+ },
170
+ "Gelatin (%w/v)": {
171
+ "min": 0.5,
172
+ "max": 80.0,
173
+ "median": 5.0,
174
+ "n": 797
175
+ },
176
+ "GelMA (%w/v)": {
177
+ "min": 1.0,
178
+ "max": 30.0,
179
+ "median": 7.0,
180
+ "n": 880
181
+ },
182
+ "laponite (%w/v)": {
183
+ "min": 0.05,
184
+ "max": 2.3,
185
+ "median": 2.3,
186
+ "n": 96
187
+ },
188
+ "graphene oxide (%w/v)": {
189
+ "min": 0.001,
190
+ "max": 1.5,
191
+ "median": 0.5,
192
+ "n": 43
193
+ },
194
+ "hydroxyapatite (%w/v)": {
195
+ "min": 2.0,
196
+ "max": 70.0,
197
+ "median": 70.0,
198
+ "n": 43
199
+ },
200
+ "Hyaluronic_Acid (%w/v)": {
201
+ "min": 0.2,
202
+ "max": 2.0,
203
+ "median": 0.3,
204
+ "n": 62
205
+ },
206
+ "hyaluronan methacrylate (%w/v)": {
207
+ "min": 0.1,
208
+ "max": 6.0,
209
+ "median": 2.0,
210
+ "n": 151
211
+ },
212
+ "NorHA (%w/v)": {
213
+ "min": 2.0,
214
+ "max": 2.0,
215
+ "median": 2.0,
216
+ "n": 4
217
+ },
218
+ "Fibroin/Fibrinogen (%w/v)": {
219
+ "min": 0.009375,
220
+ "max": 25.0,
221
+ "median": 2.0,
222
+ "n": 207
223
+ },
224
+ "Pluronic P-123 (%w/v)": {
225
+ "min": 40.0,
226
+ "max": 60.0,
227
+ "median": 50.0,
228
+ "n": 12
229
+ },
230
+ "Collagen (%w/v)": {
231
+ "min": 0.01,
232
+ "max": 7.8,
233
+ "median": 2.0,
234
+ "n": 85
235
+ },
236
+ "Chitosan (%w/v)": {
237
+ "min": 1.0,
238
+ "max": 62.0,
239
+ "median": 2.0,
240
+ "n": 74
241
+ },
242
+ "CS-AEMA (%w/v)": {
243
+ "min": 4.0,
244
+ "max": 4.0,
245
+ "median": 4.0,
246
+ "n": 8
247
+ },
248
+ "RGD (mM)": {
249
+ "min": 3.0,
250
+ "max": 3.0,
251
+ "median": 3.0,
252
+ "n": 1
253
+ },
254
+ "TCP (%w/v)": {
255
+ "min": 0.5,
256
+ "max": 3.0,
257
+ "median": 0.5,
258
+ "n": 9
259
+ },
260
+ "Gellan (%w/v)": {
261
+ "min": 0.5,
262
+ "max": 150.0,
263
+ "median": 150.0,
264
+ "n": 54
265
+ },
266
+ "bioactive glass (%w/v)": {
267
+ "min": 1.0,
268
+ "max": 50.0,
269
+ "median": 1.0,
270
+ "n": 7
271
+ },
272
+ "Nano/Methylcellulose (%w/v)": {
273
+ "min": 0.25,
274
+ "max": 80.0,
275
+ "median": 4.0,
276
+ "n": 157
277
+ },
278
+ "PEGTA (%w/v)": {
279
+ "min": 1.0,
280
+ "max": 3.0,
281
+ "median": 2.0,
282
+ "n": 33
283
+ },
284
+ "PEGMA (%w/v)": {
285
+ "min": 12.5,
286
+ "max": 100.0,
287
+ "median": 17.5,
288
+ "n": 9
289
+ },
290
+ "PEGDA (%w/v)": {
291
+ "min": 1.0,
292
+ "max": 20.0,
293
+ "median": 8.25,
294
+ "n": 119
295
+ },
296
+ "Agarose (%w/v)": {
297
+ "min": 0.5,
298
+ "max": 60.0,
299
+ "median": 24.0,
300
+ "n": 51
301
+ },
302
+ "hyaluronic acid+ Ph moieties (%w/v)": {
303
+ "min": 0.1,
304
+ "max": 1.5,
305
+ "median": 0.8,
306
+ "n": 8
307
+ },
308
+ "matrigel (%w/v)": {
309
+ "min": 5.0,
310
+ "max": 50.0,
311
+ "median": 10.0,
312
+ "n": 19
313
+ },
314
+ "CaCl2 (mM)": {
315
+ "min": 0.001,
316
+ "max": 1500.0,
317
+ "median": 100.0,
318
+ "n": 1057
319
+ },
320
+ "NaCl (mM)": {
321
+ "min": 0.72,
322
+ "max": 350.0,
323
+ "median": 145.0,
324
+ "n": 23
325
+ },
326
+ "BaCl2 (mM)": {
327
+ "min": 55.0,
328
+ "max": 60.0,
329
+ "median": 55.0,
330
+ "n": 12
331
+ },
332
+ "SrCl2 (mM)": {
333
+ "min": 20.0,
334
+ "max": 70.0,
335
+ "median": 20.0,
336
+ "n": 6
337
+ },
338
+ "CaCO3 (mM)": {
339
+ "min": 15.88,
340
+ "max": 25.71,
341
+ "median": 18.0,
342
+ "n": 9
343
+ },
344
+ "Genipin (%w/v)": {
345
+ "min": 0.025,
346
+ "max": 1.0,
347
+ "median": 0.025,
348
+ "n": 18
349
+ },
350
+ "PVA (%wt)": {
351
+ "min": 3.0,
352
+ "max": 15.0,
353
+ "median": 15.0,
354
+ "n": 50
355
+ },
356
+ "trans-glutaminase (%w/v)": {
357
+ "min": 0.04,
358
+ "max": 6.0,
359
+ "median": 1.0,
360
+ "n": 63
361
+ },
362
+ "alginate lyase (U/ml)": {
363
+ "min": 0.5,
364
+ "max": 500.0,
365
+ "median": 5.0,
366
+ "n": 7
367
+ },
368
+ "D-glucose (%w/v)": {
369
+ "min": 4.4,
370
+ "max": 4.4,
371
+ "median": 4.4,
372
+ "n": 5
373
+ },
374
+ "PLGA (%w/v)": {
375
+ "min": 100.0,
376
+ "max": 100.0,
377
+ "median": 100.0,
378
+ "n": 4
379
+ },
380
+ "vascular tissue-derived dECM (%w/v)": {
381
+ "min": 1.0,
382
+ "max": 3.0,
383
+ "median": 3.0,
384
+ "n": 7
385
+ },
386
+ "PEG-8-SH (mM)": {
387
+ "min": 2.25,
388
+ "max": 8.0,
389
+ "median": 2.25,
390
+ "n": 10
391
+ },
392
+ "Alginate dialdehyde (%w/v)": {
393
+ "min": 2.0,
394
+ "max": 7.5,
395
+ "median": 3.75,
396
+ "n": 48
397
+ },
398
+ "Alginate sulfate (%w/v)": {
399
+ "min": 1.0,
400
+ "max": 1.0,
401
+ "median": 1.0,
402
+ "n": 26
403
+ },
404
+ "RGD-modified alginate (%w/v)": {
405
+ "min": 1.0,
406
+ "max": 1.0,
407
+ "median": 1.0,
408
+ "n": 2
409
+ },
410
+ "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)": {
411
+ "min": 15.0,
412
+ "max": 15.0,
413
+ "median": 15.0,
414
+ "n": 3
415
+ },
416
+ "chondroitin sulfate methacrylate (%w/v)": {
417
+ "min": 5.0,
418
+ "max": 5.0,
419
+ "median": 5.0,
420
+ "n": 1
421
+ },
422
+ "PCL (%w/v)": {
423
+ "min": 1.0,
424
+ "max": 100.0,
425
+ "median": 8.0,
426
+ "n": 34
427
+ },
428
+ "alginate methacrylate (%w/v)": {
429
+ "min": 1.0,
430
+ "max": 3.0,
431
+ "median": 3.0,
432
+ "n": 24
433
+ },
434
+ "HRP (U/ml)": {
435
+ "min": 5.0,
436
+ "max": 100.0,
437
+ "median": 15.0,
438
+ "n": 68
439
+ },
440
+ "Pluronic F127 / Lutrol F127 (%w/v)": {
441
+ "min": 3.0,
442
+ "max": 100.0,
443
+ "median": 6.0,
444
+ "n": 83
445
+ },
446
+ "Irgacure 2959 (%w/v)": {
447
+ "min": 0.01,
448
+ "max": 2.0,
449
+ "median": 0.25,
450
+ "n": 530
451
+ },
452
+ "Eosin Y (%w/v)": {
453
+ "min": 0.5,
454
+ "max": 100.0,
455
+ "median": 0.5,
456
+ "n": 32
457
+ },
458
+ "Ruthenium (mM)": {
459
+ "min": 0.254,
460
+ "max": 1.0,
461
+ "median": 0.5,
462
+ "n": 28
463
+ },
464
+ "sodium persulfate (SPS) (mM)": {
465
+ "min": 2.52,
466
+ "max": 10.0,
467
+ "median": 5.0,
468
+ "n": 28
469
+ },
470
+ "HEPES (mM)": {
471
+ "min": 10.0,
472
+ "max": 25.0,
473
+ "median": 10.0,
474
+ "n": 22
475
+ },
476
+ "LAP (%w/v)": {
477
+ "min": 0.01,
478
+ "max": 4.46,
479
+ "median": 0.4,
480
+ "n": 310
481
+ },
482
+ "glutaraldehyde (%w/v)": {
483
+ "min": 0.125,
484
+ "max": 0.4,
485
+ "median": 0.25,
486
+ "n": 62
487
+ },
488
+ "PBS (M)": {
489
+ "min": 0.082,
490
+ "max": 0.328,
491
+ "median": 0.165,
492
+ "n": 3
493
+ },
494
+ "glycerol (%w/v)": {
495
+ "min": 10.0,
496
+ "max": 10.0,
497
+ "median": 10.0,
498
+ "n": 21
499
+ },
500
+ "cECM (%w/v)": {
501
+ "min": 0.1,
502
+ "max": 20.0,
503
+ "median": 4.6,
504
+ "n": 118
505
+ },
506
+ "gel-fu (%w/v)": {
507
+ "min": 10.0,
508
+ "max": 155.0,
509
+ "median": 100.0,
510
+ "n": 15
511
+ },
512
+ "Rose Bengal (%w/v)": {
513
+ "min": 1.0,
514
+ "max": 5.0,
515
+ "median": 5.0,
516
+ "n": 14
517
+ },
518
+ "Vitamin B2 (%w/v)": {
519
+ "min": 0.02,
520
+ "max": 0.2,
521
+ "median": 0.02,
522
+ "n": 20
523
+ },
524
+ "VEGF (%w/v)": {
525
+ "min": 0.01,
526
+ "max": 10.0,
527
+ "median": 1.0,
528
+ "n": 6
529
+ },
530
+ "Polypyrrole:PSS (%w/v)": {
531
+ "min": 0.1,
532
+ "max": 0.4,
533
+ "median": 0.2,
534
+ "n": 3
535
+ },
536
+ "borate bioactive glass (%w/v)": {
537
+ "min": 0.1,
538
+ "max": 0.1,
539
+ "median": 0.1,
540
+ "n": 1
541
+ },
542
+ "astaxanthin (%w/v)": {
543
+ "min": 0.01,
544
+ "max": 0.01,
545
+ "median": 0.01,
546
+ "n": 1
547
+ },
548
+ "PRP (%v/v)": {
549
+ "min": 20.0,
550
+ "max": 20.0,
551
+ "median": 20.0,
552
+ "n": 3
553
+ },
554
+ "methacrylated collagen (%w/v)": {
555
+ "min": 0.2,
556
+ "max": 50.0,
557
+ "median": 0.48,
558
+ "n": 25
559
+ },
560
+ "\u03b1-Toc (\u00b5M)": {
561
+ "min": 100.0,
562
+ "max": 100.0,
563
+ "median": 100.0,
564
+ "n": 5
565
+ },
566
+ "ascorbic acid (mM)": {
567
+ "min": 3.4,
568
+ "max": 3.4,
569
+ "median": 3.4,
570
+ "n": 5
571
+ },
572
+ "Liver dECM (%w/v)": {
573
+ "min": 0.5,
574
+ "max": 100.0,
575
+ "median": 2.0,
576
+ "n": 139
577
+ },
578
+ "galactosylated alginate (%w/v)": {
579
+ "min": 0.375,
580
+ "max": 1.0,
581
+ "median": 1.0,
582
+ "n": 22
583
+ },
584
+ "SC-PEG (%w/v)": {
585
+ "min": 1.44,
586
+ "max": 1.44,
587
+ "median": 1.44,
588
+ "n": 4
589
+ },
590
+ "SFMA-L (%w/v)": {
591
+ "min": 10.0,
592
+ "max": 10.0,
593
+ "median": 10.0,
594
+ "n": 1
595
+ },
596
+ "SFMA-M (%w/v)": {
597
+ "min": 10.0,
598
+ "max": 10.0,
599
+ "median": 10.0,
600
+ "n": 1
601
+ },
602
+ "SFMA-H (%w/v)": {
603
+ "min": 10.0,
604
+ "max": 10.0,
605
+ "median": 10.0,
606
+ "n": 1
607
+ },
608
+ "KdECMMA (%w/v)": {
609
+ "min": 1.0,
610
+ "max": 3.0,
611
+ "median": 2.0,
612
+ "n": 15
613
+ },
614
+ "BA silk fibroin (%w/v)": {
615
+ "min": 0.5,
616
+ "max": 3.0,
617
+ "median": 1.5,
618
+ "n": 32
619
+ },
620
+ "Carrageenan (%v)": {
621
+ "min": 0.5,
622
+ "max": 1.5,
623
+ "median": 1.0,
624
+ "n": 43
625
+ },
626
+ "Carbopol ETD 2020 NF (%w/v)": {
627
+ "min": 0.1,
628
+ "max": 1.2,
629
+ "median": 0.5,
630
+ "n": 32
631
+ },
632
+ "Carbopol Ultrez 10 NF (%w/v)": {
633
+ "min": 1.5,
634
+ "max": 1.5,
635
+ "median": 1.5,
636
+ "n": 6
637
+ },
638
+ "Carbopol NF-980 (%w/v)": {
639
+ "min": 1.2,
640
+ "max": 1.2,
641
+ "median": 1.2,
642
+ "n": 4
643
+ },
644
+ "FBS (%v/v)": {
645
+ "min": 10.0,
646
+ "max": 10.0,
647
+ "median": 10.0,
648
+ "n": 34
649
+ },
650
+ "MeTro (%w/v)": {
651
+ "min": 7.5,
652
+ "max": 7.5,
653
+ "median": 7.5,
654
+ "n": 28
655
+ },
656
+ "Triethanolamine (%v/v)": {
657
+ "min": 3.0,
658
+ "max": 3.0,
659
+ "median": 3.0,
660
+ "n": 4
661
+ },
662
+ "PEG-Fibrinogen (%w/v)": {
663
+ "min": 1.0,
664
+ "max": 1.0,
665
+ "median": 1.0,
666
+ "n": 8
667
+ },
668
+ "polyethylene glycol dimethacrylate (%w/v)": {
669
+ "min": 1.0,
670
+ "max": 1.0,
671
+ "median": 1.0,
672
+ "n": 50
673
+ },
674
+ "aprotinin (\u00b5g/ml)": {
675
+ "min": 0.2,
676
+ "max": 10.0,
677
+ "median": 0.2,
678
+ "n": 3
679
+ },
680
+ "gold nanorod (mg/mL)": {
681
+ "min": 0.1,
682
+ "max": 0.1,
683
+ "median": 0.1,
684
+ "n": 24
685
+ },
686
+ "egg white (w/v)": {
687
+ "min": 1.0,
688
+ "max": 3.0,
689
+ "median": 2.0,
690
+ "n": 10
691
+ },
692
+ "1-Vinyl-2-pyrrolidone (v/v)": {
693
+ "min": 0.75,
694
+ "max": 0.75,
695
+ "median": 0.75,
696
+ "n": 4
697
+ },
698
+ "carboxyl functionalized carbon nanotubes (%w/v)": {
699
+ "min": 0.3,
700
+ "max": 2.0,
701
+ "median": 1.0,
702
+ "n": 9
703
+ },
704
+ "polyHIPE (%w/v)": {
705
+ "min": 1.0,
706
+ "max": 5.0,
707
+ "median": 3.0,
708
+ "n": 5
709
+ },
710
+ "\u03b2-D galactose (mM)": {
711
+ "min": 40.0,
712
+ "max": 40.0,
713
+ "median": 40.0,
714
+ "n": 2
715
+ },
716
+ "hydrogen peroxide (H2O2) (%v/v)": {
717
+ "min": 0.09,
718
+ "max": 3.87,
719
+ "median": 3.87,
720
+ "n": 55
721
+ },
722
+ "lactic acid (v/v)": {
723
+ "min": 3.0,
724
+ "max": 3.0,
725
+ "median": 3.0,
726
+ "n": 30
727
+ },
728
+ "NorCol (%w/v)": {
729
+ "min": 0.2,
730
+ "max": 1.0,
731
+ "median": 0.6,
732
+ "n": 13
733
+ },
734
+ "DTT (%w/v)": {
735
+ "min": 0.5,
736
+ "max": 8.0,
737
+ "median": 0.7,
738
+ "n": 10
739
+ },
740
+ "ammonium persulfate (mM)": {
741
+ "min": 3.75,
742
+ "max": 5.0,
743
+ "median": 4.375,
744
+ "n": 16
745
+ },
746
+ "diTyr-RGD (mM)": {
747
+ "min": 2.0,
748
+ "max": 6.0,
749
+ "median": 4.0,
750
+ "n": 8
751
+ },
752
+ "PHEG-Tyr (%w/v)": {
753
+ "min": 10.0,
754
+ "max": 10.0,
755
+ "median": 10.0,
756
+ "n": 12
757
+ },
758
+ "MMP2-degradable peptide (%w/v)": {
759
+ "min": 0.1,
760
+ "max": 0.5,
761
+ "median": 0.3,
762
+ "n": 8
763
+ },
764
+ "KdECM (%w/v)": {
765
+ "min": 1.0,
766
+ "max": 5.0,
767
+ "median": 3.3,
768
+ "n": 11
769
+ },
770
+ "EDC (mg)": {
771
+ "min": 50.0,
772
+ "max": 50.0,
773
+ "median": 50.0,
774
+ "n": 22
775
+ },
776
+ "NHS (mg)": {
777
+ "min": 10.0,
778
+ "max": 25.0,
779
+ "median": 10.0,
780
+ "n": 22
781
+ },
782
+ "VA086 (%w/v)": {
783
+ "min": 0.5,
784
+ "max": 20.0,
785
+ "median": 0.75,
786
+ "n": 10
787
+ },
788
+ "PGS (%w/v)": {
789
+ "min": 2.0,
790
+ "max": 20.0,
791
+ "median": 11.0,
792
+ "n": 2
793
+ },
794
+ "thiolated HA (%w/v)": {
795
+ "min": 0.04,
796
+ "max": 0.067,
797
+ "median": 0.05,
798
+ "n": 10
799
+ },
800
+ "boron nitride nanotubes (%w/v)": {
801
+ "min": 0.05,
802
+ "max": 0.1,
803
+ "median": 0.075,
804
+ "n": 3
805
+ },
806
+ "PEDOT:PSS (ul)": {
807
+ "min": 0.1,
808
+ "max": 0.3,
809
+ "median": 0.1,
810
+ "n": 18
811
+ },
812
+ "KCl (mM)": {
813
+ "min": 5.0,
814
+ "max": 5.0,
815
+ "median": 5.0,
816
+ "n": 15
817
+ },
818
+ "skeletal muscle ECM methacrylate (%w/v)": {
819
+ "min": 3.0,
820
+ "max": 3.0,
821
+ "median": 3.0,
822
+ "n": 7
823
+ },
824
+ "PEO (%w/v)": {
825
+ "min": 0.4,
826
+ "max": 0.4,
827
+ "median": 0.4,
828
+ "n": 9
829
+ },
830
+ "Carbon dots (mg/ml)": {
831
+ "min": 20.0,
832
+ "max": 20.0,
833
+ "median": 20.0,
834
+ "n": 9
835
+ },
836
+ "Laminin (ug/ml)": {
837
+ "min": 10.0,
838
+ "max": 93.75,
839
+ "median": 70.0,
840
+ "n": 13
841
+ },
842
+ "DF-PEG (%w/v)": {
843
+ "min": 25.0,
844
+ "max": 25.0,
845
+ "median": 25.0,
846
+ "n": 6
847
+ },
848
+ "omentum ECM (%w/v)": {
849
+ "min": 2.0,
850
+ "max": 2.0,
851
+ "median": 2.0,
852
+ "n": 2
853
+ },
854
+ "thrombin (unit/ml)": {
855
+ "min": 2.5,
856
+ "max": 50.0,
857
+ "median": 10.0,
858
+ "n": 41
859
+ },
860
+ "Carbon nanotube (CNT) (w/v)": {
861
+ "min": 0.5,
862
+ "max": 6.0,
863
+ "median": 1.5,
864
+ "n": 18
865
+ },
866
+ "Phytagel (%v)": {
867
+ "min": 0.3,
868
+ "max": 0.3,
869
+ "median": 0.3,
870
+ "n": 1
871
+ },
872
+ "Laponite-XLG (%w/w)": {
873
+ "min": 2.3,
874
+ "max": 2.3,
875
+ "median": 2.3,
876
+ "n": 25
877
+ },
878
+ "sodium carboxymethyl cellulose (mg)": {
879
+ "min": 75.0,
880
+ "max": 150.0,
881
+ "median": 100.0,
882
+ "n": 60
883
+ },
884
+ "lysozyme amyloid nanofibrils:gold nanoparticles (mg)": {
885
+ "min": 2.5,
886
+ "max": 37.5,
887
+ "median": 12.5,
888
+ "n": 6
889
+ },
890
+ "Lysozyme amyloid nanofibrils (mg)": {
891
+ "min": 2.5,
892
+ "max": 37.5,
893
+ "median": 12.5,
894
+ "n": 31
895
+ },
896
+ "rGO (mg/ml)": {
897
+ "min": 150.0,
898
+ "max": 150.0,
899
+ "median": 150.0,
900
+ "n": 2
901
+ },
902
+ "Methacrylated gellan gum (%w/v)": {
903
+ "min": 2.0,
904
+ "max": 3.5,
905
+ "median": 3.0,
906
+ "n": 15
907
+ },
908
+ "Acetylsalicylic Acid (%w/w)": {
909
+ "min": 10.0,
910
+ "max": 10.0,
911
+ "median": 10.0,
912
+ "n": 3
913
+ },
914
+ "PVA methacrylate (%w/v)": {
915
+ "min": 1.0,
916
+ "max": 5.0,
917
+ "median": 5.0,
918
+ "n": 11
919
+ },
920
+ "MXene (mg/ml)": {
921
+ "min": 0.1,
922
+ "max": 5.0,
923
+ "median": 0.75,
924
+ "n": 6
925
+ }
926
+ }
cell_lines.py CHANGED
@@ -1,178 +1,1320 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  CELL_LINE_OPTIONS = [
2
- "NoCellCultured",
3
- "chondrocyteyte",
4
- "HepG2",
5
- "bMSCs",
6
- "HUVECs",
7
- "NIH3T3",
8
- "MESCs",
9
- "hiPSC-CMs /ATCCs",
10
- "CPCs",
11
- "L929",
12
- "Myoblast cells",
13
- "hiPSCs",
14
- "HepaRG",
15
- "hESCs",
16
- "10T1/2",
17
- "Cardiac progenitor cells",
18
- "NSCLC PDX",
19
- "RAMECs",
20
- "hASCs",
21
- "HAVIC",
22
- "Primary mouse hepatocyte",
23
- "PTECs",
24
- "human nasoseptal chondrocytes",
25
- "PDX",
26
- "HPFs",
27
- "U87-MG",
28
- "ESCs",
29
- "HASSMC",
30
- "dermal fibroblasts",
31
- "MC3T3-E1",
32
- "Schwann cells",
33
- "hiPSC-CMs and HS-27A",
34
- "Saos-2 ",
35
- "SU3",
36
- "hTMSCs",
37
- "HACs",
38
- "HADMSCs",
39
- "HeLa",
40
- "human primary kidney cells",
41
- "myoblasts",
42
- "MSCs",
43
- "human primary kidneycells",
44
- "293FT",
45
- "HEK 293FT",
46
- "Wnt3a-293FT",
47
- "RSC96/HUVECs",
48
- "human adipogenic mesenchymal stem cells",
49
- "HEPG2/ECs",
50
- "HUVECs/MSCs",
51
- "RHECs",
52
- "Human non-small cell lung cancer line Calu-3 (Calu-3)",
53
- "HL-1",
54
- "mouse cardiac cells",
55
- "IMR-90",
56
- "EPCs",
57
- "MRC5",
58
- "rMSC",
59
- "basil plant cell",
60
- "hNCs",
61
- "A549",
62
- "human induced pluripotent stem cell-derived cardiomyocytes",
63
- "bMSCs/hACs",
64
- "EA.hy 926 cells",
65
- "HepG2/C3A",
66
- "human epithelial lung carcinoma cells",
67
- "Human cardiac fibroblasts",
68
- "hTERT-MSC",
69
- "cardiomyocytes",
70
- "Huh7",
71
- "NRCMs",
72
- "HCF",
73
- "Wnt reporter-293FT",
74
- "neonatal rat ventricular CFs",
75
- "human coronary artery endothelial cells",
76
- "hiPSC-CM / fibroblasts",
77
- "primary mouse hepatocyte",
78
- "NIH3T3/ HUVECs",
79
- "murine macrophage-like cell line",
80
- "Endothelial cells",
81
- "Human aortic VIC",
82
- "sADSC",
83
- "HUVECs/H9C2",
84
- "neonatal rat ventricular cardiomyocytes",
85
- "MG-63",
86
- "Neonatal mouse cardiomyocytes (NMVCMs)",
87
- "human hepatic stellate cell line",
88
- "HEK-293",
89
- "aHSC",
90
- "MFCs",
91
- "fibroblasts",
92
- "HNDF",
93
- "cardiomyocyte/MSCs",
94
- "ADSCs",
95
- "HCASMCs",
96
- "cardiomyocyte",
97
- "hCPCs",
98
- "Human CM /adult human fibroblasts ",
99
- "primary rat hepatocyte",
100
- "human cardiac progenitor cells",
101
- "SMC",
102
- "Human MSCs",
103
- "ACPCs",
104
- "Huh7/HepaRG",
105
- "Human umbilical vein endothelial cells",
106
- "ATDC5",
107
- "hESC-derived HLCs",
108
- "NIH 3T3",
109
- "n neonatal mouse ventricular cardiomyocytes",
110
- "CFs/CMs/HUVECs",
111
- "MRC-5",
112
- "VIC",
113
- "eHep",
114
- "hUVECs/NIH3T3",
115
- "MC3T3",
116
- "HLC",
117
- "hepatoma",
118
- "FB",
119
- "A549 GFP+",
120
- "HPAAF",
121
- "PMHs",
122
- "HUVSMCs",
123
- "Human CPCs",
124
- "Fibroblasts/THP-1",
125
- "rat ventricular cardiomyocytes",
126
- "iCMs/iCFs/iECs",
127
- "HUVEC/HHSC",
128
- "Human CPCs / MSCs",
129
- "HepaRG/LX-2 ",
130
- "iCMs/iCFs/iECs/iCMFs",
131
- "LX-2 ",
132
- "SMMC-7721",
133
- "Hepatoblast- single cell/iESC/iMSC",
134
- "iCMFs",
135
- "Hepatoblast- spheroid/iESC/iMSC",
136
- "hBM-MSCs",
137
- "BMSCs",
138
- "HUVECs and HHSCs",
139
- "Intrahepatic cholangiocarcinoma (ICC)",
140
- "VICs",
141
- "CMs/CFs",
142
- "human neonatal dermal fi broblasts",
143
- "10T1/2 fibroblast-laden cells",
144
- "human cardiac fibroblasts",
145
- "neonatal rat ventricular CMs",
146
- "HUVECs and hiPSC-CS",
147
- "iPSC-derived CM",
148
- "Human Umbilical Vein Endothelial Cells + iPSC-derived CM",
149
- "rabbit bone marrow mesenchymal stem cells",
150
- "Neonatal rat cardiomyocytes",
151
- "NIH 3T3 mouse fibroblasts",
152
- "Human CPCs & MSCs",
153
- "NSCLC PDX/CAFs",
154
- "hiPSCs-derived HLCs",
155
- "U87",
156
- "75% hepatoblast cells, 20% iEC and 5% iMSC",
157
- "SMCs",
158
- "A549/95-D cells",
159
- "NCI-H441",
160
- "pancreatic cancer cell",
161
- "prostate cancer stem cell",
162
- "human primary parathyroid cells ",
163
- "primary human hepatocytes",
164
- "CPCs / MSCs",
165
- "CPCs ",
166
- "cardiac fibroblasts",
167
- "iPSCs-derived cardiomyocytes",
168
- "cardiomyocytes/ fibroblasts",
169
- "hiPSC-CM",
170
- "iPSCs/HUVECs",
171
- "iPSC-CMs",
172
- "iPSCs",
173
- "H1395",
174
- "PC9",
175
- "H1650",
176
- "HULEC-5a",
177
- "NCI-H1703"
178
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Cell-line vocabulary and observed seeding densities.
3
+
4
+ GENERATED by 06_webapp/build_app_data.py - do not edit by hand.
5
+ Regenerate after any change to the dataset or the benchmark tables.
6
+ """
7
+
8
+ # Ordered by frequency in the corpus, with the acellular token
9
+ # first because it selects a different mode: cell response is
10
+ # not applicable and WSSQ falls back to printability alone.
11
+ ACELLULAR_TOKEN = 'NoCellCultured'
12
+
13
  CELL_LINE_OPTIONS = [
14
+ 'NoCellCultured',
15
+ 'HUVECs',
16
+ 'HepG2',
17
+ 'bMSCs',
18
+ 'chondrocytes',
19
+ 'NIH3T3',
20
+ 'HepaRG',
21
+ 'hiPSC-CMs /ATCCs',
22
+ 'Myoblast cells',
23
+ 'H9c2',
24
+ 'HEK',
25
+ 'CPCs',
26
+ 'MESCs',
27
+ 'hiPSCs',
28
+ 'L929',
29
+ 'Primary mouse hepatocyte',
30
+ 'NSCLC PDX',
31
+ 'human primary kidney cells',
32
+ 'Cardiac progenitor cells',
33
+ '10T1/2',
34
+ 'HAVIC',
35
+ 'RAMECs',
36
+ 'hESCs',
37
+ 'human nasoseptal chondrocytes',
38
+ 'PDX',
39
+ 'PTECs',
40
+ 'HPFs',
41
+ 'MG-63',
42
+ 'dermal fibroblasts',
43
+ 'HASSMC',
44
+ 'Saos-2',
45
+ 'MC3T3-E1',
46
+ 'hASCs',
47
+ 'SU3',
48
+ 'ESCs',
49
+ 'RPTEC/TERT1',
50
+ 'hiPSC-CMs and HS-27A',
51
+ 'HADMSCs',
52
+ 'HACs',
53
+ 'MRC5',
54
+ 'HeLa',
55
+ 'RHECs',
56
+ 'cardiomyocytes',
57
+ 'human adipogenic mesenchymal stem cells',
58
+ 'MSCs',
59
+ 'HUVECs/MSCs',
60
+ 'U87-MG',
61
+ 'RSC96/HUVECs',
62
+ 'HEPG2/ECs',
63
+ 'A549',
64
+ 'Human non-small cell lung cancer line Calu-3 (Calu-3)',
65
+ 'hTMSCs',
66
+ 'mouse cardiac cells',
67
+ 'Human cardiac fibroblasts',
68
+ 'Schwann cells',
69
+ 'hTERT-MSC',
70
+ 'bMSCs/hACs',
71
+ 'basil plant cell',
72
+ 'EPCs',
73
+ 'HepaRG / LX-2',
74
+ 'Caki-1',
75
+ 'human induced pluripotent stem cell-derived cardiomyocytes',
76
+ 'human epithelial lung carcinoma cells',
77
+ 'HCF',
78
+ 'Huh7',
79
+ '3T3 Fibroblasts',
80
+ 'EA.hy 926 cells',
81
+ 'human coronary artery endothelial cells',
82
+ 'NIH3T3/ HUVECs',
83
+ 'neonatal rat ventricular CFs',
84
+ 'HepG2/C3A',
85
+ 'IMR-90',
86
+ 'rMSC',
87
+ 'hiPSC-CM / fibroblasts',
88
+ 'HUVECs/H9C2',
89
+ 'fibroblasts',
90
+ 'Endothelial cells',
91
+ 'neonatal rat ventricular cardiomyocytes',
92
+ 'Neonatal mouse cardiomyocytes (NMVCMs)',
93
+ 'murine macrophage-like cell line',
94
+ '293FT',
95
+ 'sADSC',
96
+ 'human hepatic stellate cell line',
97
+ 'aHSC',
98
+ 'NRCMs',
99
+ 'HBE',
100
+ 'HEK-293',
101
+ 'Wnt3a-293FT',
102
+ 'Human aortic VIC',
103
+ 'HEK 293FT',
104
+ 'HNDF',
105
+ 'Huh7/HepaRG',
106
+ 'hiHeps',
107
+ 'hCPCs',
108
+ 'pancreatic islets cells',
109
+ 'ICC',
110
+ 'Wnt reporter-293FT',
111
+ 'Human CPCs',
112
+ 'human cardiac progenitor cells',
113
+ 'SMC',
114
+ 'hepatoma',
115
+ 'cardiomyocyte/MSCs',
116
+ 'FB',
117
+ 'eHep',
118
+ 'PMHs',
119
+ 'Human CM /adult human fibroblasts',
120
+ 'HUVSMCs',
121
+ 'ACPCs',
122
+ 'MC3T3',
123
+ 'hNCs',
124
+ 'primary rat hepatocyte',
125
+ 'Human umbilical vein endothelial cells',
126
+ 'HPAAF',
127
+ 'VIC',
128
+ 'HLC',
129
+ 'ADSCs',
130
+ 'MFCs',
131
+ 'A549 GFP+',
132
+ 'ATDC5',
133
+ 'CFs/CMs/HUVECs',
134
+ 'n neonatal mouse ventricular cardiomyocytes',
135
+ 'Human MSCs',
136
+ 'Human CPCs / MSCs',
137
+ 'hESC-derived HLCs',
138
+ 'HCASMCs',
139
+ 'iCMs/iCFs/iECs/iCMFs',
140
+ 'VICs',
141
+ 'iPSCs',
142
+ 'primary human hepatocytes',
143
+ 'SMCs',
144
+ 'iCMs/iCFs/iECs',
145
+ 'PC9',
146
+ 'H1395',
147
+ 'H1650',
148
+ 'HUVECs and hiPSC-CS',
149
+ 'human neonatal dermal fi broblasts',
150
+ '10T1/2 fibroblast-laden cells',
151
+ 'iCMFs',
152
+ 'neonatal rat ventricular CMs',
153
+ 'U87',
154
+ 'NSCLC PDX/CAFs',
155
+ 'iPSC-derived CM',
156
+ 'NIH 3T3 mouse fibroblasts',
157
+ 'Neonatal rat cardiomyocytes',
158
+ 'hiPSCs-derived HLCs',
159
+ 'iPSCs/HUVECs',
160
+ 'HULEC-5a',
161
+ 'HUVECs and HHSCs',
162
+ 'NCI-H1703',
163
+ '75% hepatoblast cells, 20% iEC and 5% iMSC',
164
+ 'Intrahepatic cholangiocarcinoma (ICC)',
165
+ 'NCI-H441',
166
+ 'Human Umbilical Vein Endothelial Cells + iPSC-derived CM',
167
+ 'rabbit bone marrow mesenchymal stem cells',
168
+ 'CMs/CFs',
169
+ 'cardiac fibroblasts',
170
+ 'iPSCs-derived cardiomyocytes',
171
+ 'cardiomyocytes/ fibroblasts',
172
+ 'hiPSC-CM',
173
+ 'iPSC-CMs',
174
+ 'hBM-MSCs',
175
+ 'Hepatoblast- single cell/iESC/iMSC',
176
+ 'Hepatoblast- spheroid/iESC/iMSC',
177
+ 'human primary parathyroid cells',
178
+ 'prostate cancer stem cell',
179
+ 'HL-1',
180
+ 'CPCs / MSCs',
181
+ 'pancreatic cancer cell',
182
+ 'A549/95-D cells',
183
+ 'SMMC-7721',
184
+ 'rat ventricular cardiomyocytes',
185
+ 'A549 lung adenocarcinoma cells',
186
+ 'HMEC-1/ fibroblast/ THP-1',
187
+ 'HMEC-1',
188
+ 'HAVSMC',
189
+ 'HUVEC/HHSC',
190
+ 'LX-2',
191
+ 'Fibroblasts/THP-1',
192
+ 'HLF',
193
+ 'HUVECs / LX-2',
194
+ 'CCAO',
195
+ 'Hepa RG/ HUVEC',
196
+ 'BEAS2B',
197
+ 'PDGFRβ+',
198
+ 'hMSCs',
199
+ 'CiPSC-HOs',
200
+ 'tubular epithelial',
201
+ ]
202
+
203
+ CELL_LINE_COUNTS = {
204
+ 'NoCellCultured': 1573,
205
+ 'HUVECs': 75,
206
+ 'HepG2': 71,
207
+ 'bMSCs': 65,
208
+ 'chondrocytes': 39,
209
+ 'NIH3T3': 35,
210
+ 'HepaRG': 28,
211
+ 'hiPSC-CMs /ATCCs': 26,
212
+ 'Myoblast cells': 26,
213
+ 'H9c2': 25,
214
+ 'HEK': 24,
215
+ 'CPCs': 21,
216
+ 'MESCs': 20,
217
+ 'hiPSCs': 17,
218
+ 'L929': 15,
219
+ 'Primary mouse hepatocyte': 14,
220
+ 'NSCLC PDX': 13,
221
+ 'human primary kidney cells': 13,
222
+ 'Cardiac progenitor cells': 13,
223
+ '10T1/2': 13,
224
+ 'HAVIC': 12,
225
+ 'RAMECs': 12,
226
+ 'hESCs': 11,
227
+ 'human nasoseptal chondrocytes': 10,
228
+ 'PDX': 10,
229
+ 'PTECs': 10,
230
+ 'HPFs': 10,
231
+ 'MG-63': 9,
232
+ 'dermal fibroblasts': 9,
233
+ 'HASSMC': 9,
234
+ 'Saos-2': 8,
235
+ 'MC3T3-E1': 8,
236
+ 'hASCs': 8,
237
+ 'SU3': 8,
238
+ 'ESCs': 8,
239
+ 'RPTEC/TERT1': 8,
240
+ 'hiPSC-CMs and HS-27A': 8,
241
+ 'HADMSCs': 7,
242
+ 'HACs': 7,
243
+ 'MRC5': 7,
244
+ 'HeLa': 7,
245
+ 'RHECs': 6,
246
+ 'cardiomyocytes': 6,
247
+ 'human adipogenic mesenchymal stem cells': 6,
248
+ 'MSCs': 6,
249
+ 'HUVECs/MSCs': 6,
250
+ 'U87-MG': 6,
251
+ 'RSC96/HUVECs': 6,
252
+ 'HEPG2/ECs': 6,
253
+ 'A549': 6,
254
+ 'Human non-small cell lung cancer line Calu-3 (Calu-3)': 5,
255
+ 'hTMSCs': 5,
256
+ 'mouse cardiac cells': 5,
257
+ 'Human cardiac fibroblasts': 5,
258
+ 'Schwann cells': 5,
259
+ 'hTERT-MSC': 4,
260
+ 'bMSCs/hACs': 4,
261
+ 'basil plant cell': 4,
262
+ 'EPCs': 4,
263
+ 'HepaRG / LX-2': 4,
264
+ 'Caki-1': 4,
265
+ 'human induced pluripotent stem cell-derived cardiomyocytes': 4,
266
+ 'human epithelial lung carcinoma cells': 4,
267
+ 'HCF': 4,
268
+ 'Huh7': 4,
269
+ '3T3 Fibroblasts': 4,
270
+ 'EA.hy 926 cells': 4,
271
+ 'human coronary artery endothelial cells': 4,
272
+ 'NIH3T3/ HUVECs': 4,
273
+ 'neonatal rat ventricular CFs': 4,
274
+ 'HepG2/C3A': 4,
275
+ 'IMR-90': 4,
276
+ 'rMSC': 4,
277
+ 'hiPSC-CM / fibroblasts': 3,
278
+ 'HUVECs/H9C2': 3,
279
+ 'fibroblasts': 3,
280
+ 'Endothelial cells': 3,
281
+ 'neonatal rat ventricular cardiomyocytes': 3,
282
+ 'Neonatal mouse cardiomyocytes (NMVCMs)': 3,
283
+ 'murine macrophage-like cell line': 3,
284
+ '293FT': 3,
285
+ 'sADSC': 3,
286
+ 'human hepatic stellate cell line': 3,
287
+ 'aHSC': 3,
288
+ 'NRCMs': 3,
289
+ 'HBE': 3,
290
+ 'HEK-293': 3,
291
+ 'Wnt3a-293FT': 3,
292
+ 'Human aortic VIC': 3,
293
+ 'HEK 293FT': 3,
294
+ 'HNDF': 2,
295
+ 'Huh7/HepaRG': 2,
296
+ 'hiHeps': 2,
297
+ 'hCPCs': 2,
298
+ 'pancreatic islets cells': 2,
299
+ 'ICC': 2,
300
+ 'Wnt reporter-293FT': 2,
301
+ 'Human CPCs': 2,
302
+ 'human cardiac progenitor cells': 2,
303
+ 'SMC': 2,
304
+ 'hepatoma': 2,
305
+ 'cardiomyocyte/MSCs': 2,
306
+ 'FB': 2,
307
+ 'eHep': 2,
308
+ 'PMHs': 2,
309
+ 'Human CM /adult human fibroblasts': 2,
310
+ 'HUVSMCs': 2,
311
+ 'ACPCs': 2,
312
+ 'MC3T3': 2,
313
+ 'hNCs': 2,
314
+ 'primary rat hepatocyte': 2,
315
+ 'Human umbilical vein endothelial cells': 2,
316
+ 'HPAAF': 2,
317
+ 'VIC': 2,
318
+ 'HLC': 2,
319
+ 'ADSCs': 2,
320
+ 'MFCs': 2,
321
+ 'A549 GFP+': 2,
322
+ 'ATDC5': 2,
323
+ 'CFs/CMs/HUVECs': 2,
324
+ 'n neonatal mouse ventricular cardiomyocytes': 2,
325
+ 'Human MSCs': 2,
326
+ 'Human CPCs / MSCs': 2,
327
+ 'hESC-derived HLCs': 2,
328
+ 'HCASMCs': 2,
329
+ 'iCMs/iCFs/iECs/iCMFs': 1,
330
+ 'VICs': 1,
331
+ 'iPSCs': 1,
332
+ 'primary human hepatocytes': 1,
333
+ 'SMCs': 1,
334
+ 'iCMs/iCFs/iECs': 1,
335
+ 'PC9': 1,
336
+ 'H1395': 1,
337
+ 'H1650': 1,
338
+ 'HUVECs and hiPSC-CS': 1,
339
+ 'human neonatal dermal fi broblasts': 1,
340
+ '10T1/2 fibroblast-laden cells': 1,
341
+ 'iCMFs': 1,
342
+ 'neonatal rat ventricular CMs': 1,
343
+ 'U87': 1,
344
+ 'NSCLC PDX/CAFs': 1,
345
+ 'iPSC-derived CM': 1,
346
+ 'NIH 3T3 mouse fibroblasts': 1,
347
+ 'Neonatal rat cardiomyocytes': 1,
348
+ 'hiPSCs-derived HLCs': 1,
349
+ 'iPSCs/HUVECs': 1,
350
+ 'HULEC-5a': 1,
351
+ 'HUVECs and HHSCs': 1,
352
+ 'NCI-H1703': 1,
353
+ '75% hepatoblast cells, 20% iEC and 5% iMSC': 1,
354
+ 'Intrahepatic cholangiocarcinoma (ICC)': 1,
355
+ 'NCI-H441': 1,
356
+ 'Human Umbilical Vein Endothelial Cells + iPSC-derived CM': 1,
357
+ 'rabbit bone marrow mesenchymal stem cells': 1,
358
+ 'CMs/CFs': 1,
359
+ 'cardiac fibroblasts': 1,
360
+ 'iPSCs-derived cardiomyocytes': 1,
361
+ 'cardiomyocytes/ fibroblasts': 1,
362
+ 'hiPSC-CM': 1,
363
+ 'iPSC-CMs': 1,
364
+ 'hBM-MSCs': 1,
365
+ 'Hepatoblast- single cell/iESC/iMSC': 1,
366
+ 'Hepatoblast- spheroid/iESC/iMSC': 1,
367
+ 'human primary parathyroid cells': 1,
368
+ 'prostate cancer stem cell': 1,
369
+ 'HL-1': 1,
370
+ 'CPCs / MSCs': 1,
371
+ 'pancreatic cancer cell': 1,
372
+ 'A549/95-D cells': 1,
373
+ 'SMMC-7721': 1,
374
+ 'rat ventricular cardiomyocytes': 1,
375
+ 'A549 lung adenocarcinoma cells': 1,
376
+ 'HMEC-1/ fibroblast/ THP-1': 1,
377
+ 'HMEC-1': 1,
378
+ 'HAVSMC': 1,
379
+ 'HUVEC/HHSC': 1,
380
+ 'LX-2': 1,
381
+ 'Fibroblasts/THP-1': 1,
382
+ 'HLF': 1,
383
+ 'HUVECs / LX-2': 1,
384
+ 'CCAO': 1,
385
+ 'Hepa RG/ HUVEC': 1,
386
+ 'BEAS2B': 1,
387
+ 'PDGFRβ+': 1,
388
+ 'hMSCs': 1,
389
+ 'CiPSC-HOs': 1,
390
+ 'tubular epithelial': 1,
391
+ }
392
+
393
+ # Density in 10^6 cells/mL, among records using that line.
394
+ CELL_DENSITY_RANGES = {
395
+ "NoCellCultured": {
396
+ "min": 1.0,
397
+ "max": 3.0,
398
+ "median": 1.0
399
+ },
400
+ "HUVECs": {
401
+ "min": 0.035,
402
+ "max": 20.0,
403
+ "median": 5.0
404
+ },
405
+ "HepG2": {
406
+ "min": 1.0,
407
+ "max": 20.0,
408
+ "median": 1.5
409
+ },
410
+ "bMSCs": {
411
+ "min": 0.25,
412
+ "max": 20.0,
413
+ "median": 10.0
414
+ },
415
+ "chondrocytes": {
416
+ "min": 0.1,
417
+ "max": 6.0,
418
+ "median": 6.0
419
+ },
420
+ "NIH3T3": {
421
+ "min": 0.1,
422
+ "max": 5.0,
423
+ "median": 2.5
424
+ },
425
+ "HepaRG": {
426
+ "min": 1.0,
427
+ "max": 20.0,
428
+ "median": 4.0
429
+ },
430
+ "hiPSC-CMs /ATCCs": {
431
+ "min": 2.0,
432
+ "max": 2.0,
433
+ "median": 2.0
434
+ },
435
+ "Myoblast cells": {
436
+ "min": 0.366,
437
+ "max": 10.0,
438
+ "median": 1.0
439
+ },
440
+ "H9c2": {
441
+ "min": 0.04,
442
+ "max": 1.0,
443
+ "median": 1.0
444
+ },
445
+ "HEK": {
446
+ "min": 3.0,
447
+ "max": 3.0,
448
+ "median": 3.0
449
+ },
450
+ "CPCs": {
451
+ "min": 2.0,
452
+ "max": 8.0,
453
+ "median": 2.0
454
+ },
455
+ "MESCs": {
456
+ "min": 0.5,
457
+ "max": 2.0,
458
+ "median": 1.0
459
+ },
460
+ "hiPSCs": {
461
+ "min": 1.0,
462
+ "max": 15.0,
463
+ "median": 15.0
464
+ },
465
+ "L929": {
466
+ "min": 0.1,
467
+ "max": 10.0,
468
+ "median": 1.0
469
+ },
470
+ "Primary mouse hepatocyte": {
471
+ "min": 0.128,
472
+ "max": 1.0,
473
+ "median": 0.128
474
+ },
475
+ "NSCLC PDX": {
476
+ "min": 10.0,
477
+ "max": 10.0,
478
+ "median": 10.0
479
+ },
480
+ "human primary kidney cells": {
481
+ "min": 10.0,
482
+ "max": 10.0,
483
+ "median": 10.0
484
+ },
485
+ "Cardiac progenitor cells": {
486
+ "min": 5.0,
487
+ "max": 5.0,
488
+ "median": 5.0
489
+ },
490
+ "10T1/2": {
491
+ "min": 0.5,
492
+ "max": 2.0,
493
+ "median": 0.5
494
+ },
495
+ "HAVIC": {
496
+ "min": 2.5,
497
+ "max": 10.0,
498
+ "median": 3.75
499
+ },
500
+ "RAMECs": {
501
+ "min": 1.0,
502
+ "max": 1.0,
503
+ "median": 1.0
504
+ },
505
+ "hESCs": {
506
+ "min": 1.0,
507
+ "max": 1.0,
508
+ "median": 1.0
509
+ },
510
+ "human nasoseptal chondrocytes": {
511
+ "min": 4.0,
512
+ "max": 4.0,
513
+ "median": 4.0
514
+ },
515
+ "PDX": {
516
+ "min": 10.0,
517
+ "max": 10.0,
518
+ "median": 10.0
519
+ },
520
+ "PTECs": {
521
+ "min": 15.0,
522
+ "max": 15.0,
523
+ "median": 15.0
524
+ },
525
+ "HPFs": {
526
+ "min": 2.0,
527
+ "max": 2.0,
528
+ "median": 2.0
529
+ },
530
+ "MG-63": {
531
+ "min": 0.005,
532
+ "max": 5.0,
533
+ "median": 0.005
534
+ },
535
+ "dermal fibroblasts": {
536
+ "min": 10.0,
537
+ "max": 10.0,
538
+ "median": 10.0
539
+ },
540
+ "HASSMC": {
541
+ "min": 2.5,
542
+ "max": 2.5,
543
+ "median": 2.5
544
+ },
545
+ "Saos-2": {
546
+ "min": 5.0,
547
+ "max": 5.0,
548
+ "median": 5.0
549
+ },
550
+ "MC3T3-E1": {
551
+ "min": 30.0,
552
+ "max": 30.0,
553
+ "median": 30.0
554
+ },
555
+ "hASCs": {
556
+ "min": 1.0,
557
+ "max": 2.0,
558
+ "median": 1.3
559
+ },
560
+ "SU3": {
561
+ "min": 5.0,
562
+ "max": 5.0,
563
+ "median": 5.0
564
+ },
565
+ "ESCs": {
566
+ "min": 6.0,
567
+ "max": 10.0,
568
+ "median": 8.0
569
+ },
570
+ "RPTEC/TERT1": {
571
+ "min": 10.0,
572
+ "max": 10.0,
573
+ "median": 10.0
574
+ },
575
+ "hiPSC-CMs and HS-27A": {
576
+ "min": 20.0,
577
+ "max": 20.0,
578
+ "median": 20.0
579
+ },
580
+ "HADMSCs": {
581
+ "min": 2.0,
582
+ "max": 2.5,
583
+ "median": 2.5
584
+ },
585
+ "HACs": {
586
+ "min": 5.0,
587
+ "max": 15.0,
588
+ "median": 5.0
589
+ },
590
+ "MRC5": {
591
+ "min": 0.04,
592
+ "max": 5.0,
593
+ "median": 3.0
594
+ },
595
+ "HeLa": {
596
+ "min": 1.0,
597
+ "max": 150.0,
598
+ "median": 1.0
599
+ },
600
+ "RHECs": {
601
+ "min": 0.5,
602
+ "max": 0.5,
603
+ "median": 0.5
604
+ },
605
+ "cardiomyocytes": {
606
+ "min": 0.2,
607
+ "max": 10.0,
608
+ "median": 6.0
609
+ },
610
+ "human adipogenic mesenchymal stem cells": {
611
+ "min": 0.75,
612
+ "max": 2.25,
613
+ "median": 1.125
614
+ },
615
+ "MSCs": {
616
+ "min": 0.2,
617
+ "max": 5.0,
618
+ "median": 1.0
619
+ },
620
+ "HUVECs/MSCs": {
621
+ "min": 6.0,
622
+ "max": 6.0,
623
+ "median": 6.0
624
+ },
625
+ "U87-MG": {
626
+ "min": 5.25,
627
+ "max": 5.25,
628
+ "median": 5.25
629
+ },
630
+ "RSC96/HUVECs": {
631
+ "min": 1.0,
632
+ "max": 1.0,
633
+ "median": 1.0
634
+ },
635
+ "HEPG2/ECs": {
636
+ "min": 6.0,
637
+ "max": 6.0,
638
+ "median": 6.0
639
+ },
640
+ "A549": {
641
+ "min": 1.0,
642
+ "max": 5.0,
643
+ "median": 2.5
644
+ },
645
+ "Human non-small cell lung cancer line Calu-3 (Calu-3)": {
646
+ "min": 10.0,
647
+ "max": 10.0,
648
+ "median": 10.0
649
+ },
650
+ "hTMSCs": {
651
+ "min": 3.5,
652
+ "max": 3.5,
653
+ "median": 3.5
654
+ },
655
+ "mouse cardiac cells": {
656
+ "min": 0.32,
657
+ "max": 0.32,
658
+ "median": 0.32
659
+ },
660
+ "Human cardiac fibroblasts": {
661
+ "min": 1.0,
662
+ "max": 1.0,
663
+ "median": 1.0
664
+ },
665
+ "Schwann cells": {
666
+ "min": 1.0,
667
+ "max": 2.0,
668
+ "median": 1.0
669
+ },
670
+ "hTERT-MSC": {
671
+ "min": 10.0,
672
+ "max": 10.0,
673
+ "median": 10.0
674
+ },
675
+ "bMSCs/hACs": {
676
+ "min": 10.0,
677
+ "max": 10.0,
678
+ "median": 10.0
679
+ },
680
+ "EPCs": {
681
+ "min": 10.0,
682
+ "max": 10.0,
683
+ "median": 10.0
684
+ },
685
+ "HepaRG / LX-2": {
686
+ "min": 1.5,
687
+ "max": 3.0,
688
+ "median": 3.0
689
+ },
690
+ "Caki-1": {
691
+ "min": 10.0,
692
+ "max": 10.0,
693
+ "median": 10.0
694
+ },
695
+ "human induced pluripotent stem cell-derived cardiomyocytes": {
696
+ "min": 20.0,
697
+ "max": 20.0,
698
+ "median": 20.0
699
+ },
700
+ "human epithelial lung carcinoma cells": {
701
+ "min": 7.0,
702
+ "max": 7.0,
703
+ "median": 7.0
704
+ },
705
+ "HCF": {
706
+ "min": 2.0,
707
+ "max": 2.0,
708
+ "median": 2.0
709
+ },
710
+ "Huh7": {
711
+ "min": 0.5,
712
+ "max": 0.5,
713
+ "median": 0.5
714
+ },
715
+ "3T3 Fibroblasts": {
716
+ "min": 5.0,
717
+ "max": 5.0,
718
+ "median": 5.0
719
+ },
720
+ "EA.hy 926 cells": {
721
+ "min": 5.0,
722
+ "max": 5.0,
723
+ "median": 5.0
724
+ },
725
+ "human coronary artery endothelial cells": {
726
+ "min": 0.8,
727
+ "max": 1.0,
728
+ "median": 0.8
729
+ },
730
+ "NIH3T3/ HUVECs": {
731
+ "min": 1.0,
732
+ "max": 1.0,
733
+ "median": 1.0
734
+ },
735
+ "neonatal rat ventricular CFs": {
736
+ "min": 5.0,
737
+ "max": 5.0,
738
+ "median": 5.0
739
+ },
740
+ "HepG2/C3A": {
741
+ "min": 5.0,
742
+ "max": 5.0,
743
+ "median": 5.0
744
+ },
745
+ "IMR-90": {
746
+ "min": 3.5,
747
+ "max": 3.5,
748
+ "median": 3.5
749
+ },
750
+ "rMSC": {
751
+ "min": 3.5,
752
+ "max": 3.5,
753
+ "median": 3.5
754
+ },
755
+ "hiPSC-CM / fibroblasts": {
756
+ "min": 1.0,
757
+ "max": 1.0,
758
+ "median": 1.0
759
+ },
760
+ "HUVECs/H9C2": {
761
+ "min": 4.0,
762
+ "max": 4.0,
763
+ "median": 4.0
764
+ },
765
+ "fibroblasts": {
766
+ "min": 1.0,
767
+ "max": 10.0,
768
+ "median": 3.0
769
+ },
770
+ "neonatal rat ventricular cardiomyocytes": {
771
+ "min": 1.0,
772
+ "max": 200.0,
773
+ "median": 1.0
774
+ },
775
+ "Neonatal mouse cardiomyocytes (NMVCMs)": {
776
+ "min": 10.0,
777
+ "max": 10.0,
778
+ "median": 10.0
779
+ },
780
+ "murine macrophage-like cell line": {
781
+ "min": 1.0,
782
+ "max": 1.0,
783
+ "median": 1.0
784
+ },
785
+ "293FT": {
786
+ "min": 2.0,
787
+ "max": 2.0,
788
+ "median": 2.0
789
+ },
790
+ "sADSC": {
791
+ "min": 1.0,
792
+ "max": 3.0,
793
+ "median": 2.0
794
+ },
795
+ "human hepatic stellate cell line": {
796
+ "min": 3.0,
797
+ "max": 5.0,
798
+ "median": 4.0
799
+ },
800
+ "aHSC": {
801
+ "min": 3.0,
802
+ "max": 5.0,
803
+ "median": 4.0
804
+ },
805
+ "NRCMs": {
806
+ "min": 20.0,
807
+ "max": 20.0,
808
+ "median": 20.0
809
+ },
810
+ "HBE": {
811
+ "min": 0.13,
812
+ "max": 0.13,
813
+ "median": 0.13
814
+ },
815
+ "HEK-293": {
816
+ "min": 0.05,
817
+ "max": 2.0,
818
+ "median": 2.0
819
+ },
820
+ "Wnt3a-293FT": {
821
+ "min": 2.0,
822
+ "max": 2.0,
823
+ "median": 2.0
824
+ },
825
+ "Human aortic VIC": {
826
+ "min": 5.0,
827
+ "max": 5.0,
828
+ "median": 5.0
829
+ },
830
+ "HEK 293FT": {
831
+ "min": 2.0,
832
+ "max": 2.0,
833
+ "median": 2.0
834
+ },
835
+ "HNDF": {
836
+ "min": 2.0,
837
+ "max": 2.0,
838
+ "median": 2.0
839
+ },
840
+ "Huh7/HepaRG": {
841
+ "min": 1.5,
842
+ "max": 1.5,
843
+ "median": 1.5
844
+ },
845
+ "hiHeps": {
846
+ "min": 10.0,
847
+ "max": 50.0,
848
+ "median": 30.0
849
+ },
850
+ "hCPCs": {
851
+ "min": 3.0,
852
+ "max": 3.0,
853
+ "median": 3.0
854
+ },
855
+ "pancreatic islets cells": {
856
+ "min": 0.045,
857
+ "max": 0.045,
858
+ "median": 0.045
859
+ },
860
+ "ICC": {
861
+ "min": 3.0,
862
+ "max": 6.0,
863
+ "median": 4.5
864
+ },
865
+ "Wnt reporter-293FT": {
866
+ "min": 2.0,
867
+ "max": 2.0,
868
+ "median": 2.0
869
+ },
870
+ "Human CPCs": {
871
+ "min": 5e-06,
872
+ "max": 5.0,
873
+ "median": 2.5000025
874
+ },
875
+ "human cardiac progenitor cells": {
876
+ "min": 3.0,
877
+ "max": 3.0,
878
+ "median": 3.0
879
+ },
880
+ "SMC": {
881
+ "min": 2.0,
882
+ "max": 10.0,
883
+ "median": 6.0
884
+ },
885
+ "hepatoma": {
886
+ "min": 1.0,
887
+ "max": 1.0,
888
+ "median": 1.0
889
+ },
890
+ "cardiomyocyte/MSCs": {
891
+ "min": 10.0,
892
+ "max": 10.0,
893
+ "median": 10.0
894
+ },
895
+ "FB": {
896
+ "min": 1.0,
897
+ "max": 1.0,
898
+ "median": 1.0
899
+ },
900
+ "eHep": {
901
+ "min": 5.0,
902
+ "max": 5.0,
903
+ "median": 5.0
904
+ },
905
+ "PMHs": {
906
+ "min": 10.0,
907
+ "max": 10.0,
908
+ "median": 10.0
909
+ },
910
+ "Human CM /adult human fibroblasts": {
911
+ "min": 0.01,
912
+ "max": 0.2,
913
+ "median": 0.10500000000000001
914
+ },
915
+ "HUVSMCs": {
916
+ "min": 10.0,
917
+ "max": 10.0,
918
+ "median": 10.0
919
+ },
920
+ "ACPCs": {
921
+ "min": 20.0,
922
+ "max": 20.0,
923
+ "median": 20.0
924
+ },
925
+ "MC3T3": {
926
+ "min": 0.25,
927
+ "max": 0.25,
928
+ "median": 0.25
929
+ },
930
+ "hNCs": {
931
+ "min": 1.0,
932
+ "max": 15.0,
933
+ "median": 8.0
934
+ },
935
+ "primary rat hepatocyte": {
936
+ "min": 1.0,
937
+ "max": 5.0,
938
+ "median": 3.0
939
+ },
940
+ "Human umbilical vein endothelial cells": {
941
+ "min": 1.25,
942
+ "max": 10.0,
943
+ "median": 5.625
944
+ },
945
+ "HPAAF": {
946
+ "min": 0.4,
947
+ "max": 4.0,
948
+ "median": 2.2
949
+ },
950
+ "VIC": {
951
+ "min": 2.0,
952
+ "max": 10.0,
953
+ "median": 6.0
954
+ },
955
+ "HLC": {
956
+ "min": 2.0,
957
+ "max": 2.0,
958
+ "median": 2.0
959
+ },
960
+ "ADSCs": {
961
+ "min": 2.0,
962
+ "max": 2.0,
963
+ "median": 2.0
964
+ },
965
+ "MFCs": {
966
+ "min": 5.0,
967
+ "max": 5.0,
968
+ "median": 5.0
969
+ },
970
+ "A549 GFP+": {
971
+ "min": 1.5,
972
+ "max": 1.5,
973
+ "median": 1.5
974
+ },
975
+ "ATDC5": {
976
+ "min": 5.68,
977
+ "max": 5.68,
978
+ "median": 5.68
979
+ },
980
+ "CFs/CMs/HUVECs": {
981
+ "min": 30.0,
982
+ "max": 30.0,
983
+ "median": 30.0
984
+ },
985
+ "n neonatal mouse ventricular cardiomyocytes": {
986
+ "min": 10.0,
987
+ "max": 10.0,
988
+ "median": 10.0
989
+ },
990
+ "Human MSCs": {
991
+ "min": 5e-06,
992
+ "max": 5.0,
993
+ "median": 2.5000025
994
+ },
995
+ "Human CPCs / MSCs": {
996
+ "min": 5.0,
997
+ "max": 5.0,
998
+ "median": 5.0
999
+ },
1000
+ "hESC-derived HLCs": {
1001
+ "min": 10.0,
1002
+ "max": 10.0,
1003
+ "median": 10.0
1004
+ },
1005
+ "HCASMCs": {
1006
+ "min": 10.0,
1007
+ "max": 10.0,
1008
+ "median": 10.0
1009
+ },
1010
+ "iCMs/iCFs/iECs/iCMFs": {
1011
+ "min": 3.87,
1012
+ "max": 3.87,
1013
+ "median": 3.87
1014
+ },
1015
+ "VICs": {
1016
+ "min": 10.0,
1017
+ "max": 10.0,
1018
+ "median": 10.0
1019
+ },
1020
+ "iPSCs": {
1021
+ "min": 8.0,
1022
+ "max": 8.0,
1023
+ "median": 8.0
1024
+ },
1025
+ "primary human hepatocytes": {
1026
+ "min": 5.0,
1027
+ "max": 5.0,
1028
+ "median": 5.0
1029
+ },
1030
+ "SMCs": {
1031
+ "min": 10.0,
1032
+ "max": 10.0,
1033
+ "median": 10.0
1034
+ },
1035
+ "iCMs/iCFs/iECs": {
1036
+ "min": 9.6,
1037
+ "max": 9.6,
1038
+ "median": 9.6
1039
+ },
1040
+ "PC9": {
1041
+ "min": 1.0,
1042
+ "max": 1.0,
1043
+ "median": 1.0
1044
+ },
1045
+ "H1395": {
1046
+ "min": 1.0,
1047
+ "max": 1.0,
1048
+ "median": 1.0
1049
+ },
1050
+ "H1650": {
1051
+ "min": 1.0,
1052
+ "max": 1.0,
1053
+ "median": 1.0
1054
+ },
1055
+ "HUVECs and hiPSC-CS": {
1056
+ "min": 0.01,
1057
+ "max": 0.01,
1058
+ "median": 0.01
1059
+ },
1060
+ "human neonatal dermal fi broblasts": {
1061
+ "min": 2.0,
1062
+ "max": 2.0,
1063
+ "median": 2.0
1064
+ },
1065
+ "10T1/2 fibroblast-laden cells": {
1066
+ "min": 2.0,
1067
+ "max": 2.0,
1068
+ "median": 2.0
1069
+ },
1070
+ "iCMFs": {
1071
+ "min": 10.0,
1072
+ "max": 10.0,
1073
+ "median": 10.0
1074
+ },
1075
+ "neonatal rat ventricular CMs": {
1076
+ "min": 5.0,
1077
+ "max": 5.0,
1078
+ "median": 5.0
1079
+ },
1080
+ "U87": {
1081
+ "min": 5.0,
1082
+ "max": 5.0,
1083
+ "median": 5.0
1084
+ },
1085
+ "NSCLC PDX/CAFs": {
1086
+ "min": 5.0,
1087
+ "max": 5.0,
1088
+ "median": 5.0
1089
+ },
1090
+ "iPSC-derived CM": {
1091
+ "min": 8.0,
1092
+ "max": 8.0,
1093
+ "median": 8.0
1094
+ },
1095
+ "NIH 3T3 mouse fibroblasts": {
1096
+ "min": 5.0,
1097
+ "max": 5.0,
1098
+ "median": 5.0
1099
+ },
1100
+ "Neonatal rat cardiomyocytes": {
1101
+ "min": 0.2,
1102
+ "max": 0.2,
1103
+ "median": 0.2
1104
+ },
1105
+ "hiPSCs-derived HLCs": {
1106
+ "min": 10.0,
1107
+ "max": 10.0,
1108
+ "median": 10.0
1109
+ },
1110
+ "iPSCs/HUVECs": {
1111
+ "min": 10.0,
1112
+ "max": 10.0,
1113
+ "median": 10.0
1114
+ },
1115
+ "HULEC-5a": {
1116
+ "min": 6.0,
1117
+ "max": 6.0,
1118
+ "median": 6.0
1119
+ },
1120
+ "HUVECs and HHSCs": {
1121
+ "min": 1.8,
1122
+ "max": 1.8,
1123
+ "median": 1.8
1124
+ },
1125
+ "NCI-H1703": {
1126
+ "min": 6.0,
1127
+ "max": 6.0,
1128
+ "median": 6.0
1129
+ },
1130
+ "75% hepatoblast cells, 20% iEC and 5% iMSC": {
1131
+ "min": 10.0,
1132
+ "max": 10.0,
1133
+ "median": 10.0
1134
+ },
1135
+ "Intrahepatic cholangiocarcinoma (ICC)": {
1136
+ "min": 1.0,
1137
+ "max": 1.0,
1138
+ "median": 1.0
1139
+ },
1140
+ "NCI-H441": {
1141
+ "min": 6.0,
1142
+ "max": 6.0,
1143
+ "median": 6.0
1144
+ },
1145
+ "Human Umbilical Vein Endothelial Cells + iPSC-derived CM": {
1146
+ "min": 6.0,
1147
+ "max": 6.0,
1148
+ "median": 6.0
1149
+ },
1150
+ "rabbit bone marrow mesenchymal stem cells": {
1151
+ "min": 0.1,
1152
+ "max": 0.1,
1153
+ "median": 0.1
1154
+ },
1155
+ "CMs/CFs": {
1156
+ "min": 30.0,
1157
+ "max": 30.0,
1158
+ "median": 30.0
1159
+ },
1160
+ "cardiac fibroblasts": {
1161
+ "min": 5.0,
1162
+ "max": 5.0,
1163
+ "median": 5.0
1164
+ },
1165
+ "iPSCs-derived cardiomyocytes": {
1166
+ "min": 200.0,
1167
+ "max": 200.0,
1168
+ "median": 200.0
1169
+ },
1170
+ "cardiomyocytes/ fibroblasts": {
1171
+ "min": 0.2,
1172
+ "max": 0.2,
1173
+ "median": 0.2
1174
+ },
1175
+ "hiPSC-CM": {
1176
+ "min": 0.2,
1177
+ "max": 0.2,
1178
+ "median": 0.2
1179
+ },
1180
+ "iPSC-CMs": {
1181
+ "min": 4.0,
1182
+ "max": 4.0,
1183
+ "median": 4.0
1184
+ },
1185
+ "hBM-MSCs": {
1186
+ "min": 0.25,
1187
+ "max": 0.25,
1188
+ "median": 0.25
1189
+ },
1190
+ "Hepatoblast- single cell/iESC/iMSC": {
1191
+ "min": 5.6,
1192
+ "max": 5.6,
1193
+ "median": 5.6
1194
+ },
1195
+ "Hepatoblast- spheroid/iESC/iMSC": {
1196
+ "min": 5.6,
1197
+ "max": 5.6,
1198
+ "median": 5.6
1199
+ },
1200
+ "human primary parathyroid cells": {
1201
+ "min": 0.06,
1202
+ "max": 0.06,
1203
+ "median": 0.06
1204
+ },
1205
+ "prostate cancer stem cell": {
1206
+ "min": 2.0,
1207
+ "max": 2.0,
1208
+ "median": 2.0
1209
+ },
1210
+ "HL-1": {
1211
+ "min": 8.0,
1212
+ "max": 8.0,
1213
+ "median": 8.0
1214
+ },
1215
+ "CPCs / MSCs": {
1216
+ "min": 5.0,
1217
+ "max": 5.0,
1218
+ "median": 5.0
1219
+ },
1220
+ "pancreatic cancer cell": {
1221
+ "min": 2.0,
1222
+ "max": 2.0,
1223
+ "median": 2.0
1224
+ },
1225
+ "A549/95-D cells": {
1226
+ "min": 1.0,
1227
+ "max": 1.0,
1228
+ "median": 1.0
1229
+ },
1230
+ "SMMC-7721": {
1231
+ "min": 0.5,
1232
+ "max": 0.5,
1233
+ "median": 0.5
1234
+ },
1235
+ "rat ventricular cardiomyocytes": {
1236
+ "min": 10.0,
1237
+ "max": 10.0,
1238
+ "median": 10.0
1239
+ },
1240
+ "A549 lung adenocarcinoma cells": {
1241
+ "min": 10.0,
1242
+ "max": 10.0,
1243
+ "median": 10.0
1244
+ },
1245
+ "HMEC-1/ fibroblast/ THP-1": {
1246
+ "min": 35.0,
1247
+ "max": 35.0,
1248
+ "median": 35.0
1249
+ },
1250
+ "HMEC-1": {
1251
+ "min": 25.0,
1252
+ "max": 25.0,
1253
+ "median": 25.0
1254
+ },
1255
+ "HAVSMC": {
1256
+ "min": 1.5,
1257
+ "max": 1.5,
1258
+ "median": 1.5
1259
+ },
1260
+ "HUVEC/HHSC": {
1261
+ "min": 0.9,
1262
+ "max": 0.9,
1263
+ "median": 0.9
1264
+ },
1265
+ "LX-2": {
1266
+ "min": 0.5,
1267
+ "max": 0.5,
1268
+ "median": 0.5
1269
+ },
1270
+ "Fibroblasts/THP-1": {
1271
+ "min": 14.25,
1272
+ "max": 14.25,
1273
+ "median": 14.25
1274
+ },
1275
+ "HLF": {
1276
+ "min": 5.0,
1277
+ "max": 5.0,
1278
+ "median": 5.0
1279
+ },
1280
+ "HUVECs / LX-2": {
1281
+ "min": 10.0,
1282
+ "max": 10.0,
1283
+ "median": 10.0
1284
+ },
1285
+ "CCAO": {
1286
+ "min": 0.02,
1287
+ "max": 0.02,
1288
+ "median": 0.02
1289
+ },
1290
+ "Hepa RG/ HUVEC": {
1291
+ "min": 20.0,
1292
+ "max": 20.0,
1293
+ "median": 20.0
1294
+ },
1295
+ "BEAS2B": {
1296
+ "min": 2.0,
1297
+ "max": 2.0,
1298
+ "median": 2.0
1299
+ },
1300
+ "PDGFR\u03b2+": {
1301
+ "min": 0.25,
1302
+ "max": 0.25,
1303
+ "median": 0.25
1304
+ },
1305
+ "hMSCs": {
1306
+ "min": 2.0,
1307
+ "max": 2.0,
1308
+ "median": 2.0
1309
+ },
1310
+ "CiPSC-HOs": {
1311
+ "min": 2.5,
1312
+ "max": 2.5,
1313
+ "median": 2.5
1314
+ },
1315
+ "tubular epithelial": {
1316
+ "min": 0.4,
1317
+ "max": 0.4,
1318
+ "median": 0.4
1319
+ }
1320
+ }
corpus_reference.json ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "biomaterials": [
3
+ "Alginate (%w/v)",
4
+ "PVA-HA (%w/v)",
5
+ "CaSO4 (%w/v)",
6
+ "Na2HPO4 (%w/v)",
7
+ "Gelatin (%w/v)",
8
+ "GelMA (%w/v)",
9
+ "laponite (%w/v)",
10
+ "graphene oxide (%w/v)",
11
+ "hydroxyapatite (%w/v)",
12
+ "Hyaluronic_Acid (%w/v)",
13
+ "hyaluronan methacrylate (%w/v)",
14
+ "NorHA (%w/v)",
15
+ "Fibroin/Fibrinogen (%w/v)",
16
+ "Pluronic P-123 (%w/v)",
17
+ "Collagen (%w/v)",
18
+ "Chitosan (%w/v)",
19
+ "CS-AEMA (%w/v)",
20
+ "RGD (mM)",
21
+ "TCP (%w/v)",
22
+ "Gellan (%w/v)",
23
+ "bioactive glass (%w/v)",
24
+ "Nano/Methylcellulose (%w/v)",
25
+ "PEGTA (%w/v)",
26
+ "PEGMA (%w/v)",
27
+ "PEGDA (%w/v)",
28
+ "Agarose (%w/v)",
29
+ "hyaluronic acid+ Ph moieties (%w/v)",
30
+ "matrigel (%w/v)",
31
+ "CaCl2 (mM)",
32
+ "NaCl (mM)",
33
+ "BaCl2 (mM)",
34
+ "SrCl2 (mM)",
35
+ "CaCO3 (mM)",
36
+ "Genipin (%w/v)",
37
+ "PVA (%wt)",
38
+ "trans-glutaminase (%w/v)",
39
+ "alginate lyase (U/ml)",
40
+ "D-glucose (%w/v)",
41
+ "PLGA (%w/v)",
42
+ "vascular tissue-derived dECM (%w/v)",
43
+ "PEG-8-SH (mM)",
44
+ "Alginate dialdehyde (%w/v)",
45
+ "Alginate sulfate (%w/v)",
46
+ "RGD-modified alginate (%w/v)",
47
+ "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
48
+ "chondroitin sulfate methacrylate (%w/v)",
49
+ "PCL (%w/v)",
50
+ "alginate methacrylate (%w/v)",
51
+ "HRP (U/ml)",
52
+ "Pluronic F127 / Lutrol F127 (%w/v)",
53
+ "Irgacure 2959 (%w/v)",
54
+ "Eosin Y (%w/v)",
55
+ "Ruthenium (mM)",
56
+ "sodium persulfate (SPS) (mM)",
57
+ "HEPES (mM)",
58
+ "LAP (%w/v)",
59
+ "glutaraldehyde (%w/v)",
60
+ "PBS (M)",
61
+ "glycerol (%w/v)",
62
+ "cECM (%w/v)",
63
+ "gel-fu (%w/v)",
64
+ "Rose Bengal (%w/v)",
65
+ "Vitamin B2 (%w/v)",
66
+ "VEGF (%w/v)",
67
+ "Polypyrrole:PSS (%w/v)",
68
+ "borate bioactive glass (%w/v)",
69
+ "astaxanthin (%w/v)",
70
+ "PRP (%v/v)",
71
+ "methacrylated collagen (%w/v)",
72
+ "\u03b1-Toc (\u00b5M)",
73
+ "ascorbic acid (mM)",
74
+ "Liver dECM (%w/v)",
75
+ "galactosylated alginate (%w/v)",
76
+ "SC-PEG (%w/v)",
77
+ "SFMA-L (%w/v)",
78
+ "SFMA-M (%w/v)",
79
+ "SFMA-H (%w/v)",
80
+ "KdECMMA (%w/v)",
81
+ "BA silk fibroin (%w/v)",
82
+ "Carrageenan (%v)",
83
+ "Carbopol ETD 2020 NF (%w/v)",
84
+ "Carbopol Ultrez 10 NF (%w/v)",
85
+ "Carbopol NF-980 (%w/v)",
86
+ "FBS (%v/v)",
87
+ "MeTro (%w/v)",
88
+ "Triethanolamine (%v/v)",
89
+ "PEG-Fibrinogen (%w/v)",
90
+ "polyethylene glycol dimethacrylate (%w/v)",
91
+ "aprotinin (\u00b5g/ml)",
92
+ "gold nanorod (mg/mL)",
93
+ "egg white (w/v)",
94
+ "1-Vinyl-2-pyrrolidone (v/v)",
95
+ "carboxyl functionalized carbon nanotubes (%w/v)",
96
+ "polyHIPE (%w/v)",
97
+ "\u03b2-D galactose (mM)",
98
+ "hydrogen peroxide (H2O2) (%v/v)",
99
+ "lactic acid (v/v)",
100
+ "NorCol (%w/v)",
101
+ "DTT (%w/v)",
102
+ "ammonium persulfate (mM)",
103
+ "diTyr-RGD (mM)",
104
+ "PHEG-Tyr (%w/v)",
105
+ "MMP2-degradable peptide (%w/v)",
106
+ "KdECM (%w/v)",
107
+ "EDC (mg)",
108
+ "NHS (mg)",
109
+ "VA086 (%w/v)",
110
+ "PGS (%w/v)",
111
+ "thiolated HA (%w/v)",
112
+ "boron nitride nanotubes (%w/v)",
113
+ "PEDOT:PSS (ul)",
114
+ "KCl (mM)",
115
+ "skeletal muscle ECM methacrylate (%w/v)",
116
+ "PEO (%w/v)",
117
+ "Carbon dots (mg/ml)",
118
+ "Laminin (ug/ml)",
119
+ "DF-PEG (%w/v)",
120
+ "omentum ECM (%w/v)",
121
+ "thrombin (unit/ml)",
122
+ "Carbon nanotube (CNT) (w/v)",
123
+ "Phytagel (%v)",
124
+ "Laponite-XLG (%w/w)",
125
+ "sodium carboxymethyl cellulose (mg)",
126
+ "lysozyme amyloid nanofibrils:gold nanoparticles (mg)",
127
+ "Lysozyme amyloid nanofibrils (mg)",
128
+ "rGO (mg/ml)",
129
+ "Methacrylated gellan gum (%w/v)",
130
+ "Acetylsalicylic Acid (%w/w)",
131
+ "PVA methacrylate (%w/v)",
132
+ "MXene (mg/ml)"
133
+ ],
134
+ "print_params": [
135
+ "Physical Crosslinking Duration (s)",
136
+ "Photo Crosslinking Duration (s)",
137
+ "Extrusion Pressure (kPa)",
138
+ "Nozzle Movement Speed (mm/s)",
139
+ "Nozzle Diameter (\u00b5m)",
140
+ "Syringe Temperature (\u00b0C)",
141
+ "Substrate Temperature (\u00b0C)"
142
+ ],
143
+ "predictors": [
144
+ "Alginate (%w/v)",
145
+ "PVA-HA (%w/v)",
146
+ "CaSO4 (%w/v)",
147
+ "Na2HPO4 (%w/v)",
148
+ "Gelatin (%w/v)",
149
+ "GelMA (%w/v)",
150
+ "laponite (%w/v)",
151
+ "graphene oxide (%w/v)",
152
+ "hydroxyapatite (%w/v)",
153
+ "Hyaluronic_Acid (%w/v)",
154
+ "hyaluronan methacrylate (%w/v)",
155
+ "NorHA (%w/v)",
156
+ "Fibroin/Fibrinogen (%w/v)",
157
+ "Pluronic P-123 (%w/v)",
158
+ "Collagen (%w/v)",
159
+ "Chitosan (%w/v)",
160
+ "CS-AEMA (%w/v)",
161
+ "RGD (mM)",
162
+ "TCP (%w/v)",
163
+ "Gellan (%w/v)",
164
+ "bioactive glass (%w/v)",
165
+ "Nano/Methylcellulose (%w/v)",
166
+ "PEGTA (%w/v)",
167
+ "PEGMA (%w/v)",
168
+ "PEGDA (%w/v)",
169
+ "Agarose (%w/v)",
170
+ "hyaluronic acid+ Ph moieties (%w/v)",
171
+ "matrigel (%w/v)",
172
+ "CaCl2 (mM)",
173
+ "NaCl (mM)",
174
+ "BaCl2 (mM)",
175
+ "SrCl2 (mM)",
176
+ "CaCO3 (mM)",
177
+ "Genipin (%w/v)",
178
+ "PVA (%wt)",
179
+ "trans-glutaminase (%w/v)",
180
+ "alginate lyase (U/ml)",
181
+ "D-glucose (%w/v)",
182
+ "PLGA (%w/v)",
183
+ "vascular tissue-derived dECM (%w/v)",
184
+ "PEG-8-SH (mM)",
185
+ "Alginate dialdehyde (%w/v)",
186
+ "Alginate sulfate (%w/v)",
187
+ "RGD-modified alginate (%w/v)",
188
+ "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
189
+ "chondroitin sulfate methacrylate (%w/v)",
190
+ "PCL (%w/v)",
191
+ "alginate methacrylate (%w/v)",
192
+ "HRP (U/ml)",
193
+ "Pluronic F127 / Lutrol F127 (%w/v)",
194
+ "Irgacure 2959 (%w/v)",
195
+ "Eosin Y (%w/v)",
196
+ "Ruthenium (mM)",
197
+ "sodium persulfate (SPS) (mM)",
198
+ "HEPES (mM)",
199
+ "LAP (%w/v)",
200
+ "glutaraldehyde (%w/v)",
201
+ "PBS (M)",
202
+ "glycerol (%w/v)",
203
+ "cECM (%w/v)",
204
+ "gel-fu (%w/v)",
205
+ "Rose Bengal (%w/v)",
206
+ "Vitamin B2 (%w/v)",
207
+ "VEGF (%w/v)",
208
+ "Polypyrrole:PSS (%w/v)",
209
+ "borate bioactive glass (%w/v)",
210
+ "astaxanthin (%w/v)",
211
+ "PRP (%v/v)",
212
+ "methacrylated collagen (%w/v)",
213
+ "\u03b1-Toc (\u00b5M)",
214
+ "ascorbic acid (mM)",
215
+ "Liver dECM (%w/v)",
216
+ "galactosylated alginate (%w/v)",
217
+ "SC-PEG (%w/v)",
218
+ "SFMA-L (%w/v)",
219
+ "SFMA-M (%w/v)",
220
+ "SFMA-H (%w/v)",
221
+ "KdECMMA (%w/v)",
222
+ "BA silk fibroin (%w/v)",
223
+ "Carrageenan (%v)",
224
+ "Carbopol ETD 2020 NF (%w/v)",
225
+ "Carbopol Ultrez 10 NF (%w/v)",
226
+ "Carbopol NF-980 (%w/v)",
227
+ "FBS (%v/v)",
228
+ "MeTro (%w/v)",
229
+ "Triethanolamine (%v/v)",
230
+ "PEG-Fibrinogen (%w/v)",
231
+ "polyethylene glycol dimethacrylate (%w/v)",
232
+ "aprotinin (\u00b5g/ml)",
233
+ "gold nanorod (mg/mL)",
234
+ "egg white (w/v)",
235
+ "1-Vinyl-2-pyrrolidone (v/v)",
236
+ "carboxyl functionalized carbon nanotubes (%w/v)",
237
+ "polyHIPE (%w/v)",
238
+ "\u03b2-D galactose (mM)",
239
+ "hydrogen peroxide (H2O2) (%v/v)",
240
+ "lactic acid (v/v)",
241
+ "NorCol (%w/v)",
242
+ "DTT (%w/v)",
243
+ "ammonium persulfate (mM)",
244
+ "diTyr-RGD (mM)",
245
+ "PHEG-Tyr (%w/v)",
246
+ "MMP2-degradable peptide (%w/v)",
247
+ "KdECM (%w/v)",
248
+ "EDC (mg)",
249
+ "NHS (mg)",
250
+ "VA086 (%w/v)",
251
+ "PGS (%w/v)",
252
+ "thiolated HA (%w/v)",
253
+ "boron nitride nanotubes (%w/v)",
254
+ "PEDOT:PSS (ul)",
255
+ "KCl (mM)",
256
+ "skeletal muscle ECM methacrylate (%w/v)",
257
+ "PEO (%w/v)",
258
+ "Carbon dots (mg/ml)",
259
+ "Laminin (ug/ml)",
260
+ "DF-PEG (%w/v)",
261
+ "omentum ECM (%w/v)",
262
+ "thrombin (unit/ml)",
263
+ "Carbon nanotube (CNT) (w/v)",
264
+ "Phytagel (%v)",
265
+ "Laponite-XLG (%w/w)",
266
+ "sodium carboxymethyl cellulose (mg)",
267
+ "lysozyme amyloid nanofibrils:gold nanoparticles (mg)",
268
+ "Lysozyme amyloid nanofibrils (mg)",
269
+ "rGO (mg/ml)",
270
+ "Methacrylated gellan gum (%w/v)",
271
+ "Acetylsalicylic Acid (%w/w)",
272
+ "PVA methacrylate (%w/v)",
273
+ "MXene (mg/ml)",
274
+ "Cell Line",
275
+ "Cell Density (million cells/mL)",
276
+ "Physical Crosslinking Duration (s)",
277
+ "Photo Crosslinking Duration (s)",
278
+ "Extrusion Pressure (kPa)",
279
+ "Nozzle Movement Speed (mm/s)",
280
+ "Nozzle Diameter (\u00b5m)",
281
+ "Syringe Temperature (\u00b0C)",
282
+ "Substrate Temperature (\u00b0C)"
283
+ ],
284
+ "targets": [
285
+ "Printability",
286
+ "Cell Response"
287
+ ],
288
+ "n_records": 2646
289
+ }
corpus_reference.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:93af4ac5ab671ead33c91de0ebcc7888b08c1d8b41abb217445f5404a609d584
3
+ size 116700
deploy/models/classifiers/random/cell_response__adaboost.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8933115563a17d4ba1ce2bcb15daa1564c86e5b697a11a3350c8424fef507f93
3
+ size 44341
deploy/models/classifiers/random/cell_response__bagged_trees.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec7fd79460aca6c9212d7757da8b19b01f50ef17b35fbb1e4817a935429fd8f3
3
+ size 1306142
deploy/models/classifiers/random/cell_response__bernoulli_naive_bayes.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:282feda9b6e91e96d5dd1ec9cf6ec43d51e8658a38aae0ff94f763ee9e800368
3
+ size 4218
deploy/models/classifiers/random/cell_response__catboost.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e8acf6b8932614de2bd91250155ade47fe8a9249d4190d24aacb7212f8ec9a9f
3
+ size 110986
deploy/models/classifiers/random/cell_response__decision_tree.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fec56bf6dad067b23827be89f8a816e5c10871268acce82da80894fbb58a5e25
3
+ size 3478
deploy/models/classifiers/random/cell_response__extra_tree.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0128ac01ef824a6b8df6f745106d695b6a593a292666ae517d3d68924d514153
3
+ size 3469
deploy/models/classifiers/random/cell_response__extra_trees.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2a1ebaf96b9d13fcc91b5a3c1ca09633eab16f7b33162624917e435cde2f2efd
3
+ size 1600493
deploy/models/classifiers/random/cell_response__gaussian_naive_bayes.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d00e8cc7debd29d176894f559c3b043bd0ef03c6b1d0ecc6c86fa2149f6855d0
3
+ size 7337
deploy/models/classifiers/random/cell_response__gradient_boosting.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0a4332f43c8af0932c05662b97fde1f0d67525773457cd72ddcc670273462767
3
+ size 28636
deploy/models/classifiers/random/cell_response__hist_gradient_boosting.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2bf42d2ad81b2b130b6d295e9d97480d9ada674e89d701575c2bf7be6d379ecb
3
+ size 645934
deploy/models/classifiers/random/cell_response__k_nearest_neighbours.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:780fbe925315a38f89bbf907dac7e4b1f587266533df7c0115e0fc04276b40a0
3
+ size 53470
deploy/models/classifiers/random/cell_response__k_nn_distance_weighted.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:534a360ee86a358052a823d1107c6c002e4e9ef58a3e84f1c2c860f025c2222d
3
+ size 127106
deploy/models/classifiers/random/cell_response__lightgbm.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2c198a22bf0a01258f0e9196472c74797ae0529ab244b1c3e0fe0d6c40e1d23a
3
+ size 552411
deploy/models/classifiers/random/cell_response__linear_discriminant.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:14d6b11c6b801eef3e7f9fa0c6ad28c0a9366fad15cce208bd00725d313adcb2
3
+ size 145025
deploy/models/classifiers/random/cell_response__linear_svm.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6f41c3e869a8250cbbf74ea42271cd3a07684d46794b48a1ff042f29cf8cd9d9
3
+ size 5535
deploy/models/classifiers/random/cell_response__logistic_regression.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe67a6655375a6e0bfcc706115cc992ed566ba87e57a5696f22843b1b681c1a9
3
+ size 6535
deploy/models/classifiers/random/cell_response__logistic_regression_balanced.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fc90e1018ef9d648de7adb5da1b60e267f4b1e357f538b8fdaa5c6b246c0c97d
3
+ size 6514
deploy/models/classifiers/random/cell_response__mlp_256_128.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f4bf03e58aea3bb7c171b22d4bf03077652236328dcbf30317765bd28c95a35
3
+ size 877150
deploy/models/classifiers/random/cell_response__nearest_centroid.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:579188d8f4730741cc6d4fcec2d042de26c5f1c4e9810ab23851e2152ec467e3
3
+ size 7171
deploy/models/classifiers/random/cell_response__passive_aggressive.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d8c577687544c0044204267fce8e94a8fcd695be0e7d8e4660e029bf2b409f7
3
+ size 12296
deploy/models/classifiers/random/cell_response__perceptron.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:12bd23ecb4a5e58d8a3ba47a549bc38b7e9d027a1dde92c474a5e673b8cd3d34
3
+ size 4111
deploy/models/classifiers/random/cell_response__quadratic_discriminant.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:006ecba15ba6131d1dff15163c6168615363be3716e8eb3fc96a4b63b0f44fc7
3
+ size 29521
deploy/models/classifiers/random/cell_response__random_forest.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1b5b4c7ddd69ecf0575e2cad3dc3b94992ded2934f1fbc74498182984e396165
3
+ size 4994
deploy/models/classifiers/random/cell_response__random_forest_balanced.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7be9e347e9d4921977931954bf9d2326544452d96d7bcd2e9ddbfae163a95b77
3
+ size 3902841
deploy/models/classifiers/random/cell_response__ridge_classifier.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:887429d6d2f49ef7c671d9be75c29bb94bc1c7e3abbe202dba13596533c96af9
3
+ size 6651
deploy/models/classifiers/random/cell_response__sgd_hinge.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0070b38894cb7ef91f72dae63d7efe23ce835ee369e481d3b366062c886d3baa
3
+ size 4886
deploy/models/classifiers/random/cell_response__soft_voting_rf_xgb_lr.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:19f3ea953fb061519b7d0fa42416c8edacffc93f762e87cab7a90f5692643340
3
+ size 5233168
deploy/models/classifiers/random/cell_response__stacking_rf_xgb_lr_lr.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ed798ca8edf3a2f54aae8fde0cc0aaa3eba4457897b97ffa19be49c85c035ba
3
+ size 5233839
deploy/models/classifiers/random/cell_response__svm_polynomial.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bd644c182976274d6ec346169b332e3f18d29912c2152e91f3b6ae17c3155a34
3
+ size 60088
deploy/models/classifiers/random/cell_response__svm_rbf.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec357aa39dbb6631b933c1e98e4d4b2df6135cc5237a40b2d24dfb6c7d128e45
3
+ size 66004
deploy/models/classifiers/random/cell_response__xgboost.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9408add167a4c715a875fd88daf4457c46f058ba91c8fa6f9ca57ce1b7d9bb8
3
+ size 189666
deploy/models/classifiers/random/printability__adaboost.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61fa142eee65a9a3dab6e40de5ec7715340c56563254ca52623e45f0a3681625
3
+ size 48212
deploy/models/classifiers/random/printability__bagged_trees.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98d12f997b7e7bcb6789ddbf32dacddcc53c859e265778f18682b7b693397e51
3
+ size 3775738
deploy/models/classifiers/random/printability__bernoulli_naive_bayes.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98d82bc7b87ea7a6e51932768636f4f9cb2e62d7f796d1f1f2aa420230ae0071
3
+ size 3497
deploy/models/classifiers/random/printability__catboost.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:45c920875ad42ed56cade9561ab095e950e209819d68f2e061ff68534dd73fd3
3
+ size 787008
deploy/models/classifiers/random/printability__decision_tree.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:768ec1872e1fc323154af785e6f71d554389de56fb43aa2c0624e6a70852a6b5
3
+ size 10029
deploy/models/classifiers/random/printability__extra_tree.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:591f152e86031f04d9d92a5cd88164dcb636509e63d6726010c6814b512bfa75
3
+ size 9601
deploy/models/classifiers/random/printability__extra_trees.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e152e1e7423d5e3306b7abe26ee13ed67596433b1e910f818ed6840a284fffa7
3
+ size 487961
deploy/models/classifiers/random/printability__gaussian_naive_bayes.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d806b6407b0cc8ab12d7fa836dee8811e349c8e740b7983016111b02d2239223
3
+ size 6142
deploy/models/classifiers/random/printability__gradient_boosting.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:47a1931819018e107c00db7279b2e46722b92606c7d7fc1bf95f5f2728e9e7fa
3
+ size 955239
deploy/models/classifiers/random/printability__hist_gradient_boosting.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8503f4f620cc539db9e017106b8df556958c3e3191c60da4568d428ac0e2f64f
3
+ size 1555799
deploy/models/classifiers/random/printability__k_nearest_neighbours.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:454d01feb6c9b0927614bf2537641b445aac81664bb0df17b33a7472fccceb49
3
+ size 53695
deploy/models/classifiers/random/printability__k_nn_distance_weighted.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:09f59127db7f4e8d30b7ad810bcdd3eda19749fb13b10ac68fe4e14a32991767
3
+ size 53689