exp+fix: 轮次31——确定界连续认证抓到段内碰撞,但单段P50 22.9ms进不了20ms;自适应cap收益经交错复测撤回
- Kin.cert_lever 用三角不等式给全姿态位移界(替代 JS 随机采样×1.05),界量级 5.93–48.86 mm/整步 - Kin.certify_segments 批量前沿二分+每根查询预算:48 固定段中预算32判安全37/碰撞1/未知拒10, 第180题1.08–1.10s漏检段以5次查询判碰撞;未知只拒不放 - 修 seg_seg_dist 第二条退化成点不投影(模型内退化胶囊686受影响);但7586个加密点的组最小间隙 逐位不变、2000样本JS对拍仍 PASS → 不解释轮次30漏检、不改既有成绩 - 负结论:B=1 单段认证 P50 22.93/P90 58.43/P99 101.57 ms,432次中225次>20ms; 自适应宽相位 cap 交错复测耗时无收益,先前1.6×为GPU顺序漂移,已在日志撤回 - 成本模型 L=ceil(log2(bound/(ga+gb)))、查询=2^L+1 与实测对齐(安全段2–17次,未知段33–65次) - 新增8项段认证测试+1项回放测试(14→23项通过);变异实测证明张量 max_depth 回绕会误放行
This commit is contained in:
+126
-3
@@ -153,11 +153,124 @@ def replay_dense(path, indices, max_step_deg=0.05, device='cpu', out=None):
|
||||
return result
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def replay_certificates(path, indices, device='cpu', out=None, repeats=5, extra_segments=()):
|
||||
saved = torch.load(path, map_location='cpu', weights_only=True)
|
||||
kin = Kin(device=device, dtype=torch.float64)
|
||||
if saved.get('kin_fp') != kin.fp:
|
||||
raise ValueError('trace/kin fingerprint mismatch')
|
||||
if not indices or min(indices) < 0 or max(indices) >= len(saved['tasks']) or repeats < 1:
|
||||
raise ValueError('valid task indices and positive repeats required')
|
||||
rows, offset = [], 0
|
||||
for _, per, count in saved['batches']:
|
||||
for index in indices:
|
||||
if not offset <= index < offset + count:
|
||||
continue
|
||||
b = index - offset
|
||||
nstep = min(per['traj'].shape[1] - 1, math.floor((float(per['limit'][b]) + 1e-6) / DT))
|
||||
if torch.isfinite(per['t_done'][b]):
|
||||
nstep = min(nstep, round(float(per['t_done'][b]) / DT))
|
||||
traj = per['traj'][b, :nstep + 1]
|
||||
moving = ((traj[1:] - traj[:-1]).abs().max(-1).values > 1e-6).nonzero().flatten().tolist()
|
||||
if not moving:
|
||||
continue
|
||||
plans = per['plans']
|
||||
nearest, nearest_gap = None, float('inf')
|
||||
for it in moving:
|
||||
pi = int(torch.searchsorted(plans['it'], it, right=True)) - 1
|
||||
step = it - int(plans['it'][pi])
|
||||
if 0 <= step < plans['commit'].shape[2] and plans['certified'][b, pi, step]:
|
||||
gap = float(plans['gaps'][b, pi, [step, plans['commit'].shape[2] + step]].min())
|
||||
if gap < nearest_gap:
|
||||
nearest, nearest_gap = it, gap
|
||||
selected = {moving[0], moving[len(moving) // 2], moving[-1]}
|
||||
if nearest is not None:
|
||||
selected.add(nearest)
|
||||
for task_index, it in extra_segments:
|
||||
if task_index == index:
|
||||
if it not in moving:
|
||||
raise ValueError('extra segment must be a moving segment before deadline or arrival')
|
||||
selected.add(it)
|
||||
for it in sorted(selected):
|
||||
rows.append({'i': index, 'start_it': it, 'a': traj[it].tolist(), 'b': traj[it + 1].tolist()})
|
||||
offset += count
|
||||
if not rows:
|
||||
raise ValueError('no moving segments in selected tasks')
|
||||
a = torch.tensor([r['a'] for r in rows], dtype=torch.float64, device=device)
|
||||
b = torch.tensor([r['b'] for r in rows], dtype=torch.float64, device=device)
|
||||
started = time.perf_counter()
|
||||
print('certificate replay: %d tasks, %d fixed moving segments' % (len(indices), len(rows)), flush=True)
|
||||
summaries = {}
|
||||
for budget in (2, 8, 32):
|
||||
result = kin.certify_segments(a, b, max_queries=budget)
|
||||
summaries[str(budget)] = {key: int(result[key].sum()) for key in ('safe', 'collision', 'unresolved', 'outside_box')}
|
||||
summaries[str(budget)]['queries_p50'] = float(result['queries'].double().quantile(0.5))
|
||||
summaries[str(budget)]['queries_max'] = int(result['queries'].max())
|
||||
for j, row in enumerate(rows):
|
||||
row.setdefault('budgets', {})[str(budget)] = {key: value[j].item() for key, value in result.items()}
|
||||
print('budget %d: %s' % (budget, summaries[str(budget)]), flush=True)
|
||||
latencies = []
|
||||
for j, row in enumerate(rows):
|
||||
aa, bb = a[j:j + 1], b[j:j + 1]
|
||||
discrete = kin.gaps(torch.cat([aa, aa + 0.5 * (bb - aa), bb]), 0.5)['gm'].min(-1).values
|
||||
row['previous_midpoint_endpoint_gap_mm'] = discrete.tolist()
|
||||
nseg = max(2, 2 * math.ceil(float((bb - aa).abs().max()) / 0.02))
|
||||
alpha = torch.linspace(0, 1, nseg + 1, dtype=torch.float64, device=device)
|
||||
points = aa + alpha[:, None] * (bb - aa)
|
||||
dense = torch.cat([kin.gaps(chunk, 0.5)['gm'].min(-1).values for chunk in points.split(64)])
|
||||
row['dense_points'] = len(points)
|
||||
row['dense_min_gap_mm'] = float(dense.min())
|
||||
row['dense_min_fraction'] = float(alpha[dense.argmin()])
|
||||
for _ in range(3):
|
||||
kin.certify_segments(aa, bb)
|
||||
elapsed = []
|
||||
for _ in range(repeats):
|
||||
if a.is_cuda:
|
||||
torch.cuda.synchronize(a.device)
|
||||
t0 = time.perf_counter()
|
||||
result = kin.certify_segments(aa, bb)
|
||||
if a.is_cuda:
|
||||
torch.cuda.synchronize(a.device)
|
||||
elapsed.append((time.perf_counter() - t0) * 1000)
|
||||
row['batch_single_match'] = all(value.item() == row['budgets']['32'][key] for key, value in result.items())
|
||||
row['latency_ms'] = elapsed
|
||||
latencies.extend(elapsed)
|
||||
print('segment %d/%d task %d it %d dense %.6f mm queries %d latency %.2f ms' %
|
||||
(j + 1, len(rows), row['i'], row['start_it'], row['dense_min_gap_mm'],
|
||||
row['budgets']['32']['queries'], sum(elapsed) / len(elapsed)), flush=True)
|
||||
elapsed = torch.tensor(latencies, dtype=torch.float64)
|
||||
result = {
|
||||
'trace': str(path), 'tasks_sha256': saved.get('tasks_sha256'), 'kin_fp': kin.fp,
|
||||
'device': str(device), 'gpu': torch.cuda.get_device_name(a.device) if a.is_cuda else None,
|
||||
'torch_version': str(torch.__version__), 'cpu_threads': torch.get_num_threads(), 'dtype': 'float64',
|
||||
'indices': indices, 'extra_segments': list(extra_segments),
|
||||
'selection': 'First/middle/last moving segment and minimum original certified gap per task; plus explicit extra_segments.',
|
||||
'cap_mm': 20.0, 'eps_mm': 1e-4, 'max_depth': 12, 'dense_max_step_deg': 0.01,
|
||||
'timing': {'scope': 'B=1 certificate only, resident inputs, 3 warmups per segment; synchronized wall time, not policy or scheduling latency.',
|
||||
'repeats_per_segment': repeats, 'calls': len(latencies),
|
||||
'p50_ms': float(elapsed.quantile(0.5)), 'p99_ms': float(elapsed.quantile(0.99)),
|
||||
'max_ms': float(elapsed.max()), 'over_20ms': int((elapsed > 20).sum())},
|
||||
'budgets': summaries,
|
||||
'dense_collision_segments': sum(r['dense_min_gap_mm'] <= 0 for r in rows),
|
||||
'accepted_dense_collision_segments': sum(r['dense_min_gap_mm'] <= 0 and r['budgets']['32']['safe'] for r in rows),
|
||||
'batch_single_match': all(r['batch_single_match'] for r in rows),
|
||||
'scope': 'Fixed recorded segments, not a new rollout. Dense replay is finite sampling; floating-point certification error is not formally bounded.',
|
||||
'wall_s': time.perf_counter() - started, 'rows': rows,
|
||||
}
|
||||
if out:
|
||||
with open(out, 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, indent=2, allow_nan=False)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--ckpt')
|
||||
ap.add_argument('--trace')
|
||||
ap.add_argument('--dense_indices', help='逗号分隔的任务下标;回放实际轨迹,不依赖原 col 标记')
|
||||
ap.add_argument('--cert_indices', help='固定实际段的连续认证、加密对照和单段计时')
|
||||
ap.add_argument('--cert_segments', default='', help='额外指定任务号:零基段号,逗号分隔')
|
||||
ap.add_argument('--repeats', type=int, default=5)
|
||||
ap.add_argument('--max_step_deg', type=float, default=0.05)
|
||||
ap.add_argument('--samples', default='ml/data/samples')
|
||||
ap.add_argument('--split', default='val')
|
||||
@@ -169,14 +282,24 @@ def main():
|
||||
ap.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu')
|
||||
ap.add_argument('--out', default=None)
|
||||
a = ap.parse_args()
|
||||
if a.dense_indices and a.cert_indices:
|
||||
ap.error('--dense_indices and --cert_indices are mutually exclusive')
|
||||
if a.cert_segments and not a.cert_indices:
|
||||
ap.error('--cert_segments requires --cert_indices')
|
||||
if a.trace:
|
||||
if a.dense_indices:
|
||||
if a.cert_indices:
|
||||
extra = [tuple(map(int, segment.split(':'))) for segment in a.cert_segments.split(',') if segment]
|
||||
indices = [int(i) for i in a.cert_indices.split(',')]
|
||||
if any(len(segment) != 2 or segment[0] not in indices for segment in extra):
|
||||
ap.error('--cert_segments requires task:segment pairs from --cert_indices')
|
||||
replay_certificates(a.trace, indices, a.device, a.out, a.repeats, extra)
|
||||
elif a.dense_indices:
|
||||
replay_dense(a.trace, [int(i) for i in a.dense_indices.split(',')], a.max_step_deg, a.device, a.out)
|
||||
else:
|
||||
replay_first_hits(a.trace, a.out)
|
||||
return
|
||||
if a.dense_indices:
|
||||
ap.error('--dense_indices requires --trace')
|
||||
if a.dense_indices or a.cert_indices:
|
||||
ap.error('--dense_indices and --cert_indices require --trace')
|
||||
if not a.ckpt:
|
||||
ap.error('--ckpt or --trace is required')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user