Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
c3c9aea
Add Antweiler-Freyberger (2025) iterative quadrature estimator.
hmgaudecker Apr 15, 2026
193b785
Wire transition function into AF likelihood with shock integration.
hmgaudecker Apr 15, 2026
9397fa6
Add long_running test comparing AF and CHS on MODEL2 data.
hmgaudecker Apr 15, 2026
7967277
Use same uninformed start values for AF and CHS in comparison tests.
hmgaudecker Apr 15, 2026
b03ad70
Add transition constraints, LogSumExp, fix MODEL2 convergence.
hmgaudecker Apr 15, 2026
cb93617
Implement AF investment equation for endogenous factors.
hmgaudecker Apr 15, 2026
04e2d54
Wire start_params argument through AF estimation pipeline.
hmgaudecker Apr 15, 2026
766ad09
Add posterior state extraction for AF via get_filtered_states().
hmgaudecker Apr 15, 2026
765d1c6
Fix 5 issues from code review.
hmgaudecker Apr 15, 2026
852f9ab
Add income-conditional initial draws and translog smoke test.
hmgaudecker Apr 22, 2026
e5b9176
Add fixed_params to AF estimation for time-invariant latent factors.
hmgaudecker Apr 22, 2026
a72e9cc
Use FixedConstraint instead of bounds clamping in AF; add fixed_param…
hmgaudecker Apr 22, 2026
43ce258
Pin optimagic at the probability-allow-fixed-entries branch.
hmgaudecker Apr 22, 2026
907462a
Add MATLAB AF CES + translog reproduction scaffold.
hmgaudecker Apr 22, 2026
8068b7e
Batch the AF likelihood across observations so large grids fit a GPU.
hmgaudecker Apr 22, 2026
2b5f539
Use a joint Halton draw in the AF transition likelihood.
hmgaudecker Apr 22, 2026
cadb799
Harmonise AF CNLSY reproduction: treat investment as a regular latent.
hmgaudecker Apr 23, 2026
c58e495
Add CES reparameterisation helper with level-shift accounting.
hmgaudecker Apr 23, 2026
cbaf02c
Compare skillmodels loglike vs MATLAB's initial-period params.
hmgaudecker Apr 23, 2026
2e5d85d
Match MATLAB normalisation: skills period 0 only, investment never.
hmgaudecker Apr 23, 2026
74c117c
Extend MATLAB loglike comparison to all three periods.
hmgaudecker Apr 23, 2026
564bdc3
Add has_production_shock and has_initial_distribution to FactorSpec.
hmgaudecker Apr 23, 2026
faed06f
Use new FactorSpec flags in MATLAB CES repro and fix translation bugs.
hmgaudecker Apr 23, 2026
6fd7502
Add block-diagonal sandwich standard errors for AF (Phase 1).
hmgaudecker Apr 24, 2026
ab87767
Add full cross-period Newey-McFadden sandwich for AF SEs (Phase 2).
hmgaudecker Apr 24, 2026
8927953
Address code-review feedback on AF SE implementation.
hmgaudecker Apr 24, 2026
d6cd2cb
Hold-last-value imputation for log_income at period 2 in CNLSY loader.
hmgaudecker Apr 24, 2026
b85132f
Vendor CNLSY data file in test directory.
hmgaudecker Apr 27, 2026
4f45d01
Use 20k Halton draws in AF-vs-CHS CNLSY comparison.
hmgaudecker Apr 27, 2026
61607c2
Match MATLAB normalisations exactly in MATLAB-CES/translog comparison.
hmgaudecker Apr 29, 2026
4b34034
Use pinv for AF inference's information-matrix inversion.
hmgaudecker Apr 30, 2026
aea7b86
Bound log_ces phi from above to prevent gradient overflow.
hmgaudecker May 4, 2026
a1a232f
Fix full-sandwich SE: pinv must not assume Hermitian on asymmetric A.
hmgaudecker May 4, 2026
5e8e785
Fix two stale assertions in matlab_ces_repro tests.
hmgaudecker May 4, 2026
83682bd
Add score-resampling cluster bootstrap to AF inference.
hmgaudecker May 4, 2026
281ff84
Add log_ces_with_constant production function.
hmgaudecker May 4, 2026
f558f27
Add Snellius SLURM script for translog AF sim sweep.
hmgaudecker May 6, 2026
8ec7b6d
Snellius script: also run CHS for translog comparison.
hmgaudecker May 6, 2026
7325837
AF: importance-sample carry-over (MATLAB likelihood_12 style).
hmgaudecker May 6, 2026
619286a
Snellius: add conda-flavoured translog sweep script.
hmgaudecker May 6, 2026
dfc2dbf
Use $CONDA_PREFIX/bin/python explicitly in conda slurm script
hmgaudecker May 6, 2026
3b279ee
Snellius slurm: skip 'conda activate' (no conda.sh on H100 nodes); se…
hmgaudecker May 6, 2026
a8e6061
Snellius slurm: trim resources to actually-used (16 CPUs, 96G mem, 8h)
hmgaudecker May 6, 2026
809551c
Skip identity constraint at the final aug-period of each meas-type.
hmgaudecker May 7, 2026
8ffa34c
Snellius slurm: trim wall time to 5h.
hmgaudecker May 7, 2026
ed52604
AF transition: drop prev-period inv-meas from chained-sample weight.
hmgaudecker May 7, 2026
e7d0aac
AF transition: rebuild chained sample from joint Halton (fixes sigma_…
hmgaudecker May 7, 2026
f9d4652
Snellius slurm: drop CHS workers, trim wall time to 3.5h.
hmgaudecker May 8, 2026
4da39c1
Add Marvin (Bonn HPC) slurm script for translog AF sweep.
hmgaudecker May 8, 2026
494c9ac
AF: add moment-based initialization strategy (Phase A, opt-in).
hmgaudecker May 9, 2026
ce5371e
AF: add two-stage measurement system option (Phase B, opt-in).
hmgaudecker May 9, 2026
d4711cb
AF: consolidate inference around the score bootstrap (AF 2025 §4.2).
hmgaudecker May 9, 2026
cebb9cb
AF: flip initialization_strategy default to moment_based; require exp…
hmgaudecker May 9, 2026
960140a
Add Marvin slurm script for translog 3-way comparison at n_halton=2000.
hmgaudecker May 9, 2026
a088278
Marvin slurm: write 3-way comparison results to fresh estimates_3way_…
hmgaudecker May 9, 2026
4d006bf
Add moment-based start values for CHS, hybrid Spearman+OLS, cuda13 env
hmgaudecker May 10, 2026
b58fb12
AF: propagate cross-period equality constraints through the chain
hmgaudecker May 10, 2026
7c0539a
AF validate: relax measurement count to >=2, warn below 3
hmgaudecker May 11, 2026
5844104
Add `pool_equality_groups` helper for moment-init starts
hmgaudecker May 11, 2026
db66382
tests: bump joint-Halton sigma_prod_1 tolerance 30% -> 35%
hmgaudecker May 11, 2026
081fbe6
Add AMN (Attanasio-Meghir-Nix 2020) standalone estimator
hmgaudecker May 11, 2026
a614c3d
Remove scripts/ and docs/superpowers/ from skillmodels
hmgaudecker May 11, 2026
7aefedf
Move CHS Kalman-filter modules into a `skillmodels.chs` subpackage
hmgaudecker May 11, 2026
22a12b5
AF likelihood: mask NaN measurements so missing data doesn't poison g…
hmgaudecker May 11, 2026
20d2688
Move tests/matlab_ces_repro into the parent workspace
hmgaudecker May 11, 2026
506828b
Add portable environment files for the af-estimator branch
hmgaudecker May 11, 2026
2871ecf
AF transition: bridge `@register_params` user functions to AF's calli…
hmgaudecker May 11, 2026
20a63b6
AF result: drop per-period importance samples before returning
hmgaudecker May 11, 2026
7ef2694
Remove Spearman as a final-estimator option
hmgaudecker May 11, 2026
fb3f15e
AF result: materialise every JAX array to numpy before returning
hmgaudecker May 11, 2026
ed6f40c
AF result: free JIT/XLA caches before materialising arrays to numpy
hmgaudecker May 11, 2026
5cebcb2
Reorganise package into common / chs / af / amn subpackages
hmgaudecker May 11, 2026
00b4171
AF result: free samples_per_component before materialising the rest
hmgaudecker May 11, 2026
e509451
Add the full Attanasio-Meghir-Nix (2020) estimator
hmgaudecker May 11, 2026
052e5e4
Make AMN the default start-value strategy for CHS and AF
hmgaudecker May 11, 2026
d0b8d2a
Add scikit-learn dependency for AMN mixture EM
hmgaudecker May 11, 2026
f0df40b
Add scikit-learn to deployment env files
hmgaudecker May 11, 2026
4a517c6
Fall back to Spearman when AMN cannot handle the model
hmgaudecker May 11, 2026
852b095
AMN Stage 3: support every transition function via generic jax.vmap NLS
hmgaudecker May 11, 2026
f612949
AF: honour within-step equality constraints
hmgaudecker May 12, 2026
15ef656
AF: cap samples_per_component halton count for posterior summary
hmgaudecker May 13, 2026
af42b3f
AF: simplify recent within-step + halton-summary helpers
hmgaudecker May 13, 2026
68a98a7
AF: address code-review concerns on the recent halton/eq commits
hmgaudecker May 13, 2026
1a171f4
common: move anchor_states_df, create_state_ranges, apply_anchored_tr…
hmgaudecker May 13, 2026
ce26207
common: strict signatures requiring pre-computed inputs
hmgaudecker May 13, 2026
cd1b3c2
rename EstimationOptions → CHSEstimationOptions
hmgaudecker May 13, 2026
d260be8
narrow top-level __init__ to model_spec building blocks
hmgaudecker May 13, 2026
64be065
detach CHSEstimationOptions from ModelSpec
hmgaudecker May 13, 2026
040953b
AF: jaxopt L-BFGS-B optimizer backend
hmgaudecker May 13, 2026
1de00a6
docs: cover three-estimator architecture + AF/jaxopt
hmgaudecker May 13, 2026
5f274a4
AF: default optimizer_backend to "auto" (picks jaxopt on GPU)
hmgaudecker May 14, 2026
f2810af
Fix four upstream bugs surfaced by the three-estimator tutorial
hmgaudecker May 14, 2026
074cc83
tutorial.ipynb: rebuild three-estimator walkthrough end-to-end
hmgaudecker May 14, 2026
627f6b5
Review-followup: consolidate selector helpers, fix latent bugs, jaxop…
hmgaudecker May 14, 2026
08dc07d
docs: add AMN and compare-estimators how-tos; touch up architecture /…
hmgaudecker May 14, 2026
2206212
Enable JAX x64 at package import to unblock jaxopt LBFGSB on JAX >= 0.10
hmgaudecker May 14, 2026
d7d29ea
Beartype perimeter on user-facing API
hmgaudecker May 14, 2026
18f5de1
Fix jaxopt LBFGSB s32/s64 verifier crash by disabling XLA pass
hmgaudecker May 14, 2026
97b7047
Activate beartype.claw on the whole skillmodels package in tests
hmgaudecker May 14, 2026
a003970
Fix CI test failures in test_af_inference under whole-package claw
hmgaudecker May 14, 2026
58344a5
jaxopt backend: match scipy_lbfgsb's gtol OR ftol stopping rule
hmgaudecker May 15, 2026
2d5525a
af/optimagic: unwrap MappingProxyType in optimizer_options before splat
hmgaudecker May 15, 2026
d5b72d6
af: defer device→host materialisation to AFEstimationResult.to_numpy()
hmgaudecker May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ repos:
rev: 0.9.1
hooks:
- id: nbstripout
# The getting-started tutorial ships pre-rendered (`execute: false` in
# myst.yml); stripping its outputs would leave the docs site empty.
exclude: ^docs/getting_started/tutorial\.ipynb$
args:
- --extra-keys
- metadata.kernelspec metadata.language_info.version metadata.vscode
Expand Down
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ When writing new public-facing code, always accept and return `period`. Convert

## Testing

- pytest with markers: `wip`, `unit`, `integration`, `end_to_end`
- pytest with markers: `wip`, `unit`, `integration`, `end_to_end`, `long_running`
- Test files mirror source structure in `tests/`
- Memory profiling available via pytest-memray (Unix only)
- MATLAB AF CES / translog reproduction tests live in the parent workspace at
`../matlab_ces_repro/` (alongside `sim_repro/`), not in this library. They depend on
reference data at `/home/hmg/sciebo/Skill estimation/` and the CNLSY xls bundled
beside them. Run from the workspace root.
100 changes: 100 additions & 0 deletions docs/explanations/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Package Architecture

Skillmodels hosts three estimators under one model specification. The package
layout reflects that:

```
src/skillmodels/
├── common/ Estimator-agnostic machinery
│ ├── model_spec.py ModelSpec, FactorSpec, AnchoringSpec, Normalizations
│ ├── types.py ProcessedModel, Dimensions, Labels,
│ │ EndogenousFactorsInfo, ParsingInfo, ...
│ ├── process_model.py process_model(spec) -> ProcessedModel
│ ├── process_data.py long-format data -> internal arrays
│ ├── params_index.py 4-level MultiIndex used by all estimators
│ ├── parse_params.py flat vector <-> structured params
│ ├── constraints.py get_constraints, FixedConstraintWithValue,
│ │ collect_fixed_locs, project_to_probability_constraints
│ ├── selector.py select_by_loc, align_index_names
│ ├── transition_functions.py linear / translog / log_ces / ...
│ ├── transitions.py apply_anchored_transition (sigma-points-agnostic)
│ ├── anchoring.py anchor / unanchor states
│ ├── state_ranges.py create_state_ranges
│ ├── simulate_data.py simulate_dataset, simulate_policy_effect
│ ├── variance_decomposition.py signal/noise decomposition
│ └── diagnostic_plots.py plot_residual_boxplots, plot_likelihood_contributions
├── chs/ Cunha-Heckman-Schennach Kalman MLE
│ ├── options.py CHSEstimationOptions
│ ├── kalman_filters.py square-root unscented Kalman filter
│ ├── likelihood.py jitted log-likelihood
│ ├── likelihood_debug.py non-jitted variant with debug arrays
│ ├── maximization_inputs.py get_maximization_inputs(...)
│ ├── filtered_states.py get_filtered_states(...)
│ └── process_debug_data.py post-process Kalman debug arrays
├── af/ Antweiler-Freyberger sequential Halton MLE
│ ├── types.py AFEstimationOptions, AFEstimationResult, ...
│ ├── estimate.py estimate_af(...) -- top-level orchestration
│ ├── initial_period.py period-0 mixture + measurement system MLE
│ ├── transition_period.py period-t transition + measurement-system MLE
│ ├── likelihood.py jitted period-specific log-likelihoods
│ ├── halton.py quadrature nodes / weights
│ ├── batching.py obs-batching for the autodiff chunking
│ ├── posterior_states.py conditional-distribution materialisation
│ ├── inference.py compute_af_standard_errors (cluster bootstrap)
│ └── jaxopt_backend.py on-device L-BFGS-B alternative
└── amn/ Attanasio-Meghir-Nix 2020 (three-stage)
├── types.py AMNEstimationOptions, ...
├── estimate.py estimate_amn(...) -- top-level orchestration
├── mixture_em.py Stage 1: EM on the augmented mixture
├── minimum_distance.py Stage 2: structural recovery
├── simulate_and_regress.py Stage 3: synthetic-panel regression
├── moments.py Spearman + Bartlett start-values
├── start_values.py get_spearman_start_params, pool_equality_groups
├── posterior_states.py simulate factor paths from fitted mixture
└── inference.py compute_amn_standard_errors (cluster bootstrap)
```

## How the layers interact

Every estimator reads the same `ModelSpec` and produces the same canonical
params DataFrame (4-level MultiIndex
`(category, period, name1, name2)`). The differences live entirely below the
spec:

- **CHS** consumes `process_model(spec) -> ProcessedModel`, then plugs that
into the Kalman recursion. `CHSEstimationOptions` is passed at call time
to `get_maximization_inputs(spec, data, chs_options=...)`.
- **AF** also calls `process_model`, but uses `ProcessedModel` only for the
parameter index, labels, and transition info. The Kalman filter is not
invoked; period-specific Halton designs replace the predict step.
- **AMN** likewise calls `process_model` for the index/labels, then runs its
three-stage pipeline. The result re-uses the same params DataFrame format
so the AMN output can seed CHS or AF estimation when desired.

`process_model` itself is structural: it takes only the spec and produces
shapes, labels, transition info, and an `EndogenousFactorsInfo`. It does not
carry any estimator-specific tuning. Each estimator's options class
(`CHSEstimationOptions`, `AFEstimationOptions`, `AMNEstimationOptions`) is
passed in at call time.

## Why this split

The package grew organically: CHS was the original codebase; AF and AMN were
later additions. Earlier iterations stored CHS-only options on `ModelSpec`,
which made the spec leak CHS assumptions into a notionally agnostic container.
The split into `common/`, `chs/`, `af/`, `amn/` makes the scope of each piece
explicit at the import site:

- `from skillmodels import ModelSpec` — pure structural description.
- `from skillmodels.chs import CHSEstimationOptions, get_maximization_inputs`
— CHS-specific.
- `from skillmodels.af import estimate_af, AFEstimationOptions` — AF-specific.
- `from skillmodels.common.variance_decomposition import decompose_measurement_variance`
— works for any estimator, given pre-computed filtered states.

The architectural principle: a function lives in `common/` iff it does not
import from `chs/`, `af/`, or `amn/`. Anything that does belongs in the
relevant subpackage. There is one practical exception:
`CHSEstimationOptions` is defined in `chs/options.py` but the
`process_model` orchestration in `common/` doesn't read it (it reads the
structural `ModelSpec.n_mixtures` field instead), so the layering is clean.
39 changes: 37 additions & 2 deletions docs/explanations/names_and_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,47 @@ of factors are arbitrary).

