Skip to content
Draft
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ The package contains the following models in the `models` folder:
**Occasionally binding constraints**|yes|yes|yes|yes|yes||||yes|||yes||
**Global solution**||||yes|yes|||||||yes||
**Estimation**|yes|yes|yes|||yes||yes|yes|yes|yes|||
**Balanced growth path**||yes|yes||||yes|yes|yes|yes|||||
**Balanced growth path**|yes|yes|yes||||yes|yes|yes|yes|||||
**Model input**|macro (julia)|text file|text file|text file|text file|macro (julia)|module (julia)|text file|text file|text file|text file|text file|text file|
**Timing convention**|end-of-period|end-of-period||end-of-period|start-of-period|start-of-period|end-of-period|end-of-period|end-of-period|end-of-period|end-of-period|start-of-period|start-of-period|

Expand Down
184 changes: 184 additions & 0 deletions docs/src/how-to/balanced_growth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Balanced Growth Path Handling

Many DSGE models feature non-stationary variables that grow along a balanced growth path (BGP). Common examples include output, consumption, capital, and wages in models with technological progress. `MacroModelling.jl` provides automatic handling of such models through the `auto_detrend`, `deflator`, and `trend_var` options.

## Overview

In a model with balanced growth, certain variables grow at constant rates over time. For example, with labor-augmenting technological progress at rate `γ`, output, consumption, and capital all grow at rate `γ` in the long run.

To solve such models using perturbation methods, we need to transform the non-stationary variables into stationary ones by "detrending" - dividing by the level of technology (or another appropriate trend variable).

`MacroModelling.jl` automates this process with two approaches:

### Automatic Detection (Recommended)

Use `auto_detrend = true` to let the package automatically:
1. Detect trend variables from equation patterns (e.g., `A[0] = γ * A[-1]`)
2. Identify which variables should be deflated by each trend
3. Apply the detrending transformation
4. Solve for the detrended steady state

### Manual Specification

Alternatively, you can manually specify:
1. Which variables should be detrended using `deflator` in `@model`
2. Trend variable growth factors using `trend_var` in `@parameters`

## Automatic Detrending

The simplest way to handle balanced growth is to use automatic detection:

```julia
@model RBC_growth auto_detrend = true begin
# Technology grows at rate γ - automatically detected as trend variable
A[0] = γ * A[-1]

# Variables multiplied by A will be auto-detected as trending
y[0] = A[0] * k[-1]^α
c[0] + k[0] = y[0] + (1-δ)*k[-1]
1/c[0] = β * (1/c[1]) * (α * y[1]/k[0] + 1-δ)
end

@parameters RBC_growth begin
γ = 1.02 # 2% growth rate
α = 0.33
β = 0.99
δ = 0.025
end
```

The package will:
1. Detect `A` as a trend variable (from `A[0] = γ * A[-1]`)
2. Identify `y`, `c`, `k` as variables that should be divided by `A`
3. Transform the equations to work with detrended (stationary) variables
4. Solve for the balanced growth path steady state

## Manual Specification

### Specifying Deflators

Use the `deflator` option in `@model` to specify which variables are non-stationary and what their deflator (trend variable) is:

```julia
@model RBC_growth deflator = Dict(:y => :A, :c => :A, :k => :A) begin
# A is the trend variable (e.g., technology level)
A[0] = γ * A[-1]

# Write equations in terms of detrended variables (y/A, c/A, k/A)
# The package transforms y[0] → y[0] * A[0] internally
y[0] = k[-1]^α
c[0] + k[0] = y[0] + (1-δ)*k[-1]
1/c[0] = β * (1/c[1]) * (α * y[1]/k[0] + 1-δ)
end
```

The `deflator` option takes a `Dict{Symbol, Symbol}` where:
- Keys are the non-stationary variables to be detrended
- Values are the trend variables to use as deflators

### Specifying Trend Variable Growth Factors

Use the `trend_var` option in `@parameters` to document the growth factors of trend variables:

```julia
@parameters RBC_growth trend_var = Dict(:A => :γ) begin
γ = 1.02 # 2% growth rate
α = 0.33
β = 0.99
δ = 0.025
end
```

## How It Works

### Automatic Detection

When `auto_detrend = true`, the package:

