Skip to content

Add numerical guards for CES with heterogeneous epsilon#1096

Open
vahid-ahmadi wants to merge 1 commit intoPSLmodels:masterfrom
vahid-ahmadi:fix/ces-numerical-guards
Open

Add numerical guards for CES with heterogeneous epsilon#1096
vahid-ahmadi wants to merge 1 commit intoPSLmodels:masterfrom
vahid-ahmadi:fix/ces-numerical-guards

Conversation

@vahid-ahmadi
Copy link
Copy Markdown

Summary

  • Adds _FLOOR = 1e-12 guards in firm.py to prevent 0^(negative) → inf/NaN when CES epsilon ≠ 1
  • Fixes bug in get_MPx: np.any(x) == 0 should be np.any(x == 0) (the original always evaluates to False)
  • Guards applied in get_Y, get_MPx, get_KLratio_KLonly, and solve_L — only in the general CES code paths; Cobb-Douglas (ε=1) is unchanged

Motivation

When epsilon varies across sectors (e.g., Energy=0.50, Real Estate=0.40, Info & Finance=1.20), the CES production function raises K, L, K_g to (epsilon-1)/epsilon. For epsilon < 1, this exponent is negative, so any zero or near-zero input during initial solver iterations produces inf/NaN and crashes the solver.

This was blocking OG-UK from using calibrated sector-specific CES elasticities from Chirinko (2008) and Knoblach et al. (2020). With this fix, OG-UK's 8-sector model with heterogeneous epsilon (0.40–1.30) converges successfully in both SS and TPI.

Test plan

  • Existing OG-Core tests pass (guards only activate when inputs ≈ 0)
  • OG-UK 8-sector SS with heterogeneous epsilon converges (tested, resource constraint ~1e-6)
  • Single-sector (M=1, ε=1.0) results unchanged (guards not activated for Cobb-Douglas)

🤖 Generated with Claude Code

…epsilon

When epsilon differs from 1.0 across sectors, the CES production function
raises K, L, and K_g to (epsilon-1)/epsilon, which is negative for epsilon < 1.
If any input is zero or near-zero (common during initial solver iterations),
this produces inf or NaN, causing the SS and TPI solvers to fail.

Changes:
- Add _FLOOR (1e-12) constant and apply np.maximum() guards before all
  CES power operations in get_Y, get_MPx, get_KLratio_KLonly, and solve_L
- Fix bug in get_MPx: np.any(x) == 0 should be np.any(x == 0)
- Guard denominator in get_KLratio_KLonly CES branch against negative values

These guards only affect the general CES code paths (epsilon != 1); the
Cobb-Douglas special case (epsilon == 1) is unchanged.

Tested with OG-UK 8-sector model using heterogeneous epsilon values
(0.40-1.30) — SS solver now converges successfully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
vahid-ahmadi added a commit to vahid-ahmadi/OG-UK that referenced this pull request Mar 31, 2026
Two changes:

1. Make single-sector (M=1) the default run mode. The 8-sector industry
   calibration is now opt-in via `multi_sector=True` parameter on
   solve_steady_state() and run_transition_path(), or via the
   `multi-sector` CLI flag:
     uv run python examples/run_oguk.py ss pooled              # M=1
     uv run python examples/run_oguk.py ss pooled multi-sector # M=8

2. Enable calibrated heterogeneous CES elasticities (epsilon) in the
   8-sector mode. Previously forced to 1.0 (Cobb-Douglas) for all
   sectors due to solver NaN issues — now uses literature values from
   Chirinko (2008) and Knoblach et al. (2020):
     Energy=0.50, Construction=0.70, Trade & Transport=1.00,
     Info & Finance=1.20, Real Estate=0.40, Business Services=1.30,
     Public & Other=0.90, Manufacturing=0.80

   Supporting changes:
   - Recalibrate TFP (Z) using CES Solow residuals instead of
     Cobb-Douglas residuals when epsilon != 1
   - Use hybr root-finder (Powell hybrid) instead of LM for
     multi-sector SS — LM gets stuck at ~1e-5 residuals
   - Relax mindist_SS and RC_SS to 1e-4 for multi-sector

   Requires OG-Core PR PSLmodels/OG-Core#1096 (numerical guards for
   CES production functions).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdebacker
Copy link
Copy Markdown
Member

@vahid-ahmadi Can you merge in upstream changes? And then run make format?

I think tests should pass and this will be set to merge then. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants