dsh-eval-guard 0.6.1 → 0.7.0
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/assets/neural-pendulum/SPEC.md +468 -0
- package/assets/neural-pendulum/TASK.md +358 -0
- package/assets/neural-pendulum/sim.cpp +126 -0
- package/assets/neural-pendulum/sim_eval +0 -0
- package/assets/neural-pendulum/workspace.zip +0 -0
- package/eval/neural-pendulum.py +15 -0
- package/lib/client/index.js +41 -4
- package/lib/client/index.js.map +1 -1
- package/lib/index.js +454 -60
- package/neural-cifar-eval/README.md +57 -0
- package/neural-cifar-eval/cifar_cache.py +201 -0
- package/neural-cifar-eval/common.py +67 -0
- package/neural-cifar-eval/fetch_cifar.py +31 -0
- package/neural-cifar-eval/hidden/.gitkeep +1 -0
- package/neural-cifar-eval/ops_eval.py +502 -0
- package/neural-cifar-eval/run_eval.py +208 -0
- package/neural-pendulum-eval/run_eval.py +289 -0
- package/neural-pendulum-eval/sim_eval +0 -0
- package/package.json +4 -2
- package/eval/__pycache__/common.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-cifar.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-fp32.cpython-310.pyc +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Neural-Pendulum:从零神经网络基础设施与双驱动三阶摆控制
|
|
2
|
+
|
|
3
|
+
## 任务目标
|
|
4
|
+
|
|
5
|
+
从零实现一套纯 C++17、仅 CPU、fp32 的神经网络基础设施,并用它训练一个神经网络控制器,控制预编译的双驱动三节摆。
|
|
6
|
+
|
|
7
|
+
目标:
|
|
8
|
+
|
|
9
|
+
- 从三杆全部向下的固定初始状态起摆
|
|
10
|
+
- 保持三根杆全部向上至少 t_tgt = 10.0 秒
|
|
11
|
+
- 同时保证神经网络基础设施全部通过公开测试
|
|
12
|
+
|
|
13
|
+
允许使用 PyTorch 做开发阶段的自查和数值对照。最终提交源码中不得出现 torch,评测环境不提供 Python/torch。
|
|
14
|
+
|
|
15
|
+
## 环境
|
|
16
|
+
|
|
17
|
+
工作区提供预编译仿真器:
|
|
18
|
+
|
|
19
|
+
~~~
|
|
20
|
+
tools/sim
|
|
21
|
+
~~~
|
|
22
|
+
|
|
23
|
+
不提供仿真器源码和物理参数。
|
|
24
|
+
|
|
25
|
+
### 初始状态
|
|
26
|
+
|
|
27
|
+
训练时每次 reset:
|
|
28
|
+
|
|
29
|
+
- 三根杆接近全向下
|
|
30
|
+
- 角度以全向下为中心,只加小范围随机噪声
|
|
31
|
+
- 不添加固定角度偏差
|
|
32
|
+
- 带小范围随机初始角速度
|
|
33
|
+
|
|
34
|
+
评测时:
|
|
35
|
+
|
|
36
|
+
- 使用固定初始状态
|
|
37
|
+
- 三个固定角度互不相同
|
|
38
|
+
- 三个固定角度的绝对值也互不相同
|
|
39
|
+
- 初始角速度为 0
|
|
40
|
+
- 不加任何随机扰动
|
|
41
|
+
|
|
42
|
+
### 驱动结构
|
|
43
|
+
|
|
44
|
+
~~~text
|
|
45
|
+
joint 1:驱动
|
|
46
|
+
joint 2:被动
|
|
47
|
+
joint 3:驱动
|
|
48
|
+
~~~
|
|
49
|
+
|
|
50
|
+
第一关节和第三关节各有一个归一化力矩输入。
|
|
51
|
+
|
|
52
|
+
### 观测
|
|
53
|
+
|
|
54
|
+
控制器每一步只获得三个关节角度:
|
|
55
|
+
|
|
56
|
+
~~~text
|
|
57
|
+
a_i = wrap_to_pi(theta_i - pi) i = 1,2,3
|
|
58
|
+
obs = [a1, a2, a3] (rad)
|
|
59
|
+
~~~
|
|
60
|
+
|
|
61
|
+
即 3 维观测,单位 rad,不除以 pi。
|
|
62
|
+
|
|
63
|
+
- theta_i 是第 i 根杆的绝对角度
|
|
64
|
+
- a_i 是相对竖直向上的角度误差
|
|
65
|
+
- 不提供角速度
|
|
66
|
+
- 不提供第三杆末端位置
|
|
67
|
+
- 不提供杆长、质量、惯量等物理参数
|
|
68
|
+
|
|
69
|
+
控制器的内部结构和训练方法由你自行设计。
|
|
70
|
+
|
|
71
|
+
### 动作
|
|
72
|
+
|
|
73
|
+
控制器输出两个归一化力矩:
|
|
74
|
+
|
|
75
|
+
~~~text
|
|
76
|
+
[u1, u3] ∈ [-1, 1]^2
|
|
77
|
+
~~~
|
|
78
|
+
|
|
79
|
+
分别作用于第一关节和第三关节。第二关节始终无驱动。
|
|
80
|
+
|
|
81
|
+
非有限值、超范围或格式错误会判为控制器失败。
|
|
82
|
+
|
|
83
|
+
### 控制与仿真
|
|
84
|
+
|
|
85
|
+
~~~
|
|
86
|
+
控制频率 = 100 Hz
|
|
87
|
+
控制周期 = 0.01 s
|
|
88
|
+
最大仿真时间 = 30.0 s
|
|
89
|
+
t_tgt = 10.0 s
|
|
90
|
+
全向上容差 = 每根杆与竖直方向夹角 <= 0.05 rad
|
|
91
|
+
~~~
|
|
92
|
+
|
|
93
|
+
评测会计算:
|
|
94
|
+
|
|
95
|
+
~~~
|
|
96
|
+
t_can = 三根杆同时处于全向上容差内的最长连续时间
|
|
97
|
+
~~~
|
|
98
|
+
|
|
99
|
+
当:
|
|
100
|
+
|
|
101
|
+
~~~
|
|
102
|
+
t_can >= t_tgt
|
|
103
|
+
且基础设施测试全部通过
|
|
104
|
+
~~~
|
|
105
|
+
|
|
106
|
+
任务完成,计时停止。
|
|
107
|
+
|
|
108
|
+
## 训练用仿真接口
|
|
109
|
+
|
|
110
|
+
tools/sim 使用 JSONL 协议。
|
|
111
|
+
|
|
112
|
+
输入:
|
|
113
|
+
|
|
114
|
+
~~~json
|
|
115
|
+
{"cmd":"reset"}
|
|
116
|
+
{"cmd":"step","torque":[0.5,-0.5]}
|
|
117
|
+
~~~
|
|
118
|
+
|
|
119
|
+
输出:
|
|
120
|
+
|
|
121
|
+
~~~json
|
|
122
|
+
{"obs":[3 floats]}
|
|
123
|
+
~~~
|
|
124
|
+
|
|
125
|
+
每个 episode 最多 3000 步(30.0 s)。
|
|
126
|
+
|
|
127
|
+
达到最大步数后再发送 step:
|
|
128
|
+
|
|
129
|
+
~~~json
|
|
130
|
+
{"error":"episode_finished"}
|
|
131
|
+
~~~
|
|
132
|
+
|
|
133
|
+
此时不返回新观测,也不会继续推进仿真。
|
|
134
|
+
必须 reset 后才能开始新 episode;reset 前的 step 都返回该错误。
|
|
135
|
+
|
|
136
|
+
可以反复 reset / step 进行数据采集、模型训练和自测。
|
|
137
|
+
|
|
138
|
+
环境只返回状态,不提供 reward、loss、cost 或 done 信号。
|
|
139
|
+
训练信号由你自行构造。
|
|
140
|
+
|
|
141
|
+
## 交付内容
|
|
142
|
+
|
|
143
|
+
只提交源码,不提交编译产物。
|
|
144
|
+
|
|
145
|
+
源码根目录必须支持:
|
|
146
|
+
|
|
147
|
+
~~~
|
|
148
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
149
|
+
cmake --build build -j4
|
|
150
|
+
~~~
|
|
151
|
+
|
|
152
|
+
必须产出两个入口:
|
|
153
|
+
|
|
154
|
+
~~~
|
|
155
|
+
build/nn_selftest
|
|
156
|
+
build/control
|
|
157
|
+
~~~
|
|
158
|
+
|
|
159
|
+
### 1. 基础设施入口
|
|
160
|
+
|
|
161
|
+
build/nn_selftest 必须运行公开基础设施测试并返回 0 表示通过。
|
|
162
|
+
|
|
163
|
+
必须包含的基础设施:
|
|
164
|
+
|
|
165
|
+
- fp32 tensor,形状检查与广播
|
|
166
|
+
- elementwise add/sub/mul/div
|
|
167
|
+
- matmul / linear
|
|
168
|
+
- relu / softmax
|
|
169
|
+
- layernorm
|
|
170
|
+
- conv2d,支持 stride/padding/dilation/groups
|
|
171
|
+
- MSE
|
|
172
|
+
- matmul/linear/relu/layernorm/conv2d/MSE 反向
|
|
173
|
+
- SGD momentum
|
|
174
|
+
- AdamW
|
|
175
|
+
- 模型保存/加载
|
|
176
|
+
- 线程池
|
|
177
|
+
|
|
178
|
+
占位符或空实现会被判为基础设施错误。
|
|
179
|
+
|
|
180
|
+
公开测试文件在工作区中,评测时使用评测侧的干净副本。
|
|
181
|
+
|
|
182
|
+
### 2. 控制器入口
|
|
183
|
+
|
|
184
|
+
build/control 是一个常驻进程,通过 stdin/stdout 使用 JSONL 通信。
|
|
185
|
+
|
|
186
|
+
输入:
|
|
187
|
+
|
|
188
|
+
~~~json
|
|
189
|
+
{"type":"reset"}
|
|
190
|
+
{"type":"step","step":0,"state":[3 floats]}
|
|
191
|
+
~~~
|
|
192
|
+
|
|
193
|
+
输出:
|
|
194
|
+
|
|
195
|
+
~~~json
|
|
196
|
+
{"type":"ready"}
|
|
197
|
+
{"type":"action","torque":[0.5,-0.5]}
|
|
198
|
+
~~~
|
|
199
|
+
|
|
200
|
+
规则:
|
|
201
|
+
|
|
202
|
+
- 每次评测启动一个新进程
|
|
203
|
+
- 先收到 reset,必须回复 ready
|
|
204
|
+
- 每个 step 必须在 100 ms 内回复
|
|
205
|
+
- 控制器可以保存内部历史状态
|
|
206
|
+
- 崩溃、超时、非法输出会判为控制器失败
|
|
207
|
+
|
|
208
|
+
## 提交与测试
|
|
209
|
+
|
|
210
|
+
使用宿主工具:
|
|
211
|
+
|
|
212
|
+
~~~text
|
|
213
|
+
eval_submit
|
|
214
|
+
eval_test
|
|
215
|
+
~~~
|
|
216
|
+
|
|
217
|
+
### eval_submit
|
|
218
|
+
|
|
219
|
+
把当前工作区源码快照提交到宿主侧不可读目录。每次提交独立保留,不覆盖旧提交。提交本身不改变分数。
|
|
220
|
+
|
|
221
|
+
### eval_test
|
|
222
|
+
|
|
223
|
+
由宿主对最新提交快照进行构建和隐藏仿真评估,返回:
|
|
224
|
+
|
|
225
|
+
~~~text
|
|
226
|
+
stage
|
|
227
|
+
t_can
|
|
228
|
+
completed
|
|
229
|
+
error
|
|
230
|
+
~~~
|
|
231
|
+
|
|
232
|
+
只有 eval_test 会更新分数。第一次 eval_test 之前必须先 eval_submit,否则 stage = no_submission。
|
|
233
|
+
|
|
234
|
+
阶段枚举:
|
|
235
|
+
|
|
236
|
+
~~~text
|
|
237
|
+
no_submission
|
|
238
|
+
compile
|
|
239
|
+
infra
|
|
240
|
+
controller
|
|
241
|
+
simulation
|
|
242
|
+
scoring
|
|
243
|
+
completed
|
|
244
|
+
busy
|
|
245
|
+
~~~
|
|
246
|
+
|
|
247
|
+
最终排行分数不会返回给你,只用于赛后排名。
|
|
248
|
+
|
|
249
|
+
## 完成条件
|
|
250
|
+
|
|
251
|
+
当测试返回:
|
|
252
|
+
|
|
253
|
+
~~~
|
|
254
|
+
stage = completed
|
|
255
|
+
t_can >= 10.0
|
|
256
|
+
~~~
|
|
257
|
+
|
|
258
|
+
即任务完成。
|
|
259
|
+
|
|
260
|
+
完成后:
|
|
261
|
+
|
|
262
|
+
- 立即停止修改
|
|
263
|
+
- 立即总结你的实现
|
|
264
|
+
- 之后的提交和测试请求都会被拒绝
|
|
265
|
+
|
|
266
|
+
## 约束
|
|
267
|
+
|
|
268
|
+
- 只允许 C++17 + CMake + C++ 标准库
|
|
269
|
+
- 仅 CPU,fp32
|
|
270
|
+
- 提交源码中不得出现 torch
|
|
271
|
+
- 评测环境无 Python/torch
|
|
272
|
+
- 无网络
|
|
273
|
+
- 不得修改仿真器或公开测试
|
|
274
|
+
- 不得提交编译产物
|
|
275
|
+
- 赛后人工审核:控制器必须是神经网络,且必须实际调用你实现的神经网络基础设施;手写控制律、查表、固定轨迹或外部依赖会被判无效
|
|
276
|
+
|
|
277
|
+
## 优化目标
|
|
278
|
+
|
|
279
|
+
从接近全向下的初始状态起摆,使三根杆全部进入竖直向上容差,并尽可能长时间保持。
|
|
280
|
+
|
|
281
|
+
这是一个控制目标,不是固定损失函数。环境不提供 reward/loss;你需要自己决定训练信号、损失、奖励或优化方式。
|
|
282
|
+
|
|
283
|
+
## 评分规则
|
|
284
|
+
|
|
285
|
+
任务从会话开始时刻计时。
|
|
286
|
+
|
|
287
|
+
### 测试请求与分数
|
|
288
|
+
|
|
289
|
+
提交源码本身不改变分数。
|
|
290
|
+
|
|
291
|
+
只有申请测试会更新当前分数:
|
|
292
|
+
|
|
293
|
+
~~~
|
|
294
|
+
无效测试 / 编译失败 / 基础设施失败 / 控制器失败 / 仿真失败 / 无提交:
|
|
295
|
+
score(t) = -1
|
|
296
|
+
|
|
297
|
+
有效测试:
|
|
298
|
+
score(t) = (min(t_can, t_tgt) - t_tgt) / t_tgt
|
|
299
|
+
~~~
|
|
300
|
+
|
|
301
|
+
其中:
|
|
302
|
+
|
|
303
|
+
~~~
|
|
304
|
+
t_tgt = 10.0 s
|
|
305
|
+
score(t) 的范围是 [-1, 0]
|
|
306
|
+
t_can = 0 时 score = -1
|
|
307
|
+
t_can >= t_tgt 时 score = 0
|
|
308
|
+
~~~
|
|
309
|
+
|
|
310
|
+
分数从测试请求时刻开始生效,直到下一次测试请求。
|
|
311
|
+
|
|
312
|
+
第一次测试请求之前,score = -1。
|
|
313
|
+
|
|
314
|
+
### 最终分数
|
|
315
|
+
|
|
316
|
+
最终分数是时间积分,不除以总时长:
|
|
317
|
+
|
|
318
|
+
~~~
|
|
319
|
+
final = integral_0^T_done score(t) dt
|
|
320
|
+
~~~
|
|
321
|
+
|
|
322
|
+
T_done 是第一次返回 completed 的测试请求时间。
|
|
323
|
+
|
|
324
|
+
因此:
|
|
325
|
+
|
|
326
|
+
- 越早达到完成条件,负分区间越短,最终分数越高
|
|
327
|
+
- 拖得越久,负分区间越长
|
|
328
|
+
- 完成后停止积分
|
|
329
|
+
- 未完成不参与最终排名
|
|
330
|
+
|
|
331
|
+
### 完成时
|
|
332
|
+
|
|
333
|
+
当测试返回 completed 时:
|
|
334
|
+
|
|
335
|
+
- 当前 score 固定为 0
|
|
336
|
+
- 时间积分立即停止
|
|
337
|
+
- 之后的提交和测试全部拒绝
|
|
338
|
+
- 不会再有新的分数变化
|
|
339
|
+
|
|
340
|
+
### 测试结果与最终分数
|
|
341
|
+
|
|
342
|
+
测试结果会返回:
|
|
343
|
+
|
|
344
|
+
- stage
|
|
345
|
+
- t_can
|
|
346
|
+
- completed
|
|
347
|
+
- error
|
|
348
|
+
|
|
349
|
+
最终排行分数不会返回给你,只用于赛后排名。
|
|
350
|
+
|
|
351
|
+
## 建议工作顺序
|
|
352
|
+
|
|
353
|
+
1. 阅读公开测试和 tools/sim 协议
|
|
354
|
+
2. 先实现最小可用的 tensor/算子/反向/优化器
|
|
355
|
+
3. 跑通 nn_selftest
|
|
356
|
+
4. 用 tools/sim 采集数据并训练控制器
|
|
357
|
+
5. 反复提交和测试,优先把 t_can 提升到 10 秒
|
|
358
|
+
6. 完成后立即总结
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#include <algorithm>
|
|
2
|
+
#include <array>
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <cstdio>
|
|
5
|
+
#include <cstring>
|
|
6
|
+
#include <iostream>
|
|
7
|
+
#include <random>
|
|
8
|
+
#include <sstream>
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
constexpr int N = 3;
|
|
14
|
+
constexpr double DT = 0.01;
|
|
15
|
+
constexpr int SUB = 4;
|
|
16
|
+
constexpr int MAX_STEPS = 3000;
|
|
17
|
+
constexpr double PI = 3.14159265358979323846;
|
|
18
|
+
constexpr double GRAV = 9.81;
|
|
19
|
+
constexpr double DAMP = 0.01;
|
|
20
|
+
constexpr double TAU_MAX = 18.0;
|
|
21
|
+
const std::array<double, N> MASS{{1.0, 1.0, 1.0}};
|
|
22
|
+
const std::array<double, N> LEN{{0.5, 0.5, 0.5}};
|
|
23
|
+
const std::array<double, N> R{{0.25, 0.25, 0.25}};
|
|
24
|
+
const std::array<double, N> INERTIA{{MASS[0]*LEN[0]*LEN[0]/12.0, MASS[1]*LEN[1]*LEN[1]/12.0, MASS[2]*LEN[2]*LEN[2]/12.0}};
|
|
25
|
+
const std::array<double, N> GCOEF{{MASS[0]*R[0] + LEN[0]*(MASS[1]+MASS[2]),
|
|
26
|
+
MASS[1]*R[1] + LEN[1]*MASS[2],
|
|
27
|
+
MASS[2]*R[2]}};
|
|
28
|
+
|
|
29
|
+
struct Vec { std::array<double,N> v{}; };
|
|
30
|
+
struct State { std::array<double,N> theta{}; std::array<double,N> omega{}; };
|
|
31
|
+
|
|
32
|
+
double wrap(double x) {
|
|
33
|
+
while (x <= -PI) x += 2*PI;
|
|
34
|
+
while (x > PI) x -= 2*PI;
|
|
35
|
+
return x;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static std::array<std::array<double,N>,N> mass_matrix(const std::array<double,N>& q) {
|
|
39
|
+
std::array<std::array<double,N>,N> M{};
|
|
40
|
+
for (int i=0;i<N;i++) for (int j=0;j<N;j++) {
|
|
41
|
+
if (i==j) {
|
|
42
|
+
double tail=0; for (int k=i+1;k<N;k++) tail += MASS[k];
|
|
43
|
+
M[i][j] = INERTIA[i] + MASS[i]*R[i]*R[i] + LEN[i]*LEN[i]*tail;
|
|
44
|
+
} else {
|
|
45
|
+
int a=std::min(i,j), b=std::max(i,j);
|
|
46
|
+
M[i][j] = LEN[a] * GCOEF[b] * std::cos(q[i]-q[j]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return M;
|
|
50
|
+
}
|
|
51
|
+
static void solve3(const std::array<std::array<double,N>,N>& A, const std::array<double,N>& b, std::array<double,N>& x) {
|
|
52
|
+
double a[3][4];
|
|
53
|
+
for(int i=0;i<3;i++){ for(int j=0;j<3;j++) a[i][j]=A[i][j]; a[i][3]=b[i]; }
|
|
54
|
+
for(int c=0;c<3;c++){
|
|
55
|
+
int p=c; for(int r=c+1;r<3;r++) if(std::fabs(a[r][c])>std::fabs(a[p][c])) p=r;
|
|
56
|
+
if(p!=c) for(int j=0;j<4;j++) std::swap(a[c][j],a[p][j]);
|
|
57
|
+
double d=a[c][c]; if(std::fabs(d)<1e-15) d=(d<0?-1e-15:1e-15);
|
|
58
|
+
for(int j=c;j<4;j++) a[c][j]/=d;
|
|
59
|
+
for(int r=0;r<3;r++) if(r!=c){ double f=a[r][c]; for(int j=c;j<4;j++) a[r][j]-=f*a[c][j]; }
|
|
60
|
+
}
|
|
61
|
+
for(int i=0;i<3;i++) x[i]=a[i][3];
|
|
62
|
+
}
|
|
63
|
+
static void deriv(const State& s, const std::array<double,N>& tau, State& out) {
|
|
64
|
+
auto M = mass_matrix(s.theta);
|
|
65
|
+
std::array<double,N> C{}, G{}, Q{}, rhs{};
|
|
66
|
+
for(int i=0;i<N;i++){
|
|
67
|
+
double ci=0;
|
|
68
|
+
for(int j=0;j<N;j++) if(j!=i) ci += M[i][j] * std::sin(s.theta[i]-s.theta[j]) * s.omega[j]*s.omega[j];
|
|
69
|
+
C[i]=ci;
|
|
70
|
+
G[i]=GRAV*std::sin(s.theta[i])*GCOEF[i];
|
|
71
|
+
}
|
|
72
|
+
// joint damping: b * D^T D omega, D lower bidiagonal with -1 previous
|
|
73
|
+
for(int i=0;i<N;i++){
|
|
74
|
+
double e=s.omega[i] - (i>0?s.omega[i-1]:0.0);
|
|
75
|
+
Q[i] -= DAMP*e;
|
|
76
|
+
}
|
|
77
|
+
for(int i=0;i<N;i++) rhs[i]=tau[i]-C[i]-G[i]+Q[i];
|
|
78
|
+
solve3(M, rhs, out.omega);
|
|
79
|
+
out.theta = s.omega;
|
|
80
|
+
}
|
|
81
|
+
} // namespace
|
|
82
|
+
|
|
83
|
+
int main(int argc, char** argv) {
|
|
84
|
+
bool eval=false; int seed=0;
|
|
85
|
+
for(int i=1;i<argc;i++){ std::string a=argv[i]; if(a=="--eval") eval=true; else if(a=="--seed" && i+1<argc) seed=std::atoi(argv[++i]); }
|
|
86
|
+
std::mt19937 rng(seed);
|
|
87
|
+
std::uniform_real_distribution<double> u(-1.0,1.0);
|
|
88
|
+
State s;
|
|
89
|
+
int steps=0; bool ready=false;
|
|
90
|
+
auto reset=[&](){ steps=0; ready=true; if(eval){ s.theta={0.013,-0.008,0.021}; s.omega={0,0,0}; } else { s.theta={u(rng)*0.05,u(rng)*0.05,u(rng)*0.05}; s.omega={u(rng)*0.02,u(rng)*0.02,u(rng)*0.02}; } };
|
|
91
|
+
// zero state
|
|
92
|
+
s.theta={0,0,0}; s.omega={0,0,0};
|
|
93
|
+
std::string line;
|
|
94
|
+
while(std::getline(std::cin,line)){
|
|
95
|
+
if(line.empty()) continue;
|
|
96
|
+
if(line.find("reset")!=std::string::npos){ reset(); std::cout << "{\"obs\":[" << wrap(s.theta[0]-PI) << "," << wrap(s.theta[1]-PI) << "," << wrap(s.theta[2]-PI) << "]}\n" << std::flush; continue; }
|
|
97
|
+
if(line.find("step")!=std::string::npos){
|
|
98
|
+
if(!ready || steps>=MAX_STEPS){ std::cout << "{\"error\":\"episode_finished\"}\n" << std::flush; continue; }
|
|
99
|
+
// parse torque array
|
|
100
|
+
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); } }
|
|
101
|
+
u1=std::max(-1.0,std::min(1.0,u1)); u3=std::max(-1.0,std::min(1.0,u3));
|
|
102
|
+
std::array<double,N> tau{{u1*TAU_MAX,0.0,u3*TAU_MAX}};
|
|
103
|
+
// one control step with 4 RK4 substeps
|
|
104
|
+
double h=DT/SUB;
|
|
105
|
+
for(int sub=0;sub<SUB;sub++){
|
|
106
|
+
State k1=s,k2=s,k3=s,k4=s; State d{};
|
|
107
|
+
deriv(k1,tau,d); for(int i=0;i<N;i++){ k2.theta[i]=s.theta[i]+0.5*h*d.theta[i]; k2.omega[i]=s.omega[i]+0.5*h*d.omega[i]; }
|
|
108
|
+
deriv(k2,tau,d); for(int i=0;i<N;i++){ k3.theta[i]=s.theta[i]+0.5*h*d.theta[i]; k3.omega[i]=s.omega[i]+0.5*h*d.omega[i]; }
|
|
109
|
+
deriv(k3,tau,d); for(int i=0;i<N;i++){ k4.theta[i]=s.theta[i]+h*d.theta[i]; k4.omega[i]=s.omega[i]+h*d.omega[i]; }
|
|
110
|
+
deriv(k4,tau,d);
|
|
111
|
+
// need derivatives at all; use helper recompute arrays
|
|
112
|
+
State ds1{},ds2{},ds3{},ds4{};
|
|
113
|
+
deriv(k1,tau,ds1); deriv(k2,tau,ds2); deriv(k3,tau,ds3); deriv(k4,tau,ds4);
|
|
114
|
+
for(int i=0;i<N;i++){
|
|
115
|
+
s.theta[i] += h/6.0*(ds1.theta[i]+2*ds2.theta[i]+2*ds3.theta[i]+ds4.theta[i]);
|
|
116
|
+
s.omega[i] += h/6.0*(ds1.omega[i]+2*ds2.omega[i]+2*ds3.omega[i]+ds4.omega[i]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
steps++;
|
|
120
|
+
std::cout << "{\"obs\":[" << wrap(s.theta[0]-PI) << "," << wrap(s.theta[1]-PI) << "," << wrap(s.theta[2]-PI) << "]}\n" << std::flush;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
std::cout << "{\"error\":\"bad_command\"}\n" << std::flush;
|
|
124
|
+
}
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Plugin wrapper for the standalone Neural-Pendulum evaluator."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
HERE = Path(__file__).resolve().parent
|
|
9
|
+
STANDALONE = HERE.parent / 'neural-pendulum-eval' / 'run_eval.py'
|
|
10
|
+
|
|
11
|
+
if not STANDALONE.is_file():
|
|
12
|
+
print('neural-pendulum-eval/run_eval.py missing; copy the standalone evaluator folder first', file=sys.stderr)
|
|
13
|
+
raise SystemExit(1)
|
|
14
|
+
|
|
15
|
+
os.execv(sys.executable, [sys.executable, '-X', 'utf8', str(STANDALONE), *sys.argv[1:]])
|
package/lib/client/index.js
CHANGED
|
@@ -94,7 +94,9 @@ window.__ModuleLoader__.load({
|
|
|
94
94
|
pipProxyUrl: "",
|
|
95
95
|
cifarProxyUrl: "",
|
|
96
96
|
cifarMirror: "toronto",
|
|
97
|
-
customCifarUrl: ""
|
|
97
|
+
customCifarUrl: "",
|
|
98
|
+
parallelEval: false,
|
|
99
|
+
taskId: ""
|
|
98
100
|
};
|
|
99
101
|
const box = {
|
|
100
102
|
border: "1px solid var(--vscode-panel-border, #3c3c3c)",
|
|
@@ -159,7 +161,9 @@ window.__ModuleLoader__.load({
|
|
|
159
161
|
pipProxyUrl: typeof value?.pipProxyUrl === "string" ? value.pipProxyUrl : "",
|
|
160
162
|
cifarProxyUrl: typeof value?.cifarProxyUrl === "string" ? value.cifarProxyUrl : "",
|
|
161
163
|
cifarMirror: typeof value?.cifarMirror === "string" && value.cifarMirror !== "" ? value.cifarMirror : "toronto",
|
|
162
|
-
customCifarUrl: typeof value?.customCifarUrl === "string" ? value.customCifarUrl : ""
|
|
164
|
+
customCifarUrl: typeof value?.customCifarUrl === "string" ? value.customCifarUrl : "",
|
|
165
|
+
parallelEval: value?.parallelEval === true,
|
|
166
|
+
taskId: typeof value?.taskId === "string" ? value.taskId : ""
|
|
163
167
|
};
|
|
164
168
|
}
|
|
165
169
|
/** The settings card. */
|
|
@@ -237,8 +241,15 @@ window.__ModuleLoader__.load({
|
|
|
237
241
|
return () => clearInterval(timer);
|
|
238
242
|
}, []);
|
|
239
243
|
(0, react.useEffect)(() => {
|
|
240
|
-
if (state !== null && state.tasks.length > 0 && (taskId === "" || !state.tasks.some((task) => task.id === taskId)))
|
|
241
|
-
|
|
244
|
+
if (state !== null && state.tasks.length > 0 && (taskId === "" || !state.tasks.some((task) => task.id === taskId))) {
|
|
245
|
+
if (draft.taskId !== "" && state.tasks.some((task) => task.id === draft.taskId)) setTaskId(draft.taskId);
|
|
246
|
+
else setTaskId(state.tasks[0].id);
|
|
247
|
+
}
|
|
248
|
+
}, [
|
|
249
|
+
state,
|
|
250
|
+
taskId,
|
|
251
|
+
draft.taskId
|
|
252
|
+
]);
|
|
242
253
|
const ready = snapshot.status === "ready" && snapshot.writable;
|
|
243
254
|
const save = async (patch) => {
|
|
244
255
|
if (!ready || snapshot.revision === void 0) return;
|
|
@@ -274,6 +285,7 @@ window.__ModuleLoader__.load({
|
|
|
274
285
|
};
|
|
275
286
|
const selectTask = (next) => {
|
|
276
287
|
setTaskId(next);
|
|
288
|
+
save({ taskId: next });
|
|
277
289
|
try {
|
|
278
290
|
localStorage.setItem("evalGuard.taskId", next);
|
|
279
291
|
} catch {}
|
|
@@ -332,6 +344,7 @@ window.__ModuleLoader__.load({
|
|
|
332
344
|
};
|
|
333
345
|
const install = async () => {
|
|
334
346
|
if (workspaceId === "" || taskId === "") return;
|
|
347
|
+
await save({ taskId });
|
|
335
348
|
setError("");
|
|
336
349
|
setNotice("");
|
|
337
350
|
setJobNotice("");
|
|
@@ -368,6 +381,7 @@ window.__ModuleLoader__.load({
|
|
|
368
381
|
};
|
|
369
382
|
const evaluate = async () => {
|
|
370
383
|
if (workspaceId === "" || taskId === "") return;
|
|
384
|
+
await save({ taskId });
|
|
371
385
|
setError("");
|
|
372
386
|
setNotice("");
|
|
373
387
|
setJobNotice("");
|
|
@@ -814,6 +828,23 @@ window.__ModuleLoader__.load({
|
|
|
814
828
|
" ",
|
|
815
829
|
t("settings.timeLimit")
|
|
816
830
|
] }),
|
|
831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
832
|
+
style: { marginLeft: 16 },
|
|
833
|
+
children: [
|
|
834
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
835
|
+
type: "checkbox",
|
|
836
|
+
checked: draft.parallelEval,
|
|
837
|
+
disabled: !ready,
|
|
838
|
+
onChange: (event) => void save({ parallelEval: event.target.checked })
|
|
839
|
+
}),
|
|
840
|
+
" ",
|
|
841
|
+
t("settings.parallelEval")
|
|
842
|
+
]
|
|
843
|
+
}),
|
|
844
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
845
|
+
style: note,
|
|
846
|
+
children: t("settings.parallelEvalHint")
|
|
847
|
+
}),
|
|
817
848
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
818
849
|
type: "number",
|
|
819
850
|
min: 1,
|
|
@@ -998,6 +1029,8 @@ window.__ModuleLoader__.load({
|
|
|
998
1029
|
"settings.runningForce": "强行开启",
|
|
999
1030
|
"settings.timeLimit": "开启最长时长",
|
|
1000
1031
|
"settings.timeLimitHint": "从每条用户消息发送时开始计时;到点只终止当前轮次,并递归清理子代理与后台任务。",
|
|
1032
|
+
"settings.parallelEval": "多 agent 并行评测",
|
|
1033
|
+
"settings.parallelEvalHint": "启用后,安装任务时会在提示词中提醒 agent:运行环境可能因其他 agent 的行为而波动,需要自行适应。",
|
|
1001
1034
|
"settings.minutes": "分钟",
|
|
1002
1035
|
"settings.forbidden": "额外禁止路径",
|
|
1003
1036
|
"settings.forbiddenHint": "每行一个绝对路径;# 开头为注释;! 开头表示在禁止范围内放开该子路径;按行顺序生效。默认空。",
|
|
@@ -1041,6 +1074,8 @@ window.__ModuleLoader__.load({
|
|
|
1041
1074
|
"settings.recent": "最近事件",
|
|
1042
1075
|
"settings.none": "暂无。",
|
|
1043
1076
|
"settings.warningTitle": "评测守护警告",
|
|
1077
|
+
"settings.cifarProxy": "CIFAR 下载代理",
|
|
1078
|
+
"settings.cifarMirror": "CIFAR 下载镜像",
|
|
1044
1079
|
"settings.close": "关闭"
|
|
1045
1080
|
};
|
|
1046
1081
|
const en = {
|
|
@@ -1053,6 +1088,8 @@ window.__ModuleLoader__.load({
|
|
|
1053
1088
|
"settings.runningForce": "Force enable",
|
|
1054
1089
|
"settings.timeLimit": "Enable time limit",
|
|
1055
1090
|
"settings.timeLimitHint": "The clock starts at each user message; at the deadline only the current turn is cancelled, and subagents/jobs are cleaned up recursively.",
|
|
1091
|
+
"settings.parallelEval": "Parallel agent evaluation",
|
|
1092
|
+
"settings.parallelEvalHint": "When enabled, install prompts warn the agent that the runtime may fluctuate because other agents share the machine; it must adapt.",
|
|
1056
1093
|
"settings.minutes": "minutes",
|
|
1057
1094
|
"settings.forbidden": "Extra forbidden paths",
|
|
1058
1095
|
"settings.forbiddenHint": "One absolute path per line; # comments; ! re-allows a subpath; later lines win. Empty by default.",
|