Saeed commited on
Commit
bf33e7b
Β·
1 Parent(s): bfdcef9

First Commit

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* 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
 
 
 
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
Dataset.xlsx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bdb613c74c01c0b60410f283e33d7ea5ffbed8aaef459fe41d11768a2821c781
3
+ size 1016051
README.md CHANGED
@@ -11,4 +11,116 @@ license: cc-by-nc-4.0
11
  short_description: ML Applications in Tissue Engineering
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  short_description: ML Applications in Tissue Engineering
12
  ---
13
 
14
+
15
+ # πŸ‘¬ MLATE V3: Multi-Tissue Scaffold Prediction Platform
16
+
17
+ **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.
18
+
19
+ > πŸ“„ *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*
20
+
21
+ ---
22
+
23
+ ## πŸš€ Features
24
+
25
+ - πŸ”¬ Predict scaffold quality based on printability and cell response
26
+ - πŸ§ͺ Optimize biomaterial concentrations, cell densities, and printing parameters using Optuna
27
+ - 🧠 Powered by two fine-tuned **CatBoostClassifier** models
28
+ - πŸ“‹ Automatically generates fabrication protocols with Gemini API
29
+ - πŸ” Enforces safe defaults and intelligent UI input validation
30
+ - 🧱 Uses a real-world, curated dataset of **2847 samples** across **multiple tissues and cell lines**
31
+
32
+ ---
33
+
34
+ ## πŸ“‚ Dataset
35
+
36
+ This project includes a publicly available dataset (`Dataset.xlsx`) containing:
37
+ - 123 biomaterials
38
+ - 175 cell lines
39
+ - 7 printing parameters
40
+ - Scaffold performance labels
41
+
42
+ 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.
43
+
44
+ You may also optionally add this to Hugging Face Datasets for broader access.
45
+
46
+ ---
47
+
48
+ ## βš™οΈ How It Works
49
+
50
+ 1. **Input**: User selects biomaterials, cell line, and printing parameters with min/max/step values
51
+ 2. **Optimization**: Optuna runs 50 trials to maximize predicted scaffold quality (WSSQ)
52
+ 3. **Prediction**:
53
+ - Two CatBoost models are used to predict:
54
+ - `Printability` (3-class)
55
+ - `Cell Response` (5-class)
56
+ - Probabilistic predictions are mapped to expected scores
57
+ 4. **Scaffold Quality**: A weighted combination of printability and cell response
58
+ 5. **Procedure Generation**: A Gemini API prompt generates custom step-by-step fabrication instructions
59
+
60
+ ---
61
+
62
+ ## πŸ’» Running Locally
63
+
64
+ Clone the repo and install dependencies:
65
+
66
+ ```bash
67
+ git clone https://huggingface.co/spaces/your-username/MLATE-V3
68
+ cd MLATE-V3
69
+
70
+ # Create virtual environment (optional)
71
+ python -m venv venv
72
+ source venv/bin/activate # or venv\Scripts\activate on Windows
73
+
74
+ # Install dependencies
75
+ pip install -r requirements.txt
76
+
77
+ # Set your Gemini API key
78
+ export GEMINI_API_KEY=your_key_here # or set in .env
79
+
80
+ # Run the app
81
+ streamlit run app.py
82
+ ```
83
+
84
+ ---
85
+
86
+ ## πŸ“œ License
87
+
88
+ This project is licensed under the **Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)**.
89
+
90
+ You are free to:
91
+ - Share and adapt the code
92
+ - Use the dataset for academic research
93
+
94
+ But:
95
+ - **Commercial use is prohibited**
96
+ - **Citation is required** (see below)
97
+
98
+ ---
99
+
100
+ ## πŸ“š Citation
101
+
102
+ If you use MLATE V3 or its dataset in your research, please cite:
103
+
104
+ > Rafieyan et al. (preprint, 2025).
105
+ > *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*
106
+ > *(Preprint link to be added after publication)*
107
+
108
+ BibTeX:
109
+ ```bibtex
110
+ @article{rafieyan2025mlate,
111
+ author = {Rafieyan, Saeed and others},
112
+ 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},
113
+ journal = {Preprint},
114
+ year = {2025}
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ## ⚠️ Disclaimer
121
+
122
+ 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.
123
+
124
+ ---
125
+
126
+ Developed by [Saeed Rafieyan](https://sraf.ir)
app.py ADDED
@@ -0,0 +1,616 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import joblib
5
+ import optuna
6
+ import numpy as np
7
+ import openai
8
+ from catboost import CatBoostClassifier
9
+
10
+
11
+ from google import genai
12
+ from google.genai import types
13
+
14
+
15
+
16
+ # keep a reference to the real number_input
17
+ _original_number_input = st.number_input
18
+
19
+ def safe_number_input(label, **kwargs):
20
+ """
21
+ Clamp `value` into [min_value, max_value] and warn if we had to adjust.
22
+ Then call the real st.number_input with the clamped default.
23
+ """
24
+ min_value = kwargs.get("min_value", float("-inf"))
25
+ max_value = kwargs.get("max_value", float("inf"))
26
+ value = kwargs.get("value", min_value)
27
+ # clamp
28
+ clamped = min(max(value, min_value), max_value)
29
+ if clamped != value:
30
+ st.warning(
31
+ f"⚠️ Default for β€œ{label}” ({value}) was outside "
32
+ f"[{min_value}, {max_value}]; using {clamped} instead."
33
+ )
34
+ kwargs["value"] = clamped
35
+ return _original_number_input(label, **kwargs)
36
+
37
+ # override streamlit's number_input globally
38
+ st.number_input = safe_number_input
39
+
40
+
41
+
42
+ # directly use your Gemini key
43
+ gemini_key = os.getenv("GEMINI_API_KEY")
44
+ if not gemini_key:
45
+ st.error("🚨 Gemini API key not found. Please set GEMINI_API_KEY in your Space settings.")
46
+ st.stop()
47
+
48
+ client = genai.Client(api_key=gemini_key)
49
+
50
+
51
+ # ─── Scaffold Quality Function ───────────────────────────────────────────────────
52
+ def scaffold_quality_combined(printability, cell_response,
53
+ weight_printability=0.3, weight_cell_response=0.7):
54
+ if printability == 0:
55
+ return 0.0
56
+ if cell_response == 1:
57
+ return 100 * (printability / 3.0)
58
+ norm_p = printability / 3.0
59
+ norm_c = (cell_response - 1) / 4.0
60
+ hm = (weight_printability + weight_cell_response) / (
61
+ (weight_printability / norm_p) +
62
+ (weight_cell_response / norm_c)
63
+ )
64
+ mc = (norm_p**weight_printability) * (norm_c**weight_cell_response)
65
+ return 100 * ((hm + mc) / 2.0)
66
+
67
+ # ─── Constants ────────────────────────────────────────────────────────────────────
68
+ BIOMATERIAL_OPTIONS = [
69
+ "Alginate (%w/v)",
70
+ "PVA-HA (%w/v)",
71
+ "CaSO4 (%w/v)",
72
+ "Na2HPO4 (%w/v)",
73
+ "Gelatin (%w/v)",
74
+ "GelMA (%w/v)",
75
+ "laponite (%w/v)",
76
+ "graphene oxide (%w/v)",
77
+ "hydroxyapatite (%w/v)",
78
+ "Hyaluronic_Acid (%w/v)",
79
+ "hyaluronan metacrylate (%w/v)",
80
+ "NorHA (%w/v)",
81
+ "Fibroin/Fibrinogen (%w/v)",
82
+ "Pluronic P-123 (%w/v)",
83
+ "Collagen (%w/v)",
84
+ "Chitosan (%w/v)",
85
+ "CS-AEMA (%w/v)",
86
+ "RGD (mM)",
87
+ "TCP (%w/v)",
88
+ "Gellan (%w/v)",
89
+ "bioactive glass (%w/v)",
90
+ "Nano/Methycellulose (%w/v)",
91
+ "PEGTA (%w/v)",
92
+ "PEGMA (%w/v)",
93
+ "PEGDA (%w/v)",
94
+ "Agarose (%w/v)",
95
+ " hyaluronic acid+ Ph moieties (%w/v)",
96
+ "matrigel (%w/v)",
97
+ "CaCl2(mM)",
98
+ "NaCl(mM)",
99
+ "BaCl2(mM)",
100
+ "SrCl2(mM)",
101
+ "CaCO3 (mM)",
102
+ "Genipin (%w/v)",
103
+ "PVA (%wt)",
104
+ "trans-glutaminase (%w/v)",
105
+ "alginate lyase (U/ml)",
106
+ "D-glucose (%w/v)",
107
+ "PLGA (%w/v)",
108
+ "vascular tissued-derived dECM (%w/v)",
109
+ "PEG-8-SH (mM)",
110
+ "Alginate dialdehyde (%w/v)",
111
+ "Alginate sulfate (%w/v)",
112
+ "RGD-modified alginate (%w/v)",
113
+ "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
114
+ "chondroitin sulfate methacrylate (%w/v)",
115
+ "PCL (%w/v)",
116
+ "alginate methacrylate (%w/v)",
117
+ "HRP (U/ml)",
118
+ "Pluronic F127 (%w/v)/Lutrol F127 (%w/v)",
119
+ "Irgacure 2959 (%w/v)",
120
+ "Eosin Y (%w/v)",
121
+ "Ruthenium (mM)",
122
+ "sodium persulfate (SPS) (mM)",
123
+ "HEPES (mM)",
124
+ "LAP (%w/v)",
125
+ "glutaraldehyde (%w/v)",
126
+ "PBS (M)",
127
+ "glycerol (%w/v)",
128
+ "cECM (%w/v)",
129
+ "gel-fu(%w/v)",
130
+ "Rose Bengal (%w/v)",
131
+ "Vitamin B2(%w/v)",
132
+ "VEGF(%w/v)",
133
+ "Polypyrrole:PSS(%w/v)",
134
+ "boratebioactiveglass(%w/v)",
135
+ "astaxanthin(%w/v)",
136
+ "PRP (%v/v)",
137
+ "methacrylated collagen (%w/v)",
138
+ "Ξ±-Toc (Β΅M)",
139
+ "ascorbic acid (mM)",
140
+ "Liver dECM(%w/v)",
141
+ "galactosylated alginate (%w/v)",
142
+ "SC-PEG(%w/v)",
143
+ "SFMA-L(%w/v)",
144
+ "SFMA-M(%w/v)",
145
+ "SFMA-H(%w/v)",
146
+ "KdECMMA(%w/v)",
147
+ "BA silk fibronin (%w/v)",
148
+ "Carrageenan(%v)",
149
+ "Carbopol ETD 2020 NF (%w/v)",
150
+ "Carbopol Ultrez 10 NF(%w/v)",
151
+ "Carbopol NF-980(%w/v)",
152
+ "FBS (%v/v)",
153
+ "MeTro (%w/v)",
154
+ "Triethanolamine (%v/v)",
155
+ "PEG-Fibrinogen (%w/v)",
156
+ "polyethylene glycol dimethacrylate (%w/v)",
157
+ "aprotinin (Β΅g/ml)",
158
+ "gold nanorod (mg/mL)",
159
+ "egg white (w/v)",
160
+ "1-Vinyl-2-Pyrrolidione (v/v)",
161
+ "carboxyl functionalized carbon nanotubes (%w/v)",
162
+ "polyHIPE (%w/v)",
163
+ "Ξ²-D galactose (mM)",
164
+ "hydrogen peroxide (H2O2) (%v/v)",
165
+ "lactic acid v/v",
166
+ "NorCol (%w/v)",
167
+ "DDT (%w/v)",
168
+ "ammonium persulfate (mM)",
169
+ "diTyr-RGD (mM)",
170
+ "PHEG-Tyr (%w/v)",
171
+ "MMP2-degradable peptide (%w/v)",
172
+ "KdECM (%w/v)",
173
+ "EDC (mg)",
174
+ "NHS (mg)",
175
+ "VA086 (%w/v)",
176
+ "PGS (%w/v)",
177
+ "thiolated HA (%w/v)",
178
+ "boron nitride nanotubes (%w/v)",
179
+ "PEDOT:PSS (ul)",
180
+ "KCl (mM)",
181
+ "skeletal muscle ECM methacrylate (%w/v)",
182
+ "PEO (%w/v)",
183
+ "Carbon dots (mg/ml)",
184
+ "Laminin (ug/ml)",
185
+ "DF-PEG (%w/v)",
186
+ "omenta ECM (%w/v)",
187
+ "thrombin (unit/ml)",
188
+ "Carbon nanotube (CNT) (w/v)",
189
+ "Phytagel(%v)",
190
+ "Laponite-XLG (%w/w)"
191
+ ]
192
+
193
+ CELL_LINE_OPTIONS = [
194
+ "chondrocyteyte",
195
+ "HepG2",
196
+ "bMSCs",
197
+ "HUVECs",
198
+ "NIH3T3",
199
+ "MESCs",
200
+ "hiPSC-CMs /ATCCs",
201
+ "CPCs",
202
+ "L929",
203
+ "Myoblast cells",
204
+ "hiPSCs",
205
+ "HepaRG",
206
+ "hESCs",
207
+ "10T1/2",
208
+ "Cardiac progenitor cells",
209
+ "NSCLC PDX",
210
+ "RAMECs",
211
+ "hASCs",
212
+ "HAVIC",
213
+ "Primary mouse hepatocyte",
214
+ "PTECs",
215
+ "human nasoseptal chondrocytes",
216
+ "PDX",
217
+ "HPFs",
218
+ "U87-MG",
219
+ "ESCs",
220
+ "HASSMC",
221
+ "dermal fibroblasts",
222
+ "MC3T3-E1",
223
+ "Schwann cells",
224
+ "hiPSC-CMs and HS-27A",
225
+ "Saos-2 ",
226
+ "SU3",
227
+ "hTMSCs",
228
+ "HACs",
229
+ "HADMSCs",
230
+ "HeLa",
231
+ "human primary kidney cells",
232
+ "myoblasts",
233
+ "MSCs",
234
+ "human primary kidneycells",
235
+ "293FT",
236
+ "HEK 293FT",
237
+ "Wnt3a-293FT",
238
+ "RSC96/HUVECs",
239
+ "human adipogenic mesenchymal stem cells",
240
+ "HEPG2/ECs",
241
+ "HUVECs/MSCs",
242
+ "RHECs",
243
+ "Human non-small cell lung cancer line Calu-3 (Calu-3)",
244
+ "HL-1",
245
+ "mouse cardiac cells",
246
+ "IMR-90",
247
+ "EPCs",
248
+ "MRC5",
249
+ "rMSC",
250
+ "basil plant cell",
251
+ "hNCs",
252
+ "A549",
253
+ "human induced pluripotent stem cell-derived cardiomyocytes",
254
+ "bMSCs/hACs",
255
+ "EA.hy 926 cells",
256
+ "HepG2/C3A",
257
+ "human epithelial lung carcinoma cells",
258
+ "Human cardiac fibroblasts",
259
+ "hTERT-MSC",
260
+ "cardiomyocytes",
261
+ "Huh7",
262
+ "NRCMs",
263
+ "HCF",
264
+ "Wnt reporter-293FT",
265
+ "neonatal rat ventricular CFs",
266
+ "human coronary artery endothelial cells",
267
+ "hiPSC-CM / fibroblasts",
268
+ "primary mouse hepatocyte",
269
+ "NIH3T3/ HUVECs",
270
+ "murine macrophage-like cell line",
271
+ "Endothelial cells",
272
+ "Human aortic VIC",
273
+ "sADSC",
274
+ "HUVECs/H9C2",
275
+ "neonatal rat ventricular cardiomyocytes",
276
+ "MG-63",
277
+ "Neonatal mouse cardiomyocytes (NMVCMs)",
278
+ "human hepatic stellate cell line",
279
+ "HEK-293",
280
+ "aHSC",
281
+ "MFCs",
282
+ "fibroblasts",
283
+ "HNDF",
284
+ "cardiomyocyte/MSCs",
285
+ "ADSCs",
286
+ "HCASMCs",
287
+ "cardiomyocyte",
288
+ "hCPCs",
289
+ "Human CM /adult human fibroblasts ",
290
+ "primary rat hepatocyte",
291
+ "human cardiac progenitor cells",
292
+ "SMC",
293
+ "Human MSCs",
294
+ "ACPCs",
295
+ "Huh7/HepaRG",
296
+ "Human umbilical vein endothelial cells",
297
+ "ATDC5",
298
+ "hESC-derived HLCs",
299
+ "NIH 3T3",
300
+ "n neonatal mouse ventricular cardiomyocytes",
301
+ "CFs/CMs/HUVECs",
302
+ "MRC-5",
303
+ "VIC",
304
+ "eHep",
305
+ "hUVECs/NIH3T3",
306
+ "MC3T3",
307
+ "HLC",
308
+ "hepatoma",
309
+ "FB",
310
+ "A549 GFP+",
311
+ "HPAAF",
312
+ "PMHs",
313
+ "HUVSMCs",
314
+ "Human CPCs",
315
+ "Fibroblasts/THP-1",
316
+ "rat ventricular cardiomyocytes",
317
+ "iCMs/iCFs/iECs",
318
+ "HUVEC/HHSC",
319
+ "Human CPCs / MSCs",
320
+ "HepaRG/LX-2 ",
321
+ "iCMs/iCFs/iECs/iCMFs",
322
+ "LX-2 ",
323
+ "SMMC-7721",
324
+ "Hepatoblast- single cell/iESC/iMSC",
325
+ "iCMFs",
326
+ "Hepatoblast- spheroid/iESC/iMSC",
327
+ "hBM-MSCs",
328
+ "BMSCs",
329
+ "HUVECs and HHSCs",
330
+ "Intrahepatic cholangiocarcinoma (ICC)",
331
+ "VICs",
332
+ "CMs/CFs",
333
+ "human neonatal dermal fi broblasts",
334
+ "10T1/2 fibroblast-laden cells",
335
+ "human cardiac fibroblasts",
336
+ "neonatal rat ventricular CMs",
337
+ "HUVECs and hiPSC-CS",
338
+ "iPSC-derived CM",
339
+ "Human Umbilical Vein Endothelial Cells + iPSC-derived CM",
340
+ "rabbit bone marrow mesenchymal stem cells",
341
+ "Neonatal rat cardiomyocytes",
342
+ "NIH 3T3 mouse fibroblasts",
343
+ "Human CPCs & MSCs",
344
+ "NSCLC PDX/CAFs",
345
+ "hiPSCs-derived HLCs",
346
+ "U87",
347
+ "75% hepatoblast cells, 20% iEC and 5% iMSC",
348
+ "SMCs",
349
+ "A549/95-D cells",
350
+ "NCI-H441",
351
+ "pancreatic cancer cell",
352
+ "prostate cancer stem cell",
353
+ "human primary parathyroid cells ",
354
+ "primary human hepatocytes",
355
+ "CPCs / MSCs",
356
+ "CPCs ",
357
+ "cardiac fibroblasts",
358
+ "iPSCs-derived cardiomyocytes",
359
+ "cardiomyocytes/ fibroblasts",
360
+ "hiPSC-CM",
361
+ "iPSCs/HUVECs",
362
+ "iPSC-CMs",
363
+ "iPSCs",
364
+ "H1395",
365
+ "PC9",
366
+ "H1650",
367
+ "HULEC-5a",
368
+ "NCI-H1703"
369
+ ]
370
+
371
+ PRINT_PARAM_NAMES = [
372
+ "Physical Crosslinking Duration (s)",
373
+ "Photo Crosslinking Duration (s)",
374
+ "Extrusion Pressure (kPa)",
375
+ "Nozzle Movement Speed (mm/s)",
376
+ "Nozzle Diameter (Β΅m)",
377
+ "Syringe Temperature (Β°C)",
378
+ "Substrate Temperature (Β°C)",
379
+ ]
380
+
381
+ # ─── Load Encoder, Scalers, Models ────────────────────────────────────────────────
382
+ @st.cache_resource
383
+ def load_encoder():
384
+ return joblib.load('cell_line_encoder.joblib')
385
+
386
+ @st.cache_resource
387
+ def load_scalers():
388
+ return (
389
+ joblib.load('scaler_printability.joblib'),
390
+ joblib.load('scaler_cell_response.joblib')
391
+ )
392
+
393
+ @st.cache_resource
394
+ def load_models():
395
+ m_p = CatBoostClassifier(); m_p.load_model('catboost_printability.cbm')
396
+ m_c = CatBoostClassifier(); m_c.load_model('catboost_cell_response.cbm')
397
+ return m_p, m_c
398
+
399
+ encoder = load_encoder()
400
+ scaler_print, scaler_cell = load_scalers()
401
+ model_print, model_cell = load_models()
402
+
403
+ feature_order_print = list(scaler_print.feature_names_in_)
404
+ feature_order_cell = list(scaler_cell.feature_names_in_)
405
+
406
+ # ─── Session State Initialization ────────────────────────────────────────────────
407
+ if 'bio_rows' not in st.session_state:
408
+ st.session_state.bio_rows = [{
409
+ 'mat': BIOMATERIAL_OPTIONS[0],
410
+ 'min': 0.0, 'max': 10.0, 'step': 0.1
411
+ }]
412
+
413
+ if 'density_range' not in st.session_state:
414
+ st.session_state.density_range = {'min': 0.0, 'max': 10.0, 'step': 0.1}
415
+
416
+ if 'pp_ranges' not in st.session_state:
417
+ st.session_state.pp_ranges = {
418
+ name: {'min': 0.0, 'max': 10.0, 'step': 3.0}
419
+ for name in PRINT_PARAM_NAMES
420
+ }
421
+
422
+ # ─── App Layout ──────────────────────────────────────────────────────────────────
423
+ st.title("🧬 MLATE: Machine Learning Applications in Tissue Engieering")
424
+ st.markdown(
425
+ "<p style='font-size:0.8em; color:grey;'>"
426
+ "An integrated platform for predicting the quality of 3D (bio)printed scaffolds "
427
+ "in tissue engineering. For more details, please refer to and cite our paper: "
428
+ "<a href='https://doi.org/xxx' target='_blank'>https://doi.org/xxx</a>"
429
+ "</p>",
430
+ unsafe_allow_html=True
431
+ )
432
+
433
+ # ─── Biomaterials Section ────────────────────────────────────────────────────────
434
+ st.subheader("Biomaterials (enter range for each)")
435
+ if st.button("βž• Add Biomaterial"):
436
+ used = {r['mat'] for r in st.session_state.bio_rows}
437
+ available = [m for m in BIOMATERIAL_OPTIONS if m not in used]
438
+ if available:
439
+ st.session_state.bio_rows.append({
440
+ 'mat': available[0], 'min': 0.0, 'max': 10.0, 'step': 0.1
441
+ })
442
+ st.rerun()
443
+
444
+ for i, row in enumerate(st.session_state.bio_rows):
445
+ used_except_current = {
446
+ r['mat'] for idx, r in enumerate(st.session_state.bio_rows) if idx != i
447
+ }
448
+ options = [m for m in BIOMATERIAL_OPTIONS if m not in used_except_current]
449
+
450
+ c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
451
+ mat = c1.selectbox(
452
+ "", options,
453
+ index=options.index(row['mat']) if row['mat'] in options else 0,
454
+ key=f"bio_mat_{i}"
455
+ )
456
+ st.session_state.bio_rows[i]['mat'] = mat
457
+
458
+ mn = c2.number_input(
459
+ "Min", min_value=0.0, max_value=row['max'],
460
+ value=row['min'], step=row['step'], key=f"bio_min_{i}"
461
+ )
462
+ # ← min_value for Max is now the step; default value at least step
463
+ mx = c3.number_input(
464
+ "Max", min_value=row['step'], max_value=100.0,
465
+ value=max(row['max'], row['step']), step=row['step'],
466
+ key=f"bio_max_{i}"
467
+ )
468
+ st.session_state.bio_rows[i].update(min=mn, max=mx)
469
+
470
+ st.session_state.bio_rows[i]['step'] = c4.number_input(
471
+ "Step", min_value=0.0,
472
+ max_value=(mx - mn) if mx > mn else 0.1,
473
+ value=row['step'], step=0.1, key=f"bio_step_{i}"
474
+ )
475
+
476
+ if c5.button("❌", key=f"rem_{i}"):
477
+ st.session_state.bio_rows.pop(i)
478
+ st.rerun()
479
+
480
+ st.markdown("---")
481
+
482
+ # ─── Cell Line & Density Section ────────────────────────────────────────────────
483
+ st.subheader("Cell Line & Density")
484
+ col1, col2, col3, col4 = st.columns([2,1,1,1])
485
+ cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS)
486
+
487
+ dr = st.session_state.density_range
488
+ dmin = col2.number_input(
489
+ "Min Density", min_value=0.0, max_value=dr['max'],
490
+ value=dr['min'], step=dr['step'], key="cd_min"
491
+ )
492
+ # ← ensure Max Density cannot go below the step
493
+ dmax = col3.number_input(
494
+ "Max Density", min_value=dr['step'], max_value=1000.0,
495
+ value=max(dr['max'], dr['step']), step=dr['step'], key="cd_max"
496
+ )
497
+ dr.update(min=dmin, max=dmax)
498
+
499
+ dr['step'] = col4.number_input(
500
+ "Step", min_value=0.0,
501
+ max_value=(dmax - dmin) if dmax > dmin else 0.1,
502
+ value=dr['step'], step=0.1, key="cd_step"
503
+ )
504
+
505
+ st.markdown("---")
506
+
507
+ # ─── Printing Parameters Section ────────────────────────────────────────────────
508
+ st.subheader("Printing Parameters (enter range)")
509
+ for name in PRINT_PARAM_NAMES:
510
+ pmin = st.session_state.pp_ranges[name]['min']
511
+ pmax = st.session_state.pp_ranges[name]['max']
512
+ pstep = st.session_state.pp_ranges[name]['step']
513
+
514
+ c1, c2, c3, c4 = st.columns([2,1,1,1])
515
+ c1.write(name)
516
+ pmin = c2.number_input(
517
+ "Min", min_value=0.0, max_value=pmax,
518
+ value=pmin, step=pstep, key=f"pp_min_{name}"
519
+ )
520
+ # ← enforce Max β‰₯ step
521
+ pmax = c3.number_input(
522
+ "Max", min_value=pstep, max_value=10000.0,
523
+ value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
524
+ )
525
+ pstep = c4.number_input(
526
+ "Step", min_value=0.0,
527
+ max_value=(pmax - pmin) if pmax > pmin else 1.0,
528
+ value=pstep, step=max(1e-3, pstep/10),
529
+ key=f"pp_step_{name}"
530
+ )
531
+ st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
532
+
533
+ st.markdown("---")
534
+
535
+ # ─── Optuna Optimize & Display ──────────────────────────────────────────────────
536
+ if st.button("πŸ› οΈ Optimize WSSQ"):
537
+ with st.spinner("Running Optuna…"):
538
+ def objective(trial):
539
+ bi_vals = {
540
+ r['mat']: trial.suggest_float(
541
+ f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
542
+ )
543
+ for r in st.session_state.bio_rows
544
+ }
545
+ for m in BIOMATERIAL_OPTIONS:
546
+ bi_vals.setdefault(m, 0.0)
547
+
548
+ cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
549
+ "cell_density", dr['min'], dr['max'], step=dr['step']
550
+ )
551
+
552
+ pp_vals = {
553
+ name: trial.suggest_float(
554
+ f"pp__{name}",
555
+ st.session_state.pp_ranges[name]['min'],
556
+ st.session_state.pp_ranges[name]['max'],
557
+ step=st.session_state.pp_ranges[name]['step']
558
+ )
559
+ for name in PRINT_PARAM_NAMES
560
+ }
561
+
562
+ feat = {**bi_vals, **pp_vals}
563
+ feat["Cell Density (cells/mL)"] = cd
564
+ feat.update(
565
+ encoder.transform(pd.DataFrame({"Cell Line":[cell_line]}))
566
+ .iloc[0].to_dict()
567
+ )
568
+
569
+ X = pd.DataFrame([feat])
570
+ Xp = X.reindex(columns=feature_order_print, fill_value=0.0)
571
+ Xc = X.reindex(columns=feature_order_cell, fill_value=0.0)
572
+
573
+ p_proba = model_print.predict_proba(scaler_print.transform(Xp))[0]
574
+ c_proba = model_cell .predict_proba(scaler_cell .transform(Xc))[0]
575
+ exp_p = float(np.dot(p_proba, model_print.classes_.astype(float)))
576
+ exp_c = float(np.dot(c_proba, model_cell .classes_.astype(float)))
577
+
578
+ return scaffold_quality_combined(exp_p, exp_c)
579
+
580
+ study = optuna.create_study(direction="maximize")
581
+ study.optimize(objective, n_trials=50)
582
+ best = study.best_trial
583
+
584
+ st.success(f"πŸ† Best WSSQ: **{best.value:.3f}**")
585
+
586
+ best_df = pd.Series(best.params, name="value") \
587
+ .rename_axis("parameter") \
588
+ .to_frame()
589
+ st.table(best_df)
590
+
591
+ # ─── Fabrication Procedure via GPT ───────────────────────────────────────────────
592
+ st.markdown("## 🏭 Fabrication Procedure")
593
+ with st.spinner("Generating fabrication procedure…"):
594
+ density = best.params.get("cell_density", 0.0)
595
+ prompt = (
596
+ "Based on the following optimized scaffold parameters AND your selected cell line:\n\n"
597
+ f"β€’ Cell line: {cell_line}\n"
598
+ "Optimized scaffold parameters:\n"
599
+ f"{best.params}\n\n"
600
+ "Write a detailed, step-by-step fabrication procedure for 3D (bio)printing "
601
+ "this scaffold β€” including materials, equipment, printing settings, and post‑processing."
602
+ )
603
+
604
+ resp = client.models.generate_content(
605
+ model="gemini-2.0-flash-001",
606
+ contents=prompt,
607
+ config=types.GenerateContentConfig(
608
+ system_instruction=(
609
+ "You are a biomedical engineering professor with tissue engineering experties. "
610
+ "you know how to print 3d (bio)printed scaffolds in labs."
611
+ )
612
+ )
613
+ )
614
+
615
+ procedure = resp.text
616
+ st.markdown(procedure)
catboost_cell_response.cbm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8eeb02258c9780147ea7e4030757f3ba7d4d80b7530f74cff79cd3e0481c23e
3
+ size 98406080
catboost_printability.cbm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c0a21e8657aa8d990f62750f9a8342227ba51ec7f02136a8882069ba7a64952
3
+ size 81574688
cell_line_encoder.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c62b2bc4682c3e67f364511201060dea195f93ed9ff90bd930a1443cc3aa73d
3
+ size 25655
scaler_cell_response.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0e8ad571c0a19952513271331ff8140a64eff4325c0b5d11f702ffca2c38a8c9
3
+ size 7703
scaler_printability.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0f5c37a1cb5bd29c1a4fb868808e305545c4930b31a22e25e8e7271e861fe1a4
3
+ size 7703