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

Simple Firm Investment Problem #27

Closed
azev77 opened this issue Dec 17, 2021 · 3 comments
Closed

Simple Firm Investment Problem #27

azev77 opened this issue Dec 17, 2021 · 3 comments

Comments

@azev77
Copy link

azev77 commented Dec 17, 2021

Consider the simple problem (w/ closed form solution):
image

I tried coding it up following your investment example:

using EconPDEs
stategrid = OrderedDict(:k => range(0.0, 5.0, length = 1000))
solend = OrderedDict(:V => ones(1000))
function f(state::NamedTuple, sol::NamedTuple)
    r = 0.05; δ = 0.10; z = 0.20;
    k = state.k
    V, Vk_up, Vk_down, Vkk = sol.V, sol.Vk_up, sol.Vk_down, sol.Vkk
    #
    Vk = Vk_up
    iter = 0
    @label start
    i = Vk - 1.0
    μk = i - δ*k
    if (iter == 0) & (μk <= 0)
        iter += 1
        Vk = Vk_down
        @goto start
    end 
    Vt = - (z*k - i -0.5*(i^2.0) + μk*Vk - r*V)
    (Vt = Vt,)
end
y, residual_norm =  pdesolve(f, stategrid, solend)

I also tried:

using EconPDEs
stategrid = OrderedDict(:k => range(0.0, 5.0, length = 1000))
solend = OrderedDict(:V => ones(1000))
function f(state::NamedTuple, sol::NamedTuple)
    r = 0.05; δ = 0.10; z = 0.20;
    k = state.k
    V, Vk_up, Vk_down, Vkk = sol.V, sol.Vk_up, sol.Vk_down, sol.Vkk
    #
    Vk = Vk_up
    iter = 0
    @label start
    i = Vk - 1.0
    μk = i - δ*k
    Vk = (μk >= 0) ? Vk_up : Vk_down
    Vt = - (z*k - i -0.5*(i^2.0) + μk*Vk - r*V)
    (Vt = Vt,), (; V, Vk, Vkk, k)
end
y, residual_norm =  pdesolve(f, stategrid, solend)

I'm not sure I understand how solve a model correctly w/ your package.
In both cases I get the same wrong value function not equal to the closed form solution.
The affine, closed-form solution: image

How can I solve this simple firm investment problem with your package?

@azev77
Copy link
Author

azev77 commented Dec 17, 2021

I was able to solve this w/ my own generic HJB Solver:

