Skip to content

Commit

Permalink
Create select function
Browse files Browse the repository at this point in the history
  • Loading branch information
abelsiqueira committed Oct 3, 2016
1 parent 06b54f0 commit dc1d1c9
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/classf.json

Large diffs are not rendered by default.

153 changes: 146 additions & 7 deletions src/classification.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import JSON

# Some enumerations and correspondance dicts
const objtypes = [:none, :constant, :linear, :quadratic, :sum_of_squares, :other]
const objtypes = ["none", "constant", "linear", "quadratic", "sum_of_squares", "other"]
const classdb_objtype = [x=>objtypes[i] for (i,x) in enumerate("NCLQSO")]

const contypes = [:unc, :only_fixed, :only_bounds, :network, :linear, :quadratic, :general]
const contypes = ["unc", "fixed_vars", "bounds", "network", "linear", "quadratic", "general"]
const classdb_contype = [x=>contypes[i] for (i,x) in enumerate("UXBNLQO")]

const origins = [:academic, :modelling, :real]
const origins = ["academic", "modelling", "real"]
const classdb_origin = [x=>origins[i] for (i,x) in enumerate("AMR")]

"""`create_class()`
Creates the file `classf.json`, running each problem in `\$MASTSIF/CLASSF.DB` and
extracting the necessary information. It should be left alone, unless you think
it is not updated. If you do, please open an issue at
[https://github.com/JuliaSmoothOptimizers/CUTEst.jl](https://github.com/JuliaSmoothOptimizers/CUTEst.jl)
"""
function create_class()
classdb = open(readlines, joinpath(ENV["MASTSIF"], "CLASSF.DB"))
problems = Dict()
Expand Down Expand Up @@ -43,15 +50,147 @@ function create_class()
:ineq_below => length(nlp.meta.jlow),
:ineq_above => length(nlp.meta.jupp),
:ineq_both => length(nlp.meta.jrng),
:linear => length(nlp.meta.nlin),
:nonlinear => length(nlp.meta.nnln)
:linear => nlp.meta.nlin,
:nonlinear => nlp.meta.nnln
)
)

cutest_finalize(nlp)
println("done")
end
open("classf.json", "w") do jsonfile
open(joinpath(dirname(@__FILE__), "classf.json"), "w") do jsonfile
JSON.print(jsonfile, problems)
end
end

"""select(;min_var=0, max_var=Inf, min_con=0, max_con=Inf,
objtype=*, contype=*,
only_free_var=false, only_bnd_var=false,
only_linear_con=false, only_nonlinear_con=false,
only_equ_con=false, only_ineq_con=false,
custom_filter=*)
Returns a subset of the CUTEst problems using the classification file
`classf.json`. This file is export together with the package, so if you have an
old CUTEst installation, it can lead to inconsistencies.
- `min_var` and `max_var` set the number of variables in the problem;
- `min_con` and `max_con` set the number of constraints in the problem
(e.g., use `max_con=0` for unconstrained or `min_con=1` for constrained)
- `only_*` flags are self-explaining. Note that they appear in conflicting
pairs. Both can be false, but only one can be true.
- `objtype` is the classification of the objective function according to the
[MASTSIF classification file](http://www.cuter.rl.ac.uk//Problems/classification.shtml).
It can be a number, a symbol, a string, or an array of those.
1, :none or "none" means there is no objective function;
2, :constant or "constant" means the objective function is a constant;
3, :linear or "linear" means the objective function is a linear functional;
4, :quadratic or "quadratic" means the objective function is quadratic;
5, :sum_of_squares or "sum_of_squares" means the objective function is a sum of squares
6, :other or "other" means the objective function is none of the above.
- `contype` is the classification of the constraints according to the same
MASTSIF classification file.
1, :unc or "unc" means there are no constraints at all;
2, :fixed_vars or "fixed_vars" means the only constraints are fixed variables;
3, :bounds or "bounds" means the only constraints are bounded variables;
4, :network or "network" means the constraints represent the adjacency matrix of a (linear) network;
5, :linear or "linear" means the constraints are linear;
6, :quadratic or "quadratic" means the constraints are quadratic;
7, :other or "other" means the constraints are more general.
- `custom_filter` is a function to be applied to the problem data, which is a
dict with the following fields:
"objtype" - String one of the above objective function types
"contype" - String one of the above constraint types
"regular" - Bool whether the problem is regular or not
"derivative_order" - Int order of the highest derivative available
"origin" - String origin of the problem: "academic", "modelling" or "real"
"has_interval_var" - Bool whether it has interval variables
"variables" - Dict with the following fields
"can_choose" - Bool whether you can change the number of variables via parameters
"number" - Int the number of variables (if `can_choose`, the default)
"fixed" - Int the number of fixed variables
"free" - Int the number of free variables
"bounded_below" - Int the number of variables bounded only from below
"bounded_above" - Int the number of variables bounded only from above
"bounded_both" - Int the number of variables bounded from below and above
"constraints" - Dict with the following fields
"can_choose" - Bool whether you can change the number of constraints via parameters
"number" - Int the number of constraints (if `can_choose`, the default)
"equality" - Int the number of equality constraints
"ineq_below" - Int the number of inequalities of the form c(x) ≧ cl
"ineq_above" - Int the number of inequalities of the form c(x) ≦ cu
"ineq_both" - Int the number of inequalities of the form cl ≦ c(x) ≦ cu
"linear" - Int the number of linear constraints
"nonlinear" - Int the number of nonlinear constraints
For instance, if you'd like to choose only problems with fixed number of
variables, you can pass
custom_filter=x->x["variables"]["can_choose"]==false
"""
function select(;min_var=0, max_var=Inf, min_con=0, max_con=Inf,
objtype=objtypes, contype=contypes,
only_free_var=false, only_bnd_var=false,
only_linear_con=false, only_nonlinear_con=false,
only_equ_con=false, only_ineq_con=false,
custom_filter::Function=x->true)
# Checks for conflicting option
@assert !only_free_var || !only_bnd_var
@assert !only_linear_con || !only_nonlinear_con
@assert !only_equ_con || !only_ineq_con

