From 2fc8f043fb8d73af43be9450f7b6bbdd4e7f32dd Mon Sep 17 00:00:00 2001 From: Ananyapam De Date: Tue, 25 Aug 2026 02:02:38 +0200 Subject: [PATCH] feat(gam): add mgcv Markov random field smooths --- README.md | 4 +- docs/generate_notebooks.py | 7 + docs/notebooks/01_gam.ipynb | 8 + nampy/gam/compiler/compile_model.py | 32 +- nampy/gam/compiler/factory.py | 19 + nampy/gam/compiler/linked_basis.py | 20 + nampy/gam/model/api.py | 2 + nampy/gam/smooths/__init__.py | 4 + nampy/gam/smooths/categorical/__init__.py | 4 + nampy/gam/smooths/categorical/fs.py | 55 +- nampy/gam/smooths/categorical/mrf.py | 532 ++++++++++++++++++ nampy/gam/smooths/tensor/marginals.py | 40 +- nampy/gam/smooths/tensor/te.py | 7 +- nampy/gam/smooths/tensor/ti.py | 7 +- nampy/gam/specs/__init__.py | 2 + nampy/gam/specs/build.py | 2 +- nampy/gam/specs/modeling.py | 22 +- nampy/gam/specs/smooth.py | 8 + nampy/gam/specs/smooth_build.py | 17 +- nampy/gam/splines/basis/__init__.py | 3 +- nampy/gam/splines/basis/natparam.py | 47 +- pyproject.toml | 1 + tests/SUBSYSTEM_COVERAGE.md | 4 +- tests/TAXONOMY.md | 2 +- tests/_taxonomy_registry.py | 7 + tests/mgcv_invariant_policy.py | 13 + tests/mgcv_parity_utils.py | 19 + .../test_mgcv_mrf_combinations_parity.py | 330 +++++++++++ ...ed10a0b664d82ecfc26f5dd2befcfb7f8f.json.gz | Bin 0 -> 7550 bytes ...727d0344c9027823c795d20976a4c137c7.json.gz | Bin 0 -> 22573 bytes ...66f765717beef59e9003e17ad59329c7e7.json.gz | Bin 0 -> 28701 bytes ...7bdffb98e8b512dc5793135a6960af3e81.json.gz | Bin 0 -> 7644 bytes ...ab5e702066187cc9d137ea913031a0136b.json.gz | Bin 0 -> 7521 bytes ...85a97d0ef90b0395ad95d3de1ed8bcda07.json.gz | Bin 0 -> 7083 bytes ...b2610a413ca4a2405d4ad23b30c7991668.json.gz | Bin 0 -> 205 bytes ...1a8049cf8c1009a9b302bfb413ae56b475.json.gz | Bin 0 -> 733 bytes ...81f640b34624fcdd4e335dd5dddf8fd255.json.gz | Bin 0 -> 7633 bytes ...e932b291793f67e6b2a61970828e256ca9.json.gz | Bin 0 -> 6775 bytes ...7a319fb56aac54554ef381189e3dc4c54b.json.gz | Bin 0 -> 190 bytes ...7084c37b7505f7256addf4e26f3d9984f4.json.gz | Bin 0 -> 7493 bytes ...a5287aa02f7fc49c6adc65b4617278d40c.json.gz | Bin 0 -> 13685 bytes ...67a4cb228001ddb3378fcff149cadaf09d.json.gz | Bin 0 -> 6595 bytes ...02beb51f47043321e82e821a268b7a0ac1.json.gz | Bin 0 -> 309 bytes ...cad15f41e94d79d6a726f6b861c4019a7d.json.gz | Bin 0 -> 203 bytes ...9f6deda98bcfc5385ed2d8c472e4424c61.json.gz | Bin 0 -> 403 bytes ...7d7ed732cc3b582ca99fef3e71d8676e0d.json.gz | Bin 0 -> 405 bytes ...53a8b62b5a8ec1ff4466c4e0b9aadc2587.json.gz | Bin 0 -> 204 bytes ...5a8a2cace168c1eaf35c7c0744b1e7d352.json.gz | Bin 0 -> 11704 bytes ...f98724cc28c79f32fa88539e7245ab08e8.json.gz | Bin 0 -> 45473 bytes .../test_mgcv_raw_constructor_parity.py | 127 +++++ 50 files changed, 1297 insertions(+), 48 deletions(-) create mode 100644 nampy/gam/smooths/categorical/mrf.py create mode 100644 tests/parity/test_mgcv_mrf_combinations_parity.py create mode 100644 tests/reference_fixtures/mgcv/24692abbf588cc2353db4619413347ed10a0b664d82ecfc26f5dd2befcfb7f8f.json.gz create mode 100644 tests/reference_fixtures/mgcv/26b50d46b9c090f7fe718a8a41857b727d0344c9027823c795d20976a4c137c7.json.gz create mode 100644 tests/reference_fixtures/mgcv/271fd33286df58a5a765b5050dcb4c66f765717beef59e9003e17ad59329c7e7.json.gz create mode 100644 tests/reference_fixtures/mgcv/39c6a5f6d15955a6b3b004a8fcfa117bdffb98e8b512dc5793135a6960af3e81.json.gz create mode 100644 tests/reference_fixtures/mgcv/3ea99ff49e623cb0e62c8e8083c5fcab5e702066187cc9d137ea913031a0136b.json.gz create mode 100644 tests/reference_fixtures/mgcv/4b9d1ba93528d739e9891d79824baf85a97d0ef90b0395ad95d3de1ed8bcda07.json.gz create mode 100644 tests/reference_fixtures/mgcv/7176ffa5c1e63d9ef773d854b9c76db2610a413ca4a2405d4ad23b30c7991668.json.gz create mode 100644 tests/reference_fixtures/mgcv/7a7e673de690a99c4c73b203538f061a8049cf8c1009a9b302bfb413ae56b475.json.gz create mode 100644 tests/reference_fixtures/mgcv/820d08a40e4007add953053315d2fb81f640b34624fcdd4e335dd5dddf8fd255.json.gz create mode 100644 tests/reference_fixtures/mgcv/87597278c5b988e5fe13233855628ae932b291793f67e6b2a61970828e256ca9.json.gz create mode 100644 tests/reference_fixtures/mgcv/9040616bb3018ab240edecbe723efe7a319fb56aac54554ef381189e3dc4c54b.json.gz create mode 100644 tests/reference_fixtures/mgcv/972cff6f163bd961e0dd7a5888f6807084c37b7505f7256addf4e26f3d9984f4.json.gz create mode 100644 tests/reference_fixtures/mgcv/9cc8059d8c86e6989a24bf7cd0a02ea5287aa02f7fc49c6adc65b4617278d40c.json.gz create mode 100644 tests/reference_fixtures/mgcv/a86349395966d2d9252b7ae12478b767a4cb228001ddb3378fcff149cadaf09d.json.gz create mode 100644 tests/reference_fixtures/mgcv/a8eedc37e4c14e7bcb1650c7bcb35602beb51f47043321e82e821a268b7a0ac1.json.gz create mode 100644 tests/reference_fixtures/mgcv/c278dfbf9a39db4338cbc402145129cad15f41e94d79d6a726f6b861c4019a7d.json.gz create mode 100644 tests/reference_fixtures/mgcv/c5299d99e7fa518f78ec67d4e915ee9f6deda98bcfc5385ed2d8c472e4424c61.json.gz create mode 100644 tests/reference_fixtures/mgcv/e09bf05038a2383dfeafe6b62e75027d7ed732cc3b582ca99fef3e71d8676e0d.json.gz create mode 100644 tests/reference_fixtures/mgcv/e90433678134533e0bd33782cf7a1953a8b62b5a8ec1ff4466c4e0b9aadc2587.json.gz create mode 100644 tests/reference_fixtures/mgcv/f5304b44e26a8362e75f387a53d64e5a8a2cace168c1eaf35c7c0744b1e7d352.json.gz create mode 100644 tests/reference_fixtures/mgcv/fa6672ade967e6993708380f389dc9f98724cc28c79f32fa88539e7245ab08e8.json.gz diff --git a/README.md b/README.md index 26ea9c57..5ddb40de 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,8 @@ result, and prediction interfaces. | Formula surface | Supported terms | | ------------------ | ---------------------------------------------------------------------------------------------------- | | Univariate smooths | `s(..., bs='bs')`, `cr`, `cs`, `cc`, `cp`, `ds`, `gp`, `ps`, `tp`, `ts` | -| Structured smooths | random effects `re`, factor smooths `fs`, sum-to-zero factor smooths `sz` | -| Tensor products | `te(...)` and `ti(...)` over supported numeric marginals | +| Structured smooths | Markov random fields `mrf`, random effects `re`, factor smooths `fs`, sum-to-zero factor smooths `sz` | +| Tensor products | `te(...)` and `ti(...)` over supported numeric or MRF marginals | | Parametric terms | numeric and factor terms, supported interactions, intercept policies, and formula offsets | | Shared smoothing | supported `id=` groups, fixed/free smoothing parameters, `select=True`, and `pc=` on supported bases | diff --git a/docs/generate_notebooks.py b/docs/generate_notebooks.py index ea428518..332025d9 100644 --- a/docs/generate_notebooks.py +++ b/docs/generate_notebooks.py @@ -1515,6 +1515,7 @@ def gam_notebook() -> dict: | `cc` | cyclic cubic spline for periodic covariates | | `ps` | P-spline with difference penalties | | `gp` | low-rank Gaussian-process smooth with spherical, power-exponential, or Matérn covariance | +| `mrf` | region effect coupled by a neighbor graph, polygon boundary, or supplied penalty | | `tp`, `ts` | thin-plate regression spline; `ts` adds shrinkage | | `te(...)` | scale-invariant tensor product including main-effect directions | | `ti(...)` | tensor interaction with marginal main-effect directions removed | @@ -1539,6 +1540,12 @@ def gam_notebook() -> dict: "gaussian_process": GAM( formula="demand ~ s(temperature, humidity, bs='gp', k=20, m=[3, 1.0])" ), + "markov_random_field": GAM( + formula=( + "demand ~ s(region, bs='mrf', " + "xt={'nb': {'north': ['south'], 'south': ['north']}})" + ) + ), "thin_plate": GAM(formula="demand ~ s(temperature, bs='tp', k=10)"), "shrinkage_thin_plate": GAM(formula="demand ~ s(temperature, bs='ts', k=10)"), "tensor_surface": GAM( diff --git a/docs/notebooks/01_gam.ipynb b/docs/notebooks/01_gam.ipynb index 8c93cec3..6280af49 100644 --- a/docs/notebooks/01_gam.ipynb +++ b/docs/notebooks/01_gam.ipynb @@ -630,6 +630,7 @@ "| `cc` | cyclic cubic spline for periodic covariates |\n", "| `ps` | P-spline with difference penalties |\n", "| `gp` | low-rank Gaussian-process smooth with spherical, power-exponential, or Mat\u00e9rn covariance |\n", + "| `mrf` | region effect coupled by a neighbor graph, polygon boundary, or supplied penalty |\n", "| `tp`, `ts` | thin-plate regression spline; `ts` adds shrinkage |\n", "| `te(...)` | scale-invariant tensor product including main-effect directions |\n", "| `ti(...)` | tensor interaction with marginal main-effect directions removed |\n", @@ -654,6 +655,7 @@ " 'cyclic',\n", " 'factor_smooth',\n", " 'gaussian_process',\n", + " 'markov_random_field',\n", " 'p_spline',\n", " 'random_effect',\n", " 'shrinkage_cubic',\n", @@ -680,6 +682,12 @@ " \"gaussian_process\": GAM(\n", " formula=\"demand ~ s(temperature, humidity, bs='gp', k=20, m=[3, 1.0])\"\n", " ),\n", + " \"markov_random_field\": GAM(\n", + " formula=(\n", + " \"demand ~ s(region, bs='mrf', \"\n", + " \"xt={'nb': {'north': ['south'], 'south': ['north']}})\"\n", + " )\n", + " ),\n", " \"thin_plate\": GAM(formula=\"demand ~ s(temperature, bs='tp', k=10)\"),\n", " \"shrinkage_thin_plate\": GAM(formula=\"demand ~ s(temperature, bs='ts', k=10)\"),\n", " \"tensor_surface\": GAM(\n", diff --git a/nampy/gam/compiler/compile_model.py b/nampy/gam/compiler/compile_model.py index 17bfd7fc..470367ca 100644 --- a/nampy/gam/compiler/compile_model.py +++ b/nampy/gam/compiler/compile_model.py @@ -36,10 +36,10 @@ def _apply_overlapping_parametric_identifiability( n_obs = int(compiled_predictors[0].design_matrix.shape[0]) expanded_columns = [] owners: list[tuple[int, int | None, int | None]] = [] - keep_by_component: list[dict[int, np.ndarray]] = [ - {} for _ in compiled_predictors + keep_by_component: list[dict[int, np.ndarray]] = [{} for _ in compiled_predictors] + keep_intercept = [ + bool(predictor.has_intercept) for predictor in compiled_predictors ] - keep_intercept = [bool(predictor.has_intercept) for predictor in compiled_predictors] def append_column(column, targets, owner): expanded = np.zeros(n_obs * n_linear_predictors, dtype=np.float64) @@ -101,9 +101,7 @@ def append_column(column, targets, owner): dropped_local: list[int] = [] for term_index, term in enumerate(predictor.compiled_terms): basis = np.asarray(term.basis_train, dtype=np.float64) - keep = component_keep.get( - term_index, np.ones(basis.shape[1], dtype=bool) - ) + keep = component_keep.get(term_index, np.ones(basis.shape[1], dtype=bool)) kept_indices = np.flatnonzero(keep) selection = np.eye(basis.shape[1], dtype=np.float64)[:, kept_indices] metadata = dict(getattr(term, "metadata", {}) or {}) @@ -199,7 +197,11 @@ def _full_predictor_matrix(predictor, X: np.ndarray) -> tuple[np.ndarray, np.nda Z_fit = np.asarray(predictor.design_matrix, dtype=np.float64) pred_blocks = [] for term in predictor.compiled_terms: - use_raw = bool(getattr(term, "metadata", {}).get("expose_raw_prediction_basis")) + term_metadata = dict(getattr(term, "metadata", {}) or {}) + use_raw = bool( + term_metadata.get("expose_raw_prediction_basis") + or term_metadata.get("prediction_basis_map") is not None + ) if use_raw: block = np.asarray( term.prediction_parameterization_matrix(X), dtype=np.float64 @@ -250,7 +252,7 @@ def _fit_to_prediction_parameterization_map( # qr(Xp, LAPACK=TRUE) -> Rrank(R) -> triangular solve on QtX -> restore pivots. Q, R, piv = scipy_qr(X_pred, mode="economic", pivoting=True) p_pred = int(R.shape[1]) - rank = upper_triangular_rrank(R, tol=float(np.finfo(np.float64).eps**0.9)) + rank = upper_triangular_rrank(R, tol=float(np.finfo(np.float64).eps ** 0.9)) QtX = np.asarray(Q.T @ X_fit, dtype=np.float64)[:rank, :] if rank < p_pred: R1 = np.asarray(R[:rank, :], dtype=np.float64) @@ -342,9 +344,7 @@ def compile_model( tuple( int(value) - 1 for value in ( - (getattr(spec, "metadata", {}) or {}).get( - "lpi", (component_index + 1,) - ) + (getattr(spec, "metadata", {}) or {}).get("lpi", (component_index + 1,)) or (component_index + 1,) ) ) @@ -352,9 +352,9 @@ def compile_model( else (component_index,) for component_index, spec in enumerate(predictor_specs) ) - n_linear_predictors = max( - (max(indices) for indices in component_lpi if indices), default=0 - ) + 1 + n_linear_predictors = ( + max((max(indices) for indices in component_lpi if indices), default=0) + 1 + ) compiled_predictors = compile_predictors( X=X, @@ -452,9 +452,7 @@ def compile_model( ( np.zeros(int(predictor.n_coef), dtype=bool) if predictor.positive_coefficient_mask is None - else np.asarray( - predictor.positive_coefficient_mask, dtype=bool - ) + else np.asarray(predictor.positive_coefficient_mask, dtype=bool) ), ] ), diff --git a/nampy/gam/compiler/factory.py b/nampy/gam/compiler/factory.py index 8cfa2bfe..122f2479 100644 --- a/nampy/gam/compiler/factory.py +++ b/nampy/gam/compiler/factory.py @@ -10,6 +10,7 @@ FSmoothInteractionTerm, SZSmoothInteractionTerm, ) +from ..smooths.categorical.mrf import MarkovRandomFieldTerm from ..smooths.categorical.re import RandomEffectTerm from ..smooths.parametric import LinearTerm from ..smooths.registry import make_smooth_term @@ -29,6 +30,7 @@ DuchonSplineSmoothSpec, FactorSmoothInteractionSpec, GaussianProcessSmoothSpec, + MarkovRandomFieldSmoothSpec, PSplineSmoothSpec, RandomEffectSmoothSpec, ShapeConstrainedSmoothSpec, @@ -228,6 +230,23 @@ def instantiate_term(term_like: TermSpec | Any): metadata=metadata, ) + if isinstance(smooth_spec, MarkovRandomFieldSmoothSpec): + return MarkovRandomFieldTerm( + feature=features, + k=smooth_spec.k, + label=label, + term_id=term_like.term_id, + smoothing_id=smoothing_id, + by=by, + sp=smooth_spec.sp, + select=smooth_spec.select, + fixed=smooth_spec.fx, + constraint_mode=smooth_spec.constraint_mode, + knots=smooth_spec.knots, + xt=smooth_spec.xt, + metadata=metadata, + ) + if isinstance(smooth_spec, ShapeConstrainedSmoothSpec): if len(features) == 2: return BivariateShapePSplineTerm( diff --git a/nampy/gam/compiler/linked_basis.py b/nampy/gam/compiler/linked_basis.py index ea9aff0e..ea5da12b 100644 --- a/nampy/gam/compiler/linked_basis.py +++ b/nampy/gam/compiler/linked_basis.py @@ -103,6 +103,26 @@ def attach_shared_basis_metadata(predictor_specs, X, feature_names): "pc= is not supported across id-linked s() terms with different " "feature sets; mgcv 1.9-4 fails while constructing the shared basis." ) + has_mrf = any( + term.smooth_spec is not None + and ( + str(getattr(term.smooth_spec, "bs", "")).lower() == "mrf" + or ( + isinstance(getattr(term.smooth_spec, "bs", None), (list, tuple)) + and "mrf" + in { + str(value).lower() + for value in getattr(term.smooth_spec, "bs", ()) + } + ) + ) + for term in group_terms + ) + if has_mrf and len(feature_tuples) > 1: + raise NotImplementedError( + "id= is not supported across MRF terms with different feature " + "sets; mgcv 1.9-4 loses the factor topology while pooling them." + ) for term in group_terms[1:]: _clone_linked_smooth_spec(base_term, term) diff --git a/nampy/gam/model/api.py b/nampy/gam/model/api.py index c9e9ba9a..b658c333 100644 --- a/nampy/gam/model/api.py +++ b/nampy/gam/model/api.py @@ -174,6 +174,7 @@ def _restore_prediction_na_rows(value, retained_rows, n_rows): "covariance", "select", "knots", + "xt", "min_sp", "drop_intercept", "formula", @@ -240,6 +241,7 @@ def __init__( self.covariance = str(self.hparams.get("covariance", "bayes")).lower() self.select = bool(self.hparams.get("select", False)) self.knots = self.hparams.get("knots", None) + self.xt = self.hparams.get("xt", None) self.min_sp = self.hparams.get("min_sp", None) self.drop_intercept = self.hparams.get("drop_intercept", None) self.positive_transform = str( diff --git a/nampy/gam/smooths/__init__.py b/nampy/gam/smooths/__init__.py index 07ab6dae..65c16552 100644 --- a/nampy/gam/smooths/__init__.py +++ b/nampy/gam/smooths/__init__.py @@ -1,4 +1,5 @@ from .categorical.fs import FSmoothInteractionTerm, SZSmoothInteractionTerm +from .categorical.mrf import MarkovRandomFieldTerm from .categorical.re import RandomEffectTerm from .registry import make_smooth_term, register_smooth from .shape.scop import ShapeConstrainedPSplineTerm @@ -35,6 +36,7 @@ cr = cs = cc = CubicSplineTerm ds = DuchonSplineTerm gp = GaussianProcessTerm +mrf = MarkovRandomFieldTerm cp = ps = PSplineTerm1D tp = ts = ThinPlateSplineTerm fs = FSmoothInteractionTerm @@ -63,6 +65,7 @@ "CubicSplineTerm", "DuchonSplineTerm", "GaussianProcessTerm", + "MarkovRandomFieldTerm", "DerivativeBSplineTerm1D", "PSplineTerm1D", "ThinPlateSplineTerm", @@ -80,6 +83,7 @@ "cc", "ds", "gp", + "mrf", "cp", "ps", "tp", diff --git a/nampy/gam/smooths/categorical/__init__.py b/nampy/gam/smooths/categorical/__init__.py index a446930f..8479c011 100644 --- a/nampy/gam/smooths/categorical/__init__.py +++ b/nampy/gam/smooths/categorical/__init__.py @@ -8,11 +8,13 @@ try_numeric_1d, ) from .fs import FSmoothInteractionTerm, SZSmoothInteractionTerm +from .mrf import MarkovRandomFieldTerm from .re import RandomEffectTerm fs = FSmoothInteractionTerm sz = SZSmoothInteractionTerm re = RandomEffectTerm +mrf = MarkovRandomFieldTerm __all__ = [ "as_object_1d", @@ -23,9 +25,11 @@ "factor_indicator_matrix", "factor_levels_from_metadata", "RandomEffectTerm", + "MarkovRandomFieldTerm", "FSmoothInteractionTerm", "SZSmoothInteractionTerm", "fs", "sz", "re", + "mrf", ] diff --git a/nampy/gam/smooths/categorical/fs.py b/nampy/gam/smooths/categorical/fs.py index 484dcb63..6dd0a113 100644 --- a/nampy/gam/smooths/categorical/fs.py +++ b/nampy/gam/smooths/categorical/fs.py @@ -27,6 +27,7 @@ is_factor_like_vector, stable_unique_levels, ) +from .mrf import MarkovRandomFieldTerm from .re import RandomEffectTerm @@ -110,7 +111,7 @@ def _build_base_smooth_term( Build the per-level base smooth used inside fs/sz. Supported base smooth classes in the current codebase: - bs, cr, cs, cc, cp, ds, gp, ps, tp, ts + bs, cr, cs, cc, cp, ds, gp, mrf, ps, tp, ts """ base_bs = str(base_bs).lower() metric_features = list(metric_features) @@ -132,12 +133,13 @@ def _build_base_smooth_term( "cp", "ds", "gp", + "mrf", "ps", "tp", "ts", }: raise NotImplementedError( - "Extra xt options are currently only supported for bs/cp/ds/gp/ps/tp/ts " + "Extra xt options are currently only supported for bs/cp/ds/gp/mrf/ps/tp/ts " "base smooths, " f"got xt={xt_rest!r} with base bs={base_bs!r}." ) @@ -240,6 +242,22 @@ def _build_base_smooth_term( metadata=metadata, ) + if base_bs == "mrf": + return MarkovRandomFieldTerm( + feature=metric_features[0], + k=k, + label=label, + smoothing_id=None, + by=by, + sp=None, + select=bool(select), + fixed=bool(fixed), + constraint_mode=str(constraint_mode), + knots=knots, + xt=xt_rest, + metadata=metadata, + ) + if base_bs in {"tp", "ts"}: return make_smooth_term( base_bs, @@ -262,7 +280,7 @@ def _build_base_smooth_term( raise NotImplementedError( f"Current {mode} implementation supports base bs in " - f"{{'bs','cr','cs','cc','cp','ds','gp','ps','tp','ts'}}, got {base_bs!r}." + f"{{'bs','cr','cs','cc','cp','ds','gp','mrf','ps','tp','ts'}}, got {base_bs!r}." ) @@ -273,6 +291,8 @@ def _penalty_rank_from_base_term(base_term, basis_matrix, penalty_matrix) -> int return int(base_term._setup.rank) if isinstance(base_term, GaussianProcessTerm): return int(base_term._setup.rank) + if isinstance(base_term, MarkovRandomFieldTerm): + return int(base_term._setup.rank) if isinstance(base_term, PSplineTerm1D) and len(base_term.penalties) > 0: if str(base_term.basis_name).lower() == "cp": return int(base_term._setup.rank) @@ -668,6 +688,8 @@ def fit(self, X, feature_names): metadata=dict(self.metadata), ) base_term.fit(X, feature_names) + if isinstance(base_term, MarkovRandomFieldTerm): + self.metadata = dict(base_term.metadata) if len(base_term.penalties) > 1: raise NotImplementedError( @@ -675,6 +697,15 @@ def fit(self, X, feature_names): ) self._base_term = base_term + if ( + isinstance(base_term, MarkovRandomFieldTerm) + and base_term._setup.used_low_rank + ): + raise NotImplementedError( + "mgcv 1.9-4 cannot predict an fs smooth with a reduced-rank " + "MRF base because its factor-smooth P matrix is dimensionally " + "incompatible with the full region indicator." + ) B_setup, S0, _ = self._base_constructor_fit_matrices() B_setup = np.asarray(B_setup, dtype=np.float64) B0 = np.asarray(self._base_constructor_predict_matrix(X), dtype=np.float64) @@ -722,6 +753,15 @@ def fit(self, X, feature_names): null_d = B0.shape[1] - r self._base_transform = P_coef + if isinstance(base_term, MarkovRandomFieldTerm): + # The raw prediction callback below deliberately exposes mgcv's + # double-P MRF/fs prediction surface. Tell the compiler which + # inverse block map recovers the constructor matrix solely for its + # fit-vs-prediction parameterization audit; actual prediction does + # not apply this metadata map. + self.metadata["prediction_basis_map"] = np.kron( + np.eye(n_levels, dtype=np.float64), np.linalg.inv(P_coef) + ) self._base_range_penalty_diag = np.concatenate( [D, np.zeros(null_d, dtype=np.float64)] ) @@ -801,6 +841,13 @@ def transform_new(self, X_new): ) if self._base_transform is not None: B0_new = B0_new @ self._base_transform + if isinstance(self._base_term, MarkovRandomFieldTerm): + # Upstream Predict.matrix.fs.interaction changes the object back + # to class mrf.smooth after overwriting object$P with the fs + # natural-parameter transform. Predict.matrix.mrf.smooth applies + # that P once and the fs wrapper applies it a second time. Preserve + # this observable mgcv 1.9-4 prediction parameterization exactly. + B0_new = B0_new @ self._base_transform B_new = rowwise_kronecker([Ifac, B0_new]) z = by_values_from_new_data(X_new, self._by_state) @@ -948,6 +995,8 @@ def fit(self, X, feature_names): metadata=dict(self.metadata), ) base_term.fit(X, feature_names) + if isinstance(base_term, MarkovRandomFieldTerm): + self.metadata = dict(base_term.metadata) if len(base_term.penalties) > 1: raise NotImplementedError( diff --git a/nampy/gam/smooths/categorical/mrf.py b/nampy/gam/smooths/categorical/mrf.py new file mode 100644 index 00000000..8360838d --- /dev/null +++ b/nampy/gam/smooths/categorical/mrf.py @@ -0,0 +1,532 @@ +"""Markov-random-field smooths matching mgcv ``bs='mrf'``.""" + +from __future__ import annotations + +import warnings +from dataclasses import dataclass + +import numpy as np +import pandas as pd + +from ...constraints.absorption import ( + fit_single_penalty_with_constraint_policy, + fit_single_penalty_with_setup_basis, +) +from ...penalties.algebra import penalty_rescale_factor, scale_penalty +from ...splines.basis.natparam import nat_param_type0 +from ..registry import register_smooth +from ..smooth_base import BaseSmoothTerm, _resolve_feature, column_as_object +from .categorical_utils import factor_levels_from_metadata + + +def _canonical_label(value) -> str | None: + if pd.isna(value): + return None + if isinstance(value, (bool, np.bool_)): + return "TRUE" if bool(value) else "FALSE" + if isinstance(value, (int, np.integer)): + return str(int(value)) + if isinstance(value, (float, np.floating)): + number = float(value) + if number.is_integer(): + return str(int(number)) + return format(number, ".15g") + return str(value) + + +def _canonical_labels(values, *, boolean_coded=False) -> np.ndarray: + raw = np.asarray(values, dtype=object).ravel() + labels = [] + for value in raw: + if ( + boolean_coded + and isinstance(value, (int, np.integer, float, np.floating)) + and not isinstance(value, (bool, np.bool_)) + and float(value) in {0.0, 1.0} + ): + labels.append("TRUE" if float(value) == 1.0 else "FALSE") + else: + labels.append(_canonical_label(value)) + return np.asarray(labels, dtype=object) + + +def _sorted_factor_levels(values) -> list[str]: + raw = np.asarray(values, dtype=object).ravel() + raw = np.asarray([value for value in raw if not pd.isna(value)], dtype=object) + try: + ordered = np.unique(raw).tolist() + except (TypeError, ValueError): + ordered = sorted(set(_canonical_labels(raw))) + return [str(_canonical_label(value)) for value in ordered] + + +def _knot_levels(knots) -> list[str]: + if isinstance(knots, pd.Categorical): + return [str(_canonical_label(value)) for value in knots.categories] + if isinstance(knots, pd.Series) and isinstance(knots.dtype, pd.CategoricalDtype): + return [str(_canonical_label(value)) for value in knots.cat.categories] + return _sorted_factor_levels(np.asarray(knots, dtype=object)) + + +def _indicator_matrix(values, levels) -> np.ndarray: + labels = _canonical_labels(values) + index = {str(level): j for j, level in enumerate(levels)} + out = np.zeros((labels.size, len(levels)), dtype=np.float64) + for row, label in enumerate(labels): + if label is not None and label in index: + out[row, index[label]] = 1.0 + return out + + +def _polygons_to_neighbors(polygons) -> dict[str, list[str]]: + if not isinstance(polygons, dict): + raise TypeError( + "MRF xt['polys'] must be a mapping from area names to polygons." + ) + names = [str(_canonical_label(name)) for name in polygons] + vertices: dict[str, set[tuple[float, float]]] = {} + for raw_name, polygon in polygons.items(): + array = np.asarray(polygon, dtype=np.float64) + if array.ndim != 2 or array.shape[1] != 2: + raise ValueError("Each MRF polygon must be a two-column matrix.") + finite = array[np.isfinite(array).all(axis=1)] + vertices[str(_canonical_label(raw_name))] = { + tuple(map(float, row)) for row in finite + } + return { + name: [ + other + for other in names + if other != name and vertices[name] & vertices[other] + ] + for name in names + } + + +def _neighbor_penalty(neighbors, levels) -> np.ndarray: + if not isinstance(neighbors, dict) or len(neighbors) == 0: + raise TypeError("MRF xt['nb'] must be a non-empty named mapping.") + area_names = [str(_canonical_label(name)) for name in neighbors] + if sorted(area_names) != sorted(levels): + raise ValueError( + "mismatch between nb/polys supplied area names and data area names" + ) + + entry_modes = set() + for raw_adjacent in neighbors.values(): + adjacent = np.asarray(raw_adjacent, dtype=object).ravel().tolist() + if not adjacent: + continue + numeric_flags = [ + isinstance(value, (int, np.integer, float, np.floating)) + and not isinstance(value, (bool, np.bool_)) + and float(value).is_integer() + for value in adjacent + ] + if any(numeric_flags) and not all(numeric_flags): + raise TypeError( + "MRF neighbour entries must use either all area names or all " + "one-based numeric indices." + ) + entry_modes.add("numeric" if all(numeric_flags) else "named") + if len(entry_modes) > 1: + raise TypeError( + "MRF neighbour lists must use one uniform representation: either " + "area names or one-based numeric indices." + ) + neighbor_mode = next(iter(entry_modes), "named") + + S = np.zeros((len(levels), len(levels)), dtype=np.float64) + level_index = {name: i for i, name in enumerate(levels)} + for raw_name, raw_adjacent in neighbors.items(): + name = str(_canonical_label(raw_name)) + adjacent = np.asarray(raw_adjacent, dtype=object).ravel().tolist() + if neighbor_mode == "numeric": + adjacent_names = [] + for value in adjacent: + position = int(value) - 1 + if position < 0 or position >= len(area_names): + raise IndexError("MRF neighbour index is outside the named list.") + adjacent_names.append(area_names[position]) + else: + requested = {str(_canonical_label(value)) for value in adjacent} + # mgcv maps named neighbours through `which(nb.names %in% entry)`, + # so names are set-like: ordering, duplicates, and unknown names + # disappear before the degree is computed. + adjacent_names = [value for value in area_names if value in requested] + + row = level_index[name] + S[row, row] = float(len(adjacent_names)) + for adjacent_name in adjacent_names: + column = level_index[adjacent_name] + if column != row: + S[row, column] = -1.0 + if np.any(S != S.T): + raise ValueError("Something wrong with auto- penalty construction") + return S + + +def _supplied_penalty(penalty, levels) -> np.ndarray: + if isinstance(penalty, pd.DataFrame): + column_names = [str(_canonical_label(value)) for value in penalty.columns] + if sorted(column_names) != sorted(levels): + raise ValueError("penalty column names don't match supplied area names!") + lookup_rows = {str(_canonical_label(value)): value for value in penalty.index} + lookup_cols = {str(_canonical_label(value)): value for value in penalty.columns} + penalty = penalty.loc[ + [lookup_rows[level] for level in levels], + [lookup_cols[level] for level in levels], + ] + matrix = np.asarray(penalty, dtype=np.float64) + if matrix.ndim != 2 or matrix.shape[0] != matrix.shape[1]: + raise ValueError("supplied penalty not square!") + if matrix.shape != (len(levels), len(levels)): + raise ValueError("supplied penalty wrong dimension!") + return matrix + + +@dataclass +class MarkovRandomFieldSetup: + levels: tuple[str, ...] + basis_train: np.ndarray + penalty: np.ndarray + raw_penalty: np.ndarray + P: np.ndarray | None + rank: int + null_space_dim: int + bs_dim: int + used_low_rank: bool + plot_me: bool + boolean_coded: bool + + +def build_markov_random_field_setup( + values, *, k=-1, xt=None, knots=None, factor_levels=None +) -> MarkovRandomFieldSetup: + """Port ``smooth.construct.mrf.smooth.spec`` before smoothCon scaling.""" + raw_values = np.asarray(values, dtype=object).ravel() + declared = ( + [] + if factor_levels is None + else np.asarray(factor_levels, dtype=object).ravel().tolist() + ) + boolean_coded = bool( + declared and all(isinstance(value, (bool, np.bool_)) for value in declared) + ) + labels = _canonical_labels(raw_values, boolean_coded=boolean_coded) + if any(value is None for value in labels): + raise ValueError("MRF smooths do not allow missing regions in fitting.") + + if factor_levels is None: + data_levels = _sorted_factor_levels(labels) + else: + data_levels = sorted(str(_canonical_label(value)) for value in factor_levels) + levels = data_levels if knots is None else _knot_levels(knots) + if any(value not in levels for value in data_levels): + raise ValueError( + "data contain regions that are not contained in the knot specification" + ) + observed_levels = {value for value in labels if value is not None} + if any(value not in levels for value in observed_levels): + raise ValueError( + "data contain regions that are not contained in the knot specification" + ) + + bs_dim = len(levels) if int(k) < 0 else int(k) + if bs_dim > len(levels): + raise ValueError("MRF basis dimension set too high") + if bs_dim <= 2 and bs_dim < len(levels): + raise ValueError( + "A reduced-rank MRF basis with k<=2 is malformed in mgcv 1.9-4; use k>=3." + ) + if xt is None: + raise ValueError( + "penalty matrix, boundary polygons and/or neighbours list must be supplied in xt" + ) + if not isinstance(xt, dict): + raise TypeError("MRF xt must be a dictionary.") + + plot_me = xt.get("polys") is not None + if xt.get("penalty") is not None: + raw_penalty = _supplied_penalty(xt["penalty"], levels) + else: + neighbors = xt.get("nb") + if neighbors is None: + polygons = xt.get("polys") + if polygons is None: + raise ValueError("no spatial information provided!") + neighbors = _polygons_to_neighbors(polygons) + raw_penalty = _neighbor_penalty(neighbors, levels) + + X = _indicator_matrix(labels, levels) + P = None + if bs_dim < len(levels): + missing = np.flatnonzero(np.sum(X, axis=0) == 0.0) + X_complete = X + if missing.size: + dummy = np.zeros((missing.size, X.shape[1]), dtype=np.float64) + dummy[np.arange(missing.size), missing] = 1.0 + X_complete = np.vstack([dummy, X]) + natural = nat_param_type0(X_complete, raw_penalty) + # Retain the final (least penalized) natural-parameter columns. + keep = np.arange(len(levels) - bs_dim, len(levels), dtype=int) + X_natural = np.asarray(natural["X"], dtype=np.float64) + X = X_natural[missing.size :, keep] if missing.size else X_natural[:, keep] + P = np.asarray(natural["P"], dtype=np.float64)[:, keep] + diagonal = np.asarray( + [natural["D"][i] if i < natural["rank"] else 0.0 for i in keep], + dtype=np.float64, + ) + penalty = np.diag(diagonal) + rank = int(np.sum(keep < natural["rank"])) + else: + penalty = np.asarray(raw_penalty, dtype=np.float64) + eigenvalues = np.linalg.eigvalsh(penalty) + largest = float(np.max(eigenvalues)) if eigenvalues.size else 0.0 + rank = int(np.sum(eigenvalues > np.finfo(float).eps ** 0.8 * largest)) + + return MarkovRandomFieldSetup( + levels=tuple(levels), + basis_train=np.asarray(X, dtype=np.float64), + penalty=np.asarray(penalty, dtype=np.float64), + raw_penalty=np.asarray(raw_penalty, dtype=np.float64), + P=P, + rank=rank, + null_space_dim=int(bs_dim - rank), + bs_dim=int(bs_dim), + used_low_rank=bool(P is not None), + plot_me=bool(plot_me), + boolean_coded=boolean_coded, + ) + + +def predict_markov_random_field(values, setup: MarkovRandomFieldSetup) -> np.ndarray: + labels = _canonical_labels(values, boolean_coded=setup.boolean_coded) + unknown = sorted( + { + str(value) + for value in labels + if value is not None and value not in setup.levels + } + ) + if unknown: + raise ValueError( + f"MRF prediction data contain unknown regions {unknown}; " + f"known regions are {list(setup.levels)}." + ) + basis = _indicator_matrix(labels, setup.levels) + if setup.P is not None: + basis = basis @ setup.P + return np.asarray(basis, dtype=np.float64) + + +@register_smooth("mrf") +class MarkovRandomFieldTerm(BaseSmoothTerm): + term_type = "smooth" + basis_name = "mrf" + supports_tensor_marginal = True + + def __init__( + self, + feature, + k=-1, + label=None, + term_id=None, + smoothing_id=None, + by=None, + sp=None, + select=False, + fixed=False, + constraint_mode="auto", + knots=None, + xt=None, + metadata=None, + ): + features = list(feature) if not isinstance(feature, (str, int)) else [feature] + if len(features) != 1: + raise ValueError("MRF smooths require exactly one region covariate.") + super().__init__( + feature=features, + label=label or f"s({features[0]})", + term_id=term_id, + smoothing_id=smoothing_id, + by=by, + sp=sp, + metadata=metadata, + ) + self.k = int(k) + self.select = bool(select) + self.fixed = bool(fixed) + self.constraint_mode = str(constraint_mode).lower() + self.knots = knots + self.xt = xt + self._feature_index = None + self._feature_name = None + self._factor_feature_indices = None + self._factor_feature_names = None + self._factor_levels = None + self._setup = None + self._basis_train = None + self._penalties = None + + @property + def expected_linked_penalty_count(self): + return None if self.select else 1 + + def fit(self, X, feature_names): + if self.fixed and self.sp is not None: + sp_values = np.asarray(self.sp, dtype=np.float64).ravel() + if np.any(sp_values >= 0.0): + raise ValueError( + "incorrect number of smoothing parameters supplied for a smooth term" + ) + index, name = _resolve_feature(self.feature[0], feature_names) + values = column_as_object(X, index) + declared_levels = factor_levels_from_metadata(self.metadata, name) + if declared_levels is None: + try: + np.asarray(values, dtype=np.float64) + except (TypeError, ValueError): + pass + else: + warnings.warn( + "argument of mrf should be a factor variable", stacklevel=2 + ) + + self._set_by_state(X, feature_names) + self._feature_index = index + self._feature_name = name + self._factor_feature_indices = [index] + self._factor_feature_names = [name] + self._set_resolved_features([name]) + + shared_X = self._linked_id_setup_matrix(feature_names) + setup_values = values if shared_X is None else column_as_object(shared_X, index) + self._setup = build_markov_random_field_setup( + setup_values, + k=self.k, + xt=self.xt, + knots=self.knots, + factor_levels=(declared_levels if shared_X is None else None), + ) + self._factor_levels = [list(self._setup.levels)] + if declared_levels is not None and self.knots is not None: + meta = dict(self.metadata or {}) + factor_meta = dict(meta.get("factor_levels_by_feature", {})) + current = dict(factor_meta.get(str(name), {})) + allowed = list(current.get("levels", declared_levels)) + for value in self._setup.levels: + if value not in allowed: + allowed.append(value) + current["levels"] = allowed + factor_meta[str(name)] = current + meta["factor_levels_by_feature"] = factor_meta + self.metadata = meta + setup_base = np.asarray(self._setup.basis_train, dtype=np.float64) + base = ( + setup_base + if shared_X is None + else predict_markov_random_field(values, self._setup) + ) + raw_penalty = np.asarray(self._setup.penalty, dtype=np.float64) + penalty = scale_penalty(setup_base, raw_penalty) + self._set_penalty_rescale_factors( + [penalty_rescale_factor(setup_base, raw_penalty)] + ) + + auto_constrain = bool(self._by_state.is_constant) + if shared_X is None: + result = fit_single_penalty_with_constraint_policy( + base, + penalty, + self._by_state, + constraint_mode=self.constraint_mode, + fixed=self.fixed, + auto_constrain_when=auto_constrain, + ) + else: + result = fit_single_penalty_with_setup_basis( + base, + setup_base, + penalty, + self._by_state, + constraint_mode=self.constraint_mode, + fixed=self.fixed, + auto_constrain_when=auto_constrain, + ) + self._basis_train = result.basis_train + self._penalties = result.penalties + self._record_constraint_result( + result.constraint_kind, + result.constraint_transform, + absorbed_by="runtime" if result.constraint_transform is not None else None, + ) + return self + + def get_penalty_definitions(self): + self._require_fitted() + if not self.penalties: + return [] + metadata = self._penalty_metadata_with_scale( + { + "term_type": self.term_type, + "basis_name": self.basis_name, + "feature": list(self.feature), + "label": self.label, + "by": self.by, + "by_name": self._by_state.feature_name, + "constraint_mode": self.constraint_mode, + "constraint_kind": self.constraint_kind, + "knots": self.knots, + "xt": self.xt, + "levels": list(self._setup.levels), + "fixed": self.fixed, + }, + penalty_index=0, + ) + return self._build_penalty_block( + self.penalties[0], + rank=min(int(self._setup.rank), int(self.penalties[0].shape[0])), + smooth_metadata=metadata, + selection_metadata={**metadata, "is_selection_penalty": True}, + ) + + def transform_new(self, X_new): + self._require_fitted() + basis = predict_markov_random_field( + column_as_object(X_new, self._feature_index), self._setup + ) + return self._apply_constraint_transform_and_by(basis, X_new) + + def tensor_marginal_fit_matrices( + self, *, centered=False, apply_np=False, x_train=None + ): + del apply_np, x_train + self._require_fitted() + setup_base = np.asarray(self._setup.basis_train, dtype=np.float64) + setup_penalty = np.asarray(self._setup.penalty, dtype=np.float64) + if centered: + return super().tensor_marginal_fit_matrices(centered=True) + return setup_base, setup_penalty, None + + def tensor_marginal_predict_matrix( + self, X_new, *, centered=False, np_transform=None + ): + basis = ( + self.transform_new(X_new) + if centered + else predict_markov_random_field( + column_as_object(X_new, self._feature_index), self._setup + ) + ) + if np_transform is not None: + basis = basis @ np.asarray(np_transform, dtype=np.float64) + return np.asarray(basis, dtype=np.float64) + + +__all__ = [ + "MarkovRandomFieldSetup", + "MarkovRandomFieldTerm", + "build_markov_random_field_setup", + "predict_markov_random_field", +] diff --git a/nampy/gam/smooths/tensor/marginals.py b/nampy/gam/smooths/tensor/marginals.py index afdf833d..cec7fb08 100644 --- a/nampy/gam/smooths/tensor/marginals.py +++ b/nampy/gam/smooths/tensor/marginals.py @@ -6,6 +6,7 @@ from ...penalties.tensor import normalize_tensor_marginal_penalty from ..algebra import rowwise_kronecker +from ..categorical.mrf import MarkovRandomFieldTerm from ..smooth_base import column_as_float from ..univariate.bs import DerivativeBSplineTerm1D from ..univariate.cr import CubicSplineTerm @@ -15,7 +16,7 @@ from ..univariate.tp import ThinPlateSplineTerm TENSOR_MARGINAL_BASES = frozenset( - {"bs", "cr", "cs", "cc", "cp", "ds", "gp", "ps", "tp", "ts"} + {"bs", "cr", "cs", "cc", "cp", "ds", "gp", "mrf", "ps", "tp", "ts"} ) @@ -46,16 +47,15 @@ def make_tensor_marginal_term( knots=None, centered=False, shared_basis_setup=None, + metadata=None, ): basis = str(basis).lower() validate_tensor_marginal_bases([basis]) constraint_mode = "always" if centered else "never" marginal_features = _as_marginal_features(feature) - metadata = ( - None - if shared_basis_setup is None - else {"shared_basis_setup": shared_basis_setup} - ) + metadata = dict(metadata or {}) + if shared_basis_setup is not None: + metadata["shared_basis_setup"] = shared_basis_setup if basis in {"cr", "cs", "cc"}: if len(marginal_features) != 1: raise ValueError( @@ -147,6 +147,23 @@ def make_tensor_marginal_term( metadata=metadata, ) + if basis == "mrf": + if len(marginal_features) != 1: + raise ValueError("Tensor marginal basis 'mrf' only handles one feature.") + return MarkovRandomFieldTerm( + feature=marginal_features[0], + k=k, + xt=xt, + label=str(feature), + smoothing_id=None, + by=None, + select=False, + fixed=False, + constraint_mode=constraint_mode, + knots=knots, + metadata=metadata, + ) + if basis in {"tp", "ts"}: return ThinPlateSplineTerm( feature=marginal_features, @@ -281,6 +298,7 @@ def build_tensor_marginal_terms( knots=None, centered=False, shared_basis_setups=None, + metadata=None, ): features = list(feature) if not isinstance(feature, (str, int)) else [feature] k_list = [int(k)] * len(features) if np.isscalar(k) else [int(v) for v in k] @@ -343,6 +361,7 @@ def build_tensor_marginal_terms( knots=knots_i, centered=center_i, shared_basis_setup=shared_i, + metadata=metadata, ) marginals.append(term) feature_ids.append(feat) @@ -372,6 +391,7 @@ def build_tensor_product_components( x_train = ( column_as_float(X, marginal_indices[0]) if len(marginal_indices) == 1 + and str(getattr(m, "basis_name", "")).lower() != "mrf" else None ) shared_setup = getattr(m, "shared_basis_setup", None) @@ -380,7 +400,11 @@ def build_tensor_product_components( and str(shared_setup.get("mode", "")).lower() == "linked_id" and shared_setup.get("pooled_feature_values") ) - if use_linked_id_predict_path and len(marginal_indices) == 1: + if ( + use_linked_id_predict_path + and len(marginal_indices) == 1 + and str(getattr(m, "basis_name", "")).lower() != "mrf" + ): x_train = np.asarray( shared_setup["pooled_feature_values"][0], dtype=np.float64 ).ravel() @@ -478,7 +502,7 @@ def _tensor_marginal_eval_from_x(term, x, *, centered=False): def _tensor_np_reparameterization(term, x_train, basis_dim, *, centered=False): - if str(getattr(term, "basis_name", "")).lower() in {"cr", "cs", "cc"}: + if str(getattr(term, "basis_name", "")).lower() in {"cr", "cs", "cc", "mrf"}: return None if x_train is None: return None diff --git a/nampy/gam/smooths/tensor/te.py b/nampy/gam/smooths/tensor/te.py index 146dbc56..ec45ebfa 100644 --- a/nampy/gam/smooths/tensor/te.py +++ b/nampy/gam/smooths/tensor/te.py @@ -131,9 +131,12 @@ def fit(self, X, feature_names): knots=self.knots, centered=False, shared_basis_setups=marginal_shared_setups, + metadata=self.metadata, ) for term in marginals: term.fit(X, feature_names) + if str(getattr(term, "basis_name", "")).lower() == "mrf": + self.metadata = dict(term.metadata) feature_indices, feature_names_resolved = resolve_tensor_marginal_features( marginals ) @@ -223,9 +226,7 @@ def point_basis_fn(point): self._set_penalty_rescale_factors( [ float(scale) - for scale, keep in zip( - penalty_scales, keep_penalties, strict=True - ) + for scale, keep in zip(penalty_scales, keep_penalties, strict=True) if keep ] ) diff --git a/nampy/gam/smooths/tensor/ti.py b/nampy/gam/smooths/tensor/ti.py index 51877be7..de0ce16f 100644 --- a/nampy/gam/smooths/tensor/ti.py +++ b/nampy/gam/smooths/tensor/ti.py @@ -137,9 +137,12 @@ def fit(self, X, feature_names): knots=self.knots, centered=self._mc, shared_basis_setups=marginal_shared_setups, + metadata=self.metadata, ) for term in marginals: term.fit(X, feature_names) + if str(getattr(term, "basis_name", "")).lower() == "mrf": + self.metadata = dict(term.metadata) feature_indices, feature_names_resolved = resolve_tensor_marginal_features( marginals ) @@ -209,9 +212,7 @@ def point_basis_fn(point): self._set_penalty_rescale_factors( [ float(scale) - for scale, keep in zip( - penalty_scales, keep_penalties, strict=True - ) + for scale, keep in zip(penalty_scales, keep_penalties, strict=True) if keep ] ) diff --git a/nampy/gam/specs/__init__.py b/nampy/gam/specs/__init__.py index b8d9d702..0b849c2e 100644 --- a/nampy/gam/specs/__init__.py +++ b/nampy/gam/specs/__init__.py @@ -12,6 +12,7 @@ DuchonSplineSmoothSpec, FactorSmoothInteractionSpec, GaussianProcessSmoothSpec, + MarkovRandomFieldSmoothSpec, PSplineSmoothSpec, RandomEffectSmoothSpec, ShapeConstrainedSmoothSpec, @@ -36,6 +37,7 @@ "DuchonSplineSmoothSpec", "FactorSmoothInteractionSpec", "GaussianProcessSmoothSpec", + "MarkovRandomFieldSmoothSpec", "PSplineSmoothSpec", "RandomEffectSmoothSpec", "ShapeConstrainedSmoothSpec", diff --git a/nampy/gam/specs/build.py b/nampy/gam/specs/build.py index a93add5c..66d980f1 100644 --- a/nampy/gam/specs/build.py +++ b/nampy/gam/specs/build.py @@ -477,7 +477,7 @@ def _fs_by_without_factor_feature_base_spec(smooth_spec: FactorSmoothInteraction if base_bs == "ps": kwargs["m"] = None if xt_rest is None else xt_rest.get("m", None) - elif base_bs in {"ds", "gp", "tp", "ts"}: + elif base_bs in {"ds", "gp", "mrf", "tp", "ts"}: if base_bs in {"ds", "gp"}: kwargs["m"] = smooth_spec.m kwargs["xt"] = xt_rest diff --git a/nampy/gam/specs/modeling.py b/nampy/gam/specs/modeling.py index 371ded20..60251958 100644 --- a/nampy/gam/specs/modeling.py +++ b/nampy/gam/specs/modeling.py @@ -21,6 +21,19 @@ def make_predictor_specs(model, feature_names, *, knots=None): for name in feature_names: term_knots = knots_for_feature(model, name, knots=knots) + model_xt = getattr(model, "xt", None) + is_global_mrf_xt = ( + basis == "mrf" + and isinstance(model_xt, dict) + and bool({"penalty", "nb", "polys"}.intersection(model_xt)) + ) + term_xt = ( + model_xt.get(str(name)) + if isinstance(model_xt, dict) + and str(name) in model_xt + and not is_global_mrf_xt + else model_xt + ) if basis in {"cr", "cs", "cc"}: main_terms.append( @@ -42,7 +55,7 @@ def make_predictor_specs(model, feature_names, *, knots=None): metadata={}, ) ) - elif basis in {"bs", "ds", "gp", "ps", "cp"}: + elif basis in {"bs", "ds", "gp", "mrf", "ps", "cp"}: main_terms.append( TermSpec( kind="smooth", @@ -57,6 +70,7 @@ def make_predictor_specs(model, feature_names, *, knots=None): fx=False, select=bool(model.select), knots=term_knots, + xt=term_xt, ), smoothing_id=None, label=name, @@ -105,7 +119,7 @@ def make_predictor_specs(model, feature_names, *, knots=None): else: raise NotImplementedError( "Automatic main-effect construction currently supports " - "{'bs','cr','cs','cc','cp','ds','gp','ps','tp','ts','re'}, " + "{'bs','cr','cs','cc','cp','ds','gp','mrf','ps','tp','ts','re'}, " f"got {model.basis!r}." ) @@ -150,9 +164,7 @@ def prepare_formula_inputs( build_result.predictor_specs, build_result.component_lpi, strict=True ): spec.metadata["lpi"] = tuple(int(v) for v in lpi) - spec.metadata["n_linear_predictors"] = int( - build_result.n_linear_predictors - ) + spec.metadata["n_linear_predictors"] = int(build_result.n_linear_predictors) return ( parsed, build_result.predictor_specs, diff --git a/nampy/gam/specs/smooth.py b/nampy/gam/specs/smooth.py index a8164142..d06281bf 100644 --- a/nampy/gam/specs/smooth.py +++ b/nampy/gam/specs/smooth.py @@ -76,6 +76,13 @@ class GaussianProcessSmoothSpec(BaseSmoothSpec): pc: Any = None +@dataclass(frozen=True) +class MarkovRandomFieldSmoothSpec(BaseSmoothSpec): + bs: str = "mrf" + xt: Any = None + constraint_mode: str = "auto" + + @dataclass(frozen=True) class ShapeConstrainedSmoothSpec(BaseSmoothSpec): """SCAM SCOP-spline specification for a named shape basis code.""" @@ -152,6 +159,7 @@ class TensorInteractionSmoothSpec(BaseSmoothSpec): DerivativeBSplineSmoothSpec, DuchonSplineSmoothSpec, GaussianProcessSmoothSpec, + MarkovRandomFieldSmoothSpec, CubicShrinkageSmoothSpec, PSplineSmoothSpec, ShapeConstrainedSmoothSpec, diff --git a/nampy/gam/specs/smooth_build.py b/nampy/gam/specs/smooth_build.py index 40420e35..06685d58 100644 --- a/nampy/gam/specs/smooth_build.py +++ b/nampy/gam/specs/smooth_build.py @@ -16,6 +16,7 @@ DuchonSplineSmoothSpec, FactorSmoothInteractionSpec, GaussianProcessSmoothSpec, + MarkovRandomFieldSmoothSpec, PSplineSmoothSpec, RandomEffectSmoothSpec, ShapeConstrainedSmoothSpec, @@ -145,6 +146,19 @@ def _build_s_gp(opts) -> GaussianProcessSmoothSpec: ) +def _build_s_mrf(opts) -> MarkovRandomFieldSmoothSpec: + return MarkovRandomFieldSmoothSpec( + special="s", + k=opts["k"], + fx=opts["fx"], + select=opts["select"], + sp=opts["sp"], + knots=opts["knots"], + xt=opts["xt"], + constraint_mode=opts["constraint_mode"], + ) + + def _build_s_shape(opts) -> ShapeConstrainedSmoothSpec: return ShapeConstrainedSmoothSpec( special="s", @@ -236,6 +250,7 @@ def _build_s_sz(opts) -> SumToZeroFactorSmoothSpec: "bs": _build_s_bs, "ds": _build_s_ds, "gp": _build_s_gp, + "mrf": _build_s_mrf, "tp": _build_s_tp, "ts": _build_s_ts, "re": _build_s_re, @@ -553,7 +568,7 @@ def _default_k_for_smooth(kind, basis, features, default_k): # mgcv::te()/ti() default k to 5^d per marginal. The current # Python tensor surface supports one feature per marginal, so d = 1. return [5] * len(features) - if str(basis).lower() in {"bs", "ds", "gp", "tp", "ts"}: + if str(basis).lower() in {"bs", "ds", "gp", "mrf", "tp", "ts"}: # mgcv/R/smooth.r::s() leaves k = -1. The basis constructor then # resolves its dimension-dependent default (TP/TS use M + 8/27/100; # DS uses M + 10/30/100; GP uses d + 1 + 10/30/100). A flat default here would be wrong in more diff --git a/nampy/gam/splines/basis/__init__.py b/nampy/gam/splines/basis/__init__.py index 18878fab..b668b754 100644 --- a/nampy/gam/splines/basis/__init__.py +++ b/nampy/gam/splines/basis/__init__.py @@ -1,7 +1,7 @@ """Low-level basis algebra and invariant helpers for spline primitives.""" from .cr import cr_exact_null_basis_from_knots, cr_spl, cr_spl_predict -from .natparam import nat_param_type1 +from .natparam import nat_param_type0, nat_param_type1 from .tp import eta, tp_T __all__ = [ @@ -10,5 +10,6 @@ "cr_exact_null_basis_from_knots", "eta", "tp_T", + "nat_param_type0", "nat_param_type1", ] diff --git a/nampy/gam/splines/basis/natparam.py b/nampy/gam/splines/basis/natparam.py index 54bd7054..49eabd8a 100644 --- a/nampy/gam/splines/basis/natparam.py +++ b/nampy/gam/splines/basis/natparam.py @@ -237,4 +237,49 @@ def nat_param_type1(X, S, rank=None, tol=None, unit_fnorm=True): } -__all__ = ["nat_param_type1"] +def nat_param_type0(X, S, rank=None, tol=None, unit_fnorm=True): + """Python implementation of ``mgcv::nat.param(X, S, type=0)``. + + Unlike type 1, the positive natural-parameter penalty eigenvalues are + retained rather than normalized to one. MRF reduced-rank construction + uses this exact parameterization before keeping the least penalized + columns. + """ + X = np.asarray(X, dtype=np.float64) + S = np.asarray(S, dtype=np.float64) + tol = np.finfo(float).eps**0.8 if tol is None else float(tol) + + Q, R = _r_linpack_qr(X, tol) + if matrix_is_rank_deficient(R): + raise ValueError( + "Model matrix is not full rank in natural-parameter construction." + ) + + tmp = _r_triangular_solve(R.T, S.T, lower=True) + RSR = _r_triangular_solve(R.T, tmp.T, lower=True) + evals, U = _r_symmetric_eigh_descending(RSR) + + if rank is None or rank < 1 or rank > S.shape[0]: + max_eval = np.max(evals) if evals.size else 0.0 + rank = int(np.sum(evals > max_eval * tol)) + rank = max(0, min(int(rank), S.shape[0])) + + D = np.asarray(evals[:rank], dtype=np.float64).copy() + Xn = np.asarray(Q @ U, dtype=np.float64) + P = _r_triangular_solve(R, U, lower=False) + + if unit_fnorm: + if rank > 0: + scale = 1.0 / np.sqrt(np.mean(Xn[:, :rank] ** 2)) + Xn[:, :rank] *= scale + P[:, :rank] *= scale + D *= scale**2 + if rank < Xn.shape[1]: + scalef = 1.0 / np.sqrt(np.mean(Xn[:, rank:] ** 2)) + Xn[:, rank:] *= scalef + P[:, rank:] *= scalef + + return {"X": Xn, "D": D, "P": P, "rank": int(rank)} + + +__all__ = ["nat_param_type0", "nat_param_type1"] diff --git a/pyproject.toml b/pyproject.toml index 67e09538..8ebdcccf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -209,6 +209,7 @@ markers = [ "smooth_cp: tests covering cyclic P-spline smooths", "smooth_ds: tests covering Duchon regression spline smooths", "smooth_gp: tests covering Gaussian-process smooths", + "smooth_mrf: tests covering Markov-random-field smooths", "smooth_ps: tests covering P-spline smooths", "smooth_tp: tests covering thin plate smooths", "smooth_ts: tests covering shrinkage thin plate smooths", diff --git a/tests/SUBSYSTEM_COVERAGE.md b/tests/SUBSYSTEM_COVERAGE.md index 6232deb2..1fbfbb88 100644 --- a/tests/SUBSYSTEM_COVERAGE.md +++ b/tests/SUBSYSTEM_COVERAGE.md @@ -15,7 +15,7 @@ local development notes rather than duplicated here. | Subsystem | Primary owner(s) | Primary tests | Notes | | --- | --- | --- | --- | | Formula/spec parsing | `nampy/gam/formula/`, `nampy/gam/specs/` | `tests/parity/test_mgcv_formula_parse_parity.py` | Direct formula parity vs `mgcv`. | -| Smooth constructors / raw basis owners | `nampy/gam/smooths/`, `nampy/gam/splines/` | `tests/smooths/test_mgcv_raw_constructor_parity.py`, `tests/smooths/test_mgcv_smoothcon_parity.py`, `tests/parity/test_gam_spec_build_owner_contracts.py`, `tests/parity/test_mgcv_cp_combinations_parity.py`, `tests/parity/test_mgcv_bs_combinations_parity.py`, `tests/parity/test_mgcv_ds_combinations_parity.py`, `tests/parity/test_mgcv_gp_combinations_parity.py` | Basis, penalty, constructor, and smoothCon surfaces, including cyclic P-splines (`cp`), integrated-derivative B-splines (`bs`), multivariate Duchon splines (`ds`), and five-family stationary/nonstationary Gaussian-process smooths (`gp`) across prediction, selection, linked bases, tensor/factor-smooth combinations, and the upstream tensor-`m` wrong-length warning and zero fallback. | +| Smooth constructors / raw basis owners | `nampy/gam/smooths/`, `nampy/gam/splines/` | `tests/smooths/test_mgcv_raw_constructor_parity.py`, `tests/smooths/test_mgcv_smoothcon_parity.py`, `tests/parity/test_gam_spec_build_owner_contracts.py`, `tests/parity/test_mgcv_cp_combinations_parity.py`, `tests/parity/test_mgcv_bs_combinations_parity.py`, `tests/parity/test_mgcv_ds_combinations_parity.py`, `tests/parity/test_mgcv_gp_combinations_parity.py`, `tests/parity/test_mgcv_mrf_combinations_parity.py` | Basis, penalty, constructor, and smoothCon surfaces, including cyclic P-splines (`cp`), integrated-derivative B-splines (`bs`), multivariate Duchon splines (`ds`), five-family stationary/nonstationary Gaussian-process smooths (`gp`), and graph/polygon/direct-penalty Markov random fields (`mrf`) across prediction, selection, linked bases, tensor/factor-smooth combinations, and explicit malformed-upstream boundaries. | | `pc=` / linked `id=` routing | smooth metadata + linked basis owners | `tests/smooths/test_mgcv_pc_id_parity.py` | Localizes shared-smoothing and point-constraint issues, including `te`/`ti` point constraints. | | Design / pre-fit assembly | `nampy/gam/compiler/`, `nampy/gam/fit/penalized_system.py` | `tests/optimization/test_mgcv_gam_setup_assembly_parity.py`, `tests/optimization/test_mgcv_preoptimization_blocks_parity.py`, `tests/optimization/test_mgcv_preoptimization_reparam_parity.py` | Setup, blocks, and reparameterization parity, including one global shared-component block with overlapping linear-predictor indices. | | Term wrapping / by-variable / offset routing | predictor wrapping + compiled term owners | `tests/optimization/test_gam_term_wrapping_owner_contracts.py` | Localizes wrapped predictor blocks, offset routing, and general-family block ownership before broader prediction parity. | @@ -47,7 +47,7 @@ closes the former seven-stage under-tested backlog. | Pipeline stage | Combination coverage now owned | | --- | --- | | 1. Formula parsing and canonical specs | Formula lists/shared components, transformed covariates, supported numeric/factor interactions, factor-by smooths, formula and fit offsets, intercept policies, weights, knots, `min_sp`, `drop_intercept`, and tensor-`m` warning/fallback behavior; the combined interaction recipe is rebuilt on newdata and compared with a committed `mgcv` reference fixture. | -| 2. Runtime terms and low-level bases | Train/newdata pairing for every supported basis (`bs/cr/cs/cc/cp/ds/gp/ps/tp/ts/re/fs/sz/te/ti`), including univariate, multivariate Duchon/GP, and structured boundary response/SE parity. Row-permutation contracts cover linked and identified-FS terms, PS/TP/TS bases, `te`/`ti`, SZ, and factor-by smooths. Non-unique constructor representations use column-space projectors and penalized response operators instead of arbitrary coefficient orientation. | +| 2. Runtime terms and low-level bases | Train/newdata pairing for every supported basis (`bs/cr/cs/cc/cp/ds/gp/mrf/ps/tp/ts/re/fs/sz/te/ti`), including univariate, multivariate Duchon/GP, categorical MRF, and structured boundary response/SE parity. Row-permutation contracts cover linked and identified-FS terms, PS/TP/TS bases, `te`/`ti`, SZ, and factor-by smooths. Non-unique constructor representations use column-space projectors and penalized response operators instead of arbitrary coefficient orientation. | | 3. Constructed/wrapped terms | Numeric-by plus linked `id=`, factor-by plus linked `id=`, tensor-by, and mixed fixed/free/select penalty ownership, numerical coefficient-map composition, and fitted response/SE parity. | | 4. Predictor/model compilation | Multi-predictor layouts with unequal intercept and offset policies, overlapping shared-component coefficient indices, and three-term linked, fixed/free, select, and rank-deficient assembly; supported `gaulss` and `gammals` layouts are also fitted and compared with `mgcv`. | | 5. Side conditions and identifiability | Repeated, nested, reverse-formula-order, tensor/main-effect, three-way, both identified near-rank regimes, zero-width, no-intercept, ordered factor-by, linked, general-family, two-predictor, SZ, and exempt random/factor smooth cases. Deletion rank and behavior replace raw pivot-column identity where QR/eigen choices are non-unique. | diff --git a/tests/TAXONOMY.md b/tests/TAXONOMY.md index c86ea87d..d0f528c6 100644 --- a/tests/TAXONOMY.md +++ b/tests/TAXONOMY.md @@ -12,7 +12,7 @@ The GAM test suite is intentionally overlapping. The goal is fast subset runs an - `tests/`: shared helpers, marker inference, taxonomy registry, static reference fixtures, and parity-generation R scripts ## Taxonomy Axes -- `smooth_`: `bs`, `cr`, `cs`, `cc`, `cp`, `ds`, `gp`, `ps`, `tp`, `ts`, `te`, `ti`, `fs`, `sz`, `re` +- `smooth_`: `bs`, `cr`, `cs`, `cc`, `cp`, `ds`, `gp`, `mrf`, `ps`, `tp`, `ts`, `te`, `ti`, `fs`, `sz`, `re` - `family_`: `gaussian`, `binomial`, `poisson`, `gamma`, `negbin`, `gaulss`, `gammals`, `general` - `method_`: `fixed`, `reml`, `ml`, `laml`, `gcv`, `ubre` - `link_`: `identity`, `log`, `inverse`, `logit`, `probit`, `cloglog`, `cauchit`, `sqrt` diff --git a/tests/_taxonomy_registry.py b/tests/_taxonomy_registry.py index ae41327c..0fef056d 100644 --- a/tests/_taxonomy_registry.py +++ b/tests/_taxonomy_registry.py @@ -21,6 +21,7 @@ def _leaf(leaf_id: str, *nodeid_parts: str) -> LeafCoverageExpectation: "cp": "smooth_cp", "ds": "smooth_ds", "gp": "smooth_gp", + "mrf": "smooth_mrf", "ps": "smooth_ps", "tp": "smooth_tp", "ts": "smooth_ts", @@ -87,6 +88,7 @@ def _leaf(leaf_id: str, *nodeid_parts: str) -> LeafCoverageExpectation: "test_mgcv_smoothcon_parity.py": {"surface_smoothcon"}, "test_mgcv_raw_constructor_parity.py": {"surface_smoothcon"}, "test_mgcv_ds_combinations_parity.py": {"surface_snapshot"}, + "test_mgcv_mrf_combinations_parity.py": {"surface_snapshot"}, "test_mgcv_score_hist_trace_parity.py": {"surface_trace"}, "test_mgcv_optimization_lifecycle_parity.py": {"surface_trace"}, "test_mgcv_linked_id_trace_parity.py": {"surface_trace"}, @@ -122,6 +124,7 @@ def _leaf(leaf_id: str, *nodeid_parts: str) -> LeafCoverageExpectation: "test_mgcv_known_gaps.py", "test_mgcv_output_parity.py", "test_mgcv_ds_combinations_parity.py", + "test_mgcv_mrf_combinations_parity.py", "test_mgcv_score_hist_trace_parity.py", "test_mgcv_linked_id_trace_parity.py", "test_mgcv_score_gamma_parity.py", @@ -149,6 +152,10 @@ def _leaf(leaf_id: str, *nodeid_parts: str) -> LeafCoverageExpectation: "tests/smooths/test_mgcv_raw_constructor_parity.py", "tests/smooths/test_mgcv_smoothcon_parity.py", ), + "smooth_mrf": ( + "tests/parity/test_mgcv_mrf_combinations_parity.py", + "tests/smooths/test_mgcv_raw_constructor_parity.py", + ), "smooth_cs": ( "tests/smooths/test_mgcv_pc_id_parity.py", "tests/smooths/test_mgcv_raw_constructor_parity.py", diff --git a/tests/mgcv_invariant_policy.py b/tests/mgcv_invariant_policy.py index 097555bd..0643ee82 100644 --- a/tests/mgcv_invariant_policy.py +++ b/tests/mgcv_invariant_policy.py @@ -320,6 +320,17 @@ def _canonicalize_gp_raw_state(state): return state +def _canonicalize_mrf_raw_state(state): + extra = state["extra"] + if extra.get("P") is not None: + state["X"] = np.asarray(state["X"], dtype=np.float64) @ np.asarray( + state["X"], dtype=np.float64 + ).T + extra["P"] = stable_column_space_projector(extra["P"]) + state["S"] = [penalty_spectrum(S) for S in state["S"]] + return state + + def _canonicalize_cs_raw_state(state): state["S"] = [penalty_spectrum(S) for S in state["S"]] return state @@ -378,6 +389,8 @@ def canonicalize_raw_representation_state(state: dict[str, Any]) -> dict[str, An return _canonicalize_duchon_raw_state(state) if class_name == "gp.smooth": return _canonicalize_gp_raw_state(state) + if class_name == "mrf.smooth": + return _canonicalize_mrf_raw_state(state) if class_name == "fs.interaction": return _canonicalize_fs_raw_state(state) if class_name == "sz.interaction": diff --git a/tests/mgcv_parity_utils.py b/tests/mgcv_parity_utils.py index 85a4c65f..dafc3353 100644 --- a/tests/mgcv_parity_utils.py +++ b/tests/mgcv_parity_utils.py @@ -1455,6 +1455,13 @@ def _normalize_raw_constructor_knots(knots): if value is None: out[str(key)] = None continue + if isinstance(value, pd.Categorical): + out[str(key)] = { + "__factor__": True, + "values": np.asarray(value, dtype=object).ravel().tolist(), + "levels": np.asarray(value.categories, dtype=object).ravel().tolist(), + } + continue arr = np.asarray(value, dtype=object).ravel() out[str(key)] = arr.tolist() return out @@ -1521,6 +1528,12 @@ def _run_mgcv_raw_constructor( kraw <- fromJSON(args[[4]], simplifyVector = FALSE) kn <- lapply(kraw, function(v) { if (is.null(v)) return(NULL) + if (is.list(v) && isTRUE(v$`__factor__`)) { + return(factor( + unlist(v$values, recursive = TRUE, use.names = FALSE), + levels = unlist(v$levels, recursive = TRUE, use.names = FALSE) + )) + } vals <- unlist(v, recursive = TRUE, use.names = FALSE) if (is.numeric(vals)) return(unname(as.numeric(vals))) if (is.integer(vals)) return(unname(as.integer(vals))) @@ -1638,6 +1651,12 @@ def _run_mgcv_raw_constructor( shift = pack_vector(sm$shift, "numeric"), gp_defn = pack_vector(sm$gp.defn, "numeric") ), + "mrf.smooth" = list( + P = pack_matrix(sm$P), + knots = pack_vector(sm$knots, "character"), + plot_me = isTRUE(sm$plot.me), + noterp = isTRUE(sm$noterp) + ), "random.effect" = list( C = pack_constraint(sm$C), random = isTRUE(sm$random), diff --git a/tests/parity/test_mgcv_mrf_combinations_parity.py b/tests/parity/test_mgcv_mrf_combinations_parity.py new file mode 100644 index 00000000..e63bcb99 --- /dev/null +++ b/tests/parity/test_mgcv_mrf_combinations_parity.py @@ -0,0 +1,330 @@ +"""Integrated parity coverage for Markov-random-field smooths (``bs='mrf'``).""" + +from __future__ import annotations + +import numpy as np +import pandas as pd +import pytest + +from nampy.gam import GAM +from nampy.gam.smooths.categorical.mrf import build_markov_random_field_setup +from tests.mgcv_parity_utils import ( + _assert_basic_mgcv_parity, + _fit_nampy_model, + _fit_nampy_snapshot, + _run_mgcv_snapshot, +) + +_PATH_NB = '{"a":["b"],"b":["a","c"],"c":["b","d"],"d":["c","e"],"e":["d"]}' +_DISCONNECTED_NB = '{"a":["b"],"b":["a"],"c":["d"],"d":["c"],"e":[]}' +_SPD_PENALTY = "[[2,-1,0,0,0],[-1,3,-1,0,0],[0,-1,3,-1,0],[0,0,-1,3,-1],[0,0,0,-1,2]]" + + +def _mrf_data(seed=921, n=180): + rng = np.random.default_rng(seed) + region = np.resize(np.asarray(["a", "b", "c", "d", "e"], dtype=object), n) + group = np.resize(np.asarray(["u", "v"], dtype=object), n) + x = rng.uniform(-1.5, 1.5, size=n) + z = 0.8 + rng.uniform(-0.3, 0.5, size=n) + effect = {"a": -0.6, "b": -0.2, "c": 0.15, "d": 0.45, "e": 0.75} + y = ( + np.asarray([effect[value] for value in region]) + + 0.35 * np.sin(1.3 * x) + + 0.2 * (group == "v") + + rng.normal(scale=0.12, size=n) + ) + return pd.DataFrame({"y": y, "region": region, "group": group, "x": x, "z": z}) + + +def _numeric_mrf_data(seed=930, n=150): + data = _mrf_data(seed=seed, n=n) + data["region"] = np.resize(np.arange(1, 6, dtype=np.float64), n) + return data + + +def _assert_snapshot_fit(actual, expected, *, atol=4e-7): + for key in ("response", "link"): + np.testing.assert_allclose( + actual["predictions"][key], + expected["predictions"][key], + atol=atol, + rtol=atol, + ) + np.testing.assert_allclose( + actual["fit"]["edf_total"], + expected["fit"]["edf_total"], + atol=atol, + rtol=atol, + ) + + +def test_mrf_full_rank_reml_matches_mgcv(): + data = _mrf_data(seed=922) + formula = f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}})' + _assert_basic_mgcv_parity( + _fit_nampy_snapshot(data, formula, "gaussian", "REML"), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + pred_atol=5e-7, + pred_rtol=5e-7, + sp_log_atol=8e-6, + criterion_atol=5e-7, + ) + + +def test_mrf_numeric_by_select_adds_null_penalty_and_matches_mgcv(): + data = _mrf_data(seed=923) + formula = f'y ~ s(region, by=z, bs="mrf", k=3, xt={{"nb":{_PATH_NB}}})' + model = _fit_nampy_model(data, formula, "gaussian", "REML", select=True) + assert len(model.smoothing_params) == 2 + _assert_basic_mgcv_parity( + model.parity_snapshot(X=data, include_covariances=True), + _run_mgcv_snapshot(data, formula, "gaussian", "REML", select=True), + pred_atol=7e-7, + pred_rtol=7e-7, + sp_log_atol=5e-5, + criterion_atol=6e-7, + ) + + +def test_mrf_factor_by_fixed_sp_matches_mgcv(): + data = _mrf_data(seed=924) + formula = f'y ~ s(region, by=group, bs="mrf", k=3, xt={{"nb":{_PATH_NB}}}, sp=.7)' + _assert_snapshot_fit( + _fit_nampy_snapshot(data, formula, "gaussian", "fixed"), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + atol=8e-7, + ) + + +def test_mrf_factor_by_linked_id_shares_one_sp_and_matches_mgcv(): + data = _mrf_data(seed=931) + formula = ( + f'y ~ s(region, by=group, bs="mrf", k=3, ' + f'xt={{"nb":{_PATH_NB}}}, id="shared_mrf", sp=.7)' + ) + model = _fit_nampy_model(data, formula, "gaussian", "fixed") + assert len(model.smoothing_params) == 1 + _assert_snapshot_fit( + model.parity_snapshot(X=data, include_covariances=True), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + atol=8e-7, + ) + + +def test_mrf_disconnected_select_retains_one_null_penalty_after_centering(): + data = _mrf_data(seed=925) + formula = f'y ~ s(region, bs="mrf", xt={{"nb":{_DISCONNECTED_NB}}})' + model = _fit_nampy_model(data, formula, "gaussian", "REML", select=True) + assert len(model.smoothing_params) == 2 + _assert_basic_mgcv_parity( + model.parity_snapshot(X=data, include_covariances=True), + _run_mgcv_snapshot(data, formula, "gaussian", "REML", select=True), + pred_atol=7e-7, + pred_rtol=7e-7, + sp_log_atol=1e-5, + criterion_atol=6e-7, + ) + + +def test_mrf_positive_definite_penalty_and_fx_match_mgcv(): + data = _mrf_data(seed=926) + formula = f'y ~ s(region, bs="mrf", xt={{"penalty":{_SPD_PENALTY}}}, sp=.7)' + model = _fit_nampy_model(data, formula, "gaussian", "fixed") + penalty = model.gam_result_.compiled_model.compiled_penalties[0] + assert penalty.rank == penalty.matrix.shape[0] + _assert_snapshot_fit( + model.parity_snapshot(X=data, include_covariances=True), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + ) + + fixed_formula = f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}}, fx=True)' + fixed = _fit_nampy_model(data, fixed_formula, "gaussian", "fixed") + assert fixed.gam_result_.compiled_model.compiled_penalties == () + _assert_snapshot_fit( + fixed.parity_snapshot(X=data, include_covariances=True), + _run_mgcv_snapshot(data, fixed_formula, "gaussian", "REML"), + ) + + +@pytest.mark.parametrize( + "special", + ["te", "ti"], +) +def test_mrf_tensor_margin_fixed_sp_matches_mgcv(special): + data = _mrf_data(seed=927, n=150) + formula = ( + f'y ~ {special}(region, x, bs=["mrf","cr"], k=[3,5], ' + f'xt=[{{"nb":{_PATH_NB}}},None], sp=[.6,.8])' + ) + _assert_snapshot_fit( + _fit_nampy_snapshot(data, formula, "gaussian", "fixed"), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + atol=1e-6, + ) + + +@pytest.mark.parametrize("basis", ["fs", "sz"]) +def test_mrf_numeric_factor_smooth_base_fixed_sp_matches_mgcv(basis): + data = _numeric_mrf_data(seed=932) + numeric_nb = '{"1":["2"],"2":["1","3"],"3":["2","4"],"4":["3","5"],"5":["4"]}' + formula = ( + f'y ~ s(group, region, bs="{basis}", k=5, ' + f'xt={{"bs":"mrf","nb":{numeric_nb}}}, sp=[.6,.8])' + ) + _assert_snapshot_fit( + _fit_nampy_snapshot(data, formula, "gaussian", "fixed"), + _run_mgcv_snapshot(data, formula, "gaussian", "REML"), + atol=1e-6, + ) + + +def test_mrf_array_api_and_persistence(tmp_path): + data = _mrf_data(seed=928, n=100) + nb = { + "a": ["b"], + "b": ["a", "c"], + "c": ["b", "d"], + "d": ["c", "e"], + "e": ["d"], + } + X = data[["region"]] + model = GAM( + family="gaussian", + basis="mrf", + k=3, + xt={"nb": nb}, + optimize_smoothing=False, + smoothing_params=[0.7], + ).fit(X=X, y=data["y"].to_numpy(dtype=np.float64)) + expected = model.predict(X.iloc[:8]) + path = tmp_path / "mrf.pkl" + model.save_model(path) + restored = GAM.load_model(path) + np.testing.assert_allclose(restored.predict(X.iloc[:8]), expected) + + +def test_mrf_validation_and_linked_boundaries_are_explicit(): + values = np.resize(np.asarray(["a", "b", "c"], dtype=object), 30) + nb = {"a": ["b"], "b": ["a", "c"], "c": ["b"]} + with pytest.raises(ValueError, match="must be supplied in xt"): + build_markov_random_field_setup(values) + with pytest.raises(ValueError, match="dimension set too high"): + build_markov_random_field_setup(values, k=4, xt={"nb": nb}) + with pytest.raises(ValueError, match="k<=2"): + build_markov_random_field_setup(values, k=1, xt={"nb": nb}) + with pytest.raises(ValueError, match="k<=2"): + build_markov_random_field_setup(values, k=2, xt={"nb": nb}) + with pytest.raises(ValueError, match="auto- penalty construction"): + build_markov_random_field_setup( + values, + xt={"nb": {"a": ["b"], "b": [], "c": []}}, + ) + with pytest.raises(TypeError, match="uniform representation"): + build_markov_random_field_setup( + values, + xt={"nb": {"a": [2], "b": ["a", "c"], "c": [2]}}, + ) + duplicate_named = build_markov_random_field_setup( + values, + xt={ + "nb": { + "a": ["b", "b", "unknown"], + "b": ["a", "c"], + "c": ["b"], + } + }, + ) + assert duplicate_named.raw_penalty[0, 0] == 1.0 + with pytest.raises(ValueError, match="not contained in the knot specification"): + build_markov_random_field_setup( + np.asarray(["a", "stale"], dtype=object), + xt={"nb": nb}, + factor_levels=["a", "b", "c"], + ) + + data = _mrf_data(seed=929, n=80) + with pytest.raises(NotImplementedError, match="different feature sets"): + GAM( + formula=( + f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}}, id="g")' + f' + s(group, bs="mrf", xt={{"nb":{{"u":["v"],"v":["u"]}}}}, id="g")' + ), + optimize_smoothing=False, + ).fit(data=data) + + formula = f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}}, sp=.7)' + model = _fit_nampy_model(data, formula, "gaussian", "fixed") + bad = data.iloc[:3].copy() + bad.loc[bad.index[0], "region"] = "unknown" + with pytest.raises(ValueError, match="unseen levels|unknown regions"): + model.predict(bad) + + with pytest.raises(ValueError, match="incorrect number of smoothing parameters"): + GAM( + formula=( + f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}}, fx=True, sp=.7)' + ), + optimize_smoothing=False, + ).fit(data=data) + with pytest.raises(NotImplementedError, match="pc"): + GAM( + formula=(f'y ~ s(region, bs="mrf", xt={{"nb":{_PATH_NB}}}, pc="a")'), + optimize_smoothing=False, + ).fit(data=data) + + +def test_mrf_boolean_regions_and_array_xt_namespace_are_safe(): + boolean_data = pd.DataFrame( + { + "y": np.linspace(-0.2, 0.4, 20), + "region": np.resize(np.asarray([False, True]), 20), + } + ) + boolean_model = GAM( + formula=( + 'y ~ s(region, bs="mrf", ' + 'xt={"nb":{"FALSE":["TRUE"],"TRUE":["FALSE"]}}, sp=.7)' + ), + optimize_smoothing=False, + ).fit(data=boolean_data) + prediction = boolean_model.predict(boolean_data.iloc[:4]) + assert np.isfinite(prediction).all() + assert ( + np.linalg.norm( + boolean_model.gam_result_.compiled_model.compiled_terms[0].basis_train + ) + > 0.0 + ) + + array_data = pd.DataFrame( + {"nb": np.resize(np.asarray(["a", "b", "c"], dtype=object), 30)} + ) + array_model = GAM( + family="gaussian", + basis="mrf", + k=3, + xt={"nb": {"a": ["b"], "b": ["a", "c"], "c": ["b"]}}, + optimize_smoothing=False, + smoothing_params=[0.7], + ).fit(X=array_data, y=np.linspace(-0.4, 0.5, len(array_data))) + assert np.isfinite(array_model.predict(array_data.iloc[:5])).all() + + +@pytest.mark.parametrize("special", ["te", "ti"]) +def test_mrf_tensor_knots_promote_unobserved_region_for_prediction(special): + data = _mrf_data(seed=934, n=80) + data = data.loc[data["region"] != "e"].reset_index(drop=True) + knots = pd.Categorical( + ["a", "b", "c", "d", "e"], + categories=["a", "b", "c", "d", "e"], + ) + formula = ( + f'y ~ {special}(region, x, bs=["mrf","cr"], k=[3,5], ' + f'xt=[{{"nb":{_PATH_NB}}},None], sp=[.6,.8])' + ) + model = GAM(formula=formula, optimize_smoothing=False).fit( + data=data, knots={"region": knots} + ) + newdata = data.iloc[:2].copy() + newdata.loc[newdata.index[0], "region"] = "e" + assert np.isfinite(model.predict(newdata)).all() diff --git a/tests/reference_fixtures/mgcv/24692abbf588cc2353db4619413347ed10a0b664d82ecfc26f5dd2befcfb7f8f.json.gz b/tests/reference_fixtures/mgcv/24692abbf588cc2353db4619413347ed10a0b664d82ecfc26f5dd2befcfb7f8f.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..2fe1f7aa8e2add3e6beee5e2fa7e4351bfd53c8e GIT binary patch literal 7550 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 b@@K*Sg%3B{Co7Dhk6=)Ni8Tfi1?7JL685`p literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/26b50d46b9c090f7fe718a8a41857b727d0344c9027823c795d20976a4c137c7.json.gz b/tests/reference_fixtures/mgcv/26b50d46b9c090f7fe718a8a41857b727d0344c9027823c795d20976a4c137c7.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..74806981dac5878db31e1edb5b092fadb49556f2 GIT binary patch literal 22573 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 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/271fd33286df58a5a765b5050dcb4c66f765717beef59e9003e17ad59329c7e7.json.gz b/tests/reference_fixtures/mgcv/271fd33286df58a5a765b5050dcb4c66f765717beef59e9003e17ad59329c7e7.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3f3b76c4a70bea3b98fa896684adea6e3f9f7fc6 GIT binary patch literal 28701 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�a&Jm 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 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/7176ffa5c1e63d9ef773d854b9c76db2610a413ca4a2405d4ad23b30c7991668.json.gz b/tests/reference_fixtures/mgcv/7176ffa5c1e63d9ef773d854b9c76db2610a413ca4a2405d4ad23b30c7991668.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..744eb041bf1af460dc1a8cfa59ea4b58f6f117a2 GIT binary patch literal 205 zcmV;;05bm{iwFP!00002|Lv2(3c@fDME_;ZQL55|`3k{{ASJAAils?fk`xN1|L&%s zco98#EK6X**AeDtHkvz?FS Hp#%T`BfDOE literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/7a7e673de690a99c4c73b203538f061a8049cf8c1009a9b302bfb413ae56b475.json.gz b/tests/reference_fixtures/mgcv/7a7e673de690a99c4c73b203538f061a8049cf8c1009a9b302bfb413ae56b475.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..4313120b3ae7ec59362606be3b1fc9a8a8383d24 GIT binary patch literal 733 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 !+xK4ZfK&;e;>_fLgQB z5DTGeD3`dTD_g&M4<(nv#xKN(BP+H&~8`1xGPtq>&4mD z*z(%#>*n3lAKW^&(pNUY+Ib3jxZe2p_n3;(Dj0<>I3_`g;l|caqR>5-95Vpx+RM^v z$+FVwzaL6nXWzFCli=y=b%k5{{9AWioD=`ZTZCg#Kll4D3v3iaOr4j+pEmBAp1WEb z-Kx6}-^@GB5PpwRwPcGLHSh6sUR)frP9e@T zjZUhH?sYZ)*+%|8^8C`WtkEv_B_~L)`);=OHrUaMq|R7Of(LXO>fGAvD-ks4+g?$y!mdxQ{pbubv<_7-@`-iJ za)Ng}Np6{ZSz#n6T_QT%-Y_EW61e%d`xtZ^aC>@h{&LZ|5gZWeeVSyt?cXYX(bw^N zlKxwkZ&Bmd74yyQm`<~omFu9tbxj{)ojBFRUnfBUH;bWEAun8Sp3gwE zp*Ux5dN`MQq8?1_L(^2>WGp>g_Ea%`kH)y)6@9udsO@TVBgaj$ptA<_P9}MG>`i9{xpwk}$Pe<6_)_LYj+r_o9Iyda8e(H_hlcD@Agfenyw< zvqneGso)*jLCIh99;DGrIHFCs$%5_$hdyENOc=xaT(Aw)X&G_T4YerdZa(HJk+cEb7YsILVhlh5Q154M7(fih7#+SzJ&PfGDykPA?D@`9wiQ~QHeTmVFXT{(f z2WnUVP}(0TP0FBXo*Rljv45DA_E>$L1r}3ylhFzllH{WCw;VXQ`2Wf8W1G!Q`$vzH zKG@TbIscc{|LOeS;mZE@MdwtbQ{%(#9(E%|=TraavSixR@3mHHlcW0I9-%4UD{6Nq zRZ~1nqW>MsLmQ4YBd#V@SxihS@8&lYkS=oh5BbG$Qh z*nhurCDtuJC*pTxwu8Td*~kP>-&4LW_tO$%DI`!&_;2`zul0Nfy?iC+Iazdwe0}%s Fe*hn8?VB;;p%g)4*!}iy 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 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/9040616bb3018ab240edecbe723efe7a319fb56aac54554ef381189e3dc4c54b.json.gz b/tests/reference_fixtures/mgcv/9040616bb3018ab240edecbe723efe7a319fb56aac54554ef381189e3dc4c54b.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..56334c845c9f1687afb2049f7625bc525a84e38a GIT binary patch literal 190 zcmV;v073sBiwFP!00002|Lu}Z3WG2Zh41p#X)(HLPEqKlP)e9;O2zz05;)twlX;JMv-uX>pdJgf9(vzt4FyLz_A>-J%EYD=Cn>7()ca39+3p>?jcA@LcXy~i zDb;p<^-vCFt2;}Vs=y@BT1rWKa;AV3-{<5)$RCEu%sEma?F!{5YXt@$a^hJK+b_N? sIFCgWN2nYpBQV;KbqOmi=a)&HMrw`T7E6Ihet@diFC_de_z(jC0Pf^cKmY&$ literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/972cff6f163bd961e0dd7a5888f6807084c37b7505f7256addf4e26f3d9984f4.json.gz b/tests/reference_fixtures/mgcv/972cff6f163bd961e0dd7a5888f6807084c37b7505f7256addf4e26f3d9984f4.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..826f15e8b6e296036d9a0d0e90d6956e81ff82e1 GIT binary patch literal 7493 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 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/9cc8059d8c86e6989a24bf7cd0a02ea5287aa02f7fc49c6adc65b4617278d40c.json.gz b/tests/reference_fixtures/mgcv/9cc8059d8c86e6989a24bf7cd0a02ea5287aa02f7fc49c6adc65b4617278d40c.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1c16ea4ae3520e2fd25b7a57a10453d63ad37ecc GIT binary patch literal 13685 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 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/a8eedc37e4c14e7bcb1650c7bcb35602beb51f47043321e82e821a268b7a0ac1.json.gz b/tests/reference_fixtures/mgcv/a8eedc37e4c14e7bcb1650c7bcb35602beb51f47043321e82e821a268b7a0ac1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..9c2f9518dd4dd34a07fc23bb417396d838173157 GIT binary patch literal 309 zcmV-50m}X#iwFP!00002|Ls=GYQr!P{Fgne!4A&F*M34T1!Kh8mQxWuf+UBK;D7Hb zxRAOfX-iTl6}m{X+Q-;1nj<_xSsj7%jWiwSP=dzbx+?JgY}`k^HZY#_s;9=j7@94CR&Ie{JT^_dmOP+iK_W#3S)YJQ9z@Bk}mh zJkA`z61wH@%u#hGQhi#D7kPQ&~;4pfG$CDjeM$6EA!!vwspE6QL%J^d$ z@3dqUrKCYGqBb<1pCYT7x@gxgytX#Hz?7RaFoROuZ?P5p4RUBUP!;=2s@R(^qgVi^dO7(T_%8uzl1iv?d!2FG>+Q3{)DuULXZLxfbfWd;wj21o)){ F008MDSI__e literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/c5299d99e7fa518f78ec67d4e915ee9f6deda98bcfc5385ed2d8c472e4424c61.json.gz b/tests/reference_fixtures/mgcv/c5299d99e7fa518f78ec67d4e915ee9f6deda98bcfc5385ed2d8c472e4424c61.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..042c071f1819449bc0c5e268fb6140e9808404ea GIT binary patch literal 403 zcmb2|=3oE;rvGnmcxN9r;9+?1f6->?oYQ@4|8yPHYYyzWzHE(2|D+`W8h_t!zHRuT z`66$bghqdJuII-6cS;)4SH$rP97?;Z>)gO~%Yyf{El0#L&1FY7DR{~)-LiDsB`y6Y zFTtoyU~Z~emU=|6q1`zK2|`Ao@w8Jlv6hhNh~_u65xRhc)gGHvs< rZ~fC9wo&r+lsj){srB66vEuN`6+#9_@|dUE)iG=ep6e?i&cFZwZavS1 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/e09bf05038a2383dfeafe6b62e75027d7ed732cc3b582ca99fef3e71d8676e0d.json.gz b/tests/reference_fixtures/mgcv/e09bf05038a2383dfeafe6b62e75027d7ed732cc3b582ca99fef3e71d8676e0d.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1dc4ed7625806408370c35c6ec910bd993f30a64 GIT binary patch literal 405 zcmb2|=3oE;rvGneoXwIkM!~FznlF!Z(hv(_vM#k@9#b*`^xIQ+}!f=(zR7H&6QTTDLQ+G zTsT~Lv8_`zMe(a@$ER2A(No{?tx7q+{e0E0cf0J{=|q*7a+RrYcte z*P?|A8@0}73Oo0(2yqu&S;G@#up(%4cIXdt$#XpNJ1#xn;bmyRY>X9c_|N<%cbeLT zw#<9E$9L)c4dHi|UUK&P?571b>+I*2AAW3g-7ItWzwF0mf5dfng~WZ~`Jf%!ZMb|2 zGwU^-m8H$j2O2uOd=|3onUYkwy5>*lkG~}`;8jN|)!vh?*I vgNkyg{_Oi!CGp2o13rE+==)o=>h?l=cGvgMtlq}DpVvQl`|7kNQ3eJ8x(vdR literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/e90433678134533e0bd33782cf7a1953a8b62b5a8ec1ff4466c4e0b9aadc2587.json.gz b/tests/reference_fixtures/mgcv/e90433678134533e0bd33782cf7a1953a8b62b5a8ec1ff4466c4e0b9aadc2587.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..37be9cb84ff127549e70b0f98a2194fc4948941f GIT binary patch literal 204 zcmV;-05ks|iwFP!00002|Lv2(3WG2ZME_;ZX|Wa$%~uq9DHI7?jZ{pcBoPGh-@6go zWAV_VEP-7nGrN~J;|BSdp;XtpS}90aioTs7P*Wrpb8$F{Y%Olio2R_wh}LpKA&PS0 z=??jG)5n}@TfaMY$L{!}#}`AZr|2mWeT8(RF#?_I zV_+|cP+_1-tM!6Rz?17Bi{%YnT1RK4 G1ONcP(qGR2 literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/f5304b44e26a8362e75f387a53d64e5a8a2cace168c1eaf35c7c0744b1e7d352.json.gz b/tests/reference_fixtures/mgcv/f5304b44e26a8362e75f387a53d64e5a8a2cace168c1eaf35c7c0744b1e7d352.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..904d23f41a133bceaed7bc33e2057fa214c40d86 GIT binary patch literal 11704 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 VH3(1Fb0S|Nfg4|8HNub({~yg2zO?`V literal 0 HcmV?d00001 diff --git a/tests/reference_fixtures/mgcv/fa6672ade967e6993708380f389dc9f98724cc28c79f32fa88539e7245ab08e8.json.gz b/tests/reference_fixtures/mgcv/fa6672ade967e6993708380f389dc9f98724cc28c79f32fa88539e7245ab08e8.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..69853fcae6005ef51a59075fcfb5e1a09a60cf76 GIT binary patch literal 45473 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