dsh-eval-guard 0.7.2 → 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.
- package/ISSUES.md +239 -0
- package/README.md +48 -18
- package/assets/neural-cifar/workspace.zip +0 -0
- package/assets/neural-cpp/workspace.zip +0 -0
- package/assets/neural-fp32/workspace.zip +0 -0
- package/assets/neural-pendulum/SPEC.md +43 -35
- package/assets/neural-pendulum/TASK.md +139 -33
- package/assets/neural-pendulum/rl-feasibility/README.md +60 -0
- package/assets/neural-pendulum/rl-feasibility/RESULTS.md +370 -0
- package/assets/neural-pendulum/rl-feasibility/evaluate.py +107 -0
- package/assets/neural-pendulum/rl-feasibility/finalize_run.py +168 -0
- package/assets/neural-pendulum/rl-feasibility/models.py +124 -0
- package/assets/neural-pendulum/rl-feasibility/obs.py +205 -0
- package/assets/neural-pendulum/rl-feasibility/plot_selected.py +55 -0
- package/assets/neural-pendulum/rl-feasibility/reference_controller.py +112 -0
- package/assets/neural-pendulum/rl-feasibility/results/curves_selected.png +0 -0
- package/assets/neural-pendulum/rl-feasibility/results/reference_controller.json +417 -0
- package/assets/neural-pendulum/rl-feasibility/results/summary.json +1599 -0
- package/assets/neural-pendulum/rl-feasibility/results/table.md +39 -0
- package/assets/neural-pendulum/rl-feasibility/sim.py +324 -0
- package/assets/neural-pendulum/rl-feasibility/summarize.py +175 -0
- package/assets/neural-pendulum/rl-feasibility/task.py +342 -0
- package/assets/neural-pendulum/rl-feasibility/test_contract.py +188 -0
- package/assets/neural-pendulum/rl-feasibility/train_ppo.py +403 -0
- package/assets/neural-pendulum/rl-feasibility/train_sac.py +406 -0
- package/assets/neural-pendulum/sim.cpp +13 -2
- package/assets/neural-pendulum/sim_eval +0 -0
- package/assets/neural-pendulum/workspace.zip +0 -0
- package/eval/README.md +19 -9
- package/eval/bomberman.py +5 -3
- package/eval/commit0.py +10 -3
- package/eval/common.py +169 -5
- package/eval/ctf.py +4 -2
- package/eval/featurebench.py +12 -8
- package/eval/land.py +4 -2
- package/eval/legacy/bomberman.py +5 -2
- package/eval/legacy/ctf.py +5 -2
- package/eval/legacy/featurebench.py +55 -15
- package/eval/legacy/land.py +6 -3
- package/eval/legacy/swe_bench_pro.py +57 -16
- package/eval/legacy/tank.py +2 -2
- package/eval/legacy/warehouse.py +4 -4
- package/eval/neural-cpp.py +9 -6
- package/eval/neural-fp32.py +17 -5
- package/eval/swe-bench-pro.py +11 -7
- package/eval/tank.py +11 -2
- package/eval/warehouse.py +17 -1
- package/lib/client/index.js +56 -29
- package/lib/client/index.js.map +1 -1
- package/lib/index.js +883 -298
- package/native/dsh-eval-supervisor +0 -0
- package/native/supervisor.c +232 -36
- package/neural-cifar-eval/README.md +5 -0
- package/neural-cifar-eval/common.py +13 -0
- package/neural-cifar-eval/ops_eval.py +17 -5
- package/neural-cifar-eval/run_eval.py +42 -32
- package/neural-pendulum-eval/README.md +62 -0
- package/neural-pendulum-eval/ops_eval.py +603 -0
- package/neural-pendulum-eval/run_eval.py +218 -137
- package/neural-pendulum-eval/sim_eval +0 -0
- package/package.json +7 -11
- package/assets/neural-pendulum/hidden/test_infra.cpp +0 -631
- package/assets/neural-pendulum/include/nn/nn.hpp +0 -88
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""SAC + 2x256 tanh MLP + sin/cos H=4 observation, angle-only simulator.
|
|
2
|
+
|
|
3
|
+
Experiments:
|
|
4
|
+
--init rand : theta_i ~ U(-.05,.05), omega_i ~ U(-.02,.02), no bias
|
|
5
|
+
--init fixed : training reset is exactly theta=[0.013,-0.008,0.021], omega=0
|
|
6
|
+
|
|
7
|
+
32 vectorised environments by default, replay buffer, auto entropy tuning,
|
|
8
|
+
shared-trunk twin Q, UTD = 0.25 (8 gradient steps per 32-env vector step).
|
|
9
|
+
The simulator exposes angles only; observation, reward, termination, success
|
|
10
|
+
and t_can all live in trainer-side code (obs.py / task.py).
|
|
11
|
+
|
|
12
|
+
TensorBoard scalars (tb/<exp>/):
|
|
13
|
+
train/episode_return, train/episode_length, train/t_can, train/t_can_max,
|
|
14
|
+
train/success_rate, train/ever_up_frac, train/first_up_time, train/up_frac,
|
|
15
|
+
train/action_abs_mean, train/replay_size, train/steps_per_s, train/alpha,
|
|
16
|
+
train/entropy, train/q_mean, train/q_target_mean,
|
|
17
|
+
loss/actor, loss/critic, loss/alpha, loss/policy, loss/value, loss/entropy,
|
|
18
|
+
eval/t_can, eval/success, eval/ep_return, eval/ever_up, eval/final_ang_err,
|
|
19
|
+
eval/first_up_t, eval/up_frac, eval_final/*, time/elapsed_min
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import copy
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import time
|
|
28
|
+
|
|
29
|
+
import numpy as np
|
|
30
|
+
import torch
|
|
31
|
+
import torch.nn as nn
|
|
32
|
+
import torch.nn.functional as F
|
|
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_sac_policy
|
|
38
|
+
from models import SquashedGaussianActor, TwinQ
|
|
39
|
+
from obs import OBS_MODES, obs_spec
|
|
40
|
+
from sim import EPISODE_STEPS, Params, TorchVecSim
|
|
41
|
+
from task import (TORCH_REWARD_NOTE, TorchTask, get_reward_preset, make_init,
|
|
42
|
+
sample_train_init)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Replay:
|
|
46
|
+
def __init__(self, cap, dim, act_dim=2):
|
|
47
|
+
self.cap = int(cap)
|
|
48
|
+
self.obs = np.zeros((cap, dim), np.float32)
|
|
49
|
+
self.act = np.zeros((cap, act_dim), np.float32)
|
|
50
|
+
self.rew = np.zeros((cap, 1), np.float32)
|
|
51
|
+
self.nobs = np.zeros((cap, dim), np.float32)
|
|
52
|
+
self.done = np.zeros((cap, 1), np.float32)
|
|
53
|
+
self.ptr = 0
|
|
54
|
+
self.size = 0
|
|
55
|
+
|
|
56
|
+
def push(self, obs, act, rew, nobs, done):
|
|
57
|
+
n = obs.shape[0]
|
|
58
|
+
idx = (self.ptr + np.arange(n)) % self.cap
|
|
59
|
+
self.obs[idx] = obs
|
|
60
|
+
self.act[idx] = act
|
|
61
|
+
self.rew[idx] = np.asarray(rew).reshape(n, 1)
|
|
62
|
+
self.nobs[idx] = nobs
|
|
63
|
+
self.done[idx] = np.asarray(done).reshape(n, 1)
|
|
64
|
+
self.ptr = (self.ptr + n) % self.cap
|
|
65
|
+
self.size = min(self.cap, self.size + n)
|
|
66
|
+
|
|
67
|
+
def sample(self, batch):
|
|
68
|
+
idx = np.random.randint(0, self.size, size=batch)
|
|
69
|
+
return (torch.from_numpy(self.obs[idx]),
|
|
70
|
+
torch.from_numpy(self.act[idx]),
|
|
71
|
+
torch.from_numpy(self.rew[idx]),
|
|
72
|
+
torch.from_numpy(self.nobs[idx]),
|
|
73
|
+
torch.from_numpy(self.done[idx]))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def soft_update(tgt, src, tau):
|
|
77
|
+
with torch.no_grad():
|
|
78
|
+
for a, b in zip(tgt.parameters(), src.parameters()):
|
|
79
|
+
a.mul_(1 - tau).add_(tau * b)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def sac_update(actor, q, qt, act_opt, q_opt, log_alpha, alpha_opt, replay, args):
|
|
83
|
+
o, ac, r, no, d = replay.sample(args.batch)
|
|
84
|
+
with torch.no_grad():
|
|
85
|
+
na, nlogp = actor.sample(no)
|
|
86
|
+
q_next = qt(no, na).min(dim=1).values
|
|
87
|
+
target = r.squeeze(-1) + args.gamma * (1.0 - d.squeeze(-1)) * (
|
|
88
|
+
q_next - log_alpha.exp() * nlogp)
|
|
89
|
+
q_pred = q(o, ac)
|
|
90
|
+
critic_loss = 0.5 * F.mse_loss(q_pred, target.unsqueeze(1).expand(-1, 2))
|
|
91
|
+
q_opt.zero_grad()
|
|
92
|
+
critic_loss.backward()
|
|
93
|
+
nn.utils.clip_grad_norm_(q.parameters(), 10.0)
|
|
94
|
+
q_opt.step()
|
|
95
|
+
|
|
96
|
+
for p_ in q.parameters():
|
|
97
|
+
p_.requires_grad_(False)
|
|
98
|
+
pa, logp = actor.sample(o)
|
|
99
|
+
q_pi = q(o, pa).min(dim=1).values
|
|
100
|
+
actor_loss = (log_alpha.exp().detach() * logp - q_pi).mean()
|
|
101
|
+
act_opt.zero_grad()
|
|
102
|
+
actor_loss.backward()
|
|
103
|
+
nn.utils.clip_grad_norm_(actor.parameters(), 10.0)
|
|
104
|
+
act_opt.step()
|
|
105
|
+
for p_ in q.parameters():
|
|
106
|
+
p_.requires_grad_(True)
|
|
107
|
+
|
|
108
|
+
alpha_loss = -(log_alpha.exp() * (logp.detach() + args.target_entropy)).mean()
|
|
109
|
+
alpha_opt.zero_grad()
|
|
110
|
+
alpha_loss.backward()
|
|
111
|
+
alpha_opt.step()
|
|
112
|
+
|
|
113
|
+
soft_update(qt, q, args.tau)
|
|
114
|
+
return dict(actor=float(actor_loss), critic=float(critic_loss),
|
|
115
|
+
alpha=float(alpha_loss),
|
|
116
|
+
q=float(q_pred.mean()), qt=float(target.mean()),
|
|
117
|
+
entropy=float(-logp.detach().mean()))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def main():
|
|
121
|
+
ap = argparse.ArgumentParser()
|
|
122
|
+
ap.add_argument("--exp", type=str, required=True)
|
|
123
|
+
ap.add_argument("--seed", type=int, default=0)
|
|
124
|
+
ap.add_argument("--init", type=str, default="rand",
|
|
125
|
+
choices=["rand", "fixed", "mix"],
|
|
126
|
+
help="rand=down noise, fixed=eval state, "
|
|
127
|
+
"mix=curriculum with up_frac near upright")
|
|
128
|
+
ap.add_argument("--up-frac", type=float, default=0.5,
|
|
129
|
+
help="fraction of resets near upright when --init mix")
|
|
130
|
+
ap.add_argument("--obs", type=str, default="full",
|
|
131
|
+
choices=sorted(OBS_MODES),
|
|
132
|
+
help="observation mode (see obs.py)")
|
|
133
|
+
ap.add_argument("--reward", type=str, default="base",
|
|
134
|
+
choices=["base", "sparse", "energy"],
|
|
135
|
+
help="reward preset (see task.py REWARD_PRESETS)")
|
|
136
|
+
ap.add_argument("--envs", type=int, default=32)
|
|
137
|
+
ap.add_argument("--minutes", type=float, default=30.0)
|
|
138
|
+
ap.add_argument("--steps", type=int, default=10 ** 12)
|
|
139
|
+
ap.add_argument("--hidden", type=int, default=256)
|
|
140
|
+
ap.add_argument("--depth", type=int, default=2)
|
|
141
|
+
ap.add_argument("--lr", type=float, default=3e-4)
|
|
142
|
+
ap.add_argument("--gamma", type=float, default=0.99)
|
|
143
|
+
ap.add_argument("--tau", type=float, default=0.005)
|
|
144
|
+
ap.add_argument("--batch", type=int, default=256)
|
|
145
|
+
ap.add_argument("--warmup", type=int, default=5000)
|
|
146
|
+
ap.add_argument("--buffer", type=int, default=300000)
|
|
147
|
+
ap.add_argument("--utd", type=float, default=0.25,
|
|
148
|
+
help="gradient steps per environment step")
|
|
149
|
+
ap.add_argument("--target-entropy", type=float, default=-2.0)
|
|
150
|
+
ap.add_argument("--alpha-init", type=float, default=0.2)
|
|
151
|
+
ap.add_argument("--episode-steps", type=int, default=EPISODE_STEPS)
|
|
152
|
+
ap.add_argument("--eval-every", type=int, default=20000)
|
|
153
|
+
ap.add_argument("--eval-start", type=int, default=20000)
|
|
154
|
+
ap.add_argument("--tb-root", type=str, default="tb")
|
|
155
|
+
ap.add_argument("--ckpt-dir", type=str, default="checkpoints")
|
|
156
|
+
ap.add_argument("--out", type=str, default="results")
|
|
157
|
+
ap.add_argument("--tau-max", type=float, default=18.0)
|
|
158
|
+
ap.add_argument("--damping", type=float, default=0.01)
|
|
159
|
+
ap.add_argument("--log-every", type=int, default=10,
|
|
160
|
+
help="log every N vector steps")
|
|
161
|
+
ap.add_argument("--robust-starts", type=int, default=8,
|
|
162
|
+
help="deterministic extra rollouts from the training "
|
|
163
|
+
"reset distribution at the end (0 disables)")
|
|
164
|
+
args = ap.parse_args()
|
|
165
|
+
|
|
166
|
+
torch.manual_seed(args.seed)
|
|
167
|
+
np.random.seed(args.seed)
|
|
168
|
+
for d in (args.tb_root, args.ckpt_dir, args.out):
|
|
169
|
+
os.makedirs(d, exist_ok=True)
|
|
170
|
+
exp = args.exp
|
|
171
|
+
writer = SummaryWriter(os.path.join(args.tb_root, exp))
|
|
172
|
+
p = Params(tau_max=args.tau_max, b=args.damping)
|
|
173
|
+
spec = obs_spec(args.obs)
|
|
174
|
+
obs_dim = spec["dim"]
|
|
175
|
+
reward_w = get_reward_preset(args.reward)
|
|
176
|
+
sim = TorchVecSim(p, n=args.envs, episode_steps=args.episode_steps)
|
|
177
|
+
task = TorchTask(p, n=args.envs, reward_w=reward_w, obs_mode=args.obs)
|
|
178
|
+
gen = torch.Generator().manual_seed(args.seed * 1000 + 11)
|
|
179
|
+
updates_per_vec = max(1, int(round(args.utd * args.envs)))
|
|
180
|
+
eff_utd = updates_per_vec / args.envs
|
|
181
|
+
|
|
182
|
+
init_meta = dict(mode=args.init,
|
|
183
|
+
training=("theta_i ~ U(-0.05,0.05), omega_i ~ U(-0.02,0.02); no bias"
|
|
184
|
+
if args.init == "rand" else
|
|
185
|
+
("theta=[0.013,-0.008,0.021], omega=0 (same as eval)"
|
|
186
|
+
if args.init == "fixed" else
|
|
187
|
+
f"curriculum: {args.up_frac:.2f} near upright "
|
|
188
|
+
f"(pi + U(-0.05,0.05)), rest near down")),
|
|
189
|
+
eval="theta=[0.013,-0.008,0.021], omega=0 (fixed, no noise)")
|
|
190
|
+
meta = dict(algo="sac", exp=exp, seed=args.seed, envs=args.envs,
|
|
191
|
+
obs=spec, obs_dim=obs_dim, init=init_meta,
|
|
192
|
+
reward=reward_w, reward_preset=args.reward,
|
|
193
|
+
reward_note=TORCH_REWARD_NOTE,
|
|
194
|
+
updates_per_vector=updates_per_vec, effective_utd=eff_utd,
|
|
195
|
+
args=vars(args))
|
|
196
|
+
writer.add_text("config", json.dumps(meta, indent=2), 0)
|
|
197
|
+
print(json.dumps(meta, indent=2), flush=True)
|
|
198
|
+
|
|
199
|
+
actor = SquashedGaussianActor(obs_dim, 2, hidden=args.hidden, depth=args.depth)
|
|
200
|
+
q = TwinQ(obs_dim, 2, hidden=args.hidden, depth=args.depth)
|
|
201
|
+
qt = copy.deepcopy(q)
|
|
202
|
+
act_opt = torch.optim.Adam(actor.parameters(), lr=args.lr)
|
|
203
|
+
q_opt = torch.optim.Adam(q.parameters(), lr=args.lr)
|
|
204
|
+
log_alpha = torch.log(torch.tensor(args.alpha_init)).clone().detach().requires_grad_(True)
|
|
205
|
+
alpha_opt = torch.optim.Adam([log_alpha], lr=args.lr)
|
|
206
|
+
|
|
207
|
+
replay = Replay(args.buffer, obs_dim, 2)
|
|
208
|
+
th, om = make_init(args.envs, args.init, gen, up_frac=args.up_frac)
|
|
209
|
+
theta = sim.reset(th, om)
|
|
210
|
+
obs = task.reset(theta)
|
|
211
|
+
|
|
212
|
+
t_start = time.time()
|
|
213
|
+
total = 0
|
|
214
|
+
n_updates = 0
|
|
215
|
+
vec_steps = 0
|
|
216
|
+
next_eval = args.eval_start
|
|
217
|
+
best_t_can = -1.0
|
|
218
|
+
best_eval = None
|
|
219
|
+
best_path = None
|
|
220
|
+
last_path = None
|
|
221
|
+
eval_curve = []
|
|
222
|
+
last = dict(actor=float("nan"), critic=float("nan"), alpha=float("nan"),
|
|
223
|
+
q=float("nan"), qt=float("nan"), entropy=float("nan"))
|
|
224
|
+
ep = {k: [] for k in ("return", "t_can", "ever_up", "first_up", "up_frac", "length")}
|
|
225
|
+
|
|
226
|
+
def save_ckpt(path, ev):
|
|
227
|
+
torch.save(dict(model=actor.state_dict(), q=q.state_dict(),
|
|
228
|
+
log_alpha=log_alpha.detach(), obs=spec,
|
|
229
|
+
obs_dim=obs_dim, init=init_meta, reward=reward_w,
|
|
230
|
+
reward_preset=args.reward,
|
|
231
|
+
args=vars(args), global_steps=total,
|
|
232
|
+
n_updates=n_updates, eval=ev, algo="sac"),
|
|
233
|
+
os.path.join(args.ckpt_dir, path))
|
|
234
|
+
|
|
235
|
+
def log_eval(ev):
|
|
236
|
+
nonlocal best_t_can, best_eval, best_path, last_path
|
|
237
|
+
eval_curve.append(dict(global_steps=total, n_updates=n_updates, **ev))
|
|
238
|
+
for k, v in ev.items():
|
|
239
|
+
if isinstance(v, (int, float)):
|
|
240
|
+
writer.add_scalar(f"eval/{k}", v, total)
|
|
241
|
+
if (ev["t_can"] > best_t_can
|
|
242
|
+
or (ev["t_can"] == best_t_can and best_eval is not None
|
|
243
|
+
and ev["ep_return"] > best_eval["ep_return"])):
|
|
244
|
+
best_t_can = ev["t_can"]
|
|
245
|
+
best_eval = dict(ev)
|
|
246
|
+
best_path = exp + "_best.pt"
|
|
247
|
+
save_ckpt(best_path, ev)
|
|
248
|
+
last_path = exp + "_last.pt"
|
|
249
|
+
save_ckpt(last_path, ev)
|
|
250
|
+
|
|
251
|
+
while total < args.steps:
|
|
252
|
+
if (time.time() - t_start) / 60.0 > args.minutes:
|
|
253
|
+
print("time budget reached", flush=True)
|
|
254
|
+
break
|
|
255
|
+
if replay.size < args.warmup:
|
|
256
|
+
u = torch.rand(args.envs, 2) * 2.0 - 1.0
|
|
257
|
+
else:
|
|
258
|
+
with torch.no_grad():
|
|
259
|
+
u, _ = actor.sample(obs)
|
|
260
|
+
u = torch.clamp(u, -1.0, 1.0)
|
|
261
|
+
theta = sim.step(u)
|
|
262
|
+
nobs, rew = task.step(theta, u)
|
|
263
|
+
done = sim.steps >= sim.episode_steps
|
|
264
|
+
replay.push(obs.detach().cpu().numpy(), u.detach().cpu().numpy(),
|
|
265
|
+
rew.detach().cpu().numpy(), nobs.detach().cpu().numpy(),
|
|
266
|
+
done.detach().cpu().numpy())
|
|
267
|
+
if bool(done.any()):
|
|
268
|
+
idx = done.nonzero().flatten()
|
|
269
|
+
summ = task.summary(idx)
|
|
270
|
+
ep["return"] += summ["ep_return"].tolist()
|
|
271
|
+
ep["t_can"] += summ["t_can"].tolist()
|
|
272
|
+
ep["ever_up"] += summ["ever_up"].tolist()
|
|
273
|
+
ep["first_up"] += summ["first_up_t"].tolist()
|
|
274
|
+
ep["up_frac"] += summ["up_frac"].tolist()
|
|
275
|
+
ep["length"] += summ["length"].tolist()
|
|
276
|
+
th, om = make_init(idx.numel(), args.init, gen, up_frac=args.up_frac)
|
|
277
|
+
reset_theta = sim.reset(th, om, idx)
|
|
278
|
+
task.reset(reset_theta, idx)
|
|
279
|
+
nobs = task.obs
|
|
280
|
+
obs = nobs
|
|
281
|
+
total += args.envs
|
|
282
|
+
vec_steps += 1
|
|
283
|
+
|
|
284
|
+
if replay.size >= args.warmup:
|
|
285
|
+
for _ in range(updates_per_vec):
|
|
286
|
+
last = sac_update(actor, q, qt, act_opt, q_opt, log_alpha,
|
|
287
|
+
alpha_opt, replay, args)
|
|
288
|
+
n_updates += 1
|
|
289
|
+
|
|
290
|
+
if vec_steps % args.log_every == 0:
|
|
291
|
+
elapsed = time.time() - t_start
|
|
292
|
+
if ep["return"]:
|
|
293
|
+
writer.add_scalar("train/episode_return",
|
|
294
|
+
float(np.mean(ep["return"])), total)
|
|
295
|
+
writer.add_scalar("train/episode_length",
|
|
296
|
+
float(np.mean(ep["length"])), total)
|
|
297
|
+
writer.add_scalar("train/t_can", float(np.mean(ep["t_can"])), total)
|
|
298
|
+
writer.add_scalar("train/t_can_max", float(np.max(ep["t_can"])), total)
|
|
299
|
+
writer.add_scalar("train/success_rate",
|
|
300
|
+
float(np.mean(np.asarray(ep["t_can"]) >= 10.0)), total)
|
|
301
|
+
writer.add_scalar("train/ever_up_frac",
|
|
302
|
+
float(np.mean(ep["ever_up"])), total)
|
|
303
|
+
writer.add_scalar("train/up_frac",
|
|
304
|
+
float(np.mean(ep["up_frac"])), total)
|
|
305
|
+
fu = [x for x in ep["first_up"] if x > 0]
|
|
306
|
+
if fu:
|
|
307
|
+
writer.add_scalar("train/first_up_time", float(np.mean(fu)), total)
|
|
308
|
+
ep = {k: [] for k in ep}
|
|
309
|
+
writer.add_scalar("train/replay_size", replay.size, total)
|
|
310
|
+
writer.add_scalar("train/steps_per_s", total / max(elapsed, 1e-9), total)
|
|
311
|
+
writer.add_scalar("train/alpha", float(log_alpha.detach().exp()), total)
|
|
312
|
+
writer.add_scalar("train/action_abs_mean", float(u.abs().mean()), total)
|
|
313
|
+
writer.add_scalar("train/action_std", float(u.std()), total)
|
|
314
|
+
writer.add_scalar("loss/actor", last["actor"], total)
|
|
315
|
+
writer.add_scalar("loss/critic", last["critic"], total)
|
|
316
|
+
writer.add_scalar("loss/alpha", last["alpha"], total)
|
|
317
|
+
writer.add_scalar("loss/policy", last["actor"], total)
|
|
318
|
+
writer.add_scalar("loss/value", last["critic"], total)
|
|
319
|
+
writer.add_scalar("loss/entropy", last["entropy"], total)
|
|
320
|
+
writer.add_scalar("train/entropy", last["entropy"], total)
|
|
321
|
+
writer.add_scalar("train/q_mean", last["q"], total)
|
|
322
|
+
writer.add_scalar("train/q_target_mean", last["qt"], total)
|
|
323
|
+
writer.add_scalar("train/global_steps", total, total)
|
|
324
|
+
writer.add_scalar("time/elapsed_min", elapsed / 60.0, total)
|
|
325
|
+
writer.flush()
|
|
326
|
+
|
|
327
|
+
if total >= next_eval:
|
|
328
|
+
next_eval += args.eval_every
|
|
329
|
+
ev = evaluate(make_sac_policy(actor), p, args.episode_steps,
|
|
330
|
+
reward_w=reward_w, obs_mode=args.obs)
|
|
331
|
+
log_eval(ev)
|
|
332
|
+
print(f"steps {total/1e3:7.1f}k upd {n_updates:6d} "
|
|
333
|
+
f"EVAL tc {ev['t_can']:6.2f} succ {ev['success']:.0f} "
|
|
334
|
+
f"everup {ev['ever_up']:.0f} angerr {ev['final_ang_err']:.3f} "
|
|
335
|
+
f"ret {ev['ep_return']:8.1f} q {last['q']:8.2f} "
|
|
336
|
+
f"a {float(log_alpha.detach().exp()):.2f} "
|
|
337
|
+
f"({time.time()-t_start:.0f}s)", flush=True)
|
|
338
|
+
writer.flush()
|
|
339
|
+
|
|
340
|
+
ev_final = evaluate(make_sac_policy(actor), p, args.episode_steps,
|
|
341
|
+
reward_w=reward_w, obs_mode=args.obs)
|
|
342
|
+
eval_curve.append(dict(global_steps=total, n_updates=n_updates, **ev_final))
|
|
343
|
+
for k, v in ev_final.items():
|
|
344
|
+
if isinstance(v, (int, float)):
|
|
345
|
+
writer.add_scalar(f"eval_final/{k}", v, total)
|
|
346
|
+
final_path = exp + "_final.pt"
|
|
347
|
+
save_ckpt(final_path, ev_final)
|
|
348
|
+
last_path = exp + "_last.pt"
|
|
349
|
+
save_ckpt(last_path, ev_final)
|
|
350
|
+
|
|
351
|
+
# Extra deterministic rollouts from the training reset distribution. This
|
|
352
|
+
# is NOT the mandated fixed-state eval; it checks generalisation.
|
|
353
|
+
rob = None
|
|
354
|
+
rob_best = None
|
|
355
|
+
if args.robust_starts > 0:
|
|
356
|
+
g = torch.Generator().manual_seed(args.seed * 1000 + 123)
|
|
357
|
+
th_r, om_r = sample_train_init(args.robust_starts, gen=g)
|
|
358
|
+
starts = list(zip(th_r.numpy(), om_r.numpy()))
|
|
359
|
+
rob = evaluate_starts(make_sac_policy(actor), p, starts,
|
|
360
|
+
args.episode_steps, reward_w=reward_w,
|
|
361
|
+
obs_mode=args.obs)
|
|
362
|
+
for k, v in rob.items():
|
|
363
|
+
if isinstance(v, (int, float)):
|
|
364
|
+
writer.add_scalar(f"robust/{k}", v, total)
|
|
365
|
+
if best_path is not None:
|
|
366
|
+
ck = torch.load(os.path.join(args.ckpt_dir, best_path),
|
|
367
|
+
map_location="cpu")
|
|
368
|
+
actor_best = SquashedGaussianActor(obs_dim, 2, hidden=args.hidden,
|
|
369
|
+
depth=args.depth)
|
|
370
|
+
actor_best.load_state_dict(ck["model"])
|
|
371
|
+
rob_best = evaluate_starts(make_sac_policy(actor_best), p, starts,
|
|
372
|
+
args.episode_steps, reward_w=reward_w,
|
|
373
|
+
obs_mode=args.obs)
|
|
374
|
+
for k, v in rob_best.items():
|
|
375
|
+
if isinstance(v, (int, float)):
|
|
376
|
+
writer.add_scalar(f"robust_best/{k}", v, total)
|
|
377
|
+
|
|
378
|
+
wall = time.time() - t_start
|
|
379
|
+
out = dict(algo="sac", exp=exp, seed=args.seed, envs=args.envs,
|
|
380
|
+
obs=spec, obs_dim=obs_dim, init=init_meta,
|
|
381
|
+
reward=reward_w, reward_preset=args.reward,
|
|
382
|
+
reward_note=TORCH_REWARD_NOTE,
|
|
383
|
+
args=vars(args), updates_per_vector=updates_per_vec,
|
|
384
|
+
effective_utd=eff_utd, minutes_budget=args.minutes,
|
|
385
|
+
global_steps=total, gradient_steps=n_updates,
|
|
386
|
+
wall_time_s=wall, steps_per_s=total / max(wall, 1e-9),
|
|
387
|
+
updates_per_s=n_updates / max(wall, 1e-9),
|
|
388
|
+
final_eval=ev_final, best_eval=best_eval, best_t_can=best_t_can,
|
|
389
|
+
eval_curve=eval_curve, robust=rob, robust_best=rob_best,
|
|
390
|
+
final_train=last,
|
|
391
|
+
checkpoints=dict(best=best_path, last=last_path,
|
|
392
|
+
final=final_path))
|
|
393
|
+
with open(os.path.join(args.out, exp + ".json"), "w") as f:
|
|
394
|
+
json.dump(out, f, indent=2)
|
|
395
|
+
writer.flush()
|
|
396
|
+
writer.close()
|
|
397
|
+
print("DONE " + json.dumps(dict(
|
|
398
|
+
exp=exp, init=args.init, seed=args.seed, envs=args.envs,
|
|
399
|
+
global_steps=total, gradient_steps=n_updates, wall_s=round(wall, 1),
|
|
400
|
+
steps_per_s=round(out["steps_per_s"], 1),
|
|
401
|
+
eval_t_can=ev_final["t_can"], best_t_can=best_t_can,
|
|
402
|
+
eval_return=ev_final["ep_return"])), flush=True)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
if __name__ == "__main__":
|
|
406
|
+
main()
|
|
@@ -65,7 +65,13 @@ static void deriv(const State& s, const std::array<double,N>& tau, State& out) {
|
|
|
65
65
|
std::array<double,N> C{}, G{}, Q{}, rhs{};
|
|
66
66
|
for(int i=0;i<N;i++){
|
|
67
67
|
double ci=0;
|
|
68
|
-
for(int j=0;j<N;j++) if(j!=i)
|
|
68
|
+
for(int j=0;j<N;j++) if(j!=i) {
|
|
69
|
+
// Coriolis coefficient: d(M_ij)/d(theta_j) = LEN[min] * GCOEF[max] * sin(...);
|
|
70
|
+
// M_ij itself already contains cos(...), so multiplying M_ij by sin would
|
|
71
|
+
// add an extra cos factor and inject energy at high speed (NaN after ~7 s).
|
|
72
|
+
const int a=std::min(i,j), b=std::max(i,j);
|
|
73
|
+
ci += LEN[a] * GCOEF[b] * std::sin(s.theta[i]-s.theta[j]) * s.omega[j]*s.omega[j];
|
|
74
|
+
}
|
|
69
75
|
C[i]=ci;
|
|
70
76
|
G[i]=GRAV*std::sin(s.theta[i])*GCOEF[i];
|
|
71
77
|
}
|
|
@@ -101,7 +107,12 @@ int main(int argc, char** argv) {
|
|
|
101
107
|
// parse torque array
|
|
102
108
|
double u1=0,u3=0; auto p=line.find('['); if(p!=std::string::npos){ char* end=nullptr; u1=std::strtod(line.c_str()+p+1,&end); if(end){ const char* q=end; while(*q && (*q==','||*q==' ')) q++; u3=std::strtod(q,&end); } }
|
|
103
109
|
u1=std::max(-1.0,std::min(1.0,u1)); u3=std::max(-1.0,std::min(1.0,u3));
|
|
104
|
-
|
|
110
|
+
// Joint 3 is an internal motor between link 2 and link 3. In absolute
|
|
111
|
+
// coordinates its generalized force is (tau1, -tau3, tau3): the reaction
|
|
112
|
+
// on link 2 is part of the same joint torque. (tau1, 0, tau3) would be a
|
|
113
|
+
// reactionless external torque on link 3 and would contradict joint 2
|
|
114
|
+
// being passive.
|
|
115
|
+
std::array<double,N> tau{{u1*TAU_MAX,-u3*TAU_MAX,u3*TAU_MAX}};
|
|
105
116
|
// one control step with 4 RK4 substeps
|
|
106
117
|
double h=DT/SUB;
|
|
107
118
|
for(int sub=0;sub<SUB;sub++){
|
|
Binary file
|
|
Binary file
|
package/eval/README.md
CHANGED
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
| 任务 | 脚本 | 评分 |
|
|
6
6
|
|---|---|---|
|
|
7
|
-
| tank | eval/tank.py | 官方引擎 + BT
|
|
8
|
-
| warehouse | eval/warehouse.py | 正确性 +
|
|
9
|
-
| land | eval/land.py | 官方 round_robin/rate |
|
|
10
|
-
| bomberman | eval/bomberman.py | 官方 tournament/run |
|
|
11
|
-
| ctf | eval/ctf.py | 官方 arena plan/run/rate |
|
|
12
|
-
| commit0 | eval/commit0.py |
|
|
13
|
-
| swe-bench-pro | eval/swe-bench-pro.py | 隐藏 F2P/P2P
|
|
14
|
-
| featurebench | eval/featurebench.py | 隐藏
|
|
7
|
+
| tank | eval/tank.py | 官方引擎 + BT,对基线(quick 20 局) |
|
|
8
|
+
| warehouse | eval/warehouse.py | 正确性 + 相对基线加速比(quick 仅 pub_small) |
|
|
9
|
+
| land | eval/land.py | 官方 round_robin/rate(quick 仅单挑 2 图) |
|
|
10
|
+
| bomberman | eval/bomberman.py | 官方 tournament/run(quick 仅 2 人 1 种子) |
|
|
11
|
+
| ctf | eval/ctf.py | 官方 arena plan/run/rate(quick 仅 2/3 方) |
|
|
12
|
+
| commit0 | eval/commit0.py | 单元测试通过率(测试来自安装快照) |
|
|
13
|
+
| swe-bench-pro | eval/swe-bench-pro.py | 隐藏 F2P/P2P 节点全部通过才 resolved |
|
|
14
|
+
| featurebench | eval/featurebench.py | 隐藏 feature 测试 + 全部回归文件通过才 resolved |
|
|
15
|
+
| neural-cpp | eval/neural-cpp.py | 隐藏 C++ 测试;accuracy / matrixAccuracy / speedGeomean / score |
|
|
16
|
+
| neural-fp32 | eval/neural-fp32.py | 逐操作 MRE/阈值/速度;无统一 score |
|
|
17
|
+
| neural-cifar | eval/neural-cifar.py | FP32 算子指标 + 隐藏验证集准确率/FPS(独立目录) |
|
|
18
|
+
| neural-pendulum | eval/neural-pendulum.py | FP32 算子指标 + 固定初态控制 t_can(独立目录) |
|
|
15
19
|
|
|
16
20
|
eval/legacy/ 保存从独立评测目录迁移过来的评测器;eval/arena/ 保存官方引擎、裁判和历史对手。common.py 负责临时工作区、结果解析和结果文件写入。
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
## 完整性约束(宿主注入)
|
|
23
|
+
|
|
24
|
+
- `EVAL_BASELINE_DIR`:安装成功时快照的测试/配置目录。commit0/SWE/FeatureBench 的 wrapper 会在评测前用它覆盖候选副本,并删除候选新增的 `conftest.py`;同时校验 `.venv` 指纹(`venv.json`)。
|
|
25
|
+
- `EVAL_RESULT_FILE` / `EVAL_JOB_DIR`:只由顶层 evaluator 使用;所有 evaluator 再启动子进程时会剥掉这些变量,候选二进制拿不到结果文件路径。
|
|
26
|
+
- 隐藏资产(neural-cpp 的隐藏测试、CIFAR 的验证标签)在候选构建/推理开始前读入内存;CIFAR 的图像副本会放到独立临时目录,标签不落盘到候选可见路径。
|
|
27
|
+
|
|
28
|
+
后台任务由 host 的 JobManager 执行,日志和结果保存在 $DSH_HOME/eval-guard/jobs/<job-id>/。Job 子进程的环境变量已收敛为白名单(PATH/HOME/LANG/LC_*/TMPDIR/SSL_* 等)。
|
package/eval/bomberman.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Single-workspace
|
|
2
|
+
"""Single-workspace bomberman evaluator wrapper."""
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
import shutil, tempfile
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from common import EVAL_ROOT, candidate_row, emit, fail, parse_workspace, run_legacy
|
|
7
7
|
|
|
8
8
|
workspace = parse_workspace()
|
|
9
|
-
with tempfile.TemporaryDirectory(prefix='
|
|
9
|
+
with tempfile.TemporaryDirectory(prefix='bomberman-eval-') as td:
|
|
10
10
|
temp = Path(td)
|
|
11
11
|
bot = workspace / 'submission' / 'bot.cpp'
|
|
12
12
|
if not bot.is_file():
|
|
@@ -17,4 +17,6 @@ with tempfile.TemporaryDirectory(prefix='bomber-eval-') as td:
|
|
|
17
17
|
if not result.get('ok'):
|
|
18
18
|
raise SystemExit(fail({'task': 'bomberman', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
|
|
19
19
|
row = candidate_row(result['summary'])
|
|
20
|
-
|
|
20
|
+
if not row:
|
|
21
|
+
raise SystemExit(fail({'task': 'bomberman', 'ok': False, 'error': 'candidate row missing from evaluator summary'}))
|
|
22
|
+
emit({'task': 'bomberman', 'ok': True, 'score': row.get('score', row.get('rating', 0)), 'rating': row.get('rating', row.get('theta', 0)), 'theta': row.get('theta', 0)})
|
package/eval/commit0.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Single-workspace Commit0 evaluator wrapper."""
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
import os,
|
|
4
|
+
import os, tempfile
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, run_legacy
|
|
6
|
+
from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, restore_baseline, run_legacy, verify_venv
|
|
7
7
|
|
|
8
8
|
workspace = parse_workspace()
|
|
9
|
+
problem = verify_venv(workspace)
|
|
10
|
+
if problem is not None:
|
|
11
|
+
raise SystemExit(fail({'task': 'commit0', 'ok': False, 'error': problem}))
|
|
9
12
|
with tempfile.TemporaryDirectory(prefix='commit0-eval-') as td:
|
|
10
13
|
temp = Path(td)
|
|
11
14
|
candidate = temp / 'candidate' / 'workspace'
|
|
@@ -13,7 +16,11 @@ with tempfile.TemporaryDirectory(prefix='commit0-eval-') as td:
|
|
|
13
16
|
clean_copy(workspace, candidate, ignore_venv=True)
|
|
14
17
|
venv = workspace / '.venv'
|
|
15
18
|
if venv.is_dir():
|
|
16
|
-
|
|
19
|
+
try:
|
|
20
|
+
os.symlink(venv, candidate / '.venv')
|
|
21
|
+
except FileExistsError:
|
|
22
|
+
pass
|
|
23
|
+
restore_baseline(candidate)
|
|
17
24
|
result = run_legacy(EVAL_ROOT / 'legacy' / 'commit0.py', ['--submissions', str(temp)], temp / 'out')
|
|
18
25
|
if not result.get('ok'):
|
|
19
26
|
raise SystemExit(fail({'task': 'commit0', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
|