using LinearAlgebra, SparseArrays, Plots
if 1==1
    ρ = 0.05; δ = 0.10; A = 0.20;
    r(s,a) = A*s -a -0.5*(a)^2.0
    μ(s,a) = a - δ*s
    dr(s,a) = -1 -a 
    FOC(s,Vs) = Vs - 1
    μ_inv(s,ṡ) =+ δ*s 
    s_ss = (A-ρ-δ)/*+δ))
    a_ss = δ*s_ss
    v_ss = r(s_ss,a_ss)/ρ        # ∫exp(-ρt)*r(s_ss,a_ss) dt = r(s_ss,a_ss)/ρ    
    #s_min = 0.00*s_ss  
    s_min = -.5  
    s_max = 2.000*s_ss
    # verify things are well defined @ corners 
    s_max, μ_inv(s_max,0), dr(s_max,μ_inv(s_max,0))
    s_min, μ_inv(s_min,0), dr(s_min,μ_inv(s_min,0))
    H = 10_000;
    s = collect(LinRange(s_min, s_max, H))
    ds = (s_max-s_min)/(H-1)
    dVf, dVb            = [zeros(H,1) for i in 1:2]
    dV_Upwind, a_Upwind = [zeros(H,1) for i in 1:2]
    dVf_end       = dr(s_max,μ_inv(s_max,0))  
    dVb_1         = dr(s_min,μ_inv(s_min,0))
    v0 = v_ss *ones(H)
    v0 = @. r(s, μ_inv(s,0))/ρ #initial guess for V
    v = v0
end    
Δ = 1_000; maxit = 10_000= 10e-8; dist=[];

# Generic HJB Solver
# Parimonious: dV_Upwind = dVf.*If + dVb.*Ib + dV0.*I0
for n=1:maxit
	V=v
    dV = (V[2:end] - V[1:end-1])/ds  

    # forward difference: if ṡ>0
    dVf = [dV; dVf_end]
    af  = FOC.(s,dVf)                 # choice w forward difference
    μ_f = μ.(s, af)                   # ṡ      w forward difference
    If  = μ_f .> 0

	# backward difference: if ṡ<0
    dVb = [dVb_1; dV]
    ab  = FOC.(s,dVb)                          # choice w backward difference
    μ_b = μ.(s, ab)
    Ib  = μ_b .< 0

    # neither difference: if ṡ=0
	a0  = μ_inv.(s,0)        # c if ṡ=0
    dV0 = dr.(s,a0)
    μ_0 = μ.(s, a0)          # μ_0 == zero(s)
    I0  = (1.0 .- If - Ib)   # choice betw forward & backward difference

	# I_concave = dVb .> dVf
    # scatter(I_concave) #1 everywhere EXCEPT @ last point H. 

    dV_Upwind = dVf.*If + dVb.*Ib + dV0.*I0   
    a_Upwind  = FOC.(s,dV_Upwind)    
    μ_Upwind  = μ.(s, a_Upwind)                                            
    u = r.(s,a_Upwind)

    # create the transition matrix AA
    X = -min.(μ_b,0)/ds
    Y = -max.(μ_f,0)/ds + min.(μ_b,0)/ds
    Z = max.(μ_f,0)/ds

    a1 = sparse(Diagonal((Y[:])))
    a2 = [zeros(1,H); sparse(Diagonal((X[2:H]))) zeros(H-1,1)]
    a3 = [zeros(H-1,1) sparse(Diagonal((Z[1:H-1]))); zeros(1,H)]
    AA = a1 + a2 + a3

    # Solve for new value 
    B =+ 1/Δ)*sparse(I,H,H) - AA
    b = u + V./Δ
    V = B \ b

    # 8: stopping criteria 
    V_change = V-v
    v = V 

	push!(dist,findmax(abs.(V_change))[1])
    println(n, " ", dist[n])
	if dist[n] .< ε
		println("Value Function Converged Iteration=")
		println(n)
		break
	end
end
s_dot = μ.(s, a_Upwind)
v_err = r.(s, a_Upwind) + dV_Upwind.*s_dot - ρ.*v # approx @ borrowing constraint
plot(s, v, xlabel="s", ylabel="V(s)",legend=false, title="")
plot!([s_ss],  seriestype = :vline, lab="", color="grey", l=:dash)
plot!([v_ss],  seriestype = :hline, lab="", color="grey", l=:dash)

# Simulate.
using Interpolations
â = LinearInterpolation(s, a_Upwind[:], extrapolation_bc = Line())
Δt = 0.01; T = 150; time = 0.0:Δt:T
s_sim, a_sim, ṡ_sim = [zeros(length(time),1) for i in 1:3]
s_0 =0.5*s_ss 
s_sim[1] = s_0 
for i in 2:length(time)
    a_sim[i-1] = (s_sim[i-1]) 
    ṡ_sim[i-1] = μ(s_sim[i-1], a_sim[i-1])
    s_sim[i] = s_sim[i-1] + Δt * ṡ_sim[i-1]
    # s_sim[i] = s_sim[i-1] + Δt * μ(s_sim[i-1], a_sim[i-1])
end
ix = 1:(length(time)-1)
plot()
plot!(time[ix], s_sim[ix], lab = "s")
plot!(time[ix], a_sim[ix], lab = "c")
plot!(time[ix], ṡ_sim[ix], lab = "")
plot!([s_min],  seriestype = :hline, lab="", color="grey", l=:dash)
plot!([s_ss a_ss],  seriestype = :hline, lab="", color="grey", l=:dash)

image

@azev77
Copy link
Author

azev77 commented Dec 17, 2021

  1. Is there a chance your solver might be able to be more generic in the future? Allowing n_s states, n_a actions etc?
  2. It now works when I set the minimum point in the capital grid to be very negative.
    stategrid = OrderedDict(:k => range(-7.0, 7.0, length = 1000))
    Is there any way the algorithm can guide the user how to solve the problem?

@matthieugomez
Copy link
Owner

matthieugomez commented Jun 17, 2024

To solve your model, I needed to add the constraint that the drift of capital must be non negative at k = 0. This constraint is necessary because, otherwise, at k =0, the upwinding procedure uses the value of the derivative outside the grid. By default, EconPDEs assumes that the value of the derivative outside the grid is zero, which is inconsistent with the full problem. Another alternative, as you realized is to choose a grid big enough that the boundary conditions do not matter much for the value function at typical capital levels.

Here is the modified code that would solve your model:

using EconPDEs
stategrid = OrderedDict(:k => range(0.0, 5.0, length = 1000))
solend = OrderedDict(:V => ones(1000))
function f(state::NamedTuple, sol::NamedTuple)
    r = 0.05; δ = 0.10; z = 0.20
    k = state.k
    V, Vk_up, Vk_down, Vkk = sol.V, sol.Vk_up, sol.Vk_down, sol.Vkk
    #
    Vk = Vk_up
    iter = 0
    @label start
    i = Vk - 1.0
    μk = i - δ*k
    if (iter == 0) & (μk <= 0)
        iter += 1
        Vk = Vk_down
        @goto start
    end 
   # ensures that drift non negative at zero
    if (k  0) & (μk < 0)
        i = δ * k
        μk = 0.0
    end
    Vt = - (z*k - i -0.5*(i^2.0) + μk*Vk - r*V)
    (Vt = Vt,)
end
y, residual_norm =  pdesolve(f, stategrid, solend)
# check value function is linear
plot(stategrid[:k], y[:V])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants