Interaction Screening (PSST)#
SuperGLM.screen_interactions ranks candidate interactions to help choose
which models to refit. It probes residual structure using the interaction
terms SuperGLM would fit for each pair. Five pair kinds screen today:
ti— spline x spline: ati()-style tensor deviation surface.spline_cat— spline x factor: a reference-coded deviation curve for each non-base level.numeric_cat— numeric x factor: a slope on the numeric for each non-base level.cat_cat— factor x factor: the cross-level cells of the two-way table.numeric_numeric— numeric x numeric: a single product tilt.
An OrderedCategorical margin screens as a spline on its mapped
level scores — the axis its own refit builds — so an OC x spline pair is a
ti row and an OC x factor pair a spline_cat row.
PSST stands for Penalized Smooth Score Test. It uses the fitted mains model to score candidate interactions without fitting a complete new model for each pair. Work includes passes over the observations and matrix calculations in each candidate’s coefficient space; its cost depends on both row count and term size.
model = SuperGLM(
family="poisson", features=feats, weight_semantics="frequency"
).fit_reml(X, y, sample_weight=exposure)
table = model.screen_interactions(X, y, sample_weight=exposure)
print(table.head())
# feature_a feature_b kind statistic z edf0 lambda0 n_cells approx
The test asks, for each pair: after profiling out what the pair’s own main
effects already explain, does the working residual carry structure shaped like
the block this pair would refit as? For the penalized kinds the probe is
evaluated at a ladder of complexity budgets (edf0, default (2, 4, 8, 16)
effective degrees of freedom) and each pair is ranked by its best
ranking score z, using the candidate’s Gaussian-reference mean and variance.
Different budgets probe simple and more flexible shapes. The unpenalized kinds have no
penalty to scan: they are evaluated once, at the block’s own dimension.
What the screen does#
Suppose a claims model already has separate smooth effects for driver age and vehicle power. It can express an age effect that applies to every vehicle and a power effect that applies to every driver. An interaction can express something those two effects cannot: for example, high power having a larger effect among young drivers.
PSST asks whether the fitted model’s remaining errors contain that kind of joint structure. For each eligible pair it:
Takes the fitted model’s residual score and working weights.
Builds the interaction shape that SuperGLM would use for that pair, such as a smooth surface or a separate slope for each category.
Removes directions the pair’s own main effects can already represent.
Calculates the candidate’s extra gain in a penalized working quadratic, beyond the pair’s main-effect adjustments. Smooth candidates are tried at several complexity budgets.
Returns a ranking for deciding which complete models to fit and evaluate.
Step 4 solves a small candidate problem while keeping the baseline working model fixed. It does not repeat likelihood fitting and smoothing selection for every pair. The pair projection also does not adjust jointly for every other fitted main effect. This is why the score is a useful probe of a refit, but does not tell us exactly what that refit will achieve.
Thirty features give at most 30 * 29 / 2 = 435 pairs, before unsupported
kinds and resource limits are excluded. PSST scores those pairs individually.
It does not search all subsets of 435 interactions, assign features to LSS
predictors, or find a globally best model. Adding an interaction and refitting
can change which pair looks useful next.
How FAST differs#
The original FAST algorithm
also ranks interactions against an additive baseline. For a pair of features,
it tries one split on each axis, forming four rectangles, and measures how
well a constant residual prediction in each rectangle improves the fit.
Cumulative tables let it evaluate split locations without scanning all rows
again for each split. InterpretML’s current implementation uses binned,
objective-specific interaction gains and accepts a baseline through
init_score.
PSST instead evaluates the spline, slope or categorical term that the proposed SuperGLM refit would use, with a complexity ladder for penalized terms. That requires more matrix work. The intended benefit is a ranking suited to those refit shapes; superiority for other models or datasets does not follow.
In the recorded comparison, screening ten pairs on 200,000 rows took 2.77–4.89 seconds for PSST and 0.106–0.113 seconds for FAST. These are historical measurements, not timings of every subsequent implementation. Both screens avoid the complete candidate-model refits.
Pair kinds#
kind |
probe block |
penalty |
probe df |
refit target |
|---|---|---|---|---|
|
centered spline ⊗ centered spline |
kron-sum |
|
|
|
centered spline menu ⊗ level contrasts |
|
|
|
|
the numeric’s column on each non-base level |
none |
|
|
|
level contrasts ⊗ level contrasts |
none |
|
|
|
the two numerics’ product |
none |
1 |
|
For the three unpenalized kinds the probe columns are the refit term’s
columns: the kron of two contrast menus on a level-pair cell is that cell’s
indicator, and the per-level slope columns are exactly what a
NumericCategorical builds. For ti and spline_cat the probe spans the
refit term’s identifiable deviation space — the part the pair’s own mains
cannot absorb.
This holds for cr parents as well as ps, but only because both
interaction types now resolve a cubic regression spline the same way.
Cubic regression splines carry two bases — a main effect uses the
projected-B-spline CubicRegressionSpline, while an interaction marginal
uses CardinalCRSpline, which is closer to mgcv’s bs="cr".
TensorInteraction had that routing and SplineCategorical did not, so a
spline_cat probe could span directions its own refit could not build: on
a skewed margin at n_knots=16 the containment of probe span in refit span
measured 0.675. Both now go through one shared resolution — including its
fallbacks, so a select=True parent or a penalty order the cardinal basis
does not implement keeps its own basis on both sides — and the identity
holds by construction. See
issue #191.
probe df is the unpenalized block’s dimension before the data
reduces it: for those kinds the edf0 column reports the rank actually
achieved, which is lower when cells are empty or their columns collinear (an
11 x 22 cat_cat table can report 208 rather than 210).
A spline_cat row can also be confirmed as a FactorSmooth when pooling
across levels is wanted rather than reference-coded deviations — same parents,
penalized level curves; see Interactions.
What gets swept. candidates=None pairs every eligible fitted feature:
splines, OrderedCategorical, Categorical and Numeric.
Polynomial, RandomEffect and any
OrderedCategorical carrying specials= have no screenable margin — a
special is a free level with no position on the spline axis, so the pair
would need a composite margin. Each of them is reported in
table.attrs["deferred_features"], a {feature: reason} mapping, and naming
one in candidates raises with that same reason. A Categorical carrying a
grouping= is eligible too: screening and
the confirmatory interaction refit both validate original labels and apply the
same grouping once. Spline x numeric has both margins but no refit target yet,
and is deferred until a varying-coefficient term exists.
The deferral has two remedies, and which one applies is a modelling question
rather than a screening one. If the Numeric margin is really curved, respec
it as a Spline and the pair screens as ti — that is usually the right
answer, and the mis-specification was the larger problem anyway. If the margin
is genuinely linear, as log1p(Density) is in the example below, respeccing it
would be over-fitting to dodge a deferral: leave it, and the pair waits.
Reaching for the first remedy by reflex is how a deferral gets “fixed” by
introducing a worse mains model. Both cases drop out of the default sweep
rather than reporting a null result, and naming one in candidates= raises
that deferral specifically, not a generic “unknown feature” error. Pairs the
model already fits as an interaction — of any class, FactorSmooth
included — are excluded too: the screen profiles only the parent mains, so it
cannot re-screen a term already in the model.
A mixed sweep comes back as one sorted table. The example below screens an
80,000-row sample of the freMTPL2 frequency book under
weight_semantics="frequency" (y the claim rate ClaimNb / Exposure,
sample_weight the exposure, phi estimated at 4.82). Every number here is
therefore on the replication contract’s sum(sample_weight) - edf scale, not
the default prior one.
The specification is spelled out rather than assumed, because the screen is defined against the fitted mains. A margin mis-specified there does not merely go unscreened — it changes which pairs are screenable at all:
df["LogDensity"] = np.log1p(df["Density"])
features = {
"DrivAge": Spline(kind="ps", n_knots=8),
"VehAge": Spline(kind="ps", n_knots=12),
# Bonus-Malus is strongly curved, and its mass piles at the scale
# minimum; tempered-quantile knots are this library's stated default
# for exactly that shape (see Feature Types).
"BonusMalus": Spline(kind="ps", n_knots=12,
knot_strategy="quantile_tempered", knot_alpha=0.2),
# log1p(Density) is the one margin here that genuinely is linear: give
# it a spline and the smooth collapses to edf 1.4 of rank 11, buying
# 1.0 deviance. Raw Density is not linear at all -- straightening it
# is what the log is for.
"LogDensity": Numeric(),
"VehBrand": Categorical(),
"Region": Categorical(),
}
table = model.screen_interactions(df, y, sample_weight=exposure)
print(table.to_string(index=False))
# feature_a feature_b kind statistic z edf0 lambda0 n_cells approx
# VehAge BonusMalus ti 5.691019 2.323521 1.999999 1.023168e+02 5244 False
# BonusMalus VehBrand spline_cat 11.960557 0.438411 9.999937 7.818059e+09 1012 False
# DrivAge BonusMalus ti 1.810771 -0.121587 2.000001 2.611293e+02 7360 False
# VehAge VehBrand spline_cat 7.805321 -0.490671 9.999576 3.779926e+09 627 False
# DrivAge VehAge ti 0.547723 -0.925665 2.000001 5.044218e+01 4560 False
# DrivAge VehBrand spline_cat 1.958228 -1.798192 9.999927 1.301005e+10 880 False
# LogDensity VehBrand numeric_cat 1.087366 -1.992925 10.000000 0.000000e+00 11 False
# BonusMalus Region spline_cat 6.972190 -2.164533 20.999891 4.198786e+09 2024 False
# DrivAge Region spline_cat 6.753990 -2.198200 20.999860 6.858867e+09 1760 False
# LogDensity Region numeric_cat 5.266162 -2.427784 21.000000 0.000000e+00 22 False
# VehAge Region spline_cat 2.008917 -2.930369 20.998740 2.449233e+09 1254 False
# VehBrand Region cat_cat 39.860759 -8.243705 208.000000 0.000000e+00 242 False
Every eligible pair, four kinds, one ranking by z alone. Twelve rows, not
fifteen: the three spline x numeric pairs are deferred, so they never enter
the sweep.
Read the top two rows together, because they show what z actually ranks.
Confirming each by refit — the gate, not the score:
row |
kind |
|
probe df |
refit gain |
|---|---|---|---|---|
|
|
2.32 |
2 |
43.0 |
|
|
0.44 |
10 |
73.0 |
The second pair buys more deviance and ranks below the first: 43.0 on 2 df
is 21.5 per df against 7.3 for the second pair. The current z adjusts the
local score for probe complexity; it does not rank by total refit gain or
directly by gain per df.
These two gains are measured on the training data. They show that complexity
adjustment and total gain can disagree, but do not establish which pair is
the better use of a refit budget. That requires held-out gains and refit
costs. A holdout study of this ranking, against confirmatory refits on a
200,000-row split, is in
Screening Evaluation.
statistic is not comparable down that column: the cat_cat row’s 39.9 is a
208-dimensional block and the numeric_cat row’s 1.1 a 10-dimensional one. The
large lambda0 values are bracket edges at clamped rungs, not fitted smoothing
parameters. Ten of the twelve rows carry a negative z, which is ordinary: a
statistic can land below its rung’s noise floor, and on this book most pairs
do. The cat_cat row’s -8.24 is the extreme case — a 208-df block whose
statistic, scaled by the whole model’s dispersion, lands at 39.9, so the
global phi is conservative for that block. A large negative z only pushes
a pair down the queue; it never promotes one. Nothing here was binned or
refused (approx is False throughout, no NaN rows).
Read the top row against its own kind’s measured noise maximum below (9.48 for
ti): 2.32 does not clear it — and the refit bought 43.0 deviance anyway. That
is the screen working as described rather than a contradiction: the floor is
the largest value a wide null battery produced, not a threshold a real pair
must beat, and the confirmatory refit is what settles the question.
Reading the output#
kindnames the block that was probed, and with it the term you would refit to confirm the row. Kinds share one sorted table, but the currentzdoes not give them identical null distributions. Read the reference-law limitation and measured floors below before comparing kinds.Rank by
z, and onlyz. For the penalized kinds (ti,spline_cat)statistic,edf0andlambda0describe the pair’s winning rung, so they are not comparable between rows; at a clamped rungedf0holds the achieved value andlambda0is a bracket edge rather than an interpretable smoothing parameter. For the unpenalized kinds (numeric_cat,cat_cat,numeric_numeric) there is a single rung:edf0reports the block’s achieved rank andlambda0is0, and theedf0=argument does not apply to them.statisticis the dispersion-scaled score statistic.zis a ranking heuristic, not a p-value. By default, the statistic is scaled by the Pearson dispersion of the mains fit;phi=overrides it. Each rung uses the mean and variance of its fixed Gaussian reference law. Estimating the baseline and choosing the best rung affect the sampling distribution, so this standardization does not give a normal score or a p-value. The mathematical distinction and measured floors are below.n_cellsis the grid the probe assembled: the product of the two margins’ grid sizes, where a spline or OC margin contributes its support size, a factor contributes its level countL, and a numeric contributes 1 (so anumeric_catrow reports the factor’s levels and anumeric_numericrow reports 1).The winning
edf0is a shape diagnostic for penalized rows with materialz. A win at rung 2 means tilt-level evidence (a simple in-in surface); wins at 8-16 mean genuinely curved or high-frequency structure. Under the pure null the winning rung is meaningless.Confirm by refitting. Refit promising pairs as their
kind’s refit target and assess held-out predictive gain and cost. Near-tiedzvalues are common. A large score need not imply the greatest predictive gain, and a refit on the same data does not remove selection bias.NaN rows are skipped or refused pairs, not failures. A gridded pair (
ti,spline_cat) is skipped when it exceeds the cell or intermediate budgets even after the quantile-binning fallback, when its tensor curvature block alone is too large for the budget (binning cannot shrink basis dimensions, so those skip with no binning attempted), when its block is too wide to solve inside the budget (see below), or when the statistic degenerates. Anumeric_catpair has no grid to shrink, so a factor too wide for the pair’s blocks is refused rather than approximated: the gate holds the largest of those blocks, the(L + 1)-wide overlap curvature, to the budget —(L + 1)^2 <= max_cells, which admits factors up to 2235 levels — and the block-dimension gate below holds the same width to the solve budget, which at the default is the binding one and admits 1710. Raisingmax_cellslifts both and computes the pair exactly.numeric_numericcontracts to 3x3 blocks whatever the supports and is never refused. All such rows sort last.approx=Truemeans the row’s probe basis differs from what a confirmatory refit would build — either a spline margin was quantile-binned for the screen, or that pair’s refit would discretize lossily. Only rows with a spline or OC margin can carry it:cat_cat,numeric_catandnumeric_numericrows are alwaysapprox=False, and refusal is not approximation.
Measured null floors#
These are measured maxima over a null battery, not calibrated quantiles.
benchmarks/screening_null_floors.py --seeds 40 fits 160 mains models (four
families x 40 seeds, n=8000 rows each) with no interactions in the truth,
and screens 3520 pairs. The battery was rerun after correcting the reference
variance; all fits completed, with no non-finite scores or warnings.
The paired measurement
records both normalizations.
kind |
rows |
mean |
p90 |
max |
probe df |
|---|---|---|---|---|---|
|
480 |
0.52 |
1.93 |
9.48 |
2-16 |
|
1440 |
0.44 |
1.89 |
5.66 |
2-16 |
|
960 |
0.00 |
1.19 |
7.54 |
1-3 |
|
480 |
-0.01 |
1.20 |
3.98 |
2-6 |
|
160 |
-0.07 |
1.07 |
4.91 |
1 |
Nine of every ten rows fall below 1.1-1.9, depending on kind, yet the maxima
reach 3.98-9.48. A z of 5 is therefore not evidence by itself. The correction
raises the ti maximum from 7.31 to 9.48 and the spline_cat maximum from
5.53 to 5.66. The three unpenalized kinds retain their normalization. Larger
scores after this change do not by themselves demonstrate greater detection
power: null scores rise too.
The two largest rows are a rung-2 ti and a 1-df numeric_cat (a slope on a
two-level factor). All six largest rows have edf0 <= 4. Low-dimensional
quadratics can have substantial right skew even after mean and variance
standardization. The unpenalized kinds show this directly:
kind |
probe df |
rows |
max |
|---|---|---|---|
|
1 |
320 |
7.54 |
|
2 |
320 |
4.45 |
|
3 |
320 |
4.78 |
|
2 |
160 |
3.18 |
|
3 |
160 |
3.98 |
|
6 |
160 |
2.90 |
|
1 |
160 |
4.91 |
These maxima are not monotone in df and depend on the number of draws. They do not establish a universal ordering of the kinds’ tails.
The dispersed Gaussian arm has the largest score in four of the five kinds,
including both maxima above 6: 7.54 on numeric_cat and 9.48 on ti.
The other families reach at most 4.59 (Poisson), 5.34 (gamma) and 5.53
(binomial). A maximum over these four fitted scenarios is not a bound for a
new dataset or a different fitted baseline.
Ordered-categorical (OC) and plain spline margins give the following results:
kind |
margins |
mean |
p90 |
max |
|---|---|---|---|---|
|
plain |
0.45 |
1.90 |
9.48 |
|
OC |
0.56 |
1.96 |
5.57 |
|
plain |
0.48 |
1.89 |
5.66 |
|
OC |
0.36 |
1.89 |
5.53 |
The bulk differences are small in this battery, but these measurements do not establish equivalent tails or isolate the effect of a short score grid.
The regression suite uses z < 10 on its specified null fixtures. This is a
test bound, not a screening threshold or a probability guarantee. The observed
maximum of 9.48 already approaches it, and widening a sweep gives noise more
opportunities to produce a large score. Select candidates for refitting using
the ranking and the modelling context; assess the refits on held-out data.
What it inherits from the fit#
Screening linearizes at the fitted model: both the offset and the
sample_weight used at fit time are applied automatically (weights
only when the fit’s were non-unit; pass either only to override), and
the mains model’s own smoothness choices define what “leftover” means.
Inherited arrays are in training row order, so inheriting requires
X/y to be the retained training data — to screen a holdout,
subsample, or reordered frame, pass sample_weight (and offset)
explicitly. A badly specified mains model screens against the
wrong baseline — screening quality is downstream of fit quality. The
Pearson dispersion that scales the statistic is attached to the result as
table.attrs["phi"] and can be overridden with phi=. By default its
residual degrees of freedom follow the fitted model’s declared
weight_semantics: sum(sample_weight) - edf under "frequency", the count
of positive-weight rows minus edf under "prior" (the default). The screen
and fitted model therefore stay on the same dispersion scale. The worked
example below declares weight_semantics="frequency", so every number it
prints is on the sum(sample_weight) - edf scale.
Factor margins are read through the fitted spec: levels are indexed in the
fit’s own order, any LevelGrouping collapse is applied exactly as the fit
applies it, and a level the fit never saw raises through the spec’s own
validator rather than screening quietly.
Parent smooths must be single-penalty: mains fitted with select=True raise
up front, because ti() terms cannot be built on such parents either. For an
OrderedCategorical margin the same check applies to its inner spline.
Screening always probes the exact-basis tensor, including for parents
that discretize (whose confirmatory refit uses binned marginal
supports). That is the same support-discretization gap as the quantile
fallback — measured at ~3.5% relative z on signal pairs — and it never
affects which basis the confirmatory refit itself uses. To make the gap
visible in the output rather than doc-only, any pair whose refit would
discretize lossily carries approx=True, applying the gate that refit
itself uses: a ti() refit bins its marginal supports only when BOTH parents
resolve to fit-time discretization (per-spec discrete overriding the model
flag), while a SplineCategorical refit bins whenever its ONE spline parent
does. Either way the row is flagged only when some margin that refit would bin
has a cardinality exceeding its resolved bin count — lossless binning returns
the exact unique support, so low-cardinality rating factors stay
approx=False. An OC margin lives on at most one score point per level: it
does not reach the binning fallback in practice, and its refits do not
discretize at all, so OC pairs stay exact on both sides.
Measured limits#
Corner-localized effects. An interaction confined to a thin corner of the joint support (young driver x high power, with little data there) screens weakly — there is simply little signal-carrying data, and a full refit faces the same limit. Expect low
z, not a false positive.Rare cells and rare levels. A
cat_catcell or aspline_catlevel with few rows contributes little to the block, so it screens honestly weak for the same reason — the confirmatory refit is equally starved there. Rare levels cost ranking power, not correctness.Heavily correlated pairs. At rho ~ 0.85 the joint support is a ridge; the off-ridge tensor directions are thinly identified and a real signal is demoted, not lost. The refit faces the same identifiability.
Continuous x continuous cardinality. Pairs whose unique-value grid (or curvature-intermediate allocation, bounded at a small multiple of the same budget) exceeds
max_cellsfall back to quantile binning (screen_binsempirical-quantile support points per margin, basis evaluated at within-bin means) and are flaggedapprox=Truein the output. Pairs within budget are always computed exactly — the fallback never touches them. Screening-only: a confirmatory refit of a flagged pair uses the full data.Block dimension, which costs time rather than memory.
max_cellsis an allocation ceiling, and every allocation above grows ask^2in the probe block’s dimension — but the solve grows ask^3, because each rung takes a(k, k)decomposition: a pivoted QR of the pair’s design factor with one SVD beside it. For the gridded kinds a probe column collinear with the overlap span is the routine case rather than the exception (one emptycat_catcell or one singleton level makes one; theVehBrand x Regionrow above reportsedf0 = 208against a nominal 210 for exactly this reason), and it is settled by a rank cut on that factor — the Cholesky and its pseudo-inverse fallback this bullet used to describe went with the assembled Grams in issue #257, without moving the cost class. The same knob therefore bounds time:k^3 <= 1000 * max_cellsfor an unpenalized block, and the same budget against twice the work for a penalized one, whose ladder can bisect rather than clamp. At the default that admitsk <= 1709(cat_caton two 42-level factors,numeric_caton a 1710-level factor) andk <= 1357forti/spline_cat, measured on the reference box at 0.81 s and 0.67 s per pair — the unpenalized figure a few percent low since that rung’s rank became a count rather than a trace (measured 1.056x at that corner, and it is what makes the reportededf0the rank rather thank). Those two figures are moment-route figures and the dense path is measured slower since it moved onto design factors (issue #257): 4.09x on the ladder and 4.17x end to end at the widest geometry benchmarked, which carries them to roughly 3.3 s and 2.7 s per pair. The ceilings themselves are dimensional and are unchanged, so no pair is admitted or refused differently; what the move spent is the ~1.5 s per-pair calibration behind them, which is no longer met at the ceiling and is refitted separately. Wider blocks are refused with a NaN row, immediately — binning cannot shrink a basis dimension — and raisingmax_cellslifts the refusal. For scale, the block the old allocation-only ceiling admitted (k = 4290, two 67-level factors) measured 24 s and 1.3 GB for one pair.…except
spline_cat, which has no block-dimension ceiling. Grouped by level, a spline x categorical pair’s bordered system is an arrow matrix: its curvature is block-diagonal because levels have disjoint row support, its penaltykron(S_spline, I)is block-diagonal on the same grouping, and the only thing coupling the levels is the intercept and the spline main — a border of1 + k_splinecolumns, independent of the level count. So a pair the ceiling above refuses is retried through a kernel that factorizes that arrow in time and memory linear in the level count, rather than cubic and quadratic. The dense path still scores every pair it can, unchanged; the structured path only extends where it stops. The block-dimension ceiling is not the only place it stops, so it is not the only door into the kernel: a pair whose support intermediatesupport * contrasts^2 + L * k_spline^2blows the same budget is handed over too, whenever the structured path’s transposed intermediatesupport * k_spline^2still fits — which happens with the dense block far below the ceiling. Measured: a 22-level factor against aps(8)margin with 46,119 distinct values is scored by the kernel, exactly, at a block dimension of 231 against the ceiling of 1,357. Exhausting the binning fallback is a third door. So do not read the level count alone to decide which kernel scored a pair — and read the unpinned level count when you do, since alevels=universe with no training rows behind it is pinned to base and widens no block: against a library-defaultSpline()margin (13 probe columns) over a 400-point support, 140 declared levels of which 105 are populated stays on the dense path at a block of 13 x 104 = 1,352, where 106 populated levels would not. The support matters because the block width is only the first gate — hold that same 140/105 factor and widen the spline and the pair takes the support exit instead at an unchanged block width (measured: 1,800 distinct values dense, 1,848 arrow). Nor is a NaN row a routing signal in reverse: aspline_catpair whose ladder resolved no direction at any rung gets one on EITHER path. Measured end to end, best of three, one BLAS thread: 200 levels 0.013 s, 1,000 levels 0.048 s, 5,000 levels 0.32 s — against 0.40 s for the 124 levels the dense path tops out at when the block ceiling is what binds. Above roughly five thousand levels the binding cost is no longer screening but fitting the mains model the screen runs against, whose factor contributes one column per level and which nodiscrete=setting compresses — discretization is support compression for continuous covariates, and a factor is already on its own grid.Three things bound the structured path, and
max_cellsscales all three. The level blocks areL x (k_spline + 1)^2, which at the default and a width-11 spline admits 34,722 levels. Before the reference-variance correction, the kernel alone measured there at 1.22 s and 201 MB for a four-rung ladder, and linear below it at 0.16 s for 5,000 and 0.63 s for 20,000. The cell table issupport x L, and beside it the spline menu’s outer products aresupport x k_spline^2; a pair over either is quantile-binned on its spline margin and flaggedapprox, the same degradation the dense path applies to its own intermediate. And the ladder itself is budgeted in arrow factorizations: two evaluate the bracket, each distinct emitted lambda needs one final variance pass, and a rung inside the bracket needs bisection as well. A ladder clamped to one edge therefore costs three passes. Each final variance pass also adds a QR tree over the levels. A pair that cannot afford the reserved work is refused with a NaN row; the work estimate is not a wall-clock guarantee. Which of those a pair hits depends on its shape — a narrow spline against a huge factor is bounded by the block stacks, a wide spline against a small one by the ladder, since one evaluation is cubic ink_splinewhere every allocation is quadratic.A wide factor’s
zis an omnibus statistic, and it is diluted. Read theedf0column before comparing a widespline_catrow against a narrow one.kron(S_spline, I)leaves the constant and the linear direction unpenalized per level, so its null space is2(L-1); the mains absorb the constants and roughlyL-1degrees of freedom survive at any penalty. Every rung of the ladder therefore clamps to the same edge, and the pair is only ever tested atedf0 ~ L-1— the ladder’s whole point, scanning budgets for the one that best matches the signal, is unavailable to it. This is a property of the penalty, not of the level count: it holds forps,bsandcrmargins, whose penalties have a null space, and not forns, whose penalty is full rank. Aspline_catpair on annsmargin hasedf0 = 0at maximum penalty, so every rung genuinely searches and the ladder costs tens of arrow factorizations. The earlier measurement was 106 against 2 on the same 400-level pair, before final variance passes were added. That difference is why the evaluation budget includes search work. Under a fixed Gaussian reference withdunpenalized identified directions and profiled noncentralityLambda, the expected score isLambda / sqrt(2*d). Adding directions while holding that noncentrality fixed reduces the expected score. At fixed dimension and noncentrality, concentrating the interaction in a few levels does not change this omnibus statistic’s distribution. A low score therefore does not establish that every level-specific effect is absent or that a candidate refit would be useless. A pooled or fully penalized refit changes the alternative and its regularization; it does not guarantee stronger screening. Check its benefit against the intended alternative and held-out performance.Factor and numeric margins have no such cardinality limit. A factor margin never bins — its support is the fitted level set — and a numeric margin never grids at all: it enters its probe linearly, so moments of the numeric accumulated over the other margin’s cells are its exact sufficient statistics, at any cardinality — reduced, level by level, to a factor of the same design rather than to its curvature. Neither margin can raise
approx. The one degradation available to a numeric-margin pair is refusal (numeric_catwith a factor wider than its blocks), and a refused row is a NaN row, never an approximated one.
Provenance#
The basic calculation has a direct mathematical interpretation. Let \(U\) be the candidate’s profiled working score, \(V\) its Fisher working curvature and \(S\succeq0\) its penalty. Fix the penalty weight \(\lambda\ge0\). For each candidate coefficient vector \(b\), profile unpenalized adjustments to the intercept and the pair’s main-effect columns. Relative to the nuisance-only profiled optimum, the remaining working gain is
When \(V+\lambda S\) is positive definite on the retained candidate space, completing the square gives
Thus the raw score is twice the candidate’s extra quadratic gain beyond what
those main-effect adjustments can achieve alone. It is not necessarily twice
the total gain from the unchanged fitted coefficients. The table reports
statistic after division by dispersion,
estimated from the mains fit by default or supplied through phi=.
The identity is exact for that quadratic; a complete refit changes
the working model and need not achieve the same gain.
Classical score testing uses this kind of slope-and-curvature calculation at the null fit, avoiding a fit under every alternative. There is also an established literature on testing smooth components, including Zhang and Lin (2003), and on score-based interaction tests, including GESAT (Lin et al., 2013). These provide related constructions, not a theorem for PSST’s particular penalty choice, pair-only projection and maximum over complexity budgets.
Reference distribution and current limitation#
If the geometry is fixed and \(U\sim N(0,\phi V)\) with known dispersion, the quadratic has a weighted chi-square reference law:
Here the \(Z_j\) are independent standard normals and the \(a_j\) are the candidate’s shrinkage eigenvalues. Both execution paths use
For an identified unpenalized block, every retained \(a_j=1\), so this reduces to the usual \(\sqrt{2\mathrm{edf}_0}\) denominator. With shrinkage, that old denominator was too large. The dense path obtains the sum of squares from its existing decomposition. The structured path computes the same quantity from diagonal and cross-level blocks, without assembling the full smoother.
This standardizes the fixed Gaussian reference at one rung. Fitting the baseline affects the score’s mean and covariance; estimated dispersion and smoothing, non-Gaussian responses, and selecting the best rung introduce further questions. Simulating normal draws from a fixed candidate matrix does not automatically give exact p-values for the fitted-model procedure. PSST currently returns a ranking, with empirical checks of its behavior. Candidate refits and held-out evaluation are the next step.
What PSST combines, and what has been measured#
PSST combines candidate spaces tied to SuperGLM’s refit terms, penalty weights
chosen to attain screening-EDF budgets, a maximum over those budgets, and
factor-based assembly. Quantized screens are identified by approx.
This combination explains the design; establishing its originality requires
a more complete literature comparison.
The FAST comparison used the earlier normalization, refits every candidate, and measures held-out gain. In its two specifications, that PSST ranking agrees more closely with held-out refit gain; FAST is faster and agrees more closely with training gain. These are descriptive results from 8 and 10 pairs on one book, not evidence of a universal ranking advantage. Wider candidate sets and replicated data or splits are needed, with the dependence between overlapping pairs accounted for.