File size: 2,716 Bytes
fd5ba83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
from __future__ import annotations

"""
Analyze a human parts-based formula with Pino.

This is a small utility for the user-supplied Matthew tea/woods formula. It:
1. Imports the parts-based JSON via pino.importer.
2. Runs the physical/IFRA verifier.
3. Runs the PIMT critic (cloud or local fallback).
4. Prints a compact human-readable summary.
"""

import json
import logging
import sys
from pathlib import Path

from pino.importer import import_formula
from pino.inference_cloud import predict_cloud
from pino.verifier import FragrancePipelineVerifier

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
logger = logging.getLogger("pino.analyze_formula")


def analyze(path: str | Path) -> None:
    imported = import_formula(path)
    if imported["unresolved"]:
        logger.warning("Unresolved materials: %s", imported["unresolved"])

    formula = imported["formula"]
    print(f"Imported formula with {len(formula)} ingredients; sum = {sum(i['weight_fraction'] for i in formula):.6f}")

    verifier = FragrancePipelineVerifier(
        temperature_k=298.15,
        ambient_pressure_pa=101325.0,
        headspace_volume_m3=1e-3,
        liquid_volume_m3=1e-6,
        density_g_ml=0.9,
        surface_area_m2=1e-4,
        mass_transfer_coefficient=1e-4,
    )

    result = verifier.run_sim(formula, duration_seconds=8 * 3600.0, interval_seconds=600.0)
    print(f"Physical/IFRA status: {result['status']}")
    print(f"IFRA passed: {result['ifra_report'].get('passed', False)}")
    if not result["ifra_report"].get("passed", False):
        print("IFRA violations:", json.dumps(result["ifra_report"].get("violations", []), indent=2))

    if result["status"] in ("rejected", "depleted"):
        logger.error("Formula rejected by verifier: %s", result.get("message"))
        return

    formula_payload = {
        "formula": formula,
        "trajectory": result["trajectory"],
        "metadata": {"source": str(path), "analysis": True},
    }

    prediction = predict_cloud(formula_payload)
    print("Prediction keys:", list(prediction.keys()))
    print("Objective shape:", len(prediction.get("objective", [[]])[0][0]))
    print("Subjective vector:", prediction.get("subjective", [[]])[0])

    # Save full analysis
    output = {
        "imported": imported,
        "verification": result,
        "prediction": prediction,
    }
    out_path = Path("data/formulas/matthew_tea_woods_analysis.json")
    out_path.write_text(json.dumps(output, indent=2, ensure_ascii=False))
    print(f"Full analysis written to {out_path}")


if __name__ == "__main__":
    analyze(sys.argv[1] if len(sys.argv) > 1 else "data/formulas/matthew_tea_woods.json")