Skip to content

Commit ba732be

Browse files
hmgaudeckerclaude
andcommitted
aca-model updates: sharding config, regimes, ACA overlay, tooling + ty
Squash-merge of feat/pytask-slurm-gpu-flags — GridConfig sharding cleanup (discrete-only, ll×css flags removed), regime/ACA-overlay refinements, plus the tooling bump (CI→pylcm main), ty fixes, and stale-test cleanup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6537348 commit ba732be

31 files changed

Lines changed: 506 additions & 208 deletions

.github/workflows/main.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
---
22
name: main
3+
# aca-model is a git submodule of the aca-dev workspace and has no pixi config
4+
# of its own — the pixi environments live in the parent workspace, whose
5+
# `tests-cpu` env has editable path-dependencies on private sibling repos that a
6+
# standalone CI runner cannot clone. CI therefore installs with pip directly.
37
concurrency:
48
group: ${{ github.head_ref || github.run_id }}
59
cancel-in-progress: true
@@ -26,10 +30,10 @@ jobs:
2630
- uses: actions/setup-python@v6
2731
with:
2832
python-version: ${{ matrix.python-version }}
29-
- name: Install pylcm (feature branch — revert to @main once pylcm#348/#350 merge)
33+
- name: Install pylcm from main
3034
run: >-
3135
pip install "pylcm @
32-
git+https://github.com/OpenSourceEconomics/pylcm.git@feat/runtime-grid-extra-params"
36+
git+https://github.com/OpenSourceEconomics/pylcm.git@main"
3337
- name: Install aca-model with test deps
3438
run: pip install -e . pytest pdbp
3539
- name: Run pytest

.gitignore

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,59 @@
1-
__pycache__/
2-
*.py[cod]
1+
# Claude Code
2+
.claude/
3+
4+
# Distribution / packaging
5+
*.egg
36
*.egg-info/
4-
dist/
7+
*.manifest
8+
*.spec
9+
.eggs/
10+
.installed.cfg
511
build/
6-
bld/
12+
dist/
13+
MANIFEST
14+
sdist/
15+
wheels/
16+
17+
# IDE
18+
.idea/
19+
.vscode/
20+
21+
# Jupyter / Jupyter Book
22+
.ipynb_checkpoints/
23+
_build
24+
25+
# macOS
26+
.DS_Store
27+
28+
# pixi
729
.pixi/
30+
node_modules/
31+
32+
# pytask
833
.pytask/
34+
.pytask.sqlite3
35+
bld/
36+
out/
37+
pytask.lock
38+
pytask.lock.journal
39+
40+
# Python
41+
__pycache__/
42+
*.py[cod]
43+
*.so
44+
*$py.class
45+
46+
# Ruff
47+
.ruff_cache/
48+
49+
# Testing
50+
.cache/
951
.coverage
52+
.coverage.*
53+
.hypothesis/
54+
.pytest_cache/
55+
coverage.xml
1056
htmlcov/
57+
58+
# Version file (generated by hatch-vcs)
59+
src/*/_version.py

.pre-commit-config.yaml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
repos:
33
- repo: meta
44
hooks:
5-
- id: check-hooks-apply
5+
# check-hooks-apply is omitted: aca-model ships no notebooks yet, so the
6+
# boilerplate nbstripout hook matches nothing and that meta check would
7+
# fail. Re-add it once the repo gains a notebook.
68
- id: check-useless-excludes
79
- repo: https://github.com/tox-dev/pyproject-fmt
8-
rev: v2.21.1
10+
rev: v2.23.0
911
hooks:
1012
- id: pyproject-fmt
1113
- repo: https://github.com/lyz-code/yamlfix
@@ -37,6 +39,7 @@ repos:
3739
- id: name-tests-test
3840
args:
3941
- --pytest-test-first
42+
exclude: ^tests/helpers/
4043
- id: no-commit-to-branch
4144
args:
4245
- --branch
@@ -46,8 +49,12 @@ repos:
4649
rev: v1.38.0
4750
hooks:
4851
- id: yamllint
52+
- repo: https://github.com/python-jsonschema/check-jsonschema
53+
rev: 0.37.2
54+
hooks:
55+
- id: check-github-workflows
4956
- repo: https://github.com/astral-sh/ruff-pre-commit
50-
rev: v0.15.12
57+
rev: v0.15.16
5158
hooks:
5259
- id: ruff-check
5360
args:
@@ -61,6 +68,13 @@ repos:
6168
- jupyter
6269
- pyi
6370
- python
71+
- repo: https://github.com/kynan/nbstripout
72+
rev: 0.9.1
73+
hooks:
74+
- id: nbstripout
75+
args:
76+
- --extra-keys
77+
- metadata.kernelspec metadata.language_info.version metadata.vscode
6478
- repo: https://github.com/executablebooks/mdformat
6579
rev: 1.0.0
6680
hooks:

pyproject.toml

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
[build-system]
22
build-backend = "hatchling.build"
3-
requires = [ "hatchling" ]
3+
requires = [ "hatch-vcs", "hatchling" ]
44

55
[project]
66
name = "aca-model"
7-
version = "0.0.0"
87
description = "Core lifecycle model for the ACA structural retirement project."
98
readme = { file = "README.md", content-type = "text/markdown" }
109
keywords = [
@@ -23,11 +22,12 @@ classifiers = [
2322
"Programming Language :: Python :: 3 :: Only",
2423
"Programming Language :: Python :: 3.14",
2524
]
25+
dynamic = [ "version" ]
2626
dependencies = [
2727
"attrs",
28+
"beartype",
2829
"cloudpickle",
2930
"dags",
30-
"estimagic",
3131
"jax>=0.9",
3232
"jaxtyping",
3333
"numpy>=2.2",
@@ -43,13 +43,19 @@ email = "hmgaudecker@uni-bonn.de"
4343
[[project.maintainers]]
4444
name = "Hans-Martin von Gaudecker"
4545
email = "hmgaudecker@uni-bonn.de"
46+
[project.urls]
47+
Github = "https://github.com/OpenSourceEconomics/aca-model"
48+
Repository = "https://github.com/OpenSourceEconomics/aca-model"
49+
Tracker = "https://github.com/OpenSourceEconomics/aca-model/issues"
4650

4751
[tool.hatch]
52+
version.source = "vcs"
53+
metadata.allow-direct-references = true
54+
build.hooks.vcs.version-file = "src/aca_model/_version.py"
4855
build.targets.sdist.exclude = [ "tests" ]
4956
build.targets.sdist.only-packages = true
5057
build.targets.wheel.only-include = [ "src" ]
5158
build.targets.wheel.sources = [ "src" ]
52-
metadata.allow-direct-references = true
5359

5460
[tool.ruff]
5561
fix = true
@@ -84,9 +90,21 @@ extend-ignore = [
8490
"RUF002", # Ambiguous Unicode in docstrings (Greek letters in math)
8591
"RUF003", # Ambiguous Unicode in comments (Greek letters in math)
8692
]
87-
per-file-ignores."src/aca_model/models/*" = [ "E501" ]
88-
per-file-ignores."task_*.py" = [ "ANN", "ARG001" ]
89-
per-file-ignores."tests/*" = [ "D", "E501", "INP001", "PD011", "PLR2004", "S101" ]
93+
per-file-ignores."src/aca_model/models/*" = [
94+
"E501", # Line too long (generated model files)
95+
]
96+
per-file-ignores."task_*.py" = [
97+
"ANN", # Type annotations (use ty instead)
98+
"ARG001", # Unused function argument (pytask signatures)
99+
]
100+
per-file-ignores."tests/*" = [
101+
"D", # Docstrings
102+
"E501", # Line too long
103+
"INP001", # Implicit namespace package
104+
"PD011", # Use of .values (false positives on non-pandas objects)
105+
"PLR2004", # Magic value used in comparison
106+
"S101", # Use of assert
107+
]
90108
pydocstyle.convention = "google"
91109

92110
[tool.pyproject-fmt]
@@ -111,13 +129,13 @@ rules.unused-ignore-comment = "error"
111129

112130
[tool.pytest]
113131
ini_options.addopts = '--pdbcls=pdbp:Pdb --ignore-glob=*skip_* -m "not long_running"'
114-
ini_options.filterwarnings = []
115132
ini_options.markers = [
116133
"""\
117134
long_running: Tests that take a lot of time to run (run them via pytest -m \
118135
long_running).\
119136
""",
120137
]
138+
ini_options.filterwarnings = []
121139

122140
[tool.yamlfix]
123141
line_length = 88

src/aca_model/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
11
import jax
22

33
jax.config.update("jax_enable_x64", True)
4+
5+
# Import lcm before installing the claw so its `_jaxtyping_patch` (picklable
6+
# jaxtyping sentinel) and `MappingProxyType` pytree registration are in place.
7+
import lcm # noqa: E402, F401
8+
9+
# Install beartype's AST-rewriting claw on the whole `aca_model` package before
10+
# any submodule is imported. The claw transforms each module's AST at first
11+
# import to insert runtime type checks against its annotations; aca_model's
12+
# numerical DAG/transition/utility functions are otherwise unchecked, since
13+
# pylcm's own claw is scoped to `lcm.*`. Violations surface as beartype's
14+
# `BeartypeCallHintViolation` — aca_model is an application, not a library with
15+
# a documented exception contract.
16+
from beartype import BeartypeConf, BeartypeStrategy # noqa: E402
17+
from beartype.claw import beartype_package # noqa: E402
18+
19+
beartype_package(
20+
"aca_model",
21+
conf=BeartypeConf(strategy=BeartypeStrategy.On, is_pep484_tower=True),
22+
)
-537 Bytes
Binary file not shown.

src/aca_model/_version.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/aca_model/aca/health_insurance.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
swapping in the regime DAG.
77
"""
88

9+
from collections.abc import Mapping
910
from enum import Enum, auto
11+
from typing import Any, cast
1012

1113
import jax.numpy as jnp
1214
from lcm.params import MappingLeaf
@@ -43,7 +45,7 @@ def mandate_penalty(
4345
Penalty = clip(income * income_fraction, min, max) if uninsured and
4446
income above exemption threshold; 0 otherwise.
4547
"""
46-
sched = mandate_schedule.data
48+
sched = cast("Mapping[str, Any]", mandate_schedule.data)
4749
is_uninsured = buy_private == BuyPrivate.no
4850
exempt = gross_income < sched["exempt_income"][spousal_income]
4951
raw = jnp.clip(
@@ -67,7 +69,7 @@ def premium_subsidy(
6769
FPL kink points, subsidy = max(0, premium - income * applicable_rate).
6870
Return 0 when buy_private==no or income outside 100-400% FPL range.
6971
"""
70-
sched = premium_credit_schedule.data
72+
sched = cast("Mapping[str, Any]", premium_credit_schedule.data)
7173
kinks = sched["kinks"] # [n_kinks, 3]
7274
frac_income = sched["frac_income"] # [n_kinks]
7375

@@ -92,7 +94,7 @@ def cost_sharing(
9294
Applied to deductible, coinsurance, and OOP max.
9395
Return 1.0 when buy_private==no (no reduction for uninsured).
9496
"""
95-
sched = cost_sharing_schedule.data
97+
sched = cast("Mapping[str, Any]", cost_sharing_schedule.data)
9698
kinks = sched["kinks"] # [n_kinks, 3]
9799
factors = sched["factors"] # [n_kinks + 1]
98100
bracket = jnp.searchsorted(kinks[:, spousal_income], gross_income, side="right")
@@ -111,7 +113,8 @@ def is_medicaid_eligible(
111113
Unlike baseline SSI-based Medicaid, ACA expansion uses only income
112114
(no assets test, no Medicare requirement).
113115
"""
114-
threshold = medicaid_schedule.data["income_threshold"]
116+
sched = cast("Mapping[str, Any]", medicaid_schedule.data)
117+
threshold = sched["income_threshold"]
115118
return countable_income < threshold[spousal_income]
116119

117120

src/aca_model/agent/preferences.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,10 @@ def u_alive(
140140
coefficient_rra: FloatND,
141141
utility_scale_factor: FloatND,
142142
) -> FloatND:
143-
"""Within-period utility for every non-dead regime: CES over consumption and leisure.
143+
"""Within-period utility for every non-dead regime.
144144
145-
`leisure` is a DAG input — supplied per-regime by `leisure_canwork_retiree_or_nongroup`,
145+
CES over consumption and leisure. `leisure` is a DAG input — supplied
146+
per-regime by `leisure_canwork_retiree_or_nongroup`,
146147
`leisure_canwork_tied`, or `leisure_forcedout`.
147148
"""
148149
composite = consumption_equiv**consumption_weight * leisure ** (

0 commit comments

Comments
 (0)