| import sys |
| from pathlib import Path |
| import json |
| import numpy as np |
| import torch |
|
|
| sys.path.insert(0, "src") |
| sys.path.insert(0, "scripts") |
|
|
| from predict_shalimar import ( |
| build_model_inputs, |
| aggregate_physics_for_tokens, |
| expand_naturals_for_vle, |
| map_ingredients_to_cas, |
| parse_formula, |
| load_pimt_checkpoint, |
| HARDCODED_CAS, |
| ) |
| from pino.registry import AromaRegistry |
| from pino.embeddings import OlfactoryEmbeddingEngine |
| from pino.verifier import FragrancePipelineVerifier |
|
|
|
|
| def main(): |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| pdf_path = "/home/hermes/.hermes/cache/documents/doc_fa4254a05dd7_SHALIMAR_1990.pdf" |
|
|
| import fitz |
|
|
| doc = fitz.open(pdf_path) |
| text = "\n".join(page.get_text() for page in doc) |
| doc.close() |
|
|
| formula = parse_formula(text) |
| registry = AromaRegistry() |
| mapped = map_ingredients_to_cas(formula, registry) |
|
|
| semantic_recipe = [] |
| total = 0.0 |
| for name, (cas, qty) in mapped.items(): |
| if "dipropylene glycol" in name.lower() or not cas: |
| continue |
| semantic_recipe.append({"cas": cas, "weight_fraction": qty}) |
| total += qty |
| for item in semantic_recipe: |
| item["weight_fraction"] /= total |
|
|
| vle_recipe = expand_naturals_for_vle(semantic_recipe) |
| verifier = FragrancePipelineVerifier() |
| result = verifier.run_sim(vle_recipe, duration_seconds=8 * 3600, interval_seconds=600, skip_ifra=True) |
| if result["status"] != "passed": |
| print("Simulation failed") |
| return |
|
|
| engine = OlfactoryEmbeddingEngine() |
| tokens = build_model_inputs(semantic_recipe, registry, engine).to(device) |
| physics, _ = aggregate_physics_for_tokens(result["trajectory"], semantic_recipe) |
| physics_t = torch.from_numpy(physics).unsqueeze(0).float().to(device) |
| mask = torch.zeros(1, tokens.size(1), dtype=torch.bool, device=device) |
|
|
| model, heads, _ = load_pimt_checkpoint() |
| model.to(device) |
| heads.to(device) |
|
|
| activations = {} |
|
|
| def make_hook(name): |
| def fn(m, i, o): |
| activations[name] = o.detach() if not isinstance(o, tuple) else o[0].detach() |
| return fn |
|
|
| model.gating.register_forward_hook(make_hook("film")) |
| model.encoder.register_forward_hook(make_hook("encoder")) |
|
|
| with torch.no_grad(): |
| latent = model(tokens, physics_t, src_key_padding_mask=mask) |
| output = heads(latent) |
|
|
| print(f"tokens shape: {tokens.shape}") |
| print(f"physics shape: {physics_t.shape}") |
| print(f"physics per-channel mean time-variance: {physics_t[0].var(dim=0).mean(dim=0).cpu().numpy()}") |
| print(f"latent shape: {latent.shape}") |
| print(f"latent mean per-token time-variance: {latent[0].var(dim=0).mean().item():.6e}") |
| print(f"latent max per-token time-variance: {latent[0].var(dim=0).max().item():.6e}") |
| for k in ["film", "encoder"]: |
| v = activations[k] |
| print(f"{k} shape: {v.shape}") |
| print(f" mean per-token time-variance: {v[0].var(dim=0).mean().item():.6e}") |
| print(f" max per-token time-variance: {v[0].var(dim=0).max().item():.6e}") |
| obj = output["objective"] |
| print(f"objective shape: {obj.shape}") |
| print(f" mean per-dim time-variance: {obj[0].var(dim=0).mean().item():.6e}") |
| print(f" max per-dim time-variance: {obj[0].var(dim=0).max().item():.6e}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|