if typeof(objtype) == Int
objtype = [objtypes[objtype]]
elseif typeof(objtype) == Symbol
objtype = [string(objtype)]
elseif typeof(objtype) <: AbstractString
objtype = [objtype]
elseif typeof(objtype) == Vector{Int}
objtype = objtypes[objtype]
elseif typeof(objtype) == Vector{Symbol}
objtype = map(string, objtype)
end
if typeof(contype) == Int
contype = [contypes[contype]]
elseif typeof(contype) == Symbol
contype = [string(contype)]
elseif typeof(contype) <: AbstractString
contype = [contype]
elseif typeof(contype) == Vector{Int}
contype = contypes[contype]
elseif typeof(contype) == Vector{Symbol}
contype = map(string, contype)
end

@assert objtype objtypes
@assert contype contypes

data = JSON.parsefile(joinpath(dirname(@__FILE__), "classf.json"))
problems = keys(data)
selection = []
for p in problems
pv = data[p]["variables"]
pc = data[p]["constraints"]
nvar, ncon = pv["number"], pc["number"]
if nvar < min_var || nvar > max_var ||
ncon < min_con || ncon > max_con ||
(only_free_var && pv["free"] < nvar) ||
(only_bnd_var && pv["free"] > 0) ||
(only_linear_con && pc["linear"] < ncon) ||
(only_nonlinear_con && pc["linear"] > 0) ||
(only_equ_con && pc["equality"] < ncon) ||
(only_ineq_con && pc["equality"] > 0) ||
!(data[p]["objtype"] in objtype) ||
!(data[p]["contype"] in contype) ||
!(custom_filter(data[p]))
continue
end
push!(selection, p)
end
return selection
end
1 change: 1 addition & 0 deletions test/REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
FactCheck
MathProgBase
Ipopt
JuMP
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Base.Test, CUTEst, Ipopt, JuMP, MathProgBase, NLPModels
using Base.Test, CUTEst, FactCheck, Ipopt, JuMP, MathProgBase, NLPModels

for problem in ["HS32", "HS4"]
global problem
Expand All @@ -12,6 +12,7 @@ end

include("consistency.jl")
include("test_mpb.jl")
include("test_select.jl")

problems = randsubseq(readdir(get(ENV, "MASTSIF", "") ), 0.01)

Expand Down
160 changes: 160 additions & 0 deletions test/test_select.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
small_problem = "HS4"
large_problem = "AUG3D"
unc_problem = "BARD"
con_problem = "HS51"
equ_problem = "BT1"
ineq_problem = "HS15"
lin_problem = "ANTWERP"
nln_problem = "HS99"
free_problem = "ROSENBR"
bnd_problem = "HS99"

objtypes_problems = ["CHANDHEQ", "HS8", "TRO5X5", "BT8", "HIMMELBF", "JIMACK"]
contypes_problems = ["DIXMAANI", "AIRCRFTB", "BQPGAUSS", "DALLASM", "HS54", "HANGING", "SYNTHES1"]

facts("CUTEst selection tool") do
problems = filter(x->ismatch(r"SIF$",x), readdir(ENV["MASTSIF"]))
np = length(problems)
@fact np --> length(CUTEst.select())
context("Variables") do
selection = CUTEst.select(max_var=10)
n = length(selection)
@fact small_problem in selection --> true
@fact large_problem in selection --> false
selection = CUTEst.select(min_var=11)
@fact small_problem in selection --> false
@fact large_problem in selection --> true
@fact np --> n + length(selection)
selection = CUTEst.select(only_free_var=true)
@fact free_problem in selection --> true
@fact bnd_problem in selection --> false
selection = CUTEst.select(only_bnd_var=true)
@fact free_problem in selection --> false
@fact bnd_problem in selection --> true
end

