Agent skill
policyengine-core
PolicyEngine Core simulation engine - the foundation powering all PolicyEngine calculations
Install this agent skill to your Project
npx add-skill https://github.com/PolicyEngine/policyengine-claude/tree/main/skills/tools-and-apis/policyengine-core-skill
SKILL.md
PolicyEngine Core
PolicyEngine Core is the microsimulation engine that powers all PolicyEngine calculations. It's a fork of OpenFisca-Core adapted for PolicyEngine's needs.
For Users
What is Core?
When you use policyengine.org to calculate taxes or benefits, PolicyEngine Core is the "calculator" running behind the scenes.
Core provides:
- The simulation engine that processes tax rules
- Variable and parameter management
- Entity relationships (person → family → household)
- Period handling (2024, 2025, etc.)
You don't interact with Core directly - you use it through:
- Web app: policyengine.org
- Python packages: policyengine-us, policyengine-uk
- API: api.policyengine.org
Why Core Matters
Core ensures:
- ✅ Accuracy - Calculations follow official rules exactly
- ✅ Consistency - Same rules applied everywhere
- ✅ Transparency - All rules traceable to legislation
- ✅ Performance - Vectorized calculations for speed
For Analysts
Understanding Core Concepts
When writing PolicyEngine code, you'll encounter Core concepts:
Variables:
- Represent quantities (income_tax, ctc, snap, etc.)
- Defined for specific entities (person, household, tax_unit)
- Calculated from formulas or set directly
Parameters:
- Policy rules that change over time (tax rates, benefit amounts)
- Organized hierarchically (gov.irs.credits.ctc.amount.base_amount)
- Stored in YAML files
Entities:
- Person: Individual
- Family: Family unit
- Tax unit: Tax filing unit
- Household: Physical household
- Marital unit: Marital status grouping
- SPM unit: Supplemental Poverty Measure unit
Periods:
- Year: 2024, 2025, etc.
- Month: 2024-01, 2024-02, etc.
- Specific dates: 2024-06-15
Core in Action
from policyengine_us import Simulation
# When you create a simulation
sim = Simulation(situation=household)
# Core manages:
# - Entity relationships
# - Variable dependencies
# - Parameter lookups
# - Period conversions
# When you calculate
result = sim.calculate("income_tax", 2026)
# Core:
# 1. Checks if already calculated
# 2. Identifies dependencies (income → AGI → taxable income → tax)
# 3. Calculates dependencies first
# 4. Applies formulas
# 5. Returns result
Core vs Country Packages
Core (policyengine-core):
- Generic simulation engine
- No specific tax/benefit rules
- Variable and parameter infrastructure
Country packages (policyengine-us, etc.):
- Built on Core
- Contain specific tax/benefit rules
- Define variables and parameters for that country
Relationship:
policyengine-core (engine)
↓ powers
policyengine-us (US rules)
↓ used by
policyengine-api (REST API)
↓ serves
policyengine-app (web interface)
For Contributors
Repository
Location: PolicyEngine/policyengine-core Origin: Fork of OpenFisca-Core
Clone:
git clone https://github.com/PolicyEngine/policyengine-core
Current Architecture
To see current structure:
tree policyengine_core/
# Key directories:
# - variables/ - Variable class and infrastructure
# - parameters/ - Parameter class and infrastructure
# - entities/ - Entity definitions
# - simulations/ - Simulation class
# - periods/ - Period handling
# - reforms/ - Reform application
To understand a specific component:
# Variable system
cat policyengine_core/variables/variable.py
# Parameter system
cat policyengine_core/parameters/parameter.py
# Simulation engine
cat policyengine_core/simulations/simulation.py
# Entity system
cat policyengine_core/entities/entity.py
Key Classes
Variable:
# To see Variable class implementation
cat policyengine_core/variables/variable.py
# Variables in country packages inherit from this:
from policyengine_core.variables import Variable
class income_tax(Variable):
value_type = float
entity = Person
label = "Income tax"
definition_period = YEAR
def formula(person, period, parameters):
# Vectorized formula
return calculate_tax(...)
Simulation:
# To see Simulation class implementation
cat policyengine_core/simulations/simulation.py
# Manages calculation graph and caching
sim = Simulation(situation=situation)
sim.calculate("variable", period)
Parameters:
# To see Parameter handling
cat policyengine_core/parameters/parameter_node.py
# Access in formulas:
parameters(period).gov.irs.credits.ctc.amount.base_amount
Vectorization (Critical!)
Core requires vectorized operations - no if-elif-else with arrays:
❌ Wrong (scalar logic):
if age < 18:
eligible = True
else:
eligible = False
✅ Correct (vectorized):
eligible = age < 18 # NumPy boolean array
Why: Core processes many households simultaneously for performance.
To see vectorization examples:
# Search for where() usage (vectorized if-then-else)
grep -r "np.where" policyengine_core/
# Find select() usage (vectorized case statements)
grep -r "select" policyengine_core/
Formula Dependencies
Core automatically resolves variable dependencies:
class taxable_income(Variable):
def formula(person, period, parameters):
# Core automatically calculates these first:
agi = person("adjusted_gross_income", period)
deduction = person("standard_deduction", period)
return agi - deduction
class income_tax(Variable):
def formula(person, period, parameters):
# Core knows to calculate taxable_income first
taxable = person("taxable_income", period)
return apply_brackets(taxable, ...)
To see dependency resolution:
# Find trace functionality
grep -r "trace" policyengine_core/simulations/
# Enable in your code:
simulation.trace = True
simulation.calculate("income_tax", 2026)
Period Handling
To see period implementation:
cat policyengine_core/periods/period.py
# Period types:
# - YEAR: 2024
# - MONTH: 2024-01
# - ETERNITY: permanent values
Usage in variables:
# Annual variable
definition_period = YEAR # Called with 2024
# Monthly variable
definition_period = MONTH # Called with "2024-01"
# Convert periods
yearly_value = person("monthly_income", period.this_year) * 12
Testing Core Changes
To run Core tests:
cd policyengine-core
make test
# Specific test
pytest tests/core/test_variables.py -v
To test in country package:
# Changes to Core affect all country packages
cd policyengine-us
uv pip install -e ../policyengine-core # Local development install
make test
Key Differences from OpenFisca
PolicyEngine Core differs from OpenFisca-Core:
To see PolicyEngine changes:
# Compare to OpenFisca
# Core fork diverged to add:
# - Enhanced performance
# - Better error messages
# - PolicyEngine-specific features
# See commit history for PolicyEngine changes
git log --oneline
Core Development Workflow
Making Changes to Core
-
Clone repo:
bashgit clone https://github.com/PolicyEngine/policyengine-core -
Install for development:
bashmake install -
Make changes to variable.py, simulation.py, etc.
-
Test locally:
bashmake test -
Test in country package:
bashcd ../policyengine-us uv pip install -e ../policyengine-core make test -
Format and commit:
bashmake format git commit -m "Description"
Understanding Impact
Changes to Core affect:
- ✅ All country packages (US, UK, Canada, IL, NG)
- ✅ The API
- ✅ The web app
- ✅ All analysis tools
Critical: Always test in multiple country packages before merging.
Common Core Patterns
Pattern 1: Adding a New Variable Type
Current variable types:
# See supported types
grep "value_type" policyengine_core/variables/variable.py
Types: int, float, bool, str, Enum, date
Pattern 2: Custom Formulas
Formula signature:
def formula(entity, period, parameters):
# entity: Person, TaxUnit, Household, etc.
# period: 2024, "2024-01", etc.
# parameters: Parameter tree for period
return calculated_value
To see formula examples:
# Search country packages for formulas
grep -A 10 "def formula" ../policyengine-us/policyengine_us/variables/ | head -50
Pattern 3: Parameter Access
Accessing parameters in formulas:
# Navigate parameter tree
param = parameters(period).gov.irs.credits.ctc.amount.base_amount
# Parameters automatically valid for period
# No need to check dates manually
To see parameter structure:
# Example from country package
tree ../policyengine-us/policyengine_us/parameters/gov/
Advanced Topics
Formula Caching
Core caches calculations automatically:
# First call calculates
tax1 = sim.calculate("income_tax", 2026)
# Second call returns cached value
tax2 = sim.calculate("income_tax", 2026) # Instant
Performance Optimization: Batching Parameter Lookups
When parameter lookups happen inside loops, batch them beforehand to avoid repeated function call overhead:
❌ Inefficient (repeated lookups):
# Inside uprate_parameters or similar functions
for instant in instants:
value = uprating_parameter(instant) # Repeated function calls
# ... use value
✅ Efficient (batched lookups):
# Pre-compute all values before the loop
value_cache = {
instant: uprating_parameter(instant)
for instant in instants
}
# Use cached values in loop
for instant in instants:
value = value_cache[instant] # Fast dictionary lookup
# ... use value
Why it matters:
- Parameter lookups involve instant/period conversions and tree traversal
- In large parameter sets (like policyengine-us), this can cause millions of redundant calls
- Example:
uprate_parametersreduced from 15s to 13.8s (8% improvement) by batching lookups
When to batch:
- Parameter lookups inside loops
- Multiple lookups of the same value at different points in code
- Any repeated
parameters(period).path.to.valuecalls
To find optimization opportunities:
# Profile import time
python -m cProfile -o profile.stats -c "from policyengine_us.system import system"
# Search for parameter lookup hotspots
grep -r "parameters(period)" policyengine_core/parameters/
Neutralizing Variables
# Set variable to zero in reform
reform = {
"income_tax": {
"2026-01-01.2100-12-31": 0
}
}
Adding Variables
Country packages add variables by inheriting from Core's Variable class.
See policyengine-us-skill for variable creation patterns.
Resources
Repository: https://github.com/PolicyEngine/policyengine-core
Documentation:
- Core API docs (see README in repo)
- OpenFisca docs (original): https://openfisca.org/doc/
Related skills:
- policyengine-us-skill - Using Core through country packages
- policyengine-standards-skill - Code quality standards
Troubleshooting
Common Issues
Variable not found:
# Error: Variable 'income_tax' not found
# Solution: Variable is defined in country package, not Core
# Use policyengine-us, not policyengine-core directly
Scalar vs array operations:
# Error: truth value of array is ambiguous
# Solution: Use np.where() instead of if-else
# See vectorization section above
Period mismatch:
# Error: Cannot compute variable_name for period 2024-01
# Solution: Check definition_period matches request
# YEAR variables need YEAR periods (2024, not "2024-01")
To debug:
# Enable tracing
sim.trace = True
sim.calculate("variable", period)
# See calculation dependency tree
Contributing to Core
Before contributing:
- Read Core README
- Understand OpenFisca architecture
- Test changes in multiple country packages
- Follow policyengine-standards-skill
Development standards:
- Python 3.10-3.13
- Ruff formatting (79-char)
- Comprehensive tests
- No breaking changes without discussion
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
policyengine-healthcare
Healthcare program modeling in PolicyEngine-US — Medicaid, ACA marketplace, CHIP, and Medicare. Covers encoding rules, running analyses, and navigating the unique complexity of US healthcare programs. Triggers: "healthcare", "health insurance", "Medicaid", "ACA", "CHIP", "Medicare", "marketplace", "premium tax credit", "APTC", "PTC", "SLCSP", "benchmark plan", "rating area", "age curve", "family tier", "coverage gap", "Medicaid expansion", "MAGI", "medicaid_magi", "aca_magi", "medicaid_income_level", "medicaid_category", "enrollment", "takeup", "take-up", "per capita", "CSR", "cost sharing", "insurance premium", "second lowest silver", "required contribution percentage", "42 CFR", "IRC 36B", "categorical eligibility", "expansion adult", "healthcare reform", "healthcare analysis", "health policy".
policyengine-us
ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-US code. Contains the correct API patterns for household calculations and population simulations using the new policyengine package. Covers US federal and state taxes/benefits. Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "earning $", "making $", "calculate benefits", "calculate taxes", "benefit for a", "what would I get", "what is the maximum", "what is the rate", "poverty line", "income limit", "benefit amount", "maximum benefit", "compare states", "TANF", "SNAP", "EITC", "CTC", "SSI", "WIC", "Section 8", "Medicaid", "ACA", "child tax credit", "earned income", "supplemental security", "housing voucher", "microsimulation", "population", "reform", "policy impact", "budgetary", "decile".
policyengine-uk
ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-UK code. Contains the correct API patterns for household calculations and population simulations using the new policyengine package (not policyengine_uk directly). Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "with income of", "earning £", "making £", "calculate benefits", "calculate taxes", "benefit for a", "tax for a", "what would I get", "what would they get", "what is the rate", "what is the threshold", "personal allowance", "maximum benefit", "income limit", "benefit amount", "how much is", "Universal Credit", "child benefit", "pension credit", "housing benefit", "council tax", "income tax", "national insurance", "JSA", "ESA", "PIP", "disability living allowance", "working tax credit", "child tax credit", "Scotland", "Wales", "UK", "microsimulation", "population", "reform", "policy impact", "budgetary", "decile".
policyengine-canada
ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-Canada code. Contains Canadian federal and provincial tax/benefit rules for household calculations. IMPORTANT: PolicyEngine-Canada does NOT have representative population microdata. Do NOT attempt microsimulation or population-level estimates for Canada. Only provide household-level analysis (single-family impacts, eligibility, benefit amounts). Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "earning $", "making $", "calculate benefits", "calculate taxes", "benefit for a", "what would I get", "what is the maximum", "what is the rate", "income limit", "benefit amount", "maximum benefit", "compare provinces", "CCB", "Canada Child Benefit", "GST credit", "HST credit", "GST/HST", "OAS", "Old Age Security", "GIS", "Guaranteed Income Supplement", "CWB", "Canada Workers Benefit", "EI", "Employment Insurance", "CPP", "Canada Pension Plan", "RRSP", "TFSA", "Ontario Child Benefit", "OCB", "Ontario Trillium Benefit", "OTB", "BC Climate Action", "Alberta Child Benefit", "Quebec", "CRA", "Canada Revenue Agency", "Canadian", "Canada", "Ontario", "British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Nova Scotia", "New Brunswick", "PEI", "Newfoundland", "Yukon", "NWT", "Nunavut", "provincial tax", "federal tax Canada".
policyengine-ui-kit-consumer
This skill should be used when setting up a new project that uses @policyengine/ui-kit, debugging CSS or styling issues in a consumer app, or when Tailwind utility classes are not being generated. Also use when creating globals.css, configuring PostCSS, or troubleshooting "no styles", "no spacing", or "no layout" problems. Triggers: "ui-kit import", "globals.css setup", "Tailwind not working", "styles not applying", "utility classes missing", "setup ui-kit", "PostCSS config", "no styling", "CSS broken", "import ui-kit", "theme.css", "no layout", "no spacing", "@tailwindcss/postcss"
policyengine-tailwind-shadcn
Tailwind CSS v4 + shadcn/ui integration patterns for PolicyEngine frontend projects. Covers @theme namespaces, CSS variable conventions, SVG var() usage, and common mistakes. Triggers: "Tailwind v4", "@theme", "shadcn", "CSS variables", "design tokens CSS", "theme.css", "@theme inline"
Didn't find tool you were looking for?