diff --git a/lib/MadNLPGPU/src/kernels.jl b/lib/MadNLPGPU/src/kernels.jl index 2ed110fa..b278a1f0 100644 --- a/lib/MadNLPGPU/src/kernels.jl +++ b/lib/MadNLPGPU/src/kernels.jl @@ -112,9 +112,14 @@ end function MadNLP.set_aug_diagonal!(kkt::MadNLP.AbstractDenseKKTSystem{T, VT, MT}, solver::MadNLP.MadNLPSolver) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} haskey(kkt.etc, :pr_diag_host) || (kkt.etc[:pr_diag_host] = Vector{T}(undef, length(kkt.pr_diag))) pr_diag_h = kkt.etc[:pr_diag_host]::Vector{T} + x = MadNLP.full(solver.x) + zl = MadNLP.full(solver.zl) + zu = MadNLP.full(solver.zu) + xl = MadNLP.full(solver.xl) + xu = MadNLP.full(solver.xu) # Broadcast is not working as MadNLP array are allocated on the CPU, # whereas pr_diag is allocated on the GPU - pr_diag_h .= solver.zl./(solver.x.-solver.xl) .+ solver.zu./(solver.xu.-solver.x) + pr_diag_h .= zl./(x.-xl) .+ zu./(xu.-x) copyto!(kkt.pr_diag, pr_diag_h) fill!(kkt.du_diag, 0.0) end diff --git a/lib/MadNLPGPU/test/densekkt_gpu.jl b/lib/MadNLPGPU/test/densekkt_gpu.jl index 619e9b20..bb560270 100644 --- a/lib/MadNLPGPU/test/densekkt_gpu.jl +++ b/lib/MadNLPGPU/test/densekkt_gpu.jl @@ -22,18 +22,18 @@ function _compare_gpu_with_cpu(KKTSystem, n, m, ind_fixed) # Solve on CPU h_solver = MadNLP.MadNLPSolver(nlp; madnlp_options...) - MadNLP.solve!(h_solver) + results_cpu = MadNLP.solve!(h_solver) # Solve on GPU d_solver = MadNLPGPU.CuMadNLPSolver(nlp; madnlp_options...) - MadNLP.solve!(d_solver) + results_gpu = MadNLP.solve!(d_solver) @test isa(d_solver.kkt, KKTSystem{T, CuVector{T}, CuMatrix{T}}) # # Check that both results match exactly @test h_solver.cnt.k == d_solver.cnt.k - @test h_solver.obj_val ≈ d_solver.obj_val atol=atol - @test h_solver.x ≈ d_solver.x atol=atol - @test h_solver.y ≈ d_solver.y atol=atol + @test results_cpu.objective ≈ results_gpu.objective + @test results_cpu.solution ≈ results_gpu.solution atol=atol + @test results_cpu.multipliers ≈ results_gpu.multipliers atol=atol end end diff --git a/src/IPM/IPM.jl b/src/IPM/IPM.jl index 9f3d6385..c1fadbc4 100644 --- a/src/IPM/IPM.jl +++ b/src/IPM/IPM.jl @@ -18,15 +18,15 @@ mutable struct MadNLPSolver{T, KKTSystem <: AbstractKKTSystem{T}, Model <: Abstr nlb::Int nub::Int - x::Vector{T} # primal (after reformulation) + x::PrimalVector{T, Vector{T}} # primal (after reformulation) y::Vector{T} # dual - zl::Vector{T} # dual (after reformulation) - zu::Vector{T} # dual (after reformulation) - xl::Vector{T} # primal lower bound (after reformulation) - xu::Vector{T} # primal upper bound (after reformulation) + zl::PrimalVector{T, Vector{T}} # dual (after reformulation) + zu::PrimalVector{T, Vector{T}} # dual (after reformulation) + xl::PrimalVector{T, Vector{T}} # primal lower bound (after reformulation) + xu::PrimalVector{T, Vector{T}} # primal upper bound (after reformulation) obj_val::T - f::Vector{T} + f::PrimalVector{T, Vector{T}} c::Vector{T} jacl::Vector{T} @@ -40,11 +40,10 @@ mutable struct MadNLPSolver{T, KKTSystem <: AbstractKKTSystem{T}, Model <: Abstr _w3::KKTVec _w4::KKTVec - x_trial::Vector{T} + x_trial::PrimalVector{T, Vector{T}} c_trial::Vector{T} obj_val_trial::T - x_slk::Vector{T} c_slk::SubVector{T} rhs::Vector{T} @@ -140,62 +139,70 @@ function MadNLPSolver{T,KKTSystem}( @trace(logger,"Initializing variables.") ind_cons = get_index_constraints(nlp; fixed_variable_treatment=opt.fixed_variable_treatment) ns = length(ind_cons.ind_ineq) - n = get_nvar(nlp)+ns + nx = get_nvar(nlp) + n = nx+ns m = get_ncon(nlp) # Initialize KKT kkt = KKTSystem(nlp, ind_cons) - xl = [get_lvar(nlp);view(get_lcon(nlp),ind_cons.ind_ineq)] - xu = [get_uvar(nlp);view(get_ucon(nlp),ind_cons.ind_ineq)] - x = [get_x0(nlp);zeros(T,ns)] - y = copy(get_y0(nlp)) - zl= zeros(T,get_nvar(nlp)+ns) - zu= zeros(T,get_nvar(nlp)+ns) + # Primal variable + x = PrimalVector{T, Vector{T}}(nx, ns) + variable(x) .= get_x0(nlp) + # Bounds + xl = PrimalVector{T, Vector{T}}(nx, ns) + variable(xl) .= get_lvar(nlp) + slack(xl) .= view(get_lcon(nlp), ind_cons.ind_ineq) + xu = PrimalVector{T, Vector{T}}(nx, ns) + variable(xu) .= get_uvar(nlp) + slack(xu) .= view(get_ucon(nlp), ind_cons.ind_ineq) + zl = PrimalVector{T, Vector{T}}(nx, ns) + zu = PrimalVector{T, Vector{T}}(nx, ns) + # Gradient + f = PrimalVector{T, Vector{T}}(nx, ns) - f = zeros(T,n) # not sure why, but seems necessary to initialize to 0 when used with Plasmo interface - c = zeros(T,m) + y = copy(get_y0(nlp)) + c = zeros(T, m) n_jac = nnz_jacobian(kkt) nlb = length(ind_cons.ind_lb) nub = length(ind_cons.ind_ub) - x_trial=Vector{T}(undef,n) - c_trial=Vector{T}(undef,m) + x_trial = PrimalVector{T, Vector{T}}(nx, ns) + c_trial = Vector{T}(undef, m) - x_slk= _madnlp_unsafe_wrap(x,ns, get_nvar(nlp)+1) - c_slk= view(c,ind_cons.ind_ineq) + c_slk = view(c,ind_cons.ind_ineq) rhs = (get_lcon(nlp).==get_ucon(nlp)).*get_lcon(nlp) - x_lr = view(x, ind_cons.ind_lb) - x_ur = view(x, ind_cons.ind_ub) - xl_r = view(xl, ind_cons.ind_lb) - xu_r = view(xu, ind_cons.ind_ub) - zl_r = view(zl, ind_cons.ind_lb) - zu_r = view(zu, ind_cons.ind_ub) - x_trial_lr = view(x_trial, ind_cons.ind_lb) - x_trial_ur = view(x_trial, ind_cons.ind_ub) + x_lr = view(full(x), ind_cons.ind_lb) + x_ur = view(full(x), ind_cons.ind_ub) + xl_r = view(full(xl), ind_cons.ind_lb) + xu_r = view(full(xu), ind_cons.ind_ub) + zl_r = view(full(zl), ind_cons.ind_lb) + zu_r = view(full(zu), ind_cons.ind_ub) + x_trial_lr = view(full(x_trial), ind_cons.ind_lb) + x_trial_ur = view(full(x_trial), ind_cons.ind_ub) if is_reduced(kkt) - _w1 = ReducedKKTVector{T,typeof(x)}(n, m) - _w2 = ReducedKKTVector{T,typeof(x)}(n, m) - _w3 = ReducedKKTVector{T,typeof(x)}(n, m) - _w4 = ReducedKKTVector{T,typeof(x)}(n, m) + _w1 = ReducedKKTVector{T,typeof(c)}(n, m) + _w2 = ReducedKKTVector{T,typeof(c)}(n, m) + _w3 = ReducedKKTVector{T,typeof(c)}(n, m) + _w4 = ReducedKKTVector{T,typeof(c)}(n, m) else - _w1 = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) - _w2 = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) - _w3 = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) - _w4 = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) + _w1 = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) + _w2 = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) + _w3 = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) + _w4 = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) end jacl = zeros(T,n) # spblas may throw an error if not initialized to zero - d = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) + d = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) dx_lr = view(d.xp, ind_cons.ind_lb) # TODO dx_ur = view(d.xp, ind_cons.ind_ub) # TODO - p = UnreducedKKTVector{T,typeof(x)}(n, m, nlb, nub) + p = UnreducedKKTVector{T,typeof(c)}(n, m, nlb, nub) obj_scale = T[1.0] con_scale = ones(T,m) @@ -223,7 +230,7 @@ function MadNLPSolver{T,KKTSystem}( jacl, d, p, _w1, _w2, _w3, _w4, - x_trial,c_trial,0.,x_slk,c_slk,rhs, + x_trial,c_trial,0.,c_slk,rhs, ind_cons.ind_ineq,ind_cons.ind_fixed,ind_cons.ind_llb,ind_cons.ind_uub, x_lr,x_ur,xl_r,xu_r,zl_r,zu_r,dx_lr,dx_ur,x_trial_lr,x_trial_ur, linear_solver,iterator, diff --git a/src/IPM/callbacks.jl b/src/IPM/callbacks.jl index 72e0f5f3..a4a66226 100644 --- a/src/IPM/callbacks.jl +++ b/src/IPM/callbacks.jl @@ -1,128 +1,137 @@ -function eval_f_wrapper(solver::MadNLPSolver, x::Vector{T}) where T +function eval_f_wrapper(solver::MadNLPSolver, x::PrimalVector{T}) where T nlp = solver.nlp cnt = solver.cnt @trace(solver.logger,"Evaluating objective.") - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) - cnt.eval_function_time += @elapsed obj_val = (get_minimize(nlp) ? 1. : -1.) * obj(nlp,x_nlpmodel) - cnt.obj_cnt+=1 - cnt.obj_cnt==1 && (is_valid(obj_val) || throw(InvalidNumberException(:obj))) - return obj_val*solver.obj_scale[] + cnt.eval_function_time += @elapsed begin + sense = (get_minimize(nlp) ? one(T) : -one(T)) + obj_val = sense * obj(nlp, variable(x)) + end + cnt.obj_cnt += 1 + if cnt.obj_cnt == 1 && !is_valid(obj_val) + throw(InvalidNumberException(:obj)) + end + return obj_val * solver.obj_scale[] end -function eval_grad_f_wrapper!(solver::MadNLPSolver, f::Vector{T},x::Vector{T}) where T +function eval_grad_f_wrapper!(solver::MadNLPSolver, f::PrimalVector{T}, x::PrimalVector{T}) where T nlp = solver.nlp cnt = solver.cnt @trace(solver.logger,"Evaluating objective gradient.") obj_scaling = solver.obj_scale[] * (get_minimize(nlp) ? one(T) : -one(T)) - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) - f_nlpmodel = _madnlp_unsafe_wrap(f, get_nvar(nlp)) cnt.eval_function_time += @elapsed grad!( nlp, - x_nlpmodel, - f_nlpmodel + variable(x), + variable(f), ) - _scal!(obj_scaling, f) + _scal!(obj_scaling, full(f)) cnt.obj_grad_cnt+=1 - cnt.obj_grad_cnt==1 && (is_valid(f) || throw(InvalidNumberException(:grad))) + if cnt.obj_grad_cnt == 1 && !is_valid(full(f)) + throw(InvalidNumberException(:grad)) + end return f end -function eval_cons_wrapper!(solver::MadNLPSolver, c::Vector{T},x::Vector{T}) where T +function eval_cons_wrapper!(solver::MadNLPSolver, c::Vector{T}, x::PrimalVector{T}) where T nlp = solver.nlp cnt = solver.cnt @trace(solver.logger, "Evaluating constraints.") - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) - c_nlpmodel = _madnlp_unsafe_wrap(c, get_ncon(nlp)) cnt.eval_function_time += @elapsed cons!( nlp, - x_nlpmodel, - c_nlpmodel + variable(x), + c, ) - view(c,solver.ind_ineq).-=view(x,get_nvar(nlp)+1:solver.n) + view(c,solver.ind_ineq) .-= slack(x) c .-= solver.rhs c .*= solver.con_scale cnt.con_cnt+=1 - cnt.con_cnt==2 && (is_valid(c) || throw(InvalidNumberException(:cons))) + if cnt.con_cnt == 1 && !is_valid(c) + throw(InvalidNumberException(:cons)) + end return c end -function eval_jac_wrapper!(solver::MadNLPSolver, kkt::AbstractKKTSystem, x::Vector{T}) where T +function eval_jac_wrapper!(solver::MadNLPSolver, kkt::AbstractKKTSystem, x::PrimalVector{T}) where T nlp = solver.nlp cnt = solver.cnt ns = length(solver.ind_ineq) @trace(solver.logger, "Evaluating constraint Jacobian.") jac = get_jacobian(kkt) - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) - jac_nlpmodel = _madnlp_unsafe_wrap(jac, get_nnzj(nlp.meta)) cnt.eval_function_time += @elapsed jac_coord!( nlp, - x_nlpmodel, - jac_nlpmodel + variable(x), + jac, ) compress_jacobian!(kkt) - cnt.con_jac_cnt+=1 - cnt.con_jac_cnt==1 && (is_valid(jac) || throw(InvalidNumberException(:jac))) + cnt.con_jac_cnt += 1 + if cnt.con_jac_cnt == 1 && !is_valid(jac) + throw(InvalidNumberException(:jac)) + end @trace(solver.logger,"Constraint jacobian evaluation started.") return jac end -function eval_lag_hess_wrapper!(solver::MadNLPSolver, kkt::AbstractKKTSystem, x::Vector{T},l::Vector{T};is_resto=false) where T +function eval_lag_hess_wrapper!(solver::MadNLPSolver, kkt::AbstractKKTSystem, x::PrimalVector{T},l::Vector{T};is_resto=false) where T nlp = solver.nlp cnt = solver.cnt @trace(solver.logger,"Evaluating Lagrangian Hessian.") - dual(solver._w1) .= l.*solver.con_scale + dual(solver._w1) .= l .* solver.con_scale hess = get_hessian(kkt) - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) - hess_nlpmodel = _madnlp_unsafe_wrap(hess, get_nnzh(nlp.meta)) + scale = (get_minimize(nlp) ? one(T) : -one(T)) + scale *= (is_resto ? zero(T) : solver.obj_scale[]) cnt.eval_function_time += @elapsed hess_coord!( nlp, - x_nlpmodel, + variable(x), dual(solver._w1), - hess_nlpmodel; - obj_weight = (get_minimize(nlp) ? 1. : -1.) * (is_resto ? 0.0 : solver.obj_scale[]) + hess; + obj_weight = scale, ) compress_hessian!(kkt) - cnt.lag_hess_cnt+=1 - cnt.lag_hess_cnt==1 && (is_valid(hess) || throw(InvalidNumberException(:hess))) + cnt.lag_hess_cnt += 1 + if cnt.lag_hess_cnt == 1 && !is_valid(hess) + throw(InvalidNumberException(:hess)) + end return hess end -function eval_jac_wrapper!(solver::MadNLPSolver, kkt::AbstractDenseKKTSystem, x::Vector{T}) where T +function eval_jac_wrapper!(solver::MadNLPSolver, kkt::AbstractDenseKKTSystem, x::PrimalVector{T}) where T nlp = solver.nlp cnt = solver.cnt ns = length(solver.ind_ineq) @trace(solver.logger, "Evaluating constraint Jacobian.") jac = get_jacobian(kkt) - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) cnt.eval_function_time += @elapsed jac_dense!( nlp, - x_nlpmodel, - jac + variable(x), + jac, ) compress_jacobian!(kkt) cnt.con_jac_cnt+=1 - cnt.con_jac_cnt==1 && (is_valid(jac) || throw(InvalidNumberException(:jac))) + if cnt.con_jac_cnt == 1 && !is_valid(jac) + throw(InvalidNumberException(:jac)) + end @trace(solver.logger,"Constraint jacobian evaluation started.") return jac end -function eval_lag_hess_wrapper!(solver::MadNLPSolver, kkt::AbstractDenseKKTSystem, x::Vector{T},l::Vector{T};is_resto=false) where T +function eval_lag_hess_wrapper!(solver::MadNLPSolver, kkt::AbstractDenseKKTSystem, x::PrimalVector{T},l::Vector{T};is_resto=false) where T nlp = solver.nlp cnt = solver.cnt @trace(solver.logger,"Evaluating Lagrangian Hessian.") - dual(solver._w1) .= l.*solver.con_scale + dual(solver._w1) .= l .* solver.con_scale hess = get_hessian(kkt) - x_nlpmodel = _madnlp_unsafe_wrap(x, get_nvar(nlp)) + scale = is_resto ? zero(T) : get_minimize(nlp) ? solver.obj_scale[] : -solver.obj_scale[] cnt.eval_function_time += @elapsed hess_dense!( nlp, - x_nlpmodel, + variable(x), dual(solver._w1), hess; - obj_weight = (get_minimize(nlp) ? 1. : -1.) * (is_resto ? 0.0 : solver.obj_scale[]) + obj_weight = scale, ) compress_hessian!(kkt) cnt.lag_hess_cnt+=1 - cnt.lag_hess_cnt==1 && (is_valid(hess) || throw(InvalidNumberException(:hess))) + if cnt.lag_hess_cnt == 1 && !is_valid(hess) + throw(InvalidNumberException(:hess)) + end return hess end diff --git a/src/IPM/kernels.jl b/src/IPM/kernels.jl index 79a90a55..b9fa00aa 100644 --- a/src/IPM/kernels.jl +++ b/src/IPM/kernels.jl @@ -2,9 +2,14 @@ # KKT system updates ------------------------------------------------------- # Set diagonal function set_aug_diagonal!(kkt::AbstractKKTSystem, solver::MadNLPSolver{T}) where T + x = full(solver.x) + xl = full(solver.xl) + xu = full(solver.xu) + zl = full(solver.zl) + zu = full(solver.zu) @inbounds @simd for i in eachindex(kkt.pr_diag) - kkt.pr_diag[i] = solver.zl[i] /(solver.x[i] - solver.xl[i]) - kkt.pr_diag[i] += solver.zu[i] /(solver.xu[i] - solver.x[i]) + kkt.pr_diag[i] = zl[i] /(x[i] - xl[i]) + kkt.pr_diag[i] += zu[i] /(xu[i] - x[i]) end fill!(kkt.du_diag, zero(T)) return @@ -25,9 +30,14 @@ end # Robust restoration function set_aug_RR!(kkt::AbstractKKTSystem, solver::MadNLPSolver, RR::RobustRestorer) + x = full(solver.x) + xl = full(solver.xl) + xu = full(solver.xu) + zl = full(solver.zl) + zu = full(solver.zu) @inbounds @simd for i in eachindex(kkt.pr_diag) - kkt.pr_diag[i] = solver.zl[i] / (solver.x[i] - solver.xl[i]) - kkt.pr_diag[i] += solver.zu[i] / (solver.xu[i] - solver.x[i]) + RR.zeta * RR.D_R[i]^2 + kkt.pr_diag[i] = zl[i] / (x[i] - xl[i]) + kkt.pr_diag[i] += zu[i] / (xu[i] - x[i]) + RR.zeta * RR.D_R[i]^2 end @inbounds @simd for i in eachindex(kkt.du_diag) kkt.du_diag[i] = -RR.pp[i] /RR.zp[i] - RR.nn[i] /RR.zn[i] @@ -51,12 +61,23 @@ function set_aug_RR!(kkt::SparseUnreducedKKTSystem, solver::MadNLPSolver, RR::Ro end return end +function set_f_RR!(solver::MadNLPSolver, RR::RobustRestorer) + x = full(solver.x) + @inbounds @simd for i in eachindex(RR.f_R) + RR.f_R[i] = RR.zeta * RR.D_R[i]^2 *(x[i]-RR.x_ref[i]) + end +end + # Set RHS function set_aug_rhs!(solver::MadNLPSolver, kkt::AbstractKKTSystem, c) px = primal(solver.p) + x = primal(solver.x) + f = primal(solver.f) + xl = primal(solver.xl) + xu = primal(solver.xu) @inbounds @simd for i in eachindex(px) - px[i] = -solver.f[i] + solver.mu / (solver.x[i] - solver.xl[i]) - solver.mu / (solver.xu[i] - solver.x[i]) - solver.jacl[i] + px[i] = -f[i] + solver.mu / (x[i] - xl[i]) - solver.mu / (xu[i] - x[i]) - solver.jacl[i] end py = dual(solver.p) @inbounds @simd for i in eachindex(py) @@ -65,9 +86,12 @@ function set_aug_rhs!(solver::MadNLPSolver, kkt::AbstractKKTSystem, c) return end function set_aug_rhs!(solver::MadNLPSolver, kkt::SparseUnreducedKKTSystem, c) + f = primal(solver.f) + zl = primal(solver.zl) + zu = primal(solver.zu) px = primal(solver.p) @inbounds @simd for i in eachindex(px) - px[i] = -solver.f[i] + solver.zl[i] - solver.zu[i] - solver.jacl[i] + px[i] = -f[i] + zl[i] - zu[i] - solver.jacl[i] end py = dual(solver.p) @inbounds @simd for i in eachindex(py) @@ -81,6 +105,7 @@ function set_aug_rhs!(solver::MadNLPSolver, kkt::SparseUnreducedKKTSystem, c) @inbounds @simd for i in eachindex(pzu) pzu[i] = (solver.xu_r[i] -solver.x_ur[i]) * kkt.u_lower[i] - solver.mu / kkt.u_lower[i] end +# >>>>>>> origin/master return end @@ -99,9 +124,13 @@ end function set_aug_rhs_RR!( solver::MadNLPSolver, kkt::AbstractKKTSystem, RR::RobustRestorer, rho, ) + x = full(solver.x) + xl = full(solver.xl) + xu = full(solver.xu) + px = primal(solver.p) @inbounds @simd for i in eachindex(px) - px[i] = -RR.f_R[i] -solver.jacl[i] + RR.mu_R / (solver.x[i] - solver.xl[i]) - RR.mu_R / (solver.xu[i] - solver.x[i]) + px[i] = -RR.f_R[i] -solver.jacl[i] + RR.mu_R / (x[i] - xl[i]) - RR.mu_R / (xu[i] - x[i]) end py = dual(solver.p) @inbounds @simd for i in eachindex(py) @@ -135,18 +164,32 @@ function finish_aug_solve!(solver::MadNLPSolver, kkt::SparseUnreducedKKTSystem, end # Initial +function set_initial_bounds!(solver::MadNLPSolver{T}) where T + @inbounds @simd for i in eachindex(solver.xl_r) + solver.xl_r[i] -= max(one(T),abs(solver.xl_r[i]))*solver.opt.tol + end + @inbounds @simd for i in eachindex(solver.xu_r) + solver.xu_r[i] += max(one(T),abs(solver.xu_r[i]))*solver.opt.tol + end +end function set_initial_rhs!(solver::MadNLPSolver{T}, kkt::AbstractKKTSystem) where T + f = primal(solver.f) + zl = primal(solver.zl) + zu = primal(solver.zu) px = primal(solver.p) @inbounds @simd for i in eachindex(px) - px[i] = -solver.f[i] + solver.zl[i] - solver.zu[i] + px[i] = -f[i] + zl[i] - zu[i] end fill!(dual(solver.p), zero(T)) return end function set_initial_rhs!(solver::MadNLPSolver{T}, kkt::SparseUnreducedKKTSystem) where T + f = primal(solver.f) + zl = primal(solver.zl) + zu = primal(solver.zu) px = primal(solver.p) @inbounds @simd for i in eachindex(px) - px[i] = -solver.f[i] + solver.zl[i] - solver.zu[i] + px[i] = -f[i] + zl[i] - zu[i] end fill!(dual(solver.p), zero(T)) fill!(dual_lb(solver.p), zero(T)) @@ -163,6 +206,16 @@ function set_aug_rhs_ifr!(solver::MadNLPSolver{T}, kkt::AbstractKKTSystem) where end return end +function set_g_ifr!(solver::MadNLPSolver, g) + f = full(solver.f) + x = full(solver.x) + xl = full(solver.xl) + xu = full(solver.xu) + @inbounds @simd for i in eachindex(g) + g[i] = f[i] - solver.mu / (x[i]-xl[i]) + solver.mu / (xu[i]-x[i]) + solver.jacl[i] + end +end + # Finish RR function finish_aug_solve_RR!(dpp, dnn, dzp, dzn, l, dl, pp, nn, zp, zn, mu_R, rho) @@ -175,6 +228,19 @@ function finish_aug_solve_RR!(dpp, dnn, dzp, dzn, l, dl, pp, nn, zp, zn, mu_R, r return end +# Scaling +function unscale!(solver::AbstractMadNLPSolver) + x_slk = slack(solver.x) + solver.obj_val /= solver.obj_scale[] + @inbounds @simd for i in eachindex(solver.c) + solver.c[i] /= solver.con_scale[i] + solver.c[i] += solver.rhs[i] + end + @inbounds @simd for i in eachindex(solver.c_slk) + solver.c_slk[i] += x_slk[i] + end +end + # Kernel functions --------------------------------------------------------- is_valid(val::Real) = !(isnan(val) || isinf(val)) function is_valid(vec::AbstractArray) diff --git a/src/IPM/restoration.jl b/src/IPM/restoration.jl index db69ebb5..ff765c67 100644 --- a/src/IPM/restoration.jl +++ b/src/IPM/restoration.jl @@ -66,7 +66,7 @@ function initialize_robust_restorer!(solver::AbstractMadNLPSolver{T}) where T solver.RR == nothing && (solver.RR = RobustRestorer(solver)) RR = solver.RR - copyto!(RR.x_ref, solver.x) + copyto!(RR.x_ref, full(solver.x)) RR.theta_ref = get_theta(solver.c) @inbounds @simd for i in eachindex(RR.D_R) RR.D_R[i] = min(one(T), one(T) / abs(RR.x_ref[i])) @@ -84,7 +84,7 @@ function initialize_robust_restorer!(solver::AbstractMadNLPSolver{T}) where T RR.zn[i] = RR.mu_R / RR.nn[i] end - RR.obj_val_R = get_obj_val_R(RR.pp,RR.nn,RR.D_R,solver.x,RR.x_ref,solver.opt.rho,RR.zeta) + RR.obj_val_R = get_obj_val_R(RR.pp,RR.nn,RR.D_R,full(solver.x),RR.x_ref,solver.opt.rho,RR.zeta) fill!(RR.f_R, zero(T)) empty!(RR.filter) push!(RR.filter, (solver.theta_max,-Inf)) diff --git a/src/IPM/solver.jl b/src/IPM/solver.jl index f8fa1079..60d0fd38 100644 --- a/src/IPM/solver.jl +++ b/src/IPM/solver.jl @@ -17,19 +17,20 @@ function initialize!(solver::AbstractMadNLPSolver{T}) where T @trace(solver.logger,"Initializing slack variables.") cons!(solver.nlp,get_x0(solver.nlp),_madnlp_unsafe_wrap(solver.c,get_ncon(solver.nlp))) solver.cnt.con_cnt += 1 - copyto!(solver.x_slk, solver.c_slk) + copyto!(slack(solver.x), solver.c_slk) # Initialization @trace(solver.logger,"Initializing primal and bound duals.") fill!(solver.zl_r, one(T)) fill!(solver.zu_r, one(T)) - @inbounds @simd for i in eachindex(solver.xl_r) - solver.xl_r[i] -= max(1,abs(solver.xl_r[i]))*solver.opt.tol - end - @inbounds @simd for i in eachindex(solver.xu_r) - solver.xu_r[i] += max(1,abs(solver.xu_r[i]))*solver.opt.tol - end - initialize_variables!(solver.x,solver.xl,solver.xu,solver.opt.bound_push,solver.opt.bound_fac) + + set_initial_bounds!(solver) + initialize_variables!( + full(solver.x), + full(solver.xl), + full(solver.xu), + solver.opt.bound_push,solver.opt.bound_fac + ) # Automatic scaling (constraints) @trace(solver.logger,"Computing constraint scaling.") @@ -47,8 +48,8 @@ function initialize!(solver::AbstractMadNLPSolver{T}) where T eval_grad_f_wrapper!(solver, solver.f,solver.x) @trace(solver.logger,"Computing objective scaling.") if solver.opt.nlp_scaling - solver.obj_scale[] = scale_objective(solver.nlp, solver.f; max_gradient=solver.opt.nlp_scaling_max_gradient) - _scal!(solver.obj_scale[], solver.f) + solver.obj_scale[] = scale_objective(solver.nlp, full(solver.f); max_gradient=solver.opt.nlp_scaling_max_gradient) + _scal!(solver.obj_scale[], full(solver.f)) end # Initialize dual variables @@ -82,7 +83,7 @@ end function reinitialize!(solver::AbstractMadNLPSolver) - view(solver.x,1:get_nvar(solver.nlp)) .= get_x0(solver.nlp) + variable(solver.x) .= get_x0(solver.nlp) solver.obj_val = eval_f_wrapper(solver, solver.x) eval_grad_f_wrapper!(solver, solver.f, solver.x) @@ -111,16 +112,16 @@ function solve!( ) if x != nothing - solver.x[1:get_nvar(nlp)] .= x + full(solver.x)[1:get_nvar(nlp)] .= x end if y != nothing solver.y[1:get_ncon(nlp)] .= y end if zl != nothing - solver.zl[1:get_nvar(nlp)] .= zl + full(solver.zl)[1:get_nvar(nlp)] .= zl end if zu != nothing - solver.zu[1:get_nvar(nlp)] .= zu + full(solver.zu)[1:get_nvar(nlp)] .= zu end if !isempty(kwargs) @@ -187,31 +188,32 @@ function solve!( end -function unscale!(solver::AbstractMadNLPSolver) - solver.obj_val /= solver.obj_scale[] - @inbounds @simd for i in eachindex(solver.c) - solver.c[i] /= solver.con_scale[i] - solver.c[i] += solver.rhs[i] - end - @inbounds @simd for i in eachindex(solver.c_slk) - solver.c_slk[i] += solver.x_slk[i] - end -end - -function regular!(solver::AbstractMadNLPSolver) +function regular!(solver::AbstractMadNLPSolver{T}) where T while true if (solver.cnt.k!=0 && !solver.opt.jacobian_constant) eval_jac_wrapper!(solver, solver.kkt, solver.x) end jtprod!(solver.jacl, solver.kkt, solver.y) fixed_variable_treatment_vec!(solver.jacl,solver.ind_fixed) - fixed_variable_treatment_z!(solver.zl,solver.zu,solver.f,solver.jacl,solver.ind_fixed) + fixed_variable_treatment_z!( + full(solver.zl), + full(solver.zu), + full(solver.f), + solver.jacl, + solver.ind_fixed, + ) sd = get_sd(solver.y,solver.zl_r,solver.zu_r,solver.opt.s_max) sc = get_sc(solver.zl_r,solver.zu_r,solver.opt.s_max) solver.inf_pr = get_inf_pr(solver.c) - solver.inf_du = get_inf_du(solver.f,solver.zl,solver.zu,solver.jacl,sd) + solver.inf_du = get_inf_du( + full(solver.f), + full(solver.zl), + full(solver.zu), + solver.jacl, + sd, + ) solver.inf_compl = get_inf_compl(solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,0.,sc) inf_compl_mu = get_inf_compl(solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,solver.mu,sc) @@ -267,10 +269,22 @@ function regular!(solver::AbstractMadNLPSolver) @trace(solver.logger,"Backtracking line search initiated.") theta = get_theta(solver.c) varphi= get_varphi(solver.obj_val,solver.x_lr,solver.xl_r,solver.xu_r,solver.x_ur,solver.mu) - varphi_d = get_varphi_d(solver.f,solver.x,solver.xl,solver.xu,primal(solver.d),solver.mu) - - - alpha_max = get_alpha_max(solver.x,solver.xl,solver.xu,primal(solver.d),solver.tau) + varphi_d = get_varphi_d( + primal(solver.f), + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + primal(solver.d), + solver.mu, + ) + + alpha_max = get_alpha_max( + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + primal(solver.d), + solver.tau, + ) solver.alpha_z = get_alpha_z(solver.zl_r,solver.zu_r,dual_lb(solver.d),dual_ub(solver.d),solver.tau) alpha_min = get_alpha_min(theta,varphi_d,solver.theta_min,solver.opt.gamma_theta,solver.opt.gamma_phi, solver.opt.alpha_min_frac,solver.opt.delta,solver.opt.s_theta,solver.opt.s_phi) @@ -278,12 +292,12 @@ function regular!(solver::AbstractMadNLPSolver) solver.alpha = alpha_max varphi_trial= 0. theta_trial = 0. - small_search_norm = get_rel_search_norm(solver.x, primal(solver.d)) < 10*eps(eltype(solver.x)) + small_search_norm = get_rel_search_norm(primal(solver.x), primal(solver.d)) < 10*eps(T) switching_condition = is_switching(varphi_d,solver.alpha,solver.opt.s_phi,solver.opt.delta,2.,solver.opt.s_theta) armijo_condition = false while true - copyto!(solver.x_trial, solver.x) - axpy!(solver.alpha, primal(solver.d), solver.x_trial) + copyto!(full(solver.x_trial), full(solver.x)) + axpy!(solver.alpha, primal(solver.d), primal(solver.x_trial)) solver.obj_val_trial = eval_f_wrapper(solver, solver.x_trial) eval_cons_wrapper!(solver, solver.c_trial, solver.x_trial) @@ -312,14 +326,14 @@ function regular!(solver::AbstractMadNLPSolver) return RESTORE else @trace(solver.logger,"Step rejected; proceed with the next trial step.") - solver.alpha * norm(primal(solver.d)) < eps(eltype(solver.x))*10 && + solver.alpha * norm(primal(solver.d)) < eps(T)*10 && return solver.cnt.acceptable_cnt >0 ? SOLVED_TO_ACCEPTABLE_LEVEL : SEARCH_DIRECTION_BECOMES_TOO_SMALL end end @trace(solver.logger,"Updating primal-dual variables.") - copyto!(solver.x, solver.x_trial) + copyto!(full(solver.x), full(solver.x_trial)) copyto!(solver.c, solver.c_trial) solver.obj_val = solver.obj_val_trial adjusted = adjust_boundary!(solver.x_lr,solver.xl_r,solver.x_ur,solver.xu_r,solver.mu) @@ -329,8 +343,18 @@ function regular!(solver::AbstractMadNLPSolver) axpy!(solver.alpha,dual(solver.d),solver.y) axpy!(solver.alpha_z, dual_lb(solver.d), solver.zl_r) axpy!(solver.alpha_z, dual_ub(solver.d), solver.zu_r) - reset_bound_dual!(solver.zl,solver.x,solver.xl,solver.mu,solver.opt.kappa_sigma) - reset_bound_dual!(solver.zu,solver.xu,solver.x,solver.mu,solver.opt.kappa_sigma) + reset_bound_dual!( + primal(solver.zl), + primal(solver.x), + primal(solver.xl), + solver.mu,solver.opt.kappa_sigma, + ) + reset_bound_dual!( + primal(solver.zu), + primal(solver.xu), + primal(solver.x), + solver.mu,solver.opt.kappa_sigma, + ) eval_grad_f_wrapper!(solver, solver.f,solver.x) if !switching_condition || !armijo_condition @@ -346,30 +370,45 @@ end function restore!(solver::AbstractMadNLPSolver) solver.del_w = 0 # Backup the previous primal iterate - copyto!(primal(solver._w1), solver.x) + copyto!(primal(solver._w1), full(solver.x)) copyto!(dual(solver._w1), solver.y) copyto!(dual(solver._w2), solver.c) - F = get_F(solver.c,solver.f,solver.zl,solver.zu,solver.jacl,solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,solver.mu) + F = get_F( + solver.c, + primal(solver.f), + primal(solver.zl), + primal(solver.zu), + solver.jacl, + solver.x_lr, + solver.xl_r, + solver.zl_r, + solver.xu_r, + solver.x_ur, + solver.zu_r, + solver.mu, + ) solver.cnt.t = 0 solver.alpha_z = 0.0 solver.ftype = "R" while true - solver.alpha = min(get_alpha_max(solver.x,solver.xl,solver.xu,primal(solver.d),solver.tau), - get_alpha_z(solver.zl_r,solver.zu_r,dual_lb(solver.d),dual_ub(solver.d),solver.tau)) - - axpy!(solver.alpha, primal(solver.d), solver.x) + alpha_max = get_alpha_max( + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + primal(solver.d), + solver.tau, + ) + solver.alpha = min( + alpha_max, + get_alpha_z(solver.zl_r,solver.zu_r,dual_lb(solver.d),dual_ub(solver.d),solver.tau), + ) + + axpy!(solver.alpha, primal(solver.d), full(solver.x)) axpy!(solver.alpha, dual(solver.d), solver.y) - # Note: axpy! does not support non-contiguous view - dlb = dual_lb(solver.d) - @inbounds @simd for i in eachindex(solver.zl_r) - solver.zl_r[i] += solver.alpha * dlb[i] - end - dub = dual_ub(solver.d) - @inbounds @simd for i in eachindex(solver.zu_r) - solver.zu_r[i] += solver.alpha * dub[i] - end + axpy!(solver.alpha, dual_lb(solver.d), solver.zl_r) + axpy!(solver.alpha, dual_ub(solver.d), solver.zu_r) eval_cons_wrapper!(solver,solver.c,solver.x) eval_grad_f_wrapper!(solver,solver.f,solver.x) @@ -379,9 +418,21 @@ function restore!(solver::AbstractMadNLPSolver) jtprod!(solver.jacl,solver.kkt,solver.y) F_trial = get_F( - solver.c,solver.f,solver.zl,solver.zu,solver.jacl,solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,solver.mu) + solver.c, + primal(solver.f), + primal(solver.zl), + primal(solver.zu), + solver.jacl, + solver.x_lr, + solver.xl_r, + solver.zl_r, + solver.xu_r, + solver.x_ur, + solver.zu_r, + solver.mu, + ) if F_trial > solver.opt.soft_resto_pderror_reduction_factor*F - copyto!(solver.x, primal(solver._w1)) + copyto!(primal(solver.x), primal(solver._w1)) copyto!(solver.y, dual(solver._w1)) copyto!(solver.c, dual(solver._w2)) # backup the previous primal iterate return ROBUST @@ -407,7 +458,13 @@ function restore!(solver::AbstractMadNLPSolver) sd = get_sd(solver.y,solver.zl_r,solver.zu_r,solver.opt.s_max) sc = get_sc(solver.zl_r,solver.zu_r,solver.opt.s_max) solver.inf_pr = get_inf_pr(solver.c) - solver.inf_du = get_inf_du(solver.f,solver.zl,solver.zu,solver.jacl,sd) + solver.inf_du = get_inf_du( + primal(solver.f), + primal(solver.zl), + primal(solver.zu), + solver.jacl, + sd, + ) solver.inf_compl = get_inf_compl(solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,0.,sc) inf_compl_mu = get_inf_compl(solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,solver.mu,sc) @@ -426,7 +483,7 @@ function restore!(solver::AbstractMadNLPSolver) end end -function robust!(solver::MadNLPSolver) +function robust!(solver::MadNLPSolver{T}) where T initialize_robust_restorer!(solver) RR = solver.RR while true @@ -435,19 +492,31 @@ function robust!(solver::MadNLPSolver) end jtprod!(solver.jacl, solver.kkt, solver.y) fixed_variable_treatment_vec!(solver.jacl,solver.ind_fixed) - fixed_variable_treatment_z!(solver.zl,solver.zu,solver.f,solver.jacl,solver.ind_fixed) + fixed_variable_treatment_z!( + full(solver.zl), + full(solver.zu), + full(solver.f), + solver.jacl, + solver.ind_fixed, + ) # evaluate termination criteria @trace(solver.logger,"Evaluating restoration phase termination criteria.") sd = get_sd(solver.y,solver.zl_r,solver.zu_r,solver.opt.s_max) sc = get_sc(solver.zl_r,solver.zu_r,solver.opt.s_max) solver.inf_pr = get_inf_pr(solver.c) - solver.inf_du = get_inf_du(solver.f,solver.zl,solver.zu,solver.jacl,sd) + solver.inf_du = get_inf_du( + primal(solver.f), + primal(solver.zl), + primal(solver.zu), + solver.jacl, + sd, + ) solver.inf_compl = get_inf_compl(solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,0.,sc) # Robust restoration phase error RR.inf_pr_R = get_inf_pr_R(solver.c,RR.pp,RR.nn) - RR.inf_du_R = get_inf_du_R(RR.f_R,solver.y,solver.zl,solver.zu,solver.jacl,RR.zp,RR.zn,solver.opt.rho,sd) + RR.inf_du_R = get_inf_du_R(RR.f_R,solver.y,primal(solver.zl),primal(solver.zu),solver.jacl,RR.zp,RR.zn,solver.opt.rho,sd) RR.inf_compl_R = get_inf_compl_R( solver.x_lr,solver.xl_r,solver.zl_r,solver.xu_r,solver.x_ur,solver.zu_r,RR.pp,RR.zp,RR.nn,RR.zn,0.,sc) inf_compl_mu_R = get_inf_compl_R( @@ -493,10 +562,23 @@ function robust!(solver::MadNLPSolver) theta_R = get_theta_R(solver.c,RR.pp,RR.nn) varphi_R = get_varphi_R(RR.obj_val_R,solver.x_lr,solver.xl_r,solver.xu_r,solver.x_ur,RR.pp,RR.nn,RR.mu_R) - varphi_d_R = get_varphi_d_R(RR.f_R,solver.x,solver.xl,solver.xu,primal(solver.d),RR.pp,RR.nn,RR.dpp,RR.dnn,RR.mu_R,solver.opt.rho) + varphi_d_R = get_varphi_d_R( + RR.f_R, + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + primal(solver.d), + RR.pp,RR.nn,RR.dpp,RR.dnn,RR.mu_R,solver.opt.rho, + ) # set alpha_min - alpha_max = get_alpha_max_R(solver.x,solver.xl,solver.xu,primal(solver.d),RR.pp,RR.dpp,RR.nn,RR.dnn,RR.tau_R) + alpha_max = get_alpha_max_R( + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + primal(solver.d), + RR.pp,RR.dpp,RR.nn,RR.dnn,RR.tau_R, + ) solver.alpha_z = get_alpha_z_R(solver.zl_r,solver.zu_r,dual_lb(solver.d),dual_ub(solver.d),RR.zp,RR.dzp,RR.zn,RR.dzn,RR.tau_R) alpha_min = get_alpha_min(theta_R,varphi_d_R,solver.theta_min,solver.opt.gamma_theta,solver.opt.gamma_phi, solver.opt.alpha_min_frac,solver.opt.delta,solver.opt.s_theta,solver.opt.s_phi) @@ -507,20 +589,20 @@ function robust!(solver::MadNLPSolver) solver.cnt.l = 1 theta_R_trial = 0. varphi_R_trial = 0. - small_search_norm = get_rel_search_norm(solver.x, primal(solver.d)) < 10*eps(eltype(solver.x)) + small_search_norm = get_rel_search_norm(primal(solver.x), primal(solver.d)) < 10*eps(T) switching_condition = is_switching(varphi_d_R,solver.alpha,solver.opt.s_phi,solver.opt.delta,theta_R,solver.opt.s_theta) armijo_condition = false while true - copyto!(solver.x_trial,solver.x) + copyto!(full(solver.x_trial), full(solver.x)) copyto!(RR.pp_trial,RR.pp) copyto!(RR.nn_trial,RR.nn) - axpy!(solver.alpha,primal(solver.d),solver.x_trial) + axpy!(solver.alpha,primal(solver.d),primal(solver.x_trial)) axpy!(solver.alpha,RR.dpp,RR.pp_trial) axpy!(solver.alpha,RR.dnn,RR.nn_trial) RR.obj_val_R_trial = get_obj_val_R( - RR.pp_trial,RR.nn_trial,RR.D_R,solver.x_trial,RR.x_ref,solver.opt.rho,RR.zeta) + RR.pp_trial,RR.nn_trial,RR.D_R,primal(solver.x_trial),RR.x_ref,solver.opt.rho,RR.zeta) eval_cons_wrapper!(solver, solver.c_trial, solver.x_trial) theta_R_trial = get_theta_R(solver.c_trial,RR.pp_trial,RR.nn_trial) varphi_R_trial = get_varphi_R( @@ -543,21 +625,19 @@ function robust!(solver::MadNLPSolver) return RESTORATION_FAILED else @trace(solver.logger,"Step rejected; proceed with the next trial step.") - solver.alpha < eps(eltype(solver.x))*10 && return solver.cnt.acceptable_cnt >0 ? + solver.alpha < eps(T)*10 && return solver.cnt.acceptable_cnt >0 ? SOLVED_TO_ACCEPTABLE_LEVEL : SEARCH_DIRECTION_BECOMES_TOO_SMALL end end @trace(solver.logger,"Updating primal-dual variables.") - copyto!(solver.x, solver.x_trial) + copyto!(full(solver.x), full(solver.x_trial)) copyto!(solver.c, solver.c_trial) copyto!(RR.pp, RR.pp_trial) copyto!(RR.nn, RR.nn_trial) RR.obj_val_R=RR.obj_val_R_trial - @inbounds @simd for i in eachindex(RR.f_R) - RR.f_R[i] = RR.zeta * RR.D_R[i]^2 *(solver.x[i]-RR.x_ref[i]) - end + set_f_RR!(solver,RR) axpy!(solver.alpha, dual(solver.d), solver.y) axpy!(solver.alpha_z, dual_lb(solver.d),solver.zl_r) @@ -565,8 +645,18 @@ function robust!(solver::MadNLPSolver) axpy!(solver.alpha_z, RR.dzp,RR.zp) axpy!(solver.alpha_z, RR.dzn,RR.zn) - reset_bound_dual!(solver.zl,solver.x,solver.xl,RR.mu_R,solver.opt.kappa_sigma) - reset_bound_dual!(solver.zu,solver.xu,solver.x,RR.mu_R,solver.opt.kappa_sigma) + reset_bound_dual!( + primal(solver.zl), + primal(solver.x), + primal(solver.xl), + RR.mu_R, solver.opt.kappa_sigma, + ) + reset_bound_dual!( + primal(solver.zu), + primal(solver.xu), + primal(solver.x), + RR.mu_R, solver.opt.kappa_sigma, + ) reset_bound_dual!(RR.zp,RR.pp,RR.mu_R,solver.opt.kappa_sigma) reset_bound_dual!(RR.zn,RR.nn,RR.mu_R,solver.opt.kappa_sigma) @@ -662,12 +752,10 @@ function inertia_free_reg(solver::MadNLPSolver) t = primal(solver._w3) n = primal(solver._w2) wx= primal(solver._w4) + g = full(solver.x_trial) # just to avoid new allocation + fill!(dual(solver._w3), 0) - - g = solver.x_trial # just to avoid new allocation - @inbounds @simd for i in eachindex(g) - g[i] = solver.f[i] - solver.mu / (solver.x[i]-solver.xl[i]) + solver.mu / (solver.xu[i]-solver.x[i]) + solver.jacl[i] - end + set_g_ifr!(solver,g) fixed_variable_treatment_vec!(primal(solver._w1), solver.ind_fixed) fixed_variable_treatment_vec!(primal(solver.p), solver.ind_fixed) @@ -675,9 +763,8 @@ function inertia_free_reg(solver::MadNLPSolver) factorize_wrapper!(solver) solve_status = (solve_refine_wrapper!(solver,d0,p0) && solve_refine_wrapper!(solver,solver.d,solver.p)) - @inbounds @simd for i in eachindex(t) - t[i] = dx[i] - n[i] - end + copyto!(t,dx) + axpy!(-1.,n,t) mul!(solver._w4, solver.kkt, solver._w3) # prepartation for curv_test n_trial = 0 solver.del_w = del_w_prev = 0. @@ -701,9 +788,9 @@ function inertia_free_reg(solver::MadNLPSolver) factorize_wrapper!(solver) solve_status = (solve_refine_wrapper!(solver,d0,p0) && solve_refine_wrapper!(solver,solver.d,solver.p)) - @inbounds @simd for i in eachindex(dx) - t[i] = dx[i] - n[i] - end + copyto!(t,dx) + axpy!(-1.,n,t) + mul!(solver._w4, solver.kkt, solver._w3) # prepartation for curv_test n_trial += 1 end @@ -720,20 +807,27 @@ function second_order_correction(solver::AbstractMadNLPSolver,alpha_max,theta,va wx = primal(solver._w1) wy = dual(solver._w1) - @inbounds @simd for i in eachindex(wy) - wy[i] = alpha_max * solver.c[i] + solver.c_trial[i] - end + copyto!(wy, solver.c_trial) + axpy!(alpha_max, solver.c, wy) + theta_soc_old = theta_trial for p=1:solver.opt.max_soc # compute second order correction set_aug_rhs!(solver, solver.kkt, wy) - dual_inf_perturbation!(primal(solver.p),solver.ind_llb,solver.ind_uub,solver.mu,solver.opt.kappa_d) + dual_inf_perturbation!( + primal(solver.p), + solver.ind_llb,solver.ind_uub,solver.mu,solver.opt.kappa_d, + ) solve_refine_wrapper!(solver,solver._w1,solver.p) - alpha_soc = get_alpha_max(solver.x,solver.xl,solver.xu,wx,solver.tau) - - copyto!(solver.x_trial, solver.x) - axpy!(alpha_soc, wx, solver.x_trial) - eval_cons_wrapper!(solver, solver.c_trial,solver.x_trial) + alpha_soc = get_alpha_max( + primal(solver.x), + primal(solver.xl), + primal(solver.xu), + wx,solver.tau) + + copyto!(primal(solver.x_trial), primal(solver.x)) + axpy!(alpha_soc, wx, primal(solver.x_trial)) + eval_cons_wrapper!(solver, solver.c_trial, solver.x_trial) solver.obj_val_trial = eval_f_wrapper(solver, solver.x_trial) theta_soc = get_theta(solver.c_trial) diff --git a/src/IPM/utils.jl b/src/IPM/utils.jl index d3075f70..43e04569 100644 --- a/src/IPM/utils.jl +++ b/src/IPM/utils.jl @@ -15,12 +15,12 @@ end MadNLPExecutionStats(solver::MadNLPSolver) =MadNLPExecutionStats( solver.status, - _madnlp_unsafe_wrap(solver.x, get_nvar(solver.nlp)), + primal(solver.x), solver.obj_val,solver.c, solver.inf_du, solver.inf_pr, solver.y, - _madnlp_unsafe_wrap(solver.zl, get_nvar(solver.nlp)), - _madnlp_unsafe_wrap(solver.zu, get_nvar(solver.nlp)), + primal(solver.zl), + primal(solver.zu), solver.cnt.k, get_counters(solver.nlp),solver.cnt.total_time ) diff --git a/src/KKT/rhs.jl b/src/KKT/rhs.jl index f5080d46..52aaa51c 100644 --- a/src/KKT/rhs.jl +++ b/src/KKT/rhs.jl @@ -152,3 +152,30 @@ primal_dual(rhs::UnreducedKKTVector) = rhs.x dual_lb(rhs::UnreducedKKTVector) = rhs.xzl dual_ub(rhs::UnreducedKKTVector) = rhs.xzu + +""" + PrimalVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + +Primal vector ``(x, s)``. + +""" +struct PrimalVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + values::VT + x::VT # unsafe view + s::VT # unsafe view +end + +function PrimalVector{T, VT}(nx::Int, ns::Int) where {T, VT <: AbstractVector{T}} + values = VT(undef, nx+ns) ; fill!(values, zero(T)) + return PrimalVector{T, VT}( + values, + _madnlp_unsafe_wrap(values, nx), + _madnlp_unsafe_wrap(values, ns, nx+1), + ) +end + +full(rhs::PrimalVector) = rhs.values +primal(rhs::PrimalVector) = rhs.values +variable(rhs::PrimalVector) = rhs.x +slack(rhs::PrimalVector) = rhs.s + diff --git a/src/KKT/sparse.jl b/src/KKT/sparse.jl index fc5e1467..4989662f 100644 --- a/src/KKT/sparse.jl +++ b/src/KKT/sparse.jl @@ -7,6 +7,7 @@ Implement the [`AbstractReducedKKTSystem`](@ref) in sparse COO format. """ struct SparseKKTSystem{T, VT, MT} <: AbstractReducedKKTSystem{T, VT, MT} hess::VT + jac_callback::VT jac::VT pr_diag::VT du_diag::VT @@ -33,6 +34,7 @@ Implement the [`AbstractUnreducedKKTSystem`](@ref) in sparse COO format. """ struct SparseUnreducedKKTSystem{T, VT, MT} <: AbstractUnreducedKKTSystem{T, VT, MT} hess::VT + jac_callback::VT jac::VT pr_diag::VT du_diag::VT @@ -72,6 +74,8 @@ function jtprod!(y::AbstractVector, kkt::AbstractSparseKKTSystem, x::AbstractVec mul!(y, kkt.jac_com', x) end +get_jacobian(kkt::AbstractSparseKKTSystem) = kkt.jac_callback + nnz_jacobian(kkt::AbstractSparseKKTSystem) = nnz(kkt.jac_raw) function compress_jacobian!(kkt::AbstractSparseKKTSystem{T, VT, MT}) where {T, VT, MT<:SparseMatrixCSC{T, Int32}} @@ -106,37 +110,47 @@ function SparseKKTSystem{T, VT, MT}( hess_sparsity_I, hess_sparsity_J, jac_sparsity_I, jac_sparsity_J, ) where {T, VT, MT} + n_slack = length(ind_ineq) n_jac = length(jac_sparsity_I) n_hess = length(hess_sparsity_I) + n_tot = n + n_slack - aug_vec_length = n+m - aug_mat_length = n+m+n_hess+n_jac + aug_vec_length = n_tot+m + aug_mat_length = n_tot+m+n_hess+n_jac+n_slack I = Vector{Int32}(undef, aug_mat_length) J = Vector{Int32}(undef, aug_mat_length) V = VT(undef, aug_mat_length) fill!(V, 0.0) # Need to initiate V to avoid NaN - offset = n+n_jac+n_hess+m + offset = n_tot+n_jac+n_slack+n_hess+m - I[1:n] .= 1:n - I[n+1:n+n_hess] = hess_sparsity_I - I[n+n_hess+1:n+n_hess+n_jac] .= (jac_sparsity_I.+n) - I[n+n_hess+n_jac+1:offset] .= (n+1:n+m) + I[1:n_tot] .= 1:n_tot + I[n_tot+1:n_tot+n_hess] = hess_sparsity_I + I[n_tot+n_hess+1:n_tot+n_hess+n_jac] .= (jac_sparsity_I.+n_tot) + I[n_tot+n_hess+n_jac+1:n_tot+n_hess+n_jac+n_slack] .= ind_ineq .+ n_tot + I[n_tot+n_hess+n_jac+n_slack+1:offset] .= (n_tot+1:n_tot+m) - J[1:n] .= 1:n - J[n+1:n+n_hess] = hess_sparsity_J - J[n+n_hess+1:n+n_hess+n_jac] .= jac_sparsity_J - J[n+n_hess+n_jac+1:offset] .= (n+1:n+m) + J[1:n_tot] .= 1:n_tot + J[n_tot+1:n_tot+n_hess] = hess_sparsity_J + J[n_tot+n_hess+1:n_tot+n_hess+n_jac] .= jac_sparsity_J + J[n_tot+n_hess+n_jac+1:n_tot+n_hess+n_jac+n_slack] .= (n+1:n+n_slack) + J[n_tot+n_hess+n_jac+n_slack+1:offset] .= (n_tot+1:n_tot+m) - pr_diag = _madnlp_unsafe_wrap(V, n) - du_diag = _madnlp_unsafe_wrap(V, m, n_jac+n_hess+n+1) + pr_diag = _madnlp_unsafe_wrap(V, n_tot) + du_diag = _madnlp_unsafe_wrap(V, m, n_jac+n_slack+n_hess+n_tot+1) - hess = _madnlp_unsafe_wrap(V, n_hess, n+1) - jac = _madnlp_unsafe_wrap(V, n_jac, n_hess+n+1) + hess = _madnlp_unsafe_wrap(V, n_hess, n_tot+1) + jac = _madnlp_unsafe_wrap(V, n_jac+n_slack, n_hess+n_tot+1) + jac_callback = _madnlp_unsafe_wrap(V, n_jac, n_hess+n_tot+1) aug_raw = SparseMatrixCOO(aug_vec_length,aug_vec_length,I,J,V) - jac_raw = SparseMatrixCOO(m,n,jac_sparsity_I,jac_sparsity_J,jac) + jac_raw = SparseMatrixCOO( + m, n_tot, + Int32[jac_sparsity_I; ind_ineq], + Int32[jac_sparsity_J; n+1:n+n_slack], + jac, + ) aug_com = MT(aug_raw) jac_com = MT(jac_raw) @@ -149,10 +163,10 @@ function SparseKKTSystem{T, VT, MT}( else zeros(Int, 0) end - jac_scaling = ones(T, n_jac) + jac_scaling = ones(T, n_jac+n_slack) return SparseKKTSystem{T, VT, MT}( - hess, jac, pr_diag, du_diag, + hess, jac_callback, jac, pr_diag, du_diag, aug_raw, aug_com, aug_csc_map, jac_raw, jac_com, jac_csc_map, ind_ineq, ind_fixed, ind_aug_fixed, jac_scaling, @@ -161,9 +175,8 @@ end # Build KKT system directly from AbstractNLPModel function SparseKKTSystem{T, VT, MT}(nlp::AbstractNLPModel, ind_cons=get_index_constraints(nlp)) where {T, VT, MT} - n_slack = length(ind_cons.ind_ineq) # Deduce KKT size. - n = get_nvar(nlp) + n_slack + n = get_nvar(nlp) m = get_ncon(nlp) # Evaluate sparsity pattern jac_I = Vector{Int32}(undef, get_nnzj(nlp.meta)) @@ -175,9 +188,6 @@ function SparseKKTSystem{T, VT, MT}(nlp::AbstractNLPModel, ind_cons=get_index_co hess_structure!(nlp,hess_I,hess_J) force_lower_triangular!(hess_I,hess_J) - # Incorporate slack's sparsity pattern - append!(jac_I, ind_cons.ind_ineq) - append!(jac_J, get_nvar(nlp)+1:get_nvar(nlp)+n_slack) return SparseKKTSystem{T, VT, MT}( n, m, ind_cons.ind_ineq, ind_cons.ind_fixed, @@ -199,50 +209,60 @@ function SparseUnreducedKKTSystem{T, VT, MT}( jac_sparsity_I, jac_sparsity_J, ind_lb, ind_ub, ) where {T, VT, MT} + n_slack = length(ind_ineq) n_jac = length(jac_sparsity_I) n_hess = length(hess_sparsity_I) + n_tot = n + n_slack - aug_mat_length = n + m + n_hess + n_jac + 2*nlb + 2*nub - aug_vec_length = n+m+nlb+nub + aug_mat_length = n_tot + m + n_hess + n_jac + n_slack + 2*nlb + 2*nub + aug_vec_length = n_tot + m + nlb + nub I = Vector{Int32}(undef, aug_mat_length) J = Vector{Int32}(undef, aug_mat_length) V = zeros(aug_mat_length) - offset = n+n_jac+n_hess+m - - I[1:n] .= 1:n - I[n+1:n+n_hess] = hess_sparsity_I - I[n+n_hess+1:n+n_hess+n_jac].=(jac_sparsity_I.+n) - I[n+n_hess+n_jac+1:offset].=(n+1:n+m) - - J[1:n] .= 1:n - J[n+1:n+n_hess] = hess_sparsity_J - J[n+n_hess+1:n+n_hess+n_jac].=jac_sparsity_J - J[n+n_hess+n_jac+1:offset].=(n+1:n+m) - - I[offset+1:offset+nlb] .= (1:nlb).+(n+m) - I[offset+nlb+1:offset+2nlb] .= (1:nlb).+(n+m) - I[offset+2nlb+1:offset+2nlb+nub] .= (1:nub).+(n+m+nlb) - I[offset+2nlb+nub+1:offset+2nlb+2nub] .= (1:nub).+(n+m+nlb) - J[offset+1:offset+nlb] .= (1:nlb).+(n+m) + offset = n_tot + n_jac + n_slack + n_hess + m + + I[1:n_tot] .= 1:n_tot + I[n_tot+1:n_tot+n_hess] = hess_sparsity_I + I[n_tot+n_hess+1:n_tot+n_hess+n_jac].=(jac_sparsity_I.+n_tot) + I[n_tot+n_hess+n_jac+1:n_tot+n_hess+n_jac+n_slack].=(ind_ineq.+n_tot) + I[n_tot+n_hess+n_jac+n_slack+1:offset].=(n_tot+1:n_tot+m) + + J[1:n_tot] .= 1:n_tot + J[n_tot+1:n_tot+n_hess] = hess_sparsity_J + J[n_tot+n_hess+1:n_tot+n_hess+n_jac] .= jac_sparsity_J + J[n_tot+n_hess+n_jac+1:n_tot+n_hess+n_jac+n_slack] .= (n+1:n+n_slack) + J[n_tot+n_hess+n_jac+n_slack+1:offset].=(n_tot+1:n_tot+m) + + I[offset+1:offset+nlb] .= (1:nlb).+(n_tot+m) + I[offset+nlb+1:offset+2nlb] .= (1:nlb).+(n_tot+m) + I[offset+2nlb+1:offset+2nlb+nub] .= (1:nub).+(n_tot+m+nlb) + I[offset+2nlb+nub+1:offset+2nlb+2nub] .= (1:nub).+(n_tot+m+nlb) + J[offset+1:offset+nlb] .= (1:nlb).+(n_tot+m) J[offset+nlb+1:offset+2nlb] .= ind_lb - J[offset+2nlb+1:offset+2nlb+nub] .= (1:nub).+(n+m+nlb) + J[offset+2nlb+1:offset+2nlb+nub] .= (1:nub).+(n_tot+m+nlb) J[offset+2nlb+nub+1:offset+2nlb+2nub] .= ind_ub - pr_diag = _madnlp_unsafe_wrap(V,n) - du_diag = _madnlp_unsafe_wrap(V,m, n_jac+n_hess+n+1) + pr_diag = _madnlp_unsafe_wrap(V,n_tot) + du_diag = _madnlp_unsafe_wrap(V,m, n_jac + n_slack+n_hess+n_tot+1) l_diag = _madnlp_unsafe_wrap(V, nlb, offset+1) l_lower= _madnlp_unsafe_wrap(V, nlb, offset+nlb+1) u_diag = _madnlp_unsafe_wrap(V, nub, offset+2nlb+1) u_lower= _madnlp_unsafe_wrap(V, nub, offset+2nlb+nub+1) - hess = _madnlp_unsafe_wrap(V, n_hess, n+1) - jac = _madnlp_unsafe_wrap(V, n_jac, n_hess+n+1) + hess = _madnlp_unsafe_wrap(V, n_hess, n_tot+1) + jac = _madnlp_unsafe_wrap(V, n_jac + n_slack, n_hess+n_tot+1) + jac_callback = _madnlp_unsafe_wrap(V, n_jac, n_hess+n_tot+1) aug_raw = SparseMatrixCOO(aug_vec_length,aug_vec_length,I,J,V) - jac_raw = SparseMatrixCOO(m,n,jac_sparsity_I,jac_sparsity_J,jac) + jac_raw = SparseMatrixCOO( + m, n_tot, + Int32[jac_sparsity_I; ind_ineq], + Int32[jac_sparsity_J; n+1:n+n_slack], + jac, + ) aug_com = MT(aug_raw) jac_com = MT(jac_raw) @@ -250,7 +270,7 @@ function SparseUnreducedKKTSystem{T, VT, MT}( aug_csc_map = get_mapping(aug_com, aug_raw) jac_csc_map = get_mapping(jac_com, jac_raw) - jac_scaling = ones(T, n_jac) + jac_scaling = ones(T, n_jac+n_slack) ind_aug_fixed = if isa(aug_com, SparseMatrixCSC) _get_fixed_variable_index(aug_com, ind_fixed) @@ -259,7 +279,7 @@ function SparseUnreducedKKTSystem{T, VT, MT}( end return SparseUnreducedKKTSystem{T, VT, MT}( - hess, jac, pr_diag, du_diag, + hess, jac_callback, jac, pr_diag, du_diag, l_diag, u_diag, l_lower, u_lower, aug_raw, aug_com, aug_csc_map, jac_raw, jac_com, jac_csc_map, @@ -272,7 +292,7 @@ function SparseUnreducedKKTSystem{T, VT, MT}(nlp::AbstractNLPModel, ind_cons=get nlb = length(ind_cons.ind_lb) nub = length(ind_cons.ind_ub) # Deduce KKT size. - n = get_nvar(nlp) + n_slack + n = get_nvar(nlp) m = get_ncon(nlp) # Evaluate sparsity pattern jac_I = Vector{Int32}(undef, get_nnzj(nlp.meta)) @@ -284,9 +304,6 @@ function SparseUnreducedKKTSystem{T, VT, MT}(nlp::AbstractNLPModel, ind_cons=get hess_structure!(nlp,hess_I,hess_J) force_lower_triangular!(hess_I,hess_J) - # Incorporate slack's sparsity pattern - append!(jac_I, ind_cons.ind_ineq) - append!(jac_J, get_nvar(nlp)+1:get_nvar(nlp)+n_slack) return SparseUnreducedKKTSystem{T, VT, MT}( n, m, nlb, nub, ind_cons.ind_ineq, ind_cons.ind_fixed, diff --git a/test/madnlp_dense.jl b/test/madnlp_dense.jl index 9c021b82..2c80602c 100644 --- a/test/madnlp_dense.jl +++ b/test/madnlp_dense.jl @@ -33,15 +33,15 @@ function _compare_dense_with_sparse( solver = MadNLPSolver(nlp; sparse_options...) solverd = MadNLPSolver(nlp; dense_options...) - MadNLP.solve!(solver) - MadNLP.solve!(solverd) + result_sparse = MadNLP.solve!(solver) + result_dense = MadNLP.solve!(solverd) # Check that dense formulation matches exactly sparse formulation - @test solverd.status == MadNLP.SOLVE_SUCCEEDED - @test solver.cnt.k == solverd.cnt.k - @test solver.obj_val ≈ solverd.obj_val atol=atol - @test solver.x ≈ solverd.x atol=atol - @test solver.y ≈ solverd.y atol=atol + @test result_dense.status == MadNLP.SOLVE_SUCCEEDED + @test result_sparse.iter == result_dense.iter + @test result_sparse.objective ≈ result_dense.objective atol=atol + @test result_sparse.solution ≈ result_dense.solution atol=atol + @test result_sparse.multipliers ≈ result_dense.multipliers atol=atol @test solver.kkt.jac_com[:, 1:n] == solverd.kkt.jac if isa(solverd.kkt, MadNLP.AbstractReducedKKTSystem) @test Symmetric(solver.kkt.aug_com, :L) ≈ solverd.kkt.aug_com atol=atol diff --git a/test/madnlp_test.jl b/test/madnlp_test.jl index 3f0a4124..5cc383fe 100644 --- a/test/madnlp_test.jl +++ b/test/madnlp_test.jl @@ -118,6 +118,30 @@ end @test solver.status == MadNLP.SOLVE_SUCCEEDED end +@testset "MadNLP callback allocations" begin + nlp = MadNLPTests.HS15Model() + solver = MadNLPSolver(nlp) + kkt = solver.kkt + x, f, c = solver.x, solver.f, solver.c + # Precompile + MadNLP.eval_f_wrapper(solver, x) + MadNLP.eval_grad_f_wrapper!(solver, f, x) + MadNLP.eval_cons_wrapper!(solver, c, x) + MadNLP.eval_jac_wrapper!(solver, kkt, x) + MadNLP.eval_lag_hess_wrapper!(solver, kkt, x, solver.y) + + n_allocs = @allocated MadNLP.eval_f_wrapper(solver, x) + @test n_allocs == 16 # objective is still allocating + n_allocs = @allocated MadNLP.eval_grad_f_wrapper!(solver, f, x) + @test n_allocs == 0 + n_allocs = @allocated MadNLP.eval_cons_wrapper!(solver, c, x) + @test n_allocs == 0 + n_allocs = @allocated MadNLP.eval_jac_wrapper!(solver, kkt, x) + @test n_allocs == 0 + n_allocs = @allocated MadNLP.eval_lag_hess_wrapper!(solver, kkt, x, solver.y) + @test n_allocs == 0 +end + @testset "MadNLP timings" begin nlp = MadNLPTests.HS15Model() solver = MadNLPSolver(nlp)