diff --git a/.github/julia/.gitignore b/.github/julia/.gitignore new file mode 100644 index 00000000..f8848d0c --- /dev/null +++ b/.github/julia/.gitignore @@ -0,0 +1,2 @@ +# Don't check Julia's Manifest.toml file into Git +Manifest.toml diff --git a/.github/julia/Project.toml b/.github/julia/Project.toml new file mode 100644 index 00000000..4aa4906a --- /dev/null +++ b/.github/julia/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/.github/julia/runtests.jl b/.github/julia/runtests.jl new file mode 100644 index 00000000..e2c0a4e1 --- /dev/null +++ b/.github/julia/runtests.jl @@ -0,0 +1,132 @@ +# 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 +import Uno_jll + +""" + Optimizer() + +Create a new `AmplNLWriter.Optimizer` object that uses Uno as the backing +solver. +""" +function Optimizer() + options = String["logger=SILENT"] + return AmplNLWriter.Optimizer(Uno_jll.amplexe, 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( + Optimizer; + 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, + ) + # 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(Optimizer; primal_target) +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. + # + # Variable duals aren't written to .sol + r"^test_conic_linear_VectorOfVariables_2$", + r"^test_linear_integration$", + 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 LOCALLY_SOLVED. + r"^test_linear_transform$", + # 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 diff --git a/.github/workflows/julia-tests-ubuntu.yml b/.github/workflows/julia-tests-ubuntu.yml index 91e916d1..186363f5 100644 --- a/.github/workflows/julia-tests-ubuntu.yml +++ b/.github/workflows/julia-tests-ubuntu.yml @@ -1,4 +1,4 @@ -name: CI +name: JuliaCI on: push: branches: [main] @@ -19,17 +19,17 @@ jobs: name: Julia - ${{ github.event_name }} runs-on: ubuntu-latest steps: - - name: Checkout Uno - uses: actions/checkout@v4 - - name: Install Julia 1.7 - uses: julia-actions/setup-julia@v2 + - uses: actions/checkout@v4 + # Install Julia 1.7 for BinaryBuilder. Note that this is an old version of + # Julia, but it is required for compatibility with BinaryBuilder. + - uses: julia-actions/setup-julia@v2 with: version: "1.7" arch: x64 - - name: Set the environment variables BINARYBUILDER_AUTOMATIC_APPLE, UNO_RELEASE, UNO_COMMIT - shell: bash + # Set environment variables required by BinaryBuilder. The specific + # version of UNO_RELEASE is unimportant, since Uno_jll doesn't depend on it + - name: Set the environment variables run: | - echo "BINARYBUILDER_AUTOMATIC_APPLE=true" >> $GITHUB_ENV echo "UNO_RELEASE=1.1.1" >> $GITHUB_ENV if [ "${{ github.event_name }}" = "pull_request" ]; then echo "UNO_COMMIT=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV @@ -38,19 +38,21 @@ jobs: echo "UNO_COMMIT=${{ github.sha }}" >> $GITHUB_ENV echo "UNO_URL=https://github.com/${{ github.repository }}.git" >> $GITHUB_ENV fi - - name: Generate Uno_jll.jl + - name: Compile Uno_jll run: | julia --color=yes -e 'using Pkg; Pkg.add("BinaryBuilder")' - julia --color=yes .github/julia/build_tarballs_yggdrasil.jl x86_64-linux-gnu-libgfortran5 --verbose --deploy="local" - - name: Install Julia LTS - uses: julia-actions/setup-julia@v2 + julia --color=yes .github/julia/build_tarballs_yggdrasil.jl x86_64-linux-gnu-cxx11 --verbose --deploy="local" + # Now install a newer version of Julia to actually test Uno_jll. We choose + # v1.10 because it is the current Long-Term Support (LTS). + - uses: julia-actions/setup-julia@v2 with: version: "1.10" arch: x64 - - - name: Test AmplNLWriter.jl - shell: bash + - uses: julia-actions/cache@v1 + - name: Test .github/julia/runtests.jl + shell: julia --color=yes --project=.github/julia {0} run: | - git clone https://github.com/jump-dev/AmplNLWriter.jl - cd AmplNLWriter.jl/test/MINLPTests/ - julia --project --color=yes -e 'using Pkg; Pkg.develop(path="/home/runner/.julia/dev/Uno_jll"); Pkg.develop(path="../.."); Pkg.instantiate(); include("run_minlptests.jl")' + using Pkg + Pkg.instantiate() + Pkg.develop(path="/home/runner/.julia/dev/Uno_jll") + include("/home/runner/work/Uno/Uno/.github/julia/runtests.jl")