diff --git a/.ci/ci.jl b/.ci/ci.jl index af7c4032..80c9b323 100644 --- a/.ci/ci.jl +++ b/.ci/ci.jl @@ -11,6 +11,8 @@ if ARGS[1] == "full" pkgs = ["MadNLPHSL","MadNLPPardiso","MadNLPMumps","MadNLPGPU","MadNLPKrylov"] elseif ARGS[1] == "basic" pkgs = ["MadNLPMumps","MadNLPKrylov"] +elseif ARGS[1] == "gpu" + pkgs = ["MadNLPGPU"] else error("proper argument should be given - full or basic") end diff --git a/lib/MadNLPGPU/src/kernels.jl b/lib/MadNLPGPU/src/kernels.jl index d9861733..9a903ce4 100644 --- a/lib/MadNLPGPU/src/kernels.jl +++ b/lib/MadNLPGPU/src/kernels.jl @@ -123,7 +123,7 @@ end DenseKKTSystem kernels =# -function MadNLP.mul!(y::AbstractVector, kkt::MadNLP.DenseKKTSystem{T, VT, MT}, x::AbstractVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} +function LinearAlgebra.mul!(y::AbstractVector, kkt::MadNLP.DenseKKTSystem{T, VT, MT}, x::AbstractVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} # Load buffers haskey(kkt.etc, :hess_w1) || (kkt.etc[:hess_w1] = CuVector{T}(undef, size(kkt.aug_com, 1))) haskey(kkt.etc, :hess_w2) || (kkt.etc[:hess_w2] = CuVector{T}(undef, size(kkt.aug_com, 1))) @@ -136,6 +136,9 @@ function MadNLP.mul!(y::AbstractVector, kkt::MadNLP.DenseKKTSystem{T, VT, MT}, x LinearAlgebra.mul!(d_y, kkt.aug_com, d_x) copyto!(y, d_y) end +function LinearAlgebra.mul!(y::MadNLP.ReducedKKTVector, kkt::MadNLP.DenseKKTSystem{T, VT, MT}, x::MadNLP.ReducedKKTVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} + LinearAlgebra.mul!(MadNLP.full(y), kkt, MadNLP.full(x)) +end @kernel function _build_dense_kkt_system_kernel!( dest, hess, jac, pr_diag, du_diag, diag_hess, ind_ineq, con_scale, n, m, ns @@ -252,7 +255,7 @@ function MadNLP._build_condensed_kkt_system!( wait(ev) end -function MadNLP.mul!(y::AbstractVector, kkt::MadNLP.DenseCondensedKKTSystem{T, VT, MT}, x::AbstractVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} +function LinearAlgebra.mul!(y::AbstractVector, kkt::MadNLP.DenseCondensedKKTSystem{T, VT, MT}, x::AbstractVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} if length(y) == length(x) == size(kkt.aug_com, 1) # Load buffers haskey(kkt.etc, :hess_w1) || (kkt.etc[:hess_w1] = CuVector{T}(undef, size(kkt.aug_com, 1))) @@ -279,6 +282,9 @@ function MadNLP.mul!(y::AbstractVector, kkt::MadNLP.DenseCondensedKKTSystem{T, V copyto!(y, d_y) end end +function LinearAlgebra.mul!(y::MadNLP.ReducedKKTVector, kkt::MadNLP.DenseCondensedKKTSystem{T, VT, MT}, x::MadNLP.ReducedKKTVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} + LinearAlgebra.mul!(MadNLP.full(y), kkt, MadNLP.full(x)) +end function MadNLP.jprod_ineq!(y::AbstractVector, kkt::MadNLP.DenseCondensedKKTSystem{T, VT, MT}, x::AbstractVector) where {T, VT<:CuVector{T}, MT<:CuMatrix{T}} # Create buffers diff --git a/src/IPM/IPM.jl b/src/IPM/IPM.jl index d4fa0fe2..5e309001 100644 --- a/src/IPM/IPM.jl +++ b/src/IPM/IPM.jl @@ -31,38 +31,14 @@ mutable struct InteriorPointSolver{KKTSystem} <: AbstractInteriorPointSolver jacl::Vector{Float64} - d::Vector{Float64} - dx::StrideOneVector{Float64} - dl::StrideOneVector{Float64} - dzl::StrideOneVector{Float64} - dzu::StrideOneVector{Float64} - - p::Vector{Float64} - px::StrideOneVector{Float64} - pl::StrideOneVector{Float64} - - pzl::Union{Nothing,StrideOneVector{Float64}} - pzu::Union{Nothing,StrideOneVector{Float64}} - - _w1::Vector{Float64} - _w1x::StrideOneVector{Float64} - _w1l::StrideOneVector{Float64} - _w1zl::Union{Nothing,StrideOneVector{Float64}} - _w1zu::Union{Nothing,StrideOneVector{Float64}} - - _w2::Vector{Float64} - _w2x::StrideOneVector{Float64} - _w2l::StrideOneVector{Float64} - _w2zl::Union{Nothing,StrideOneVector{Float64}} - _w2zu::Union{Nothing,StrideOneVector{Float64}} - - _w3::Vector{Float64} - _w3x::StrideOneVector{Float64} - _w3l::StrideOneVector{Float64} - - _w4::Vector{Float64} - _w4x::StrideOneVector{Float64} - _w4l::StrideOneVector{Float64} + d::AbstractKKTVector{Float64, Vector{Float64}} + p::AbstractKKTVector{Float64, Vector{Float64}} + + _w1::AbstractKKTVector{Float64, Vector{Float64}} + _w2::AbstractKKTVector{Float64, Vector{Float64}} + + _w3::AbstractKKTVector{Float64, Vector{Float64}} + _w4::AbstractKKTVector{Float64, Vector{Float64}} x_trial::Vector{Float64} c_trial::Vector{Float64} @@ -201,42 +177,18 @@ function InteriorPointSolver{KKTSystem}(nlp::AbstractNLPModel, opt::Options; aug_vec_length = is_reduced(kkt) ? n+m : n+m+nlb+nub - _w1 = zeros(aug_vec_length) # fixes the random failure for inertia-free + Unreduced - _w1x= view(_w1,1:n) - _w1l= view(_w1,n+1:n+m) - _w1zl = is_reduced(kkt) ? nothing : view(_w1,n+m+1:n+m+nlb) - _w1zu = is_reduced(kkt) ? nothing : view(_w1,n+m+nlb+1:n+m+nlb+nub) - - - _w2 = Vector{Float64}(undef,aug_vec_length) - _w2x= view(_w2,1:n) - _w2l= view(_w2,n+1:n+m) - _w2zl = is_reduced(kkt) ? nothing : view(_w2,n+m+1:n+m+nlb) - _w2zu = is_reduced(kkt) ? nothing : view(_w2,n+m+nlb+1:n+m+nlb+nub) - - _w3 = zeros(aug_vec_length) # fixes the random failure for inertia-free + Unreduced - _w3x= view(_w3,1:n) - _w3l= view(_w3,n+1:n+m) - _w4 = zeros(aug_vec_length) # need to initialize to zero due to mul! - _w4x= view(_w4,1:n) - _w4l= view(_w4,n+1:n+m) - + _w1 = is_reduced(kkt) ? ReducedKKTVector(n, m) : UnreducedKKTVector(n, m, nlb, nub) + _w2 = is_reduced(kkt) ? ReducedKKTVector(n, m) : UnreducedKKTVector(n, m, nlb, nub) + _w3 = is_reduced(kkt) ? ReducedKKTVector(n, m) : UnreducedKKTVector(n, m, nlb, nub) + _w4 = is_reduced(kkt) ? ReducedKKTVector(n, m) : UnreducedKKTVector(n, m, nlb, nub) jacl = zeros(n) # spblas may throw an error if not initialized to zero - d = Vector{Float64}(undef,aug_vec_length) - dx= view(d,1:n) - dl= view(d,n+1:n+m) - dzl= is_reduced(kkt) ? Vector{Float64}(undef,nlb) : view(d,n+m+1:n+m+nlb) - dzu= is_reduced(kkt) ? Vector{Float64}(undef,nub) : view(d,n+m+nlb+1:n+m+nlb+nub) - dx_lr = view(dx,ind_cons.ind_lb) - dx_ur = view(dx,ind_cons.ind_ub) + d = UnreducedKKTVector(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 = Vector{Float64}(undef,aug_vec_length) - px= view(p,1:n) - pl= view(p,n+1:n+m) - pzl= is_reduced(kkt) ? Vector{Float64}(undef,nlb) : view(p,n+m+1:n+m+nlb) - pzu= is_reduced(kkt) ? Vector{Float64}(undef,nub) : view(p,n+m+nlb+1:n+m+nlb+nub) + p = UnreducedKKTVector(n, m, nlb, nub) obj_scale = [1.0] con_scale = ones(m) @@ -246,11 +198,10 @@ function InteriorPointSolver{KKTSystem}(nlp::AbstractNLPModel, opt::Options; cnt.linear_solver_time = @elapsed linear_solver = opt.linear_solver.Solver(get_kkt(kkt) ; option_dict=option_linear_solver,logger=logger) - n_kkt = size(get_kkt(kkt), 1) + n_kkt = size(kkt, 1) + buffer_vec = similar(full(d), n_kkt) @trace(logger,"Initializing iterative solver.") - iterator = opt.iterator.Solver( - similar(d, n_kkt), - (b, x)->mul!(b, kkt, x), (x)->solve!(linear_solver, x) ; option_dict=option_linear_solver) + iterator = opt.iterator(linear_solver, kkt, buffer_vec) @trace(logger,"Initializing fixed variable treatment scheme.") @@ -263,8 +214,8 @@ function InteriorPointSolver{KKTSystem}(nlp::AbstractNLPModel, opt::Options; return InteriorPointSolver{KKTSystem}(nlp,kkt,opt,cnt,logger, n,m,nlb,nub,x,l,zl,zu,xl,xu,0.,f,c, jacl, - d,dx,dl,dzl,dzu,p,px,pl,pzl,pzu, - _w1,_w1x,_w1l,_w1zl,_w1zu,_w2,_w2x,_w2l,_w2zl,_w2zu,_w3,_w3x,_w3l,_w4,_w4x,_w4l, + d, p, + _w1, _w2, _w3, _w4, x_trial,c_trial,0.,x_slk,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, diff --git a/src/IPM/callbacks.jl b/src/IPM/callbacks.jl index 49427b98..e2043336 100644 --- a/src/IPM/callbacks.jl +++ b/src/IPM/callbacks.jl @@ -51,10 +51,10 @@ function eval_lag_hess_wrapper!(ipp::InteriorPointSolver, kkt::AbstractKKTSystem nlp = ipp.nlp cnt = ipp.cnt @trace(ipp.logger,"Evaluating Lagrangian Hessian.") - ipp._w1l .= l.*ipp.con_scale + dual(ipp._w1) .= l.*ipp.con_scale hess = get_hessian(kkt) cnt.eval_function_time += @elapsed hess_coord!( - nlp, view(x,1:get_nvar(nlp)), ipp._w1l, view(hess,1:get_nnzh(nlp)); + nlp, view(x,1:get_nvar(nlp)), dual(ipp._w1), view(hess, 1:get_nnzh(nlp)); obj_weight = (get_minimize(nlp) ? 1. : -1.) * (is_resto ? 0.0 : ipp.obj_scale[])) compress_hessian!(kkt) cnt.lag_hess_cnt+=1 @@ -80,10 +80,10 @@ function eval_lag_hess_wrapper!(ipp::InteriorPointSolver, kkt::AbstractDenseKKTS nlp = ipp.nlp cnt = ipp.cnt @trace(ipp.logger,"Evaluating Lagrangian Hessian.") - ipp._w1l .= l.*ipp.con_scale + dual(ipp._w1) .= l.*ipp.con_scale hess = get_hessian(kkt) cnt.eval_function_time += @elapsed hess_dense!( - nlp, view(x,1:get_nvar(nlp)), ipp._w1l, hess; + nlp, view(x,1:get_nvar(nlp)), dual(ipp._w1), hess; obj_weight = (get_minimize(nlp) ? 1. : -1.) * (is_resto ? 0.0 : ipp.obj_scale[])) compress_hessian!(kkt) cnt.lag_hess_cnt+=1 diff --git a/src/IPM/factorization.jl b/src/IPM/factorization.jl index 3ffc2864..ae6742eb 100644 --- a/src/IPM/factorization.jl +++ b/src/IPM/factorization.jl @@ -5,32 +5,44 @@ function factorize_wrapper!(ips::InteriorPointSolver) ips.cnt.linear_solver_time += @elapsed factorize!(ips.linear_solver) end -function solve_refine_wrapper!(ips::InteriorPointSolver, x,b) +function solve_refine_wrapper!( + ips::InteriorPointSolver, + x::AbstractKKTVector, + b::AbstractKKTVector, +) cnt = ips.cnt @trace(ips.logger,"Iterative solution started.") - fixed_variable_treatment_vec!(b, ips.ind_fixed) + fixed_variable_treatment_vec!(full(b), ips.ind_fixed) + + cnt.linear_solver_time += @elapsed begin + result = solve_refine!(x, ips.iterator, b) + end - cnt.linear_solver_time += @elapsed (result = solve_refine!(x, ips.iterator, b)) if result == :Solved solve_status = true else if improve!(ips.linear_solver) cnt.linear_solver_time += @elapsed begin factorize!(ips.linear_solver) - solve_status = (solve_refine!(x, ips.iterator, b) == :Solved ? true : false) + ret = solve_refine!(x, ips.iterator, b) + solve_status = (ret == :Solved) end else solve_status = false end end - fixed_variable_treatment_vec!(x, ips.ind_fixed) + fixed_variable_treatment_vec!(full(x), ips.ind_fixed) return solve_status end -function solve_refine_wrapper!(ips::InteriorPointSolver{<:DenseCondensedKKTSystem}, x, b) +function solve_refine_wrapper!( + ips::InteriorPointSolver{<:DenseCondensedKKTSystem}, + x::AbstractKKTVector, + b::AbstractKKTVector, +) cnt = ips.cnt @trace(ips.logger,"Iterative solution started.") - fixed_variable_treatment_vec!(b, ips.ind_fixed) + fixed_variable_treatment_vec!(full(b), ips.ind_fixed) kkt = ips.kkt @@ -39,26 +51,26 @@ function solve_refine_wrapper!(ips::InteriorPointSolver{<:DenseCondensedKKTSyste n_condensed = n + n_eq # load buffers - b_c = view(ips._w1, 1:n_condensed) - x_c = view(ips._w2, 1:n_condensed) - jv_x = view(ips._w3, 1:ns) # for jprod - jv_t = ips._w4x # for jtprod - v_c = ips._w4l + b_c = view(full(ips._w1), 1:n_condensed) + x_c = view(full(ips._w2), 1:n_condensed) + jv_x = view(full(ips._w3), 1:ns) # for jprod + jv_t = primal(ips._w4) # for jtprod + v_c = dual(ips._w4) Σs = get_slack_regularization(kkt) α = get_scaling_inequalities(kkt) # Decompose right hand side - bx = view(b, 1:n) - bs = view(b, n+1:n+ns) - by = view(b, kkt.ind_eq_shifted) - bz = view(b, kkt.ind_ineq_shifted) + bx = view(full(b), 1:n) + bs = view(full(b), n+1:n+ns) + by = view(full(b), kkt.ind_eq_shifted) + bz = view(full(b), kkt.ind_ineq_shifted) # Decompose results - xx = view(x, 1:n) - xs = view(x, n+1:n+ns) - xy = view(x, kkt.ind_eq_shifted) - xz = view(x, kkt.ind_ineq_shifted) + xx = view(full(x), 1:n) + xs = view(full(x), n+1:n+ns) + xy = view(full(x), kkt.ind_eq_shifted) + xz = view(full(x), kkt.ind_ineq_shifted) v_c .= 0.0 v_c[kkt.ind_ineq] .= (Σs .* bz .+ α .* bs) ./ α.^2 @@ -77,7 +89,7 @@ function solve_refine_wrapper!(ips::InteriorPointSolver{<:DenseCondensedKKTSyste xz .= sqrt.(Σs) ./ α .* jv_x .- Σs .* bz ./ α.^2 .- bs ./ α xs .= (bs .+ α .* xz) ./ Σs - fixed_variable_treatment_vec!(x, ips.ind_fixed) + fixed_variable_treatment_vec!(full(x), ips.ind_fixed) return solve_status end diff --git a/src/IPM/kernels.jl b/src/IPM/kernels.jl index 0fd3a65b..945168cd 100644 --- a/src/IPM/kernels.jl +++ b/src/IPM/kernels.jl @@ -30,61 +30,61 @@ end # Set RHS function set_aug_rhs!(ips::InteriorPointSolver, kkt::AbstractKKTSystem, c) - ips.px.=.-ips.f.+ips.mu./(ips.x.-ips.xl).-ips.mu./(ips.xu.-ips.x).-ips.jacl - ips.pl.=.-c + primal(ips.p) .= .-ips.f.+ips.mu./(ips.x.-ips.xl).-ips.mu./(ips.xu.-ips.x).-ips.jacl + dual(ips.p) .= .-c end function set_aug_rhs!(ips::InteriorPointSolver, kkt::SparseUnreducedKKTSystem, c) - ips.px.=.-ips.f.+ips.zl.-ips.zu.-ips.jacl - ips.pl.=.-c - ips.pzl.=(ips.xl_r-ips.x_lr).*kkt.l_lower .+ ips.mu./kkt.l_lower - ips.pzu.=(ips.xu_r-ips.x_ur).*kkt.u_lower .- ips.mu./kkt.u_lower + primal(ips.p) .= .-ips.f.+ips.zl.-ips.zu.-ips.jacl + dual(ips.p) .= .-c + dual_lb(ips.p) .= (ips.xl_r-ips.x_lr).*kkt.l_lower .+ ips.mu./kkt.l_lower + dual_ub(ips.p) .= (ips.xu_r-ips.x_ur).*kkt.u_lower .- ips.mu./kkt.u_lower end function set_aug_rhs_ifr!(ips::InteriorPointSolver, kkt::SparseUnreducedKKTSystem,c) - ips._w1x .= 0. - ips._w1l .= .-c - ips._w1zl.= 0. - ips._w1zu.= 0. + primal(ips._w1) .= 0.0 + dual(ips._w1) .= .-c + dual_lb(ips._w1) .= 0.0 + dual_ub(ips._w1) .= 0.0 end # Set RHS RR function set_aug_rhs_RR!( ips::InteriorPointSolver, kkt::AbstractKKTSystem, RR::RobustRestorer, rho, ) - ips.px.=.-RR.f_R.-ips.jacl.+RR.mu_R./(ips.x.-ips.xl).-RR.mu_R./(ips.xu.-ips.x) - ips.pl.=.-ips.c.+RR.pp.-RR.nn.+(RR.mu_R.-(rho.-ips.l).*RR.pp)./RR.zp.-(RR.mu_R.-(rho.+ips.l).*RR.nn)./RR.zn + primal(ips.p) .= .-RR.f_R.-ips.jacl.+RR.mu_R./(ips.x.-ips.xl).-RR.mu_R./(ips.xu.-ips.x) + dual(ips.p) .= .-ips.c.+RR.pp.-RR.nn.+(RR.mu_R.-(rho.-ips.l).*RR.pp)./RR.zp.-(RR.mu_R.-(rho.+ips.l).*RR.nn)./RR.zn end # Finish function finish_aug_solve!(ips::InteriorPointSolver, kkt::AbstractKKTSystem, mu) - ips.dzl.= (mu.-ips.zl_r.*ips.dx_lr)./(ips.x_lr.-ips.xl_r).-ips.zl_r - ips.dzu.= (mu.+ips.zu_r.*ips.dx_ur)./(ips.xu_r.-ips.x_ur).-ips.zu_r + dual_lb(ips.d) .= (mu.-ips.zl_r.*ips.dx_lr)./(ips.x_lr.-ips.xl_r).-ips.zl_r + dual_ub(ips.d) .= (mu.+ips.zu_r.*ips.dx_ur)./(ips.xu_r.-ips.x_ur).-ips.zu_r end function finish_aug_solve!(ips::InteriorPointSolver, kkt::SparseUnreducedKKTSystem, mu) - ips.dzl.*=.-kkt.l_lower - ips.dzu.*=kkt.u_lower - ips.dzl.= (mu.-ips.zl_r.*ips.dx_lr)./(ips.x_lr.-ips.xl_r).-ips.zl_r - ips.dzu.= (mu.+ips.zu_r.*ips.dx_ur)./(ips.xu_r.-ips.x_ur).-ips.zu_r + dual_lb(ips.d) .*= .-kkt.l_lower + dual_ub(ips.d) .*= kkt.u_lower + dual_lb(ips.d) .= (mu.-ips.zl_r.*ips.dx_lr)./(ips.x_lr.-ips.xl_r).-ips.zl_r + dual_ub(ips.d) .= (mu.+ips.zu_r.*ips.dx_ur)./(ips.xu_r.-ips.x_ur).-ips.zu_r end # Initial function set_initial_rhs!(ips::InteriorPointSolver, kkt::AbstractKKTSystem) - ips.px .= .-ips.f.+ips.zl.-ips.zu - ips.pl .= 0.0 + primal(ips.p) .= .-ips.f.+ips.zl.-ips.zu + dual(ips.p) .= 0.0 end function set_initial_rhs!(ips::InteriorPointSolver, kkt::SparseUnreducedKKTSystem) - ips.px .= .-ips.f.+ips.zl.-ips.zu - ips.pl .= 0.0 - ips.pzl.= 0.0 - ips.pzu.= 0.0 + primal(ips.p) .= .-ips.f.+ips.zl.-ips.zu + dual(ips.p) .= 0.0 + dual_lb(ips.p) .= 0.0 + dual_ub(ips.p) .= 0.0 end # Set ifr function set_aug_rhs_ifr!(ips::InteriorPointSolver, kkt::AbstractKKTSystem) - ips._w1x .= 0.0 - ips._w1l .= .-ips.c + primal(ips._w1) .= 0.0 + dual(ips._w1) .= .-ips.c end # Finish RR diff --git a/src/IPM/restoration.jl b/src/IPM/restoration.jl index 6c816f88..ca349450 100644 --- a/src/IPM/restoration.jl +++ b/src/IPM/restoration.jl @@ -43,9 +43,22 @@ function RobustRestorer(ips::AbstractInteriorPointSolver) pp_trial = Vector{Float64}(undef,ips.m) nn_trial = Vector{Float64}(undef,ips.m) - return RobustRestorer(0.,ips._w2x,ips._w1x,0.,ips._w3x,0.,ips._w3l,ips._w4l, - zp,zn,dpp,dnn,dzp,dzn,ips._w2l,ips._w1l, - 0.,0.,0.,0.,0.,0.,Tuple{Float64,Float64}[]) + return RobustRestorer( + 0., + primal(ips._w2), + primal(ips._w1), + 0., + primal(ips._w3), + 0., + dual(ips._w3), + dual(ips._w4), + zp, zn, + dpp, dnn, dzp, dzn, + dual(ips._w2), + dual(ips._w1), + 0.,0.,0.,0.,0.,0., + Tuple{Float64,Float64}[], + ) end function initialize_robust_restorer!(ips::AbstractInteriorPointSolver) diff --git a/src/IPM/solver.jl b/src/IPM/solver.jl index c9702bd3..17428d49 100644 --- a/src/IPM/solver.jl +++ b/src/IPM/solver.jl @@ -41,7 +41,11 @@ function initialize!(ips::AbstractInteriorPointSolver) initialize!(ips.kkt) factorize_wrapper!(ips) solve_refine_wrapper!(ips,ips.d,ips.p) - norm(ips.dl,Inf)>ips.opt.constr_mult_init_max ? (ips.l.= 0.) : (ips.l.= ips.dl) + if norm(dual(ips.d), Inf) > ips.opt.constr_mult_init_max + fill!(ips.l, 0.0) + else + copyto!(ips.l, dual(ips.d)) + end end # Initializing @@ -183,7 +187,7 @@ function regular!(ips::AbstractInteriorPointSolver) if ips.opt.inertia_correction_method == INERTIA_FREE set_aug_rhs_ifr!(ips, ips.kkt) end - dual_inf_perturbation!(ips.px,ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) + dual_inf_perturbation!(primal(ips.p),ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) # start inertia conrrection @trace(ips.logger,"Solving primal-dual system.") @@ -199,23 +203,23 @@ function regular!(ips::AbstractInteriorPointSolver) @trace(ips.logger,"Backtracking line search initiated.") theta = get_theta(ips.c) varphi= get_varphi(ips.obj_val,ips.x_lr,ips.xl_r,ips.xu_r,ips.x_ur,ips.mu) - varphi_d = get_varphi_d(ips.f,ips.x,ips.xl,ips.xu,ips.dx,ips.mu) + varphi_d = get_varphi_d(ips.f,ips.x,ips.xl,ips.xu,primal(ips.d),ips.mu) - alpha_max = get_alpha_max(ips.x,ips.xl,ips.xu,ips.dx,ips.tau) - ips.alpha_z = get_alpha_z(ips.zl_r,ips.zu_r,ips.dzl,ips.dzu,ips.tau) + alpha_max = get_alpha_max(ips.x,ips.xl,ips.xu,primal(ips.d),ips.tau) + ips.alpha_z = get_alpha_z(ips.zl_r,ips.zu_r,dual_lb(ips.d),dual_ub(ips.d),ips.tau) alpha_min = get_alpha_min(theta,varphi_d,ips.theta_min,ips.opt.gamma_theta,ips.opt.gamma_phi, ips.opt.alpha_min_frac,ips.opt.delta,ips.opt.s_theta,ips.opt.s_phi) ips.cnt.l = 1 ips.alpha = alpha_max varphi_trial= 0. theta_trial = 0. - small_search_norm = get_rel_search_norm(ips.x,ips.dx) < 10*eps(Float64) + small_search_norm = get_rel_search_norm(ips.x, primal(ips.d)) < 10*eps(Float64) switching_condition = is_switching(varphi_d,ips.alpha,ips.opt.s_phi,ips.opt.delta,2.,ips.opt.s_theta) armijo_condition = false while true - copyto!(ips.x_trial,ips.x) - axpy!(ips.alpha,ips.dx,ips.x_trial) + copyto!(ips.x_trial, ips.x) + axpy!(ips.alpha, primal(ips.d), ips.x_trial) ips.obj_val_trial = eval_f_wrapper(ips, ips.x_trial) eval_cons_wrapper!(ips, ips.c_trial, ips.x_trial) @@ -244,7 +248,7 @@ function regular!(ips::AbstractInteriorPointSolver) return RESTORE else @trace(ips.logger,"Step rejected; proceed with the next trial step.") - ips.alpha * norm(ips.dx) < eps(Float64)*10 && + ips.alpha * norm(primal(ips.d)) < eps(Float64)*10 && return ips.cnt.acceptable_cnt >0 ? SOLVED_TO_ACCEPTABLE_LEVEL : SEARCH_DIRECTION_BECOMES_TOO_SMALL end @@ -258,9 +262,9 @@ function regular!(ips::AbstractInteriorPointSolver) adjusted > 0 && @warn(ips.logger,"In iteration $(ips.cnt.k), $adjusted Slack too small, adjusting variable bound") - axpy!(ips.alpha,ips.dl,ips.l) - axpy!(ips.alpha_z,ips.dzl,ips.zl_r) - axpy!(ips.alpha_z,ips.dzu,ips.zu_r) + axpy!(ips.alpha,dual(ips.d),ips.l) + axpy!(ips.alpha_z, dual_lb(ips.d), ips.zl_r) + axpy!(ips.alpha_z, dual_ub(ips.d), ips.zu_r) reset_bound_dual!(ips.zl,ips.x,ips.xl,ips.mu,ips.opt.kappa_sigma) reset_bound_dual!(ips.zu,ips.xu,ips.x,ips.mu,ips.opt.kappa_sigma) eval_grad_f_wrapper!(ips, ips.f,ips.x) @@ -277,9 +281,9 @@ end function restore!(ips::AbstractInteriorPointSolver) ips.del_w=0 - ips._w1x .= ips.x # backup the previous primal iterate - ips._w1l .= ips.l # backup the previous primal iterate - ips._w2l .= ips.c # backup the previous primal iterate + primal(ips._w1) .= ips.x # backup the previous primal iterate + dual(ips._w1) .= ips.l # backup the previous primal iterate + dual(ips._w2) .= ips.c # backup the previous primal iterate F = get_F(ips.c,ips.f,ips.zl,ips.zu,ips.jacl,ips.x_lr,ips.xl_r,ips.zl_r,ips.xu_r,ips.x_ur,ips.zu_r,ips.mu) ips.cnt.t = 0 @@ -287,13 +291,13 @@ function restore!(ips::AbstractInteriorPointSolver) ips.ftype = "R" while true - ips.alpha = min(get_alpha_max(ips.x,ips.xl,ips.xu,ips.dx,ips.tau), - get_alpha_z(ips.zl_r,ips.zu_r,ips.dzl,ips.dzu,ips.tau)) + ips.alpha = min(get_alpha_max(ips.x,ips.xl,ips.xu,primal(ips.d),ips.tau), + get_alpha_z(ips.zl_r,ips.zu_r,dual_lb(ips.d),dual_ub(ips.d),ips.tau)) - ips.x .+= ips.alpha.*ips.dx - ips.l .+= ips.alpha.*ips.dl - ips.zl_r.+=ips.alpha.*ips.dzl - ips.zu_r.+=ips.alpha.*ips.dzu + ips.x .+= ips.alpha.* primal(ips.d) + ips.l .+= ips.alpha.* dual(ips.d) + ips.zl_r.+=ips.alpha.* dual_lb(ips.d) + ips.zu_r.+=ips.alpha.* dual_ub(ips.d) eval_cons_wrapper!(ips,ips.c,ips.x) eval_grad_f_wrapper!(ips,ips.f,ips.x) @@ -305,9 +309,9 @@ function restore!(ips::AbstractInteriorPointSolver) F_trial = get_F( ips.c,ips.f,ips.zl,ips.zu,ips.jacl,ips.x_lr,ips.xl_r,ips.zl_r,ips.xu_r,ips.x_ur,ips.zu_r,ips.mu) if F_trial > ips.opt.soft_resto_pderror_reduction_factor*F - ips.x.=ips._w1x - ips.l.=ips._w1l - ips.c.=ips._w2l # backup the previous primal iterate + ips.x .= primal(ips._w1) + ips.l .= dual(ips._w1) + ips.c .= dual(ips._w2) # backup the previous primal iterate return ROBUST end @@ -341,7 +345,7 @@ function restore!(ips::AbstractInteriorPointSolver) set_aug_diagonal!(ips.kkt,ips) set_aug_rhs!(ips, ips.kkt, ips.c) - dual_inf_perturbation!(ips.px,ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) + dual_inf_perturbation!(primal(ips.p),ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) factorize_wrapper!(ips) solve_refine_wrapper!(ips,ips.d,ips.p) finish_aug_solve!(ips, ips.kkt, ips.mu) @@ -412,16 +416,16 @@ function robust!(ips::InteriorPointSolver) solve_refine_wrapper!(ips,ips.d,ips.p) finish_aug_solve!(ips, ips.kkt, RR.mu_R) - finish_aug_solve_RR!(RR.dpp,RR.dnn,RR.dzp,RR.dzn,ips.l,ips.dl,RR.pp,RR.nn,RR.zp,RR.zn,RR.mu_R,ips.opt.rho) + finish_aug_solve_RR!(RR.dpp,RR.dnn,RR.dzp,RR.dzn,ips.l,dual(ips.d),RR.pp,RR.nn,RR.zp,RR.zn,RR.mu_R,ips.opt.rho) theta_R = get_theta_R(ips.c,RR.pp,RR.nn) varphi_R = get_varphi_R(RR.obj_val_R,ips.x_lr,ips.xl_r,ips.xu_r,ips.x_ur,RR.pp,RR.nn,RR.mu_R) - varphi_d_R = get_varphi_d_R(RR.f_R,ips.x,ips.xl,ips.xu,ips.dx,RR.pp,RR.nn,RR.dpp,RR.dnn,RR.mu_R,ips.opt.rho) + varphi_d_R = get_varphi_d_R(RR.f_R,ips.x,ips.xl,ips.xu,primal(ips.d),RR.pp,RR.nn,RR.dpp,RR.dnn,RR.mu_R,ips.opt.rho) # set alpha_min - alpha_max = get_alpha_max_R(ips.x,ips.xl,ips.xu,ips.dx,RR.pp,RR.dpp,RR.nn,RR.dnn,RR.tau_R) - ips.alpha_z = get_alpha_z_R(ips.zl_r,ips.zu_r,ips.dzl,ips.dzu,RR.zp,RR.dzp,RR.zn,RR.dzn,RR.tau_R) + alpha_max = get_alpha_max_R(ips.x,ips.xl,ips.xu,primal(ips.d),RR.pp,RR.dpp,RR.nn,RR.dnn,RR.tau_R) + ips.alpha_z = get_alpha_z_R(ips.zl_r,ips.zu_r,dual_lb(ips.d),dual_ub(ips.d),RR.zp,RR.dzp,RR.zn,RR.dzn,RR.tau_R) alpha_min = get_alpha_min(theta_R,varphi_d_R,ips.theta_min,ips.opt.gamma_theta,ips.opt.gamma_phi, ips.opt.alpha_min_frac,ips.opt.delta,ips.opt.s_theta,ips.opt.s_phi) @@ -431,7 +435,7 @@ function robust!(ips::InteriorPointSolver) ips.cnt.l = 1 theta_R_trial = 0. varphi_R_trial = 0. - small_search_norm = get_rel_search_norm(ips.x,ips.dx) < 10*eps(Float64) + small_search_norm = get_rel_search_norm(ips.x, primal(ips.d)) < 10*eps(Float64) switching_condition = is_switching(varphi_d_R,ips.alpha,ips.opt.s_phi,ips.opt.delta,theta_R,ips.opt.s_theta) armijo_condition = false @@ -439,7 +443,7 @@ function robust!(ips::InteriorPointSolver) copyto!(ips.x_trial,ips.x) copyto!(RR.pp_trial,RR.pp) copyto!(RR.nn_trial,RR.nn) - axpy!(ips.alpha,ips.dx,ips.x_trial) + axpy!(ips.alpha,primal(ips.d),ips.x_trial) axpy!(ips.alpha,RR.dpp,RR.pp_trial) axpy!(ips.alpha,RR.dnn,RR.nn_trial) @@ -481,9 +485,9 @@ function robust!(ips::InteriorPointSolver) RR.obj_val_R=RR.obj_val_R_trial RR.f_R .= RR.zeta.*RR.D_R.^2 .*(ips.x.-RR.x_ref) - axpy!(ips.alpha, ips.dl,ips.l ) - axpy!(ips.alpha_z, ips.dzl,ips.zl_r) - axpy!(ips.alpha_z, ips.dzu,ips.zu_r) + axpy!(ips.alpha, dual(ips.d), ips.l) + axpy!(ips.alpha_z, dual_lb(ips.d),ips.zl_r) + axpy!(ips.alpha_z, dual_ub(ips.d),ips.zu_r) axpy!(ips.alpha_z, RR.dzp,RR.zp) axpy!(ips.alpha_z, RR.dzn,RR.zn) @@ -520,7 +524,11 @@ function robust!(ips::InteriorPointSolver) factorize_wrapper!(ips) solve_refine_wrapper!(ips,ips.d,ips.p) - norm(ips.dl,Inf)>ips.opt.constr_mult_init_max ? (ips.l.= 0) : (ips.l.= ips.dl) + if norm(dual(ips.d), Inf)>ips.opt.constr_mult_init_max + fill!(ips.l, 0.0) + else + copyto!(ips.l, dual(ips.d)) + end ips.cnt.k+=1 return REGULAR @@ -575,23 +583,22 @@ function inertia_free_reg(ips::InteriorPointSolver) @trace(ips.logger,"Inertia-free regularization started.") p0 = ips._w1 - d0= ips._w2 - t = ips._w3x - n = ips._w2x - wx= ips._w4x - ips._w3l.=0 + d0 = ips._w2 + t = primal(ips._w3) + n = primal(ips._w2) + wx= primal(ips._w4) + fill!(dual(ips._w3), 0) g = ips.x_trial # just to avoid new allocation g .= ips.f.-ips.mu./(ips.x.-ips.xl).+ips.mu./(ips.xu.-ips.x).+ips.jacl - fixed_variable_treatment_vec!(ips._w1x,ips.ind_fixed) - fixed_variable_treatment_vec!(ips.px,ips.ind_fixed) - fixed_variable_treatment_vec!(g,ips.ind_fixed) - # end + fixed_variable_treatment_vec!(primal(ips._w1), ips.ind_fixed) + fixed_variable_treatment_vec!(primal(ips.p), ips.ind_fixed) + fixed_variable_treatment_vec!(g, ips.ind_fixed) factorize_wrapper!(ips) solve_status = (solve_refine_wrapper!(ips,d0,p0) && solve_refine_wrapper!(ips,ips.d,ips.p)) - t .= ips.dx.-n + t .= primal(ips.d) .- n mul!(ips._w4, ips.kkt, ips._w3) # prepartation for curv_test n_trial = 0 ips.del_w = del_w_prev = 0. @@ -615,7 +622,7 @@ function inertia_free_reg(ips::InteriorPointSolver) factorize_wrapper!(ips) solve_status = (solve_refine_wrapper!(ips,d0,p0) && solve_refine_wrapper!(ips,ips.d,ips.p)) - t .= ips.dx.-n + t .= primal(ips.d) .- n mul!(ips._w4, ips.kkt, ips._w3) # prepartation for curv_test n_trial += 1 end @@ -630,16 +637,16 @@ function second_order_correction(ips::AbstractInteriorPointSolver,alpha_max::Flo theta_trial::Float64,varphi_d::Float64,switching_condition::Bool) @trace(ips.logger,"Second-order correction started.") - ips._w1l .= alpha_max .* ips.c .+ ips.c_trial + dual(ips._w1) .= alpha_max .* ips.c .+ ips.c_trial theta_soc_old = theta_trial for p=1:ips.opt.max_soc # compute second order correction - set_aug_rhs!(ips, ips.kkt, ips._w1l) - dual_inf_perturbation!(ips.px,ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) + set_aug_rhs!(ips, ips.kkt, dual(ips._w1)) + dual_inf_perturbation!(primal(ips.p),ips.ind_llb,ips.ind_uub,ips.mu,ips.opt.kappa_d) solve_refine_wrapper!(ips,ips._w1,ips.p) - alpha_soc = get_alpha_max(ips.x,ips.xl,ips.xu,ips._w1x,ips.tau) + alpha_soc = get_alpha_max(ips.x,ips.xl,ips.xu,primal(ips._w1),ips.tau) - ips.x_trial .= ips.x.+alpha_soc.*ips._w1x + ips.x_trial .= ips.x .+ alpha_soc .* primal(ips._w1) eval_cons_wrapper!(ips, ips.c_trial,ips.x_trial) ips.obj_val_trial = eval_f_wrapper(ips, ips.x_trial) diff --git a/src/KKT/KKTsystem.jl b/src/KKT/KKTsystem.jl index 6916f3f6..263fb955 100644 --- a/src/KKT/KKTsystem.jl +++ b/src/KKT/KKTsystem.jl @@ -242,6 +242,7 @@ end compress_hessian!(kkt::AbstractKKTSystem) = nothing +include("rhs.jl") include("sparse.jl") include("dense.jl") diff --git a/src/KKT/dense.jl b/src/KKT/dense.jl index 4c371477..0f8d5482 100644 --- a/src/KKT/dense.jl +++ b/src/KKT/dense.jl @@ -139,6 +139,9 @@ num_variables(kkt::DenseKKTSystem) = length(kkt.pr_diag) function mul!(y::AbstractVector, kkt::DenseKKTSystem, x::AbstractVector) mul!(y, kkt.aug_com, x) end +function mul!(y::ReducedKKTVector, kkt::DenseKKTSystem, x::ReducedKKTVector) + mul!(full(y), kkt.aug_com, full(x)) +end # Special getters for Jacobian function get_jacobian(kkt::DenseKKTSystem) @@ -374,6 +377,10 @@ function mul!(y::AbstractVector, kkt::DenseCondensedKKTSystem, x::AbstractVector end end +function mul!(y::ReducedKKTVector, kkt::DenseCondensedKKTSystem, x::ReducedKKTVector) + mul!(full(y), kkt, full(x)) +end + function jprod_ineq!(y::AbstractVector, kkt::DenseCondensedKKTSystem, x::AbstractVector) mul!(y, kkt.jac_ineq, x) end diff --git a/src/KKT/rhs.jl b/src/KKT/rhs.jl new file mode 100644 index 00000000..c6e27c11 --- /dev/null +++ b/src/KKT/rhs.jl @@ -0,0 +1,160 @@ + +""" + AbstractKKTVector{T, VT} + +Supertype for KKT's right-hand-side vectors ``(x, s, y, z, ν, w)``. +""" +abstract type AbstractKKTVector{T, VT} end + +""" + full(X::AbstractKKTVector) + +Return the all the values stored inside the KKT vector `X`. +""" +function full end + +Base.length(rhs::AbstractKKTVector) = length(full(rhs)) + +""" + number_primal(X::AbstractKKTVector) + +Get total number of primal values ``(x, s)`` in KKT vector `X`. +""" +number_primal(rhs::AbstractKKTVector) = length(primal(rhs)) + +""" + number_dual(X::AbstractKKTVector) + +Get total number of dual values ``(y, z)`` in KKT vector `X`. +""" +number_dual(rhs::AbstractKKTVector) = length(dual(rhs)) + +""" + primal(X::AbstractKKTVector) + +Return the primal values ``(x, s)`` stored in the KKT vector `X`. +""" +function primal end + +""" + dual(X::AbstractKKTVector) + +Return the dual values ``(y, z)`` stored in the KKT vector `X`. +""" +function dual end + +""" + primal_dual(X::AbstractKKTVector) + +Return both the primal and the dual values ``(x, s, y, z)`` stored in the KKT vector `X`. +""" +function primal_dual end + +""" + dual_lb(X::AbstractKKTVector) + +Return the dual values ``ν`` associated to the lower-bound +stored in the KKT vector `X`. +""" +function dual_lb end + +""" + dual_ub(X::AbstractKKTVector) + +Return the dual values ``w`` associated to the upper-bound +stored in the KKT vector `X`. +""" +function dual_ub end + +function Base.fill!(rhs::AbstractKKTVector{T}, val::T) where T + fill!(full(rhs), val) +end + +# Overload basic BLAS operations. +norm(X::AbstractKKTVector, p::Real) = norm(full(X), p) +dot(X::AbstractKKTVector, Y::AbstractKKTVector) = dot(full(X), full(Y)) +function axpy!(a::Number, X::AbstractKKTVector, Y::AbstractKKTVector) + axpy!(a, full(X), full(Y)) +end + +#= + ReducedKKTVector +=# + +""" + ReducedKKTVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + +KKT vector ``(x, s, y, z)``, associated to a [`AbstractReducedKKTSystem`](@ref). + +Compared to [`UnreducedKKTVector`](@ref), it does not store +the dual values associated to the primal's lower and upper bounds. +""" +struct ReducedKKTVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + values::VT + xp::VT # unsafe view + xl::VT # unsafe view +end + +ReducedKKTVector(n::Int, m::Int, nlb::Int, nub::Int) = ReducedKKTVector(n, m) +function ReducedKKTVector(n::Int, m::Int) + x = Vector{Float64}(undef, n + m) + fill!(x, 0.0) + # Wrap directly array x to avoid dealing with views + pp = pointer(x) + xp = unsafe_wrap(Vector{Float64}, pp, n) + pd = pointer(x, n + 1) + xl = unsafe_wrap(Vector{Float64}, pd, m) + return ReducedKKTVector{Float64, Vector{Float64}}(x, xp, xl) +end +function ReducedKKTVector(rhs::AbstractKKTVector) + return ReducedKKTVector(number_primal(rhs), number_dual(rhs)) +end + +full(rhs::ReducedKKTVector) = rhs.values +primal(rhs::ReducedKKTVector) = rhs.xp +dual(rhs::ReducedKKTVector) = rhs.xl +primal_dual(rhs::ReducedKKTVector) = rhs.values + + +#= + UnreducedKKTVector +=# + +""" + UnreducedKKTVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + +Full KKT vector ``(x, s, y, z, ν, w)``, associated to a [`AbstractUnreducedKKTSystem`](@ref). + +""" +struct UnreducedKKTVector{T, VT<:AbstractVector{T}} <: AbstractKKTVector{T, VT} + values::VT + x::VT # unsafe view + xp::VT # unsafe view + xl::VT # unsafe view + xzl::VT # unsafe view + xzu::VT # unsafe view +end + +function UnreducedKKTVector(n::Int, m::Int, nlb::Int, nub::Int) + values = Vector{Float64}(undef, n + m + nlb + nub) + fill!(values, 0.0) + # Wrap directly array x to avoid dealing with views + pp = pointer(values) + x = unsafe_wrap(Vector{Float64}, pp, n + m) # Primal-Dual + xp = unsafe_wrap(Vector{Float64}, pp, n) # Primal + pd = pointer(values, n + 1) + xl = unsafe_wrap(Vector{Float64}, pd, m) # Dual + pzl = pointer(values, n + m + 1) + xzl = unsafe_wrap(Vector{Float64}, pzl, nlb) # Lower bound + pzu = pointer(values, n + m + nlb + 1) + xzu = unsafe_wrap(Vector{Float64}, pzu, nub) # Upper bound + return UnreducedKKTVector{Float64, Vector{Float64}}(values, x, xp, xl, xzl, xzu) +end + +full(rhs::UnreducedKKTVector) = rhs.values +primal(rhs::UnreducedKKTVector) = rhs.xp +dual(rhs::UnreducedKKTVector) = rhs.xl +primal_dual(rhs::UnreducedKKTVector) = rhs.x +dual_lb(rhs::UnreducedKKTVector) = rhs.xzl +dual_ub(rhs::UnreducedKKTVector) = rhs.xzu + diff --git a/src/KKT/sparse.jl b/src/KKT/sparse.jl index a62a73f2..2ccbe676 100644 --- a/src/KKT/sparse.jl +++ b/src/KKT/sparse.jl @@ -65,6 +65,9 @@ const AbstractSparseKKTSystem{T, MT} = Union{SparseKKTSystem{T, MT}, SparseUnred function mul!(y::AbstractVector, kkt::AbstractSparseKKTSystem, x::AbstractVector) mul!(y, Symmetric(kkt.aug_com, :L), x) end +function mul!(y::AbstractKKTVector, kkt::AbstractSparseKKTSystem, x::AbstractKKTVector) + mul!(full(y), Symmetric(kkt.aug_com, :L), full(x)) +end function jtprod!(y::AbstractVector, kkt::AbstractSparseKKTSystem, x::AbstractVector) mul!(y, kkt.jac_com', x) diff --git a/src/LinearSolvers/backsolve.jl b/src/LinearSolvers/backsolve.jl new file mode 100644 index 00000000..0a32078c --- /dev/null +++ b/src/LinearSolvers/backsolve.jl @@ -0,0 +1,94 @@ +# MadNLP.jl +# Created by Sungho Shin (sungho.shin@wisc.edu) + +struct RichardsonIterator{T, VT, KKT, LinSolver} <: AbstractIterator + linear_solver::LinSolver + kkt::KKT + residual::VT + max_iter::Int + tol::T + acceptable_tol::T + logger::Logger +end +function RichardsonIterator( + linear_solver::AbstractLinearSolver, + kkt::AbstractKKTSystem, + res::AbstractVector; + max_iter=10, tol=1e-10, acceptable_tol=1e-5, logger=Logger(), +) + return RichardsonIterator( + linear_solver, kkt, res, max_iter, tol, acceptable_tol, logger, + ) +end + +# Solve reduced KKT system. Require only the primal/dual values. +function solve_refine!( + x::AbstractKKTVector{T, VT}, + solver::RichardsonIterator{T, VT, KKT}, + b::AbstractKKTVector{T, VT}, +) where {T, VT, KKT<:AbstractReducedKKTSystem} + solve_refine!(primal_dual(x), solver, primal_dual(b)) +end + +# Solve unreduced KKT system. Require UnreducedKKTVector as inputs. +function solve_refine!( + x::UnreducedKKTVector{T, VT}, + solver::RichardsonIterator{T, VT, KKT}, + b::UnreducedKKTVector{T, VT}, +) where {T, VT, KKT<:AbstractUnreducedKKTSystem} + solve_refine!(full(x), solver, full(b)) +end + +function solve_refine!( + x::AbstractVector{T}, + solver::RichardsonIterator{T}, + b::AbstractVector{T}, +) where T + @debug(solver.logger, "Iterative solver initiated") + + ε = solver.residual + norm_b = norm(b, Inf) + + fill!(x, zero(T)) + fill!(ε, zero(T)) + + ε = solver.residual + axpy!(-1, b, ε) + norm_res = norm(ε, Inf) + residual_ratio = norm_res / (one(T) + norm_b) + + iter = 0 + residual_ratio_old = Inf + noprogress = 0 + + while true + mod(iter, 10)==0 && + @debug(solver.logger,"iter ||res||") + @debug(solver.logger, @sprintf("%4i %6.2e", iter, residual_ratio)) + iter += 1 + if (iter > solver.max_iter) || (residual_ratio < solver.tol) + break + end + + solve!(solver.linear_solver, ε) + axpy!(-1, ε, x) + mul!(ε, solver.kkt, x) + axpy!(-1, b, ε) + norm_res = norm(ε, Inf) + + residual_ratio_old = residual_ratio + residual_ratio = norm_res / (one(T)+norm_b) + end + + @debug(solver.logger, @sprintf( + "Iterative solver terminated with %4i refinement steps and residual = %6.2e", + iter, residual_ratio), + ) + + if residual_ratio < solver.acceptable_tol + return :Solved + else + return :Failed + end +end + diff --git a/src/LinearSolvers/lapack.jl b/src/LinearSolvers/lapack.jl index 15ffcf17..bd671149 100644 --- a/src/LinearSolvers/lapack.jl +++ b/src/LinearSolvers/lapack.jl @@ -2,7 +2,7 @@ module MadNLPLapackCPU import ..MadNLP: @kwdef, Logger, @debug, @warn, @error, - AbstractOptions, AbstractLinearSolver, set_options!, tril_to_full!, + AbstractOptions, AbstractLinearSolver, StrideOneVector, set_options!, tril_to_full!, libblas, BlasInt, @blasfunc, SymbolicException,FactorizationException,SolveException,InertiaException, introduce, factorize!, solve!, improve!, is_inertia, inertia @@ -99,7 +99,7 @@ function factorize!(M::Solver) error(LOGGER,"Invalid lapackcpu_algorithm") end end -function solve!(M::Solver,x) +function solve!(M::Solver, x::StrideOneVector{Float64}) if M.opt.lapackcpu_algorithm == BUNCHKAUFMAN solve_bunchkaufman!(M,x) elseif M.opt.lapackcpu_algorithm == LU diff --git a/src/LinearSolvers/linearsolvers.jl b/src/LinearSolvers/linearsolvers.jl index e939b9dd..19dbea16 100644 --- a/src/LinearSolvers/linearsolvers.jl +++ b/src/LinearSolvers/linearsolvers.jl @@ -1,20 +1,91 @@ # MadNLP.jl # Created by Sungho Shin (sungho.shin@wisc.edu) +#= + LinearSolver's interface +=# +""" + AbstractLinearSolver + +Abstract type for linear solver targeting +the resolution of the linear system ``Ax=b``. +""" abstract type AbstractLinearSolver end + +""" + introduce(::AbstractLinearSolver) + +Print the name of the linear solver. +""" +function introduce end + +""" + factorize!(::AbstractLinearSolver) + +Factorize the matrix ``A`` and updates the factors +inside the `AbstractLinearSolver` instance. +""" +function factorize! end + +""" + solve!(::AbstractLinearSolver, x::AbstractVector) + +Solve the linear system ``Ax = b``. + +This function assumes the linear system has been +factorized previously with [`factorize!`](@ref). +""" +function solve! end + +""" + is_inertia(::AbstractLinearSolver) + +Return `true` if the linear solver supports the +computation of the inertia of the linear system. +""" +function is_inertia end + +""" + inertia(::AbstractLinearSolver) + +Return the inertia `(n, m, p)` of the linear system +as a tuple. + +### Note +The inertia is defined as a tuple ``(n, m, p)``, +with +- ``n``: number of positive eigenvalues +- ``m``: number of negative eigenvalues +- ``p``: number of zero eigenvalues +""" +function inertia end + +function improve! end + +# Default function for AbstractKKTVector +function solve!(s::AbstractLinearSolver, x::AbstractKKTVector) + solve!(s, full(x)) +end + +#= + Iterator's interface +=# abstract type AbstractIterator end -# dummy solver type -mutable struct EmptyLinearSolver <: AbstractLinearSolver end +""" + solve_refine!(x, ::AbstractIterator, b) + +Solve the linear system ``Ax = b`` using iterative +refinement. The object `AbstractIterator` stores an instance +of a [`AbstractLinearSolver`](@ref) for the backsolve +operations. -# dummy functions -introduce(::EmptyLinearSolver) = "" -factorize!(M::EmptyLinearSolver) = M -solve!(::EmptyLinearSolver,x) = x -is_inertia(::EmptyLinearSolver) = false -inertia(::EmptyLinearSolver) = (0,0,0) -improve!(::EmptyLinearSolver) = false -solve_refine!(y,::AbstractIterator,x) = nothing +### Notes +This function assumes the matrix stores in the linear solver +has been factorized previously. + +""" +function solve_refine! end # LinearSolverExceptions struct SymbolicException <: Exception end @@ -24,7 +95,11 @@ struct InertiaException <: Exception end LinearSolverException=Union{SymbolicException,FactorizationException,SolveException,InertiaException} # iterative solvers -include("richardson.jl") +include("backsolve.jl") + +#= + DEFAULT SOLVERS +=# # dense solvers include("lapack.jl") diff --git a/src/LinearSolvers/richardson.jl b/src/LinearSolvers/richardson.jl deleted file mode 100644 index 121aced2..00000000 --- a/src/LinearSolvers/richardson.jl +++ /dev/null @@ -1,78 +0,0 @@ -# MadNLP.jl -# Created by Sungho Shin (sungho.shin@wisc.edu) - -module MadNLPRichardson - -import ..MadNLP: - @kwdef, Logger, @debug, @warn, @error, - norm, - AbstractOptions, AbstractIterator, set_options!, @sprintf, StrideOneVector, - solve_refine! - -@kwdef mutable struct Options <: AbstractOptions - richardson_max_iter::Int = 10 - richardson_tol::Float64 = 1e-10 - richardson_acceptable_tol::Float64 = 1e-5 -end - -mutable struct Solver <: AbstractIterator - res::Vector{Float64} - mul!::Function - div!::Function - opt::Options - logger::Logger -end -function Solver(res::Vector{Float64},mul!,div!; - opt=Options(),logger=Logger(), - option_dict::Dict{Symbol,Any}=Dict{Symbol,Any}(),kwargs...) - !isempty(kwargs) && (for (key,val) in kwargs; option_dict[key]=val; end) - set_options!(opt,option_dict) - return Solver(res,mul!,div!,opt,logger) -end - -function solve_refine!(x::StrideOneVector{Float64}, - IS::Solver, - b::AbstractVector{Float64}) - @debug(IS.logger,"Iterative solver initiated") - norm_b = norm(b,Inf) - x.=0 - IS.res.=.-b - norm_res = norm(IS.res,Inf) - residual_ratio = norm_res/(1+norm_b) - - iter = 0 - residual_ratio_old = Inf - noprogress = 0 - - while true - mod(iter,10)==0 && - @debug(IS.logger,"iter ||res||") - @debug(IS.logger,@sprintf("%4i %6.2e",iter,residual_ratio)) - iter += 1 - iter > IS.opt.richardson_max_iter && break - residual_ratio < IS.opt.richardson_tol && break - # residual_ratio>=residual_ratio_old && residual_ratio>IS.opt.richardson_acceptable_tol && (noprogress+=1) - # if noprogress >= 3 - # @debug(logger,@sprintf( - # "Iterative solver terminated with %4i refinement steps and residual = %6.2e", - # iter,residual_ratio)) - # return :Singular - # end - - IS.div!(IS.res) - x.-=IS.res - IS.mul!(IS.res,x) - IS.res.-=b - norm_res = norm(IS.res,Inf) - - residual_ratio_old = residual_ratio - residual_ratio = norm_res/(1+norm_b) - end - - @debug(IS.logger,@sprintf( - "Iterative solver terminated with %4i refinement steps and residual = %6.2e", - iter,residual_ratio)) - - return (residual_ratio < IS.opt.richardson_acceptable_tol ? :Solved : :Failed) -end -end # module diff --git a/src/MadNLP.jl b/src/MadNLP.jl index b3239c58..b6c1d38f 100644 --- a/src/MadNLP.jl +++ b/src/MadNLP.jl @@ -30,8 +30,8 @@ include("utils.jl") include("options.jl") include("matrixtools.jl") include("nlpmodels.jl") -include(joinpath("LinearSolvers","linearsolvers.jl")) include(joinpath("KKT", "KKTsystem.jl")) +include(joinpath("LinearSolvers","linearsolvers.jl")) include(joinpath("IPM", "IPM.jl")) include(joinpath("Interfaces","interfaces.jl")) diff --git a/src/options.jl b/src/options.jl index ec752262..e88f916b 100644 --- a/src/options.jl +++ b/src/options.jl @@ -23,7 +23,7 @@ end disable_garbage_collector::Bool = false blas_num_threads::Int = 1 linear_solver::Module - iterator::Module = default_iterator() + iterator::Type = RichardsonIterator # Output options output_file::String = "" diff --git a/src/utils.jl b/src/utils.jl index 8476e10f..ebd1ddb8 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -4,7 +4,6 @@ # Build info default_linear_solver() = MadNLPUmfpack default_dense_solver() = MadNLPLapackCPU -default_iterator() = MadNLPRichardson # Dummy module