From b20b341c86303cf8e3f244904ddb21328f32ef6b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Tue, 28 May 2019 02:32:49 -0400 Subject: [PATCH 01/20] start allowing generic floats or rationals --- src/Cones/Cones.jl | 20 +- src/Cones/orthant.jl | 12 +- src/Hypatia.jl | 4 +- src/Models/Models.jl | 27 +- src/Models/linear.jl | 32 +- src/Solvers/Solvers.jl | 50 +-- .../combined_step/naive.jl | 48 +-- .../combined_step/stepper.jl | 79 +++-- src/Solvers/homogeneous_self_dual/solver.jl | 186 +++++----- test/native.jl | 118 +++---- test/runtests.jl | 332 +++++++++--------- 11 files changed, 466 insertions(+), 442 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index b52d8cf7d..8d935174c 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -11,6 +11,8 @@ using ForwardDiff using DiffResults # using TimerOutputs +import Hypatia.HypReal + abstract type Cone end include("orthant.jl") @@ -29,7 +31,7 @@ include("wsospolyinterpmat.jl") include("wsospolyinterpsoc.jl") use_dual(cone::Cone) = cone.use_dual -load_point(cone::Cone, point::AbstractVector{Float64}) = (cone.point = point) +load_point(cone::Cone, point::AbstractVector{<:HypReal}) = (cone.point = point) dimension(cone::Cone) = cone.dim function factorize_hess(cone::Cone) @@ -48,17 +50,17 @@ inv_hess(cone::Cone) = inv(cone.F) hess_fact(cone::Cone) = cone.F # hessL(cone::Cone) = cone.F.L # inv_hessL(cone::Cone) = inv(cone.F.L) -hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::Cone) = mul!(prod, Symmetric(cone.H, :U), arr) -inv_hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::Cone) = ldiv!(prod, cone.F, arr) -# hessL_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::Cone) = mul!(prod, cone.F.L, arr) -# inv_hessL_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::Cone) = ldiv!(prod, cone.F.L, arr) +hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = mul!(prod, Symmetric(cone.H, :U), arr) +inv_hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = ldiv!(prod, cone.F, arr) +# hessL_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = mul!(prod, cone.F.L, arr) +# inv_hessL_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = ldiv!(prod, cone.F.L, arr) # utilities for converting between smat and svec forms (lower triangle) for symmetric matrices # TODO only need to do lower triangle if use symmetric matrix types const rt2 = sqrt(2) const rt2i = inv(rt2) -function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{T}) where {T <: Real} +function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{T}) where {T <: HypReal} k = 1 m = size(mat, 1) for i in 1:m, j in 1:i @@ -72,7 +74,7 @@ function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{T}) where {T return vec end -function svec_to_smat!(mat::AbstractMatrix{T}, vec::AbstractVector{T}) where {T <: Real} +function svec_to_smat!(mat::AbstractMatrix{T}, vec::AbstractVector{T}) where {T <: HypReal} k = 1 m = size(mat, 1) for i in 1:m, j in 1:i @@ -86,7 +88,7 @@ function svec_to_smat!(mat::AbstractMatrix{T}, vec::AbstractVector{T}) where {T return mat end -function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{Complex{T}}) where {T <: Real} +function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{Complex{T}}) where {T <: HypReal} k = 1 m = size(mat, 1) for i in 1:m, j in 1:i @@ -104,7 +106,7 @@ function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{Complex{T}}) return vec end -function svec_to_smat!(mat::AbstractMatrix{Complex{T}}, vec::AbstractVector{T}) where {T <: Real} +function svec_to_smat!(mat::AbstractMatrix{Complex{T}}, vec::AbstractVector{T}) where {T <: HypReal} k = 1 m = size(mat, 1) for i in 1:m, j in 1:i diff --git a/src/Cones/orthant.jl b/src/Cones/orthant.jl index 463db77d8..f178ca799 100644 --- a/src/Cones/orthant.jl +++ b/src/Cones/orthant.jl @@ -14,7 +14,7 @@ mutable struct Nonnegative <: Cone use_dual::Bool dim::Int - point::AbstractVector{Float64} + point::AbstractVector{<:HypReal} function Nonnegative(dim::Int, is_dual::Bool) cone = new() @@ -31,7 +31,7 @@ mutable struct Nonpositive <: Cone use_dual::Bool dim::Int - point::AbstractVector{Float64} + point::AbstractVector{<:HypReal} function Nonpositive(dim::Int, is_dual::Bool) cone = new() @@ -50,8 +50,8 @@ setup_data(cone::OrthantCone) = nothing get_nu(cone::OrthantCone) = cone.dim -set_initial_point(arr::AbstractVector{Float64}, cone::Nonnegative) = (@. arr = 1.0; arr) -set_initial_point(arr::AbstractVector{Float64}, cone::Nonpositive) = (@. arr = -1.0; arr) +set_initial_point(arr::AbstractVector{<:HypReal}, cone::Nonnegative) = (@. arr = 1.0; arr) +set_initial_point(arr::AbstractVector{<:HypReal}, cone::Nonpositive) = (@. arr = -1.0; arr) check_in_cone(cone::Nonnegative) = all(u -> (u > 0.0), cone.point) check_in_cone(cone::Nonpositive) = all(u -> (u < 0.0), cone.point) @@ -60,5 +60,5 @@ grad(cone::OrthantCone) = -inv.(cone.point) hess(cone::OrthantCone) = Diagonal(abs2.(inv.(cone.point))) inv_hess(cone::OrthantCone) = Diagonal(abs2.(cone.point)) -hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::OrthantCone) = (@. prod = arr / cone.point / cone.point; prod) -inv_hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::OrthantCone) = (@. prod = arr * cone.point * cone.point; prod) +hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::OrthantCone) = (@. prod = arr / cone.point / cone.point; prod) +inv_hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::OrthantCone) = (@. prod = arr * cone.point * cone.point; prod) diff --git a/src/Hypatia.jl b/src/Hypatia.jl index f4c755583..af35b848c 100644 --- a/src/Hypatia.jl +++ b/src/Hypatia.jl @@ -4,8 +4,10 @@ Copyright 2018, Chris Coey and contributors module Hypatia +const HypReal = Union{AbstractFloat, Rational} + # submodules -include("ModelUtilities/ModelUtilities.jl") +# include("ModelUtilities/ModelUtilities.jl") include("Cones/Cones.jl") include("Models/Models.jl") include("Solvers/Solvers.jl") diff --git a/src/Models/Models.jl b/src/Models/Models.jl index d78e2b0f6..d851e01d4 100644 --- a/src/Models/Models.jl +++ b/src/Models/Models.jl @@ -10,20 +10,21 @@ using LinearAlgebra using SparseArrays import Hypatia.Cones +import Hypatia.HypReal -mutable struct Point - x::Vector{Float64} - y::Vector{Float64} - z::Vector{Float64} - s::Vector{Float64} +mutable struct Point{T <: HypReal} + x::Vector{T} + y::Vector{T} + z::Vector{T} + s::Vector{T} - z_views::Vector{SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int}},true}} - s_views::Vector{SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int}},true}} - dual_views::Vector{SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int}},true}} - primal_views::Vector{SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int}},true}} + z_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} + s_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} + dual_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} + primal_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} - function Point(x, y, z, s, cones, cone_idxs) - point = new() + function Point(x::Vector{T}, y::Vector{T}, z::Vector{T}, s::Vector{T}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}) where {T <: HypReal} + point = new{T}() point.x = x point.y = y @@ -39,9 +40,9 @@ mutable struct Point end end -abstract type Model end +abstract type Model{T <: HypReal} end -abstract type LinearModel <: Model end +abstract type LinearModel{T <: HypReal} <: Model{T} end include("linear.jl") # TODO other model types eg quadratic obj, convex differentiable obj diff --git a/src/Models/linear.jl b/src/Models/linear.jl index b15995037..515466ed4 100644 --- a/src/Models/linear.jl +++ b/src/Models/linear.jl @@ -30,23 +30,23 @@ The primal-dual optimality conditions are: # TODO check model data consistency -mutable struct RawLinearModel <: LinearModel +mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} n::Int p::Int q::Int - c::Vector{Float64} - A::AbstractMatrix{Float64} - b::Vector{Float64} - G::AbstractMatrix{Float64} - h::Vector{Float64} + c::Vector{T} + A::AbstractMatrix{T} + b::Vector{T} + G::AbstractMatrix{T} + h::Vector{T} cones::Vector{Cones.Cone} - cone_idxs::Vector{UnitRange{Int}} - nu::Float64 + cone_idxs::Vector{UnitRange{Int}} # TODO allow generic Integer type for UnitRange parameter + nu::T - initial_point::Point + initial_point::Point{T} - function RawLinearModel(c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, G::AbstractMatrix{Float64}, h::Vector{Float64}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}) - model = new() + function RawLinearModel(c::Vector{T}, A::AbstractMatrix{T}, b::Vector{T}, G::AbstractMatrix{T}, h::Vector{T}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}) where {T <: HypReal} + model = new{T}() model.n = length(c) model.p = length(b) @@ -58,10 +58,10 @@ mutable struct RawLinearModel <: LinearModel model.h = h model.cones = cones model.cone_idxs = cone_idxs - model.nu = isempty(cones) ? 0.0 : sum(Cones.get_nu, cones) + model.nu = isempty(cones) ? zero(T) : sum(Cones.get_nu, cones) # get initial point - point = Point(Float64[], Float64[], similar(h), similar(h), cones, cone_idxs) + point = Point(T[], T[], similar(h), similar(h), cones, cone_idxs) set_initial_cone_point(point, model.cones) # solve for y as least squares solution to A'y = -c - G'z @@ -80,7 +80,7 @@ mutable struct RawLinearModel <: LinearModel end end -mutable struct PreprocessedLinearModel <: LinearModel +mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} c_raw::Vector{Float64} A_raw::AbstractMatrix{Float64} b_raw::Vector{Float64} @@ -108,8 +108,8 @@ mutable struct PreprocessedLinearModel <: LinearModel # TODO could optionally rescale rows of [A, b] and [G, h] and [A', G', c] and variables # NOTE (pivoted) QR factorizations are usually rank-revealing but may be unreliable, see http://www.math.sjsu.edu/~foster/rankrevealingcode.html - function PreprocessedLinearModel(c::Vector{Float64}, A::AbstractMatrix{Float64}, b::Vector{Float64}, G::AbstractMatrix{Float64}, h::Vector{Float64}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}; tol_QR::Float64 = 1e-13) - model = new() + function PreprocessedLinearModel(c::Vector{T}, A::AbstractMatrix{T}, b::Vector{T}, G::AbstractMatrix{T}, h::Vector{Float64}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}; tol_QR::Float64 = 1e-13) where {T <: HypReal} + model = new{T}() model.c_raw = c model.A_raw = A model.b_raw = b diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index 5bb96212b..0f06e0e1c 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -14,19 +14,20 @@ using TimerOutputs import Hypatia.Cones import Hypatia.Models +import Hypatia.HypReal -abstract type Solver end +abstract type Solver{T <: HypReal} end # homogeneous self-dual embedding algorithm -abstract type HSDStepper end -abstract type CombinedHSDSystemSolver end +abstract type HSDStepper{T <: HypReal} end +abstract type CombinedHSDSystemSolver{T <: HypReal} end include("homogeneous_self_dual/solver.jl") include("homogeneous_self_dual/combined_step/stepper.jl") include("homogeneous_self_dual/combined_step/naive.jl") -include("homogeneous_self_dual/combined_step/naiveelim.jl") -include("homogeneous_self_dual/combined_step/symindef.jl") -include("homogeneous_self_dual/combined_step/qrchol.jl") -# include("homogeneous_self_dual/combined_step/cholchol.jl") +# include("homogeneous_self_dual/combined_step/naiveelim.jl") +# include("homogeneous_self_dual/combined_step/symindef.jl") +# include("homogeneous_self_dual/combined_step/qrchol.jl") +# # include("homogeneous_self_dual/combined_step/cholchol.jl") # TODO sequential quadratic algorithm for linear, quadratic, and smooth convex models @@ -45,29 +46,30 @@ get_z(solver::Solver) = copy(solver.point.z) get_z(solver::Solver, model::Models.Model) = get_z(solver) get_x(solver::Solver) = copy(solver.point.x) -function get_x(solver::Solver, model::Models.PreprocessedLinearModel) - x = zeros(length(model.c_raw)) +function get_x(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where T + x = zeros(T, length(model.c_raw)) x[model.x_keep_idxs] = solver.point.x # unpreprocess solver's solution return x end get_x(solver::Solver, model::Models.Model) = get_x(solver) get_y(solver::Solver) = copy(solver.point.y) -function get_y(solver::Solver, model::Models.PreprocessedLinearModel) - y = zeros(length(model.b_raw)) +function get_y(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where T + y = zeros(T, length(model.b_raw)) y[model.y_keep_idxs] = solver.point.y # unpreprocess solver's solution return y end get_y(solver::Solver, model::Models.Model) = get_y(solver) # check conic certificates are valid +# TODO pick default tols based on T function get_certificates( - solver::Solver, - model::Models.LinearModel; + solver::Solver{T}, + model::Models.LinearModel{T}; test::Bool = true, - atol::Float64 = 1e-4, - rtol::Float64 = 1e-4, - ) + atol = max(1e-5, sqrt(sqrt(eps(T)))), + rtol = atol, + ) where T status = get_status(solver) primal_obj = get_primal_obj(solver) dual_obj = get_dual_obj(solver) @@ -83,20 +85,20 @@ function get_certificates( @test A * x ≈ b atol=atol rtol=rtol @test G * x + s ≈ h atol=atol rtol=rtol @test G' * z + A' * y ≈ -c atol=atol rtol=rtol - @test dot(s, z) ≈ 0.0 atol=atol rtol=rtol - @test dot(c, x) ≈ primal_obj atol=1e-8 rtol=1e-8 - @test dot(b, y) + dot(h, z) ≈ -dual_obj atol=1e-8 rtol=1e-8 + @test dot(s, z) ≈ zero(T) atol=atol rtol=rtol + @test dot(c, x) ≈ primal_obj atol=atol^2 rtol=rtol^2 + @test dot(b, y) + dot(h, z) ≈ -dual_obj atol=atol^2 rtol=rtol^2 elseif status == :PrimalInfeasible # @test isnan(primal_obj) - @test dual_obj > 0 - @test dot(b, y) + dot(h, z) ≈ -dual_obj atol=1e-8 rtol=1e-8 + @test dual_obj > zero(T) + @test dot(b, y) + dot(h, z) ≈ -dual_obj atol=atol^2 rtol=rtol^2 @test G' * z ≈ -A' * y atol=atol rtol=rtol elseif status == :DualInfeasible # @test isnan(dual_obj) - @test primal_obj < 0 - @test dot(c, x) ≈ primal_obj atol=1e-8 rtol=1e-8 + @test primal_obj < zero(T) + @test dot(c, x) ≈ primal_obj atol=atol^2 rtol=rtol^2 @test G * x ≈ -s atol=atol rtol=rtol - @test A * x ≈ zeros(length(y)) atol=atol rtol=rtol + @test A * x ≈ zeros(T, length(y)) atol=atol rtol=rtol elseif status == :IllPosed # TODO primal vs dual ill-posed statuses and conditions end diff --git a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl index 694149117..b64af0acd 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl @@ -1,11 +1,11 @@ -mutable struct NaiveCombinedHSDSystemSolver <: CombinedHSDSystemSolver +mutable struct NaiveCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSolver{T} use_sparse::Bool lhs_copy lhs lhs_H_k - rhs::Matrix{Float64} + rhs::Matrix{T} x1 x2 @@ -19,30 +19,30 @@ mutable struct NaiveCombinedHSDSystemSolver <: CombinedHSDSystemSolver s2 kap_row::Int - function NaiveCombinedHSDSystemSolver(model::Models.LinearModel; use_sparse::Bool = false) + function NaiveCombinedHSDSystemSolver(model::Models.LinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} (n, p, q) = (model.n, model.p, model.q) - system_solver = new() + system_solver = new{T}() system_solver.use_sparse = use_sparse # x y z kap s tau if use_sparse - system_solver.lhs_copy = Float64[ - spzeros(n,n) model.A' model.G' spzeros(n) spzeros(n,q) model.c; - -model.A spzeros(p,p) spzeros(p,q) spzeros(p) spzeros(p,q) model.b; - spzeros(q,n) spzeros(q,p) sparse(1.0I,q,q) spzeros(q) sparse(1.0I,q,q) spzeros(q); - spzeros(1,n) spzeros(1,p) spzeros(1,q) 1.0 spzeros(1,q) 1.0; - -model.G spzeros(q,p) spzeros(q,q) spzeros(q) sparse(-1.0I,q,q) model.h; - -model.c' -model.b' -model.h' -1.0 spzeros(1,q) 0.0; + system_solver.lhs_copy = T[ + spzeros(T,n,n) model.A' model.G' spzeros(T,n) spzeros(T,n,q) model.c; + -model.A spzeros(T,p,p) spzeros(T,p,q) spzeros(T,p) spzeros(T,p,q) model.b; + spzeros(T,q,n) spzeros(T,q,p) sparse(one(T)*I,q,q) spzeros(T,q) sparse(one(T)*I,q,q) spzeros(T,q); + spzeros(T,1,n) spzeros(T,1,p) spzeros(T,1,q) one(T) spzeros(T,1,q) one(T); + -model.G spzeros(T,q,p) spzeros(T,q,q) spzeros(T,q) sparse(-one(T)*I,q,q) model.h; + -model.c' -model.b' -model.h' -one(T) spzeros(T,1,q) zero(T); ] @assert issparse(system_solver.lhs_copy) else - system_solver.lhs_copy = [ - zeros(n,n) model.A' model.G' zeros(n) zeros(n,q) model.c; - -model.A zeros(p,p) zeros(p,q) zeros(p) zeros(p,q) model.b; - zeros(q,n) zeros(q,p) Matrix(1.0I,q,q) zeros(q) Matrix(1.0I,q,q) zeros(q); - zeros(1,n) zeros(1,p) zeros(1,q) 1.0 zeros(1,q) 1.0; - -model.G zeros(q,p) zeros(q,q) zeros(q) Matrix(-1.0I,q,q) model.h; - -model.c' -model.b' -model.h' -1.0 zeros(1,q) 0.0; + system_solver.lhs_copy = T[ + zeros(T,n,n) model.A' model.G' zeros(T,n) zeros(T,n,q) model.c; + -model.A zeros(T,p,p) zeros(T,p,q) zeros(T,p) zeros(T,p,q) model.b; + zeros(T,q,n) zeros(T,q,p) Matrix(one(T)*I,q,q) zeros(T,q) Matrix(one(T)*I,q,q) zeros(T,q); + zeros(T,1,n) zeros(T,1,p) zeros(T,1,q) one(T) zeros(T,1,q) one(T); + -model.G zeros(T,q,p) zeros(T,q,q) zeros(T,q) Matrix(-one(T)*I,q,q) model.h; + -model.c' -model.b' -model.h' -one(T) zeros(T,1,q) zero(T); ] end @@ -54,7 +54,7 @@ mutable struct NaiveCombinedHSDSystemSolver <: CombinedHSDSystemSolver end system_solver.lhs_H_k = [view_k(k) for k in eachindex(model.cones)] - system_solver.rhs = zeros(size(system_solver.lhs, 1), 2) + system_solver.rhs = zeros(T, size(system_solver.lhs, 1), 2) rows = 1:n system_solver.x1 = view(system_solver.rhs, rows, 1) system_solver.x2 = view(system_solver.rhs, rows, 2) @@ -76,7 +76,7 @@ mutable struct NaiveCombinedHSDSystemSolver <: CombinedHSDSystemSolver end end -function get_combined_directions(solver::HSDSolver, system_solver::NaiveCombinedHSDSystemSolver) +function get_combined_directions(solver::HSDSolver{T}, system_solver::NaiveCombinedHSDSystemSolver{T}) where T model = solver.model cones = model.cones lhs = system_solver.lhs @@ -96,9 +96,9 @@ function get_combined_directions(solver::HSDSolver, system_solver::NaiveCombined # update rhs matrix system_solver.x1 .= solver.x_residual - system_solver.x2 .= 0.0 + system_solver.x2 .= zero(T) system_solver.y1 .= solver.y_residual - system_solver.y2 .= 0.0 + system_solver.y2 .= zero(T) for k in eachindex(cones) duals_k = solver.point.dual_views[k] g = Cones.grad(cones[k]) @@ -106,11 +106,11 @@ function get_combined_directions(solver::HSDSolver, system_solver::NaiveCombined @. system_solver.z2_k[k] = -duals_k - mu * g end system_solver.s1 .= solver.z_residual - system_solver.s2 .= 0.0 + system_solver.s2 .= zero(T) rhs[kap_row, 1] = -kap rhs[kap_row, 2] = -kap + mu / tau rhs[end, 1] = kap + solver.primal_obj_t - solver.dual_obj_t - rhs[end, 2] = 0.0 + rhs[end, 2] = zero(T) # solve system if system_solver.use_sparse diff --git a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl index 225f6f51a..7d6c34670 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl @@ -1,16 +1,16 @@ -mutable struct CombinedHSDStepper <: HSDStepper - system_solver::CombinedHSDSystemSolver - max_nbhd::Float64 +mutable struct CombinedHSDStepper{T <: HypReal} <: HSDStepper{T} + system_solver::CombinedHSDSystemSolver{T} + max_nbhd::T - prev_affine_alpha::Float64 + prev_affine_alpha::T prev_affine_alpha_iters::Int - prev_gamma::Float64 - prev_alpha::Float64 + prev_gamma::T + prev_alpha::T prev_alpha_iters::Int - z_temp::Vector{Float64} - s_temp::Vector{Float64} + z_temp::Vector{T} + s_temp::Vector{T} primal_views dual_views nbhd_temp @@ -18,26 +18,26 @@ mutable struct CombinedHSDStepper <: HSDStepper cones_loaded::Vector{Bool} function CombinedHSDStepper( - model::Models.LinearModel; - system_solver::CombinedHSDSystemSolver = (model isa Models.PreprocessedLinearModel ? QRCholCombinedHSDSystemSolver(model) : NaiveCombinedHSDSystemSolver(model)), - max_nbhd::Float64 = 0.75, - ) - stepper = new() + model::Models.LinearModel{T}; + system_solver::CombinedHSDSystemSolver{T} = (model isa Models.PreprocessedLinearModel{T} ? QRCholCombinedHSDSystemSolver(model) : NaiveCombinedHSDSystemSolver(model)), + max_nbhd::T = T(0.75), + ) where {T <: HypReal} + stepper = new{T}() stepper.system_solver = system_solver stepper.max_nbhd = max_nbhd - stepper.prev_affine_alpha = 0.9999 + stepper.prev_affine_alpha = T(0.9999) stepper.prev_affine_alpha_iters = 0 - stepper.prev_gamma = 0.9999 - stepper.prev_alpha = 0.9999 + stepper.prev_gamma = T(0.9999) + stepper.prev_alpha = T(0.9999) stepper.prev_alpha_iters = 0 stepper.z_temp = similar(model.h) stepper.s_temp = similar(model.h) stepper.primal_views = [view(Cones.use_dual(model.cones[k]) ? stepper.z_temp : stepper.s_temp, model.cone_idxs[k]) for k in eachindex(model.cones)] stepper.dual_views = [view(Cones.use_dual(model.cones[k]) ? stepper.s_temp : stepper.z_temp, model.cone_idxs[k]) for k in eachindex(model.cones)] - stepper.nbhd_temp = [Vector{Float64}(undef, length(model.cone_idxs[k])) for k in eachindex(model.cones)] + stepper.nbhd_temp = [Vector{T}(undef, length(model.cone_idxs[k])) for k in eachindex(model.cones)] stepper.cones_outside_nbhd = trues(length(model.cones)) stepper.cones_loaded = trues(length(model.cones)) @@ -45,7 +45,7 @@ mutable struct CombinedHSDStepper <: HSDStepper end end -function step(solver::HSDSolver, stepper::CombinedHSDStepper) +function step(solver::HSDSolver{T}, stepper::CombinedHSDStepper{T}) where T model = solver.model point = solver.point @@ -53,8 +53,8 @@ function step(solver::HSDSolver, stepper::CombinedHSDStepper) @timeit solver.timer "directions" (x_pred, x_corr, y_pred, y_corr, z_pred, z_corr, s_pred, s_corr, tau_pred, tau_corr, kap_pred, kap_corr) = get_combined_directions(solver, stepper.system_solver) # calculate correction factor gamma by finding distance affine_alpha for stepping in affine direction - @timeit solver.timer "aff_alpha" (affine_alpha, affine_alpha_iters) = find_max_alpha_in_nbhd(z_pred, s_pred, tau_pred, kap_pred, 0.9999, stepper.prev_affine_alpha, stepper, solver) - gamma = (1.0 - affine_alpha)^3 # TODO allow different function (heuristic) + @timeit solver.timer "aff_alpha" (affine_alpha, affine_alpha_iters) = find_max_alpha_in_nbhd(z_pred, s_pred, tau_pred, kap_pred, T(0.9999), stepper.prev_affine_alpha, stepper, solver) + gamma = (one(T) - affine_alpha)^3 # TODO allow different function (heuristic) stepper.prev_affine_alpha = affine_alpha stepper.prev_affine_alpha_iters = affine_alpha_iters stepper.prev_gamma = gamma @@ -62,7 +62,7 @@ function step(solver::HSDSolver, stepper::CombinedHSDStepper) # find distance alpha for stepping in combined direction z_comb = z_pred s_comb = s_pred - pred_factor = 1.0 - gamma + pred_factor = one(T) - gamma @. z_comb = pred_factor * z_pred + gamma * z_corr @. s_comb = pred_factor * s_pred + gamma * s_corr tau_comb = pred_factor * tau_pred + gamma * tau_corr @@ -76,7 +76,7 @@ function step(solver::HSDSolver, stepper::CombinedHSDStepper) s_comb = s_corr tau_comb = tau_corr kap_comb = kap_corr - @timeit solver.timer "corr_alpha" (alpha, corr_alpha_iters) = find_max_alpha_in_nbhd(z_comb, s_comb, tau_comb, kap_comb, stepper.max_nbhd, 0.9999, stepper, solver) + @timeit solver.timer "corr_alpha" (alpha, corr_alpha_iters) = find_max_alpha_in_nbhd(z_comb, s_comb, tau_comb, kap_comb, stepper.max_nbhd, T(0.9999), stepper, solver) alpha_iters += corr_alpha_iters @. point.x += alpha * x_corr @@ -95,7 +95,7 @@ function step(solver::HSDSolver, stepper::CombinedHSDStepper) solver.kap += alpha * kap_comb calc_mu(solver) - @assert solver.tau > 0.0 && solver.kap > 0.0 && solver.mu > 0.0 + @assert solver.tau > zero(T) && solver.kap > zero(T) && solver.mu > zero(T) return point end @@ -123,7 +123,16 @@ end # backtracking line search to find large distance to step in direction while remaining inside cones and inside a given neighborhood # TODO try infinite norm neighborhood, which is cheaper to check, or enforce that for each cone we are within a smaller neighborhood separately -function find_max_alpha_in_nbhd(z_dir::AbstractVector{Float64}, s_dir::AbstractVector{Float64}, tau_dir::Float64, kap_dir::Float64, nbhd::Float64, prev_alpha::Float64, stepper::CombinedHSDStepper, solver::HSDSolver) +function find_max_alpha_in_nbhd( + z_dir::AbstractVector{T}, + s_dir::AbstractVector{T}, + tau_dir::T, + kap_dir::T, + nbhd::T, + prev_alpha::T, + stepper::CombinedHSDStepper, + solver::HSDSolver, + ) where T point = solver.point model = solver.model cones = model.cones @@ -131,19 +140,19 @@ function find_max_alpha_in_nbhd(z_dir::AbstractVector{Float64}, s_dir::AbstractV s_temp = stepper.s_temp # alpha = 0.9999 # TODO make this an option - alpha = min(prev_alpha * 1.4, 0.9999) + alpha = min(prev_alpha * T(1.4), T(0.9999)) - if kap_dir < 0.0 + if kap_dir < zero(T) alpha = min(alpha, -solver.kap / kap_dir) end - if tau_dir < 0.0 + if tau_dir < zero(T) alpha = min(alpha, -solver.tau / tau_dir) end # TODO what about mu? quadratic equation. need dot(s_temp, z_temp) + tau_temp * kap_temp > 0 stepper.cones_outside_nbhd .= true - tau_temp = kap_temp = taukap_temp = mu_temp = 0.0 + tau_temp = kap_temp = taukap_temp = mu_temp = zero(T) num_pred_iters = 0 while num_pred_iters < 100 num_pred_iters += 1 @@ -153,9 +162,9 @@ function find_max_alpha_in_nbhd(z_dir::AbstractVector{Float64}, s_dir::AbstractV tau_temp = solver.tau + alpha * tau_dir kap_temp = solver.kap + alpha * kap_dir taukap_temp = tau_temp * kap_temp - mu_temp = (dot(s_temp, z_temp) + taukap_temp) / (1.0 + model.nu) + mu_temp = (dot(s_temp, z_temp) + taukap_temp) / (one(T) + model.nu) - if mu_temp > 0.0 + if mu_temp > zero(T) # accept primal iterate if it is inside the cone and neighborhood # first check incone for whichever cones were not incone last linesearch iteration in_cones = true @@ -195,11 +204,11 @@ function find_max_alpha_in_nbhd(z_dir::AbstractVector{Float64}, s_dir::AbstractV # mul!(stepper.nbhd_temp[k], Cones.inv_hess(cone_k), stepper.dual_views[k]) nbhd_sqr_k = dot(stepper.dual_views[k], stepper.nbhd_temp[k]) - if nbhd_sqr_k <= -1e-5 + if nbhd_sqr_k <= T(-1e-5) println("numerical issue for cone: nbhd_sqr_k is $nbhd_sqr_k") in_nbhds = false break - elseif nbhd_sqr_k > 0.0 + elseif nbhd_sqr_k > zero(T) full_nbhd_sqr += nbhd_sqr_k if full_nbhd_sqr > abs2(mu_temp * nbhd) in_nbhds = false @@ -214,14 +223,14 @@ function find_max_alpha_in_nbhd(z_dir::AbstractVector{Float64}, s_dir::AbstractV end end - if alpha < 1e-2 # TODO option for parameter + if alpha < T(1e-2) # TODO option for parameter # alpha is very small so just let it be zero - alpha = 0.0 + alpha = zero(T) break end # iterate is outside the neighborhood: decrease alpha - alpha *= 0.8 # TODO option for parameter + alpha *= T(0.8) # TODO option for parameter end return (alpha, num_pred_iters) diff --git a/src/Solvers/homogeneous_self_dual/solver.jl b/src/Solvers/homogeneous_self_dual/solver.jl index e89e25679..0b4ec4faa 100644 --- a/src/Solvers/homogeneous_self_dual/solver.jl +++ b/src/Solvers/homogeneous_self_dual/solver.jl @@ -3,58 +3,60 @@ Copyright 2018, Chris Coey and contributors interior point type and functions for algorithms based on homogeneous self dual embedding -TODO make internal statuses types +TODO +make internal statuses types +set default tolerances based on real type =# -mutable struct HSDSolver <: Solver - model::Models.LinearModel +mutable struct HSDSolver{T <: HypReal} <: Solver{T} + model::Models.LinearModel{T} - stepper::HSDStepper + stepper::HSDStepper{T} verbose::Bool - tol_rel_opt::Float64 - tol_abs_opt::Float64 - tol_feas::Float64 - tol_slow::Float64 + tol_rel_opt::T + tol_abs_opt::T + tol_feas::T + tol_slow::T max_iters::Int time_limit::Float64 - x_conv_tol::Float64 - y_conv_tol::Float64 - z_conv_tol::Float64 - - point::Models.Point - tau::Float64 - kap::Float64 - mu::Float64 - - x_residual::Vector{Float64} - y_residual::Vector{Float64} - z_residual::Vector{Float64} - x_norm_res_t::Float64 - y_norm_res_t::Float64 - z_norm_res_t::Float64 - x_norm_res::Float64 - y_norm_res::Float64 - z_norm_res::Float64 - - primal_obj_t::Float64 - dual_obj_t::Float64 - primal_obj::Float64 - dual_obj::Float64 - gap::Float64 - rel_gap::Float64 - x_feas::Float64 - y_feas::Float64 - z_feas::Float64 + x_conv_tol::T + y_conv_tol::T + z_conv_tol::T + + point::Models.Point{T} + tau::T + kap::T + mu::T + + x_residual::Vector{T} + y_residual::Vector{T} + z_residual::Vector{T} + x_norm_res_t::T + y_norm_res_t::T + z_norm_res_t::T + x_norm_res::T + y_norm_res::T + z_norm_res::T + + primal_obj_t::T + dual_obj_t::T + primal_obj::T + dual_obj::T + gap::T + rel_gap::T + x_feas::T + y_feas::T + z_feas::T prev_is_slow::Bool prev2_is_slow::Bool - prev_gap::Float64 - prev_rel_gap::Float64 - prev_x_feas::Float64 - prev_y_feas::Float64 - prev_z_feas::Float64 + prev_gap::T + prev_rel_gap::T + prev_x_feas::T + prev_y_feas::T + prev_z_feas::T status::Symbol num_iters::Int @@ -62,58 +64,58 @@ mutable struct HSDSolver <: Solver timer::TimerOutput function HSDSolver( - model::Models.LinearModel; - stepper::HSDStepper = CombinedHSDStepper(model), + model::Models.LinearModel{T}; + stepper::HSDStepper{T} = CombinedHSDStepper(model), verbose::Bool = true, - tol_rel_opt::Float64 = 1e-6, - tol_abs_opt::Float64 = 1e-7, - tol_feas::Float64 = 1e-7, - tol_slow::Float64 = 5e-3, - max_iters::Int = 500, - time_limit::Float64 = 3e2, - ) - solver = new() + tol_rel_opt = max(1e-10, sqrt(eps(T))), + tol_abs_opt = 1e1 * tol_rel_opt, + tol_feas = tol_abs_opt, + tol_slow = 5e-3, + max_iters::Int = 250, + time_limit = 3e2, + ) where {T <: HypReal} + solver = new{T}() solver.model = model solver.stepper = stepper solver.verbose = verbose - solver.tol_rel_opt = tol_rel_opt - solver.tol_abs_opt = tol_abs_opt - solver.tol_feas = tol_feas - solver.tol_slow = tol_slow + solver.tol_rel_opt = T(tol_rel_opt) + solver.tol_abs_opt = T(tol_abs_opt) + solver.tol_feas = T(tol_feas) + solver.tol_slow = T(tol_slow) solver.max_iters = max_iters solver.time_limit = time_limit - solver.x_conv_tol = inv(max(1.0, norm(model.c))) - solver.y_conv_tol = inv(max(1.0, norm(model.b))) - solver.z_conv_tol = inv(max(1.0, norm(model.h))) + solver.x_conv_tol = inv(max(one(T), norm(model.c))) + solver.y_conv_tol = inv(max(one(T), norm(model.b))) + solver.z_conv_tol = inv(max(one(T), norm(model.h))) solver.point = model.initial_point - solver.tau = 1.0 - solver.kap = 1.0 - solver.mu = NaN + solver.tau = one(T) + solver.kap = one(T) + solver.mu = T(NaN) solver.x_residual = similar(model.c) solver.y_residual = similar(model.b) solver.z_residual = similar(model.h) - solver.x_norm_res_t = NaN - solver.y_norm_res_t = NaN - solver.z_norm_res_t = NaN - solver.x_norm_res = NaN - solver.y_norm_res = NaN - solver.z_norm_res = NaN - - solver.primal_obj_t = NaN - solver.dual_obj_t = NaN - solver.primal_obj = NaN - solver.dual_obj = NaN - solver.gap = NaN - solver.rel_gap = NaN - solver.x_feas = NaN - solver.y_feas = NaN - solver.z_feas = NaN + solver.x_norm_res_t = T(NaN) + solver.y_norm_res_t = T(NaN) + solver.z_norm_res_t = T(NaN) + solver.x_norm_res = T(NaN) + solver.y_norm_res = T(NaN) + solver.z_norm_res = T(NaN) + + solver.primal_obj_t = T(NaN) + solver.dual_obj_t = T(NaN) + solver.primal_obj = T(NaN) + solver.dual_obj = T(NaN) + solver.gap = T(NaN) + solver.rel_gap = T(NaN) + solver.x_feas = T(NaN) + solver.y_feas = T(NaN) + solver.z_feas = T(NaN) solver.prev_is_slow = false solver.prev2_is_slow = false @@ -137,12 +139,12 @@ get_kappa(solver::HSDSolver) = solver.kap get_mu(solver::HSDSolver) = solver.mu # TODO maybe use iteration interface rather than while loop -function solve(solver::HSDSolver) +function solve(solver::HSDSolver{T}) where T solver.status = :SolveCalled start_time = time() calc_mu(solver) - if isnan(solver.mu) || abs(1.0 - solver.mu) > 1e-6 + if isnan(solver.mu) || abs(one(T) - solver.mu) > T(1e-6) error("initial mu is $(solver.mu) (should be 1.0)") end @@ -186,7 +188,7 @@ function solve(solver::HSDSolver) return end -function calc_convergence_params(solver::HSDSolver) +function calc_convergence_params(solver::HSDSolver{T}) where T model = solver.model point = solver.point @@ -201,12 +203,12 @@ function calc_convergence_params(solver::HSDSolver) solver.primal_obj = solver.primal_obj_t / solver.tau solver.dual_obj = solver.dual_obj_t / solver.tau solver.gap = dot(point.z, point.s) # TODO maybe should adapt original Alfonso condition instead of using this CVXOPT condition - if solver.primal_obj < 0.0 + if solver.primal_obj < zero(T) solver.rel_gap = solver.gap / -solver.primal_obj - elseif solver.dual_obj > 0.0 + elseif solver.dual_obj > zero(T) solver.rel_gap = solver.gap / solver.dual_obj else - solver.rel_gap = NaN + solver.rel_gap = T(NaN) end solver.x_feas = solver.x_norm_res * solver.x_conv_tol @@ -216,7 +218,7 @@ function calc_convergence_params(solver::HSDSolver) return end -function check_convergence(solver::HSDSolver) +function check_convergence(solver::HSDSolver{T}) where T # check convergence criteria # TODO nearly primal or dual infeasible or nearly optimal cases? if max(solver.x_feas, solver.y_feas, solver.z_feas) <= solver.tol_feas && @@ -225,7 +227,7 @@ function check_convergence(solver::HSDSolver) solver.status = :Optimal return true end - if solver.dual_obj_t > 0.0 + if solver.dual_obj_t > zero(T) infres_pr = solver.x_norm_res_t * solver.x_conv_tol / solver.dual_obj_t if infres_pr <= solver.tol_feas solver.verbose && println("primal infeasibility detected; terminating") @@ -233,7 +235,7 @@ function check_convergence(solver::HSDSolver) return true end end - if solver.primal_obj_t < 0.0 + if solver.primal_obj_t < zero(T) infres_du = -max(solver.y_norm_res_t * solver.y_conv_tol, solver.z_norm_res_t * solver.z_conv_tol) / solver.primal_obj_t if infres_du <= solver.tol_feas solver.verbose && println("dual infeasibility detected; terminating") @@ -241,19 +243,19 @@ function check_convergence(solver::HSDSolver) return true end end - if solver.mu <= solver.tol_feas * 1e-2 && solver.tau <= solver.tol_feas * 1e-2 * min(1.0, solver.kap) + if solver.mu <= solver.tol_feas * T(1e-2) && solver.tau <= solver.tol_feas * T(1e-2) * min(one(T), solver.kap) solver.verbose && println("ill-posedness detected; terminating") solver.status = :IllPosed return true end - max_improve = 0.0 + max_improve = zero(T) for (curr, prev) in ((solver.gap, solver.prev_gap), (solver.rel_gap, solver.prev_rel_gap), (solver.x_feas, solver.prev_x_feas), (solver.y_feas, solver.prev_y_feas), (solver.z_feas, solver.prev_z_feas)) if isnan(prev) || isnan(curr) continue end - max_improve = max(max_improve, (prev - curr) / (abs(prev) + 1e-8)) + max_improve = max(max_improve, (prev - curr) / (abs(prev) + T(1e-6))) end if max_improve < solver.tol_slow if solver.prev_is_slow && solver.prev2_is_slow @@ -272,7 +274,7 @@ function check_convergence(solver::HSDSolver) return false end -function calc_residual(solver::HSDSolver) +function calc_residual(solver::HSDSolver{T}) where T model = solver.model point = solver.point @@ -302,8 +304,8 @@ function calc_residual(solver::HSDSolver) return end -function calc_mu(solver::HSDSolver) +function calc_mu(solver::HSDSolver{T}) where T solver.mu = (dot(solver.point.z, solver.point.s) + solver.tau * solver.kap) / - (1.0 + solver.model.nu) + (one(T) + solver.model.nu) return solver.mu end diff --git a/test/native.jl b/test/native.jl index f32ae9a52..86fa7d0b3 100644 --- a/test/native.jl +++ b/test/native.jl @@ -6,20 +6,20 @@ import Random # solve model, check conic certificates are valid and return certificate data function solve_and_check( - c::Vector{Float64}, - A::AbstractMatrix{Float64}, - b::Vector{Float64}, - G::AbstractMatrix{Float64}, - h::Vector{Float64}, + c::Vector{T}, + A::AbstractMatrix{T}, + b::Vector{T}, + G::AbstractMatrix{T}, + h::Vector{T}, cones::Vector{<:CO.Cone}, cone_idxs::Vector{UnitRange{Int}}, system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool; - atol::Float64 = 1e-4, - rtol::Float64 = 1e-4, + atol = max(1e-5, sqrt(sqrt(eps(T)))), + rtol = atol, use_sparse::Bool = false, - ) + ) where {T <: HypReal} model = linear_model(c, A, b, G, h, cones, cone_idxs) stepper = SO.CombinedHSDStepper(model, system_solver = system_solver(model, use_sparse = use_sparse)) solver = SO.HSDSolver(model, verbose = verbose, stepper = stepper) @@ -27,7 +27,7 @@ function solve_and_check( return SO.get_certificates(solver, model, test = true, atol = atol, rtol = rtol) end -function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[-1, 0] A = Matrix{Float64}(undef, 0, 2) b = Float64[] @@ -51,7 +51,7 @@ function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mo end end -function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -74,7 +74,7 @@ function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.status == :Optimal end -function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -90,7 +90,7 @@ function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear @test_throws ErrorException("some primal equality constraints are inconsistent") linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) end -function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -107,23 +107,23 @@ function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear @test_throws ErrorException("some dual equality constraints are inconsistent") linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) end -function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (6, 3, 6) - c = rand(0.0:9.0, n) - A = rand(-9.0:9.0, p, n) - b = A * ones(n) - h = zeros(q) + c = rand(T(0):T(9), n) + A = rand(T(-9):T(9), p, n) + b = A * ones(T, n) + h = zeros(T, q) cone_idxs = [1:q] # nonnegative cone - G = SparseMatrixCSC(-1.0I, q, n) + G = SparseMatrixCSC(-one(T) * I, q, n) cones = [CO.Nonnegative(q)] rnn = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) @test rnn.status == :Optimal # nonpositive cone - G = SparseMatrixCSC(1.0I, q, n) + G = SparseMatrixCSC(one(T) * I, q, n) cones = [CO.Nonpositive(q)] rnp = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) @test rnp.status == :Optimal @@ -131,14 +131,14 @@ function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode @test rnp.primal_obj ≈ rnn.primal_obj atol=1e-4 rtol=1e-4 end -function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (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 = rand(q, n) - Matrix(2.0I, q, n) - h = G * ones(n) + c = rand(T(0):T(9), n) + A = rand(T(-9):T(9), p, n) + b = A * ones(T, n) + G = rand(T, q, n) - Matrix(T(2) * I, q, n) + h = G * ones(T, n) cone_idxs = [1:q] # use dual barrier @@ -154,7 +154,7 @@ function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 end -function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (15, 6, 15) c = rand(0.0:9.0, n) @@ -177,7 +177,7 @@ function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 end -function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (n, p, q) = (5, 2, 10) c = rand(0.0:9.0, n) @@ -201,7 +201,7 @@ function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 end -function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, -1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, inv(sqrt(2))] @@ -217,7 +217,7 @@ function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.y ≈ [1, 1] atol=1e-4 rtol=1e-4 end -function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) c = Float64[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) @@ -232,7 +232,7 @@ function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.primal_obj ≈ 1 atol=1e-4 rtol=1e-4 end -function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[1, 0, 0, 0, 0, 0] A = zeros(0, 6) b = zeros(0) @@ -247,7 +247,7 @@ function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.x ≈ zeros(6) atol=1e-4 rtol=1e-4 end -function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, -0.4] @@ -263,7 +263,7 @@ function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.y ≈ [1, 0] atol=1e-4 rtol=1e-4 end -function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) c = Float64[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) @@ -278,7 +278,7 @@ function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.primal_obj ≈ 1 atol=1e-4 rtol=1e-4 end -function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) l = 3 L = 2l + 1 c = collect(Float64, -l:l) @@ -298,7 +298,7 @@ function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test sum(abs, r.x) ≈ 1 atol=1e-4 rtol=1e-4 end -function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, -1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, inv(sqrt(2))] @@ -317,7 +317,7 @@ function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, -1, -1] A = Float64[1 0 0] b = Float64[0] @@ -335,7 +335,7 @@ function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 0, -1, -1] A = Float64[1 0 0 0; 0 1 0 0] b = Float64[1/2, 1] @@ -353,7 +353,7 @@ function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 0, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1/2, 1] / sqrt(2) @@ -371,7 +371,7 @@ function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 1, -1, -1] A = Float64[1 0 0 0] b = Float64[0] @@ -389,7 +389,7 @@ function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, -1, 0] A = Float64[1 0 0; 0 0 1] b = Float64[1/2, 1] @@ -407,7 +407,7 @@ function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, -1, 0] A = Float64[1 0 1] b = Float64[0] @@ -425,7 +425,7 @@ function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) 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] @@ -443,7 +443,7 @@ function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear end end -function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[1, 0, 0, 1] A = Float64[0 0 1 0] b = Float64[1] @@ -458,7 +458,7 @@ function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, @test r.x ≈ [1, 0, 1, 1] atol=1e-4 rtol=1e-4 end -function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[1, 1, 1] A = Float64[0 1 0; 1 0 0] b = Float64[2, 1] @@ -476,7 +476,7 @@ function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.z ≈ c + A' * r.y atol=1e-4 rtol=1e-4 end -function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[-1, 0, 0] A = Float64[0 1 0] b = Float64[0] @@ -490,7 +490,7 @@ function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 end -function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[1, 1, 1] A = Matrix{Float64}(undef, 0, 3) b = Vector{Float64}(undef, 0) @@ -506,7 +506,7 @@ function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test isempty(r.y) end -function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 0, 1] A = Float64[0 1 0; 1 0 0] b = Float64[1, -1] @@ -521,7 +521,7 @@ function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m @test r.x ≈ [-1, 1, exp(-2)] atol=1e-4 rtol=1e-4 end -function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[1, 0, -1, 0, 0, -1] A = Float64[1 1 0 1/2 0 0; 0 0 0 0 1 0] b = Float64[2, 1] @@ -536,7 +536,7 @@ function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ @test r.x[[1, 2, 4]] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 end -function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 0, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1/2, 1] @@ -554,7 +554,7 @@ function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[0, 0, 1] A = Float64[1 0 0; 0 1 0] b = Float64[0, 1] @@ -572,7 +572,7 @@ function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) 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] @@ -587,7 +587,7 @@ function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ @test r.x[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 end -function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) c = Float64[-1, 0, 0] A = Float64[0 0 1; 0 1 0] b = Float64[1/2, 1] @@ -605,7 +605,7 @@ function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) l = 4 c = vcat(0.0, ones(l)) A = Float64[1.0 zeros(1, l)] @@ -624,7 +624,7 @@ function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) l = 4 c = ones(l) A = zeros(0, l) @@ -643,7 +643,7 @@ function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ end end -function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) (Xn, Xm) = (3, 4) Xnm = Xn * Xm @@ -669,7 +669,7 @@ function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, lin end end -function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) side = 4 dim = 2 + div(side * (side + 1), 2) @@ -692,7 +692,7 @@ function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea @test r.z[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.z[3:end]) / r.z[1]) + side) ≈ r.z[2] atol=1e-4 rtol=1e-4 end -function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) @@ -715,7 +715,7 @@ function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea @test r.z[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.z[3:end]) / r.z[2]) ≈ r.z[1] atol=1e-4 rtol=1e-4 end -function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) @@ -736,7 +736,7 @@ function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea @test r.x ≈ [0, 0] atol=1e-4 rtol=1e-4 end -function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) l = 5 c = vcat(0.0, -ones(l)) A = Float64[1 zeros(1, l)] @@ -753,7 +753,7 @@ function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear @test r.s[1] ≈ 1 atol=1e-4 rtol=1e-4 end -function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, verbose::Bool) +function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) l = 5 c = vcat(0.0, -ones(l)) A = Float64[1 zeros(1, l)] diff --git a/test/runtests.jl b/test/runtests.jl index 8bfab5776..05f2264f1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,69 +7,75 @@ using LinearAlgebra using SparseArrays using Test import Hypatia +import Hypatia.HypReal const HYP = Hypatia const CO = HYP.Cones const MO = HYP.Models const SO = HYP.Solvers -const MU = HYP.ModelUtilities +# const MU = HYP.ModelUtilities -include(joinpath(@__DIR__, "interpolation.jl")) -include(joinpath(@__DIR__, "barriers.jl")) +# include(joinpath(@__DIR__, "interpolation.jl")) +# include(joinpath(@__DIR__, "barriers.jl")) include(joinpath(@__DIR__, "native.jl")) -include(joinpath(@__DIR__, "MathOptInterface.jl")) +# include(joinpath(@__DIR__, "MathOptInterface.jl")) -examples_dir = joinpath(@__DIR__, "../examples") -include(joinpath(examples_dir, "envelope/native.jl")) -include(joinpath(examples_dir, "linearopt/native.jl")) -include(joinpath(examples_dir, "polymin/native.jl")) -include(joinpath(examples_dir, "contraction/JuMP.jl")) -include(joinpath(examples_dir, "densityest/JuMP.jl")) -include(joinpath(examples_dir, "envelope/JuMP.jl")) -include(joinpath(examples_dir, "expdesign/JuMP.jl")) -include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) -include(joinpath(examples_dir, "muconvexity/JuMP.jl")) -include(joinpath(examples_dir, "polymin/JuMP.jl")) -include(joinpath(examples_dir, "polynorm/JuMP.jl")) -include(joinpath(examples_dir, "regionofattr/JuMP.jl")) -include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) -include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) -include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) +# examples_dir = joinpath(@__DIR__, "../examples") +# include(joinpath(examples_dir, "envelope/native.jl")) +# include(joinpath(examples_dir, "linearopt/native.jl")) +# include(joinpath(examples_dir, "polymin/native.jl")) +# include(joinpath(examples_dir, "contraction/JuMP.jl")) +# include(joinpath(examples_dir, "densityest/JuMP.jl")) +# include(joinpath(examples_dir, "envelope/JuMP.jl")) +# include(joinpath(examples_dir, "expdesign/JuMP.jl")) +# include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) +# include(joinpath(examples_dir, "muconvexity/JuMP.jl")) +# include(joinpath(examples_dir, "polymin/JuMP.jl")) +# include(joinpath(examples_dir, "polynorm/JuMP.jl")) +# include(joinpath(examples_dir, "regionofattr/JuMP.jl")) +# include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) +# include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) +# include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) @testset "Hypatia tests" begin -@info("starting interpolation tests") -@testset "interpolation tests" begin - fekete_sample() - test_recover_lagrange_polys() - test_recover_cheb_polys() -end - -@info("starting barrier tests") -barrier_testfuns = [ - test_epinormeucl_barrier, - test_epinorinf_barrier, - test_epinormspectral_barrier, - test_epiperpower_barrier, - test_epipersquare_barrier, - test_epipersumexp_barrier, - test_hypogeomean_barrier, - test_hypoperlog_barrier, - test_hypoperlogdet_barrier, - test_semidefinite_barrier, - test_wsospolyinterp_barrier, - test_wsospolyinterpmat_barrier, - test_wsospolyinterpsoc_barrier, - ] -@testset "barrier functions tests: $t" for t in barrier_testfuns - t() -end +# @info("starting interpolation tests") +# @testset "interpolation tests" begin +# fekete_sample() +# test_recover_lagrange_polys() +# test_recover_cheb_polys() +# end +# +# @info("starting barrier tests") +# barrier_testfuns = [ +# test_epinormeucl_barrier, +# test_epinorinf_barrier, +# test_epinormspectral_barrier, +# test_epiperpower_barrier, +# test_epipersquare_barrier, +# test_epipersumexp_barrier, +# test_hypogeomean_barrier, +# test_hypoperlog_barrier, +# test_hypoperlogdet_barrier, +# test_semidefinite_barrier, +# test_wsospolyinterp_barrier, +# test_wsospolyinterpmat_barrier, +# test_wsospolyinterpsoc_barrier, +# ] +# @testset "barrier functions tests: $t" for t in barrier_testfuns +# t() +# end @info("starting native interface tests") -verbose = false +verbose = true +real_types = [ + Float32, + Float64, + BigFloat, + ] system_solvers = [ - SO.QRCholCombinedHSDSystemSolver, - SO.SymIndefCombinedHSDSystemSolver, - SO.NaiveElimCombinedHSDSystemSolver, + # SO.QRCholCombinedHSDSystemSolver, + # SO.SymIndefCombinedHSDSystemSolver, + # SO.NaiveElimCombinedHSDSystemSolver, SO.NaiveCombinedHSDSystemSolver, ] testfuns_singular = [ @@ -78,126 +84,126 @@ testfuns_singular = [ inconsistent1, inconsistent2, ] -@testset "preprocessing tests: $t, $s" for t in testfuns_singular, s in system_solvers - t(s, MO.PreprocessedLinearModel, verbose) -end +# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types +# t(s, T, MO.PreprocessedLinearModel, verbose) +# end linear_models = [ - MO.PreprocessedLinearModel, + # MO.PreprocessedLinearModel, MO.RawLinearModel, ] testfuns_nonsingular = [ - orthant1, + # orthant1, orthant2, - orthant3, - orthant4, - epinorminf1, - epinorminf2, - epinorminf3, - epinorminf4, - epinorminf5, - epinorminf6, - epinormeucl1, - epinormeucl2, - epipersquare1, - epipersquare2, - epipersquare3, - semidefinite1, - semidefinite2, - semidefinite3, - semidefinitecomplex1, - hypoperlog1, - hypoperlog2, - hypoperlog3, - hypoperlog4, - epiperpower1, - epiperpower2, - epiperpower3, - hypogeomean1, - hypogeomean2, - hypogeomean3, - hypogeomean4, - epinormspectral1, - hypoperlogdet1, - hypoperlogdet2, - hypoperlogdet3, - epipersumexp1, - epipersumexp2, - ] -@testset "native tests: $t, $s, $m" for t in testfuns_nonsingular, s in system_solvers, m in linear_models - if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel - continue # QRChol linear system solver needs preprocessed model - end - t(s, m, verbose) -end - -@info("starting MathOptInterface tests") -verbose = false -system_solvers = [ - SO.NaiveCombinedHSDSystemSolver, - SO.QRCholCombinedHSDSystemSolver, - ] -linear_models = [ - MO.PreprocessedLinearModel, # MOI tests require preprocessing + # orthant3, + # orthant4, + # epinorminf1, + # epinorminf2, + # epinorminf3, + # epinorminf4, + # epinorminf5, + # epinorminf6, + # epinormeucl1, + # epinormeucl2, + # epipersquare1, + # epipersquare2, + # epipersquare3, + # semidefinite1, + # semidefinite2, + # semidefinite3, + # semidefinitecomplex1, + # hypoperlog1, + # hypoperlog2, + # hypoperlog3, + # hypoperlog4, + # epiperpower1, + # epiperpower2, + # epiperpower3, + # hypogeomean1, + # hypogeomean2, + # hypogeomean3, + # hypogeomean4, + # epinormspectral1, + # hypoperlogdet1, + # hypoperlogdet2, + # hypoperlogdet3, + # epipersumexp1, + # epipersumexp2, ] -@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models - test_moi(d, s, m, verbose) -end - -@info("starting native examples tests") -native_options = ( - verbose = true, - max_iters = 150, - time_limit = 6e2, # 1 minute - ) -@testset "native examples" begin - @testset "envelope" begin test_envelope(; native_options..., - ) end - @testset "linearopt" begin test_linearopt(; native_options..., - ) end - @testset "polymin" begin test_polymin(; native_options..., - tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - ) end +@testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types + # if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel + # continue # QRChol linear system solver needs preprocessed model + # end + t(s, m, T, verbose) end -@info("starting JuMP examples tests") -JuMP_options = ( - verbose = true, - test_certificates = true, - max_iters = 250, - time_limit = 6e2, # 1 minute - ) -@testset "JuMP examples" begin - @testset "contraction" begin test_contractionJuMP(; JuMP_options..., - tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, - ) end - @testset "densityest" begin test_densityestJuMP(; JuMP_options..., - tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, - ) end - @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., - ) end - @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., - ) end - @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., - tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, - ) end - @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., - ) end - @testset "polymin" begin test_polyminJuMP(; JuMP_options..., - tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - ) end - @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., - ) end - @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., - tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, - ) end - @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., - ) end - @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., - tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, - ) end - @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., - tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, - ) end -end +# @info("starting MathOptInterface tests") +# verbose = false +# system_solvers = [ +# SO.NaiveCombinedHSDSystemSolver, +# SO.QRCholCombinedHSDSystemSolver, +# ] +# linear_models = [ +# MO.PreprocessedLinearModel, # MOI tests require preprocessing +# ] +# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models +# test_moi(d, s, m, verbose) +# end +# +# @info("starting native examples tests") +# native_options = ( +# verbose = true, +# max_iters = 150, +# time_limit = 6e2, # 1 minute +# ) +# @testset "native examples" begin +# @testset "envelope" begin test_envelope(; native_options..., +# ) end +# @testset "linearopt" begin test_linearopt(; native_options..., +# ) end +# @testset "polymin" begin test_polymin(; native_options..., +# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, +# ) end +# end +# +# @info("starting JuMP examples tests") +# JuMP_options = ( +# verbose = true, +# test_certificates = true, +# max_iters = 250, +# time_limit = 6e2, # 1 minute +# ) +# @testset "JuMP examples" begin +# @testset "contraction" begin test_contractionJuMP(; JuMP_options..., +# tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, +# ) end +# @testset "densityest" begin test_densityestJuMP(; JuMP_options..., +# tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, +# ) end +# @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., +# ) end +# @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., +# ) end +# @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., +# tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, +# ) end +# @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., +# ) end +# @testset "polymin" begin test_polyminJuMP(; JuMP_options..., +# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, +# ) end +# @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., +# ) end +# @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., +# tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, +# ) end +# @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., +# ) end +# @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., +# tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, +# ) end +# @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., +# tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, +# ) end +# end end From ecde36c25976df9997045f174136113daa6a259b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Tue, 28 May 2019 19:42:35 -0400 Subject: [PATCH 02/20] preproc and orthant native tests working with bigfloat and float32; some cleanup and new error messages in preproc --- src/Models/Models.jl | 9 +- src/Models/linear.jl | 140 ++++-- src/Solvers/Solvers.jl | 10 +- .../combined_step/naive.jl | 2 +- .../combined_step/stepper.jl | 2 +- src/Solvers/homogeneous_self_dual/solver.jl | 10 +- test/native.jl | 407 ++++++++++-------- test/runtests.jl | 18 +- 8 files changed, 346 insertions(+), 252 deletions(-) diff --git a/src/Models/Models.jl b/src/Models/Models.jl index d851e01d4..354c4d52d 100644 --- a/src/Models/Models.jl +++ b/src/Models/Models.jl @@ -23,7 +23,14 @@ mutable struct Point{T <: HypReal} dual_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} primal_views::Vector{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int}}, true}} - function Point(x::Vector{T}, y::Vector{T}, z::Vector{T}, s::Vector{T}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}) where {T <: HypReal} + function Point( + x::Vector{T}, + y::Vector{T}, + z::Vector{T}, + s::Vector{T}, + cones::Vector{<:Cones.Cone}, + cone_idxs::Vector{UnitRange{Int}}, + ) where {T <: HypReal} point = new{T}() point.x = x diff --git a/src/Models/linear.jl b/src/Models/linear.jl index 515466ed4..5d148db9f 100644 --- a/src/Models/linear.jl +++ b/src/Models/linear.jl @@ -26,9 +26,23 @@ The primal-dual optimality conditions are: s in K z in K* ``` + +TODO check model data consistency =# -# TODO check model data consistency +function set_initial_cone_point(point, cones) + for k in eachindex(cones) + cone_k = cones[k] + Cones.setup_data(cone_k) + primal_k = point.primal_views[k] + Cones.set_initial_point(primal_k, cone_k) + Cones.load_point(cone_k, primal_k) + @assert Cones.check_in_cone(cone_k) + g = Cones.grad(cone_k) + @. point.dual_views[k] = -g + end + return point +end mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} n::Int @@ -45,7 +59,24 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} initial_point::Point{T} - function RawLinearModel(c::Vector{T}, A::AbstractMatrix{T}, b::Vector{T}, G::AbstractMatrix{T}, h::Vector{T}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}) where {T <: HypReal} + function RawLinearModel{T}( + c::Vector, + A::AbstractMatrix, + b::Vector, + G::AbstractMatrix, + h::Vector, + cones::Vector{<:Cones.Cone}, + cone_idxs::Vector{UnitRange{Int}}, + ) where {T <: HypReal} + if (issparse(A) || issparse(G)) && T != Float64 + error("can only use Float64 types with sparse A or G (generic QR not implemented)") + end + c = convert(Vector{T}, c) + A = convert(AbstractMatrix{T}, A) + b = convert(Vector{T}, b) + G = convert(AbstractMatrix{T}, G) + h = convert(Vector{T}, h) + model = new{T}() model.n = length(c) @@ -71,7 +102,11 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} # solve for x as least squares solution to Ax = b, Gx = h - s if !iszero(model.n) - point.x = vcat(A, G) \ vcat(b, h - point.s) + AG = vcat(A, G) + if issparse(AG) && T != Float64 + error("can only use Float64 types with sparse [A; G] (generic QR not implemented)") + end + point.x = AG \ vcat(b, h - point.s) end model.initial_point = point @@ -80,36 +115,57 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} end end +get_original_data(model::RawLinearModel) = (model.c, model.A, model.b, model.G, model.h, model.cones, model.cone_idxs) + +# TODO could optionally rescale rows of [A, b] and [G, h] and [A', G', c] and variables +# NOTE (pivoted) QR factorizations are usually rank-revealing but may be unreliable, see http://www.math.sjsu.edu/~foster/rankrevealingcode.html mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} - c_raw::Vector{Float64} - A_raw::AbstractMatrix{Float64} - b_raw::Vector{Float64} - G_raw::AbstractMatrix{Float64} + c_raw::Vector{T} + A_raw::AbstractMatrix{T} + b_raw::Vector{T} + G_raw::AbstractMatrix{T} n::Int p::Int q::Int - c::Vector{Float64} - A::AbstractMatrix{Float64} - b::Vector{Float64} - G::AbstractMatrix{Float64} - h::Vector{Float64} + c::Vector{T} + A::AbstractMatrix{T} + b::Vector{T} + G::AbstractMatrix{T} + h::Vector{T} cones::Vector{Cones.Cone} cone_idxs::Vector{UnitRange{Int}} - nu::Float64 + nu::T x_keep_idxs::AbstractVector{Int} y_keep_idxs::AbstractVector{Int} - Ap_R::AbstractMatrix{Float64} - Ap_Q1::AbstractMatrix{Float64} + Ap_R::AbstractMatrix{T} + Ap_Q1::AbstractMatrix{T} Ap_Q2 initial_point::Point - # TODO could optionally rescale rows of [A, b] and [G, h] and [A', G', c] and variables - # NOTE (pivoted) QR factorizations are usually rank-revealing but may be unreliable, see http://www.math.sjsu.edu/~foster/rankrevealingcode.html - function PreprocessedLinearModel(c::Vector{T}, A::AbstractMatrix{T}, b::Vector{T}, G::AbstractMatrix{T}, h::Vector{Float64}, cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}; tol_QR::Float64 = 1e-13) where {T <: HypReal} + function PreprocessedLinearModel{T}( + c::Vector, + A::AbstractMatrix, + b::Vector, + G::AbstractMatrix, + h::Vector, + cones::Vector{<:Cones.Cone}, + cone_idxs::Vector{UnitRange{Int}}; + tol_QR::Real = max(1e-14, 1e3 * eps(T)), + ) where {T <: HypReal} + if (issparse(A) || issparse(G)) && T != Float64 + error("can only use Float64 types with sparse A or G (generic QR not implemented)") + end + c = convert(Vector{T}, c) + A = convert(AbstractMatrix{T}, A) + b = convert(Vector{T}, b) + G = convert(AbstractMatrix{T}, G) + h = convert(Vector{T}, h) + model = new{T}() + model.c_raw = c model.A_raw = A model.b_raw = b @@ -121,10 +177,10 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} model.h = h model.cones = cones model.cone_idxs = cone_idxs - model.nu = isempty(cones) ? 0.0 : sum(Cones.get_nu, cones) + model.nu = isempty(cones) ? zero(T) : sum(Cones.get_nu, cones) # get initial point and preprocess - point = Point(Float64[], Float64[], similar(h), similar(h), cones, cone_idxs) + point = Point(T[], T[], similar(h), similar(h), cones, cone_idxs) set_initial_cone_point(point, model.cones) # solve for x as least squares solution to Ax = b, Gx = h - s @@ -132,6 +188,9 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} # get pivoted QR # TODO when Julia has a unified QR interface, replace this AG = vcat(A, G) if issparse(AG) + if T != Float64 + error("can only use Float64 types with sparse [A; G] (generic QR not implemented)") + end AG_fact = qr(AG, tol = tol_QR) else AG_fact = qr(AG, Val(true)) @@ -154,18 +213,19 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} # TODO optimize all below if issparse(AG) x_keep_idxs = AG_fact.pcol[1:AG_rank] - AG_Q1 = Matrix{Float64}(undef, p + q, AG_rank) - AG_Q1[AG_fact.prow, :] = AG_fact.Q * Matrix{Float64}(I, p + q, AG_rank) + AG_Q1 = Matrix{T}(undef, p + q, AG_rank) + AG_Q1[AG_fact.prow, :] = AG_fact.Q * Matrix{T}(I, p + q, AG_rank) else x_keep_idxs = AG_fact.p[1:AG_rank] - AG_Q1 = AG_fact.Q * Matrix{Float64}(I, p + q, AG_rank) + AG_Q1 = AG_fact.Q * Matrix{T}(I, p + q, AG_rank) end AG_R = UpperTriangular(AG_R[1:AG_rank, 1:AG_rank]) c_sub = c[x_keep_idxs] yz_sub = AG_Q1 * (AG_R' \ c_sub) - if norm(AG' * yz_sub - c, Inf) > tol_QR - error("some dual equality constraints are inconsistent") + residual = norm(AG' * yz_sub - c, Inf) + if residual > tol_QR + error("some dual equality constraints are inconsistent (residual $residual, tolerance $tol_QR)") end println("removed $(n - AG_rank) out of $n dual equality constraints") c = c_sub @@ -199,11 +259,11 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} # TODO optimize all below if issparse(A) y_keep_idxs = Ap_fact.pcol[1:Ap_rank] - A_Q = Matrix{Float64}(undef, n, n) - A_Q[Ap_fact.prow, :] = Ap_fact.Q * Matrix{Float64}(I, n, n) + A_Q = Matrix{T}(undef, n, n) + A_Q[Ap_fact.prow, :] = Ap_fact.Q * Matrix{T}(I, n, n) else y_keep_idxs = Ap_fact.p[1:Ap_rank] - A_Q = Ap_fact.Q * Matrix{Float64}(I, n, n) + A_Q = Ap_fact.Q * Matrix{T}(I, n, n) end Ap_Q1 = A_Q[:, 1:Ap_rank] Ap_Q2 = A_Q[:, (Ap_rank + 1):n] @@ -213,8 +273,9 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} if Ap_rank < p # some dependent primal equalities, so check if they are consistent x_sub = Ap_Q1 * (Ap_R' \ b_sub) - if norm(A * x_sub - b, Inf) > tol_QR - error("some primal equality constraints are inconsistent") + residual = norm(A * x_sub - b, Inf) + if residual > tol_QR + error("some primal equality constraints are inconsistent (residual $residual, tolerance $tol_QR)") end println("removed $(p - Ap_rank) out of $p primal equality constraints") end @@ -227,8 +288,8 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} model.Ap_Q2 = Ap_Q2 else y_keep_idxs = Int[] - model.Ap_R = UpperTriangular(zeros(0, 0)) - model.Ap_Q1 = zeros(n, 0) + model.Ap_R = UpperTriangular(zeros(T, 0, 0)) + model.Ap_Q1 = zeros(T, n, 0) model.Ap_Q2 = I end @@ -247,19 +308,4 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} end end -function set_initial_cone_point(point, cones) - for k in eachindex(cones) - cone_k = cones[k] - Cones.setup_data(cone_k) - primal_k = point.primal_views[k] - Cones.set_initial_point(primal_k, cone_k) - Cones.load_point(cone_k, primal_k) - @assert Cones.check_in_cone(cone_k) - g = Cones.grad(cone_k) - @. point.dual_views[k] = -g - end - return point -end - -get_original_data(model::RawLinearModel) = (model.c, model.A, model.b, model.G, model.h, model.cones, model.cone_idxs) get_original_data(model::PreprocessedLinearModel) = (model.c_raw, model.A_raw, model.b_raw, model.G_raw, model.h, model.cones, model.cone_idxs) diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index 0f06e0e1c..79c5dee7e 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -46,20 +46,20 @@ get_z(solver::Solver) = copy(solver.point.z) get_z(solver::Solver, model::Models.Model) = get_z(solver) get_x(solver::Solver) = copy(solver.point.x) -function get_x(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where T +function get_x(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where {T <: HypReal} x = zeros(T, length(model.c_raw)) x[model.x_keep_idxs] = solver.point.x # unpreprocess solver's solution return x end -get_x(solver::Solver, model::Models.Model) = get_x(solver) +get_x(solver::Solver{T}, model::Models.Model{T}) where {T <: HypReal} = get_x(solver) get_y(solver::Solver) = copy(solver.point.y) -function get_y(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where T +function get_y(solver::Solver{T}, model::Models.PreprocessedLinearModel{T}) where {T <: HypReal} y = zeros(T, length(model.b_raw)) y[model.y_keep_idxs] = solver.point.y # unpreprocess solver's solution return y end -get_y(solver::Solver, model::Models.Model) = get_y(solver) +get_y(solver::Solver{T}, model::Models.Model{T}) where {T <: HypReal} = get_y(solver) # check conic certificates are valid # TODO pick default tols based on T @@ -69,7 +69,7 @@ function get_certificates( test::Bool = true, atol = max(1e-5, sqrt(sqrt(eps(T)))), rtol = atol, - ) where T + ) where {T <: HypReal} status = get_status(solver) primal_obj = get_primal_obj(solver) dual_obj = get_dual_obj(solver) diff --git a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl index b64af0acd..b28c9b295 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl @@ -19,7 +19,7 @@ mutable struct NaiveCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSo s2 kap_row::Int - function NaiveCombinedHSDSystemSolver(model::Models.LinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} + function NaiveCombinedHSDSystemSolver{T}(model::Models.LinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} (n, p, q) = (model.n, model.p, model.q) system_solver = new{T}() system_solver.use_sparse = use_sparse diff --git a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl index 7d6c34670..38f706679 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl @@ -17,7 +17,7 @@ mutable struct CombinedHSDStepper{T <: HypReal} <: HSDStepper{T} cones_outside_nbhd::Vector{Bool} cones_loaded::Vector{Bool} - function CombinedHSDStepper( + function CombinedHSDStepper{T}( model::Models.LinearModel{T}; system_solver::CombinedHSDSystemSolver{T} = (model isa Models.PreprocessedLinearModel{T} ? QRCholCombinedHSDSystemSolver(model) : NaiveCombinedHSDSystemSolver(model)), max_nbhd::T = T(0.75), diff --git a/src/Solvers/homogeneous_self_dual/solver.jl b/src/Solvers/homogeneous_self_dual/solver.jl index 0b4ec4faa..8bbd3ce1d 100644 --- a/src/Solvers/homogeneous_self_dual/solver.jl +++ b/src/Solvers/homogeneous_self_dual/solver.jl @@ -63,13 +63,13 @@ mutable struct HSDSolver{T <: HypReal} <: Solver{T} solve_time::Float64 timer::TimerOutput - function HSDSolver( + function HSDSolver{T}( model::Models.LinearModel{T}; stepper::HSDStepper{T} = CombinedHSDStepper(model), verbose::Bool = true, - tol_rel_opt = max(1e-10, sqrt(eps(T))), - tol_abs_opt = 1e1 * tol_rel_opt, - tol_feas = tol_abs_opt, + tol_rel_opt = max(1e-12, 1e-2 * cbrt(eps(T))), + tol_abs_opt = tol_rel_opt, + tol_feas = tol_rel_opt, tol_slow = 5e-3, max_iters::Int = 250, time_limit = 3e2, @@ -255,7 +255,7 @@ function check_convergence(solver::HSDSolver{T}) where T if isnan(prev) || isnan(curr) continue end - max_improve = max(max_improve, (prev - curr) / (abs(prev) + T(1e-6))) + max_improve = max(max_improve, (prev - curr) / (abs(prev) + eps(T))) end if max_improve < solver.tol_slow if solver.prev_is_slow && solver.prev2_is_slow diff --git a/test/native.jl b/test/native.jl index 86fa7d0b3..41f98aa48 100644 --- a/test/native.jl +++ b/test/native.jl @@ -6,52 +6,57 @@ import Random # solve model, check conic certificates are valid and return certificate data function solve_and_check( - c::Vector{T}, - A::AbstractMatrix{T}, - b::Vector{T}, - G::AbstractMatrix{T}, - h::Vector{T}, + c::Vector, + A::AbstractMatrix, + b::Vector, + G::AbstractMatrix, + h::Vector, cones::Vector{<:CO.Cone}, cone_idxs::Vector{UnitRange{Int}}, - system_solver::Type{<:SO.CombinedHSDSystemSolver}, - linear_model::Type{<:MO.LinearModel}, + linear_model::Type{<:MO.LinearModel{T}}, + system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, verbose::Bool; - atol = max(1e-5, sqrt(sqrt(eps(T)))), - rtol = atol, + atol::Real = max(1e-5, sqrt(sqrt(eps(T)))), + rtol::Real = atol, use_sparse::Bool = false, ) where {T <: HypReal} model = linear_model(c, A, b, G, h, cones, cone_idxs) - stepper = SO.CombinedHSDStepper(model, system_solver = system_solver(model, use_sparse = use_sparse)) - solver = SO.HSDSolver(model, verbose = verbose, stepper = stepper) + stepper = SO.CombinedHSDStepper{T}(model, system_solver = system_solver(model, use_sparse = use_sparse)) + solver = SO.HSDSolver{T}(model, verbose = verbose, stepper = stepper) SO.solve(solver) return SO.get_certificates(solver, model, test = true, atol = atol, rtol = rtol) end -function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) - c = Float64[-1, 0] - A = Matrix{Float64}(undef, 0, 2) - b = Float64[] - G = Float64[1 0] - h = Float64[1] +function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) + + c = T[-1, 0] + A = zeros(T, 0, 2) + b = T[] + G = T[1 0] + h = T[1] cones = [CO.Nonnegative(1, false)] cone_idxs = [1:1] for use_sparse in (false, true) if use_sparse + if T != Float64 + continue # TODO currently cannot do preprocessing with sparse A or G if not using Float64 + end A = sparse(A) G = sparse(G) end - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose, use_sparse = use_sparse) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose, use_sparse = use_sparse) @test r.status == :Optimal - @test r.primal_obj ≈ -1 atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, 0] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ T(-1) atol=tol rtol=tol + @test r.x ≈ T[1, 0] atol=tol rtol=tol @test isempty(r.y) - @test_throws ErrorException("some dual equality constraints are inconsistent") linear_model(Float64[-1, -1], A, b, G, h, cones, cone_idxs) + @test_throws ErrorException linear_model(T[-1, -1], A, b, G, h, cones, cone_idxs) end end -function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -70,11 +75,11 @@ function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.Nonpositive(q)] cone_idxs = [1:q] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal end -function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -87,10 +92,10 @@ function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear b[11:15] = 2 * (rnd1 * b[1:5] - rnd2 * b[6:10]) h = zeros(q) - @test_throws ErrorException("some primal equality constraints are inconsistent") linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) + @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) end -function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} Random.seed!(1) (n, p, q) = (30, 15, 30) A = rand(-9.0:9.0, p, n) @@ -104,10 +109,11 @@ function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear c[11:15] = 2 * (rnd1 * c[1:5] - rnd2 * c[6:10]) h = zeros(q) - @test_throws ErrorException("some dual equality constraints are inconsistent") linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) + @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) end -function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) (n, p, q) = (6, 3, 6) c = rand(T(0):T(9), n) @@ -119,19 +125,20 @@ function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode # nonnegative cone G = SparseMatrixCSC(-one(T) * I, q, n) cones = [CO.Nonnegative(q)] - rnn = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + rnn = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test rnn.status == :Optimal # nonpositive cone G = SparseMatrixCSC(one(T) * I, q, n) cones = [CO.Nonpositive(q)] - rnp = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + rnp = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test rnp.status == :Optimal - @test rnp.primal_obj ≈ rnn.primal_obj atol=1e-4 rtol=1e-4 + @test rnp.primal_obj ≈ rnn.primal_obj atol=tol rtol=tol end -function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) (n, p, q) = (5, 2, 10) c = rand(T(0):T(9), n) @@ -143,18 +150,19 @@ function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode # use dual barrier cones = [CO.Nonnegative(q, true)] - r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # use primal barrier cones = [CO.Nonnegative(q, false)] - r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal - @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 + @test r1.primal_obj ≈ r2.primal_obj atol=tol rtol=tol end -function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) (n, p, q) = (15, 6, 15) c = rand(0.0:9.0, n) @@ -166,18 +174,19 @@ function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode # use dual barrier cones = [CO.Nonpositive(q, true)] - r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # use primal barrier cones = [CO.Nonpositive(q, false)] - r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal - @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 + @test r1.primal_obj ≈ r2.primal_obj atol=tol rtol=tol end -function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) (n, p, q) = (5, 2, 10) c = rand(0.0:9.0, n) @@ -189,19 +198,20 @@ function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_mode # mixture of nonnegative and nonpositive cones cones = [CO.Nonnegative(4, false), CO.Nonnegative(6, true)] cone_idxs = [1:4, 5:10] - r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # only nonnegative cone cones = [CO.Nonnegative(10, false)] cone_idxs = [1:10] - r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal - @test r1.primal_obj ≈ r2.primal_obj atol=1e-4 rtol=1e-4 + @test r1.primal_obj ≈ r2.primal_obj atol=tol rtol=tol end -function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, -1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, inv(sqrt(2))] @@ -210,14 +220,15 @@ function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(3)] cone_idxs = [1:3] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -1 - inv(sqrt(2)) atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, inv(sqrt(2)), 1] atol=1e-4 rtol=1e-4 - @test r.y ≈ [1, 1] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -1 - inv(sqrt(2)) atol=tol rtol=tol + @test r.x ≈ [1, inv(sqrt(2)), 1] atol=tol rtol=tol + @test r.y ≈ [1, 1] atol=tol rtol=tol end -function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) c = Float64[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) @@ -227,12 +238,13 @@ function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(6)] cone_idxs = [1:6] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 1 atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 1 atol=tol rtol=tol end -function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[1, 0, 0, 0, 0, 0] A = zeros(0, 6) b = zeros(0) @@ -241,13 +253,14 @@ function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(6)] cone_idxs = [1:6] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ zeros(6) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ zeros(6) atol=tol rtol=tol end -function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, -0.4] @@ -256,14 +269,15 @@ function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(3, true)] cone_idxs = [1:3] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -1 atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, -0.4, 0.6] atol=1e-4 rtol=1e-4 - @test r.y ≈ [1, 0] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -1 atol=tol rtol=tol + @test r.x ≈ [1, -0.4, 0.6] atol=tol rtol=tol + @test r.y ≈ [1, 0] atol=tol rtol=tol end -function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) c = Float64[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) @@ -273,12 +287,13 @@ function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(6, true)] cone_idxs = [1:6] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 1 atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 1 atol=tol rtol=tol end -function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 3 L = 2l + 1 c = collect(Float64, -l:l) @@ -290,15 +305,16 @@ function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.EpiNormInf(L + 1, true), CO.EpiNormInf(L + 1, false)] cone_idxs = [1:(L + 1), (L + 2):(2L + 2)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -l + 1 atol=1e-4 rtol=1e-4 - @test r.x[2] ≈ 0.5 atol=1e-4 rtol=1e-4 - @test r.x[end - 1] ≈ -0.5 atol=1e-4 rtol=1e-4 - @test sum(abs, r.x) ≈ 1 atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -l + 1 atol=tol rtol=tol + @test r.x[2] ≈ 0.5 atol=tol rtol=tol + @test r.x[end - 1] ≈ -0.5 atol=tol rtol=tol + @test sum(abs, r.x) ≈ 1 atol=tol rtol=tol end -function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, -1, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1, inv(sqrt(2))] @@ -309,15 +325,16 @@ function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false) cones = [CO.EpiNormEucl(3, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, inv(sqrt(2)), inv(sqrt(2))] atol=1e-4 rtol=1e-4 - @test r.y ≈ [sqrt(2), 0] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -sqrt(2) atol=tol rtol=tol + @test r.x ≈ [1, inv(sqrt(2)), inv(sqrt(2))] atol=tol rtol=tol + @test r.y ≈ [sqrt(2), 0] atol=tol rtol=tol end end -function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, -1, -1] A = Float64[1 0 0] b = Float64[0] @@ -328,14 +345,15 @@ function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false) cones = [CO.EpiNormEucl(3, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ zeros(3) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ zeros(3) atol=tol rtol=tol end end -function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 0, -1, -1] A = Float64[1 0 0 0; 0 1 0 0] b = Float64[1/2, 1] @@ -346,14 +364,15 @@ function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.EpiPerSquare(4, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -sqrt(2) atol=1e-4 rtol=1e-4 - @test r.x[3:4] ≈ [1, 1] / sqrt(2) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -sqrt(2) atol=tol rtol=tol + @test r.x[3:4] ≈ [1, 1] / sqrt(2) atol=tol rtol=tol end end -function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 0, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1/2, 1] / sqrt(2) @@ -364,14 +383,15 @@ function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.EpiPerSquare(3, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -inv(sqrt(2)) atol=1e-4 rtol=1e-4 - @test r.x[2] ≈ inv(sqrt(2)) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -inv(sqrt(2)) atol=tol rtol=tol + @test r.x[2] ≈ inv(sqrt(2)) atol=tol rtol=tol end end -function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 1, -1, -1] A = Float64[1 0 0 0] b = Float64[0] @@ -382,14 +402,15 @@ function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.EpiPerSquare(4, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ zeros(4) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ zeros(4) atol=tol rtol=tol end end -function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, -1, 0] A = Float64[1 0 0; 0 0 1] b = Float64[1/2, 1] @@ -400,14 +421,15 @@ function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.PosSemidef(3, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -1 atol=1e-4 rtol=1e-4 - @test r.x[2] ≈ 1 atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -1 atol=tol rtol=tol + @test r.x[2] ≈ 1 atol=tol rtol=tol end end -function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, -1, 0] A = Float64[1 0 1] b = Float64[0] @@ -418,14 +440,15 @@ function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.PosSemidef(3, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ zeros(3) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ zeros(3) atol=tol rtol=tol end end -function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) 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] @@ -436,14 +459,15 @@ function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear for is_dual in (true, false) cones = [CO.PosSemidef(6, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 1.249632 atol=1e-4 rtol=1e-4 - @test r.x ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 1.249632 atol=tol rtol=tol + @test r.x ≈ [0.491545, 0.647333, 0.426249, 0.571161, 0.531874, 0.331838] atol=tol rtol=tol end end -function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[1, 0, 0, 1] A = Float64[0 0 1 0] b = Float64[1] @@ -452,13 +476,14 @@ function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, cone_idxs = [1:4] cones = [CO.PosSemidef{ComplexF64}(4)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 2 atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, 0, 1, 1] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 2 atol=tol rtol=tol + @test r.x ≈ [1, 0, 1, 1] atol=tol rtol=tol end -function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[1, 1, 1] A = Float64[0 1 0; 1 0 0] b = Float64[2, 1] @@ -467,16 +492,17 @@ function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.HypoPerLog()] cone_idxs = [1:3] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal ehalf = exp(1 / 2) - @test r.primal_obj ≈ 2 * ehalf + 3 atol=1e-4 rtol=1e-4 - @test r.x ≈ [1, 2, 2 * ehalf] atol=1e-4 rtol=1e-4 - @test r.y ≈ -[1 + ehalf / 2, 1 + ehalf] atol=1e-4 rtol=1e-4 - @test r.z ≈ c + A' * r.y atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 2 * ehalf + 3 atol=tol rtol=tol + @test r.x ≈ [1, 2, 2 * ehalf] atol=tol rtol=tol + @test r.y ≈ -[1 + ehalf / 2, 1 + ehalf] atol=tol rtol=tol + @test r.z ≈ c + A' * r.y atol=tol rtol=tol end -function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[-1, 0, 0] A = Float64[0 1 0] b = Float64[0] @@ -485,12 +511,13 @@ function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.HypoPerLog()] cone_idxs = [1:3] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol end -function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[1, 1, 1] A = Matrix{Float64}(undef, 0, 3) b = Vector{Float64}(undef, 0) @@ -499,14 +526,15 @@ function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.HypoPerLog(), CO.Nonnegative(1)] cone_idxs = [1:3, 4:4] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ [0, 0, 0] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ [0, 0, 0] atol=tol rtol=tol @test isempty(r.y) end -function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 0, 1] A = Float64[0 1 0; 1 0 0] b = Float64[1, -1] @@ -515,13 +543,14 @@ function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_m cones = [CO.HypoPerLog(true)] cone_idxs = [1:3] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ exp(-2) atol=1e-4 rtol=1e-4 - @test r.x ≈ [-1, 1, exp(-2)] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ exp(-2) atol=tol rtol=tol + @test r.x ≈ [-1, 1, exp(-2)] atol=tol rtol=tol end -function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[1, 0, -1, 0, 0, -1] A = Float64[1 1 0 1/2 0 0; 0 0 0 0 1 0] b = Float64[2, 1] @@ -530,13 +559,14 @@ function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ cones = [CO.EpiPerPower(5.0, false), CO.EpiPerPower(2.5, false)] cone_idxs = [1:3, 4:6] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -1.80734 atol=1e-4 rtol=1e-4 - @test r.x[[1, 2, 4]] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -1.80734 atol=tol rtol=tol + @test r.x[[1, 2, 4]] ≈ [0.0639314, 0.783361, 2.30542] atol=tol rtol=tol end -function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 0, -1] A = Float64[1 0 0; 0 1 0] b = Float64[1/2, 1] @@ -547,14 +577,15 @@ function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false) cones = [CO.EpiPerPower(2.0, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ (is_dual ? -sqrt(2) : -inv(sqrt(2))) atol=1e-4 rtol=1e-4 - @test r.x[1:2] ≈ [1/2, 1] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ (is_dual ? -sqrt(2) : -inv(sqrt(2))) atol=tol rtol=tol + @test r.x[1:2] ≈ [1/2, 1] atol=tol rtol=tol end end -function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[0, 0, 1] A = Float64[1 0 0; 0 1 0] b = Float64[0, 1] @@ -565,14 +596,15 @@ function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false), alpha in [1.5; 2.0] # TODO objective gap is large when alpha is different e.g. 2.5, investigate why cones = [CO.EpiPerPower(alpha, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose, atol=1e-3, rtol=1e-3) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose, atol=1e-3, rtol=1e-3) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-3 rtol=1e-3 - @test r.x[1:2] ≈ [0, 1] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=10tol rtol=10tol + @test r.x[1:2] ≈ [0, 1] atol=tol rtol=tol end end -function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) 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] @@ -581,13 +613,14 @@ function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ cones = [CO.HypoGeomean([0.2, 0.8], false), CO.HypoGeomean([0.4, 0.6], false)] cone_idxs = [1:3, 4:6] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ -1.80734 atol=1e-4 rtol=1e-4 - @test r.x[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ -1.80734 atol=tol rtol=tol + @test r.x[1:3] ≈ [0.0639314, 0.783361, 2.30542] atol=tol rtol=tol end -function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) c = Float64[-1, 0, 0] A = Float64[0 0 1; 0 1 0] b = Float64[1/2, 1] @@ -598,14 +631,15 @@ function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false) cones = [CO.HypoGeomean([0.5, 0.5], is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ (is_dual ? 0 : -inv(sqrt(2))) atol=1e-4 rtol=1e-4 - @test r.x[2:3] ≈ [1, 0.5] atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ (is_dual ? 0 : -inv(sqrt(2))) atol=tol rtol=tol + @test r.x[2:3] ≈ [1, 0.5] atol=tol rtol=tol end end -function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 4 c = vcat(0.0, ones(l)) A = Float64[1.0 zeros(1, l)] @@ -617,14 +651,15 @@ function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ b = is_dual ? [-1.0] : [1.0] cones = [CO.HypoGeomean(fill(inv(l), l), is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ (is_dual ? 1.0 : l) atol=1e-4 rtol=1e-4 - @test r.x[2:end] ≈ (is_dual ? inv(l) : 1.0) * ones(l) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ (is_dual ? 1.0 : l) atol=tol rtol=tol + @test r.x[2:end] ≈ (is_dual ? inv(l) : 1.0) * ones(l) atol=tol rtol=tol end end -function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 4 c = ones(l) A = zeros(0, l) @@ -636,14 +671,15 @@ function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_ for is_dual in (true, false) cones = [CO.HypoGeomean(fill(inv(l), l), is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.primal_obj ≈ 0 atol=1e-4 rtol=1e-4 - @test r.x ≈ zeros(l) atol=1e-4 rtol=1e-4 + @test r.primal_obj ≈ 0 atol=tol rtol=tol + @test r.x ≈ zeros(l) atol=tol rtol=tol end end -function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) (Xn, Xm) = (3, 4) Xnm = Xn * Xm @@ -657,19 +693,20 @@ function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, lin for is_dual in (true, false) cones = [CO.EpiNormSpectral(Xn, Xm, is_dual)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal if is_dual - @test sum(svdvals!(reshape(r.s[2:end], Xn, Xm))) ≈ r.s[1] atol=1e-4 rtol=1e-4 - @test svdvals!(reshape(r.z[2:end], Xn, Xm))[1] ≈ r.z[1] atol=1e-4 rtol=1e-4 + @test sum(svdvals!(reshape(r.s[2:end], Xn, Xm))) ≈ r.s[1] atol=tol rtol=tol + @test svdvals!(reshape(r.z[2:end], Xn, Xm))[1] ≈ r.z[1] atol=tol rtol=tol else - @test svdvals!(reshape(r.s[2:end], Xn, Xm))[1] ≈ r.s[1] atol=1e-4 rtol=1e-4 - @test sum(svdvals!(reshape(r.z[2:end], Xn, Xm))) ≈ r.z[1] atol=1e-4 rtol=1e-4 + @test svdvals!(reshape(r.s[2:end], Xn, Xm))[1] ≈ r.s[1] atol=tol rtol=tol + @test sum(svdvals!(reshape(r.z[2:end], Xn, Xm))) ≈ r.z[1] atol=tol rtol=tol end end end -function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) side = 4 dim = 2 + div(side * (side + 1), 2) @@ -684,15 +721,16 @@ function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea cones = [CO.HypoPerLogdet(dim)] cone_idxs = [1:dim] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.x[1] ≈ -r.primal_obj atol=1e-4 rtol=1e-4 - @test r.x[2] ≈ 1 atol=1e-4 rtol=1e-4 - @test r.s[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.s[3:end]) / r.s[2]) ≈ r.s[1] atol=1e-4 rtol=1e-4 - @test r.z[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.z[3:end]) / r.z[1]) + side) ≈ r.z[2] atol=1e-4 rtol=1e-4 + @test r.x[1] ≈ -r.primal_obj atol=tol rtol=tol + @test r.x[2] ≈ 1 atol=tol rtol=tol + @test r.s[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.s[3:end]) / r.s[2]) ≈ r.s[1] atol=tol rtol=tol + @test r.z[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.z[3:end]) / r.z[1]) + side) ≈ r.z[2] atol=tol rtol=tol end -function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) @@ -707,15 +745,16 @@ function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea cones = [CO.HypoPerLogdet(dim, true)] cone_idxs = [1:dim] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.x[2] ≈ r.primal_obj atol=1e-4 rtol=1e-4 - @test r.x[1] ≈ -1 atol=1e-4 rtol=1e-4 - @test r.s[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.s[3:end]) / r.s[1]) + side) ≈ r.s[2] atol=1e-4 rtol=1e-4 - @test r.z[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.z[3:end]) / r.z[2]) ≈ r.z[1] atol=1e-4 rtol=1e-4 + @test r.x[2] ≈ r.primal_obj atol=tol rtol=tol + @test r.x[1] ≈ -1 atol=tol rtol=tol + @test r.s[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.s[3:end]) / r.s[1]) + side) ≈ r.s[2] atol=tol rtol=tol + @test r.z[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.z[3:end]) / r.z[2]) ≈ r.z[1] atol=tol rtol=tol end -function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) @@ -730,13 +769,14 @@ function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linea cones = [CO.HypoPerLogdet(dim)] cone_idxs = [1:dim] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.x[1] ≈ -r.primal_obj atol=1e-4 rtol=1e-4 - @test r.x ≈ [0, 0] atol=1e-4 rtol=1e-4 + @test r.x[1] ≈ -r.primal_obj atol=tol rtol=tol + @test r.x ≈ [0, 0] atol=tol rtol=tol end -function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) A = Float64[1 zeros(1, l)] @@ -746,14 +786,15 @@ function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear cones = [CO.EpiPerSumExp(l + 2)] cone_idxs = [1:(l + 2)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.x[1] ≈ 1 atol=1e-4 rtol=1e-4 - @test r.s[2] ≈ 0 atol=1e-4 rtol=1e-4 - @test r.s[1] ≈ 1 atol=1e-4 rtol=1e-4 + @test r.x[1] ≈ 1 atol=tol rtol=tol + @test r.s[2] ≈ 0 atol=tol rtol=tol + @test r.s[1] ≈ 1 atol=tol rtol=tol end -function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear_model::Type{<:MO.LinearModel}, T::Type{<:HypReal}, verbose::Bool) +function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} + tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) A = Float64[1 zeros(1, l)] @@ -763,9 +804,9 @@ function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver}, linear cones = [CO.EpiPerSumExp(l + 2)] cone_idxs = [1:(l + 2)] - r = solve_and_check(c, A, b, G, h, cones, cone_idxs, system_solver, linear_model, verbose) + r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal - @test r.x[1] ≈ 1 atol=1e-4 rtol=1e-4 - @test r.s[2] ≈ 1 atol=1e-4 rtol=1e-4 - @test r.s[2] * sum(exp, r.s[3:end] / r.s[2]) ≈ r.s[1] atol=1e-4 rtol=1e-4 + @test r.x[1] ≈ 1 atol=tol rtol=tol + @test r.s[2] ≈ 1 atol=tol rtol=tol + @test r.s[2] * sum(exp, r.s[3:end] / r.s[2]) ≈ r.s[1] atol=tol rtol=tol end diff --git a/test/runtests.jl b/test/runtests.jl index 05f2264f1..b589e16e4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -68,8 +68,8 @@ include(joinpath(@__DIR__, "native.jl")) @info("starting native interface tests") verbose = true real_types = [ - Float32, Float64, + Float32, BigFloat, ] system_solvers = [ @@ -84,18 +84,18 @@ testfuns_singular = [ inconsistent1, inconsistent2, ] -# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types -# t(s, T, MO.PreprocessedLinearModel, verbose) -# end +@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types + t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +end linear_models = [ - # MO.PreprocessedLinearModel, + MO.PreprocessedLinearModel, MO.RawLinearModel, ] testfuns_nonsingular = [ - # orthant1, + orthant1, orthant2, - # orthant3, - # orthant4, + orthant3, + orthant4, # epinorminf1, # epinorminf2, # epinorminf3, @@ -133,7 +133,7 @@ testfuns_nonsingular = [ # if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel # continue # QRChol linear system solver needs preprocessed model # end - t(s, m, T, verbose) + t(s{T}, m{T}, verbose) end # @info("starting MathOptInterface tests") From 49bcc1367ca2ba63be6d25506d5a65bbc6cec0fb Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 01:40:54 -0400 Subject: [PATCH 03/20] get more cones passing tests with generic reals --- src/Cones/Cones.jl | 50 ++++---- src/Cones/epinormeucl.jl | 38 +++--- src/Cones/epinorminf.jl | 38 +++--- src/Cones/epinormspectral.jl | 2 +- src/Cones/epiperpower.jl | 2 +- src/Cones/epipersquare.jl | 8 +- src/Cones/epipersumexp.jl | 2 +- src/Cones/hypogeomean.jl | 2 +- src/Cones/hypoperlog.jl | 2 +- src/Cones/hypoperlogdet.jl | 2 +- src/Cones/orthant.jl | 39 +++--- src/Cones/semidefinite.jl | 6 +- src/Cones/wsospolyinterp.jl | 4 +- src/Cones/wsospolyinterpmat.jl | 2 +- src/Cones/wsospolyinterpsoc.jl | 2 +- test/native.jl | 220 ++++++++++++++++----------------- test/runtests.jl | 20 +-- 17 files changed, 220 insertions(+), 219 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 8d935174c..3a6c89d84 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -7,53 +7,55 @@ functions and caches for cones module Cones using LinearAlgebra +import LinearAlgebra.BlasReal using ForwardDiff using DiffResults -# using TimerOutputs import Hypatia.HypReal -abstract type Cone end +const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} + +abstract type Cone{T <: HypReal} end include("orthant.jl") include("epinorminf.jl") include("epinormeucl.jl") -include("epipersquare.jl") -include("hypoperlog.jl") -include("epiperpower.jl") -include("epipersumexp.jl") -include("hypogeomean.jl") -include("epinormspectral.jl") -include("semidefinite.jl") -include("hypoperlogdet.jl") -include("wsospolyinterp.jl") -include("wsospolyinterpmat.jl") -include("wsospolyinterpsoc.jl") +# include("epipersquare.jl") +# include("hypoperlog.jl") +# include("epiperpower.jl") +# include("epipersumexp.jl") +# include("hypogeomean.jl") +# include("epinormspectral.jl") +# include("semidefinite.jl") +# include("hypoperlogdet.jl") +# include("wsospolyinterp.jl") +# include("wsospolyinterpmat.jl") +# include("wsospolyinterpsoc.jl") use_dual(cone::Cone) = cone.use_dual -load_point(cone::Cone, point::AbstractVector{<:HypReal}) = (cone.point = point) +load_point(cone::Cone{T}, point::AbstractVector{T}) where {T <: HypReal} = (cone.point = point) dimension(cone::Cone) = cone.dim -function factorize_hess(cone::Cone) +function factorize_hess(cone::Cone{T}) where {T <: BlasReal} @. cone.H2 = cone.H - # cone.F = bunchkaufman!(Symmetric(cone.H2, :U), true, check = false) # return issuccess(cone.F) - cone.F = cholesky!(Symmetric(cone.H2, :U), Val(true), check = false) return isposdef(cone.F) end +function factorize_hess(cone::Cone{T}) where {T <: HypReal} + @. cone.H2 = cone.H + cone.F = cholesky!(Symmetric(cone.H2, :U), check = false) # TODO generic pivoted cholesky not implemented yet in Julia + return isposdef(cone.F) +end + grad(cone::Cone) = cone.g hess(cone::Cone) = Symmetric(cone.H, :U) -inv_hess(cone::Cone) = inv(cone.F) +inv_hess(cone::Cone) = Symmetric(inv(cone.F), :U) hess_fact(cone::Cone) = cone.F -# hessL(cone::Cone) = cone.F.L -# inv_hessL(cone::Cone) = inv(cone.F.L) -hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = mul!(prod, Symmetric(cone.H, :U), arr) -inv_hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = ldiv!(prod, cone.F, arr) -# hessL_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = mul!(prod, cone.F.L, arr) -# inv_hessL_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::Cone) = ldiv!(prod, cone.F.L, arr) +hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::Cone) where {T <: HypReal} = mul!(prod, Symmetric(cone.H, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::Cone) where {T <: HypReal} = ldiv!(prod, cone.F, arr) # utilities for converting between smat and svec forms (lower triangle) for symmetric matrices # TODO only need to do lower triangle if use symmetric matrix types diff --git a/src/Cones/epinormeucl.jl b/src/Cones/epinormeucl.jl index 732203c79..029776386 100644 --- a/src/Cones/epinormeucl.jl +++ b/src/Cones/epinormeucl.jl @@ -8,53 +8,53 @@ barrier from "Self-Scaled Barriers and Interior-Point Methods for Convex Program -log(u^2 - norm(w)^2) =# -mutable struct EpiNormEucl <: Cone +mutable struct EpiNormEucl{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - Hi::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + Hi::Matrix{T} - function EpiNormEucl(dim::Int, is_dual::Bool) - cone = new() + function EpiNormEucl{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -EpiNormEucl(dim::Int) = EpiNormEucl(dim, false) +EpiNormEucl{T}(dim::Int) where {T <: HypReal} = EpiNormEucl{T}(dim, false) -function setup_data(cone::EpiNormEucl) +function setup_data(cone::EpiNormEucl{T}) where {T <: HypReal} dim = cone.dim - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) cone.Hi = similar(cone.H) return end get_nu(cone::EpiNormEucl) = 1 -set_initial_point(arr::AbstractVector{Float64}, cone::EpiNormEucl) = (@. arr = 0.0; arr[1] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiNormEucl{T}) where {T <: HypReal} = (@. arr = zero(T); arr[1] = one(T); arr) -function check_in_cone(cone::EpiNormEucl) +function check_in_cone(cone::EpiNormEucl{T}) where {T <: HypReal} u = cone.point[1] w = view(cone.point, 2:cone.dim) - if u <= 0.0 + if u <= zero(T) return false end dist = abs2(u) - sum(abs2, w) - if dist <= 0.0 + if dist <= zero(T) return false end @. cone.g = cone.point / dist - cone.g[1] *= -1.0 + cone.g[1] = -cone.g[1] Hi = cone.Hi - mul!(Hi, cone.point, cone.point') # TODO syrk + mul!(Hi, cone.point, cone.point') # TODO use syrk @. Hi += Hi Hi[1, 1] -= dist for j in 2:cone.dim @@ -64,7 +64,7 @@ function check_in_cone(cone::EpiNormEucl) H = cone.H @. H = Hi for j in 2:cone.dim - H[1, j] = H[j, 1] = -H[j, 1] + H[1, j] = H[j, 1] = -H[j, 1] # TODO only need upper tri end @. H *= abs2(inv(dist)) @@ -73,4 +73,4 @@ end inv_hess(cone::EpiNormEucl) = Symmetric(cone.Hi, :U) -inv_hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::EpiNormEucl) = mul!(prod, Symmetric(cone.Hi, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::EpiNormEucl{T}) where {T <: HypReal} = mul!(prod, Symmetric(cone.Hi, :U), arr) diff --git a/src/Cones/epinorminf.jl b/src/Cones/epinorminf.jl index 8f4594980..e308aeea6 100644 --- a/src/Cones/epinorminf.jl +++ b/src/Cones/epinorminf.jl @@ -10,53 +10,53 @@ barrier from "Barrier Functions in Interior Point Methods" by Osman Guler TODO for efficiency, don't construct full H matrix (arrow fill) =# -mutable struct EpiNormInf <: Cone +mutable struct EpiNormInf{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F - function EpiNormInf(dim::Int, is_dual::Bool) - cone = new() + function EpiNormInf{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -EpiNormInf(dim::Int) = EpiNormInf(dim, false) +EpiNormInf{T}(dim::Int) where {T <: HypReal} = EpiNormInf{T}(dim, false) -function setup_data(cone::EpiNormInf) +function setup_data(cone::EpiNormInf{T}) where {T <: HypReal} dim = cone.dim - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) - cone.H2 = similar(cone.H) + cone.g = zeros(T, dim) + cone.H = zeros(T, dim, dim) + cone.H2 = copy(cone.H) return end get_nu(cone::EpiNormInf) = cone.dim -set_initial_point(arr::AbstractVector{Float64}, cone::EpiNormInf) = (@. arr = 0.0; arr[1] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiNormInf{T}) where {T <: HypReal} = (@. arr = zero(T); arr[1] = one(T); arr) -function check_in_cone(cone::EpiNormInf) +function check_in_cone(cone::EpiNormInf{T}) where {T <: HypReal} u = cone.point[1] w = view(cone.point, 2:cone.dim) if u <= maximum(abs, w) return false end - # TODO don't explicitly construct full matrix + # TODO don't explicitly construct full matrix (arrow) g = cone.g H = cone.H usqr = abs2(u) - g1 = 0.0 - h1 = 0.0 + g1 = zero(T) + h1 = zero(T) for j in eachindex(w) - iuwj = 2.0 / (usqr - abs2(w[j])) + iuwj = T(2) / (usqr - abs2(w[j])) g1 += iuwj wiuwj = w[j] * iuwj h1 += abs2(iuwj) diff --git a/src/Cones/epinormspectral.jl b/src/Cones/epinormspectral.jl index baadba9eb..ee08cc29b 100644 --- a/src/Cones/epinormspectral.jl +++ b/src/Cones/epinormspectral.jl @@ -13,7 +13,7 @@ barrier from "Interior-Point Polynomial Algorithms in Convex Programming" by Nes # TODO eliminate allocations for incone check =# -mutable struct EpiNormSpectral <: Cone +mutable struct EpiNormSpectral{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int n::Int diff --git a/src/Cones/epiperpower.jl b/src/Cones/epiperpower.jl index 8ad66c7c0..02bb4a189 100644 --- a/src/Cones/epiperpower.jl +++ b/src/Cones/epiperpower.jl @@ -12,7 +12,7 @@ TODO get gradient and hessian analytically (may be nicer if redefine as u >= v/a TODO although this barrier has a lower parameter, maybe the more standard barrier is more numerically robust =# -mutable struct EpiPerPower <: Cone +mutable struct EpiPerPower{T <: HypReal} <: Cone{T} use_dual::Bool alpha::Float64 diff --git a/src/Cones/epipersquare.jl b/src/Cones/epipersquare.jl index 015e471d6..058090794 100644 --- a/src/Cones/epipersquare.jl +++ b/src/Cones/epipersquare.jl @@ -9,7 +9,7 @@ barrier from "Self-Scaled Barriers and Interior-Point Methods for Convex Program -log(2*u*v - norm_2(w)^2) =# -mutable struct EpiPerSquare <: Cone +mutable struct EpiPerSquare{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int @@ -77,9 +77,9 @@ function check_in_cone(cone::EpiPerSquare) end # calcg!(g::AbstractVector{Float64}, cone::EpiPerSquare) = (@. g = cone.point/cone.dist; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) -# calcHiarr!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::EpiPerSquare) = mul!(prod, cone.Hi, arr) -# calcHarr!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::EpiPerSquare) = mul!(prod, cone.H, arr) +# calcHiarr!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, cone.Hi, arr) +# calcHarr!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, cone.H, arr) inv_hess(cone::EpiPerSquare) = Symmetric(cone.Hi, :U) -inv_hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::EpiPerSquare) = mul!(prod, Symmetric(cone.Hi, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, Symmetric(cone.Hi, :U), arr) diff --git a/src/Cones/epipersumexp.jl b/src/Cones/epipersumexp.jl index 0fe86d9d5..81b616270 100644 --- a/src/Cones/epipersumexp.jl +++ b/src/Cones/epipersumexp.jl @@ -12,7 +12,7 @@ TODO use the numerically safer way to evaluate LSE function TODO compare alternative barrier -log(u - v*sum(wi -> exp(wi/v), w)) - log(u) - log(v) =# -mutable struct EpiPerSumExp <: Cone +mutable struct EpiPerSumExp{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int diff --git a/src/Cones/hypogeomean.jl b/src/Cones/hypogeomean.jl index dc6d6a38e..9f5d8b941 100644 --- a/src/Cones/hypogeomean.jl +++ b/src/Cones/hypogeomean.jl @@ -11,7 +11,7 @@ dual barrier (modified by reflecting around u = 0 and using dual cone definition TODO try to make barrier evaluation more efficient =# -mutable struct HypoGeomean <: Cone +mutable struct HypoGeomean{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int alpha::Vector{Float64} diff --git a/src/Cones/hypoperlog.jl b/src/Cones/hypoperlog.jl index 62f3e8ef0..bcc116158 100644 --- a/src/Cones/hypoperlog.jl +++ b/src/Cones/hypoperlog.jl @@ -14,7 +14,7 @@ TODO could write the inverse hessian analytically rather than factorizing TODO choose a better interior direction =# -mutable struct HypoPerLog <: Cone +mutable struct HypoPerLog{T <: HypReal} <: Cone{T} use_dual::Bool point::AbstractVector{Float64} diff --git a/src/Cones/hypoperlogdet.jl b/src/Cones/hypoperlogdet.jl index d3421e94c..b66070d48 100644 --- a/src/Cones/hypoperlogdet.jl +++ b/src/Cones/hypoperlogdet.jl @@ -9,7 +9,7 @@ barrier (guessed, based on analogy to hypoperlog barrier) -log(v*logdet(W/v) - u) - logdet(W) - log(v) =# -mutable struct HypoPerLogdet <: Cone +mutable struct HypoPerLogdet{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int side::Int diff --git a/src/Cones/orthant.jl b/src/Cones/orthant.jl index f178ca799..9de03c5ec 100644 --- a/src/Cones/orthant.jl +++ b/src/Cones/orthant.jl @@ -10,55 +10,56 @@ nonnegative cone: -sum_i(log(u_i)) nonpositive cone: -sum_i(log(-u_i)) =# -mutable struct Nonnegative <: Cone +mutable struct Nonnegative{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - point::AbstractVector{<:HypReal} + point::AbstractVector{T} - function Nonnegative(dim::Int, is_dual::Bool) - cone = new() + function Nonnegative{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -Nonnegative(dim::Int) = Nonnegative(dim, false) -Nonnegative() = Nonnegative(1) +Nonnegative{T}(dim::Int) where {T <: HypReal} = Nonnegative{T}(dim, false) +Nonnegative{T}() where {T <: HypReal} = Nonnegative{T}(1) -mutable struct Nonpositive <: Cone +mutable struct Nonpositive{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - point::AbstractVector{<:HypReal} + point::AbstractVector{T} - function Nonpositive(dim::Int, is_dual::Bool) - cone = new() + function Nonpositive{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -Nonpositive(dim::Int) = Nonpositive(dim, false) -Nonpositive() = Nonpositive(1) +Nonpositive{T}(dim::Int) where {T <: HypReal} = Nonpositive{T}(dim, false) +Nonpositive{T}() where {T <: HypReal} = Nonpositive{T}(1) -OrthantCone = Union{Nonnegative, Nonpositive} +const OrthantCone{T <: HypReal} = Union{Nonnegative{T}, Nonpositive{T}} setup_data(cone::OrthantCone) = nothing get_nu(cone::OrthantCone) = cone.dim -set_initial_point(arr::AbstractVector{<:HypReal}, cone::Nonnegative) = (@. arr = 1.0; arr) -set_initial_point(arr::AbstractVector{<:HypReal}, cone::Nonpositive) = (@. arr = -1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::Nonnegative{T}) where {T <: HypReal} = (@. arr = one(T); arr) +set_initial_point(arr::AbstractVector{T}, cone::Nonpositive{T}) where {T <: HypReal} = (@. arr = -one(T); arr) -check_in_cone(cone::Nonnegative) = all(u -> (u > 0.0), cone.point) -check_in_cone(cone::Nonpositive) = all(u -> (u < 0.0), cone.point) +check_in_cone(cone::Nonnegative{T}) where {T <: HypReal} = all(u -> (u > zero(T)), cone.point) +check_in_cone(cone::Nonpositive{T}) where {T <: HypReal} = all(u -> (u < zero(T)), cone.point) +# TODO eliminate allocs grad(cone::OrthantCone) = -inv.(cone.point) hess(cone::OrthantCone) = Diagonal(abs2.(inv.(cone.point))) inv_hess(cone::OrthantCone) = Diagonal(abs2.(cone.point)) -hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::OrthantCone) = (@. prod = arr / cone.point / cone.point; prod) -inv_hess_prod!(prod::AbstractArray{<:HypReal}, arr::AbstractArray{<:HypReal}, cone::OrthantCone) = (@. prod = arr * cone.point * cone.point; prod) +hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::OrthantCone{T}) where {T <: HypReal} = (@. prod = arr / cone.point / cone.point; prod) +inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::OrthantCone{T}) where {T <: HypReal} = (@. prod = arr * cone.point * cone.point; prod) diff --git a/src/Cones/semidefinite.jl b/src/Cones/semidefinite.jl index 01b228a57..b367d211e 100644 --- a/src/Cones/semidefinite.jl +++ b/src/Cones/semidefinite.jl @@ -18,7 +18,7 @@ TODO RealOrComplexF64 = Union{Float64, ComplexF64} -mutable struct PosSemidef{T <: RealOrComplexF64} <: Cone +mutable struct PosSemidef{T <: HypRealOrComplex{T2}} <: Cone{T2} use_dual::Bool dim::Int side::Int @@ -29,7 +29,7 @@ mutable struct PosSemidef{T <: RealOrComplexF64} <: Cone Hi::Matrix{Float64} mat::Matrix{T} - function PosSemidef{T}(dim::Int, is_dual::Bool) where {T <: RealOrComplexF64} + function PosSemidef{T}(dim::Int, is_dual::Bool) where {T <: HypRealOrComplex} cone = new{T}() cone.use_dual = is_dual cone.dim = dim # real vector dimension @@ -181,4 +181,4 @@ end inv_hess(cone::PosSemidef) = Symmetric(cone.Hi, :U) -inv_hess_prod!(prod::AbstractArray{Float64}, arr::AbstractArray{Float64}, cone::PosSemidef) = mul!(prod, Symmetric(cone.Hi, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::PosSemidef) = mul!(prod, Symmetric(cone.Hi, :U), arr) diff --git a/src/Cones/wsospolyinterp.jl b/src/Cones/wsospolyinterp.jl index f693a62d2..ae817f7d7 100644 --- a/src/Cones/wsospolyinterp.jl +++ b/src/Cones/wsospolyinterp.jl @@ -12,9 +12,7 @@ TODO - check if gradient and hessian are correct for complex case =# -RealOrComplexF64 = Union{Float64, ComplexF64} - -mutable struct WSOSPolyInterp{T <: RealOrComplexF64} <: Cone +mutable struct WSOSPolyInterp{T <: RealOrComplexF64}{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int Ps::Vector{Matrix{T}} diff --git a/src/Cones/wsospolyinterpmat.jl b/src/Cones/wsospolyinterpmat.jl index c2e84bc05..17db66e96 100644 --- a/src/Cones/wsospolyinterpmat.jl +++ b/src/Cones/wsospolyinterpmat.jl @@ -6,7 +6,7 @@ interpolation-based weighted-sum-of-squares (multivariate) polynomial matrix con definition and dual barrier extended from "Sum-of-squares optimization without semidefinite programming" by D. Papp and S. Yildiz, available at https://arxiv.org/abs/1712.01792 =# -mutable struct WSOSPolyInterpMat <: Cone +mutable struct WSOSPolyInterpMat{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int R::Int diff --git a/src/Cones/wsospolyinterpsoc.jl b/src/Cones/wsospolyinterpsoc.jl index 4f61cb3b7..0e4cdb1d1 100644 --- a/src/Cones/wsospolyinterpsoc.jl +++ b/src/Cones/wsospolyinterpsoc.jl @@ -6,7 +6,7 @@ interpolation-based weighted-sum-of-squares (multivariate) polynomial second ord definition and dual barrier extended from "Sum-of-squares optimization without semidefinite programming" by D. Papp and S. Yildiz, available at https://arxiv.org/abs/1712.01792 =# -mutable struct WSOSPolyInterpSOC <: Cone +mutable struct WSOSPolyInterpSOC{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int R::Int diff --git a/test/native.jl b/test/native.jl index 41f98aa48..eff935729 100644 --- a/test/native.jl +++ b/test/native.jl @@ -35,7 +35,7 @@ function dimension1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear b = T[] G = T[1 0] h = T[1] - cones = [CO.Nonnegative(1, false)] + cones = [CO.Nonnegative{T}(1, false)] cone_idxs = [1:1] for use_sparse in (false, true) @@ -72,7 +72,7 @@ function consistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linea G[:, 11:15] = rnd1 * G[:, 1:5] - rnd2 * G[:, 6:10] c[11:15] = rnd1 * c[1:5] - rnd2 * c[6:10] h = zeros(q) - cones = [CO.Nonpositive(q)] + cones = [CO.Nonpositive{T}(q)] cone_idxs = [1:q] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -92,7 +92,7 @@ function inconsistent1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin b[11:15] = 2 * (rnd1 * b[1:5] - rnd2 * b[6:10]) h = zeros(q) - @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) + @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative{T}(q)], [1:q]) end function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} @@ -109,7 +109,7 @@ function inconsistent2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin c[11:15] = 2 * (rnd1 * c[1:5] - rnd2 * c[6:10]) h = zeros(q) - @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative(q)], [1:q]) + @test_throws ErrorException linear_model(c, A, b, G, h, [CO.Nonnegative{T}(q)], [1:q]) end function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} @@ -124,13 +124,13 @@ function orthant1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_m # nonnegative cone G = SparseMatrixCSC(-one(T) * I, q, n) - cones = [CO.Nonnegative(q)] + cones = [CO.Nonnegative{T}(q)] rnn = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test rnn.status == :Optimal # nonpositive cone G = SparseMatrixCSC(one(T) * I, q, n) - cones = [CO.Nonpositive(q)] + cones = [CO.Nonpositive{T}(q)] rnp = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test rnp.status == :Optimal @@ -149,12 +149,12 @@ function orthant2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_m cone_idxs = [1:q] # use dual barrier - cones = [CO.Nonnegative(q, true)] + cones = [CO.Nonnegative{T}(q, true)] r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # use primal barrier - cones = [CO.Nonnegative(q, false)] + cones = [CO.Nonnegative{T}(q, false)] r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal @@ -173,12 +173,12 @@ function orthant3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_m cone_idxs = [1:q] # use dual barrier - cones = [CO.Nonpositive(q, true)] + cones = [CO.Nonpositive{T}(q, true)] r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # use primal barrier - cones = [CO.Nonpositive(q, false)] + cones = [CO.Nonpositive{T}(q, false)] r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal @@ -196,13 +196,13 @@ function orthant4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_m h = G * ones(n) # mixture of nonnegative and nonpositive cones - cones = [CO.Nonnegative(4, false), CO.Nonnegative(6, true)] + cones = [CO.Nonnegative{T}(4, false), CO.Nonnegative{T}(6, true)] cone_idxs = [1:4, 5:10] r1 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r1.status == :Optimal # only nonnegative cone - cones = [CO.Nonnegative(10, false)] + cones = [CO.Nonnegative{T}(10, false)] cone_idxs = [1:10] r2 = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r2.status == :Optimal @@ -212,12 +212,12 @@ end function epinorminf1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, -1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, inv(sqrt(2))] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cones = [CO.EpiNormInf(3)] + c = T[0, -1, -1] + A = T[1 0 0; 0 1 0] + b = T[1, inv(sqrt(2))] + G = SparseMatrixCSC(-one(T) * I, 3, 3) + h = zeros(T, 3) + cones = [CO.EpiNormInf{T}(3)] cone_idxs = [1:3] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -230,12 +230,12 @@ end function epinorminf2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) - c = Float64[1, 0, 0, 0, 0, 0] + c = T[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) b = A * ones(6) G = rand(6, 6) h = G * ones(6) - cones = [CO.EpiNormInf(6)] + cones = [CO.EpiNormInf{T}(6)] cone_idxs = [1:6] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -245,12 +245,12 @@ end function epinorminf3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[1, 0, 0, 0, 0, 0] + c = T[1, 0, 0, 0, 0, 0] A = zeros(0, 6) b = zeros(0) - G = SparseMatrixCSC(-1.0I, 6, 6) + G = Diagonal(-I, 6) h = zeros(6) - cones = [CO.EpiNormInf(6)] + cones = [CO.EpiNormInf{T}(6)] cone_idxs = [1:6] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -261,12 +261,12 @@ end function epinorminf4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, -0.4] - G = SparseMatrixCSC(-1.0I, 3, 3) + c = T[0, 1, -1] + A = T[1 0 0; 0 1 0] + b = T[1, -0.4] + G = Diagonal(-I, 3) h = zeros(3) - cones = [CO.EpiNormInf(3, true)] + cones = [CO.EpiNormInf{T}(3, true)] cone_idxs = [1:3] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -279,12 +279,12 @@ end function epinorminf5(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) Random.seed!(1) - c = Float64[1, 0, 0, 0, 0, 0] + c = T[1, 0, 0, 0, 0, 0] A = rand(-9.0:9.0, 3, 6) b = A * ones(6) G = rand(6, 6) h = G * ones(6) - cones = [CO.EpiNormInf(6, true)] + cones = [CO.EpiNormInf{T}(6, true)] cone_idxs = [1:6] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -296,13 +296,13 @@ function epinorminf6(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linea tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 3 L = 2l + 1 - c = collect(Float64, -l:l) + c = collect(T, -l:l) A = spzeros(2, L) A[1, 1] = A[1, L] = A[2, 1] = 1.0; A[2, L] = -1.0 - b = Float64[0, 0] + b = T[0, 0] G = [spzeros(1, L); sparse(1.0I, L, L); spzeros(1, L); sparse(2.0I, L, L)] h = zeros(2L + 2); h[1] = 1.0; h[L + 2] = 1.0 - cones = [CO.EpiNormInf(L + 1, true), CO.EpiNormInf(L + 1, false)] + cones = [CO.EpiNormInf{T}(L + 1, true), CO.EpiNormInf{T}(L + 1, false)] cone_idxs = [1:(L + 1), (L + 2):(2L + 2)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -315,15 +315,15 @@ end function epinormeucl1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, -1, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1, inv(sqrt(2))] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) + c = T[0, -1, -1] + A = T[1 0 0; 0 1 0] + b = T[1, inv(sqrt(2))] + G = Matrix{T}(-I, 3, 3) + h = zeros(T, 3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.EpiNormEucl(3, is_dual)] + cones = [CO.EpiNormEucl{T}(3, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -335,15 +335,15 @@ end function epinormeucl2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, -1, -1] - A = Float64[1 0 0] - b = Float64[0] - G = SparseMatrixCSC(-1.0I, 3, 3) + c = T[0, -1, -1] + A = T[1 0 0] + b = T[0] + G = Diagonal(-one(T) * I, 3) h = zeros(3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.EpiNormEucl(3, is_dual)] + cones = [CO.EpiNormEucl{T}(3, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -354,9 +354,9 @@ end function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 0, -1, -1] - A = Float64[1 0 0 0; 0 1 0 0] - b = Float64[1/2, 1] + c = T[0, 0, -1, -1] + A = T[1 0 0 0; 0 1 0 0] + b = T[1/2, 1] G = SparseMatrixCSC(-1.0I, 4, 4) h = zeros(4) cone_idxs = [1:4] @@ -373,9 +373,9 @@ end function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 0, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1/2, 1] / sqrt(2) + c = T[0, 0, -1] + A = T[1 0 0; 0 1 0] + b = T[1/2, 1] / sqrt(2) G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -392,9 +392,9 @@ end function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 1, -1, -1] - A = Float64[1 0 0 0] - b = Float64[0] + c = T[0, 1, -1, -1] + A = T[1 0 0 0] + b = T[0] G = SparseMatrixCSC(-1.0I, 4, 4) h = zeros(4) cone_idxs = [1:4] @@ -411,9 +411,9 @@ end function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, -1, 0] - A = Float64[1 0 0; 0 0 1] - b = Float64[1/2, 1] + c = T[0, -1, 0] + A = T[1 0 0; 0 0 1] + b = T[1/2, 1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -430,9 +430,9 @@ end function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, -1, 0] - A = Float64[1 0 1] - b = Float64[0] + c = T[0, -1, 0] + A = T[1 0 1] + b = T[0] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -449,9 +449,9 @@ end function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - 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] + c = T[1, 0, 1, 0, 0, 1] + A = T[1 2 3 4 5 6; 1 1 1 1 1 1] + b = T[10, 3] G = SparseMatrixCSC(-1.0I, 6, 6) h = zeros(6) cone_idxs = [1:6] @@ -468,9 +468,9 @@ end function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[1, 0, 0, 1] - A = Float64[0 0 1 0] - b = Float64[1] + c = T[1, 0, 0, 1] + A = T[0 0 1 0] + b = T[1] G = Diagonal([-1, -sqrt(2), -sqrt(2), -1]) h = zeros(4) cone_idxs = [1:4] @@ -484,9 +484,9 @@ end function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[1, 1, 1] - A = Float64[0 1 0; 1 0 0] - b = Float64[2, 1] + c = T[1, 1, 1] + A = T[0 1 0; 1 0 0] + b = T[2, 1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cones = [CO.HypoPerLog()] @@ -503,9 +503,9 @@ end function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[-1, 0, 0] - A = Float64[0 1 0] - b = Float64[0] + c = T[-1, 0, 0] + A = T[0 1 0] + b = T[0] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cones = [CO.HypoPerLog()] @@ -518,12 +518,12 @@ end function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[1, 1, 1] - A = Matrix{Float64}(undef, 0, 3) - b = Vector{Float64}(undef, 0) + c = T[1, 1, 1] + A = zeros(T, 0, 3) + b = zeros(T, 0) G = sparse([1, 2, 3, 4], [1, 2, 3, 1], -ones(4)) h = zeros(4) - cones = [CO.HypoPerLog(), CO.Nonnegative(1)] + cones = [CO.HypoPerLog(), CO.Nonnegative{T}(1)] cone_idxs = [1:3, 4:4] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -535,9 +535,9 @@ end function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 0, 1] - A = Float64[0 1 0; 1 0 0] - b = Float64[1, -1] + c = T[0, 0, 1] + A = T[0 1 0; 1 0 0] + b = T[1, -1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cones = [CO.HypoPerLog(true)] @@ -551,9 +551,9 @@ end function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[1, 0, -1, 0, 0, -1] - A = Float64[1 1 0 1/2 0 0; 0 0 0 0 1 0] - b = Float64[2, 1] + c = T[1, 0, -1, 0, 0, -1] + A = T[1 1 0 1/2 0 0; 0 0 0 0 1 0] + b = T[2, 1] G = SparseMatrixCSC(-1.0I, 6, 6) h = zeros(6) cones = [CO.EpiPerPower(5.0, false), CO.EpiPerPower(2.5, false)] @@ -567,9 +567,9 @@ end function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 0, -1] - A = Float64[1 0 0; 0 1 0] - b = Float64[1/2, 1] + c = T[0, 0, -1] + A = T[1 0 0; 0 1 0] + b = T[1/2, 1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -586,9 +586,9 @@ end function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[0, 0, 1] - A = Float64[1 0 0; 0 1 0] - b = Float64[0, 1] + c = T[0, 0, 1] + A = T[1 0 0; 0 1 0] + b = T[0, 1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -605,9 +605,9 @@ end function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - 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] + c = T[1, 0, 0, -1, -1, 0] + A = T[1 1 1/2 0 0 0; 0 0 0 0 0 1] + b = T[2, 1] G = SparseMatrixCSC(-1.0I, 6, 6)[[4, 1, 2, 5, 3, 6], :] h = zeros(6) cones = [CO.HypoGeomean([0.2, 0.8], false), CO.HypoGeomean([0.4, 0.6], false)] @@ -621,9 +621,9 @@ end function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = Float64[-1, 0, 0] - A = Float64[0 0 1; 0 1 0] - b = Float64[1/2, 1] + c = T[-1, 0, 0] + A = T[0 0 1; 0 1 0] + b = T[1/2, 1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) cone_idxs = [1:3] @@ -642,7 +642,7 @@ function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 4 c = vcat(0.0, ones(l)) - A = Float64[1.0 zeros(1, l)] + A = T[1.0 zeros(1, l)] G = SparseMatrixCSC(-1.0I, l + 1, l + 1) h = zeros(l + 1) cone_idxs = [1:(l + 1)] @@ -664,7 +664,7 @@ function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line c = ones(l) A = zeros(0, l) b = zeros(0) - G = Float64[zeros(1, l); Matrix(-1.0I, l, l)] + G = T[zeros(1, l); Matrix(-1.0I, l, l)] h = zeros(l + 1) cone_idxs = [1:(l + 1)] @@ -710,9 +710,9 @@ function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li Random.seed!(1) side = 4 dim = 2 + div(side * (side + 1), 2) - c = Float64[-1, 0] - A = Float64[0 1] - b = Float64[1] + c = T[-1, 0] + A = T[0 1] + b = T[1] G = SparseMatrixCSC(-1.0I, dim, 2) mat_half = randn(side, side) mat = mat_half * mat_half' @@ -734,9 +734,9 @@ function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) - c = Float64[0, 1] - A = Float64[1 0] - b = Float64[-1] + c = T[0, 1] + A = T[1 0] + b = T[-1] G = SparseMatrixCSC(-1.0I, dim, 2) mat_half = rand(side, side) mat = mat_half * mat_half' @@ -758,9 +758,9 @@ function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) - c = Float64[-1, 0] - A = Float64[0 1] - b = Float64[0] + c = T[-1, 0] + A = T[0 1] + b = T[0] G = SparseMatrixCSC(-1.0I, dim, 2) mat_half = rand(side, side) mat = mat_half * mat_half' @@ -779,9 +779,9 @@ function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) - A = Float64[1 zeros(1, l)] - b = Float64[1] - G = Float64[-1 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] + A = T[1 zeros(1, l)] + b = T[1] + G = T[-1 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] h = zeros(l + 2) cones = [CO.EpiPerSumExp(l + 2)] cone_idxs = [1:(l + 2)] @@ -797,9 +797,9 @@ function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) - A = Float64[1 zeros(1, l)] - b = Float64[1] - G = Float64[-1.0 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] + A = T[1 zeros(1, l)] + b = T[1] + G = T[-1.0 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] h = zeros(l + 2); h[2] = 1.0 cones = [CO.EpiPerSumExp(l + 2)] cone_idxs = [1:(l + 2)] diff --git a/test/runtests.jl b/test/runtests.jl index b589e16e4..c62812e10 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,7 +66,7 @@ include(joinpath(@__DIR__, "native.jl")) # end @info("starting native interface tests") -verbose = true +verbose = false real_types = [ Float64, Float32, @@ -84,26 +84,26 @@ testfuns_singular = [ inconsistent1, inconsistent2, ] -@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types - t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -end +# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types +# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +# end linear_models = [ MO.PreprocessedLinearModel, MO.RawLinearModel, ] testfuns_nonsingular = [ - orthant1, - orthant2, - orthant3, - orthant4, + # orthant1, + # orthant2, + # orthant3, + # orthant4, # epinorminf1, # epinorminf2, # epinorminf3, # epinorminf4, # epinorminf5, # epinorminf6, - # epinormeucl1, - # epinormeucl2, + epinormeucl1, + epinormeucl2, # epipersquare1, # epipersquare2, # epipersquare3, From 0e97d677f4326e479c3b23a4da63296e629781e9 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 01:59:57 -0400 Subject: [PATCH 04/20] more native tests working --- src/Cones/Cones.jl | 2 +- src/Cones/epipersquare.jl | 54 ++++++++++++++++++++------------------- test/native.jl | 18 ++++++------- test/runtests.jl | 10 ++++---- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 3a6c89d84..cca8c253a 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -20,7 +20,7 @@ abstract type Cone{T <: HypReal} end include("orthant.jl") include("epinorminf.jl") include("epinormeucl.jl") -# include("epipersquare.jl") +include("epipersquare.jl") # include("hypoperlog.jl") # include("epiperpower.jl") # include("epipersumexp.jl") diff --git a/src/Cones/epipersquare.jl b/src/Cones/epipersquare.jl index 058090794..0d6e93062 100644 --- a/src/Cones/epipersquare.jl +++ b/src/Cones/epipersquare.jl @@ -12,53 +12,55 @@ barrier from "Self-Scaled Barriers and Interior-Point Methods for Convex Program mutable struct EpiPerSquare{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - Hi::Matrix{Float64} - - function EpiPerSquare(dim::Int, is_dual::Bool) - cone = new() + + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + Hi::Matrix{T} + + function EpiPerSquare{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -EpiPerSquare(dim::Int) = EpiPerSquare(dim, false) +EpiPerSquare{T}(dim::Int) where {T <: HypReal} = EpiPerSquare{T}(dim, false) -function setup_data(cone::EpiPerSquare) +function setup_data(cone::EpiPerSquare{T}) where {T <: HypReal} dim = cone.dim - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) - cone.Hi = similar(cone.H) + cone.g = zeros(T, dim) + cone.H = zeros(T, dim, dim) + cone.Hi = copy(cone.H) return end get_nu(cone::EpiPerSquare) = 2 -set_initial_point(arr::AbstractVector{Float64}, cone::EpiPerSquare) = (@. arr = 0.0; arr[1] = 1.0; arr[2] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiPerSquare{T}) where {T <: HypReal} = (@. arr = zero(T); arr[1] = one(T); arr[2] = one(T); arr) -function check_in_cone(cone::EpiPerSquare) +function check_in_cone(cone::EpiPerSquare{T}) where {T <: HypReal} u = cone.point[1] v = cone.point[2] w = view(cone.point, 3:cone.dim) - if u <= 0.0 || v <= 0.0 + if u <= zero(T) || v <= zero(T) return false end - nrm2 = 0.5*sum(abs2, w) - dist = u*v - nrm2 - if dist <= 0.0 + nrm2 = T(0.5) * sum(abs2, w) + dist = u * v - nrm2 + if dist <= zero(T) return false end @. cone.g = cone.point / dist - (cone.g[1], cone.g[2]) = (-cone.g[2], -cone.g[1]) + tmp = -cone.g[2] + cone.g[2] = -cone.g[1] + cone.g[1] = tmp Hi = cone.Hi mul!(Hi, cone.point, cone.point') # TODO syrk - Hi[2, 1] = Hi[1, 2] = nrm2 + Hi[2, 1] = Hi[1, 2] = nrm2 # TODO only need upper tri for j in 3:cone.dim Hi[j, j] += dist end @@ -71,15 +73,15 @@ function check_in_cone(cone::EpiPerSquare) end H[1, 1] = Hi[2, 2] H[2, 2] = Hi[1, 1] - @. H *= abs2(inv(dist)) + @. H = H / dist / dist return true end -# calcg!(g::AbstractVector{Float64}, cone::EpiPerSquare) = (@. g = cone.point/cone.dist; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) -# calcHiarr!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, cone.Hi, arr) -# calcHarr!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, cone.H, arr) +# calcg!(g::AbstractVector{T}, cone::EpiPerSquare) = (@. g = cone.point/cone.dist; tmp = g[1]; g[1] = -g[2]; g[2] = -tmp; g) +# calcHiarr!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::EpiPerSquare) = mul!(prod, cone.Hi, arr) +# calcHarr!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::EpiPerSquare) = mul!(prod, cone.H, arr) inv_hess(cone::EpiPerSquare) = Symmetric(cone.Hi, :U) -inv_hess_prod!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::EpiPerSquare) = mul!(prod, Symmetric(cone.Hi, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::EpiPerSquare{T}) where {T <: HypReal} = mul!(prod, Symmetric(cone.Hi, :U), arr) diff --git a/test/native.jl b/test/native.jl index eff935729..01b97afb7 100644 --- a/test/native.jl +++ b/test/native.jl @@ -357,12 +357,12 @@ function epipersquare1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin c = T[0, 0, -1, -1] A = T[1 0 0 0; 0 1 0 0] b = T[1/2, 1] - G = SparseMatrixCSC(-1.0I, 4, 4) - h = zeros(4) + G = Matrix{T}(-I, 4, 4) + h = zeros(T, 4) cone_idxs = [1:4] for is_dual in (true, false) - cones = [CO.EpiPerSquare(4, is_dual)] + cones = [CO.EpiPerSquare{T}(4, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -376,12 +376,12 @@ function epipersquare2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin c = T[0, 0, -1] A = T[1 0 0; 0 1 0] b = T[1/2, 1] / sqrt(2) - G = SparseMatrixCSC(-1.0I, 3, 3) + G = Matrix{T}(-I, 3, 3) h = zeros(3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.EpiPerSquare(3, is_dual)] + cones = [CO.EpiPerSquare{T}(3, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -392,15 +392,15 @@ end function epipersquare3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[0, 1, -1, -1] - A = T[1 0 0 0] - b = T[0] + c = [0, 1, -1, -1] + A = [1 0 0 0] + b = [0] G = SparseMatrixCSC(-1.0I, 4, 4) h = zeros(4) cone_idxs = [1:4] for is_dual in (true, false) - cones = [CO.EpiPerSquare(4, is_dual)] + cones = [CO.EpiPerSquare{T}(4, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal diff --git a/test/runtests.jl b/test/runtests.jl index c62812e10..8fe0d0358 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -102,11 +102,11 @@ testfuns_nonsingular = [ # epinorminf4, # epinorminf5, # epinorminf6, - epinormeucl1, - epinormeucl2, - # epipersquare1, - # epipersquare2, - # epipersquare3, + # epinormeucl1, + # epinormeucl2, + epipersquare1, + epipersquare2, + epipersquare3, # semidefinite1, # semidefinite2, # semidefinite3, From 929274ea986df0f2a352a19913fda97983b25cd1 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 16:18:57 -0400 Subject: [PATCH 05/20] more cones updated --- Manifest.toml | 22 ++++---- src/Cones/Cones.jl | 12 ++--- src/Cones/epinormspectral.jl | 47 ++++++++-------- src/Cones/epiperpower.jl | 30 +++++------ src/Cones/hypogeomean.jl | 34 ++++++------ src/Cones/hypoperlog.jl | 38 ++++++------- src/Cones/orthant.jl | 1 + src/Cones/semidefinite.jl | 47 ++++++++-------- test/native.jl | 102 +++++++++++++++++------------------ test/runtests.jl | 10 ++-- 10 files changed, 168 insertions(+), 175 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index 54f3adb35..3bd436438 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -29,9 +29,9 @@ version = "0.5.4" [[CSV]] deps = ["CategoricalArrays", "DataFrames", "Dates", "Mmap", "Parsers", "PooledArrays", "Profile", "Tables", "Unicode", "WeakRefStrings"] -git-tree-sha1 = "f64241c9688ae3eb003bce26ffd9ed863cfb824c" +git-tree-sha1 = "239240112fc5f18fb934942a5b6f207a8f9e45d2" uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.5.2" +version = "0.5.5" [[Calculus]] deps = ["Compat"] @@ -77,9 +77,9 @@ version = "4.0.0" [[DataFrames]] deps = ["CategoricalArrays", "Compat", "IteratorInterfaceExtensions", "Missings", "PooledArrays", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "StatsBase", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "3891a62fd843662af9f78f25bdd415530b9b9c1e" +git-tree-sha1 = "279baa6358fd5e944deccab88434f69c74cfc722" uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "0.18.2" +version = "0.18.3" [[DataStructures]] deps = ["InteractiveUtils", "OrderedCollections", "Random", "Serialization", "Test"] @@ -230,9 +230,9 @@ version = "0.9.7" [[Parsers]] deps = ["Dates", "Test"] -git-tree-sha1 = "162855122e7d2b7ffbcdd8d19d6b18472f2117bc" +git-tree-sha1 = "eaed2db080700f1013f0fc05667ecb2a082265a1" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "0.3.4" +version = "0.3.5" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] @@ -364,10 +364,10 @@ deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" [[SumOfSquares]] -deps = ["JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateMoments", "MultivariatePolynomials", "Pkg", "PolyJuMP", "Reexport", "SemialgebraicSets", "SparseArrays", "Test"] -git-tree-sha1 = "2fde6e851a9b9b5d1f33b108d8fa71857346051b" +deps = ["JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateMoments", "MultivariatePolynomials", "Pkg", "PolyJuMP", "Reexport", "SemialgebraicSets", "SparseArrays"] +git-tree-sha1 = "153b6aa62a5712644ae0786417fc249ea5794580" uuid = "4b9e565b-77fc-50a5-a571-1244f986bda1" -version = "0.3.1" +version = "0.3.2" [[TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -377,9 +377,9 @@ version = "1.0.0" [[Tables]] deps = ["IteratorInterfaceExtensions", "LinearAlgebra", "Requires", "TableTraits", "Test"] -git-tree-sha1 = "83b4a0261e5d01274f12b35d4c2212386fb15569" +git-tree-sha1 = "c5d784c61e9d243a5a6a8458d19f535b70bdedeb" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "0.2.3" +version = "0.2.4" [[Test]] deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index cca8c253a..abf1153da 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -18,15 +18,15 @@ const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} abstract type Cone{T <: HypReal} end include("orthant.jl") -include("epinorminf.jl") -include("epinormeucl.jl") -include("epipersquare.jl") +# include("epinorminf.jl") +# include("epinormeucl.jl") +# include("epipersquare.jl") +# include("semidefinite.jl") # include("hypoperlog.jl") # include("epiperpower.jl") -# include("epipersumexp.jl") # include("hypogeomean.jl") -# include("epinormspectral.jl") -# include("semidefinite.jl") +include("epinormspectral.jl") +# include("epipersumexp.jl") # include("hypoperlogdet.jl") # include("wsospolyinterp.jl") # include("wsospolyinterpmat.jl") diff --git a/src/Cones/epinormspectral.jl b/src/Cones/epinormspectral.jl index ee08cc29b..289c16aa9 100644 --- a/src/Cones/epinormspectral.jl +++ b/src/Cones/epinormspectral.jl @@ -9,7 +9,6 @@ W is vectorized column-by-column (i.e. vec(W) in Julia) barrier from "Interior-Point Polynomial Algorithms in Convex Programming" by Nesterov & Nemirovskii 1994 -logdet(u*I_n - W*W'/u) - log(u) -# TODO don't use ForwardDiff: use identity for inverse of matrix plus I and properties of SVD unitary matrices # TODO eliminate allocations for incone check =# @@ -18,18 +17,18 @@ mutable struct EpiNormSpectral{T <: HypReal} <: Cone{T} dim::Int n::Int m::Int - - point::AbstractVector{Float64} - W::Matrix{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + + point::AbstractVector{T} + W::Matrix{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F - function EpiNormSpectral(n::Int, m::Int, is_dual::Bool) + function EpiNormSpectral{T}(n::Int, m::Int, is_dual::Bool) where {T <: HypReal} @assert n <= m dim = n * m + 1 - cone = new() + cone = new{T}() cone.use_dual = is_dual cone.dim = dim cone.n = n @@ -38,46 +37,46 @@ mutable struct EpiNormSpectral{T <: HypReal} <: Cone{T} end end -EpiNormSpectral(n::Int, m::Int) = EpiNormSpectral(n, m, false) +EpiNormSpectral{T}(n::Int, m::Int) where {T <: HypReal} = EpiNormSpectral{T}(n, m, false) -function setup_data(cone::EpiNormSpectral) +function setup_data(cone::EpiNormSpectral{T}) where {T <: HypReal} dim = cone.dim - cone.W = Matrix{Float64}(undef, cone.n, cone.m) - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) + cone.W = Matrix{T}(undef, cone.n, cone.m) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) cone.H2 = similar(cone.H) return end get_nu(cone::EpiNormSpectral) = cone.n + 1 -set_initial_point(arr::AbstractVector{Float64}, cone::EpiNormSpectral) = (@. arr = 0.0; arr[1] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiNormSpectral{T}) where {T <: HypReal} = (@. arr = zero(T); arr[1] = one(T); arr) -function check_in_cone(cone::EpiNormSpectral) +function check_in_cone(cone::EpiNormSpectral{T}) where {T <: HypReal} u = cone.point[1] - if u <= 0.0 + if u <= zero(T) return false end W = cone.W W[:] = view(cone.point, 2:cone.dim) n = cone.n m = cone.m - X = Symmetric(W * W') + X = Symmetric(W * W') # TODO syrk Z = Symmetric(u * I - X / u) - F = cholesky(Z, Val(true), check = false) # TODO in place + F = cholesky(Z, Val(true), check = false) # TODO in place; doesn't work for generic reals if !isposdef(F) return false end # TODO figure out structured form of inverse? could simplify algebra Zi = Symmetric(inv(F)) - Eu = Symmetric(I + X / u^2) - cone.H .= 0.0 + Eu = Symmetric(I + X / u / u) + cone.H .= zero(T) - cone.g[1] = -dot(Zi, Eu) - 1 / u + cone.g[1] = -dot(Zi, Eu) - inv(u) cone.g[2:end] = vec(2 * Zi * W / u) ZiEuZi = Symmetric(Zi * Eu * Zi) - cone.H[1, 1] = dot(ZiEuZi, Eu) + (2 * dot(Zi, X) / u + 1) / u / u + cone.H[1, 1] = dot(ZiEuZi, Eu) + (2 * dot(Zi, X) / u + one(T)) / u / u cone.H[1, 2:end] = vec(-2 * (ZiEuZi + Zi / u) * W / u) tmpvec = zeros(n) @@ -109,7 +108,5 @@ function check_in_cone(cone::EpiNormSpectral) p += 1 end - # @assert isapprox(Symmetric(cone.H, :U) * cone.point, -cone.g, atol = 1e-6, rtol = 1e-6) - return factorize_hess(cone) end diff --git a/src/Cones/epiperpower.jl b/src/Cones/epiperpower.jl index 02bb4a189..a0b91f56e 100644 --- a/src/Cones/epiperpower.jl +++ b/src/Cones/epiperpower.jl @@ -14,17 +14,17 @@ TODO although this barrier has a lower parameter, maybe the more standard barrie mutable struct EpiPerPower{T <: HypReal} <: Cone{T} use_dual::Bool - alpha::Float64 + alpha::Real - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F barfun::Function diffres - function EpiPerPower(alpha::Float64, is_dual::Bool) + function EpiPerPower{T}(alpha::Real, is_dual::Bool) where {T <: HypReal} @assert alpha > 1.0 cone = new() cone.use_dual = is_dual @@ -33,12 +33,12 @@ mutable struct EpiPerPower{T <: HypReal} <: Cone{T} end end -EpiPerPower(alpha::Float64) = EpiPerPower(alpha, false) +EpiPerPower{T}(alpha::Real) where {T <: HypReal} = EpiPerPower{T}(alpha, false) -function setup_data(cone::EpiPerPower) - cone.g = Vector{Float64}(undef, 3) - cone.H = Matrix{Float64}(undef, 3, 3) - cone.H2 = similar(cone.H) +function setup_data(cone::EpiPerPower{T}) where {T <: HypReal} + cone.g = zeros(T, 3) + cone.H = zeros(T, 3, 3) + cone.H2 = copy(cone.H) alpha = cone.alpha ialpha2 = 2.0 * inv(alpha) @@ -53,13 +53,13 @@ end dimension(cone::EpiPerPower) = 3 -get_nu(cone::EpiPerPower) = 3 - 2 * min(inv(cone.alpha), 1.0 - inv(cone.alpha)) +get_nu(cone::EpiPerPower) = 3 - 2 * min(inv(cone.alpha), 1 - inv(cone.alpha)) -set_initial_point(arr::AbstractVector{Float64}, cone::EpiPerPower) = (arr[1] = 1.0; arr[2] = 1.0; arr[3] = 0.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiPerPower{T}) where {T <: HypReal} = (arr[1] = one(T); arr[2] = one(T); arr[3] = zero(T); arr) -function check_in_cone(cone::EpiPerPower) +function check_in_cone(cone::EpiPerPower{T}) where {T <: HypReal} (u, v, w) = cone.point - if u <= 0.0 || v <= 0.0 || u <= v * (abs(w / v))^cone.alpha + if u <= zero(T) || v <= zero(T) || u <= v * (abs(w / v))^cone.alpha return false end diff --git a/src/Cones/hypogeomean.jl b/src/Cones/hypogeomean.jl index 9f5d8b941..b8cb3597f 100644 --- a/src/Cones/hypogeomean.jl +++ b/src/Cones/hypogeomean.jl @@ -14,23 +14,23 @@ TODO try to make barrier evaluation more efficient mutable struct HypoGeomean{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - alpha::Vector{Float64} + alpha::Vector{<:Real} - ialpha::Vector{Float64} - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + ialpha::Vector{<:Real} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F barfun::Function diffres - function HypoGeomean(alpha::Vector{Float64}, is_dual::Bool) + function HypoGeomean{T}(alpha::Vector{<:Real}, is_dual::Bool) where {T <: HypReal} dim = length(alpha) + 1 @assert dim >= 3 @assert all(ai >= 0.0 for ai in alpha) - @assert sum(alpha) == 1.0 - cone = new() + @assert sum(alpha) == 1.0 # TODO this check may be too strict + cone = new{T}() cone.use_dual = !is_dual # using dual barrier cone.dim = dim cone.alpha = alpha @@ -38,16 +38,16 @@ mutable struct HypoGeomean{T <: HypReal} <: Cone{T} end end -HypoGeomean(alpha::Vector{Float64}) = HypoGeomean(alpha, false) +HypoGeomean{T}(alpha::Vector{<:Real}) where {T <: HypReal} = HypoGeomean{T}(alpha, false) -function setup_data(cone::HypoGeomean) +function setup_data(cone::HypoGeomean{T}) where {T <: HypReal} dim = cone.dim alpha = cone.alpha ialpha = inv.(alpha) cone.ialpha = ialpha - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) - cone.H2 = similar(cone.H) + cone.g = zeros(T, dim) + cone.H = zeros(T, dim, dim) + cone.H2 = copy(cone.H) function barfun(point) u = point[1] w = view(point, 2:dim) @@ -60,12 +60,12 @@ end get_nu(cone::HypoGeomean) = cone.dim -set_initial_point(arr::AbstractVector{Float64}, cone::HypoGeomean) = (@. arr = 1.0; arr[1] = -prod(cone.ialpha[i]^cone.alpha[i] for i in eachindex(cone.alpha)) / cone.dim; arr) +set_initial_point(arr::AbstractVector{T}, cone::HypoGeomean{T}) where {T <: HypReal} = (@. arr = one(T); arr[1] = -prod(cone.ialpha[i]^cone.alpha[i] for i in eachindex(cone.alpha)) / cone.dim; arr) -function check_in_cone(cone::HypoGeomean) +function check_in_cone(cone::HypoGeomean{T}) where {T <: HypReal} u = cone.point[1] w = view(cone.point, 2:cone.dim) - if u >= 0.0 || any(wi <= 0.0 for wi in w) + if u >= zero(T) || any(wi <= zero(T) for wi in w) return false end if sum(cone.alpha[i] * log(w[i] * cone.ialpha[i]) for i in eachindex(cone.alpha)) <= log(-u) diff --git a/src/Cones/hypoperlog.jl b/src/Cones/hypoperlog.jl index bcc116158..3c121f7f0 100644 --- a/src/Cones/hypoperlog.jl +++ b/src/Cones/hypoperlog.jl @@ -17,24 +17,24 @@ TODO choose a better interior direction mutable struct HypoPerLog{T <: HypReal} <: Cone{T} use_dual::Bool - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F - function HypoPerLog(is_dual::Bool) - cone = new() + function HypoPerLog{T}(is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual return cone end end -HypoPerLog() = HypoPerLog(false) +HypoPerLog{T}() where {T <: HypReal} = HypoPerLog{T}(false) -function setup_data(cone::HypoPerLog) - cone.g = Vector{Float64}(undef, 3) - cone.H = Matrix{Float64}(undef, 3, 3) +function setup_data(cone::HypoPerLog{T}) where {T <: HypReal} + cone.g = Vector{T}(undef, 3) + cone.H = Matrix{T}(undef, 3, 3) cone.H2 = similar(cone.H) return end @@ -43,17 +43,17 @@ dimension(cone::HypoPerLog) = 3 get_nu(cone::HypoPerLog) = 3 -set_initial_point(arr::AbstractVector{Float64}, cone::HypoPerLog) = (arr[1] = -1.0; arr[2] = 1.0; arr[3] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::HypoPerLog{T}) where {T <: HypReal} = (arr[1] = -one(T); arr[2] = one(T); arr[3] = one(T); arr) -function check_in_cone(cone::HypoPerLog) +function check_in_cone(cone::HypoPerLog{T}) where {T <: HypReal} (u, v, w) = cone.point - if (v <= 0.0) || (w <= 0.0) + if (v <= zero(T)) || (w <= zero(T)) return false end lwv = log(w / v) vlwv = v * lwv vlwvu = vlwv - u - if vlwvu <= 0.0 + if vlwvu <= zero(T) return false end @@ -61,18 +61,18 @@ function check_in_cone(cone::HypoPerLog) ivlwvu = inv(vlwvu) g = cone.g g[1] = ivlwvu - g[2] = ivlwvu * (v - u - 2.0 * vlwvu) / v - g[3] = -(1.0 + v * ivlwvu) / w + g[2] = ivlwvu * (v - u - vlwvu - vlwvu) / v + g[3] = -(one(T) + v * ivlwvu) / w # Hessian vw = v / w ivlwvu2 = abs2(ivlwvu) H = cone.H H[1, 1] = ivlwvu2 - H[1, 2] = H[2, 1] = -(lwv - 1.0) * ivlwvu2 + H[1, 2] = H[2, 1] = -(lwv - one(T)) * ivlwvu2 H[1, 3] = H[3, 1] = -vw * ivlwvu2 - H[2, 2] = abs2(lwv - 1.0) * ivlwvu2 + ivlwvu / v + inv(abs2(v)) - H[2, 3] = H[3, 2] = vw * (lwv - 1.0) * ivlwvu2 - ivlwvu / w + H[2, 2] = abs2(lwv - one(T)) * ivlwvu2 + ivlwvu / v + inv(abs2(v)) + H[2, 3] = H[3, 2] = vw * (lwv - one(T)) * ivlwvu2 - ivlwvu / w H[3, 3] = abs2(vw) * ivlwvu2 + vw / w * ivlwvu + inv(abs2(w)) return factorize_hess(cone) diff --git a/src/Cones/orthant.jl b/src/Cones/orthant.jl index 9de03c5ec..02b806a67 100644 --- a/src/Cones/orthant.jl +++ b/src/Cones/orthant.jl @@ -53,6 +53,7 @@ get_nu(cone::OrthantCone) = cone.dim set_initial_point(arr::AbstractVector{T}, cone::Nonnegative{T}) where {T <: HypReal} = (@. arr = one(T); arr) set_initial_point(arr::AbstractVector{T}, cone::Nonpositive{T}) where {T <: HypReal} = (@. arr = -one(T); arr) +# TODO can change the u > 0.0 check to signbit function check_in_cone(cone::Nonnegative{T}) where {T <: HypReal} = all(u -> (u > zero(T)), cone.point) check_in_cone(cone::Nonpositive{T}) where {T <: HypReal} = all(u -> (u < zero(T)), cone.point) diff --git a/src/Cones/semidefinite.jl b/src/Cones/semidefinite.jl index b367d211e..b25ce32b9 100644 --- a/src/Cones/semidefinite.jl +++ b/src/Cones/semidefinite.jl @@ -16,53 +16,48 @@ TODO - eliminate redundant svec_to_smat calls =# -RealOrComplexF64 = Union{Float64, ComplexF64} - -mutable struct PosSemidef{T <: HypRealOrComplex{T2}} <: Cone{T2} +mutable struct PosSemidef{T <: HypReal, R <: HypRealOrComplex{T}} <: Cone{T} use_dual::Bool dim::Int side::Int - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - Hi::Matrix{Float64} - mat::Matrix{T} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + Hi::Matrix{T} + mat::Matrix{R} - function PosSemidef{T}(dim::Int, is_dual::Bool) where {T <: HypRealOrComplex} - cone = new{T}() + function PosSemidef{T, R}(dim::Int, is_dual::Bool) where {R <: HypRealOrComplex{T}} where {T <: HypReal} + cone = new{T, R}() cone.use_dual = is_dual cone.dim = dim # real vector dimension - if T <: Complex + if R <: Complex side = isqrt(dim) # real lower triangle and imaginary under diagonal @assert side^2 == dim else side = round(Int, sqrt(0.25 + 2.0 * dim) - 0.5) # real lower triangle - @assert side * (side + 1) / 2 == dim + @assert side * (side + 1) == 2 * dim end cone.side = side return cone end end -# default to real -PosSemidef(dim::Int) = PosSemidef{Float64}(dim, false) -PosSemidef(dim::Int, is_dual::Bool) = PosSemidef{Float64}(dim, is_dual) -PosSemidef{T}(dim::Int) where {T <: RealOrComplexF64} = PosSemidef{T}(dim, false) +PosSemidef{T, R}(dim::Int) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = PosSemidef{T, R}(dim, false) -function setup_data(cone::PosSemidef{T}) where T +function setup_data(cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} dim = cone.dim - cone.mat = Matrix{T}(undef, cone.side, cone.side) - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) cone.Hi = similar(cone.H) + cone.mat = Matrix{R}(undef, cone.side, cone.side) return end get_nu(cone::PosSemidef) = cone.side -function set_initial_point(arr::AbstractVector{Float64}, cone::PosSemidef{T}) where T - incr_off = (T <: Complex) ? 2 : 1 +function set_initial_point(arr::AbstractVector{T}, cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} + incr_off = (R <: Complex) ? 2 : 1 arr .= 0.0 k = 1 for i in 1:cone.side, j in 1:i @@ -76,10 +71,10 @@ function set_initial_point(arr::AbstractVector{Float64}, cone::PosSemidef{T}) wh return arr end -function check_in_cone(cone::PosSemidef{T}) where T +function check_in_cone(cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} mat = cone.mat svec_to_smat!(mat, cone.point) - F = cholesky!(Hermitian(mat), Val(true), check = false) + F = cholesky!(Hermitian(mat), Val(true), check = false) # TODO doesn't work for generic T non-BLAS type if !isposdef(F) return false end @@ -94,7 +89,7 @@ function check_in_cone(cone::PosSemidef{T}) where T Hi = cone.Hi # TODO refactor - if T <: Complex + if R <: Complex k = 1 for i in 1:cone.side, j in 1:i k2 = 1 @@ -181,4 +176,4 @@ end inv_hess(cone::PosSemidef) = Symmetric(cone.Hi, :U) -inv_hess_prod!(prod::AbstractVecOrMat{Float64}, arr::AbstractVecOrMat{Float64}, cone::PosSemidef) = mul!(prod, Symmetric(cone.Hi, :U), arr) +inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = mul!(prod, Symmetric(cone.Hi, :U), arr) diff --git a/test/native.jl b/test/native.jl index 01b97afb7..3f186bd74 100644 --- a/test/native.jl +++ b/test/native.jl @@ -414,12 +414,12 @@ function semidefinite1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin c = T[0, -1, 0] A = T[1 0 0; 0 0 1] b = T[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) + G = Matrix{T}(-I, 3, 3) + h = zeros(T, 3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.PosSemidef(3, is_dual)] + cones = [CO.PosSemidef{T, T}(3, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -433,12 +433,12 @@ function semidefinite2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin c = T[0, -1, 0] A = T[1 0 1] b = T[0] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) + G = Diagonal(-one(T) * I, 3) + h = zeros(T, 3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.PosSemidef(3, is_dual)] + cones = [CO.PosSemidef{T, T}(3, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -449,15 +449,15 @@ end function semidefinite3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[1, 0, 1, 0, 0, 1] - A = T[1 2 3 4 5 6; 1 1 1 1 1 1] - b = T[10, 3] + c = [1, 0, 1, 0, 0, 1] + A = [1 2 3 4 5 6; 1 1 1 1 1 1] + b = [10, 3] G = SparseMatrixCSC(-1.0I, 6, 6) h = zeros(6) cone_idxs = [1:6] for is_dual in (true, false) - cones = [CO.PosSemidef(6, is_dual)] + cones = [CO.PosSemidef{T, T}(6, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -471,10 +471,10 @@ function semidefinitecomplex1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T c = T[1, 0, 0, 1] A = T[0 0 1 0] b = T[1] - G = Diagonal([-1, -sqrt(2), -sqrt(2), -1]) - h = zeros(4) + G = diagm(T[-1, -sqrt(2), -sqrt(2), -1]) + h = zeros(T, 4) cone_idxs = [1:4] - cones = [CO.PosSemidef{ComplexF64}(4)] + cones = [CO.PosSemidef{T, Complex{T}}(4)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -487,9 +487,9 @@ function hypoperlog1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linea c = T[1, 1, 1] A = T[0 1 0; 1 0 0] b = T[2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) - cones = [CO.HypoPerLog()] + G = Matrix{T}(-I, 3, 3) + h = zeros(T, 3) + cones = [CO.HypoPerLog{T}()] cone_idxs = [1:3] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -503,12 +503,12 @@ end function hypoperlog2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[-1, 0, 0] - A = T[0 1 0] - b = T[0] - G = SparseMatrixCSC(-1.0I, 3, 3) + c = [-1, 0, 0] + A = [0 1 0] + b = [0] + G = Diagonal(-I, 3) h = zeros(3) - cones = [CO.HypoPerLog()] + cones = [CO.HypoPerLog{T}()] cone_idxs = [1:3] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -518,12 +518,12 @@ end function hypoperlog3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[1, 1, 1] - A = zeros(T, 0, 3) - b = zeros(T, 0) + c = [1, 1, 1] + A = zeros(0, 3) + b = zeros(0) G = sparse([1, 2, 3, 4], [1, 2, 3, 1], -ones(4)) h = zeros(4) - cones = [CO.HypoPerLog(), CO.Nonnegative{T}(1)] + cones = [CO.HypoPerLog{T}(), CO.Nonnegative{T}(1)] cone_idxs = [1:3, 4:4] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -540,7 +540,7 @@ function hypoperlog4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linea b = T[1, -1] G = SparseMatrixCSC(-1.0I, 3, 3) h = zeros(3) - cones = [CO.HypoPerLog(true)] + cones = [CO.HypoPerLog{T}(true)] cone_idxs = [1:3] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -554,9 +554,9 @@ function epiperpower1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line c = T[1, 0, -1, 0, 0, -1] A = T[1 1 0 1/2 0 0; 0 0 0 0 1 0] b = T[2, 1] - G = SparseMatrixCSC(-1.0I, 6, 6) - h = zeros(6) - cones = [CO.EpiPerPower(5.0, false), CO.EpiPerPower(2.5, false)] + G = Matrix{T}(-I, 6, 6) + h = zeros(T, 6) + cones = [CO.EpiPerPower{T}(5.0, false), CO.EpiPerPower{T}(2.5, false)] cone_idxs = [1:3, 4:6] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -567,15 +567,15 @@ end function epiperpower2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[0, 0, -1] - A = T[1 0 0; 0 1 0] - b = T[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) + c = [0, 0, -1] + A = [1 0 0; 0 1 0] + b = [1/2, 1] + G = Diagonal(-I, 3) h = zeros(3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.EpiPerPower(2.0, is_dual)] + cones = [CO.EpiPerPower{T}(2.0, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -589,12 +589,12 @@ function epiperpower3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line c = T[0, 0, 1] A = T[1 0 0; 0 1 0] b = T[0, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) - h = zeros(3) + G = SparseMatrixCSC(-one(T) * I, 3, 3) + h = zeros(T, 3) cone_idxs = [1:3] for is_dual in (true, false), alpha in [1.5; 2.0] # TODO objective gap is large when alpha is different e.g. 2.5, investigate why - cones = [CO.EpiPerPower(alpha, is_dual)] + cones = [CO.EpiPerPower{T}(alpha, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose, atol=1e-3, rtol=1e-3) @test r.status == :Optimal @@ -608,9 +608,9 @@ function hypogeomean1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line c = T[1, 0, 0, -1, -1, 0] A = T[1 1 1/2 0 0 0; 0 0 0 0 0 1] b = T[2, 1] - G = SparseMatrixCSC(-1.0I, 6, 6)[[4, 1, 2, 5, 3, 6], :] - h = zeros(6) - cones = [CO.HypoGeomean([0.2, 0.8], false), CO.HypoGeomean([0.4, 0.6], false)] + G = Matrix{T}(-1.0I, 6, 6)[[4, 1, 2, 5, 3, 6], :] + h = zeros(T, 6) + cones = [CO.HypoGeomean{T}([0.2, 0.8], false), CO.HypoGeomean{T}([0.4, 0.6], false)] cone_idxs = [1:3, 4:6] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -621,15 +621,15 @@ end function hypogeomean2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} tol = max(1e-5, sqrt(sqrt(eps(T)))) - c = T[-1, 0, 0] - A = T[0 0 1; 0 1 0] - b = T[1/2, 1] - G = SparseMatrixCSC(-1.0I, 3, 3) + c = [-1, 0, 0] + A = [0 0 1; 0 1 0] + b = [1/2, 1] + G = Matrix(-I, 3, 3) h = zeros(3) cone_idxs = [1:3] for is_dual in (true, false) - cones = [CO.HypoGeomean([0.5, 0.5], is_dual)] + cones = [CO.HypoGeomean{T}([0.5, 0.5], is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -649,7 +649,7 @@ function hypogeomean3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line for is_dual in (true, false) b = is_dual ? [-1.0] : [1.0] - cones = [CO.HypoGeomean(fill(inv(l), l), is_dual)] + cones = [CO.HypoGeomean{T}(fill(inv(l), l), is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -664,12 +664,12 @@ function hypogeomean4(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, line c = ones(l) A = zeros(0, l) b = zeros(0) - G = T[zeros(1, l); Matrix(-1.0I, l, l)] + G = [zeros(1, l); Matrix(-1.0I, l, l)] h = zeros(l + 1) cone_idxs = [1:(l + 1)] for is_dual in (true, false) - cones = [CO.HypoGeomean(fill(inv(l), l), is_dual)] + cones = [CO.HypoGeomean{T}(fill(inv(l), l), is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @@ -684,14 +684,14 @@ function epinormspectral1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, (Xn, Xm) = (3, 4) Xnm = Xn * Xm c = vcat(1.0, zeros(Xnm)) - A = [spzeros(Xnm, 1) sparse(1.0I, Xnm, Xnm)] + A = [zeros(Xnm, 1) Matrix(1.0I, Xnm, Xnm)] b = rand(Xnm) - G = sparse(-1.0I, Xnm + 1, Xnm + 1) + G = Matrix(-1.0I, Xnm + 1, Xnm + 1) h = vcat(0.0, rand(Xnm)) cone_idxs = [1:(Xnm + 1)] for is_dual in (true, false) - cones = [CO.EpiNormSpectral(Xn, Xm, is_dual)] + cones = [CO.EpiNormSpectral{T}(Xn, Xm, is_dual)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal diff --git a/test/runtests.jl b/test/runtests.jl index 8fe0d0358..c70847bf8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,7 +66,7 @@ include(joinpath(@__DIR__, "native.jl")) # end @info("starting native interface tests") -verbose = false +verbose = true real_types = [ Float64, Float32, @@ -104,9 +104,9 @@ testfuns_nonsingular = [ # epinorminf6, # epinormeucl1, # epinormeucl2, - epipersquare1, - epipersquare2, - epipersquare3, + # epipersquare1, + # epipersquare2, + # epipersquare3, # semidefinite1, # semidefinite2, # semidefinite3, @@ -122,7 +122,7 @@ testfuns_nonsingular = [ # hypogeomean2, # hypogeomean3, # hypogeomean4, - # epinormspectral1, + epinormspectral1, # hypoperlogdet1, # hypoperlogdet2, # hypoperlogdet3, From b52204179be81a537cca9208e01b54f862471f14 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 16:41:12 -0400 Subject: [PATCH 06/20] all native tests functioning --- src/Cones/Cones.jl | 4 +-- src/Cones/epipersumexp.jl | 30 +++++++++--------- src/Cones/hypoperlogdet.jl | 63 +++++++++++++++++++------------------- test/native.jl | 48 ++++++++++++++--------------- test/runtests.jl | 6 ++-- 5 files changed, 75 insertions(+), 76 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index abf1153da..bfd43a60d 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -25,9 +25,9 @@ include("orthant.jl") # include("hypoperlog.jl") # include("epiperpower.jl") # include("hypogeomean.jl") -include("epinormspectral.jl") -# include("epipersumexp.jl") +# include("epinormspectral.jl") # include("hypoperlogdet.jl") +include("epipersumexp.jl") # include("wsospolyinterp.jl") # include("wsospolyinterpmat.jl") # include("wsospolyinterpsoc.jl") diff --git a/src/Cones/epipersumexp.jl b/src/Cones/epipersumexp.jl index 81b616270..d1c94ff56 100644 --- a/src/Cones/epipersumexp.jl +++ b/src/Cones/epipersumexp.jl @@ -16,35 +16,35 @@ mutable struct EpiPerSumExp{T <: HypReal} <: Cone{T} use_dual::Bool dim::Int - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F barfun::Function diffres - function EpiPerSumExp(dim::Int, is_dual::Bool) - cone = new() + function EpiPerSumExp{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim return cone end end -EpiPerSumExp(dim::Int) = EpiPerSumExp(dim, false) +EpiPerSumExp{T}(dim::Int) where {T <: HypReal} = EpiPerSumExp{T}(dim, false) -function setup_data(cone::EpiPerSumExp) +function setup_data(cone::EpiPerSumExp{T}) where {T <: HypReal} dim = cone.dim - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) - cone.H2 = similar(cone.H) + cone.g = zeros(T, dim) + cone.H = zeros(T, dim, dim) + cone.H2 = copy(cone.H) function barfun(point) u = point[1] v = point[2] w = view(point, 3:dim) # return -log(u - v*sum(wi -> exp(wi/v), w)) - log(u) - log(v) - return -log(log(u) - log(v) - log(sum(wi -> exp(wi / v), w))) - log(u) - 2.0 * log(v) # TODO use the numerically safer way to evaluate LSE function + return -log(log(u) - log(v) - log(sum(wi -> exp(wi / v), w))) - log(u) - 2 * log(v) # TODO use the numerically safer way to evaluate LSE function end cone.barfun = barfun cone.diffres = DiffResults.HessianResult(cone.g) @@ -53,13 +53,13 @@ end get_nu(cone::EpiPerSumExp) = 3 # TODO does this increase with dim > 3? -set_initial_point(arr::AbstractVector{Float64}, cone::EpiPerSumExp) = (@. arr = -log(cone.dim - 2); arr[1] = 2.0; arr[2] = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::EpiPerSumExp{T}) where {T <: HypReal} = (@. arr = -log(T(cone.dim - 2)); arr[1] = T(2); arr[2] = one(T); arr) -function check_in_cone(cone::EpiPerSumExp) +function check_in_cone(cone::EpiPerSumExp{T}) where {T <: HypReal} u = cone.point[1] v = cone.point[2] w = view(cone.point, 3:cone.dim) - if u <= 0.0 || v <= 0.0 || u <= v * sum(wi -> exp(wi / v), w) # TODO use the numerically safer way to evaluate LSE function + if u <= zero(T) || v <= zero(T) || u <= v * sum(wi -> exp(wi / v), w) # TODO use the numerically safer way to evaluate LSE function return false end diff --git a/src/Cones/hypoperlogdet.jl b/src/Cones/hypoperlogdet.jl index b66070d48..b69e85af1 100644 --- a/src/Cones/hypoperlogdet.jl +++ b/src/Cones/hypoperlogdet.jl @@ -14,15 +14,15 @@ mutable struct HypoPerLogdet{T <: HypReal} <: Cone{T} dim::Int side::Int - point::AbstractVector{Float64} - mat::Matrix{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} + point::AbstractVector{T} + mat::Matrix{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} F - function HypoPerLogdet(dim::Int, is_dual::Bool) - cone = new() + function HypoPerLogdet{T}(dim::Int, is_dual::Bool) where {T <: HypReal} + cone = new{T}() cone.use_dual = is_dual cone.dim = dim cone.side = round(Int, sqrt(0.25 + 2 * (dim - 2)) - 0.5) @@ -30,60 +30,61 @@ mutable struct HypoPerLogdet{T <: HypReal} <: Cone{T} end end -HypoPerLogdet(dim::Int) = HypoPerLogdet(dim, false) +HypoPerLogdet{T}(dim::Int) where {T <: HypReal} = HypoPerLogdet{T}(dim, false) -function setup_data(cone::HypoPerLogdet) +function setup_data(cone::HypoPerLogdet{T}) where {T <: HypReal} dim = cone.dim side = round(Int, sqrt(0.25 + 2 * (dim - 2)) - 0.5) side = cone.side - cone.mat = Matrix{Float64}(undef, side, side) - cone.g = Vector{Float64}(undef, dim) - cone.H = Matrix{Float64}(undef, dim, dim) + cone.mat = Matrix{T}(undef, side, side) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) cone.H2 = similar(cone.H) return end get_nu(cone::HypoPerLogdet) = cone.side + 2 -function set_initial_point(arr::AbstractVector{Float64}, cone::HypoPerLogdet) - arr[1] = -1.0 - arr[2] = 1.0 - smat_to_svec!(view(arr, 3:cone.dim), Matrix(1.0I, cone.side, cone.side)) +function set_initial_point(arr::AbstractVector{T}, cone::HypoPerLogdet{T}) where {T <: HypReal} + arr[1] = -one(T) + arr[2] = one(T) + smat_to_svec!(view(arr, 3:cone.dim), Matrix(one(T) * I, cone.side, cone.side)) # TODO remove allocs return arr end -function check_in_cone(cone::HypoPerLogdet) +# TODO remove allocs +function check_in_cone(cone::HypoPerLogdet{T}) where {T <: HypReal} u = cone.point[1] v = cone.point[2] - if v <= 0.0 + if v <= zero(T) return false end W = cone.mat svec_to_smat!(W, view(cone.point, 3:cone.dim)) - F = cholesky(Symmetric(W), Val(true), check = false) + F = cholesky(Symmetric(W), Val(true), check = false) # TODO doesn't work for generic reals if !isposdef(F) || u >= v * (logdet(F) - cone.side * log(v)) return false end L = logdet(W / v) z = v * L - u - Wi = inv(W) + Wi = Symmetric(inv(W)) n = cone.side dim = cone.dim vzi = v / z - cone.g[1] = 1 / z - cone.g[2] = (n - L) / z - 1 / v - gwmat = -Wi * (1 + vzi) + cone.g[1] = inv(z) + cone.g[2] = (T(n) - L) / z - inv(v) + gwmat = -Wi * (one(T) + vzi) smat_to_svec!(view(cone.g, 3:dim), gwmat) - cone.H[1, 1] = 1 / z / z - cone.H[1, 2] = (n - L) / z / z + cone.H[1, 1] = inv(z) / z + cone.H[1, 2] = (T(n) - L) / z / z Huwmat = -vzi * Wi / z smat_to_svec!(view(cone.H, 1, 3:dim), Huwmat) - cone.H[2, 2] = (-n + L)^2 / z / z + n / (v * z) + 1 / v / v - Hvwmat = ((-n + L) * vzi - 1) * Wi / z + cone.H[2, 2] = abs2(T(-n) + L) / z / z + T(n) / (v * z) + inv(v) / v + Hvwmat = ((T(-n) + L) * vzi - one(T)) * Wi / z smat_to_svec!(view(cone.H, 2, 3:dim), Hvwmat) k = 3 @@ -91,11 +92,11 @@ function check_in_cone(cone::HypoPerLogdet) k2 = 3 for i2 in 1:n, j2 in 1:i2 if (i == j) && (i2 == j2) - cone.H[k2, k] = abs2(Wi[i2, i]) * (vzi + 1) + Wi[i, i] * Wi[i2, i2] * vzi^2 + cone.H[k2, k] = abs2(Wi[i2, i]) * (vzi + one(T)) + Wi[i, i] * Wi[i2, i2] * abs2(vzi) elseif (i != j) && (i2 != j2) - cone.H[k2, k] = (Wi[i2, i] * Wi[j, j2] + Wi[j2, i] * Wi[j, i2]) * (vzi + 1) + 2 * Wi[i, j] * Wi[i2, j2] * vzi^2 + cone.H[k2, k] = (Wi[i2, i] * Wi[j, j2] + Wi[j2, i] * Wi[j, i2]) * (vzi + one(T)) + 2 * Wi[i, j] * Wi[i2, j2] * abs2(vzi) else - cone.H[k2, k] = rt2 * (Wi[i2, i] * Wi[j, j2] * (vzi + 1) + Wi[i, j] * Wi[i2, j2] * vzi^2) + cone.H[k2, k] = rt2 * (Wi[i2, i] * Wi[j, j2] * (vzi + one(T)) + Wi[i, j] * Wi[i2, j2] * abs2(vzi)) end if k2 == k break @@ -105,7 +106,5 @@ function check_in_cone(cone::HypoPerLogdet) k += 1 end - # @assert isapprox(Symmetric(cone.H, :U) * cone.point, -cone.g, atol = 1e-6, rtol = 1e-6) - return factorize_hess(cone) end diff --git a/test/native.jl b/test/native.jl index 3f186bd74..9498f510c 100644 --- a/test/native.jl +++ b/test/native.jl @@ -713,20 +713,20 @@ function hypoperlogdet1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li c = T[-1, 0] A = T[0 1] b = T[1] - G = SparseMatrixCSC(-1.0I, dim, 2) - mat_half = randn(side, side) + G = Matrix{T}(-I, dim, 2) + mat_half = rand(T, side, side) mat = mat_half * mat_half' - h = zeros(dim) + h = zeros(T, dim) CO.smat_to_svec!(view(h, 3:dim), mat) - cones = [CO.HypoPerLogdet(dim)] + cones = [CO.HypoPerLogdet{T}(dim)] cone_idxs = [1:dim] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @test r.x[1] ≈ -r.primal_obj atol=tol rtol=tol @test r.x[2] ≈ 1 atol=tol rtol=tol - @test r.s[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.s[3:end]) / r.s[2]) ≈ r.s[1] atol=tol rtol=tol - @test r.z[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.z[3:end]) / r.z[1]) + side) ≈ r.z[2] atol=tol rtol=tol + @test r.s[2] * logdet(CO.svec_to_smat!(zeros(T, side, side), r.s[3:end]) / r.s[2]) ≈ r.s[1] atol=tol rtol=tol + @test r.z[1] * (logdet(CO.svec_to_smat!(zeros(T, side, side), -r.z[3:end]) / r.z[1]) + T(side)) ≈ r.z[2] atol=tol rtol=tol end function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} @@ -737,20 +737,20 @@ function hypoperlogdet2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li c = T[0, 1] A = T[1 0] b = T[-1] - G = SparseMatrixCSC(-1.0I, dim, 2) - mat_half = rand(side, side) + G = Matrix{T}(-I, dim, 2) + mat_half = rand(T, side, side) mat = mat_half * mat_half' - h = zeros(dim) + h = zeros(T, dim) CO.smat_to_svec!(view(h, 3:dim), mat) - cones = [CO.HypoPerLogdet(dim, true)] + cones = [CO.HypoPerLogdet{T}(dim, true)] cone_idxs = [1:dim] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @test r.status == :Optimal @test r.x[2] ≈ r.primal_obj atol=tol rtol=tol @test r.x[1] ≈ -1 atol=tol rtol=tol - @test r.s[1] * (logdet(CO.svec_to_smat!(zeros(side, side), -r.s[3:end]) / r.s[1]) + side) ≈ r.s[2] atol=tol rtol=tol - @test r.z[2] * logdet(CO.svec_to_smat!(zeros(side, side), r.z[3:end]) / r.z[2]) ≈ r.z[1] atol=tol rtol=tol + @test r.s[1] * (logdet(CO.svec_to_smat!(zeros(T, side, side), -r.s[3:end]) / r.s[1]) + T(side)) ≈ r.s[2] atol=tol rtol=tol + @test r.z[2] * logdet(CO.svec_to_smat!(zeros(T, side, side), r.z[3:end]) / r.z[2]) ≈ r.z[1] atol=tol rtol=tol end function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, linear_model::Type{<:MO.LinearModel{T}}, verbose::Bool) where {T <: HypReal} @@ -758,15 +758,15 @@ function hypoperlogdet3(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, li Random.seed!(1) side = 3 dim = 2 + div(side * (side + 1), 2) - c = T[-1, 0] - A = T[0 1] - b = T[0] + c = [-1, 0] + A = [0 1] + b = [0] G = SparseMatrixCSC(-1.0I, dim, 2) mat_half = rand(side, side) mat = mat_half * mat_half' h = zeros(dim) CO.smat_to_svec!(view(h, 3:dim), mat) - cones = [CO.HypoPerLogdet(dim)] + cones = [CO.HypoPerLogdet{T}(dim)] cone_idxs = [1:dim] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -779,11 +779,11 @@ function epipersumexp1(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) - A = T[1 zeros(1, l)] - b = T[1] - G = T[-1 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] + A = [1 zeros(1, l)] + b = [1] + G = [-1 zeros(1, l); zeros(1, l + 1); zeros(l, 1) sparse(-1.0I, l, l)] h = zeros(l + 2) - cones = [CO.EpiPerSumExp(l + 2)] + cones = [CO.EpiPerSumExp{T}(l + 2)] cone_idxs = [1:(l + 2)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) @@ -797,11 +797,11 @@ function epipersumexp2(system_solver::Type{<:SO.CombinedHSDSystemSolver{T}}, lin tol = max(1e-5, sqrt(sqrt(eps(T)))) l = 5 c = vcat(0.0, -ones(l)) - A = T[1 zeros(1, l)] - b = T[1] - G = T[-1.0 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] + A = [1 zeros(1, l)] + b = [1] + G = [-1.0 spzeros(1, l); spzeros(1, l + 1); spzeros(l, 1) sparse(-1.0I, l, l)] h = zeros(l + 2); h[2] = 1.0 - cones = [CO.EpiPerSumExp(l + 2)] + cones = [CO.EpiPerSumExp{T}(l + 2)] cone_idxs = [1:(l + 2)] r = solve_and_check(c, A, b, G, h, cones, cone_idxs, linear_model, system_solver, verbose) diff --git a/test/runtests.jl b/test/runtests.jl index c70847bf8..c9fa77fe7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -122,12 +122,12 @@ testfuns_nonsingular = [ # hypogeomean2, # hypogeomean3, # hypogeomean4, - epinormspectral1, + # epinormspectral1, # hypoperlogdet1, # hypoperlogdet2, # hypoperlogdet3, - # epipersumexp1, - # epipersumexp2, + epipersumexp1, + epipersumexp2, ] @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types # if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel From bbc1368d1a9d5f4c29f5b67ce4dba8a881bade4b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 17:44:00 -0400 Subject: [PATCH 07/20] all linear system solvers functioning --- src/Cones/Cones.jl | 18 ++-- src/Solvers/Solvers.jl | 8 +- .../combined_step/naive.jl | 2 +- .../combined_step/naiveelim.jl | 44 +++++----- .../combined_step/qrchol.jl | 50 +++++------ .../combined_step/symindef.jl | 47 +++++----- test/runtests.jl | 88 +++++++++---------- 7 files changed, 127 insertions(+), 130 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index bfd43a60d..1fdf1417a 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -18,15 +18,15 @@ const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} abstract type Cone{T <: HypReal} end include("orthant.jl") -# include("epinorminf.jl") -# include("epinormeucl.jl") -# include("epipersquare.jl") -# include("semidefinite.jl") -# include("hypoperlog.jl") -# include("epiperpower.jl") -# include("hypogeomean.jl") -# include("epinormspectral.jl") -# include("hypoperlogdet.jl") +include("epinorminf.jl") +include("epinormeucl.jl") +include("epipersquare.jl") +include("semidefinite.jl") +include("hypoperlog.jl") +include("epiperpower.jl") +include("hypogeomean.jl") +include("epinormspectral.jl") +include("hypoperlogdet.jl") include("epipersumexp.jl") # include("wsospolyinterp.jl") # include("wsospolyinterpmat.jl") diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index 79c5dee7e..6d58fab28 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -24,10 +24,10 @@ abstract type CombinedHSDSystemSolver{T <: HypReal} end include("homogeneous_self_dual/solver.jl") include("homogeneous_self_dual/combined_step/stepper.jl") include("homogeneous_self_dual/combined_step/naive.jl") -# include("homogeneous_self_dual/combined_step/naiveelim.jl") -# include("homogeneous_self_dual/combined_step/symindef.jl") -# include("homogeneous_self_dual/combined_step/qrchol.jl") -# # include("homogeneous_self_dual/combined_step/cholchol.jl") +include("homogeneous_self_dual/combined_step/naiveelim.jl") +include("homogeneous_self_dual/combined_step/symindef.jl") +include("homogeneous_self_dual/combined_step/qrchol.jl") +# include("homogeneous_self_dual/combined_step/cholchol.jl") # TODO sequential quadratic algorithm for linear, quadratic, and smooth convex models diff --git a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl index b28c9b295..1a35a8f9e 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/naive.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/naive.jl @@ -76,7 +76,7 @@ mutable struct NaiveCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSo end end -function get_combined_directions(solver::HSDSolver{T}, system_solver::NaiveCombinedHSDSystemSolver{T}) where T +function get_combined_directions(solver::HSDSolver{T}, system_solver::NaiveCombinedHSDSystemSolver{T}) where {T <: HypReal} model = solver.model cones = model.cones lhs = system_solver.lhs diff --git a/src/Solvers/homogeneous_self_dual/combined_step/naiveelim.jl b/src/Solvers/homogeneous_self_dual/combined_step/naiveelim.jl index 824403eea..bdf4bfaac 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/naiveelim.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/naiveelim.jl @@ -34,12 +34,12 @@ kap + mu/(taubar^2)*tau = taurhs --> kap = taurhs - mu/(taubar^2)*tau TODO reduce allocations =# -mutable struct NaiveElimCombinedHSDSystemSolver <: CombinedHSDSystemSolver +mutable struct NaiveElimCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSolver{T} use_sparse::Bool lhs_copy lhs - rhs::Matrix{Float64} + rhs::Matrix{T} x1 x2 @@ -49,31 +49,31 @@ mutable struct NaiveElimCombinedHSDSystemSolver <: CombinedHSDSystemSolver z2 z1_k z2_k - s1::Vector{Float64} - s2::Vector{Float64} + s1::Vector{T} + s2::Vector{T} s1_k s2_k - function NaiveElimCombinedHSDSystemSolver(model::Models.LinearModel; use_sparse::Bool = false) + function NaiveElimCombinedHSDSystemSolver{T}(model::Models.LinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} (n, p, q) = (model.n, model.p, model.q) npq1 = n + p + q + 1 - system_solver = new() + system_solver = new{T}() system_solver.use_sparse = use_sparse if use_sparse - system_solver.lhs_copy = Float64[ - spzeros(n,n) model.A' model.G' model.c; - -model.A spzeros(p,p) spzeros(p,q) model.b; - -model.G spzeros(q,p) sparse(1.0I,q,q) model.h; - -model.c' -model.b' -model.h' 1.0; + system_solver.lhs_copy = T[ + spzeros(T,n,n) model.A' model.G' model.c; + -model.A spzeros(T,p,p) spzeros(T,p,q) model.b; + -model.G spzeros(T,q,p) sparse(one(T)*I,q,q) model.h; + -model.c' -model.b' -model.h' one(T); ] @assert issparse(system_solver.lhs_copy) else - system_solver.lhs_copy = Float64[ - zeros(n,n) model.A' model.G' model.c; - -model.A zeros(p,p) zeros(p,q) model.b; - -model.G zeros(q,p) Matrix(1.0I,q,q) model.h; - -model.c' -model.b' -model.h' 1.0; + system_solver.lhs_copy = T[ + zeros(T,n,n) model.A' model.G' model.c; + -model.A zeros(T,p,p) zeros(T,p,q) model.b; + -model.G zeros(T,q,p) Matrix(one(T)*I,q,q) model.h; + -model.c' -model.b' -model.h' one(T); ] end @@ -85,7 +85,7 @@ mutable struct NaiveElimCombinedHSDSystemSolver <: CombinedHSDSystemSolver # end # system_solver.lhs_H_k = [view_k(k) for k in eachindex(model.cones)] - rhs = Matrix{Float64}(undef, npq1, 2) + rhs = Matrix{T}(undef, npq1, 2) system_solver.rhs = rhs rows = 1:n system_solver.x1 = view(rhs, rows, 1) @@ -108,7 +108,7 @@ mutable struct NaiveElimCombinedHSDSystemSolver <: CombinedHSDSystemSolver end end -function get_combined_directions(solver::HSDSolver, system_solver::NaiveElimCombinedHSDSystemSolver) +function get_combined_directions(solver::HSDSolver{T}, system_solver::NaiveElimCombinedHSDSystemSolver{T}) where {T <: HypReal} model = solver.model (n, p, q) = (model.n, model.p, model.q) cones = model.cones @@ -132,11 +132,11 @@ function get_combined_directions(solver::HSDSolver, system_solver::NaiveElimComb # update rhs matrix x1 .= solver.x_residual - x2 .= 0.0 + x2 .= zero(T) y1 .= solver.y_residual - y2 .= 0.0 + y2 .= zero(T) z1 .= solver.z_residual - z2 .= 0.0 + z2 .= zero(T) for k in eachindex(cones) duals_k = solver.point.dual_views[k] g = Cones.grad(cones[k]) @@ -144,7 +144,7 @@ function get_combined_directions(solver::HSDSolver, system_solver::NaiveElimComb @. s2_k[k] = -duals_k - mu * g end tau_rhs = [-kap, -kap + mu / tau] - kap_rhs = [kap + solver.primal_obj_t - solver.dual_obj_t, 0.0] + kap_rhs = [kap + solver.primal_obj_t - solver.dual_obj_t, zero(T)] # update lhs matrix copyto!(lhs, system_solver.lhs_copy) diff --git a/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl b/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl index fb6ab20b0..e558654a5 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl @@ -4,12 +4,12 @@ Copyright 2018, Chris Coey and contributors QR + Cholesky linear system solver =# -mutable struct QRCholCombinedHSDSystemSolver <: CombinedHSDSystemSolver +mutable struct QRCholCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSolver{T} use_sparse::Bool - xi::Matrix{Float64} - yi::Matrix{Float64} - zi::Matrix{Float64} + xi::Matrix{T} + yi::Matrix{T} + zi::Matrix{T} x1 y1 @@ -54,14 +54,14 @@ mutable struct QRCholCombinedHSDSystemSolver <: CombinedHSDSystemSolver HGxi_k Gxi_k - function QRCholCombinedHSDSystemSolver(model::Models.PreprocessedLinearModel; use_sparse::Bool = false) + function QRCholCombinedHSDSystemSolver{T}(model::Models.PreprocessedLinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} (n, p, q) = (model.n, model.p, model.q) - system_solver = new() + system_solver = new{T}() system_solver.use_sparse = use_sparse - xi = Matrix{Float64}(undef, n, 3) - yi = Matrix{Float64}(undef, p, 3) - zi = Matrix{Float64}(undef, q, 3) + xi = Matrix{T}(undef, n, 3) + yi = Matrix{T}(undef, p, 3) + zi = Matrix{T}(undef, q, 3) system_solver.xi = xi system_solver.yi = yi system_solver.zi = zi @@ -90,22 +90,22 @@ mutable struct QRCholCombinedHSDSystemSolver <: CombinedHSDSystemSolver system_solver.z3_temp_k = [view(zi_temp, idxs, 3) for idxs in model.cone_idxs] nmp = n - p - system_solver.bxGHbz = Matrix{Float64}(undef, n, 3) + system_solver.bxGHbz = Matrix{T}(undef, n, 3) system_solver.Q1x = similar(system_solver.bxGHbz) - system_solver.GQ1x = Matrix{Float64}(undef, q, 3) + system_solver.GQ1x = Matrix{T}(undef, q, 3) system_solver.HGQ1x = similar(system_solver.GQ1x) - system_solver.GHGQ1x = Matrix{Float64}(undef, n, 3) - system_solver.Q2div = Matrix{Float64}(undef, nmp, 3) + system_solver.GHGQ1x = Matrix{T}(undef, n, 3) + system_solver.Q2div = Matrix{T}(undef, nmp, 3) system_solver.GQ2 = model.G * model.Ap_Q2 if use_sparse - if system_solver.GQ2 isa Matrix{Float64} + if system_solver.GQ2 isa Matrix{T} error("to use sparse factorization for direction finding, cannot use dense A or G matrices") end - system_solver.HGQ2 = spzeros(Float64, q, nmp) - system_solver.Q2GHGQ2 = spzeros(Float64, nmp, nmp) + system_solver.HGQ2 = spzeros(T, q, nmp) + system_solver.Q2GHGQ2 = spzeros(T, nmp, nmp) else - system_solver.HGQ2 = Matrix{Float64}(undef, q, nmp) - system_solver.Q2GHGQ2 = Matrix{Float64}(undef, nmp, nmp) + system_solver.HGQ2 = Matrix{T}(undef, q, nmp) + system_solver.Q2GHGQ2 = Matrix{T}(undef, nmp, nmp) end system_solver.Q2x = similar(system_solver.Q1x) system_solver.Gxi = similar(system_solver.GQ1x) @@ -123,7 +123,7 @@ mutable struct QRCholCombinedHSDSystemSolver <: CombinedHSDSystemSolver end end -function get_combined_directions(solver::HSDSolver, system_solver::QRCholCombinedHSDSystemSolver) +function get_combined_directions(solver::HSDSolver{T}, system_solver::QRCholCombinedHSDSystemSolver{T}) where {T <: HypReal} model = solver.model cones = model.cones cone_idxs = model.cone_idxs @@ -177,10 +177,10 @@ function get_combined_directions(solver::HSDSolver, system_solver::QRCholCombine @. x1 = -model.c @. x2 = solver.x_residual - @. x3 = 0.0 + @. x3 = zero(T) @. y1 = model.b @. y2 = -solver.y_residual - @. y3 = 0.0 + @. y3 = zero(T) @. z1_temp = model.h @. z2_temp = -solver.z_residual @@ -241,18 +241,18 @@ function get_combined_directions(solver::HSDSolver, system_solver::QRCholCombine if !issuccess(F) println("sparse linear system matrix factorization failed") mul!(Q2GHGQ2, GQ2', HGQ2) - F = ldlt(Symmetric(Q2GHGQ2), shift = 1e-4, check = false) + F = ldlt(Symmetric(Q2GHGQ2), shift = T(1e-4), check = false) if !issuccess(F) error("could not fix failure of positive definiteness (mu is $mu); terminating") end end Q2div .= F \ Q2div # TODO eliminate allocs (see https://github.com/JuliaLang/julia/issues/30084) else - F = cholesky!(Symmetric(Q2GHGQ2), Val(true), check = false) # TODO prealloc cholesky auxiliary vectors using posvx + F = cholesky!(Symmetric(Q2GHGQ2), Val(true), check = false) # TODO prealloc cholesky auxiliary vectors using posvx; not implemented for generic reals if !isposdef(F) println("dense linear system matrix factorization failed") mul!(Q2GHGQ2, GQ2', HGQ2) - Q2GHGQ2 += 1e-4I + Q2GHGQ2 += T(1e-4) * I F = bunchkaufman!(Symmetric(Q2GHGQ2), true, check = false) # TODO prealloc with old sysvx code if !issuccess(F) error("could not fix failure of positive definiteness (mu is $mu); terminating") @@ -294,7 +294,7 @@ function get_combined_directions(solver::HSDSolver, system_solver::QRCholCombine (tau_pred, kap_pred) = lift!(x2, y2, z2, z2_temp, solver.kap + solver.primal_obj_t - solver.dual_obj_t, -solver.kap) @. z2_temp -= solver.z_residual - (tau_corr, kap_corr) = lift!(x3, y3, z3, z3_temp, 0.0, -solver.kap + mu / solver.tau) + (tau_corr, kap_corr) = lift!(x3, y3, z3, z3_temp, zero(T), -solver.kap + mu / solver.tau) return (x2, x3, y2, y3, z2, z3, z2_temp, z3_temp, tau_pred, tau_corr, kap_pred, kap_corr) end diff --git a/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl b/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl index b59c0cb0d..69bc54a6d 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl @@ -17,12 +17,12 @@ symmetrize the LHS matrix by multiplying some equations by -1 and by premultiply TODO reduce allocations =# -mutable struct SymIndefCombinedHSDSystemSolver <: CombinedHSDSystemSolver +mutable struct SymIndefCombinedHSDSystemSolver{T <: HypReal} <: CombinedHSDSystemSolver{T} use_sparse::Bool lhs_copy lhs - rhs::Matrix{Float64} + rhs::Matrix{T} x1 x2 @@ -36,33 +36,33 @@ mutable struct SymIndefCombinedHSDSystemSolver <: CombinedHSDSystemSolver z1_k z2_k z3_k - s1::Vector{Float64} - s2::Vector{Float64} - s3::Vector{Float64} + s1::Vector{T} + s2::Vector{T} + s3::Vector{T} s1_k s2_k s3_k - function SymIndefCombinedHSDSystemSolver(model::Models.LinearModel; use_sparse::Bool = false) + function SymIndefCombinedHSDSystemSolver{T}(model::Models.LinearModel{T}; use_sparse::Bool = false) where {T <: HypReal} (n, p, q) = (model.n, model.p, model.q) npq = n + p + q - system_solver = new() + system_solver = new{T}() system_solver.use_sparse = use_sparse # x y z # lower symmetric if use_sparse - system_solver.lhs_copy = Float64[ - spzeros(n,n) spzeros(n,p) spzeros(n,q); - model.A spzeros(p,p) spzeros(p,q); - model.G spzeros(q,p) sparse(-1.0I,q,q); + system_solver.lhs_copy = T[ + spzeros(T,n,n) spzeros(T,n,p) spzeros(T,n,q); + model.A spzeros(T,p,p) spzeros(T,p,q); + model.G spzeros(T,q,p) sparse(-one(T)*I,q,q); ] @assert issparse(system_solver.lhs_copy) else - system_solver.lhs_copy = Float64[ - zeros(n,n) zeros(n,p) zeros(n,q); - model.A zeros(p,p) zeros(p,q); - model.G zeros(q,p) Matrix(-1.0I,q,q); + system_solver.lhs_copy = T[ + zeros(T,n,n) zeros(T,n,p) zeros(T,n,q); + model.A zeros(T,p,p) zeros(T,p,q); + model.G zeros(T,q,p) Matrix(-one(T)I,q,q); ] end @@ -74,7 +74,7 @@ mutable struct SymIndefCombinedHSDSystemSolver <: CombinedHSDSystemSolver # end # system_solver.lhs_H_k = [view_k(k) for k in eachindex(model.cones)] - rhs = Matrix{Float64}(undef, npq, 3) + rhs = Matrix{T}(undef, npq, 3) system_solver.rhs = rhs rows = 1:n system_solver.x1 = view(rhs, rows, 1) @@ -95,15 +95,12 @@ mutable struct SymIndefCombinedHSDSystemSolver <: CombinedHSDSystemSolver system_solver.s1 = similar(rhs, q) system_solver.s2 = similar(rhs, q) system_solver.s3 = similar(rhs, q) - # system_solver.s1_k = [view(system_solver.s1, model.cone_idxs[k]) for k in eachindex(model.cones)] - # system_solver.s2_k = [view(system_solver.s2, model.cone_idxs[k]) for k in eachindex(model.cones)] - # system_solver.s3_k = [view(system_solver.s3, model.cone_idxs[k]) for k in eachindex(model.cones)] return system_solver end end -function get_combined_directions(solver::HSDSolver, system_solver::SymIndefCombinedHSDSystemSolver) +function get_combined_directions(solver::HSDSolver{T}, system_solver::SymIndefCombinedHSDSystemSolver{T}) where {T <: HypReal} model = solver.model (n, p, q) = (model.n, model.p, model.q) cones = model.cones @@ -131,13 +128,13 @@ function get_combined_directions(solver::HSDSolver, system_solver::SymIndefCombi @. x1 = -model.c x2 .= solver.x_residual - x3 .= 0.0 + x3 .= zero(T) y1 .= model.b @. y2 = -solver.y_residual - y3 .= 0.0 + y3 .= zero(T) z1 .= model.h @. z2 .= -solver.z_residual - z3 .= 0.0 + z3 .= zero(T) # update lhs matrix copyto!(lhs, system_solver.lhs_copy) @@ -174,7 +171,7 @@ function get_combined_directions(solver::HSDSolver, system_solver::SymIndefCombi end rhs .= F \ rhs else - F = bunchkaufman!(Symmetric(lhs, :L), true, check = true) + F = bunchkaufman!(Symmetric(lhs, :L), true, check = true) # TODO doesn't work for generic reals (need LDLT) ldiv!(F, rhs) end @@ -204,7 +201,7 @@ function get_combined_directions(solver::HSDSolver, system_solver::SymIndefCombi (tau_pred, kap_pred) = lift!(x2, y2, z2, s2, kap + solver.primal_obj_t - solver.dual_obj_t, -kap) @. s2 -= solver.z_residual - (tau_corr, kap_corr) = lift!(x3, y3, z3, s3, 0.0, -kap + mu / tau) + (tau_corr, kap_corr) = lift!(x3, y3, z3, s3, zero(T), -kap + mu / tau) return (x2, x3, y2, y3, z2, z3, s2, s3, tau_pred, tau_corr, kap_pred, kap_corr) end diff --git a/test/runtests.jl b/test/runtests.jl index c9fa77fe7..750f5fd61 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,16 +66,16 @@ include(joinpath(@__DIR__, "native.jl")) # end @info("starting native interface tests") -verbose = true +verbose = false real_types = [ Float64, Float32, BigFloat, ] system_solvers = [ - # SO.QRCholCombinedHSDSystemSolver, - # SO.SymIndefCombinedHSDSystemSolver, - # SO.NaiveElimCombinedHSDSystemSolver, + SO.QRCholCombinedHSDSystemSolver, + SO.SymIndefCombinedHSDSystemSolver, + SO.NaiveElimCombinedHSDSystemSolver, SO.NaiveCombinedHSDSystemSolver, ] testfuns_singular = [ @@ -84,55 +84,55 @@ testfuns_singular = [ inconsistent1, inconsistent2, ] -# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types -# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -# end +@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types + t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +end linear_models = [ MO.PreprocessedLinearModel, MO.RawLinearModel, ] testfuns_nonsingular = [ - # orthant1, - # orthant2, - # orthant3, - # orthant4, - # epinorminf1, - # epinorminf2, - # epinorminf3, - # epinorminf4, - # epinorminf5, - # epinorminf6, - # epinormeucl1, - # epinormeucl2, - # epipersquare1, - # epipersquare2, - # epipersquare3, - # semidefinite1, - # semidefinite2, - # semidefinite3, - # semidefinitecomplex1, - # hypoperlog1, - # hypoperlog2, - # hypoperlog3, - # hypoperlog4, - # epiperpower1, - # epiperpower2, - # epiperpower3, - # hypogeomean1, - # hypogeomean2, - # hypogeomean3, - # hypogeomean4, - # epinormspectral1, - # hypoperlogdet1, - # hypoperlogdet2, - # hypoperlogdet3, + orthant1, + orthant2, + orthant3, + orthant4, + epinorminf1, + epinorminf2, + epinorminf3, + epinorminf4, + epinorminf5, + epinorminf6, + epinormeucl1, + epinormeucl2, + epipersquare1, + epipersquare2, + epipersquare3, + semidefinite1, + semidefinite2, + semidefinite3, + semidefinitecomplex1, + hypoperlog1, + hypoperlog2, + hypoperlog3, + hypoperlog4, + epiperpower1, + epiperpower2, + epiperpower3, + hypogeomean1, + hypogeomean2, + hypogeomean3, + hypogeomean4, + epinormspectral1, + hypoperlogdet1, + hypoperlogdet2, + hypoperlogdet3, epipersumexp1, epipersumexp2, ] @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types - # if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel - # continue # QRChol linear system solver needs preprocessed model - # end + if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel + continue # QRChol linear system solver needs preprocessed model + end t(s{T}, m{T}, verbose) end From c632c239e5015de2771e8afcfff7e5b11461391b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 18:06:48 -0400 Subject: [PATCH 08/20] most barrier tests updated --- src/Cones/hypogeomean.jl | 3 +- test/barriers.jl | 144 ++++++++++++++++---------------- test/runtests.jl | 175 ++++++++++++++++++++------------------- 3 files changed, 162 insertions(+), 160 deletions(-) diff --git a/src/Cones/hypogeomean.jl b/src/Cones/hypogeomean.jl index b8cb3597f..b4d1dc0a7 100644 --- a/src/Cones/hypogeomean.jl +++ b/src/Cones/hypogeomean.jl @@ -29,7 +29,8 @@ mutable struct HypoGeomean{T <: HypReal} <: Cone{T} dim = length(alpha) + 1 @assert dim >= 3 @assert all(ai >= 0.0 for ai in alpha) - @assert sum(alpha) == 1.0 # TODO this check may be too strict + tol = 1e3 * eps(T) + @assert sum(alpha) ≈ 1 atol=tol rtol=tol cone = new{T}() cone.use_dual = !is_dual # using dual barrier cone.dim = dim diff --git a/test/barriers.jl b/test/barriers.jl index ca023eb08..dd08ecc4f 100644 --- a/test/barriers.jl +++ b/test/barriers.jl @@ -4,132 +4,132 @@ Copyright 2018, Chris Coey, Lea Kapelevich and contributors import Random -function pass_through_cone(cone::CO.Cone, num_checks::Int) +function pass_through_cone(cone::CO.Cone{T}; num_checks::Int = 2) where {T <: HypReal} CO.setup_data(cone) for _ in 1:num_checks - point = zeros(CO.dimension(cone)) + point = Vector{T}(undef, CO.dimension(cone)) CO.set_initial_point(point, cone) CO.load_point(cone, point) - @test CO.check_in_cone(cone) - @test -dot(cone.point, cone.g) ≈ CO.get_nu(cone) atol=1e-9 rtol=1e-9 - @test Symmetric(cone.H, :U) * cone.point ≈ -cone.g atol=1e-9 rtol=1e-9 + tol = max(1e-12, sqrt(eps(T))) + @test -dot(cone.point, CO.grad(cone)) ≈ CO.get_nu(cone) atol=tol rtol=tol + @test CO.hess(cone) * cone.point ≈ -CO.grad(cone) atol=tol rtol=tol end return end -function test_epinormeucl_barrier() +function test_epinormeucl_barrier(T::Type{<:HypReal}) for dim in [2, 3, 5] - cone = CO.EpiNormEucl(dim) - pass_through_cone(cone, 1) + cone = CO.EpiNormEucl{T}(dim) + pass_through_cone(cone) end return end -function test_epinorinf_barrier() - for dim in [3, 5, 7] - cone = CO.EpiNormInf(dim) - pass_through_cone(cone, 1) +function test_epinorinf_barrier(T::Type{<:HypReal}) + for dim in [3, 5, 8] + cone = CO.EpiNormInf{T}(dim) + pass_through_cone(cone) end return end -function test_epinormspectral_barrier() +function test_epinormspectral_barrier(T::Type{<:HypReal}) for (n, m) in [(1, 3), (2, 4)] - cone = CO.EpiNormSpectral(n, m) - pass_through_cone(cone, 1) + cone = CO.EpiNormSpectral{T}(n, m) + pass_through_cone(cone) end return end -function test_epiperpower_barrier() +function test_epiperpower_barrier(T::Type{<:HypReal}) for alpha in [1.5, 2.5] - cone = CO.EpiPerPower(alpha) - pass_through_cone(cone, 1) + cone = CO.EpiPerPower{T}(alpha) + pass_through_cone(cone) end return end -function test_epipersquare_barrier() - for dim in [3, 5, 7] - cone = CO.EpiPerSquare(dim) - pass_through_cone(cone, 1) +function test_epipersquare_barrier(T::Type{<:HypReal}) + for dim in [3, 5, 8] + cone = CO.EpiPerSquare{T}(dim) + pass_through_cone(cone) end return end -function test_epipersumexp_barrier() - for dim in [3, 5, 7] - cone = CO.EpiPerSumExp(dim) - pass_through_cone(cone, 1) +function test_epipersumexp_barrier(T::Type{<:HypReal}) + for dim in [3, 5, 8] + cone = CO.EpiPerSumExp{T}(dim) + pass_through_cone(cone) end return end -function test_hypogeomean_barrier() +function test_hypogeomean_barrier(T::Type{<:HypReal}) Random.seed!(1) - for dim in [3, 5, 7] - alpha = rand(dim - 1) + for dim in [3, 5, 8] + alpha = rand(T, dim - 1) alpha ./= sum(alpha) - cone = CO.HypoGeomean(alpha) - pass_through_cone(cone, 1) + cone = CO.HypoGeomean{T}(alpha) + pass_through_cone(cone) end return end -function test_hypoperlog_barrier() - cone = CO.HypoPerLog() - pass_through_cone(cone, 1) +function test_hypoperlog_barrier(T::Type{<:HypReal}) + cone = CO.HypoPerLog{T}() + pass_through_cone(cone) return end -function test_hypoperlogdet_barrier() +function test_hypoperlogdet_barrier(T::Type{<:HypReal}) for dim in [3, 5, 8] - cone = CO.HypoPerLogdet(dim) - pass_through_cone(cone, 1) + cone = CO.HypoPerLogdet{T}(dim) + pass_through_cone(cone) end return end -function test_semidefinite_barrier() +function test_semidefinite_barrier(T::Type{<:HypReal}) for dim in [1, 3, 6] - cone = CO.PosSemidef{Float64}(dim) # real - pass_through_cone(cone, 1) + cone = CO.PosSemidef{T, T}(dim) # real + pass_through_cone(cone) end for dim in [1, 4, 9] - cone = CO.PosSemidef{ComplexF64}(dim) # complex - pass_through_cone(cone, 1) - end - return -end - -# TODO also test complex -function test_wsospolyinterp_barrier() - Random.seed!(1) - for n in 1:3, halfdeg in 1:3 - (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) - cone = CO.WSOSPolyInterp(U, [P0], true) - pass_through_cone(cone, 1) + cone = CO.PosSemidef{T, Complex{T}}(dim) # complex + pass_through_cone(cone) end return end -function test_wsospolyinterpmat_barrier() - Random.seed!(1) - for n in 1:3, halfdeg in 1:3, R in 1:3 - (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) - cone = CO.WSOSPolyInterpMat(R, U, [P0], true) - pass_through_cone(cone, 1) - end - return -end - -function test_wsospolyinterpsoc_barrier() - Random.seed!(1) - for n in 1:2, halfdeg in 1:2, R in 3:3 - (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) - cone = CO.WSOSPolyInterpSOC(R, U, [P0], true) - pass_through_cone(cone, 1) - end - return -end +# # TODO also test complex +# function test_wsospolyinterp_barrier(T::Type{<:HypReal}) +# Random.seed!(1) +# for n in 1:3, halfdeg in 1:3 +# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) +# cone = CO.WSOSPolyInterp{T, Complex{T}}(U, [P0], true) +# pass_through_cone(cone) +# end +# return +# end +# +# function test_wsospolyinterpmat_barrier(T::Type{<:HypReal}) +# Random.seed!(1) +# for n in 1:3, halfdeg in 1:3, R in 1:3 +# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) +# cone = CO.WSOSPolyInterpMat{T}(R, U, [P0], true) +# pass_through_cone(cone) +# end +# return +# end +# +# function test_wsospolyinterpsoc_barrier(T::Type{<:HypReal}) +# Random.seed!(1) +# for n in 1:2, halfdeg in 1:2, R in 3:3 +# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) +# cone = CO.WSOSPolyInterpSOC{T}(R, U, [P0], true) +# pass_through_cone(cone) +# end +# return +# end diff --git a/test/runtests.jl b/test/runtests.jl index 750f5fd61..473094688 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,9 +14,9 @@ const MO = HYP.Models const SO = HYP.Solvers # const MU = HYP.ModelUtilities -# include(joinpath(@__DIR__, "interpolation.jl")) -# include(joinpath(@__DIR__, "barriers.jl")) -include(joinpath(@__DIR__, "native.jl")) +include(joinpath(@__DIR__, "interpolation.jl")) +include(joinpath(@__DIR__, "barriers.jl")) +# include(joinpath(@__DIR__, "native.jl")) # include(joinpath(@__DIR__, "MathOptInterface.jl")) # examples_dir = joinpath(@__DIR__, "../examples") @@ -38,104 +38,105 @@ include(joinpath(@__DIR__, "native.jl")) @testset "Hypatia tests" begin -# @info("starting interpolation tests") +@info("starting interpolation tests") # @testset "interpolation tests" begin # fekete_sample() # test_recover_lagrange_polys() # test_recover_cheb_polys() # end -# -# @info("starting barrier tests") -# barrier_testfuns = [ -# test_epinormeucl_barrier, -# test_epinorinf_barrier, -# test_epinormspectral_barrier, -# test_epiperpower_barrier, -# test_epipersquare_barrier, -# test_epipersumexp_barrier, -# test_hypogeomean_barrier, -# test_hypoperlog_barrier, -# test_hypoperlogdet_barrier, -# test_semidefinite_barrier, -# test_wsospolyinterp_barrier, -# test_wsospolyinterpmat_barrier, -# test_wsospolyinterpsoc_barrier, -# ] -# @testset "barrier functions tests: $t" for t in barrier_testfuns -# t() -# end -@info("starting native interface tests") -verbose = false real_types = [ Float64, Float32, BigFloat, ] -system_solvers = [ - SO.QRCholCombinedHSDSystemSolver, - SO.SymIndefCombinedHSDSystemSolver, - SO.NaiveElimCombinedHSDSystemSolver, - SO.NaiveCombinedHSDSystemSolver, - ] -testfuns_singular = [ - dimension1, - consistent1, - inconsistent1, - inconsistent2, - ] -@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types - t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -end -linear_models = [ - MO.PreprocessedLinearModel, - MO.RawLinearModel, - ] -testfuns_nonsingular = [ - orthant1, - orthant2, - orthant3, - orthant4, - epinorminf1, - epinorminf2, - epinorminf3, - epinorminf4, - epinorminf5, - epinorminf6, - epinormeucl1, - epinormeucl2, - epipersquare1, - epipersquare2, - epipersquare3, - semidefinite1, - semidefinite2, - semidefinite3, - semidefinitecomplex1, - hypoperlog1, - hypoperlog2, - hypoperlog3, - hypoperlog4, - epiperpower1, - epiperpower2, - epiperpower3, - hypogeomean1, - hypogeomean2, - hypogeomean3, - hypogeomean4, - epinormspectral1, - hypoperlogdet1, - hypoperlogdet2, - hypoperlogdet3, - epipersumexp1, - epipersumexp2, + +@info("starting barrier tests") +barrier_testfuns = [ + test_epinormeucl_barrier, + test_epinorinf_barrier, + test_epinormspectral_barrier, + test_epiperpower_barrier, + test_epipersquare_barrier, + test_epipersumexp_barrier, + test_hypogeomean_barrier, + test_hypoperlog_barrier, + test_hypoperlogdet_barrier, + test_semidefinite_barrier, + # test_wsospolyinterp_barrier, + # test_wsospolyinterpmat_barrier, + # test_wsospolyinterpsoc_barrier, ] -@testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types - if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel - continue # QRChol linear system solver needs preprocessed model - end - t(s{T}, m{T}, verbose) +@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types + t(T) end +# @info("starting native interface tests") +# verbose = false +# system_solvers = [ +# SO.QRCholCombinedHSDSystemSolver, +# SO.SymIndefCombinedHSDSystemSolver, +# SO.NaiveElimCombinedHSDSystemSolver, +# SO.NaiveCombinedHSDSystemSolver, +# ] +# testfuns_singular = [ +# dimension1, +# consistent1, +# inconsistent1, +# inconsistent2, +# ] +# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types +# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +# end +# linear_models = [ +# MO.PreprocessedLinearModel, +# MO.RawLinearModel, +# ] +# testfuns_nonsingular = [ +# orthant1, +# orthant2, +# orthant3, +# orthant4, +# epinorminf1, +# epinorminf2, +# epinorminf3, +# epinorminf4, +# epinorminf5, +# epinorminf6, +# epinormeucl1, +# epinormeucl2, +# epipersquare1, +# epipersquare2, +# epipersquare3, +# semidefinite1, +# semidefinite2, +# semidefinite3, +# semidefinitecomplex1, +# hypoperlog1, +# hypoperlog2, +# hypoperlog3, +# hypoperlog4, +# epiperpower1, +# epiperpower2, +# epiperpower3, +# hypogeomean1, +# hypogeomean2, +# hypogeomean3, +# hypogeomean4, +# epinormspectral1, +# hypoperlogdet1, +# hypoperlogdet2, +# hypoperlogdet3, +# epipersumexp1, +# epipersumexp2, +# ] +# @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types +# if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel +# continue # QRChol linear system solver needs preprocessed model +# end +# t(s{T}, m{T}, verbose) +# end + # @info("starting MathOptInterface tests") # verbose = false # system_solvers = [ From 82218c0e3fd1ee2d5c2adb19dd194b9dc6c703d7 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 29 May 2019 19:11:46 -0400 Subject: [PATCH 09/20] update MOI code; MOI tests functioning --- src/Cones/Cones.jl | 4 +- src/Cones/wsospolyinterp.jl | 2 +- src/Hypatia.jl | 4 ++ src/MathOptInterface/cones.jl | 80 ++++++++++++++++----------------- src/MathOptInterface/wrapper.jl | 14 +++--- src/Models/Models.jl | 1 - src/Solvers/Solvers.jl | 1 - test/runtests.jl | 67 +++++++++++++-------------- 8 files changed, 85 insertions(+), 88 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 1fdf1417a..4bfe72965 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -10,10 +10,8 @@ using LinearAlgebra import LinearAlgebra.BlasReal using ForwardDiff using DiffResults - import Hypatia.HypReal - -const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} +import Hypatia.HypRealOrComplex abstract type Cone{T <: HypReal} end diff --git a/src/Cones/wsospolyinterp.jl b/src/Cones/wsospolyinterp.jl index ae817f7d7..49407798e 100644 --- a/src/Cones/wsospolyinterp.jl +++ b/src/Cones/wsospolyinterp.jl @@ -12,7 +12,7 @@ TODO - check if gradient and hessian are correct for complex case =# -mutable struct WSOSPolyInterp{T <: RealOrComplexF64}{T <: HypReal} <: Cone{T} +mutable struct WSOSPolyInterp{T <: HypReal, R <: HypRealOrComplex{T}} <: Cone{T} use_dual::Bool dim::Int Ps::Vector{Matrix{T}} diff --git a/src/Hypatia.jl b/src/Hypatia.jl index af35b848c..afb63a89d 100644 --- a/src/Hypatia.jl +++ b/src/Hypatia.jl @@ -5,6 +5,10 @@ Copyright 2018, Chris Coey and contributors module Hypatia const HypReal = Union{AbstractFloat, Rational} +const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} + +const rt2 = sqrt(2) +const rt2i = inv(rt2) # submodules # include("ModelUtilities/ModelUtilities.jl") diff --git a/src/MathOptInterface/cones.jl b/src/MathOptInterface/cones.jl index 242421c8e..e7d6c0133 100644 --- a/src/MathOptInterface/cones.jl +++ b/src/MathOptInterface/cones.jl @@ -5,36 +5,34 @@ definitions of conic sets not already defined by MathOptInterface and functions for converting between Hypatia and MOI cone definitions =# -RealOrComplexF64 = Union{Float64, ComplexF64} - export WSOSPolyInterpCone -struct WSOSPolyInterpCone{T <: RealOrComplexF64} <: MOI.AbstractVectorSet +struct WSOSPolyInterpCone{T <: HypReal, R <: HypRealOrComplex{T}} <: MOI.AbstractVectorSet dimension::Int - Ps::Vector{Matrix{T}} - is_dual::Bool -end -WSOSPolyInterpCone(dimension::Int, Ps::Vector{Matrix{T}}) where {T <: RealOrComplexF64} = WSOSPolyInterpCone{T}(dimension, Ps, false) - -export WSOSPolyInterpMatCone - -struct WSOSPolyInterpMatCone <: MOI.AbstractVectorSet - R::Int - U::Int - ipwt::Vector{Matrix{Float64}} - is_dual::Bool -end -WSOSPolyInterpMatCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpMatCone(R, U, ipwt, false) - -export WSOSPolyInterpSOCCone # TODO rename, terrible name - -struct WSOSPolyInterpSOCCone <: MOI.AbstractVectorSet - R::Int - U::Int - ipwt::Vector{Matrix{Float64}} + Ps::Vector{Matrix{R}} is_dual::Bool end -WSOSPolyInterpSOCCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpSOCCone(R, U, ipwt, false) +WSOSPolyInterpCone{T, R}(dimension::Int, Ps::Vector{Matrix{R}}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = WSOSPolyInterpCone{T, R}(dimension, Ps, false) + +# export WSOSPolyInterpMatCone +# +# struct WSOSPolyInterpMatCone <: MOI.AbstractVectorSet +# R::Int +# U::Int +# ipwt::Vector{Matrix{Float64}} +# is_dual::Bool +# end +# WSOSPolyInterpMatCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpMatCone(R, U, ipwt, false) +# +# export WSOSPolyInterpSOCCone # TODO rename, terrible name +# +# struct WSOSPolyInterpSOCCone <: MOI.AbstractVectorSet +# R::Int +# U::Int +# ipwt::Vector{Matrix{Float64}} +# is_dual::Bool +# end +# WSOSPolyInterpSOCCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpSOCCone(R, U, ipwt, false) MOIOtherCones = ( MOI.SecondOrderCone, @@ -44,20 +42,20 @@ MOIOtherCones = ( MOI.GeometricMeanCone, MOI.PositiveSemidefiniteConeTriangle, MOI.LogDetConeTriangle, - WSOSPolyInterpCone, - WSOSPolyInterpMatCone, - WSOSPolyInterpSOCCone, + WSOSPolyInterpCone{<:HypReal, <:HypRealOrComplex}, + # WSOSPolyInterpMatCone, + # WSOSPolyInterpSOCCone, ) # MOI cones for which no transformation is needed -cone_from_moi(s::MOI.SecondOrderCone) = Cones.EpiNormEucl(MOI.dimension(s)) -cone_from_moi(s::MOI.RotatedSecondOrderCone) = Cones.EpiPerSquare(MOI.dimension(s)) -cone_from_moi(s::MOI.ExponentialCone) = Cones.HypoPerLog() -cone_from_moi(s::MOI.GeometricMeanCone) = (l = MOI.dimension(s) - 1; Cones.HypoGeomean(fill(inv(l), l))) -cone_from_moi(s::MOI.PowerCone{Float64}) = Cones.EpiPerPower(inv(s.exponent)) -cone_from_moi(s::WSOSPolyInterpCone) = Cones.WSOSPolyInterp(s.dimension, s.Ps, s.is_dual) -cone_from_moi(s::WSOSPolyInterpMatCone) = Cones.WSOSPolyInterpMat(s.R, s.U, s.ipwt, s.is_dual) -cone_from_moi(s::WSOSPolyInterpSOCCone) = Cones.WSOSPolyInterpSOC(s.R, s.U, s.ipwt, s.is_dual) +cone_from_moi(s::MOI.SecondOrderCone) = Cones.EpiNormEucl{Float64}(MOI.dimension(s)) +cone_from_moi(s::MOI.RotatedSecondOrderCone) = Cones.EpiPerSquare{Float64}(MOI.dimension(s)) +cone_from_moi(s::MOI.ExponentialCone) = Cones.HypoPerLog{Float64}() +cone_from_moi(s::MOI.GeometricMeanCone) = (l = MOI.dimension(s) - 1; Cones.HypoGeomean{Float64}(fill(inv(l), l))) +cone_from_moi(s::MOI.PowerCone{Float64}) = Cones.EpiPerPower{Float64}(inv(s.exponent)) +cone_from_moi(s::WSOSPolyInterpCone{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = Cones.WSOSPolyInterp{T, R}(s.dimension, s.Ps, s.is_dual) +# cone_from_moi(s::WSOSPolyInterpMatCone) = Cones.WSOSPolyInterpMat(s.R, s.U, s.ipwt, s.is_dual) +# cone_from_moi(s::WSOSPolyInterpSOCCone) = Cones.WSOSPolyInterpSOC(s.R, s.U, s.ipwt, s.is_dual) cone_from_moi(s::MOI.AbstractVectorSet) = error("MOI set $s is not recognized") function build_var_cone(fi::MOI.VectorOfVariables, si::MOI.AbstractVectorSet, dim::Int, q::Int) @@ -78,8 +76,6 @@ end # MOI cones requiring transformations (eg rescaling, changing order) # TODO later remove if MOI gets scaled triangle sets -const rt2 = sqrt(2) -const rt2i = inv(rt2) svec_scale(dim) = [(i == j ? 1.0 : rt2) for i in 1:round(Int, sqrt(0.25 + 2.0 * dim) - 0.5) for j in 1:i] svec_unscale(dim) = [(i == j ? 1.0 : rt2i) for i in 1:round(Int, sqrt(0.25 + 2.0 * dim) - 0.5) for j in 1:i] @@ -88,7 +84,7 @@ svec_unscale(dim) = [(i == j ? 1.0 : rt2i) for i in 1:round(Int, sqrt(0.25 + 2.0 function build_var_cone(fi::MOI.VectorOfVariables, si::MOI.PositiveSemidefiniteConeTriangle, dim::Int, q::Int) IGi = (q + 1):(q + dim) VGi = -svec_scale(dim) - conei = Cones.PosSemidef(dim) + conei = Cones.PosSemidef{Float64, Float64}(dim) return (IGi, VGi, conei) end @@ -98,7 +94,7 @@ function build_constr_cone(fi::MOI.VectorAffineFunction{Float64}, si::MOI.Positi VGi = [-vt.scalar_term.coefficient * scalevec[vt.output_index] for vt in fi.terms] Ihi = (q + 1):(q + dim) Vhi = scalevec .* fi.constants - conei = Cones.PosSemidef(dim) + conei = Cones.PosSemidef{Float64, Float64}(dim) return (IGi, VGi, Ihi, Vhi, conei) end @@ -106,7 +102,7 @@ end function build_var_cone(fi::MOI.VectorOfVariables, si::MOI.LogDetConeTriangle, dim::Int, q::Int) IGi = (q + 1):(q + dim) VGi = vcat(-1.0, -1.0, -svec_scale(dim - 2)) - conei = Cones.HypoPerLogdet(dim) + conei = Cones.HypoPerLogdet{Float64}(dim) return (IGi, VGi, conei) end @@ -116,6 +112,6 @@ function build_constr_cone(fi::MOI.VectorAffineFunction{Float64}, si::MOI.LogDet VGi = [-vt.scalar_term.coefficient * scalevec[vt.output_index] for vt in fi.terms] Ihi = (q + 1):(q + dim) Vhi = scalevec .* fi.constants - conei = Cones.HypoPerLogdet(dim) + conei = Cones.HypoPerLogdet{Float64}(dim) return (IGi, VGi, Ihi, Vhi, conei) end diff --git a/src/MathOptInterface/wrapper.jl b/src/MathOptInterface/wrapper.jl index e8a721bd9..26c65e029 100644 --- a/src/MathOptInterface/wrapper.jl +++ b/src/MathOptInterface/wrapper.jl @@ -69,8 +69,8 @@ Optimizer(; use_dense::Bool = true, test_certificates::Bool = false, verbose::Bool = false, - system_solver::Type{<:Solvers.CombinedHSDSystemSolver} = Solvers.QRCholCombinedHSDSystemSolver, - linear_model::Type{<:Models.LinearModel} = Models.PreprocessedLinearModel, + system_solver::Type{<:Solvers.CombinedHSDSystemSolver} = Solvers.QRCholCombinedHSDSystemSolver{Float64}, + linear_model::Type{<:Models.LinearModel} = Models.PreprocessedLinearModel{Float64}, max_iters::Int = 500, time_limit::Float64 = 3.6e3, # TODO should be Inf tol_rel_opt::Float64 = 1e-6, @@ -302,7 +302,7 @@ function MOI.copy_to( if q > nonneg_start # exists at least one nonnegative constraint - push!(cones, Cones.Nonnegative(q - nonneg_start)) + push!(cones, Cones.Nonnegative{Float64}(q - nonneg_start)) push!(cone_idxs, (nonneg_start + 1):q) end @@ -370,7 +370,7 @@ function MOI.copy_to( if q > nonpos_start # exists at least one nonpositive constraint - push!(cones, Cones.Nonpositive(q - nonpos_start)) + push!(cones, Cones.Nonpositive{Float64}(q - nonpos_start)) push!(cone_idxs, (nonpos_start + 1):q) end @@ -445,7 +445,7 @@ function MOI.copy_to( opt.interval_scales = interval_scales if q > interval_start # exists at least one interval-type constraint - push!(cones, Cones.EpiNormInf(q - interval_start)) + push!(cones, Cones.EpiNormInf{Float64}(q - interval_start)) push!(cone_idxs, (interval_start + 1):q) end @@ -506,8 +506,8 @@ function MOI.optimize!(opt::Optimizer) return end model = opt.linear_model(copy(opt.c), copy(opt.A), copy(opt.b), copy(opt.G), copy(opt.h), opt.cones, opt.cone_idxs) - stepper = Solvers.CombinedHSDStepper(model, system_solver = opt.system_solver(model)) - solver = Solvers.HSDSolver( + stepper = Solvers.CombinedHSDStepper{Float64}(model, system_solver = opt.system_solver(model)) + solver = Solvers.HSDSolver{Float64}( model, stepper = stepper, verbose = opt.verbose, max_iters = opt.max_iters, time_limit = opt.time_limit, tol_rel_opt = opt.tol_rel_opt, tol_abs_opt = opt.tol_abs_opt, tol_feas = opt.tol_feas, diff --git a/src/Models/Models.jl b/src/Models/Models.jl index 354c4d52d..708e39ec7 100644 --- a/src/Models/Models.jl +++ b/src/Models/Models.jl @@ -8,7 +8,6 @@ module Models using LinearAlgebra using SparseArrays - import Hypatia.Cones import Hypatia.HypReal diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index 6d58fab28..e54152718 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -11,7 +11,6 @@ using LinearAlgebra using SparseArrays using Test using TimerOutputs - import Hypatia.Cones import Hypatia.Models import Hypatia.HypReal diff --git a/test/runtests.jl b/test/runtests.jl index 473094688..39771c177 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,7 @@ using SparseArrays using Test import Hypatia import Hypatia.HypReal +import Hypatia.HypRealOrComplex const HYP = Hypatia const CO = HYP.Cones const MO = HYP.Models @@ -16,8 +17,8 @@ const SO = HYP.Solvers include(joinpath(@__DIR__, "interpolation.jl")) include(joinpath(@__DIR__, "barriers.jl")) -# include(joinpath(@__DIR__, "native.jl")) -# include(joinpath(@__DIR__, "MathOptInterface.jl")) +include(joinpath(@__DIR__, "native.jl")) +include(joinpath(@__DIR__, "MathOptInterface.jl")) # examples_dir = joinpath(@__DIR__, "../examples") # include(joinpath(examples_dir, "envelope/native.jl")) @@ -51,25 +52,25 @@ real_types = [ BigFloat, ] -@info("starting barrier tests") -barrier_testfuns = [ - test_epinormeucl_barrier, - test_epinorinf_barrier, - test_epinormspectral_barrier, - test_epiperpower_barrier, - test_epipersquare_barrier, - test_epipersumexp_barrier, - test_hypogeomean_barrier, - test_hypoperlog_barrier, - test_hypoperlogdet_barrier, - test_semidefinite_barrier, - # test_wsospolyinterp_barrier, - # test_wsospolyinterpmat_barrier, - # test_wsospolyinterpsoc_barrier, - ] -@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types - t(T) -end +# @info("starting barrier tests") +# barrier_testfuns = [ +# test_epinormeucl_barrier, +# test_epinorinf_barrier, +# test_epinormspectral_barrier, +# test_epiperpower_barrier, +# test_epipersquare_barrier, +# test_epipersumexp_barrier, +# test_hypogeomean_barrier, +# test_hypoperlog_barrier, +# test_hypoperlogdet_barrier, +# test_semidefinite_barrier, +# # test_wsospolyinterp_barrier, +# # test_wsospolyinterpmat_barrier, +# # test_wsospolyinterpsoc_barrier, +# ] +# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types +# t(T) +# end # @info("starting native interface tests") # verbose = false @@ -137,18 +138,18 @@ end # t(s{T}, m{T}, verbose) # end -# @info("starting MathOptInterface tests") -# verbose = false -# system_solvers = [ -# SO.NaiveCombinedHSDSystemSolver, -# SO.QRCholCombinedHSDSystemSolver, -# ] -# linear_models = [ -# MO.PreprocessedLinearModel, # MOI tests require preprocessing -# ] -# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models -# test_moi(d, s, m, verbose) -# end +@info("starting MathOptInterface tests") +verbose = false +system_solvers = [ + SO.NaiveCombinedHSDSystemSolver, + SO.QRCholCombinedHSDSystemSolver, + ] +linear_models = [ + MO.PreprocessedLinearModel, # MOI tests require preprocessing + ] +@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models + test_moi(d, s{Float64}, m{Float64}, verbose) +end # # @info("starting native examples tests") # native_options = ( From 1aff7d901c1e12e6a68b496da6c3121695a945e0 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 00:57:59 -0400 Subject: [PATCH 10/20] fix some more type parameters in defaults; minor test cleanup; two examples working --- examples/linearopt/native.jl | 7 +- .../combined_step/stepper.jl | 14 +- src/Solvers/homogeneous_self_dual/solver.jl | 12 +- test/MathOptInterface.jl | 6 +- test/native.jl | 2 + test/runtests.jl | 165 +++++++++--------- 6 files changed, 103 insertions(+), 103 deletions(-) diff --git a/examples/linearopt/native.jl b/examples/linearopt/native.jl index ada4f9b50..807b270b9 100644 --- a/examples/linearopt/native.jl +++ b/examples/linearopt/native.jl @@ -15,7 +15,6 @@ const HYP = Hypatia const CO = HYP.Cones const MO = HYP.Models const SO = HYP.Solvers -const MU = HYP.ModelUtilities function linearopt( m::Int, @@ -30,7 +29,7 @@ function linearopt( c = rand(0.0:9.0, n) G = Diagonal(-1.0I, n) # TODO uniformscaling h = zeros(n) - cones = [CO.Nonnegative(n)] + cones = [CO.Nonnegative{Float64}(n)] cone_idxs = [1:n] return (c = c, A = A, b = b, G = G, h = h, cones = cones, cone_idxs = cone_idxs) end @@ -43,8 +42,8 @@ linearopt4() = linearopt(15, 20, nzfrac = 1 / 4) function test_linearopt(instance::Function; options, rseed::Int = 1) Random.seed!(rseed) d = instance() - model = MO.PreprocessedLinearModel(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) - solver = SO.HSDSolver(model; options...) + model = MO.PreprocessedLinearModel{Float64}(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) + solver = SO.HSDSolver{Float64}(model; options...) SO.solve(solver) r = SO.get_certificates(solver, model, test = true, atol = 1e-3, rtol = 1e-3) @test r.status == :Optimal diff --git a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl index 38f706679..5950a7e96 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/stepper.jl @@ -19,7 +19,7 @@ mutable struct CombinedHSDStepper{T <: HypReal} <: HSDStepper{T} function CombinedHSDStepper{T}( model::Models.LinearModel{T}; - system_solver::CombinedHSDSystemSolver{T} = (model isa Models.PreprocessedLinearModel{T} ? QRCholCombinedHSDSystemSolver(model) : NaiveCombinedHSDSystemSolver(model)), + system_solver::CombinedHSDSystemSolver{T} = (model isa Models.PreprocessedLinearModel{T} ? QRCholCombinedHSDSystemSolver{T}(model) : NaiveCombinedHSDSystemSolver{T}(model)), max_nbhd::T = T(0.75), ) where {T <: HypReal} stepper = new{T}() @@ -45,7 +45,7 @@ mutable struct CombinedHSDStepper{T <: HypReal} <: HSDStepper{T} end end -function step(solver::HSDSolver{T}, stepper::CombinedHSDStepper{T}) where T +function step(solver::HSDSolver{T}, stepper::CombinedHSDStepper{T}) where {T <: HypReal} model = solver.model point = solver.point @@ -100,8 +100,8 @@ function step(solver::HSDSolver{T}, stepper::CombinedHSDStepper{T}) where T return point end -function print_iteration_stats(solver::HSDSolver, stepper::CombinedHSDStepper) - if solver.num_iters == 0 +function print_iteration_stats(solver::HSDSolver{T}, stepper::CombinedHSDStepper{T}) where {T <: HypReal} + if iszero(solver.num_iters) @printf("\n%5s %12s %12s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s\n", "iter", "p_obj", "d_obj", "abs_gap", "rel_gap", "x_feas", "y_feas", "z_feas", "tau", "kap", "mu", @@ -130,9 +130,9 @@ function find_max_alpha_in_nbhd( kap_dir::T, nbhd::T, prev_alpha::T, - stepper::CombinedHSDStepper, - solver::HSDSolver, - ) where T + stepper::CombinedHSDStepper{T}, + solver::HSDSolver{T}, + ) where {T <: HypReal} point = solver.point model = solver.model cones = model.cones diff --git a/src/Solvers/homogeneous_self_dual/solver.jl b/src/Solvers/homogeneous_self_dual/solver.jl index 8bbd3ce1d..2cc4c04b1 100644 --- a/src/Solvers/homogeneous_self_dual/solver.jl +++ b/src/Solvers/homogeneous_self_dual/solver.jl @@ -65,7 +65,7 @@ mutable struct HSDSolver{T <: HypReal} <: Solver{T} function HSDSolver{T}( model::Models.LinearModel{T}; - stepper::HSDStepper{T} = CombinedHSDStepper(model), + stepper::HSDStepper{T} = CombinedHSDStepper{T}(model), verbose::Bool = true, tol_rel_opt = max(1e-12, 1e-2 * cbrt(eps(T))), tol_abs_opt = tol_rel_opt, @@ -139,7 +139,7 @@ get_kappa(solver::HSDSolver) = solver.kap get_mu(solver::HSDSolver) = solver.mu # TODO maybe use iteration interface rather than while loop -function solve(solver::HSDSolver{T}) where T +function solve(solver::HSDSolver{T}) where {T <: HypReal} solver.status = :SolveCalled start_time = time() @@ -188,7 +188,7 @@ function solve(solver::HSDSolver{T}) where T return end -function calc_convergence_params(solver::HSDSolver{T}) where T +function calc_convergence_params(solver::HSDSolver{T}) where {T <: HypReal} model = solver.model point = solver.point @@ -218,7 +218,7 @@ function calc_convergence_params(solver::HSDSolver{T}) where T return end -function check_convergence(solver::HSDSolver{T}) where T +function check_convergence(solver::HSDSolver{T}) where {T <: HypReal} # check convergence criteria # TODO nearly primal or dual infeasible or nearly optimal cases? if max(solver.x_feas, solver.y_feas, solver.z_feas) <= solver.tol_feas && @@ -274,7 +274,7 @@ function check_convergence(solver::HSDSolver{T}) where T return false end -function calc_residual(solver::HSDSolver{T}) where T +function calc_residual(solver::HSDSolver{T}) where {T <: HypReal} model = solver.model point = solver.point @@ -304,7 +304,7 @@ function calc_residual(solver::HSDSolver{T}) where T return end -function calc_mu(solver::HSDSolver{T}) where T +function calc_mu(solver::HSDSolver{T}) where {T <: HypReal} solver.mu = (dot(solver.point.z, solver.point.s) + solver.tau * solver.kap) / (one(T) + solver.model.nu) return solver.mu diff --git a/test/MathOptInterface.jl b/test/MathOptInterface.jl index 58e2494c9..8d8424b21 100644 --- a/test/MathOptInterface.jl +++ b/test/MathOptInterface.jl @@ -4,9 +4,9 @@ Copyright 2018, Chris Coey and contributors import MathOptInterface const MOI = MathOptInterface -MOIT = MOI.Test -MOIB = MOI.Bridges -MOIU = MOI.Utilities +const MOIT = MOI.Test +const MOIB = MOI.Bridges +const MOIU = MOI.Utilities MOIU.@model(HypatiaModelData, (), diff --git a/test/native.jl b/test/native.jl index 9498f510c..ebd05db17 100644 --- a/test/native.jl +++ b/test/native.jl @@ -3,6 +3,8 @@ Copyright 2018, Chris Coey and contributors =# import Random +using LinearAlgebra +using SparseArrays # solve model, check conic certificates are valid and return certificate data function solve_and_check( diff --git a/test/runtests.jl b/test/runtests.jl index 39771c177..6812a6c65 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,9 +2,6 @@ Copyright 2018, Chris Coey, Lea Kapelevich and contributors =# -import Random -using LinearAlgebra -using SparseArrays using Test import Hypatia import Hypatia.HypReal @@ -15,19 +12,19 @@ const MO = HYP.Models const SO = HYP.Solvers # const MU = HYP.ModelUtilities -include(joinpath(@__DIR__, "interpolation.jl")) -include(joinpath(@__DIR__, "barriers.jl")) -include(joinpath(@__DIR__, "native.jl")) -include(joinpath(@__DIR__, "MathOptInterface.jl")) +# include(joinpath(@__DIR__, "interpolation.jl")) +# include(joinpath(@__DIR__, "barriers.jl")) +# include(joinpath(@__DIR__, "native.jl")) +# include(joinpath(@__DIR__, "MathOptInterface.jl")) -# examples_dir = joinpath(@__DIR__, "../examples") +examples_dir = joinpath(@__DIR__, "../examples") # include(joinpath(examples_dir, "envelope/native.jl")) -# include(joinpath(examples_dir, "linearopt/native.jl")) +include(joinpath(examples_dir, "linearopt/native.jl")) # include(joinpath(examples_dir, "polymin/native.jl")) # include(joinpath(examples_dir, "contraction/JuMP.jl")) # include(joinpath(examples_dir, "densityest/JuMP.jl")) # include(joinpath(examples_dir, "envelope/JuMP.jl")) -# include(joinpath(examples_dir, "expdesign/JuMP.jl")) +include(joinpath(examples_dir, "expdesign/JuMP.jl")) # include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) # include(joinpath(examples_dir, "muconvexity/JuMP.jl")) # include(joinpath(examples_dir, "polymin/JuMP.jl")) @@ -37,20 +34,22 @@ include(joinpath(@__DIR__, "MathOptInterface.jl")) # include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) # include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) + +# @info("starting Hypatia tests") @testset "Hypatia tests" begin -@info("starting interpolation tests") +# @info("starting interpolation tests") # @testset "interpolation tests" begin # fekete_sample() # test_recover_lagrange_polys() # test_recover_cheb_polys() # end -real_types = [ - Float64, - Float32, - BigFloat, - ] +# real_types = [ +# Float64, +# Float32, +# BigFloat, +# ] # @info("starting barrier tests") # barrier_testfuns = [ @@ -138,74 +137,74 @@ real_types = [ # t(s{T}, m{T}, verbose) # end -@info("starting MathOptInterface tests") -verbose = false -system_solvers = [ - SO.NaiveCombinedHSDSystemSolver, - SO.QRCholCombinedHSDSystemSolver, - ] -linear_models = [ - MO.PreprocessedLinearModel, # MOI tests require preprocessing - ] -@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models - test_moi(d, s{Float64}, m{Float64}, verbose) -end -# -# @info("starting native examples tests") -# native_options = ( -# verbose = true, -# max_iters = 150, -# time_limit = 6e2, # 1 minute -# ) -# @testset "native examples" begin -# @testset "envelope" begin test_envelope(; native_options..., -# ) end -# @testset "linearopt" begin test_linearopt(; native_options..., -# ) end -# @testset "polymin" begin test_polymin(; native_options..., -# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, -# ) end +# @info("starting MathOptInterface tests") +# verbose = false +# system_solvers = [ +# SO.NaiveCombinedHSDSystemSolver, +# SO.QRCholCombinedHSDSystemSolver, +# ] +# linear_models = [ +# MO.PreprocessedLinearModel, # MOI tests require preprocessing +# ] +# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models +# test_moi(d, s{Float64}, m{Float64}, verbose) # end # -# @info("starting JuMP examples tests") -# JuMP_options = ( -# verbose = true, -# test_certificates = true, -# max_iters = 250, -# time_limit = 6e2, # 1 minute -# ) -# @testset "JuMP examples" begin -# @testset "contraction" begin test_contractionJuMP(; JuMP_options..., -# tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, -# ) end -# @testset "densityest" begin test_densityestJuMP(; JuMP_options..., -# tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, -# ) end -# @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., -# ) end -# @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., -# ) end -# @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., -# tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, -# ) end -# @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., -# ) end -# @testset "polymin" begin test_polyminJuMP(; JuMP_options..., -# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, -# ) end -# @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., -# ) end -# @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., -# tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, -# ) end -# @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., -# ) end -# @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., -# tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, -# ) end -# @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., -# tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, -# ) end -# end +@info("starting native examples tests") +native_options = ( + verbose = true, + max_iters = 150, + time_limit = 6e2, # 1 minute + ) +@testset "native examples" begin + # @testset "envelope" begin test_envelope(; native_options..., + # ) end + @testset "linearopt" begin test_linearopt(; native_options..., + ) end + # @testset "polymin" begin test_polymin(; native_options..., + # tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + # ) end +end + +@info("starting JuMP examples tests") +JuMP_options = ( + verbose = true, + test_certificates = true, + max_iters = 250, + time_limit = 6e2, # 1 minute + ) +@testset "JuMP examples" begin + # @testset "contraction" begin test_contractionJuMP(; JuMP_options..., + # tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, + # ) end + # @testset "densityest" begin test_densityestJuMP(; JuMP_options..., + # tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, + # ) end + # @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., + # ) end + @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., + ) end + # @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., + # tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, + # ) end + # @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., + # ) end + # @testset "polymin" begin test_polyminJuMP(; JuMP_options..., + # tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + # ) end + # @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., + # ) end + # @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., + # tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, + # ) end + # @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., + # ) end + # @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., + # tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, + # ) end + # @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., + # tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, + # ) end +end end From 5ec820c907b9e2360508da4a106f2b5dcc9a833c Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 01:44:31 -0400 Subject: [PATCH 11/20] native tests updated --- examples/envelope/JuMP.jl | 2 +- examples/envelope/native.jl | 8 +++--- examples/polymin/JuMP.jl | 4 +-- examples/polymin/native.jl | 11 +++---- src/Cones/Cones.jl | 6 ++-- src/Cones/wsospolyinterp.jl | 57 +++++++++++++++++++------------------ src/Hypatia.jl | 2 +- test/runtests.jl | 40 +++++++++++++------------- 8 files changed, 65 insertions(+), 65 deletions(-) diff --git a/examples/envelope/JuMP.jl b/examples/envelope/JuMP.jl index d5580312b..a545feea2 100644 --- a/examples/envelope/JuMP.jl +++ b/examples/envelope/JuMP.jl @@ -33,7 +33,7 @@ function envelopeJuMP( model = JuMP.Model() JuMP.@variable(model, fpv[j in 1:U]) # values at Fekete points JuMP.@objective(model, Max, dot(fpv, w)) # integral over domain (via quadrature) - JuMP.@constraint(model, [i in 1:npoly], polys[:, i] .- fpv in HYP.WSOSPolyInterpCone(U, [P0, PWts...])) + JuMP.@constraint(model, [i in 1:npoly], polys[:, i] .- fpv in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...])) return (model = model,) end diff --git a/examples/envelope/native.jl b/examples/envelope/native.jl index f49499146..a9bc093f4 100644 --- a/examples/envelope/native.jl +++ b/examples/envelope/native.jl @@ -15,9 +15,9 @@ using Test import Hypatia const HYP = Hypatia const CO = HYP.Cones -const MU = HYP.ModelUtilities const MO = HYP.Models const SO = HYP.Solvers +const MU = HYP.ModelUtilities function envelope( npoly::Int, @@ -51,7 +51,7 @@ function envelope( h = zeros(npoly * U) end - cones = [CO.WSOSPolyInterp(U, [P0, PWts...], !primal_wsos) for k in 1:npoly] + cones = [CO.WSOSPolyInterp{Float64, Float64}(U, [P0, PWts...], !primal_wsos) for k in 1:npoly] cone_idxs = [(1 + (k - 1) * U):(k * U) for k in 1:npoly] return (c = c, A = A, b = b, G = G, h = h, cones = cones, cone_idxs = cone_idxs) @@ -67,8 +67,8 @@ envelope6() = envelope(2, 30, 1, 30, primal_wsos = false) function test_envelope(instance::Function; options, rseed::Int = 1) Random.seed!(rseed) d = instance() - model = MO.PreprocessedLinearModel(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) - solver = SO.HSDSolver(model; options...) + model = MO.PreprocessedLinearModel{Float64}(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) + solver = SO.HSDSolver{Float64}(model; options...) SO.solve(solver) r = SO.get_certificates(solver, model, test = true, atol = 1e-4, rtol = 1e-4) @test r.status == :Optimal diff --git a/examples/polymin/JuMP.jl b/examples/polymin/JuMP.jl index 8eed831c9..0bbed07f7 100644 --- a/examples/polymin/JuMP.jl +++ b/examples/polymin/JuMP.jl @@ -7,8 +7,6 @@ see description in examples/polymin/native.jl import Random using LinearAlgebra using Test -import MathOptInterface -const MOI = MathOptInterface import JuMP import SumOfSquares import Hypatia @@ -30,7 +28,7 @@ function polyminJuMP( if use_wsos Random.seed!(rseed) (U, pts, P0, PWts, _) = MU.interpolate(dom, halfdeg, sample = sample, sample_factor = 100) - cone = HYP.WSOSPolyInterpCone(U, [P0, PWts...], !primal_wsos) + cone = HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...], !primal_wsos) interp_vals = [f(x => pts[j, :]) for j in 1:U] model = JuMP.Model() diff --git a/examples/polymin/native.jl b/examples/polymin/native.jl index 47c01028e..0961ffe5a 100644 --- a/examples/polymin/native.jl +++ b/examples/polymin/native.jl @@ -20,6 +20,7 @@ const HYP = Hypatia const CO = HYP.Cones const MO = HYP.Models const SO = HYP.Solvers +const MU = HYP.ModelUtilities include(joinpath(@__DIR__, "data.jl")) @@ -48,7 +49,7 @@ function polyminreal( G = Diagonal(-1.0I, U) # TODO use UniformScaling h = zeros(U) end - cones = [CO.WSOSPolyInterp(U, [P0, PWts...], !primal_wsos)] + cones = [CO.WSOSPolyInterp{Float64, Float64}(U, [P0, PWts...], !primal_wsos)] cone_idxs = [1:U] return (c = c, A = A, b = b, G = G, h = h, cones = cones, cone_idxs = cone_idxs, true_obj = true_obj) @@ -75,7 +76,7 @@ polyminreal17() = polyminreal(:lotkavolterra, 3, primal_wsos = false) function polymincomplex( polyname::Symbol, halfdeg::Int; - primal_wsos = true, + primal_wsos::Bool = true, sample_factor::Int = 100, use_QR::Bool = false, ) @@ -142,7 +143,7 @@ function polymincomplex( G = Diagonal(-1.0I, U) h = zeros(U) end - cones = [CO.WSOSPolyInterp(U, P_data, !primal_wsos)] + cones = [CO.WSOSPolyInterp{Float64, ComplexF64}(U, P_data, !primal_wsos)] cone_idxs = [1:U] return (c = c, A = A, b = b, G = G, h = h, cones = cones, cone_idxs = cone_idxs, true_obj = true_obj) @@ -166,8 +167,8 @@ polymincomplex14() = polymincomplex(:denseunit1d, 2, primal_wsos = false) function test_polymin(instance::Function; options, rseed::Int = 1) Random.seed!(rseed) d = instance() - model = MO.PreprocessedLinearModel(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) - solver = SO.HSDSolver(model; options...) + model = MO.PreprocessedLinearModel{Float64}(d.c, d.A, d.b, d.G, d.h, d.cones, d.cone_idxs) + solver = SO.HSDSolver{Float64}(model; options...) SO.solve(solver) r = SO.get_certificates(solver, model, test = true, atol = 1e-4, rtol = 1e-4) @test r.status == :Optimal diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 4bfe72965..f258c9b25 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -12,6 +12,8 @@ using ForwardDiff using DiffResults import Hypatia.HypReal import Hypatia.HypRealOrComplex +import Hypatia.rt2 +import Hypatia.rt2i abstract type Cone{T <: HypReal} end @@ -26,7 +28,7 @@ include("hypogeomean.jl") include("epinormspectral.jl") include("hypoperlogdet.jl") include("epipersumexp.jl") -# include("wsospolyinterp.jl") +include("wsospolyinterp.jl") # include("wsospolyinterpmat.jl") # include("wsospolyinterpsoc.jl") @@ -57,8 +59,6 @@ inv_hess_prod!(prod::AbstractVecOrMat{T}, arr::AbstractVecOrMat{T}, cone::Cone) # utilities for converting between smat and svec forms (lower triangle) for symmetric matrices # TODO only need to do lower triangle if use symmetric matrix types -const rt2 = sqrt(2) -const rt2i = inv(rt2) function smat_to_svec!(vec::AbstractVector{T}, mat::AbstractMatrix{T}) where {T <: HypReal} k = 1 diff --git a/src/Cones/wsospolyinterp.jl b/src/Cones/wsospolyinterp.jl index 49407798e..1156a94c7 100644 --- a/src/Cones/wsospolyinterp.jl +++ b/src/Cones/wsospolyinterp.jl @@ -15,25 +15,25 @@ TODO mutable struct WSOSPolyInterp{T <: HypReal, R <: HypRealOrComplex{T}} <: Cone{T} use_dual::Bool dim::Int - Ps::Vector{Matrix{T}} + Ps::Vector{Matrix{R}} - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} - Hi::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} + Hi::Matrix{T} F # TODO prealloc - tmpLL::Vector{Matrix{T}} - tmpUL::Vector{Matrix{T}} - tmpLU::Vector{Matrix{T}} - tmpUU::Matrix{T} - ΛFs::Vector{CholeskyPivoted{T, Matrix{T}}} + tmpLL::Vector{Matrix{R}} + tmpUL::Vector{Matrix{R}} + tmpLU::Vector{Matrix{R}} + tmpUU::Matrix{R} + ΛFs::Vector{CholeskyPivoted{R, Matrix{R}}} - function WSOSPolyInterp(dim::Int, Ps::Vector{Matrix{T}}, is_dual::Bool) where {T <: RealOrComplexF64} + function WSOSPolyInterp{T, R}(dim::Int, Ps::Vector{Matrix{R}}, is_dual::Bool) where {R <: HypRealOrComplex{T}} where {T <: HypReal} for k in eachindex(Ps) @assert size(Ps[k], 1) == dim end - cone = new{T}() + cone = new{T, R}() cone.use_dual = !is_dual # using dual barrier cone.dim = dim cone.Ps = Ps @@ -41,31 +41,32 @@ mutable struct WSOSPolyInterp{T <: HypReal, R <: HypRealOrComplex{T}} <: Cone{T} end end -WSOSPolyInterp(dim::Int, Ps::Vector{Matrix{T}}) where {T <: RealOrComplexF64} = WSOSPolyInterp{T}(dim, Ps, false) +WSOSPolyInterp{T, R}(dim::Int, Ps::Vector{Matrix{R}}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = WSOSPolyInterp{T, R}(dim, Ps, false) -function setup_data(cone::WSOSPolyInterp{T}) where T +function setup_data(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} dim = cone.dim - cone.g = Vector{Float64}(undef, dim) + cone.g = Vector{T}(undef, dim) cone.H = similar(cone.g, dim, dim) cone.H2 = similar(cone.H) cone.Hi = similar(cone.H) Ps = cone.Ps - cone.tmpLL = [Matrix{T}(undef, size(Pk, 2), size(Pk, 2)) for Pk in Ps] - cone.tmpUL = [Matrix{T}(undef, dim, size(Pk, 2)) for Pk in Ps] - cone.tmpLU = [Matrix{T}(undef, size(Pk, 2), dim) for Pk in Ps] - cone.tmpUU = Matrix{T}(undef, dim, dim) - cone.ΛFs = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(Ps)) + cone.tmpLL = [Matrix{R}(undef, size(Pk, 2), size(Pk, 2)) for Pk in Ps] + cone.tmpUL = [Matrix{R}(undef, dim, size(Pk, 2)) for Pk in Ps] + cone.tmpLU = [Matrix{R}(undef, size(Pk, 2), dim) for Pk in Ps] + cone.tmpUU = Matrix{R}(undef, dim, dim) + cone.ΛFs = Vector{CholeskyPivoted{R, Matrix{R}}}(undef, length(Ps)) return end get_nu(cone::WSOSPolyInterp) = sum(size(Pk, 2) for Pk in cone.Ps) -set_initial_point(arr::AbstractVector{Float64}, cone::WSOSPolyInterp) = (@. arr = 1.0; arr) +set_initial_point(arr::AbstractVector{T}, cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = (@. arr = one(T); arr) -_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: Real} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) -_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: Real} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) +# TODO need a generic method for non-BlasReal +_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: LinearAlgebra.BlasReal} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) +_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: LinearAlgebra.BlasReal} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) -function check_in_cone(cone::WSOSPolyInterp) +function check_in_cone(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} Ps = cone.Ps LLs = cone.tmpLL ULs = cone.tmpUL @@ -85,7 +86,7 @@ function check_in_cone(cone::WSOSPolyInterp) mul!(LLk, Pk', ULk) # pivoted cholesky and triangular solve method - ΛFk = cholesky!(Hermitian(LLk, :L), Val(true), check = false) + ΛFk = cholesky!(Hermitian(LLk, :L), Val(true), check = false) # TODO doesn't work for generic reals if !isposdef(ΛFk) return false end @@ -94,8 +95,8 @@ function check_in_cone(cone::WSOSPolyInterp) g = cone.g H = cone.H - @. g = 0.0 - @. H = 0.0 + @. g = zero(T) + @. H = zero(T) for k in eachindex(Ps) # TODO can be done in parallel, but need multiple tmp3s LUk = LUs[k] diff --git a/src/Hypatia.jl b/src/Hypatia.jl index afb63a89d..335c160ca 100644 --- a/src/Hypatia.jl +++ b/src/Hypatia.jl @@ -11,7 +11,7 @@ const rt2 = sqrt(2) const rt2i = inv(rt2) # submodules -# include("ModelUtilities/ModelUtilities.jl") +include("ModelUtilities/ModelUtilities.jl") include("Cones/Cones.jl") include("Models/Models.jl") include("Solvers/Solvers.jl") diff --git a/test/runtests.jl b/test/runtests.jl index 6812a6c65..0c10c5f06 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,12 +19,12 @@ const SO = HYP.Solvers examples_dir = joinpath(@__DIR__, "../examples") # include(joinpath(examples_dir, "envelope/native.jl")) -include(joinpath(examples_dir, "linearopt/native.jl")) -# include(joinpath(examples_dir, "polymin/native.jl")) +# include(joinpath(examples_dir, "linearopt/native.jl")) +include(joinpath(examples_dir, "polymin/native.jl")) # include(joinpath(examples_dir, "contraction/JuMP.jl")) # include(joinpath(examples_dir, "densityest/JuMP.jl")) # include(joinpath(examples_dir, "envelope/JuMP.jl")) -include(joinpath(examples_dir, "expdesign/JuMP.jl")) +# include(joinpath(examples_dir, "expdesign/JuMP.jl")) # include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) # include(joinpath(examples_dir, "muconvexity/JuMP.jl")) # include(joinpath(examples_dir, "polymin/JuMP.jl")) @@ -150,21 +150,21 @@ include(joinpath(examples_dir, "expdesign/JuMP.jl")) # test_moi(d, s{Float64}, m{Float64}, verbose) # end # -@info("starting native examples tests") -native_options = ( - verbose = true, - max_iters = 150, - time_limit = 6e2, # 1 minute - ) -@testset "native examples" begin - # @testset "envelope" begin test_envelope(; native_options..., - # ) end - @testset "linearopt" begin test_linearopt(; native_options..., - ) end - # @testset "polymin" begin test_polymin(; native_options..., - # tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - # ) end -end +# @info("starting native examples tests") +# native_options = ( +# verbose = true, +# max_iters = 150, +# time_limit = 6e2, # 1 minute +# ) +# @testset "native examples" begin +# @testset "envelope" begin test_envelope(; native_options..., +# ) end +# @testset "linearopt" begin test_linearopt(; native_options..., +# ) end +# @testset "polymin" begin test_polymin(; native_options..., +# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, +# ) end +# end @info("starting JuMP examples tests") JuMP_options = ( @@ -182,8 +182,8 @@ JuMP_options = ( # ) end # @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., # ) end - @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., - ) end + # @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., + # ) end # @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., # tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, # ) end From a3be2516d1d256a9b5c8b5571f1f56881aab20ac Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 18:59:53 -0400 Subject: [PATCH 12/20] update WSOSmat cone --- examples/muconvexity/JuMP.jl | 2 +- src/Cones/Cones.jl | 2 +- src/Cones/wsospolyinterpmat.jl | 105 +++++++++++++++++---------------- src/MathOptInterface/cones.jl | 24 ++++---- test/runtests.jl | 26 ++++---- 5 files changed, 80 insertions(+), 79 deletions(-) diff --git a/examples/muconvexity/JuMP.jl b/examples/muconvexity/JuMP.jl index 33381f743..e5d9b4bdf 100644 --- a/examples/muconvexity/JuMP.jl +++ b/examples/muconvexity/JuMP.jl @@ -40,7 +40,7 @@ function muconvexityJuMP( if use_wsos d = div(maximum(DP.maxdegree.(H)) + 1, 2) (U, pts, P0, PWts, _) = MU.interpolate(dom, d, sample = true, sample_factor = 100) - mat_wsos_cone = HYP.WSOSPolyInterpMatCone(n, U, [P0, PWts...]) + mat_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(n, U, [P0, PWts...]) JuMP.@constraint(model, [H[i, j](x => pts[u, :]) * (i == j ? 1.0 : rt2) for i in 1:n for j in 1:i for u in 1:U] in mat_wsos_cone) else PolyJuMP.setpolymodule!(model, SumOfSquares) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index f258c9b25..38638828f 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -29,7 +29,7 @@ include("epinormspectral.jl") include("hypoperlogdet.jl") include("epipersumexp.jl") include("wsospolyinterp.jl") -# include("wsospolyinterpmat.jl") +include("wsospolyinterpmat.jl") # include("wsospolyinterpsoc.jl") use_dual(cone::Cone) = cone.use_dual diff --git a/src/Cones/wsospolyinterpmat.jl b/src/Cones/wsospolyinterpmat.jl index 17db66e96..52348f5a6 100644 --- a/src/Cones/wsospolyinterpmat.jl +++ b/src/Cones/wsospolyinterpmat.jl @@ -11,28 +11,28 @@ mutable struct WSOSPolyInterpMat{T <: HypReal} <: Cone{T} dim::Int R::Int U::Int - ipwt::Vector{Matrix{Float64}} + ipwt::Vector{Matrix{T}} - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - H2::Matrix{Float64} - Hi::Matrix{Float64} + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + H2::Matrix{T} + Hi::Matrix{T} F - mat::Vector{Matrix{Float64}} - matfact::Vector{CholeskyPivoted{Float64, Matrix{Float64}}} - tmp1::Vector{Matrix{Float64}} - tmp2::Matrix{Float64} - tmp3::Matrix{Float64} - blockmats::Vector{Vector{Vector{Matrix{Float64}}}} - blockfacts::Vector{Vector{CholeskyPivoted{Float64, Matrix{Float64}}}} - PlambdaP::Matrix{Float64} - - function WSOSPolyInterpMat(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}, is_dual::Bool) + mat::Vector{Matrix{T}} + matfact::Vector{CholeskyPivoted{T, Matrix{T}}} + tmp1::Vector{Matrix{T}} + tmp2::Matrix{T} + tmp3::Matrix{T} + blockmats::Vector{Vector{Vector{Matrix{T}}}} + blockfacts::Vector{Vector{CholeskyPivoted{T, Matrix{T}}}} + PlambdaP::Matrix{T} + + function WSOSPolyInterpMat{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}, is_dual::Bool) where {T <: HypReal} for ipwtj in ipwt @assert size(ipwtj, 1) == U end - cone = new() + cone = new{T}() cone.use_dual = !is_dual # using dual barrier dim = U * div(R * (R + 1), 2) cone.dim = dim @@ -43,42 +43,42 @@ mutable struct WSOSPolyInterpMat{T <: HypReal} <: Cone{T} end end -WSOSPolyInterpMat(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpMat(R, U, ipwt, false) +WSOSPolyInterpMat{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}) where {T <: HypReal} = WSOSPolyInterpMat{T}(R, U, ipwt, false) -function setup_data(cone::WSOSPolyInterpMat) +function setup_data(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} dim = cone.dim U = cone.U R = cone.R ipwt = cone.ipwt - cone.g = similar(ipwt[1], dim) - cone.H = similar(ipwt[1], dim, dim) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) cone.H2 = similar(cone.H) cone.Hi = similar(cone.H) - cone.mat = [similar(ipwt[1], size(ipwtj, 2) * R, size(ipwtj, 2) * R) for ipwtj in ipwt] - cone.matfact = Vector{CholeskyPivoted{Float64, Matrix{Float64}}}(undef, length(ipwt)) - cone.tmp1 = [similar(ipwt[1], size(ipwtj, 2), U) for ipwtj in ipwt] - cone.tmp2 = similar(ipwt[1], U, U) + cone.mat = [similar(cone.H, size(ipwtj, 2) * R, size(ipwtj, 2) * R) for ipwtj in ipwt] + cone.matfact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) + cone.tmp1 = [similar(cone.H, size(ipwtj, 2), U) for ipwtj in ipwt] + cone.tmp2 = similar(cone.H, U, U) cone.tmp3 = similar(cone.tmp2) - cone.blockmats = [Vector{Vector{Matrix{Float64}}}(undef, R) for ipwtj in ipwt] + cone.blockmats = [Vector{Vector{Matrix{T}}}(undef, R) for ipwtj in ipwt] for i in eachindex(ipwt), j in 1:R # TODO actually store 1 fewer (no diagonal) and also make this less confusing - cone.blockmats[i][j] = Vector{Matrix{Float64}}(undef, j) + cone.blockmats[i][j] = Vector{Matrix{T}}(undef, j) for k in 1:j # TODO actually need to only go up to j-1 L = size(ipwt[i], 2) - cone.blockmats[i][j][k] = Matrix{Float64}(undef, L, L) + cone.blockmats[i][j][k] = Matrix{T}(undef, L, L) end end - cone.blockfacts = [Vector{CholeskyPivoted{Float64, Matrix{Float64}}}(undef, R) for _ in eachindex(ipwt)] - cone.PlambdaP = Matrix{Float64}(undef, R * U, R * U) + cone.blockfacts = [Vector{CholeskyPivoted{T, Matrix{T}}}(undef, R) for _ in eachindex(ipwt)] + cone.PlambdaP = Matrix{T}(undef, R * U, R * U) return end get_nu(cone::WSOSPolyInterpMat) = cone.R * sum(size(ipwtj, 2) for ipwtj in cone.ipwt) -function set_initial_point(arr::AbstractVector{Float64}, cone::WSOSPolyInterpMat) +function set_initial_point(arr::AbstractVector{T}, cone::WSOSPolyInterpMat{T}) where {T <: HypReal} # sum of diagonal matrices with interpolant polynomial repeating on the diagonal idx = 1 for i in 1:cone.R, j in 1:i - arr[idx:(idx + cone.U - 1)] .= (i == j) ? 1.0 : 0.0 + arr[idx:(idx + cone.U - 1)] .= (i == j) ? one(T) : zero(T) idx += cone.U end return arr @@ -86,13 +86,14 @@ end _blockrange(inner::Int, outer::Int) = (outer * (inner - 1) + 1):(outer * inner) +# NOTE this is experimental code function check_in_cone(cone::WSOSPolyInterpMat) # check_in_cone_nowinv(cone) check_in_cone_master(cone) end # TODO all views can be allocated just once in the cone definition (delete _blockrange too) -function check_in_cone_nowinv(cone::WSOSPolyInterpMat) +function check_in_cone_nowinv(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} # @timeit "build mat" begin for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] @@ -122,8 +123,8 @@ function check_in_cone_nowinv(cone::WSOSPolyInterpMat) # end # @timeit "grad hess" begin - cone.g .= 0.0 - cone.H .= 0.0 + cone.g .= zero(T) + cone.H .= zero(T) for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] L = size(ipwtj, 2) @@ -137,7 +138,7 @@ function check_in_cone_nowinv(cone::WSOSPolyInterpMat) uo = 0 for p in 1:cone.R, q in 1:p uo += 1 - fact = (p == q) ? 1.0 : rt2 + fact = (p == q) ? one(T) : rt2 rinds = _blockrange(p, cone.U) cinds = _blockrange(q, cone.U) idxs = _blockrange(uo, cone.U) @@ -155,7 +156,7 @@ function check_in_cone_nowinv(cone::WSOSPolyInterpMat) cinds2 = _blockrange(q2, cone.U) idxs2 = _blockrange(uo2, cone.U) - fact = xor(p == q, p2 == q2) ? rt2i : 1.0 + fact = xor(p == q, p2 == q2) ? rt2i : one(T) @. cone.H[idxs, idxs2] += PlambdaP[rinds, rinds2] * PlambdaP[cinds, cinds2] * fact if (p != q) || (p2 != q2) @@ -170,16 +171,16 @@ function check_in_cone_nowinv(cone::WSOSPolyInterpMat) end # res stored lower triangle -function blockcholesky!(cone::WSOSPolyInterpMat, L::Int, j::Int) +function blockcholesky!(cone::WSOSPolyInterpMat{T}, L::Int, j::Int) where {T <: HypReal} R = cone.R res = cone.blockmats[j] tmp = zeros(L, L) facts = cone.blockfacts[j] for r in 1:R - tmp .= 0.0 + tmp .= zero(T) # blocks on the diagonal come from cholesky decomposition after back-substitution, result stored in cone.blockfacts for k in 1:(r - 1) - BLAS.syrk!('U', 'N', 1.0, res[r][k], 1.0, tmp) + BLAS.syrk!('U', 'N', one(T), res[r][k], one(T), tmp) end diag_block = cone.mat[j][_blockrange(r, L), _blockrange(r, L)] - tmp F = cholesky!(Symmetric(diag_block, :U), Val(true), check = false) @@ -192,7 +193,7 @@ function blockcholesky!(cone::WSOSPolyInterpMat, L::Int, j::Int) for s in (r + 1):R for k in 1:(r - 1) # tmp += res[r][k] * res[s][k]' - BLAS.gemm!('N', 'T', 1.0, res[r][k], res[s][k], 1.0, tmp) + BLAS.gemm!('N', 'T', one(T), res[r][k], res[s][k], one(T), tmp) end rhs = cone.mat[j][_blockrange(s, L), _blockrange(r, L)] - tmp res[s][r] = (facts[r].L \ view(rhs, facts[r].p, :))' @@ -201,7 +202,7 @@ function blockcholesky!(cone::WSOSPolyInterpMat, L::Int, j::Int) return true end -function _block_trisolve(cone::WSOSPolyInterpMat, blocknum::Int, L::Int, j::Int) +function _block_trisolve(cone::WSOSPolyInterpMat{T}, blocknum::Int, L::Int, j::Int) where {T <: HypReal} Lmat = cone.blockmats[j] R = cone.R U = cone.U @@ -210,11 +211,11 @@ function _block_trisolve(cone::WSOSPolyInterpMat, blocknum::Int, L::Int, j::Int) tmp = zeros(L, U) resvec[_blockrange(blocknum, L), :] = Fvec[blocknum].L \ view(cone.ipwt[j]', Fvec[blocknum].p, :) for r in (blocknum + 1):R - tmp .= 0.0 + tmp .= zero(T) for s in blocknum:(r - 1) # tmp -= Lmat[r][s] * resvec[_blockrange(s, L), :] resblock = resvec[_blockrange(s, L), :] - BLAS.gemm!('N', 'N', -1.0, Lmat[r][s], resblock, 1.0, tmp) + BLAS.gemm!('N', 'N', -one(T), Lmat[r][s], resblock, one(T), tmp) end resvec[_blockrange(r, L), :] = Fvec[r].L \ view(tmp, Fvec[r].p, :) end @@ -222,7 +223,7 @@ function _block_trisolve(cone::WSOSPolyInterpMat, blocknum::Int, L::Int, j::Int) end # one block-column at a time on the RHS -function _block_trisolve(cone::WSOSPolyInterpMat, L::Int, j::Int) +function _block_trisolve(cone::WSOSPolyInterpMat{T}, L::Int, j::Int) where {T <: HypReal} R = cone.R U = cone.U resmat = zeros(R * L, R * U) @@ -233,7 +234,7 @@ function _block_trisolve(cone::WSOSPolyInterpMat, L::Int, j::Int) end # multiply lower triangular block matrix transposed by itself -function _mulblocks!(cone::WSOSPolyInterpMat, mat::Matrix{Float64}, L::Int) +function _mulblocks!(cone::WSOSPolyInterpMat{T}, mat::Matrix{T}, L::Int) where {T <: HypReal} # cone.PlambdaP = mat' * mat R = cone.R U = cone.U @@ -241,7 +242,7 @@ function _mulblocks!(cone::WSOSPolyInterpMat, mat::Matrix{Float64}, L::Int) rinds = _blockrange(i, U) for j in i:R cinds = _blockrange(j, U) - tmp .= 0.0 + tmp .= zero(T) # since mat is block lower triangular rows only from max(i,j) start making a nonzero contribution to the product mulrange = ((j - 1) * L + 1):(L * R) mul!(view(cone.PlambdaP, rinds, cinds), mat[mulrange, _blockrange(i, U)]', mat[mulrange, _blockrange(j, U)]) @@ -250,7 +251,7 @@ function _mulblocks!(cone::WSOSPolyInterpMat, mat::Matrix{Float64}, L::Int) return nothing end -function check_in_cone_master(cone::WSOSPolyInterpMat) +function check_in_cone_master(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} # @timeit "build mat" begin for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] @@ -281,8 +282,8 @@ function check_in_cone_master(cone::WSOSPolyInterpMat) # end # @timeit "grad hess" begin - cone.g .= 0.0 - cone.H .= 0.0 + cone.g .= zero(T) + cone.H .= zero(T) for j in eachindex(cone.ipwt) # @timeit "W_inv" begin W_inv_j = inv(cone.matfact[j]) @@ -297,7 +298,7 @@ function check_in_cone_master(cone::WSOSPolyInterpMat) uo = 0 for p in 1:cone.R, q in 1:p uo += 1 - fact = (p == q) ? 1.0 : rt2 + fact = (p == q) ? one(T) : rt2 rinds = _blockrange(p, L) cinds = _blockrange(q, L) idxs = _blockrange(uo, cone.U) @@ -321,7 +322,7 @@ function check_in_cone_master(cone::WSOSPolyInterpMat) mul!(tmp2, ipwtj, tmp1j) mul!(tmp1j, view(W_inv_j, cinds, cinds2), ipwtj') mul!(tmp3, ipwtj, tmp1j) - fact = xor(p == q, p2 == q2) ? rt2i : 1.0 + fact = xor(p == q, p2 == q2) ? rt2i : one(T) @. cone.H[idxs, idxs2] += tmp2 * tmp3 * fact if (p != q) || (p2 != q2) diff --git a/src/MathOptInterface/cones.jl b/src/MathOptInterface/cones.jl index e7d6c0133..96e421e4a 100644 --- a/src/MathOptInterface/cones.jl +++ b/src/MathOptInterface/cones.jl @@ -14,16 +14,16 @@ struct WSOSPolyInterpCone{T <: HypReal, R <: HypRealOrComplex{T}} <: MOI.Abstrac end WSOSPolyInterpCone{T, R}(dimension::Int, Ps::Vector{Matrix{R}}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = WSOSPolyInterpCone{T, R}(dimension, Ps, false) -# export WSOSPolyInterpMatCone -# -# struct WSOSPolyInterpMatCone <: MOI.AbstractVectorSet -# R::Int -# U::Int -# ipwt::Vector{Matrix{Float64}} -# is_dual::Bool -# end -# WSOSPolyInterpMatCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpMatCone(R, U, ipwt, false) -# +export WSOSPolyInterpMatCone + +struct WSOSPolyInterpMatCone{T <: HypReal} <: MOI.AbstractVectorSet + R::Int + U::Int + ipwt::Vector{Matrix{Float64}} + is_dual::Bool +end +WSOSPolyInterpMatCone{T}(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) where {T <: HypReal} = WSOSPolyInterpMatCone{T}(R, U, ipwt, false) + # export WSOSPolyInterpSOCCone # TODO rename, terrible name # # struct WSOSPolyInterpSOCCone <: MOI.AbstractVectorSet @@ -43,7 +43,7 @@ MOIOtherCones = ( MOI.PositiveSemidefiniteConeTriangle, MOI.LogDetConeTriangle, WSOSPolyInterpCone{<:HypReal, <:HypRealOrComplex}, - # WSOSPolyInterpMatCone, + WSOSPolyInterpMatCone{<:HypReal}, # WSOSPolyInterpSOCCone, ) @@ -54,7 +54,7 @@ cone_from_moi(s::MOI.ExponentialCone) = Cones.HypoPerLog{Float64}() cone_from_moi(s::MOI.GeometricMeanCone) = (l = MOI.dimension(s) - 1; Cones.HypoGeomean{Float64}(fill(inv(l), l))) cone_from_moi(s::MOI.PowerCone{Float64}) = Cones.EpiPerPower{Float64}(inv(s.exponent)) cone_from_moi(s::WSOSPolyInterpCone{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = Cones.WSOSPolyInterp{T, R}(s.dimension, s.Ps, s.is_dual) -# cone_from_moi(s::WSOSPolyInterpMatCone) = Cones.WSOSPolyInterpMat(s.R, s.U, s.ipwt, s.is_dual) +cone_from_moi(s::WSOSPolyInterpMatCone{T}) where {T <: HypReal} = Cones.WSOSPolyInterpMat{T}(s.R, s.U, s.ipwt, s.is_dual) # cone_from_moi(s::WSOSPolyInterpSOCCone) = Cones.WSOSPolyInterpSOC(s.R, s.U, s.ipwt, s.is_dual) cone_from_moi(s::MOI.AbstractVectorSet) = error("MOI set $s is not recognized") diff --git a/test/runtests.jl b/test/runtests.jl index 0c10c5f06..612c44dca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,7 +10,7 @@ const HYP = Hypatia const CO = HYP.Cones const MO = HYP.Models const SO = HYP.Solvers -# const MU = HYP.ModelUtilities +const MU = HYP.ModelUtilities # include(joinpath(@__DIR__, "interpolation.jl")) # include(joinpath(@__DIR__, "barriers.jl")) @@ -20,14 +20,14 @@ const SO = HYP.Solvers examples_dir = joinpath(@__DIR__, "../examples") # include(joinpath(examples_dir, "envelope/native.jl")) # include(joinpath(examples_dir, "linearopt/native.jl")) -include(joinpath(examples_dir, "polymin/native.jl")) +# include(joinpath(examples_dir, "polymin/native.jl")) # include(joinpath(examples_dir, "contraction/JuMP.jl")) # include(joinpath(examples_dir, "densityest/JuMP.jl")) -# include(joinpath(examples_dir, "envelope/JuMP.jl")) -# include(joinpath(examples_dir, "expdesign/JuMP.jl")) +include(joinpath(examples_dir, "envelope/JuMP.jl")) +include(joinpath(examples_dir, "expdesign/JuMP.jl")) # include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) -# include(joinpath(examples_dir, "muconvexity/JuMP.jl")) -# include(joinpath(examples_dir, "polymin/JuMP.jl")) +include(joinpath(examples_dir, "muconvexity/JuMP.jl")) +include(joinpath(examples_dir, "polymin/JuMP.jl")) # include(joinpath(examples_dir, "polynorm/JuMP.jl")) # include(joinpath(examples_dir, "regionofattr/JuMP.jl")) # include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) @@ -180,18 +180,18 @@ JuMP_options = ( # @testset "densityest" begin test_densityestJuMP(; JuMP_options..., # tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, # ) end - # @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., - # ) end + @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., + ) end # @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., # ) end # @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., # tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, # ) end - # @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., - # ) end - # @testset "polymin" begin test_polyminJuMP(; JuMP_options..., - # tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - # ) end + @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., + ) end + @testset "polymin" begin test_polyminJuMP(; JuMP_options..., + tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + ) end # @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., # ) end # @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., From 8c3fe3a8adfd9cd128c5a4bb53aa55052251627a Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 19:43:44 -0400 Subject: [PATCH 13/20] update more examples --- examples/contraction/JuMP.jl | 4 ++-- examples/densityest/JuMP.jl | 4 ++-- examples/regionofattr/JuMP.jl | 6 +++--- examples/semidefinitepoly/JuMP.jl | 2 +- examples/shapeconregr/JuMP.jl | 4 ++-- test/runtests.jl | 28 ++++++++++++++-------------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/contraction/JuMP.jl b/examples/contraction/JuMP.jl index 52e15ea66..dd71967e0 100644 --- a/examples/contraction/JuMP.jl +++ b/examples/contraction/JuMP.jl @@ -57,8 +57,8 @@ function contractionJuMP( deg_R = maximum(DP.maxdegree.(R)) d_R = div(deg_R + 1, 2) (U_R, pts_R, P0_R, _, _) = MU.interpolate(dom, d_R, sample = true) - JuMP.@constraint(model, [M[i, j](pts_M[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_M] in HYP.WSOSPolyInterpMatCone(n, U_M, [P0_M])) - JuMP.@constraint(model, [-R[i, j](pts_R[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_R] in HYP.WSOSPolyInterpMatCone(n, U_R, [P0_R])) + JuMP.@constraint(model, [M[i, j](pts_M[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_M] in HYP.WSOSPolyInterpMatCone{Float64}(n, U_M, [P0_M])) + JuMP.@constraint(model, [-R[i, j](pts_R[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_R] in HYP.WSOSPolyInterpMatCone{Float64}(n, U_R, [P0_R])) else PJ.setpolymodule!(model, SumOfSquares) JuMP.@constraint(model, M - Matrix(delta * I, n, n) in JuMP.PSDCone()) diff --git a/examples/densityest/JuMP.jl b/examples/densityest/JuMP.jl index c3ce9795f..debe2cd2f 100644 --- a/examples/densityest/JuMP.jl +++ b/examples/densityest/JuMP.jl @@ -53,7 +53,7 @@ function densityestJuMP( JuMP.@variable(model, f, PolyJuMP.Poly(PX)) JuMP.@constraints(model, begin sum(w[i] * f(pts[i, :]) for i in 1:U) == 1.0 # integrate to 1 - [f(pts[i, :]) for i in 1:U] in HYP.WSOSPolyInterpCone(U, [P0, PWts...]) # density nonnegative + [f(pts[i, :]) for i in 1:U] in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...]) # density nonnegative [i in 1:nobs], vcat(z[i], 1.0, f(X[i, :])) in MOI.ExponentialCone() # hypograph of log end) else @@ -66,7 +66,7 @@ function densityestJuMP( JuMP.@variable(model, f[1:U]) JuMP.@constraints(model, begin dot(w, f) == 1.0 # integrate to 1 - f in HYP.WSOSPolyInterpCone(U, [P0, PWts...]) # density nonnegative + f in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...]) # density nonnegative [i in 1:nobs], vcat(z[i], 1.0, dot(f, basis_evals[i, :])) in MOI.ExponentialCone() # hypograph of log end) end diff --git a/examples/regionofattr/JuMP.jl b/examples/regionofattr/JuMP.jl index 27d2112f2..48a8485d4 100644 --- a/examples/regionofattr/JuMP.jl +++ b/examples/regionofattr/JuMP.jl @@ -44,9 +44,9 @@ function regionofattrJuMP(deg::Int; use_WSOS::Bool = true) (U1, pts1, P01, PWts1, quad_weights) = MU.interpolate(dom1, halfdeg, sample = false, calc_w = true) (U2, pts2, P02, PWts2, _) = MU.interpolate(dom2, halfdeg, sample = false) (U3, pts3, P03, PWts3, _) = MU.interpolate(dom3, halfdeg - 1, sample = false) - wsos_cone1 = HYP.WSOSPolyInterpCone(U1, [P01, PWts1...]) - wsos_cone2 = HYP.WSOSPolyInterpCone(U2, [P02, PWts2...]) - wsos_cone3 = HYP.WSOSPolyInterpCone(U3, [P03, PWts3...]) + wsos_cone1 = HYP.WSOSPolyInterpCone{Float64, Float64}(U1, [P01, PWts1...]) + wsos_cone2 = HYP.WSOSPolyInterpCone{Float64, Float64}(U2, [P02, PWts2...]) + wsos_cone3 = HYP.WSOSPolyInterpCone{Float64, Float64}(U3, [P03, PWts3...]) JuMP.@objective(model, Min, sum(quad_weights[u] * w(pts1[u, :]) for u in 1:U1)) JuMP.@constraints(model, begin diff --git a/examples/semidefinitepoly/JuMP.jl b/examples/semidefinitepoly/JuMP.jl index af04d0727..3c5681b53 100644 --- a/examples/semidefinitepoly/JuMP.jl +++ b/examples/semidefinitepoly/JuMP.jl @@ -29,7 +29,7 @@ function semidefinitepolyJuMP(x::Vector{DP.PolyVar{true}}, H::Matrix; use_wsos:: n = DP.nvariables(x) dom = MU.FreeDomain(n) (U, pts, P0, _, _) = MU.interpolate(dom, halfdeg, sample_factor = 20, sample = true) - mat_wsos_cone = HYP.WSOSPolyInterpMatCone(matdim, U, [P0], use_dual) + mat_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(matdim, U, [P0], use_dual) if use_dual JuMP.@variable(model, z[i in 1:n, 1:i, 1:U]) diff --git a/examples/shapeconregr/JuMP.jl b/examples/shapeconregr/JuMP.jl index ce05e6b38..632f1f457 100644 --- a/examples/shapeconregr/JuMP.jl +++ b/examples/shapeconregr/JuMP.jl @@ -72,7 +72,7 @@ function shapeconregrJuMP( if !all(iszero, mono_profile) gradient_halfdeg = div(deg, 2) (mono_U, mono_points, mono_P0, mono_PWts, _) = MU.interpolate(mono_dom, gradient_halfdeg, sample = sample, sample_factor = 50) - mono_wsos_cone = HYP.WSOSPolyInterpCone(mono_U, [mono_P0, mono_PWts...]) + mono_wsos_cone = HYP.WSOSPolyInterpCone{Float64, Float64}(mono_U, [mono_P0, mono_PWts...]) for j in 1:n if !iszero(mono_profile[j]) gradient = DP.differentiate(regressor, DP.variables(regressor)[j]) @@ -85,7 +85,7 @@ function shapeconregrJuMP( if !iszero(conv_profile) hessian_halfdeg = div(deg - 1, 2) (conv_U, conv_points, conv_P0, conv_PWts, _) = MU.interpolate(conv_dom, hessian_halfdeg, sample = sample, sample_factor = 50) - conv_wsos_cone = HYP.WSOSPolyInterpMatCone(n, conv_U, [conv_P0, conv_PWts...]) + conv_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(n, conv_U, [conv_P0, conv_PWts...]) hessian = DP.differentiate(regressor, DP.variables(regressor), 2) JuMP.@constraint(model, [conv_profile * hessian[i, j](conv_points[u, :]) * (i == j ? 1.0 : rt2) for i in 1:n for j in 1:i for u in 1:conv_U] in conv_wsos_cone) end diff --git a/test/runtests.jl b/test/runtests.jl index 612c44dca..320782f2c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,21 +18,21 @@ const MU = HYP.ModelUtilities # include(joinpath(@__DIR__, "MathOptInterface.jl")) examples_dir = joinpath(@__DIR__, "../examples") -# include(joinpath(examples_dir, "envelope/native.jl")) -# include(joinpath(examples_dir, "linearopt/native.jl")) -# include(joinpath(examples_dir, "polymin/native.jl")) -# include(joinpath(examples_dir, "contraction/JuMP.jl")) -# include(joinpath(examples_dir, "densityest/JuMP.jl")) +include(joinpath(examples_dir, "envelope/native.jl")) +include(joinpath(examples_dir, "linearopt/native.jl")) +include(joinpath(examples_dir, "polymin/native.jl")) +include(joinpath(examples_dir, "contraction/JuMP.jl")) +include(joinpath(examples_dir, "densityest/JuMP.jl")) include(joinpath(examples_dir, "envelope/JuMP.jl")) include(joinpath(examples_dir, "expdesign/JuMP.jl")) -# include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) +include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) include(joinpath(examples_dir, "muconvexity/JuMP.jl")) include(joinpath(examples_dir, "polymin/JuMP.jl")) -# include(joinpath(examples_dir, "polynorm/JuMP.jl")) -# include(joinpath(examples_dir, "regionofattr/JuMP.jl")) -# include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) -# include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) -# include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) +include(joinpath(examples_dir, "polynorm/JuMP.jl")) +include(joinpath(examples_dir, "regionofattr/JuMP.jl")) +include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) +include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) +include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) # @info("starting Hypatia tests") @@ -194,9 +194,9 @@ JuMP_options = ( ) end # @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., # ) end - # @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., - # tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, - # ) end + @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., + tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, + ) end # @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., # ) end # @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., From 4b95530aef15d6c6254c1ed8cde8c49a2081281f Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 21:11:26 -0400 Subject: [PATCH 14/20] fix bug with WSOS cone def in MOI code; fix remaining cones and JuMP tests --- examples/contraction/JuMP.jl | 4 +- examples/densityest/JuMP.jl | 4 +- examples/envelope/JuMP.jl | 2 +- examples/muconvexity/JuMP.jl | 2 +- examples/polymin/JuMP.jl | 2 +- examples/regionofattr/JuMP.jl | 6 +- examples/semidefinitepoly/JuMP.jl | 2 +- examples/shapeconregr/JuMP.jl | 4 +- src/Cones/Cones.jl | 2 +- src/Cones/wsospolyinterpsoc.jl | 101 +++++----- src/MathOptInterface/cones.jl | 44 ++--- test/barriers.jl | 60 +++--- test/runtests.jl | 299 +++++++++++++++--------------- 13 files changed, 263 insertions(+), 269 deletions(-) diff --git a/examples/contraction/JuMP.jl b/examples/contraction/JuMP.jl index dd71967e0..52e15ea66 100644 --- a/examples/contraction/JuMP.jl +++ b/examples/contraction/JuMP.jl @@ -57,8 +57,8 @@ function contractionJuMP( deg_R = maximum(DP.maxdegree.(R)) d_R = div(deg_R + 1, 2) (U_R, pts_R, P0_R, _, _) = MU.interpolate(dom, d_R, sample = true) - JuMP.@constraint(model, [M[i, j](pts_M[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_M] in HYP.WSOSPolyInterpMatCone{Float64}(n, U_M, [P0_M])) - JuMP.@constraint(model, [-R[i, j](pts_R[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_R] in HYP.WSOSPolyInterpMatCone{Float64}(n, U_R, [P0_R])) + JuMP.@constraint(model, [M[i, j](pts_M[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_M] in HYP.WSOSPolyInterpMatCone(n, U_M, [P0_M])) + JuMP.@constraint(model, [-R[i, j](pts_R[u, :]) * (i == j ? 1.0 : rt2) - (i == j ? delta : 0.0) for i in 1:n for j in 1:i for u in 1:U_R] in HYP.WSOSPolyInterpMatCone(n, U_R, [P0_R])) else PJ.setpolymodule!(model, SumOfSquares) JuMP.@constraint(model, M - Matrix(delta * I, n, n) in JuMP.PSDCone()) diff --git a/examples/densityest/JuMP.jl b/examples/densityest/JuMP.jl index debe2cd2f..c3ce9795f 100644 --- a/examples/densityest/JuMP.jl +++ b/examples/densityest/JuMP.jl @@ -53,7 +53,7 @@ function densityestJuMP( JuMP.@variable(model, f, PolyJuMP.Poly(PX)) JuMP.@constraints(model, begin sum(w[i] * f(pts[i, :]) for i in 1:U) == 1.0 # integrate to 1 - [f(pts[i, :]) for i in 1:U] in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...]) # density nonnegative + [f(pts[i, :]) for i in 1:U] in HYP.WSOSPolyInterpCone(U, [P0, PWts...]) # density nonnegative [i in 1:nobs], vcat(z[i], 1.0, f(X[i, :])) in MOI.ExponentialCone() # hypograph of log end) else @@ -66,7 +66,7 @@ function densityestJuMP( JuMP.@variable(model, f[1:U]) JuMP.@constraints(model, begin dot(w, f) == 1.0 # integrate to 1 - f in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...]) # density nonnegative + f in HYP.WSOSPolyInterpCone(U, [P0, PWts...]) # density nonnegative [i in 1:nobs], vcat(z[i], 1.0, dot(f, basis_evals[i, :])) in MOI.ExponentialCone() # hypograph of log end) end diff --git a/examples/envelope/JuMP.jl b/examples/envelope/JuMP.jl index a545feea2..d5580312b 100644 --- a/examples/envelope/JuMP.jl +++ b/examples/envelope/JuMP.jl @@ -33,7 +33,7 @@ function envelopeJuMP( model = JuMP.Model() JuMP.@variable(model, fpv[j in 1:U]) # values at Fekete points JuMP.@objective(model, Max, dot(fpv, w)) # integral over domain (via quadrature) - JuMP.@constraint(model, [i in 1:npoly], polys[:, i] .- fpv in HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...])) + JuMP.@constraint(model, [i in 1:npoly], polys[:, i] .- fpv in HYP.WSOSPolyInterpCone(U, [P0, PWts...])) return (model = model,) end diff --git a/examples/muconvexity/JuMP.jl b/examples/muconvexity/JuMP.jl index e5d9b4bdf..33381f743 100644 --- a/examples/muconvexity/JuMP.jl +++ b/examples/muconvexity/JuMP.jl @@ -40,7 +40,7 @@ function muconvexityJuMP( if use_wsos d = div(maximum(DP.maxdegree.(H)) + 1, 2) (U, pts, P0, PWts, _) = MU.interpolate(dom, d, sample = true, sample_factor = 100) - mat_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(n, U, [P0, PWts...]) + mat_wsos_cone = HYP.WSOSPolyInterpMatCone(n, U, [P0, PWts...]) JuMP.@constraint(model, [H[i, j](x => pts[u, :]) * (i == j ? 1.0 : rt2) for i in 1:n for j in 1:i for u in 1:U] in mat_wsos_cone) else PolyJuMP.setpolymodule!(model, SumOfSquares) diff --git a/examples/polymin/JuMP.jl b/examples/polymin/JuMP.jl index 0bbed07f7..edc78bc9a 100644 --- a/examples/polymin/JuMP.jl +++ b/examples/polymin/JuMP.jl @@ -28,7 +28,7 @@ function polyminJuMP( if use_wsos Random.seed!(rseed) (U, pts, P0, PWts, _) = MU.interpolate(dom, halfdeg, sample = sample, sample_factor = 100) - cone = HYP.WSOSPolyInterpCone{Float64, Float64}(U, [P0, PWts...], !primal_wsos) + cone = HYP.WSOSPolyInterpCone(U, [P0, PWts...], !primal_wsos) interp_vals = [f(x => pts[j, :]) for j in 1:U] model = JuMP.Model() diff --git a/examples/regionofattr/JuMP.jl b/examples/regionofattr/JuMP.jl index 48a8485d4..27d2112f2 100644 --- a/examples/regionofattr/JuMP.jl +++ b/examples/regionofattr/JuMP.jl @@ -44,9 +44,9 @@ function regionofattrJuMP(deg::Int; use_WSOS::Bool = true) (U1, pts1, P01, PWts1, quad_weights) = MU.interpolate(dom1, halfdeg, sample = false, calc_w = true) (U2, pts2, P02, PWts2, _) = MU.interpolate(dom2, halfdeg, sample = false) (U3, pts3, P03, PWts3, _) = MU.interpolate(dom3, halfdeg - 1, sample = false) - wsos_cone1 = HYP.WSOSPolyInterpCone{Float64, Float64}(U1, [P01, PWts1...]) - wsos_cone2 = HYP.WSOSPolyInterpCone{Float64, Float64}(U2, [P02, PWts2...]) - wsos_cone3 = HYP.WSOSPolyInterpCone{Float64, Float64}(U3, [P03, PWts3...]) + wsos_cone1 = HYP.WSOSPolyInterpCone(U1, [P01, PWts1...]) + wsos_cone2 = HYP.WSOSPolyInterpCone(U2, [P02, PWts2...]) + wsos_cone3 = HYP.WSOSPolyInterpCone(U3, [P03, PWts3...]) JuMP.@objective(model, Min, sum(quad_weights[u] * w(pts1[u, :]) for u in 1:U1)) JuMP.@constraints(model, begin diff --git a/examples/semidefinitepoly/JuMP.jl b/examples/semidefinitepoly/JuMP.jl index 3c5681b53..af04d0727 100644 --- a/examples/semidefinitepoly/JuMP.jl +++ b/examples/semidefinitepoly/JuMP.jl @@ -29,7 +29,7 @@ function semidefinitepolyJuMP(x::Vector{DP.PolyVar{true}}, H::Matrix; use_wsos:: n = DP.nvariables(x) dom = MU.FreeDomain(n) (U, pts, P0, _, _) = MU.interpolate(dom, halfdeg, sample_factor = 20, sample = true) - mat_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(matdim, U, [P0], use_dual) + mat_wsos_cone = HYP.WSOSPolyInterpMatCone(matdim, U, [P0], use_dual) if use_dual JuMP.@variable(model, z[i in 1:n, 1:i, 1:U]) diff --git a/examples/shapeconregr/JuMP.jl b/examples/shapeconregr/JuMP.jl index 632f1f457..ce05e6b38 100644 --- a/examples/shapeconregr/JuMP.jl +++ b/examples/shapeconregr/JuMP.jl @@ -72,7 +72,7 @@ function shapeconregrJuMP( if !all(iszero, mono_profile) gradient_halfdeg = div(deg, 2) (mono_U, mono_points, mono_P0, mono_PWts, _) = MU.interpolate(mono_dom, gradient_halfdeg, sample = sample, sample_factor = 50) - mono_wsos_cone = HYP.WSOSPolyInterpCone{Float64, Float64}(mono_U, [mono_P0, mono_PWts...]) + mono_wsos_cone = HYP.WSOSPolyInterpCone(mono_U, [mono_P0, mono_PWts...]) for j in 1:n if !iszero(mono_profile[j]) gradient = DP.differentiate(regressor, DP.variables(regressor)[j]) @@ -85,7 +85,7 @@ function shapeconregrJuMP( if !iszero(conv_profile) hessian_halfdeg = div(deg - 1, 2) (conv_U, conv_points, conv_P0, conv_PWts, _) = MU.interpolate(conv_dom, hessian_halfdeg, sample = sample, sample_factor = 50) - conv_wsos_cone = HYP.WSOSPolyInterpMatCone{Float64}(n, conv_U, [conv_P0, conv_PWts...]) + conv_wsos_cone = HYP.WSOSPolyInterpMatCone(n, conv_U, [conv_P0, conv_PWts...]) hessian = DP.differentiate(regressor, DP.variables(regressor), 2) JuMP.@constraint(model, [conv_profile * hessian[i, j](conv_points[u, :]) * (i == j ? 1.0 : rt2) for i in 1:n for j in 1:i for u in 1:conv_U] in conv_wsos_cone) end diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 38638828f..e85912d03 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -30,7 +30,7 @@ include("hypoperlogdet.jl") include("epipersumexp.jl") include("wsospolyinterp.jl") include("wsospolyinterpmat.jl") -# include("wsospolyinterpsoc.jl") +include("wsospolyinterpsoc.jl") use_dual(cone::Cone) = cone.use_dual load_point(cone::Cone{T}, point::AbstractVector{T}) where {T <: HypReal} = (cone.point = point) diff --git a/src/Cones/wsospolyinterpsoc.jl b/src/Cones/wsospolyinterpsoc.jl index 0e4cdb1d1..01d36ef52 100644 --- a/src/Cones/wsospolyinterpsoc.jl +++ b/src/Cones/wsospolyinterpsoc.jl @@ -11,31 +11,31 @@ mutable struct WSOSPolyInterpSOC{T <: HypReal} <: Cone{T} dim::Int R::Int U::Int - ipwt::Vector{Matrix{Float64}} - - point::AbstractVector{Float64} - g::Vector{Float64} - H::Matrix{Float64} - gtry::Vector{Float64} - Htry::Matrix{Float64} - H2::Matrix{Float64} - Hi::Matrix{Float64} + ipwt::Vector{Matrix{T}} + + point::AbstractVector{T} + g::Vector{T} + H::Matrix{T} + gtry::Vector{T} + Htry::Matrix{T} + H2::Matrix{T} + Hi::Matrix{T} F - mat::Vector{Matrix{Float64}} - matfact::Vector{CholeskyPivoted{Float64, Matrix{Float64}}} - tmp1::Vector{Matrix{Float64}} - tmp2::Vector{Matrix{Float64}} - tmp3::Matrix{Float64} - tmp4::Vector{Matrix{Float64}} - li_lambda::Vector{Vector{Matrix{Float64}}} - PlambdaiP::Vector{Vector{Vector{Matrix{Float64}}}} - lambdafact::Vector{CholeskyPivoted{Float64, Matrix{Float64}}} - - function WSOSPolyInterpSOC(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}, is_dual::Bool) + mat::Vector{Matrix{T}} + matfact::Vector{CholeskyPivoted{T, Matrix{T}}} + tmp1::Vector{Matrix{T}} + tmp2::Vector{Matrix{T}} + tmp3::Matrix{T} + tmp4::Vector{Matrix{T}} + li_lambda::Vector{Vector{Matrix{T}}} + PlambdaiP::Vector{Vector{Vector{Matrix{T}}}} + lambdafact::Vector{CholeskyPivoted{T, Matrix{T}}} + + function WSOSPolyInterpSOC{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}, is_dual::Bool) where {T <: HypReal} for ipwtj in ipwt @assert size(ipwtj, 1) == U end - cone = new() + cone = new{T}() cone.use_dual = !is_dual # using dual barrier dim = U * R cone.dim = dim @@ -46,49 +46,50 @@ mutable struct WSOSPolyInterpSOC{T <: HypReal} <: Cone{T} end end -WSOSPolyInterpSOC(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpSOC(R, U, ipwt, false) +WSOSPolyInterpSOC{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}) where {T <: HypReal} = WSOSPolyInterpSOC{T}(R, U, ipwt, false) -function setup_data(cone::WSOSPolyInterpSOC) +function setup_data(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} dim = cone.dim U = cone.U R = cone.R ipwt = cone.ipwt - cone.g = similar(ipwt[1], dim) - cone.H = similar(ipwt[1], dim, dim) - cone.gtry = similar(ipwt[1], dim) - cone.Htry = similar(ipwt[1], dim, dim) + cone.g = Vector{T}(undef, dim) + cone.H = Matrix{T}(undef, dim, dim) + cone.gtry = similar(cone.g, dim) + cone.Htry = similar(cone.g, dim, dim) cone.H2 = similar(cone.H) cone.Hi = similar(cone.H) - cone.mat = [similar(ipwt[1], size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] - cone.matfact = Vector{CholeskyPivoted{Float64, Matrix{Float64}}}(undef, length(ipwt)) - cone.tmp1 = [similar(ipwt[1], size(ipwtj, 2), U) for ipwtj in ipwt] - cone.tmp2 = [similar(ipwt[1], size(ipwtj, 2), U) for ipwtj in ipwt] - cone.tmp3 = similar(ipwt[1], U, U) - cone.tmp4 = [similar(ipwt[1], size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] - cone.li_lambda = [Vector{Matrix{Float64}}(undef, R - 1) for ipwtj in ipwt] + cone.mat = [similar(cone.g, size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] + cone.matfact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) + cone.tmp1 = [similar(cone.g, size(ipwtj, 2), U) for ipwtj in ipwt] + cone.tmp2 = [similar(cone.g, size(ipwtj, 2), U) for ipwtj in ipwt] + cone.tmp3 = similar(cone.g, U, U) + cone.tmp4 = [similar(cone.g, size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] + cone.li_lambda = [Vector{Matrix{T}}(undef, R - 1) for ipwtj in ipwt] for j in eachindex(ipwt), r in 1:(R - 1) - cone.li_lambda[j][r] = similar(ipwt[1], size(ipwt[j], 2), size(ipwt[j], 2)) + cone.li_lambda[j][r] = similar(cone.g, size(ipwt[j], 2), size(ipwt[j], 2)) end - cone.PlambdaiP = [Vector{Vector{Matrix{Float64}}}(undef, R) for ipwtj in ipwt] + cone.PlambdaiP = [Vector{Vector{Matrix{T}}}(undef, R) for ipwtj in ipwt] for j in eachindex(ipwt), r1 in 1:R - cone.PlambdaiP[j][r1] = Vector{Matrix{Float64}}(undef, r1) + cone.PlambdaiP[j][r1] = Vector{Matrix{T}}(undef, r1) for r2 in 1:r1 - cone.PlambdaiP[j][r1][r2] = similar(ipwt[1], U, U) + cone.PlambdaiP[j][r1][r2] = similar(cone.g, U, U) end end - cone.lambdafact = Vector{CholeskyPivoted{Float64, Matrix{Float64}}}(undef, length(ipwt)) + cone.lambdafact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) return end get_nu(cone::WSOSPolyInterpSOC) = sum(size(ipwtj, 2) for ipwtj in cone.ipwt) -function set_initial_point(arr::AbstractVector{Float64}, cone::WSOSPolyInterpSOC) - arr .= 0.0 - arr[1:cone.U] .= 1.0 +function set_initial_point(arr::AbstractVector{T}, cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} + arr .= zero(T) + arr[1:cone.U] .= one(T) return arr end -function check_in_cone(cone::WSOSPolyInterpSOC) +# TODO cleanup experimental code +function check_in_cone(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} # @timeit "build mat" begin for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] @@ -134,8 +135,8 @@ function check_in_cone(cone::WSOSPolyInterpSOC) # end # @timeit "grad hess" begin - cone.g .= 0.0 - cone.H .= 0.0 + cone.g .= zero(T) + cone.H .= zero(T) for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] tmp1 = cone.tmp1[j] @@ -167,14 +168,14 @@ function check_in_cone(cone::WSOSPolyInterpSOC) mul!(tmp2, li_lambda[r - 1]', ipwtj') tmp2 .= view(tmp2, matfact[j].p, :) ldiv!(matfact[j].L, tmp2) - BLAS.syrk!('U', 'T', 1.0, tmp2, 0.0, PlambdaiP[r][r]) + BLAS.syrk!('U', 'T', one(T), tmp2, zero(T), PlambdaiP[r][r]) PlambdaiP[r][r] .+= Symmetric(ipwtj * tmp1, :U) end # part of gradient/hessian when p=1 tmp2 .= view(ipwtj', cone.lambdafact[j].p, :) ldiv!(cone.lambdafact[j].L, tmp2) - BLAS.syrk!('U', 'T', 1.0, tmp2, 0.0, tmp3) + BLAS.syrk!('U', 'T', one(T), tmp2, zero(T), tmp3) for i in 1:cone.U cone.g[i] += tmp3[i, i] * (cone.R - 1) @@ -219,11 +220,5 @@ function check_in_cone(cone::WSOSPolyInterpSOC) end # j # end - # if !isapprox(Symmetric(cone.H, :U) * cone.point, -cone.g) - # @show Symmetric(cone.H, :U) * cone.point, -cone.g - # @show cone.point - # error() - # end - return factorize_hess(cone) end diff --git a/src/MathOptInterface/cones.jl b/src/MathOptInterface/cones.jl index 96e421e4a..70c6c738b 100644 --- a/src/MathOptInterface/cones.jl +++ b/src/MathOptInterface/cones.jl @@ -7,32 +7,32 @@ and functions for converting between Hypatia and MOI cone definitions export WSOSPolyInterpCone -struct WSOSPolyInterpCone{T <: HypReal, R <: HypRealOrComplex{T}} <: MOI.AbstractVectorSet +struct WSOSPolyInterpCone <: MOI.AbstractVectorSet # real case only dimension::Int - Ps::Vector{Matrix{R}} + Ps::Vector{Matrix{Float64}} is_dual::Bool end -WSOSPolyInterpCone{T, R}(dimension::Int, Ps::Vector{Matrix{R}}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = WSOSPolyInterpCone{T, R}(dimension, Ps, false) +WSOSPolyInterpCone(dimension::Int, Ps::Vector{Matrix{Float64}}) = WSOSPolyInterpCone(dimension, Ps, false) -export WSOSPolyInterpMatCone +export WSOSPolyInterpMatCone # TODO rename -struct WSOSPolyInterpMatCone{T <: HypReal} <: MOI.AbstractVectorSet +struct WSOSPolyInterpMatCone <: MOI.AbstractVectorSet R::Int U::Int ipwt::Vector{Matrix{Float64}} is_dual::Bool end -WSOSPolyInterpMatCone{T}(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) where {T <: HypReal} = WSOSPolyInterpMatCone{T}(R, U, ipwt, false) - -# export WSOSPolyInterpSOCCone # TODO rename, terrible name -# -# struct WSOSPolyInterpSOCCone <: MOI.AbstractVectorSet -# R::Int -# U::Int -# ipwt::Vector{Matrix{Float64}} -# is_dual::Bool -# end -# WSOSPolyInterpSOCCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpSOCCone(R, U, ipwt, false) +WSOSPolyInterpMatCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpMatCone(R, U, ipwt, false) + +export WSOSPolyInterpSOCCone # TODO rename + +struct WSOSPolyInterpSOCCone <: MOI.AbstractVectorSet + R::Int + U::Int + ipwt::Vector{Matrix{Float64}} + is_dual::Bool +end +WSOSPolyInterpSOCCone(R::Int, U::Int, ipwt::Vector{Matrix{Float64}}) = WSOSPolyInterpSOCCone(R, U, ipwt, false) MOIOtherCones = ( MOI.SecondOrderCone, @@ -42,9 +42,9 @@ MOIOtherCones = ( MOI.GeometricMeanCone, MOI.PositiveSemidefiniteConeTriangle, MOI.LogDetConeTriangle, - WSOSPolyInterpCone{<:HypReal, <:HypRealOrComplex}, - WSOSPolyInterpMatCone{<:HypReal}, - # WSOSPolyInterpSOCCone, + WSOSPolyInterpCone, + WSOSPolyInterpMatCone, + WSOSPolyInterpSOCCone, ) # MOI cones for which no transformation is needed @@ -53,9 +53,9 @@ cone_from_moi(s::MOI.RotatedSecondOrderCone) = Cones.EpiPerSquare{Float64}(MOI.d cone_from_moi(s::MOI.ExponentialCone) = Cones.HypoPerLog{Float64}() cone_from_moi(s::MOI.GeometricMeanCone) = (l = MOI.dimension(s) - 1; Cones.HypoGeomean{Float64}(fill(inv(l), l))) cone_from_moi(s::MOI.PowerCone{Float64}) = Cones.EpiPerPower{Float64}(inv(s.exponent)) -cone_from_moi(s::WSOSPolyInterpCone{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = Cones.WSOSPolyInterp{T, R}(s.dimension, s.Ps, s.is_dual) -cone_from_moi(s::WSOSPolyInterpMatCone{T}) where {T <: HypReal} = Cones.WSOSPolyInterpMat{T}(s.R, s.U, s.ipwt, s.is_dual) -# cone_from_moi(s::WSOSPolyInterpSOCCone) = Cones.WSOSPolyInterpSOC(s.R, s.U, s.ipwt, s.is_dual) +cone_from_moi(s::WSOSPolyInterpCone) = Cones.WSOSPolyInterp{Float64, Float64}(s.dimension, s.Ps, s.is_dual) +cone_from_moi(s::WSOSPolyInterpMatCone) = Cones.WSOSPolyInterpMat{Float64}(s.R, s.U, s.ipwt, s.is_dual) +cone_from_moi(s::WSOSPolyInterpSOCCone) = Cones.WSOSPolyInterpSOC{Float64}(s.R, s.U, s.ipwt, s.is_dual) cone_from_moi(s::MOI.AbstractVectorSet) = error("MOI set $s is not recognized") function build_var_cone(fi::MOI.VectorOfVariables, si::MOI.AbstractVectorSet, dim::Int, q::Int) diff --git a/test/barriers.jl b/test/barriers.jl index dd08ecc4f..917034351 100644 --- a/test/barriers.jl +++ b/test/barriers.jl @@ -103,33 +103,33 @@ function test_semidefinite_barrier(T::Type{<:HypReal}) return end -# # TODO also test complex -# function test_wsospolyinterp_barrier(T::Type{<:HypReal}) -# Random.seed!(1) -# for n in 1:3, halfdeg in 1:3 -# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) -# cone = CO.WSOSPolyInterp{T, Complex{T}}(U, [P0], true) -# pass_through_cone(cone) -# end -# return -# end -# -# function test_wsospolyinterpmat_barrier(T::Type{<:HypReal}) -# Random.seed!(1) -# for n in 1:3, halfdeg in 1:3, R in 1:3 -# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) -# cone = CO.WSOSPolyInterpMat{T}(R, U, [P0], true) -# pass_through_cone(cone) -# end -# return -# end -# -# function test_wsospolyinterpsoc_barrier(T::Type{<:HypReal}) -# Random.seed!(1) -# for n in 1:2, halfdeg in 1:2, R in 3:3 -# (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) -# cone = CO.WSOSPolyInterpSOC{T}(R, U, [P0], true) -# pass_through_cone(cone) -# end -# return -# end +function test_wsospolyinterp_barrier(T::Type{<:HypReal}) + Random.seed!(1) + for n in 1:3, halfdeg in 1:3 + (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + cone = CO.WSOSPolyInterp{T, T}(U, [P0], true) + pass_through_cone(cone) + end + # TODO also test complex case CO.WSOSPolyInterp{T, Complex{T}} - need complex MU interp functions first + return +end + +function test_wsospolyinterpmat_barrier(T::Type{<:HypReal}) + Random.seed!(1) + for n in 1:3, halfdeg in 1:3, R in 1:3 + (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + cone = CO.WSOSPolyInterpMat{T}(R, U, [P0], true) + pass_through_cone(cone) + end + return +end + +function test_wsospolyinterpsoc_barrier(T::Type{<:HypReal}) + Random.seed!(1) + for n in 1:2, halfdeg in 1:2, R in 3:3 + (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + cone = CO.WSOSPolyInterpSOC{T}(R, U, [P0], true) + pass_through_cone(cone) + end + return +end diff --git a/test/runtests.jl b/test/runtests.jl index 320782f2c..ba556f9c8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,10 +12,10 @@ const MO = HYP.Models const SO = HYP.Solvers const MU = HYP.ModelUtilities -# include(joinpath(@__DIR__, "interpolation.jl")) -# include(joinpath(@__DIR__, "barriers.jl")) -# include(joinpath(@__DIR__, "native.jl")) -# include(joinpath(@__DIR__, "MathOptInterface.jl")) +include(joinpath(@__DIR__, "interpolation.jl")) +include(joinpath(@__DIR__, "barriers.jl")) +include(joinpath(@__DIR__, "native.jl")) +include(joinpath(@__DIR__, "MathOptInterface.jl")) examples_dir = joinpath(@__DIR__, "../examples") include(joinpath(examples_dir, "envelope/native.jl")) @@ -34,137 +34,136 @@ include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) - -# @info("starting Hypatia tests") +@info("starting Hypatia tests") @testset "Hypatia tests" begin -# @info("starting interpolation tests") -# @testset "interpolation tests" begin -# fekete_sample() -# test_recover_lagrange_polys() -# test_recover_cheb_polys() -# end +@info("starting interpolation tests") +@testset "interpolation tests" begin + fekete_sample() + test_recover_lagrange_polys() + test_recover_cheb_polys() +end -# real_types = [ -# Float64, -# Float32, -# BigFloat, -# ] +real_types = [ + Float64, + Float32, + BigFloat, + ] -# @info("starting barrier tests") -# barrier_testfuns = [ -# test_epinormeucl_barrier, -# test_epinorinf_barrier, -# test_epinormspectral_barrier, -# test_epiperpower_barrier, -# test_epipersquare_barrier, -# test_epipersumexp_barrier, -# test_hypogeomean_barrier, -# test_hypoperlog_barrier, -# test_hypoperlogdet_barrier, -# test_semidefinite_barrier, -# # test_wsospolyinterp_barrier, -# # test_wsospolyinterpmat_barrier, -# # test_wsospolyinterpsoc_barrier, -# ] -# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types -# t(T) -# end +@info("starting barrier tests") +barrier_testfuns = [ + test_epinormeucl_barrier, + test_epinorinf_barrier, + test_epinormspectral_barrier, + test_epiperpower_barrier, + test_epipersquare_barrier, + test_epipersumexp_barrier, + test_hypogeomean_barrier, + test_hypoperlog_barrier, + test_hypoperlogdet_barrier, + test_semidefinite_barrier, + test_wsospolyinterp_barrier, + test_wsospolyinterpmat_barrier, + test_wsospolyinterpsoc_barrier, + ] +@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types + t(T) +end -# @info("starting native interface tests") -# verbose = false -# system_solvers = [ -# SO.QRCholCombinedHSDSystemSolver, -# SO.SymIndefCombinedHSDSystemSolver, -# SO.NaiveElimCombinedHSDSystemSolver, -# SO.NaiveCombinedHSDSystemSolver, -# ] -# testfuns_singular = [ -# dimension1, -# consistent1, -# inconsistent1, -# inconsistent2, -# ] -# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types -# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -# end -# linear_models = [ -# MO.PreprocessedLinearModel, -# MO.RawLinearModel, -# ] -# testfuns_nonsingular = [ -# orthant1, -# orthant2, -# orthant3, -# orthant4, -# epinorminf1, -# epinorminf2, -# epinorminf3, -# epinorminf4, -# epinorminf5, -# epinorminf6, -# epinormeucl1, -# epinormeucl2, -# epipersquare1, -# epipersquare2, -# epipersquare3, -# semidefinite1, -# semidefinite2, -# semidefinite3, -# semidefinitecomplex1, -# hypoperlog1, -# hypoperlog2, -# hypoperlog3, -# hypoperlog4, -# epiperpower1, -# epiperpower2, -# epiperpower3, -# hypogeomean1, -# hypogeomean2, -# hypogeomean3, -# hypogeomean4, -# epinormspectral1, -# hypoperlogdet1, -# hypoperlogdet2, -# hypoperlogdet3, -# epipersumexp1, -# epipersumexp2, -# ] -# @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types -# if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel -# continue # QRChol linear system solver needs preprocessed model -# end -# t(s{T}, m{T}, verbose) -# end +@info("starting native interface tests") +verbose = false +system_solvers = [ + SO.QRCholCombinedHSDSystemSolver, + SO.SymIndefCombinedHSDSystemSolver, + SO.NaiveElimCombinedHSDSystemSolver, + SO.NaiveCombinedHSDSystemSolver, + ] +testfuns_singular = [ + dimension1, + consistent1, + inconsistent1, + inconsistent2, + ] +@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types + t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +end +linear_models = [ + MO.PreprocessedLinearModel, + MO.RawLinearModel, + ] +testfuns_nonsingular = [ + orthant1, + orthant2, + orthant3, + orthant4, + epinorminf1, + epinorminf2, + epinorminf3, + epinorminf4, + epinorminf5, + epinorminf6, + epinormeucl1, + epinormeucl2, + epipersquare1, + epipersquare2, + epipersquare3, + semidefinite1, + semidefinite2, + semidefinite3, + semidefinitecomplex1, + hypoperlog1, + hypoperlog2, + hypoperlog3, + hypoperlog4, + epiperpower1, + epiperpower2, + epiperpower3, + hypogeomean1, + hypogeomean2, + hypogeomean3, + hypogeomean4, + epinormspectral1, + hypoperlogdet1, + hypoperlogdet2, + hypoperlogdet3, + epipersumexp1, + epipersumexp2, + ] +@testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types + if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel + continue # QRChol linear system solver needs preprocessed model + end + t(s{T}, m{T}, verbose) +end -# @info("starting MathOptInterface tests") -# verbose = false -# system_solvers = [ -# SO.NaiveCombinedHSDSystemSolver, -# SO.QRCholCombinedHSDSystemSolver, -# ] -# linear_models = [ -# MO.PreprocessedLinearModel, # MOI tests require preprocessing -# ] -# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models -# test_moi(d, s{Float64}, m{Float64}, verbose) -# end -# -# @info("starting native examples tests") -# native_options = ( -# verbose = true, -# max_iters = 150, -# time_limit = 6e2, # 1 minute -# ) -# @testset "native examples" begin -# @testset "envelope" begin test_envelope(; native_options..., -# ) end -# @testset "linearopt" begin test_linearopt(; native_options..., -# ) end -# @testset "polymin" begin test_polymin(; native_options..., -# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, -# ) end -# end +@info("starting MathOptInterface tests") +verbose = false +system_solvers = [ + SO.NaiveCombinedHSDSystemSolver, + SO.QRCholCombinedHSDSystemSolver, + ] +linear_models = [ + MO.PreprocessedLinearModel, # MOI tests require preprocessing + ] +@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models + test_moi(d, s{Float64}, m{Float64}, verbose) +end + +@info("starting native examples tests") +native_options = ( + verbose = true, + max_iters = 150, + time_limit = 6e2, # 1 minute + ) +@testset "native examples" begin + @testset "envelope" begin test_envelope(; native_options..., + ) end + @testset "linearopt" begin test_linearopt(; native_options..., + ) end + @testset "polymin" begin test_polymin(; native_options..., + tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + ) end +end @info("starting JuMP examples tests") JuMP_options = ( @@ -174,37 +173,37 @@ JuMP_options = ( time_limit = 6e2, # 1 minute ) @testset "JuMP examples" begin - # @testset "contraction" begin test_contractionJuMP(; JuMP_options..., - # tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, - # ) end - # @testset "densityest" begin test_densityestJuMP(; JuMP_options..., - # tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, - # ) end + @testset "contraction" begin test_contractionJuMP(; JuMP_options..., + tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, + ) end + @testset "densityest" begin test_densityestJuMP(; JuMP_options..., + tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, + ) end @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., ) end - # @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., - # ) end - # @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., - # tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, - # ) end + @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., + ) end + @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., + tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, + ) end @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., ) end @testset "polymin" begin test_polyminJuMP(; JuMP_options..., tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, ) end - # @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., - # ) end + @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., + ) end @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, ) end - # @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., - # ) end - # @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., - # tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, - # ) end - # @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., - # tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, - # ) end + @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., + ) end + @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., + tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, + ) end + @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., + tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, + ) end end end From 3ec2d3ef7caacc645125f7ca72b96370470630b5 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 30 May 2019 21:17:25 -0400 Subject: [PATCH 15/20] fix wsos barrier tests --- test/barriers.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/barriers.jl b/test/barriers.jl index 917034351..a4eb9424d 100644 --- a/test/barriers.jl +++ b/test/barriers.jl @@ -107,6 +107,7 @@ function test_wsospolyinterp_barrier(T::Type{<:HypReal}) Random.seed!(1) for n in 1:3, halfdeg in 1:3 (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + P0 = convert(Matrix{T}, P0) cone = CO.WSOSPolyInterp{T, T}(U, [P0], true) pass_through_cone(cone) end @@ -118,6 +119,7 @@ function test_wsospolyinterpmat_barrier(T::Type{<:HypReal}) Random.seed!(1) for n in 1:3, halfdeg in 1:3, R in 1:3 (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + P0 = convert(Matrix{T}, P0) cone = CO.WSOSPolyInterpMat{T}(R, U, [P0], true) pass_through_cone(cone) end @@ -128,6 +130,7 @@ function test_wsospolyinterpsoc_barrier(T::Type{<:HypReal}) Random.seed!(1) for n in 1:2, halfdeg in 1:2, R in 3:3 (U, _, P0, _, _) = MU.interpolate(MU.FreeDomain(n), halfdeg, sample = false) + P0 = convert(Matrix{T}, P0) cone = CO.WSOSPolyInterpSOC{T}(R, U, [P0], true) pass_through_cone(cone) end From 90a33ffffacb36816d6d29e16db68831e43d371c Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 31 May 2019 01:37:47 -0400 Subject: [PATCH 16/20] add dense matrix factorization fallback in initial point finding with generic reals --- src/Cones/Cones.jl | 4 +- src/Cones/wsospolyinterp.jl | 6 +- src/Models/linear.jl | 65 ++++++++---- test/runtests.jl | 202 ++++++++++++++++++------------------ 4 files changed, 150 insertions(+), 127 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index e85912d03..f12cd38f7 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -7,7 +7,7 @@ functions and caches for cones module Cones using LinearAlgebra -import LinearAlgebra.BlasReal +import LinearAlgebra.BlasFloat using ForwardDiff using DiffResults import Hypatia.HypReal @@ -36,7 +36,7 @@ use_dual(cone::Cone) = cone.use_dual load_point(cone::Cone{T}, point::AbstractVector{T}) where {T <: HypReal} = (cone.point = point) dimension(cone::Cone) = cone.dim -function factorize_hess(cone::Cone{T}) where {T <: BlasReal} +function factorize_hess(cone::Cone{T}) where {T <: BlasFloat} @. cone.H2 = cone.H # cone.F = bunchkaufman!(Symmetric(cone.H2, :U), true, check = false) # return issuccess(cone.F) diff --git a/src/Cones/wsospolyinterp.jl b/src/Cones/wsospolyinterp.jl index 1156a94c7..ef0f163a9 100644 --- a/src/Cones/wsospolyinterp.jl +++ b/src/Cones/wsospolyinterp.jl @@ -62,9 +62,9 @@ get_nu(cone::WSOSPolyInterp) = sum(size(Pk, 2) for Pk in cone.Ps) set_initial_point(arr::AbstractVector{T}, cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = (@. arr = one(T); arr) -# TODO need a generic method for non-BlasReal -_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: LinearAlgebra.BlasReal} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) -_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: LinearAlgebra.BlasReal} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) +# TODO need a generic method for non-BlasFloat +_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: LinearAlgebra.BlasFloat} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) +_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: LinearAlgebra.BlasFloat} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) function check_in_cone(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} Ps = cone.Ps diff --git a/src/Models/linear.jl b/src/Models/linear.jl index 5d148db9f..62f7eb489 100644 --- a/src/Models/linear.jl +++ b/src/Models/linear.jl @@ -44,6 +44,8 @@ function set_initial_cone_point(point, cones) return point end +const sparse_QR_reals = Float64 + mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} n::Int p::Int @@ -66,11 +68,9 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} G::AbstractMatrix, h::Vector, cones::Vector{<:Cones.Cone}, - cone_idxs::Vector{UnitRange{Int}}, + cone_idxs::Vector{UnitRange{Int}}; + use_dense_fallback::Bool = true, ) where {T <: HypReal} - if (issparse(A) || issparse(G)) && T != Float64 - error("can only use Float64 types with sparse A or G (generic QR not implemented)") - end c = convert(Vector{T}, c) A = convert(AbstractMatrix{T}, A) b = convert(Vector{T}, b) @@ -97,16 +97,35 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} # solve for y as least squares solution to A'y = -c - G'z if !iszero(model.p) - point.y = (issparse(A) ? sparse(A') : A') \ (-c - G' * point.z) + Ap = A' + if issparse(Ap) && !(T <: sparse_QR_reals) + # TODO alternative fallback is to convert sparse{T} to sparse{Float64} and do the sparse LU + if use_dense_fallback + @warn("using dense factorization of A' in initial point finding because sparse factorization for number type $T is not supported by SparseArrays") + F = qr!(Matrix(Ap)) + else + error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot find an initial point") + end + else + F = qr(Ap) + end + point.y = F \ (-c - G' * point.z) end # solve for x as least squares solution to Ax = b, Gx = h - s if !iszero(model.n) AG = vcat(A, G) - if issparse(AG) && T != Float64 - error("can only use Float64 types with sparse [A; G] (generic QR not implemented)") + if issparse(AG) && !(T <: sparse_QR_reals) + # TODO alternative fallback is to convert sparse{T} to sparse{Float64} and do the sparse LU + if use_dense_fallback + @warn("using dense factorization of [A; G] in initial point finding because sparse factorization for number type $T is not supported by SparseArrays") + AG = Matrix(AG) + else + error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot find an initial point") + end end - point.x = AG \ vcat(b, h - point.s) + F = issparse(AG) ? qr(AG) : qr!(AG) + point.x = F \ vcat(b, h - point.s) end model.initial_point = point @@ -154,10 +173,8 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} cones::Vector{<:Cones.Cone}, cone_idxs::Vector{UnitRange{Int}}; tol_QR::Real = max(1e-14, 1e3 * eps(T)), + use_dense_fallback::Bool = true, ) where {T <: HypReal} - if (issparse(A) || issparse(G)) && T != Float64 - error("can only use Float64 types with sparse A or G (generic QR not implemented)") - end c = convert(Vector{T}, c) A = convert(AbstractMatrix{T}, A) b = convert(Vector{T}, b) @@ -187,14 +204,15 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} if !iszero(n) # get pivoted QR # TODO when Julia has a unified QR interface, replace this AG = vcat(A, G) - if issparse(AG) - if T != Float64 - error("can only use Float64 types with sparse [A; G] (generic QR not implemented)") + if issparse(AG) && !(T <: sparse_QR_reals) + if use_dense_fallback + @warn("using dense factorization of [A; G] in preprocessing and initial point finding because sparse factorization for number type $T is not supported by SparseArrays") + AG = Matrix(AG) + else + error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot preprocess and find an initial point") end - AG_fact = qr(AG, tol = tol_QR) - else - AG_fact = qr(AG, Val(true)) end + AG_fact = issparse(AG) ? qr(AG, tol = tol_QR) : qr(AG, Val(true)) AG_R = AG_fact.R # TODO could replace this with rank(Ap_fact) when available for both dense and sparse @@ -240,11 +258,16 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} # solve for y as least squares solution to A'y = -c - G'z if !iszero(p) - # get pivoted QR # TODO when Julia has a unified QR interface, replace this - if issparse(A) - Ap_fact = qr(sparse(A'), tol = tol_QR) + Ap = A' + if issparse(Ap) && !(T <: sparse_QR_reals) + if use_dense_fallback + @warn("using dense factorization of A' in preprocessing and initial point finding because sparse factorization for number type $T is not supported by SparseArrays") + Ap_fact = qr!(Matrix(Ap), Val(true)) + else + error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot preprocess and find an initial point") + end else - Ap_fact = qr(A', Val(true)) + Ap_fact = issparse(A) ? qr(sparse(Ap), tol = tol_QR) : qr(Ap, Val(true)) end Ap_R = Ap_fact.R diff --git a/test/runtests.jl b/test/runtests.jl index ba556f9c8..b3f158fcb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,49 +34,49 @@ include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) -@info("starting Hypatia tests") -@testset "Hypatia tests" begin - -@info("starting interpolation tests") -@testset "interpolation tests" begin - fekete_sample() - test_recover_lagrange_polys() - test_recover_cheb_polys() -end - real_types = [ Float64, Float32, BigFloat, ] -@info("starting barrier tests") -barrier_testfuns = [ - test_epinormeucl_barrier, - test_epinorinf_barrier, - test_epinormspectral_barrier, - test_epiperpower_barrier, - test_epipersquare_barrier, - test_epipersumexp_barrier, - test_hypogeomean_barrier, - test_hypoperlog_barrier, - test_hypoperlogdet_barrier, - test_semidefinite_barrier, - test_wsospolyinterp_barrier, - test_wsospolyinterpmat_barrier, - test_wsospolyinterpsoc_barrier, - ] -@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types - t(T) -end +@info("starting Hypatia tests") +@testset "Hypatia tests" begin + +# @info("starting interpolation tests") +# @testset "interpolation tests" begin +# fekete_sample() +# test_recover_lagrange_polys() +# test_recover_cheb_polys() +# end +# +# @info("starting barrier tests") +# barrier_testfuns = [ +# test_epinormeucl_barrier, +# test_epinorinf_barrier, +# test_epinormspectral_barrier, +# test_epiperpower_barrier, +# test_epipersquare_barrier, +# test_epipersumexp_barrier, +# test_hypogeomean_barrier, +# test_hypoperlog_barrier, +# test_hypoperlogdet_barrier, +# test_semidefinite_barrier, +# test_wsospolyinterp_barrier, +# test_wsospolyinterpmat_barrier, +# test_wsospolyinterpsoc_barrier, +# ] +# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types +# t(T) +# end @info("starting native interface tests") verbose = false system_solvers = [ - SO.QRCholCombinedHSDSystemSolver, - SO.SymIndefCombinedHSDSystemSolver, + # SO.QRCholCombinedHSDSystemSolver, + # SO.SymIndefCombinedHSDSystemSolver, SO.NaiveElimCombinedHSDSystemSolver, - SO.NaiveCombinedHSDSystemSolver, + # SO.NaiveCombinedHSDSystemSolver, ] testfuns_singular = [ dimension1, @@ -136,74 +136,74 @@ testfuns_nonsingular = [ t(s{T}, m{T}, verbose) end -@info("starting MathOptInterface tests") -verbose = false -system_solvers = [ - SO.NaiveCombinedHSDSystemSolver, - SO.QRCholCombinedHSDSystemSolver, - ] -linear_models = [ - MO.PreprocessedLinearModel, # MOI tests require preprocessing - ] -@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models - test_moi(d, s{Float64}, m{Float64}, verbose) -end - -@info("starting native examples tests") -native_options = ( - verbose = true, - max_iters = 150, - time_limit = 6e2, # 1 minute - ) -@testset "native examples" begin - @testset "envelope" begin test_envelope(; native_options..., - ) end - @testset "linearopt" begin test_linearopt(; native_options..., - ) end - @testset "polymin" begin test_polymin(; native_options..., - tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - ) end -end - -@info("starting JuMP examples tests") -JuMP_options = ( - verbose = true, - test_certificates = true, - max_iters = 250, - time_limit = 6e2, # 1 minute - ) -@testset "JuMP examples" begin - @testset "contraction" begin test_contractionJuMP(; JuMP_options..., - tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, - ) end - @testset "densityest" begin test_densityestJuMP(; JuMP_options..., - tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, - ) end - @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., - ) end - @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., - ) end - @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., - tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, - ) end - @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., - ) end - @testset "polymin" begin test_polyminJuMP(; JuMP_options..., - tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, - ) end - @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., - ) end - @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., - tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, - ) end - @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., - ) end - @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., - tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, - ) end - @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., - tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, - ) end -end +# @info("starting MathOptInterface tests") +# verbose = false +# system_solvers = [ +# SO.NaiveCombinedHSDSystemSolver, +# SO.QRCholCombinedHSDSystemSolver, +# ] +# linear_models = [ +# MO.PreprocessedLinearModel, # MOI tests require preprocessing +# ] +# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models +# test_moi(d, s{Float64}, m{Float64}, verbose) +# end +# +# @info("starting native examples tests") +# native_options = ( +# verbose = true, +# max_iters = 150, +# time_limit = 6e2, # 1 minute +# ) +# @testset "native examples" begin +# @testset "envelope" begin test_envelope(; native_options..., +# ) end +# @testset "linearopt" begin test_linearopt(; native_options..., +# ) end +# @testset "polymin" begin test_polymin(; native_options..., +# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, +# ) end +# end +# +# @info("starting JuMP examples tests") +# JuMP_options = ( +# verbose = true, +# test_certificates = true, +# max_iters = 250, +# time_limit = 6e2, # 1 minute +# ) +# @testset "JuMP examples" begin +# @testset "contraction" begin test_contractionJuMP(; JuMP_options..., +# tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, +# ) end +# @testset "densityest" begin test_densityestJuMP(; JuMP_options..., +# tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, +# ) end +# @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., +# ) end +# @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., +# ) end +# @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., +# tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, +# ) end +# @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., +# ) end +# @testset "polymin" begin test_polyminJuMP(; JuMP_options..., +# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, +# ) end +# @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., +# ) end +# @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., +# tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, +# ) end +# @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., +# ) end +# @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., +# tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, +# ) end +# @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., +# tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, +# ) end +# end end From 05ee3dc9674cc9a30f4f133fb77577dca6954e9f Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 31 May 2019 16:18:33 -0400 Subject: [PATCH 17/20] add new file with some generic linear algebra methods; get wsos cone working for generic reals --- src/Cones/Cones.jl | 3 + src/Cones/wsospolyinterp.jl | 18 ++-- src/Hypatia.jl | 5 +- src/linearalgebra.jl | 29 +++++ test/barriers.jl | 1 + test/runtests.jl | 204 ++++++++++++++++++------------------ 6 files changed, 143 insertions(+), 117 deletions(-) create mode 100644 src/linearalgebra.jl diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index f12cd38f7..578b06465 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -12,6 +12,9 @@ using ForwardDiff using DiffResults import Hypatia.HypReal import Hypatia.HypRealOrComplex +import Hypatia.hyp_AtA! +import Hypatia.hyp_chol! +import Hypatia.hyp_ldiv_chol_L! import Hypatia.rt2 import Hypatia.rt2i diff --git a/src/Cones/wsospolyinterp.jl b/src/Cones/wsospolyinterp.jl index ef0f163a9..9accccc62 100644 --- a/src/Cones/wsospolyinterp.jl +++ b/src/Cones/wsospolyinterp.jl @@ -27,7 +27,7 @@ mutable struct WSOSPolyInterp{T <: HypReal, R <: HypRealOrComplex{T}} <: Cone{T} tmpUL::Vector{Matrix{R}} tmpLU::Vector{Matrix{R}} tmpUU::Matrix{R} - ΛFs::Vector{CholeskyPivoted{R, Matrix{R}}} + ΛFs::Vector function WSOSPolyInterp{T, R}(dim::Int, Ps::Vector{Matrix{R}}, is_dual::Bool) where {R <: HypRealOrComplex{T}} where {T <: HypReal} for k in eachindex(Ps) @@ -54,7 +54,7 @@ function setup_data(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} cone.tmpUL = [Matrix{R}(undef, dim, size(Pk, 2)) for Pk in Ps] cone.tmpLU = [Matrix{R}(undef, size(Pk, 2), dim) for Pk in Ps] cone.tmpUU = Matrix{R}(undef, dim, dim) - cone.ΛFs = Vector{CholeskyPivoted{R, Matrix{R}}}(undef, length(Ps)) + cone.ΛFs = Vector{Any}(undef, length(Ps)) return end @@ -62,10 +62,6 @@ get_nu(cone::WSOSPolyInterp) = sum(size(Pk, 2) for Pk in cone.Ps) set_initial_point(arr::AbstractVector{T}, cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} = (@. arr = one(T); arr) -# TODO need a generic method for non-BlasFloat -_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: LinearAlgebra.BlasFloat} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) -_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: LinearAlgebra.BlasFloat} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) - function check_in_cone(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} Ps = cone.Ps LLs = cone.tmpLL @@ -86,7 +82,7 @@ function check_in_cone(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{ mul!(LLk, Pk', ULk) # pivoted cholesky and triangular solve method - ΛFk = cholesky!(Hermitian(LLk, :L), Val(true), check = false) # TODO doesn't work for generic reals + ΛFk = hyp_chol!(Hermitian(LLk, :L)) if !isposdef(ΛFk) return false end @@ -99,11 +95,9 @@ function check_in_cone(cone::WSOSPolyInterp{T, R}) where {R <: HypRealOrComplex{ @. H = zero(T) for k in eachindex(Ps) # TODO can be done in parallel, but need multiple tmp3s - LUk = LUs[k] - ΛFk = ΛFs[k] - LUk .= view(Ps[k]', ΛFk.p, :) - ldiv!(LowerTriangular(ΛFk.L), LUk) - _AtA!(UU, LUk) + # TODO may be faster (but less numerically stable) with explicit inverse here + LUk = hyp_ldiv_chol_L!(LUs[k], ΛFs[k], Ps[k]') + hyp_AtA!(UU, LUk) for j in eachindex(g) @inbounds g[j] -= real(UU[j, j]) diff --git a/src/Hypatia.jl b/src/Hypatia.jl index 335c160ca..cc3a6c289 100644 --- a/src/Hypatia.jl +++ b/src/Hypatia.jl @@ -10,6 +10,8 @@ const HypRealOrComplex{T <: HypReal} = Union{T, Complex{T}} const rt2 = sqrt(2) const rt2i = inv(rt2) +include("linearalgebra.jl") + # submodules include("ModelUtilities/ModelUtilities.jl") include("Cones/Cones.jl") @@ -17,9 +19,6 @@ include("Models/Models.jl") include("Solvers/Solvers.jl") # MathOptInterface -using Test -using LinearAlgebra -using SparseArrays import MathOptInterface const MOI = MathOptInterface diff --git a/src/linearalgebra.jl b/src/linearalgebra.jl new file mode 100644 index 000000000..436603c24 --- /dev/null +++ b/src/linearalgebra.jl @@ -0,0 +1,29 @@ +#= +Copyright 2019, Chris Coey and contributors +=# + +using LinearAlgebra +import LinearAlgebra.BlasReal +import LinearAlgebra.BlasFloat +import LinearAlgebra.HermOrSym + + +hyp_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: BlasReal} = BLAS.syrk!('U', 'T', one(T), A, zero(T), U) +hyp_AtA!(U::Matrix{Complex{T}}, A::Matrix{Complex{T}}) where {T <: BlasReal} = BLAS.herk!('U', 'C', one(T), A, zero(T), U) +hyp_AtA!(U::Matrix{T}, A::Matrix{T}) where {T <: HypRealOrComplex{<:HypReal}} = mul!(U, A', A) + + +hyp_chol!(A::HermOrSym{T, Matrix{T}}) where {T <: BlasFloat} = cholesky!(A, Val(true), check = false) +hyp_chol!(A::HermOrSym{T, Matrix{T}}) where {T <: HypRealOrComplex{<:HypReal}} = cholesky!(A, check = false) + + +function hyp_ldiv_chol_L!(B::Matrix, F::CholeskyPivoted, A::AbstractMatrix) + copyto!(B, view(A, F.p, :)) + ldiv!(LowerTriangular(F.L), B) + return B +end +function hyp_ldiv_chol_L!(B::Matrix, F::Cholesky, A::AbstractMatrix) + copyto!(B, A) + ldiv!(LowerTriangular(F.L), B) + return B +end diff --git a/test/barriers.jl b/test/barriers.jl index a4eb9424d..eacd33929 100644 --- a/test/barriers.jl +++ b/test/barriers.jl @@ -2,6 +2,7 @@ Copyright 2018, Chris Coey, Lea Kapelevich and contributors =# +using LinearAlgebra import Random function pass_through_cone(cone::CO.Cone{T}; num_checks::Int = 2) where {T <: HypReal} diff --git a/test/runtests.jl b/test/runtests.jl index b3f158fcb..dd8b62423 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,27 +12,27 @@ const MO = HYP.Models const SO = HYP.Solvers const MU = HYP.ModelUtilities -include(joinpath(@__DIR__, "interpolation.jl")) +# include(joinpath(@__DIR__, "interpolation.jl")) include(joinpath(@__DIR__, "barriers.jl")) -include(joinpath(@__DIR__, "native.jl")) -include(joinpath(@__DIR__, "MathOptInterface.jl")) +# include(joinpath(@__DIR__, "native.jl")) +# include(joinpath(@__DIR__, "MathOptInterface.jl")) -examples_dir = joinpath(@__DIR__, "../examples") -include(joinpath(examples_dir, "envelope/native.jl")) -include(joinpath(examples_dir, "linearopt/native.jl")) -include(joinpath(examples_dir, "polymin/native.jl")) -include(joinpath(examples_dir, "contraction/JuMP.jl")) -include(joinpath(examples_dir, "densityest/JuMP.jl")) -include(joinpath(examples_dir, "envelope/JuMP.jl")) -include(joinpath(examples_dir, "expdesign/JuMP.jl")) -include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) -include(joinpath(examples_dir, "muconvexity/JuMP.jl")) -include(joinpath(examples_dir, "polymin/JuMP.jl")) -include(joinpath(examples_dir, "polynorm/JuMP.jl")) -include(joinpath(examples_dir, "regionofattr/JuMP.jl")) -include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) -include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) -include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) +# examples_dir = joinpath(@__DIR__, "../examples") +# include(joinpath(examples_dir, "envelope/native.jl")) +# include(joinpath(examples_dir, "linearopt/native.jl")) +# include(joinpath(examples_dir, "polymin/native.jl")) +# include(joinpath(examples_dir, "contraction/JuMP.jl")) +# include(joinpath(examples_dir, "densityest/JuMP.jl")) +# include(joinpath(examples_dir, "envelope/JuMP.jl")) +# include(joinpath(examples_dir, "expdesign/JuMP.jl")) +# include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) +# include(joinpath(examples_dir, "muconvexity/JuMP.jl")) +# include(joinpath(examples_dir, "polymin/JuMP.jl")) +# include(joinpath(examples_dir, "polynorm/JuMP.jl")) +# include(joinpath(examples_dir, "regionofattr/JuMP.jl")) +# include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) +# include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) +# include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) real_types = [ Float64, @@ -50,92 +50,92 @@ real_types = [ # test_recover_cheb_polys() # end # -# @info("starting barrier tests") -# barrier_testfuns = [ -# test_epinormeucl_barrier, -# test_epinorinf_barrier, -# test_epinormspectral_barrier, -# test_epiperpower_barrier, -# test_epipersquare_barrier, -# test_epipersumexp_barrier, -# test_hypogeomean_barrier, -# test_hypoperlog_barrier, -# test_hypoperlogdet_barrier, -# test_semidefinite_barrier, -# test_wsospolyinterp_barrier, -# test_wsospolyinterpmat_barrier, -# test_wsospolyinterpsoc_barrier, -# ] -# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types -# t(T) -# end - -@info("starting native interface tests") -verbose = false -system_solvers = [ - # SO.QRCholCombinedHSDSystemSolver, - # SO.SymIndefCombinedHSDSystemSolver, - SO.NaiveElimCombinedHSDSystemSolver, - # SO.NaiveCombinedHSDSystemSolver, +@info("starting barrier tests") +barrier_testfuns = [ + # test_epinormeucl_barrier, + # test_epinorinf_barrier, + # test_epinormspectral_barrier, + # test_epiperpower_barrier, + # test_epipersquare_barrier, + # test_epipersumexp_barrier, + # test_hypogeomean_barrier, + # test_hypoperlog_barrier, + # test_hypoperlogdet_barrier, + # test_semidefinite_barrier, + test_wsospolyinterp_barrier, + # test_wsospolyinterpmat_barrier, + # test_wsospolyinterpsoc_barrier, ] -testfuns_singular = [ - dimension1, - consistent1, - inconsistent1, - inconsistent2, - ] -@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types - t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -end -linear_models = [ - MO.PreprocessedLinearModel, - MO.RawLinearModel, - ] -testfuns_nonsingular = [ - orthant1, - orthant2, - orthant3, - orthant4, - epinorminf1, - epinorminf2, - epinorminf3, - epinorminf4, - epinorminf5, - epinorminf6, - epinormeucl1, - epinormeucl2, - epipersquare1, - epipersquare2, - epipersquare3, - semidefinite1, - semidefinite2, - semidefinite3, - semidefinitecomplex1, - hypoperlog1, - hypoperlog2, - hypoperlog3, - hypoperlog4, - epiperpower1, - epiperpower2, - epiperpower3, - hypogeomean1, - hypogeomean2, - hypogeomean3, - hypogeomean4, - epinormspectral1, - hypoperlogdet1, - hypoperlogdet2, - hypoperlogdet3, - epipersumexp1, - epipersumexp2, - ] -@testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types - if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel - continue # QRChol linear system solver needs preprocessed model - end - t(s{T}, m{T}, verbose) +@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types + t(T) end +# @info("starting native interface tests") +# verbose = false +# system_solvers = [ +# # SO.QRCholCombinedHSDSystemSolver, +# # SO.SymIndefCombinedHSDSystemSolver, +# SO.NaiveElimCombinedHSDSystemSolver, +# # SO.NaiveCombinedHSDSystemSolver, +# ] +# testfuns_singular = [ +# dimension1, +# consistent1, +# inconsistent1, +# inconsistent2, +# ] +# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types +# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +# end +# linear_models = [ +# MO.PreprocessedLinearModel, +# MO.RawLinearModel, +# ] +# testfuns_nonsingular = [ +# orthant1, +# orthant2, +# orthant3, +# orthant4, +# epinorminf1, +# epinorminf2, +# epinorminf3, +# epinorminf4, +# epinorminf5, +# epinorminf6, +# epinormeucl1, +# epinormeucl2, +# epipersquare1, +# epipersquare2, +# epipersquare3, +# semidefinite1, +# semidefinite2, +# semidefinite3, +# semidefinitecomplex1, +# hypoperlog1, +# hypoperlog2, +# hypoperlog3, +# hypoperlog4, +# epiperpower1, +# epiperpower2, +# epiperpower3, +# hypogeomean1, +# hypogeomean2, +# hypogeomean3, +# hypogeomean4, +# epinormspectral1, +# hypoperlogdet1, +# hypoperlogdet2, +# hypoperlogdet3, +# epipersumexp1, +# epipersumexp2, +# ] +# @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types +# if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel +# continue # QRChol linear system solver needs preprocessed model +# end +# t(s{T}, m{T}, verbose) +# end + # @info("starting MathOptInterface tests") # verbose = false # system_solvers = [ From 602bcf703a5f97eb2da31fde37c23e586dd9b668 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 31 May 2019 17:03:28 -0400 Subject: [PATCH 18/20] use generic cholesky in cones and in qrchol --- src/Cones/Cones.jl | 14 +- src/Cones/epinormspectral.jl | 8 +- src/Cones/hypoperlogdet.jl | 4 +- src/Cones/semidefinite.jl | 2 +- src/Solvers/Solvers.jl | 4 + .../combined_step/qrchol.jl | 17 +- test/runtests.jl | 159 +++++++++--------- 7 files changed, 108 insertions(+), 100 deletions(-) diff --git a/src/Cones/Cones.jl b/src/Cones/Cones.jl index 578b06465..a31425126 100644 --- a/src/Cones/Cones.jl +++ b/src/Cones/Cones.jl @@ -39,17 +39,9 @@ use_dual(cone::Cone) = cone.use_dual load_point(cone::Cone{T}, point::AbstractVector{T}) where {T <: HypReal} = (cone.point = point) dimension(cone::Cone) = cone.dim -function factorize_hess(cone::Cone{T}) where {T <: BlasFloat} - @. cone.H2 = cone.H - # cone.F = bunchkaufman!(Symmetric(cone.H2, :U), true, check = false) - # return issuccess(cone.F) - cone.F = cholesky!(Symmetric(cone.H2, :U), Val(true), check = false) - return isposdef(cone.F) -end - -function factorize_hess(cone::Cone{T}) where {T <: HypReal} - @. cone.H2 = cone.H - cone.F = cholesky!(Symmetric(cone.H2, :U), check = false) # TODO generic pivoted cholesky not implemented yet in Julia +function factorize_hess(cone::Cone) + copyto!(cone.H2, cone.H) + cone.F = hyp_chol!(Symmetric(cone.H2, :U)) return isposdef(cone.F) end diff --git a/src/Cones/epinormspectral.jl b/src/Cones/epinormspectral.jl index 289c16aa9..f4ea965bd 100644 --- a/src/Cones/epinormspectral.jl +++ b/src/Cones/epinormspectral.jl @@ -9,7 +9,7 @@ W is vectorized column-by-column (i.e. vec(W) in Julia) barrier from "Interior-Point Polynomial Algorithms in Convex Programming" by Nesterov & Nemirovskii 1994 -logdet(u*I_n - W*W'/u) - log(u) -# TODO eliminate allocations for incone check +TODO eliminate allocations =# mutable struct EpiNormSpectral{T <: HypReal} <: Cone{T} @@ -61,12 +61,14 @@ function check_in_cone(cone::EpiNormSpectral{T}) where {T <: HypReal} W[:] = view(cone.point, 2:cone.dim) n = cone.n m = cone.m - X = Symmetric(W * W') # TODO syrk + + X = Symmetric(W * W') # TODO use hyp_AtA! when prealloc'd Z = Symmetric(u * I - X / u) - F = cholesky(Z, Val(true), check = false) # TODO in place; doesn't work for generic reals + F = hyp_chol!(Z) if !isposdef(F) return false end + # TODO figure out structured form of inverse? could simplify algebra Zi = Symmetric(inv(F)) Eu = Symmetric(I + X / u / u) diff --git a/src/Cones/hypoperlogdet.jl b/src/Cones/hypoperlogdet.jl index b69e85af1..2bd0691aa 100644 --- a/src/Cones/hypoperlogdet.jl +++ b/src/Cones/hypoperlogdet.jl @@ -7,6 +7,8 @@ Copyright 2018, Chris Coey and contributors barrier (guessed, based on analogy to hypoperlog barrier) -log(v*logdet(W/v) - u) - logdet(W) - log(v) + +TODO remove allocations =# mutable struct HypoPerLogdet{T <: HypReal} <: Cone{T} @@ -61,7 +63,7 @@ function check_in_cone(cone::HypoPerLogdet{T}) where {T <: HypReal} end W = cone.mat svec_to_smat!(W, view(cone.point, 3:cone.dim)) - F = cholesky(Symmetric(W), Val(true), check = false) # TODO doesn't work for generic reals + F = hyp_chol!(Symmetric(W)) if !isposdef(F) || u >= v * (logdet(F) - cone.side * log(v)) return false end diff --git a/src/Cones/semidefinite.jl b/src/Cones/semidefinite.jl index b25ce32b9..05ceb15b9 100644 --- a/src/Cones/semidefinite.jl +++ b/src/Cones/semidefinite.jl @@ -74,7 +74,7 @@ end function check_in_cone(cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} mat = cone.mat svec_to_smat!(mat, cone.point) - F = cholesky!(Hermitian(mat), Val(true), check = false) # TODO doesn't work for generic T non-BLAS type + F = hyp_chol!(Hermitian(mat)) if !isposdef(F) return false end diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index e54152718..db7bb6e9f 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -8,12 +8,16 @@ module Solvers using Printf using LinearAlgebra +import LinearAlgebra.BlasReal using SparseArrays using Test using TimerOutputs import Hypatia.Cones import Hypatia.Models import Hypatia.HypReal +import Hypatia.hyp_AtA! +import Hypatia.hyp_chol! +import Hypatia.hyp_ldiv_chol_L! abstract type Solver{T <: HypReal} end diff --git a/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl b/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl index e558654a5..1e6a34f15 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/qrchol.jl @@ -237,7 +237,7 @@ function get_combined_directions(solver::HSDSolver{T}, system_solver::QRCholComb mul!(Q2GHGQ2, GQ2', HGQ2) if system_solver.use_sparse - F = ldlt(Symmetric(Q2GHGQ2), check = false) + F = ldlt(Symmetric(Q2GHGQ2), check = false) # TODO not implemented for generic reals if !issuccess(F) println("sparse linear system matrix factorization failed") mul!(Q2GHGQ2, GQ2', HGQ2) @@ -248,14 +248,21 @@ function get_combined_directions(solver::HSDSolver{T}, system_solver::QRCholComb end Q2div .= F \ Q2div # TODO eliminate allocs (see https://github.com/JuliaLang/julia/issues/30084) else - F = cholesky!(Symmetric(Q2GHGQ2), Val(true), check = false) # TODO prealloc cholesky auxiliary vectors using posvx; not implemented for generic reals + F = hyp_chol!(Symmetric(Q2GHGQ2)) # TODO prealloc blasreal cholesky auxiliary vectors using posvx if !isposdef(F) println("dense linear system matrix factorization failed") mul!(Q2GHGQ2, GQ2', HGQ2) Q2GHGQ2 += T(1e-4) * I - F = bunchkaufman!(Symmetric(Q2GHGQ2), true, check = false) # TODO prealloc with old sysvx code - if !issuccess(F) - error("could not fix failure of positive definiteness (mu is $mu); terminating") + if T <: BlasReal + F = bunchkaufman!(Symmetric(Q2GHGQ2), true, check = false) # TODO prealloc with old sysvx code; not implemented for generic reals + if !issuccess(F) + error("could not fix failure of positive definiteness (mu is $mu); terminating") + end + else + F = hyp_chol!(Symmetric(Q2GHGQ2)) # TODO prealloc blasreal cholesky auxiliary vectors using posvx + if !isposdef(F) + error("could not fix failure of positive definiteness (mu is $mu); terminating") + end end end ldiv!(F, Q2div) diff --git a/test/runtests.jl b/test/runtests.jl index dd8b62423..4c92f6aff 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,9 +12,9 @@ const MO = HYP.Models const SO = HYP.Solvers const MU = HYP.ModelUtilities -# include(joinpath(@__DIR__, "interpolation.jl")) +include(joinpath(@__DIR__, "interpolation.jl")) include(joinpath(@__DIR__, "barriers.jl")) -# include(joinpath(@__DIR__, "native.jl")) +include(joinpath(@__DIR__, "native.jl")) # include(joinpath(@__DIR__, "MathOptInterface.jl")) # examples_dir = joinpath(@__DIR__, "../examples") @@ -35,8 +35,8 @@ include(joinpath(@__DIR__, "barriers.jl")) # include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) real_types = [ - Float64, - Float32, + # Float64, + # Float32, BigFloat, ] @@ -50,34 +50,35 @@ real_types = [ # test_recover_cheb_polys() # end # -@info("starting barrier tests") -barrier_testfuns = [ - # test_epinormeucl_barrier, - # test_epinorinf_barrier, - # test_epinormspectral_barrier, - # test_epiperpower_barrier, - # test_epipersquare_barrier, - # test_epipersumexp_barrier, - # test_hypogeomean_barrier, - # test_hypoperlog_barrier, - # test_hypoperlogdet_barrier, - # test_semidefinite_barrier, - test_wsospolyinterp_barrier, - # test_wsospolyinterpmat_barrier, - # test_wsospolyinterpsoc_barrier, - ] -@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types - t(T) -end - -# @info("starting native interface tests") -# verbose = false -# system_solvers = [ -# # SO.QRCholCombinedHSDSystemSolver, -# # SO.SymIndefCombinedHSDSystemSolver, -# SO.NaiveElimCombinedHSDSystemSolver, -# # SO.NaiveCombinedHSDSystemSolver, +# @info("starting barrier tests") +# barrier_testfuns = [ +# test_epinormeucl_barrier, +# test_epinorinf_barrier, +# test_epinormspectral_barrier, +# test_epipersquare_barrier, +# # TODO next 3 fail with BigFloat, see https://github.com/JuliaDiff/DiffResults.jl/pull/9#issuecomment-497853361 +# test_epiperpower_barrier, +# test_epipersumexp_barrier, +# test_hypogeomean_barrier, +# test_hypoperlog_barrier, +# test_hypoperlogdet_barrier, +# test_semidefinite_barrier, +# test_wsospolyinterp_barrier, +# test_wsospolyinterpmat_barrier, +# test_wsospolyinterpsoc_barrier, # ] +# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types +# t(T) +# end + +@info("starting native interface tests") +verbose = false +system_solvers = [ + SO.QRCholCombinedHSDSystemSolver, + # SO.SymIndefCombinedHSDSystemSolver, + # SO.NaiveElimCombinedHSDSystemSolver, + # SO.NaiveCombinedHSDSystemSolver, + ] # testfuns_singular = [ # dimension1, # consistent1, @@ -87,54 +88,54 @@ end # @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types # t(s{T}, MO.PreprocessedLinearModel{T}, verbose) # end -# linear_models = [ -# MO.PreprocessedLinearModel, -# MO.RawLinearModel, -# ] -# testfuns_nonsingular = [ -# orthant1, -# orthant2, -# orthant3, -# orthant4, -# epinorminf1, -# epinorminf2, -# epinorminf3, -# epinorminf4, -# epinorminf5, -# epinorminf6, -# epinormeucl1, -# epinormeucl2, -# epipersquare1, -# epipersquare2, -# epipersquare3, -# semidefinite1, -# semidefinite2, -# semidefinite3, -# semidefinitecomplex1, -# hypoperlog1, -# hypoperlog2, -# hypoperlog3, -# hypoperlog4, -# epiperpower1, -# epiperpower2, -# epiperpower3, -# hypogeomean1, -# hypogeomean2, -# hypogeomean3, -# hypogeomean4, -# epinormspectral1, -# hypoperlogdet1, -# hypoperlogdet2, -# hypoperlogdet3, -# epipersumexp1, -# epipersumexp2, -# ] -# @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types -# if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel -# continue # QRChol linear system solver needs preprocessed model -# end -# t(s{T}, m{T}, verbose) -# end +linear_models = [ + MO.PreprocessedLinearModel, + # MO.RawLinearModel, + ] +testfuns_nonsingular = [ + # orthant1, + # orthant2, + # orthant3, + # orthant4, + # epinorminf1, + # epinorminf2, + # epinorminf3, + # epinorminf4, + # epinorminf5, + epinorminf6, + # epinormeucl1, + # epinormeucl2, + # epipersquare1, + # epipersquare2, + # epipersquare3, + semidefinite1, + # semidefinite2, + semidefinite3, + # semidefinitecomplex1, + # hypoperlog1, + # hypoperlog2, + # hypoperlog3, + # hypoperlog4, + # epiperpower1, + # epiperpower2, + # epiperpower3, + # hypogeomean1, + # hypogeomean2, + # hypogeomean3, + # hypogeomean4, + epinormspectral1, + hypoperlogdet1, + hypoperlogdet2, + hypoperlogdet3, + # epipersumexp1, + # epipersumexp2, + ] +@testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types + if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel + continue # QRChol linear system solver needs preprocessed model + end + t(s{T}, m{T}, verbose) +end # @info("starting MathOptInterface tests") # verbose = false From 6011485aee4f481f9057c830572a83a15b746e09 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 31 May 2019 18:03:18 -0400 Subject: [PATCH 19/20] fix some native tests with BigFloat --- Manifest.toml | 6 ++++++ Project.toml | 1 + src/Cones/epinormspectral.jl | 2 +- src/Cones/hypoperlogdet.jl | 2 +- src/Cones/semidefinite.jl | 4 ++-- src/Models/linear.jl | 30 ++++++++++++++---------------- test/native.jl | 1 + test/runtests.jl | 12 ++++++------ 8 files changed, 32 insertions(+), 26 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index 3bd436438..72eeeb347 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -145,6 +145,12 @@ git-tree-sha1 = "429b70c7a08ec34a20e0864366c20dfb8966e5e5" uuid = "92c85e6c-cbff-5e0c-80f7-495c94daaecd" version = "0.5.1" +[[GenericLinearAlgebra]] +deps = ["LinearAlgebra", "Printf", "Random", "Test"] +git-tree-sha1 = "ca235f9c4652b31525232a36d7832f5ee681d76a" +uuid = "14197337-ba66-59df-a3e3-ca00e7dcff7a" +version = "0.1.0" + [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" diff --git a/Project.toml b/Project.toml index 239051b89..7a30aacd8 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,7 @@ DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" GSL = "92c85e6c-cbff-5e0c-80f7-495c94daaecd" +GenericLinearAlgebra = "14197337-ba66-59df-a3e3-ca00e7dcff7a" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" diff --git a/src/Cones/epinormspectral.jl b/src/Cones/epinormspectral.jl index f4ea965bd..7d8a68f0f 100644 --- a/src/Cones/epinormspectral.jl +++ b/src/Cones/epinormspectral.jl @@ -43,7 +43,7 @@ function setup_data(cone::EpiNormSpectral{T}) where {T <: HypReal} dim = cone.dim cone.W = Matrix{T}(undef, cone.n, cone.m) cone.g = Vector{T}(undef, dim) - cone.H = Matrix{T}(undef, dim, dim) + cone.H = zeros(T, dim, dim) cone.H2 = similar(cone.H) return end diff --git a/src/Cones/hypoperlogdet.jl b/src/Cones/hypoperlogdet.jl index 2bd0691aa..3e4a86466 100644 --- a/src/Cones/hypoperlogdet.jl +++ b/src/Cones/hypoperlogdet.jl @@ -40,7 +40,7 @@ function setup_data(cone::HypoPerLogdet{T}) where {T <: HypReal} side = cone.side cone.mat = Matrix{T}(undef, side, side) cone.g = Vector{T}(undef, dim) - cone.H = Matrix{T}(undef, dim, dim) + cone.H = zeros(T, dim, dim) cone.H2 = similar(cone.H) return end diff --git a/src/Cones/semidefinite.jl b/src/Cones/semidefinite.jl index 05ceb15b9..fbc01c52e 100644 --- a/src/Cones/semidefinite.jl +++ b/src/Cones/semidefinite.jl @@ -48,8 +48,8 @@ PosSemidef{T, R}(dim::Int) where {R <: HypRealOrComplex{T}} where {T <: HypReal} function setup_data(cone::PosSemidef{T, R}) where {R <: HypRealOrComplex{T}} where {T <: HypReal} dim = cone.dim cone.g = Vector{T}(undef, dim) - cone.H = Matrix{T}(undef, dim, dim) - cone.Hi = similar(cone.H) + cone.H = zeros(T, dim, dim) + cone.Hi = zeros(T, dim, dim) cone.mat = Matrix{R}(undef, cone.side, cone.side) return end diff --git a/src/Models/linear.jl b/src/Models/linear.jl index 62f7eb489..c8c259ef3 100644 --- a/src/Models/linear.jl +++ b/src/Models/linear.jl @@ -97,17 +97,16 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} # solve for y as least squares solution to A'y = -c - G'z if !iszero(model.p) - Ap = A' - if issparse(Ap) && !(T <: sparse_QR_reals) + if issparse(A) && !(T <: sparse_QR_reals) # TODO alternative fallback is to convert sparse{T} to sparse{Float64} and do the sparse LU if use_dense_fallback @warn("using dense factorization of A' in initial point finding because sparse factorization for number type $T is not supported by SparseArrays") - F = qr!(Matrix(Ap)) + F = qr!(Matrix(A')) else error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot find an initial point") end else - F = qr(Ap) + F = qr(A') end point.y = F \ (-c - G' * point.z) end @@ -229,13 +228,13 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} point.x = AG_fact \ vcat(b, h - point.s) else # TODO optimize all below - if issparse(AG) + if AG_fact isa QRPivoted{T, Matrix{T}} + x_keep_idxs = AG_fact.p[1:AG_rank] + AG_Q1 = AG_fact.Q * Matrix{T}(I, p + q, AG_rank) + else x_keep_idxs = AG_fact.pcol[1:AG_rank] AG_Q1 = Matrix{T}(undef, p + q, AG_rank) AG_Q1[AG_fact.prow, :] = AG_fact.Q * Matrix{T}(I, p + q, AG_rank) - else - x_keep_idxs = AG_fact.p[1:AG_rank] - AG_Q1 = AG_fact.Q * Matrix{T}(I, p + q, AG_rank) end AG_R = UpperTriangular(AG_R[1:AG_rank, 1:AG_rank]) @@ -258,16 +257,15 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} # solve for y as least squares solution to A'y = -c - G'z if !iszero(p) - Ap = A' - if issparse(Ap) && !(T <: sparse_QR_reals) + if issparse(A) && !(T <: sparse_QR_reals) if use_dense_fallback @warn("using dense factorization of A' in preprocessing and initial point finding because sparse factorization for number type $T is not supported by SparseArrays") - Ap_fact = qr!(Matrix(Ap), Val(true)) + Ap_fact = qr!(Matrix(A'), Val(true)) else error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot preprocess and find an initial point") end else - Ap_fact = issparse(A) ? qr(sparse(Ap), tol = tol_QR) : qr(Ap, Val(true)) + Ap_fact = issparse(A) ? qr(sparse(A'), tol = tol_QR) : qr(A', Val(true)) end Ap_R = Ap_fact.R @@ -280,13 +278,13 @@ mutable struct PreprocessedLinearModel{T <: HypReal} <: LinearModel{T} end # TODO optimize all below - if issparse(A) + if Ap_fact isa QRPivoted{T, Matrix{T}} + y_keep_idxs = Ap_fact.p[1:Ap_rank] + A_Q = Ap_fact.Q * Matrix{T}(I, n, n) + else y_keep_idxs = Ap_fact.pcol[1:Ap_rank] A_Q = Matrix{T}(undef, n, n) A_Q[Ap_fact.prow, :] = Ap_fact.Q * Matrix{T}(I, n, n) - else - y_keep_idxs = Ap_fact.p[1:Ap_rank] - A_Q = Ap_fact.Q * Matrix{T}(I, n, n) end Ap_Q1 = A_Q[:, 1:Ap_rank] Ap_Q2 = A_Q[:, (Ap_rank + 1):n] diff --git a/test/native.jl b/test/native.jl index ebd05db17..13dcf7529 100644 --- a/test/native.jl +++ b/test/native.jl @@ -5,6 +5,7 @@ Copyright 2018, Chris Coey and contributors import Random using LinearAlgebra using SparseArrays +using GenericLinearAlgebra # solve model, check conic certificates are valid and return certificate data function solve_and_check( diff --git a/test/runtests.jl b/test/runtests.jl index 4c92f6aff..82ed2f280 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -72,11 +72,11 @@ real_types = [ # end @info("starting native interface tests") -verbose = false +verbose = true system_solvers = [ SO.QRCholCombinedHSDSystemSolver, # SO.SymIndefCombinedHSDSystemSolver, - # SO.NaiveElimCombinedHSDSystemSolver, + SO.NaiveElimCombinedHSDSystemSolver, # SO.NaiveCombinedHSDSystemSolver, ] # testfuns_singular = [ @@ -90,7 +90,7 @@ system_solvers = [ # end linear_models = [ MO.PreprocessedLinearModel, - # MO.RawLinearModel, + MO.RawLinearModel, ] testfuns_nonsingular = [ # orthant1, @@ -102,15 +102,15 @@ testfuns_nonsingular = [ # epinorminf3, # epinorminf4, # epinorminf5, - epinorminf6, + # epinorminf6, # epinormeucl1, # epinormeucl2, # epipersquare1, # epipersquare2, # epipersquare3, - semidefinite1, + # semidefinite1, # semidefinite2, - semidefinite3, + # semidefinite3, # semidefinitecomplex1, # hypoperlog1, # hypoperlog2, From 509ddc2f9b8fc110b49ab1a808e7df28b6fc25fc Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 31 May 2019 19:34:20 -0400 Subject: [PATCH 20/20] fix symindef with generic reals; fix remaining cones partially --- src/Cones/epinormspectral.jl | 4 +- src/Cones/hypoperlogdet.jl | 9 +- src/Cones/wsospolyinterpmat.jl | 194 +---------- src/Cones/wsospolyinterpsoc.jl | 17 +- src/Hypatia.jl | 1 + src/Models/linear.jl | 2 +- .../combined_step/symindef.jl | 8 +- test/runtests.jl | 318 +++++++++--------- 8 files changed, 186 insertions(+), 367 deletions(-) diff --git a/src/Cones/epinormspectral.jl b/src/Cones/epinormspectral.jl index 7d8a68f0f..79accbfea 100644 --- a/src/Cones/epinormspectral.jl +++ b/src/Cones/epinormspectral.jl @@ -62,7 +62,7 @@ function check_in_cone(cone::EpiNormSpectral{T}) where {T <: HypReal} n = cone.n m = cone.m - X = Symmetric(W * W') # TODO use hyp_AtA! when prealloc'd + X = Symmetric(W * W') # TODO use syrk Z = Symmetric(u * I - X / u) F = hyp_chol!(Z) if !isposdef(F) @@ -94,7 +94,7 @@ function check_in_cone(cone::EpiNormSpectral{T}) where {T <: HypReal} end # Zi * dZdWij * Zi - term1 = Symmetric(tmpmat + tmpmat') + term1 = Symmetric(tmpmat + tmpmat') # TODO use syrk # TODO matrixify q = p diff --git a/src/Cones/hypoperlogdet.jl b/src/Cones/hypoperlogdet.jl index 3e4a86466..23acd5750 100644 --- a/src/Cones/hypoperlogdet.jl +++ b/src/Cones/hypoperlogdet.jl @@ -64,13 +64,16 @@ function check_in_cone(cone::HypoPerLogdet{T}) where {T <: HypReal} W = cone.mat svec_to_smat!(W, view(cone.point, 3:cone.dim)) F = hyp_chol!(Symmetric(W)) - if !isposdef(F) || u >= v * (logdet(F) - cone.side * log(v)) + ldW = logdet(F) + if !isposdef(F) || u >= v * (ldW - cone.side * log(v)) return false end - L = logdet(W / v) + # L = logdet(W / v) + L = ldW - cone.side * log(v) z = v * L - u - Wi = Symmetric(inv(W)) + + Wi = Symmetric(inv(F)) n = cone.side dim = cone.dim vzi = v / z diff --git a/src/Cones/wsospolyinterpmat.jl b/src/Cones/wsospolyinterpmat.jl index 52348f5a6..2f8774234 100644 --- a/src/Cones/wsospolyinterpmat.jl +++ b/src/Cones/wsospolyinterpmat.jl @@ -20,13 +20,10 @@ mutable struct WSOSPolyInterpMat{T <: HypReal} <: Cone{T} Hi::Matrix{T} F mat::Vector{Matrix{T}} - matfact::Vector{CholeskyPivoted{T, Matrix{T}}} + matfact::Vector tmp1::Vector{Matrix{T}} tmp2::Matrix{T} tmp3::Matrix{T} - blockmats::Vector{Vector{Vector{Matrix{T}}}} - blockfacts::Vector{Vector{CholeskyPivoted{T, Matrix{T}}}} - PlambdaP::Matrix{T} function WSOSPolyInterpMat{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}, is_dual::Bool) where {T <: HypReal} for ipwtj in ipwt @@ -51,24 +48,14 @@ function setup_data(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} R = cone.R ipwt = cone.ipwt cone.g = Vector{T}(undef, dim) - cone.H = Matrix{T}(undef, dim, dim) + cone.H = zeros(T, dim, dim) cone.H2 = similar(cone.H) cone.Hi = similar(cone.H) cone.mat = [similar(cone.H, size(ipwtj, 2) * R, size(ipwtj, 2) * R) for ipwtj in ipwt] - cone.matfact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) + cone.matfact = Vector{Any}(undef, length(ipwt)) cone.tmp1 = [similar(cone.H, size(ipwtj, 2), U) for ipwtj in ipwt] cone.tmp2 = similar(cone.H, U, U) cone.tmp3 = similar(cone.tmp2) - cone.blockmats = [Vector{Vector{Matrix{T}}}(undef, R) for ipwtj in ipwt] - for i in eachindex(ipwt), j in 1:R # TODO actually store 1 fewer (no diagonal) and also make this less confusing - cone.blockmats[i][j] = Vector{Matrix{T}}(undef, j) - for k in 1:j # TODO actually need to only go up to j-1 - L = size(ipwt[i], 2) - cone.blockmats[i][j][k] = Matrix{T}(undef, L, L) - end - end - cone.blockfacts = [Vector{CholeskyPivoted{T, Matrix{T}}}(undef, R) for _ in eachindex(ipwt)] - cone.PlambdaP = Matrix{T}(undef, R * U, R * U) return end @@ -86,173 +73,7 @@ end _blockrange(inner::Int, outer::Int) = (outer * (inner - 1) + 1):(outer * inner) -# NOTE this is experimental code -function check_in_cone(cone::WSOSPolyInterpMat) - # check_in_cone_nowinv(cone) - check_in_cone_master(cone) -end - -# TODO all views can be allocated just once in the cone definition (delete _blockrange too) -function check_in_cone_nowinv(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} - # @timeit "build mat" begin - for j in eachindex(cone.ipwt) - ipwtj = cone.ipwt[j] - tmp1j = cone.tmp1[j] - L = size(ipwtj, 2) - mat = cone.mat[j] - - uo = 1 - for p in 1:cone.R, q in 1:p - point_pq = cone.point[uo:(uo + cone.U - 1)] # TODO prealloc - if p != q - @. point_pq *= rt2i - end - @. tmp1j = ipwtj' * point_pq' - - rinds = _blockrange(p, L) - cinds = _blockrange(q, L) - mul!(view(mat, rinds, cinds), tmp1j, ipwtj) - - uo += cone.U - end - - if !(blockcholesky!(cone, L, j)) - return false - end - end - # end - - # @timeit "grad hess" begin - cone.g .= zero(T) - cone.H .= zero(T) - for j in eachindex(cone.ipwt) - ipwtj = cone.ipwt[j] - L = size(ipwtj, 2) - - # perform L \ kron(ipwt) - ldivp = _block_trisolve(cone, L, j) - # ldivp' * ldivp, only fills upper triangle - _mulblocks!(cone, ldivp, L) - PlambdaP = Symmetric(cone.PlambdaP, :U) - - uo = 0 - for p in 1:cone.R, q in 1:p - uo += 1 - fact = (p == q) ? one(T) : rt2 - rinds = _blockrange(p, cone.U) - cinds = _blockrange(q, cone.U) - idxs = _blockrange(uo, cone.U) - - cone.g[idxs] -= diag(PlambdaP[rinds, cinds]) .* fact - - uo2 = 0 - for p2 in 1:cone.R, q2 in 1:p2 - uo2 += 1 - if uo2 < uo - continue - end - - rinds2 = _blockrange(p2, cone.U) - cinds2 = _blockrange(q2, cone.U) - idxs2 = _blockrange(uo2, cone.U) - - fact = xor(p == q, p2 == q2) ? rt2i : one(T) - @. cone.H[idxs, idxs2] += PlambdaP[rinds, rinds2] * PlambdaP[cinds, cinds2] * fact - - if (p != q) || (p2 != q2) - @. cone.H[idxs, idxs2] += PlambdaP[rinds, cinds2] * PlambdaP[cinds, rinds2] * fact - end - end - end - end - # end - - return factorize_hess(cone) -end - -# res stored lower triangle -function blockcholesky!(cone::WSOSPolyInterpMat{T}, L::Int, j::Int) where {T <: HypReal} - R = cone.R - res = cone.blockmats[j] - tmp = zeros(L, L) - facts = cone.blockfacts[j] - for r in 1:R - tmp .= zero(T) - # blocks on the diagonal come from cholesky decomposition after back-substitution, result stored in cone.blockfacts - for k in 1:(r - 1) - BLAS.syrk!('U', 'N', one(T), res[r][k], one(T), tmp) - end - diag_block = cone.mat[j][_blockrange(r, L), _blockrange(r, L)] - tmp - F = cholesky!(Symmetric(diag_block, :U), Val(true), check = false) - if !(isposdef(F)) - return false - end - facts[r] = F - - # blocks off the diagonal come from back-substitution, contigous blocks stored in cone.blockmats - for s in (r + 1):R - for k in 1:(r - 1) - # tmp += res[r][k] * res[s][k]' - BLAS.gemm!('N', 'T', one(T), res[r][k], res[s][k], one(T), tmp) - end - rhs = cone.mat[j][_blockrange(s, L), _blockrange(r, L)] - tmp - res[s][r] = (facts[r].L \ view(rhs, facts[r].p, :))' - end - end - return true -end - -function _block_trisolve(cone::WSOSPolyInterpMat{T}, blocknum::Int, L::Int, j::Int) where {T <: HypReal} - Lmat = cone.blockmats[j] - R = cone.R - U = cone.U - Fvec = cone.blockfacts[j] - resvec = zeros(R * L, U) - tmp = zeros(L, U) - resvec[_blockrange(blocknum, L), :] = Fvec[blocknum].L \ view(cone.ipwt[j]', Fvec[blocknum].p, :) - for r in (blocknum + 1):R - tmp .= zero(T) - for s in blocknum:(r - 1) - # tmp -= Lmat[r][s] * resvec[_blockrange(s, L), :] - resblock = resvec[_blockrange(s, L), :] - BLAS.gemm!('N', 'N', -one(T), Lmat[r][s], resblock, one(T), tmp) - end - resvec[_blockrange(r, L), :] = Fvec[r].L \ view(tmp, Fvec[r].p, :) - end - return resvec -end - -# one block-column at a time on the RHS -function _block_trisolve(cone::WSOSPolyInterpMat{T}, L::Int, j::Int) where {T <: HypReal} - R = cone.R - U = cone.U - resmat = zeros(R * L, R * U) - for r in 1:R - resmat[:, _blockrange(r, U)] = _block_trisolve(cone, r, L, j) - end - return resmat -end - -# multiply lower triangular block matrix transposed by itself -function _mulblocks!(cone::WSOSPolyInterpMat{T}, mat::Matrix{T}, L::Int) where {T <: HypReal} - # cone.PlambdaP = mat' * mat - R = cone.R - U = cone.U - for i in 1:R - rinds = _blockrange(i, U) - for j in i:R - cinds = _blockrange(j, U) - tmp .= zero(T) - # since mat is block lower triangular rows only from max(i,j) start making a nonzero contribution to the product - mulrange = ((j - 1) * L + 1):(L * R) - mul!(view(cone.PlambdaP, rinds, cinds), mat[mulrange, _blockrange(i, U)]', mat[mulrange, _blockrange(j, U)]) - end - end - return nothing -end - -function check_in_cone_master(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} - # @timeit "build mat" begin +function check_in_cone(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] tmp1j = cone.tmp1[j] @@ -274,20 +95,16 @@ function check_in_cone_master(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} uo += cone.U end - cone.matfact[j] = cholesky!(Symmetric(mat, :L), Val(true), check = false) + cone.matfact[j] = hyp_chol!(Symmetric(mat, :L)) if !isposdef(cone.matfact[j]) return false end end - # end - # @timeit "grad hess" begin cone.g .= zero(T) cone.H .= zero(T) for j in eachindex(cone.ipwt) - # @timeit "W_inv" begin W_inv_j = inv(cone.matfact[j]) - # end ipwtj = cone.ipwt[j] tmp1j = cone.tmp1[j] @@ -335,7 +152,6 @@ function check_in_cone_master(cone::WSOSPolyInterpMat{T}) where {T <: HypReal} end end end - # end return factorize_hess(cone) end diff --git a/src/Cones/wsospolyinterpsoc.jl b/src/Cones/wsospolyinterpsoc.jl index 01d36ef52..9ed11171b 100644 --- a/src/Cones/wsospolyinterpsoc.jl +++ b/src/Cones/wsospolyinterpsoc.jl @@ -22,14 +22,14 @@ mutable struct WSOSPolyInterpSOC{T <: HypReal} <: Cone{T} Hi::Matrix{T} F mat::Vector{Matrix{T}} - matfact::Vector{CholeskyPivoted{T, Matrix{T}}} + matfact::Vector tmp1::Vector{Matrix{T}} tmp2::Vector{Matrix{T}} tmp3::Matrix{T} tmp4::Vector{Matrix{T}} li_lambda::Vector{Vector{Matrix{T}}} PlambdaiP::Vector{Vector{Vector{Matrix{T}}}} - lambdafact::Vector{CholeskyPivoted{T, Matrix{T}}} + lambdafact::Vector function WSOSPolyInterpSOC{T}(R::Int, U::Int, ipwt::Vector{Matrix{T}}, is_dual::Bool) where {T <: HypReal} for ipwtj in ipwt @@ -60,7 +60,7 @@ function setup_data(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} cone.H2 = similar(cone.H) cone.Hi = similar(cone.H) cone.mat = [similar(cone.g, size(ipwtj, 2), size(ipwtj, 2)) for ipwtj in ipwt] - cone.matfact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) + cone.matfact = Vector{Any}(undef, length(ipwt)) cone.tmp1 = [similar(cone.g, size(ipwtj, 2), U) for ipwtj in ipwt] cone.tmp2 = [similar(cone.g, size(ipwtj, 2), U) for ipwtj in ipwt] cone.tmp3 = similar(cone.g, U, U) @@ -76,7 +76,7 @@ function setup_data(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} cone.PlambdaiP[j][r1][r2] = similar(cone.g, U, U) end end - cone.lambdafact = Vector{CholeskyPivoted{T, Matrix{T}}}(undef, length(ipwt)) + cone.lambdafact = Vector{Any}(undef, length(ipwt)) return end @@ -90,7 +90,6 @@ end # TODO cleanup experimental code function check_in_cone(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} - # @timeit "build mat" begin for j in eachindex(cone.ipwt) ipwtj = cone.ipwt[j] li_lambda = cone.li_lambda[j] @@ -107,8 +106,7 @@ function check_in_cone(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} @. tmp1 = ipwtj' * point_pq' mul!(tmp4, tmp1, ipwtj) mat .= tmp4 - lambdafact[j] = cholesky!(Symmetric(tmp4, :L), Val(true), check = false) - + lambdafact[j] = hyp_chol!(Symmetric(tmp4, :L)) if !isposdef(lambdafact[j]) return false end @@ -127,14 +125,12 @@ function check_in_cone(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} uo += cone.U end - matfact[j] = cholesky!(Symmetric(mat, :U), Val(true), check = false) + matfact[j] = hyp_chol!(Symmetric(mat, :U)) if !isposdef(matfact[j]) return false end end - # end - # @timeit "grad hess" begin cone.g .= zero(T) cone.H .= zero(T) for j in eachindex(cone.ipwt) @@ -218,7 +214,6 @@ function check_in_cone(cone::WSOSPolyInterpSOC{T}) where {T <: HypReal} end end end # j - # end return factorize_hess(cone) end diff --git a/src/Hypatia.jl b/src/Hypatia.jl index cc3a6c289..0183b81f2 100644 --- a/src/Hypatia.jl +++ b/src/Hypatia.jl @@ -19,6 +19,7 @@ include("Models/Models.jl") include("Solvers/Solvers.jl") # MathOptInterface +using SparseArrays import MathOptInterface const MOI = MathOptInterface diff --git a/src/Models/linear.jl b/src/Models/linear.jl index c8c259ef3..72c17dcaa 100644 --- a/src/Models/linear.jl +++ b/src/Models/linear.jl @@ -106,7 +106,7 @@ mutable struct RawLinearModel{T <: HypReal} <: LinearModel{T} error("sparse factorization for number type $T is not supported by SparseArrays, so Hypatia cannot find an initial point") end else - F = qr(A') + F = issparse(A) ? qr(sparse(A')) : qr!(Matrix(A')) end point.y = F \ (-c - G' * point.z) end diff --git a/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl b/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl index 69bc54a6d..cd2bdea50 100644 --- a/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl +++ b/src/Solvers/homogeneous_self_dual/combined_step/symindef.jl @@ -171,8 +171,12 @@ function get_combined_directions(solver::HSDSolver{T}, system_solver::SymIndefCo end rhs .= F \ rhs else - F = bunchkaufman!(Symmetric(lhs, :L), true, check = true) # TODO doesn't work for generic reals (need LDLT) - ldiv!(F, rhs) + if T <: BlasReal + F = bunchkaufman!(Symmetric(lhs, :L), true, check = true) # TODO doesn't work for generic reals (need LDLT) + ldiv!(F, rhs) + else + rhs .= Symmetric(lhs, :L) \ rhs # TODO replace with a generic julia symmetric indefinite decomposition if available, see https://github.com/JuliaLang/julia/issues/10953 + end end for k in eachindex(cones) diff --git a/test/runtests.jl b/test/runtests.jl index 82ed2f280..694df08c3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,120 +15,120 @@ const MU = HYP.ModelUtilities include(joinpath(@__DIR__, "interpolation.jl")) include(joinpath(@__DIR__, "barriers.jl")) include(joinpath(@__DIR__, "native.jl")) -# include(joinpath(@__DIR__, "MathOptInterface.jl")) +include(joinpath(@__DIR__, "MathOptInterface.jl")) -# examples_dir = joinpath(@__DIR__, "../examples") -# include(joinpath(examples_dir, "envelope/native.jl")) -# include(joinpath(examples_dir, "linearopt/native.jl")) -# include(joinpath(examples_dir, "polymin/native.jl")) -# include(joinpath(examples_dir, "contraction/JuMP.jl")) -# include(joinpath(examples_dir, "densityest/JuMP.jl")) -# include(joinpath(examples_dir, "envelope/JuMP.jl")) -# include(joinpath(examples_dir, "expdesign/JuMP.jl")) -# include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) -# include(joinpath(examples_dir, "muconvexity/JuMP.jl")) -# include(joinpath(examples_dir, "polymin/JuMP.jl")) -# include(joinpath(examples_dir, "polynorm/JuMP.jl")) -# include(joinpath(examples_dir, "regionofattr/JuMP.jl")) -# include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) -# include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) -# include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) +examples_dir = joinpath(@__DIR__, "../examples") +include(joinpath(examples_dir, "envelope/native.jl")) +include(joinpath(examples_dir, "linearopt/native.jl")) +include(joinpath(examples_dir, "polymin/native.jl")) +include(joinpath(examples_dir, "contraction/JuMP.jl")) +include(joinpath(examples_dir, "densityest/JuMP.jl")) +include(joinpath(examples_dir, "envelope/JuMP.jl")) +include(joinpath(examples_dir, "expdesign/JuMP.jl")) +include(joinpath(examples_dir, "lotkavolterra/JuMP.jl")) +include(joinpath(examples_dir, "muconvexity/JuMP.jl")) +include(joinpath(examples_dir, "polymin/JuMP.jl")) +include(joinpath(examples_dir, "polynorm/JuMP.jl")) +include(joinpath(examples_dir, "regionofattr/JuMP.jl")) +include(joinpath(examples_dir, "secondorderpoly/JuMP.jl")) +include(joinpath(examples_dir, "shapeconregr/JuMP.jl")) +include(joinpath(examples_dir, "semidefinitepoly/JuMP.jl")) real_types = [ - # Float64, - # Float32, + Float64, + Float32, BigFloat, ] @info("starting Hypatia tests") @testset "Hypatia tests" begin -# @info("starting interpolation tests") -# @testset "interpolation tests" begin -# fekete_sample() -# test_recover_lagrange_polys() -# test_recover_cheb_polys() -# end -# -# @info("starting barrier tests") -# barrier_testfuns = [ -# test_epinormeucl_barrier, -# test_epinorinf_barrier, -# test_epinormspectral_barrier, -# test_epipersquare_barrier, -# # TODO next 3 fail with BigFloat, see https://github.com/JuliaDiff/DiffResults.jl/pull/9#issuecomment-497853361 -# test_epiperpower_barrier, -# test_epipersumexp_barrier, -# test_hypogeomean_barrier, -# test_hypoperlog_barrier, -# test_hypoperlogdet_barrier, -# test_semidefinite_barrier, -# test_wsospolyinterp_barrier, -# test_wsospolyinterpmat_barrier, -# test_wsospolyinterpsoc_barrier, -# ] -# @testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types -# t(T) -# end +@info("starting interpolation tests") +@testset "interpolation tests" begin + fekete_sample() + test_recover_lagrange_polys() + test_recover_cheb_polys() +end + +@info("starting barrier tests") +barrier_testfuns = [ + test_epinormeucl_barrier, + test_epinorinf_barrier, + test_epinormspectral_barrier, + test_epipersquare_barrier, + # TODO next 3 fail with BigFloat, see https://github.com/JuliaDiff/DiffResults.jl/pull/9#issuecomment-497853361 + test_epiperpower_barrier, + test_epipersumexp_barrier, + test_hypogeomean_barrier, + test_hypoperlog_barrier, + test_hypoperlogdet_barrier, + test_semidefinite_barrier, + test_wsospolyinterp_barrier, + test_wsospolyinterpmat_barrier, + test_wsospolyinterpsoc_barrier, # NOTE not updated for generic reals (too much work) + ] +@testset "barrier functions tests: $t, $T" for t in barrier_testfuns, T in real_types + t(T) +end @info("starting native interface tests") -verbose = true +verbose = false system_solvers = [ SO.QRCholCombinedHSDSystemSolver, - # SO.SymIndefCombinedHSDSystemSolver, + SO.SymIndefCombinedHSDSystemSolver, SO.NaiveElimCombinedHSDSystemSolver, - # SO.NaiveCombinedHSDSystemSolver, + SO.NaiveCombinedHSDSystemSolver, ] -# testfuns_singular = [ -# dimension1, -# consistent1, -# inconsistent1, -# inconsistent2, -# ] -# @testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types -# t(s{T}, MO.PreprocessedLinearModel{T}, verbose) -# end +testfuns_singular = [ + dimension1, + consistent1, + inconsistent1, + inconsistent2, + ] +@testset "preprocessing tests: $t, $s, $T" for t in testfuns_singular, s in system_solvers, T in real_types + t(s{T}, MO.PreprocessedLinearModel{T}, verbose) +end linear_models = [ MO.PreprocessedLinearModel, MO.RawLinearModel, ] testfuns_nonsingular = [ - # orthant1, - # orthant2, - # orthant3, - # orthant4, - # epinorminf1, - # epinorminf2, - # epinorminf3, - # epinorminf4, - # epinorminf5, - # epinorminf6, - # epinormeucl1, - # epinormeucl2, - # epipersquare1, - # epipersquare2, - # epipersquare3, - # semidefinite1, - # semidefinite2, - # semidefinite3, - # semidefinitecomplex1, - # hypoperlog1, - # hypoperlog2, - # hypoperlog3, - # hypoperlog4, - # epiperpower1, - # epiperpower2, - # epiperpower3, - # hypogeomean1, - # hypogeomean2, - # hypogeomean3, - # hypogeomean4, + orthant1, + orthant2, + orthant3, + orthant4, + epinorminf1, + epinorminf2, + epinorminf3, + epinorminf4, + epinorminf5, + epinorminf6, + epinormeucl1, + epinormeucl2, + epipersquare1, + epipersquare2, + epipersquare3, + semidefinite1, + semidefinite2, + semidefinite3, + semidefinitecomplex1, + hypoperlog1, + hypoperlog2, + hypoperlog3, + hypoperlog4, + epiperpower1, + epiperpower2, + epiperpower3, + hypogeomean1, + hypogeomean2, + hypogeomean3, + hypogeomean4, epinormspectral1, hypoperlogdet1, hypoperlogdet2, hypoperlogdet3, - # epipersumexp1, - # epipersumexp2, + epipersumexp1, + epipersumexp2, ] @testset "native tests: $t, $s, $m, $T" for t in testfuns_nonsingular, s in system_solvers, m in linear_models, T in real_types if s == SO.QRCholCombinedHSDSystemSolver && m == MO.RawLinearModel @@ -137,74 +137,74 @@ testfuns_nonsingular = [ t(s{T}, m{T}, verbose) end -# @info("starting MathOptInterface tests") -# verbose = false -# system_solvers = [ -# SO.NaiveCombinedHSDSystemSolver, -# SO.QRCholCombinedHSDSystemSolver, -# ] -# linear_models = [ -# MO.PreprocessedLinearModel, # MOI tests require preprocessing -# ] -# @testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models -# test_moi(d, s{Float64}, m{Float64}, verbose) -# end -# -# @info("starting native examples tests") -# native_options = ( -# verbose = true, -# max_iters = 150, -# time_limit = 6e2, # 1 minute -# ) -# @testset "native examples" begin -# @testset "envelope" begin test_envelope(; native_options..., -# ) end -# @testset "linearopt" begin test_linearopt(; native_options..., -# ) end -# @testset "polymin" begin test_polymin(; native_options..., -# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, -# ) end -# end -# -# @info("starting JuMP examples tests") -# JuMP_options = ( -# verbose = true, -# test_certificates = true, -# max_iters = 250, -# time_limit = 6e2, # 1 minute -# ) -# @testset "JuMP examples" begin -# @testset "contraction" begin test_contractionJuMP(; JuMP_options..., -# tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, -# ) end -# @testset "densityest" begin test_densityestJuMP(; JuMP_options..., -# tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, -# ) end -# @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., -# ) end -# @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., -# ) end -# @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., -# tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, -# ) end -# @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., -# ) end -# @testset "polymin" begin test_polyminJuMP(; JuMP_options..., -# tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, -# ) end -# @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., -# ) end -# @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., -# tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, -# ) end -# @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., -# ) end -# @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., -# tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, -# ) end -# @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., -# tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, -# ) end -# end +@info("starting MathOptInterface tests") +verbose = false +system_solvers = [ + SO.NaiveCombinedHSDSystemSolver, + SO.QRCholCombinedHSDSystemSolver, + ] +linear_models = [ + MO.PreprocessedLinearModel, # MOI tests require preprocessing + ] +@testset "MOI tests: $(d ? "dense" : "sparse"), $s, $m" for d in (false, true), s in system_solvers, m in linear_models + test_moi(d, s{Float64}, m{Float64}, verbose) +end + +@info("starting native examples tests") +native_options = ( + verbose = true, + max_iters = 150, + time_limit = 6e2, # 1 minute + ) +@testset "native examples" begin + @testset "envelope" begin test_envelope(; native_options..., + ) end + @testset "linearopt" begin test_linearopt(; native_options..., + ) end + @testset "polymin" begin test_polymin(; native_options..., + tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + ) end +end + +@info("starting JuMP examples tests") +JuMP_options = ( + verbose = false, + test_certificates = true, + max_iters = 250, + time_limit = 6e2, # 1 minute + ) +@testset "JuMP examples" begin + @testset "contraction" begin test_contractionJuMP(; JuMP_options..., + tol_rel_opt = 1e-4, tol_abs_opt = 1e-4, tol_feas = 1e-4, + ) end + @testset "densityest" begin test_densityestJuMP(; JuMP_options..., + tol_rel_opt = 1e-5, tol_abs_opt = 1e-5, tol_feas = 1e-6, + ) end + @testset "envelope" begin test_envelopeJuMP(; JuMP_options..., + ) end + @testset "expdesign" begin test_expdesignJuMP(; JuMP_options..., + ) end + @testset "lotkavolterra" begin test_lotkavolterraJuMP(; JuMP_options..., + tol_rel_opt = 1e-5, tol_abs_opt = 1e-6, tol_feas = 1e-6, + ) end + @testset "muconvexity" begin test_muconvexityJuMP(; JuMP_options..., + ) end + @testset "polymin" begin test_polyminJuMP(; JuMP_options..., + tol_rel_opt = 1e-9, tol_abs_opt = 1e-8, tol_feas = 1e-9, + ) end + @testset "polynorm" begin test_polynormJuMP(; JuMP_options..., + ) end + @testset "regionofattr" begin test_regionofattrJuMP(; JuMP_options..., + tol_abs_opt = 1e-6, tol_rel_opt = 1e-6, tol_feas = 1e-6, + ) end + @testset "secondorderpoly" begin test_secondorderpolyJuMP(; JuMP_options..., + ) end + @testset "semidefinitepoly" begin test_semidefinitepolyJuMP(; JuMP_options..., + tol_abs_opt = 1e-7, tol_rel_opt = 1e-7, tol_feas = 1e-7, + ) end + @testset "shapeconregr" begin test_shapeconregrJuMP(; JuMP_options..., + tol_rel_opt = 1e-6, tol_abs_opt = 1e-6, tol_feas = 1e-6, + ) end +end end