@@ -32,10 +32,8 @@ function solve_allocation_by_duality(x0, auxdata, verbose=true)
3232 n = graph. J * param. N
3333
3434 # Get the Hessian structure
35- hess_str = hessian_structure_duality (auxdata)
36- auxdata = (auxdata... , hess = hess_str, hess_ind = CartesianIndex .(hess_str[1 ], hess_str[2 ]))
37- nnz_hess = length (hess_str[1 ])
38-
35+ auxdata = (auxdata... , hess = hessian_structure_duality (auxdata))
36+ nnz_hess = length (auxdata. hess[1 ])
3937
4038 prob = Ipopt. CreateIpoptProblem (
4139 n,
@@ -149,81 +147,101 @@ function hessian_duality(
149147 auxdata
150148)
151149 if values === nothing
152- # # Return the sparsity structure
153- # nz = length(findnz(tril(h))[1])
154- # resize!(rows, nz)
155- # resize!(cols, nz)
156- # r, c, _ = findnz(tril(h))
157150 r, c = auxdata. hess
158151 rows .= r
159152 cols .= c
160153 else
161154 # function hessian(x, auxdata, obj_factor, lambda)
162155 param = auxdata. param
163156 graph = auxdata. graph
157+ nodes = graph. nodes
164158 kappa = auxdata. kappa
165-
159+ beta = param. beta
160+ m1dbeta = - 1 / beta
161+ sigma = param. sigma
162+ a = param. a
163+ adam1 = a / (a - 1 )
164+ J = graph. J
165+ Zjn = graph. Zjn
166+ Lj = graph. Lj
167+ omegaj = graph. omegaj
168+ # Constant term for Q'^n_{jk}
169+ cons = m1dbeta / (1 + beta)
170+ hess_str = auxdata. hess
171+
172+ # Precompute elements
166173 res = recover_allocation_duality (x, auxdata)
167- Lambda = repeat (x, 1 , graph. J * param. N)
168- lambda = reshape (x, (graph. J, param. N))
169-
170- # Compute price index
171- P = sum (lambda .^ (1 - param. sigma), dims= 2 ) .^ (1 / (1 - param. sigma))
172- mat_P = repeat (P, param. N, param. N * graph. J)
173-
174- # Create masks
175- # This lets different products in the same location relate
176- Iij = kron (ones (param. N, param. N), I (graph. J))
177- # This is adjacency, but only for the same product (n == n')
178- Inm = kron (I (param. N), graph. adjacency)
174+ Pjn = res. Pjn
175+ PCj = res. PCj
176+ Qjkn = res. Qjkn
177+
178+ # Prepare computation of production function
179+ if a != 1
180+ psi = (Pjn .* Zjn) .^ (1 / (1 - a))
181+ Psi = sum (psi, dims= 2 )
182+ end
179183
180- # Compute terms for Hessian
181- termA = - param. sigma * (repeat (P, param. N) .^ param. sigma .* x .^ (- (param. sigma + 1 )) .* repeat (res. Cj, param. N))
184+ # Now the big loop over the hessian terms to compute
185+ ind = 0
186+
187+ # https://stackoverflow.com/questions/38901275/inbounds-propagation-rules-in-julia
188+ @inbounds for (jdnd, jn) in zip (hess_str[1 ], hess_str[2 ])
189+ ind += 1
190+ # First get the indices of the element and the respective derivative
191+ j = (jn- 1 ) % J + 1
192+ n = Int (ceil (jn / J))
193+ jd = (jdnd- 1 ) % J + 1
194+ nd = Int (ceil (jdnd / J))
195+ # Get neighbors k
196+ neighbors = nodes[j]
197+
198+ # Stores the derivative term
199+ term = 0.0
200+
201+ # Starting with C^n_j = CG, derivative: C'G + CG'
202+ if jd == j # 0 for P^n_k
203+ Cprime = Lj[j]/ omegaj[j] * (PCj[j] / Pjn[j, nd])^ sigma / param. usecond (res. cj[j], graph. hj[j]) # param.uprimeinvprime(PCj[j]/omegaj[j], graph.hj[j])
204+ G = (Pjn[j, n] / PCj[j])^ (- sigma)
205+ Gprime = sigma * (Pjn[j, n] * Pjn[j, nd])^ (- sigma) * PCj[j]^ (2 * sigma- 1 )
206+ if nd == n
207+ Gprime -= sigma / PCj[j] * G^ ((sigma+ 1 )/ sigma)
208+ end
209+ term += Cprime * G + res. Cj[j] * Gprime
210+ end
182211
183- part1 = Iij .* Lambda .^ (- param. sigma) .* Lambda' .^ (- param. sigma) .* mat_P .^ (2 * param. sigma)
212+ # Now terms sum_k((Q^n_{jk} + K_{jk}(Q^n_{jk})^(1+beta)) - Q^n_{kj})
213+ if nd == n
214+ for k in neighbors
215+ if jd == j # P^x_j
216+ diff = Pjn[k, n] - Pjn[j, n]
217+ if diff >= 0
218+ term += m1dbeta * (Pjn[k, n] / Pjn[j, n])^ 2 * Qjkn[j, k, n] / diff
219+ else
220+ term += m1dbeta * Qjkn[k, j, n] / abs (diff)
221+ end
222+ elseif jd == k # P^x_k
223+ diff = Pjn[k, n] - Pjn[j, n]
224+ if diff >= 0
225+ term -= m1dbeta * Pjn[k, n] / Pjn[j, n] * Qjkn[j, k, n] / diff
226+ else
227+ term -= m1dbeta * Pjn[j, n] / Pjn[k, n] * Qjkn[k, j, n] / abs (diff)
228+ end
229+ end
230+ end # End of k loop
231+ end
184232
185- termB = param. sigma * part1 ./ mat_P .* repeat (res. Cj, param. N, graph. J * param. N)
186-
187- termC = part1 .* repeat (graph. Lj ./ (graph. omegaj .* param. usecond .(res. cj, graph. hj)), param. N, graph. J * param. N)
188-
189- diff = Lambda' - Lambda # P^n_k - P^n_j
190- mat_kappa = repeat (kappa, param. N, param. N)
191- part1 = 1 / (param. beta * (1 + param. beta)^ (1 / param. beta)) * Inm .* mat_kappa .^ (1 / param. beta)
192- abs_diff_1betam1 = abs .(diff) .^ (1 / param. beta - 1 )
193- diffpos = diff .> 0
194- diffneg = diff .< 0
195-
196- termD = part1 .* abs_diff_1betam1 .*
197- (diffpos .* Lambda' ./ Lambda .^ (1 + 1 / param. beta) +
198- diffneg .* Lambda ./ Lambda' .^ (1 + 1 / param. beta))
199-
200- termE = - part1 .* abs_diff_1betam1 .*
201- (diffpos .* Lambda' .^ 2 ./ Lambda .^ (2 + 1 / param. beta) +
202- diffneg ./ Lambda' .^ (1 / param. beta))
203- termE = sum (termE, dims= 2 )
204-
205- # Compute labor term
206- if param. a == 1
207- X = 0
208- else
209- # This is Psi in your notes
210- denom = sum ((lambda .* graph. Zjn) .^ (1 / (1 - param. a)), dims= 2 )
211- num = (x .* graph. Zjn[:]) .^ (1 / (1 - param. a))
212- # Non-diagonal elements: using 1-a in denominator because output is subtracted
213- X = param. a / (1 - param. a) * Iij .* (graph. Zjn[:] * graph. Zjn[:]' ) .* (num * num' ) .^ param. a .*
214- repeat (graph. Lj .^ param. a ./ denom .^ (1 + param. a), param. N, graph. J * param. N)
215- # The term that is multiplied to get the diagonal
216- X_diag = (num - repeat (denom, param. N)) ./ num
217- # Adding the diagonal
218- @inbounds for i in 1 : length (X_diag)
219- X[i, i] *= X_diag[i]
233+ # Finally: need to compute production function (X)
234+ if a != 1 && jd == j
235+ X = adam1 * Zjn[j, n] * Zjn[j, nd] * (psi[j, n] * psi[j, nd])^ a / Psi[j]^ (a+ 1 ) * Lj[j]^ a
236+ if nd == n
237+ X *= 1 - Psi[j] / psi[j, n]
238+ end
239+ term -= X
220240 end
241+
242+ # Assign result
243+ values[ind] = - obj_factor * term + 1e-5 # Somehow need this increment to make it work
221244 end
222- # Compute Hessian
223- h = - obj_factor * (Diagonal (termA[:]) + termB + termC + termD + Diagonal (termE[:]) + X)
224-
225- # Return lower triangular part
226- values .= h[auxdata. hess_ind]
227245 end
228246 return
229247end
@@ -268,11 +286,12 @@ function recover_allocation_duality(x, auxdata)
268286
269287 # Calculate the flows Qjkn
270288 Qjkn = zeros (graph. J, graph. J, param. N)
271- for n in 1 : param. N
289+ nadj = findall (.! graph. adjacency)
290+ @inbounds for n in 1 : param. N
272291 Lambda = repeat (Pjn[:, n], 1 , graph. J)
273292 # Note: max(P^n_k/P^n_j-1, 0) = max(P^n_k-P^n_j, 0)/P^n_j
274293 LL = max .(Lambda' - Lambda, 0 ) # P^n_k - P^n_j
275- LL[. ! graph . adjacency ] .= 0
294+ LL[nadj ] .= 0
276295 Qjkn[:, :, n] = (1 / (1 + param. beta) * kappa .* LL ./ Lambda) .^ (1 / param. beta)
277296 end
278297
0 commit comments