diff --git a/src/MacroModelling.jl b/src/MacroModelling.jl index 73364fb0e..2c7541784 100644 --- a/src/MacroModelling.jl +++ b/src/MacroModelling.jl @@ -48,7 +48,7 @@ import Subscripts: super, sub import Krylov import Krylov: GmresWorkspace, DqgmresWorkspace, BicgstabWorkspace import LinearOperators -import DataStructures: CircularBuffer +import DataStructures: CircularBuffer, OrderedDict import MacroTools: unblock, postwalk, prewalk, @capture, flatten # import SpeedMapping: speedmapping @@ -121,26 +121,26 @@ const SYMPYWORKSPACE_RESERVED_NAMES = Set([ # Type definitions const Symbol_input = Union{Symbol,Vector{Symbol},Matrix{Symbol},Tuple{Symbol,Vararg{Symbol}}} -const String_input = Union{String,Vector{String},Matrix{String},Tuple{String,Vararg{String}}} +const String_input = Union{S,Vector{S},Matrix{S},Tuple{S,Vararg{S}}} where S <: AbstractString const ParameterType = Union{Nothing, Pair{Symbol, Float64}, - Pair{String, Float64}, + Pair{S, Float64}, Tuple{Pair{Symbol, Float64}, Vararg{Pair{Symbol, Float64}}}, - Tuple{Pair{String, Float64}, Vararg{Pair{String, Float64}}}, + Tuple{Pair{S, Float64}, Vararg{Pair{S, Float64}}}, Vector{Pair{Symbol, Float64}}, - Vector{Pair{String, Float64}}, + Vector{Pair{S, Float64}}, Pair{Symbol, Int}, - Pair{String, Int}, + Pair{S, Int}, Tuple{Pair{Symbol, Int}, Vararg{Pair{Symbol, Int}}}, - Tuple{Pair{String, Int}, Vararg{Pair{String, Int}}}, + Tuple{Pair{S, Int}, Vararg{Pair{S, Int}}}, Vector{Pair{Symbol, Int}}, - Vector{Pair{String, Int}}, + Vector{Pair{S, Int}}, Pair{Symbol, Real}, - Pair{String, Real}, + Pair{S, Real}, Tuple{Pair{Symbol, Real}, Vararg{Pair{Symbol, Real}}}, - Tuple{Pair{String, Real}, Vararg{Pair{String, Real}}}, + Tuple{Pair{S, Real}, Vararg{Pair{S, Real}}}, Vector{Pair{Symbol, Real}}, - Vector{Pair{String, Real}}, + Vector{Pair{S, Real}}, Dict{Symbol, Float64}, Tuple{Int, Vararg{Int}}, Matrix{Int}, @@ -148,7 +148,7 @@ const ParameterType = Union{Nothing, Matrix{Float64}, Tuple{Real, Vararg{Real}}, Matrix{Real}, - Vector{Float64} } + Vector{Float64} } where S <: AbstractString using DispatchDoctor @@ -204,7 +204,7 @@ export Tolerances 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 +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 # Internal export irf, girf @@ -356,6 +356,11 @@ Base.show(io::IO, ๐“‚::โ„ณ) = println(io, else "\nCalibration\nequations: " * repr(length(๐“‚.calibration_equations)) end, + if isempty(๐“‚.missing_parameters) + "" + else + "\n Missing: " * repr(length(๐“‚.missing_parameters)) + end, # "\nยน: including auxiliary variables" # "\nVariable bounds (upper,lower,any): ",sum(๐“‚.upper_bounds .< Inf),", ",sum(๐“‚.lower_bounds .> -Inf),", ",length(๐“‚.bounds), # "\nNon-stochastic-steady-state found: ",!๐“‚.solution.outdated_NSSS @@ -3408,7 +3413,14 @@ function expand_calibration_equations(calibration_equation_parameters::Vector{Sy push!(expanded_ss_var,Symbol(string(ss) * "โ—–" * string(i) * "โ——")) else push!(expanded_ss_var,ss) - push!(expanded_par_var,par_calib_list[u]) + end + end + # Handle parameters from par_calib_list - expand indexed ones, keep non-indexed + for p in par_calib_list[u] + if p โˆˆ indexed_names + push!(expanded_par_var, Symbol(string(p) * "โ—–" * string(i) * "โ——")) + else + push!(expanded_par_var, p) end end push!(expanded_ss_var_list, expanded_ss_var) @@ -6672,7 +6684,17 @@ function solve!(๐“‚::โ„ณ; # @timeit_debug timer "Write parameter inputs" begin + if !๐“‚.solution.functions_written + Core.eval(Main, :(@parameters($(Symbol(๐“‚.model_name)), report_missing_parameters = false, nothing))) + end + write_parameters_input!(๐“‚, parameters, verbose = opts.verbose) + + # Check for missing parameters after processing input + if !isempty(๐“‚.missing_parameters) + error("Cannot solve model: missing parameter values for $(๐“‚.missing_parameters). Provide them via the `parameters` keyword argument (e.g., `parameters = [:ฮฑ => 0.3, :ฮฒ => 0.99]`).") + end + # end # timeit_debug @@ -7897,38 +7919,84 @@ function write_auxiliary_indices!(๐“‚::โ„ณ) end write_parameters_input!(๐“‚::โ„ณ, parameters::Nothing; verbose::Bool = true) = return parameters -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Float64}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{String,Float64}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters[1] |> Meta.parse |> replace_indices => parameters[2]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Float64}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict(parameters), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{S,Float64}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}(parameters[1] |> Meta.parse |> replace_indices => parameters[2]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Float64},Vararg{Pair{Symbol,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) -# write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Union{Symbol,String},Union{Float64,Int}},Vararg{Pair{Union{Symbol,String},Union{Float64,Int}}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) -# write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Int},Vararg{Pair{String,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{String,Float64},Vararg{Pair{String,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Float64},Vararg{Pair{Symbol,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict(parameters), verbose = verbose) +# write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Union{Symbol,AbstractString},Union{Float64,Int}},Vararg{Pair{Union{Symbol,AbstractString},Union{Float64,Int}}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) +# write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Int},Vararg{Pair{AbstractString,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{S,Float64},Vararg{Pair{S,Float64}}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]) , verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Float64}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{String, Float64}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol, Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Float64}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol, Float64}([replace_indices(string(i[1])) => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{S, Float64}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol, Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Int}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{String,Int}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}((parameters[1] |> Meta.parse |> replace_indices) => parameters[2]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Int},Vararg{Pair{Symbol,Int}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{String,Int},Vararg{Pair{String,Int}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Int}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{String, Int}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Int}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(parameters[1])) => parameters[2]]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{S,Int}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}((parameters[1] |> Meta.parse |> replace_indices) => parameters[2]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Int},Vararg{Pair{Symbol,Int}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(i[1])) => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{S,Int},Vararg{Pair{S,Int}}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Int}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(i[1])) => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{S, Int}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Real}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{String,Real}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}((parameters[1] |> Meta.parse |> replace_indices) => parameters[2]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Real},Vararg{Pair{Symbol,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{String,Real},Vararg{Pair{String,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Real}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}(parameters), verbose = verbose) -write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{String, Real}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, Dict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{Symbol,Real}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(parameters[1])) => parameters[2]]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Pair{S,Real}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}((parameters[1] |> Meta.parse |> replace_indices) => parameters[2]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{Symbol,Real},Vararg{Pair{Symbol,Float64}}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(i[1])) => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Tuple{Pair{S,Real},Vararg{Pair{S,Float64}}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{Symbol, Real}}; verbose::Bool = true) = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([replace_indices(string(i[1])) => i[2] for i in parameters]), verbose = verbose) +write_parameters_input!(๐“‚::โ„ณ, parameters::Vector{Pair{S, Real}}; verbose::Bool = true) where S <: AbstractString = write_parameters_input!(๐“‚::โ„ณ, OrderedDict{Symbol,Float64}([i[1] |> Meta.parse |> replace_indices => i[2] for i in parameters]), verbose = verbose) -function write_parameters_input!(๐“‚::โ„ณ, parameters::Dict{Symbol,Float64}; verbose::Bool = true) +function write_parameters_input!(๐“‚::โ„ณ, parameters::OrderedDict{Symbol,Float64}; verbose::Bool = true) + # Handle missing parameters - add them if they are in the missing_parameters list + missing_params_provided = intersect(collect(keys(parameters)), ๐“‚.missing_parameters) + + if !isempty(missing_params_provided) + + # Remove the provided missing params from the missing list + setdiff!(๐“‚.missing_parameters, missing_params_provided) + + # Mark that solution needs to be recomputed + ๐“‚.solution.outdated_NSSS = true + ๐“‚.solution.outdated_algorithms = Set(all_available_algorithms) + + # If all missing parameters are now provided, print a message + if !isempty(๐“‚.missing_parameters) + @info "Remaining missing parameters: ", ๐“‚.missing_parameters + end + + # Amend parameter order by provided missing params + # declared_params = parameters that were never missing (have non-NaN values) + # We identify them as parameters that are not in the union of missing_params_provided and still-missing params + all_missing = union(missing_params_provided, ๐“‚.missing_parameters) + declared_params = setdiff(๐“‚.parameters, all_missing) + + # Get the current parameter values for declared params + declared_param_indices = indexin(declared_params, ๐“‚.parameters) + declared_values = ๐“‚.parameter_values[declared_param_indices] + + # Get values for the newly provided missing params (currently NaN in parameter_values) + # We'll set them later after the bounds check + missing_values = fill(NaN, length(missing_params_provided)) + + # Get values for the remaining missing params (still NaN) + remaining_missing_values = fill(NaN, length(๐“‚.missing_parameters)) + + # Reorder both parameters and parameter_values arrays + ๐“‚.parameters = vcat(declared_params, collect(missing_params_provided), ๐“‚.missing_parameters) + ๐“‚.parameter_values = vcat(declared_values, missing_values, remaining_missing_values) + + # Clear the NSSS_solver_cache since parameter order/count has changed + # It will be rebuilt when solve_steady_state! is called with correct parameter count + while length(๐“‚.NSSS_solver_cache) > 0 + pop!(๐“‚.NSSS_solver_cache) + end + end + + # Handle remaining parameters (not missing ones) if length(setdiff(collect(keys(parameters)),๐“‚.parameters))>0 println("Parameters not part of the model: ",setdiff(collect(keys(parameters)),๐“‚.parameters)) for kk in setdiff(collect(keys(parameters)),๐“‚.parameters) @@ -7956,7 +8024,8 @@ function write_parameters_input!(๐“‚::โ„ณ, parameters::Dict{Symbol,Float64}; ve if bounds_broken println("Parameters unchanged.") else - ntrsct_idx = map(x-> getindex(1:length(๐“‚.parameter_values),๐“‚.parameters .== x)[1],collect(keys(parameters))) + ntrsct_idx = map(x-> getindex(1:length(๐“‚.parameter_values),๐“‚.parameters .== x)[1], collect(keys(parameters))) + # ntrsct_idx = indexin(collect(keys(parameters)), ๐“‚.parameters) if !all(๐“‚.parameter_values[ntrsct_idx] .== collect(values(parameters))) && !(๐“‚.parameters[ntrsct_idx] == [:activeแต’แต‡แถœshocks]) if verbose println("Parameter changes: ") end @@ -7965,7 +8034,7 @@ function write_parameters_input!(๐“‚::โ„ณ, parameters::Dict{Symbol,Float64}; ve for i in 1:length(parameters) if ๐“‚.parameter_values[ntrsct_idx[i]] != collect(values(parameters))[i] - if collect(keys(parameters))[i] โˆˆ ๐“‚.SS_dependencies[end][2] && ๐“‚.solution.outdated_NSSS == false + if !isnothing(๐“‚.SS_dependencies) && collect(keys(parameters))[i] โˆˆ ๐“‚.SS_dependencies[end][2] && ๐“‚.solution.outdated_NSSS == false ๐“‚.solution.outdated_NSSS = true end @@ -7976,6 +8045,84 @@ function write_parameters_input!(๐“‚::โ„ณ, parameters::Dict{Symbol,Float64}; ve end end + if isempty(๐“‚.missing_parameters) + # If SS_solve_func hasn't been created yet (because parameters were provided later), + # create it now with the final parameter order + # Check if SS_solve_func is still the dummy function (x->x) + needs_ss_setup = false + try + # Try calling SS_solve_func with a single argument - the dummy function accepts this + ๐“‚.SS_solve_func([1.0]) + # If we get here, it's the dummy function, so we need to create the real one + needs_ss_setup = true + catch + # If it errors, it's the real function (which requires more arguments) + needs_ss_setup = false + end + + if needs_ss_setup + if verbose println("All parameters now provided. Setting up non-stochastic steady state problem...") end + + # Call solve_steady_state! to create SS_solve_func with the correct parameter order + # Note: solve_steady_state! will initialize NSSS_solver_cache with correct parameter count + solve_steady_state!(๐“‚, verbose = verbose) + + # Also setup OBC violation function + ๐“‚.obc_violation_equations = write_obc_violation_equations(๐“‚) + set_up_obc_violation_function!(๐“‚) + end + + start_time = time() + + opts = merge_calculation_options(verbose = verbose) + + if verbose + print("Find non-stochastic steady state:\t\t\t\t\t") + end + # time_SS_real_solve = @elapsed + SS_and_pars, (solution_error, iters) = ๐“‚.SS_solve_func(๐“‚.parameter_values, ๐“‚, opts.tol, opts.verbose, true, ๐“‚.solver_parameters) + + select_fastest_SS_solver_parameters!(๐“‚, tol = opts.tol) + + found_solution = true + + if solution_error > opts.tol.NSSS_acceptance_tol + # start_time = time() + found_solution = find_SS_solver_parameters!(๐“‚, tol = opts.tol, verbosity = 0, maxtime = 120, maxiter = 10000000) + # println("Find SS solver parameters which solve for the NSSS:\t",round(time() - start_time, digits = 3), " seconds") + if found_solution + SS_and_pars, (solution_error, iters) = ๐“‚.SS_solve_func(๐“‚.parameter_values, ๐“‚, opts.tol, opts.verbose, true, ๐“‚.solver_parameters) + end + end + + if verbose + println(round(time() - start_time, digits = 3), " seconds") + end + + if !found_solution + @warn "Could not find non-stochastic steady state. Consider setting bounds on variables or calibrated parameters in the `@parameters` section (e.g. `k > 10`)." + end + + ๐“‚.solution.non_stochastic_steady_state = SS_and_pars + ๐“‚.solution.outdated_NSSS = false + + start_time = time() + + write_auxiliary_indices!(๐“‚) + + # time_dynamic_derivs = @elapsed + write_functions_mapping!(๐“‚, 1) # Use first order perturbation by default + + ๐“‚.solution.outdated_algorithms = Set(all_available_algorithms) + + ๐“‚.solution.functions_written = true + + if verbose + println(round(time() - start_time, digits = 3), " seconds") + # @info "All missing parameters have been provided. The model can now be solved." + end + end + if ๐“‚.solution.outdated_NSSS == true && verbose println("New parameters changed the steady state.") end return nothing diff --git a/src/inspect.jl b/src/inspect.jl index 0f68fe112..1855ba2c6 100644 --- a/src/inspect.jl +++ b/src/inspect.jl @@ -361,6 +361,82 @@ function get_calibrated_parameters(๐“‚::โ„ณ; values::Bool = false)::Union{Vecto end +""" +$(SIGNATURES) +Returns the parameters which are required by the model but have not been assigned values in the `@parameters` block. These parameters must be provided via the `parameters` keyword argument in functions like `get_irf`, `get_SS`, `simulate`, etc. before the model can be solved. + +# Arguments +- $MODELยฎ + +# Returns +- `Vector{String}` of the missing parameters. + +# Examples +```jldoctest +using MacroModelling + +@model RBC_incomplete begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] +end + +@parameters RBC_incomplete begin + std_z = 0.01 + ฯ = 0.2 + # Note: ฮฑ, ฮฒ, ฮด are not defined +end + +get_missing_parameters(RBC_incomplete) +# output +3-element Vector{String}: + "ฮฑ" + "ฮฒ" + "ฮด" +``` +""" +function get_missing_parameters(๐“‚::โ„ณ)::Vector{String} + replace.(string.(๐“‚.missing_parameters), "โ—–" => "{", "โ——" => "}") +end + + +""" +$(SIGNATURES) +Returns whether the model has missing parameters that need to be provided before solving. + +# Arguments +- $MODELยฎ + +# Returns +- `Bool` indicating whether the model has missing parameters. + +# Examples +```jldoctest +using MacroModelling + +@model RBC begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] +end + +@parameters RBC begin + std_z = 0.01 + ฯ = 0.2 +end + +has_missing_parameters(RBC) +# output +true +``` +""" +function has_missing_parameters(๐“‚::โ„ณ)::Bool + !isempty(๐“‚.missing_parameters) +end + + """ $(SIGNATURES) Returns the parameters contained in the model equations. Note that these parameters might be determined by other parameters or calibration equations defined in the `@parameters` block. diff --git a/src/macros.jl b/src/macros.jl index 5aad8a5cd..c1ad4f396 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -836,6 +836,8 @@ macro model(๐“‚,ex...) $parameters, $parameters, $parameter_values, + + Symbol[], # missing_parameters - to be filled by @parameters Dict{Symbol, Float64}(), # guess @@ -1084,6 +1086,7 @@ macro parameters(๐“‚,ex...) silent = false symbolic = false precompile = false + report_missing_parameters = true perturbation_order = 1 guess = Dict{Symbol,Float64}() simplify = true @@ -1098,6 +1101,8 @@ macro parameters(๐“‚,ex...) verbose = x.args[2] : x.args[1] == :silent && x.args[2] isa Bool ? silent = x.args[2] : + x.args[1] == :report_missing_parameters && x.args[2] isa Bool ? + report_missing_parameters = x.args[2] : x.args[1] == :precompile && x.args[2] isa Bool ? precompile = x.args[2] : x.args[1] == :perturbation_order && x.args[2] isa Int ? @@ -1459,7 +1464,29 @@ macro parameters(๐“‚,ex...) calib_eq_parameters, calib_equations_list, ss_calib_list, par_calib_list = expand_calibration_equations($calib_eq_parameters, $calib_equations_list, $ss_calib_list, $par_calib_list, [mod.$๐“‚.parameters_in_equations; mod.$๐“‚.var]) calib_parameters_no_var, calib_equations_no_var_list = expand_indices($calib_parameters_no_var, $calib_equations_no_var_list, [mod.$๐“‚.parameters_in_equations; mod.$๐“‚.var]) - @assert length(setdiff(setdiff(setdiff(union(reduce(union, par_calib_list,init = []),mod.$๐“‚.parameters_in_equations),calib_parameters),calib_parameters_no_var),calib_eq_parameters)) == 0 "Undefined parameters: " * repr([setdiff(setdiff(setdiff(union(reduce(union,par_calib_list,init = []),mod.$๐“‚.parameters_in_equations),calib_parameters),calib_parameters_no_var),calib_eq_parameters)...]) + # Calculate missing parameters instead of asserting + # Include parameters from: + # 1. par_calib_list - parameters used in calibration equations (e.g., K_ss in "K[ss] = K_ss | beta") + # 2. parameters_in_equations - parameters used in model equations + # 3. par_no_var_calib_list - parameters used in parameter definitions (e.g., rho{H}{H} in "rho{F}{F} = rho{H}{H}") + # Subtract: + # 1. calib_parameters - parameters with explicit values (e.g., "ฮฑ = 0.5") + # 2. calib_parameters_no_var - parameters defined as functions of other parameters (e.g., "ฮฑ = alpha_param") + # 3. calib_eq_parameters - parameters determined by calibration equations (e.g., "beta" in "K[ss] = K_ss | beta") + all_required_params = union( + reduce(union, par_calib_list, init = Set{Symbol}()), + reduce(union, $par_no_var_calib_list, init = Set{Symbol}()), + Set{Symbol}(mod.$๐“‚.parameters_in_equations) + ) + defined_params = union( + Set{Symbol}(calib_parameters), + Set{Symbol}(calib_parameters_no_var), + Set{Symbol}(calib_eq_parameters) + ) + missing_params = collect(setdiff(all_required_params, defined_params)) + mod.$๐“‚.missing_parameters = sort(missing_params) + + has_missing_parameters = length(missing_params) > 0 for (k,v) in $bounds mod.$๐“‚.bounds[k] = haskey(mod.$๐“‚.bounds, k) ? (max(mod.$๐“‚.bounds[k][1], v[1]), min(mod.$๐“‚.bounds[k][2], v[2])) : (v[1], v[2]) @@ -1481,8 +1508,13 @@ macro parameters(๐“‚,ex...) mod.$๐“‚.ss_no_var_calib_list = $ss_no_var_calib_list mod.$๐“‚.par_no_var_calib_list = $par_no_var_calib_list - mod.$๐“‚.parameters = calib_parameters - mod.$๐“‚.parameter_values = calib_values + # Keep calib_parameters in declaration order, append missing_params at end + # This preserves declaration order for estimation and method of moments + all_params = vcat(calib_parameters, missing_params) + all_values = vcat(calib_values, fill(NaN, length(missing_params))) + + mod.$๐“‚.parameters = all_params + mod.$๐“‚.parameter_values = all_values mod.$๐“‚.calibration_equations = calib_equations_list mod.$๐“‚.parameters_as_function_of_parameters = calib_parameters_no_var mod.$๐“‚.calibration_equations_no_var = calib_equations_no_var_list @@ -1505,102 +1537,111 @@ macro parameters(๐“‚,ex...) # time_symbolics = @elapsed # time_rm_red_SS_vars = @elapsed - if !$precompile - start_time = time() + if !has_missing_parameters + if !$precompile + start_time = time() - if !$silent print("Remove redundant variables in non-stochastic steady state problem:\t") end + if !$silent print("Remove redundant variables in non-stochastic steady state problem:\t") end - symbolics = create_symbols_eqs!(mod.$๐“‚) + symbolics = create_symbols_eqs!(mod.$๐“‚) - remove_redundant_SS_vars!(mod.$๐“‚, symbolics, avoid_solve = !$simplify) + remove_redundant_SS_vars!(mod.$๐“‚, symbolics, avoid_solve = !$simplify) - if !$silent println(round(time() - start_time, digits = 3), " seconds") end + if !$silent println(round(time() - start_time, digits = 3), " seconds") end - start_time = time() - - if !$silent print("Set up non-stochastic steady state problem:\t\t\t\t") end + start_time = time() + + if !$silent print("Set up non-stochastic steady state problem:\t\t\t\t") end - solve_steady_state!(mod.$๐“‚, $symbolic, symbolics, verbose = $verbose, avoid_solve = !$simplify) # 2nd argument is SS_symbolic + solve_steady_state!(mod.$๐“‚, $symbolic, symbolics, verbose = $verbose, avoid_solve = !$simplify) # 2nd argument is SS_symbolic - mod.$๐“‚.obc_violation_equations = write_obc_violation_equations(mod.$๐“‚) - - set_up_obc_violation_function!(mod.$๐“‚) + mod.$๐“‚.obc_violation_equations = write_obc_violation_equations(mod.$๐“‚) + + set_up_obc_violation_function!(mod.$๐“‚) - if !$silent println(round(time() - start_time, digits = 3), " seconds") end - else - start_time = time() - - if !$silent print("Set up non-stochastic steady state problem:\t\t\t\t") end + if !$silent println(round(time() - start_time, digits = 3), " seconds") end + else + start_time = time() + + if !$silent print("Set up non-stochastic steady state problem:\t\t\t\t") end - solve_steady_state!(mod.$๐“‚, verbose = $verbose) + solve_steady_state!(mod.$๐“‚, verbose = $verbose) - if !$silent println(round(time() - start_time, digits = 3), " seconds") end + if !$silent println(round(time() - start_time, digits = 3), " seconds") end + end end - - start_time = time() - + + # Mark functions as written even if we skipped SS setup due to missing parameters + # This prevents solve! from re-running @parameters with nothing mod.$๐“‚.solution.functions_written = true - opts = merge_calculation_options(verbose = $verbose) + if !has_missing_parameters + start_time = time() + + opts = merge_calculation_options(verbose = $verbose) - if !$precompile - if !$silent - print("Find non-stochastic steady state:\t\t\t\t\t") - end - # time_SS_real_solve = @elapsed - SS_and_pars, (solution_error, iters) = mod.$๐“‚.SS_solve_func(mod.$๐“‚.parameter_values, mod.$๐“‚, opts.tol, opts.verbose, true, mod.$๐“‚.solver_parameters) + if !$precompile + if !$silent + print("Find non-stochastic steady state:\t\t\t\t\t") + end + # time_SS_real_solve = @elapsed + SS_and_pars, (solution_error, iters) = mod.$๐“‚.SS_solve_func(mod.$๐“‚.parameter_values, mod.$๐“‚, opts.tol, opts.verbose, true, mod.$๐“‚.solver_parameters) - select_fastest_SS_solver_parameters!(mod.$๐“‚, tol = opts.tol) + select_fastest_SS_solver_parameters!(mod.$๐“‚, tol = opts.tol) - found_solution = true + found_solution = true - if solution_error > opts.tol.NSSS_acceptance_tol - # start_time = time() - found_solution = find_SS_solver_parameters!(mod.$๐“‚, tol = opts.tol, verbosity = 0, maxtime = 120, maxiter = 10000000) - # println("Find SS solver parameters which solve for the NSSS:\t",round(time() - start_time, digits = 3), " seconds") - if found_solution - SS_and_pars, (solution_error, iters) = mod.$๐“‚.SS_solve_func(mod.$๐“‚.parameter_values, mod.$๐“‚, opts.tol, opts.verbose, true, mod.$๐“‚.solver_parameters) + if solution_error > opts.tol.NSSS_acceptance_tol + # start_time = time() + found_solution = find_SS_solver_parameters!(mod.$๐“‚, tol = opts.tol, verbosity = 0, maxtime = 120, maxiter = 10000000) + # println("Find SS solver parameters which solve for the NSSS:\t",round(time() - start_time, digits = 3), " seconds") + if found_solution + SS_and_pars, (solution_error, iters) = mod.$๐“‚.SS_solve_func(mod.$๐“‚.parameter_values, mod.$๐“‚, opts.tol, opts.verbose, true, mod.$๐“‚.solver_parameters) + end end + + if !$silent + println(round(time() - start_time, digits = 3), " seconds") + end + + if !found_solution + @warn "Could not find non-stochastic steady state. Consider setting bounds on variables or calibrated parameters in the `@parameters` section (e.g. `k > 10`)." + end + + mod.$๐“‚.solution.non_stochastic_steady_state = SS_and_pars + mod.$๐“‚.solution.outdated_NSSS = false end - if !$silent - println(round(time() - start_time, digits = 3), " seconds") - end + start_time = time() - if !found_solution - @warn "Could not find non-stochastic steady state. Consider setting bounds on variables or calibrated parameters in the `@parameters` section (e.g. `k > 10`)." + if !$silent + if $perturbation_order == 1 + print("Take symbolic derivatives up to first order:\t\t\t\t") + elseif $perturbation_order == 2 + print("Take symbolic derivatives up to second order:\t\t\t\t") + elseif $perturbation_order == 3 + print("Take symbolic derivatives up to third order:\t\t\t\t") + end end - mod.$๐“‚.solution.non_stochastic_steady_state = SS_and_pars - mod.$๐“‚.solution.outdated_NSSS = false - end - + write_auxiliary_indices!(mod.$๐“‚) - start_time = time() + # time_dynamic_derivs = @elapsed + write_functions_mapping!(mod.$๐“‚, $perturbation_order) - if !$silent - if $perturbation_order == 1 - print("Take symbolic derivatives up to first order:\t\t\t\t") - elseif $perturbation_order == 2 - print("Take symbolic derivatives up to second order:\t\t\t\t") - elseif $perturbation_order == 3 - print("Take symbolic derivatives up to third order:\t\t\t\t") + mod.$๐“‚.solution.outdated_algorithms = Set(all_available_algorithms) + + if !$silent + println(round(time() - start_time, digits = 3), " seconds") end end - write_auxiliary_indices!(mod.$๐“‚) - - # time_dynamic_derivs = @elapsed - write_functions_mapping!(mod.$๐“‚, $perturbation_order) - - mod.$๐“‚.solution.outdated_algorithms = Set(all_available_algorithms) - - if !$silent - println(round(time() - start_time, digits = 3), " seconds") + if has_missing_parameters && $report_missing_parameters + @warn "Model has been set up with incomplete parameter definitions. Missing parameters: $(missing_params). The non-stochastic steady state and perturbation solution cannot be computed until all parameters are defined. Provide missing parameter values via the `parameters` keyword argument in functions like `get_irf`, `get_SS`, `simulate`, etc." end - if !$silent Base.show(mod.$๐“‚) end + if !$silent && $report_missing_parameters Base.show(mod.$๐“‚) end nothing end end \ No newline at end of file diff --git a/src/structures.jl b/src/structures.jl index 4ef32a2b6..c2f17f6d7 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -289,6 +289,8 @@ mutable struct โ„ณ parameters_as_function_of_parameters::Vector{Symbol} parameters::Vector{Symbol} parameter_values::Vector{Float64} + + missing_parameters::Vector{Symbol} guess::Dict{Symbol, Float64} diff --git a/test/runtests.jl b/test/runtests.jl index 68a304d75..856cd0195 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -793,6 +793,208 @@ if test_set == "basic" plots = false # test_higher_order = false + @testset verbose = true "Provide parameters later" begin + include("models/Backus_Kehoe_Kydland_1992.jl") + + @model Backus_Kehoe_Kydland_1992_incomplete begin + for co in [H, F] + Y{co}[0] = ((LAMBDA{co}[0] * K{co}[-4]^theta{co} * N{co}[0]^(1-theta{co}))^(-nu{co}) + sigma{co} * Z{co}[-1]^(-nu{co}))^(-1/nu{co}) + + K{co}[0] = (1-delta{co})*K{co}[-1] + S{co}[0] + + X{co}[0] = for lag in (-4+1):0 phi{co} * S{co}[lag] end + + A{co}[0] = (1-eta{co}) * A{co}[-1] + N{co}[0] + + L{co}[0] = 1 - alpha{co} * N{co}[0] - (1-alpha{co})*eta{co} * A{co}[-1] + + U{co}[0] = (C{co}[0]^mu{co}*L{co}[0]^(1-mu{co}))^gamma{co} + + psi{co} * mu{co} / C{co}[0]*U{co}[0] = LGM[0] + + psi{co} * (1-mu{co}) / L{co}[0] * U{co}[0] * (-alpha{co}) = - LGM[0] * (1-theta{co}) / N{co}[0] * (LAMBDA{co}[0] * K{co}[-4]^theta{co}*N{co}[0]^(1-theta{co}))^(-nu{co})*Y{co}[0]^(1+nu{co}) + + for lag in 0:(4-1) + beta{co}^lag * LGM[lag]*phi{co} + end + + for lag in 1:4 + -beta{co}^lag * LGM[lag] * phi{co} * (1-delta{co}) + end = beta{co}^4 * LGM[+4] * theta{co} / K{co}[0] * (LAMBDA{co}[+4] * K{co}[0]^theta{co} * N{co}[+4]^(1-theta{co})) ^ (-nu{co})* Y{co}[+4]^(1+nu{co}) + + LGM[0] = beta{co} * LGM[+1] * (1+sigma{co} * Z{co}[0]^(-nu{co}-1)*Y{co}[+1]^(1+nu{co})) + + NX{co}[0] = (Y{co}[0] - (C{co}[0] + X{co}[0] + Z{co}[0] - Z{co}[-1]))/Y{co}[0] + end + + (LAMBDA{H}[0]-1) = rho{H}{H}*(LAMBDA{H}[-1]-1) + rho{H}{F}*(LAMBDA{F}[-1]-1) + Z_E{H} * E{H}[x] + + (LAMBDA{F}[0]-1) = rho{F}{F}*(LAMBDA{F}[-1]-1) + rho{F}{H}*(LAMBDA{H}[-1]-1) + Z_E{F} * E{F}[x] + + for co in [H,F] C{co}[0] + X{co}[0] + Z{co}[0] - Z{co}[-1] end = for co in [H,F] Y{co}[0] end + + dLGM[0] = LGM[1] / LGM[0] + + dLGM_ann[0] = for operator = :*, lag in -3:0 dLGM[lag] end + end + + @parameters Backus_Kehoe_Kydland_1992_incomplete begin + # K_ss = 11.0148 + # K[ss] = K_ss | beta + # K[ss] = 10 | beta + # F_H_ratio = 1 + K{F}[ss] / K{H}[ss] = F_H_ratio | beta{F} + K{H}[ss] = K_ss | beta{H} + + # beta = 0.99 + # mu = 0.34 + # gamma = -1.0 + # alpha = 1 + # eta = 0.5 + # theta = 0.36 + # nu = 3 + # sigma = 0.01 + # delta = 0.025 + # phi = 1/4 + # psi = 0.5 + + # Z_E = 0.00852 + + # rho{H}{H} = 0.906 + rho{F}{F} = rho{H}{H} + # rho{H}{F} = 0.088 + rho{F}{H} = rho{H}{F} + end + + pars = [ + "F_H_ratio" => 1.0, + "K_ss" => 11.0148, + "Z_E{F}" => 0.00852, + "Z_E{H}" => 0.00852, + "alpha{F}" => 1.0, + "alpha{H}" => 1.0, + "delta{F}" => 0.025, + "delta{H}" => 0.025, + "eta{F}" => 0.5, + "eta{H}" => 0.5, + "gamma{F}" => -1.0, + "gamma{H}" => -1.0, + "mu{F}" => 0.34, + "mu{H}" => 0.34, + "nu{F}" => 3.0, + "nu{H}" => 3.0, + "phi{F}" => 0.25, + "phi{H}" => 0.25, + "psi{F}" => 0.5, + "psi{H}" => 0.5, + "rho{H}{F}" => 0.088, + "rho{H}{H}" => 0.906, + "sigma{F}" => 0.01, + "sigma{H}" => 0.01, + "theta{F}" => 0.36, + "theta{H}" => 0.36 + ] + + cov1 = get_cov(Backus_Kehoe_Kydland_1992_incomplete, parameters = pars) + + cov2 = get_cov(Backus_Kehoe_Kydland_1992) + + @test cov1 โ‰ˆ cov2 + + + include("../models/Gali_2015_chapter_3_obc.jl") + + @model Gali_2015_chapter_3_obc_incomplete begin + W_real[0] = C[0] ^ ฯƒ * N[0] ^ ฯ† + + Q[0] = ฮฒ * (C[1] / C[0]) ^ (-ฯƒ) * Z[1] / Z[0] / Pi[1] + + R[0] = 1 / Q[0] + + Y[0] = A[0] * (N[0] / S[0]) ^ (1 - ฮฑ) + + R[0] = Pi[1] * realinterest[0] + + R[0] = max(Rฬ„ , 1 / ฮฒ * Pi[0] ^ ฯ•แต–โฑ * (Y[0] / Y[ss]) ^ ฯ•สธ * exp(nu[0])) + + C[0] = Y[0] + + log(A[0]) = ฯ_a * log(A[-1]) + std_a * eps_a[x] + + log(Z[0]) = ฯ_z * log(Z[-1]) - std_z * eps_z[x] + + nu[0] = ฯ_ฮฝ * nu[-1] + std_nu * eps_nu[x] + + MC[0] = W_real[0] / (S[0] * Y[0] * (1 - ฮฑ) / N[0]) + + 1 = ฮธ * Pi[0] ^ (ฯต - 1) + (1 - ฮธ) * Pi_star[0] ^ (1 - ฯต) + + S[0] = (1 - ฮธ) * Pi_star[0] ^ (( - ฯต) / (1 - ฮฑ)) + ฮธ * Pi[0] ^ (ฯต / (1 - ฮฑ)) * S[-1] + + Pi_star[0] ^ (1 + ฯต * ฮฑ / (1 - ฮฑ)) = ฯต * x_aux_1[0] / x_aux_2[0] * (1 - ฯ„) / (ฯต - 1) + + x_aux_1[0] = MC[0] * Y[0] * Z[0] * C[0] ^ (-ฯƒ) + ฮฒ * ฮธ * Pi[1] ^ (ฯต + ฮฑ * ฯต / (1 - ฮฑ)) * x_aux_1[1] + + x_aux_2[0] = Y[0] * Z[0] * C[0] ^ (-ฯƒ) + ฮฒ * ฮธ * Pi[1] ^ (ฯต - 1) * x_aux_2[1] + + log_y[0] = log(Y[0]) + + log_W_real[0] = log(W_real[0]) + + log_N[0] = log(N[0]) + + pi_ann[0] = 4 * log(Pi[0]) + + i_ann[0] = 4 * log(R[0]) + + r_real_ann[0] = 4 * log(realinterest[0]) + + M_real[0] = Y[0] / R[0] ^ ฮท + + end + + @parameters Gali_2015_chapter_3_obc_incomplete begin + ฯƒ = 1 + + ฯ† = 5 + + ฯ•แต–โฑ = 1.5 + + ฯ•สธ = 0.125 + + ฮธ = 0.75 + + ฯ_ฮฝ = 0.5 + + ฯ_z = 0.5 + + ฯ_a = 0.9 + + ฮฒ = 0.99 + + ฮท = 3.77 + + ฮฑ = 0.25 + + ฯต = 9 + + ฯ„ = 0 + + std_a = .01 + + std_z = .05 + + std_nu = .0025 + + R > 1.0001 + end + + cov1 = get_cov(Gali_2015_chapter_3_obc_incomplete, parameters = :Rฬ„ => 1.0) + + cov2 = get_cov(Gali_2015_chapter_3_obc) + + @test cov1 โ‰ˆ cov2 + end + @testset verbose = true "Code quality (Aqua.jl)" begin # Aqua.test_all(MacroModelling) @testset "Compare Project.toml and test/Project.toml" Aqua.test_project_extras(MacroModelling) diff --git a/test/test_incomplete_paras.jl b/test/test_incomplete_paras.jl new file mode 100644 index 000000000..97cada7b3 --- /dev/null +++ b/test/test_incomplete_paras.jl @@ -0,0 +1,26 @@ +# using Pkg; Pkg.offline(true) +using Revise +using MacroModelling + +@model RBC_incomplete begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] +end + +@parameters RBC_incomplete begin + std_z = 0.01 + ฯ = 0.2 + ฮฑ = 0.3 + ฮฒ = 0.99 + ฮด = 0.025 + # Note: ฮฑ, ฮฒ, ฮด are not defined +end + +RBC_incomplete.parameters +get_missing_parameters(RBC_incomplete) + +get_irf(RBC_incomplete, parameters = [:ฮฑ => 0.3, :ฮฒ => 0.99, :ฮด => 0.025]) + +RBC_incomplete.SS_solve_func \ No newline at end of file diff --git a/test/test_missing_parameters.jl b/test/test_missing_parameters.jl new file mode 100644 index 000000000..6eb9067f1 --- /dev/null +++ b/test/test_missing_parameters.jl @@ -0,0 +1,189 @@ +# Test file for missing parameters functionality + +using Test +using MacroModelling + +@testset verbose = true "Missing Parameters Functionality" begin + + @testset "Model with missing parameters - basic functionality" begin + # Define a model with equations that use parameters not yet defined + @model RBC_missing_params begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + # Only define some parameters - ฮฑ, ฮฒ, ฮด are missing + @parameters RBC_missing_params begin + std_z = 0.01 + ฯ = 0.2 + end + + # Test that missing parameters are correctly identified + @test has_missing_parameters(RBC_missing_params) + @test sort(get_missing_parameters(RBC_missing_params)) == sort(["ฮฑ", "ฮฒ", "ฮด"]) + @test length(RBC_missing_params.missing_parameters) == 3 + + # Test that get_irf throws an error when parameters are missing + @test_throws ErrorException get_irf(RBC_missing_params) + + # Test that get_SS throws an error when parameters are missing + @test_throws ErrorException get_SS(RBC_missing_params) + + # Test that simulate throws an error when parameters are missing + @test_throws ErrorException simulate(RBC_missing_params) + end + + @testset "Model with missing parameters - providing parameters via keyword" begin + # Define a model with missing parameters + @model RBC_missing_provide begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + # Only define some parameters + @parameters RBC_missing_provide begin + std_z = 0.01 + ฯ = 0.2 + end + + # Verify parameters are missing before providing them + @test has_missing_parameters(RBC_missing_provide) + + # Provide missing parameters and get IRF + irf_result = get_irf(RBC_missing_provide, parameters = [:ฮฑ => 0.5, :ฮฒ => 0.95, :ฮด => 0.02]) + + # After providing parameters, they should no longer be missing + @test !has_missing_parameters(RBC_missing_provide) + @test isempty(get_missing_parameters(RBC_missing_provide)) + + # The IRF should be a valid KeyedArray + @test irf_result isa KeyedArray + @test size(irf_result, 2) > 0 # Should have some time periods + end + + @testset "Model with all parameters defined" begin + # Define a complete model + @model RBC_complete begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + # Define all parameters + @parameters RBC_complete begin + std_z = 0.01 + ฯ = 0.2 + ฮด = 0.02 + ฮฑ = 0.5 + ฮฒ = 0.95 + end + + # Test that no parameters are missing + @test !has_missing_parameters(RBC_complete) + @test isempty(get_missing_parameters(RBC_complete)) + + # Model should solve without issues + irf_result = get_irf(RBC_complete) + @test irf_result isa KeyedArray + end + + @testset "Model with partial parameter provision" begin + # Define a model with missing parameters + @model RBC_partial begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + # Only define some parameters + @parameters RBC_partial begin + std_z = 0.01 + ฯ = 0.2 + end + + # Provide only some of the missing parameters - should still fail + @test_throws ErrorException get_irf(RBC_partial, parameters = [:ฮฑ => 0.5]) + + # Verify that ฮฑ was added but ฮฒ and ฮด are still missing + @test has_missing_parameters(RBC_partial) + remaining = get_missing_parameters(RBC_partial) + @test "ฮฑ" โˆ‰ remaining + @test "ฮฒ" โˆˆ remaining + @test "ฮด" โˆˆ remaining + end + + @testset "Model with missing parameters - get_SS" begin + # Define a model with missing parameters + @model RBC_ss_test begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + @parameters RBC_ss_test begin + std_z = 0.01 + ฯ = 0.2 + end + + # Provide missing parameters and get steady state + ss_result = get_SS(RBC_ss_test, parameters = [:ฮฑ => 0.5, :ฮฒ => 0.95, :ฮด => 0.02]) + + # Steady state should be computed + @test ss_result isa KeyedArray + @test !has_missing_parameters(RBC_ss_test) + end + + @testset "Model with missing parameters - simulation" begin + # Define a model with missing parameters + @model RBC_sim_test begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + @parameters RBC_sim_test begin + std_z = 0.01 + ฯ = 0.2 + end + + # Provide missing parameters and simulate + sim_result = simulate(RBC_sim_test, parameters = [:ฮฑ => 0.5, :ฮฒ => 0.95, :ฮด => 0.02]) + + # Simulation should work + @test sim_result isa KeyedArray + @test !has_missing_parameters(RBC_sim_test) + end + + @testset "Model display with missing parameters" begin + # Define a model with missing parameters + @model RBC_display begin + 1 / c[0] = (ฮฒ / c[1]) * (ฮฑ * exp(z[1]) * k[0]^(ฮฑ - 1) + (1 - ฮด)) + c[0] + k[0] = (1 - ฮด) * k[-1] + q[0] + q[0] = exp(z[0]) * k[-1]^ฮฑ + z[0] = ฯ * z[-1] + std_z * eps_z[x] + end + + @parameters RBC_display begin + std_z = 0.01 + ฯ = 0.2 + end + + # Capture the model display + io = IOBuffer() + show(io, RBC_display) + display_str = String(take!(io)) + + # Check that missing parameters are shown + @test occursin("Missing", display_str) + # The display shows " Missing: N" where N is the count + @test occursin("3", display_str) # 3 missing: ฮฑ, ฮฒ, ฮด + end +end