DooABLe / src /dooable /cli.py
pranamanam's picture
Upload 309 files
81ae663 verified
Raw
History Blame Contribute Delete
6.71 kB
"""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()