## Estimation Options

The `EstimationOptions` dataclass controls numerical aspects:
Each estimator has its own options dataclass, passed at call time rather than
embedded in `ModelSpec`. The three classes share no fields — what counts as a
tuning knob differs between estimators.

`CHSEstimationOptions` (from `skillmodels.chs`) controls the Kalman MLE:

- **robust_bounds**: Tightens parameter bounds to avoid numerical issues
- **bounds_distance**: How much stricter to make bounds (zeroed if robust_bounds is
false)
- **n_mixtures**: Number of mixture components in the distribution
- **sigma_points_scale**: Controls spread of sigma points in unscented Kalman filter
- **clipping_\***: Parameters for soft-clipping the log-likelihood to prevent
infinities
- **start_params_strategy**: How to seed the `params_template`. `"amn"` (default)
runs the full AMN three-stage estimator and uses its parameters as the start;
`"spearman"` uses moment-based start values; `"none"` leaves entries as NaN
for the caller to fill in.

`AFEstimationOptions` (from `skillmodels.af`) controls the sequential MLE:

- **n_halton_points**, **n_halton_points_shock**: quadrature counts.
- **n_mixture_components**: number of components in the latent-factor mixture.
- **optimizer_backend**: `"auto"` (default), `"optimagic"`, or `"jaxopt"`. Auto
picks `"jaxopt"` if a JAX GPU is visible and the model has no probability or
equality constraints; otherwise `"optimagic"`.
- **optimizer_algorithm**: the optimagic algorithm name used when the backend
is `"optimagic"`. Ignored under `"jaxopt"`.
- **initialization_strategy**: `"amn"`, `"spearman"`, or `"constant"`. Same
meaning as in CHS.

`AMNEstimationOptions` (from `skillmodels.amn`) controls the three-stage
pipeline:

- **n_mixture_components**: Stage-1 EM components.
- **em_max_iter**, **em_tol**, **em_n_init**, **em_reg_covar**: Stage-1 EM
numerical knobs.
- **n_simulation_draws**: Stage-3 synthetic-panel size.
- **minimum_distance_weighting**: Stage-2 weighting; `"identity"` (default) or
`"optimal"`.
- **investment_endogeneity**: include the control-function residual in Stage 3
for endogenous-investment models.

The shared structural field — number of mixture components in the latent
distribution — lives directly on `ModelSpec.n_mixtures`, since it changes the
model itself rather than the optimizer.
Loading
Loading