context("Constraints") do
selection = CUTEst.select(max_con = 0)
n = length(selection)
@fact unc_problem in selection --> true
@fact con_problem in selection --> false
@fact equ_problem in selection --> false
@fact ineq_problem in selection --> false
selection = CUTEst.select(min_con = 1)
@fact unc_problem in selection --> false
@fact con_problem in selection --> true
@fact equ_problem in selection --> true
@fact ineq_problem in selection --> true
@fact np --> n + length(selection)

selection = CUTEst.select(only_equ_con=true)
@fact equ_problem in selection --> true
@fact ineq_problem in selection --> false
selection = CUTEst.select(only_ineq_con=true)
@fact equ_problem in selection --> false
@fact ineq_problem in selection --> true

selection = CUTEst.select(only_linear_con=true)
@fact lin_problem in selection --> true
@fact nln_problem in selection --> false
selection = CUTEst.select(only_nonlinear_con=true)
@fact lin_problem in selection --> false
@fact nln_problem in selection --> true
end

context("Objective function types") do
n = length(CUTEst.objtypes)
npi = 0
for i = 1:n
selection = CUTEst.select(objtype=i)
npi += length(selection)
@fact objtypes_problems[i] in selection --> true
for j = setdiff(1:n, i)
@fact objtypes_problems[j] in selection --> false
end
selection_alt = CUTEst.select(objtype=CUTEst.objtypes[i])
@fact sort(selection_alt) --> sort(selection)
selection_alt = CUTEst.select(objtype=symbol(CUTEst.objtypes[i]))
@fact sort(selection_alt) --> sort(selection)
end
@fact npi --> np
for i = 1:n-1
for subset in combinations(1:n, i)
selection = CUTEst.select(objtype=subset)
for j in subset
@fact objtypes_problems[j] in selection --> true
end
for j in setdiff(1:n, subset)
@fact objtypes_problems[j] in selection --> false
end
end
end
end

context("Constraints types") do
n = length(CUTEst.contypes)
npi = 0
for i = 1:n
selection = CUTEst.select(contype=i)
npi += length(selection)
@fact contypes_problems[i] in selection --> true
for j = setdiff(1:n, i)
@fact contypes_problems[j] in selection --> false
end
selection_alt = CUTEst.select(contype=CUTEst.contypes[i])
@fact sort(selection_alt) --> sort(selection)
selection_alt = CUTEst.select(contype=symbol(CUTEst.contypes[i]))
@fact sort(selection_alt) --> sort(selection)
end
@fact npi --> np
for i = 1:n-1
for subset in combinations(1:n, i)
selection = CUTEst.select(contype=subset)
for j in subset
@fact contypes_problems[j] in selection --> true
end
for j in setdiff(1:n, subset)
@fact contypes_problems[j] in selection --> false
end
end
end
end

context("Consistency") do
set1 = CUTEst.select(contype=["unc"])
set2 = CUTEst.select(max_con=0, only_free_var=true)
println("Mastsif says U but are not unc free")
println(setdiff(set1, set2))
println("Are unc and free but Mastsif is not U")
println(setdiff(set2, set1))
#@fact sort(set1) --> sort(set2)

set1 = CUTEst.select(contype=["fixed_vars"])
set2 = CUTEst.select(max_con=0, custom_filter=x ->(
x["variables"]["fixed"] > 0) && (x["variables"]["fixed"] +
x["variables"]["free"] == x["variables"]["number"]))
println("Mastsif says X but aren't")
println(setdiff(set1, set2))
println("Unconstrained with some fixed variables, no bounds but not X")
println(setdiff(set2, set1))
#@fact sort(set1) --> sort(set2)

set1 = CUTEst.select(contype=["unc", "fixed_vars", "bounds"])
set2 = CUTEst.select(max_con=0)
println("Mastsif says U, X or B, but are not unconstrained")
println(setdiff(set1, set2))
println("Unconstrained problems, but not U, X or B")
println(setdiff(set2, set1))
#@fact sort(set1) --> sort(set2)

set1 = CUTEst.select(contype=["linear"])
set2 = CUTEst.select(min_con=1, only_linear_con=true)
println("Mastsif says L, but decoding gives otherwise")
println(setdiff(set1, set2))
println("Decoding gives linear, but Mastsif disagrees")
println(setdiff(set2, set1))
#@fact sort(set1) --> sort(set2)
end
end

FactCheck.exitstatus()

0 comments on commit dc1d1c9

Please sign in to comment.