-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from JohannesNaegele/dev
Implemented GROWTH solution with NonlinearSolve.jl, implemented adjoint functionality
- Loading branch information
Showing
14 changed files
with
740 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using Consistent | ||
using Distributions | ||
using DataFrames | ||
using Pipe | ||
using Gadfly | ||
using Random | ||
# using Zygote | ||
# using Optimization | ||
# using OptimizationOptimJL | ||
# using SciMLSensitivity | ||
|
||
# Given data for exogenous variables | ||
df = DataFrame(:G => fill(20, 60)) | ||
|
||
# Define model | ||
sim = Consistent.SIM()[:model] | ||
exos = permutedims(Matrix(df[!, sim.exogenous_variables])) | ||
lags = Consistent.SIM()[:lags] | ||
params_dict = @parameters begin | ||
θ = 0.2 | ||
α_1 = Normal(0.6, 0.01) | ||
α_2 = Normal(0.4, 0.01) | ||
end | ||
priors_dict = @parameters begin | ||
α_1 = Uniform() | ||
α_2 = Uniform() | ||
end | ||
|
||
# let's say we know that some kind of variables is prone to measurement error | ||
# if it is just a constant, we can introduce some bias variable | ||
|
||
function solve( | ||
model, lags, exos, params_dict::AbstractDict; | ||
rng=Random.default_rng(), | ||
initial=fill(1.0, length(model.endogenous_variables)) | ||
) | ||
function sample_param(x) | ||
if x isa Number | ||
return x | ||
else | ||
return rand(rng, x) | ||
end | ||
end | ||
param_values = map(x -> sample_param(params_dict[x]), sim.parameters) | ||
return Consistent.solve( | ||
sim, lags, exos, param_values, initial=initial | ||
) | ||
end | ||
|
||
# Solve model for 59 periods | ||
for i in 1:59 | ||
solution = solve( | ||
sim, | ||
lags, | ||
exos[:, begin:i], | ||
params_dict | ||
) | ||
lags = hcat(lags, solution) | ||
end | ||
|
||
# Convert results to DataFrame | ||
df = DataFrame(lags', sim.endogenous_variables) | ||
# Add time column | ||
df[!, :period] = 1:nrow(df) | ||
# Select variables, convert to long format, and plot variables | ||
@pipe df |> | ||
select(_, [:Y, :C, :YD, :period]) |> | ||
stack(_, Not(:period), variable_name=:variable) |> | ||
plot( | ||
_, | ||
x=:period, | ||
y=:value, | ||
color=:variable, | ||
Geom.line | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,100 @@ | ||
using Consistent | ||
using DataFrames | ||
using ForwardDiff | ||
using Pipe | ||
using Gadfly | ||
using Zygote | ||
using Optimization | ||
using OptimizationOptimJL | ||
using SciMLSensitivity | ||
|
||
function calibrate!(data, model, parameters=Dict()) | ||
x -> model.f!(y, x, ...) + x | ||
function loss(data,) | ||
# Given data for exogenous variables | ||
df = DataFrame(:G => 20 .+ 10 * rand(60)) | ||
|
||
# Define model | ||
sim = Consistent.SIM()[:model] | ||
exos = permutedims(Matrix(df[!, sim.exogenous_variables])) | ||
lags = Consistent.SIM()[:lags] | ||
params_dict = Consistent.SIM()[:params] | ||
param_values = map(x -> params_dict[x], sim.parameters) | ||
|
||
# Solve model for 59 periods | ||
for i in 1:59 | ||
# assume we have some randomness in our parameters | ||
solution = Consistent.solve(sim, lags, exos[:, begin:i], param_values + 0.05 * rand(3)) | ||
lags = hcat(lags, solution) | ||
end | ||
|
||
# Convert results to DataFrame | ||
df = hcat(df, DataFrame(lags', sim.endogenous_variables)) | ||
|
||
function calibrate(data, model, opt_vars, opt_params, init_params) | ||
# Set up variables | ||
exos = permutedims(Matrix(data[!, model.exogenous_variables])) | ||
reference_results = permutedims(Matrix(data[!, model.endogenous_variables])) | ||
results = Matrix(data[!, model.endogenous_variables])' # lags in | ||
results[:, 1] = Vector(data[1, model.endogenous_variables])' | ||
param_values = map(x -> init_params[x], model.parameters) | ||
param_opt_inidices = Int[] | ||
for (i, p) in enumerate(model.parameters) | ||
if p in opt_params | ||
push!(param_opt_inidices, i) | ||
end | ||
end | ||
var_opt_indices = Int[] | ||
for (i, var) in enumerate(model.endogenous_variables) | ||
if var in opt_vars | ||
push!(var_opt_indices, i) | ||
end | ||
end | ||
ForwardDiff.derivative(() -> loss, input) | ||
initial = map(x -> init_params[x], opt_params) | ||
|
||
horizon = 2:(nrow(data)) | ||
# println(horizon) | ||
|
||
# Define loss function | ||
function loss(p) | ||
pvalues = Zygote.Buffer(param_values) | ||
pvalues[1:length(param_values)] = param_values | ||
pvalues[param_opt_inidices] = p | ||
bresults = Zygote.Buffer(results) | ||
bresults[:] = results | ||
# sol = prognose!(bresults, horizon, model, exos, copy(pvalues); method=:broyden) | ||
sol = onestep_prognose!(bresults, results, horizon, model, exos, copy(pvalues); method=:broyden) | ||
# println(copy(bresults)) | ||
# println(copy(pvalues)) | ||
if sol == ReturnCode.Failure | ||
return Inf | ||
else | ||
# println(copy(bresults[:, 2:end])) | ||
return Consistent.msrmse(reference_results[:, 2:end], copy(bresults[:, 2:end])) | ||
end | ||
end | ||
|
||
# Numerical optimization | ||
adtype = Optimization.AutoZygote() | ||
optf = Optimization.OptimizationFunction((x, p) -> loss(x), adtype) | ||
optprob = Optimization.OptimizationProblem(optf, initial, lb = [0.0, 0.0], ub = [1.0, 1.0]) | ||
res = Optimization.solve(optprob, SAMIN(), maxiters = 50000) | ||
return res | ||
end | ||
|
||
model = SIM() | ||
df = DataFrame(reverse(lags[:, end-20:end-1], dims=2)', model.endogenous_variables) | ||
df | ||
sol = calibrate(df, sim, [:Y, :C], [:α_1, :α_2], Dict(:α_1 => 0.5, :α_2 => 0.5, :θ => 0.2)) | ||
|
||
# Compare to fit | ||
fitted = deepcopy(lags) | ||
prognose!(fitted, 2:60, sim, exos, vcat(0.2, sol.u); method=:broyden) | ||
# prognose!(fitted, 2:60, sim, exos, vcat(0.2, [0.6, 0.4]); method=:broyden) | ||
|
||
df_fitted = DataFrame(fitted', sim.endogenous_variables) | ||
df_fitted[!, :period] = 1:nrow(df) | ||
df_all = hcat(df, df_fitted, makeunique=true) | ||
@pipe df_all |> | ||
select(_, [:Y, :Y_1, :C, :C_1, :period]) |> | ||
stack(_, Not(:period), variable_name=:variable) |> | ||
plot( | ||
_, | ||
x=:period, | ||
y=:value, | ||
color=:variable, | ||
Geom.line | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
using Statistics | ||
|
||
""" | ||
Root-mean-square error of a time series. | ||
""" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
function prognose!(results, horizon, model, exos, param_values; method=TrustMethod()) | ||
for i in horizon | ||
sol = Consistent.solve_nonlinear(model, results[:, begin:i-1], exos, param_values, initial=results[:, i-1]) | ||
if sol.retcode == ReturnCode.Failure | ||
return ReturnCode.Failure | ||
end | ||
results[:, i] = sol.u | ||
end | ||
return ReturnCode.Success | ||
end | ||
|
||
function onestep_prognose!(results, reference_results, horizon, model, exos, param_values; method=TrustMethod()) | ||
for i in horizon | ||
sol = Consistent.solve_nonlinear(model, reference_results[:, begin:i-1], exos, param_values, initial=reference_results[:, i-1]) | ||
if sol.retcode == ReturnCode.Failure | ||
return ReturnCode.Failure | ||
end | ||
results[:, i] = sol.u | ||
end | ||
return ReturnCode.Success | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,38 @@ | ||
using NLsolve | ||
using LinearAlgebra | ||
import Optimization | ||
using NonlinearSolve | ||
|
||
function solve(model, lags, exos, params, initial=fill(1.0, length(model.endogenous_variables))) | ||
function solve(model, lags, exos, params; initial=fill(1.0, length(model.endogenous_variables)), method=:newton) | ||
nlsolve( | ||
(x, y) -> model.f!(x, y, lags, exos, params), | ||
(F, x) -> model.f!(F, x, lags, exos, params), | ||
initial, | ||
autodiff = :forward, | ||
autodiff=:forward, | ||
method=method, | ||
# iterations = 500, | ||
ftol=1e-40, | ||
xtol=1e-40 | ||
).zero | ||
end | ||
end | ||
|
||
function solve_nonlinear(model, lags, exos, params; initial=fill(1.0, length(model.endogenous_variables)), method=TrustRegion()) | ||
prob = NonlinearProblem( | ||
(F, x, p) -> model.f!(F, x, lags, exos, p), | ||
initial, | ||
params, | ||
abstol=1e-40, | ||
reltol=1e-40 | ||
) | ||
sol = NonlinearSolve.solve(prob, method) | ||
return sol | ||
end | ||
|
||
# function solve_optim(model, lags, exos, params; initial=fill(1.0, length(model.endogenous_variables)), method=:newton) | ||
# f = (x, y) -> model.f!(x, y, lags, exos, params) | ||
# prob = Optimization.OptimizationProblem( | ||
# x -> max(norm(f(x, y)) + abs(f(x, y)[1] - f(x, y)[1])), | ||
# initial, | ||
# Optimization.AutoForwardDiff() | ||
# ) | ||
# sol = Optimization.solve(prob, Optim.BFGS()) | ||
# end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.