Pandas UI for initial conditions and Markov transition probabilities.#272
Pandas UI for initial conditions and Markov transition probabilities.#272hmgaudecker merged 96 commits intomainfrom
Conversation
… Pan (RED 2019). Add mixtures of normals.
The compute_gridpoints / compute_transition_probs methods and _mixture_cdf accept scalar distribution params, not arrays. Narrow annotations to float, make _mixture_cdf params keyword-only, and add assert isinstance(val, float) in interfaces.py to narrow the type from all_params. Fix FGP page references and use DECIMAL_PRECISION in test assertions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove 4 ty:ignore comments in ages.py via assert narrowing - Fix greedy next_ prefix removal in simulation/utils.py - Set tmp=None after atomic replace in result.py - Fix error message formatting in model.py - Replace stale bug-claiming docstrings with regression guard refs (#230) - Convert rST to MyST in grids.py docstring - Remove stale docstring param in simulation/utils.py - Add PD011 to docs per-file-ignores with corrected comment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ith dags.tree utilities Convert Model class Attributes section to PEP 257 inline field docstrings. Replace f-string concatenation and .split() on QNAME_DELIMITER with qname_from_tree_path / tree_path_from_qname across 7 modules. Closes #254. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename Model.params_template to Model._params_template so users interact with the mutable dict returned by get_params_template() instead of an opaque MappingProxyType. Update docs, tests, and CLAUDE.md accordingly. Allow SLF001 in test per-file-ignores for legitimate private access. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`precise_values` was confusing — unclear how it differs from `values`. `exact_values` makes the distinction clear: `values` are JAX floats (lossy), `exact_values` are `int | Fraction` (lossless). Also renames `precise_step_size` → `exact_step_size`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s, focusing-on-the-economics guide - B3: Fix regime transitions text in regimes.ipynb (states and actions, not just state) - B4: Note alternative aggregation functions (e.g. Epstein–Zin) in regimes.ipynb - B5a: Add function-composition explanation to tiny_example.ipynb - B5b: New user guide page "Focusing on the Economics" with DAG explanation - Port regimes.ipynb from removed branch, updated for current API (no RegimeTransition/MarkovRegimeTransition wrappers, exact_values, get_params_template) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Docs: - Rename "Focusing on the Economics" → "Write Economics, Not Glue Code" and move it first in the user guide - Add explanatory cells between code blocks in function-composition notebook - Include borrowing_constraint in the first example; show dict assembly with utility_working → "utility" rename - Use AgeGrid(start=60, stop=85, step="5Y") instead of N_PERIODS - Fix mermaid dark mode with neutral theme - Remove stale #262 per-boundary transition language from regimes notebook - Use t/t+1 notation instead of prime in H function - Normalize params_workflow.ipynb cell sources to list-of-strings format Code: - Rename key → qname, parts/path → tree_path in params_processing.py and model.py for consistency with dags terminology - Simplify AgeGrid overloads: omit unused params instead of None=None - Restructure AgeGrid.__init__ to avoid assert narrowing - Replace assert with cast in interfaces.py - Render params template types as short strings (float not <class 'float'>) - Update MutableParamsTemplate to use str values - Use "compatible with" instead of "matching" for params docstrings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add real content to 6 User Guide pages (defining_models, grids, shocks, parameters, solving_and_simulating) and installation.md. Remove empty dispatchers.ipynb from explanations index. Update user_guide/index.md and getting_started/index.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rework write_economics.ipynb with DAG-first didactical approach: show the math, the dependency graph, traditional code, then pylcm. Trim regimes.ipynb to focus on regime-specific topics (terminal/non-terminal, stochastic transitions, active predicates). Both updated to current API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add annotated consumption-saving example walkthrough, development guide with setup/testing/conventions, and examples index. Add cross-links between User Guide pages and examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create explanations/dispatchers.ipynb explaining productmap, vmap_1d, and simulation_spacemap with worked examples and plotly visualizations. Restyle function_representation.ipynb: plotly plots, simplified model setup, tighter prose. Add dispatchers to TOC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tions
Enable per-target dict transitions to carry independent parameters per target
regime (to_{target}_next_{state} template keys instead of collapsing under a
single base name), and allow state_transitions to reference states that only
exist in target regimes.
Key changes:
- Relax validation to allow extra keys in state_transitions (except None)
- Add second pass in _collect_state_transitions for target-only states
- Use to_ prefix in params template for per-target transitions
- Pass target regime name (not source) to weight function builders in Q_and_F
- Extend markov detection to cover target-only stochastic states
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Raise ModelInitializationError when a single (non-per-target-dict) transition is used for a DiscreteGrid state whose categories differ across regimes. This prevents JAX from silently clipping out-of-bounds indices and producing wrong continuation values. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename "working" → "working_life" and "retired" → "retirement" for regime names, "WorkingStatus" → "LaborSupply" and "labor_supply" → "work" for action names. Update all docs, examples, test models, and test files. Regenerate regression and shock test data pickles with new names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate the two near-identical consumption-savings test models into one configurable module with support for Normal GH, Rouwenhorst, and Tauchen shocks plus configurable wealth grid type and interest rate. Add FGP (RED 2019) reference to docstring. Add Tauchen to economic validation tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scope state grids to the current regime instead of iterating all model regimes, since a regime's transition function can only reference its own states/actions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rminal period. (#277) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Catches silent bugs where probs_array[health, period] doesn't match the function signature order (period, health). Warns when indexing uses computed expressions or multiple branches that can't be validated automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… target regime-specific transitions in transition_probs_from_series and validation.
Code reviewFound 1 issue:
Lines 402 to 414 in 46b6a99 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
1. Shock states excluded via isinstance(grid, _ShockGrid) filter 2. Section separators removed from tests/test_grids.py 3. Stale docstrings updated in result.py and simulate.py 4. Test model _make_regime_markov_model fixed: active=lambda age: age < 62 5. Docstring Dict → dict in pandas_utils.py
…troduced in this PR.
timmens
left a comment
There was a problem hiding this comment.
Very very nice! No showstoppers, only a few comments on the interface.
| from lcm import initial_conditions_from_dataframe | ||
|
|
||
| df = pd.DataFrame({ | ||
| "regime": ["working", "working", "retired"], | ||
| "wealth": [10.0, 50.0, 30.0], | ||
| "health": ["good", "bad", "good"], | ||
| "age": [25.0, 25.0, 25.0], | ||
| }) | ||
|
|
||
| initial_conditions = initial_conditions_from_dataframe(df, model=model) | ||
|
|
||
| result = model.solve_and_simulate( | ||
| params=params, | ||
| initial_conditions=initial_conditions, | ||
| ) |
There was a problem hiding this comment.
We could also allow model.solve_and_simulate to accept a pandas.DataFrame as the initial conditions. And if it is a dataframe we call initial_conditions_from_dataframe?
The user would get a very similar error, and it would probably feel even more natural?
Alternatively, this could be made a model method.
There was a problem hiding this comment.
Shoot, a comment of mine here did not make it through yesterday, it seems. Sorry about that.
We went down that rout of allowing all kinds of different inputs in ttsim and it is a pain. While I agree that would be nice, I really want to keep the interface so that it accepts exactly one thing here, code maintenance is hard enough. We can expect users to be well-versed enough to run the pre-processing steps themselves.
| ) -> Array: ... | ||
|
|
||
|
|
||
| def transition_probs_from_series( |
There was a problem hiding this comment.
Consider making this a method on Model and inferring state_name from the Series' MultiIndex — the "next_" prefix on the outcome level already identifies the state (or "next_regime" for regime transitions). That would reduce the signature to:
model.transition_probs_from_series(series, regime_name="working")if I am not mistaken.
There was a problem hiding this comment.
See above for the model-method part. I am investigating the interface change right now!
Fix #242.
Fix #259.
Summary
Add pandas interoperability utilities and simplify the simulation API so users work
with DataFrames and a single
initial_conditionsdict instead of raw JAX arrays.Public API changes
New module (
lcm.pandas_utils, re-exported fromlcm):initial_conditions_from_dataframe(df, model=)— convert a DataFrame with a"regime"column to theinitial_conditionsdict expected bysimulate()andsolve_and_simulate(). Discrete state labels and the"regime"column aremapped to integer codes.
transition_probs_from_series(series, model=, regime_name=, ...)—build transition probability arrays from a pandas Series with a named MultiIndex.
Works for both state and regime transitions (inferred from
next_<x>; automaticallyreorders index levels to match the function signature.
Uses
"age"(not"period") as the time-dimension level name.validate_transition_probs(probs_array, ...)— validate shape, bounds, and row-sumsof transition probability arrays.
Simulation API simplification:
model.simulate()andmodel.solve_and_simulate()now take a singleinitial_conditions: Mapping[str, Array]instead of separateinitial_states+initial_regimes. The dict includes all state arrays plus"regime_id"(integercodes from
model.regime_names_to_ids).simulate()andvalidate_initial_conditions()updated accordingly.@categoricalnow requiresordered=True/False— breaking change to the decoratorsyntax (
@categorical→@categorical(ordered=False)). The flag flows through topd.CategoricalDtypein simulation output, so ordered categoricals (e.g. educationlevels) render correctly in pandas. Gives us a 1:1 mapping with Pandas categorical
dtypes.
Internal changes
error_handling.py): At model init, inspectstransition function source to verify
probs_array[...]indexing matches the parameterdeclaration order. Warns when the pattern is non-trivial (computed indices, multiple
subscripts).
regime_processing.py):_merge_ordered_categories()validates consistent ordering across regimes and merges category sets via topological
sort.
period now raise at model init.
next_ageparameter; moved regime transitionprobability validation from simulation to model init.
tests/test_models/basic_discrete.py(discrete + continuousstates, no stochastic transitions) and
tests/test_models/regime_markov.py(MarkovTransition on regime transitions).
Documentation
pandas_interop.md)stochastic_transitions.ipynb)initial_conditionsis now the primary APIinitial_conditionsInfrastructure
tynow runs in its own pixi environment.ai-instructionssubmodule