Spaces:
Sleeping
Sleeping
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -175,29 +175,58 @@ def _pyramid_for_ingredient(model, heads, engine, smiles, cas, objective_dim):
|
|
| 175 |
return pyr
|
| 176 |
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
def predict(arm_label, rows, progress=gr.Progress()):
|
| 179 |
-
|
| 180 |
-
if
|
| 181 |
-
|
|
|
|
| 182 |
try:
|
| 183 |
model, heads, engine, resolved, objective_dim = _load_arm(arm_label)
|
| 184 |
except Exception as e:
|
| 185 |
return None, f"Model load failed: {e}"
|
| 186 |
|
| 187 |
mats = {label: (sm, cas) for label, sm, cas in _materials()}
|
| 188 |
-
total_w = sum(
|
| 189 |
blend = np.zeros(objective_dim, dtype=np.float64) # (objective_dim,) tier-summed
|
| 190 |
tier_blend = np.zeros((3, objective_dim), dtype=np.float64)
|
| 191 |
used = []
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
if label not in mats
|
|
|
|
| 195 |
continue
|
| 196 |
sm, cas = mats[label]
|
| 197 |
pyr = _pyramid_for_ingredient(model, heads, engine, sm, cas, objective_dim) # (3, D)
|
| 198 |
w = conc / total_w
|
| 199 |
tier_blend += w * pyr
|
| 200 |
used.append(label.split(" [")[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
# Tier masses = perceived intensity over dry-down
|
| 203 |
tier_mass = tier_blend.sum(axis=1) # (3,)
|
|
|
|
| 175 |
return pyr
|
| 176 |
|
| 177 |
|
| 178 |
+
def _row_pair(r):
|
| 179 |
+
"""Extract (label, conc) from a Dataframe row regardless of shape.
|
| 180 |
+
|
| 181 |
+
Gradio 6 may deliver rows as lists, tuples, numpy arrays, or dicts keyed by
|
| 182 |
+
header name. Normalise to (str label, float conc); return ("", 0.0) on junk.
|
| 183 |
+
"""
|
| 184 |
+
try:
|
| 185 |
+
if isinstance(r, dict):
|
| 186 |
+
vals = list(r.values())
|
| 187 |
+
label = vals[0] if vals else ""
|
| 188 |
+
conc = vals[1] if len(vals) > 1 else 0
|
| 189 |
+
else:
|
| 190 |
+
label = r[0]
|
| 191 |
+
conc = r[1] if len(r) > 1 else 0
|
| 192 |
+
label = "" if label is None else str(label)
|
| 193 |
+
if hasattr(conc, "item"): # numpy scalar
|
| 194 |
+
conc = conc.item()
|
| 195 |
+
conc = float(conc) if conc not in (None, "") else 0.0
|
| 196 |
+
return label, conc
|
| 197 |
+
except (TypeError, ValueError, IndexError):
|
| 198 |
+
return "", 0.0
|
| 199 |
+
|
| 200 |
+
|
| 201 |
def predict(arm_label, rows, progress=gr.Progress()):
|
| 202 |
+
pairs = [_row_pair(r) for r in (rows or [])]
|
| 203 |
+
pairs = [(l, c) for l, c in pairs if l and c > 0]
|
| 204 |
+
if not pairs:
|
| 205 |
+
return None, "Add at least one ingredient (with a concentration > 0)."
|
| 206 |
try:
|
| 207 |
model, heads, engine, resolved, objective_dim = _load_arm(arm_label)
|
| 208 |
except Exception as e:
|
| 209 |
return None, f"Model load failed: {e}"
|
| 210 |
|
| 211 |
mats = {label: (sm, cas) for label, sm, cas in _materials()}
|
| 212 |
+
total_w = sum(c for _, c in pairs) or 1.0
|
| 213 |
blend = np.zeros(objective_dim, dtype=np.float64) # (objective_dim,) tier-summed
|
| 214 |
tier_blend = np.zeros((3, objective_dim), dtype=np.float64)
|
| 215 |
used = []
|
| 216 |
+
skipped = []
|
| 217 |
+
for label, conc in progress.tqdm(pairs, desc="Encoding"):
|
| 218 |
+
if label not in mats:
|
| 219 |
+
skipped.append(label.split(" [")[0])
|
| 220 |
continue
|
| 221 |
sm, cas = mats[label]
|
| 222 |
pyr = _pyramid_for_ingredient(model, heads, engine, sm, cas, objective_dim) # (3, D)
|
| 223 |
w = conc / total_w
|
| 224 |
tier_blend += w * pyr
|
| 225 |
used.append(label.split(" [")[0])
|
| 226 |
+
if not used:
|
| 227 |
+
return None, ("None of the selected ingredients matched the catalog. "
|
| 228 |
+
"Pick ingredients from the Quick-add list." +
|
| 229 |
+
(f" Unmatched: {', '.join(skipped[:5])}" if skipped else ""))
|
| 230 |
|
| 231 |
# Tier masses = perceived intensity over dry-down
|
| 232 |
tier_mass = tier_blend.sum(axis=1) # (3,)
|