diff --git a/Project.toml b/Project.toml index ea659b7..ecf2347 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "COPSBenchmark" uuid = "3c263af4-7bba-497b-afe4-00c59a0a48a5" -version = "0.2.0" -authors = ["MadSuite "] +version = "0.3.0" +authors = ["pacaud", "sshin23", "and contributors"] [deps] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -17,5 +17,10 @@ COPSBenchmarkExaModelsCompiler = "ExaModelsCompiler" COPSBenchmarkJuMP = "JuMP" [compat] +# ExaModels and ExaModelsCompiler are deliberately absent -- see the note in +# LuksanVlcekBenchmark: the recipe API is in no registered ExaModels version and +# ExaModelsCompiler is unregistered, so any bound would name a version that does +# not exist. JuMP = "^1.19" +Random = "1" julia = "1.11" diff --git a/test/Project.toml b/test/Project.toml index 355512a..b34a8f1 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,4 +1,5 @@ [deps] +CNLPModels = "e8ddee8d-ce21-405b-9ec9-45cea2c1b284" ExaModels = "1037b233-b668-4ce9-9b63-f9f681f55dd2" ExaModelsCompiler = "3d1e9a26-5b74-4f0c-9a2b-7c8f4e11d3a7" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" @@ -29,3 +30,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" # the first release carrying the API. ExaModels = {rev = "a9fbf9ccb5afcfb973bc09ed10d7e6b5f24e07a3", url = "https://github.com/madsuite-org/ExaModels.jl"} ExaModelsCompiler = {rev = "a9fbf9ccb5afcfb973bc09ed10d7e6b5f24e07a3", url = "https://github.com/madsuite-org/ExaModels.jl", subdir = "ExaModelsCompiler"} +CNLPModels = {rev = "master", url = "https://github.com/madsuite-org/CNLPModels.jl"} diff --git a/test/runtests.jl b/test/runtests.jl index d1575ab..83d888c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,7 @@ using JuMP using Ipopt using ExaModels using ExaModelsCompiler +import CNLPModels using NLPModels using NLPModelsIpopt using NLPModelsJuMP @@ -281,50 +282,32 @@ RECIPE_INSTANCES = [ @test (again.meta.nvar, again.meta.ncon, again.meta.x0) == first_meta end -# `compile_all` is the provider's whole AOT surface: which problems it offers, -# the arguments each is closed with, and the selection contract. Most of that -# is testable without invoking a compiler, because `select` validates names -# before `compile_library` is ever reached — so the error paths below exercise -# `compile_all` itself rather than a stand-in. One real compile follows, -# because a list that assembles is not evidence that anything in it compiles. +# `compile_all` compiles the package's models into one shared library. It is +# expensive, so this stays plain: call it with its defaults, then check that a +# model out of the resulting library is the model it should be. The read-back +# is at a size the library was not compiled for, since a model that only answers +# at its compile-time size is not a recipe. @testset "compile_all" begin - Bc = COPSBenchmark.ExaModelsBackend() - GRID = (:bearing, :minsurf, :torsion) - - # The list `compile_all` derives from the `*_recipe` names must cover the - # package. Deriving it is what keeps the extension from drifting as models - # are added; this test is what makes that a fact rather than an intention. - recipes = sort([Symbol(chopsuffix(string(n), "_recipe")) - for n in names(COPSBenchmark; all = true) - if endswith(string(n), "_recipe") && !startswith(string(n), "#")]) - @test !isempty(recipes) - @test sort(Symbol.(first.(RECIPE_INSTANCES))) == recipes - - # Every pair `compile_all` would hand the compiler has to close into a - # model. This is what breaks when a recipe and its `*_args` disagree, and - # it costs no compilation to find out. - for (name, params) in RECIPE_INSTANCES - recipe = getfield(COPSBenchmark, Symbol(name, :_recipe)) - argsf = getfield(COPSBenchmark, Symbol(name, :_args)) - m = ExaModels.ExaModel(recipe(Bc; T = Float64), argsf(Bc, params...)...) - @test m.meta.nvar > 0 + # The default `path = "@cops"` installs onto the CNLPModels search path, which + # only exists if CNLPMODELS_PATH is set. Point it at a temporary directory so + # the call stays the plain default one without writing into the user's depot. + dir = mktempdir() + r = withenv("CNLPMODELS_PATH" => dir) do + ExaModelsCompiler.compile_all(COPSBenchmark) end - - # Selection contract: an unknown name is refused rather than silently - # yielding a library missing the model the caller asked for. Refused - # before any compilation, which is what makes this cheap. - @test_throws ArgumentError ExaModelsCompiler.compile_all( - COPSBenchmark; only = [:no_such_problem]) - @test_throws ArgumentError ExaModelsCompiler.compile_all( - COPSBenchmark; exclude = recipes) - @test_throws ArgumentError ExaModelsCompiler.compile_all(Base) - - # One real compile, on a single small non-grid model, exercising the whole - # path: recipe -> library -> load. - mktempdir() do dir - r = ExaModelsCompiler.compile_all(COPSBenchmark; - path = joinpath(dir, "copstest"), - sizes = 12, only = [:chain]) - @test isfile(r.libpath) + @test isfile(r.libpath) + lib = CNLPModels.load(r.libpath) + + b = COPSBenchmark.ExaModelsBackend() + for name in (:chain, :camshape) + m = CNLPModels.CNLPModel(lib, getfield(COPSBenchmark, Symbol(name, :_args))(b, 29)...; + prefix = String(name)) + ref = getfield(COPSBenchmark, Symbol(name, :_model))(b, 29) + @test m.meta.nvar == ref.meta.nvar + @test m.meta.ncon == ref.meta.ncon + @test m.meta.x0 == ref.meta.x0 + x = ref.meta.x0 .+ 0.001 .* (1:ref.meta.nvar) + @test NLPModels.obj(m, x) == NLPModels.obj(ref, x) + @test NLPModels.grad(m, x) == NLPModels.grad(ref, x) end end