From 2213d51b31ed34faaa7c40edfbc7604d20dc8fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Thu, 25 Jan 2018 12:06:56 +0000 Subject: [PATCH 1/9] Add some docstrings --- src/interface.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/interface.jl b/src/interface.jl index 25d89c7..c29ff61 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,8 +1,18 @@ +""" +Force (re-)evaluation of the objective value at `x`. + +Returns `f(x)` and stores the value in `obj.F` +""" function value!!(obj::AbstractObjective, x) obj.f_calls .+= 1 copy!(obj.x_f, x) obj.F = obj.f(real_to_complex(obj, x)) end +""" +Evaluates the objective value at `x`. + +Returns `f(x)`, but does *not* store the value in `obj.F` +""" function value(obj::AbstractObjective, x) if x != obj.x_f obj.f_calls .+= 1 @@ -10,6 +20,11 @@ function value(obj::AbstractObjective, x) end obj.F end +""" +Evaluates the objective value at `x`. + +Returns `f(x)` and stores the value in `obj.F` +""" function value!(obj::AbstractObjective, x) if x != obj.x_f value!!(obj, x) @@ -17,6 +32,11 @@ function value!(obj::AbstractObjective, x) obj.F end +""" +Evaluates the gradient value at `x` + +This does *not* update `obj.DF`. +""" function gradient(obj::AbstractObjective, x) if x != obj.x_df tmp = copy(obj.DF) @@ -27,11 +47,21 @@ function gradient(obj::AbstractObjective, x) end obj.DF end +""" +Evaluates the gradient value at `x`. + +Stores the value in `obj.DF`. +""" function gradient!(obj::AbstractObjective, x) if x != obj.x_df gradient!!(obj, x) end end +""" +Force (re-)evaluation of the gradient value at `x`. + +Stores the value in `obj.DF`. +""" function gradient!!(obj::AbstractObjective, x) obj.df_calls .+= 1 copy!(obj.x_df, x) @@ -68,10 +98,15 @@ function hessian!!(obj::AbstractObjective, x) end # Getters are without ! and accept only an objective and index or just an objective +"Get the most recently evaluated objective value of `obj`." value(obj::AbstractObjective) = obj.F +"Get the most recently evaluated gradient of `obj`." gradient(obj::AbstractObjective) = obj.DF +"Get the most recently evaluated Jacobian of `obj`." jacobian(obj::AbstractObjective) = obj.DF +"Get the `i`th element of the most recently evaluated gradient of `obj`." gradient(obj::AbstractObjective, i::Integer) = obj.DF[i] +"Get the most recently evaluated Hessian of `obj`" hessian(obj::AbstractObjective) = obj.H value_jacobian!(obj, x) = value_jacobian!(obj, obj.F, obj.DF, x) From 8f7ba17f4bf30c112cccdc37331374652d7c03c0 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 25 Jan 2018 14:50:18 +0000 Subject: [PATCH 2/9] Introduce constraints objects for interior point NLP --- src/NLSolversBase.jl | 4 + src/objective_types/constraints.jl | 263 +++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 src/objective_types/constraints.jl diff --git a/src/NLSolversBase.jl b/src/NLSolversBase.jl index 241f9ce..7d471bd 100644 --- a/src/NLSolversBase.jl +++ b/src/NLSolversBase.jl @@ -35,6 +35,9 @@ export AbstractObjective, only_j_and_fj, clear! +export AbstractConstraints, DifferentiableConstraints, + TwiceDifferentiableConstraints, ConstraintBounds, + x_of_nans(x) = copy(x).=(eltype(x))(NaN) include("complex_real.jl") @@ -44,6 +47,7 @@ include("objective_types/oncedifferentiable.jl") include("objective_types/twicedifferentiable.jl") include("objective_types/twicedifferentiablehv.jl") include("objective_types/incomplete.jl") +include("objective_types/constraints.jl") include("interface.jl") end # module diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl new file mode 100644 index 0000000..950a2d5 --- /dev/null +++ b/src/objective_types/constraints.jl @@ -0,0 +1,263 @@ +### Constraints +# +# Constraints are specified by the user as +# lx_i ≤ x[i] ≤ ux_i # variable (box) constraints +# lc_i ≤ c(x)[i] ≤ uc_i # linear/nonlinear constraints +# and become equality constraints with l_i = u_i. ±∞ are allowed for l +# and u, in which case the relevant side(s) are unbounded. +# +# The user supplies functions to calculate c(x) and its derivatives. +# +# Of course we could unify the box-constraints into the +# linear/nonlinear constraints, but that would force the user to +# provide the variable-derivatives manually, which would be silly. +# +# This parametrization of the constraints gets "parsed" into a form +# that speeds and simplifies the algorithm, at the cost of many +# additional variables. See `parse_constraints` for details. + +struct ConstraintBounds{T} + nc::Int # Number of linear/nonlinear constraints supplied by user + # Box-constraints on variables (i.e., directly on x) + eqx::Vector{Int} # index-vector of equality-constrained x (not actually variable...) + valx::Vector{T} # value of equality-constrained x + ineqx::Vector{Int} # index-vector of other inequality-constrained variables + σx::Vector{Int8} # ±1, in constraints σ(v-b) ≥ 0 (sign depends on whether v>b or v nc + +The number of linear/nonlinear constraint functions supplied by the +user. This does not include bounds-constraints on variables. + +See also: nconstraints_x. +""" +nconstraints(cb::ConstraintBounds) = cb.nc + +""" + nconstraints_x(bounds) -> nx + +The number of "meaningful" constraints (not `±Inf`) on the x coordinates. + +See also: nconstraints. +""" +function nconstraints_x(cb::ConstraintBounds) + mi = isempty(cb.ineqx) ? 0 : maximum(cb.ineqx) + me = isempty(cb.eqx) ? 0 : maximum(cb.eqx) + nmax = max(mi, me) + hasconstraint = falses(nmax) + hasconstraint[cb.ineqx] = true + hasconstraint[cb.eqx] = true + sum(hasconstraint) +end + +function Base.show(io::IO, cb::ConstraintBounds) + indent = " " + print(io, "ConstraintBounds:") + print(io, "\n Variables:") + showeq(io, indent, cb.eqx, cb.valx, 'x', :bracket) + showineq(io, indent, cb.ineqx, cb.σx, cb.bx, 'x', :bracket) + print(io, "\n Linear/nonlinear constraints:") + showeq(io, indent, cb.eqc, cb.valc, 'c', :subscript) + showineq(io, indent, cb.ineqc, cb.σc, cb.bc, 'c', :subscript) + nothing +end + +abstract type AbstractConstraints end + +nconstraints(constraints::AbstractConstraints) = nconstraints(constraints.bounds) + +struct DifferentiableConstraintsFunction{F,J,T} <: AbstractConstraints + c!::F # c!(x, storage) stores the value of the constraint-functions at x + jacobian!::J # jacobian!(x, storage) stores the Jacobian of the constraint-functions + bounds::ConstraintBounds{T} +end + +function DifferentiableConstraintsFunction(c!, jacobian!, lx, ux, lc, uc) + b = ConstraintBounds(lx, ux, lc, uc) + DifferentiableConstraintsFunction(c!, jacobian!, b) +end + +#TODO: is this constructor necessary? +DifferentiableConstraintsFunction(c!, jacobian!, bounds::ConstraintBounds) = + DifferentiableConstraintsFunction{typeof(c!), typeof(jacobian!), eltype(b)}(c!, jacobian!, b) + +function DifferentiableConstraintsFunction(lx::AbstractArray, ux::AbstractArray) + bounds = ConstraintBounds(lx, ux, [], []) + DifferentiableConstraintsFunction(bounds) +end + +function DifferentiableConstraintsFunction(bounds::ConstraintBounds) + c! = (x,c)->nothing + J! = (x,J)->nothing + DifferentiableConstraintsFunction(c!, J!, bounds) +end + +struct TwiceDifferentiableConstraints{F,J,H,T} <: AbstractConstraints + c!::F + jacobian!::J + h!::H # Hessian of the barrier terms + bounds::ConstraintBounds{T} +end +function TwiceDifferentiableConstraints(c!, jacobian!, h!, lx, ux, lc, uc) + b = ConstraintBounds(lx, ux, lc, uc) + TwiceDifferentiableConstraints(c!, jacobian!, h!, b) +end +TwiceDifferentiableConstraints(c!, jacobian!, h!, bounds::ConstraintBounds) = + TwiceDifferentiableConstraints{typeof(c!), typeof(jacobian!), typeof(h!), eltype(bounds)}(c!, jacobian!, h!, bounds) + +function TwiceDifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) + bounds = ConstraintBounds(lx, ux, [], []) + TwiceDifferentiableConstraints(bounds) +end + +function TwiceDifferentiableConstraints(bounds::ConstraintBounds) + c! = (x,c)->nothing + J! = (x,J)->nothing + h! = (x,λ,h)->nothing + TwiceDifferentiableConstraints(c!, J!, h!, bounds) +end + + +## Utilities + +function symmetrize(l, u) + if isempty(l) && !isempty(u) + l = fill!(similar(u), -Inf) + end + if !isempty(l) && isempty(u) + u = fill!(similar(l), Inf) + end + # TODO: change to indices? + size(l) == size(u) || throw(DimensionMismatch("bounds arrays must be consistent, got sizes $(size(l)) and $(size(u))")) + _symmetrize(l, u) +end +_symmetrize{T,N}(l::AbstractArray{T,N}, u::AbstractArray{T,N}) = l, u +_symmetrize(l::Vector{Any}, u::Vector{Any}) = _symm(l, u) +_symmetrize(l, u) = _symm(l, u) + +# Designed to ensure that bounds supplied as [] don't cause +# unnecessary broadening of the eltype. Note this isn't type-stable; if +# the user cares, it can be avoided by supplying the same concrete +# type for both l and u. +function _symm(l, u) + if isempty(l) && isempty(u) + if eltype(l) == Any + # prevent promotion from returning eltype Any + l = Array{Union{}}(0) + end + if eltype(u) == Any + u = Array{Union{}}(0) + end + end + promote(l, u) +end + +""" + parse_constraints(T, l, u) -> eq, val, ineq, σ, b + +From user-supplied constraints of the form + + l_i ≤ v_i ≤ u_i + +(which include both inequality and equality constraints, the latter +when `l_i == u_i`), convert into the following representation: + + - `eq`, a vector of the indices for which `l[eq] == u[eq]` + - `val = l[eq] = u[eq]` + - `ineq`, `σ`, and `b` such that the inequality constraints can be written as + σ[k]*(v[ineq[k]] - b[k]) ≥ 0 + where `σ[k] = ±1`. + +Note that since the same `v_i` might have both lower and upper bounds, +`ineq` might have the same index twice (once with `σ`=-1 and once with `σ`=1). + +Supplying `±Inf` for elements of `l` and/or `u` implies that `v_i` is +unbounded in the corresponding direction. In such cases there is no +corresponding entry in `ineq`/`σ`/`b`. + +T is the element-type of the non-Int outputs +""" +function parse_constraints(::Type{T}, l, u) where T + size(l) == size(u) || throw(DimensionMismatch("l and u must be the same size, got $(size(l)) and $(size(u))")) + eq, ineq = Int[], Int[] + val, b = T[], T[] + σ = Array{Int8}(0) + for i = 1:length(l) + li, ui = l[i], u[i] + li <= ui || throw(ArgumentError("l must be smaller than u, got $li, $ui")) + if li == ui + push!(eq, i) + push!(val, ui) + else + if isfinite(li) + push!(ineq, i) + push!(σ, 1) + push!(b, li) + end + ui = u[i] + if isfinite(ui) + push!(ineq, i) + push!(σ, -1) + push!(b, ui) + end + end + end + eq, val, ineq, σ, b +end + +### Compact printing of constraints + +struct UnquotedString + str::AbstractString +end +Base.show(io::IO, uqstr::UnquotedString) = print(io, uqstr.str) + +Base.array_eltype_show_how(a::Vector{UnquotedString}) = false, "" + +if !isdefined(Base, :IOContext) + IOContext(io; kwargs...) = io +end + +function showeq(io, indent, eq, val, chr, style) + if !isempty(eq) + print(io, '\n', indent) + if style == :bracket + eqstrs = map((i,v) -> UnquotedString("$chr[$i]=$v"), eq, val) + else + eqstrs = map((i,v) -> UnquotedString("$(chr)_$i=$v"), eq, val) + end + Base.show_vector(IOContext(io, limit=true), eqstrs, "", "") + end +end + +function showineq(io, indent, ineqs, σs, bs, chr, style) + if !isempty(ineqs) + print(io, '\n', indent) + if style == :bracket + ineqstrs = map((i,σ,b) -> UnquotedString(string("$chr[$i]", ineqstr(σ,b))), ineqs, σs, bs) + else + ineqstrs = map((i,σ,b) -> UnquotedString(string("$(chr)_$i", ineqstr(σ,b))), ineqs, σs, bs) + end + Base.show_vector(IOContext(io, limit=true), ineqstrs, "", "") + end +end +ineqstr(σ,b) = σ>0 ? "≥$b" : "≤$b" From dc2b38f593c67cd948ba6b136319fe3532ef2933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Thu, 25 Jan 2018 14:53:00 +0000 Subject: [PATCH 3/9] Remove Function from names --- src/NLSolversBase.jl | 2 +- src/objective_types/constraints.jl | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NLSolversBase.jl b/src/NLSolversBase.jl index 7d471bd..6e4faee 100644 --- a/src/NLSolversBase.jl +++ b/src/NLSolversBase.jl @@ -36,7 +36,7 @@ export AbstractObjective, clear! export AbstractConstraints, DifferentiableConstraints, - TwiceDifferentiableConstraints, ConstraintBounds, + TwiceDifferentiableConstraints, ConstraintBounds x_of_nans(x) = copy(x).=(eltype(x))(NaN) diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl index 950a2d5..b586eb1 100644 --- a/src/objective_types/constraints.jl +++ b/src/objective_types/constraints.jl @@ -85,30 +85,30 @@ abstract type AbstractConstraints end nconstraints(constraints::AbstractConstraints) = nconstraints(constraints.bounds) -struct DifferentiableConstraintsFunction{F,J,T} <: AbstractConstraints +struct DifferentiableConstraints{F,J,T} <: AbstractConstraints c!::F # c!(x, storage) stores the value of the constraint-functions at x jacobian!::J # jacobian!(x, storage) stores the Jacobian of the constraint-functions bounds::ConstraintBounds{T} end -function DifferentiableConstraintsFunction(c!, jacobian!, lx, ux, lc, uc) +function DifferentiableConstraints(c!, jacobian!, lx, ux, lc, uc) b = ConstraintBounds(lx, ux, lc, uc) - DifferentiableConstraintsFunction(c!, jacobian!, b) + DifferentiableConstraints(c!, jacobian!, b) end #TODO: is this constructor necessary? -DifferentiableConstraintsFunction(c!, jacobian!, bounds::ConstraintBounds) = - DifferentiableConstraintsFunction{typeof(c!), typeof(jacobian!), eltype(b)}(c!, jacobian!, b) +DifferentiableConstraints(c!, jacobian!, bounds::ConstraintBounds) = + DifferentiableConstraints{typeof(c!), typeof(jacobian!), eltype(b)}(c!, jacobian!, b) -function DifferentiableConstraintsFunction(lx::AbstractArray, ux::AbstractArray) +function DifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) bounds = ConstraintBounds(lx, ux, [], []) - DifferentiableConstraintsFunction(bounds) + DifferentiableConstraints(bounds) end -function DifferentiableConstraintsFunction(bounds::ConstraintBounds) +function DifferentiableConstraints(bounds::ConstraintBounds) c! = (x,c)->nothing J! = (x,J)->nothing - DifferentiableConstraintsFunction(c!, J!, bounds) + DifferentiableConstraints(c!, J!, bounds) end struct TwiceDifferentiableConstraints{F,J,H,T} <: AbstractConstraints From 6d64590b9feafeaf39cab35f475771a743aa6d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Fri, 16 Feb 2018 06:01:12 +0100 Subject: [PATCH 4/9] Remove unnecessary methods --- src/objective_types/constraints.jl | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl index b586eb1..00f0df8 100644 --- a/src/objective_types/constraints.jl +++ b/src/objective_types/constraints.jl @@ -13,7 +13,7 @@ # provide the variable-derivatives manually, which would be silly. # # This parametrization of the constraints gets "parsed" into a form -# that speeds and simplifies the algorithm, at the cost of many +# that speeds and simplifies the IPNewton algorithm, at the cost of many # additional variables. See `parse_constraints` for details. struct ConstraintBounds{T} @@ -86,8 +86,8 @@ abstract type AbstractConstraints end nconstraints(constraints::AbstractConstraints) = nconstraints(constraints.bounds) struct DifferentiableConstraints{F,J,T} <: AbstractConstraints - c!::F # c!(x, storage) stores the value of the constraint-functions at x - jacobian!::J # jacobian!(x, storage) stores the Jacobian of the constraint-functions + c!::F # c!(storage, x) stores the value of the constraint-functions at x + jacobian!::J # jacobian!(storage, x) stores the Jacobian of the constraint-functions bounds::ConstraintBounds{T} end @@ -96,10 +96,6 @@ function DifferentiableConstraints(c!, jacobian!, lx, ux, lc, uc) DifferentiableConstraints(c!, jacobian!, b) end -#TODO: is this constructor necessary? -DifferentiableConstraints(c!, jacobian!, bounds::ConstraintBounds) = - DifferentiableConstraints{typeof(c!), typeof(jacobian!), eltype(b)}(c!, jacobian!, b) - function DifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) bounds = ConstraintBounds(lx, ux, [], []) DifferentiableConstraints(bounds) @@ -112,17 +108,15 @@ function DifferentiableConstraints(bounds::ConstraintBounds) end struct TwiceDifferentiableConstraints{F,J,H,T} <: AbstractConstraints - c!::F - jacobian!::J - h!::H # Hessian of the barrier terms + c!::F # c!(storage, x) stores the value of the constraint-functions at x + jacobian!::J # jacobian!(storage, x) stores the Jacobian of the constraint-functions + h!::H # h!(storage, x) stores the hessian of the constraint functions bounds::ConstraintBounds{T} end function TwiceDifferentiableConstraints(c!, jacobian!, h!, lx, ux, lc, uc) b = ConstraintBounds(lx, ux, lc, uc) TwiceDifferentiableConstraints(c!, jacobian!, h!, b) end -TwiceDifferentiableConstraints(c!, jacobian!, h!, bounds::ConstraintBounds) = - TwiceDifferentiableConstraints{typeof(c!), typeof(jacobian!), typeof(h!), eltype(bounds)}(c!, jacobian!, h!, bounds) function TwiceDifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) bounds = ConstraintBounds(lx, ux, [], []) @@ -233,10 +227,6 @@ Base.show(io::IO, uqstr::UnquotedString) = print(io, uqstr.str) Base.array_eltype_show_how(a::Vector{UnquotedString}) = false, "" -if !isdefined(Base, :IOContext) - IOContext(io; kwargs...) = io -end - function showeq(io, indent, eq, val, chr, style) if !isempty(eq) print(io, '\n', indent) From 5e7bcfeca597f619c341063f5898f4b414582a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Fri, 16 Feb 2018 06:25:35 +0100 Subject: [PATCH 5/9] Add tests for constraints --- src/objective_types/constraints.jl | 11 ++++++++--- test/constraints.jl | 27 +++++++++++++++++++++++++++ test/runtests.jl | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 test/constraints.jl diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl index 00f0df8..3cc07e2 100644 --- a/src/objective_types/constraints.jl +++ b/src/objective_types/constraints.jl @@ -134,10 +134,15 @@ end ## Utilities function symmetrize(l, u) - if isempty(l) && !isempty(u) + if isempty(l) && isempty(u) + # This prevents ConstraintBounds{Any} if we call + # ConstraintBounds([], [], lc, uc) (or same for lc, uc) + # TODO: Is this a bad way to do this? + l = Array{Int}(0) + u = Array{Int}(0) + elseif isempty(l) && !isempty(u) l = fill!(similar(u), -Inf) - end - if !isempty(l) && isempty(u) + elseif !isempty(l) && isempty(u) u = fill!(similar(l), Inf) end # TODO: change to indices? diff --git a/test/constraints.jl b/test/constraints.jl new file mode 100644 index 0000000..5d54a52 --- /dev/null +++ b/test/constraints.jl @@ -0,0 +1,27 @@ +@testset "Constraints" begin + cb = ConstraintBounds([], [], [0.0], [0.0]) + @test NLSolversBase.nconstraints(cb) == 1 + @test NLSolversBase.nconstraints_x(cb) == 0 + @test eltype(cb) == Float64 + + cb = ConstraintBounds([0], [0], [], []) + @test NLSolversBase.nconstraints(cb) == 0 + @test NLSolversBase.nconstraints_x(cb) == 1 + @test eltype(cb) == Int + + cb = ConstraintBounds([], [3.0], [0.0], []) + @test NLSolversBase.nconstraints(cb) == 1 + @test NLSolversBase.nconstraints_x(cb) == 1 + @test cb.bx[1] == 3.0 + @test cb.σx[1] == -1 + @test cb.bc[1] == 0.0 + @test cb.σc[1] == 1 + @test eltype(cb) == Float64 + + cb = ConstraintBounds([1,2], [3,4.0], [], [10.,20.,30.]) + io = IOBuffer() + show(io, cb) + s = String(take!(io)) + + @test s == "ConstraintBounds:\n Variables:\n x[1]≥1.0, x[1]≤3.0, x[2]≥2.0, x[2]≤4.0\n Linear/nonlinear constraints:\n c_1≤10.0, c_2≤20.0, c_3≤30.0" +end diff --git a/test/runtests.jl b/test/runtests.jl index cb74936..fb4680b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,3 +4,4 @@ using Base.Test include("objective_types.jl") include("interface.jl") include("incomplete.jl") +include("constraints.jl") From a1745d9695f84303ed385ca67803e88471913d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Fri, 16 Feb 2018 06:31:21 +0100 Subject: [PATCH 6/9] Allow failures on nightly --- appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 0a63261..0dba1b9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,8 +2,10 @@ environment: matrix: - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" +matrix: + allow_failures: + - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" + - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" branches: only: From 7a5998ca36b107b1ea03ab0cb7c6669c71f8a0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Fri, 16 Feb 2018 06:38:17 +0100 Subject: [PATCH 7/9] Remove unecessary check --- src/objective_types/constraints.jl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl index 3cc07e2..a7088fe 100644 --- a/src/objective_types/constraints.jl +++ b/src/objective_types/constraints.jl @@ -134,13 +134,7 @@ end ## Utilities function symmetrize(l, u) - if isempty(l) && isempty(u) - # This prevents ConstraintBounds{Any} if we call - # ConstraintBounds([], [], lc, uc) (or same for lc, uc) - # TODO: Is this a bad way to do this? - l = Array{Int}(0) - u = Array{Int}(0) - elseif isempty(l) && !isempty(u) + if isempty(l) && !isempty(u) l = fill!(similar(u), -Inf) elseif !isempty(l) && isempty(u) u = fill!(similar(l), Inf) From e48b4440b292120e8b8fbcca5dd1ef77bfe47cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Fri, 16 Feb 2018 07:20:07 +0100 Subject: [PATCH 8/9] Test Constraint constructors --- src/NLSolversBase.jl | 2 +- src/objective_types/constraints.jl | 14 ++-- test/constraints.jl | 102 ++++++++++++++++++++++------- test/objective_types.jl | 4 +- test/runtests.jl | 2 + 5 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/NLSolversBase.jl b/src/NLSolversBase.jl index 6e4faee..fab3d0c 100644 --- a/src/NLSolversBase.jl +++ b/src/NLSolversBase.jl @@ -35,7 +35,7 @@ export AbstractObjective, only_j_and_fj, clear! -export AbstractConstraints, DifferentiableConstraints, +export AbstractConstraints, OnceDifferentiableConstraints, TwiceDifferentiableConstraints, ConstraintBounds x_of_nans(x) = copy(x).=(eltype(x))(NaN) diff --git a/src/objective_types/constraints.jl b/src/objective_types/constraints.jl index a7088fe..c649aea 100644 --- a/src/objective_types/constraints.jl +++ b/src/objective_types/constraints.jl @@ -85,26 +85,26 @@ abstract type AbstractConstraints end nconstraints(constraints::AbstractConstraints) = nconstraints(constraints.bounds) -struct DifferentiableConstraints{F,J,T} <: AbstractConstraints +struct OnceDifferentiableConstraints{F,J,T} <: AbstractConstraints c!::F # c!(storage, x) stores the value of the constraint-functions at x jacobian!::J # jacobian!(storage, x) stores the Jacobian of the constraint-functions bounds::ConstraintBounds{T} end -function DifferentiableConstraints(c!, jacobian!, lx, ux, lc, uc) +function OnceDifferentiableConstraints(c!, jacobian!, lx, ux, lc, uc) b = ConstraintBounds(lx, ux, lc, uc) - DifferentiableConstraints(c!, jacobian!, b) + OnceDifferentiableConstraints(c!, jacobian!, b) end -function DifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) +function OnceDifferentiableConstraints(lx::AbstractArray, ux::AbstractArray) bounds = ConstraintBounds(lx, ux, [], []) - DifferentiableConstraints(bounds) + OnceDifferentiableConstraints(bounds) end -function DifferentiableConstraints(bounds::ConstraintBounds) +function OnceDifferentiableConstraints(bounds::ConstraintBounds) c! = (x,c)->nothing J! = (x,J)->nothing - DifferentiableConstraints(c!, J!, bounds) + OnceDifferentiableConstraints(c!, J!, bounds) end struct TwiceDifferentiableConstraints{F,J,H,T} <: AbstractConstraints diff --git a/test/constraints.jl b/test/constraints.jl index 5d54a52..6852d4a 100644 --- a/test/constraints.jl +++ b/test/constraints.jl @@ -1,27 +1,79 @@ @testset "Constraints" begin - cb = ConstraintBounds([], [], [0.0], [0.0]) - @test NLSolversBase.nconstraints(cb) == 1 - @test NLSolversBase.nconstraints_x(cb) == 0 - @test eltype(cb) == Float64 - - cb = ConstraintBounds([0], [0], [], []) - @test NLSolversBase.nconstraints(cb) == 0 - @test NLSolversBase.nconstraints_x(cb) == 1 - @test eltype(cb) == Int - - cb = ConstraintBounds([], [3.0], [0.0], []) - @test NLSolversBase.nconstraints(cb) == 1 - @test NLSolversBase.nconstraints_x(cb) == 1 - @test cb.bx[1] == 3.0 - @test cb.σx[1] == -1 - @test cb.bc[1] == 0.0 - @test cb.σc[1] == 1 - @test eltype(cb) == Float64 - - cb = ConstraintBounds([1,2], [3,4.0], [], [10.,20.,30.]) - io = IOBuffer() - show(io, cb) - s = String(take!(io)) - - @test s == "ConstraintBounds:\n Variables:\n x[1]≥1.0, x[1]≤3.0, x[2]≥2.0, x[2]≤4.0\n Linear/nonlinear constraints:\n c_1≤10.0, c_2≤20.0, c_3≤30.0" + @testset "ConstraintBounds" begin + cb = ConstraintBounds([], [], [0.0], [0.0]) + @test NLSolversBase.nconstraints(cb) == 1 + @test NLSolversBase.nconstraints_x(cb) == 0 + @test cb.valc == [0.0] + @test eltype(cb) == Float64 + + cb = ConstraintBounds([0], [0], [], []) + @test NLSolversBase.nconstraints(cb) == 0 + @test NLSolversBase.nconstraints_x(cb) == 1 + @test cb.valx == [0] + @test eltype(cb) == Int + io = IOBuffer() + show(io, cb) + s = String(take!(io)) + @test s == "ConstraintBounds:\n Variables:\n x[1]=0\n Linear/nonlinear constraints:" + + cb = ConstraintBounds([], [3.0], [0.0], []) + @test NLSolversBase.nconstraints(cb) == 1 + @test NLSolversBase.nconstraints_x(cb) == 1 + @test cb.bx[1] == 3.0 + @test cb.σx[1] == -1 + @test cb.bc[1] == 0.0 + @test cb.σc[1] == 1 + @test eltype(cb) == Float64 + + cb = ConstraintBounds([1,2], [3,4.0], [], [10.,20.,30.]) + io = IOBuffer() + show(io, cb) + s = String(take!(io)) + + @test s == "ConstraintBounds:\n Variables:\n x[1]≥1.0, x[1]≤3.0, x[2]≥2.0, x[2]≤4.0\n Linear/nonlinear constraints:\n c_1≤10.0, c_2≤20.0, c_3≤30.0" + + end + + @testset "Once differentiable constraints" begin + lx, ux = (1.0,2.0) + odc = OnceDifferentiableConstraints([lx], [ux]) + @test odc.bounds.bx == [lx, ux] + @test isempty(odc.bounds.bc) + + prob = MVP.ConstrainedProblems.examples["HS9"] + cbd = prob.constraintdata + cb = ConstraintBounds(cbd.lx, cbd.ux, cbd.lc, cbd.uc) + + odc = OnceDifferentiableConstraints(cb) + @test odc.bounds == cb + + odc = OnceDifferentiableConstraints(cbd.c!, cbd.jacobian!, + cbd.lx, cbd.ux, cbd.lc, cbd.uc) + @test isempty(odc.bounds.bx) + @test isempty(odc.bounds.bc) + @test odc.bounds.valc == [0.0] + + #TODO: add tests calling c! and jacobian! + end + + @testset "Twice differentiable constraints" begin + lx, ux = (1.0,2.0) + odc = TwiceDifferentiableConstraints([lx], [ux]) + @test odc.bounds.bx == [lx, ux] + @test isempty(odc.bounds.bc) + + prob = MVP.ConstrainedProblems.examples["HS9"] + cbd = prob.constraintdata + cb = ConstraintBounds(cbd.lx, cbd.ux, cbd.lc, cbd.uc) + + odc = TwiceDifferentiableConstraints(cb) + @test odc.bounds == cb + + odc = TwiceDifferentiableConstraints(cbd.c!, cbd.jacobian!, cbd.h!, + cbd.lx, cbd.ux, cbd.lc, cbd.uc) + @test isempty(odc.bounds.bx) + @test isempty(odc.bounds.bc) + @test odc.bounds.valc == [0.0] + + end end diff --git a/test/objective_types.jl b/test/objective_types.jl index f82fe06..6048fbc 100644 --- a/test/objective_types.jl +++ b/test/objective_types.jl @@ -1,4 +1,6 @@ @testset "objective types" begin + # TODO: Use OptimTestProblems + # TODO: MultivariateProblems.UnconstrainedProblems.exampples["Exponential"] # Test example function exponential(x::Vector) @@ -53,7 +55,7 @@ @test value(od, zeros(2)) == od.F @test value(od, zeros(2)) == value(od) @test gradient(od) == gcache - + od = OnceDifferentiable(exponential, exponential_gradient!, x_seed) xrand = rand(2) value_gradient!(od, xrand) diff --git a/test/runtests.jl b/test/runtests.jl index fb4680b..24b4870 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,7 @@ using NLSolversBase using Base.Test +using OptimTestProblems +MVP = OptimTestProblems.MultivariateProblems include("objective_types.jl") include("interface.jl") From bfeb4968735d29424ecd809caca88b107cbae81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Nilsen=20Riseth?= Date: Sun, 18 Feb 2018 02:49:45 +0100 Subject: [PATCH 9/9] Require OptimTestProblems for tests --- test/REQUIRE | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/REQUIRE diff --git a/test/REQUIRE b/test/REQUIRE new file mode 100644 index 0000000..a7af81a --- /dev/null +++ b/test/REQUIRE @@ -0,0 +1 @@ +OptimTestProblems 1.2