Summaries drive decisions, and a wrong summary drives a wrong one. Adding up per-view totals counts an endpoint once for every view it fails in. A union summary counts each endpoint once, at its worst slack. Both are useful, and neither should be mistaken for the other.
views = {
"V1": [-0.05, -0.02, 0.10, 0.20],
"V2": [-0.03, -0.04, 0.05, 0.30],
"V3": [0.02, -0.01, 0.00, -0.06],
}
def stats(vals):
neg = [x for x in vals if x < 0]
return {"wns": round(min(vals), 3), "tns": round(sum(neg), 3), "nvp": len(neg)}
per_view = {v: stats(s) for v, s in views.items()}
assert per_view["V1"] == {"wns": -0.05, "tns": -0.07, "nvp": 2}
assert per_view["V2"] == {"wns": -0.04, "tns": -0.07, "nvp": 2}
assert per_view["V3"] == {"wns": -0.06, "tns": -0.07, "nvp": 2}
union_vals = [min(col) for col in zip(*views.values())] # worst slack per endpoint over all views
union = stats(union_vals)
assert union == {"wns": -0.06, "tns": -0.15, "nvp": 3}
naive_tns = round(sum(p["tns"] for p in per_view.values()), 3)
naive_nvp = sum(p["nvp"] for p in per_view.values())
assert (naive_tns, naive_nvp) == (-0.21, 6)
print("PASS: union", union, "; naive sum would say TNS", naive_tns, "and", naive_nvp, "violating endpoints")Row | WNS (ns) | TNS (ns) | Violating endpoints |
|---|---|---|---|
V1 | -0.05 | -0.07 | 2 |
V2 | -0.04 | -0.07 | 2 |
V3 | -0.06 | -0.07 | 2 |
Union (worst per endpoint) | -0.06 | -0.15 | 3 |
Naive sum of views | n/a | -0.21 | 6 |
Four endpoints in three views give a union of three violating endpoints with TNS of -0.15 ns, but adding the three per-view rows claims six violations and -0.21 ns. The union is the number of endpoints that must be fixed; the per-view rows show where to look. Overall WNS is the same either way, the minimum over everything (-0.06 ns).
Summaries state whether they are per view or union; the headline number is the union.
Setup and hold are never mixed in one total.
The trend of the union number over runs is the closure indicator, not any single run's number.
They show which view owns the worst slack: the union hides that, and it is the view where the fix should be checked first.