diff --git a/.travis.yml b/.travis.yml index 10d0a4d2..fa18edce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,3 +17,7 @@ notifications: script: - echo "**** running Docker" - docker run --env-file travis_docker_env.list -t -a STDOUT -a STDIN -a STDERR -v $PWD:/mnt leethargo/scip-julia /mnt/travis_docker_test_script.sh $TRAVIS_JULIA_VERSION +after_success: + # push coverage results to Coveralls, .cov files were copied back by script above + - echo "**** submitting coverage information" + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(Codecov.process_folder())' diff --git a/README.md b/README.md index bd78cedc..d99daec3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Julia interface to [SCIP](http://scip.zib.de) solver. [![Build Status](https://travis-ci.org/SCIP-Interfaces/SCIP.jl.svg?branch=master)](https://travis-ci.org/SCIP-Interfaces/SCIP.jl) +[![Coverage Status](https://coveralls.io/repos/github/SCIP-Interfaces/SCIP.jl/badge.svg?branch=master)](https://coveralls.io/github/SCIP-Interfaces/SCIP.jl?branch=master) +[![codecov](https://codecov.io/gh/SCIP-Interfaces/SCIP.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/SCIP-Interfaces/SCIP.jl) ## Related Projects diff --git a/src/mpb_interface.jl b/src/mpb_interface.jl index 4c1f3bcd..45846f4e 100644 --- a/src/mpb_interface.jl +++ b/src/mpb_interface.jl @@ -246,13 +246,13 @@ MathProgBase.getnodecount(m::SCIPLinearQuadraticModel) = error("Not implemented function MathProgBase.addsos1!(m::SCIPLinearQuadraticModel, idx, weight) nidx = Cint(length(idx)) - cidx = convert(Vector{Cint}, idx - 1) + cidx = convert(Vector{Cint}, idx .- 1) _addSOS1(m, nidx, cidx, weight, Ptr{Cint}(C_NULL)) end function MathProgBase.addsos2!(m::SCIPLinearQuadraticModel, idx, weight) nidx = Cint(length(idx)) - cidx = convert(Vector{Cint}, idx - 1) + cidx = convert(Vector{Cint}, idx .- 1) _addSOS2(m, nidx, cidx, weight, Ptr{Cint}(C_NULL)) end @@ -286,8 +286,10 @@ function MathProgBase.addquadconstr!(m::SCIPLinearQuadraticModel, linearidx, lin crhs = rhs end _addQuadCons(m, Cint(length(linearidx)), convert(Vector{Cint}, linearidx .- 1), - linearval, Cint(length(quadrowidx)), convert(Vector{Cint}, quadrowidx .- 1), - convert(Vector{Cint}, quadcolidx .- 1), quadval, clhs, crhs, Ptr{Cint}(C_NULL)) + convert(Vector{Cdouble}, linearval), Cint(length(quadrowidx)), + convert(Vector{Cint}, quadrowidx .- 1), + convert(Vector{Cint}, quadcolidx .- 1), quadval, clhs, crhs, + Ptr{Cint}(C_NULL)) end MathProgBase.getquadconstrsolution(m::SCIPLinearQuadraticModel) = error("Not implemented for SCIP.jl") @@ -419,7 +421,7 @@ end function MathProgBase.cbaddsolution!(d::SCIPHeurCallbackData) # check for unspecified values (NaN) - if findfirst(isnan, d.sol) == 0 + if findfirst(isnan, d.sol) == nothing # add solution that was filled from cbsetsolutionvalue _heurAddSolution(d.csip_heurdata, d.sol) else diff --git a/test/REQUIRE b/test/REQUIRE index a9c6a9ac..7d68054c 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -1,2 +1 @@ -JuMP 0.16 0.19 -OffsetArrays 0.8 +JuMP diff --git a/test/csip_tests.jl b/test/csip_tests.jl new file mode 100644 index 00000000..c69c6260 --- /dev/null +++ b/test/csip_tests.jl @@ -0,0 +1,399 @@ +# Translation of tests from CSIP wrapper + +solver = SCIPSolver("display/verblevel", 0) + +@testset "LP" begin + m = Model(solver=solver) + @variable(m, x >= 0) + @variable(m, y >= 0) + @constraint(m, 2x + y <= 1.5) + @objective(m, :Min, -x) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ -0.75 + @test getvalue(x) ≈ 0.75 + @test getvalue(y) ≈ 0.0 +end + +@testset "MIP" begin + m = Model(solver=solver) + @variable(m, x[1:5], Bin) + @constraint(m, 2x[1] + 8x[2] + 4x[3] + 2x[4] + 5x[5] <= 10) + @objective(m, :Min, -5x[1] - 3x[2] - 2x[3] - 7x[4] - 4x[5]) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ -16.0 + @test getvalue(x) ≈ [1.0, 0.0, 0.0, 1.0, 1.0] +end + +@testset "MIP2 (unbounded)" begin + m = Model(solver=solver) + @variable(m, x, Int) + @objective(m, :Min, x) + + status = solve(m, suppress_warnings=true) + @test status == :Unbounded +end + +@testset "MIP3 (infeasible)" begin + m = Model(solver=solver) + @variable(m, x, Int) + @constraint(m, x >= 2) + @constraint(m, x <= 1) + + status = solve(m, suppress_warnings=true) + @test status == :Infeasible +end + +@testset "SOCP" begin + m = Model(solver=solver) + @variable(m, x) + @variable(m, y) + @variable(m, t >= 0) + @constraint(m, x + y >= 1) + @constraint(m, x^2 + y^2 <= t^2) + @objective(m, :Min, t) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ sqrt(0.5) atol=1e-5 + @test getvalue(t) ≈ sqrt(0.5) atol=1e-5 + @test getvalue(x) ≈ 0.5 atol=1e-5 + @test getvalue(y) ≈ 0.5 atol=1e-5 +end + +@testset "NLP" begin + m = Model(solver=solver) + @variable(m, x <= 0) + @variable(m, y <= 0) + @variable(m, z) + @NLconstraint(m, z^2 <= 1) + @NLobjective(m, :Max, x + y - z^3) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ 1.0 atol=1e-5 + @test getvalue(x) ≈ 0.0 atol=1e-5 + @test getvalue(y) ≈ 0.0 atol=1e-5 + @test getvalue(z) ≈ -1.0 atol=1e-5 +end + +@testset "NLP (no objective)" begin + m = Model(solver=solver) + @variable(m, z) + @NLconstraint(m, z^2 <= 1) + + status = solve(m) + @test status == :Optimal + @test getvalue(z)^2 <= 1.0 + 1e-5 +end + +@testset "quad obj" begin + m = Model(solver=solver) + @variable(m, x) + @variable(m, y) + @constraint(m, x + y >= 1) + @objective(m, :Min, x^2 + y^2) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ 0.5 atol=1e-5 + @test getvalue(x) ≈ 0.5 atol=1e-5 + @test getvalue(y) ≈ 0.5 atol=1e-5 +end + +@testset "lazy" begin + m = Model(solver=solver) + @variable(m, x <= 2) + @variable(m, y <= 2) + @objective(m, :Max, 0.5x + y) + + function lazycb(cb) + if getvalue(x) + getvalue(y) > 3.0 + @lazyconstraint(cb, x + y <= 3) + end + end + addlazycallback(m, lazycb) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ 2.5 atol=1e-5 + @test getvalue(x) ≈ 1.0 atol=1e-5 + @test getvalue(y) ≈ 2.0 atol=1e-5 +end + +@testset "lazy2 (integer)" begin + m = Model(solver=solver) + @variable(m, x <= 100.5, Int) + @objective(m, :Min, -x) + + function lazycb(cb) + if getvalue(x) > 10.5 + @lazyconstraint(cb, x <= 10.5) + end + end + addlazycallback(m, lazycb) + + status = solve(m) + @test status == :Optimal + @test getobjectivevalue(m) ≈ -10.0 atol=1e-5 + @test getvalue(x) ≈ 10.0 atol=1e-5 +end + +@testset "lazy interrupt" begin + m = Model(solver=solver) + @variable(m, x >= 1.5, Int) + + function lazycb(cb) + return JuMP.StopTheSolver + end + addlazycallback(m, lazycb) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit +end + +@testset "obj sense" begin + m = Model(solver=solver) + @variable(m, -2.3 <= x <= 4.2) + @objective(m, :Min, x) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ -2.3 + + # change sense and resolve + setobjectivesense(m, :Max) + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 4.2 + + # change sense and resolve + setobjectivesense(m, :Min) + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ -2.3 +end + +@testset "sos1" begin + m = Model(solver=solver) + @variable(m, 0 <= x <= 1) + @variable(m, 0 <= y <= 1) + @variable(m, 0 <= z <= 1) + addSOS1(m, [x, y, z]) + @objective(m, :Max, 2x + 3y + 4z) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 0.0 + @test getvalue(y) ≈ 0.0 + @test getvalue(z) ≈ 1.0 +end + +@testset "sos2" begin + m = Model(solver=solver) + @variable(m, 0 <= x <= 1) + @variable(m, 0 <= y <= 1) + @variable(m, 0 <= z <= 1) + addSOS2(m, [x, y, z]) + @objective(m, :Max, 2x + 3y + 4z) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 0.0 + @test getvalue(y) ≈ 1.0 + @test getvalue(z) ≈ 1.0 +end + +@testset "sos1 & sos2" begin + m = Model(solver=solver) + @variable(m, 0 <= x <= 1) + @variable(m, 0 <= y <= 1) + @variable(m, 0 <= z <= 1) + addSOS1(m, [ y, z]) + addSOS2(m, [x, y, z]) + @objective(m, :Max, 2x + 3y + 4z) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 1.0 + @test getvalue(y) ≈ 1.0 + @test getvalue(z) ≈ 0.0 +end + +# not adding test: manythings +# not adding test: doublelazy + +@testset "changeprob" begin + m = Model(solver=solver) + @variable(m, x, Bin) + @variable(m, y, Bin) + @variable(m, z, Bin) # have to add it to original model, + # since addvar! not implemented in SCIP.jl + @constraint(m, x + y <= 1) + @objective(m, :Max, x + 2y) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 0.0 + @test getvalue(y) ≈ 1.0 + + # now change it + @constraint(m, x + y + z <= 2) + @constraint(m, y + z <= 1) + @objective(m, :Max, x + 2y + 2z) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 1.0 + @test getvalue(y) ≈ 0.0 + @test getvalue(z) ≈ 1.0 +end + +@testset "changequadprob" begin + m = Model(solver=solver) + @variable(m, x) + @variable(m, y) + @constraint(m, x + y >= 1) + @objective(m, :Min, x^2 + y^2) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 0.5 atol=1e-5 + @test getvalue(y) ≈ 0.5 atol=1e-5 + + # now change it + @constraint(m, x + y == 1) + @objective(m, :Max, -x^2 + y) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ -0.5 atol=1e-5 + @test getvalue(y) ≈ 1.5 atol=1e-5 +end + +@testset "changevartype" begin + m = Model(solver=solver) + @variable(m, 0 <= x <= 9) + @variable(m, 0 <= y <= 9) + @constraint(m, x + y >= 1.5) + @objective(m, :Min, 2x + 3y) + + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 1.5 + @test getvalue(y) ≈ 0.0 + + # resolve with x as integer + setcategory(x, :Int) + status = solve(m) + @test status == :Optimal + @test getvalue(x) ≈ 1.0 + @test getvalue(y) ≈ 0.5 +end + +@testset "initialsol" begin + s = SCIPSolver("display/verblevel", 0, + "limits/solutions", 1, + "heuristics/trivial/freq", -1) + m = Model(solver=s) + @variable(m, 10 <= x <= 100, Int, start=23) + @objective(m, :Min, 2x) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit + @test getobjectivevalue(m) ≈ 2*23 + @test getvalue(x) ≈ 23 +end + +@testset "initialsol NLP" begin + s = SCIPSolver("display/verblevel", 0, + "limits/solutions", 1, + "heuristics/trivial/freq", -1) + m = Model(solver=s) + @variable(m, x <= 0, start=0) + @variable(m, y <= 0, start=-1) + @variable(m, z, start=-0.5) + @NLconstraint(m, z^2 <= 1) + @NLobjective(m, :Max, x + y - z^3) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit + @test getobjectivevalue(m) ≈ 0 - 1.0 + 0.5^3 + @test getvalue(x) ≈ 0.0 + @test getvalue(y) ≈ -1.0 + @test getvalue(z) ≈ -0.5 +end + +@testset "initialsol partial" begin + s = SCIPSolver("display/verblevel", 0, + "limits/solutions", 1, + "heuristics/trivial/freq", -1) + m = Model(solver=s) + @variable(m, 0 <= x <= 2, Int, start=1) + @variable(m, 0 <= y <= 2, Int) + @constraint(m, x + y == 2) + @objective(m, :Max, x + 2y) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit + @test getobjectivevalue(m) ≈ 3 + @test getvalue(x) ≈ 1 + @test getvalue(y) ≈ 1 +end + +@testset "initialsol NLP partial" begin + s = SCIPSolver("display/verblevel", 0, + "limits/solutions", 1, + "heuristics/trivial/freq", -1) + m = Model(solver=s) + @variable(m, x <= 0) + @variable(m, y <= 0, start=-1) + @variable(m, z, start=-0.5) + @NLconstraint(m, z^2 <= 1) + @NLobjective(m, :Max, x + y - z^3) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit + @test getobjectivevalue(m) ≈ 0 - 1.0 + 0.5^3 + @test getvalue(x) ≈ 0.0 + @test getvalue(y) ≈ -1.0 + @test getvalue(z) ≈ -0.5 +end + +@testset "heurcb" begin + s = SCIPSolver("display/verblevel", 0, + "limits/solutions", 1, + "heuristics/feaspump/freq", -1, + "heuristics/randrounding/freq", -1, + "heuristics/rounding/freq", -1, + "heuristics/shiftandpropagate/freq", -1, + "heuristics/shifting/freq", -1, + "heuristics/simplerounding/freq", -1, + "heuristics/trivial/freq", -1, + "presolving/maxrounds", 0, + "separating/maxroundsroot", 0) + m = Model(solver=s) + @variable(m, 0 <= x <= 3, Int) + @variable(m, 0 <= y <= 3, Int) + @constraint(m, 2x + 3y >= 6) + @constraint(m, 3x + 2y >= 6) + @objective(m, :Min, x + y) + + function heurcb(cb) + setsolutionvalue(cb, x, 2.0) + setsolutionvalue(cb, y, 2.0) + addsolution(cb) + end + addheuristiccallback(m, heurcb) + + status = solve(m, suppress_warnings=true) + @test status == :UserLimit + @test getvalue(x) ≈ 2.0 + @test getvalue(y) ≈ 2.0 +end + +# not adding test: params (not available through SCIP.jl) +# not adding test: prefix (only about output) diff --git a/test/mixintprog.jl b/test/mixintprog.jl deleted file mode 100644 index 9af65673..00000000 --- a/test/mixintprog.jl +++ /dev/null @@ -1,9 +0,0 @@ -@testset "solving MIP with mixintprog" begin - solver = SCIPSolver("display/verblevel", 0) - - # integer knapsack problem - sol = mixintprog(-[5,3,2,7,4],Float64[2 8 4 2 5],'<',10,:Int,0,1,solver) - @test sol.status == :Optimal - @test sol.objval ≈ -16.0 - @test sol.sol ≈ [1.0, 0.0, 0.0, 1.0, 1.0] -end diff --git a/test/more_tests.jl b/test/more_tests.jl index 9d810932..40035879 100644 --- a/test/more_tests.jl +++ b/test/more_tests.jl @@ -72,7 +72,7 @@ end # This makes the initial point feasible setvalue(x[3], 10.0) - solve(m) + solve(m, suppress_warnings=true) @test getvalue(x)[3] == 10.0 end @@ -89,7 +89,7 @@ end @objective(m, Max, x + 2y) - solve(m) + solve(m, suppress_warnings=true) @test getvalue(x) ≈ 1.0 @test getvalue(y) ≈ 1.0 diff --git a/test/mpb_tests.jl b/test/mpb_tests.jl new file mode 100644 index 00000000..5658fb9c --- /dev/null +++ b/test/mpb_tests.jl @@ -0,0 +1,27 @@ +testdir = joinpath(dirname(pathof(MathProgBase)), "..", "test") +solver = SCIPSolver("display/verblevel", 0) + +## can't test these because SCIP does not give access to dual solution +# include(joinpath(testdir, "linprog.jl")) +# linprogtest(solver) + +include(joinpath(testdir, "mixintprog.jl")) +mixintprogtest(solver) + +## can't use setquadobj! or get duals +include(joinpath(testdir, "quadprog.jl")) +# quadprogtest(solver) +# qpdualtest(solver) +socptest(solver) + +## can't use tests based on NLP evalutors (only expr graphs) +# include(joinpath(testdir, "nlp.jl")) +# nlptest(solver) +# nlptest_nohessian(solver) +# convexnlptest(solver) +# rosenbrocktest(solver) + +## can't test methods that are not implemented with SCIP.jl +# include(joinpath(testdir, "linproginterface.jl")) +# linprogsolvertest(solver) +# linprogsolvertestextra(solver) diff --git a/test/runtests.jl b/test/runtests.jl index 7af0cdaf..72ec93e7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,51 +1,16 @@ using Test using SCIP using MathProgBase - using JuMP -using OffsetArrays # for JuMP/test/model.jl - -include("more_tests.jl") -include("mixintprog.jl") - -joinpath(dirname(pathof(JuMP)),"test","solvers.jl") -const solver = SCIPSolver() -MathProgBase.setparameters!(solver, Silent=true, TimeLimit=300.0) -const heursolver = SCIPSolver("display/verblevel", 0, - "presolving/maxrounds", 0, - "separating/maxrounds", 0, - "separating/maxroundsroot", 0) -lp_solvers = [] -ip_solvers = [solver] -sos_solvers = [solver] -quad_solvers = [] -semi_solvers = [] -quad_mip_solvers = [] -lazy_solvers = [solver] -lazy_soc_solvers = [solver] -lazylocal_solvers = [] -cut_solvers = [] -cutlocal_solvers = [] -heur_solvers = [heursolver] -info_solvers = [] -rsoc_solvers = [solver] -minlp_solvers = [solver] -ip_dual_solvers = [solver] -soc_solvers = [solver] -nlp_solvers = [] +@testset "MathProgBase tests" begin + include("mpb_tests.jl") +end -# nlp_solvers fails because: -# - some dual information: fixed by deleting getconstrduals -# - some strange expressions: can be fixed by adding SCIPSolver to the excluded -# list in "Test ifelse". This is on JuMP's side -# - numerical tolerances: I tried setting SCIP feastol to 1e-9 and still got -# numerical issues. However, the solution is feasible for SCIP. -# nlp_solvers = [solver] +@testset "CSIP tests" begin + include("csip_tests.jl") +end -joinpath(dirname(pathof(JuMP)),"test","model.jl") -joinpath(dirname(pathof(JuMP)),"test","probmod.jl") -joinpath(dirname(pathof(JuMP)),"test","qcqpmodel.jl") -joinpath(dirname(pathof(JuMP)),"test","callback.jl") -joinpath(dirname(pathof(JuMP)),"test","nonlinear.jl") -joinpath(dirname(pathof(JuMP)),"test","solvers.jl") +@testset "Other tests" begin + include("more_tests.jl") +end