dsh-eval-guard 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/ISSUES.md +5 -1
  2. package/README.md +4 -2
  3. package/assets/neural-pendulum/SPEC.md +4 -3
  4. package/assets/neural-pendulum/TASK.md +4 -3
  5. package/assets/neural-pendulum/rl-feasibility/README.md +60 -0
  6. package/assets/neural-pendulum/rl-feasibility/RESULTS.md +370 -0
  7. package/assets/neural-pendulum/rl-feasibility/evaluate.py +107 -0
  8. package/assets/neural-pendulum/rl-feasibility/finalize_run.py +168 -0
  9. package/assets/neural-pendulum/rl-feasibility/models.py +124 -0
  10. package/assets/neural-pendulum/rl-feasibility/obs.py +205 -0
  11. package/assets/neural-pendulum/rl-feasibility/plot_selected.py +55 -0
  12. package/assets/neural-pendulum/rl-feasibility/reference_controller.py +112 -0
  13. package/assets/neural-pendulum/rl-feasibility/results/curves_selected.png +0 -0
  14. package/assets/neural-pendulum/rl-feasibility/results/reference_controller.json +417 -0
  15. package/assets/neural-pendulum/rl-feasibility/results/summary.json +1599 -0
  16. package/assets/neural-pendulum/rl-feasibility/results/table.md +39 -0
  17. package/assets/neural-pendulum/rl-feasibility/sim.py +324 -0
  18. package/assets/neural-pendulum/rl-feasibility/summarize.py +175 -0
  19. package/assets/neural-pendulum/rl-feasibility/task.py +342 -0
  20. package/assets/neural-pendulum/rl-feasibility/test_contract.py +188 -0
  21. package/assets/neural-pendulum/rl-feasibility/train_ppo.py +403 -0
  22. package/assets/neural-pendulum/rl-feasibility/train_sac.py +406 -0
  23. package/assets/neural-pendulum/sim.cpp +13 -2
  24. package/assets/neural-pendulum/sim_eval +0 -0
  25. package/assets/neural-pendulum/workspace.zip +0 -0
  26. package/lib/index.js +5 -1
  27. package/native/dsh-eval-supervisor +0 -0
  28. package/native/supervisor.c +42 -10
  29. package/neural-pendulum-eval/sim_eval +0 -0
  30. package/package.json +2 -2
  31. package/eval/__pycache__/bomberman.cpython-310.pyc +0 -0
  32. package/eval/__pycache__/commit0.cpython-310.pyc +0 -0
  33. package/eval/__pycache__/common.cpython-310.pyc +0 -0
  34. package/eval/__pycache__/ctf.cpython-310.pyc +0 -0
  35. package/eval/__pycache__/featurebench.cpython-310.pyc +0 -0
  36. package/eval/__pycache__/land.cpython-310.pyc +0 -0
  37. package/eval/__pycache__/neural-cifar.cpython-310.pyc +0 -0
  38. package/eval/__pycache__/neural-cpp.cpython-310.pyc +0 -0
  39. package/eval/__pycache__/neural-fp32.cpython-310.pyc +0 -0
  40. package/eval/__pycache__/neural-pendulum.cpython-310.pyc +0 -0
  41. package/eval/__pycache__/swe-bench-pro.cpython-310.pyc +0 -0
  42. package/eval/__pycache__/tank.cpython-310.pyc +0 -0
  43. package/eval/__pycache__/warehouse.cpython-310.pyc +0 -0
  44. package/eval/legacy/__pycache__/bomberman.cpython-310.pyc +0 -0
  45. package/eval/legacy/__pycache__/commit0.cpython-310.pyc +0 -0
  46. package/eval/legacy/__pycache__/ctf.cpython-310.pyc +0 -0
  47. package/eval/legacy/__pycache__/featurebench.cpython-310.pyc +0 -0
  48. package/eval/legacy/__pycache__/land.cpython-310.pyc +0 -0
  49. package/eval/legacy/__pycache__/swe_bench_pro.cpython-310.pyc +0 -0
  50. package/eval/legacy/__pycache__/tank.cpython-310.pyc +0 -0
  51. package/eval/legacy/__pycache__/warehouse.cpython-310.pyc +0 -0
  52. package/neural-cifar-eval/__pycache__/cifar_cache.cpython-310.pyc +0 -0
  53. package/neural-cifar-eval/__pycache__/common.cpython-310.pyc +0 -0
  54. package/neural-cifar-eval/__pycache__/fetch_cifar.cpython-310.pyc +0 -0
  55. package/neural-cifar-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
  56. package/neural-cifar-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
  57. package/neural-pendulum-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
  58. package/neural-pendulum-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
