Skip to content

Add parsing support for direct steady state constraints (y[ss] = value)#256

Draft
Copilot wants to merge 3 commits intocopilot/update-equation-modification-functionalityfrom
copilot/add-steady-state-constraints
Draft

Add parsing support for direct steady state constraints (y[ss] = value)#256
Copilot wants to merge 3 commits intocopilot/update-equation-modification-functionalityfrom
copilot/add-steady-state-constraints

Conversation

Copy link
Contributor

Copilot AI commented Feb 1, 2026

Adds syntax for directly specifying steady state values in the @parameters macro. This enables pinning variable levels for rank-deficient models where equations only determine ratios, not absolute levels.

@parameters MyModel begin
    β = 0.95
    y[ss] = 1                    # Numeric constant
    c[ss] = α / 4 * θ            # Expression involving parameters
end

Changes

Parser (src/parser.jl)

  • Detect var[ss] = expr patterns in parse_parameter_definitions_raw
  • Store constraint variables and expressions separately (not evaluated at parse time)
  • Extract parameters from constraint expressions for dependency tracking
  • Validate: constrained vars must exist, no duplicates, no conflicts with calibration equations

Data Structures (src/structures.jl, src/options_and_caches.jl)

  • Add ss_direct_constraints_vars::Vector{Symbol} and ss_direct_constraints_exprs::Vector{Any} to post_parameters_macro
  • Update all constructor sites and update_post_parameters_macro

Documentation

  • New "Direct Steady State Constraints" section in docs/src/steady_state.md
  • Updated @parameters macro docstring

Tests (test/test_ss_direct_constraints.jl)

  • Parsing: numeric, float, expression, multiple constraints, alternative markers
  • Validation: unknown variable errors, duplicate constraint errors

Note

This PR implements parsing and storage only. Solver integration to actually use constraints for rank-deficient systems is future work per the implementation plan.

Original prompt

Implementation Plan: Direct Steady State Constraints (y[ss] = value)

Overview

Add support for directly specifying steady state values in the @parameters macro:

@parameters MyModel begin
    y[ss] = 1                    # Simple numeric value
    c[ss] = alpha / 4 * theta    # Expression involving parameters
    theta = cpie / 100 + 1       # Parameter that c[ss] depends on
    β = 0.95
end

This allows users to pin down steady state values without specifying which parameter adjusts (unlike the existing param | y[ss] = target syntax).

Key complexity: The constraint value can be an arbitrary expression involving other parameters, so it must be evaluated at solve time, not parse time.

Design Decisions

When is this useful?

  • Model has algebraic rank deficiency (indeterminate SS) — the model only determines ratios/relationships, not absolute levels
  • User wants to normalize a variable (e.g., set output = 1) — this is the solution to rank deficiency

Note: This feature handles rank deficiency (continuum of solutions). True multiple discrete equilibria are a different problem.

Core Algorithm (Option 3: Sequential with Hint)

  1. First attempt: Solve model equations only (square system)
  2. If converged: Check if constraint is satisfied (informational warning if not)
  3. If failed: Solve augmented system with constraint via variable substitution
  4. Always verify: Model residuals must be ≈ 0

Why this approach?

  • Correctness: Algebraic rank depends on parameter values; cannot cache decision
  • Speed: Hint mechanism avoids redundant computation during estimation
  • Simplicity: Variable substitution is cleaner than full SQP for simple constraints

Implementation Tasks

Task 1: Parse y[ss] = value in @parameters Macro

File: src/macros.jl

Goal: Recognize and capture y[ss] = expr patterns in the @parameters block, where:

  • LHS is a variable with a steady-state index ([ss], [stst], [steady], etc.)
  • RHS is any valid expression (number, parameter, or formula)

Detection pattern:

# Expression: y[ss] = 1  OR  y[ss] = alpha / 4 * theta
# - Assignment expression (head == :(=))
# - LHS is a ref expression (head == :ref) 
# - The index matches the SS pattern (ss, stst, steady, steadystate, steady_state)
# - RHS can be: Int, Float64, Symbol (parameter name), or Expr (formula)

Key insight: The RHS can be a complex expression involving parameters. We must:

  1. Store the expression (not evaluate it at parse time)
  2. Evaluate it at solve time when parameter values are known

What to store:

ss_direct_constraints_vars = Symbol[]   # Variable names (e.g., :y, :c)
ss_direct_constraints_exprs = Any[]     # RHS expressions (stored as-is)

Integration approach:

  • Follow the existing pattern for how calibration equations (param | y[ss] = target) are parsed and stored
  • The new syntax should be detected in the same postwalk that handles other parameter definitions
  • Insert the check before/alongside the existing x.args[1] isa Symbol check for regular parameter assignments

Expression evaluation at solve time:

Generate a function that evaluates the constraint targets given current parameter values (similar to calibration equation targets):

# Generated at macro expansion time:
function ss_constraint_targets(params)
    # params is a NamedTuple or similar with all parameter values
    return [
        params.alpha / 4 * params.theta,  # from y[ss] = alpha / 4 * theta
        # ... more constraints
    ]
end

Task 2: Add Storage in Data Structures

File: src/structures.jl

Add to post_parameters_macro struct (around line 265):

struct post_parameters_macro
    # ... existing fields ...
    ss_direct_constraints_vars::Vector{Symbol}
    ss_direct_constraints_exprs::Vector{Any}  # Expr, Symbol, or Number
end

Also add a compiled evaluator function that will be generated at macro expansion time:

# In the model struct or related cache, store a function:
ss_constraint_target_func::Function  # (params_dict) -> Vector{Float64}

This function is generated from the expressions and called at solve time to get numeric target values.

Update constructor call in macros.jl (around line 1520) to include the new fields.


Task 3: Validation in @parameters Macro

File: src/macros.jl

Add validation (after parsing, before constructing post_parameters_macro):

# Validate SS direct constraints
for var in ss_direct_constraints_vars
    # Check that var is actually a model variable
    @assert var  mod.$𝓂.constants.post_model_macro.var "SS constraint variable $var is not a model variable"
    
    # Check for conflicts with calibration equations
    # Cannot have both: y[ss] = 1 AND param | y[ss] = target
    # (The calibration equations target specific SS values via a free parameter)
end

# Chec...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 2 commits February 1, 2026 23:12
Co-authored-by: thorek1 <13523097+thorek1@users.noreply.github.com>
Co-authored-by: thorek1 <13523097+thorek1@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for direct steady state constraints in parameters macro Add parsing support for direct steady state constraints (y[ss] = value) Feb 1, 2026
Copilot AI requested a review from thorek1 February 1, 2026 23:20
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