1. **Identifies trend variables**: Scans equations for patterns like `X[0] = g * X[-1]` or `X[0] = X[-1] * g` where `g` is a parameter or expression. These indicate unit root / deterministic trend processes.

2. **Identifies trending variables**: Finds variables that appear in the same equations as trend variables (heuristic approach).

3. **Applies detrending**: For each trending variable `v` with trend `T`, transforms `v[t] → v[t] / T[t]`.

### Manual Specification

When you specify a deflator for a variable, `MacroModelling.jl` automatically transforms the equations. For each variable `v` with deflator `d`:

- `v[0]` is transformed to `(v[0] * d[0])`
- `v[-1]` is transformed to `(v[-1] * d[-1])`
- `v[1]` is transformed to `(v[1] * d[1])`
- `v[ss]` is transformed to `(v[ss] * d[ss])`

This means you write your model in terms of detrended variables, and the package automatically "re-trends" them to recover the original (level) equations.

## Checking Balanced Growth Configuration

You can inspect the balanced growth path configuration using these functions:

```julia
# Check if a model has balanced growth handling enabled
has_balanced_growth(model)

# Get detailed information about the balanced growth configuration
info = get_balanced_growth_path_info(model)
# Returns: (has_balanced_growth, trend_vars, deflators, detrended_vars)
```

## Example: RBC Model with Technological Progress

Here's a complete example of an RBC model with exogenous labor-augmenting technological progress using automatic detection:

```julia
using MacroModelling

# Define the model with automatic detrending
@model RBC_BGP auto_detrend = true begin
# Technology grows at rate γ
A[0] = γ * A[-1]

# Production function
y[0] = A[0] * k[-1]^α

# Resource constraint
c[0] + k[0] = y[0] + (1-δ)*k[-1]

# Euler equation
1/c[0] = β * (1/c[1]) * (α * y[1]/k[0] + 1-δ)

# Productivity shock (stationary)
z[0] = ρ * z[-1] + σ * eps[x]
end

# Define parameters
@parameters RBC_BGP begin
γ = 1.005 # 0.5% quarterly growth
α = 0.33
β = 0.99
δ = 0.025
ρ = 0.9
σ = 0.01
end

# The model can now be solved and analyzed
get_steady_state(RBC_BGP)
```

## Notes and Best Practices

1. **Trend Variable Equation**: Always include an equation defining the evolution of the trend variable (e.g., `A[0] = γ * A[-1]`).

2. **Consistent Detrending**: Make sure all variables that grow at the same rate use the same deflator.

3. **Steady State**: The detrended variables should have well-defined steady states. If steady state solving fails, consider providing initial guesses.

4. **Automatic vs Manual**: Use `auto_detrend = true` for simple cases. For complex models with multiple trends or specific detrending requirements, manual specification with `deflator` provides more control.

5. **Comparison with Other Packages**: This feature is similar to Dynare's `trend_var` and `deflator` options, though the syntax differs slightly.

## See Also

- [`@model`](@ref) - Main model definition macro
- [`@parameters`](@ref) - Parameter definition macro
- [`get_balanced_growth_path_info`](@ref) - Get balanced growth configuration
- [`has_balanced_growth`](@ref) - Check if balanced growth is enabled
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The package contains the following models in the `models` folder:
|**Occasionally binding constraints**|yes|yes|yes|yes|yes||||yes|||yes||
|**Global solution**||||yes|yes|||||||yes||
|**Estimation**|yes|yes|yes|||yes||yes|yes|yes|yes|||
|**Balanced growth path**||yes|yes||||yes|yes|yes|yes||||
|**Balanced growth path**|yes|yes|yes||||yes|yes|yes|yes||||
|**Model input**|macro (julia)|text file|text file|text file|text file|macro (julia)|module (julia)|text file|text file|text file|text file|text file|text file|
|**Timing convention**|end-of-period|end-of-period||end-of-period|start-of-period|start-of-period|end-of-period|end-of-period|end-of-period|end-of-period|end-of-period|start-of-period|start-of-period|

Expand Down
2 changes: 1 addition & 1 deletion docs/src/unfinished_docs/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
- [ ] check warnings, errors throughout. check suppress not interfering with pigeons
- [ ] functions to reverse state_update (input: previous shock and current state, output previous state), find shocks corresponding to bringing one state to the next
- [ ] cover nested case: min(50,a+b+max(c,10))
- [ ] add balanced growth path handling
- [x] add balanced growth path handling
- [ ] autocorr and covariance with derivatives. return 3d array
- [ ] add pydsge and econpizza to overview
- [ ] add for loop parser in @parameters
Expand Down
4 changes: 4 additions & 0 deletions src/MacroModelling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ export translate_mod_file, translate_dynare_file, import_model, import_dynare
export write_mod_file, write_dynare_file, write_to_dynare_file, write_to_dynare, export_dynare, export_to_dynare, export_mod_file, export_model

export get_equations, get_steady_state_equations, get_dynamic_equations, get_calibration_equations, get_parameters, get_calibrated_parameters, get_parameters_in_equations, get_parameters_defined_by_parameters, get_parameters_defining_parameters, get_calibration_equation_parameters, get_variables, get_nonnegativity_auxiliary_variables, get_dynamic_auxiliary_variables, get_shocks, get_state_variables, get_jump_variables, get_missing_parameters, has_missing_parameters

# Balanced growth path
export has_balanced_growth, get_balanced_growth_info, get_trend_variables, get_variable_degree

# Internal
export irf, girf

Expand Down
109 changes: 109 additions & 0 deletions src/inspect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -943,4 +943,113 @@ function get_jump_variables(𝓂::ℳ)::Vector{String}
𝓂.timings.future_not_past_and_mixed |> collect |> sort .|> x -> replace.(string.(x), "◖" => "{", "◗" => "}")
end


"""
$(SIGNATURES)
Check if a model has balanced growth path structure (trend variables detected).

# Arguments
- `𝓂`: A model object

# Returns
- `Bool`: `true` if trend variables were detected, `false` otherwise.

# Examples
```julia
using MacroModelling

@model RBC_growth begin
A[0] = γ * A[-1] # Trend variable
y[0] = A[0] * k[-1]^α
# ...
end

@parameters RBC_growth begin
γ = 1.02
α = 0.33
end

has_balanced_growth(RBC_growth) # returns true
```
"""
function has_balanced_growth(𝓂::ℳ)::Bool
!isempty(𝓂.balanced_growth.trend_variables)
end


"""
$(SIGNATURES)
Get information about the balanced growth path structure of the model.

# Arguments
- `𝓂`: A model object

# Returns
- `NamedTuple` with fields:
- `trend_variables`: Dict mapping trend variable names to their growth rate parameters
- `variable_degrees`: Dict mapping variable names to their homogeneity degrees
- `growth_parameters`: Set of parameters representing growth rates

# Examples
```julia
using MacroModelling

@model RBC_growth begin
A[0] = γ * A[-1]
y[0] = A[0] * k[-1]^α
c[0] + k[0] = y[0] + (1-δ)*k[-1]
1/c[0] = β * (1/c[1]) * (α * y[1]/k[0] + 1-δ)
end

@parameters RBC_growth begin
γ = 1.02
α = 0.33
β = 0.99
δ = 0.025
end

info = get_balanced_growth_info(RBC_growth)
# info.trend_variables # Dict(:A => :γ)
# info.variable_degrees # Dict(:A => 1.0, :y => 1.0, :c => 1.0, :k => 1.0)
```
"""
function get_balanced_growth_info(𝓂::ℳ)
(
trend_variables = 𝓂.balanced_growth.trend_variables,
variable_degrees = 𝓂.balanced_growth.variable_degrees,
growth_parameters = 𝓂.balanced_growth.growth_rate_parameters
)
end


"""
$(SIGNATURES)
Get the names of trend variables in the model.

# Arguments
- `𝓂`: A model object

# Returns
- `Vector{Symbol}`: Names of variables that have been identified as trends (unit roots).
"""
function get_trend_variables(𝓂::ℳ)::Vector{Symbol}
collect(keys(𝓂.balanced_growth.trend_variables)) |> sort
end


"""
$(SIGNATURES)
Get the homogeneity degree of a specific variable.

# Arguments
- `𝓂`: A model object
- `var::Symbol`: The variable name

# Returns
- `Float64`: The homogeneity degree (0.0 for stationary, 1.0 for growing at trend rate)
"""
function get_variable_degree(𝓂::ℳ, var::Symbol)::Float64
get(𝓂.balanced_growth.variable_degrees, var, 0.0)
end

end # dispatch_doctor
Loading