@@ -0,0 +1,403 @@
1
+ """PPO + 2x128 tanh MLP + sin/cos H=4 observation, angle-only simulator.
2
+
3
+ Experiments:
4
+ --init rand : every training reset draws theta_i ~ U(-.05,.05) and
5
+ omega_i ~ U(-.02,.02), with no fixed bias.
6
+ --init fixed : every training reset is exactly the evaluation state
7
+ theta = [0.013,-0.008,0.021], omega = 0.
8
+
9
+ The simulator returns only theta and raises EpisodeLengthError after 3000
10
+ steps. Everything else -- observation, reward, done, success, t_can, return
11
+ -- is built here and in task.py, never in the simulator.
12
+
13
+ TensorBoard scalars (tb/<exp>/):
14
+ train/episode_return, train/episode_length, train/t_can, train/t_can_max,
15
+ train/success_rate, train/ever_up_frac, train/first_up_time,
16
+ train/mean_step_reward, train/action_abs_mean, train/std, train/lr,
17
+ train/approx_kl, train/clip_frac,
18
+ loss/policy, loss/value, loss/entropy,
19
+ eval/t_can, eval/success, eval/ep_return, eval/ever_up, eval/final_ang_err,
20
+ eval/first_up_t, eval/up_frac, eval_final/*,
21
+ train/global_steps, time/elapsed_min
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import json
27
+ import os
28
+ import time
29
+
30
+ import numpy as np
31
+ import torch
32
+ import torch.nn as nn
33
+ from torch.utils.tensorboard import SummaryWriter
34
+
35
+ torch.set_num_threads(int(os.environ.get("TORCH_THREADS", "4")))
36
+
37
+ from evaluate import evaluate, evaluate_starts, make_ppo_policy
38
+ from models import ActorCritic
39
+ from obs import OBS_MODES, obs_spec
40
+ from sim import EPISODE_STEPS, Params, TorchVecSim
41
+ from task import (EVAL_THETA0, TORCH_REWARD_NOTE, TorchTask,
42
+ get_reward_preset, make_init, sample_train_init)
43
+
44
+
45
+ def collect(sim, task, ac, T, args, gen, ep_stats):
46
+ B = sim.n
47
+ obs = task.obs
48
+ obs_buf = torch.zeros(T, B, obs.shape[-1])
49
+ act_buf = torch.zeros(T, B, 2)
50
+ logp_buf = torch.zeros(T, B)
51
+ rew_buf = torch.zeros(T, B)
52
+ done_buf = torch.zeros(T, B)
53
+ val_buf = torch.zeros(T, B)
54
+ for t in range(T):
55
+ with torch.no_grad():
56
+ dist = ac.dist(obs)
57
+ a = dist.sample()
58
+ logp = dist.log_prob(a).sum(-1)
59
+ v = ac.value(obs)
60
+ theta = sim.step(a)
61
+ nobs, rew = task.step(theta, a)
62
+ done = sim.steps >= sim.episode_steps
63
+ obs_buf[t] = obs
64
+ act_buf[t] = a
65
+ logp_buf[t] = logp
66
+ rew_buf[t] = rew
67
+ done_buf[t] = done.float()
68
+ val_buf[t] = v
69
+ if bool(done.any()):
70
+ idx = done.nonzero().flatten()
71
+ summ = task.summary(idx)
72
+ ep_stats["return"] += summ["ep_return"].tolist()
73
+ ep_stats["t_can"] += summ["t_can"].tolist()
74
+ ep_stats["ever_up"] += summ["ever_up"].tolist()
75
+ ep_stats["first_up"] += summ["first_up_t"].tolist()
76
+ ep_stats["up_frac"] += summ["up_frac"].tolist()
77
+ ep_stats["length"] += summ["length"].tolist()
78
+ th, om = make_init(idx.numel(), args.init, gen, up_frac=args.up_frac)
79
+ reset_theta = sim.reset(th, om, idx)
80
+ task.reset(reset_theta, idx)
81
+ nobs = task.obs
82
+ obs = nobs
83
+ with torch.no_grad():
84
+ last_v = ac.value(obs)
85
+ return dict(obs=obs_buf, act=act_buf, logp=logp_buf, rew=rew_buf,
86
+ done=done_buf, val=val_buf, last_v=last_v, last_obs=obs)
87
+
88
+
89
+ def gae(rb, vb, db, last_v, gamma, lam):
90
+ T, B = rb.shape
91
+ adv = torch.zeros(T, B)
92
+ last = torch.zeros(B)
93
+ for t in reversed(range(T)):
94
+ next_v = last_v if t == T - 1 else vb[t + 1]
95
+ nonterm = 1.0 - db[t]
96
+ delta = rb[t] + gamma * next_v * nonterm - vb[t]
97
+ last = delta + gamma * lam * nonterm * last
98
+ adv[t] = last
99
+ return adv, adv + vb
100
+
101
+
102
+ def ppo_update(ac, opt, data, args):
103
+ T, B = data["rew"].shape
104
+ obs = data["obs"].reshape(T * B, -1)
105
+ act = data["act"].reshape(T * B, ac.act_dim)
106
+ old_logp = data["logp"].reshape(T * B)
107
+ adv = data["adv"].reshape(T * B)
108
+ ret = data["ret"].reshape(T * B)
109
+ adv = (adv - adv.mean()) / (adv.std() + 1e-8)
110
+ n = obs.shape[0]
111
+ stats = dict(pg=[], v=[], ent=[], kl=[], cf=[])
112
+ stop = False
113
+ ep = 0
114
+ for ep in range(args.epochs):
115
+ idx = torch.randperm(n)
116
+ for s in range(0, n, args.minibatch):
117
+ mb = idx[s:s + args.minibatch]
118
+ dist = ac.dist(obs[mb])
119
+ logp = dist.log_prob(act[mb]).sum(-1)
120
+ ratio = (logp - old_logp[mb]).exp()
121
+ a = adv[mb]
122
+ pg = torch.max(-a * ratio,
123
+ -a * torch.clamp(ratio, 1 - args.clip, 1 + args.clip)).mean()
124
+ v = ac.value(obs[mb])
125
+ v_loss = 0.5 * ((v - ret[mb]) ** 2).mean()
126
+ ent = dist.entropy().sum(-1).mean()
127
+ loss = pg + args.vf_coef * v_loss - args.ent * ent
128
+ opt.zero_grad()
129
+ loss.backward()
130
+ nn.utils.clip_grad_norm_(ac.parameters(), 0.5)
131
+ opt.step()
132
+ with torch.no_grad():
133
+ kl = ((ratio - 1) - (logp - old_logp[mb])).mean().item()
134
+ cf = ((ratio - 1).abs() > args.clip).float().mean().item()
135
+ stats["pg"].append(pg.item()); stats["v"].append(v_loss.item())
136
+ stats["ent"].append(ent.item()); stats["kl"].append(kl)
137
+ stats["cf"].append(cf)
138
+ if kl > 2.0 * args.target_kl:
139
+ stop = True
140
+ break
141
+ if stop:
142
+ break
143
+ out = {k: float(np.mean(v)) for k, v in stats.items()}
144
+ out["epochs"] = ep + 1
145
+ return out
146
+
147
+
148
+ def main():
149
+ ap = argparse.ArgumentParser()
150
+ ap.add_argument("--exp", type=str, required=True)
151
+ ap.add_argument("--seed", type=int, default=0)
152
+ ap.add_argument("--init", type=str, default="rand",
153
+ choices=["rand", "fixed", "mix"],
154
+ help="rand=down noise, fixed=eval state, "
155
+ "mix=curriculum with up_frac near upright")
156
+ ap.add_argument("--up-frac", type=float, default=0.5,
157
+ help="fraction of resets near upright when --init mix")
158
+ ap.add_argument("--obs", type=str, default="full",
159
+ choices=sorted(OBS_MODES),
160
+ help="observation mode (see obs.py)")
161
+ ap.add_argument("--reward", type=str, default="base",
162
+ choices=["base", "sparse", "energy"],
163
+ help="reward preset (see task.py REWARD_PRESETS)")
164
+ ap.add_argument("--envs", type=int, default=64)
165
+ ap.add_argument("--horizon", type=int, default=600)
166
+ ap.add_argument("--minutes", type=float, default=25.0)
167
+ ap.add_argument("--iters", type=int, default=10 ** 9)
168
+ ap.add_argument("--lr", type=float, default=3e-4)
169
+ ap.add_argument("--gamma", type=float, default=0.99)
170
+ ap.add_argument("--lam", type=float, default=0.95)
171
+ ap.add_argument("--clip", type=float, default=0.2)
172
+ ap.add_argument("--epochs", type=int, default=8)
173
+ ap.add_argument("--minibatch", type=int, default=2048)
174
+ ap.add_argument("--ent", type=float, default=0.003)
175
+ ap.add_argument("--vf-coef", type=float, default=0.5)
176
+ ap.add_argument("--target-kl", type=float, default=0.03)
177
+ ap.add_argument("--hidden", type=int, default=128)
178
+ ap.add_argument("--depth", type=int, default=2)
179
+ ap.add_argument("--episode-steps", type=int, default=EPISODE_STEPS)
180
+ ap.add_argument("--eval-every", type=int, default=5)
181
+ ap.add_argument("--tb-root", type=str, default="tb")
182
+ ap.add_argument("--ckpt-dir", type=str, default="checkpoints")
183
+ ap.add_argument("--out", type=str, default="results")
184
+ ap.add_argument("--tau-max", type=float, default=18.0)
185
+ ap.add_argument("--damping", type=float, default=0.01)
186
+ ap.add_argument("--log-every", type=int, default=1)
187
+ ap.add_argument("--robust-starts", type=int, default=8,
188
+ help="deterministic extra rollouts from the training "
189
+ "reset distribution at the end (0 disables)")
190
+ args = ap.parse_args()
191
+
192
+ torch.manual_seed(args.seed)
193
+ np.random.seed(args.seed)
194
+ for d in (args.tb_root, args.ckpt_dir, args.out):
195
+ os.makedirs(d, exist_ok=True)
196
+ exp = args.exp
197
+ writer = SummaryWriter(os.path.join(args.tb_root, exp))
198
+ p = Params(tau_max=args.tau_max, b=args.damping)
199
+ spec = obs_spec(args.obs)
200
+ obs_dim = spec["dim"]
201
+ reward_w = get_reward_preset(args.reward)
202
+ sim = TorchVecSim(p, n=args.envs, episode_steps=args.episode_steps)
203
+ task = TorchTask(p, n=args.envs, reward_w=reward_w, obs_mode=args.obs)
204
+ gen = torch.Generator().manual_seed(args.seed * 1000 + 7)
205
+
206
+ init_meta = dict(mode=args.init,
207
+ training=("theta_i ~ U(-0.05,0.05), omega_i ~ U(-0.02,0.02); no bias"
208
+ if args.init == "rand" else
209
+ ("theta=[0.013,-0.008,0.021], omega=0 (same as eval)"
210
+ if args.init == "fixed" else
211
+ f"curriculum: {args.up_frac:.2f} near upright "
212
+ f"(pi + U(-0.05,0.05)), rest near down")),
213
+ eval="theta=[0.013,-0.008,0.021], omega=0 (fixed, no noise)")
214
+ meta = dict(algo="ppo", exp=exp, seed=args.seed, envs=args.envs,
215
+ horizon=args.horizon, obs=spec, obs_dim=obs_dim,
216
+ init=init_meta, reward=reward_w, reward_preset=args.reward,
217
+ reward_note=TORCH_REWARD_NOTE, args=vars(args))
218
+ writer.add_text("config", json.dumps(meta, indent=2), 0)
219
+ print(json.dumps(meta, indent=2), flush=True)
220
+
221
+ ac = ActorCritic(obs_dim, act_dim=2, hidden=args.hidden, depth=args.depth)
222
+ opt = torch.optim.Adam(ac.parameters(), lr=args.lr, eps=1e-5)
223
+ sched = torch.optim.lr_scheduler.LinearLR(opt, start_factor=1.0,
224
+ end_factor=0.2, total_iters=100000)
225
+
226
+ th, om = make_init(args.envs, args.init, gen, up_frac=args.up_frac)
227
+ theta = sim.reset(th, om)
228
+ task.reset(theta)
229
+
230
+ t_start = time.time()
231
+ global_steps = 0
232
+ it = 0
233
+ best_t_can = -1.0
234
+ best_eval = None
235
+ best_path = None
236
+ last_path = None
237
+ eval_curve = []
238
+ last = {}
239
+ ep_stats = {k: [] for k in ("return", "t_can", "ever_up", "first_up",
240
+ "up_frac", "length")}
241
+
242
+ def save_ckpt(path, ev):
243
+ torch.save(dict(model=ac.state_dict(), opt=opt.state_dict(),
244
+ obs_dim=obs_dim, obs=spec, init=init_meta,
245
+ reward=reward_w, reward_preset=args.reward,
246
+ args=vars(args),
247
+ iter=it, global_steps=global_steps, eval=ev,
248
+ algo="ppo"), os.path.join(args.ckpt_dir, path))
249
+
250
+ def log_eval(ev, tag=""):
251
+ nonlocal best_t_can, best_eval, best_path
252
+ eval_curve.append(dict(iter=it, global_steps=global_steps, **ev))
253
+ for k, v in ev.items():
254
+ if isinstance(v, (int, float)):
255
+ writer.add_scalar(f"eval/{k}", v, global_steps)
256
+ if (ev["t_can"] > best_t_can
257
+ or (ev["t_can"] == best_t_can and best_eval is not None
258
+ and ev["ep_return"] > best_eval["ep_return"])):
259
+ best_t_can = ev["t_can"]
260
+ best_eval = dict(ev)
261
+ best_path = exp + "_best.pt"
262
+ save_ckpt(best_path, ev)
263
+
264
+ while it < args.iters:
265
+ if (time.time() - t_start) / 60.0 > args.minutes:
266
+ print("time budget reached", flush=True)
267
+ break
268
+ data = collect(sim, task, ac, args.horizon, args, gen, ep_stats)
269
+ adv, ret = gae(data["rew"], data["val"], data["done"], data["last_v"],
270
+ args.gamma, args.lam)
271
+ data["adv"], data["ret"] = adv, ret
272
+ stats = ppo_update(ac, opt, data, args)
273
+ sched.step()
274
+ global_steps += args.horizon * args.envs
275
+ it += 1
276
+ last = stats
277
+ mean_train_ret = (float(np.mean(ep_stats["return"]))
278
+ if ep_stats["return"] else float("nan"))
279
+
280
+ if ep_stats["return"]:
281
+ writer.add_scalar("train/episode_return",
282
+ float(np.mean(ep_stats["return"])), global_steps)
283
+ writer.add_scalar("train/episode_length",
284
+ float(np.mean(ep_stats["length"])), global_steps)
285
+ writer.add_scalar("train/t_can", float(np.mean(ep_stats["t_can"])),
286
+ global_steps)
287
+ writer.add_scalar("train/t_can_max", float(np.max(ep_stats["t_can"])),
288
+ global_steps)
289
+ writer.add_scalar("train/success_rate",
290
+ float(np.mean(np.asarray(ep_stats["t_can"]) >= 10.0)),
291
+ global_steps)
292
+ writer.add_scalar("train/ever_up_frac",
293
+ float(np.mean(ep_stats["ever_up"])), global_steps)
294
+ writer.add_scalar("train/up_frac",
295
+ float(np.mean(ep_stats["up_frac"])), global_steps)
296
+ fu = [x for x in ep_stats["first_up"] if x > 0]
297
+ if fu:
298
+ writer.add_scalar("train/first_up_time", float(np.mean(fu)),
299
+ global_steps)
300
+ ep_stats = {k: [] for k in ep_stats}
301
+ writer.add_scalar("train/mean_step_reward", float(data["rew"].mean()), global_steps)
302
+ writer.add_scalar("train/action_abs_mean", float(data["act"].abs().mean()), global_steps)
303
+ writer.add_scalar("train/action_std", float(data["act"].std()), global_steps)
304
+ writer.add_scalar("train/std", float(ac.effective_log_std().exp().mean()),
305
+ global_steps)
306
+ writer.add_scalar("train/lr", opt.param_groups[0]["lr"], global_steps)
307
+ writer.add_scalar("train/approx_kl", stats["kl"], global_steps)
308
+ writer.add_scalar("train/clip_frac", stats["cf"], global_steps)
309
+ writer.add_scalar("train/global_steps", global_steps, global_steps)
310
+ writer.add_scalar("loss/policy", stats["pg"], global_steps)
311
+ writer.add_scalar("loss/value", stats["v"], global_steps)
312
+ writer.add_scalar("loss/entropy", stats["ent"], global_steps)
313
+ writer.add_scalar("time/elapsed_min", (time.time() - t_start) / 60.0,
314
+ global_steps)
315
+
316
+ if it % args.eval_every == 0 or it == 1:
317
+ ev = evaluate(make_ppo_policy(ac.actor), p, args.episode_steps,
318
+ reward_w=reward_w, obs_mode=args.obs)
319
+ log_eval(ev)
320
+ last_path = exp + "_last.pt"
321
+ save_ckpt(last_path, ev)
322
+ print(f"iter {it:4d} steps {global_steps/1e6:5.2f}M "
323
+ f"train_ret {mean_train_ret:8.1f} "
324
+ f"EVAL tc {ev['t_can']:6.2f} succ {ev['success']:.0f} "
325
+ f"everup {ev['ever_up']:.0f} angerr {ev['final_ang_err']:.3f} "
326
+ f"ret {ev['ep_return']:8.1f} pi {stats['pg']:+.3f} "
327
+ f"v {stats['v']:.2f} ent {stats['ent']:.2f} kl {stats['kl']:.3f} "
328
+ f"({time.time()-t_start:.0f}s)", flush=True)
329
+ elif it % args.log_every == 0:
330
+ print(f"iter {it:4d} steps {global_steps/1e6:5.2f}M "
331
+ f"train_ret {mean_train_ret:8.1f} "
332
+ f"pi {stats['pg']:+.3f} v {stats['v']:.2f} ent {stats['ent']:.2f} "
333
+ f"kl {stats['kl']:.3f} ({time.time()-t_start:.0f}s)", flush=True)
334
+ writer.flush()
335
+
336
+ ev_final = evaluate(make_ppo_policy(ac.actor), p, args.episode_steps,
337
+ reward_w=reward_w, obs_mode=args.obs)
338
+ eval_curve.append(dict(iter=it, global_steps=global_steps, **ev_final))
339
+ for k, v in ev_final.items():
340
+ if isinstance(v, (int, float)):
341
+ writer.add_scalar(f"eval_final/{k}", v, global_steps)
342
+ final_path = exp + "_final.pt"
343
+ save_ckpt(final_path, ev_final)
344
+ last_path = exp + "_last.pt"
345
+ save_ckpt(last_path, ev_final)
346
+
347
+ # Extra deterministic rollouts from the training reset distribution. This
348
+ # is NOT the mandated fixed-state eval; it checks generalisation.
349
+ rob = None
350
+ rob_best = None
351
+ if args.robust_starts > 0:
352
+ g = torch.Generator().manual_seed(args.seed * 1000 + 123)
353
+ th_r, om_r = sample_train_init(args.robust_starts, gen=g)
354
+ starts = list(zip(th_r.numpy(), om_r.numpy()))
355
+ rob = evaluate_starts(make_ppo_policy(ac.actor), p, starts,
356
+ args.episode_steps, reward_w=reward_w,
357
+ obs_mode=args.obs)
358
+ for k, v in rob.items():
359
+ if isinstance(v, (int, float)):
360
+ writer.add_scalar(f"robust/{k}", v, global_steps)
361
+ if best_path is not None:
362
+ ck = torch.load(os.path.join(args.ckpt_dir, best_path),
363
+ map_location="cpu")
364
+ ac_best = ActorCritic(obs_dim, act_dim=2, hidden=args.hidden,
365
+ depth=args.depth)
366
+ ac_best.load_state_dict(ck["model"])
367
+ rob_best = evaluate_starts(make_ppo_policy(ac_best.actor), p, starts,
368
+ args.episode_steps, reward_w=reward_w,
369
+ obs_mode=args.obs)
370
+ for k, v in rob_best.items():
371
+ if isinstance(v, (int, float)):
372
+ writer.add_scalar(f"robust_best/{k}", v, global_steps)
373
+
374
+ wall = time.time() - t_start
375
+ out = dict(algo="ppo", exp=exp, seed=args.seed, envs=args.envs,
376
+ horizon=args.horizon, obs=spec, obs_dim=obs_dim,
377
+ init=init_meta, reward=reward_w, reward_preset=args.reward,
378
+ reward_note=TORCH_REWARD_NOTE, args=vars(args),
379
+ minutes_budget=args.minutes, global_steps=global_steps,
380
+ iters=it, wall_time_s=wall,
381
+ steps_per_s=global_steps / max(wall, 1e-9),
382
+ final_eval=ev_final, best_eval=best_eval,
383
+ best_t_can=best_t_can, eval_curve=eval_curve,
384
+ robust=rob, robust_best=rob_best,
385
+ final_train=dict(policy_loss=last.get("pg"),
386
+ value_loss=last.get("v"),
387
+ entropy=last.get("ent"),
388
+ approx_kl=last.get("kl")),
389
+ checkpoints=dict(best=best_path, last=last_path,
390
+ final=final_path))
391
+ with open(os.path.join(args.out, exp + ".json"), "w") as f:
392
+ json.dump(out, f, indent=2)
393
+ writer.flush()
394
+ writer.close()
395
+ print("DONE " + json.dumps(dict(
396
+ exp=exp, init=args.init, seed=args.seed, global_steps=global_steps,
397
+ wall_s=round(wall, 1), steps_per_s=round(out["steps_per_s"], 1),
398
+ eval_t_can=ev_final["t_can"], best_t_can=best_t_can,
399
+ eval_return=ev_final["ep_return"])), flush=True)
400
+
401
+
402
+ if __name__ == "__main__":
403
+ main()