"""Command-line entry points for data, training, generation, and verification.""" import argparse, json from pathlib import Path import numpy as np from .graph import Graph, toy_graph, grid_graph from .exact import ( solve, sample, endpoint_distribution, expected_cost, uniform_policy, tilted_reference, ) def main(): p = argparse.ArgumentParser(prog="dooable") sub = p.add_subparsers(dest="command", required=True) b = sub.add_parser("build") b.add_argument("--parents", required=True) b.add_argument("--reagents", required=True) b.add_argument("--reactions", required=True) b.add_argument("--budget", type=int, default=2) b.add_argument("--max-nodes", type=int, default=100000) b.add_argument("--parent-limit", type=int) b.add_argument("--output", required=True) t = sub.add_parser("toy") t.add_argument("--kind", choices=["multiplicity", "grid"], default="multiplicity") t.add_argument("--output", required=True) t.add_argument("--budget", type=int, default=6) for name in ["exact", "train"]: a = sub.add_parser(name) a.add_argument("--graph", required=True) a.add_argument("--rewards") a.add_argument("--temperature", type=float, default=1.0) a.add_argument("--output", required=True) a.add_argument("--seed", type=int, default=0) if name == "train": a.add_argument("--steps", type=int, default=2000) a.add_argument("--batch-size", type=int, default=64) a.add_argument( "--backward", choices=["learned", "uniform", "exact", "unnormalized"], default="learned", ) a.add_argument("--resume") a.add_argument("--initialize") a = sub.add_parser("sample") a.add_argument("--model", required=True) a.add_argument("--n", type=int, default=1000) a.add_argument("--seed", type=int, default=0) a.add_argument("--output", required=True) a = sub.add_parser("fit-properties") a.add_argument("--data", default="data/downloads") a.add_argument("--output", required=True) a.add_argument("--seed", type=int, default=0) a = sub.add_parser("score") a.add_argument("--graph", required=True) a.add_argument("--models") a.add_argument("--output", required=True) a = sub.add_parser("replay") a.add_argument("--samples", required=True) a.add_argument("--budget", type=int, required=True) args = p.parse_args() if args.command == "build": from .chemistry import read_catalog, reactions, build_graph g = build_graph( read_catalog(args.parents, args.parent_limit), read_catalog(args.reagents), reactions(args.reactions), args.budget, args.max_nodes, ) g.save(args.output) print( json.dumps( { "nodes": len(g.nodes), "edges": len(g.edges), "outcomes": len(g.terminals), } ) ) return if args.command == "toy": g = toy_graph() if args.kind == "multiplicity" else grid_graph(6, args.budget) g.save(args.output) return if args.command in ["exact", "train", "score"]: g = Graph.load(args.graph) if args.command == "score": Path(args.output).parent.mkdir(parents=True, exist_ok=True) if args.models: from .properties import property_rewards rewards, scores = property_rewards(g, args.models) scores.to_csv(Path(args.output).with_suffix(".csv"), index=False) else: from .chemistry import descriptor_rewards rewards = descriptor_rewards(g) Path(args.output).write_text(json.dumps(rewards, indent=2)) return rewards = ( json.loads(Path(args.rewards).read_text()) if args.rewards else {y: 0.0 for y in g.terminals} ) out = Path(args.output) out.mkdir(parents=True, exist_ok=True) if args.command == "exact": sol = solve(g, rewards, args.temperature) g.save(out / "graph.json") np.save(out / "forward.npy", sol.forward) (out / "solution.json").write_text( json.dumps( { "temperature": args.temperature, "target": sol.target, "log_z": sol.log_z, "expected_cost": expected_cost(g, sol.forward), }, indent=2, ) ) else: from .learning import train _, h = train( g, rewards, args.temperature, args.steps, args.batch_size, seed=args.seed, output=out, backward=args.backward, resume=args.resume, initialize=args.initialize, ) print(json.dumps(h[-1])) return if args.command == "sample": d = Path(args.model) if (d / "forward.npy").exists(): g = Graph.load(d / "graph.json") forward = np.load(d / "forward.npy") else: from .learning import load_model model, _ = load_model(d) g = model.graph forward = model.probabilities() rows = sample(g, forward, args.n, args.seed) Path(args.output).parent.mkdir(parents=True, exist_ok=True) Path(args.output).write_text("".join(json.dumps(r) + "\n" for r in rows)) return if args.command == "fit-properties": from .properties import fit_property print( json.dumps( [ fit_property(n, args.data, args.output, args.seed) for n in ["caco2", "bace"] ], indent=2, ) ) return if args.command == "replay": from .chemistry import replay rows = [json.loads(l) for l in Path(args.samples).read_text().splitlines() if l] passed = sum(replay(r, args.budget) for r in rows) print( json.dumps( { "paths": len(rows), "replayed": passed, "fraction": passed / max(len(rows), 1), } ) ) if passed != len(rows): raise SystemExit(1) if __name__ == "__main__": main()