Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update analytical derivatives #38

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 177 additions & 5 deletions test/test_analytical_derivatives.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,181 @@
using Dynare
module TestDerivatives
using Dynare #Use analytical_derivatives branch
using LinearAlgebra
using FastLapackInterface
using Test
using SparseArrays
using SuiteSparse

#Model
context = @dynare "models/analytical_derivatives/fs2000_sa.mod" "params_derivs_order=1" "notmpterms";

model = context.models[1]
results = context.results.model_results[1]
wss = Dynare.StaticWs(context)
params = context.work.params
steady_state = context.results.model_results[1].trends.endogenous_steady_state
exogenous = context.results.model_results[1].trends.exogenous_steady_state
steadystate = results.trends.endogenous_steady_state
endogenous = repeat(steadystate, 3)
exogenous = results.trends.exogenous_steady_state

#Get StaticJacobian
df_dx = Dynare.get_static_jacobian!(wss, params, steadystate, exogenous, model)

#Get StaticJacobianParams
(df_dp, gp) = Dynare.DFunctions.SparseStaticParametersDerivatives!(steadystate, exogenous, params)


#1. Get Jacobian matrix using the inverse of df_dx
struct DerivativesWorkspace
dense_df_dx::Matrix{Float64}
df_dx_inv::Matrix{Float64}
result::Matrix{Float64}
end

function init_derivatives_workspace(n::Int, m::Int)
dense_df_dx = Matrix{Float64}(undef, n, n)
df_dx_inv = Matrix{Float64}(undef, n, n)
result = Matrix{Float64}(undef, n, m)
return DerivativesWorkspace(dense_df_dx, df_dx_inv, result)
end

function Derivatives(workspace::DerivativesWorkspace, df_dx, df_dp)
dense_df_dx = workspace.dense_df_dx
copyto!(dense_df_dx, df_dx)

if det(dense_df_dx) == 0
error("The matrix is not invertible")
end

df_dx_inv = workspace.df_dx_inv
df_dx_inv .= dense_df_dx \ I

result = workspace.result
return mul!(result, -df_dx_inv, df_dp)
end


#2. Get Jacobian matrix using FastLapackInterface (dense matrix)
struct Derivatives2Workspace
dense_df_dx::Matrix{Float64}
LU_df_dx::LU{Float64, Matrix{Float64}} #not used yet
dx_dp::Matrix{Float64}
end


function init_derivatives2_workspace(n::Int, m::Int)
dense_df_dx = Matrix{Float64}(undef, n, n)
LU_df_dx = lu(Matrix{Float64}(I, n, n)) #not used yet
dx_dp = Matrix{Float64}(undef, n, m)
return Derivatives2Workspace(dense_df_dx, LU_df_dx, dx_dp)
end


function Derivatives2(workspace::Derivatives2Workspace, df_dx, df_dp)
dense_df_dx = workspace.dense_df_dx
copyto!(dense_df_dx, df_dx)

LU_df_dx = LU(LAPACK.getrf!(LUWs(dense_df_dx), dense_df_dx)...)

if any(diag(LU_df_dx.U) .== 0)
error("The matrix is not invertible")
end

dx_dp = workspace.dx_dp
copyto!(dx_dp, -(LU_df_dx \ df_dp))
# ldiv!(-1.0, LU_df_dx, df_dp, dx_dp)

return dx_dp
end

#3. Get Jacobian matrix using lu(df_dx) (sparse matrix)
struct Derivatives3Workspace
# df_dx::SparseMatrixCSC{Float64, Int64}
LU_df_dx::SuiteSparse.UMFPACK.UmfpackLU{Float64, Int64} #not used yet
dx_dp::Matrix{Float64}
end

function init_derivatives3_workspace(n::Int, m::Int)
LU_df_dx = SuiteSparse.UMFPACK.lu(sparse(Matrix{Float64}(I, n, n))) #not used yet
dx_dp = Matrix{Float64}(undef, n, m)
return Derivatives3Workspace(LU_df_dx, dx_dp)
end

function Derivatives3(workspace::Derivatives3Workspace, df_dx, df_dp)
# Perform Sparse LU decomposition
# LU_df_dx = workspace.LU_df_dx
LU_df_dx = lu(df_dx)
# copyto!(LU_df_dx.L, lu_df_dx.L)
# copyto!(LU_df_dx.U, lu_df_dx.U)

if any(diag(LU_df_dx.U) .== 0)
error("The matrix is not invertible")
end

# Get dx_dp
dx_dp = workspace.dx_dp
copyto!(dx_dp, -(LU_df_dx \ df_dp))
# ldiv!(-1.0, LU_df_dx, df_dp, dx_dp)

return dx_dp
end


#4. Get Jacobian matrix using ldiv! (dense matrix)
struct Derivatives4Workspace
dense_df_dx::Matrix{Float64}
LU_df_dx::LU{Float64, Matrix{Float64}}
dx_dp::Matrix{Float64}
end

function init_derivatives4_workspace(n::Int, m::Int)
dense_df_dx = Matrix{Float64}(undef, n, n)
LU_df_dx = lu(Matrix{Float64}(I, n, n))
dx_dp = Matrix{Float64}(undef, n, m)
return Derivatives4Workspace(dense_df_dx, LU_df_dx, dx_dp)
end

function Derivatives4(workspace::Derivatives4Workspace, df_dx, df_dp)
dense_df_dx = workspace.dense_df_dx
copyto!(dense_df_dx, Matrix(df_dx))

# LU_df_dx = workspace.LU_df_dx
LU_df_dx = lu(dense_df_dx)
# copyto!(LU_df_dx.L, lu_df_dx.L)
# copyto!(LU_df_dx.U, lu_df_dx.U)

if any(diag(LU_df_dx.U) .== 0)
error("The matrix is not invertible")
end

dx_dp = workspace.dx_dp
n, m = size(df_dp)

for i in 1:m
dp_col = view(df_dp, :, i)
sol_col = view(dx_dp, :, i)
ldiv!(sol_col, LU_df_dx, dp_col)
sol_col .*= -1
end

return dx_dp
end


# Initialize
n = model.endogenous_nbr
m = model.parameter_nbr
workspace1 = init_derivatives_workspace(n, m)
workspace2 = init_derivatives2_workspace(n, m)
workspace3 = init_derivatives3_workspace(n, m)
workspace4 = init_derivatives4_workspace(n, m)

# Luego puedes utilizar workspace para llamar a la función Derivatives con tus matrices df_dx y df_dp
@time sol1 = Derivatives(workspace1, df_dx, df_dp);
@time sol2 = Derivatives2(workspace2, df_dx, df_dp);
@time sol3 = Derivatives3(workspace3, df_dx, df_dp);
@time sol4 = Derivatives4(workspace4, df_dx, df_dp);

(rp, gp) = Dynare.DFunctions.SparseStaticParametersDerivatives!(steady_state, exogenous, params)
# @show Derivatives(df_dx, df_dp);
@test sol1 ≈ sol2
@test sol1 ≈ sol3
@test sol1 ≈ sol4
end # end module