Skip to content

Give White's test statistical power, reject non-square games, make the dependency pins effective under npm - #7

Open
shehio wants to merge 1 commit into
developfrom
fix/white-test-and-npm-overrides
Open

Give White's test statistical power, reject non-square games, make the dependency pins effective under npm#7
shehio wants to merge 1 commit into
developfrom
fix/white-test-and-npm-overrides

Conversation

@shehio

@shehio shehio commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Summary

Three unrelated but individually small correctness fixes, all in code the test suite never executed. Two of the three modules could not even be required.

White's Test Had No Statistical Power

The auxiliary regression used the raw residuals as its dependent variable:

return F_test_with_significance(y, residuals);   // y = fitted values

White's test has to explain the variance of the errors, so the dependent variable must be û². Regressing û itself can never work: OLS residuals are orthogonal to the fitted values by construction, so the slope is zero whether the errors are homoskedastic or not.

Measured on two 200-point samples (y = 2 + 3x + s(x)·e, seeded so the numbers are reproducible):

Sample Old statistic New statistic
Constant error variance 0.7286 2.7130
Variance growing with x 0.1450 19.4002
5% critical value 3.889 — F(1, 198) 5.991 — χ²(2)

The old statistic scored lower on the heteroskedastic sample than on the homoskedastic one, and never came close to rejecting either. Across 400 replications it rejected 0% of the time in both cases; the corrected statistic has 4.5% size and 100% power on this design.

The returned statistic is now the standard Lagrange multiplier LM = n·R², distributed χ²(2) here. The previous F was also built with the wrong degrees of freedom — p = 2 for a three-parameter regression, giving DFM = 1 instead of 2.

Neither Econometrics Module Could Be Loaded

Both econometrics/index.js and econometrics/white.js opened with require('./../data/index.js'). There is no data/ directory — the module lives at src/economic-data/:

$ node -e "require('./econometrics/white.js')"
LOAD FAILED: Cannot find module './../data/index.js'

white.js also exported nothing. The require now points at the right path and sits inside quick_test, its only consumer, because src/economic-data/index.js issues a WorldBank HTTP request at module load — importing it at the top would put a network call in the test suite.

Lemke-Howson Silently Mishandles Non-Square Games

find_equilibrium zips player one's probabilities with player two's using a.map(...), which is bounded by player one's strategy count. I swept 300 random games at each shape from 2×2 to 4×4:

Shape Correct Silently wrong shape Threw
Square (2×2, 3×3, 4×4) 900/900 0 0
Wider than tall (2×3, 2×4, 3×4) 0 900/900 0
Taller than wide (3×2, 4×2, 4×3) 0 0 900/900

A 2×3 game returns a two-element mixed strategy for a player who has three strategies — plausible-looking and wrong. A 3×2 game fails deep inside normalization with Matrix has probabilities that are not numbers, which points nowhere near the cause. solve() now rejects non-square input up front with a message that names both counts.

Note: the reported symptom for this one was NaN. That did not reproduce — 0 of 2700 random games produced NaN on any shape. The defect is real but the failure mode is silent truncation, not NaN.

The Dependency Pins Are Not Effective Under npm

package.json pins nine transitive dependencies under resolutions, which is a Yarn key; npm reads overrides. Resolving the manifest with npm and no lockfile:

Package Pinned to With resolutions With overrides
tough-cookie ^4.1.3 2.5.0 4.1.4
tmp ^0.2.6 0.0.33 0.2.7
flatted ^3.4.2 2.0.2 3.4.4
braces ^3.0.3 3.0.3 and 2.3.2 3.0.3
qs ^6.14.2 6.15.3 and 6.5.5 6.15.3

With resolutions the result is byte-identical to having no pins at all.

One correction worth stating: the pins are not currently inert in CI. The repo commits a yarn.lock that Yarn generated while honouring resolutions, and npm imports it, so a fresh CI checkout does get the safe versions today. The pins hold only because that lockfile is there — they contribute nothing on their own, and would stop holding the moment yarn.lock is dropped, regenerated by npm, or misses a newly added transitive dependency.

overrides is therefore added, mirroring resolutions. Both keys are kept: yarn.lock is still committed and still load-bearing, so removing resolutions now would be a regression for anyone running Yarn. If yarn.lock is ever dropped, drop resolutions with it — two lists that can drift apart is not a good resting state.

Tests

econometrics/test/white.test.ts is new and is the first test to reach the econometrics package at all; it pins the size and the power of the statistic with a seeded generator. One case was added to the Lemke-Howson suite for non-square input.

Suite: 34 tests / 10 suites before → 37 tests / 11 suites after, all passing.

Left Alone

.github/workflows/ — there is an open PR against it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01THEv7BFJd31NjvBRehbZPL

White's test regressed the raw residuals on the fitted values, but OLS
residuals are orthogonal to the fitted values by construction, so the
auxiliary regression had nothing to find whatever the error variance did.
It now regresses the SQUARED residuals and returns the standard Lagrange
multiplier LM = n * R^2 rather than an F built with the wrong degrees of
freedom (p = 2 for a three-parameter regression).

On a 200-point sample the old statistic scored 0.7286 with constant error
variance and 0.1450 when the variance grew with x - the heteroskedastic
sample scored lower, and neither came near the 3.889 critical value. The
corrected statistic gives 2.7130 and 19.4002 against a chi-squared(2)
critical value of 5.991.

Neither econometrics module could be loaded at all: both required
'./../data/index.js', a path that does not exist, and white.js exported
nothing. The require now points at src/economic-data and sits inside
quick_test, since that module issues an HTTP request when it is loaded.

solve() silently dropped player two's extra probabilities on a 2 x 3 game
and failed inside normalization on a 3 x 2 one, because find_equilibrium
zips the two probability vectors with a.map. Non-square games are now
rejected up front.

package.json pinned transitive dependencies under 'resolutions', which npm
ignores. Resolving the manifest with npm and no lockfile leaves
tough-cookie at 2.5.0, tmp at 0.0.33, flatted at 2.0.2 and a nested
braces 2.3.2; under 'overrides' every pin takes effect. Both keys are kept
because the committed yarn.lock is currently what makes the pins hold.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant