Skip to content
Snippets Groups Projects
Commit 20e018f2 authored by Daniel Sali's avatar Daniel Sali
Browse files

Remove wait on .jls file, define DynareError

parent ae4c20e2
No related branches found
No related tags found
1 merge request!3Remove async wait
// cyclic reduction algorithm
var y, c, k, a, h, b;
varexo e, u;
verbatim;
% I want these comments included in
% example1.m 1999q1 1999y
%
var = 1;
end;
parameters beta, rho, alpha, delta, theta, psi, tau;
alpha = 0.36;
rho = 0.95;
tau = 0.025;
beta = 0.99;
delta = 0.025;
psi = 0;
theta = 2.95;
phi = 0.1;
model;
c*theta*h^(1+psi)=(1-alpha)*y;
k = beta*(((exp(b)*c)/(exp(b(+1))*c(+1)))
*(exp(b(+1))*alpha*y(+1)+(1-delta)*k));
y = exp(a)*(k(-1)^alpha)*(h^(1-alpha));
k = exp(b)*(y-c)+(1-delta)*k(-1);
a = rho*a(-1)+tau*b(-1) + e;
b = tau*a(-1)+rho*b(-1) + u;
end;
steady_state_model;
K_Y = beta*alpha /(1 - beta*(1 - delta));
H_Y = K_Y^(-alpha/(1 - alpha));
C_Y = 1 - delta*K_Y;
y = (theta*C_Y*H_Y^(1 + psi)/(1 - alpha))^(-1/(1 + psi));
c = C_Y*y;
k = K_Y*y;
h = H_Y*y;
a = 0;
b = 0;
end;
spooky;
shocks;
var e;
periods 1;
values 0.01;
var u;
periods 2;
values 0.015;
end;
perfect_foresight_setup(periods = 100);
perfect_foresight_solver;
import logging
import time
import os
from pathlib import Path
from juliacall import Main as jl
from juliacall import Main as jl, JuliaError
from .dynare_context import Context
from .errors import DynareError
logger = logging.getLogger("dynare.dynare")
def dynare(model: str | Path) -> Context:
if isinstance(model, str):
model = Path(model)
jl.seval("using Serialization")
jl.seval("using Dynare")
# Double-escape '\' as julia will also interpret the string. Only relevant on Windows
resolved_path = str(model.resolve()).replace("\\", "\\\\")
jl.seval(f'@dynare "{resolved_path}"')
jls_path = model.parent / model.stem / "output" / f"{model.stem}.jls"
timeout = int(os.getenv("DYNARE_WAIT", 600))
start_time = time.time()
while not jls_path.exists():
if time.time() - start_time > timeout:
logger.error(
f"Timeout reached: {timeout} seconds. The file {jls_path.resolve()} was not created."
try:
if isinstance(model, str):
model = Path(model)
jl.seval("using Serialization")
jl.seval("using Dynare")
# Double-escape '\' as julia will also interpret the string. Only relevant on Windows
resolved_path = str(model.resolve()).replace("\\", "\\\\")
jl.seval(
"using DataFrames, Tables, PythonCall, Dynare, LinearRationalExpectations"
)
# Convert the Julia AxisArrayTable fields of a Context to a Pandas DataFrame with PythonCall.pytable
jl.seval(
"""
using Dynare: EstimationResults, EstimatedParameters, SymbolTable, ModFileInfo
struct PyWork
analytical_steadystate_variables::Vector{Int}
data::Py
datafile::String
params::Vector{Float64}
residuals::Vector{Float64}
dynamic_variables::Vector{Float64}
exogenous_variables::Vector{Float64}
observed_variables::Vector{String}
Sigma_m::Matrix{Float64}
jacobian::Matrix{Float64}
qr_jacobian::Matrix{Float64}
model_has_trend::Vector{Bool}
histval::Matrix{Union{Float64,Missing}}
homotopy_setup::Vector{NamedTuple{(:name, :type, :index, :endvalue, :startvalue), Tuple{Symbol, SymbolType, Int64, Float64, Union{Float64, Missing}}}}
initval_endogenous::Matrix{Union{Float64,Missing}}
initval_exogenous::Matrix{Union{Float64,Missing}}
initval_exogenous_deterministic::Matrix{Union{Float64,Missing}}
endval_endogenous::Matrix{Union{Float64,Missing}}
endval_exogenous::Matrix{Union{Float64,Missing}}
endval_exogenous_deterministic::Matrix{Union{Float64,Missing}}
scenario::Dict{PeriodsSinceEpoch, Dict{PeriodsSinceEpoch, Dict{Symbol, Pair{Float64, Symbol}}}}
shocks::Vector{Float64}
perfect_foresight_setup::Dict{String,Any}
estimated_parameters::EstimatedParameters
end
function convert_to_pywork(work::Work)::PyWork
# Convert the AxisArrayTable data to a Pandas DataFrame using pytable
py_data = pytable(work.data)
return PyWork(
work.analytical_steadystate_variables,
py_data,
work.datafile,
work.params,
work.residuals,
work.dynamic_variables,
work.exogenous_variables,
work.observed_variables,
work.Sigma_m,
work.jacobian,
work.qr_jacobian,
work.model_has_trend,
work.histval,
work.homotopy_setup,
work.initval_endogenous,
work.initval_exogenous,
work.initval_exogenous_deterministic,
work.endval_endogenous,
work.endval_exogenous,
work.endval_exogenous_deterministic,
work.scenario,
work.shocks,
work.perfect_foresight_setup,
work.estimated_parameters
)
raise TimeoutError(
f"Timeout reached: {timeout} seconds. The file {jls_path.resolve()} was not created."
end
struct PySimulation
firstperiod::PeriodsSinceEpoch
lastperiod::PeriodsSinceEpoch
name::String
statement::String
data::Py
end
function convert_to_pysimulation(simulation::Simulation)::PySimulation
# Convert the AxisArrayTable data to a Pandas DataFrame using pytable
py_data = pytable(simulation.data)
return PySimulation(
simulation.firstperiod,
simulation.lastperiod,
simulation.name,
simulation.statement,
py_data
)
logger.debug(f"Waiting for the file {jls_path.resolve()} to be created")
time.sleep(1)
end
jl.seval("using DataFrames, Tables, PythonCall, Dynare, LinearRationalExpectations")
# Define the PyModelResult structure with Pandas DataFrame fields
mutable struct PyModelResult
irfs::Dict{Symbol, Py}
trends::Trends
stationary_variables::Vector{Bool}
estimation::EstimationResults
filter::Py # Pandas DataFrame
forecast::Vector{Py} # Vector of Pandas DataFrames
initial_smoother::Py # Pandas DataFrame
linearrationalexpectations::LinearRationalExpectationsResults
simulations::Vector{PySimulation}
smoother::Py # Pandas DataFrame
solution_derivatives::Vector{Matrix{Float64}}
# sparsegrids::SparsegridsResults
end
# Convert the Julia AxisArrayTable fields of a Context to a Pandas DataFrame with PythonCall.pytable
jl.seval(
"""
using Dynare: EstimationResults, EstimatedParameters, SymbolTable, ModFileInfo
struct PyWork
analytical_steadystate_variables::Vector{Int}
data::Py
datafile::String
params::Vector{Float64}
residuals::Vector{Float64}
dynamic_variables::Vector{Float64}
exogenous_variables::Vector{Float64}
observed_variables::Vector{String}
Sigma_m::Matrix{Float64}
jacobian::Matrix{Float64}
qr_jacobian::Matrix{Float64}
model_has_trend::Vector{Bool}
histval::Matrix{Union{Float64,Missing}}
homotopy_setup::Vector{NamedTuple{(:name, :type, :index, :endvalue, :startvalue), Tuple{Symbol, SymbolType, Int64, Float64, Union{Float64, Missing}}}}
initval_endogenous::Matrix{Union{Float64,Missing}}
initval_exogenous::Matrix{Union{Float64,Missing}}
initval_exogenous_deterministic::Matrix{Union{Float64,Missing}}
endval_endogenous::Matrix{Union{Float64,Missing}}
endval_exogenous::Matrix{Union{Float64,Missing}}
endval_exogenous_deterministic::Matrix{Union{Float64,Missing}}
scenario::Dict{PeriodsSinceEpoch, Dict{PeriodsSinceEpoch, Dict{Symbol, Pair{Float64, Symbol}}}}
shocks::Vector{Float64}
perfect_foresight_setup::Dict{String,Any}
estimated_parameters::EstimatedParameters
end
function convert_to_pywork(work::Work)::PyWork
# Convert the AxisArrayTable data to a Pandas DataFrame using pytable
py_data = pytable(work.data)
return PyWork(
work.analytical_steadystate_variables,
py_data,
work.datafile,
work.params,
work.residuals,
work.dynamic_variables,
work.exogenous_variables,
work.observed_variables,
work.Sigma_m,
work.jacobian,
work.qr_jacobian,
work.model_has_trend,
work.histval,
work.homotopy_setup,
work.initval_endogenous,
work.initval_exogenous,
work.initval_exogenous_deterministic,
work.endval_endogenous,
work.endval_exogenous,
work.endval_exogenous_deterministic,
work.scenario,
work.shocks,
work.perfect_foresight_setup,
work.estimated_parameters
)
end
struct PySimulation
firstperiod::PeriodsSinceEpoch
lastperiod::PeriodsSinceEpoch
name::String
statement::String
data::Py
end
function convert_to_pysimulation(simulation::Simulation)::PySimulation
# Convert the AxisArrayTable data to a Pandas DataFrame using pytable
py_data = pytable(simulation.data)
return PySimulation(
simulation.firstperiod,
simulation.lastperiod,
simulation.name,
simulation.statement,
py_data
)
end
function convert_to_pymodelresult(model_result::ModelResults)::PyModelResult
py_irfs = Dict{Symbol, Py}()
for (key, axis_array_table) in model_result.irfs
py_irfs[key] = pytable(axis_array_table)
end
py_forecast = [pytable(forecast) for forecast in model_result.forecast]
# Define the PyModelResult structure with Pandas DataFrame fields
mutable struct PyModelResult
irfs::Dict{Symbol, Py}
trends::Trends
stationary_variables::Vector{Bool}
estimation::EstimationResults
filter::Py # Pandas DataFrame
forecast::Vector{Py} # Vector of Pandas DataFrames
initial_smoother::Py # Pandas DataFrame
linearrationalexpectations::LinearRationalExpectationsResults
simulations::Vector{PySimulation}
smoother::Py # Pandas DataFrame
solution_derivatives::Vector{Matrix{Float64}}
# sparsegrids::SparsegridsResults
end
function convert_to_pymodelresult(model_result::ModelResults)::PyModelResult
py_irfs = Dict{Symbol, Py}()
for (key, axis_array_table) in model_result.irfs
py_irfs[key] = pytable(axis_array_table)
return PyModelResult(
py_irfs,
model_result.trends,
model_result.stationary_variables,
model_result.estimation,
pytable(model_result.filter),
py_forecast,
pytable(model_result.initial_smoother),
model_result.linearrationalexpectations,
[convert_to_pysimulation(simulation) for simulation in model_result.simulations],
pytable(model_result.smoother),
model_result.solution_derivatives,
# model_result.sparsegrids
)
end
struct PyResults
model_results::Vector{PyModelResult}
end
struct PyContext
symboltable::SymbolTable
models::Vector{Model}
modfileinfo::ModFileInfo
results::PyResults # Now holds PyModelResult instead of ModelResult
work::PyWork
workspaces::Dict
end
function convert_to_pycontext(ctx::Context)::PyContext
# Convert each ModelResults in the Context to PyModelResult
py_model_results = [convert_to_pymodelresult(model_result) for model_result in ctx.results.model_results]
# Create a PyResults structure with the converted PyModelResults
py_results = PyResults(py_model_results)
# Convert the Work structure
py_work = convert_to_pywork(ctx.work)
py_forecast = [pytable(forecast) for forecast in model_result.forecast]
return PyModelResult(
py_irfs,
model_result.trends,
model_result.stationary_variables,
model_result.estimation,
pytable(model_result.filter),
py_forecast,
pytable(model_result.initial_smoother),
model_result.linearrationalexpectations,
[convert_to_pysimulation(simulation) for simulation in model_result.simulations],
pytable(model_result.smoother),
model_result.solution_derivatives,
# model_result.sparsegrids
# Return a new PyContext with PyResults and PyWork
return PyContext(
ctx.symboltable,
ctx.models,
ctx.modfileinfo,
py_results, # PyResults instead of Results
py_work,
ctx.workspaces
)
end
"""
)
end
jl.seval(f'@dynare "{resolved_path}"')
struct PyResults
model_results::Vector{PyModelResult}
end
struct PyContext
symboltable::SymbolTable
models::Vector{Model}
modfileinfo::ModFileInfo
results::PyResults # Now holds PyModelResult instead of ModelResult
work::PyWork
workspaces::Dict
end
function convert_to_pycontext(ctx::Context)::PyContext
# Convert each ModelResults in the Context to PyModelResult
py_model_results = [convert_to_pymodelresult(model_result) for model_result in ctx.results.model_results]
# Create a PyResults structure with the converted PyModelResults
py_results = PyResults(py_model_results)
# Convert the Work structure
py_work = convert_to_pywork(ctx.work)
# Return a new PyContext with PyResults and PyWork
return PyContext(
ctx.symboltable,
ctx.models,
ctx.modfileinfo,
py_results, # PyResults instead of Results
py_work,
ctx.workspaces
jls_path = model.parent / model.stem / "output" / f"{model.stem}.jls"
if not jls_path.exists():
raise DynareError(
f"Model evaluation failed. No JLS file found at {jls_path}"
)
context = jl.seval(
f"""ctx = Serialization.deserialize("{jls_path.resolve()}");
convert_to_pycontext(ctx)"""
)
end
"""
)
context = jl.seval(
f"""ctx = Serialization.deserialize("{jls_path.resolve()}");
convert_to_pycontext(ctx)"""
)
return Context.from_julia(context)
return Context.from_julia(context)
except JuliaError as e:
raise DynareError.from_julia_error(e)
except Exception as e:
raise DynareError(f"An unexpected error occurred: {e}") from e
from typing import Self
class DynareError(Exception):
"""Exception raised for errors occurring during the execution of the dynare Julia command."""
def __init__(self, message):
self.message = message
super().__init__(self.message)
def __str__(self):
return f"DynareError: {self.message}"
@classmethod
def from_julia_error(cls, julia_error) -> Self:
message = f"JuliaError:\n{str(julia_error)}"
return cls(message)
from pathlib import Path
from dynare import dynare
def test_dynare():
print(dynare(Path(__file__).parent.parent / "examples" / "example1pf_bad.mod"))
if __name__ == "__main__":
test_dynare()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment