From a6c9274c083f8a9e8ed6758cfa9c38bf244b7d68 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 15 Sep 2018 00:43:29 -0400 Subject: [PATCH 01/30] up to initial iterate --- .gitignore | 1 + src/nativeinterface.jl | 223 +++++++++++++++++++---------------------- 2 files changed, 106 insertions(+), 118 deletions(-) diff --git a/.gitignore b/.gitignore index 381e0b6d2..57bff62ff 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.jl.*.cov *.jl.mem deps/deps.jl +scratch.jl diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 45e09a319..57dd96f6d 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -12,8 +12,6 @@ mutable struct AlfonsoOpt verbose::Bool # if true, prints progress at each iteration optimtol::Float64 # optimization tolerance parameter maxiter::Int # maximum number of iterations - # itrefinethres::Float64 # iterative refinement success threshold - # maxitrefinesteps::Int # maximum number of iterative refinement steps in linear system solves predlinesearch::Bool # if false, predictor step uses a fixed step size, else step size is determined via line search maxpredsmallsteps::Int # maximum number of predictor step size reductions allowed with respect to the safe fixed step size predlsmulti::Float64 # predictor line search step size multiplier @@ -24,20 +22,14 @@ mutable struct AlfonsoOpt corrlsmulti::Float64 # corrector line search step size multiplier # problem data - A::AbstractMatrix{Float64} # constraint matrix - b::Vector{Float64} # right-hand side vector - c::Vector{Float64} # cost vector - cone::Cone # primal cone object - - # algorithmic parameters - bnu::Float64 - beta::Float64 - eta::Float64 - alphapredthres::Float64 - alphapredinit::Float64 - tol_pres::Float64 - tol_dres::Float64 - tol_compl::Float64 + c::Vector{Float64} # linear cost vector, size n + o::Float64 # objective offset scalar + A::AbstractMatrix{Float64} # equality constraint matrix, size p*n + b::Vector{Float64} # equality constraint vector, size p + G::AbstractMatrix{Float64} # cone constraint matrix, size q*n + h::Vector{Float64} # cone constraint vector, size q + cone::Cone # primal cone object, size q + bnu::Float64 # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) # results status::Symbol # solver status @@ -137,132 +129,125 @@ get_rel_din(alf::AlfonsoOpt) = alf.rel_din # load and verify problem data, calculate algorithmic parameters function load_data!( alf::AlfonsoOpt, + c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, - c::Vector{Float64}, - cone::Cone, + G::AbstractMatrix{Float64}, + h::Vector{Float64}, + cone::Cone; + check=true, # TODO later make false ) + # check data consistency - (m, n) = size(A) - if (m == 0) || (n == 0) - error("input matrix A has trivial dimension $m x $n") - end - if m != length(b) - error("dimension of vector b is $(length(b)), but number of rows in matrix A is $m") - end - if n != length(c) - error("dimension of vector c is $(length(c)), but number of columns in matrix A is $n") - end - if issparse(A) - dropzeros!(A) + if check + n = length(c) + p = length(b) + q = length(h) + @assert n > 0 + @assert p + q > 0 + if n != size(A, 2) || n != size(G, 2) + error("number of variables is not consistent in A, G, and c") + end + if p != size(A, 1) + error("number of constraint rows is not consistent in A and b") + end + if q != size(G, 1) + error("number of constraint rows is not consistent in G and h") + end + # TODO do appropriate decomps at the same time, do preprocessing + if rank(A) < p + error("A matrix is not full-row-rank; some primal equalities may be redundant or inconsistent") + end + if rank(vcat(A, G)) < n + error("[A' G'] is not full-row-rank; some dual equalities may be redundant (i.e. primal variables can be removed) or inconsistent") + end end - # TODO check cone consistency in cone functions file - # idxend = 0 - # for k in eachindex(cone) - # if dimension(cone[k]) != length(coneidxs[k]) - # error("dimension of cone type $(cone[k]) does not match length of variable indices") - # end - # @assert coneidxs[k][1] == idxend + 1 - # idxend += length(coneidxs[k]) - # end - # @assert idxend == n - - # calculate complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) - bnu = 1 + barrierpar(cone) - - # calculate prediction and correction step parameters - (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter - alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size - alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size - alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size - - # calculate termination tolerances: infinity operator norms of submatrices of LHS matrix - tol_pres = max(1.0, maximum(sum(abs, A[i,:]) + abs(b[i]) for i in 1:m)) # first m rows - tol_dres = max(1.0, maximum(sum(abs, A[:,j]) + abs(c[j]) + 1.0 for j in 1:n)) # next n rows - tol_compl = max(1.0, maximum(abs, b), maximum(abs, c)) # row m+n+1 - # save data in solver object + alf.c = c alf.A = A alf.b = b - alf.c = c + alf.G = G + alf.h = h alf.cone = cone - alf.bnu = bnu - alf.beta = beta - alf.eta = eta - alf.alphapredthres = alphapredthres - alf.alphapredinit = alphapredinit - alf.tol_pres = tol_pres - alf.tol_dres = tol_dres - alf.tol_compl = tol_compl - + alf.bnu = 1.0 + barrierpar(cone) alf.status = :Loaded return alf end -# calculate initial central primal-dual iterate -function getinitialiterate(alf::AlfonsoOpt) - (A, b, c) = (alf.A, alf.b, alf.c) - (m, n) = size(A) +# perform prediction and correction steps in a loop until converged +function solve!(alf::AlfonsoOpt) + starttime = time() + + (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) + (n, p, q) = (length(c), length(b), length(h)) cone = alf.cone - # initial primal iterate - tx = similar(c) - getintdir!(tx, cone) - loadpnt!(cone, tx) - @assert incone(cone) + # preallocate arrays + ts = similar(h) + ty = similar(b) + tz = similar(h) + sa_ts = similar(ts) + dir_ts = similar(ts) - # scaling factor for the primal problem - rp = maximum((1.0 + abs(b[i]))/(1.0 + abs(sum(A[i,:]))) for i in 1:m) - # scaling factor for the dual problem - g = similar(c) - calcg!(g, cone) - rd = maximum((1.0 + abs(g[j]))/(1.0 + abs(c[j])) for j in 1:n) + loadpnt!(cone, sa_ts) + + # calculate initial central primal-dual iterate (S5.3 of V.) + # solve linear equation then step in interior direction of cone until inside cone + alf.verbose && println("finding initial iterate") + + # TODO use linsys solve function + xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] + tx .= xyz[1:n] + ty .= xyz[n+1:n+p] + sa_ts .= -xyz[n+p+1:n+p+q] + ts .= sa_ts + + if !incone(cone) + println("not in the cone") + getintdir!(dir_ts, cone) + alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small + steps = 1 + while !incone(cone) + sa_ts .= ts .+ alpha .* dir_ts + alpha *= 1.2 + steps += 1 + if steps > 100 + error("cannot find initial iterate") + end + end + @show alpha + @show steps + ts .= sa_ts + end + + @assert incone(cone) # TODO delete + calcg!(tz, cone) + tz .*= -1.0 - # central primal-dual iterate - tx .*= sqrt(rp*rd) - @assert incone(cone) # TODO a little expensive to call this twice - ty = zeros(m) tau = 1.0 - ts = calcg!(g, cone) - ts .*= -1.0 kap = 1.0 - mu = (dot(tx, ts) + tau*kap)/alf.bnu + mu = (dot(tz, ts) + tau*kap)/alf.bnu - # TODO can delete later + # TODO delete later @assert abs(1.0 - mu) < 1e-8 - @assert abs(calcnbhd(tau*kap, mu, copy(ts), copy(ts), cone)) < 1e-6 + @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 - return (tx, ty, tau, ts, kap, mu) -end + alf.verbose && println("found initial iterate") -# perform prediction and correction steps in a loop until converged -function solve!(alf::AlfonsoOpt) - starttime = time() - (A, b, c) = (alf.A, alf.b, alf.c) - (m, n) = size(A) - cone = alf.cone - # calculate initial central primal-dual iterate - alf.verbose && println("Finding initial iterate") - (tx, ty, tau, ts, kap, mu) = getinitialiterate(alf) + # calculate prediction and correction step parameters + # TODO put in prediction and correction step cache functions + (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, alf.bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter + alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + alf.bnu)) # fixed predictor step size + alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size + alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size - # preallocate arrays # TODO probably could get away with fewer. rename to temp_ - p_res = similar(ty) - d_res = similar(tx) - dir_ty = similar(ty) - dir_tx = similar(tx) - dir_ts = similar(ts) - sa_tx = copy(tx) - sa_ts = similar(ts) - g = similar(tx) - HiAt = similar(b, n, m) # TODO for very sparse LPs, using sparse here is good (diagonal hessian), but for sparse problems with dense hessians, want dense - AHiAt = similar(b, m, m) - y1 = similar(b) - x1 = similar(c) - y2 = similar(b) - x2 = similar(c) + + # (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) + tol_pres = inv(1.0 + norm(b)) + tol_dres = inv(1.0 + norm(c)) # main loop if alf.verbose @@ -271,12 +256,14 @@ function solve!(alf::AlfonsoOpt) flush(stdout) end - loadpnt!(cone, sa_tx) alf.status = :StartedIterating - alphapred = alf.alphapredinit + alphapred = alphapredinit iter = 0 while true - # calculate convergence metrics + # calculate convergence metrics + + + ctx = dot(c, tx) bty = dot(b, ty) p_obj = ctx/tau From 298eba031e8b068a9df6f8a9309477dfedf214eb Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 17 Sep 2018 18:11:00 -0400 Subject: [PATCH 02/30] use variable cones --- src/cone.jl | 14 +- src/nativeinterface.jl | 489 +++++++++++++++++++++++++++++++++++------ 2 files changed, 438 insertions(+), 65 deletions(-) diff --git a/src/cone.jl b/src/cone.jl index 5324d6b2a..b3cc51ffd 100644 --- a/src/cone.jl +++ b/src/cone.jl @@ -14,11 +14,19 @@ end # calculate complexity parameter of the barrier (sum of the primitive cone barrier parameters) barrierpar(cone::Cone) = sum(barrierpar_prm(prm) for prm in cone.prms) -function getintdir!(arr::Vector{Float64}, cone::Cone) +function getincidence!(a::Vector{Float64}, cone::Cone) + a .= 0.0 + for prm in prms, j in prm.idxs + a[j] += 1.0 + end + return a +end + +function getintdir!(a::Vector{Float64}, cone::Cone) for k in eachindex(cone.prms) - getintdir_prm!(view(arr, cone.idxs[k]), cone.prms[k]) + getintdir_prm!(view(a, cone.idxs[k]), cone.prms[k]) end - return arr + return a end # TODO can parallelize the functions acting on Cone diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 57dd96f6d..5d09798db 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -26,10 +26,10 @@ mutable struct AlfonsoOpt o::Float64 # objective offset scalar A::AbstractMatrix{Float64} # equality constraint matrix, size p*n b::Vector{Float64} # equality constraint vector, size p + varK::Cone # primal variable cone object G::AbstractMatrix{Float64} # cone constraint matrix, size q*n h::Vector{Float64} # cone constraint vector, size q - cone::Cone # primal cone object, size q - bnu::Float64 # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) + conK::Cone # primal constraint cone object # results status::Symbol # solver status @@ -132,9 +132,10 @@ function load_data!( c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, + varK::Cone, G::AbstractMatrix{Float64}, h::Vector{Float64}, - cone::Cone; + conK::Cone; check=true, # TODO later make false ) @@ -167,27 +168,29 @@ function load_data!( alf.c = c alf.A = A alf.b = b + alf.varK = varK alf.G = G alf.h = h - alf.cone = cone - alf.bnu = 1.0 + barrierpar(cone) + alf.conK = conK alf.status = :Loaded return alf end -# perform prediction and correction steps in a loop until converged -function solve!(alf::AlfonsoOpt) +# solve using basic method (no HSDE) +function solve_basic!(alf::AlfonsoOpt) starttime = time() (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) + (varK, conK) = (alf.varK, alf.conK) (n, p, q) = (length(c), length(b), length(h)) - cone = alf.cone + bnu = barrierpar(varK) + barrierpar(conK) # complexity parameter of the barrier (sum of the primitive cone barrier parameters) # preallocate arrays + tx = similar(c) ts = similar(h) ty = similar(b) - tz = similar(h) + tz = similar(ts) sa_ts = similar(ts) dir_ts = similar(ts) @@ -198,7 +201,7 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("finding initial iterate") # TODO use linsys solve function - xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] + xyz = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] tx .= xyz[1:n] ty .= xyz[n+1:n+p] sa_ts .= -xyz[n+p+1:n+p+q] @@ -226,9 +229,7 @@ function solve!(alf::AlfonsoOpt) calcg!(tz, cone) tz .*= -1.0 - tau = 1.0 - kap = 1.0 - mu = (dot(tz, ts) + tau*kap)/alf.bnu + mu = dot(tz, ts)/bnu # TODO delete later @assert abs(1.0 - mu) < 1e-8 @@ -239,15 +240,15 @@ function solve!(alf::AlfonsoOpt) # calculate prediction and correction step parameters # TODO put in prediction and correction step cache functions - (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, alf.bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter - alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + alf.bnu)) # fixed predictor step size + (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter + alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size # (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) - tol_pres = inv(1.0 + norm(b)) - tol_dres = inv(1.0 + norm(c)) + # tol_pres = inv(1.0 + norm(b)) + # tol_dres = inv(1.0 + norm(c)) # main loop if alf.verbose @@ -260,48 +261,49 @@ function solve!(alf::AlfonsoOpt) alphapred = alphapredinit iter = 0 while true - # calculate convergence metrics - - - - ctx = dot(c, tx) - bty = dot(b, ty) - p_obj = ctx/tau - d_obj = bty/tau - rel_gap = abs(ctx - bty)/(tau + abs(bty)) - # p_res = -A*tx + b*tau - mul!(p_res, A, tx) - p_res .= tau .* b .- p_res - p_inf = maximum(abs, p_res)/alf.tol_pres - # d_res = A'*ty - c*tau + ts - mul!(d_res, A', ty) - d_res .+= ts .- tau .* c - d_inf = maximum(abs, d_res)/alf.tol_dres - abs_gap = -bty + ctx + kap - compl = abs(abs_gap)/alf.tol_compl - - if alf.verbose - # print iteration statistics - @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) - flush(stdout) - end - - # check convergence criteria - if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) - if rel_gap <= alf.optimtol - alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") - alf.status = :Optimal - break - elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) - alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") - alf.status = :NearlyInfeasible - break - end - elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) - alf.verbose && println("Problem is ill-posed; terminating") - alf.status = :IllPosed - break - end + # calculate residuals + # TODO in-place + res_tx = -A'*ty - G'*tz - c*tau + res_ty = A*tx - b*tau + res_tz = ts + G*x - h*tau + res_tau = kap + dot(c, x) + dot(b, y) + dot(h, z) + + # TODO calculate convergence metrics + # ctx = dot(c, tx) + # bty = dot(b, ty) + # p_obj = ctx/tau + # d_obj = bty/tau + # rel_gap = abs(ctx - bty)/(tau + abs(bty)) + # p_inf = maximum(abs, p_res)/alf.tol_pres + # # d_res = A'*ty - c*tau + ts + # mul!(d_res, A', ty) + # d_res .+= ts .- tau .* c + # d_inf = maximum(abs, d_res)/alf.tol_dres + # abs_gap = -bty + ctx + kap + # compl = abs(abs_gap)/alf.tol_compl + # + # if alf.verbose + # # print iteration statistics + # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) + # flush(stdout) + # end + # + # # check convergence criteria + # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) + # if rel_gap <= alf.optimtol + # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") + # alf.status = :Optimal + # break + # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) + # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") + # alf.status = :NearlyInfeasible + # break + # end + # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) + # alf.verbose && println("Problem is ill-posed; terminating") + # alf.status = :IllPosed + # break + # end # check iteration limit iter += 1 @@ -313,6 +315,12 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction + + + + + + # x rhs is (ts - d_res), y rhs is p_res dir_tx .= ts .- d_res # temp for x rhs (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, p_res, A, b, c, cone, HiAt, AHiAt) @@ -342,7 +350,7 @@ function solve!(alf::AlfonsoOpt) # primal iterate is inside the cone sa_ts .= ts .+ alpha .* dir_ts sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) - sa_mu = (dot(sa_tx, sa_ts) + sa_tk)/alf.bnu + sa_mu = (dot(sa_tx, sa_ts) + sa_tk)/bnu nbhd = calcnbhd(sa_tk, sa_mu, sa_ts, g, cone) if nbhd < abs2(alf.beta*sa_mu) @@ -395,7 +403,7 @@ function solve!(alf::AlfonsoOpt) tau += alpha*dir_tau ts .+= alpha .* dir_ts kap += alpha*dir_kap - mu = (dot(tx, ts) + tau*kap)/alf.bnu + mu = (dot(tx, ts) + tau*kap)/bnu # skip correction phase if allowed and current iterate is in the eta-neighborhood if alf.corrcheck && (nbhd < abs2(alf.eta*mu)) @@ -458,7 +466,7 @@ function solve!(alf::AlfonsoOpt) tau += alpha*dir_tau ts .+= alpha .* dir_ts kap += alpha*dir_kap - mu = (dot(tx, ts) + tau*kap)/alf.bnu + mu = (dot(tx, ts) + tau*kap)/bnu # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck @@ -520,6 +528,363 @@ function solve!(alf::AlfonsoOpt) return nothing end + + + + +# # solve using HSDE +# function solve_hsde!(alf::AlfonsoOpt) +# starttime = time() +# +# (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) +# (varK, conK) = (alf.varK, alf.conK) +# (n, p, q) = (length(c), length(b), length(h)) +# bnu = 1.0 + barrierpar(varK) + barrierpar(conK) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) +# +# # preallocate arrays +# tx = similar(c) +# ts = similar(h) +# ty = similar(b) +# tz = similar(ts) +# sa_ts = similar(ts) +# dir_ts = similar(ts) +# +# loadpnt!(cone, sa_ts) +# +# # calculate initial central primal-dual iterate (S5.3 of V.) +# # solve linear equation then step in interior direction of cone until inside cone +# alf.verbose && println("finding initial iterate") +# +# # TODO use linsys solve function +# xyz = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] +# tx .= xyz[1:n] +# ty .= xyz[n+1:n+p] +# sa_ts .= -xyz[n+p+1:n+p+q] +# ts .= sa_ts +# +# if !incone(cone) +# println("not in the cone") +# getintdir!(dir_ts, cone) +# alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small +# steps = 1 +# while !incone(cone) +# sa_ts .= ts .+ alpha .* dir_ts +# alpha *= 1.2 +# steps += 1 +# if steps > 100 +# error("cannot find initial iterate") +# end +# end +# @show alpha +# @show steps +# ts .= sa_ts +# end +# +# @assert incone(cone) # TODO delete +# calcg!(tz, cone) +# tz .*= -1.0 +# +# tau = 1.0 +# kap = 1.0 +# mu = (dot(tz, ts) + tau*kap)/bnu +# +# # TODO delete later +# @assert abs(1.0 - mu) < 1e-8 +# @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 +# +# alf.verbose && println("found initial iterate") +# +# +# # calculate prediction and correction step parameters +# # TODO put in prediction and correction step cache functions +# (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter +# alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size +# alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size +# alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size +# +# +# # (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) +# # tol_pres = inv(1.0 + norm(b)) +# # tol_dres = inv(1.0 + norm(c)) +# +# # main loop +# if alf.verbose +# println("Starting iteration") +# @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "gap", "p_inf", "d_inf", "tau", "kap", "mu") +# flush(stdout) +# end +# +# alf.status = :StartedIterating +# alphapred = alphapredinit +# iter = 0 +# while true +# # calculate residuals +# # TODO in-place +# res_tx = -A'*ty - G'*tz - c*tau +# res_ty = A*tx - b*tau +# res_tz = ts + G*x - h*tau +# res_tau = kap + dot(c, x) + dot(b, y) + dot(h, z) +# +# # TODO calculate convergence metrics +# # ctx = dot(c, tx) +# # bty = dot(b, ty) +# # p_obj = ctx/tau +# # d_obj = bty/tau +# # rel_gap = abs(ctx - bty)/(tau + abs(bty)) +# # p_inf = maximum(abs, p_res)/alf.tol_pres +# # # d_res = A'*ty - c*tau + ts +# # mul!(d_res, A', ty) +# # d_res .+= ts .- tau .* c +# # d_inf = maximum(abs, d_res)/alf.tol_dres +# # abs_gap = -bty + ctx + kap +# # compl = abs(abs_gap)/alf.tol_compl +# # +# # if alf.verbose +# # # print iteration statistics +# # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) +# # flush(stdout) +# # end +# # +# # # check convergence criteria +# # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) +# # if rel_gap <= alf.optimtol +# # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") +# # alf.status = :Optimal +# # break +# # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) +# # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") +# # alf.status = :NearlyInfeasible +# # break +# # end +# # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) +# # alf.verbose && println("Problem is ill-posed; terminating") +# # alf.status = :IllPosed +# # break +# # end +# +# # check iteration limit +# iter += 1 +# if iter >= alf.maxiter +# alf.verbose && println("Reached iteration limit; terminating") +# alf.status = :IterationLimit +# break +# end +# +# # prediction phase +# # calculate prediction direction +# +# +# +# +# +# +# # x rhs is (ts - d_res), y rhs is p_res +# dir_tx .= ts .- d_res # temp for x rhs +# (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, p_res, A, b, c, cone, HiAt, AHiAt) +# +# dir_tau = (abs_gap - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) +# dir_ty .= y2 .+ dir_tau .* y1 +# dir_tx .= x2 .+ dir_tau .* x1 +# mul!(dir_ts, A', dir_ty) +# dir_ts .= dir_tau .* c .- dir_ts .- d_res +# dir_kap = -abs_gap + dot(b, dir_ty) - dot(c, dir_tx) +# +# # determine step length alpha by line search +# alpha = alphapred +# nbhd = Inf +# alphaprevok = true +# predfail = false +# nprediters = 0 +# while true +# nprediters += 1 +# +# sa_tx .= tx .+ alpha .* dir_tx +# +# # accept primal iterate if +# # - decreased alpha and it is the first inside the cone and beta-neighborhood or +# # - increased alpha and it is inside the cone and the first to leave beta-neighborhood +# if incone(cone) +# # primal iterate is inside the cone +# sa_ts .= ts .+ alpha .* dir_ts +# sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) +# sa_mu = (dot(sa_tx, sa_ts) + sa_tk)/bnu +# nbhd = calcnbhd(sa_tk, sa_mu, sa_ts, g, cone) +# +# if nbhd < abs2(alf.beta*sa_mu) +# # iterate is inside the beta-neighborhood +# if !alphaprevok || (alpha > alf.predlsmulti) +# # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 +# if alf.predlinesearch +# alphapred = alpha +# end +# break +# end +# +# alphaprevok = true +# alpha = alpha/alf.predlsmulti # increase alpha +# continue +# end +# +# # # iterate is outside the beta-neighborhood +# # if alphaprevok # TODO technically this should be only if nprediters > 1, but it seems to work +# # # previous iterate was inside the beta-neighborhood +# # if alf.predlinesearch +# # alphapred = alpha*alf.predlsmulti +# # end +# # break +# # end +# end +# +# # primal iterate is either +# # - outside the cone or +# # - inside the cone and outside the beta-neighborhood and previous iterate was outside the beta-neighborhood +# if alpha < alf.alphapredthres +# # alpha is very small, so predictor has failed +# predfail = true +# alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") +# alf.status = :PredictorFail +# break +# end +# +# alphaprevok = false +# alpha = alf.predlsmulti*alpha # decrease alpha +# end +# # @show nprediters +# if predfail +# break +# end +# +# # step distance alpha in the direction +# ty .+= alpha .* dir_ty +# tx .= sa_tx +# tau += alpha*dir_tau +# ts .+= alpha .* dir_ts +# kap += alpha*dir_kap +# mu = (dot(tx, ts) + tau*kap)/bnu +# +# # skip correction phase if allowed and current iterate is in the eta-neighborhood +# if alf.corrcheck && (nbhd < abs2(alf.eta*mu)) +# continue +# end +# +# # correction phase +# corrfail = false +# ncorrsteps = 0 +# while true +# ncorrsteps += 1 +# +# # calculate correction direction +# # x rhs is (ts + mu*g), y rhs is 0 +# calcg!(g, cone) +# dir_tx .= ts .+ mu .* g # temp for x rhs +# dir_ty .= 0.0 # temp for y rhs +# (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, dir_ty, A, b, c, cone, HiAt, AHiAt) +# +# dir_tau = (mu/tau - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) +# dir_ty .= y2 .+ dir_tau .* y1 +# dir_tx .= x2 .+ dir_tau .* x1 +# # dir_ts = -A'*dir_ty + c*dir_tau +# mul!(dir_ts, A', dir_ty) +# dir_ts .= dir_tau .* c .- dir_ts +# dir_kap = dot(b, dir_ty) - dot(c, dir_tx) +# +# # determine step length alpha by line search +# alpha = alf.alphacorr +# ncorrlsiters = 0 +# while ncorrlsiters <= alf.maxcorrlsiters +# ncorrlsiters += 1 +# +# sa_tx .= tx .+ alpha .* dir_tx +# +# if incone(cone) +# # primal iterate tx is inside the cone, so terminate line search +# break +# end +# +# # primal iterate tx is outside the cone +# if ncorrlsiters == alf.maxcorrlsiters +# # corrector failed +# corrfail = true +# alf.verbose && println("Corrector could not improve the solution ($ncorrlsiters line search steps); terminating") +# alf.status = :CorrectorFail +# break +# end +# +# alpha = alf.corrlsmulti*alpha # decrease alpha +# end +# # @show ncorrlsiters +# if corrfail +# break +# end +# +# # step distance alpha in the direction +# ty .+= alpha .* dir_ty +# tx .= sa_tx +# tau += alpha*dir_tau +# ts .+= alpha .* dir_ts +# kap += alpha*dir_kap +# mu = (dot(tx, ts) + tau*kap)/bnu +# +# # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps +# if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck +# sa_ts .= ts +# if calcnbhd(tau*kap, mu, sa_ts, g, cone) <= abs2(alf.eta*mu) +# break +# elseif ncorrsteps == alf.maxcorrsteps +# # nbhd_eta > eta, so corrector failed +# corrfail = true +# alf.verbose && println("Corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") +# alf.status = :CorrectorFail +# break +# end +# end +# end +# # @show ncorrsteps +# if corrfail +# break +# end +# end +# +# alf.verbose && println("\nFinished in $iter iterations\nInternal status is $(alf.status)\n") +# +# # calculate final solution and iteration statistics +# alf.niters = iter +# +# tx ./= tau +# alf.x = tx +# ty ./= tau +# alf.y = ty +# alf.tau = tau +# ts ./= tau +# alf.s = ts +# alf.kap = kap +# +# alf.pobj = dot(c, alf.x) +# alf.dobj = dot(b, alf.y) +# alf.dgap = alf.pobj - alf.dobj +# alf.cgap = dot(alf.s, alf.x) +# alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) +# alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) +# +# # alf.pres = b - A*alf.x +# mul!(p_res, A, alf.x) +# p_res .= b .- p_res +# alf.pres = p_res +# # alf.dres = c - A'*alf.y - alf.s +# mul!(d_res, A', alf.y) +# d_res .= c .- d_res .- alf.s +# alf.dres = d_res +# +# alf.pin = norm(alf.pres) +# alf.din = norm(alf.dres) +# alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) +# alf.rel_din = alf.din/(1.0 + norm(c, Inf)) +# +# alf.solvetime = time() - starttime +# +# return nothing +# end + function calcnbhd(tk, mu, sa_ts, g, cone) calcg!(g, cone) sa_ts .+= mu .* g From 5203d11bc3c332045b025e2b3943d89659eb3455 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 21 Sep 2018 09:03:55 -0400 Subject: [PATCH 03/30] pred and corr finally seem to work --- src/nativeinterface.jl | 1082 ++++++++++++++++++++++------------------ test/nativeexamples.jl | 580 +++++++++++---------- 2 files changed, 909 insertions(+), 753 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 5d09798db..f4ff23d96 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -1,12 +1,4 @@ -#= -Copyright 2018, David Papp, Sercan Yildiz, and contributors -an implementation of the algorithm for non-symmetric conic optimization Alfonso (https://github.com/dpapp-github/alfonso) and analyzed in the paper: -D. Papp and S. Yildiz. On "A homogeneous interior-point algorithm for nonsymmetric convex conic optimization" -available at https://arxiv.org/abs/1712.00492 -=# - -# TODO add time limit option and use it in loop mutable struct AlfonsoOpt # options verbose::Bool # if true, prints progress at each iteration @@ -26,7 +18,7 @@ mutable struct AlfonsoOpt o::Float64 # objective offset scalar A::AbstractMatrix{Float64} # equality constraint matrix, size p*n b::Vector{Float64} # equality constraint vector, size p - varK::Cone # primal variable cone object + # varK::Cone # primal variable cone object G::AbstractMatrix{Float64} # cone constraint matrix, size q*n h::Vector{Float64} # cone constraint vector, size q conK::Cone # primal constraint cone object @@ -132,7 +124,7 @@ function load_data!( c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, - varK::Cone, + # varK::Cone, G::AbstractMatrix{Float64}, h::Vector{Float64}, conK::Cone; @@ -168,7 +160,7 @@ function load_data!( alf.c = c alf.A = A alf.b = b - alf.varK = varK + # alf.varK = varK alf.G = G alf.h = h alf.conK = conK @@ -177,14 +169,386 @@ function load_data!( return alf end -# solve using basic method (no HSDE) -function solve_basic!(alf::AlfonsoOpt) +# solve without HSDE +# function solve_basic!(alf::AlfonsoOpt) +# starttime = time() +# +# (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) +# cone = alf.conK +# # (varK, conK) = (alf.varK, alf.conK) +# (n, p, q) = (length(c), length(b), length(h)) +# bnu = barrierpar(cone) # + barrierpar(varK) # complexity parameter of the barrier (sum of the primitive cone barrier parameters) +# +# # preallocate arrays +# tx = similar(c) +# ts = similar(h) +# ty = similar(b) +# tz = similar(ts) +# sa_ts = similar(ts) +# sa_tz = similar(tz) +# dir_ts = similar(ts) +# g = similar(ts) +# +# loadpnt!(cone, sa_ts) +# +# # calculate initial central primal-dual iterate (S5.3 of V.) +# # solve linear equation then step in interior direction of cone until inside cone +# alf.verbose && println("finding initial iterate") +# +# # TODO use linsys solve function +# # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] +# lhs = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) +# xyz = lhs\[-c; b; h] +# tx .= xyz[1:n] +# ty .= xyz[n+1:n+p] +# sa_ts .= -xyz[n+p+1:n+p+q] +# ts .= sa_ts +# +# if !incone(cone) +# println("not in the cone") +# getintdir!(dir_ts, cone) +# alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small +# steps = 1 +# while !incone(cone) +# sa_ts .= ts .+ alpha .* dir_ts +# alpha *= 1.2 +# steps += 1 +# if steps > 100 +# error("cannot find initial iterate") +# end +# end +# @show alpha +# @show steps +# ts .= sa_ts +# end +# +# @assert incone(cone) # TODO delete +# calcg!(tz, cone) +# tz .*= -1.0 +# mu = dot(tz, ts)/bnu +# +# # TODO delete later +# @assert abs(1.0 - mu) < 1e-8 +# @assert calcnbhd(mu, copy(tz), copy(tz), cone) < 1e-6 +# +# alf.verbose && println("found initial iterate") +# +# +# # calculate prediction and correction step parameters +# # TODO put in prediction and correction step cache functions +# (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter +# alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size +# alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size +# alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size +# +# # # main loop +# # if alf.verbose +# # println("Starting iteration") +# # @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "gap", "p_inf", "d_inf", "tau", "kap", "mu") +# # flush(stdout) +# # end +# +# alf.status = :StartedIterating +# alphapred = alphapredinit +# iter = 0 +# while true +# # calculate residuals +# # TODO in-place +# res_tx = A'*ty + G'*tz + c +# res_ty = A*tx - b +# res_tz = ts + G*tx - h +# +# # TODO calculate convergence metrics +# # ctx = dot(c, tx) +# # bty = dot(b, ty) +# # p_obj = ctx/tau +# # d_obj = bty/tau +# # rel_gap = abs(ctx - bty)/(tau + abs(bty)) +# # p_inf = maximum(abs, p_res)/alf.tol_pres +# # # d_res = A'*ty - c*tau + ts +# # mul!(d_res, A', ty) +# # d_res .+= ts .- tau .* c +# # d_inf = maximum(abs, d_res)/alf.tol_dres +# # abs_gap = -bty + ctx + kap +# # compl = abs(abs_gap)/alf.tol_compl +# # +# # if alf.verbose +# # # print iteration statistics +# # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) +# # flush(stdout) +# # end +# # +# # # check convergence criteria +# # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) +# # if rel_gap <= alf.optimtol +# # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") +# # alf.status = :Optimal +# # break +# # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) +# # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") +# # alf.status = :NearlyInfeasible +# # break +# # end +# # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) +# # alf.verbose && println("Problem is ill-posed; terminating") +# # alf.status = :IllPosed +# # break +# # end +# +# # check iteration limit +# iter += 1 +# if iter >= alf.maxiter +# alf.verbose && println("Reached iteration limit; terminating") +# alf.status = :IterationLimit +# break +# end +# +# # prediction phase +# # calculate prediction direction +# Hi = zeros(q, q) +# calcHiarr!(Hi, Matrix(1.0I, q, q), cone) +# lhs.data[n+p+1:n+p+q,n+p+1:n+p+q] .= -Hi +# dir = lhs\[-res_tx; -res_ty; ts - res_tz] +# +# dir_tx = dir[1:n] +# dir_ty = dir[n+1:n+p] +# dir_tz = dir[n+p+1:n+p+q] +# # (dir_tx, dir_ty, dir_tz) = finddirection(alf, lhs, -res_tx, -res_ty, ts - res_tz) +# # dir_ts = -res_tz - G*dir_tx +# dir_ts = -ts - Hi*dir_tz +# # @show norm(dir_ts - (-ts - Hi*dir_tz)) +# +# # determine step length alpha by line search +# alpha = alphapred +# nbhd = Inf +# alphaprevok = true +# predfail = false +# nprediters = 0 +# while true +# nprediters += 1 +# +# sa_ts .= ts .+ alpha .* dir_ts +# @show alpha +# # accept primal iterate if +# # - decreased alpha and it is the first inside the cone and beta-neighborhood or +# # - increased alpha and it is inside the cone and the first to leave beta-neighborhood +# if incone(cone) +# # primal iterate is inside the cone +# sa_tz .= tz .+ alpha .* dir_tz +# sa_mu = dot(sa_ts, sa_tz)/bnu +# nbhd = calcnbhd(sa_mu, sa_tz, g, cone) +# @show sqrt(nbhd)/sa_mu, beta +# if nbhd < abs2(beta*sa_mu) +# # iterate is inside the beta-neighborhood +# if !alphaprevok || (alpha > alf.predlsmulti) +# # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 +# if alf.predlinesearch +# alphapred = alpha +# end +# break +# end +# +# alphaprevok = true +# alpha = alpha/alf.predlsmulti # increase alpha +# continue +# end +# end +# +# # primal iterate is either +# # - outside the cone or +# # - inside the cone and outside the beta-neighborhood and previous iterate was outside the beta-neighborhood +# if alpha < alphapredthres +# # alpha is very small, so predictor has failed +# predfail = true +# alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") +# alf.status = :PredictorFail +# break +# end +# +# alphaprevok = false +# alpha = alf.predlsmulti*alpha # decrease alpha +# end +# # @show nprediters +# if predfail +# break +# end +# +# # step distance alpha in the direction +# tx .+= alpha .* dir_tx +# ty .+= alpha .* dir_ty +# tz .+= alpha .* dir_tz +# ts .= sa_ts +# mu = dot(ts, tz)/bnu +# @show mu +# +# # skip correction phase if allowed and current iterate is in the eta-neighborhood +# if alf.corrcheck && (nbhd < abs2(eta*mu)) +# continue +# end +# +# # correction phase +# corrfail = false +# ncorrsteps = 0 +# while true +# ncorrsteps += 1 +# +# # calculate correction direction +# Hi = zeros(q, q) +# calcHiarr!(Hi, Matrix(1.0I, q, q), cone) +# +# lhsnew = [zeros(n, n) A' G'; -A zeros(p, p) zeros(p, q); -G zeros(q, p) -Hi/mu] +# +# Hipsi = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies +# dir = lhsnew\[zeros(n); zeros(p); Hipsi] +# +# dir_tx = dir[1:n] +# dir_ty = dir[n+1:n+p] +# dir_tz = dir[n+p+1:n+p+q] +# # (dir_tx, dir_ty, dir_tz) = finddirection(alf, lhs, zeros(n), zeros(p), tz + mu*calcg!(g, cone)) +# dir_ts = Hipsi + Hi/mu*dir_tz +# # dir_ts = -ts - Hi*dir_tz +# # dir_ts = -ts - mu*calcg!(g, cone) - Hi*dir_tz +# # @show norm(dir_ts - (-tz - mu*calcg!(g, cone) - Hi*dir_tz)) +# +# # determine step length alpha by line search +# alpha = alf.alphacorr +# ncorrlsiters = 0 +# while ncorrlsiters <= alf.maxcorrlsiters +# ncorrlsiters += 1 +# +# sa_ts .= ts .+ alpha .* dir_ts +# @show alpha +# if incone(cone) +# # primal iterate tx is inside the cone, so terminate line search +# break +# end +# +# # primal iterate tx is outside the cone +# if ncorrlsiters == alf.maxcorrlsiters +# # corrector failed +# corrfail = true +# alf.verbose && println("Corrector could not improve the solution ($ncorrlsiters line search steps); terminating") +# alf.status = :CorrectorFail +# break +# end +# +# alpha = alf.corrlsmulti*alpha # decrease alpha +# end +# # @show ncorrlsiters +# if corrfail +# break +# end +# +# # step distance alpha in the direction +# tx .+= alpha .* dir_tx +# ty .+= alpha .* dir_ty +# tz .+= alpha .* dir_tz +# ts .= sa_ts +# mu = dot(ts, tz)/bnu +# @show mu +# +# # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps +# if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck +# sa_tz .= tz +# nbhd = calcnbhd(mu, sa_tz, g, cone) +# @show sqrt(nbhd)/mu, eta +# if nbhd <= abs2(eta*mu) +# break +# elseif ncorrsteps == alf.maxcorrsteps +# # nbhd_eta > eta, so corrector failed +# corrfail = true +# alf.verbose && println("Corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") +# alf.status = :CorrectorFail +# break +# end +# end +# end +# # @show ncorrsteps +# if corrfail +# break +# end +# +# return +# end +# +# alf.verbose && println("\nFinished in $iter iterations\nInternal status is $(alf.status)\n") +# +# # calculate final solution and iteration statistics +# alf.niters = iter +# +# # tx ./= tau +# # alf.x = tx +# # ty ./= tau +# # alf.y = ty +# # alf.tau = tau +# # ts ./= tau +# # alf.s = ts +# # alf.kap = kap +# # +# # alf.pobj = dot(c, alf.x) +# # alf.dobj = dot(b, alf.y) +# # alf.dgap = alf.pobj - alf.dobj +# # alf.cgap = dot(alf.s, alf.x) +# # alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) +# # alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) +# # +# # # alf.pres = b - A*alf.x +# # mul!(p_res, A, alf.x) +# # p_res .= b .- p_res +# # alf.pres = p_res +# # # alf.dres = c - A'*alf.y - alf.s +# # mul!(d_res, A', alf.y) +# # d_res .= c .- d_res .- alf.s +# # alf.dres = d_res +# # +# # alf.pin = norm(alf.pres) +# # alf.din = norm(alf.dres) +# # alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) +# # alf.rel_din = alf.din/(1.0 + norm(c, Inf)) +# +# alf.solvetime = time() - starttime +# +# return nothing +# end +# +# +# # function finddirection(alf, lhs, rhs_tx, rhs_ty, rhs_tz) +# # (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) +# # cone = alf.conK +# # (n, p, q) = (length(c), length(b), length(h)) +# # +# # Hi = zeros(q, q) +# # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) +# # lhs.data[n+p+1:n+p+q,n+p+1:n+p+q] .= -Hi +# # dir = lhs\[rhs_tx; rhs_ty; rhs_tz] +# # +# # dir_tx = dir[1:n] +# # dir_ty = dir[n+1:n+p] +# # dir_tz = dir[n+p+1:n+p+q] +# # +# # return (dir_tx, dir_ty, dir_tz) +# # end +# +# function calcnbhd(mu, sa_tz, g, cone) +# sa_tz .+= mu .* calcg!(g, cone) +# calcHiarr!(g, sa_tz, cone) +# return dot(sa_tz, g) +# end + + + + + + +# solve using HSDE +function solve!(alf::AlfonsoOpt) starttime = time() (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) - (varK, conK) = (alf.varK, alf.conK) + cone = alf.conK + # (varK, conK) = (alf.varK, alf.conK) (n, p, q) = (length(c), length(b), length(h)) - bnu = barrierpar(varK) + barrierpar(conK) # complexity parameter of the barrier (sum of the primitive cone barrier parameters) + bnu = 1.0 + barrierpar(cone) # + barrierpar(varK) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) # preallocate arrays tx = similar(c) @@ -192,7 +556,9 @@ function solve_basic!(alf::AlfonsoOpt) ty = similar(b) tz = similar(ts) sa_ts = similar(ts) + sa_tz = similar(tz) dir_ts = similar(ts) + g = similar(ts) loadpnt!(cone, sa_ts) @@ -201,7 +567,8 @@ function solve_basic!(alf::AlfonsoOpt) alf.verbose && println("finding initial iterate") # TODO use linsys solve function - xyz = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] + # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] + xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] tx .= xyz[1:n] ty .= xyz[n+1:n+p] sa_ts .= -xyz[n+p+1:n+p+q] @@ -229,7 +596,9 @@ function solve_basic!(alf::AlfonsoOpt) calcg!(tz, cone) tz .*= -1.0 - mu = dot(tz, ts)/bnu + tau = 1.0 + kap = 1.0 + mu = (dot(tz, ts) + tau*kap)/bnu # TODO delete later @assert abs(1.0 - mu) < 1e-8 @@ -265,9 +634,20 @@ function solve_basic!(alf::AlfonsoOpt) # TODO in-place res_tx = -A'*ty - G'*tz - c*tau res_ty = A*tx - b*tau - res_tz = ts + G*x - h*tau - res_tau = kap + dot(c, x) + dot(b, y) + dot(h, z) - + res_ts = ts + G*tx - h*tau + res_tau = kap + dot(c, tx) + dot(b, ty) + dot(h, tz) + + println(iter) + @show res_tx + @show res_ty + @show res_ts + @show res_tau + @show tau + @show kap + @show mu + @show dot(c, tx)/tau + @show -(dot(b, ty) + dot(h, tz))/tau + # TODO calculate convergence metrics # ctx = dot(c, tx) # bty = dot(b, ty) @@ -315,23 +695,45 @@ function solve_basic!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction + # (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) = finddirection(alf, res_tx, res_ty, res_tz, res_tau, ts, kap*tau, kap, tau) + # Hi = zeros(q, q) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + # + # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi/mu h; -c' -b' -h' tau^2/mu] + # rhsnew = -lhsnew*[tx; ty; tz; tau] + [zeros(n); zeros(p); ts; kap] + # dir = lhsnew\rhsnew + # + # dir_tx = dir[1:n] + # dir_ty = dir[n+1:n+p] + # dir_tz = dir[n+p+1:n+p+q] + # dir_tau = dir[n+p+q+1] + # dir_ts = Hi/mu*(-tz - dir_tz) + # dir_kap = tau^2/mu*(-tau - dir_tau) + + sa_ts .= ts + H = Diagonal(inv.(abs2.(sa_ts))) + + # tx ty tz kap ts tau + lhsbig = [ + zeros(n, n) A' G' zeros(n) zeros(n, q) c; + -A zeros(p, p) zeros(p, q) zeros(p) zeros(p, q) b; + zeros(q, n) zeros(q, p) Matrix(1.0I, q, q) zeros(q) mu*H zeros(q); + zeros(1, n) zeros(1, p) zeros(1, q) 1.0 zeros(1, q) mu/tau^2 + -G zeros(q, p) zeros(q, q) zeros(q) Matrix(-1.0I, q, q) h; + -c' -b' -h' -1.0 zeros(1, q) 0.0; + ] + + rhsbig = [res_tx; res_ty; -tz; -kap; res_ts; res_tau] + dir = lhsbig\rhsbig + dir_tx = dir[1:n] + dir_ty = dir[n+1:n+p] + dir_tz = dir[n+p+1:n+p+q] + dir_kap = dir[n+p+q+1] + dir_ts = dir[n+p+q+2:n+p+2*q+1] + dir_tau = dir[n+p+2*q+2] - - - - # x rhs is (ts - d_res), y rhs is p_res - dir_tx .= ts .- d_res # temp for x rhs - (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, p_res, A, b, c, cone, HiAt, AHiAt) - - dir_tau = (abs_gap - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) - dir_ty .= y2 .+ dir_tau .* y1 - dir_tx .= x2 .+ dir_tau .* x1 - mul!(dir_ts, A', dir_ty) - dir_ts .= dir_tau .* c .- dir_ts .- d_res - dir_kap = -abs_gap + dot(b, dir_ty) - dot(c, dir_tx) - # determine step length alpha by line search alpha = alphapred nbhd = Inf @@ -341,19 +743,20 @@ function solve_basic!(alf::AlfonsoOpt) while true nprediters += 1 - sa_tx .= tx .+ alpha .* dir_tx - + sa_ts .= ts .+ alpha .* dir_ts + @show alpha # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood if incone(cone) # primal iterate is inside the cone - sa_ts .= ts .+ alpha .* dir_ts + sa_tz .= tz .+ alpha .* dir_tz sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) - sa_mu = (dot(sa_tx, sa_ts) + sa_tk)/bnu - nbhd = calcnbhd(sa_tk, sa_mu, sa_ts, g, cone) + sa_mu = (dot(sa_ts, sa_tz) + sa_tk)/bnu + nbhd = calcnbhd(sa_tk, sa_mu, sa_tz, g, cone) + @show sqrt(nbhd)/sa_mu - if nbhd < abs2(alf.beta*sa_mu) + if nbhd < abs2(beta*sa_mu) # iterate is inside the beta-neighborhood if !alphaprevok || (alpha > alf.predlsmulti) # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 @@ -367,21 +770,12 @@ function solve_basic!(alf::AlfonsoOpt) alpha = alpha/alf.predlsmulti # increase alpha continue end - - # # iterate is outside the beta-neighborhood - # if alphaprevok # TODO technically this should be only if nprediters > 1, but it seems to work - # # previous iterate was inside the beta-neighborhood - # if alf.predlinesearch - # alphapred = alpha*alf.predlsmulti - # end - # break - # end end # primal iterate is either # - outside the cone or # - inside the cone and outside the beta-neighborhood and previous iterate was outside the beta-neighborhood - if alpha < alf.alphapredthres + if alpha < alphapredthres # alpha is very small, so predictor has failed predfail = true alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") @@ -398,15 +792,20 @@ function solve_basic!(alf::AlfonsoOpt) end # step distance alpha in the direction + tx .+= alpha .* dir_tx ty .+= alpha .* dir_ty - tx .= sa_tx + tz .+= alpha .* dir_tz + ts .= sa_ts tau += alpha*dir_tau - ts .+= alpha .* dir_ts kap += alpha*dir_kap - mu = (dot(tx, ts) + tau*kap)/bnu + mu = (dot(ts, tz) + tau*kap)/bnu + # @show mu + # @show tau, kap + # @show tx + # @show sqrt(nbhd)/mu # skip correction phase if allowed and current iterate is in the eta-neighborhood - if alf.corrcheck && (nbhd < abs2(alf.eta*mu)) + if alf.corrcheck && (nbhd < abs2(eta*mu)) continue end @@ -417,19 +816,48 @@ function solve_basic!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - # x rhs is (ts + mu*g), y rhs is 0 - calcg!(g, cone) - dir_tx .= ts .+ mu .* g # temp for x rhs - dir_ty .= 0.0 # temp for y rhs - (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, dir_ty, A, b, c, cone, HiAt, AHiAt) - - dir_tau = (mu/tau - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) - dir_ty .= y2 .+ dir_tau .* y1 - dir_tx .= x2 .+ dir_tau .* x1 - # dir_ts = -A'*dir_ty + c*dir_tau - mul!(dir_ts, A', dir_ty) - dir_ts .= dir_tau .* c .- dir_ts - dir_kap = dot(b, dir_ty) - dot(c, dir_tx) + # (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) = finddirection(alf, zeros(n), zeros(p), -mu*calcg!(g, cone), mu/tau, tz, kap*tau, kap, tau) + + # Hi = zeros(q, q) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + # + # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) -Hi/mu h; -c' -b' -h' -tau^2/mu] + # Hipsis = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies + # Hipsik = tau^2/mu*(kap - mu/tau) + # dir = lhsnew\[zeros(n); zeros(p); Hipsis; Hipsik] + # + # dir_tx = dir[1:n] + # dir_ty = dir[n+1:n+p] + # dir_tz = dir[n+p+1:n+p+q] + # dir_tau = dir[n+p+q+1] + # + # dir_ts = Hipsis + Hi/mu*dir_tz + # # dir_ts = -G*dir_tx + h*dir_tau + # dir_kap = Hipsik + tau^2/mu*dir_tau + # # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) + + # Hi = zeros(q, q) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + H = Diagonal(inv.(abs2.(sa_ts))) + + # tx ty tz kap ts tau + lhsbig = [ + zeros(n, n) A' G' zeros(n) zeros(n, q) c; + -A zeros(p, p) zeros(p, q) zeros(p) zeros(p, q) b; + zeros(q, n) zeros(q, p) Matrix(1.0I, q, q) zeros(q) mu*H zeros(q); + zeros(1, n) zeros(1, p) zeros(1, q) 1.0 zeros(1, q) mu/tau^2 + -G zeros(q, p) zeros(q, q) zeros(q) Matrix(-1.0I, q, q) h; + -c' -b' -h' -1.0 zeros(1, q) 0.0; + ] + + rhsbig = [zeros(n); zeros(p); -(tz + mu*calcg!(g, cone)); -(kap - mu/tau); zeros(q); 0.0] + dir = lhsbig\rhsbig + dir_tx = dir[1:n] + dir_ty = dir[n+1:n+p] + dir_tz = dir[n+p+1:n+p+q] + dir_kap = dir[n+p+q+1] + dir_ts = dir[n+p+q+2:n+p+2*q+1] + dir_tau = dir[n+p+2*q+2] # determine step length alpha by line search alpha = alf.alphacorr @@ -437,8 +865,7 @@ function solve_basic!(alf::AlfonsoOpt) while ncorrlsiters <= alf.maxcorrlsiters ncorrlsiters += 1 - sa_tx .= tx .+ alpha .* dir_tx - + sa_ts .= ts .+ alpha .* dir_ts if incone(cone) # primal iterate tx is inside the cone, so terminate line search break @@ -461,17 +888,19 @@ function solve_basic!(alf::AlfonsoOpt) end # step distance alpha in the direction + tx .+= alpha .* dir_tx ty .+= alpha .* dir_ty - tx .= sa_tx + tz .+= alpha .* dir_tz + ts .= sa_ts tau += alpha*dir_tau - ts .+= alpha .* dir_ts kap += alpha*dir_kap - mu = (dot(tx, ts) + tau*kap)/bnu + mu = (dot(ts, tz) + tau*kap)/bnu # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck - sa_ts .= ts - if calcnbhd(tau*kap, mu, sa_ts, g, cone) <= abs2(alf.eta*mu) + sa_tz .= tz + nbhd = calcnbhd(tau*kap, mu, sa_tz, g, cone) + if nbhd <= abs2(eta*mu) break elseif ncorrsteps == alf.maxcorrsteps # nbhd_eta > eta, so corrector failed @@ -493,439 +922,132 @@ function solve_basic!(alf::AlfonsoOpt) # calculate final solution and iteration statistics alf.niters = iter - tx ./= tau - alf.x = tx - ty ./= tau - alf.y = ty - alf.tau = tau - ts ./= tau - alf.s = ts - alf.kap = kap - - alf.pobj = dot(c, alf.x) - alf.dobj = dot(b, alf.y) - alf.dgap = alf.pobj - alf.dobj - alf.cgap = dot(alf.s, alf.x) - alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) - alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) - - # alf.pres = b - A*alf.x - mul!(p_res, A, alf.x) - p_res .= b .- p_res - alf.pres = p_res - # alf.dres = c - A'*alf.y - alf.s - mul!(d_res, A', alf.y) - d_res .= c .- d_res .- alf.s - alf.dres = d_res - - alf.pin = norm(alf.pres) - alf.din = norm(alf.dres) - alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) - alf.rel_din = alf.din/(1.0 + norm(c, Inf)) + # tx ./= tau + # alf.x = tx + # ty ./= tau + # alf.y = ty + # alf.tau = tau + # ts ./= tau + # alf.s = ts + # alf.kap = kap + # + # alf.pobj = dot(c, alf.x) + # alf.dobj = dot(b, alf.y) + # alf.dgap = alf.pobj - alf.dobj + # alf.cgap = dot(alf.s, alf.x) + # alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) + # alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) + # + # # alf.pres = b - A*alf.x + # mul!(p_res, A, alf.x) + # p_res .= b .- p_res + # alf.pres = p_res + # # alf.dres = c - A'*alf.y - alf.s + # mul!(d_res, A', alf.y) + # d_res .= c .- d_res .- alf.s + # alf.dres = d_res + # + # alf.pin = norm(alf.pres) + # alf.din = norm(alf.dres) + # alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) + # alf.rel_din = alf.din/(1.0 + norm(c, Inf)) alf.solvetime = time() - starttime return nothing end +function calcnbhd(tk, mu, sa_tz, g, cone) + calcg!(g, cone) + sa_tz .+= mu .* g + calcHiarr!(g, sa_tz, cone) + return (tk - mu)^2 + dot(sa_tz, g) +end +function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_tau, rhs_ts, rhs_kap, kap, tau) + (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) + cone = alf.conK + (n, p, q) = (length(c), length(b), length(h)) + Hi = zeros(q, q) + calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + lhs = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi h; -c' -b' -h' kap/tau] -# # solve using HSDE -# function solve_hsde!(alf::AlfonsoOpt) -# starttime = time() -# -# (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) -# (varK, conK) = (alf.varK, alf.conK) -# (n, p, q) = (length(c), length(b), length(h)) -# bnu = 1.0 + barrierpar(varK) + barrierpar(conK) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) -# -# # preallocate arrays -# tx = similar(c) -# ts = similar(h) -# ty = similar(b) -# tz = similar(ts) -# sa_ts = similar(ts) -# dir_ts = similar(ts) -# -# loadpnt!(cone, sa_ts) -# -# # calculate initial central primal-dual iterate (S5.3 of V.) -# # solve linear equation then step in interior direction of cone until inside cone -# alf.verbose && println("finding initial iterate") -# -# # TODO use linsys solve function -# xyz = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] -# tx .= xyz[1:n] -# ty .= xyz[n+1:n+p] -# sa_ts .= -xyz[n+p+1:n+p+q] -# ts .= sa_ts -# -# if !incone(cone) -# println("not in the cone") -# getintdir!(dir_ts, cone) -# alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small -# steps = 1 -# while !incone(cone) -# sa_ts .= ts .+ alpha .* dir_ts -# alpha *= 1.2 -# steps += 1 -# if steps > 100 -# error("cannot find initial iterate") -# end -# end -# @show alpha -# @show steps -# ts .= sa_ts -# end -# -# @assert incone(cone) # TODO delete -# calcg!(tz, cone) -# tz .*= -1.0 -# -# tau = 1.0 -# kap = 1.0 -# mu = (dot(tz, ts) + tau*kap)/bnu -# -# # TODO delete later -# @assert abs(1.0 - mu) < 1e-8 -# @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 -# -# alf.verbose && println("found initial iterate") -# -# -# # calculate prediction and correction step parameters -# # TODO put in prediction and correction step cache functions -# (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter -# alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size -# alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size -# alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size -# -# -# # (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) -# # tol_pres = inv(1.0 + norm(b)) -# # tol_dres = inv(1.0 + norm(c)) -# -# # main loop -# if alf.verbose -# println("Starting iteration") -# @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "gap", "p_inf", "d_inf", "tau", "kap", "mu") -# flush(stdout) -# end -# -# alf.status = :StartedIterating -# alphapred = alphapredinit -# iter = 0 -# while true -# # calculate residuals -# # TODO in-place -# res_tx = -A'*ty - G'*tz - c*tau -# res_ty = A*tx - b*tau -# res_tz = ts + G*x - h*tau -# res_tau = kap + dot(c, x) + dot(b, y) + dot(h, z) -# -# # TODO calculate convergence metrics -# # ctx = dot(c, tx) -# # bty = dot(b, ty) -# # p_obj = ctx/tau -# # d_obj = bty/tau -# # rel_gap = abs(ctx - bty)/(tau + abs(bty)) -# # p_inf = maximum(abs, p_res)/alf.tol_pres -# # # d_res = A'*ty - c*tau + ts -# # mul!(d_res, A', ty) -# # d_res .+= ts .- tau .* c -# # d_inf = maximum(abs, d_res)/alf.tol_dres -# # abs_gap = -bty + ctx + kap -# # compl = abs(abs_gap)/alf.tol_compl -# # -# # if alf.verbose -# # # print iteration statistics -# # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) -# # flush(stdout) -# # end -# # -# # # check convergence criteria -# # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) -# # if rel_gap <= alf.optimtol -# # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") -# # alf.status = :Optimal -# # break -# # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) -# # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") -# # alf.status = :NearlyInfeasible -# # break -# # end -# # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) -# # alf.verbose && println("Problem is ill-posed; terminating") -# # alf.status = :IllPosed -# # break -# # end -# -# # check iteration limit -# iter += 1 -# if iter >= alf.maxiter -# alf.verbose && println("Reached iteration limit; terminating") -# alf.status = :IterationLimit -# break -# end -# -# # prediction phase -# # calculate prediction direction -# -# -# -# -# -# -# # x rhs is (ts - d_res), y rhs is p_res -# dir_tx .= ts .- d_res # temp for x rhs -# (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, p_res, A, b, c, cone, HiAt, AHiAt) -# -# dir_tau = (abs_gap - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) -# dir_ty .= y2 .+ dir_tau .* y1 -# dir_tx .= x2 .+ dir_tau .* x1 -# mul!(dir_ts, A', dir_ty) -# dir_ts .= dir_tau .* c .- dir_ts .- d_res -# dir_kap = -abs_gap + dot(b, dir_ty) - dot(c, dir_tx) -# -# # determine step length alpha by line search -# alpha = alphapred -# nbhd = Inf -# alphaprevok = true -# predfail = false -# nprediters = 0 -# while true -# nprediters += 1 -# -# sa_tx .= tx .+ alpha .* dir_tx -# -# # accept primal iterate if -# # - decreased alpha and it is the first inside the cone and beta-neighborhood or -# # - increased alpha and it is inside the cone and the first to leave beta-neighborhood -# if incone(cone) -# # primal iterate is inside the cone -# sa_ts .= ts .+ alpha .* dir_ts -# sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) -# sa_mu = (dot(sa_tx, sa_ts) + sa_tk)/bnu -# nbhd = calcnbhd(sa_tk, sa_mu, sa_ts, g, cone) -# -# if nbhd < abs2(alf.beta*sa_mu) -# # iterate is inside the beta-neighborhood -# if !alphaprevok || (alpha > alf.predlsmulti) -# # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 -# if alf.predlinesearch -# alphapred = alpha -# end -# break -# end -# -# alphaprevok = true -# alpha = alpha/alf.predlsmulti # increase alpha -# continue -# end -# -# # # iterate is outside the beta-neighborhood -# # if alphaprevok # TODO technically this should be only if nprediters > 1, but it seems to work -# # # previous iterate was inside the beta-neighborhood -# # if alf.predlinesearch -# # alphapred = alpha*alf.predlsmulti -# # end -# # break -# # end -# end -# -# # primal iterate is either -# # - outside the cone or -# # - inside the cone and outside the beta-neighborhood and previous iterate was outside the beta-neighborhood -# if alpha < alf.alphapredthres -# # alpha is very small, so predictor has failed -# predfail = true -# alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") -# alf.status = :PredictorFail -# break -# end -# -# alphaprevok = false -# alpha = alf.predlsmulti*alpha # decrease alpha -# end -# # @show nprediters -# if predfail -# break -# end -# -# # step distance alpha in the direction -# ty .+= alpha .* dir_ty -# tx .= sa_tx -# tau += alpha*dir_tau -# ts .+= alpha .* dir_ts -# kap += alpha*dir_kap -# mu = (dot(tx, ts) + tau*kap)/bnu + xyzt = lhs\[rhs_tx; rhs_ty; rhs_tz - rhs_ts; rhs_tau - rhs_kap/tau] + + dir_tx = xyzt[1:n] + dir_ty = xyzt[n+1:n+p] + dir_tz = xyzt[n+p+1:n+p+q] + dir_tau = xyzt[n+p+q+1] + dir_ts = -G*dir_tx + h*dir_tau - rhs_tz + dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau + + return (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) +end + + +# function solvesystem(alf, b_tx, b_ty, b_tz) +# # TODO s10.3 of V. # -# # skip correction phase if allowed and current iterate is in the eta-neighborhood -# if alf.corrcheck && (nbhd < abs2(alf.eta*mu)) -# continue -# end # -# # correction phase -# corrfail = false -# ncorrsteps = 0 -# while true -# ncorrsteps += 1 +# sol_ty = AGW2 \ AHi*(b_tx + ) +# return +# end # -# # calculate correction direction -# # x rhs is (ts + mu*g), y rhs is 0 -# calcg!(g, cone) -# dir_tx .= ts .+ mu .* g # temp for x rhs -# dir_ty .= 0.0 # temp for y rhs -# (y1, x1, y2, x2) = solvelinsys(y1, x1, y2, x2, mu, dir_tx, dir_ty, A, b, c, cone, HiAt, AHiAt) -# -# dir_tau = (mu/tau - kap - dot(b, y2) + dot(c, x2))/(mu/tau^2 + dot(b, y1) - dot(c, x1)) -# dir_ty .= y2 .+ dir_tau .* y1 -# dir_tx .= x2 .+ dir_tau .* x1 -# # dir_ts = -A'*dir_ty + c*dir_tau -# mul!(dir_ts, A', dir_ty) -# dir_ts .= dir_tau .* c .- dir_ts -# dir_kap = dot(b, dir_ty) - dot(c, dir_tx) +# function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_tau, rhs_ts, rhs_kap, kap, tau) # -# # determine step length alpha by line search -# alpha = alf.alphacorr -# ncorrlsiters = 0 -# while ncorrlsiters <= alf.maxcorrlsiters -# ncorrlsiters += 1 # -# sa_tx .= tx .+ alpha .* dir_tx +# invmu = inv(mu) # -# if incone(cone) -# # primal iterate tx is inside the cone, so terminate line search -# break -# end +# # TODO could ultimately be faster or more stable to do cholesky.L ldiv everywhere than to do full hessian ldiv +# calcHiarr!(HiAt, A', cone) +# HiAt .*= invmu +# mul!(AHiAt, A, HiAt) +# F = cholesky!(Symmetric(AHiAt)) # -# # primal iterate tx is outside the cone -# if ncorrlsiters == alf.maxcorrlsiters -# # corrector failed -# corrfail = true -# alf.verbose && println("Corrector could not improve the solution ($ncorrlsiters line search steps); terminating") -# alf.status = :CorrectorFail -# break -# end +# # TODO can parallelize 1 and 2 +# # y2 = F\(rhs_ty + HiAt'*rhs_tx) +# mul!(y2, HiAt', rhs_tx) +# y2 .+= rhs_ty +# ldiv!(F, y2) # y2 done # -# alpha = alf.corrlsmulti*alpha # decrease alpha -# end -# # @show ncorrlsiters -# if corrfail -# break -# end +# # x2 = Hi*invmu*(A'*y2 - rhs_tx) +# mul!(x2, A', y2) +# rhs_tx .= x2 .- rhs_tx # destroys rhs_tx +# rhs_tx .*= invmu +# calcHiarr!(x2, rhs_tx, cone) # x2 done # -# # step distance alpha in the direction -# ty .+= alpha .* dir_ty -# tx .= sa_tx -# tau += alpha*dir_tau -# ts .+= alpha .* dir_ts -# kap += alpha*dir_kap -# mu = (dot(tx, ts) + tau*kap)/bnu +# # y1 = F\(b + HiAt'*c) +# mul!(y1, HiAt', c) +# y1 .+= b +# ldiv!(F, y1) # y1 done # -# # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps -# if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck -# sa_ts .= ts -# if calcnbhd(tau*kap, mu, sa_ts, g, cone) <= abs2(alf.eta*mu) -# break -# elseif ncorrsteps == alf.maxcorrsteps -# # nbhd_eta > eta, so corrector failed -# corrfail = true -# alf.verbose && println("Corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") -# alf.status = :CorrectorFail -# break -# end -# end -# end -# # @show ncorrsteps -# if corrfail -# break -# end -# end +# # x1 = Hi*invmu*(A'*y1 - c) +# mul!(rhs_tx, A', y1) +# rhs_tx .-= c +# rhs_tx .*= invmu +# calcHiarr!(x1, rhs_tx, cone) # x1 done # -# alf.verbose && println("\nFinished in $iter iterations\nInternal status is $(alf.status)\n") # -# # calculate final solution and iteration statistics -# alf.niters = iter +# x1 +# y1 +# z1 # -# tx ./= tau -# alf.x = tx -# ty ./= tau -# alf.y = ty -# alf.tau = tau -# ts ./= tau -# alf.s = ts -# alf.kap = kap -# -# alf.pobj = dot(c, alf.x) -# alf.dobj = dot(b, alf.y) -# alf.dgap = alf.pobj - alf.dobj -# alf.cgap = dot(alf.s, alf.x) -# alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) -# alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) -# -# # alf.pres = b - A*alf.x -# mul!(p_res, A, alf.x) -# p_res .= b .- p_res -# alf.pres = p_res -# # alf.dres = c - A'*alf.y - alf.s -# mul!(d_res, A', alf.y) -# d_res .= c .- d_res .- alf.s -# alf.dres = d_res -# -# alf.pin = norm(alf.pres) -# alf.din = norm(alf.dres) -# alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) -# alf.rel_din = alf.din/(1.0 + norm(c, Inf)) +# x2 +# y2 +# z2 # -# alf.solvetime = time() - starttime +# dir_tau = (rhs_tau - rhs_kap/tau + dot(c, x2) + dot(b, y2) + dot(h, z2))/(kap/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) +# dir_tx = x2 + dir_tau * x1 +# dir_ty = y2 + dir_tau * y1 +# dir_tz = z2 + dir_tau * z1 +# dir_ts = -G*dir_tx + h*dir_tau - rhs_tz +# dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau # -# return nothing +# return (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) # end -function calcnbhd(tk, mu, sa_ts, g, cone) - calcg!(g, cone) - sa_ts .+= mu .* g - calcHiarr!(g, sa_ts, cone) - return (tk - mu)^2 + dot(sa_ts, g) -end - -function solvelinsys(y1, x1, y2, x2, mu, rhs_tx, rhs_ty, A, b, c, cone, HiAt, AHiAt) - invmu = inv(mu) - - # TODO could ultimately be faster or more stable to do cholesky.L ldiv everywhere than to do full hessian ldiv - calcHiarr!(HiAt, A', cone) - HiAt .*= invmu - mul!(AHiAt, A, HiAt) - F = cholesky!(Symmetric(AHiAt)) - - # TODO can parallelize 1 and 2 - # y2 = F\(rhs_ty + HiAt'*rhs_tx) - mul!(y2, HiAt', rhs_tx) - y2 .+= rhs_ty - ldiv!(F, y2) # y2 done - - # x2 = Hi*invmu*(A'*y2 - rhs_tx) - mul!(x2, A', y2) - rhs_tx .= x2 .- rhs_tx # destroys rhs_tx - rhs_tx .*= invmu - calcHiarr!(x2, rhs_tx, cone) # x2 done - - # y1 = F\(b + HiAt'*c) - mul!(y1, HiAt', c) - y1 .+= b - ldiv!(F, y1) # y1 done - - # x1 = Hi*invmu*(A'*y1 - c) - mul!(rhs_tx, A', y1) - rhs_tx .-= c - rhs_tx .*= invmu - calcHiarr!(x1, rhs_tx, cone) # x1 done - - return (y1, x1, y2, x2) -end function getbetaeta(maxcorrsteps, bnu) if maxcorrsteps <= 2 diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 6d814428a..a6b0aec55 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,291 +1,325 @@ -@testset "large dense lp example (dense A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, use_data=true) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -end - -@testset "large sparse lp example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "small dense lp example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(d_alf, 50, 100, dense=true, tosparse=false) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(s_alf, 50, 100, dense=true, tosparse=true) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "1D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -end - -@testset "2D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 4, 2, 7, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 4, 2, 7, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "3D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(alf, 2, 3, 3, 5, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "4D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(alf, 2, 3, 4, 4, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -# most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -@testset "Butcher" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :butcher, 2) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -end - -@testset "Caprasse" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :caprasse, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -end - -@testset "Goldstein-Price" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :goldsteinprice, 7) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -end - -# out of memory during interpolation calculations -# @testset "Heart" begin +# @testset "large dense lp example (dense A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :heart, 2) +# build_lp!(alf, 500, 1000, use_data=true) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 # end - -@testset "Lotka-Volterra" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :lotkavolterra, 3) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -end - -# out of memory during interpolation calculations -# @testset "Magnetism-7" begin +# +# @testset "large sparse lp example (sparse A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :magnetism7, 2) +# build_lp!(alf, 500, 1000, dense=false) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small dense lp example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "1D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# end +# +# @testset "2D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "3D poly envelope example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(alf, 2, 3, 3, 5, dense=false) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "4D poly envelope example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(alf, 2, 3, 4, 4, dense=false) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end +# +# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html +# @testset "Butcher" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :butcher, 2) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Caprasse" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :caprasse, 4) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Goldstein-Price" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :goldsteinprice, 7) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# end +# +# # out of memory during interpolation calculations +# # @testset "Heart" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :heart, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Lotka-Volterra" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :lotkavolterra, 3) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# end +# +# # out of memory during interpolation calculations +# # @testset "Magnetism-7" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :magnetism7, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Motzkin" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) +# build_namedpoly!(alf, :motzkin, 7) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Reaction-diffusion" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :reactiondiffusion, 4) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Robinson" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) +# build_namedpoly!(alf, :robinson, 8) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# end +# +# # tolerances not satisfied +# @testset "Rosenbrock" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=20) +# build_namedpoly!(alf, :rosenbrock, 3) +# @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +# end +# +# # tolerances not satisfied +# @testset "Schwefel" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=25) +# build_namedpoly!(alf, :schwefel, 4) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +# end +# +# @testset "small second-order cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, -1, -1] +# A = Float64[1 0 0; 0 1 0] +# b = Float64[1, 1/sqrt(2)] +# cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_y(alf) ≈ [-sqrt(2), 0] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 +# end +# +# @testset "small exponential cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[1, 1, 1] +# A = Float64[0 1 0; 1 0 0] +# b = Float64[2, 1] +# cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test dot(Alfonso.get_y(alf), b) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_y(alf) ≈ [1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_s(alf) ≈ (c - A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small power cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[1, 0, 0, -1, -1, 0] +# A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] +# b = Float64[2, 1] +# cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 +# end +# +# @testset "small rotated second-order cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, 0, -1, -1] +# A = Float64[1 0 0 0; 0 1 0 0] +# b = Float64[1/2, 1] +# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small rotated second-order cone problem 2" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, 0, -1] +# A = Float64[1 0 0; 0 1 0] +# b = Float64[1/2, 1]/sqrt(2) +# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small positive semidefinite cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, -1, 0] +# A = Float64[1 0 0; 0 0 1] +# b = Float64[1/2, 1] +# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 +# end +# +# @testset "small positive semidefinite cone problem 2" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[1, 0, 1, 0, 0, 1] +# A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] +# b = Float64[10, 3] +# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 # end -@testset "Motzkin" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) - build_namedpoly!(alf, :motzkin, 7) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -end - -@testset "Reaction-diffusion" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :reactiondiffusion, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -end - -@testset "Robinson" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) - build_namedpoly!(alf, :robinson, 8) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -end -# tolerances not satisfied -@testset "Rosenbrock" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=20) - build_namedpoly!(alf, :rosenbrock, 3) +@testset "small LP easy" begin + (n, p, q) = (10, 8, 9) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = Matrix{Float64}(I, q, n) # TODO try I + h = G*ones(n) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 - @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 -end - -# tolerances not satisfied -@testset "Schwefel" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=25) - build_namedpoly!(alf, :schwefel, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 - @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 + # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 end -@testset "small second-order cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, 1/sqrt(2)] - cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ [-sqrt(2), 0] atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 -end -@testset "small exponential cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 1, 1] - A = Float64[0 1 0; 1 0 0] - b = Float64[2, 1] - cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) - Alfonso.load_data!(alf, A, b, c, cone) +@testset "small LP" begin + (n, p, q) = (5, 2, 5) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = Matrix{Float64}(I, q, n) # TODO try I + h = G*ones(n) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test dot(Alfonso.get_y(alf), b) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ [1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_s(alf) ≈ (c - A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 -end - -@testset "small power cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 0, 0, -1, -1, 0] - A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] - b = Float64[2, 1] - cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 -end - -@testset "small rotated second-order cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1, -1] - A = Float64[1 0 0 0; 0 1 0 0] - b = Float64[1/2, 1] - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 -end - -@testset "small rotated second-order cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1/2, 1]/sqrt(2) - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 -end - -@testset "small positive semidefinite cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, 0] - A = Float64[1 0 0; 0 0 1] - b = Float64[1/2, 1] - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 -end - -@testset "small positive semidefinite cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 0, 1, 0, 0, 1] - A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] - b = Float64[10, 3] - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) - Alfonso.load_data!(alf, A, b, c, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 + # @test Alfonso.get_status(alf) == :Optimal + # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 end From 34756ac19abbfd1fb6e3ef18dbc78c4c850a9968 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 21 Sep 2018 18:36:26 -0400 Subject: [PATCH 04/30] use CVXOPT convergence criteria, working for some small LPs --- src/nativeinterface.jl | 575 +++++++++-------------------------------- test/nativeexamples.jl | 25 +- 2 files changed, 146 insertions(+), 454 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index f4ff23d96..9b8139063 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -1,8 +1,27 @@ - +""" +solves a pair of primal and dual cone programs + minimize c'*x + subject to G*x + s = h + A*x = b + s in K + maximize -h'*z - b'*y + subject to G'*z + A'*y + c = 0 + z in K* +K is a convex cone defined as a Cartesian product of recognized primitive cones, and K* is its dual cone + +the primal-dual optimality conditions are + G*x + s = h, A*x = b + G'*z + A'*y + c = 0 + s in K, z in K* + s'*z = 0 + +""" mutable struct AlfonsoOpt # options verbose::Bool # if true, prints progress at each iteration - optimtol::Float64 # optimization tolerance parameter + tolrelopt::Float64 # relative optimality gap tolerance + tolabsopt::Float64 # absolute optimality gap tolerance + tolfeas::Float64 # feasibility tolerance maxiter::Int # maximum number of iterations predlinesearch::Bool # if false, predictor step uses a fixed step size, else step size is determined via line search maxpredsmallsteps::Int # maximum number of predictor step size reductions allowed with respect to the safe fixed step size @@ -15,7 +34,6 @@ mutable struct AlfonsoOpt # problem data c::Vector{Float64} # linear cost vector, size n - o::Float64 # objective offset scalar A::AbstractMatrix{Float64} # equality constraint matrix, size p*n b::Vector{Float64} # equality constraint vector, size p # varK::Cone # primal variable cone object @@ -46,11 +64,13 @@ mutable struct AlfonsoOpt rel_din::Float64 # final relative dual infeasibility # TODO match natural order of options listed above - function AlfonsoOpt(verbose, optimtol, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) + function AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) alf = new() alf.verbose = verbose - alf.optimtol = optimtol + alf.tolrelopt = tolrelopt + alf.tolabsopt = tolabsopt + alf.tolfeas = tolfeas alf.maxiter = maxiter alf.predlinesearch = predlinesearch alf.maxpredsmallsteps = maxpredsmallsteps @@ -69,11 +89,13 @@ end function AlfonsoOpt(; verbose = false, - optimtol = 1e-6, - maxiter = 1e3, + tolrelopt = 1e-6, + tolabsopt = 1e-7, + tolfeas = 1e-7, + maxiter = 2e2, predlinesearch = true, maxpredsmallsteps = 8, - maxcorrsteps = 8, # NOTE doubled in .m code + maxcorrsteps = 8, corrcheck = true, maxcorrlsiters = 8, alphacorr = 1.0, @@ -81,8 +103,8 @@ function AlfonsoOpt(; corrlsmulti = 0.5, ) - if !(1e-10 <= optimtol <= 1e-2) - error("optimtol must be from 1e-10 to 1e-2") + if min(tolrelopt, tolabsopt, tolfeas) < 1e-10 || max(tolrelopt, tolabsopt, tolfeas) > 1e-2 + error("tolrelopt, tolabsopt, tolfeas must be between 1e-10 and 1e-2") end if maxiter < 1 error("maxiter must be at least 1") @@ -94,7 +116,7 @@ function AlfonsoOpt(; error("maxcorrsteps must be from 1 to 8") end - return AlfonsoOpt(verbose, optimtol, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) + return AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) end get_status(alf::AlfonsoOpt) = alf.status @@ -169,378 +191,7 @@ function load_data!( return alf end -# solve without HSDE -# function solve_basic!(alf::AlfonsoOpt) -# starttime = time() -# -# (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) -# cone = alf.conK -# # (varK, conK) = (alf.varK, alf.conK) -# (n, p, q) = (length(c), length(b), length(h)) -# bnu = barrierpar(cone) # + barrierpar(varK) # complexity parameter of the barrier (sum of the primitive cone barrier parameters) -# -# # preallocate arrays -# tx = similar(c) -# ts = similar(h) -# ty = similar(b) -# tz = similar(ts) -# sa_ts = similar(ts) -# sa_tz = similar(tz) -# dir_ts = similar(ts) -# g = similar(ts) -# -# loadpnt!(cone, sa_ts) -# -# # calculate initial central primal-dual iterate (S5.3 of V.) -# # solve linear equation then step in interior direction of cone until inside cone -# alf.verbose && println("finding initial iterate") -# -# # TODO use linsys solve function -# # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] -# lhs = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) -# xyz = lhs\[-c; b; h] -# tx .= xyz[1:n] -# ty .= xyz[n+1:n+p] -# sa_ts .= -xyz[n+p+1:n+p+q] -# ts .= sa_ts -# -# if !incone(cone) -# println("not in the cone") -# getintdir!(dir_ts, cone) -# alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small -# steps = 1 -# while !incone(cone) -# sa_ts .= ts .+ alpha .* dir_ts -# alpha *= 1.2 -# steps += 1 -# if steps > 100 -# error("cannot find initial iterate") -# end -# end -# @show alpha -# @show steps -# ts .= sa_ts -# end -# -# @assert incone(cone) # TODO delete -# calcg!(tz, cone) -# tz .*= -1.0 -# mu = dot(tz, ts)/bnu -# -# # TODO delete later -# @assert abs(1.0 - mu) < 1e-8 -# @assert calcnbhd(mu, copy(tz), copy(tz), cone) < 1e-6 -# -# alf.verbose && println("found initial iterate") -# -# -# # calculate prediction and correction step parameters -# # TODO put in prediction and correction step cache functions -# (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter -# alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size -# alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size -# alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size -# -# # # main loop -# # if alf.verbose -# # println("Starting iteration") -# # @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "gap", "p_inf", "d_inf", "tau", "kap", "mu") -# # flush(stdout) -# # end -# -# alf.status = :StartedIterating -# alphapred = alphapredinit -# iter = 0 -# while true -# # calculate residuals -# # TODO in-place -# res_tx = A'*ty + G'*tz + c -# res_ty = A*tx - b -# res_tz = ts + G*tx - h -# -# # TODO calculate convergence metrics -# # ctx = dot(c, tx) -# # bty = dot(b, ty) -# # p_obj = ctx/tau -# # d_obj = bty/tau -# # rel_gap = abs(ctx - bty)/(tau + abs(bty)) -# # p_inf = maximum(abs, p_res)/alf.tol_pres -# # # d_res = A'*ty - c*tau + ts -# # mul!(d_res, A', ty) -# # d_res .+= ts .- tau .* c -# # d_inf = maximum(abs, d_res)/alf.tol_dres -# # abs_gap = -bty + ctx + kap -# # compl = abs(abs_gap)/alf.tol_compl -# # -# # if alf.verbose -# # # print iteration statistics -# # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) -# # flush(stdout) -# # end -# # -# # # check convergence criteria -# # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) -# # if rel_gap <= alf.optimtol -# # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") -# # alf.status = :Optimal -# # break -# # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) -# # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") -# # alf.status = :NearlyInfeasible -# # break -# # end -# # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) -# # alf.verbose && println("Problem is ill-posed; terminating") -# # alf.status = :IllPosed -# # break -# # end -# -# # check iteration limit -# iter += 1 -# if iter >= alf.maxiter -# alf.verbose && println("Reached iteration limit; terminating") -# alf.status = :IterationLimit -# break -# end -# -# # prediction phase -# # calculate prediction direction -# Hi = zeros(q, q) -# calcHiarr!(Hi, Matrix(1.0I, q, q), cone) -# lhs.data[n+p+1:n+p+q,n+p+1:n+p+q] .= -Hi -# dir = lhs\[-res_tx; -res_ty; ts - res_tz] -# -# dir_tx = dir[1:n] -# dir_ty = dir[n+1:n+p] -# dir_tz = dir[n+p+1:n+p+q] -# # (dir_tx, dir_ty, dir_tz) = finddirection(alf, lhs, -res_tx, -res_ty, ts - res_tz) -# # dir_ts = -res_tz - G*dir_tx -# dir_ts = -ts - Hi*dir_tz -# # @show norm(dir_ts - (-ts - Hi*dir_tz)) -# -# # determine step length alpha by line search -# alpha = alphapred -# nbhd = Inf -# alphaprevok = true -# predfail = false -# nprediters = 0 -# while true -# nprediters += 1 -# -# sa_ts .= ts .+ alpha .* dir_ts -# @show alpha -# # accept primal iterate if -# # - decreased alpha and it is the first inside the cone and beta-neighborhood or -# # - increased alpha and it is inside the cone and the first to leave beta-neighborhood -# if incone(cone) -# # primal iterate is inside the cone -# sa_tz .= tz .+ alpha .* dir_tz -# sa_mu = dot(sa_ts, sa_tz)/bnu -# nbhd = calcnbhd(sa_mu, sa_tz, g, cone) -# @show sqrt(nbhd)/sa_mu, beta -# if nbhd < abs2(beta*sa_mu) -# # iterate is inside the beta-neighborhood -# if !alphaprevok || (alpha > alf.predlsmulti) -# # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 -# if alf.predlinesearch -# alphapred = alpha -# end -# break -# end -# -# alphaprevok = true -# alpha = alpha/alf.predlsmulti # increase alpha -# continue -# end -# end -# -# # primal iterate is either -# # - outside the cone or -# # - inside the cone and outside the beta-neighborhood and previous iterate was outside the beta-neighborhood -# if alpha < alphapredthres -# # alpha is very small, so predictor has failed -# predfail = true -# alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") -# alf.status = :PredictorFail -# break -# end -# -# alphaprevok = false -# alpha = alf.predlsmulti*alpha # decrease alpha -# end -# # @show nprediters -# if predfail -# break -# end -# -# # step distance alpha in the direction -# tx .+= alpha .* dir_tx -# ty .+= alpha .* dir_ty -# tz .+= alpha .* dir_tz -# ts .= sa_ts -# mu = dot(ts, tz)/bnu -# @show mu -# -# # skip correction phase if allowed and current iterate is in the eta-neighborhood -# if alf.corrcheck && (nbhd < abs2(eta*mu)) -# continue -# end -# -# # correction phase -# corrfail = false -# ncorrsteps = 0 -# while true -# ncorrsteps += 1 -# -# # calculate correction direction -# Hi = zeros(q, q) -# calcHiarr!(Hi, Matrix(1.0I, q, q), cone) -# -# lhsnew = [zeros(n, n) A' G'; -A zeros(p, p) zeros(p, q); -G zeros(q, p) -Hi/mu] -# -# Hipsi = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies -# dir = lhsnew\[zeros(n); zeros(p); Hipsi] -# -# dir_tx = dir[1:n] -# dir_ty = dir[n+1:n+p] -# dir_tz = dir[n+p+1:n+p+q] -# # (dir_tx, dir_ty, dir_tz) = finddirection(alf, lhs, zeros(n), zeros(p), tz + mu*calcg!(g, cone)) -# dir_ts = Hipsi + Hi/mu*dir_tz -# # dir_ts = -ts - Hi*dir_tz -# # dir_ts = -ts - mu*calcg!(g, cone) - Hi*dir_tz -# # @show norm(dir_ts - (-tz - mu*calcg!(g, cone) - Hi*dir_tz)) -# -# # determine step length alpha by line search -# alpha = alf.alphacorr -# ncorrlsiters = 0 -# while ncorrlsiters <= alf.maxcorrlsiters -# ncorrlsiters += 1 -# -# sa_ts .= ts .+ alpha .* dir_ts -# @show alpha -# if incone(cone) -# # primal iterate tx is inside the cone, so terminate line search -# break -# end -# -# # primal iterate tx is outside the cone -# if ncorrlsiters == alf.maxcorrlsiters -# # corrector failed -# corrfail = true -# alf.verbose && println("Corrector could not improve the solution ($ncorrlsiters line search steps); terminating") -# alf.status = :CorrectorFail -# break -# end -# -# alpha = alf.corrlsmulti*alpha # decrease alpha -# end -# # @show ncorrlsiters -# if corrfail -# break -# end -# -# # step distance alpha in the direction -# tx .+= alpha .* dir_tx -# ty .+= alpha .* dir_ty -# tz .+= alpha .* dir_tz -# ts .= sa_ts -# mu = dot(ts, tz)/bnu -# @show mu -# -# # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps -# if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck -# sa_tz .= tz -# nbhd = calcnbhd(mu, sa_tz, g, cone) -# @show sqrt(nbhd)/mu, eta -# if nbhd <= abs2(eta*mu) -# break -# elseif ncorrsteps == alf.maxcorrsteps -# # nbhd_eta > eta, so corrector failed -# corrfail = true -# alf.verbose && println("Corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") -# alf.status = :CorrectorFail -# break -# end -# end -# end -# # @show ncorrsteps -# if corrfail -# break -# end -# -# return -# end -# -# alf.verbose && println("\nFinished in $iter iterations\nInternal status is $(alf.status)\n") -# -# # calculate final solution and iteration statistics -# alf.niters = iter -# -# # tx ./= tau -# # alf.x = tx -# # ty ./= tau -# # alf.y = ty -# # alf.tau = tau -# # ts ./= tau -# # alf.s = ts -# # alf.kap = kap -# # -# # alf.pobj = dot(c, alf.x) -# # alf.dobj = dot(b, alf.y) -# # alf.dgap = alf.pobj - alf.dobj -# # alf.cgap = dot(alf.s, alf.x) -# # alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) -# # alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) -# # -# # # alf.pres = b - A*alf.x -# # mul!(p_res, A, alf.x) -# # p_res .= b .- p_res -# # alf.pres = p_res -# # # alf.dres = c - A'*alf.y - alf.s -# # mul!(d_res, A', alf.y) -# # d_res .= c .- d_res .- alf.s -# # alf.dres = d_res -# # -# # alf.pin = norm(alf.pres) -# # alf.din = norm(alf.dres) -# # alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) -# # alf.rel_din = alf.din/(1.0 + norm(c, Inf)) -# -# alf.solvetime = time() - starttime -# -# return nothing -# end -# -# -# # function finddirection(alf, lhs, rhs_tx, rhs_ty, rhs_tz) -# # (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) -# # cone = alf.conK -# # (n, p, q) = (length(c), length(b), length(h)) -# # -# # Hi = zeros(q, q) -# # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) -# # lhs.data[n+p+1:n+p+q,n+p+1:n+p+q] .= -Hi -# # dir = lhs\[rhs_tx; rhs_ty; rhs_tz] -# # -# # dir_tx = dir[1:n] -# # dir_ty = dir[n+1:n+p] -# # dir_tz = dir[n+p+1:n+p+q] -# # -# # return (dir_tx, dir_ty, dir_tz) -# # end -# -# function calcnbhd(mu, sa_tz, g, cone) -# sa_tz .+= mu .* calcg!(g, cone) -# calcHiarr!(g, sa_tz, cone) -# return dot(sa_tz, g) -# end - - - - - - -# solve using HSDE +# solve using homogeneous self-dual embedding function solve!(alf::AlfonsoOpt) starttime = time() @@ -568,14 +219,13 @@ function solve!(alf::AlfonsoOpt) # TODO use linsys solve function # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] - xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] + xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] # TODO this is currently different from what CVXOPT does tx .= xyz[1:n] ty .= xyz[n+1:n+p] sa_ts .= -xyz[n+p+1:n+p+q] ts .= sa_ts if !incone(cone) - println("not in the cone") getintdir!(dir_ts, cone) alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small steps = 1 @@ -604,8 +254,7 @@ function solve!(alf::AlfonsoOpt) @assert abs(1.0 - mu) < 1e-8 @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 - alf.verbose && println("found initial iterate") - + alf.verbose && println("initial iterate found") # calculate prediction and correction step parameters # TODO put in prediction and correction step cache functions @@ -615,14 +264,16 @@ function solve!(alf::AlfonsoOpt) alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size - # (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) - # tol_pres = inv(1.0 + norm(b)) - # tol_dres = inv(1.0 + norm(c)) + (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) + tol_res_tx = max(1.0, norm_c) + tol_res_ty = max(1.0, norm_b) + tol_res_tz = max(1.0, norm_h) + # main loop if alf.verbose - println("Starting iteration") - @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "gap", "p_inf", "d_inf", "tau", "kap", "mu") + println("starting iteration") + @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "rel_gap", "p_inf", "d_inf", "tau", "kap", "mu") flush(stdout) end @@ -632,63 +283,87 @@ function solve!(alf::AlfonsoOpt) while true # calculate residuals # TODO in-place - res_tx = -A'*ty - G'*tz - c*tau - res_ty = A*tx - b*tau - res_ts = ts + G*tx - h*tau - res_tau = kap + dot(c, tx) + dot(b, ty) + dot(h, tz) - - println(iter) - @show res_tx - @show res_ty - @show res_ts - @show res_tau - @show tau - @show kap - @show mu - @show dot(c, tx)/tau - @show -(dot(b, ty) + dot(h, tz))/tau - - # TODO calculate convergence metrics - # ctx = dot(c, tx) - # bty = dot(b, ty) - # p_obj = ctx/tau - # d_obj = bty/tau - # rel_gap = abs(ctx - bty)/(tau + abs(bty)) - # p_inf = maximum(abs, p_res)/alf.tol_pres - # # d_res = A'*ty - c*tau + ts - # mul!(d_res, A', ty) - # d_res .+= ts .- tau .* c - # d_inf = maximum(abs, d_res)/alf.tol_dres - # abs_gap = -bty + ctx + kap - # compl = abs(abs_gap)/alf.tol_compl - # - # if alf.verbose - # # print iteration statistics - # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, p_obj, d_obj, rel_gap, p_inf, d_inf, tau, kap, mu) - # flush(stdout) - # end - # - # # check convergence criteria - # if (p_inf <= alf.optimtol) && (d_inf <= alf.optimtol) - # if rel_gap <= alf.optimtol - # alf.verbose && println("Problem is feasible and an approximate optimal solution was found; terminating") - # alf.status = :Optimal - # break - # elseif (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) - # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") - # alf.status = :NearlyInfeasible - # break - # end - # elseif (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) + res_x = -A'*ty - G'*tz + nres_x = norm(res_x) + res_tx = res_x - c*tau + nres_tx = norm(res_tx)/tau + + res_y = A*tx + nres_y = norm(res_y) + res_ty = res_y - b*tau + nres_ty = norm(res_ty)/tau + + res_z = ts + G*tx + nres_z = norm(res_z) + res_tz = res_z - h*tau + nres_tz = norm(res_tz)/tau + + (cx, by, hz) = (dot(c, tx), dot(b, ty), dot(h, tz)) + + res_tau = kap + cx + by + hz + + obj_pr = cx/tau + obj_du = -(by + hz)/tau + + gap = dot(tz, ts) # TODO is this right? maybe should adapt original alfonso conditions + + # TODO maybe add small epsilon to denominators that are zero to avoid NaNs, and get rid of isnans further down + if obj_pr < 0.0 + relgap = gap/-obj_pr + elseif obj_pr > 0.0 + relgap = gap/obj_du + else + relgap = NaN + end + + nres_pr = max(nres_ty*tol_res_ty, nres_tz*tol_res_tz) + nres_du = nres_tx*tol_res_tx + + if hz + by < 0.0 + infres_pr = nres_x*tol_res_tx/(-hz - by) + else + infres_pr = NaN + end + if cx < 0.0 + infres_du = -max(nres_y*tol_res_ty, nres_z*tol_res_tz)/cx + else + infres_du = NaN + end + + if alf.verbose + # print iteration statistics + @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, obj_pr, obj_du, relgap, nres_pr, nres_du, tau, kap, mu) + flush(stdout) + end + + # check convergence criteria + # TODO update options + if nres_pr <= alf.tolfeas && nres_du <= alf.tolfeas && (gap <= alf.tolabsopt || (!isnan(relgap) && relgap <= alf.tolrelopt)) + alf.verbose && println("optimal solution found; terminating") + alf.status = :Optimal + break + elseif !isnan(infres_pr) && infres_pr <= alf.tolfeas + alf.verbose && println("primal infeasibility detected; terminating") + alf.status = :PrimalInfeasible + break + elseif !isnan(infres_du) && infres_du <= alf.tolfeas + alf.verbose && println("dual infeasibility detected; terminating") + alf.status = :DualInfeasible + break + end + # TODO nearly primal or dual infeasible or nearly optimal cases? + # TODO ill-posed case? + # if (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) + # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") + # alf.status = :NearlyInfeasible + # if (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) # alf.verbose && println("Problem is ill-posed; terminating") # alf.status = :IllPosed - # break - # end # check iteration limit iter += 1 if iter >= alf.maxiter - alf.verbose && println("Reached iteration limit; terminating") + alf.verbose && println("iteration limit reached; terminating") alf.status = :IterationLimit break end @@ -724,7 +399,7 @@ function solve!(alf::AlfonsoOpt) -c' -b' -h' -1.0 zeros(1, q) 0.0; ] - rhsbig = [res_tx; res_ty; -tz; -kap; res_ts; res_tau] + rhsbig = [res_tx; res_ty; -tz; -kap; res_tz; res_tau] dir = lhsbig\rhsbig dir_tx = dir[1:n] dir_ty = dir[n+1:n+p] @@ -744,7 +419,7 @@ function solve!(alf::AlfonsoOpt) nprediters += 1 sa_ts .= ts .+ alpha .* dir_ts - @show alpha + # @show alpha # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood @@ -754,7 +429,7 @@ function solve!(alf::AlfonsoOpt) sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) sa_mu = (dot(sa_ts, sa_tz) + sa_tk)/bnu nbhd = calcnbhd(sa_tk, sa_mu, sa_tz, g, cone) - @show sqrt(nbhd)/sa_mu + # @show sqrt(nbhd)/sa_mu if nbhd < abs2(beta*sa_mu) # iterate is inside the beta-neighborhood @@ -778,7 +453,7 @@ function solve!(alf::AlfonsoOpt) if alpha < alphapredthres # alpha is very small, so predictor has failed predfail = true - alf.verbose && println("Predictor could not improve the solution ($nprediters line search steps); terminating") + alf.verbose && println("predictor could not improve the solution ($nprediters line search steps); terminating") alf.status = :PredictorFail break end @@ -875,7 +550,7 @@ function solve!(alf::AlfonsoOpt) if ncorrlsiters == alf.maxcorrlsiters # corrector failed corrfail = true - alf.verbose && println("Corrector could not improve the solution ($ncorrlsiters line search steps); terminating") + alf.verbose && println("corrector could not improve the solution ($ncorrlsiters line search steps); terminating") alf.status = :CorrectorFail break end @@ -905,7 +580,7 @@ function solve!(alf::AlfonsoOpt) elseif ncorrsteps == alf.maxcorrsteps # nbhd_eta > eta, so corrector failed corrfail = true - alf.verbose && println("Corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") + alf.verbose && println("corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") alf.status = :CorrectorFail break end @@ -917,7 +592,7 @@ function solve!(alf::AlfonsoOpt) end end - alf.verbose && println("\nFinished in $iter iterations\nInternal status is $(alf.status)\n") + alf.verbose && println("\nfinished in $iter iterations; internal status is $(alf.status)\n") # calculate final solution and iteration statistics alf.niters = iter diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index a6b0aec55..a1e146fda 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -291,13 +291,30 @@ # end -@testset "small LP easy" begin +@testset "small LP 1" begin + (n, p, q) = (30, 12, 30) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = Matrix{Float64}(-1.0I, q, n) # TODO try I + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + # @test Alfonso.get_status(alf) == :Optimal + # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +end + + +@testset "small LP 2" begin (n, p, q) = (10, 8, 9) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(I, q, n) # TODO try I - h = G*ones(n) + G = Matrix{Float64}(-1.0I, q, n) # TODO try I + h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) Alfonso.load_data!(alf, c, A, b, G, h, cone) @@ -308,7 +325,7 @@ end -@testset "small LP" begin +@testset "small LP 3" begin (n, p, q) = (5, 2, 5) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) From 3c63dcea3c15ec25bc240295771c9a41b2db4645 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 02:12:28 -0400 Subject: [PATCH 05/30] update examples for format, uncomment some tests --- examples/envelope/envelope.jl | 13 ++-- examples/lp/lp.jl | 8 +- examples/namedpoly/namedpoly.jl | 5 +- src/nativeinterface.jl | 66 +++++++++------- test/nativeexamples.jl | 132 +++++++++++++++----------------- test/runtests.jl | 2 +- 6 files changed, 122 insertions(+), 104 deletions(-) diff --git a/examples/envelope/envelope.jl b/examples/envelope/envelope.jl index 9417e4fa7..ba6561e4b 100644 --- a/examples/envelope/envelope.jl +++ b/examples/envelope/envelope.jl @@ -21,15 +21,18 @@ function build_envelope!(alf::Alfonso.AlfonsoOpt, npoly::Int, deg::Int, n::Int, (L, U, pts, P0, P, w) = Alfonso.interpolate(n, d, calc_w=true) LWts = fill(binomial(n+d-1, n), n) wtVals = 1.0 .- pts.^2 - PWts = [Array((qr(Diagonal(sqrt.(wtVals[:,j]))*P[:,1:LWts[j]])).Q) for j in 1:n] + PWts = [Array((qr(Diagonal(sqrt.(wtVals[:, j])) * P[:, 1:LWts[j]])).Q) for j in 1:n] # set up problem data if dense - A = repeat(Array(1.0I, U, U), outer=(1, npoly)) # dense A + A = repeat(Array(1.0I, U, U), outer=(1, npoly)) + G = Matrix(-1.0I, npoly*U, npoly*U) # TODO uniformscaling? else - A = repeat(sparse(1.0I, U, U), outer=(1, npoly)) # sparse A + A = repeat(sparse(1.0I, U, U), outer=(1, npoly)) + G = SparseMatrixCSC(-1.0I, npoly*U, npoly*U) end b = w + h = zeros(npoly*U) if use_data # use provided data in data folder c = vec(readdlm(joinpath(@__DIR__, "data/c$(size(A,2)).txt"), ',', Float64)) @@ -37,12 +40,12 @@ function build_envelope!(alf::Alfonso.AlfonsoOpt, npoly::Int, deg::Int, n::Int, # generate random data Random.seed!(rseed) LDegs = binomial(n+deg, n) - c = vec(P0[:,1:LDegs]*rand(-9:9, LDegs, npoly)) + c = vec(P0[:, 1:LDegs]*rand(-9:9, LDegs, npoly)) end cone = Alfonso.Cone([Alfonso.SumOfSquaresCone(U, [P, PWts...]) for k in 1:npoly], [1+(k-1)*U:k*U for k in 1:npoly]) - return Alfonso.load_data!(alf, A, b, c, cone) + return Alfonso.load_data!(alf, c, A, b, G, h, cone) end # alf = Alfonso.AlfonsoOpt(maxiter=100, verbose=true) diff --git a/examples/lp/lp.jl b/examples/lp/lp.jl index 6948f4818..9e4b71075 100644 --- a/examples/lp/lp.jl +++ b/examples/lp/lp.jl @@ -32,9 +32,15 @@ function build_lp!(alf::Alfonso.AlfonsoOpt, m::Int, n::Int; use_data::Bool=false if tosparse && !issparse(A) A = sparse(A) end + if dense + G = Matrix(-1.0I, n, n) # TODO uniformscaling? + else + G = SparseMatrixCSC(-1.0I, n, n) + end + h = zeros(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(n)], [1:n]) - return Alfonso.load_data!(alf, A, b, c, cone) + return Alfonso.load_data!(alf, c, A, b, G, h, cone) end # alf = Alfonso.AlfonsoOpt(maxiter=100, verbose=true) diff --git a/examples/namedpoly/namedpoly.jl b/examples/namedpoly/namedpoly.jl index e23ea80b4..b5c28916b 100644 --- a/examples/namedpoly/namedpoly.jl +++ b/examples/namedpoly/namedpoly.jl @@ -68,9 +68,12 @@ function build_namedpoly!(alf::Alfonso.AlfonsoOpt, polyname::Symbol, d::Int) A = ones(1, U) b = [1.0,] c = [fn(pts[j,:]...) for j in 1:U] + G = Matrix(-1.0I, U, U) # TODO uniformscaling? + h = zeros(U) + cone = Alfonso.Cone([Alfonso.SumOfSquaresCone(U, [P0, PWts...])], [1:U]) - return Alfonso.load_data!(alf, A, b, c, cone) + return Alfonso.load_data!(alf, c, A, b, G, h, cone) end # alf = Alfonso.AlfonsoOpt(maxiter=100, verbose=false) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 9b8139063..ee3517c9d 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -45,13 +45,15 @@ mutable struct AlfonsoOpt status::Symbol # solver status solvetime::Float64 # total solve time niters::Int # total number of iterations - y::Vector{Float64} # final value of the dual free variables x::Vector{Float64} # final value of the primal variables + y::Vector{Float64} # final value of the dual free variables + z::Vector{Float64} # final value of the dual cone variables tau::Float64 # final value of the tau-variable - s::Vector{Float64} # final value of the dual slack variables + s::Vector{Float64} # final value of the primal cone variables kap::Float64 # final value of the kappa-variable pobj::Float64 # final primal objective value dobj::Float64 # final dual objective value + dgap::Float64 # final duality gap cgap::Float64 # final complementarity gap rel_dgap::Float64 # final relative duality gap @@ -89,10 +91,10 @@ end function AlfonsoOpt(; verbose = false, - tolrelopt = 1e-6, - tolabsopt = 1e-7, - tolfeas = 1e-7, - maxiter = 2e2, + tolrelopt = 1e-5, + tolabsopt = 1e-6, + tolfeas = 1e-6, + maxiter = 5e2, predlinesearch = true, maxpredsmallsteps = 8, maxcorrsteps = 8, @@ -169,11 +171,12 @@ function load_data!( if q != size(G, 1) error("number of constraint rows is not consistent in G and h") end - # TODO do appropriate decomps at the same time, do preprocessing - if rank(A) < p + # TODO do appropriate decomps at the same time, do preprocessing + # TODO rank currently missing method for sparse A, G + if !issparse(A) && rank(A) < p error("A matrix is not full-row-rank; some primal equalities may be redundant or inconsistent") end - if rank(vcat(A, G)) < n + if !issparse(A) && !issparse(G) && rank(vcat(A, G)) < n error("[A' G'] is not full-row-rank; some dual equalities may be redundant (i.e. primal variables can be removed) or inconsistent") end end @@ -215,7 +218,7 @@ function solve!(alf::AlfonsoOpt) # calculate initial central primal-dual iterate (S5.3 of V.) # solve linear equation then step in interior direction of cone until inside cone - alf.verbose && println("finding initial iterate") + alf.verbose && println("\nfinding initial iterate") # TODO use linsys solve function # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] @@ -231,9 +234,9 @@ function solve!(alf::AlfonsoOpt) steps = 1 while !incone(cone) sa_ts .= ts .+ alpha .* dir_ts - alpha *= 1.2 + alpha *= 1.5 steps += 1 - if steps > 100 + if steps > 25 error("cannot find initial iterate") end end @@ -387,7 +390,9 @@ function solve!(alf::AlfonsoOpt) # dir_kap = tau^2/mu*(-tau - dir_tau) sa_ts .= ts - H = Diagonal(inv.(abs2.(sa_ts))) + Hi = zeros(q, q) + calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + H = inv(Hi) # tx ty tz kap ts tau lhsbig = [ @@ -511,9 +516,9 @@ function solve!(alf::AlfonsoOpt) # dir_kap = Hipsik + tau^2/mu*dir_tau # # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - # Hi = zeros(q, q) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - H = Diagonal(inv.(abs2.(sa_ts))) + Hi = zeros(q, q) + calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + H = inv(Hi) # tx ty tz kap ts tau lhsbig = [ @@ -597,17 +602,24 @@ function solve!(alf::AlfonsoOpt) # calculate final solution and iteration statistics alf.niters = iter - # tx ./= tau - # alf.x = tx - # ty ./= tau - # alf.y = ty - # alf.tau = tau - # ts ./= tau - # alf.s = ts - # alf.kap = kap - # - # alf.pobj = dot(c, alf.x) - # alf.dobj = dot(b, alf.y) + + + tx ./= tau + alf.x = tx + ty ./= tau + alf.y = ty + tz ./= tau + alf.z = tz + alf.tau = tau + ts ./= tau + alf.s = ts + alf.kap = kap + + alf.pobj = dot(c, alf.x) + alf.dobj = -dot(b, alf.y) - dot(h, alf.z) # TODO already calculated; maybe should just calculate in getter functions though + + + # alf.dgap = alf.pobj - alf.dobj # alf.cgap = dot(alf.s, alf.x) # alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index a1e146fda..341c9c208 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -15,42 +15,42 @@ # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end -# -# @testset "small dense lp example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end -# -# @testset "1D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# end -# + +@testset "small dense lp example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end + +@testset "1D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +end + # @testset "2D poly envelope example (dense vs sparse A)" begin # # dense methods # d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -67,7 +67,7 @@ # @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 # @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 # end -# + # @testset "3D poly envelope example (sparse A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # build_envelope!(alf, 2, 3, 3, 5, dense=false) @@ -84,25 +84,25 @@ # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end # -# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -# @testset "Butcher" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :butcher, 2) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Caprasse" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :caprasse, 4) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -# end -# +# most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html +@testset "Butcher" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :butcher, 2) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +end + +@testset "Caprasse" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :caprasse, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +end + # @testset "Goldstein-Price" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # build_namedpoly!(alf, :goldsteinprice, 7) @@ -111,7 +111,7 @@ # @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 # end -# + # # out of memory during interpolation calculations # # @testset "Heart" begin # # alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -296,15 +296,13 @@ c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(-1.0I, q, n) # TODO try I + G = Matrix{Float64}(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - # @test Alfonso.get_status(alf) == :Optimal - # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_status(alf) == :Optimal end @@ -313,15 +311,13 @@ end c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(-1.0I, q, n) # TODO try I + G = Matrix{Float64}(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - # @test Alfonso.get_status(alf) == :Optimal - # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_status(alf) == :Optimal end @@ -330,13 +326,11 @@ end c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(I, q, n) # TODO try I + G = Matrix{Float64}(1.0I, q, n) h = G*ones(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - # @test Alfonso.get_status(alf) == :Optimal - # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - # @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_status(alf) == :DualInfeasible end diff --git a/test/runtests.jl b/test/runtests.jl index e18934a2c..2351e4a31 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Alfonso using Test -verbflag = false # Alfonso verbose option +verbflag = true # Alfonso verbose option # TODO interpolation tests From 6e5445cf936083c541d34e0c9c5d9433951e395e Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 14:27:42 -0400 Subject: [PATCH 06/30] fix conv tol bug, get more tests working --- Manifest.toml | 8 +- src/nativeinterface.jl | 234 +++++++++++------------------------------ test/nativeexamples.jl | 152 +++++++++++++------------- 3 files changed, 142 insertions(+), 252 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index 1ac395a21..3edb2addd 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -9,9 +9,9 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[BinaryProvider]] deps = ["Libdl", "Pkg", "SHA", "Test"] -git-tree-sha1 = "b530fbeb6f41ab5a83fbe3db1fcbe879334bcd2d" +git-tree-sha1 = "48c147e63431adbcee69bc40b04c3f0fec0a4982" uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.4.2" +version = "0.5.0" [[Combinatorics]] deps = ["LinearAlgebra", "Polynomials", "Test"] @@ -91,9 +91,9 @@ uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" [[Polynomials]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "af883752c4935425a3ab30031a2069254c451b8b" +git-tree-sha1 = "1a1eae52956658a6acae6fa1b6d6c3d488192895" uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -version = "0.5.0" +version = "0.5.1" [[Printf]] deps = ["Unicode"] diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index ee3517c9d..73046cedb 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -36,7 +36,6 @@ mutable struct AlfonsoOpt c::Vector{Float64} # linear cost vector, size n A::AbstractMatrix{Float64} # equality constraint matrix, size p*n b::Vector{Float64} # equality constraint vector, size p - # varK::Cone # primal variable cone object G::AbstractMatrix{Float64} # cone constraint matrix, size q*n h::Vector{Float64} # cone constraint vector, size q conK::Cone # primal constraint cone object @@ -45,12 +44,16 @@ mutable struct AlfonsoOpt status::Symbol # solver status solvetime::Float64 # total solve time niters::Int # total number of iterations + x::Vector{Float64} # final value of the primal variables + s::Vector{Float64} # final value of the primal cone variables y::Vector{Float64} # final value of the dual free variables z::Vector{Float64} # final value of the dual cone variables - tau::Float64 # final value of the tau-variable - s::Vector{Float64} # final value of the primal cone variables - kap::Float64 # final value of the kappa-variable + + tau::Float64 # final value of the tau variable + kap::Float64 # final value of the kappa variable + mu::Float64 # final value of mu + pobj::Float64 # final primal objective value dobj::Float64 # final dual objective value @@ -91,9 +94,9 @@ end function AlfonsoOpt(; verbose = false, - tolrelopt = 1e-5, - tolabsopt = 1e-6, - tolfeas = 1e-6, + tolrelopt = 1e-6, + tolabsopt = 1e-7, + tolfeas = 1e-7, maxiter = 5e2, predlinesearch = true, maxpredsmallsteps = 8, @@ -124,23 +127,19 @@ end get_status(alf::AlfonsoOpt) = alf.status get_solvetime(alf::AlfonsoOpt) = alf.solvetime get_niters(alf::AlfonsoOpt) = alf.niters -get_y(alf::AlfonsoOpt) = copy(alf.y) + get_x(alf::AlfonsoOpt) = copy(alf.x) -get_tau(alf::AlfonsoOpt) = alf.tau get_s(alf::AlfonsoOpt) = copy(alf.s) +get_y(alf::AlfonsoOpt) = copy(alf.y) +get_z(alf::AlfonsoOpt) = copy(alf.z) + +get_tau(alf::AlfonsoOpt) = alf.tau get_kappa(alf::AlfonsoOpt) = alf.kappa -get_pobj(alf::AlfonsoOpt) = alf.pobj -get_dobj(alf::AlfonsoOpt) = alf.dobj -get_dgap(alf::AlfonsoOpt) = alf.dgap -get_cgap(alf::AlfonsoOpt) = alf.cgap -get_rel_dgap(alf::AlfonsoOpt) = alf.rel_dgap -get_rel_cgap(alf::AlfonsoOpt) = alf.rel_cgap -get_pres(alf::AlfonsoOpt) = copy(alf.pres) -get_dres(alf::AlfonsoOpt) = copy(alf.dres) -get_pin(alf::AlfonsoOpt) = alf.pin -get_din(alf::AlfonsoOpt) = alf.din -get_rel_pin(alf::AlfonsoOpt) = alf.rel_pin -get_rel_din(alf::AlfonsoOpt) = alf.rel_din +get_mu(alf::AlfonsoOpt) = alf.mu + +get_pobj(alf::AlfonsoOpt) = dot(alf.c, alf.x) +get_dobj(alf::AlfonsoOpt) = -dot(alf.b, alf.y) - dot(alf.h, alf.z) + # load and verify problem data, calculate algorithmic parameters function load_data!( @@ -148,44 +147,46 @@ function load_data!( c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, - # varK::Cone, G::AbstractMatrix{Float64}, h::Vector{Float64}, conK::Cone; - check=true, # TODO later make false + check=false, # check rank conditions ) # check data consistency + n = length(c) + p = length(b) + q = length(h) + @assert n > 0 + @assert p + q > 0 + if n != size(A, 2) || n != size(G, 2) + error("number of variables is not consistent in A, G, and c") + end + if p != size(A, 1) + error("number of constraint rows is not consistent in A and b") + end + if q != size(G, 1) + error("number of constraint rows is not consistent in G and h") + end + + # check rank conditions + # TODO do appropriate decomps at the same time, do preprocessing if check - n = length(c) - p = length(b) - q = length(h) - @assert n > 0 - @assert p + q > 0 - if n != size(A, 2) || n != size(G, 2) - error("number of variables is not consistent in A, G, and c") - end - if p != size(A, 1) - error("number of constraint rows is not consistent in A and b") - end - if q != size(G, 1) - error("number of constraint rows is not consistent in G and h") - end - # TODO do appropriate decomps at the same time, do preprocessing # TODO rank currently missing method for sparse A, G - if !issparse(A) && rank(A) < p + if issparse(A) || issparse(G) + error("rank cannot currently be determined for sparse A or G") + end + if rank(A) < p error("A matrix is not full-row-rank; some primal equalities may be redundant or inconsistent") end - if !issparse(A) && !issparse(G) && rank(vcat(A, G)) < n + if rank(vcat(A, G)) < n error("[A' G'] is not full-row-rank; some dual equalities may be redundant (i.e. primal variables can be removed) or inconsistent") end end - # save data in solver object alf.c = c alf.A = A alf.b = b - # alf.varK = varK alf.G = G alf.h = h alf.conK = conK @@ -200,9 +201,8 @@ function solve!(alf::AlfonsoOpt) (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK - # (varK, conK) = (alf.varK, alf.conK) (n, p, q) = (length(c), length(b), length(h)) - bnu = 1.0 + barrierpar(cone) # + barrierpar(varK) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) + bnu = 1.0 + barrierpar(cone) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) # preallocate arrays tx = similar(c) @@ -221,7 +221,6 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("\nfinding initial iterate") # TODO use linsys solve function - # lhs = Symmetric([diagm(getincidence!(tx, varK)) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] # TODO this is currently different from what CVXOPT does tx .= xyz[1:n] ty .= xyz[n+1:n+p] @@ -259,6 +258,11 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("initial iterate found") + # calculate tolerances for convergence + tol_res_tx = inv(max(1.0, norm(c))) + tol_res_ty = inv(max(1.0, norm(b))) + tol_res_tz = inv(max(1.0, norm(h))) + # calculate prediction and correction step parameters # TODO put in prediction and correction step cache functions (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter @@ -266,13 +270,6 @@ function solve!(alf::AlfonsoOpt) alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size - - (norm_c, norm_b, norm_h) = (norm(c), norm(b), norm(h)) - tol_res_tx = max(1.0, norm_c) - tol_res_ty = max(1.0, norm_b) - tol_res_tz = max(1.0, norm_h) - - # main loop if alf.verbose println("starting iteration") @@ -340,7 +337,7 @@ function solve!(alf::AlfonsoOpt) end # check convergence criteria - # TODO update options + # TODO nearly primal or dual infeasible or nearly optimal cases? if nres_pr <= alf.tolfeas && nres_du <= alf.tolfeas && (gap <= alf.tolabsopt || (!isnan(relgap) && relgap <= alf.tolrelopt)) alf.verbose && println("optimal solution found; terminating") alf.status = :Optimal @@ -353,15 +350,11 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("dual infeasibility detected; terminating") alf.status = :DualInfeasible break + elseif mu <= alf.tolfeas*1e-2 && tau <= alf.tolfeas*1e-2*min(1.0, kap) + alf.verbose && println("ill-posedness detected; terminating") + alf.status = :IllPosed + break end - # TODO nearly primal or dual infeasible or nearly optimal cases? - # TODO ill-posed case? - # if (compl <= alf.optimtol) && (tau <= alf.optimtol*1e-02*max(1.0, kap)) - # alf.verbose && println("Problem is nearly primal or dual infeasible; terminating") - # alf.status = :NearlyInfeasible - # if (tau <= alf.optimtol*1e-02*min(1.0, kap)) && (mu <= alf.optimtol*1e-02) - # alf.verbose && println("Problem is ill-posed; terminating") - # alf.status = :IllPosed # check iteration limit iter += 1 @@ -413,7 +406,6 @@ function solve!(alf::AlfonsoOpt) dir_ts = dir[n+p+q+2:n+p+2*q+1] dir_tau = dir[n+p+2*q+2] - # determine step length alpha by line search alpha = alphapred nbhd = Inf @@ -424,7 +416,7 @@ function solve!(alf::AlfonsoOpt) nprediters += 1 sa_ts .= ts .+ alpha .* dir_ts - # @show alpha + # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood @@ -434,7 +426,6 @@ function solve!(alf::AlfonsoOpt) sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) sa_mu = (dot(sa_ts, sa_tz) + sa_tk)/bnu nbhd = calcnbhd(sa_tk, sa_mu, sa_tz, g, cone) - # @show sqrt(nbhd)/sa_mu if nbhd < abs2(beta*sa_mu) # iterate is inside the beta-neighborhood @@ -466,7 +457,6 @@ function solve!(alf::AlfonsoOpt) alphaprevok = false alpha = alf.predlsmulti*alpha # decrease alpha end - # @show nprediters if predfail break end @@ -479,10 +469,6 @@ function solve!(alf::AlfonsoOpt) tau += alpha*dir_tau kap += alpha*dir_kap mu = (dot(ts, tz) + tau*kap)/bnu - # @show mu - # @show tau, kap - # @show tx - # @show sqrt(nbhd)/mu # skip correction phase if allowed and current iterate is in the eta-neighborhood if alf.corrcheck && (nbhd < abs2(eta*mu)) @@ -562,7 +548,6 @@ function solve!(alf::AlfonsoOpt) alpha = alf.corrlsmulti*alpha # decrease alpha end - # @show ncorrlsiters if corrfail break end @@ -583,7 +568,7 @@ function solve!(alf::AlfonsoOpt) if nbhd <= abs2(eta*mu) break elseif ncorrsteps == alf.maxcorrsteps - # nbhd_eta > eta, so corrector failed + # outside eta neighborhood, so corrector failed corrfail = true alf.verbose && println("corrector phase finished outside the eta-neighborhood ($ncorrsteps correction steps); terminating") alf.status = :CorrectorFail @@ -591,7 +576,6 @@ function solve!(alf::AlfonsoOpt) end end end - # @show ncorrsteps if corrfail break end @@ -599,46 +583,15 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("\nfinished in $iter iterations; internal status is $(alf.status)\n") - # calculate final solution and iteration statistics + # calculate solution and iteration statistics alf.niters = iter - - - - tx ./= tau - alf.x = tx - ty ./= tau - alf.y = ty - tz ./= tau - alf.z = tz + alf.x = tx ./= tau + alf.s = ts ./= tau + alf.y = ty ./= tau + alf.z = tz ./= tau alf.tau = tau - ts ./= tau - alf.s = ts alf.kap = kap - - alf.pobj = dot(c, alf.x) - alf.dobj = -dot(b, alf.y) - dot(h, alf.z) # TODO already calculated; maybe should just calculate in getter functions though - - - - # alf.dgap = alf.pobj - alf.dobj - # alf.cgap = dot(alf.s, alf.x) - # alf.rel_dgap = alf.dgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) - # alf.rel_cgap = alf.cgap/(1.0 + abs(alf.pobj) + abs(alf.dobj)) - # - # # alf.pres = b - A*alf.x - # mul!(p_res, A, alf.x) - # p_res .= b .- p_res - # alf.pres = p_res - # # alf.dres = c - A'*alf.y - alf.s - # mul!(d_res, A', alf.y) - # d_res .= c .- d_res .- alf.s - # alf.dres = d_res - # - # alf.pin = norm(alf.pres) - # alf.din = norm(alf.dres) - # alf.rel_pin = alf.pin/(1.0 + norm(b, Inf)) - # alf.rel_din = alf.din/(1.0 + norm(c, Inf)) - + alf.mu = mu alf.solvetime = time() - starttime return nothing @@ -673,69 +626,6 @@ function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_tau, rhs_ts, rhs_kap, ka return (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) end - -# function solvesystem(alf, b_tx, b_ty, b_tz) -# # TODO s10.3 of V. -# -# -# sol_ty = AGW2 \ AHi*(b_tx + ) -# return -# end -# -# function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_tau, rhs_ts, rhs_kap, kap, tau) -# -# -# invmu = inv(mu) -# -# # TODO could ultimately be faster or more stable to do cholesky.L ldiv everywhere than to do full hessian ldiv -# calcHiarr!(HiAt, A', cone) -# HiAt .*= invmu -# mul!(AHiAt, A, HiAt) -# F = cholesky!(Symmetric(AHiAt)) -# -# # TODO can parallelize 1 and 2 -# # y2 = F\(rhs_ty + HiAt'*rhs_tx) -# mul!(y2, HiAt', rhs_tx) -# y2 .+= rhs_ty -# ldiv!(F, y2) # y2 done -# -# # x2 = Hi*invmu*(A'*y2 - rhs_tx) -# mul!(x2, A', y2) -# rhs_tx .= x2 .- rhs_tx # destroys rhs_tx -# rhs_tx .*= invmu -# calcHiarr!(x2, rhs_tx, cone) # x2 done -# -# # y1 = F\(b + HiAt'*c) -# mul!(y1, HiAt', c) -# y1 .+= b -# ldiv!(F, y1) # y1 done -# -# # x1 = Hi*invmu*(A'*y1 - c) -# mul!(rhs_tx, A', y1) -# rhs_tx .-= c -# rhs_tx .*= invmu -# calcHiarr!(x1, rhs_tx, cone) # x1 done -# -# -# x1 -# y1 -# z1 -# -# x2 -# y2 -# z2 -# -# dir_tau = (rhs_tau - rhs_kap/tau + dot(c, x2) + dot(b, y2) + dot(h, z2))/(kap/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) -# dir_tx = x2 + dir_tau * x1 -# dir_ty = y2 + dir_tau * y1 -# dir_tz = z2 + dir_tau * z1 -# dir_ts = -G*dir_tx + h*dir_tau - rhs_tz -# dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau -# -# return (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) -# end - - function getbetaeta(maxcorrsteps, bnu) if maxcorrsteps <= 2 if bnu < 10.0 diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 341c9c208..9d208bf5f 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -51,31 +51,31 @@ end @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 end -# @testset "2D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end +@testset "2D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 4, 2, 7, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 4, 2, 7, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end + +@testset "3D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(alf, 2, 3, 3, 5, dense=false) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end -# @testset "3D poly envelope example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(alf, 2, 3, 3, 5, dense=false) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end -# # @testset "4D poly envelope example (sparse A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # build_envelope!(alf, 2, 3, 4, 4, dense=false) @@ -83,7 +83,7 @@ end # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end -# + # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html @testset "Butcher" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -112,65 +112,65 @@ end # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 # end -# # out of memory during interpolation calculations -# # @testset "Heart" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :heart, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Lotka-Volterra" begin +# out of memory during interpolation calculations +# @testset "Heart" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :lotkavolterra, 3) +# build_namedpoly!(alf, :heart, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 # end -# -# # out of memory during interpolation calculations -# # @testset "Magnetism-7" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :magnetism7, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Motzkin" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) -# build_namedpoly!(alf, :motzkin, 7) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Reaction-diffusion" begin + +@testset "Lotka-Volterra" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :lotkavolterra, 3) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +end + +# out of memory during interpolation calculations +# @testset "Magnetism-7" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :reactiondiffusion, 4) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Robinson" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5) -# build_namedpoly!(alf, :robinson, 8) +# build_namedpoly!(alf, :magnetism7, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 # end -# + +@testset "Motzkin" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :motzkin, 7) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +end + +@testset "Reaction-diffusion" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :reactiondiffusion, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +end + +@testset "Robinson" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :robinson, 8) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +end + # # tolerances not satisfied # @testset "Rosenbrock" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=20) +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=20) # build_namedpoly!(alf, :rosenbrock, 3) # @time Alfonso.solve!(alf) # # @test Alfonso.get_status(alf) == :Optimal @@ -180,14 +180,14 @@ end # # # tolerances not satisfied # @testset "Schwefel" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, optimtol=1e-5, maxpredsmallsteps=25) +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=25) # build_namedpoly!(alf, :schwefel, 4) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end -# + # @testset "small second-order cone problem" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # c = Float64[0, -1, -1] From 430ead219f23cbf277f97e853d912768c090055f Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 15:39:44 -0400 Subject: [PATCH 07/30] refactor direction finding --- src/nativeinterface.jl | 162 +++++++++++++++++------------------------ test/nativeexamples.jl | 162 ++++++++++++++++++++--------------------- 2 files changed, 148 insertions(+), 176 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 73046cedb..a2916d1ae 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -213,6 +213,7 @@ function solve!(alf::AlfonsoOpt) sa_tz = similar(tz) dir_ts = similar(ts) g = similar(ts) + Hi = zeros(q, q) loadpnt!(cone, sa_ts) @@ -270,6 +271,19 @@ function solve!(alf::AlfonsoOpt) alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size + + H = Matrix(1.0I, q, q) + # tx ty tz kap ts tau + lhsbig = [ + zeros(n,n) A' G' zeros(n) zeros(n,q) c; + -A zeros(p,p) zeros(p,q) zeros(p) zeros(p,q) b; + zeros(q,n) zeros(q,p) Matrix(1.0I,q,q) zeros(q) mu*H zeros(q); + zeros(1,n) zeros(1,p) zeros(1,q) 1.0 zeros(1,q) mu/tau^2; + -G zeros(q,p) zeros(q,q) zeros(q) Matrix(-1.0I,q,q) h; + -c' -b' -h' -1.0 zeros(1,q) 0.0; + ] + + # main loop if alf.verbose println("starting iteration") @@ -366,45 +380,8 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction - # (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) = finddirection(alf, res_tx, res_ty, res_tz, res_tau, ts, kap*tau, kap, tau) - - # Hi = zeros(q, q) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # - # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi/mu h; -c' -b' -h' tau^2/mu] - # rhsnew = -lhsnew*[tx; ty; tz; tau] + [zeros(n); zeros(p); ts; kap] - # dir = lhsnew\rhsnew - # - # dir_tx = dir[1:n] - # dir_ty = dir[n+1:n+p] - # dir_tz = dir[n+p+1:n+p+q] - # dir_tau = dir[n+p+q+1] - # dir_ts = Hi/mu*(-tz - dir_tz) - # dir_kap = tau^2/mu*(-tau - dir_tau) - sa_ts .= ts - Hi = zeros(q, q) - calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - H = inv(Hi) - - # tx ty tz kap ts tau - lhsbig = [ - zeros(n, n) A' G' zeros(n) zeros(n, q) c; - -A zeros(p, p) zeros(p, q) zeros(p) zeros(p, q) b; - zeros(q, n) zeros(q, p) Matrix(1.0I, q, q) zeros(q) mu*H zeros(q); - zeros(1, n) zeros(1, p) zeros(1, q) 1.0 zeros(1, q) mu/tau^2 - -G zeros(q, p) zeros(q, q) zeros(q) Matrix(-1.0I, q, q) h; - -c' -b' -h' -1.0 zeros(1, q) 0.0; - ] - - rhsbig = [res_tx; res_ty; -tz; -kap; res_tz; res_tau] - dir = lhsbig\rhsbig - dir_tx = dir[1:n] - dir_ty = dir[n+1:n+p] - dir_tz = dir[n+p+1:n+p+q] - dir_kap = dir[n+p+q+1] - dir_ts = dir[n+p+q+2:n+p+2*q+1] - dir_tau = dir[n+p+2*q+2] + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, lhsbig, Hi, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) # determine step length alpha by line search alpha = alphapred @@ -482,48 +459,7 @@ function solve!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - # (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) = finddirection(alf, zeros(n), zeros(p), -mu*calcg!(g, cone), mu/tau, tz, kap*tau, kap, tau) - - # Hi = zeros(q, q) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # - # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) -Hi/mu h; -c' -b' -h' -tau^2/mu] - # Hipsis = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies - # Hipsik = tau^2/mu*(kap - mu/tau) - # dir = lhsnew\[zeros(n); zeros(p); Hipsis; Hipsik] - # - # dir_tx = dir[1:n] - # dir_ty = dir[n+1:n+p] - # dir_tz = dir[n+p+1:n+p+q] - # dir_tau = dir[n+p+q+1] - # - # dir_ts = Hipsis + Hi/mu*dir_tz - # # dir_ts = -G*dir_tx + h*dir_tau - # dir_kap = Hipsik + tau^2/mu*dir_tau - # # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - - Hi = zeros(q, q) - calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - H = inv(Hi) - - # tx ty tz kap ts tau - lhsbig = [ - zeros(n, n) A' G' zeros(n) zeros(n, q) c; - -A zeros(p, p) zeros(p, q) zeros(p) zeros(p, q) b; - zeros(q, n) zeros(q, p) Matrix(1.0I, q, q) zeros(q) mu*H zeros(q); - zeros(1, n) zeros(1, p) zeros(1, q) 1.0 zeros(1, q) mu/tau^2 - -G zeros(q, p) zeros(q, q) zeros(q) Matrix(-1.0I, q, q) h; - -c' -b' -h' -1.0 zeros(1, q) 0.0; - ] - - rhsbig = [zeros(n); zeros(p); -(tz + mu*calcg!(g, cone)); -(kap - mu/tau); zeros(q); 0.0] - dir = lhsbig\rhsbig - dir_tx = dir[1:n] - dir_ty = dir[n+1:n+p] - dir_tz = dir[n+p+1:n+p+q] - dir_kap = dir[n+p+q+1] - dir_ts = dir[n+p+q+2:n+p+2*q+1] - dir_tau = dir[n+p+2*q+2] + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, lhsbig, Hi, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) # determine step length alpha by line search alpha = alf.alphacorr @@ -565,6 +501,7 @@ function solve!(alf::AlfonsoOpt) if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck sa_tz .= tz nbhd = calcnbhd(tau*kap, mu, sa_tz, g, cone) + @show sqrt(nbhd)/mu if nbhd <= abs2(eta*mu) break elseif ncorrsteps == alf.maxcorrsteps @@ -604,26 +541,61 @@ function calcnbhd(tk, mu, sa_tz, g, cone) return (tk - mu)^2 + dot(sa_tz, g) end - -function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_tau, rhs_ts, rhs_kap, kap, tau) +function finddirection(alf, lhsbig, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK (n, p, q) = (length(c), length(b), length(h)) - Hi = zeros(q, q) calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - lhs = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi h; -c' -b' -h' kap/tau] - - xyzt = lhs\[rhs_tx; rhs_ty; rhs_tz - rhs_ts; rhs_tau - rhs_kap/tau] - - dir_tx = xyzt[1:n] - dir_ty = xyzt[n+1:n+p] - dir_tz = xyzt[n+p+1:n+p+q] - dir_tau = xyzt[n+p+q+1] - dir_ts = -G*dir_tx + h*dir_tau - rhs_tz - dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau - - return (dir_tx, dir_ty, dir_tz, dir_ts, dir_kap, dir_tau) + H = inv(Hi) + + # tx ty tz kap ts tau + lhsbig[n+p+1:n+p+q, n+p+q+2:n+p+q+1+q] .= mu*H + lhsbig[n+p+q+1, end] = mu/tau^2 + rhsbig = [rhs_tx; rhs_ty; rhs_tz; rhs_kap; rhs_ts; rhs_tau] + + dir = lhsbig\rhsbig + dir_tx = dir[1:n] + dir_ty = dir[n+1:n+p] + dir_tz = dir[n+p+1:n+p+q] + dir_kap = dir[n+p+q+1] + dir_ts = dir[n+p+q+2:n+p+2*q+1] + dir_tau = dir[n+p+2*q+2] + + + + + # Hi = zeros(q, q) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + # lhs = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi h; -c' -b' -h' kap/tau] + # + # xyzt = lhs\[rhs_tx; rhs_ty; rhs_tz - rhs_ts; rhs_tau - rhs_kap/tau] + # + # dir_tx = xyzt[1:n] + # dir_ty = xyzt[n+1:n+p] + # dir_tz = xyzt[n+p+1:n+p+q] + # dir_tau = xyzt[n+p+q+1] + # dir_ts = -G*dir_tx + h*dir_tau - rhs_tz + # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau + + + # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) -Hi/mu h; -c' -b' -h' -tau^2/mu] + # Hipsis = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies + # Hipsik = tau^2/mu*(kap - mu/tau) + # dir = lhsnew\[zeros(n); zeros(p); Hipsis; Hipsik] + # + # dir_tx = dir[1:n] + # dir_ty = dir[n+1:n+p] + # dir_tz = dir[n+p+1:n+p+q] + # dir_tau = dir[n+p+q+1] + # + # dir_ts = Hipsis + Hi/mu*dir_tz + # # dir_ts = -G*dir_tx + h*dir_tau + # dir_kap = Hipsik + tau^2/mu*dir_tau + # # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) + + + return (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) end function getbetaeta(maxcorrsteps, bnu) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 9d208bf5f..28c790182 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -51,39 +51,39 @@ end @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 end -@testset "2D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 4, 2, 7, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 4, 2, 7, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "3D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(alf, 2, 3, 3, 5, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -# @testset "4D poly envelope example (sparse A)" begin +# @testset "2D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "3D poly envelope example (sparse A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(alf, 2, 3, 4, 4, dense=false) +# build_envelope!(alf, 2, 3, 3, 5, dense=false) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end - +# +# # @testset "4D poly envelope example (sparse A)" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_envelope!(alf, 2, 3, 4, 4, dense=false) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# # end +# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html @testset "Butcher" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -103,70 +103,70 @@ end @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 end -# @testset "Goldstein-Price" begin +# # @testset "Goldstein-Price" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :goldsteinprice, 7) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# # end +# +# # out of memory during interpolation calculations +# # @testset "Heart" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :heart, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Lotka-Volterra" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :goldsteinprice, 7) +# build_namedpoly!(alf, :lotkavolterra, 3) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 # end - -# out of memory during interpolation calculations -# @testset "Heart" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :heart, 2) +# +# # out of memory during interpolation calculations +# # @testset "Magnetism-7" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :magnetism7, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Motzkin" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_namedpoly!(alf, :motzkin, 7) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 # end - -@testset "Lotka-Volterra" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :lotkavolterra, 3) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -end - -# out of memory during interpolation calculations -# @testset "Magnetism-7" begin +# +# @testset "Reaction-diffusion" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :magnetism7, 2) +# build_namedpoly!(alf, :reactiondiffusion, 4) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Robinson" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_namedpoly!(alf, :robinson, 8) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 # end - -@testset "Motzkin" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_namedpoly!(alf, :motzkin, 7) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -end - -@testset "Reaction-diffusion" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :reactiondiffusion, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -end - -@testset "Robinson" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_namedpoly!(alf, :robinson, 8) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -end # # tolerances not satisfied # @testset "Rosenbrock" begin From 16b120949fab9b85248827afd3a52be7326f2886 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 16:23:43 -0400 Subject: [PATCH 08/30] improve linear system solve size partially --- src/nativeinterface.jl | 98 +++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index a2916d1ae..f587e4951 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -271,19 +271,6 @@ function solve!(alf::AlfonsoOpt) alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size alphapredinit = (alf.predlinesearch ? min(100*alphapredfix, 0.9999) : alphapredfix) # predictor step size - - H = Matrix(1.0I, q, q) - # tx ty tz kap ts tau - lhsbig = [ - zeros(n,n) A' G' zeros(n) zeros(n,q) c; - -A zeros(p,p) zeros(p,q) zeros(p) zeros(p,q) b; - zeros(q,n) zeros(q,p) Matrix(1.0I,q,q) zeros(q) mu*H zeros(q); - zeros(1,n) zeros(1,p) zeros(1,q) 1.0 zeros(1,q) mu/tau^2; - -G zeros(q,p) zeros(q,q) zeros(q) Matrix(-1.0I,q,q) h; - -c' -b' -h' -1.0 zeros(1,q) 0.0; - ] - - # main loop if alf.verbose println("starting iteration") @@ -381,7 +368,7 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction sa_ts .= ts - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, lhsbig, Hi, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, Hi, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) # determine step length alpha by line search alpha = alphapred @@ -459,7 +446,7 @@ function solve!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, lhsbig, Hi, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, Hi, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) # determine step length alpha by line search alpha = alf.alphacorr @@ -501,7 +488,7 @@ function solve!(alf::AlfonsoOpt) if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck sa_tz .= tz nbhd = calcnbhd(tau*kap, mu, sa_tz, g, cone) - @show sqrt(nbhd)/mu + # @show sqrt(nbhd)/mu if nbhd <= abs2(eta*mu) break elseif ncorrsteps == alf.maxcorrsteps @@ -541,59 +528,54 @@ function calcnbhd(tk, mu, sa_tz, g, cone) return (tk - mu)^2 + dot(sa_tz, g) end -function finddirection(alf, lhsbig, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) +function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK (n, p, q) = (length(c), length(b), length(h)) calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - H = inv(Hi) + + # H = inv(Hi) + # lhsbig[n+p+1:n+p+q, n+p+q+2:n+p+q+1+q] .= mu*H + # lhsbig[n+p+q+1, end] = mu/tau^2 + # rhsbig = [rhs_tx; rhs_ty; rhs_tz; rhs_kap; rhs_ts; rhs_tau] + # dir = lhsbig\rhsbig + # dir_tx = dir[1:n] + # dir_ty = dir[n+1:n+p] + # dir_tz = dir[n+p+1:n+p+q] + # dir_kap = dir[n+p+q+1] + # dir_ts = dir[n+p+q+2:n+p+2*q+1] + # dir_tau = dir[n+p+2*q+2] # tx ty tz kap ts tau - lhsbig[n+p+1:n+p+q, n+p+q+2:n+p+q+1+q] .= mu*H - lhsbig[n+p+q+1, end] = mu/tau^2 - rhsbig = [rhs_tx; rhs_ty; rhs_tz; rhs_kap; rhs_ts; rhs_tau] + # lhsbig = [ + # zeros(n,n) A' G' zeros(n) zeros(n,q) c; + # -A zeros(p,p) zeros(p,q) zeros(p) zeros(p,q) b; + # zeros(q,n) zeros(q,p) Matrix(1.0I,q,q) zeros(q) mu*H zeros(q); + # zeros(1,n) zeros(1,p) zeros(1,q) 1.0 zeros(1,q) mu/tau^2; + # -G zeros(q,p) zeros(q,q) zeros(q) Matrix(-1.0I,q,q) h; + # -c' -b' -h' -1.0 zeros(1,q) 0.0; + # ] + # dir_tz + mu*H*dir_ts = rhs_tz + # dir_ts = Hi/mu*(rhs_tz - dir_tz) + # dir_kap + mu/tau^2*dir_tau = rhs_kap + # dir_kap = rhs_kap - mu/tau^2*dir_tau + + lhs = [ + zeros(n,n) A' G' c; + -A zeros(p,p) zeros(p,q) b; + -G zeros(q,p) Hi/mu h; + -c' -b' -h' mu/tau/tau; + ] + rhs = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz; rhs_tau + rhs_kap] + dir = lhs\rhs - dir = lhsbig\rhsbig dir_tx = dir[1:n] dir_ty = dir[n+1:n+p] dir_tz = dir[n+p+1:n+p+q] - dir_kap = dir[n+p+q+1] - dir_ts = dir[n+p+q+2:n+p+2*q+1] - dir_tau = dir[n+p+2*q+2] - - - - - # Hi = zeros(q, q) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # lhs = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) Hi h; -c' -b' -h' kap/tau] - # - # xyzt = lhs\[rhs_tx; rhs_ty; rhs_tz - rhs_ts; rhs_tau - rhs_kap/tau] - # - # dir_tx = xyzt[1:n] - # dir_ty = xyzt[n+1:n+p] - # dir_tz = xyzt[n+p+1:n+p+q] - # dir_tau = xyzt[n+p+q+1] - # dir_ts = -G*dir_tx + h*dir_tau - rhs_tz - # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau - - - # lhsnew = [zeros(n, n) A' G' c; -A zeros(p, p) zeros(p, q) b; -G zeros(q, p) -Hi/mu h; -c' -b' -h' -tau^2/mu] - # Hipsis = Hi/mu*(tz + mu*calcg!(g, cone)) # TODO simplifies - # Hipsik = tau^2/mu*(kap - mu/tau) - # dir = lhsnew\[zeros(n); zeros(p); Hipsis; Hipsik] - # - # dir_tx = dir[1:n] - # dir_ty = dir[n+1:n+p] - # dir_tz = dir[n+p+1:n+p+q] - # dir_tau = dir[n+p+q+1] - # - # dir_ts = Hipsis + Hi/mu*dir_tz - # # dir_ts = -G*dir_tx + h*dir_tau - # dir_kap = Hipsik + tau^2/mu*dir_tau - # # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - + dir_tau = dir[n+p+q+1] + dir_ts = -G*dir_tx + h*dir_tau - rhs_ts + dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau return (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) end From 4f7f9851c96450f389e29f81e2d958d5cc75489d Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 16:48:03 -0400 Subject: [PATCH 09/30] use two symmetric linsys solves and combine --- src/nativeinterface.jl | 63 +++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index f587e4951..c7772b0cf 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -535,18 +535,6 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # H = inv(Hi) - # lhsbig[n+p+1:n+p+q, n+p+q+2:n+p+q+1+q] .= mu*H - # lhsbig[n+p+q+1, end] = mu/tau^2 - # rhsbig = [rhs_tx; rhs_ty; rhs_tz; rhs_kap; rhs_ts; rhs_tau] - # dir = lhsbig\rhsbig - # dir_tx = dir[1:n] - # dir_ty = dir[n+1:n+p] - # dir_tz = dir[n+p+1:n+p+q] - # dir_kap = dir[n+p+q+1] - # dir_ts = dir[n+p+q+2:n+p+2*q+1] - # dir_tau = dir[n+p+2*q+2] - # tx ty tz kap ts tau # lhsbig = [ # zeros(n,n) A' G' zeros(n) zeros(n,q) c; @@ -561,19 +549,50 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau # dir_kap + mu/tau^2*dir_tau = rhs_kap # dir_kap = rhs_kap - mu/tau^2*dir_tau + + # lhs = [ + # zeros(n,n) A' G' c; + # -A zeros(p,p) zeros(p,q) b; + # -G zeros(q,p) Hi/mu h; + # -c' -b' -h' mu/tau/tau; + # ] + # rhs = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz; rhs_tau + rhs_kap] + # dir = lhs\rhs + # + # dir_tx = dir[1:n] + # dir_ty = dir[n+1:n+p] + # dir_tz = dir[n+p+1:n+p+q] + # dir_tau = dir[n+p+q+1] + # dir_ts = -G*dir_tx + h*dir_tau - rhs_ts + # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau + + + # solve two symmetric systems and combine the solutions lhs = [ - zeros(n,n) A' G' c; - -A zeros(p,p) zeros(p,q) b; - -G zeros(q,p) Hi/mu h; - -c' -b' -h' mu/tau/tau; + zeros(n,n) A' G'; + -A zeros(p,p) zeros(p,q); + -G zeros(q,p) Hi/mu; ] - rhs = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz; rhs_tau + rhs_kap] - dir = lhs\rhs - dir_tx = dir[1:n] - dir_ty = dir[n+1:n+p] - dir_tz = dir[n+p+1:n+p+q] - dir_tau = dir[n+p+q+1] + # system 1 + rhs1 = [-c; -b; -h] + sol1 = lhs\rhs1 + x1 = sol1[1:n] + y1 = sol1[n+1:n+p] + z1 = sol1[n+p+1:end] + + # system 2 + rhs2 = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz] + sol2 = lhs\rhs2 + x2 = sol2[1:n] + y2 = sol2[n+1:n+p] + z2 = sol2[n+p+1:end] + + # combine + dir_tau = ((rhs_tau + rhs_kap) + dot(c, x2) + dot(b, y2) + dot(h, z2))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) + dir_tx = x2 + dir_tau * x1 + dir_ty = y2 + dir_tau * y1 + dir_tz = z2 + dir_tau * z1 dir_ts = -G*dir_tx + h*dir_tau - rhs_ts dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau From a01d0f689101ef6fea79ffa2c4279b8c5269ce6f Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 22 Sep 2018 18:56:40 -0400 Subject: [PATCH 10/30] initial work on QR+cholesky linsys solve --- src/nativeinterface.jl | 85 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index c7772b0cf..38024756c 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -40,6 +40,8 @@ mutable struct AlfonsoOpt h::Vector{Float64} # cone constraint vector, size q conK::Cone # primal constraint cone object + At_qr # QR factorization of A' + # results status::Symbol # solver status solvetime::Float64 # total solve time @@ -169,14 +171,16 @@ function load_data!( error("number of constraint rows is not consistent in G and h") end - # check rank conditions - # TODO do appropriate decomps at the same time, do preprocessing + # perform QR decomposition of A' for use in linear system solves + # TODO only use for factorization-based linear system solves + At_qr = issparse(A) ? qr(sparse(A')) : qr(A') # TODO this should be automatic in Julia for transpose + # TODO rank for qr decomp should be implemented in Julia - see https://github.com/JuliaLang/julia/blob/f8b52dab77415a22d28497f48407aca92fbbd4c3/stdlib/LinearAlgebra/src/qr.jl#L895 if check - # TODO rank currently missing method for sparse A, G + # check rank conditions if issparse(A) || issparse(G) error("rank cannot currently be determined for sparse A or G") end - if rank(A) < p + if rank(A) < p # TODO change to rank(At_qr) error("A matrix is not full-row-rank; some primal equalities may be redundant or inconsistent") end if rank(vcat(A, G)) < n @@ -190,6 +194,7 @@ function load_data!( alf.G = G alf.h = h alf.conK = conK + alf.At_qr = At_qr alf.status = :Loaded return alf @@ -532,8 +537,9 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK (n, p, q) = (length(c), length(b), length(h)) + At_qr = alf.At_qr - calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) # tx ty tz kap ts tau # lhsbig = [ @@ -566,13 +572,72 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau # dir_ts = -G*dir_tx + h*dir_tau - rhs_ts # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau + # lhs = [ + # zeros(n,n) A' G'; + # -A zeros(p,p) zeros(p,q); + # -G zeros(q,p) Hi/mu; + # ] + # solve two symmetric systems and combine the solutions - lhs = [ - zeros(n,n) A' G'; - -A zeros(p,p) zeros(p,q); - -G zeros(q,p) Hi/mu; - ] + # use QR + cholesky method from CVXOPT + # (1) eliminate equality constraints via QR of A' + # (2) solve reduced system by cholesky + + # |0 A' G | * |ux| = |bx| + # |A 0 0 | |uy| |by| + # |G 0 -Hi/mu| |uz| |bz| + + + # A' = [Q1 Q2] * [R1; 0] + Q1 = At_qr.Q[:,1:p] + Q2 = At_qr.Q[:,p+1:end] + R1 = At_qr.R + + ch = cholesky!(Symmetric(Q2'*G'*Hi/mu*G*Q2)) # TODO maybe bunch-kaufman + + + + + # invmu = inv(mu) + # calcHiarr!(HiG, G, cone) + # mul!(GtHiG, G', HiG) + # GtHiG .*= invmu + # chG = cholesky!(Symmetric(GtHiG)) # TODO maybe bunch-kaufman + + + + # invmu = inv(mu) + + # calcHiarr!(HiAt, A', cone) + # HiAt .*= invmu + # mul!(AHiAt, A, HiAt) + # F = cholesky!(Symmetric(AHiAt)) + # + # # TODO can parallelize 1 and 2 + # # y2 = F\(rhs_ty + HiAt'*rhs_tx) + # mul!(y2, HiAt', rhs_tx) + # y2 .+= rhs_ty + # ldiv!(F, y2) # y2 done + # + # # x2 = Hi*invmu*(A'*y2 - rhs_tx) + # mul!(x2, A', y2) + # rhs_tx .= x2 .- rhs_tx # destroys rhs_tx + # rhs_tx .*= invmu + # calcHiarr!(x2, rhs_tx, cone) # x2 done + # + # # y1 = F\(b + HiAt'*c) + # mul!(y1, HiAt', c) + # y1 .+= b + # ldiv!(F, y1) # y1 done + # + # # x1 = Hi*invmu*(A'*y1 - c) + # mul!(rhs_tx, A', y1) + # rhs_tx .-= c + # rhs_tx .*= invmu + # calcHiarr!(x1, rhs_tx, cone) # x1 done + + # system 1 rhs1 = [-c; -b; -h] From 58893004156d0334c5629270160de005798d6758 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sun, 23 Sep 2018 16:24:51 -0400 Subject: [PATCH 11/30] get qr+cholesky linsys solve working for dense LP --- src/nativeinterface.jl | 149 ++++++++++++++--------------------------- test/nativeexamples.jl | 106 ++++++++++++++--------------- 2 files changed, 105 insertions(+), 150 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 38024756c..7e924069f 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -40,7 +40,9 @@ mutable struct AlfonsoOpt h::Vector{Float64} # cone constraint vector, size q conK::Cone # primal constraint cone object - At_qr # QR factorization of A' + Q1 # QR factorization of A' + Q2 + Ri # results status::Symbol # solver status @@ -173,7 +175,21 @@ function load_data!( # perform QR decomposition of A' for use in linear system solves # TODO only use for factorization-based linear system solves + # TODO reduce allocs, time + # A' = [Q1 Q2] * [R1; 0] At_qr = issparse(A) ? qr(sparse(A')) : qr(A') # TODO this should be automatic in Julia for transpose + + Q1 = At_qr.Q[:,1:p] + Q2 = At_qr.Q[:,p+1:n] + # TODO problematic for sparse or large? versus: + # Q = Array(At_qr.Q) + # Q1 = Q[:,1:p] + # Q2 = Q[:,p+1:n] + + R = At_qr.R # TODO check upper tri + @assert istriu(R) + Ri = inv(UpperTriangular(R)) + # TODO rank for qr decomp should be implemented in Julia - see https://github.com/JuliaLang/julia/blob/f8b52dab77415a22d28497f48407aca92fbbd4c3/stdlib/LinearAlgebra/src/qr.jl#L895 if check # check rank conditions @@ -194,7 +210,9 @@ function load_data!( alf.G = G alf.h = h alf.conK = conK - alf.At_qr = At_qr + alf.Q1 = Q1 + alf.Q2 = Q2 + alf.Ri = Ri alf.status = :Loaded return alf @@ -537,121 +555,58 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK (n, p, q) = (length(c), length(b), length(h)) - At_qr = alf.At_qr + (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # tx ty tz kap ts tau - # lhsbig = [ - # zeros(n,n) A' G' zeros(n) zeros(n,q) c; - # -A zeros(p,p) zeros(p,q) zeros(p) zeros(p,q) b; - # zeros(q,n) zeros(q,p) Matrix(1.0I,q,q) zeros(q) mu*H zeros(q); - # zeros(1,n) zeros(1,p) zeros(1,q) 1.0 zeros(1,q) mu/tau^2; - # -G zeros(q,p) zeros(q,q) zeros(q) Matrix(-1.0I,q,q) h; - # -c' -b' -h' -1.0 zeros(1,q) 0.0; - # ] - # dir_tz + mu*H*dir_ts = rhs_tz - # dir_ts = Hi/mu*(rhs_tz - dir_tz) - # dir_kap + mu/tau^2*dir_tau = rhs_kap - # dir_kap = rhs_kap - mu/tau^2*dir_tau - - - # lhs = [ - # zeros(n,n) A' G' c; - # -A zeros(p,p) zeros(p,q) b; - # -G zeros(q,p) Hi/mu h; - # -c' -b' -h' mu/tau/tau; - # ] - # rhs = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz; rhs_tau + rhs_kap] - # dir = lhs\rhs - # - # dir_tx = dir[1:n] - # dir_ty = dir[n+1:n+p] - # dir_tz = dir[n+p+1:n+p+q] - # dir_tau = dir[n+p+q+1] - # dir_ts = -G*dir_tx + h*dir_tau - rhs_ts - # dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau - - # lhs = [ - # zeros(n,n) A' G'; - # -A zeros(p,p) zeros(p,q); - # -G zeros(q,p) Hi/mu; - # ] - + g = zeros(q) + calcg!(g, cone) + H = Diagonal(g .* g) # TODO LP only # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT # (1) eliminate equality constraints via QR of A' # (2) solve reduced system by cholesky - # |0 A' G | * |ux| = |bx| + # |0 A' G' | * |ux| = |bx| # |A 0 0 | |uy| |by| # |G 0 -Hi/mu| |uz| |bz| # A' = [Q1 Q2] * [R1; 0] - Q1 = At_qr.Q[:,1:p] - Q2 = At_qr.Q[:,p+1:end] - R1 = At_qr.R - - ch = cholesky!(Symmetric(Q2'*G'*Hi/mu*G*Q2)) # TODO maybe bunch-kaufman - - - - - # invmu = inv(mu) - # calcHiarr!(HiG, G, cone) - # mul!(GtHiG, G', HiG) - # GtHiG .*= invmu - # chG = cholesky!(Symmetric(GtHiG)) # TODO maybe bunch-kaufman - - - - # invmu = inv(mu) - - # calcHiarr!(HiAt, A', cone) - # HiAt .*= invmu - # mul!(AHiAt, A, HiAt) - # F = cholesky!(Symmetric(AHiAt)) - # - # # TODO can parallelize 1 and 2 - # # y2 = F\(rhs_ty + HiAt'*rhs_tx) - # mul!(y2, HiAt', rhs_tx) - # y2 .+= rhs_ty - # ldiv!(F, y2) # y2 done - # - # # x2 = Hi*invmu*(A'*y2 - rhs_tx) - # mul!(x2, A', y2) - # rhs_tx .= x2 .- rhs_tx # destroys rhs_tx - # rhs_tx .*= invmu - # calcHiarr!(x2, rhs_tx, cone) # x2 done - # - # # y1 = F\(b + HiAt'*c) - # mul!(y1, HiAt', c) - # y1 .+= b - # ldiv!(F, y1) # y1 done - # - # # x1 = Hi*invmu*(A'*y1 - c) - # mul!(rhs_tx, A', y1) - # rhs_tx .-= c - # rhs_tx .*= invmu - # calcHiarr!(x1, rhs_tx, cone) # x1 done + # [Q1 Q2]' * G' * mu*H * G * [Q1 Q2] + muH = Symmetric(mu*H) + QH = G'*muH*G + K22 = Q2'*QH*Q2 # TODO use syrk + K11 = Q1'*QH*Q1 + K12 = Q1'*QH*Q2 + # factorize K22 = Q2' * G' * mu*H * G * Q2 + K22_ch = cholesky!(Symmetric(K22)) # TODO in-place; maybe bunch-kaufman + # TODO refac systems 1 and 2 # system 1 - rhs1 = [-c; -b; -h] - sol1 = lhs\rhs1 - x1 = sol1[1:n] - y1 = sol1[n+1:n+p] - z1 = sol1[n+p+1:end] + (bx, by, muHbz) = (-c, b, muH*h) + + bxGHbz = bx + G'*muHbz + Q1tx = Ri'*by + Q2tx = K22_ch\(Q2'*bxGHbz - K12'*Q1tx) + + y1 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) + x1 = Q1*Q1tx + Q2*Q2tx + z1 = muH*G*x1 - muHbz # system 2 - rhs2 = [rhs_tx; rhs_ty; rhs_ts + Hi/mu*rhs_tz] - sol2 = lhs\rhs2 - x2 = sol2[1:n] - y2 = sol2[n+1:n+p] - z2 = sol2[n+p+1:end] + (bx, by, muHbz) = (rhs_tx, -rhs_ty, -muH*rhs_ts - rhs_tz) + + bxGHbz = bx + G'*muHbz + Q1tx = Ri'*by + Q2tx = K22_ch\(Q2'*bxGHbz - K12'*Q1tx) + + y2 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) + x2 = Q1*Q1tx + Q2*Q2tx + z2 = muH*G*x2 - muHbz # combine dir_tau = ((rhs_tau + rhs_kap) + dot(c, x2) + dot(b, y2) + dot(h, z2))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 28c790182..33e6a3f45 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,7 +1,7 @@ - +# # @testset "large dense lp example (dense A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, use_data=true) +# build_lp!(alf, 500, 1000, use_data=true, dense=true) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 @@ -16,40 +16,40 @@ # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end -@testset "small dense lp example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(d_alf, 50, 100, dense=true, tosparse=false) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(s_alf, 50, 100, dense=true, tosparse=true) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "1D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @testset "small dense lp example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -end +# @testset "1D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# end # @testset "2D poly envelope example (dense vs sparse A)" begin # # dense methods @@ -85,23 +85,23 @@ end # # end # # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -@testset "Butcher" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :butcher, 2) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -end - -@testset "Caprasse" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :caprasse, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -end +# @testset "Butcher" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :butcher, 2) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Caprasse" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :caprasse, 4) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# end # # @testset "Goldstein-Price" begin # # alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -332,5 +332,5 @@ end alf = Alfonso.AlfonsoOpt(verbose=true) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :DualInfeasible + @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) end From 1111280cce95846160efba05a99f8c6d3a4872e7 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sun, 23 Sep 2018 16:45:38 -0400 Subject: [PATCH 12/30] prepare to work on hessian vector product functions --- src/cone.jl | 8 -------- src/nativeinterface.jl | 36 ++++++++++++++++-------------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/cone.jl b/src/cone.jl index b3cc51ffd..c8a179c4f 100644 --- a/src/cone.jl +++ b/src/cone.jl @@ -14,14 +14,6 @@ end # calculate complexity parameter of the barrier (sum of the primitive cone barrier parameters) barrierpar(cone::Cone) = sum(barrierpar_prm(prm) for prm in cone.prms) -function getincidence!(a::Vector{Float64}, cone::Cone) - a .= 0.0 - for prm in prms, j in prm.idxs - a[j] += 1.0 - end - return a -end - function getintdir!(a::Vector{Float64}, cone::Cone) for k in eachindex(cone.prms) getintdir_prm!(view(a, cone.idxs[k]), cone.prms[k]) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 7e924069f..5231441e7 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -557,11 +557,8 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau (n, p, q) = (length(c), length(b), length(h)) (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - - g = zeros(q) - calcg!(g, cone) - H = Diagonal(g .* g) # TODO LP only + calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + H = inv(Hi) # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT @@ -575,38 +572,37 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau # A' = [Q1 Q2] * [R1; 0] # [Q1 Q2]' * G' * mu*H * G * [Q1 Q2] - muH = Symmetric(mu*H) - - QH = G'*muH*G - K22 = Q2'*QH*Q2 # TODO use syrk - K11 = Q1'*QH*Q1 - K12 = Q1'*QH*Q2 + HG = mu*H*G + GHG = G'*HG + K22 = Q2'*GHG*Q2 # TODO use syrk + K11 = Q1'*GHG*Q1 + K12 = Q1'*GHG*Q2 # factorize K22 = Q2' * G' * mu*H * G * Q2 - K22_ch = cholesky!(Symmetric(K22)) # TODO in-place; maybe bunch-kaufman + K22_F = bunchkaufman!(Symmetric(K22)) # TODO cholesky vs bunch-kaufman? # TODO refac systems 1 and 2 # system 1 - (bx, by, muHbz) = (-c, b, muH*h) + (bx, by, Hbz) = (-c, b, mu*H*h) - bxGHbz = bx + G'*muHbz + bxGHbz = bx + G'*Hbz Q1tx = Ri'*by - Q2tx = K22_ch\(Q2'*bxGHbz - K12'*Q1tx) + Q2tx = K22_F\(Q2'*bxGHbz - K12'*Q1tx) y1 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) x1 = Q1*Q1tx + Q2*Q2tx - z1 = muH*G*x1 - muHbz + z1 = HG*x1 - Hbz # system 2 - (bx, by, muHbz) = (rhs_tx, -rhs_ty, -muH*rhs_ts - rhs_tz) + (bx, by, Hbz) = (rhs_tx, -rhs_ty, -mu*H*rhs_ts - rhs_tz) - bxGHbz = bx + G'*muHbz + bxGHbz = bx + G'*Hbz Q1tx = Ri'*by - Q2tx = K22_ch\(Q2'*bxGHbz - K12'*Q1tx) + Q2tx = K22_F\(Q2'*bxGHbz - K12'*Q1tx) y2 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) x2 = Q1*Q1tx + Q2*Q2tx - z2 = muH*G*x2 - muHbz + z2 = HG*x2 - Hbz # combine dir_tau = ((rhs_tau + rhs_kap) + dot(c, x2) + dot(b, y2) + dot(h, z2))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) From 8fb501f9db979c4ce6cc002240eedf4c1b3d1149 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sun, 23 Sep 2018 21:09:03 -0400 Subject: [PATCH 13/30] hessian for nonneg cone, get sparse and dense QRs working --- examples/lp/lp.jl | 6 +-- src/cone.jl | 14 ++++++ src/nativeinterface.jl | 46 ++++++++++++------- src/primitivecones/nonnegative.jl | 11 ++++- test/nativeexamples.jl | 73 ++++++++++++++++--------------- 5 files changed, 93 insertions(+), 57 deletions(-) diff --git a/examples/lp/lp.jl b/examples/lp/lp.jl index 9e4b71075..d176b3069 100644 --- a/examples/lp/lp.jl +++ b/examples/lp/lp.jl @@ -32,11 +32,7 @@ function build_lp!(alf::Alfonso.AlfonsoOpt, m::Int, n::Int; use_data::Bool=false if tosparse && !issparse(A) A = sparse(A) end - if dense - G = Matrix(-1.0I, n, n) # TODO uniformscaling? - else - G = SparseMatrixCSC(-1.0I, n, n) - end + G = SparseMatrixCSC(-1.0I, n, n) h = zeros(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(n)], [1:n]) diff --git a/src/cone.jl b/src/cone.jl index c8a179c4f..50b075269 100644 --- a/src/cone.jl +++ b/src/cone.jl @@ -38,6 +38,20 @@ function calcg!(g::Vector{Float64}, cone::Cone) return g end +function calcHarr!(prod::AbstractMatrix{Float64}, arr::AbstractMatrix{Float64}, cone::Cone) + for k in eachindex(cone.prms) + calcHarr_prm!(view(prod, cone.idxs[k], :), view(arr, cone.idxs[k], :), cone.prms[k]) + end + return prod +end + +function calcHarr!(prod::AbstractVector{Float64}, arr::AbstractVector{Float64}, cone::Cone) + for k in eachindex(cone.prms) + calcHarr_prm!(view(prod, cone.idxs[k]), view(arr, cone.idxs[k]), cone.prms[k]) + end + return prod +end + function calcHiarr!(Hi_mat::AbstractMatrix{Float64}, mat::AbstractMatrix{Float64}, cone::Cone) for k in eachindex(cone.prms) calcHiarr_prm!(view(Hi_mat, cone.idxs[k], :), view(mat, cone.idxs[k], :), cone.prms[k]) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 5231441e7..36548a063 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -177,18 +177,30 @@ function load_data!( # TODO only use for factorization-based linear system solves # TODO reduce allocs, time # A' = [Q1 Q2] * [R1; 0] - At_qr = issparse(A) ? qr(sparse(A')) : qr(A') # TODO this should be automatic in Julia for transpose + if issparse(A) + F = qr(sparse(A')) - Q1 = At_qr.Q[:,1:p] - Q2 = At_qr.Q[:,p+1:n] - # TODO problematic for sparse or large? versus: - # Q = Array(At_qr.Q) - # Q1 = Q[:,1:p] - # Q2 = Q[:,p+1:n] + Q1 = zeros(n, p) + Q2 = zeros(n, n-p) + Ri = zeros(p, p) - R = At_qr.R # TODO check upper tri - @assert istriu(R) - Ri = inv(UpperTriangular(R)) + Q = F.Q*Matrix(1.0I, n, n) # TODO should this be sparse? + Q1[F.prow, F.pcol] = Q[:, 1:p] + Q2[F.prow, :] = Q[:, p+1:n] + + @assert istriu(F.R) + Ri[F.pcol, F.pcol] = inv(UpperTriangular(F.R)) + else + F = qr(A') + + Q = F.Q*Matrix(1.0I, n, n) + Q1 = Q[:,1:p] + Q2 = Q[:,p+1:n] + + @assert norm(A' - Q1*F.R) < 1e-10 # TODO delete later + @assert istriu(F.R) + Ri = inv(UpperTriangular(F.R)) + end # TODO rank for qr decomp should be implemented in Julia - see https://github.com/JuliaLang/julia/blob/f8b52dab77415a22d28497f48407aca92fbbd4c3/stdlib/LinearAlgebra/src/qr.jl#L895 if check @@ -544,6 +556,7 @@ function solve!(alf::AlfonsoOpt) return nothing end +# TODO put this inside the cone functions and ideally don't use Hi function calcnbhd(tk, mu, sa_tz, g, cone) calcg!(g, cone) sa_tz .+= mu .* g @@ -557,8 +570,8 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau (n, p, q) = (length(c), length(b), length(h)) (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) - calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - H = inv(Hi) + # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) + # H = inv(Hi) # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT @@ -572,7 +585,10 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau # A' = [Q1 Q2] * [R1; 0] # [Q1 Q2]' * G' * mu*H * G * [Q1 Q2] - HG = mu*H*G + HG = zeros(q, n) # TODO prealloc instead of Hi + Hvec = zeros(q) + + calcHarr!(HG, mu*G, cone) GHG = G'*HG K22 = Q2'*GHG*Q2 # TODO use syrk K11 = Q1'*GHG*Q1 @@ -583,7 +599,7 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau # TODO refac systems 1 and 2 # system 1 - (bx, by, Hbz) = (-c, b, mu*H*h) + (bx, by, Hbz) = (-c, b, calcHarr!(Hvec, mu*h, cone)) bxGHbz = bx + G'*Hbz Q1tx = Ri'*by @@ -594,7 +610,7 @@ function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau z1 = HG*x1 - Hbz # system 2 - (bx, by, Hbz) = (rhs_tx, -rhs_ty, -mu*H*rhs_ts - rhs_tz) + (bx, by, Hbz) = (rhs_tx, -rhs_ty, -calcHarr!(Hvec, mu*rhs_ts, cone) - rhs_tz) bxGHbz = bx + G'*Hbz Q1tx = Ri'*by diff --git a/src/primitivecones/nonnegative.jl b/src/primitivecones/nonnegative.jl index f709dfea7..75dc529a4 100644 --- a/src/primitivecones/nonnegative.jl +++ b/src/primitivecones/nonnegative.jl @@ -3,10 +3,12 @@ mutable struct NonnegativeCone <: PrimitiveCone dim::Int pnt::AbstractVector{Float64} + invpnt::Vector{Float64} function NonnegativeCone(dim::Int) prm = new() prm.dim = dim + prm.invpnt = Vector{Float64}(undef, dim) return prm end end @@ -16,5 +18,12 @@ barrierpar_prm(prm::NonnegativeCone) = prm.dim getintdir_prm!(arr::AbstractVector{Float64}, prm::NonnegativeCone) = (arr .= 1.0; arr) loadpnt_prm!(prm::NonnegativeCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) incone_prm(prm::NonnegativeCone) = all(x -> (x > 0.0), prm.pnt) -calcg_prm!(g::AbstractVector{Float64}, prm::NonnegativeCone) = (g .= inv.(prm.pnt) .* -1.0; g) + +function calcg_prm!(g::AbstractVector{Float64}, prm::NonnegativeCone) + prm.invpnt = inv.(prm.pnt) + g .= -1.0 * prm.invpnt + return g +end + +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.invpnt) .* arr; prod) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.pnt) .* arr; prod) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 33e6a3f45..61cc9eb0b 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,37 +1,37 @@ -# -# @testset "large dense lp example (dense A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, use_data=true, dense=true) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# end -# -# @testset "large sparse lp example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, dense=false) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end -# @testset "small dense lp example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end +@testset "large dense lp example (dense A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, use_data=true, dense=true) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +end + +@testset "large sparse lp example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "small dense lp example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end # @testset "1D poly envelope example (dense vs sparse A)" begin # # dense methods @@ -296,7 +296,8 @@ c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(-1.0I, q, n) + # G = -1.0I + G = SparseMatrixCSC(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) @@ -311,7 +312,7 @@ end c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(-1.0I, q, n) + G = SparseMatrixCSC(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) @@ -326,7 +327,7 @@ end c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = Matrix{Float64}(1.0I, q, n) + G = SparseMatrixCSC(-1.0I, q, n) h = G*ones(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=true) From 7ea1b2725457bf8249f83302710b828f42c8964a Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sun, 23 Sep 2018 21:30:58 -0400 Subject: [PATCH 14/30] hessian for SOS cone, SOS examples working --- examples/envelope/envelope.jl | 3 +- examples/namedpoly/namedpoly.jl | 2 +- src/cone.jl | 12 +- src/primitivecones/sumofsquares.jl | 4 +- test/nativeexamples.jl | 272 ++++++++++++++--------------- 5 files changed, 147 insertions(+), 146 deletions(-) diff --git a/examples/envelope/envelope.jl b/examples/envelope/envelope.jl index ba6561e4b..eced2f640 100644 --- a/examples/envelope/envelope.jl +++ b/examples/envelope/envelope.jl @@ -26,11 +26,10 @@ function build_envelope!(alf::Alfonso.AlfonsoOpt, npoly::Int, deg::Int, n::Int, # set up problem data if dense A = repeat(Array(1.0I, U, U), outer=(1, npoly)) - G = Matrix(-1.0I, npoly*U, npoly*U) # TODO uniformscaling? else A = repeat(sparse(1.0I, U, U), outer=(1, npoly)) - G = SparseMatrixCSC(-1.0I, npoly*U, npoly*U) end + G = SparseMatrixCSC(-1.0I, npoly*U, npoly*U) b = w h = zeros(npoly*U) if use_data diff --git a/examples/namedpoly/namedpoly.jl b/examples/namedpoly/namedpoly.jl index b5c28916b..6ef8697d9 100644 --- a/examples/namedpoly/namedpoly.jl +++ b/examples/namedpoly/namedpoly.jl @@ -68,7 +68,7 @@ function build_namedpoly!(alf::Alfonso.AlfonsoOpt, polyname::Symbol, d::Int) A = ones(1, U) b = [1.0,] c = [fn(pts[j,:]...) for j in 1:U] - G = Matrix(-1.0I, U, U) # TODO uniformscaling? + G = SparseMatrixCSC(-1.0I, U, U) # TODO uniformscaling? h = zeros(U) cone = Alfonso.Cone([Alfonso.SumOfSquaresCone(U, [P0, PWts...])], [1:U]) diff --git a/src/cone.jl b/src/cone.jl index 50b075269..1d5e2f3e3 100644 --- a/src/cone.jl +++ b/src/cone.jl @@ -52,18 +52,18 @@ function calcHarr!(prod::AbstractVector{Float64}, arr::AbstractVector{Float64}, return prod end -function calcHiarr!(Hi_mat::AbstractMatrix{Float64}, mat::AbstractMatrix{Float64}, cone::Cone) +function calcHiarr!(prod::AbstractMatrix{Float64}, arr::AbstractMatrix{Float64}, cone::Cone) for k in eachindex(cone.prms) - calcHiarr_prm!(view(Hi_mat, cone.idxs[k], :), view(mat, cone.idxs[k], :), cone.prms[k]) + calcHiarr_prm!(view(prod, cone.idxs[k], :), view(arr, cone.idxs[k], :), cone.prms[k]) end - return Hi_mat + return prod end -function calcHiarr!(Hi_vec::AbstractVector{Float64}, vec::AbstractVector{Float64}, cone::Cone) +function calcHiarr!(prod::AbstractVector{Float64}, arr::AbstractVector{Float64}, cone::Cone) for k in eachindex(cone.prms) - calcHiarr_prm!(view(Hi_vec, cone.idxs[k]), view(vec, cone.idxs[k]), cone.prms[k]) + calcHiarr_prm!(view(prod, cone.idxs[k]), view(arr, cone.idxs[k]), cone.prms[k]) end - return Hi_vec + return prod end # utilities for converting between smat and svec forms (lower triangle) for symmetric matrices diff --git a/src/primitivecones/sumofsquares.jl b/src/primitivecones/sumofsquares.jl index 4b4240e1c..4157bfce9 100644 --- a/src/primitivecones/sumofsquares.jl +++ b/src/primitivecones/sumofsquares.jl @@ -56,7 +56,8 @@ function incone_prm(prm::SumOfSquaresCone) prm.H .+= abs2.(prm.Vp2) end - prm.F = cholesky!(prm.H, check=false) + # TODO copy over H and do cholesky in-place + prm.F = cholesky(prm.H, check=false) if !issuccess(prm.F) return false end @@ -65,3 +66,4 @@ end calcg_prm!(g::AbstractVector{Float64}, prm::SumOfSquaresCone) = (g .= prm.g; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SumOfSquaresCone) = ldiv!(prod, prm.F, arr) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SumOfSquaresCone) = mul!(prod, prm.H, arr) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 61cc9eb0b..f7569cd1b 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,31 +1,66 @@ -@testset "large dense lp example (dense A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, use_data=true, dense=true) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -end +# @testset "large dense lp example (dense A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(alf, 500, 1000, use_data=true, dense=true) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# end +# +# @testset "large sparse lp example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end -@testset "large sparse lp example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# @testset "small dense lp example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end + +@testset "1D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 end -@testset "small dense lp example (dense vs sparse A)" begin +@testset "2D poly envelope example (dense vs sparse A)" begin # dense methods d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + build_envelope!(d_alf, 2, 4, 2, 7, dense=true) @time Alfonso.solve!(d_alf) @test Alfonso.get_status(d_alf) == :Optimal # sparse methods s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + build_envelope!(s_alf, 2, 4, 2, 7, dense=false) @time Alfonso.solve!(s_alf) @test Alfonso.get_status(s_alf) == :Optimal @@ -33,141 +68,106 @@ end @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 end -# @testset "1D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# end +@testset "3D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(alf, 2, 3, 3, 5, dense=false) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "4D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_envelope!(alf, 2, 3, 4, 4, dense=false) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end -# @testset "2D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end -# -# @testset "3D poly envelope example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(alf, 2, 3, 3, 5, dense=false) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end -# -# # @testset "4D poly envelope example (sparse A)" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_envelope!(alf, 2, 3, 4, 4, dense=false) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# # end -# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -# @testset "Butcher" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :butcher, 2) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Caprasse" begin +@testset "Butcher" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :butcher, 2) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +end + +@testset "Caprasse" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :caprasse, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +end + +# @testset "Goldstein-Price" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :caprasse, 4) +# build_namedpoly!(alf, :goldsteinprice, 7) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 # end -# # @testset "Goldstein-Price" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :goldsteinprice, 7) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# # end -# -# # out of memory during interpolation calculations -# # @testset "Heart" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :heart, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Lotka-Volterra" begin +# out of memory during interpolation calculations +# @testset "Heart" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :lotkavolterra, 3) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -# end -# -# # out of memory during interpolation calculations -# # @testset "Magnetism-7" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :magnetism7, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Motzkin" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_namedpoly!(alf, :motzkin, 7) +# build_namedpoly!(alf, :heart, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 # end -# -# @testset "Reaction-diffusion" begin + +@testset "Lotka-Volterra" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :lotkavolterra, 3) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +end + +# out of memory during interpolation calculations +# @testset "Magnetism-7" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :reactiondiffusion, 4) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Robinson" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_namedpoly!(alf, :robinson, 8) +# build_namedpoly!(alf, :magnetism7, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 # end +@testset "Motzkin" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :motzkin, 7) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +end + +@testset "Reaction-diffusion" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :reactiondiffusion, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +end + +@testset "Robinson" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :robinson, 8) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +end + # # tolerances not satisfied # @testset "Rosenbrock" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=20) From a6aaae56cadcd7083f53ee95db5b90b4f3030d69 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 02:26:19 -0400 Subject: [PATCH 15/30] improve initial iterate finding procedure --- Manifest.toml | 4 +- src/nativeinterface.jl | 29 ++++++++----- test/nativeexamples.jl | 99 ++++++++++++++++++++++-------------------- 3 files changed, 70 insertions(+), 62 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index 3edb2addd..bbaac696e 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -78,9 +78,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[MathOptInterface]] deps = ["Compat", "Unicode"] -git-tree-sha1 = "f2791f990529b7aea1fcb9fd45b636cd3a9d1e79" +git-tree-sha1 = "ba12e7ce825c1458c03f88aae84fa630d882d303" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "0.6.0" +version = "0.6.1" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 36548a063..531a219c0 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -252,21 +252,26 @@ function solve!(alf::AlfonsoOpt) loadpnt!(cone, sa_ts) - # calculate initial central primal-dual iterate (S5.3 of V.) + # calculate initial central primal-dual iterate # solve linear equation then step in interior direction of cone until inside cone alf.verbose && println("\nfinding initial iterate") - # TODO use linsys solve function - xyz = Symmetric([zeros(n, n) A' G'; A zeros(p, p) zeros(p, q); G zeros(q, p) I]) \ [-c; b; h] # TODO this is currently different from what CVXOPT does - tx .= xyz[1:n] - ty .= xyz[n+1:n+p] - sa_ts .= -xyz[n+p+1:n+p+q] + (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) + GHG = Matrix(G'*G) # TODO slow if diagonal (varcones) + K22 = Q2'*GHG*Q2 # TODO use syrk + K11 = Q1'*GHG*Q1 + K12 = Q1'*GHG*Q2 + Q1tx = Ri'*b + Q2tx = Symmetric(K22)\(Q2'*G'*h - K12'*Q1tx) + ty .= Ri*(Q1'*G'*h - K11*Q1tx - K12*Q2tx) + tx .= Q1*Q1tx + Q2*Q2tx + sa_ts .= -G*tx + h ts .= sa_ts if !incone(cone) getintdir!(dir_ts, cone) alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small - steps = 1 + steps = 0 while !incone(cone) sa_ts .= ts .+ alpha .* dir_ts alpha *= 1.5 @@ -366,11 +371,11 @@ function solve!(alf::AlfonsoOpt) infres_du = NaN end - if alf.verbose - # print iteration statistics - @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, obj_pr, obj_du, relgap, nres_pr, nres_du, tau, kap, mu) - flush(stdout) - end + # if alf.verbose + # # print iteration statistics + # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, obj_pr, obj_du, relgap, nres_pr, nres_du, tau, kap, mu) + # flush(stdout) + # end # check convergence criteria # TODO nearly primal or dual infeasible or nearly optimal cases? diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index f7569cd1b..a8bd262de 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,37 +1,37 @@ -# @testset "large dense lp example (dense A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, use_data=true, dense=true) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# end -# -# @testset "large sparse lp example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end +@testset "large dense lp example (dense A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, use_data=true, dense=true) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +end -# @testset "small dense lp example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end +@testset "large sparse lp example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "small dense lp example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end @testset "1D poly envelope example (dense vs sparse A)" begin # dense methods @@ -68,21 +68,21 @@ end @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 end -@testset "3D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(alf, 2, 3, 3, 5, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "4D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_envelope!(alf, 2, 3, 4, 4, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end +# @testset "3D poly envelope example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(alf, 2, 3, 3, 5, dense=false) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "4D poly envelope example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_envelope!(alf, 2, 3, 4, 4, dense=false) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html @testset "Butcher" begin @@ -290,8 +290,9 @@ end # @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 # end - +# @testset "small LP 1" begin + Random.seed!(1) (n, p, q) = (30, 12, 30) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) @@ -308,6 +309,7 @@ end @testset "small LP 2" begin + Random.seed!(1) (n, p, q) = (10, 8, 9) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) @@ -323,6 +325,7 @@ end @testset "small LP 3" begin + Random.seed!(1) (n, p, q) = (5, 2, 5) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) From 00312828d81b09e16c2d23247892f475574e293c Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 15:21:19 -0400 Subject: [PATCH 16/30] psd cone hessian and examples working --- src/primitivecones/positivesemidefinite.jl | 27 +- test/nativeexamples.jl | 431 +++++++++++---------- 2 files changed, 238 insertions(+), 220 deletions(-) diff --git a/src/primitivecones/positivesemidefinite.jl b/src/primitivecones/positivesemidefinite.jl index daa92a478..290fa4d45 100644 --- a/src/primitivecones/positivesemidefinite.jl +++ b/src/primitivecones/positivesemidefinite.jl @@ -4,19 +4,19 @@ mutable struct PositiveSemidefiniteCone <: PrimitiveCone dim::Int side::Int pnt::AbstractVector{Float64} - g::Vector{Float64} mat::Matrix{Float64} mat2::Matrix{Float64} matpnt::Matrix{Float64} + matinv::Matrix{Float64} function PositiveSemidefiniteCone(dim::Int) prm = new() prm.dim = dim prm.side = round(Int, sqrt(0.25 + dim + dim) - 0.5) - prm.g = Vector{Float64}(undef, dim) prm.mat = Matrix{Float64}(undef, prm.side, prm.side) prm.mat2 = copy(prm.mat) prm.matpnt = copy(prm.mat) + prm.matinv = copy(prm.mat) return prm end end @@ -33,13 +33,12 @@ function incone_prm(prm::PositiveSemidefiniteCone) return false end - grad = -inv(F) # TODO reduce allocs - mattovec!(prm.g, grad) + prm.matinv = -inv(F) # TODO reduce allocs vectomat!(prm.matpnt, prm.pnt) return true end -calcg_prm!(g::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) = (g .= prm.g; g) +calcg_prm!(g::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) = (mattovec!(g, prm.matinv); g) function calcHiarr_prm!(prod::AbstractVector{Float64}, arr::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) vectomat!(prm.mat, arr) @@ -58,3 +57,21 @@ function calcHiarr_prm!(prod::AbstractMatrix{Float64}, arr::AbstractMatrix{Float end return prod end + +function calcHarr_prm!(prod::AbstractVector{Float64}, arr::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) + vectomat!(prm.mat, arr) + mul!(prm.mat2, prm.mat, prm.matinv) + mul!(prm.mat, prm.matinv, prm.mat2) + mattovec!(prod, prm.mat) + return prod +end + +function calcHarr_prm!(prod::AbstractMatrix{Float64}, arr::AbstractMatrix{Float64}, prm::PositiveSemidefiniteCone) + for j in 1:size(arr, 2) + vectomat!(prm.mat, view(arr, :, j)) + mul!(prm.mat2, prm.mat, prm.matinv) + mul!(prm.mat, prm.matinv, prm.mat2) + mattovec!(view(prod, :, j), prm.mat) + end + return prod +end diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index a8bd262de..9a7643e11 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,172 +1,172 @@ - -@testset "large dense lp example (dense A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, use_data=true, dense=true) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -end - -@testset "large sparse lp example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "small dense lp example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(d_alf, 50, 100, dense=true, tosparse=false) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(s_alf, 50, 100, dense=true, tosparse=true) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "1D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -end - -@testset "2D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 4, 2, 7, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 4, 2, 7, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -# @testset "3D poly envelope example (sparse A)" begin +# +# @testset "large dense lp example (dense A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(alf, 2, 3, 3, 5, dense=false) +# build_lp!(alf, 500, 1000, use_data=true, dense=true) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 # end # -# @testset "4D poly envelope example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_envelope!(alf, 2, 3, 4, 4, dense=false) +# @testset "large sparse lp example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end - -# most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -@testset "Butcher" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :butcher, 2) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -end - -@testset "Caprasse" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :caprasse, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -end - -# @testset "Goldstein-Price" begin +# +# @testset "small dense lp example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "1D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +# end +# +# @testset "2D poly envelope example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end +# +# # @testset "3D poly envelope example (sparse A)" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_envelope!(alf, 2, 3, 3, 5, dense=false) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# # end +# # +# # @testset "4D poly envelope example (sparse A)" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# # build_envelope!(alf, 2, 3, 4, 4, dense=false) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# # end +# +# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html +# @testset "Butcher" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :goldsteinprice, 7) +# build_namedpoly!(alf, :butcher, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 # end - -# out of memory during interpolation calculations -# @testset "Heart" begin +# +# @testset "Caprasse" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :heart, 2) +# build_namedpoly!(alf, :caprasse, 4) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 # end - -@testset "Lotka-Volterra" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :lotkavolterra, 3) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -end - -# out of memory during interpolation calculations -# @testset "Magnetism-7" begin +# +# # @testset "Goldstein-Price" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :goldsteinprice, 7) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# # end +# +# # out of memory during interpolation calculations +# # @testset "Heart" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :heart, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Lotka-Volterra" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :magnetism7, 2) +# build_namedpoly!(alf, :lotkavolterra, 3) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# end +# +# # out of memory during interpolation calculations +# # @testset "Magnetism-7" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # build_namedpoly!(alf, :magnetism7, 2) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# # end +# +# @testset "Motzkin" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_namedpoly!(alf, :motzkin, 7) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Reaction-diffusion" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :reactiondiffusion, 4) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# end +# +# @testset "Robinson" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_namedpoly!(alf, :robinson, 8) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 # end - -@testset "Motzkin" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_namedpoly!(alf, :motzkin, 7) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -end - -@testset "Reaction-diffusion" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_namedpoly!(alf, :reactiondiffusion, 4) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -end - -@testset "Robinson" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_namedpoly!(alf, :robinson, 8) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -end # # tolerances not satisfied # @testset "Rosenbrock" begin @@ -262,79 +262,80 @@ end # @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 # end # -# @testset "small positive semidefinite cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, -1, 0] -# A = Float64[1 0 0; 0 0 1] -# b = Float64[1/2, 1] -# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 -# end -# -# @testset "small positive semidefinite cone problem 2" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 0, 1, 0, 0, 1] -# A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] -# b = Float64[10, 3] -# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 -# end - -# -@testset "small LP 1" begin - Random.seed!(1) - (n, p, q) = (30, 12, 30) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - # G = -1.0I - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) +@testset "small positive semidefinite cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, 0] + A = Float64[1 0 0; 0 0 1] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 end - -@testset "small LP 2" begin - Random.seed!(1) - (n, p, q) = (10, 8, 9) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) +@testset "small positive semidefinite cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 0, 1, 0, 0, 1] + A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] + b = Float64[10, 3] + G = SparseMatrixCSC(-1.0I, 6, 6) + h = zeros(6) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 end - - -@testset "small LP 3" begin - Random.seed!(1) - (n, p, q) = (5, 2, 5) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = G*ones(n) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) -end +# +# @testset "small LP 1" begin +# Random.seed!(1) +# (n, p, q) = (30, 12, 30) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# # G = -1.0I +# G = SparseMatrixCSC(-1.0I, q, n) +# h = zeros(q) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=true) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# end +# +# @testset "small LP 2" begin +# Random.seed!(1) +# (n, p, q) = (10, 8, 9) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# G = SparseMatrixCSC(-1.0I, q, n) +# h = zeros(q) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=true) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# end +# +# @testset "small LP 3" begin +# Random.seed!(1) +# (n, p, q) = (5, 2, 5) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# G = SparseMatrixCSC(-1.0I, q, n) +# h = G*ones(n) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=true) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) +# end From cc7dd69e0533cf8453b857dd0c090c5b7a3495b4 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 17:02:05 -0400 Subject: [PATCH 17/30] SOC/RSOC hessian and examples working --- src/primitivecones/rotatedsecondorder.jl | 42 +++--- src/primitivecones/secondorder.jl | 31 +++-- test/nativeexamples.jl | 155 ++++++++++++----------- 3 files changed, 123 insertions(+), 105 deletions(-) diff --git a/src/primitivecones/rotatedsecondorder.jl b/src/primitivecones/rotatedsecondorder.jl index 34f358143..54d526592 100644 --- a/src/primitivecones/rotatedsecondorder.jl +++ b/src/primitivecones/rotatedsecondorder.jl @@ -3,14 +3,15 @@ mutable struct RotatedSecondOrderCone <: PrimitiveCone dim::Int pnt::AbstractVector{Float64} - g::Vector{Float64} + disth::Float64 Hi::Matrix{Float64} + H::Matrix{Float64} function RotatedSecondOrderCone(dim::Int) prm = new() prm.dim = dim - prm.g = Vector{Float64}(undef, dim) prm.Hi = Matrix{Float64}(undef, dim, dim) + prm.H = copy(prm.Hi) return prm end end @@ -25,27 +26,28 @@ function incone_prm(prm::RotatedSecondOrderCone) if (pnt[1] <= 0) || (pnt[2] <= 0) return false end - nrm2 = 0.5*sum(abs2, pnt[j] for j in 3:prm.dim) - disth = pnt[1]*pnt[2] - nrm2 - if disth > 0.0 - g = prm.g - g .= pnt - g[1] = -pnt[2] - g[2] = -pnt[1] - g ./= disth + prm.disth = pnt[1]*pnt[2] - nrm2 + if prm.disth <= 0.0 + return false + end - Hi = prm.Hi - mul!(Hi, pnt, pnt') - Hi[2,1] = Hi[1,2] = nrm2 - for j in 3:prm.dim - Hi[j,j] += disth - end - return true + mul!(prm.Hi, pnt, pnt') + prm.Hi[2,1] = prm.Hi[1,2] = nrm2 + for j in 3:prm.dim + prm.Hi[j,j] += prm.disth + end + prm.H .= prm.Hi + for j in 3:prm.dim + prm.H[1,j] = prm.H[j,1] = -prm.Hi[2,j] + prm.H[2,j] = prm.H[j,2] = -prm.Hi[1,j] end - return false + prm.H[1,1] = prm.Hi[2,2] + prm.H[2,2] = prm.Hi[1,1] + prm.H .*= inv(prm.disth)^2 + return true end -calcg_prm!(g::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (g .= prm.g; g) -# TODO if later use Linv instead of Hinv, see Vandenberghe coneprog.dvi for analytical Linv +calcg_prm!(g::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (g .= prm.pnt ./ prm.disth; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::RotatedSecondOrderCone) = mul!(prod, prm.Hi, arr) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::RotatedSecondOrderCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/secondorder.jl b/src/primitivecones/secondorder.jl index db6352a06..3f11cbb47 100644 --- a/src/primitivecones/secondorder.jl +++ b/src/primitivecones/secondorder.jl @@ -5,11 +5,13 @@ mutable struct SecondOrderCone <: PrimitiveCone pnt::AbstractVector{Float64} dist::Float64 Hi::Matrix{Float64} + H::Matrix{Float64} function SecondOrderCone(dim::Int) prm = new() prm.dim = dim prm.Hi = Matrix{Float64}(undef, dim, dim) + prm.H = copy(prm.Hi) return prm end end @@ -23,20 +25,25 @@ function incone_prm(prm::SecondOrderCone) if prm.pnt[1] <= 0 return false end - prm.dist = abs2(prm.pnt[1]) - sum(abs2, prm.pnt[j] for j in 2:prm.dim) - if prm.dist > 0.0 - mul!(prm.Hi, prm.pnt, prm.pnt') - prm.Hi .+= prm.Hi - prm.Hi[1,1] -= prm.dist - for j in 2:prm.dim - prm.Hi[j,j] += prm.dist - end - return true + if prm.dist <= 0.0 + return false + end + + mul!(prm.Hi, prm.pnt, prm.pnt') + prm.Hi .+= prm.Hi + prm.Hi[1,1] -= prm.dist + for j in 2:prm.dim + prm.Hi[j,j] += prm.dist + end + prm.H .= prm.Hi + for j in 2:prm.dim + prm.H[1,j] = prm.H[j,1] = -prm.H[j,1] end - return false + prm.H .*= inv(prm.dist)^2 + return true end -calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (g .= inv(prm.dist) .* prm.pnt; g[1] *= -1.0; g) -# TODO if later use Linv instead of Hinv, see Vandenberghe coneprog.dvi for analytical Linv +calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (g .= inv(prm.dist) .* prm.pnt; g[1] = -g[1]; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.Hi, arr) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.H, arr) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 9a7643e11..dde441b79 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -188,20 +188,87 @@ # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end -# @testset "small second-order cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, -1, -1] -# A = Float64[1 0 0; 0 1 0] -# b = Float64[1, 1/sqrt(2)] -# cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_y(alf) ≈ [-sqrt(2), 0] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 -# end +@testset "small second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1, 1/sqrt(2)] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1, -1] + A = Float64[1 0 0 0; 0 1 0 0] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 4, 4) + h = zeros(4) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1/2, 1]/sqrt(2) + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 +end + +@testset "small positive semidefinite cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, 0] + A = Float64[1 0 0; 0 0 1] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 +end + +@testset "small positive semidefinite cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 0, 1, 0, 0, 1] + A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] + b = Float64[10, 3] + G = SparseMatrixCSC(-1.0I, 6, 6) + h = zeros(6) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 +end + # # @testset "small exponential cone problem" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -234,66 +301,8 @@ # @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 # end # -# @testset "small rotated second-order cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, 0, -1, -1] -# A = Float64[1 0 0 0; 0 1 0 0] -# b = Float64[1/2, 1] -# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 -# end -# -# @testset "small rotated second-order cone problem 2" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, 0, -1] -# A = Float64[1 0 0; 0 1 0] -# b = Float64[1/2, 1]/sqrt(2) -# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 -# end -# -@testset "small positive semidefinite cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, 0] - A = Float64[1 0 0; 0 0 1] - b = Float64[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 -end -@testset "small positive semidefinite cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 0, 1, 0, 0, 1] - A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] - b = Float64[10, 3] - G = SparseMatrixCSC(-1.0I, 6, 6) - h = zeros(6) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 -end -# + # @testset "small LP 1" begin # Random.seed!(1) # (n, p, q) = (30, 12, 30) From e8e50c9714bacb93af0d6617711e7b4fbdea9cc9 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 19:16:43 -0400 Subject: [PATCH 18/30] update for all cones, remove power cone for now (needs AD) --- src/Alfonso.jl | 10 +- src/nativeinterface.jl | 12 +- src/primitivecones/exponential.jl | 60 +-- src/primitivecones/nonnegative.jl | 4 +- src/primitivecones/positivesemidefinite.jl | 2 + src/primitivecones/power.jl | 50 +-- src/primitivecones/rotatedsecondorder.jl | 2 + src/primitivecones/secondorder.jl | 4 +- test/nativeexamples.jl | 411 ++++++++++----------- 9 files changed, 294 insertions(+), 261 deletions(-) diff --git a/src/Alfonso.jl b/src/Alfonso.jl index 38c905639..fd70310a5 100644 --- a/src/Alfonso.jl +++ b/src/Alfonso.jl @@ -6,7 +6,15 @@ module Alfonso include("interpolation.jl") include("cone.jl") - for primcone in ["nonnegative", "sumofsquares", "secondorder", "exponential", "power", "rotatedsecondorder", "positivesemidefinite"] + for primcone in [ + "nonnegative", + "sumofsquares", + "secondorder", + "exponential", + # "power", + "rotatedsecondorder", + "positivesemidefinite", + ] include(joinpath(@__DIR__, "primitivecones", primcone * ".jl")) end include("nativeinterface.jl") diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 531a219c0..708fe5a89 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -103,10 +103,10 @@ function AlfonsoOpt(; tolfeas = 1e-7, maxiter = 5e2, predlinesearch = true, - maxpredsmallsteps = 8, - maxcorrsteps = 8, + maxpredsmallsteps = 15, + maxcorrsteps = 15, corrcheck = true, - maxcorrlsiters = 8, + maxcorrlsiters = 15, alphacorr = 1.0, predlsmulti = 0.7, corrlsmulti = 0.5, @@ -119,10 +119,10 @@ function AlfonsoOpt(; error("maxiter must be at least 1") end if maxpredsmallsteps < 1 - error("maxcorrsteps must be at least 1") + error("maxpredsmallsteps must be at least 1") end - if !(1 <= maxcorrsteps <= 8) - error("maxcorrsteps must be from 1 to 8") + if maxcorrsteps < 1 + error("maxcorrsteps must be at least 1") end return AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) diff --git a/src/primitivecones/exponential.jl b/src/primitivecones/exponential.jl index ed9dc7e9f..9d6446fc2 100644 --- a/src/primitivecones/exponential.jl +++ b/src/primitivecones/exponential.jl @@ -6,12 +6,15 @@ mutable struct ExponentialCone <: PrimitiveCone pnt::AbstractVector{Float64} g::AbstractVector{Float64} - Hi::Symmetric{Float64,Array{Float64,2}} # TODO could be faster as StaticArray + H::Matrix{Float64} # TODO could be faster as StaticArray + Hi::Matrix{Float64} + F function ExponentialCone() prm = new() prm.g = Vector{Float64}(undef, 3) - prm.Hi = Symmetric(similar(prm.g, 3, 3)) + prm.H = similar(prm.g, 3, 3) + prm.Hi = copy(prm.H) return prm end end @@ -27,33 +30,46 @@ function incone_prm(prm::ExponentialCone) return false end - ylzy = y*log(z/y) - dist = ylzy - x - if dist <= 0.0 + lzy = log(z/y) + ylzy = y*lzy + ylzyx = ylzy - x + if ylzyx <= 0.0 return false end - invdist = inv(dist) # gradient + iylzyx = inv(ylzyx) g = prm.g - g[1] = invdist # 1/(-x + y log(z/y)) - g[2] = invdist * (y - x - 2*dist) / y # (x + y - 2 y log(z/y))/(y (-x + y log(z/y))) - g[3] = (-1 - y*invdist) / z # (-1 + y/(x - y log(z/y)))/z - - # Hessian inverse - HiU = prm.Hi.data # upper triangle - den = 2*y + dist - invden = inv(den) - # TODO refactor more repeated subexpressions - HiU[1,1] = -(-2*ylzy^3 + (4*x - y)*abs2(ylzy) + (-3*abs2(x) + 2*y*x - 2*abs2(y))*ylzy + x*(abs2(x) - 2*y*x + 2*abs2(y))) * invden # (-2 y^3 log^3(z/y) + (4 x - y) y^2 log^2(z/y) + y (-3 x^2 + 2 y x - 2 y^2) log(z/y) + x (x^2 - 2 y x + 2 y^2))/(x - 2 y - y log(z/y)) - HiU[1,2] = y * (abs2(ylzy) - x*ylzy + x*y) * invden # (y^2 (y log^2(z/y) - x log(z/y) + x))/(-x + 2 y + y log(z/y)) - HiU[1,3] = y * z * (2*ylzy - x) * invden # (y z (2 y log(z/y) - x))/(-x + 2 y + y log(z/y)) - HiU[2,2] = abs2(y) * (1 - y*invden) # (y^2 (-x + y + y log(z/y)))/(-x + 2 y + y log(z/y)) - HiU[2,3] = abs2(y) * z * invden # (y^2 z)/(-x + 2 y + y log(z/y)) - HiU[3,3] = abs2(z) * (1 - y*invden) # (z^2 (-x + y + y log(z/y)))/(-x + 2 y + y log(z/y))) + g[1] = iylzyx # 1/(-x + y log(z/y)) + g[2] = iylzyx * (y - x - 2*ylzyx) / y # (x + y - 2 y log(z/y))/(y (-x + y log(z/y))) + g[3] = (-1 - y*iylzyx) / z # (-1 + y/(x - y log(z/y)))/z + + # Hessian + yz = y/z + H = prm.H + H[1,1] = abs2(iylzyx) + H[1,2] = H[2,1] = -(lzy - 1.0)*H[1,1] + H[1,3] = H[3,1] = -yz*H[1,1] + H[2,2] = abs2(lzy - 1.0)*H[1,1] + iylzyx/y + inv(abs2(y)) + H[2,3] = H[3,2] = yz*(lzy - 1.0)*H[1,1] - iylzyx/z + H[3,3] = abs2(yz)*H[1,1] + yz/z*iylzyx + inv(abs2(z)) + + prm.Hi .= H + prm.F = bunchkaufman!(Symmetric(prm.Hi)) + + # old code for inverse hessian + # den = 2*y + dist + # invden = inv(den) + # Hi[1,1] = -(-2*ylzy^3 + (4*x - y)*abs2(ylzy) + (-3*abs2(x) + 2*y*x - 2*abs2(y))*ylzy + x*(abs2(x) - 2*y*x + 2*abs2(y))) * invden # (-2 y^3 log^3(z/y) + (4 x - y) y^2 log^2(z/y) + y (-3 x^2 + 2 y x - 2 y^2) log(z/y) + x (x^2 - 2 y x + 2 y^2))/(x - 2 y - y log(z/y)) + # Hi[1,2] = y * (abs2(ylzy) - x*ylzy + x*y) * invden # (y^2 (y log^2(z/y) - x log(z/y) + x))/(-x + 2 y + y log(z/y)) + # Hi[1,3] = y * z * (2*ylzy - x) * invden # (y z (2 y log(z/y) - x))/(-x + 2 y + y log(z/y)) + # Hi[2,2] = abs2(y) * (1 - y*invden) # (y^2 (-x + y + y log(z/y)))/(-x + 2 y + y log(z/y)) + # Hi[2,3] = abs2(y) * z * invden # (y^2 z)/(-x + 2 y + y log(z/y)) + # Hi[3,3] = abs2(z) * (1 - y*invden) # (z^2 (-x + y + y log(z/y)))/(-x + 2 y + y log(z/y))) return true end calcg_prm!(g::AbstractVector{Float64}, prm::ExponentialCone) = (g .= prm.g; g) -calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::ExponentialCone) = mul!(prod, prm.Hi, arr) +calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::ExponentialCone) = ldiv!(prod, prm.F, arr) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::ExponentialCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/nonnegative.jl b/src/primitivecones/nonnegative.jl index 75dc529a4..1a3a2fea1 100644 --- a/src/primitivecones/nonnegative.jl +++ b/src/primitivecones/nonnegative.jl @@ -1,5 +1,7 @@ # nonnegative orthant cone +# barrier is -sum_j ln x_j +# from Nesterov & Todd "Self-Scaled Barriers and Interior-Point Methods for Convex Programming" mutable struct NonnegativeCone <: PrimitiveCone dim::Int pnt::AbstractVector{Float64} @@ -25,5 +27,5 @@ function calcg_prm!(g::AbstractVector{Float64}, prm::NonnegativeCone) return g end -calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.invpnt) .* arr; prod) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.pnt) .* arr; prod) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.invpnt) .* arr; prod) diff --git a/src/primitivecones/positivesemidefinite.jl b/src/primitivecones/positivesemidefinite.jl index 290fa4d45..9e1607f3d 100644 --- a/src/primitivecones/positivesemidefinite.jl +++ b/src/primitivecones/positivesemidefinite.jl @@ -1,5 +1,7 @@ # positive semidefinite cone (lower triangle, off-diagonals scaled) +# barrier for matrix cone is -ln det(X) +# from Nesterov & Todd "Self-Scaled Barriers and Interior-Point Methods for Convex Programming" mutable struct PositiveSemidefiniteCone <: PrimitiveCone dim::Int side::Int diff --git a/src/primitivecones/power.jl b/src/primitivecones/power.jl index a4139b304..797080d40 100644 --- a/src/primitivecones/power.jl +++ b/src/primitivecones/power.jl @@ -1,4 +1,8 @@ -* + +# TODO use AD for the barrier function +# TODO should we just do the n-dim power cone? any benefit from 3-d restriction? + + # power cone (MathOptInterface definition) parametrized by power α # x^α * y^(1-α) >= abs(z), x,y >= 0 # barrier from Skajaa & Ye 2014 is @@ -31,36 +35,36 @@ function incone_prm(prm::PowerCone) end α = prm.exponent - lhs = x^α * y^(1-α) - dist = lhs - abs(z) - if dist <= 0.0 + if x^α * y^(1-α) - abs(z) <= 0.0 return false end - # TODO refactor more repeated subexpressions # gradient - g = prm.g - g[1] = (α - (2*α*y^2*x^(2*α))/(y^2*x^(2*α) - z^2*y^(2*α)) - 1)/x # (α - (2 α y^2 x^(2 α))/(y^2 x^(2 α) - z^2 y^(2 α)) - 1)/x - g[2] = ((α - 2)*y^2*x^(2*α) + α*z^2*y^(2*α))/(y*(y^2*x^(2*α) - z^2*y^(2*α))) # ((α - 2) y^2 x^(2 α) + α z^2 y^(2 α))/(y (y^2 x^(2 α) - z^2 y^(2 α))) - g[3] = (2*z)/(x^(2*α)*y^(2 - 2*α) - z^2) # (2 z)/(x^(2 α) y^(2 - 2 α) - z^2) - # Hessian inverse - HiU = prm.Hi.data # upper triangle - HiU[1,1] = (x^2*(2*y^(2*α + 2)*z^2*(2*α^2 - 3*α + 1)*x^(2*α) + y^4*(α - 2)*x^(4*α) + y^(4*α)*z^4*α))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) - # (x^2 (2 y^(2 α + 2) z^2 (2 α^2 - 3 α + 1) x^(2 α) + y^4 (α - 2) x^(4 α) + y^(4 α) z^4 α))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) - HiU[1,2] = (4*x^(2*α + 1)*y^(2*α + 3)*z^2*(α - 1)*α)/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) - # (4 x^(2 α + 1) y^(2 α + 3) z^2 (α - 1) α)/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) - HiU[1,3] = (2*x^(2*α + 1)*y^2*z*α*(y^2*(α - 2)*x^(2*α) + y^(2*α)*z^2*α))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) - # (2 x^(2 α + 1) y^2 z α (y^2 (α - 2) x^(2 α) + y^(2 α) z^2 α))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) - HiU[2,2] = -(-2*y^(2*α + 4)*z^2*α*(2*α - 1)*x^(2*α) + y^6*(α + 1)*x^(4*α) + y^(4*α + 2)*z^4*(α - 1))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) - # -(-2 y^(2 α + 4) z^2 α (2 α - 1) x^(2 α) + y^6 (α + 1) x^(4 α) + y^(4 α + 2) z^4 (α - 1))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) - HiU[2,3] = (2*x^(2*α)*y^3*z*(α - 1)*(y^2*(α + 1)*x^(2*α) + y^(2*α)*z^2*(α - 1)))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) - # (2 x^(2 α) y^3 z (α - 1) (y^2 (α + 1) x^(2 α) + y^(2 α) z^2 (α - 1)))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) - HiU[3,3] = (y^(4*α + 2)*z^4*(11*α^2 - 11*α + 2)*x^(2*α) - 3*y^(2*α + 4)*z^2*(α - 1)*α*x^(4*α) + y^6*(α^2 - α - 2)*x^(6*α) - y^(6*α)*z^6*(α - 1)*α)/(4*y^(4*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + 2*y^(2*α + 4)*(α^2 - α - 2)*x^(4*α) - 2*y^(6*α)*z^4*(α - 1)*α) - # (y^(4 α + 2) z^4 (11 α^2 - 11 α + 2) x^(2 α) - 3 y^(2 α + 4) z^2 (α - 1) α x^(4 α) + y^6 (α^2 - α - 2) x^(6 α) - y^(6 α) z^6 (α - 1) α)/(4 y^(4 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + 2 y^(2 α + 4) (α^2 - α - 2) x^(4 α) - 2 y^(6 α) z^4 (α - 1) α) + # Hessian + + + # old code for gradient and inverse hessian + # g[1] = (α - (2*α*y^2*x^(2*α))/(y^2*x^(2*α) - z^2*y^(2*α)) - 1)/x # (α - (2 α y^2 x^(2 α))/(y^2 x^(2 α) - z^2 y^(2 α)) - 1)/x + # g[2] = ((α - 2)*y^2*x^(2*α) + α*z^2*y^(2*α))/(y*(y^2*x^(2*α) - z^2*y^(2*α))) # ((α - 2) y^2 x^(2 α) + α z^2 y^(2 α))/(y (y^2 x^(2 α) - z^2 y^(2 α))) + # g[3] = (2*z)/(x^(2*α)*y^(2 - 2*α) - z^2) # (2 z)/(x^(2 α) y^(2 - 2 α) - z^2) + + # Hi[1,1] = (x^2*(2*y^(2*α + 2)*z^2*(2*α^2 - 3*α + 1)*x^(2*α) + y^4*(α - 2)*x^(4*α) + y^(4*α)*z^4*α))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) + # # (x^2 (2 y^(2 α + 2) z^2 (2 α^2 - 3 α + 1) x^(2 α) + y^4 (α - 2) x^(4 α) + y^(4 α) z^4 α))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) + # Hi[1,2] = (4*x^(2*α + 1)*y^(2*α + 3)*z^2*(α - 1)*α)/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) + # # (4 x^(2 α + 1) y^(2 α + 3) z^2 (α - 1) α)/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) + # Hi[1,3] = (2*x^(2*α + 1)*y^2*z*α*(y^2*(α - 2)*x^(2*α) + y^(2*α)*z^2*α))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) + # # (2 x^(2 α + 1) y^2 z α (y^2 (α - 2) x^(2 α) + y^(2 α) z^2 α))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) + # Hi[2,2] = -(-2*y^(2*α + 4)*z^2*α*(2*α - 1)*x^(2*α) + y^6*(α + 1)*x^(4*α) + y^(4*α + 2)*z^4*(α - 1))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) + # # -(-2 y^(2 α + 4) z^2 α (2 α - 1) x^(2 α) + y^6 (α + 1) x^(4 α) + y^(4 α + 2) z^4 (α - 1))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) + # Hi[2,3] = (2*x^(2*α)*y^3*z*(α - 1)*(y^2*(α + 1)*x^(2*α) + y^(2*α)*z^2*(α - 1)))/(2*y^(2*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + y^4*(α^2 - α - 2)*x^(4*α) - y^(4*α)*z^4*(α - 1)*α) + # # (2 x^(2 α) y^3 z (α - 1) (y^2 (α + 1) x^(2 α) + y^(2 α) z^2 (α - 1)))/(2 y^(2 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + y^4 (α^2 - α - 2) x^(4 α) - y^(4 α) z^4 (α - 1) α) + # Hi[3,3] = (y^(4*α + 2)*z^4*(11*α^2 - 11*α + 2)*x^(2*α) - 3*y^(2*α + 4)*z^2*(α - 1)*α*x^(4*α) + y^6*(α^2 - α - 2)*x^(6*α) - y^(6*α)*z^6*(α - 1)*α)/(4*y^(4*α + 2)*z^2*(1 - 2*α)^2*x^(2*α) + 2*y^(2*α + 4)*(α^2 - α - 2)*x^(4*α) - 2*y^(6*α)*z^4*(α - 1)*α) + # # (y^(4 α + 2) z^4 (11 α^2 - 11 α + 2) x^(2 α) - 3 y^(2 α + 4) z^2 (α - 1) α x^(4 α) + y^6 (α^2 - α - 2) x^(6 α) - y^(6 α) z^6 (α - 1) α)/(4 y^(4 α + 2) z^2 (1 - 2 α)^2 x^(2 α) + 2 y^(2 α + 4) (α^2 - α - 2) x^(4 α) - 2 y^(6 α) z^4 (α - 1) α) return true end calcg_prm!(g::AbstractVector{Float64}, prm::PowerCone) = (g .= prm.g; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::PowerCone) = mul!(prod, prm.Hi, arr) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::PowerCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/rotatedsecondorder.jl b/src/primitivecones/rotatedsecondorder.jl index 54d526592..43fe724b3 100644 --- a/src/primitivecones/rotatedsecondorder.jl +++ b/src/primitivecones/rotatedsecondorder.jl @@ -1,5 +1,7 @@ # rotated second order cone +# barrier is -ln(2*x*y - norm(z)^2) +# from Nesterov & Todd "Self-Scaled Barriers and Interior-Point Methods for Convex Programming" mutable struct RotatedSecondOrderCone <: PrimitiveCone dim::Int pnt::AbstractVector{Float64} diff --git a/src/primitivecones/secondorder.jl b/src/primitivecones/secondorder.jl index 3f11cbb47..66554f343 100644 --- a/src/primitivecones/secondorder.jl +++ b/src/primitivecones/secondorder.jl @@ -1,5 +1,7 @@ # second order cone +# barrier is -ln(x^2 - norm(y)^2) +# from Nesterov & Todd "Self-Scaled Barriers and Interior-Point Methods for Convex Programming" mutable struct SecondOrderCone <: PrimitiveCone dim::Int pnt::AbstractVector{Float64} @@ -44,6 +46,6 @@ function incone_prm(prm::SecondOrderCone) return true end -calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (g .= inv(prm.dist) .* prm.pnt; g[1] = -g[1]; g) +calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (g .= prm.pnt ./ prm.dist; g[1] = -g[1]; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.Hi, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.H, arr) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index dde441b79..4a0170a99 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,99 +1,99 @@ -# -# @testset "large dense lp example (dense A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, use_data=true, dense=true) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# end -# -# @testset "large sparse lp example (sparse A)" begin + +@testset "large dense lp example (dense A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, use_data=true, dense=true) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +end + +@testset "large sparse lp example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "small dense lp example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end + +@testset "1D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 +end + +@testset "2D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 4, 2, 7, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 4, 2, 7, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end + +# @testset "3D poly envelope example (sparse A)" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) +# build_envelope!(alf, 2, 3, 3, 5, dense=false) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end # -# @testset "small dense lp example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end -# -# @testset "1D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 -# end -# -# @testset "2D poly envelope example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(d_alf, 2, 4, 2, 7, dense=true) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(s_alf, 2, 4, 2, 7, dense=false) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end -# -# # @testset "3D poly envelope example (sparse A)" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_envelope!(alf, 2, 3, 3, 5, dense=false) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# # end -# # -# # @testset "4D poly envelope example (sparse A)" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# # build_envelope!(alf, 2, 3, 4, 4, dense=false) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# # end -# -# # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html -# @testset "Butcher" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :butcher, 2) +# @testset "4D poly envelope example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_envelope!(alf, 2, 3, 4, 4, dense=false) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 # end -# + +# most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html +@testset "Butcher" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :butcher, 2) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 +end + # @testset "Caprasse" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # build_namedpoly!(alf, :caprasse, 4) @@ -102,73 +102,72 @@ # @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 # @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 # end -# -# # @testset "Goldstein-Price" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :goldsteinprice, 7) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# # end -# -# # out of memory during interpolation calculations -# # @testset "Heart" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :heart, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Lotka-Volterra" begin + +# @testset "Goldstein-Price" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :lotkavolterra, 3) +# build_namedpoly!(alf, :goldsteinprice, 7) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 # end -# -# # out of memory during interpolation calculations -# # @testset "Magnetism-7" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # build_namedpoly!(alf, :magnetism7, 2) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 -# # end -# -# @testset "Motzkin" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_namedpoly!(alf, :motzkin, 7) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 -# end -# -# @testset "Reaction-diffusion" begin + +# out of memory during interpolation calculations +# @testset "Heart" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :reactiondiffusion, 4) +# build_namedpoly!(alf, :heart, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -1.36775 atol=1e-4 rtol=1e-4 # end -# -# @testset "Robinson" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_namedpoly!(alf, :robinson, 8) + +@testset "Lotka-Volterra" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :lotkavolterra, 3) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 +end + +# out of memory during interpolation calculations +# @testset "Magnetism-7" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_namedpoly!(alf, :magnetism7, 2) # @time Alfonso.solve!(alf) # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ -0.25 atol=1e-4 rtol=1e-4 # end -# # tolerances not satisfied +@testset "Motzkin" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :motzkin, 7) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 +end + +@testset "Reaction-diffusion" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :reactiondiffusion, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 +end + +@testset "Robinson" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_namedpoly!(alf, :robinson, 8) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +end + # @testset "Rosenbrock" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=20) # build_namedpoly!(alf, :rosenbrock, 3) @@ -178,7 +177,6 @@ # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end # -# # tolerances not satisfied # @testset "Schwefel" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=25) # build_namedpoly!(alf, :schwefel, 4) @@ -269,24 +267,25 @@ end @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 end -# -# @testset "small exponential cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 1, 1] -# A = Float64[0 1 0; 1 0 0] -# b = Float64[2, 1] -# cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test dot(Alfonso.get_y(alf), b) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_y(alf) ≈ [1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_s(alf) ≈ (c - A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 -# end -# +@testset "small exponential cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 1, 1] + A = Float64[0 1 0; 1 0 0] + b = Float64[2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 +end + # @testset "small power cone problem" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) # c = Float64[1, 0, 0, -1, -1, 0] @@ -300,51 +299,49 @@ end # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 # @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 # end -# +@testset "small LP 1" begin + Random.seed!(1) + (n, p, q) = (30, 12, 30) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + # G = -1.0I + G = SparseMatrixCSC(-1.0I, q, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal +end -# @testset "small LP 1" begin -# Random.seed!(1) -# (n, p, q) = (30, 12, 30) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# # G = -1.0I -# G = SparseMatrixCSC(-1.0I, q, n) -# h = zeros(q) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=true) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# end -# -# @testset "small LP 2" begin -# Random.seed!(1) -# (n, p, q) = (10, 8, 9) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# G = SparseMatrixCSC(-1.0I, q, n) -# h = zeros(q) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=true) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# end -# -# @testset "small LP 3" begin -# Random.seed!(1) -# (n, p, q) = (5, 2, 5) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# G = SparseMatrixCSC(-1.0I, q, n) -# h = G*ones(n) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=true) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) -# end +@testset "small LP 2" begin + Random.seed!(1) + (n, p, q) = (10, 8, 9) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small LP 3" begin + Random.seed!(1) + (n, p, q) = (5, 2, 5) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = G*ones(n) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=true) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) +end From 9ac242eb69df85d2ada9607250aff5778bf6dd17 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 19:36:55 -0400 Subject: [PATCH 19/30] cleanup tests and printing a bit --- src/nativeinterface.jl | 22 +++++++++++----------- test/nativeexamples.jl | 32 +++++++++++++++++++++++++++----- test/runtests.jl | 2 +- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 708fe5a89..1cca60869 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -280,8 +280,7 @@ function solve!(alf::AlfonsoOpt) error("cannot find initial iterate") end end - @show alpha - @show steps + alf.verbose && println("$steps steps taken for initial iterate") ts .= sa_ts end @@ -371,11 +370,11 @@ function solve!(alf::AlfonsoOpt) infres_du = NaN end - # if alf.verbose - # # print iteration statistics - # @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, obj_pr, obj_du, relgap, nres_pr, nres_du, tau, kap, mu) - # flush(stdout) - # end + if alf.verbose + # print iteration statistics + @printf("%5d %12.4e %12.4e %9.2e %9.2e %9.2e %9.2e %9.2e %9.2e\n", iter, obj_pr, obj_du, relgap, nres_pr, nres_du, tau, kap, mu) + flush(stdout) + end # check convergence criteria # TODO nearly primal or dual infeasible or nearly optimal cases? @@ -549,10 +548,11 @@ function solve!(alf::AlfonsoOpt) # calculate solution and iteration statistics alf.niters = iter - alf.x = tx ./= tau - alf.s = ts ./= tau - alf.y = ty ./= tau - alf.z = tz ./= tau + invtau = inv(tau) + alf.x = tx .*= invtau + alf.s = ts .*= invtau + alf.y = ty .*= invtau + alf.z = tz .*= invtau alf.tau = tau alf.kap = kap alf.mu = mu diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 4a0170a99..94d589987 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -3,6 +3,7 @@ alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_lp!(alf, 500, 1000, use_data=true, dense=true) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 75 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 @@ -12,6 +13,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 70 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 end @@ -21,12 +23,14 @@ end d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_lp!(d_alf, 50, 100, dense=true, tosparse=false) @time Alfonso.solve!(d_alf) + @test Alfonso.get_niters(d_alf) <= 40 @test Alfonso.get_status(d_alf) == :Optimal # sparse methods s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_lp!(s_alf, 50, 100, dense=true, tosparse=true) @time Alfonso.solve!(s_alf) + @test Alfonso.get_niters(s_alf) <= 40 @test Alfonso.get_status(s_alf) == :Optimal @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 @@ -38,6 +42,7 @@ end d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_envelope!(d_alf, 2, 5, 1, 5, use_data=true, dense=true) @time Alfonso.solve!(d_alf) + @test Alfonso.get_niters(d_alf) <= 30 @test Alfonso.get_status(d_alf) == :Optimal @test Alfonso.get_pobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(d_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 @@ -46,6 +51,7 @@ end s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_envelope!(s_alf, 2, 5, 1, 5, use_data=true, dense=false) @time Alfonso.solve!(s_alf) + @test Alfonso.get_niters(s_alf) <= 30 @test Alfonso.get_status(s_alf) == :Optimal @test Alfonso.get_pobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 @@ -56,12 +62,14 @@ end d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_envelope!(d_alf, 2, 4, 2, 7, dense=true) @time Alfonso.solve!(d_alf) + @test Alfonso.get_niters(d_alf) <= 55 @test Alfonso.get_status(d_alf) == :Optimal # sparse methods s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_envelope!(s_alf, 2, 4, 2, 7, dense=false) @time Alfonso.solve!(s_alf) + @test Alfonso.get_niters(s_alf) <= 55 @test Alfonso.get_status(s_alf) == :Optimal @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 @@ -89,6 +97,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_namedpoly!(alf, :butcher, 2) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 35 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @@ -126,6 +135,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_namedpoly!(alf, :lotkavolterra, 3) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 35 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ -20.8 atol=1e-4 rtol=1e-4 @@ -145,6 +155,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) build_namedpoly!(alf, :motzkin, 7) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 35 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-4 rtol=1e-4 @@ -154,6 +165,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_namedpoly!(alf, :reactiondiffusion, 4) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 35 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 @@ -163,6 +175,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) build_namedpoly!(alf, :robinson, 8) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 35 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 @@ -196,6 +209,7 @@ end cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -213,6 +227,7 @@ end cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -229,6 +244,7 @@ end cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -245,6 +261,7 @@ end cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -261,6 +278,7 @@ end cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -277,6 +295,7 @@ end cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 @@ -310,24 +329,26 @@ end G = SparseMatrixCSC(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 25 @test Alfonso.get_status(alf) == :Optimal end @testset "small LP 2" begin Random.seed!(1) - (n, p, q) = (10, 8, 9) + (n, p, q) = (10, 8, 10) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) G = SparseMatrixCSC(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal end @@ -340,8 +361,9 @@ end G = SparseMatrixCSC(-1.0I, q, n) h = G*ones(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=true) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) in (:DualInfeasible, :Optimal) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal end diff --git a/test/runtests.jl b/test/runtests.jl index 2351e4a31..e18934a2c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Alfonso using Test -verbflag = true # Alfonso verbose option +verbflag = false # Alfonso verbose option # TODO interpolation tests From d853972083b74405195df561da3a1e4bfcd03ed1 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 24 Sep 2018 19:54:07 -0400 Subject: [PATCH 20/30] compute appropriate certificates --- src/nativeinterface.jl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 1cca60869..c84571169 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -381,18 +381,41 @@ function solve!(alf::AlfonsoOpt) if nres_pr <= alf.tolfeas && nres_du <= alf.tolfeas && (gap <= alf.tolabsopt || (!isnan(relgap) && relgap <= alf.tolrelopt)) alf.verbose && println("optimal solution found; terminating") alf.status = :Optimal + + invtau = inv(tau) + alf.x = tx .*= invtau + alf.s = ts .*= invtau + alf.y = ty .*= invtau + alf.z = tz .*= invtau break elseif !isnan(infres_pr) && infres_pr <= alf.tolfeas alf.verbose && println("primal infeasibility detected; terminating") alf.status = :PrimalInfeasible + + invobj = inv(-by - hz) + alf.x = tx .= NaN + alf.s = ts .= NaN + alf.y = ty .*= invobj + alf.z = tz .*= invobj break elseif !isnan(infres_du) && infres_du <= alf.tolfeas alf.verbose && println("dual infeasibility detected; terminating") alf.status = :DualInfeasible + + invobj = inv(-cx) + alf.x = tx .*= invobj + alf.s = ts .*= invobj + alf.y = ty .= NaN + alf.z = tz .= NaN break elseif mu <= alf.tolfeas*1e-2 && tau <= alf.tolfeas*1e-2*min(1.0, kap) alf.verbose && println("ill-posedness detected; terminating") alf.status = :IllPosed + + alf.x = tx .= NaN + alf.s = ts .= NaN + alf.y = ty .= NaN + alf.z = tz .= NaN break end @@ -548,11 +571,6 @@ function solve!(alf::AlfonsoOpt) # calculate solution and iteration statistics alf.niters = iter - invtau = inv(tau) - alf.x = tx .*= invtau - alf.s = ts .*= invtau - alf.y = ty .*= invtau - alf.z = tz .*= invtau alf.tau = tau alf.kap = kap alf.mu = mu From d6f3d36a96df1316b502aac5656172c71538a04c Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 01:59:30 -0400 Subject: [PATCH 21/30] refactored and improved operations in linear system solves, clarified some comments --- src/nativeinterface.jl | 177 +++++++++++++++++++---------------------- test/nativeexamples.jl | 100 +++++++++++------------ test/runtests.jl | 2 + 3 files changed, 132 insertions(+), 147 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index c84571169..43196b832 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -40,16 +40,17 @@ mutable struct AlfonsoOpt h::Vector{Float64} # cone constraint vector, size q conK::Cone # primal constraint cone object - Q1 # QR factorization of A' - Q2 - Ri + # QR factorization of A' matrix + Q1::Matrix{Float64} + Q2::Matrix{Float64} + Ri::AbstractMatrix{Float64} # results status::Symbol # solver status solvetime::Float64 # total solve time niters::Int # total number of iterations - x::Vector{Float64} # final value of the primal variables + x::Vector{Float64} # final value of the primal free variables s::Vector{Float64} # final value of the primal cone variables y::Vector{Float64} # final value of the dual free variables z::Vector{Float64} # final value of the dual cone variables @@ -144,7 +145,6 @@ get_mu(alf::AlfonsoOpt) = alf.mu get_pobj(alf::AlfonsoOpt) = dot(alf.c, alf.x) get_dobj(alf::AlfonsoOpt) = -dot(alf.b, alf.y) - dot(alf.h, alf.z) - # load and verify problem data, calculate algorithmic parameters function load_data!( alf::AlfonsoOpt, @@ -154,7 +154,7 @@ function load_data!( G::AbstractMatrix{Float64}, h::Vector{Float64}, conK::Cone; - check=false, # check rank conditions + check::Bool=false, # check rank conditions ) # check data consistency @@ -175,40 +175,38 @@ function load_data!( # perform QR decomposition of A' for use in linear system solves # TODO only use for factorization-based linear system solves - # TODO reduce allocs, time + # TODO reduce allocs, improve efficiency # A' = [Q1 Q2] * [R1; 0] if issparse(A) + alf.verbose && println("\nJulia is currently missing some sparse matrix methods that could improve performance; Alfonso may perform better if A is loaded as a dense matrix") + # TODO currently using dense Q1, Q2, R - probably some should be sparse F = qr(sparse(A')) + @assert length(F.prow) == n + @assert length(F.pcol) == p + @assert istriu(F.R) + Q = F.Q*Matrix(1.0I, n, n) Q1 = zeros(n, p) - Q2 = zeros(n, n-p) - Ri = zeros(p, p) - - Q = F.Q*Matrix(1.0I, n, n) # TODO should this be sparse? Q1[F.prow, F.pcol] = Q[:, 1:p] + Q2 = zeros(n, n-p) Q2[F.prow, :] = Q[:, p+1:n] - - @assert istriu(F.R) + Ri = zeros(p, p) Ri[F.pcol, F.pcol] = inv(UpperTriangular(F.R)) else F = qr(A') + @assert istriu(F.R) Q = F.Q*Matrix(1.0I, n, n) - Q1 = Q[:,1:p] - Q2 = Q[:,p+1:n] - - @assert norm(A' - Q1*F.R) < 1e-10 # TODO delete later - @assert istriu(F.R) + Q1 = Q[:, 1:p] + Q2 = Q[:, p+1:n] Ri = inv(UpperTriangular(F.R)) + @assert norm(A'*Ri - Q1) < 1e-8 # TODO delete later end - # TODO rank for qr decomp should be implemented in Julia - see https://github.com/JuliaLang/julia/blob/f8b52dab77415a22d28497f48407aca92fbbd4c3/stdlib/LinearAlgebra/src/qr.jl#L895 if check # check rank conditions - if issparse(A) || issparse(G) - error("rank cannot currently be determined for sparse A or G") - end - if rank(A) < p # TODO change to rank(At_qr) + # TODO rank for qr decomp should be implemented in Julia - see https://github.com/JuliaLang/julia/blob/f8b52dab77415a22d28497f48407aca92fbbd4c3/stdlib/LinearAlgebra/src/qr.jl#L895 + if rank(A) < p # TODO change to rank(F) error("A matrix is not full-row-rank; some primal equalities may be redundant or inconsistent") end if rank(vcat(A, G)) < n @@ -240,40 +238,44 @@ function solve!(alf::AlfonsoOpt) bnu = 1.0 + barrierpar(cone) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) # preallocate arrays + # primal and dual variables multiplied by tau tx = similar(c) ts = similar(h) ty = similar(b) tz = similar(ts) - sa_ts = similar(ts) - sa_tz = similar(tz) - dir_ts = similar(ts) + # gradient evaluations at ls_ts of the barrier function for K g = similar(ts) - Hi = zeros(q, q) + # search directions + dir_ts = similar(ts) + # values during line searches + ls_ts = similar(ts) + ls_tz = similar(tz) - loadpnt!(cone, sa_ts) + # cone functions evaluate barrier derivatives at ls_ts + loadpnt!(cone, ls_ts) # calculate initial central primal-dual iterate - # solve linear equation then step in interior direction of cone until inside cone alf.verbose && println("\nfinding initial iterate") + # solve linear equation then step in interior direction of cone until inside cone + # |0 A' G'| * |tx| = |0| + # |A 0 0 | |ty| |b| + # |G 0 -I| |ts| |h| (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) - GHG = Matrix(G'*G) # TODO slow if diagonal (varcones) - K22 = Q2'*GHG*Q2 # TODO use syrk - K11 = Q1'*GHG*Q1 - K12 = Q1'*GHG*Q2 - Q1tx = Ri'*b - Q2tx = Symmetric(K22)\(Q2'*G'*h - K12'*Q1tx) - ty .= Ri*(Q1'*G'*h - K11*Q1tx - K12*Q2tx) - tx .= Q1*Q1tx + Q2*Q2tx - sa_ts .= -G*tx + h - ts .= sa_ts + GQ2 = G*Q2 + Q1x = Q1*Ri'*b + Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) + tx .= Q1x .+ Q2x + ts .= h - G*tx + ty .= Ri*Q1'*G'*ts + ls_ts .= ts if !incone(cone) getintdir!(dir_ts, cone) - alpha = 1.0 # TODO starting alpha maybe should depend on sa_ts (eg norm like in Alfonso) in case 1.0 is too large/small + alpha = 1.0 # TODO starting alpha maybe should depend on ls_ts (eg norm like in Alfonso) in case 1.0 is too large/small steps = 0 while !incone(cone) - sa_ts .= ts .+ alpha .* dir_ts + ls_ts .= ts .+ alpha .* dir_ts alpha *= 1.5 steps += 1 if steps > 25 @@ -281,7 +283,7 @@ function solve!(alf::AlfonsoOpt) end end alf.verbose && println("$steps steps taken for initial iterate") - ts .= sa_ts + ts .= ls_ts end @assert incone(cone) # TODO delete @@ -429,8 +431,8 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction - sa_ts .= ts - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, Hi, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) + ls_ts .= ts + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) # determine step length alpha by line search alpha = alphapred @@ -441,19 +443,19 @@ function solve!(alf::AlfonsoOpt) while true nprediters += 1 - sa_ts .= ts .+ alpha .* dir_ts + ls_ts .= ts .+ alpha .* dir_ts # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood if incone(cone) # primal iterate is inside the cone - sa_tz .= tz .+ alpha .* dir_tz - sa_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) - sa_mu = (dot(sa_ts, sa_tz) + sa_tk)/bnu - nbhd = calcnbhd(sa_tk, sa_mu, sa_tz, g, cone) + ls_tz .= tz .+ alpha .* dir_tz + ls_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) + ls_mu = (dot(ls_ts, ls_tz) + ls_tk)/bnu + nbhd = calcnbhd(ls_tk, ls_mu, ls_tz, g, cone) - if nbhd < abs2(beta*sa_mu) + if nbhd < abs2(beta*ls_mu) # iterate is inside the beta-neighborhood if !alphaprevok || (alpha > alf.predlsmulti) # either the previous iterate was outside the beta-neighborhood or increasing alpha again will make it > 1 @@ -491,7 +493,7 @@ function solve!(alf::AlfonsoOpt) tx .+= alpha .* dir_tx ty .+= alpha .* dir_ty tz .+= alpha .* dir_tz - ts .= sa_ts + ts .= ls_ts tau += alpha*dir_tau kap += alpha*dir_kap mu = (dot(ts, tz) + tau*kap)/bnu @@ -508,7 +510,7 @@ function solve!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, Hi, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) + (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) # determine step length alpha by line search alpha = alf.alphacorr @@ -516,7 +518,7 @@ function solve!(alf::AlfonsoOpt) while ncorrlsiters <= alf.maxcorrlsiters ncorrlsiters += 1 - sa_ts .= ts .+ alpha .* dir_ts + ls_ts .= ts .+ alpha .* dir_ts if incone(cone) # primal iterate tx is inside the cone, so terminate line search break @@ -541,15 +543,15 @@ function solve!(alf::AlfonsoOpt) tx .+= alpha .* dir_tx ty .+= alpha .* dir_ty tz .+= alpha .* dir_tz - ts .= sa_ts + ts .= ls_ts tau += alpha*dir_tau kap += alpha*dir_kap mu = (dot(ts, tz) + tau*kap)/bnu # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck - sa_tz .= tz - nbhd = calcnbhd(tau*kap, mu, sa_tz, g, cone) + ls_tz .= tz + nbhd = calcnbhd(tau*kap, mu, ls_tz, g, cone) # @show sqrt(nbhd)/mu if nbhd <= abs2(eta*mu) break @@ -579,69 +581,50 @@ function solve!(alf::AlfonsoOpt) return nothing end -# TODO put this inside the cone functions and ideally don't use Hi -function calcnbhd(tk, mu, sa_tz, g, cone) +# TODO put this inside the cone functions +function calcnbhd(tk, mu, ls_tz, g, cone) calcg!(g, cone) - sa_tz .+= mu .* g - calcHiarr!(g, sa_tz, cone) - return (tk - mu)^2 + dot(sa_tz, g) + ls_tz .+= mu .* g + calcHiarr!(g, ls_tz, cone) + return (tk - mu)^2 + dot(ls_tz, g) end -function finddirection(alf, Hi, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) +function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) cone = alf.conK (n, p, q) = (length(c), length(b), length(h)) (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) - # calcHiarr!(Hi, Matrix(1.0I, q, q), cone) - # H = inv(Hi) - # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT # (1) eliminate equality constraints via QR of A' # (2) solve reduced system by cholesky - # |0 A' G' | * |ux| = |bx| # |A 0 0 | |uy| |by| # |G 0 -Hi/mu| |uz| |bz| - # A' = [Q1 Q2] * [R1; 0] - # [Q1 Q2]' * G' * mu*H * G * [Q1 Q2] - HG = zeros(q, n) # TODO prealloc instead of Hi + HG = zeros(q, n) # TODO prealloc Hvec = zeros(q) - - calcHarr!(HG, mu*G, cone) + calcHarr!(HG, G, cone) + HG .*= mu GHG = G'*HG - K22 = Q2'*GHG*Q2 # TODO use syrk - K11 = Q1'*GHG*Q1 - K12 = Q1'*GHG*Q2 # factorize K22 = Q2' * G' * mu*H * G * Q2 - K22_F = bunchkaufman!(Symmetric(K22)) # TODO cholesky vs bunch-kaufman? - - # TODO refac systems 1 and 2 - # system 1 - (bx, by, Hbz) = (-c, b, calcHarr!(Hvec, mu*h, cone)) - - bxGHbz = bx + G'*Hbz - Q1tx = Ri'*by - Q2tx = K22_F\(Q2'*bxGHbz - K12'*Q1tx) - - y1 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) - x1 = Q1*Q1tx + Q2*Q2tx - z1 = HG*x1 - Hbz - - # system 2 - (bx, by, Hbz) = (rhs_tx, -rhs_ty, -calcHarr!(Hvec, mu*rhs_ts, cone) - rhs_tz) - - bxGHbz = bx + G'*Hbz - Q1tx = Ri'*by - Q2tx = K22_F\(Q2'*bxGHbz - K12'*Q1tx) + K22_F = bunchkaufman!(Symmetric(Q2'*GHG*Q2)) # TODO cholesky vs bunch-kaufman? + + function calcxyz(bx, by, Hbz) + bxGHbz = bx + G'*Hbz + Q1x = Q1*Ri'*by + Q2x = Q2*(K22_F\(Q2'*(bxGHbz - GHG*Q1x))) + xi = Q1x + Q2x + yi = Ri*Q1'*(bxGHbz - GHG*xi) + zi = HG*xi - Hbz + return (xi, yi, zi) + end - y2 = Ri*(Q1'*bxGHbz - K11*Q1tx - K12*Q2tx) - x2 = Q1*Q1tx + Q2*Q2tx - z2 = HG*x2 - Hbz + (x1, y1, z1) = calcxyz(-c, b, calcHarr!(Hvec, mu*h, cone)) + (x2, y2, z2) = calcxyz(rhs_tx, -rhs_ty, -calcHarr!(Hvec, mu*rhs_ts, cone) - rhs_tz) # TODO in correction, rhs_ts is zeros # combine dir_tau = ((rhs_tau + rhs_kap) + dot(c, x2) + dot(b, y2) + dot(h, z2))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 94d589987..5f7218fd2 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,4 +1,53 @@ +@testset "small LP 1" begin + Random.seed!(1) + (n, p, q) = (30, 12, 30) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + # G = -1.0I + G = SparseMatrixCSC(-1.0I, q, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 25 + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small LP 2" begin + Random.seed!(1) + (n, p, q) = (10, 8, 10) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small LP 3" begin + Random.seed!(1) + (n, p, q) = (5, 2, 5) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = G*ones(n) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal +end + @testset "large dense lp example (dense A)" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_lp!(alf, 500, 1000, use_data=true, dense=true) @@ -97,7 +146,7 @@ end alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_namedpoly!(alf, :butcher, 2) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 35 + # @test Alfonso.get_niters(alf) <= 40 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @@ -318,52 +367,3 @@ end # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 # @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 # end - -@testset "small LP 1" begin - Random.seed!(1) - (n, p, q) = (30, 12, 30) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - # G = -1.0I - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 25 - @test Alfonso.get_status(alf) == :Optimal -end - -@testset "small LP 2" begin - Random.seed!(1) - (n, p, q) = (10, 8, 10) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal -end - -@testset "small LP 3" begin - Random.seed!(1) - (n, p, q) = (5, 2, 5) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = G*ones(n) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal -end diff --git a/test/runtests.jl b/test/runtests.jl index e18934a2c..9f658f650 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -32,3 +32,5 @@ include(joinpath(@__DIR__, "nativeexamples.jl")) # @testset "Continuous linear problems" begin # MOIT.contlineartest(MOIB.SplitInterval{Float64}(optimizer), config) # end + +return nothing From 0bb4445aae7f2e65d9a90280623eb8a8294d95a2 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 17:06:55 -0400 Subject: [PATCH 22/30] reduced allocations drastically, more to go --- Manifest.toml | 10 +- src/nativeinterface.jl | 254 +++++++++++++++++++++++++++-------------- 2 files changed, 174 insertions(+), 90 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index bbaac696e..15e819625 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -21,15 +21,15 @@ version = "0.7.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "ae262fa91da6a74e8937add6b613f58cd56cdad4" +git-tree-sha1 = "ff2595695fc4f14427358ce2593f867085c45dcb" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "1.1.0" +version = "1.2.0" [[Conda]] deps = ["Compat", "JSON", "VersionParsing"] -git-tree-sha1 = "a47f9a2c7b80095e6a935536795635522fe27f5d" +git-tree-sha1 = "85b5bf3ffcf4f39abe019dab1dd00a0aead8d882" uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" -version = "1.0.1" +version = "1.0.2" [[Dates]] deps = ["Printf"] @@ -149,4 +149,4 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" deps = ["Compat"] git-tree-sha1 = "c9d5aa108588b978bd859554660c8a5c4f2f7669" uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" -version = "1.1.2" +version = "1.1.3" diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 43196b832..7e2c42aba 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -1,20 +1,73 @@ + +mutable struct LinSysCache + Q1 + Q2 + Ri + HG + GHG + GHGQ2 + Q2GHGQ2 + bxGHbz + Riby + Q1x + rhs + Q2div + Q2x + GHGxi + Q1yirhs + HGxi + x1 + y1 + z1 + + function LinSysCache(Q1::Matrix{Float64}, Q2::Matrix{Float64}, Ri::AbstractMatrix{Float64}, n::Int, p::Int, q::Int) + L = new() + nmp = n - p + @assert size(Q1) == (n, p) + @assert size(Q2) == (n, nmp) + @assert size(Ri) == (p, p) + L.Q1 = Q1 + L.Q2 = Q2 + L.Ri = Ri + L.HG = Matrix{Float64}(undef, q, n) + L.GHG = Matrix{Float64}(undef, n, n) + L.GHGQ2 = Matrix{Float64}(undef, n, nmp) + L.Q2GHGQ2 = Matrix{Float64}(undef, nmp, nmp) + L.bxGHbz = Vector{Float64}(undef, n) + L.Riby = Vector{Float64}(undef, p) + L.Q1x = Vector{Float64}(undef, n) + L.rhs = Vector{Float64}(undef, n) + L.Q2div = Vector{Float64}(undef, nmp) + L.Q2x = Vector{Float64}(undef, n) + L.GHGxi = Vector{Float64}(undef, n) + L.Q1yirhs = Vector{Float64}(undef, p) + L.HGxi = Vector{Float64}(undef, q) + L.x1 = Vector{Float64}(undef, n) + L.y1 = Vector{Float64}(undef, p) + L.z1 = Vector{Float64}(undef, q) + return L + end +end + """ solves a pair of primal and dual cone programs - minimize c'*x - subject to G*x + s = h - A*x = b - s in K - maximize -h'*z - b'*y - subject to G'*z + A'*y + c = 0 - z in K* -K is a convex cone defined as a Cartesian product of recognized primitive cones, and K* is its dual cone + primal (over x,s): + min c'x : duals + b - Ax == 0 (y) + h - Gx == s in K (z) + dual (over z,y): + max -b'y - h'z : duals + c + A'y + G'z == 0 (x) + z in K* (s) +where K is a convex cone defined as a Cartesian product of recognized primitive cones, and K* is its dual cone the primal-dual optimality conditions are - G*x + s = h, A*x = b - G'*z + A'*y + c = 0 - s in K, z in K* - s'*z = 0 - + b - Ax == 0 + h - Gx == s + c + A'y + G'z == 0 + s'z == 0 + s in K + z in K* """ mutable struct AlfonsoOpt # options @@ -38,12 +91,10 @@ mutable struct AlfonsoOpt b::Vector{Float64} # equality constraint vector, size p G::AbstractMatrix{Float64} # cone constraint matrix, size q*n h::Vector{Float64} # cone constraint vector, size q - conK::Cone # primal constraint cone object + cone::Cone # primal constraint cone object + + L::LinSysCache # cache for direction finding functions - # QR factorization of A' matrix - Q1::Matrix{Float64} - Q2::Matrix{Float64} - Ri::AbstractMatrix{Float64} # results status::Symbol # solver status @@ -54,25 +105,12 @@ mutable struct AlfonsoOpt s::Vector{Float64} # final value of the primal cone variables y::Vector{Float64} # final value of the dual free variables z::Vector{Float64} # final value of the dual cone variables - tau::Float64 # final value of the tau variable kap::Float64 # final value of the kappa variable mu::Float64 # final value of mu - pobj::Float64 # final primal objective value dobj::Float64 # final dual objective value - dgap::Float64 # final duality gap - cgap::Float64 # final complementarity gap - rel_dgap::Float64 # final relative duality gap - rel_cgap::Float64 # final relative complementarity gap - pres::Vector{Float64} # final primal residuals - dres::Vector{Float64} # final dual residuals - pin::Float64 # final primal infeasibility - din::Float64 # final dual infeasibility - rel_pin::Float64 # final relative primal infeasibility - rel_din::Float64 # final relative dual infeasibility - # TODO match natural order of options listed above function AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) alf = new() @@ -153,7 +191,7 @@ function load_data!( b::Vector{Float64}, G::AbstractMatrix{Float64}, h::Vector{Float64}, - conK::Cone; + cone::Cone; check::Bool=false, # check rank conditions ) @@ -219,10 +257,8 @@ function load_data!( alf.b = b alf.G = G alf.h = h - alf.conK = conK - alf.Q1 = Q1 - alf.Q2 = Q2 - alf.Ri = Ri + alf.cone = cone + alf.L = LinSysCache(Q1, Q2, Ri, n, p, q) alf.status = :Loaded return alf @@ -232,8 +268,7 @@ end function solve!(alf::AlfonsoOpt) starttime = time() - (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) - cone = alf.conK + (c, A, b, G, h, cone) = (alf.c, alf.A, alf.b, alf.G, alf.h, alf.cone) (n, p, q) = (length(c), length(b), length(h)) bnu = 1.0 + barrierpar(cone) # complexity parameter nu-bar of the augmented barrier (sum of the primitive cone barrier parameters plus 1) @@ -246,6 +281,9 @@ function solve!(alf::AlfonsoOpt) # gradient evaluations at ls_ts of the barrier function for K g = similar(ts) # search directions + dir_tx = similar(tx) + dir_ty = similar(ty) + dir_tz = similar(tz) dir_ts = similar(ts) # values during line searches ls_ts = similar(ts) @@ -261,7 +299,9 @@ function solve!(alf::AlfonsoOpt) # |A 0 0 | |ty| |b| # |G 0 -I| |ts| |h| - (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) + L = alf.L + (Q1, Q2, Ri) = (L.Q1, L.Q2, L.Ri) + GQ2 = G*Q2 Q1x = Q1*Ri'*b Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) @@ -431,8 +471,11 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction - ls_ts .= ts - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, res_tx, res_ty, -tz, -kap, res_tz, res_tau, mu, tau) + @. dir_tx = res_tx + @. dir_ty = res_ty + @. dir_tz = -tz + @. dir_ts = res_tz + (dir_kap, dir_tau) = finddirection!(dir_tx, dir_ty, dir_tz, dir_ts, -kap, res_tau, mu, tau, alf) # determine step length alpha by line search alpha = alphapred @@ -443,14 +486,14 @@ function solve!(alf::AlfonsoOpt) while true nprediters += 1 - ls_ts .= ts .+ alpha .* dir_ts + @. ls_ts = ts + alpha*dir_ts # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood if incone(cone) # primal iterate is inside the cone - ls_tz .= tz .+ alpha .* dir_tz + @. ls_tz = tz + alpha*dir_tz ls_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) ls_mu = (dot(ls_ts, ls_tz) + ls_tk)/bnu nbhd = calcnbhd(ls_tk, ls_mu, ls_tz, g, cone) @@ -490,10 +533,10 @@ function solve!(alf::AlfonsoOpt) end # step distance alpha in the direction - tx .+= alpha .* dir_tx - ty .+= alpha .* dir_ty - tz .+= alpha .* dir_tz - ts .= ls_ts + @. tx += alpha*dir_tx + @. ty += alpha*dir_ty + @. tz += alpha*dir_tz + @. ts = ls_ts tau += alpha*dir_tau kap += alpha*dir_kap mu = (dot(ts, tz) + tau*kap)/bnu @@ -510,7 +553,12 @@ function solve!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) = finddirection(alf, zeros(n), zeros(p), -(tz + mu*calcg!(g, cone)), -(kap - mu/tau), zeros(q), 0.0, mu, tau) + @. dir_tx = 0.0 + @. dir_ty = 0.0 + calcg!(g, cone) + @. dir_tz = -tz - mu*g + @. dir_ts = 0.0 + (dir_kap, dir_tau) = finddirection!(dir_tx, dir_ty, dir_tz, dir_ts, -kap + mu/tau, 0.0, mu, tau, alf) # determine step length alpha by line search alpha = alf.alphacorr @@ -518,7 +566,7 @@ function solve!(alf::AlfonsoOpt) while ncorrlsiters <= alf.maxcorrlsiters ncorrlsiters += 1 - ls_ts .= ts .+ alpha .* dir_ts + @. ls_ts = ts + alpha*dir_ts if incone(cone) # primal iterate tx is inside the cone, so terminate line search break @@ -540,17 +588,17 @@ function solve!(alf::AlfonsoOpt) end # step distance alpha in the direction - tx .+= alpha .* dir_tx - ty .+= alpha .* dir_ty - tz .+= alpha .* dir_tz - ts .= ls_ts + @. tx += alpha*dir_tx + @. ty += alpha*dir_ty + @. tz += alpha*dir_tz + @. ts = ls_ts tau += alpha*dir_tau kap += alpha*dir_kap mu = (dot(ts, tz) + tau*kap)/bnu # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck - ls_tz .= tz + @. ls_tz = tz nbhd = calcnbhd(tau*kap, mu, ls_tz, g, cone) # @show sqrt(nbhd)/mu if nbhd <= abs2(eta*mu) @@ -584,16 +632,16 @@ end # TODO put this inside the cone functions function calcnbhd(tk, mu, ls_tz, g, cone) calcg!(g, cone) - ls_tz .+= mu .* g + @. ls_tz += mu*g calcHiarr!(g, ls_tz, cone) return (tk - mu)^2 + dot(ls_tz, g) end -function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu, tau) - (c, A, b, G, h) = (alf.c, alf.A, alf.b, alf.G, alf.h) - cone = alf.conK +function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, tau, alf) + (c, A, b, G, h, cone) = (alf.c, alf.A, alf.b, alf.G, alf.h, alf.cone) (n, p, q) = (length(c), length(b), length(h)) - (Q1, Q2, Ri) = (alf.Q1, alf.Q2, alf.Ri) + L = alf.L + (Q1, Q2, Ri, HG, GHG, GHGQ2, Q2GHGQ2, x1, y1, z1) = (L.Q1, L.Q2, L.Ri, L.HG, L.GHG, L.GHGQ2, L.Q2GHGQ2, L.x1, L.y1, L.z1) # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT @@ -604,37 +652,73 @@ function finddirection(alf, rhs_tx, rhs_ty, rhs_tz, rhs_kap, rhs_ts, rhs_tau, mu # |G 0 -Hi/mu| |uz| |bz| # A' = [Q1 Q2] * [R1; 0] - HG = zeros(q, n) # TODO prealloc - Hvec = zeros(q) + # factorize Q2' * G' * mu*H * G * Q2 calcHarr!(HG, G, cone) - HG .*= mu - GHG = G'*HG - - # factorize K22 = Q2' * G' * mu*H * G * Q2 - K22_F = bunchkaufman!(Symmetric(Q2'*GHG*Q2)) # TODO cholesky vs bunch-kaufman? - - function calcxyz(bx, by, Hbz) - bxGHbz = bx + G'*Hbz - Q1x = Q1*Ri'*by - Q2x = Q2*(K22_F\(Q2'*(bxGHbz - GHG*Q1x))) - xi = Q1x + Q2x - yi = Ri*Q1'*(bxGHbz - GHG*xi) - zi = HG*xi - Hbz - return (xi, yi, zi) + @. HG *= mu + mul!(GHG, G', HG) + mul!(GHGQ2, GHG, Q2) + mul!(Q2GHGQ2, Q2', GHGQ2) + + # TODO cholesky vs bunch-kaufman? + F = bunchkaufman!(Symmetric(Q2GHGQ2)) + # F = cholesky!(Symmetric(Q2GHGQ2)) + + # (x2, y2, z2) = (rhs_tx, -rhs_ty, -mu*H*rhs_ts - rhs_tz) + @. rhs_ty *= -1.0 + @. rhs_tz *= -1.0 + if !iszero(rhs_ts) + calcHarr!(z1, rhs_ts, cone) + @. rhs_tz -= mu*z1 end + calcxyz!(rhs_tx, rhs_ty, rhs_tz, F, alf) - (x1, y1, z1) = calcxyz(-c, b, calcHarr!(Hvec, mu*h, cone)) - (x2, y2, z2) = calcxyz(rhs_tx, -rhs_ty, -calcHarr!(Hvec, mu*rhs_ts, cone) - rhs_tz) # TODO in correction, rhs_ts is zeros + # (x1, y1, z1) = (-c, b, mu*H*h) + @. x1 = -c + @. y1 = b + calcHarr!(z1, h, cone) + @. z1 *= mu + calcxyz!(x1, y1, z1, F, alf) # combine - dir_tau = ((rhs_tau + rhs_kap) + dot(c, x2) + dot(b, y2) + dot(h, z2))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) - dir_tx = x2 + dir_tau * x1 - dir_ty = y2 + dir_tau * y1 - dir_tz = z2 + dir_tau * z1 - dir_ts = -G*dir_tx + h*dir_tau - rhs_ts - dir_kap = -dot(c, dir_tx) - dot(b, dir_ty) - dot(h, dir_tz) - rhs_tau - - return (dir_tx, dir_ty, dir_tz, dir_kap, dir_ts, dir_tau) + dir_tau = (rhs_tau + rhs_kap + dot(c, rhs_tx) + dot(b, rhs_ty) + dot(h, rhs_tz))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) + @. rhs_tx += dir_tau*x1 + @. rhs_ty += dir_tau*y1 + @. rhs_tz += dir_tau*z1 + mul!(z1, G, rhs_tx) + @. rhs_ts = -z1 + h*dir_tau - rhs_ts + dir_kap = -dot(c, rhs_tx) - dot(b, rhs_ty) - dot(h, rhs_tz) - rhs_tau + + return (dir_kap, dir_tau) +end + +function calcxyz!(xi, yi, zi, F, alf) + L = alf.L + (Q1, Q2, Ri, HG, GHG, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, GHGxi, Q1yirhs, HGxi) = (L.Q1, L.Q2, L.Ri, L.HG, L.GHG, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.GHGxi, L.Q1yirhs, L.HGxi) + + # bxGHbz = bx + G'*Hbz + mul!(bxGHbz, alf.G', zi) + @. bxGHbz += xi + # Q1x = Q1*Ri'*by + mul!(Riby, Ri', yi) + mul!(Q1x, Q1, Riby) + # Q2x = Q2*(K22_F\(Q2'*(bxGHbz - GHG*Q1x))) + mul!(rhs, GHG, Q1x) + @. rhs = bxGHbz - rhs + mul!(Q2div, Q2', rhs) + ldiv!(F, Q2div) + mul!(Q2x, Q2, Q2div) + # xi = Q1x + Q2x + @. xi = Q1x + Q2x + # yi = Ri*Q1'*(bxGHbz - GHG*xi) + mul!(GHGxi, GHG, xi) + @. bxGHbz -= GHGxi + mul!(Q1yirhs, Q1', bxGHbz) + mul!(yi, Ri, Q1yirhs) + # zi = HG*xi - Hbz + mul!(HGxi, HG, xi) + @. zi = HGxi - zi + + return (xi, yi, zi) end function getbetaeta(maxcorrsteps, bnu) From afa85ccca7ce68cad0ff2fbf13921088c100c737 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 18:35:24 -0400 Subject: [PATCH 23/30] refac initial iterate, reduce allocs more --- src/cone.jl | 6 +- src/nativeinterface.jl | 136 +++++++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/cone.jl b/src/cone.jl index 1d5e2f3e3..8d941d266 100644 --- a/src/cone.jl +++ b/src/cone.jl @@ -14,11 +14,11 @@ end # calculate complexity parameter of the barrier (sum of the primitive cone barrier parameters) barrierpar(cone::Cone) = sum(barrierpar_prm(prm) for prm in cone.prms) -function getintdir!(a::Vector{Float64}, cone::Cone) +function getintdir!(dir::Vector{Float64}, cone::Cone) for k in eachindex(cone.prms) - getintdir_prm!(view(a, cone.idxs[k]), cone.prms[k]) + getintdir_prm!(view(dir, cone.idxs[k]), cone.prms[k]) end - return a + return dir end # TODO can parallelize the functions acting on Cone diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 7e2c42aba..f8c24be8d 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -275,9 +275,9 @@ function solve!(alf::AlfonsoOpt) # preallocate arrays # primal and dual variables multiplied by tau tx = similar(c) - ts = similar(h) ty = similar(b) - tz = similar(ts) + tz = similar(h) + ts = similar(h) # gradient evaluations at ls_ts of the barrier function for K g = similar(ts) # search directions @@ -286,59 +286,14 @@ function solve!(alf::AlfonsoOpt) dir_tz = similar(tz) dir_ts = similar(ts) # values during line searches - ls_ts = similar(ts) ls_tz = similar(tz) + ls_ts = similar(ts) # cone functions evaluate barrier derivatives at ls_ts loadpnt!(cone, ls_ts) - # calculate initial central primal-dual iterate - alf.verbose && println("\nfinding initial iterate") - # solve linear equation then step in interior direction of cone until inside cone - # |0 A' G'| * |tx| = |0| - # |A 0 0 | |ty| |b| - # |G 0 -I| |ts| |h| - - L = alf.L - (Q1, Q2, Ri) = (L.Q1, L.Q2, L.Ri) - - GQ2 = G*Q2 - Q1x = Q1*Ri'*b - Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) - tx .= Q1x .+ Q2x - ts .= h - G*tx - ty .= Ri*Q1'*G'*ts - ls_ts .= ts - - if !incone(cone) - getintdir!(dir_ts, cone) - alpha = 1.0 # TODO starting alpha maybe should depend on ls_ts (eg norm like in Alfonso) in case 1.0 is too large/small - steps = 0 - while !incone(cone) - ls_ts .= ts .+ alpha .* dir_ts - alpha *= 1.5 - steps += 1 - if steps > 25 - error("cannot find initial iterate") - end - end - alf.verbose && println("$steps steps taken for initial iterate") - ts .= ls_ts - end - - @assert incone(cone) # TODO delete - calcg!(tz, cone) - tz .*= -1.0 - - tau = 1.0 - kap = 1.0 - mu = (dot(tz, ts) + tau*kap)/bnu - - # TODO delete later - @assert abs(1.0 - mu) < 1e-8 - @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 - - alf.verbose && println("initial iterate found") + # find initial primal-dual iterate + (tau, kap, mu) = findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # calculate tolerances for convergence tol_res_tx = inv(max(1.0, norm(c))) @@ -600,7 +555,6 @@ function solve!(alf::AlfonsoOpt) if (ncorrsteps == alf.maxcorrsteps) || alf.corrcheck @. ls_tz = tz nbhd = calcnbhd(tau*kap, mu, ls_tz, g, cone) - # @show sqrt(nbhd)/mu if nbhd <= abs2(eta*mu) break elseif ncorrsteps == alf.maxcorrsteps @@ -637,6 +591,86 @@ function calcnbhd(tk, mu, ls_tz, g, cone) return (tk - mu)^2 + dot(ls_tz, g) end +# calculate initial central primal-dual iterate +function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) + (b, G, h, cone) = (alf.b, alf.G, alf.h, alf.cone) + L = alf.L + (Q1, Q2, Ri, Q2GHGQ2, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, Q1yirhs) = (L.Q1, L.Q2, L.Ri, L.Q2GHGQ2, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.Q1yirhs) + + alf.verbose && println("\nfinding initial iterate") + + # solve linear equation + # |0 A' G'| * |tx| = |0| + # |A 0 0 | |ty| |b| + # |G 0 -I| |ts| |h| + + # GQ2 = G*Q2 + # Q1x = Q1*Ri'*b + # Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) + # tx = Q1x .+ Q2x + # ts = h - G*tx + # ty = Ri*Q1'*G'*ts + + GQ2 = G*Q2 # TODO not prealloced + mul!(Q2GHGQ2, GQ2', GQ2) + F = bunchkaufman!(Symmetric(Q2GHGQ2)) + + # Q1x = Q1*Ri'*b + mul!(Riby, Ri', b) + mul!(Q1x, Q1, Riby) + # Q2x = Q2*(F\(GQ2'*(h - G*Q1x))) + mul!(rhs, G, Q1x) + @. rhs = h - rhs + mul!(Q2div, GQ2', rhs) + ldiv!(F, Q2div) + mul!(Q2x, Q2, Q2div) + # tx = Q1x + Q2x + @. tx = Q1x + Q2x + # ts = h - G*tx + mul!(ts, G, tx) + @. ts = h - ts + # ty = Ri*Q1'*G'*ts + mul!(bxGHbz, G', ts) + mul!(Q1yirhs, Q1', bxGHbz) + mul!(ty, Ri, Q1yirhs) + + # from ts, step along interior direction of cone until ts is inside cone + @. ls_ts = ts + if !incone(cone) + dir_ts = getintdir!(rhs, cone) + alpha = 1.0 # TODO starting alpha maybe should depend on ls_ts (eg norm like in Alfonso) in case 1.0 is too large/small + steps = 0 + while !incone(cone) + @. ls_ts = ts + alpha*dir_ts + alpha *= 1.5 + steps += 1 + if steps > 25 + error("cannot find initial iterate") + end + end + alf.verbose && println("$steps steps taken for initial iterate") + @. ts = ls_ts + end + + @assert incone(cone) # TODO delete + calcg!(tz, cone) + @. tz *= -1.0 + + tau = 1.0 + kap = 1.0 + mu = (dot(tz, ts) + tau*kap)/bnu + + # TODO delete later + @assert abs(1.0 - mu) < 1e-8 + @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 + + alf.verbose && println("initial iterate found") + + return (tau, kap, mu) +end + + + function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, tau, alf) (c, A, b, G, h, cone) = (alf.c, alf.A, alf.b, alf.G, alf.h, alf.cone) (n, p, q) = (length(c), length(b), length(h)) From 667ceb82c6eee8e3e04e6087de88f98a925c7f78 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 19:44:52 -0400 Subject: [PATCH 24/30] further reduce allocations, rename some variables to improve clarity --- src/nativeinterface.jl | 132 ++++++++++++++++++++--------------------- test/nativeexamples.jl | 3 +- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index f8c24be8d..f80ce4b18 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -135,6 +135,7 @@ mutable struct AlfonsoOpt end end +# initialize a model object function AlfonsoOpt(; verbose = false, tolrelopt = 1e-6, @@ -183,7 +184,7 @@ get_mu(alf::AlfonsoOpt) = alf.mu get_pobj(alf::AlfonsoOpt) = dot(alf.c, alf.x) get_dobj(alf::AlfonsoOpt) = -dot(alf.b, alf.y) - dot(alf.h, alf.z) -# load and verify problem data, calculate algorithmic parameters +# verify problem data and load into model object function load_data!( alf::AlfonsoOpt, c::Vector{Float64}, @@ -264,7 +265,7 @@ function load_data!( return alf end -# solve using homogeneous self-dual embedding +# solve using predictor-corrector algorithm based on homogeneous self-dual embedding function solve!(alf::AlfonsoOpt) starttime = time() @@ -278,19 +279,19 @@ function solve!(alf::AlfonsoOpt) ty = similar(b) tz = similar(h) ts = similar(h) - # gradient evaluations at ls_ts of the barrier function for K - g = similar(ts) - # search directions - dir_tx = similar(tx) - dir_ty = similar(ty) - dir_tz = similar(tz) - dir_ts = similar(ts) # values during line searches ls_tz = similar(tz) ls_ts = similar(ts) - # cone functions evaluate barrier derivatives at ls_ts loadpnt!(cone, ls_ts) + # gradient evaluations at ls_ts of the barrier function for K + g = similar(ts) + # helper arrays for residuals, right-hand-sides, and search directions + tmp_tx = similar(tx) + tmp_tx2 = similar(tx) + tmp_ty = similar(ty) + tmp_tz = similar(tz) + tmp_ts = similar(ts) # find initial primal-dual iterate (tau, kap, mu) = findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) @@ -301,7 +302,6 @@ function solve!(alf::AlfonsoOpt) tol_res_tz = inv(max(1.0, norm(h))) # calculate prediction and correction step parameters - # TODO put in prediction and correction step cache functions (beta, eta, cpredfix) = getbetaeta(alf.maxcorrsteps, bnu) # beta: large neighborhood parameter, eta: small neighborhood parameter alphapredfix = cpredfix/(eta + sqrt(2*eta^2 + bnu)) # fixed predictor step size alphapredthres = (alf.predlsmulti^alf.maxpredsmallsteps)*alphapredfix # minimum predictor step size @@ -318,30 +318,33 @@ function solve!(alf::AlfonsoOpt) alphapred = alphapredinit iter = 0 while true - # calculate residuals - # TODO in-place - res_x = -A'*ty - G'*tz - nres_x = norm(res_x) - res_tx = res_x - c*tau - nres_tx = norm(res_tx)/tau - - res_y = A*tx - nres_y = norm(res_y) - res_ty = res_y - b*tau - nres_ty = norm(res_ty)/tau - - res_z = ts + G*tx - nres_z = norm(res_z) - res_tz = res_z - h*tau - nres_tz = norm(res_tz)/tau + # calculate residuals and convergence parameters + invtau = inv(tau) + + # tmp_tx = -A'*ty - G'*tz - c*tau + mul!(tmp_tx2, A', ty) + mul!(tmp_tx, G', tz) + @. tmp_tx = -tmp_tx2 - tmp_tx + nres_x = norm(tmp_tx) + @. tmp_tx -= c*tau + nres_tx = norm(tmp_tx)*invtau + + # tmp_ty = A*tx - b*tau + mul!(tmp_ty, A, tx) + nres_y = norm(tmp_ty) + @. tmp_ty -= b*tau + nres_ty = norm(tmp_ty)*invtau + + # tmp_tz = ts + G*tx - h*tau + mul!(tmp_tz, G, tx) + @. tmp_tz += ts + nres_z = norm(tmp_tz) + @. tmp_tz -= h*tau + nres_tz = norm(tmp_tz)*invtau (cx, by, hz) = (dot(c, tx), dot(b, ty), dot(h, tz)) - - res_tau = kap + cx + by + hz - - obj_pr = cx/tau - obj_du = -(by + hz)/tau - + obj_pr = cx*invtau + obj_du = -(by + hz)*invtau gap = dot(tz, ts) # TODO is this right? maybe should adapt original alfonso conditions # TODO maybe add small epsilon to denominators that are zero to avoid NaNs, and get rid of isnans further down @@ -379,7 +382,6 @@ function solve!(alf::AlfonsoOpt) alf.verbose && println("optimal solution found; terminating") alf.status = :Optimal - invtau = inv(tau) alf.x = tx .*= invtau alf.s = ts .*= invtau alf.y = ty .*= invtau @@ -426,11 +428,9 @@ function solve!(alf::AlfonsoOpt) # prediction phase # calculate prediction direction - @. dir_tx = res_tx - @. dir_ty = res_ty - @. dir_tz = -tz - @. dir_ts = res_tz - (dir_kap, dir_tau) = finddirection!(dir_tx, dir_ty, dir_tz, dir_ts, -kap, res_tau, mu, tau, alf) + @. tmp_ts = tmp_tz + @. tmp_tz = -tz + (tmp_kap, tmp_tau) = finddirection!(tmp_tx, tmp_ty, tmp_tz, tmp_ts, -kap, kap + cx + by + hz, mu, tau, alf) # determine step length alpha by line search alpha = alphapred @@ -441,15 +441,15 @@ function solve!(alf::AlfonsoOpt) while true nprediters += 1 - @. ls_ts = ts + alpha*dir_ts + @. ls_ts = ts + alpha*tmp_ts # accept primal iterate if # - decreased alpha and it is the first inside the cone and beta-neighborhood or # - increased alpha and it is inside the cone and the first to leave beta-neighborhood if incone(cone) # primal iterate is inside the cone - @. ls_tz = tz + alpha*dir_tz - ls_tk = (tau + alpha*dir_tau)*(kap + alpha*dir_kap) + @. ls_tz = tz + alpha*tmp_tz + ls_tk = (tau + alpha*tmp_tau)*(kap + alpha*tmp_kap) ls_mu = (dot(ls_ts, ls_tz) + ls_tk)/bnu nbhd = calcnbhd(ls_tk, ls_mu, ls_tz, g, cone) @@ -488,12 +488,12 @@ function solve!(alf::AlfonsoOpt) end # step distance alpha in the direction - @. tx += alpha*dir_tx - @. ty += alpha*dir_ty - @. tz += alpha*dir_tz + @. tx += alpha*tmp_tx + @. ty += alpha*tmp_ty + @. tz += alpha*tmp_tz @. ts = ls_ts - tau += alpha*dir_tau - kap += alpha*dir_kap + tau += alpha*tmp_tau + kap += alpha*tmp_kap mu = (dot(ts, tz) + tau*kap)/bnu # skip correction phase if allowed and current iterate is in the eta-neighborhood @@ -508,12 +508,12 @@ function solve!(alf::AlfonsoOpt) ncorrsteps += 1 # calculate correction direction - @. dir_tx = 0.0 - @. dir_ty = 0.0 + @. tmp_tx = 0.0 + @. tmp_ty = 0.0 calcg!(g, cone) - @. dir_tz = -tz - mu*g - @. dir_ts = 0.0 - (dir_kap, dir_tau) = finddirection!(dir_tx, dir_ty, dir_tz, dir_ts, -kap + mu/tau, 0.0, mu, tau, alf) + @. tmp_tz = -tz - mu*g + @. tmp_ts = 0.0 + (tmp_kap, tmp_tau) = finddirection!(tmp_tx, tmp_ty, tmp_tz, tmp_ts, -kap + mu/tau, 0.0, mu, tau, alf) # determine step length alpha by line search alpha = alf.alphacorr @@ -521,7 +521,7 @@ function solve!(alf::AlfonsoOpt) while ncorrlsiters <= alf.maxcorrlsiters ncorrlsiters += 1 - @. ls_ts = ts + alpha*dir_ts + @. ls_ts = ts + alpha*tmp_ts if incone(cone) # primal iterate tx is inside the cone, so terminate line search break @@ -543,12 +543,12 @@ function solve!(alf::AlfonsoOpt) end # step distance alpha in the direction - @. tx += alpha*dir_tx - @. ty += alpha*dir_ty - @. tz += alpha*dir_tz + @. tx += alpha*tmp_tx + @. ty += alpha*tmp_ty + @. tz += alpha*tmp_tz @. ts = ls_ts - tau += alpha*dir_tau - kap += alpha*dir_kap + tau += alpha*tmp_tau + kap += alpha*tmp_kap mu = (dot(ts, tz) + tau*kap)/bnu # finish if allowed and current iterate is in the eta-neighborhood, or if taken max steps @@ -607,7 +607,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # GQ2 = G*Q2 # Q1x = Q1*Ri'*b # Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) - # tx = Q1x .+ Q2x + # tx = Q1x + Q2x # ts = h - G*tx # ty = Ri*Q1'*G'*ts @@ -637,11 +637,11 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # from ts, step along interior direction of cone until ts is inside cone @. ls_ts = ts if !incone(cone) - dir_ts = getintdir!(rhs, cone) + tmp_ts = getintdir!(rhs, cone) alpha = 1.0 # TODO starting alpha maybe should depend on ls_ts (eg norm like in Alfonso) in case 1.0 is too large/small steps = 0 while !incone(cone) - @. ls_ts = ts + alpha*dir_ts + @. ls_ts = ts + alpha*tmp_ts alpha *= 1.5 steps += 1 if steps > 25 @@ -669,13 +669,11 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) return (tau, kap, mu) end - - +# calculate new prediction or correction direction given rhs of KKT linear system function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, tau, alf) - (c, A, b, G, h, cone) = (alf.c, alf.A, alf.b, alf.G, alf.h, alf.cone) - (n, p, q) = (length(c), length(b), length(h)) + (c, b, G, h, cone) = (alf.c, alf.b, alf.G, alf.h, alf.cone) L = alf.L - (Q1, Q2, Ri, HG, GHG, GHGQ2, Q2GHGQ2, x1, y1, z1) = (L.Q1, L.Q2, L.Ri, L.HG, L.GHG, L.GHGQ2, L.Q2GHGQ2, L.x1, L.y1, L.z1) + (Q2, HG, GHG, GHGQ2, Q2GHGQ2, x1, y1, z1) = (L.Q2, L.HG, L.GHG, L.GHGQ2, L.Q2GHGQ2, L.x1, L.y1, L.z1) # solve two symmetric systems and combine the solutions # use QR + cholesky method from CVXOPT @@ -725,6 +723,7 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta return (dir_kap, dir_tau) end +# calculate solution to reduced symmetric linear systems function calcxyz!(xi, yi, zi, F, alf) L = alf.L (Q1, Q2, Ri, HG, GHG, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, GHGxi, Q1yirhs, HGxi) = (L.Q1, L.Q2, L.Ri, L.HG, L.GHG, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.GHGxi, L.Q1yirhs, L.HGxi) @@ -755,6 +754,7 @@ function calcxyz!(xi, yi, zi, F, alf) return (xi, yi, zi) end +# get neighborhood parameters depending on magnitude of barrier parameter and maximum number of correction steps function getbetaeta(maxcorrsteps, bnu) if maxcorrsteps <= 2 if bnu < 10.0 diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 5f7218fd2..86fa80024 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -6,7 +6,8 @@ A = rand(-9.0:9.0, p, n) b = A*ones(n) # G = -1.0I - G = SparseMatrixCSC(-1.0I, q, n) + @assert n == q + G = Diagonal(-1.0I, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=verbflag) From b9784120609d2b6780f2bac301f1ed79d34ecaac Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 20:31:52 -0400 Subject: [PATCH 25/30] reduced allocs during iteration to essentially zero --- src/nativeinterface.jl | 20 +++++++++++++------- src/primitivecones/nonnegative.jl | 10 +++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index f80ce4b18..6b5436ba3 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -448,6 +448,7 @@ function solve!(alf::AlfonsoOpt) # - increased alpha and it is inside the cone and the first to leave beta-neighborhood if incone(cone) # primal iterate is inside the cone + @. ls_tz = tz + alpha*tmp_tz ls_tk = (tau + alpha*tmp_tau)*(kap + alpha*tmp_kap) ls_mu = (dot(ls_ts, ls_tz) + ls_tk)/bnu @@ -611,9 +612,10 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # ts = h - G*tx # ty = Ri*Q1'*G'*ts - GQ2 = G*Q2 # TODO not prealloced + GQ2 = G*Q2 # TODO not prealloced; all allocs in this function are from here mul!(Q2GHGQ2, GQ2', GQ2) - F = bunchkaufman!(Symmetric(Q2GHGQ2)) + # F = bunchkaufman!(Symmetric(Q2GHGQ2)) + F = cholesky!(Symmetric(Q2GHGQ2)) # Q1x = Q1*Ri'*b mul!(Riby, Ri', b) @@ -662,8 +664,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # TODO delete later @assert abs(1.0 - mu) < 1e-8 - @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 - + # @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 alf.verbose && println("initial iterate found") return (tau, kap, mu) @@ -691,9 +692,14 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta mul!(GHGQ2, GHG, Q2) mul!(Q2GHGQ2, Q2', GHGQ2) - # TODO cholesky vs bunch-kaufman? - F = bunchkaufman!(Symmetric(Q2GHGQ2)) - # F = cholesky!(Symmetric(Q2GHGQ2)) + # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite + # TODO does it matter that F could be either type? + F = cholesky!(Symmetric(Q2GHGQ2), check=false) + if !issuccess(F) + alf.verbose && println("linear system matrix was nearly not positive definite") + mul!(Q2GHGQ2, Q2', GHGQ2) + F = bunchkaufman!(Symmetric(Q2GHGQ2)) + end # (x2, y2, z2) = (rhs_tx, -rhs_ty, -mu*H*rhs_ts - rhs_tz) @. rhs_ty *= -1.0 diff --git a/src/primitivecones/nonnegative.jl b/src/primitivecones/nonnegative.jl index 1a3a2fea1..6f0e5edd0 100644 --- a/src/primitivecones/nonnegative.jl +++ b/src/primitivecones/nonnegative.jl @@ -17,15 +17,15 @@ end dimension(prm::NonnegativeCone) = prm.dim barrierpar_prm(prm::NonnegativeCone) = prm.dim -getintdir_prm!(arr::AbstractVector{Float64}, prm::NonnegativeCone) = (arr .= 1.0; arr) +getintdir_prm!(arr::AbstractVector{Float64}, prm::NonnegativeCone) = (@. arr = 1.0; arr) loadpnt_prm!(prm::NonnegativeCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) incone_prm(prm::NonnegativeCone) = all(x -> (x > 0.0), prm.pnt) function calcg_prm!(g::AbstractVector{Float64}, prm::NonnegativeCone) - prm.invpnt = inv.(prm.pnt) - g .= -1.0 * prm.invpnt + @. prm.invpnt = inv(prm.pnt) + @. g = -prm.invpnt return g end -calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.pnt) .* arr; prod) -calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (prod .= abs2.(prm.invpnt) .* arr; prod) +calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (@. prod = abs2(prm.pnt)*arr; prod) +calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::NonnegativeCone) = (@. prod = abs2(prm.invpnt)*arr; prod) From 579eb6d3f61bf58d18426a65779c1ff3b1ec32e4 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 20:45:09 -0400 Subject: [PATCH 26/30] reduce allocs for SOS cone barrier functions --- src/nativeinterface.jl | 6 +- src/primitivecones/sumofsquares.jl | 20 +- test/nativeexamples.jl | 402 ++++++++++++++--------------- 3 files changed, 216 insertions(+), 212 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 6b5436ba3..eae5b266e 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -3,6 +3,7 @@ mutable struct LinSysCache Q1 Q2 Ri + GQ2 HG GHG GHGQ2 @@ -29,6 +30,7 @@ mutable struct LinSysCache L.Q1 = Q1 L.Q2 = Q2 L.Ri = Ri + L.GQ2 = Matrix{Float64}(undef, q, nmp) L.HG = Matrix{Float64}(undef, q, n) L.GHG = Matrix{Float64}(undef, n, n) L.GHGQ2 = Matrix{Float64}(undef, n, nmp) @@ -596,7 +598,7 @@ end function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) (b, G, h, cone) = (alf.b, alf.G, alf.h, alf.cone) L = alf.L - (Q1, Q2, Ri, Q2GHGQ2, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, Q1yirhs) = (L.Q1, L.Q2, L.Ri, L.Q2GHGQ2, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.Q1yirhs) + (Q1, Q2, Ri, GQ2, Q2GHGQ2, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, Q1yirhs) = (L.Q1, L.Q2, L.Ri, L.GQ2, L.Q2GHGQ2, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.Q1yirhs) alf.verbose && println("\nfinding initial iterate") @@ -612,7 +614,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # ts = h - G*tx # ty = Ri*Q1'*G'*ts - GQ2 = G*Q2 # TODO not prealloced; all allocs in this function are from here + mul!(GQ2, G, Q2) mul!(Q2GHGQ2, GQ2', GQ2) # F = bunchkaufman!(Symmetric(Q2GHGQ2)) F = cholesky!(Symmetric(Q2GHGQ2)) diff --git a/src/primitivecones/sumofsquares.jl b/src/primitivecones/sumofsquares.jl index 4157bfce9..a465bbf36 100644 --- a/src/primitivecones/sumofsquares.jl +++ b/src/primitivecones/sumofsquares.jl @@ -6,6 +6,7 @@ mutable struct SumOfSquaresCone <: PrimitiveCone pnt::AbstractVector{Float64} g::Vector{Float64} H::Matrix{Float64} + Hchol::Matrix{Float64} F::Cholesky{Float64,Array{Float64,2}} ipwtpnt::Vector{Matrix{Float64}} Vp::Vector{Matrix{Float64}} @@ -20,6 +21,7 @@ mutable struct SumOfSquaresCone <: PrimitiveCone prm.ipwt = ipwt prm.g = similar(ipwt[1], dim) prm.H = similar(ipwt[1], dim, dim) + prm.Hchol = copy(prm.H) prm.ipwtpnt = [similar(ipwt[1], size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] prm.Vp = [similar(ipwt[1], dim, size(ipwtj, 2)) for ipwtj in ipwt] prm.Vp2 = similar(ipwt[1], dim, dim) @@ -29,12 +31,12 @@ end dimension(prm::SumOfSquaresCone) = prm.dim barrierpar_prm(prm::SumOfSquaresCone) = sum(size(ipwtj, 2) for ipwtj in prm.ipwt) -getintdir_prm!(arr::AbstractVector{Float64}, prm::SumOfSquaresCone) = (arr .= 1.0; arr) +getintdir_prm!(arr::AbstractVector{Float64}, prm::SumOfSquaresCone) = (@. arr = 1.0; arr) loadpnt_prm!(prm::SumOfSquaresCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) function incone_prm(prm::SumOfSquaresCone) - prm.g .= 0.0 - prm.H .= 0.0 + @. prm.g = 0.0 + @. prm.H = 0.0 for j in eachindex(prm.ipwt) # TODO can do this loop in parallel (use separate Vp2[j]) # prm.ipwtpnt[j] = prm.ipwt[j]'*Diagonal(prm.pnt)*prm.ipwt[j] @@ -46,24 +48,24 @@ function incone_prm(prm::SumOfSquaresCone) return false end - prm.Vp[j] .= prm.ipwt[j] # TODO this shouldn't be necessary if don't have to use rdiv + @. prm.Vp[j] = prm.ipwt[j] # TODO this shouldn't be necessary if don't have to use rdiv rdiv!(prm.Vp[j], F.U) mul!(prm.Vp2, prm.Vp[j], prm.Vp[j]') # TODO if parallel, need to use separate Vp2[j] for i in eachindex(prm.g) - @inbounds prm.g[i] -= prm.Vp2[i,i] + prm.g[i] -= prm.Vp2[i,i] end - prm.H .+= abs2.(prm.Vp2) + @. prm.H += abs2(prm.Vp2) end - # TODO copy over H and do cholesky in-place - prm.F = cholesky(prm.H, check=false) + @. prm.Hchol = prm.H + prm.F = cholesky!(prm.Hchol, check=false) if !issuccess(prm.F) return false end return true end -calcg_prm!(g::AbstractVector{Float64}, prm::SumOfSquaresCone) = (g .= prm.g; g) +calcg_prm!(g::AbstractVector{Float64}, prm::SumOfSquaresCone) = (@. g = prm.g; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SumOfSquaresCone) = ldiv!(prod, prm.F, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SumOfSquaresCone) = mul!(prod, prm.H, arr) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 86fa80024..68557793f 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,91 +1,91 @@ -@testset "small LP 1" begin - Random.seed!(1) - (n, p, q) = (30, 12, 30) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - # G = -1.0I - @assert n == q - G = Diagonal(-1.0I, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 25 - @test Alfonso.get_status(alf) == :Optimal -end - -@testset "small LP 2" begin - Random.seed!(1) - (n, p, q) = (10, 8, 10) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal -end - -@testset "small LP 3" begin - Random.seed!(1) - (n, p, q) = (5, 2, 5) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = G*ones(n) - cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal -end - -@testset "large dense lp example (dense A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, use_data=true, dense=true) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 75 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -end - -@testset "large sparse lp example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 70 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "small dense lp example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(d_alf, 50, 100, dense=true, tosparse=false) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_niters(d_alf) <= 40 - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(s_alf, 50, 100, dense=true, tosparse=true) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_niters(s_alf) <= 40 - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end +# @testset "small LP 1" begin +# Random.seed!(1) +# (n, p, q) = (30, 12, 30) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# # G = -1.0I +# @assert n == q +# G = Diagonal(-1.0I, n) +# h = zeros(q) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 25 +# @test Alfonso.get_status(alf) == :Optimal +# end +# +# @testset "small LP 2" begin +# Random.seed!(1) +# (n, p, q) = (10, 8, 10) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# G = SparseMatrixCSC(-1.0I, q, n) +# h = zeros(q) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 20 +# @test Alfonso.get_status(alf) == :Optimal +# end +# +# @testset "small LP 3" begin +# Random.seed!(1) +# (n, p, q) = (5, 2, 5) +# c = rand(0.0:9.0, n) +# A = rand(-9.0:9.0, p, n) +# b = A*ones(n) +# G = SparseMatrixCSC(-1.0I, q, n) +# h = G*ones(n) +# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 20 +# @test Alfonso.get_status(alf) == :Optimal +# end +# +# @testset "large dense lp example (dense A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(alf, 500, 1000, use_data=true, dense=true) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 75 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +# end +# +# @testset "large sparse lp example (sparse A)" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 70 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small dense lp example (dense vs sparse A)" begin +# # dense methods +# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) +# @time Alfonso.solve!(d_alf) +# @test Alfonso.get_niters(d_alf) <= 40 +# @test Alfonso.get_status(d_alf) == :Optimal +# +# # sparse methods +# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) +# @time Alfonso.solve!(s_alf) +# @test Alfonso.get_niters(s_alf) <= 40 +# @test Alfonso.get_status(s_alf) == :Optimal +# +# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +# end @testset "1D poly envelope example (dense vs sparse A)" begin # dense methods @@ -248,123 +248,123 @@ end # @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end - -@testset "small second-order cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, 1/sqrt(2)] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 -end - -@testset "small rotated second-order cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1, -1] - A = Float64[1 0 0 0; 0 1 0 0] - b = Float64[1/2, 1] - G = SparseMatrixCSC(-1.0I, 4, 4) - h = zeros(4) - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 -end - -@testset "small rotated second-order cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1/2, 1]/sqrt(2) - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 -end - -@testset "small positive semidefinite cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, 0] - A = Float64[1 0 0; 0 0 1] - b = Float64[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 -end - -@testset "small positive semidefinite cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 0, 1, 0, 0, 1] - A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] - b = Float64[10, 3] - G = SparseMatrixCSC(-1.0I, 6, 6) - h = zeros(6) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 -end - -@testset "small exponential cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 1, 1] - A = Float64[0 1 0; 1 0 0] - b = Float64[2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 -end - -# @testset "small power cone problem" begin +# +# @testset "small second-order cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, -1, -1] +# A = Float64[1 0 0; 0 1 0] +# b = Float64[1, 1/sqrt(2)] +# G = SparseMatrixCSC(-1.0I, 3, 3) +# h = zeros(3) +# cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 15 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 +# end +# +# @testset "small rotated second-order cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, 0, -1, -1] +# A = Float64[1 0 0 0; 0 1 0 0] +# b = Float64[1/2, 1] +# G = SparseMatrixCSC(-1.0I, 4, 4) +# h = zeros(4) +# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 15 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small rotated second-order cone problem 2" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, 0, -1] +# A = Float64[1 0 0; 0 1 0] +# b = Float64[1/2, 1]/sqrt(2) +# G = SparseMatrixCSC(-1.0I, 3, 3) +# h = zeros(3) +# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 20 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 +# end +# +# @testset "small positive semidefinite cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[0, -1, 0] +# A = Float64[1 0 0; 0 0 1] +# b = Float64[1/2, 1] +# G = SparseMatrixCSC(-1.0I, 3, 3) +# h = zeros(3) +# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 15 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 +# end +# +# @testset "small positive semidefinite cone problem 2" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[1, 0, 1, 0, 0, 1] +# A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] +# b = Float64[10, 3] +# G = SparseMatrixCSC(-1.0I, 6, 6) +# h = zeros(6) +# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 20 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 +# end +# +# @testset "small exponential cone problem" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 0, 0, -1, -1, 0] -# A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] +# c = Float64[1, 1, 1] +# A = Float64[0 1 0; 1 0 0] # b = Float64[2, 1] -# cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) -# Alfonso.load_data!(alf, A, b, c, cone) +# G = SparseMatrixCSC(-1.0I, 3, 3) +# h = zeros(3) +# cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) +# Alfonso.load_data!(alf, c, A, b, G, h, cone) # @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 20 # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 +# @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 +# @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 # end +# +# # @testset "small power cone problem" begin +# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# # c = Float64[1, 0, 0, -1, -1, 0] +# # A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] +# # b = Float64[2, 1] +# # cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) +# # Alfonso.load_data!(alf, A, b, c, cone) +# # @time Alfonso.solve!(alf) +# # @test Alfonso.get_status(alf) == :Optimal +# # @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# # @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 +# # end From e6719a13348988b32f50c1ff4fdc54e02561abb7 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 26 Sep 2018 21:05:25 -0400 Subject: [PATCH 27/30] use Diagonal matrix type instead of sparse in examples; restore tests --- examples/envelope/envelope.jl | 4 +- examples/lp/lp.jl | 2 +- examples/namedpoly/namedpoly.jl | 2 +- src/nativeinterface.jl | 1 + test/nativeexamples.jl | 455 ++++++++++++++++---------------- 5 files changed, 232 insertions(+), 232 deletions(-) diff --git a/examples/envelope/envelope.jl b/examples/envelope/envelope.jl index eced2f640..c8667a393 100644 --- a/examples/envelope/envelope.jl +++ b/examples/envelope/envelope.jl @@ -27,9 +27,9 @@ function build_envelope!(alf::Alfonso.AlfonsoOpt, npoly::Int, deg::Int, n::Int, if dense A = repeat(Array(1.0I, U, U), outer=(1, npoly)) else - A = repeat(sparse(1.0I, U, U), outer=(1, npoly)) + A = repeat(sparse(1.0I, U, U), outer=(1, npoly)) # TODO maybe construct without repeat end - G = SparseMatrixCSC(-1.0I, npoly*U, npoly*U) + G = Diagonal(-1.0I, npoly*U) # TODO uniformscaling b = w h = zeros(npoly*U) if use_data diff --git a/examples/lp/lp.jl b/examples/lp/lp.jl index d176b3069..8593aaaa9 100644 --- a/examples/lp/lp.jl +++ b/examples/lp/lp.jl @@ -32,7 +32,7 @@ function build_lp!(alf::Alfonso.AlfonsoOpt, m::Int, n::Int; use_data::Bool=false if tosparse && !issparse(A) A = sparse(A) end - G = SparseMatrixCSC(-1.0I, n, n) + G = Diagonal(-1.0I, n) # TODO uniformscaling h = zeros(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(n)], [1:n]) diff --git a/examples/namedpoly/namedpoly.jl b/examples/namedpoly/namedpoly.jl index 6ef8697d9..657e6117d 100644 --- a/examples/namedpoly/namedpoly.jl +++ b/examples/namedpoly/namedpoly.jl @@ -68,7 +68,7 @@ function build_namedpoly!(alf::Alfonso.AlfonsoOpt, polyname::Symbol, d::Int) A = ones(1, U) b = [1.0,] c = [fn(pts[j,:]...) for j in 1:U] - G = SparseMatrixCSC(-1.0I, U, U) # TODO uniformscaling? + G = Diagonal(-1.0I, U) # TODO uniformscaling? h = zeros(U) cone = Alfonso.Cone([Alfonso.SumOfSquaresCone(U, [P0, PWts...])], [1:U]) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index eae5b266e..e56aacf99 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -696,6 +696,7 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite # TODO does it matter that F could be either type? + # TODO what about LDL decomp? F = cholesky!(Symmetric(Q2GHGQ2), check=false) if !issuccess(F) alf.verbose && println("linear system matrix was nearly not positive definite") diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 68557793f..086cc4ba3 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,91 +1,90 @@ -# @testset "small LP 1" begin -# Random.seed!(1) -# (n, p, q) = (30, 12, 30) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# # G = -1.0I -# @assert n == q -# G = Diagonal(-1.0I, n) -# h = zeros(q) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 25 -# @test Alfonso.get_status(alf) == :Optimal -# end -# -# @testset "small LP 2" begin -# Random.seed!(1) -# (n, p, q) = (10, 8, 10) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# G = SparseMatrixCSC(-1.0I, q, n) -# h = zeros(q) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 20 -# @test Alfonso.get_status(alf) == :Optimal -# end -# -# @testset "small LP 3" begin -# Random.seed!(1) -# (n, p, q) = (5, 2, 5) -# c = rand(0.0:9.0, n) -# A = rand(-9.0:9.0, p, n) -# b = A*ones(n) -# G = SparseMatrixCSC(-1.0I, q, n) -# h = G*ones(n) -# cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 20 -# @test Alfonso.get_status(alf) == :Optimal -# end -# -# @testset "large dense lp example (dense A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, use_data=true, dense=true) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 75 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 -# end -# -# @testset "large sparse lp example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 70 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end -# -# @testset "small dense lp example (dense vs sparse A)" begin -# # dense methods -# d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(d_alf, 50, 100, dense=true, tosparse=false) -# @time Alfonso.solve!(d_alf) -# @test Alfonso.get_niters(d_alf) <= 40 -# @test Alfonso.get_status(d_alf) == :Optimal -# -# # sparse methods -# s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_lp!(s_alf, 50, 100, dense=true, tosparse=true) -# @time Alfonso.solve!(s_alf) -# @test Alfonso.get_niters(s_alf) <= 40 -# @test Alfonso.get_status(s_alf) == :Optimal -# -# @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -# end +@testset "small LP 1" begin + Random.seed!(1) + (n, p, q) = (30, 12, 30) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + @assert n == q + G = Diagonal(-1.0I, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 25 + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small LP 2" begin + Random.seed!(1) + (n, p, q) = (10, 8, 10) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = zeros(q) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small LP 3" begin + Random.seed!(1) + (n, p, q) = (5, 2, 5) + c = rand(0.0:9.0, n) + A = rand(-9.0:9.0, p, n) + b = A*ones(n) + G = SparseMatrixCSC(-1.0I, q, n) + h = G*ones(n) + cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "large dense lp example (dense A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, use_data=true, dense=true) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 75 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 +end + +@testset "large sparse lp example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 70 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "small dense lp example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(d_alf, 50, 100, dense=true, tosparse=false) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_niters(d_alf) <= 40 + @test Alfonso.get_status(d_alf) == :Optimal + + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_lp!(s_alf, 50, 100, dense=true, tosparse=true) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_niters(s_alf) <= 40 + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 +end @testset "1D poly envelope example (dense vs sparse A)" begin # dense methods @@ -126,28 +125,28 @@ end @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 end -# @testset "3D poly envelope example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_envelope!(alf, 2, 3, 3, 5, dense=false) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end -# -# @testset "4D poly envelope example (sparse A)" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_envelope!(alf, 2, 3, 4, 4, dense=false) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -# end +@testset "3D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(alf, 2, 3, 3, 5, dense=false) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end + +@testset "4D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_envelope!(alf, 2, 3, 4, 4, dense=false) + @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 +end # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html @testset "Butcher" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) build_namedpoly!(alf, :butcher, 2) @time Alfonso.solve!(alf) - # @test Alfonso.get_niters(alf) <= 40 + @test Alfonso.get_niters(alf) <= 40 @test Alfonso.get_status(alf) == :Optimal @test Alfonso.get_pobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 @@ -221,25 +220,25 @@ end @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 end -@testset "Robinson" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_namedpoly!(alf, :robinson, 8) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 35 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -end +# @testset "Robinson" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) +# build_namedpoly!(alf, :robinson, 8) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_niters(alf) <= 35 +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +# end # @testset "Rosenbrock" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=20) # build_namedpoly!(alf, :rosenbrock, 3) # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_status(alf) == :Optimal # @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end -# + # @testset "Schwefel" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=25) # build_namedpoly!(alf, :schwefel, 4) @@ -248,123 +247,123 @@ end # @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end -# -# @testset "small second-order cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, -1, -1] -# A = Float64[1 0 0; 0 1 0] -# b = Float64[1, 1/sqrt(2)] -# G = SparseMatrixCSC(-1.0I, 3, 3) -# h = zeros(3) -# cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 15 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 -# end -# -# @testset "small rotated second-order cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, 0, -1, -1] -# A = Float64[1 0 0 0; 0 1 0 0] -# b = Float64[1/2, 1] -# G = SparseMatrixCSC(-1.0I, 4, 4) -# h = zeros(4) -# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 15 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 -# end -# -# @testset "small rotated second-order cone problem 2" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, 0, -1] -# A = Float64[1 0 0; 0 1 0] -# b = Float64[1/2, 1]/sqrt(2) -# G = SparseMatrixCSC(-1.0I, 3, 3) -# h = zeros(3) -# cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 20 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 -# end -# -# @testset "small positive semidefinite cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[0, -1, 0] -# A = Float64[1 0 0; 0 0 1] -# b = Float64[1/2, 1] -# G = SparseMatrixCSC(-1.0I, 3, 3) -# h = zeros(3) -# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 15 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 -# end -# -# @testset "small positive semidefinite cone problem 2" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 0, 1, 0, 0, 1] -# A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] -# b = Float64[10, 3] -# G = SparseMatrixCSC(-1.0I, 6, 6) -# h = zeros(6) -# cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 20 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 -# end -# -# @testset "small exponential cone problem" begin + +@testset "small second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1, 1/sqrt(2)] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1, -1] + A = Float64[1 0 0 0; 0 1 0 0] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 4, 4) + h = zeros(4) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1/2, 1]/sqrt(2) + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 +end + +@testset "small positive semidefinite cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, 0] + A = Float64[1 0 0; 0 0 1] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 +end + +@testset "small positive semidefinite cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 0, 1, 0, 0, 1] + A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] + b = Float64[10, 3] + G = SparseMatrixCSC(-1.0I, 6, 6) + h = zeros(6) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 +end + +@testset "small exponential cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 1, 1] + A = Float64[0 1 0; 1 0 0] + b = Float64[2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 +end + +# @testset "small power cone problem" begin # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 1, 1] -# A = Float64[0 1 0; 1 0 0] +# c = Float64[1, 0, 0, -1, -1, 0] +# A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] # b = Float64[2, 1] -# G = SparseMatrixCSC(-1.0I, 3, 3) -# h = zeros(3) -# cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) -# Alfonso.load_data!(alf, c, A, b, G, h, cone) +# cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) +# Alfonso.load_data!(alf, A, b, c, cone) # @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 20 # @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 -# @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 # end -# -# # @testset "small power cone problem" begin -# # alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# # c = Float64[1, 0, 0, -1, -1, 0] -# # A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] -# # b = Float64[2, 1] -# # cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) -# # Alfonso.load_data!(alf, A, b, c, cone) -# # @time Alfonso.solve!(alf) -# # @test Alfonso.get_status(alf) == :Optimal -# # @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# # @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 -# # end From f65af8a8d51e13f76124b2f52a84adea854a2fa9 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 27 Sep 2018 01:28:31 -0400 Subject: [PATCH 28/30] remove a couple redundant prealloc arrays --- src/nativeinterface.jl | 53 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index e56aacf99..15016364e 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -1,48 +1,39 @@ mutable struct LinSysCache - Q1 Q2 - Ri + RiQ1 GQ2 HG GHG GHGQ2 Q2GHGQ2 bxGHbz - Riby Q1x rhs Q2div Q2x GHGxi - Q1yirhs HGxi x1 y1 z1 - function LinSysCache(Q1::Matrix{Float64}, Q2::Matrix{Float64}, Ri::AbstractMatrix{Float64}, n::Int, p::Int, q::Int) + function LinSysCache(Q1::AbstractMatrix{Float64}, Q2::AbstractMatrix{Float64}, Ri::AbstractMatrix{Float64}, G::AbstractMatrix{Float64}, n::Int, p::Int, q::Int) L = new() nmp = n - p - @assert size(Q1) == (n, p) - @assert size(Q2) == (n, nmp) - @assert size(Ri) == (p, p) - L.Q1 = Q1 L.Q2 = Q2 - L.Ri = Ri - L.GQ2 = Matrix{Float64}(undef, q, nmp) - L.HG = Matrix{Float64}(undef, q, n) + L.RiQ1 = Ri*Q1' + L.GQ2 = G*Q2 + L.HG = Matrix{Float64}(undef, q, n) # TODO don't enforce dense on some L.GHG = Matrix{Float64}(undef, n, n) L.GHGQ2 = Matrix{Float64}(undef, n, nmp) L.Q2GHGQ2 = Matrix{Float64}(undef, nmp, nmp) L.bxGHbz = Vector{Float64}(undef, n) - L.Riby = Vector{Float64}(undef, p) L.Q1x = Vector{Float64}(undef, n) L.rhs = Vector{Float64}(undef, n) L.Q2div = Vector{Float64}(undef, nmp) L.Q2x = Vector{Float64}(undef, n) L.GHGxi = Vector{Float64}(undef, n) - L.Q1yirhs = Vector{Float64}(undef, p) L.HGxi = Vector{Float64}(undef, q) L.x1 = Vector{Float64}(undef, n) L.y1 = Vector{Float64}(undef, p) @@ -95,8 +86,7 @@ mutable struct AlfonsoOpt h::Vector{Float64} # cone constraint vector, size q cone::Cone # primal constraint cone object - L::LinSysCache # cache for direction finding functions - + L::LinSysCache # cache for linear system solves # results status::Symbol # solver status @@ -261,7 +251,7 @@ function load_data!( alf.G = G alf.h = h alf.cone = cone - alf.L = LinSysCache(Q1, Q2, Ri, n, p, q) + alf.L = LinSysCache(Q1, Q2, Ri, G, n, p, q) alf.status = :Loaded return alf @@ -598,7 +588,7 @@ end function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) (b, G, h, cone) = (alf.b, alf.G, alf.h, alf.cone) L = alf.L - (Q1, Q2, Ri, GQ2, Q2GHGQ2, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, Q1yirhs) = (L.Q1, L.Q2, L.Ri, L.GQ2, L.Q2GHGQ2, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.Q1yirhs) + (Q2, RiQ1, GQ2, Q2GHGQ2, bxGHbz, Q1x, rhs, Q2div, Q2x) = (L.Q2, L.RiQ1, L.GQ2, L.Q2GHGQ2, L.bxGHbz, L.Q1x, L.rhs, L.Q2div, L.Q2x) alf.verbose && println("\nfinding initial iterate") @@ -607,21 +597,25 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # |A 0 0 | |ty| |b| # |G 0 -I| |ts| |h| - # GQ2 = G*Q2 # Q1x = Q1*Ri'*b # Q2x = Q2*(Symmetric(GQ2'*GQ2)\(GQ2'*(h - G*Q1x))) # tx = Q1x + Q2x # ts = h - G*tx # ty = Ri*Q1'*G'*ts - mul!(GQ2, G, Q2) + # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite + # TODO does it matter that F could be either type? + # TODO what about LDL decomp? mul!(Q2GHGQ2, GQ2', GQ2) - # F = bunchkaufman!(Symmetric(Q2GHGQ2)) - F = cholesky!(Symmetric(Q2GHGQ2)) + F = cholesky!(Symmetric(Q2GHGQ2), check=false) + if !issuccess(F) + alf.verbose && println("linear system matrix was nearly not positive definite") + mul!(Q2GHGQ2, Q2', GHGQ2) + F = bunchkaufman!(Symmetric(Q2GHGQ2)) + end # Q1x = Q1*Ri'*b - mul!(Riby, Ri', b) - mul!(Q1x, Q1, Riby) + mul!(Q1x, RiQ1', b) # Q2x = Q2*(F\(GQ2'*(h - G*Q1x))) mul!(rhs, G, Q1x) @. rhs = h - rhs @@ -635,8 +629,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) @. ts = h - ts # ty = Ri*Q1'*G'*ts mul!(bxGHbz, G', ts) - mul!(Q1yirhs, Q1', bxGHbz) - mul!(ty, Ri, Q1yirhs) + mul!(ty, RiQ1, bxGHbz) # from ts, step along interior direction of cone until ts is inside cone @. ls_ts = ts @@ -735,14 +728,13 @@ end # calculate solution to reduced symmetric linear systems function calcxyz!(xi, yi, zi, F, alf) L = alf.L - (Q1, Q2, Ri, HG, GHG, bxGHbz, Riby, Q1x, rhs, Q2div, Q2x, GHGxi, Q1yirhs, HGxi) = (L.Q1, L.Q2, L.Ri, L.HG, L.GHG, L.bxGHbz, L.Riby, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.GHGxi, L.Q1yirhs, L.HGxi) + (Q2, RiQ1, HG, GHG, bxGHbz, Q1x, rhs, Q2div, Q2x, GHGxi, HGxi) = (L.Q2, L.RiQ1, L.HG, L.GHG, L.bxGHbz, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.GHGxi, L.HGxi) # bxGHbz = bx + G'*Hbz mul!(bxGHbz, alf.G', zi) @. bxGHbz += xi # Q1x = Q1*Ri'*by - mul!(Riby, Ri', yi) - mul!(Q1x, Q1, Riby) + mul!(Q1x, RiQ1', yi) # Q2x = Q2*(K22_F\(Q2'*(bxGHbz - GHG*Q1x))) mul!(rhs, GHG, Q1x) @. rhs = bxGHbz - rhs @@ -754,8 +746,7 @@ function calcxyz!(xi, yi, zi, F, alf) # yi = Ri*Q1'*(bxGHbz - GHG*xi) mul!(GHGxi, GHG, xi) @. bxGHbz -= GHGxi - mul!(Q1yirhs, Q1', bxGHbz) - mul!(yi, Ri, Q1yirhs) + mul!(yi, RiQ1, bxGHbz) # zi = HG*xi - Hbz mul!(HGxi, HG, xi) @. zi = HGxi - zi From 7ccb0611e3d7c4bdf4d27d5fd30c0f49ac85ecda Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 28 Sep 2018 22:47:38 -0400 Subject: [PATCH 29/30] reorder tests, add comments, add constraint cone test --- Manifest.toml | 2 +- README.md | 26 ++ src/Alfonso.jl | 2 +- src/nativeinterface.jl | 80 +++--- src/primitivecones/exponential.jl | 10 +- src/primitivecones/positivesemidefinite.jl | 6 +- src/primitivecones/power.jl | 2 +- src/primitivecones/rotatedsecondorder.jl | 8 +- src/primitivecones/secondorder.jl | 10 +- test/moiexamples.jl | 43 --- test/nativeexamples.jl | 309 ++++++++++----------- test/runtests.jl | 2 +- 12 files changed, 231 insertions(+), 269 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index 15e819625..5460bf30c 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -139,7 +139,7 @@ deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[UUIDs]] -deps = ["Random"] +deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [[Unicode]] diff --git a/README.md b/README.md index be8ae662d..844db3b4e 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,29 @@ (under construction) unofficial Julia re-implementation of alfonso (ALgorithm FOr Non-Symmetric Optimization), originally by D. Papp and S. Yıldız: https://github.com/dpapp-github/alfonso only works on Julia v0.7+ + +solves a pair of primal and dual cone programs + +primal (over x,s): +``` + min c'x : duals + b - Ax == 0 (y) + h - Gx == s in K (z) +``` +dual (over z,y): +``` + max -b'y - h'z : duals + c + A'y + G'z == 0 (x) + z in K* (s) +``` +where K is a convex cone defined as a Cartesian product of recognized primitive cones, and K* is its dual cone + +the primal-dual optimality conditions are +``` + b - Ax == 0 + h - Gx == s + c + A'y + G'z == 0 + s'z == 0 + s in K + z in K* +``` diff --git a/src/Alfonso.jl b/src/Alfonso.jl index fd70310a5..0a59cadf8 100644 --- a/src/Alfonso.jl +++ b/src/Alfonso.jl @@ -11,7 +11,7 @@ module Alfonso "sumofsquares", "secondorder", "exponential", - # "power", + # "power", "rotatedsecondorder", "positivesemidefinite", ] diff --git a/src/nativeinterface.jl b/src/nativeinterface.jl index 15016364e..d01cb119e 100644 --- a/src/nativeinterface.jl +++ b/src/nativeinterface.jl @@ -1,4 +1,11 @@ +# cache for linear system solves (to avoid allocations) +# use QR + cholesky method from CVXOPT +# (1) eliminate equality constraints via QR of A' +# (2) solve reduced system by cholesky +# |0 A' G' | * |ux| = |bx| +# |A 0 0 | |uy| |by| +# |G 0 -Hi/mu| |uz| |bz| mutable struct LinSysCache Q2 RiQ1 @@ -42,26 +49,7 @@ mutable struct LinSysCache end end -""" -solves a pair of primal and dual cone programs - primal (over x,s): - min c'x : duals - b - Ax == 0 (y) - h - Gx == s in K (z) - dual (over z,y): - max -b'y - h'z : duals - c + A'y + G'z == 0 (x) - z in K* (s) -where K is a convex cone defined as a Cartesian product of recognized primitive cones, and K* is its dual cone - -the primal-dual optimality conditions are - b - Ax == 0 - h - Gx == s - c + A'y + G'z == 0 - s'z == 0 - s in K - z in K* -""" +# model object containing options, problem data, linear system cache, and solution mutable struct AlfonsoOpt # options verbose::Bool # if true, prints progress at each iteration @@ -103,8 +91,7 @@ mutable struct AlfonsoOpt pobj::Float64 # final primal objective value dobj::Float64 # final dual objective value - # TODO match natural order of options listed above - function AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) + function AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, predlsmulti, corrcheck, maxcorrsteps, alphacorr, maxcorrlsiters, corrlsmulti) alf = new() alf.verbose = verbose @@ -114,11 +101,11 @@ mutable struct AlfonsoOpt alf.maxiter = maxiter alf.predlinesearch = predlinesearch alf.maxpredsmallsteps = maxpredsmallsteps - alf.maxcorrsteps = maxcorrsteps + alf.predlsmulti = predlsmulti alf.corrcheck = corrcheck - alf.maxcorrlsiters = maxcorrlsiters + alf.maxcorrsteps = maxcorrsteps alf.alphacorr = alphacorr - alf.predlsmulti = predlsmulti + alf.maxcorrlsiters = maxcorrlsiters alf.corrlsmulti = corrlsmulti alf.status = :NotLoaded @@ -136,11 +123,11 @@ function AlfonsoOpt(; maxiter = 5e2, predlinesearch = true, maxpredsmallsteps = 15, - maxcorrsteps = 15, + predlsmulti = 0.7, corrcheck = true, - maxcorrlsiters = 15, + maxcorrsteps = 15, alphacorr = 1.0, - predlsmulti = 0.7, + maxcorrlsiters = 15, corrlsmulti = 0.5, ) @@ -157,7 +144,7 @@ function AlfonsoOpt(; error("maxcorrsteps must be at least 1") end - return AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, maxcorrsteps, corrcheck, maxcorrlsiters, alphacorr, predlsmulti, corrlsmulti) + return AlfonsoOpt(verbose, tolrelopt, tolabsopt, tolfeas, maxiter, predlinesearch, maxpredsmallsteps, predlsmulti, corrcheck, maxcorrsteps, alphacorr, maxcorrlsiters, corrlsmulti) end get_status(alf::AlfonsoOpt) = alf.status @@ -205,7 +192,6 @@ function load_data!( end # perform QR decomposition of A' for use in linear system solves - # TODO only use for factorization-based linear system solves # TODO reduce allocs, improve efficiency # A' = [Q1 Q2] * [R1; 0] if issparse(A) @@ -585,10 +571,10 @@ function calcnbhd(tk, mu, ls_tz, g, cone) end # calculate initial central primal-dual iterate -function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) +function findinitialiterate!(tx::Vector{Float64}, ty::Vector{Float64}, tz::Vector{Float64}, ts::Vector{Float64}, ls_ts::Vector{Float64}, bnu::Float64, alf::AlfonsoOpt) (b, G, h, cone) = (alf.b, alf.G, alf.h, alf.cone) L = alf.L - (Q2, RiQ1, GQ2, Q2GHGQ2, bxGHbz, Q1x, rhs, Q2div, Q2x) = (L.Q2, L.RiQ1, L.GQ2, L.Q2GHGQ2, L.bxGHbz, L.Q1x, L.rhs, L.Q2div, L.Q2x) + (Q2, RiQ1, GQ2, Q2GHGQ2, bxGHbz, Q1x, rhs, Q2div, Q2x, HGxi) = (L.Q2, L.RiQ1, L.GQ2, L.Q2GHGQ2, L.bxGHbz, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.HGxi) alf.verbose && println("\nfinding initial iterate") @@ -603,9 +589,8 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # ts = h - G*tx # ty = Ri*Q1'*G'*ts - # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite + # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite (TODO could try LDL instead) # TODO does it matter that F could be either type? - # TODO what about LDL decomp? mul!(Q2GHGQ2, GQ2', GQ2) F = cholesky!(Symmetric(Q2GHGQ2), check=false) if !issuccess(F) @@ -617,9 +602,9 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # Q1x = Q1*Ri'*b mul!(Q1x, RiQ1', b) # Q2x = Q2*(F\(GQ2'*(h - G*Q1x))) - mul!(rhs, G, Q1x) - @. rhs = h - rhs - mul!(Q2div, GQ2', rhs) + mul!(HGxi, G, Q1x) + @. HGxi = h - HGxi + mul!(Q2div, GQ2', HGxi) ldiv!(F, Q2div) mul!(Q2x, Q2, Q2div) # tx = Q1x + Q2x @@ -634,7 +619,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) # from ts, step along interior direction of cone until ts is inside cone @. ls_ts = ts if !incone(cone) - tmp_ts = getintdir!(rhs, cone) + tmp_ts = getintdir!(tz, cone) alpha = 1.0 # TODO starting alpha maybe should depend on ls_ts (eg norm like in Alfonso) in case 1.0 is too large/small steps = 0 while !incone(cone) @@ -657,8 +642,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) kap = 1.0 mu = (dot(tz, ts) + tau*kap)/bnu - # TODO delete later - @assert abs(1.0 - mu) < 1e-8 + @assert abs(1.0 - mu) < 1e-10 # @assert calcnbhd(tau*kap, mu, copy(tz), copy(tz), cone) < 1e-6 alf.verbose && println("initial iterate found") @@ -666,7 +650,7 @@ function findinitialiterate!(tx, ty, tz, ts, ls_ts, bnu, alf) end # calculate new prediction or correction direction given rhs of KKT linear system -function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, tau, alf) +function finddirection!(rhs_tx::Vector{Float64}, rhs_ty::Vector{Float64}, rhs_tz::Vector{Float64}, rhs_ts::Vector{Float64}, rhs_kap::Float64, rhs_tau::Float64, mu::Float64, tau::Float64, alf::AlfonsoOpt) (c, b, G, h, cone) = (alf.c, alf.b, alf.G, alf.h, alf.cone) L = alf.L (Q2, HG, GHG, GHGQ2, Q2GHGQ2, x1, y1, z1) = (L.Q2, L.HG, L.GHG, L.GHGQ2, L.Q2GHGQ2, L.x1, L.y1, L.z1) @@ -687,9 +671,8 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta mul!(GHGQ2, GHG, Q2) mul!(Q2GHGQ2, Q2', GHGQ2) - # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite + # bunchkaufman allocates more than cholesky, but doesn't fail when approximately quasidefinite (TODO could try LDL instead) # TODO does it matter that F could be either type? - # TODO what about LDL decomp? F = cholesky!(Symmetric(Q2GHGQ2), check=false) if !issuccess(F) alf.verbose && println("linear system matrix was nearly not positive definite") @@ -704,14 +687,14 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta calcHarr!(z1, rhs_ts, cone) @. rhs_tz -= mu*z1 end - calcxyz!(rhs_tx, rhs_ty, rhs_tz, F, alf) + calcxyz!(rhs_tx, rhs_ty, rhs_tz, F, G, L) # (x1, y1, z1) = (-c, b, mu*H*h) @. x1 = -c @. y1 = b calcHarr!(z1, h, cone) @. z1 *= mu - calcxyz!(x1, y1, z1, F, alf) + calcxyz!(x1, y1, z1, F, G, L) # combine dir_tau = (rhs_tau + rhs_kap + dot(c, rhs_tx) + dot(b, rhs_ty) + dot(h, rhs_tz))/(mu/tau/tau - dot(c, x1) - dot(b, y1) - dot(h, z1)) @@ -726,12 +709,11 @@ function finddirection!(rhs_tx, rhs_ty, rhs_tz, rhs_ts, rhs_kap, rhs_tau, mu, ta end # calculate solution to reduced symmetric linear systems -function calcxyz!(xi, yi, zi, F, alf) - L = alf.L +function calcxyz!(xi::Vector{Float64}, yi::Vector{Float64}, zi::Vector{Float64}, F, G::AbstractMatrix{Float64}, L::LinSysCache) (Q2, RiQ1, HG, GHG, bxGHbz, Q1x, rhs, Q2div, Q2x, GHGxi, HGxi) = (L.Q2, L.RiQ1, L.HG, L.GHG, L.bxGHbz, L.Q1x, L.rhs, L.Q2div, L.Q2x, L.GHGxi, L.HGxi) # bxGHbz = bx + G'*Hbz - mul!(bxGHbz, alf.G', zi) + mul!(bxGHbz, G', zi) @. bxGHbz += xi # Q1x = Q1*Ri'*by mul!(Q1x, RiQ1', yi) @@ -755,7 +737,7 @@ function calcxyz!(xi, yi, zi, F, alf) end # get neighborhood parameters depending on magnitude of barrier parameter and maximum number of correction steps -function getbetaeta(maxcorrsteps, bnu) +function getbetaeta(maxcorrsteps::Int, bnu::Float64) if maxcorrsteps <= 2 if bnu < 10.0 return (0.1810, 0.0733, 0.0225) diff --git a/src/primitivecones/exponential.jl b/src/primitivecones/exponential.jl index 9d6446fc2..cf35af41a 100644 --- a/src/primitivecones/exponential.jl +++ b/src/primitivecones/exponential.jl @@ -7,14 +7,14 @@ mutable struct ExponentialCone <: PrimitiveCone pnt::AbstractVector{Float64} g::AbstractVector{Float64} H::Matrix{Float64} # TODO could be faster as StaticArray - Hi::Matrix{Float64} + H2::Matrix{Float64} F function ExponentialCone() prm = new() prm.g = Vector{Float64}(undef, 3) prm.H = similar(prm.g, 3, 3) - prm.Hi = copy(prm.H) + prm.H2 = copy(prm.H) return prm end end @@ -54,8 +54,8 @@ function incone_prm(prm::ExponentialCone) H[2,3] = H[3,2] = yz*(lzy - 1.0)*H[1,1] - iylzyx/z H[3,3] = abs2(yz)*H[1,1] + yz/z*iylzyx + inv(abs2(z)) - prm.Hi .= H - prm.F = bunchkaufman!(Symmetric(prm.Hi)) + @. prm.H2 = H + prm.F = bunchkaufman!(Symmetric(prm.H2)) # old code for inverse hessian # den = 2*y + dist @@ -70,6 +70,6 @@ function incone_prm(prm::ExponentialCone) return true end -calcg_prm!(g::AbstractVector{Float64}, prm::ExponentialCone) = (g .= prm.g; g) +calcg_prm!(g::AbstractVector{Float64}, prm::ExponentialCone) = (@. g = prm.g; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::ExponentialCone) = ldiv!(prod, prm.F, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::ExponentialCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/positivesemidefinite.jl b/src/primitivecones/positivesemidefinite.jl index 9e1607f3d..212d5c635 100644 --- a/src/primitivecones/positivesemidefinite.jl +++ b/src/primitivecones/positivesemidefinite.jl @@ -18,14 +18,14 @@ mutable struct PositiveSemidefiniteCone <: PrimitiveCone prm.mat = Matrix{Float64}(undef, prm.side, prm.side) prm.mat2 = copy(prm.mat) prm.matpnt = copy(prm.mat) - prm.matinv = copy(prm.mat) + # prm.matinv = copy(prm.mat) return prm end end dimension(prm::PositiveSemidefiniteCone) = prm.dim barrierpar_prm(prm::PositiveSemidefiniteCone) = prm.side -getintdir_prm!(arr::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) = mattovec!(arr, Matrix(1.0I, prm.side, prm.side)) +getintdir_prm!(arr::AbstractVector{Float64}, prm::PositiveSemidefiniteCone) = mattovec!(arr, Matrix(1.0I, prm.side, prm.side)) # TODO eliminate allocs loadpnt_prm!(prm::PositiveSemidefiniteCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) function incone_prm(prm::PositiveSemidefiniteCone) @@ -35,7 +35,7 @@ function incone_prm(prm::PositiveSemidefiniteCone) return false end - prm.matinv = -inv(F) # TODO reduce allocs + prm.matinv = -inv(F) # TODO eliminate allocs vectomat!(prm.matpnt, prm.pnt) return true end diff --git a/src/primitivecones/power.jl b/src/primitivecones/power.jl index 797080d40..67716bc91 100644 --- a/src/primitivecones/power.jl +++ b/src/primitivecones/power.jl @@ -65,6 +65,6 @@ function incone_prm(prm::PowerCone) return true end -calcg_prm!(g::AbstractVector{Float64}, prm::PowerCone) = (g .= prm.g; g) +calcg_prm!(g::AbstractVector{Float64}, prm::PowerCone) = (@. g = prm.g; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::PowerCone) = mul!(prod, prm.Hi, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::PowerCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/rotatedsecondorder.jl b/src/primitivecones/rotatedsecondorder.jl index 43fe724b3..fb05f49ce 100644 --- a/src/primitivecones/rotatedsecondorder.jl +++ b/src/primitivecones/rotatedsecondorder.jl @@ -20,7 +20,7 @@ end dimension(prm::RotatedSecondOrderCone) = prm.dim barrierpar_prm(prm::RotatedSecondOrderCone) = 2.0 -getintdir_prm!(arr::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (arr[1:2] .= 1.0; arr[3:end] .= 0.0; arr) +getintdir_prm!(arr::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (@. arr[1:2] = 1.0; @. arr[3:end] = 0.0; arr) loadpnt_prm!(prm::RotatedSecondOrderCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) function incone_prm(prm::RotatedSecondOrderCone) @@ -39,17 +39,17 @@ function incone_prm(prm::RotatedSecondOrderCone) for j in 3:prm.dim prm.Hi[j,j] += prm.disth end - prm.H .= prm.Hi + @. prm.H = prm.Hi for j in 3:prm.dim prm.H[1,j] = prm.H[j,1] = -prm.Hi[2,j] prm.H[2,j] = prm.H[j,2] = -prm.Hi[1,j] end prm.H[1,1] = prm.Hi[2,2] prm.H[2,2] = prm.Hi[1,1] - prm.H .*= inv(prm.disth)^2 + @. prm.H *= inv(prm.disth)^2 return true end -calcg_prm!(g::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (g .= prm.pnt ./ prm.disth; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) +calcg_prm!(g::AbstractVector{Float64}, prm::RotatedSecondOrderCone) = (@. g = prm.pnt/prm.disth; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::RotatedSecondOrderCone) = mul!(prod, prm.Hi, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::RotatedSecondOrderCone) = mul!(prod, prm.H, arr) diff --git a/src/primitivecones/secondorder.jl b/src/primitivecones/secondorder.jl index 66554f343..dcefdaf3f 100644 --- a/src/primitivecones/secondorder.jl +++ b/src/primitivecones/secondorder.jl @@ -20,7 +20,7 @@ end dimension(prm::SecondOrderCone) = prm.dim barrierpar_prm(prm::SecondOrderCone) = 1.0 -getintdir_prm!(arr::AbstractVector{Float64}, prm::SecondOrderCone) = (arr[1] = 1.0; arr[2:end] .= 0.0; arr) +getintdir_prm!(arr::AbstractVector{Float64}, prm::SecondOrderCone) = (arr[1] = 1.0; @. arr[2:end] = 0.0; arr) loadpnt_prm!(prm::SecondOrderCone, pnt::AbstractVector{Float64}) = (prm.pnt = pnt) function incone_prm(prm::SecondOrderCone) @@ -33,19 +33,19 @@ function incone_prm(prm::SecondOrderCone) end mul!(prm.Hi, prm.pnt, prm.pnt') - prm.Hi .+= prm.Hi + @. prm.Hi += prm.Hi prm.Hi[1,1] -= prm.dist for j in 2:prm.dim prm.Hi[j,j] += prm.dist end - prm.H .= prm.Hi + @. prm.H = prm.Hi for j in 2:prm.dim prm.H[1,j] = prm.H[j,1] = -prm.H[j,1] end - prm.H .*= inv(prm.dist)^2 + @. prm.H *= inv(prm.dist)^2 return true end -calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (g .= prm.pnt ./ prm.dist; g[1] = -g[1]; g) +calcg_prm!(g::AbstractVector{Float64}, prm::SecondOrderCone) = (@. g = prm.pnt/prm.dist; g[1] = -g[1]; g) calcHiarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.Hi, arr) calcHarr_prm!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, prm::SecondOrderCone) = mul!(prod, prm.H, arr) diff --git a/test/moiexamples.jl b/test/moiexamples.jl index b6e02588a..e69de29bb 100644 --- a/test/moiexamples.jl +++ b/test/moiexamples.jl @@ -1,43 +0,0 @@ - -# TODO -# @testset "envelope example" begin -# opt = build_envelope(2, 5, 1, 5, use_data=true, native=false) -# MOI.optimize!(opt) -# @test MOI.get(opt, MOI.TerminationStatus()) == MOI.Success -# @test MOI.get(opt, MOI.ObjectiveValue()) ≈ -25.502777 atol=1e-4 -# @test MOI.get(opt, MOI.ObjectiveBound()) ≈ -25.502777 atol=1e-4 -# end -# -# @testset "lp example" begin -# opt = build_lp(500, 1000, use_data=true, native=false) -# MOI.optimize!(opt) -# @test MOI.get(opt, MOI.TerminationStatus()) == MOI.Success -# @test MOI.get(opt, MOI.ObjectiveValue()) ≈ 2055.807 atol=1e-4 -# @test MOI.get(opt, MOI.ObjectiveBound()) ≈ 2055.807 atol=1e-4 -# end -# -# @testset "namedpoly examples" begin -# @testset "Goldstein-Price" begin -# opt = build_namedpoly(:goldsteinprice, 7, native=false) -# MOI.optimize!(opt) -# @test MOI.get(opt, MOI.TerminationStatus()) == MOI.Success -# @test MOI.get(opt, MOI.ObjectiveValue()) ≈ 3 atol=1e-4 -# @test MOI.get(opt, MOI.ObjectiveBound()) ≈ 3 atol=1e-4 -# end -# -# @testset "Robinson" begin -# opt = build_namedpoly(:robinson, 8, native=false) -# MOI.optimize!(opt) -# @test MOI.get(opt, MOI.TerminationStatus()) == MOI.Success -# @test MOI.get(opt, MOI.ObjectiveValue()) ≈ 0.814814 atol=1e-4 -# @test MOI.get(opt, MOI.ObjectiveBound()) ≈ 0.814814 atol=1e-4 -# end -# -# @testset "Lotka-Volterra" begin -# opt = build_namedpoly(:lotkavolterra, 3, native=false) -# MOI.optimize!(opt) -# @test MOI.get(opt, MOI.TerminationStatus()) == MOI.Success -# @test MOI.get(opt, MOI.ObjectiveValue()) ≈ -20.8 atol=1e-4 -# @test MOI.get(opt, MOI.ObjectiveBound()) ≈ -20.8 atol=1e-4 -# end -# end diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index 086cc4ba3..f97a9e36d 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -1,72 +1,170 @@ -@testset "small LP 1" begin +@testset "small lp 1" begin Random.seed!(1) - (n, p, q) = (30, 12, 30) + (n, p, q) = (10, 8, 10) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - @assert n == q - G = Diagonal(-1.0I, n) + G = SparseMatrixCSC(-1.0I, q, n) h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 25 @test Alfonso.get_status(alf) == :Optimal end -@testset "small LP 2" begin +@testset "small lp 2" begin Random.seed!(1) - (n, p, q) = (10, 8, 10) + (n, p, q) = (5, 2, 10) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = zeros(q) + G = rand(q, n) - Matrix(2.0I, q, n) + h = G*ones(n) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal end -@testset "small LP 3" begin +@testset "small lp 3" begin Random.seed!(1) - (n, p, q) = (5, 2, 5) + (n, p, q) = (30, 12, 30) c = rand(0.0:9.0, n) A = rand(-9.0:9.0, p, n) b = A*ones(n) - G = SparseMatrixCSC(-1.0I, q, n) - h = G*ones(n) + @assert n == q + G = Diagonal(-1.0I, n) + h = zeros(q) cone = Alfonso.Cone([Alfonso.NonnegativeCone(q)], [1:q]) alf = Alfonso.AlfonsoOpt(verbose=verbflag) Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) + @test Alfonso.get_status(alf) == :Optimal +end + +@testset "small second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, -1, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1, 1/sqrt(2)] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1, -1] + A = Float64[1 0 0 0; 0 1 0 0] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 4, 4) + h = zeros(4) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 +end + +@testset "small rotated second-order cone problem 2" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[0, 0, -1] + A = Float64[1 0 0; 0 1 0] + b = Float64[1/2, 1]/sqrt(2) + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 end -@testset "large dense lp example (dense A)" begin +@testset "small positive semidefinite cone problem" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, use_data=true, dense=true) + c = Float64[0, -1, 0] + A = Float64[1 0 0; 0 0 1] + b = Float64[1/2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 75 + @test Alfonso.get_niters(alf) <= 15 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 end -@testset "large sparse lp example (sparse A)" begin +@testset "small positive semidefinite cone problem 2" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) + c = Float64[1, 0, 1, 0, 0, 1] + A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] + b = Float64[10, 3] + G = SparseMatrixCSC(-1.0I, 6, 6) + h = zeros(6) + cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 70 + @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 end +@testset "small exponential cone problem" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + c = Float64[1, 1, 1] + A = Float64[0 1 0; 1 0 0] + b = Float64[2, 1] + G = SparseMatrixCSC(-1.0I, 3, 3) + h = zeros(3) + cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) + Alfonso.load_data!(alf, c, A, b, G, h, cone) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 20 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 +end + +# @testset "small power cone problem" begin +# alf = Alfonso.AlfonsoOpt(verbose=verbflag) +# c = Float64[1, 0, 0, -1, -1, 0] +# A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] +# b = Float64[2, 1] +# cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) +# Alfonso.load_data!(alf, A, b, c, cone) +# @time Alfonso.solve!(alf) +# @test Alfonso.get_status(alf) == :Optimal +# @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 +# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 +# @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 +# end + @testset "small dense lp example (dense vs sparse A)" begin # dense methods d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -106,41 +204,6 @@ end @test Alfonso.get_dobj(s_alf) ≈ -25.502777 atol=1e-4 rtol=1e-4 end -@testset "2D poly envelope example (dense vs sparse A)" begin - # dense methods - d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(d_alf, 2, 4, 2, 7, dense=true) - @time Alfonso.solve!(d_alf) - @test Alfonso.get_niters(d_alf) <= 55 - @test Alfonso.get_status(d_alf) == :Optimal - - # sparse methods - s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(s_alf, 2, 4, 2, 7, dense=false) - @time Alfonso.solve!(s_alf) - @test Alfonso.get_niters(s_alf) <= 55 - @test Alfonso.get_status(s_alf) == :Optimal - - @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 -end - -@testset "3D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - build_envelope!(alf, 2, 3, 3, 5, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - -@testset "4D poly envelope example (sparse A)" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) - build_envelope!(alf, 2, 3, 4, 4, dense=false) - @time Alfonso.solve!(alf) - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 -end - # most values taken from https://people.sc.fsu.edu/~jburkardt/py_src/polynomials/polynomials.html @testset "Butcher" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) @@ -248,122 +311,56 @@ end # @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 # end -@testset "small second-order cone problem" begin +@testset "large dense lp example (dense A)" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, 1/sqrt(2)] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.SecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) + build_lp!(alf, 500, 1000, use_data=true, dense=true) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_niters(alf) <= 75 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 1/sqrt(2), 1/sqrt(2)] atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 2055.807 atol=1e-4 rtol=1e-4 end -@testset "small rotated second-order cone problem" begin +@testset "large sparse lp example (sparse A)" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1, -1] - A = Float64[1 0 0 0; 0 1 0 0] - b = Float64[1/2, 1] - G = SparseMatrixCSC(-1.0I, 4, 4) - h = zeros(4) - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(4)], [1:4]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) + build_lp!(alf, 500, 1000, dense=false, nzfrac=10/1000) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 + @test Alfonso.get_niters(alf) <= 70 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[3:4] ≈ [1, 1]/sqrt(2) atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 end -@testset "small rotated second-order cone problem 2" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, 0, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1/2, 1]/sqrt(2) - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.RotatedSecondOrderCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1/sqrt(2) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1/sqrt(2) atol=1e-4 rtol=1e-4 -end +@testset "2D poly envelope example (dense vs sparse A)" begin + # dense methods + d_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(d_alf, 2, 4, 2, 7, dense=true) + @time Alfonso.solve!(d_alf) + @test Alfonso.get_niters(d_alf) <= 55 + @test Alfonso.get_status(d_alf) == :Optimal -@testset "small positive semidefinite cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[0, -1, 0] - A = Float64[1 0 0; 0 0 1] - b = Float64[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(3)], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) - @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 15 - @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ -1 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf)[2] ≈ 1 atol=1e-4 rtol=1e-4 + # sparse methods + s_alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_envelope!(s_alf, 2, 4, 2, 7, dense=false) + @time Alfonso.solve!(s_alf) + @test Alfonso.get_niters(s_alf) <= 55 + @test Alfonso.get_status(s_alf) == :Optimal + + @test Alfonso.get_pobj(d_alf) ≈ Alfonso.get_pobj(s_alf) atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(d_alf) ≈ Alfonso.get_dobj(s_alf) atol=1e-4 rtol=1e-4 end -@testset "small positive semidefinite cone problem 2" begin +@testset "3D poly envelope example (sparse A)" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 0, 1, 0, 0, 1] - A = Float64[1 2 3 4 5 6; 1 1 1 1 1 1] - b = Float64[10, 3] - G = SparseMatrixCSC(-1.0I, 6, 6) - h = zeros(6) - cone = Alfonso.Cone([Alfonso.PositiveSemidefiniteCone(6)], [1:6]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) + build_envelope!(alf, 2, 3, 3, 5, dense=false) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ 1.249632 atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 end -@testset "small exponential cone problem" begin - alf = Alfonso.AlfonsoOpt(verbose=verbflag) - c = Float64[1, 1, 1] - A = Float64[0 1 0; 1 0 0] - b = Float64[2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cone = Alfonso.Cone([Alfonso.ExponentialCone()], [1:3]) - Alfonso.load_data!(alf, c, A, b, G, h, cone) +@testset "4D poly envelope example (sparse A)" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) + build_envelope!(alf, 2, 3, 4, 4, dense=false) @time Alfonso.solve!(alf) - @test Alfonso.get_niters(alf) <= 20 @test Alfonso.get_status(alf) == :Optimal - @test Alfonso.get_pobj(alf) ≈ (2*exp(1/2)+3) atol=1e-4 rtol=1e-4 - @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test dot(Alfonso.get_y(alf), b) ≈ -Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 - @test Alfonso.get_x(alf) ≈ [1, 2, 2*exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_y(alf) ≈ -[1+exp(1/2)/2, 1+exp(1/2)] atol=1e-4 rtol=1e-4 - @test Alfonso.get_z(alf) ≈ (c + A'*Alfonso.get_y(alf)) atol=1e-4 rtol=1e-4 + @test Alfonso.get_pobj(alf) ≈ Alfonso.get_dobj(alf) atol=1e-4 rtol=1e-4 end - -# @testset "small power cone problem" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# c = Float64[1, 0, 0, -1, -1, 0] -# A = Float64[1 1 1/2 0 0 0; 0 0 0 0 0 1] -# b = Float64[2, 1] -# cone = Alfonso.Cone([Alfonso.PowerCone(0.2), Alfonso.PowerCone(0.4)], [[1, 2, 4], [3, 6, 5]]) -# Alfonso.load_data!(alf, A, b, c, cone) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -1.80734 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ Alfonso.get_pobj(alf) atol=1e-4 rtol=1e-4 -# @test Alfonso.get_x(alf)[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 -# end diff --git a/test/runtests.jl b/test/runtests.jl index 9f658f650..b72b564d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Alfonso using Test -verbflag = false # Alfonso verbose option +verbflag = true # Alfonso verbose option # TODO interpolation tests From 0d70c654b3eed5faee5f9a315e300649b2fdd022 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 28 Sep 2018 23:13:39 -0400 Subject: [PATCH 30/30] restore many tests --- test/nativeexamples.jl | 86 ++++++++++++++++++++++-------------------- test/runtests.jl | 2 +- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/test/nativeexamples.jl b/test/nativeexamples.jl index f97a9e36d..ca1237a2d 100644 --- a/test/nativeexamples.jl +++ b/test/nativeexamples.jl @@ -215,23 +215,25 @@ end @test Alfonso.get_dobj(alf) ≈ -1.4393333333 atol=1e-4 rtol=1e-4 end -# @testset "Caprasse" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :caprasse, 4) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 -# end +@testset "Caprasse" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolfeas=5e-7) + build_namedpoly!(alf, :caprasse, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 45 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ -3.1800966258 atol=1e-4 rtol=1e-4 +end -# @testset "Goldstein-Price" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag) -# build_namedpoly!(alf, :goldsteinprice, 7) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 -# end +@testset "Goldstein-Price" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolfeas=1e-10) + build_namedpoly!(alf, :goldsteinprice, 7) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 60 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 3 atol=1e-4 rtol=1e-4 +end # out of memory during interpolation calculations # @testset "Heart" begin @@ -283,33 +285,35 @@ end @test Alfonso.get_dobj(alf) ≈ -36.71269068 atol=1e-4 rtol=1e-4 end -# @testset "Robinson" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6) -# build_namedpoly!(alf, :robinson, 8) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_niters(alf) <= 35 -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -# @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 -# end +@testset "Robinson" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :robinson, 8) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 40 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 + @test Alfonso.get_dobj(alf) ≈ 0.814814 atol=1e-4 rtol=1e-4 +end -# @testset "Rosenbrock" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=20) -# build_namedpoly!(alf, :rosenbrock, 3) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 -# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 -# end +@testset "Rosenbrock" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolfeas=1.1e-8) + build_namedpoly!(alf, :rosenbrock, 3) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 65 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-2 rtol=1e-2 + @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +end -# @testset "Schwefel" begin -# alf = Alfonso.AlfonsoOpt(verbose=verbflag, tolrelopt=1e-5, tolabsopt=1e-6, tolfeas=1e-6, maxpredsmallsteps=25) -# build_namedpoly!(alf, :schwefel, 4) -# @time Alfonso.solve!(alf) -# @test Alfonso.get_status(alf) == :Optimal -# @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 -# @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 -# end +@testset "Schwefel" begin + alf = Alfonso.AlfonsoOpt(verbose=verbflag) + build_namedpoly!(alf, :schwefel, 4) + @time Alfonso.solve!(alf) + @test Alfonso.get_niters(alf) <= 50 + @test Alfonso.get_status(alf) == :Optimal + @test Alfonso.get_pobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 + @test Alfonso.get_dobj(alf) ≈ 0 atol=1e-3 rtol=1e-3 +end @testset "large dense lp example (dense A)" begin alf = Alfonso.AlfonsoOpt(verbose=verbflag) diff --git a/test/runtests.jl b/test/runtests.jl index b72b564d0..9f658f650 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Alfonso using Test -verbflag = true # Alfonso verbose option +verbflag = false # Alfonso verbose option # TODO interpolation tests