From b183431995e9cdcd93efcee6739cac24bb512597 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 28 Oct 2024 16:28:02 +1300 Subject: [PATCH] Add Julia tests to GitHub actions --- .github/workflows/julia-tests-ubuntu.yml | 28 +++++ unotest/.gitignore | 2 + unotest/Project.toml | 12 ++ unotest/runtests.jl | 135 +++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 .github/workflows/julia-tests-ubuntu.yml create mode 100644 unotest/.gitignore create mode 100644 unotest/Project.toml create mode 100644 unotest/runtests.jl diff --git a/.github/workflows/julia-tests-ubuntu.yml b/.github/workflows/julia-tests-ubuntu.yml new file mode 100644 index 00000000..c575b412 --- /dev/null +++ b/.github/workflows/julia-tests-ubuntu.yml @@ -0,0 +1,28 @@ +name: CI +on: + push: + branches: [main] + pull_request: + types: [opened, synchronize, reopened] +# needed to allow julia-actions/cache to delete old caches that it has created +permissions: + actions: write + contents: read +jobs: + test: + name: Julia - ${{ github.event_name }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Compile uno_ampl + run: echo 'TODO' + - uses: julia-actions/setup-julia@v2 + with: + version: '1' + arch: x86 + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - name: Install + run: julia --color=yes --project=unotest -e 'import Pkg; Pkg.instantiate()' + - name: Run + run: julia --color=yes --project=unotest unotest/runtests.jl diff --git a/unotest/.gitignore b/unotest/.gitignore new file mode 100644 index 00000000..f8848d0c --- /dev/null +++ b/unotest/.gitignore @@ -0,0 +1,2 @@ +# Don't check Julia's Manifest.toml file into Git +Manifest.toml diff --git a/unotest/Project.toml b/unotest/Project.toml new file mode 100644 index 00000000..4aa4906a --- /dev/null +++ b/unotest/Project.toml @@ -0,0 +1,12 @@ +[deps] +AmplNLWriter = "7c4d4715-977e-5154-bfe0-e096adeac482" +MINLPTests = "ee0a3090-8ee9-5cdb-b8cb-8eeba3165522" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[compat] +AmplNLWriter = "1" +MINLPTests = "0.6.1" +MathOptInterface = "1.33" +Test = "1.10" +julia = "1.10" diff --git a/unotest/runtests.jl b/unotest/runtests.jl new file mode 100644 index 00000000..49e43149 --- /dev/null +++ b/unotest/runtests.jl @@ -0,0 +1,135 @@ +# Copyright (c) 2018-2024: Charlie Vanaret and contributors +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +# For help with this file, please contact `@odow` on GitHub. + +using Test + +import AmplNLWriter +import MathOptInterface as MOI +import MINLPTests + +""" + Optimizer() + +Create a new `AmplNLWriter.Optimizer` object that uses Uno as the backing +solver. +""" +function Optimizer() + options = String["logger=SILENT"] + return AmplNLWriter.Optimizer("uno_ampl", options) +end + +# This testset runs https://github.com/jump-dev/MINLPTests.jl +@testset "MINLPTests" begin + primal_target = Dict( + MINLPTests.FEASIBLE_PROBLEM => MOI.FEASIBLE_POINT, + # If Uno starts writing a .sol file with an infeasible point, change + # this to `=> MOI.INFEASIBLE_POINT` + MINLPTests.INFEASIBLE_PROBLEM => MOI.NO_SOLUTION, + ) + # This function tests (potentially) non-convex nonlinear programs. The tests + # are meant to be "easy" in the sense that most NLP solvers can find the + # same global minimum, but a test failure can sometimes be allowed. + MINLPTests.test_nlp_expr(; + exclude = [ + # Remove once https://github.com/cvanaret/Uno/issues/39 is fixed + "005_010", + # Okay to exclude forever: AmplNLWriter does not support + # user-defined functions. + "006_010", + # Remove once https://github.com/cvanaret/Uno/issues/38 is fixed + "007_010", + ], + primal_target = primal_target, + ) do + return + end + # This function tests convex nonlinear programs. Test failures here should + # never be allowed, because even local NLP solvers should find the global + # optimum. + MINLPTests.test_nlp_cvx_expr(; primal_target) do + return AmplNLWriter.Optimizer(Uno_jll.amplexe, ["logger=SILENT"]) + end +end + +# This testset runs the full gamut of MOI.Test.runtests. There are a number of +# tests in here with weird edge cases, so a variety of exclusions are expected. +@testset "MathOptInterface.test" begin + optimizer = MOI.instantiate( + Optimizer; + with_cache_type = Float64, + with_bridge_type = Float64, + ) + MOI.Test.runtests( + optimizer, + MOI.Test.Config( + # These are pretty loose tolerances so that all tests pass. There + # are few tests with weird numerics. If tests fail because of + # tolerances, it might be okay to make these looser, or you could + # tighten the tolerances used by Uno. + atol = 1e-4, + rtol = 1e-4, + optimal_status = MOI.LOCALLY_SOLVED, + infeasible_status = MOI.LOCALLY_INFEASIBLE, + exclude = Any[ + # It's okay to exclude BasisStatus, since AmplNLWriter doesn't + # support this information, so too with ObjectiveBound, since + # Uno is a local NLP solver. + MOI.VariableBasisStatus, + MOI.ConstraintBasisStatus, + MOI.ObjectiveBound, + ], + ); + exclude = [ + # ================================================================== + # The following tests are bugs. + # + # We should fix issues in Uno, and then try removing these lines. + # + # These tests return OTHER_LIMIT or OTHER_ERROR instead of + # LOCALLY_SOLVED. + r"^test_conic_linear_VectorOfVariables_2$", + r"^test_linear_integration$", + r"^test_linear_transform$", + r"^test_nonlinear_expression_hs109$", + r"^test_quadratic_constraint_GreaterThan$", + r"^test_quadratic_constraint_LessThan$", + r"^test_solve_VariableIndex_ConstraintDual_MAX_SENSE$", + r"^test_solve_VariableIndex_ConstraintDual_MIN_SENSE$", + # These tests return OTHER_LIMIT instead of DUAL_INFEASIBLE. It + # might be acceptable to leave this as-is, but it would be better to + # fix. + r"^test_solve_TerminationStatus_DUAL_INFEASIBLE$", + # These tests return OTHER_LIMIT instead of LOCALLY_INFEASIBLE. It + # might be acceptable to leave this as-is, but it would be better to + # fix. + r"^test_conic_NormInfinityCone_INFEASIBLE$", + r"^test_conic_NormOneCone_INFEASIBLE$", + r"^test_conic_linear_INFEASIBLE$", + r"^test_conic_linear_INFEASIBLE_2$", + r"^test_linear_INFEASIBLE$", + r"^test_linear_INFEASIBLE_2$", + r"^test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_", + # ================================================================== + # The following tests are okay to exclude forever. + # + # Uno does not support integrality, and AmplNLWriter has no way of + # telling if a particular binary supports integers or not (it + # defaults to assuming yes). + "Indicator", + r"[Ii]nteger", + "Semicontinuous", + "Semiinteger", + "SOS1", + "SOS2", + "ZeroOne", + r"^test_cpsat_", + r"^test_attribute_SolverVersion$", + r"^test_nonlinear_invalid$", + r"^test_basic_VectorNonlinearFunction_", + ], + ) +end