ADR-1484: float_ms_ssim takes the magnitude of a scale's terms before pow(); upstream raises a negative structure term to a fractional power and returns NaN on anti-correlated frames¶
- Status: Accepted
- Date: 2026-10-02
- Deciders: lusoris
- Tags:
upstream-divergence,ms-ssim,correctness,numerics,rc3,fork-local
Context¶
The reference for code the fork inherited from Netflix/vmaf is Netflix's source; a difference needs an ADR that names it and gives its size (see References). ADR-1033 (item 2) made this change as one of eleven guards and says neither what upstream does nor how far the fork is from it. ADR-1033 is accepted and is not edited; this ADR adds the comparison.
Upstream, libvmaf/src/feature/ms_ssim.c:294 at Netflix 9e48141b:
s, the structure term of a scale, is a correlation and is negative where the reference and the distorted picture are anti-correlated. The exponents are fractional, so pow() returns NaN and the frame's float_ms_ssim is NaN. The Rouse path of the same file already takes the magnitude of the same term (ms_ssim.c:86).
The fork, core/src/feature/ms_ssim.c, since PR #641 (a295f4a70, 2026-06-04):
msssim *= pow(fabs((double)l), (double)alphas[idx]) *
pow(fabs((double)c), (double)betas[idx]) *
pow(fabs((double)s), (double)gammas[idx]);
Decision¶
The fork keeps the magnitude before pow(). Where a scale's structure term is negative the fork returns a finite float_ms_ssim and upstream NaN; where all three terms are non-negative the expression is upstream's value.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| Restore upstream's expression | Same code as upstream | The score is NaN on such frames; in the fork a non-finite score fails the frame (ADR-1302), so the run would end with an error where it now returns a value | The magnitude is what the file's own Rouse path does |
Magnitude of s only, as the fork's upstream pull request does | The smallest change | None in value: l and c are built from non-negative means and standard deviations | Equal in value; the fork's line predates the pull request. At the sync that brings upstream's fix the fork takes upstream's line |
Consequences¶
- Measured size (audit of 2026-10-02: Netflix
cea2b4d8, whosems_ssim.cis9e48141b's, against the fork with its unintended differences reverted; GCC 16.2.1, x86-64, C API at%.17g, scalar and default dispatch): on the 1920x1080 checkerboard pair with 10 pixel squares upstream returns NaN on 3 of 3 frames and the fork 0.9895 (frame 2: 0.98950781588819681). No other fixture of the audit has a negative term: with this change and the separable decimation (ADR-0125) undone in a scratch build,float_ms_ssimwas identical to upstream on 18 fixtures and 4 option variants. - Upstream status: Netflix/vmaf#1665 (open, sent by the fork) takes the magnitude of
sonly. - Ends when upstream merges #1665. The allowlist entry of the upstream parity guard then goes stale and is removed.
References¶
- Fork PR #641 (
a295f4a70); ADR-1033 item 2. - Upstream:
libvmaf/src/feature/ms_ssim.c:86,:294at Netflix9e48141b; Netflix/vmaf#1665. - Source:
req(popup answer, 2026-10-02): "Netflix's source, deviations only by ADR".