feat(optimizer): masked MSE, strategy seeds, adversarial IFRA guard; add accords/materials/importer
fd5ba83 | 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") | |