From 1d1813cae8075e8bf9c5a00b33473479ebd0d050 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 22 May 2024 10:29:00 +0200 Subject: [PATCH 01/49] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4dbf9f54f..075e4cce2 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,11 @@ A solver for Mixed-Integer Convex Optimization that uses Frank-Wolfe methods for ## Overview -The Boscia.jl solver combines (a variant of) the Frank-Wolfe algorithm with a branch-and-bound like algorithm to solve mixed-integer convex optimization problems of the form -`min_{x ∈ C, x_I ∈ Z^n} f(x)`, -where `f` is a differentiable convex function, `C` is a convex and compact set, and `I` is a set of indices of integral variables. - -They are especially useful when we have a method to optimize a linear function over `C` and the integrality constraints in a compuationally efficient way. -`C` is specified using the MathOptInterface API or any DSL like JuMP implementing it. +The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorithm and a branch-and-bound-like algorithm to solve mixed-integer convex optimization problems. These problems are of the form: +**min_{x ∈ C, x_I ∈ Z^n} f(x)**, +where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. +This approach is particularly effective when we can efficiently optimize a linear function over C and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming **JuMP** that implements this API. A paper presenting the package with mathematical explanations and numerous examples can be found here: > Convex integer optimization with Frank-Wolfe methods: [2208.11010](https://arxiv.org/abs/2208.11010) From 6fd57a8991000d22b4e0896f92c1ca5756d5d106 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 22 May 2024 10:52:21 +0200 Subject: [PATCH 02/49] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 075e4cce2..1b875322b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorith **min_{x ∈ C, x_I ∈ Z^n} f(x)**, where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. -This approach is particularly effective when we can efficiently optimize a linear function over C and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming **JuMP** that implements this API. +This approach is particularly effective when we can efficiently optimize a linear function over C and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. A paper presenting the package with mathematical explanations and numerous examples can be found here: > Convex integer optimization with Frank-Wolfe methods: [2208.11010](https://arxiv.org/abs/2208.11010) @@ -20,6 +20,12 @@ A paper presenting the package with mathematical explanations and numerous examp ## Installation +If you haven't downloaded JULIA yet go on this link and download it according to your system requirement . ['Julia'](https://julialang.org/downloads/). +Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REPL mode and run +''' Boscia +pkg> add Boscia +''' + Add the Boscia stable release with: ```julia From 5a696ee35db6a68bce173558823a1cd1c8861237 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 22 May 2024 10:54:38 +0200 Subject: [PATCH 03/49] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1b875322b..dbbc79d91 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,11 @@ A paper presenting the package with mathematical explanations and numerous examp If you haven't downloaded JULIA yet go on this link and download it according to your system requirement . ['Julia'](https://julialang.org/downloads/). Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REPL mode and run -''' Boscia -pkg> add Boscia -''' +```Boscia +pkg > add Boscia + +``` +or alternatively you can do this Add the Boscia stable release with: From 659616fda136f31b2cd42124b4740eae397557d9 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 22 May 2024 10:58:39 +0200 Subject: [PATCH 04/49] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index dbbc79d91..2f84b57a6 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,14 @@ import Pkg Pkg.add(url="https://github.com/ZIB-IOL/Boscia.jl", rev="main") ``` +If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) +**For Window Users** You need not to download whole SCIP binary instead you can follow **Custom Installation** mentioned on this page and download and link SCIP with your JULIA . + + + + + + ## Getting started Here is a simple example to get started. For more examples, see the examples folder in the package. From b4917f81786c1b0d73a118555136d60c60f0115b Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 22 May 2024 10:59:11 +0200 Subject: [PATCH 05/49] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2f84b57a6..bc49b4ce7 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Pkg.add(url="https://github.com/ZIB-IOL/Boscia.jl", rev="main") ``` If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) + + **For Window Users** You need not to download whole SCIP binary instead you can follow **Custom Installation** mentioned on this page and download and link SCIP with your JULIA . From 4f10b66999e0b4b2f2fce1c0a7a7d2e49e5a060f Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 22 May 2024 14:11:56 +0200 Subject: [PATCH 06/49] Initial commit From 47a2e9c229abc792c6464386f371dcb977c91223 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 22 May 2024 14:12:11 +0200 Subject: [PATCH 07/49] Files generated by PkgTemplates PkgTemplates version: 0.7.49 --- .github/dependabot.yml | 7 +++ .github/workflows/CI.yml | 79 ++++++++++++++++++++++++++++++ .github/workflows/CompatHelper.yml | 16 ++++++ .github/workflows/TagBot.yml | 31 ++++++++++++ .gitignore | 5 ++ LICENSE | 21 ++++++++ Manifest.toml | 7 +++ Project.toml | 13 +++++ README.md | 6 +++ docs/Project.toml | 3 ++ docs/make.jl | 23 +++++++++ docs/src/index.md | 14 ++++++ src/Boscia.jl | 5 ++ test/runtests.jl | 6 +++ 14 files changed, 236 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/CompatHelper.yml create mode 100644 .github/workflows/TagBot.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Manifest.toml create mode 100644 Project.toml create mode 100644 README.md create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/src/index.md create mode 100644 src/Boscia.jl create mode 100644 test/runtests.jl diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..700707ced --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 000000000..1862fcd78 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,79 @@ +name: CI +on: + push: + branches: + - master + tags: ['*'] + pull_request: + workflow_dispatch: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created + actions: write + contents: read + strategy: + fail-fast: false + matrix: + version: + - '1.1' + - '1.10' + - '1.6' + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v4 + with: + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + docs: + name: Documentation + runs-on: ubuntu-latest + permissions: + actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created + contents: write + statuses: write + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - uses: julia-actions/cache@v1 + - name: Configure doc environment + shell: julia --project=docs --color=yes {0} + run: | + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate() + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-docdeploy@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + - name: Run doctests + shell: julia --project=docs --color=yes {0} + run: | + using Documenter: DocMeta, doctest + using Boscia + DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) + doctest(Boscia) diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 000000000..cba9134c6 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,16 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} + run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml new file mode 100644 index 000000000..0cd3114ec --- /dev/null +++ b/.github/workflows/TagBot.yml @@ -0,0 +1,31 @@ +name: TagBot +on: + issue_comment: + types: + - created + workflow_dispatch: + inputs: + lookback: + default: "3" +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read +jobs: + TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..088705052 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.jl.*.cov +*.jl.cov +*.jl.mem +/docs/Manifest.toml +/docs/build/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b7087614b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Saurabh Srivastava + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 000000000..c3e1543b7 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,7 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.3" +manifest_format = "2.0" +project_hash = "0815649208799fa12993731994e4db6bcf5e6067" + +[deps] diff --git a/Project.toml b/Project.toml new file mode 100644 index 000000000..9e1b0b92c --- /dev/null +++ b/Project.toml @@ -0,0 +1,13 @@ +name = "Boscia" +uuid = "630d6612-02b6-44cc-9b59-bf75311a0c58" +authors = ["Saurabh Srivastava"] +version = "1.0.0-DEV" + +[compat] +julia = "1.1" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md new file mode 100644 index 000000000..2964dc17e --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Boscia + +[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/stable/) +[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/dev/) +[![Build Status](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml?query=branch%3Amaster) +[![Coverage](https://codecov.io/gh/saurabhintoml/Boscia.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/saurabhintoml/Boscia.jl) diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 000000000..d473f72ea --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +Boscia = "630d6612-02b6-44cc-9b59-bf75311a0c58" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 000000000..8591e47c4 --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,23 @@ +using Boscia +using Documenter + +DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) + +makedocs(; + modules=[Boscia], + authors="Saurabh Srivastava", + sitename="Boscia.jl", + format=Documenter.HTML(; + canonical="https://saurabhintoml.github.io/Boscia.jl", + edit_link="master", + assets=String[], + ), + pages=[ + "Home" => "index.md", + ], +) + +deploydocs(; + repo="github.com/saurabhintoml/Boscia.jl", + devbranch="master", +) diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 000000000..75c6f9264 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,14 @@ +```@meta +CurrentModule = Boscia +``` + +# Boscia + +Documentation for [Boscia](https://github.com/saurabhintoml/Boscia.jl). + +```@index +``` + +```@autodocs +Modules = [Boscia] +``` diff --git a/src/Boscia.jl b/src/Boscia.jl new file mode 100644 index 000000000..44264cda2 --- /dev/null +++ b/src/Boscia.jl @@ -0,0 +1,5 @@ +module Boscia + +# Write your package code here. + +end diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 000000000..a7a287e55 --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,6 @@ +using Boscia +using Test + +@testset "Boscia.jl" begin + # Write your tests here. +end From 116abb3ae866354da8d57ae39e9bc00adf6f5907 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Mon, 3 Jun 2024 11:57:27 +0200 Subject: [PATCH 08/49] Boscia --- basics.md | 4 + contributing.md | 4 + docs/make.jl | 138 +++++++++- docs/src/basics.md | 8 + docs/src/contributing.md | 8 + docs/src/examples.md | 8 + docs/src/examples/plot_utils.jl | 438 +++++++++++++++++++++++++++++++ docs/src/index.md | 14 +- examples.md | 4 + examples/HiGHS_example.jl | 34 +++ examples/approx_planted_point.jl | 139 ++++++++++ examples/big_float_example.jl | 63 +++++ examples/plot_utils.jl | 438 +++++++++++++++++++++++++++++++ 13 files changed, 1287 insertions(+), 13 deletions(-) create mode 100644 basics.md create mode 100644 contributing.md create mode 100644 docs/src/basics.md create mode 100644 docs/src/contributing.md create mode 100644 docs/src/examples.md create mode 100644 docs/src/examples/plot_utils.jl create mode 100644 examples.md create mode 100644 examples/HiGHS_example.jl create mode 100644 examples/approx_planted_point.jl create mode 100644 examples/big_float_example.jl create mode 100644 examples/plot_utils.jl diff --git a/basics.md b/basics.md new file mode 100644 index 000000000..5fd675900 --- /dev/null +++ b/basics.md @@ -0,0 +1,4 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/BASICS.md" +``` + diff --git a/contributing.md b/contributing.md new file mode 100644 index 000000000..7362b2bf0 --- /dev/null +++ b/contributing.md @@ -0,0 +1,4 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/CONTRIBUTING.md" +``` + diff --git a/docs/make.jl b/docs/make.jl index 8591e47c4..9466e5c1b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,23 +1,153 @@ +using Pkg +pkg"activate .. " + + using Boscia using Documenter +using SparseArrays +using LinearAlgebra + +using Literate, Test + +# Activate the project environment +project_path = joinpath(dirname(@__DIR__)) # Adjust this path if necessary +Pkg.activate(project_path) + +# Ensure all dependencies are installed +Pkg.instantiate() + +EXAMPLE_DIR = joinpath(dirname(@__DIR__), "examples") +DOCS_EXAMPLE_DIR = joinpath(@__DIR__, "src", "examples") +DOCS_REFERENCE_DIR = joinpath(@__DIR__, "src", "reference") + +function file_list(dir, extension) + return filter(file -> endswith(file, extension), sort(readdir(dir))) +end + +# includes plot_utils to the example file before running it +function include_utils(content) + return """ + import Boscia ; include(joinpath(dirname(pathof(Boscia)), "../examples/plot_utils.jl")) # hide + """ * content +end -DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) +function literate_directory(jl_dir, md_dir) + for filename in file_list(md_dir, ".md") + filepath = joinpath(md_dir, filename) + rm(filepath) + end + for filename in file_list(jl_dir, ".jl") + filepath = joinpath(jl_dir, filename) + # `include` the file to test it before `#src` lines are removed. It is + # in a testset to isolate local variables between files. + if startswith(filename, "docs") + Literate.markdown( + filepath, md_dir; + documenter=true, flavor=Literate.DocumenterFlavor(), preprocess=include_utils, + ) + end + end + return nothing +end + +literate_directory(EXAMPLE_DIR, DOCS_EXAMPLE_DIR) +cp(joinpath(EXAMPLE_DIR, "plot_utils.jl"), joinpath(DOCS_EXAMPLE_DIR, "plot_utils.jl") , force = true) + +ENV["GKSwstype"] = "100" + +generated_path = joinpath(@__DIR__, "src") +base_url = "https://github.com/saurabhintoml/Boscia.jl/" +isdir(generated_path) || mkdir(generated_path) + +open(joinpath(generated_path, "contributing.md"), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)CONTRIBUTING.md" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), "CONTRIBUTING.md")) + println(io, line) + end +end + +open(joinpath(generated_path, "basics.md"), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)BASICS.md" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), "BASICS.md")) + println(io, line) + end +end + +open(joinpath(generated_path, "Examples.md"), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)EXAMPLES.md" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), "EXAMPLES.md")) + println(io, line) + end +end + +open(joinpath(generated_path, "index.md"), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)README.md" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), "README.md")) + println(io, line) + end +end makedocs(; modules=[Boscia], authors="Saurabh Srivastava", + repo = "https://saurabhintoml.github.io/Boscia.jl/blob/{commit}{path}#L{line}" , sitename="Boscia.jl", format=Documenter.HTML(; - canonical="https://saurabhintoml.github.io/Boscia.jl", + prettyurls = get(ENV , "CI" , nothing) == "true" , + repolink ="https://saurabhintoml.github.io/Boscia.jl", edit_link="master", assets=String[], ), pages=[ "Home" => "index.md", + "How Does it Work" => "basics.md" , + + + "Examples" => "examples.md", + "API reference" => + [joinpath("reference", f) for f in file_list(DOCS_REFERENCE_DIR, ".md")], + "Contributing" => "contributing.md", ], ) deploydocs(; repo="github.com/saurabhintoml/Boscia.jl", - devbranch="master", -) + push_preview = true , + devbranch = "master" ) + diff --git a/docs/src/basics.md b/docs/src/basics.md new file mode 100644 index 000000000..2f71859d1 --- /dev/null +++ b/docs/src/basics.md @@ -0,0 +1,8 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/BASICS.md" +``` + +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/BASICS.md" +``` + diff --git a/docs/src/contributing.md b/docs/src/contributing.md new file mode 100644 index 000000000..558a2b060 --- /dev/null +++ b/docs/src/contributing.md @@ -0,0 +1,8 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/CONTRIBUTING.md" +``` + +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/CONTRIBUTING.md" +``` + diff --git a/docs/src/examples.md b/docs/src/examples.md new file mode 100644 index 000000000..af4448e24 --- /dev/null +++ b/docs/src/examples.md @@ -0,0 +1,8 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/EXAMPLES.md" +``` + +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/EXAMPLES.md" +``` + diff --git a/docs/src/examples/plot_utils.jl b/docs/src/examples/plot_utils.jl new file mode 100644 index 000000000..c0d938223 --- /dev/null +++ b/docs/src/examples/plot_utils.jl @@ -0,0 +1,438 @@ +using Plots +using FiniteDifferences + +""" +plot_results +Given a series of list, generate subplots. +list_data_y -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the y-values of the series inside a subplot). +list_data_x -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the x-values of the series inside a subplot). +So if we have one plot with two series, these might look like: + list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] + list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] +And if we have two plots, each with two series, these might look like: + list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] + list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] +list_label -> contains the labels for the series that will be plotted, +which has to have a length equal to the number of series that are being plotted: + list_label = ["Series 1", "Series 2"] +list_axis_x -> contains the labels for the x-axis that will be plotted, +which has to have a length equal to the number of subplots: + list_axis_x = ["x-axis plot 1", "x-axis plot 1"] +list_axis_y -> Same as list_axis_x but for the y-axis +xscalelog -> A list of values indicating the type of axes to use in each subplot, +must be equal to the number of subplots: + xscalelog = [:log, :identity] +yscalelog -> Same as xscalelog but for the y-axis +""" +function plot_results( + list_data_y, + list_data_x, + list_label, + list_axis_x, + list_axis_y; + filename=nothing, + xscalelog=nothing, + yscalelog=nothing, + legend_position=nothing, + list_style=fill(:solid, length(list_label)), + list_color=get_color_palette(:auto, plot_color(:white)), + list_markers=[ + :circle, + :rect, + :utriangle, + :diamond, + :hexagon, + :+, + :x, + :star5, + :cross, + :xcross, + :dtriangle, + :rtriangle, + :ltriangle, + :pentagon, + :heptagon, + :octagon, + :star4, + :star6, + :star7, + :star8, + :vline, + :hline, + ], + number_markers_per_line=10, + line_width=3.0, + marker_size=5.0, + transparency_markers=0.45, + font_size_axis=12, + font_size_legend=9, +) + gr() + plt = nothing + list_plots = Plots.Plot{Plots.GRBackend}[] + #Plot an appropiate number of plots + for i in eachindex(list_data_x) + for j in eachindex(list_data_x[i]) + if isnothing(xscalelog) + xscale = :identity + else + xscale = xscalelog[i] + end + if isnothing(yscalelog) + yscale = :log + else + yscale = yscalelog[i] + end + if isnothing(legend_position) + position_legend = :best + legend_display = true + else + position_legend = legend_position[i] + if isnothing(position_legend) + legend_display = false + else + legend_display = true + end + end + if j == 1 + if legend_display + plt = plot( + list_data_x[i][j], + list_data_y[i][j], + label="", + xaxis=xscale, + yaxis=yscale, + ylabel=list_axis_y[i], + xlabel=list_axis_x[i], + legend=position_legend, + yguidefontsize=font_size_axis, + xguidefontsize=font_size_axis, + legendfontsize=font_size_legend, + width=line_width, + linestyle=list_style[j], + color=list_color[j], + grid=true, + ) + else + plt = plot( + list_data_x[i][j], + list_data_y[i][j], + label="", + xaxis=xscale, + yaxis=yscale, + ylabel=list_axis_y[i], + xlabel=list_axis_x[i], + yguidefontsize=font_size_axis, + xguidefontsize=font_size_axis, + width=line_width, + linestyle=list_style[j], + color=list_color[j], + grid=true, + ) + end + else + if legend_display + plot!( + list_data_x[i][j], + list_data_y[i][j], + label="", + width=line_width, + linestyle=list_style[j], + color=list_color[j], + legend=position_legend, + ) + else + plot!( + list_data_x[i][j], + list_data_y[i][j], + label="", + width=line_width, + linestyle=list_style[j], + color=list_color[j], + ) + end + end + if xscale == :log + indices = + round.( + Int, + 10 .^ (range( + log10(1), + log10(length(list_data_x[i][j])), + length=number_markers_per_line, + )), + ) + scatter!( + list_data_x[i][j][indices], + list_data_y[i][j][indices], + markershape=list_markers[j], + markercolor=list_color[j], + markersize=marker_size, + markeralpha=transparency_markers, + label=list_label[j], + legend=position_legend, + ) + else + scatter!( + view( + list_data_x[i][j], + 1:length(list_data_x[i][j])÷number_markers_per_line:length( + list_data_x[i][j], + ), + ), + view( + list_data_y[i][j], + 1:length(list_data_y[i][j])÷number_markers_per_line:length( + list_data_y[i][j], + ), + ), + markershape=list_markers[j], + markercolor=list_color[j], + markersize=marker_size, + markeralpha=transparency_markers, + label=list_label[j], + legend=position_legend, + ) + end + end + push!(list_plots, plt) + end + fp = plot(list_plots..., layout=length(list_plots)) + plot!(size=(600, 400)) + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +# Recipe for plotting markers in plot_trajectories +@recipe function f(::Type{Val{:samplemarkers}}, x, y, z; n_markers=10, log=false) + n = length(y) + + # Choose datapoints for markers + if log + xmin = log10(x[1]) + xmax = log10(x[end]) + thresholds = collect(xmin:(xmax-xmin)/(n_markers-1):xmax) + indices = [argmin(i -> abs(t - log10(x[i])), eachindex(x)) for t in thresholds] + else + indices = 1:Int(ceil(length(x) / n_markers)):n + end + sx, sy = x[indices], y[indices] + + # add an empty series with the correct type for legend markers + @series begin + seriestype := :path + markershape --> :auto + x := [] + y := [] + end + # add a series for the line + @series begin + primary := false # no legend entry + markershape := :none # ensure no markers + seriestype := :path + seriescolor := get(plotattributes, :seriescolor, :auto) + x := x + y := y + end + # return a series for the sampled markers + primary := false + seriestype := :scatter + markershape --> :auto + x := sx + y := sy + z_order := 1 +end + +function plot_trajectories( + data, + label; + filename=nothing, + xscalelog=false, + yscalelog=true, + legend_position=:topright, + lstyle=fill(:solid, length(data)), + marker_shapes=nothing, + n_markers=10, + reduce_size=false, + primal_offset=1e-8, + line_width=1.3, + empty_marker=false, + extra_plot=false, + extra_plot_label="", +) + # theme(:dark) + # theme(:vibrant) + Plots.gr() + + x = [] + y = [] + offset = 2 + + function sub_plot(idx_x, idx_y; legend=false, xlabel="", ylabel="", y_offset=0) + + fig = nothing + + for (i, trajectory) in enumerate(data) + + l = length(trajectory) + if reduce_size && l > 1000 + indices = Int.(round.(collect(1:l/1000:l))) + trajectory = trajectory[indices] + end + + x = [trajectory[j][idx_x] for j in offset:length(trajectory)] + y = [trajectory[j][idx_y] + y_offset for j in offset:length(trajectory)] + + if marker_shapes !== nothing && n_markers >= 2 + marker_args = Dict( + :st => :samplemarkers, + :n_markers => n_markers, + :shape => marker_shapes[i], + :log => xscalelog, + :markercolor => empty_marker ? :white : :match, + :markerstrokecolor => empty_marker ? i : :match, + ) + else + marker_args = Dict() + end + + if i == 1 + fig = plot( + x, + y, + label=label[i], + xaxis=xscalelog ? :log : :identity, + yaxis=yscalelog ? :log : :identity, + xlabel=xlabel, + ylabel=ylabel, + legend=legend, + yguidefontsize=8, + xguidefontsize=8, + legendfontsize=8, + width=line_width, + linestyle=lstyle[i]; + marker_args..., + ) + else + plot!(x, y, label=label[i], width=line_width, linestyle=lstyle[i]; marker_args...) + end + end + return fig + end + + pit = sub_plot(1, 2; legend=legend_position, ylabel="Primal", y_offset=primal_offset) + pti = sub_plot(5, 2; y_offset=primal_offset) + dit = sub_plot(1, 4; xlabel="Iterations", ylabel="FW gap") + dti = sub_plot(5, 4; xlabel="Time (s)") + + if extra_plot + iit = sub_plot(1, 6; ylabel=extra_plot_label) + iti = sub_plot(5, 6) + fp = plot(pit, pti, iit, iti, dit, dti, layout=(3, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 600)) + else + fp = plot(pit, pti, dit, dti, layout=(2, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 400)) + end + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +function plot_sparsity( + data, + label; + filename=nothing, + xscalelog=false, + legend_position=:topright, + yscalelog=true, + lstyle=fill(:solid, length(data)), + marker_shapes=nothing, + n_markers=10, + empty_marker=false, + reduce_size=false, +) + Plots.gr() + + xscale = xscalelog ? :log : :identity + yscale = yscalelog ? :log : :identity + offset = 2 + + function subplot(idx_x, idx_y, ylabel) + + fig = nothing + for (i, trajectory) in enumerate(data) + + l = length(trajectory) + if reduce_size && l > 1000 + indices = Int.(round.(collect(1:l/1000:l))) + trajectory = trajectory[indices] + end + + + x = [trajectory[j][idx_x] for j in offset:length(trajectory)] + y = [trajectory[j][idx_y] for j in offset:length(trajectory)] + if marker_shapes !== nothing && n_markers >= 2 + marker_args = Dict( + :st => :samplemarkers, + :n_markers => n_markers, + :shape => marker_shapes[i], + :log => xscalelog, + :startmark => 5 + 20 * (i - 1), + :markercolor => empty_marker ? :white : :match, + :markerstrokecolor => empty_marker ? i : :match, + ) + else + marker_args = Dict() + end + if i == 1 + fig = plot( + x, + y; + label=label[i], + xaxis=xscale, + yaxis=yscale, + ylabel=ylabel, + legend=legend_position, + yguidefontsize=8, + xguidefontsize=8, + legendfontsize=8, + linestyle=lstyle[i], + marker_args..., + ) + else + plot!(x, y; label=label[i], linestyle=lstyle[i], marker_args...) + end + end + + return fig + end + + ps = subplot(6, 2, "Primal") + ds = subplot(6, 4, "FW gap") + + fp = plot(ps, ds, layout=(1, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 200)) + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +""" +Check if the gradient using finite differences matches the grad! provided. +""" +function check_gradients(grad!, f, gradient, num_tests=10, tolerance=1.0e-5) + for i in 1:num_tests + random_point = similar(gradient) + random_point .= rand(length(gradient)) + grad!(gradient, random_point) + if norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient) > tolerance + @warn "There is a noticeable difference between the gradient provided and + the gradient computed using finite differences.:\n$(norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient))" + end + end +end diff --git a/docs/src/index.md b/docs/src/index.md index 75c6f9264..70f51d6d6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,14 +1,10 @@ ```@meta -CurrentModule = Boscia +EditURL = "https://github.com/saurabhintoml/Boscia.jl/README.md" ``` # Boscia -Documentation for [Boscia](https://github.com/saurabhintoml/Boscia.jl). - -```@index -``` - -```@autodocs -Modules = [Boscia] -``` +[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/stable/) +[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/dev/) +[![Build Status](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml?query=branch%3Amaster) +[![Coverage](https://codecov.io/gh/saurabhintoml/Boscia.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/saurabhintoml/Boscia.jl) diff --git a/examples.md b/examples.md new file mode 100644 index 000000000..4a51b1018 --- /dev/null +++ b/examples.md @@ -0,0 +1,4 @@ +```@meta +EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/EXAMPLES.md" +``` + diff --git a/examples/HiGHS_example.jl b/examples/HiGHS_example.jl new file mode 100644 index 000000000..043d77f3c --- /dev/null +++ b/examples/HiGHS_example.jl @@ -0,0 +1,34 @@ +using Boscia +using FrankWolfe +using Random +using HiGHS +using LinearAlgebra +import MathOptInterface + +const MOI = MathOptInterface + +n = 6 + +const diffw = 0.5 * ones(n) +o = HiGHS.Optimizer() + +MOI.set(o, MOI.Silent(), true) + +x = MOI.add_variables(o, n) + +for xi in x + MOI.add_constraint(o, xi, MOI.GreaterThan(0.0)) + MOI.add_constraint(o, xi, MOI.LessThan(1.0)) + MOI.add_constraint(o, xi, MOI.ZeroOne()) +end +lmo = FrankWolfe.MathOptLMO(o) + +function f(x) + return 0.5 * sum((x .- diffw) .^ 2) +end + +function grad!(storage, x) + @. storage = x - diffw +end + +x, _, result = Boscia.solve(f, grad!, lmo, verbose=true) diff --git a/examples/approx_planted_point.jl b/examples/approx_planted_point.jl new file mode 100644 index 000000000..04a709ea0 --- /dev/null +++ b/examples/approx_planted_point.jl @@ -0,0 +1,139 @@ +using Boscia +using FrankWolfe +using Test +using Random +using SCIP +# using Statistics +using LinearAlgebra +using Distributions +import MathOptInterface +const MOI = MathOptInterface + +include("cube_blmo.jl") + +n = 20 +diffi = Random.rand(Bool, n) * 0.6 .+ 0.3 + +@testset "Approximate planted point - Integer" begin + + function f(x) + return 0.5 * sum((x[i] - diffi[i])^2 for i in eachindex(x)) + end + function grad!(storage, x) + @. storage = x - diffi + end + + @testset "Using SCIP" begin + o = SCIP.Optimizer() + MOI.set(o, MOI.Silent(), true) + MOI.empty!(o) + x = MOI.add_variables(o, n) + for xi in x + MOI.add_constraint(o, xi, MOI.GreaterThan(0.0)) + MOI.add_constraint(o, xi, MOI.LessThan(1.0)) + MOI.add_constraint(o, xi, MOI.ZeroOne()) # or MOI.Integer() + end + lmo = FrankWolfe.MathOptLMO(o) + + x, _, result = Boscia.solve(f, grad!, lmo, verbose=true) + + @test x == round.(diffi) + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end + + @testset "Using Cube LMO" begin + int_vars = collect(1:n) + + bounds = Boscia.IntegerBounds() + for i in 1:n + push!(bounds, (i, 0.0), :greaterthan) + push!(bounds, (i, 1.0), :lessthan) + end + blmo = CubeBLMO(n, int_vars, bounds) + + x, _, result = Boscia.solve(f, grad!, blmo, verbose=true) + + @test x == round.(diffi) + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end + + @testset "Using Cube Simple LMO" begin + int_vars = collect(1:n) + lbs = zeros(n) + ubs = ones(n) + + sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars) + + x, _, result = + Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true) + + @test x == round.(diffi) + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end +end + + +@testset "Approximate planted point - Mixed" begin + + function f(x) + return 0.5 * sum((x[i] - diffi[i])^2 for i in eachindex(x)) + end + function grad!(storage, x) + @. storage = x - diffi + end + + int_vars = unique!(rand(collect(1:n), Int(floor(n / 2)))) + + @testset "Using SCIP" begin + o = SCIP.Optimizer() + MOI.set(o, MOI.Silent(), true) + MOI.empty!(o) + x = MOI.add_variables(o, n) + for xi in x + MOI.add_constraint(o, xi, MOI.GreaterThan(0.0)) + MOI.add_constraint(o, xi, MOI.LessThan(1.0)) + if xi.value in int_vars + MOI.add_constraint(o, xi, MOI.ZeroOne()) # or MOI.Integer() + end + end + lmo = FrankWolfe.MathOptLMO(o) + + x, _, result = Boscia.solve(f, grad!, lmo, verbose=true) + + sol = diffi + sol[int_vars] = round.(sol[int_vars]) + @test sum(isapprox.(x, sol, atol=1e-6, rtol=1e-2)) == n + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end + + @testset "Using Cube LMO" begin + bounds = Boscia.IntegerBounds() + for i in 1:n + push!(bounds, (i, 0.0), :greaterthan) + push!(bounds, (i, 1.0), :lessthan) + end + blmo = CubeBLMO(n, int_vars, bounds) + + x, _, result = Boscia.solve(f, grad!, blmo, verbose=true) + + sol = diffi + sol[int_vars] = round.(sol[int_vars]) + @test sum(isapprox.(x, sol, atol=1e-6, rtol=1e-2)) == n + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end + + @testset "Using Cube Simple LMO" begin + lbs = zeros(n) + ubs = ones(n) + + sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars) + + x, _, result = + Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true) + + sol = diffi + sol[int_vars] = round.(sol[int_vars]) + @test sum(isapprox.(x, sol, atol=1e-6, rtol=1e-2)) == n + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) + end +end diff --git a/examples/big_float_example.jl b/examples/big_float_example.jl new file mode 100644 index 000000000..c738bc47e --- /dev/null +++ b/examples/big_float_example.jl @@ -0,0 +1,63 @@ +using Random +using Boscia +using Test +using DoubleFloats + +n = 200 +diffi = Random.rand(Bool, n) * 0.6 .+ 0.3 + +@testset "Using BigFloat" begin + function f(x) + x = BigFloat.(x) + return float(0.5 * sum((x[i] - diffi[i])^2 for i in eachindex(x))) + end + function grad!(storage, x) + x = BigFloat.(x) + @. storage = x - diffi + return float.(storage) + end + + int_vars = collect(1:n) + lbs = zeros(n) + ubs = ones(n) + + sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars) + custom_heuristics= [Boscia.Heuristic(Boscia.rounding_lmo_01_heuristic, 0.7, :rounding_lmo_01_heuristic), + Boscia.Heuristic(Boscia.probability_rounding, 0.7, :probability_rounding)] + + x, _, result = + Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true, time_limit=120, custom_heuristics = custom_heuristics) + + if result[:total_time_in_sec] < 125 + @test x == round.(diffi) + end + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) +end + +@testset "Using DoubleFloat" begin + function f(x) + x = Double64.(x) + return float(0.5 * sum((x[i] - diffi[i])^2 for i in eachindex(x))) + end + function grad!(storage, x) + x = Double64.(x) + @. storage = x - diffi + return float.(storage) + end + + int_vars = collect(1:n) + lbs = zeros(n) + ubs = ones(n) + + sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars) + custom_heuristics= [Boscia.Heuristic(Boscia.rounding_lmo_01_heuristic, 0.7, :rounding_lmo_01_heuristic), + Boscia.Heuristic(Boscia.probability_rounding, 0.7, :probability_rounding)] + + x, _, result = + Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true, time_limit=125, custom_heuristics = custom_heuristics) + + if result[:total_time_in_sec] < 125 + @test x == round.(diffi) + end + @test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3) +end diff --git a/examples/plot_utils.jl b/examples/plot_utils.jl new file mode 100644 index 000000000..c0d938223 --- /dev/null +++ b/examples/plot_utils.jl @@ -0,0 +1,438 @@ +using Plots +using FiniteDifferences + +""" +plot_results +Given a series of list, generate subplots. +list_data_y -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the y-values of the series inside a subplot). +list_data_x -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the x-values of the series inside a subplot). +So if we have one plot with two series, these might look like: + list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] + list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] +And if we have two plots, each with two series, these might look like: + list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] + list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] +list_label -> contains the labels for the series that will be plotted, +which has to have a length equal to the number of series that are being plotted: + list_label = ["Series 1", "Series 2"] +list_axis_x -> contains the labels for the x-axis that will be plotted, +which has to have a length equal to the number of subplots: + list_axis_x = ["x-axis plot 1", "x-axis plot 1"] +list_axis_y -> Same as list_axis_x but for the y-axis +xscalelog -> A list of values indicating the type of axes to use in each subplot, +must be equal to the number of subplots: + xscalelog = [:log, :identity] +yscalelog -> Same as xscalelog but for the y-axis +""" +function plot_results( + list_data_y, + list_data_x, + list_label, + list_axis_x, + list_axis_y; + filename=nothing, + xscalelog=nothing, + yscalelog=nothing, + legend_position=nothing, + list_style=fill(:solid, length(list_label)), + list_color=get_color_palette(:auto, plot_color(:white)), + list_markers=[ + :circle, + :rect, + :utriangle, + :diamond, + :hexagon, + :+, + :x, + :star5, + :cross, + :xcross, + :dtriangle, + :rtriangle, + :ltriangle, + :pentagon, + :heptagon, + :octagon, + :star4, + :star6, + :star7, + :star8, + :vline, + :hline, + ], + number_markers_per_line=10, + line_width=3.0, + marker_size=5.0, + transparency_markers=0.45, + font_size_axis=12, + font_size_legend=9, +) + gr() + plt = nothing + list_plots = Plots.Plot{Plots.GRBackend}[] + #Plot an appropiate number of plots + for i in eachindex(list_data_x) + for j in eachindex(list_data_x[i]) + if isnothing(xscalelog) + xscale = :identity + else + xscale = xscalelog[i] + end + if isnothing(yscalelog) + yscale = :log + else + yscale = yscalelog[i] + end + if isnothing(legend_position) + position_legend = :best + legend_display = true + else + position_legend = legend_position[i] + if isnothing(position_legend) + legend_display = false + else + legend_display = true + end + end + if j == 1 + if legend_display + plt = plot( + list_data_x[i][j], + list_data_y[i][j], + label="", + xaxis=xscale, + yaxis=yscale, + ylabel=list_axis_y[i], + xlabel=list_axis_x[i], + legend=position_legend, + yguidefontsize=font_size_axis, + xguidefontsize=font_size_axis, + legendfontsize=font_size_legend, + width=line_width, + linestyle=list_style[j], + color=list_color[j], + grid=true, + ) + else + plt = plot( + list_data_x[i][j], + list_data_y[i][j], + label="", + xaxis=xscale, + yaxis=yscale, + ylabel=list_axis_y[i], + xlabel=list_axis_x[i], + yguidefontsize=font_size_axis, + xguidefontsize=font_size_axis, + width=line_width, + linestyle=list_style[j], + color=list_color[j], + grid=true, + ) + end + else + if legend_display + plot!( + list_data_x[i][j], + list_data_y[i][j], + label="", + width=line_width, + linestyle=list_style[j], + color=list_color[j], + legend=position_legend, + ) + else + plot!( + list_data_x[i][j], + list_data_y[i][j], + label="", + width=line_width, + linestyle=list_style[j], + color=list_color[j], + ) + end + end + if xscale == :log + indices = + round.( + Int, + 10 .^ (range( + log10(1), + log10(length(list_data_x[i][j])), + length=number_markers_per_line, + )), + ) + scatter!( + list_data_x[i][j][indices], + list_data_y[i][j][indices], + markershape=list_markers[j], + markercolor=list_color[j], + markersize=marker_size, + markeralpha=transparency_markers, + label=list_label[j], + legend=position_legend, + ) + else + scatter!( + view( + list_data_x[i][j], + 1:length(list_data_x[i][j])÷number_markers_per_line:length( + list_data_x[i][j], + ), + ), + view( + list_data_y[i][j], + 1:length(list_data_y[i][j])÷number_markers_per_line:length( + list_data_y[i][j], + ), + ), + markershape=list_markers[j], + markercolor=list_color[j], + markersize=marker_size, + markeralpha=transparency_markers, + label=list_label[j], + legend=position_legend, + ) + end + end + push!(list_plots, plt) + end + fp = plot(list_plots..., layout=length(list_plots)) + plot!(size=(600, 400)) + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +# Recipe for plotting markers in plot_trajectories +@recipe function f(::Type{Val{:samplemarkers}}, x, y, z; n_markers=10, log=false) + n = length(y) + + # Choose datapoints for markers + if log + xmin = log10(x[1]) + xmax = log10(x[end]) + thresholds = collect(xmin:(xmax-xmin)/(n_markers-1):xmax) + indices = [argmin(i -> abs(t - log10(x[i])), eachindex(x)) for t in thresholds] + else + indices = 1:Int(ceil(length(x) / n_markers)):n + end + sx, sy = x[indices], y[indices] + + # add an empty series with the correct type for legend markers + @series begin + seriestype := :path + markershape --> :auto + x := [] + y := [] + end + # add a series for the line + @series begin + primary := false # no legend entry + markershape := :none # ensure no markers + seriestype := :path + seriescolor := get(plotattributes, :seriescolor, :auto) + x := x + y := y + end + # return a series for the sampled markers + primary := false + seriestype := :scatter + markershape --> :auto + x := sx + y := sy + z_order := 1 +end + +function plot_trajectories( + data, + label; + filename=nothing, + xscalelog=false, + yscalelog=true, + legend_position=:topright, + lstyle=fill(:solid, length(data)), + marker_shapes=nothing, + n_markers=10, + reduce_size=false, + primal_offset=1e-8, + line_width=1.3, + empty_marker=false, + extra_plot=false, + extra_plot_label="", +) + # theme(:dark) + # theme(:vibrant) + Plots.gr() + + x = [] + y = [] + offset = 2 + + function sub_plot(idx_x, idx_y; legend=false, xlabel="", ylabel="", y_offset=0) + + fig = nothing + + for (i, trajectory) in enumerate(data) + + l = length(trajectory) + if reduce_size && l > 1000 + indices = Int.(round.(collect(1:l/1000:l))) + trajectory = trajectory[indices] + end + + x = [trajectory[j][idx_x] for j in offset:length(trajectory)] + y = [trajectory[j][idx_y] + y_offset for j in offset:length(trajectory)] + + if marker_shapes !== nothing && n_markers >= 2 + marker_args = Dict( + :st => :samplemarkers, + :n_markers => n_markers, + :shape => marker_shapes[i], + :log => xscalelog, + :markercolor => empty_marker ? :white : :match, + :markerstrokecolor => empty_marker ? i : :match, + ) + else + marker_args = Dict() + end + + if i == 1 + fig = plot( + x, + y, + label=label[i], + xaxis=xscalelog ? :log : :identity, + yaxis=yscalelog ? :log : :identity, + xlabel=xlabel, + ylabel=ylabel, + legend=legend, + yguidefontsize=8, + xguidefontsize=8, + legendfontsize=8, + width=line_width, + linestyle=lstyle[i]; + marker_args..., + ) + else + plot!(x, y, label=label[i], width=line_width, linestyle=lstyle[i]; marker_args...) + end + end + return fig + end + + pit = sub_plot(1, 2; legend=legend_position, ylabel="Primal", y_offset=primal_offset) + pti = sub_plot(5, 2; y_offset=primal_offset) + dit = sub_plot(1, 4; xlabel="Iterations", ylabel="FW gap") + dti = sub_plot(5, 4; xlabel="Time (s)") + + if extra_plot + iit = sub_plot(1, 6; ylabel=extra_plot_label) + iti = sub_plot(5, 6) + fp = plot(pit, pti, iit, iti, dit, dti, layout=(3, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 600)) + else + fp = plot(pit, pti, dit, dti, layout=(2, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 400)) + end + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +function plot_sparsity( + data, + label; + filename=nothing, + xscalelog=false, + legend_position=:topright, + yscalelog=true, + lstyle=fill(:solid, length(data)), + marker_shapes=nothing, + n_markers=10, + empty_marker=false, + reduce_size=false, +) + Plots.gr() + + xscale = xscalelog ? :log : :identity + yscale = yscalelog ? :log : :identity + offset = 2 + + function subplot(idx_x, idx_y, ylabel) + + fig = nothing + for (i, trajectory) in enumerate(data) + + l = length(trajectory) + if reduce_size && l > 1000 + indices = Int.(round.(collect(1:l/1000:l))) + trajectory = trajectory[indices] + end + + + x = [trajectory[j][idx_x] for j in offset:length(trajectory)] + y = [trajectory[j][idx_y] for j in offset:length(trajectory)] + if marker_shapes !== nothing && n_markers >= 2 + marker_args = Dict( + :st => :samplemarkers, + :n_markers => n_markers, + :shape => marker_shapes[i], + :log => xscalelog, + :startmark => 5 + 20 * (i - 1), + :markercolor => empty_marker ? :white : :match, + :markerstrokecolor => empty_marker ? i : :match, + ) + else + marker_args = Dict() + end + if i == 1 + fig = plot( + x, + y; + label=label[i], + xaxis=xscale, + yaxis=yscale, + ylabel=ylabel, + legend=legend_position, + yguidefontsize=8, + xguidefontsize=8, + legendfontsize=8, + linestyle=lstyle[i], + marker_args..., + ) + else + plot!(x, y; label=label[i], linestyle=lstyle[i], marker_args...) + end + end + + return fig + end + + ps = subplot(6, 2, "Primal") + ds = subplot(6, 4, "FW gap") + + fp = plot(ps, ds, layout=(1, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) + plot!(size=(600, 200)) + if filename !== nothing + savefig(fp, filename) + end + return fp +end + +""" +Check if the gradient using finite differences matches the grad! provided. +""" +function check_gradients(grad!, f, gradient, num_tests=10, tolerance=1.0e-5) + for i in 1:num_tests + random_point = similar(gradient) + random_point .= rand(length(gradient)) + grad!(gradient, random_point) + if norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient) > tolerance + @warn "There is a noticeable difference between the gradient provided and + the gradient computed using finite differences.:\n$(norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient))" + end + end +end From d97242abe0a6bae3f6ac9239d419783a0a2ad708 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:55:30 +0200 Subject: [PATCH 09/49] Update README.md --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bc49b4ce7..4a972c162 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorith **min_{x ∈ C, x_I ∈ Z^n} f(x)**, where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. -This approach is particularly effective when we can efficiently optimize a linear function over C and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. -A paper presenting the package with mathematical explanations and numerous examples can be found here: +This approach is particularly effective if we can solve the mixed-integer linear minimization problem over C efficiently and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. +The paper presenting the package with mathematical explanations and numerous examples can be found here: > Convex integer optimization with Frank-Wolfe methods: [2208.11010](https://arxiv.org/abs/2208.11010) @@ -20,7 +20,7 @@ A paper presenting the package with mathematical explanations and numerous examp ## Installation -If you haven't downloaded JULIA yet go on this link and download it according to your system requirement . ['Julia'](https://julialang.org/downloads/). + Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REPL mode and run ```Boscia pkg > add Boscia @@ -35,13 +35,11 @@ import Pkg Pkg.add("Boscia") ``` -Or get the latest master branch with: -```julia -import Pkg -Pkg.add(url="https://github.com/ZIB-IOL/Boscia.jl", rev="main") -``` + If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) +If you want to use SCIP within Boscia and your OS is windows, you will have download SCIP separately, see SCIP.jl. +Note that you do not necessarily have to download the binaries but can also use the installer provided by SCIP. **For Window Users** You need not to download whole SCIP binary instead you can follow **Custom Installation** mentioned on this page and download and link SCIP with your JULIA . From 0e7eaccee4619babf7225a86b7a68cd370d3bce8 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Tue, 11 Jun 2024 14:43:51 +0200 Subject: [PATCH 10/49] DELeted docs --- basics.md | 4 - contributing.md | 4 - docs/Project.toml | 3 - docs/make.jl | 153 ----------- docs/src/basics.md | 8 - docs/src/contributing.md | 8 - docs/src/examples.md | 8 - docs/src/examples/plot_utils.jl | 438 ------------------------------- docs/src/index.md | 10 - examples.md | 4 - examples/HiGHS_example.jl | 9 +- examples/approx_planted_point.jl | 9 + examples/big_float_example.jl | 11 +- examples/birkhoff.jl | 8 + examples/cube_blmo.jl | 28 ++ examples/int_sparse_reg.jl | 11 +- examples/lasso.jl | 10 +- examples/low_dim_in_high_dim.jl | 8 +- examples/mps-example.jl | 8 +- examples/sparse_reg.jl | 6 +- 20 files changed, 101 insertions(+), 647 deletions(-) delete mode 100644 basics.md delete mode 100644 contributing.md delete mode 100644 docs/Project.toml delete mode 100644 docs/make.jl delete mode 100644 docs/src/basics.md delete mode 100644 docs/src/contributing.md delete mode 100644 docs/src/examples.md delete mode 100644 docs/src/examples/plot_utils.jl delete mode 100644 docs/src/index.md delete mode 100644 examples.md diff --git a/basics.md b/basics.md deleted file mode 100644 index 5fd675900..000000000 --- a/basics.md +++ /dev/null @@ -1,4 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/BASICS.md" -``` - diff --git a/contributing.md b/contributing.md deleted file mode 100644 index 7362b2bf0..000000000 --- a/contributing.md +++ /dev/null @@ -1,4 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/CONTRIBUTING.md" -``` - diff --git a/docs/Project.toml b/docs/Project.toml deleted file mode 100644 index d473f72ea..000000000 --- a/docs/Project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[deps] -Boscia = "630d6612-02b6-44cc-9b59-bf75311a0c58" -Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl deleted file mode 100644 index 9466e5c1b..000000000 --- a/docs/make.jl +++ /dev/null @@ -1,153 +0,0 @@ -using Pkg -pkg"activate .. " - - -using Boscia -using Documenter -using SparseArrays -using LinearAlgebra - -using Literate, Test - -# Activate the project environment -project_path = joinpath(dirname(@__DIR__)) # Adjust this path if necessary -Pkg.activate(project_path) - -# Ensure all dependencies are installed -Pkg.instantiate() - -EXAMPLE_DIR = joinpath(dirname(@__DIR__), "examples") -DOCS_EXAMPLE_DIR = joinpath(@__DIR__, "src", "examples") -DOCS_REFERENCE_DIR = joinpath(@__DIR__, "src", "reference") - -function file_list(dir, extension) - return filter(file -> endswith(file, extension), sort(readdir(dir))) -end - -# includes plot_utils to the example file before running it -function include_utils(content) - return """ - import Boscia ; include(joinpath(dirname(pathof(Boscia)), "../examples/plot_utils.jl")) # hide - """ * content -end - -function literate_directory(jl_dir, md_dir) - for filename in file_list(md_dir, ".md") - filepath = joinpath(md_dir, filename) - rm(filepath) - end - for filename in file_list(jl_dir, ".jl") - filepath = joinpath(jl_dir, filename) - # `include` the file to test it before `#src` lines are removed. It is - # in a testset to isolate local variables between files. - if startswith(filename, "docs") - Literate.markdown( - filepath, md_dir; - documenter=true, flavor=Literate.DocumenterFlavor(), preprocess=include_utils, - ) - end - end - return nothing -end - -literate_directory(EXAMPLE_DIR, DOCS_EXAMPLE_DIR) -cp(joinpath(EXAMPLE_DIR, "plot_utils.jl"), joinpath(DOCS_EXAMPLE_DIR, "plot_utils.jl") , force = true) - -ENV["GKSwstype"] = "100" - -generated_path = joinpath(@__DIR__, "src") -base_url = "https://github.com/saurabhintoml/Boscia.jl/" -isdir(generated_path) || mkdir(generated_path) - -open(joinpath(generated_path, "contributing.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)CONTRIBUTING.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "CONTRIBUTING.md")) - println(io, line) - end -end - -open(joinpath(generated_path, "basics.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)BASICS.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "BASICS.md")) - println(io, line) - end -end - -open(joinpath(generated_path, "Examples.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)EXAMPLES.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "EXAMPLES.md")) - println(io, line) - end -end - -open(joinpath(generated_path, "index.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)README.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "README.md")) - println(io, line) - end -end - -makedocs(; - modules=[Boscia], - authors="Saurabh Srivastava", - repo = "https://saurabhintoml.github.io/Boscia.jl/blob/{commit}{path}#L{line}" , - sitename="Boscia.jl", - format=Documenter.HTML(; - prettyurls = get(ENV , "CI" , nothing) == "true" , - repolink ="https://saurabhintoml.github.io/Boscia.jl", - edit_link="master", - assets=String[], - ), - pages=[ - "Home" => "index.md", - "How Does it Work" => "basics.md" , - - - "Examples" => "examples.md", - "API reference" => - [joinpath("reference", f) for f in file_list(DOCS_REFERENCE_DIR, ".md")], - "Contributing" => "contributing.md", - ], -) - -deploydocs(; - repo="github.com/saurabhintoml/Boscia.jl", - push_preview = true , - devbranch = "master" ) - diff --git a/docs/src/basics.md b/docs/src/basics.md deleted file mode 100644 index 2f71859d1..000000000 --- a/docs/src/basics.md +++ /dev/null @@ -1,8 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/BASICS.md" -``` - -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/BASICS.md" -``` - diff --git a/docs/src/contributing.md b/docs/src/contributing.md deleted file mode 100644 index 558a2b060..000000000 --- a/docs/src/contributing.md +++ /dev/null @@ -1,8 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/CONTRIBUTING.md" -``` - -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/CONTRIBUTING.md" -``` - diff --git a/docs/src/examples.md b/docs/src/examples.md deleted file mode 100644 index af4448e24..000000000 --- a/docs/src/examples.md +++ /dev/null @@ -1,8 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/EXAMPLES.md" -``` - -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/EXAMPLES.md" -``` - diff --git a/docs/src/examples/plot_utils.jl b/docs/src/examples/plot_utils.jl deleted file mode 100644 index c0d938223..000000000 --- a/docs/src/examples/plot_utils.jl +++ /dev/null @@ -1,438 +0,0 @@ -using Plots -using FiniteDifferences - -""" -plot_results -Given a series of list, generate subplots. -list_data_y -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the y-values of the series inside a subplot). -list_data_x -> contains a list of a list of lists (where each list refers to a subplot, and a list of lists refers to the x-values of the series inside a subplot). -So if we have one plot with two series, these might look like: - list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] - list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]] -And if we have two plots, each with two series, these might look like: - list_data_y = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] - list_data_x = [[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]], [[7, 8, 9, 10, 11, 12], [7, 8, 9, 10, 11, 12]]] -list_label -> contains the labels for the series that will be plotted, -which has to have a length equal to the number of series that are being plotted: - list_label = ["Series 1", "Series 2"] -list_axis_x -> contains the labels for the x-axis that will be plotted, -which has to have a length equal to the number of subplots: - list_axis_x = ["x-axis plot 1", "x-axis plot 1"] -list_axis_y -> Same as list_axis_x but for the y-axis -xscalelog -> A list of values indicating the type of axes to use in each subplot, -must be equal to the number of subplots: - xscalelog = [:log, :identity] -yscalelog -> Same as xscalelog but for the y-axis -""" -function plot_results( - list_data_y, - list_data_x, - list_label, - list_axis_x, - list_axis_y; - filename=nothing, - xscalelog=nothing, - yscalelog=nothing, - legend_position=nothing, - list_style=fill(:solid, length(list_label)), - list_color=get_color_palette(:auto, plot_color(:white)), - list_markers=[ - :circle, - :rect, - :utriangle, - :diamond, - :hexagon, - :+, - :x, - :star5, - :cross, - :xcross, - :dtriangle, - :rtriangle, - :ltriangle, - :pentagon, - :heptagon, - :octagon, - :star4, - :star6, - :star7, - :star8, - :vline, - :hline, - ], - number_markers_per_line=10, - line_width=3.0, - marker_size=5.0, - transparency_markers=0.45, - font_size_axis=12, - font_size_legend=9, -) - gr() - plt = nothing - list_plots = Plots.Plot{Plots.GRBackend}[] - #Plot an appropiate number of plots - for i in eachindex(list_data_x) - for j in eachindex(list_data_x[i]) - if isnothing(xscalelog) - xscale = :identity - else - xscale = xscalelog[i] - end - if isnothing(yscalelog) - yscale = :log - else - yscale = yscalelog[i] - end - if isnothing(legend_position) - position_legend = :best - legend_display = true - else - position_legend = legend_position[i] - if isnothing(position_legend) - legend_display = false - else - legend_display = true - end - end - if j == 1 - if legend_display - plt = plot( - list_data_x[i][j], - list_data_y[i][j], - label="", - xaxis=xscale, - yaxis=yscale, - ylabel=list_axis_y[i], - xlabel=list_axis_x[i], - legend=position_legend, - yguidefontsize=font_size_axis, - xguidefontsize=font_size_axis, - legendfontsize=font_size_legend, - width=line_width, - linestyle=list_style[j], - color=list_color[j], - grid=true, - ) - else - plt = plot( - list_data_x[i][j], - list_data_y[i][j], - label="", - xaxis=xscale, - yaxis=yscale, - ylabel=list_axis_y[i], - xlabel=list_axis_x[i], - yguidefontsize=font_size_axis, - xguidefontsize=font_size_axis, - width=line_width, - linestyle=list_style[j], - color=list_color[j], - grid=true, - ) - end - else - if legend_display - plot!( - list_data_x[i][j], - list_data_y[i][j], - label="", - width=line_width, - linestyle=list_style[j], - color=list_color[j], - legend=position_legend, - ) - else - plot!( - list_data_x[i][j], - list_data_y[i][j], - label="", - width=line_width, - linestyle=list_style[j], - color=list_color[j], - ) - end - end - if xscale == :log - indices = - round.( - Int, - 10 .^ (range( - log10(1), - log10(length(list_data_x[i][j])), - length=number_markers_per_line, - )), - ) - scatter!( - list_data_x[i][j][indices], - list_data_y[i][j][indices], - markershape=list_markers[j], - markercolor=list_color[j], - markersize=marker_size, - markeralpha=transparency_markers, - label=list_label[j], - legend=position_legend, - ) - else - scatter!( - view( - list_data_x[i][j], - 1:length(list_data_x[i][j])÷number_markers_per_line:length( - list_data_x[i][j], - ), - ), - view( - list_data_y[i][j], - 1:length(list_data_y[i][j])÷number_markers_per_line:length( - list_data_y[i][j], - ), - ), - markershape=list_markers[j], - markercolor=list_color[j], - markersize=marker_size, - markeralpha=transparency_markers, - label=list_label[j], - legend=position_legend, - ) - end - end - push!(list_plots, plt) - end - fp = plot(list_plots..., layout=length(list_plots)) - plot!(size=(600, 400)) - if filename !== nothing - savefig(fp, filename) - end - return fp -end - -# Recipe for plotting markers in plot_trajectories -@recipe function f(::Type{Val{:samplemarkers}}, x, y, z; n_markers=10, log=false) - n = length(y) - - # Choose datapoints for markers - if log - xmin = log10(x[1]) - xmax = log10(x[end]) - thresholds = collect(xmin:(xmax-xmin)/(n_markers-1):xmax) - indices = [argmin(i -> abs(t - log10(x[i])), eachindex(x)) for t in thresholds] - else - indices = 1:Int(ceil(length(x) / n_markers)):n - end - sx, sy = x[indices], y[indices] - - # add an empty series with the correct type for legend markers - @series begin - seriestype := :path - markershape --> :auto - x := [] - y := [] - end - # add a series for the line - @series begin - primary := false # no legend entry - markershape := :none # ensure no markers - seriestype := :path - seriescolor := get(plotattributes, :seriescolor, :auto) - x := x - y := y - end - # return a series for the sampled markers - primary := false - seriestype := :scatter - markershape --> :auto - x := sx - y := sy - z_order := 1 -end - -function plot_trajectories( - data, - label; - filename=nothing, - xscalelog=false, - yscalelog=true, - legend_position=:topright, - lstyle=fill(:solid, length(data)), - marker_shapes=nothing, - n_markers=10, - reduce_size=false, - primal_offset=1e-8, - line_width=1.3, - empty_marker=false, - extra_plot=false, - extra_plot_label="", -) - # theme(:dark) - # theme(:vibrant) - Plots.gr() - - x = [] - y = [] - offset = 2 - - function sub_plot(idx_x, idx_y; legend=false, xlabel="", ylabel="", y_offset=0) - - fig = nothing - - for (i, trajectory) in enumerate(data) - - l = length(trajectory) - if reduce_size && l > 1000 - indices = Int.(round.(collect(1:l/1000:l))) - trajectory = trajectory[indices] - end - - x = [trajectory[j][idx_x] for j in offset:length(trajectory)] - y = [trajectory[j][idx_y] + y_offset for j in offset:length(trajectory)] - - if marker_shapes !== nothing && n_markers >= 2 - marker_args = Dict( - :st => :samplemarkers, - :n_markers => n_markers, - :shape => marker_shapes[i], - :log => xscalelog, - :markercolor => empty_marker ? :white : :match, - :markerstrokecolor => empty_marker ? i : :match, - ) - else - marker_args = Dict() - end - - if i == 1 - fig = plot( - x, - y, - label=label[i], - xaxis=xscalelog ? :log : :identity, - yaxis=yscalelog ? :log : :identity, - xlabel=xlabel, - ylabel=ylabel, - legend=legend, - yguidefontsize=8, - xguidefontsize=8, - legendfontsize=8, - width=line_width, - linestyle=lstyle[i]; - marker_args..., - ) - else - plot!(x, y, label=label[i], width=line_width, linestyle=lstyle[i]; marker_args...) - end - end - return fig - end - - pit = sub_plot(1, 2; legend=legend_position, ylabel="Primal", y_offset=primal_offset) - pti = sub_plot(5, 2; y_offset=primal_offset) - dit = sub_plot(1, 4; xlabel="Iterations", ylabel="FW gap") - dti = sub_plot(5, 4; xlabel="Time (s)") - - if extra_plot - iit = sub_plot(1, 6; ylabel=extra_plot_label) - iti = sub_plot(5, 6) - fp = plot(pit, pti, iit, iti, dit, dti, layout=(3, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) - plot!(size=(600, 600)) - else - fp = plot(pit, pti, dit, dti, layout=(2, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) - plot!(size=(600, 400)) - end - if filename !== nothing - savefig(fp, filename) - end - return fp -end - -function plot_sparsity( - data, - label; - filename=nothing, - xscalelog=false, - legend_position=:topright, - yscalelog=true, - lstyle=fill(:solid, length(data)), - marker_shapes=nothing, - n_markers=10, - empty_marker=false, - reduce_size=false, -) - Plots.gr() - - xscale = xscalelog ? :log : :identity - yscale = yscalelog ? :log : :identity - offset = 2 - - function subplot(idx_x, idx_y, ylabel) - - fig = nothing - for (i, trajectory) in enumerate(data) - - l = length(trajectory) - if reduce_size && l > 1000 - indices = Int.(round.(collect(1:l/1000:l))) - trajectory = trajectory[indices] - end - - - x = [trajectory[j][idx_x] for j in offset:length(trajectory)] - y = [trajectory[j][idx_y] for j in offset:length(trajectory)] - if marker_shapes !== nothing && n_markers >= 2 - marker_args = Dict( - :st => :samplemarkers, - :n_markers => n_markers, - :shape => marker_shapes[i], - :log => xscalelog, - :startmark => 5 + 20 * (i - 1), - :markercolor => empty_marker ? :white : :match, - :markerstrokecolor => empty_marker ? i : :match, - ) - else - marker_args = Dict() - end - if i == 1 - fig = plot( - x, - y; - label=label[i], - xaxis=xscale, - yaxis=yscale, - ylabel=ylabel, - legend=legend_position, - yguidefontsize=8, - xguidefontsize=8, - legendfontsize=8, - linestyle=lstyle[i], - marker_args..., - ) - else - plot!(x, y; label=label[i], linestyle=lstyle[i], marker_args...) - end - end - - return fig - end - - ps = subplot(6, 2, "Primal") - ds = subplot(6, 4, "FW gap") - - fp = plot(ps, ds, layout=(1, 2)) # layout = @layout([A{0.01h}; [B C; D E]])) - plot!(size=(600, 200)) - if filename !== nothing - savefig(fp, filename) - end - return fp -end - -""" -Check if the gradient using finite differences matches the grad! provided. -""" -function check_gradients(grad!, f, gradient, num_tests=10, tolerance=1.0e-5) - for i in 1:num_tests - random_point = similar(gradient) - random_point .= rand(length(gradient)) - grad!(gradient, random_point) - if norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient) > tolerance - @warn "There is a noticeable difference between the gradient provided and - the gradient computed using finite differences.:\n$(norm(grad(central_fdm(5, 1), f, random_point)[1] - gradient))" - end - end -end diff --git a/docs/src/index.md b/docs/src/index.md deleted file mode 100644 index 70f51d6d6..000000000 --- a/docs/src/index.md +++ /dev/null @@ -1,10 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/README.md" -``` - -# Boscia - -[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/stable/) -[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://saurabhintoml.github.io/Boscia.jl/dev/) -[![Build Status](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/saurabhintoml/Boscia.jl/actions/workflows/CI.yml?query=branch%3Amaster) -[![Coverage](https://codecov.io/gh/saurabhintoml/Boscia.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/saurabhintoml/Boscia.jl) diff --git a/examples.md b/examples.md deleted file mode 100644 index 4a51b1018..000000000 --- a/examples.md +++ /dev/null @@ -1,4 +0,0 @@ -```@meta -EditURL = "https://github.com/saurabhintoml/Boscia.jl/tree/main/EXAMPLES.md" -``` - diff --git a/examples/HiGHS_example.jl b/examples/HiGHS_example.jl index 043d77f3c..321c1dd5e 100644 --- a/examples/HiGHS_example.jl +++ b/examples/HiGHS_example.jl @@ -1,4 +1,11 @@ -using Boscia +# This script sets up and solves a linear optimization problem using the Boscia and FrankWolfe packages in Julia. +# The problem involves a 6-dimensional variable `x` that is constrained to be between 0 and 1 (inclusive) and is also restricted to binary values (0 or 1). +# The objective function `f(x)` is a quadratic function representing the squared Euclidean distance from a vector `diffw` (which is set to 0.5 for all components). +# The gradient of the objective function is computed by `grad!`. +# The `HiGHS.Optimizer` is used to handle the optimization problem and the `FrankWolfe.MathOptLMO` interface connects it to the Frank-Wolfe algorithm. +# The final solution `x` and the result status are obtained using `Boscia.solve`.using Boscia + + using FrankWolfe using Random using HiGHS diff --git a/examples/approx_planted_point.jl b/examples/approx_planted_point.jl index 04a709ea0..3431d0f08 100644 --- a/examples/approx_planted_point.jl +++ b/examples/approx_planted_point.jl @@ -1,3 +1,12 @@ + +# This script tests different methods for solving optimization problems involving approximate planted points +# with integer and mixed integer constraints. The optimization methods used are SCIP, Cube LMO, and Cube Simple LMO. +# The script defines a function f(x) representing the objective function and its gradient. It then runs several +# test sets using these methods, checks the solution against expected values, and verifies the correctness of +# the results using `@test` assertions. + +# We first import the necessary packages, including SCIP and LinearAlgebra to visualize the feasible set. + using Boscia using FrankWolfe using Test diff --git a/examples/big_float_example.jl b/examples/big_float_example.jl index c738bc47e..1acceb56a 100644 --- a/examples/big_float_example.jl +++ b/examples/big_float_example.jl @@ -1,4 +1,13 @@ -using Random +# This script performs optimization tests using the Boscia package with high precision arithmetic +# (BigFloat and DoubleFloat) on a function that has integer constraints. The goal is to verify +# the correctness and performance of the optimization process using these high-precision types. +# It includes two test sets, one using BigFloat and another using DoubleFloat, and employs +# custom heuristics for solving the optimization problem. The results are compared against +# expected values and checked for accuracy using `@test` assertionsusing Random + +# We first import the necessary packages, which will run the required test . + + using Boscia using Test using DoubleFloats diff --git a/examples/birkhoff.jl b/examples/birkhoff.jl index 7c7025b48..84d07be91 100644 --- a/examples/birkhoff.jl +++ b/examples/birkhoff.jl @@ -1,3 +1,11 @@ +# This script demonstrates the use of Frank-Wolfe optimization on the Birkhoff polytope with +# permutation matrices. It utilizes the Boscia and SCIP packages to solve a bilinear optimization +# problem. The objective is to minimize the Frobenius norm between a weighted sum of permutation +# matrices and a given doubly stochastic matrix Xstar. The script defines the objective function +# and its gradient, constructs a Linear Minimization Oracle (LMO) using SCIP, and performs the +# optimization. The results are validated using test sets to ensure the solution quality. + + using Boscia using FrankWolfe using Test diff --git a/examples/cube_blmo.jl b/examples/cube_blmo.jl index 76fa6230d..1d2a75539 100644 --- a/examples/cube_blmo.jl +++ b/examples/cube_blmo.jl @@ -1,4 +1,32 @@ ## How to implement the BLMO Interface using the cube as an example +# The code provided is an implementation of a Bounded Linear Minimization Oracle (BLMO) for a cube, using the Julia programming language and leveraging the Boscia and Bonobo libraries. This BLMO is designed to work within the structure of these libraries to facilitate linear minimization problems within bounded constraints. +# +# Key Components and Functions: +# +# 1. CubeBLMO Structure: +# - `CubeBLMO` is a mutable struct representing the BLMO with fields for the number of dimensions (`n`), integer variables (`int_vars`), bounds (`bounds`), and the time taken for solving (`solving_time`). +# +# 2. Necessary Functions: +# - `compute_extreme_point`: Computes an extreme point of the cube by checking the sign of the gradient. +# - `build_global_bounds`: Builds global bounds for integer variables. +# - Information Retrieval Functions: +# - `get_list_of_variables`, `get_integer_variables`, `get_int_var` +# - `get_lower_bound_list`, `get_upper_bound_list`, `get_bound` +# - Bounds Management Functions: +# - `set_bound!`, `delete_bounds!`, `add_bound_constraint!` +# - Checks: +# - `is_constraint_on_int_var`, `is_bound_in`, `is_linear_feasible` +# - `has_integer_constraint` +# +# 3. Optional Safety Functions: +# - `build_LMO_correct`: Ensures the LMO is built correctly. +# - `check_feasibility`: Checks the feasibility of the constraints. +# - `is_valid_split`: Validates a split for branching in a tree structure. +# +# 4. Logging: +# - `get_BLMO_solve_data`: Retrieves the solving time data. + + using Boscia using Bonobo using Dates diff --git a/examples/int_sparse_reg.jl b/examples/int_sparse_reg.jl index 1d7a0a3b9..0ca2e5641 100644 --- a/examples/int_sparse_reg.jl +++ b/examples/int_sparse_reg.jl @@ -1,4 +1,13 @@ -using Statistics +# This script performs integer sparse regression, aiming to minimize the norm of (y - A * x)² subject to constraints. + # The constraints are: + # - 0 <= x_i <= l, where l = 5 + # - ∑ x_i <= k, where k = 4 + # - x_i ∈ Z for i = 1, ..., n + # The objective function f(x) represents the squared Euclidean distance between y and A * x. + # The optimization problem is solved using the Boscia and FrankWolfe packages with the SCIP optimizer. + # The final solution x and the result status are obtained using `Boscia.solve`.using Statistics + + using Boscia using FrankWolfe using Random diff --git a/examples/lasso.jl b/examples/lasso.jl index 3743ac87c..d24d85677 100644 --- a/examples/lasso.jl +++ b/examples/lasso.jl @@ -1,4 +1,12 @@ -using Statistics +# This script performs Lasso regression, aiming to minimize the norm of (y - A * β)² subject to constraints. + # The constraints are: + # - -M <= β_i <= M + # - ∑ z_i <= k + # - z_i ∈ {0,1} for i = 1, ..., p + # The objective function f(x) represents the squared Euclidean distance between y and A * β, with additional penalty terms. + # The optimization problem is solved using the Boscia and FrankWolfe packages with the SCIP optimizer. + # The final solution x and the result status are obtained using `Boscia.solve`.using Statistics + using Distributions using Boscia using FrankWolfe diff --git a/examples/low_dim_in_high_dim.jl b/examples/low_dim_in_high_dim.jl index d8fcb749f..b225942dc 100644 --- a/examples/low_dim_in_high_dim.jl +++ b/examples/low_dim_in_high_dim.jl @@ -1,4 +1,10 @@ -using Boscia +# This code optimizes a low-dimensional convex function over a high-dimensional cube. It defines a function f(x) and its gradient, +# sets up two different optimization approaches: one using the SCIP optimizer and the other using a simple bounded linear minimization +# oracle (BLMO) tailored for a cube. The function `f(x)` represents a quadratic function involving a matrix `W` and a reference point. +# The optimization problem is solved using Boscia and FrankWolfe packages, and the final solution and the result status are obtained +# using `Boscia.solve`.using Boscia + + using FrankWolfe using Test using Random diff --git a/examples/mps-example.jl b/examples/mps-example.jl index 6ea84f0ed..5c126ba89 100644 --- a/examples/mps-example.jl +++ b/examples/mps-example.jl @@ -1,4 +1,10 @@ -using Boscia +# This code optimizes the MPS 22433 instance from the MIPLIB library. It minimizes the distance to randomly picked vertices +# as the objective function. The number of variables is 429, with no integer variables. The code reads the MPS file, sets up +# the SCIP optimizer, and uses the FrankWolfe method for optimization. It pushes the optimum towards the interior by utilizing +# a trick and then defines the objective function and its gradient. Finally, it solves the optimization problem using Boscia +# and ensures the obtained solution is optimal.using Boscia + + using FrankWolfe using Test using Random diff --git a/examples/sparse_reg.jl b/examples/sparse_reg.jl index 112ea7525..37e88b3d7 100644 --- a/examples/sparse_reg.jl +++ b/examples/sparse_reg.jl @@ -1,4 +1,8 @@ -using Statistics +# This code performs sparse regression using the Boscia optimization framework. The objective is to minimize the function +# ||y - Aβ||² + λ_0 ∑ z_i + λ_2 ||β||², subject to the constraints -Mz_i <= β_i <= Mz_i and ∑ z_i <= k, where z_i ∈ {0,1} +# for i = 1,..,p. It sets up the SCIP optimizer, defines the objective function and its gradient, and solves the optimization +# problem using the FrankWolfe method. Finally, it ensures that the obtained solution is optimal.using Statistics + using Boscia using FrankWolfe using Random From 502b9c3bcd74fea48b51ae02462f35d2fdabb680 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Tue, 11 Jun 2024 21:08:45 +0200 Subject: [PATCH 11/49] Merge changes from master into main --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a972c162..cf418e430 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,9 @@ Note that you do not necessarily have to download the binaries but can also use Here is a simple example to get started. For more examples, see the examples folder in the package. + ```julia + using Boscia using FrankWolfe using Random @@ -100,8 +102,6 @@ Parameter settings. Total number of varibales: 6 Number of integer variables: 0 Number of binary variables: 6 - - --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Iteration Open Bound Incumbent Gap (abs) Gap (rel) Time (s) Nodes/sec FW (ms) LMO (ms) LMO (calls c) FW (Its) #ActiveSet Discarded --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -138,3 +138,4 @@ Search Statistics. LMO calls / sec: 1205.1724137931035 Nodes / sec: 218.96551724137933 LMO calls / node: 5.503937007874016 +``` \ No newline at end of file From 3b3dbb311d15fbae4d7b413f38cb303b5445fc6d Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Tue, 11 Jun 2024 21:13:33 +0200 Subject: [PATCH 12/49] Add docs directory --- docs | 1 + 1 file changed, 1 insertion(+) create mode 160000 docs diff --git a/docs b/docs new file mode 160000 index 000000000..5e6d3821f --- /dev/null +++ b/docs @@ -0,0 +1 @@ +Subproject commit 5e6d3821f5a33127c37e7e833b700affc216a575 From c16b07f630a04ee0c332a1de99595819612c00cc Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:17:06 +0200 Subject: [PATCH 13/49] Update README.md --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cf418e430..160b1a72e 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ A solver for Mixed-Integer Convex Optimization that uses Frank-Wolfe methods for The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorithm and a branch-and-bound-like algorithm to solve mixed-integer convex optimization problems. These problems are of the form: **min_{x ∈ C, x_I ∈ Z^n} f(x)**, -where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. - This approach is particularly effective if we can solve the mixed-integer linear minimization problem over C efficiently and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. The paper presenting the package with mathematical explanations and numerous examples can be found here: @@ -26,10 +24,8 @@ Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REP pkg > add Boscia ``` -or alternatively you can do this - -Add the Boscia stable release with: +or alternatively via Pkg in any Julia code: ```julia import Pkg Pkg.add("Boscia") @@ -37,7 +33,10 @@ Pkg.add("Boscia") +Suggested change If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) + + If you want to use SCIP within Boscia and your OS is windows, you will have download SCIP separately, see SCIP.jl. Note that you do not necessarily have to download the binaries but can also use the installer provided by SCIP. @@ -55,7 +54,6 @@ Here is a simple example to get started. For more examples, see the examples fol ```julia - using Boscia using FrankWolfe using Random @@ -138,4 +136,4 @@ Search Statistics. LMO calls / sec: 1205.1724137931035 Nodes / sec: 218.96551724137933 LMO calls / node: 5.503937007874016 -``` \ No newline at end of file +``` From 7b1e8889ff55af0e973d649ac77dd162c2a7cc66 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 12:52:25 +0200 Subject: [PATCH 14/49] Committing changes to docs directory --- src/interface.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interface.jl b/src/interface.jl index 65fe63ad9..50ea13944 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,3 +1,5 @@ +# Interface.jl + """ solve From 6d4412b0b5aa6e0d090dc9dfdc6c1675b73eb9f0 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:01:23 +0200 Subject: [PATCH 15/49] Committing changes to CI workflow and docs --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index 5e6d3821f..d842182de 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 5e6d3821f5a33127c37e7e833b700affc216a575 +Subproject commit d842182de8e4810ea3e2d312da92264041e8848a From 74445d967b385d712570e842dc508eff7bdfbf21 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:26:17 +0200 Subject: [PATCH 16/49] Commit message describing your changes --- docs | 1 - 1 file changed, 1 deletion(-) delete mode 160000 docs diff --git a/docs b/docs deleted file mode 160000 index d842182de..000000000 --- a/docs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d842182de8e4810ea3e2d312da92264041e8848a From 295fa089fcb9a3acd5197362039830d4e9241fa5 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:30:42 +0200 Subject: [PATCH 17/49] Committing staged changes in docs directoy --- docs | 1 + 1 file changed, 1 insertion(+) create mode 160000 docs diff --git a/docs b/docs new file mode 160000 index 000000000..39f8f6407 --- /dev/null +++ b/docs @@ -0,0 +1 @@ +Subproject commit 39f8f6407c389163ae985586201be94e002e88cf From 37c36851f8894134744315c6c277cf285a7d15cc Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:45:22 +0200 Subject: [PATCH 18/49] Remove docs directory --- docs | 1 - 1 file changed, 1 deletion(-) delete mode 160000 docs diff --git a/docs b/docs deleted file mode 160000 index 39f8f6407..000000000 --- a/docs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 39f8f6407c389163ae985586201be94e002e88cf From ce042615dc526552dea61751af8d10935b23cae9 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:49:19 +0200 Subject: [PATCH 19/49] added docs --- docs | 1 + 1 file changed, 1 insertion(+) create mode 160000 docs diff --git a/docs b/docs new file mode 160000 index 000000000..5e6d3821f --- /dev/null +++ b/docs @@ -0,0 +1 @@ +Subproject commit 5e6d3821f5a33127c37e7e833b700affc216a575 From 00fbd57027953c7ff73f4a2022d618d0f7cc534b Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 13:59:14 +0200 Subject: [PATCH 20/49] Remove docs directory --- .gitmodules | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb From 8da59bec4089352adf8f89a1ce911090b7f9ad18 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 14:01:11 +0200 Subject: [PATCH 21/49] Remove docs directory --- docs | 1 - 1 file changed, 1 deletion(-) delete mode 160000 docs diff --git a/docs b/docs deleted file mode 160000 index 5e6d3821f..000000000 --- a/docs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5e6d3821f5a33127c37e7e833b700affc216a575 From 9e60c1954d078715b46c0502be4bbb3da86bb1ee Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 14:10:18 +0200 Subject: [PATCH 22/49] test2 --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/make.jl diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 000000000..5740ff63f --- /dev/null +++ b/docs/make.jl @@ -0,0 +1 @@ +# comment \ No newline at end of file From e657e902e08b0d8bff5b5f0b2408163df092fe02 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 14:18:23 +0200 Subject: [PATCH 23/49] test --- docs/src/basics.md | 47 ++++++++++ docs/src/how_to_run.md | 0 docs/src/index.md | 141 +++++++++++++++++++++++++++++ docs/src/reference/0_reference.md | 3 + docs/src/reference/1_algorithms.md | 12 +++ docs/src/reference/2_blmo_build.md | 24 +++++ docs/src/reference/custom.md | 24 +++++ docs/src/reference/fw_variant.md | 10 ++ docs/src/reference/utilities.md | 10 ++ 9 files changed, 271 insertions(+) create mode 100644 docs/src/basics.md create mode 100644 docs/src/how_to_run.md create mode 100644 docs/src/index.md create mode 100644 docs/src/reference/0_reference.md create mode 100644 docs/src/reference/1_algorithms.md create mode 100644 docs/src/reference/2_blmo_build.md create mode 100644 docs/src/reference/custom.md create mode 100644 docs/src/reference/fw_variant.md create mode 100644 docs/src/reference/utilities.md diff --git a/docs/src/basics.md b/docs/src/basics.md new file mode 100644 index 000000000..1a987c733 --- /dev/null +++ b/docs/src/basics.md @@ -0,0 +1,47 @@ +# How does it work ? + +Boscia focuses on mixed-integer convex problems in which the nonlinear constraints and objectives are convex and presents a new algorithmic framework for solving these problems that exploit recent advances in so-called Frank-Wolfe (FW) or Conditional Gradient (CG) methods. The problem class we consider is of the type: +$$ +\min ( f(x) ) \text{ where } x \in \mathbb{R}^n, \, x \in X +$$ + +where \( X \) is a compact nonconvex set admitting a boundable linear minimization oracle (LMO), i.e., a set over which optimizing a linear function can be done efficiently (comparatively to the original problem), even when bounds are added or modified. Formally, we consider we have access to an oracle taking new bounds \( (l, u) \) and a direction \( d \): + +Given : +$$ +\( (l, u, d) \in \mathbb{R}^n \times \mathbb{R}^n \times \mathbb{R}^n \), \( \arg\min_{v \in \mathbb{R}^n} \langle v, d \rangle \) + \text {subject to} \( v \in X \cap [l, u] \). +$$ + + +## BPCG + + +The classic FW algorithm's reliance on an LMO is inefficient for polytopes due to expensive calls. The Blended Pairwise Conditional Gradient (BPCG) algorithm improves efficiency by combining FW steps with pairwise steps that avoid LMO calls. BPCG maintains a smaller active set of vertices, enhancing performance. We use a modified lazified BPCG from FrankWolfe.jl. + +# Branch and Bound Techniques + +In this section, we present the techniques derived from branch-and-bound techniques that can be used in our +framework . + +## FW gap based termination + +Frank-Wolfe methods produce primal feasible iterates and an FW gap, offering many inexpensive iterations with a gradually increasing dual bound. This allows early termination of nodes when the dual bound reaches the best incumbent's objective value, avoiding unnecessary computations. Nodes can be stopped anytime to produce a useful dual bound, aiding overall progress. This flexibility contrasts with other nonlinear solvers, enabling more efficient optimization. + +## Tree stae-dependent termination and evolving error + +We implement termination criteria in node processing to reduce iterations, prioritizing nodes with promising lower bounds. An adaptive Frank-Wolfe gap criterion increases precision with depth in the branch-and-bound tree. BPCG's convex combinations of integer extreme points ensure rapid convergence, even with reduced precision runs. This approach balances efficiency and accuracy in solving optimization problems. + +## Strong Branching + +In tackling large discrete optimization problems, our method utilizes Frank-Wolfe algorithms to approximate lower bound improvements efficiently. By relaxing strong branching to continuous LPs for the Linear Minimization Oracle and controlling FW iterations or gap tolerances, we balance computational cost with accuracy. This approach optimizes variable selection in branch-and-bound algorithms, enhancing scalability and efficiency in solving complex discrete optimization challenges. + +## Dual fixing and tightening + +In subproblems where variables are at bounds, our approach utilizes convexity and primal solutions to tighten dual bounds effectively. Drawing from methods pioneered by Dantzig and extended in various contexts, we leverage Frank-Wolfe methods and FW gaps, adaptable to scenarios without explicit dual solutions, such as those involving MIP-based LMOs. + + + + + + diff --git a/docs/src/how_to_run.md b/docs/src/how_to_run.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 000000000..cf418e430 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,141 @@ +# Boscia.jl + +[![Build Status](https://github.com/ZIB-IOL/Boscia.jl/workflows/CI/badge.svg)](https://github.com/ZIB-IOL/Boscia.jl/actions) +[![Coverage](https://codecov.io/gh/ZIB-IOL/Boscia.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/ZIB-IOL/Boscia.jl) + +A solver for Mixed-Integer Convex Optimization that uses Frank-Wolfe methods for convex relaxations and a branch-and-bound algorithm. + +## Overview + +The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorithm and a branch-and-bound-like algorithm to solve mixed-integer convex optimization problems. These problems are of the form: +**min_{x ∈ C, x_I ∈ Z^n} f(x)**, +where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. + +This approach is particularly effective if we can solve the mixed-integer linear minimization problem over C efficiently and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. +The paper presenting the package with mathematical explanations and numerous examples can be found here: + +> Convex integer optimization with Frank-Wolfe methods: [2208.11010](https://arxiv.org/abs/2208.11010) + +`Boscia.jl` uses [`FrankWolfe.jl`](https://github.com/ZIB-IOL/FrankWolfe.jl) for solving the convex subproblems, [`Bonobo.jl`](https://github.com/Wikunia/Bonobo.jl) for managing the search tree, and oracles optimizing linear functions over the feasible set, for instance calling [SCIP](https://scipopt.org) or any MOI-compatible solver to solve MIP subproblems. + +## Installation + + +Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REPL mode and run +```Boscia +pkg > add Boscia + +``` +or alternatively you can do this + +Add the Boscia stable release with: + +```julia +import Pkg +Pkg.add("Boscia") +``` + + + +If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) +If you want to use SCIP within Boscia and your OS is windows, you will have download SCIP separately, see SCIP.jl. +Note that you do not necessarily have to download the binaries but can also use the installer provided by SCIP. + + +**For Window Users** You need not to download whole SCIP binary instead you can follow **Custom Installation** mentioned on this page and download and link SCIP with your JULIA . + + + + + + +## Getting started + +Here is a simple example to get started. For more examples, see the examples folder in the package. + + +```julia + +using Boscia +using FrankWolfe +using Random +using SCIP +using LinearAlgebra +import MathOptInterface +const MOI = MathOptInterface + +n = 6 + +const diffw = 0.5 * ones(n) +o = SCIP.Optimizer() + +MOI.set(o, MOI.Silent(), true) + +x = MOI.add_variables(o, n) + +for xi in x + MOI.add_constraint(o, xi, MOI.GreaterThan(0.0)) + MOI.add_constraint(o, xi, MOI.LessThan(1.0)) + MOI.add_constraint(o, xi, MOI.ZeroOne()) +end + +lmo = FrankWolfe.MathOptLMO(o) + +function f(x) + return sum(0.5*(x.-diffw).^2) +end + +function grad!(storage, x) + @. storage = x-diffw +end + +x, _, result = Boscia.solve(f, grad!, lmo, verbose = true) + +Boscia Algorithm. + +Parameter settings. + Tree traversal strategy: Move best bound + Branching strategy: Most infeasible + Absolute dual gap tolerance: 1.000000e-06 + Relative dual gap tolerance: 1.000000e-02 + Frank-Wolfe subproblem tolerance: 1.000000e-05 + Total number of varibales: 6 + Number of integer variables: 0 + Number of binary variables: 6 +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Iteration Open Bound Incumbent Gap (abs) Gap (rel) Time (s) Nodes/sec FW (ms) LMO (ms) LMO (calls c) FW (Its) #ActiveSet Discarded +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +* 1 2 -1.202020e-06 7.500000e-01 7.500012e-01 Inf 3.870000e-01 7.751938e+00 237 2 9 13 1 0 + 100 27 6.249998e-01 7.500000e-01 1.250002e-01 2.000004e-01 5.590000e-01 2.271914e+02 0 0 641 0 1 0 + 127 0 7.500000e-01 7.500000e-01 0.000000e+00 0.000000e+00 5.770000e-01 2.201040e+02 0 0 695 0 1 0 +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Postprocessing + +Blended Pairwise Conditional Gradient Algorithm. +MEMORY_MODE: FrankWolfe.InplaceEmphasis() STEPSIZE: Adaptive EPSILON: 1.0e-7 MAXITERATION: 10000 TYPE: Float64 +GRADIENTTYPE: Nothing LAZY: true lazy_tolerance: 2.0 +[ Info: In memory_mode memory iterates are written back into x0! + +---------------------------------------------------------------------------------------------------------------- + Type Iteration Primal Dual Dual Gap Time It/sec #ActiveSet +---------------------------------------------------------------------------------------------------------------- + Last 0 7.500000e-01 7.500000e-01 0.000000e+00 1.086583e-03 0.000000e+00 1 +---------------------------------------------------------------------------------------------------------------- + PP 0 7.500000e-01 7.500000e-01 0.000000e+00 1.927792e-03 0.000000e+00 1 +---------------------------------------------------------------------------------------------------------------- + +Solution Statistics. + Solution Status: Optimal (tree empty) + Primal Objective: 0.75 + Dual Bound: 0.75 + Dual Gap (relative): 0.0 + +Search Statistics. + Total number of nodes processed: 127 + Total number of lmo calls: 699 + Total time (s): 0.58 + LMO calls / sec: 1205.1724137931035 + Nodes / sec: 218.96551724137933 + LMO calls / node: 5.503937007874016 +``` \ No newline at end of file diff --git a/docs/src/reference/0_reference.md b/docs/src/reference/0_reference.md new file mode 100644 index 000000000..c772c1e77 --- /dev/null +++ b/docs/src/reference/0_reference.md @@ -0,0 +1,3 @@ +# API Reference + +The pages in this section reference the documentation for specific types and functions. \ No newline at end of file diff --git a/docs/src/reference/1_algorithms.md b/docs/src/reference/1_algorithms.md new file mode 100644 index 000000000..d4c6679f2 --- /dev/null +++ b/docs/src/reference/1_algorithms.md @@ -0,0 +1,12 @@ +# Algorithms + +This section contains information about interface.jl + +## Interface + +```@autodocs +Modules = [Boscia] +Pages = ["interface.jl"] +``` + + diff --git a/docs/src/reference/2_blmo_build.md b/docs/src/reference/2_blmo_build.md new file mode 100644 index 000000000..dbbd4ed2c --- /dev/null +++ b/docs/src/reference/2_blmo_build.md @@ -0,0 +1,24 @@ +# LMO + +This section contains information about interface blmo_interface.jl , build_lmo.jl and time_tracking_lmo.jl + +## BLMO_Interface + +```@autodocs +Modules = [Boscia] +Pages = ["blmo_interface.jl"] +``` + +## Build LMO + +```@autodocs +Modules = [Boscia] +Pages = ["build_lmo.jl"] +``` + +## Time Tracking LMO + +```@autodocs +Modules = [Boscia] +Pages = ["time_tracking_lmo.jl"] +``` \ No newline at end of file diff --git a/docs/src/reference/custom.md b/docs/src/reference/custom.md new file mode 100644 index 000000000..40c18798c --- /dev/null +++ b/docs/src/reference/custom.md @@ -0,0 +1,24 @@ +# CusBono IntBound Problem + +This section contain information about custom_bonobo.jl , integer_bounds.jl and problem.jl + +## Custom Bonobo + +```@autodocs +Modules = [Boscia] +Pages = ["custom_bonobo.jl"] +``` + +## Integer Bounds + +```@autodocs +Modules = [Boscia] +Pages = ["integer_bounds.jl"] +``` + +## Problem + +```@autodocs +Modules = [Boscia] +Pages = ["problem.jl"] +``` \ No newline at end of file diff --git a/docs/src/reference/fw_variant.md b/docs/src/reference/fw_variant.md new file mode 100644 index 000000000..ef938b1ce --- /dev/null +++ b/docs/src/reference/fw_variant.md @@ -0,0 +1,10 @@ +# FW_variant + +This file contains information about all the frank_wolfe_variants.jl + +## Frank Wolfe + +```@autodocs +Modules = [Boscia] +Pages = ["frank_wolfe_variants.jl"] +``` \ No newline at end of file diff --git a/docs/src/reference/utilities.md b/docs/src/reference/utilities.md new file mode 100644 index 000000000..debcf1dd5 --- /dev/null +++ b/docs/src/reference/utilities.md @@ -0,0 +1,10 @@ +# Utilities + +This section contain information about utilities.jl + +## utilities + +```@autodocs +Modules = [Boscia] +Pages = ["utilities.jl"] +``` \ No newline at end of file From 74db1e496e11d3d49172d4a7c38b07809ad9f0d1 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 19 Jun 2024 14:19:39 +0200 Subject: [PATCH 24/49] make --- docs/make.jl | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 5740ff63f..058aa737f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1 +1,51 @@ -# comment \ No newline at end of file +using Documenter +using Boscia + +working_dir = @__DIR__ + +# Function to copy contents of README.md to index.md +function copy_readme_to_index() + readme_path = joinpath(working_dir, "..", "README.md") # Adjusted path + index_path = joinpath(working_dir, k"src", "index.md") + readme_content = read(readme_path, String) + write(index_path, readme_content) +end + + + +# Call the functions to update index.md and 1_algorithms.md +copy_readme_to_index() + + +# Generate documentation +makedocs( + sitename = "Boscia.jl", + modules = [Boscia], + format = Documenter.HTML(repolink = "https://github.com/saurabhintoml/Boscia.jl.git"), + pages = [ + "Home" => "index.md", + "How does it work?" => "basics.md", + + "API Reference" => [ + "reference/0_reference.md", + "reference/1_algorithms.md", + "reference/2_blmo_build.md" , + "reference/custom.md" , + "reference/fw_variant.md" , + "reference/utilities.md" + + ] + ], + warnonly = true +) + +# Deploy documentation +deploydocs( + repo = "https://github.com/saurabhintoml/Boscia.jl.git", + devbranch = "main", + devurl = "dev", + target = "build", + branch = "gh-pages", + versions = ["stable" => "v^", "v#.#"], + push_preview = true +) From dee270d55e8330865da96c35ce7656175f16d855 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:52:29 +0200 Subject: [PATCH 25/49] Update utilities.md --- docs/src/reference/utilities.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/src/reference/utilities.md b/docs/src/reference/utilities.md index debcf1dd5..18f7320ce 100644 --- a/docs/src/reference/utilities.md +++ b/docs/src/reference/utilities.md @@ -1,10 +1,8 @@ # Utilities -This section contain information about utilities.jl -## utilities ```@autodocs Modules = [Boscia] Pages = ["utilities.jl"] -``` \ No newline at end of file +``` From 77e33b8e7afefb722c79b933e50c2f362aaa289d Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:53:40 +0200 Subject: [PATCH 26/49] Update utilities.md --- docs/src/reference/utilities.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/reference/utilities.md b/docs/src/reference/utilities.md index 18f7320ce..030feb412 100644 --- a/docs/src/reference/utilities.md +++ b/docs/src/reference/utilities.md @@ -1,5 +1,7 @@ # Utilities +This section contain some utility function + ```@autodocs From 4950810c54bac85318815046509ab1a9b5cd69c8 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:55:09 +0200 Subject: [PATCH 27/49] Update fw_variant.md --- docs/src/reference/fw_variant.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/reference/fw_variant.md b/docs/src/reference/fw_variant.md index ef938b1ce..79c0dbd2e 100644 --- a/docs/src/reference/fw_variant.md +++ b/docs/src/reference/fw_variant.md @@ -1,10 +1,10 @@ -# FW_variant +Here is the list with the currently supported Frank-Wolfe variants. + -This file contains information about all the frank_wolfe_variants.jl ## Frank Wolfe ```@autodocs Modules = [Boscia] Pages = ["frank_wolfe_variants.jl"] -``` \ No newline at end of file +``` From aa03829f21e3b96e42323250bd56858cf0c325be Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:00:21 +0200 Subject: [PATCH 28/49] Update custom.md --- docs/src/reference/custom.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/reference/custom.md b/docs/src/reference/custom.md index 40c18798c..39a169f3e 100644 --- a/docs/src/reference/custom.md +++ b/docs/src/reference/custom.md @@ -1,6 +1,6 @@ -# CusBono IntBound Problem +# Bonobo -This section contain information about custom_bonobo.jl , integer_bounds.jl and problem.jl +This section contains the customization of the branch-and-bound implementation of Bonobo.jl. ## Custom Bonobo @@ -21,4 +21,4 @@ Pages = ["integer_bounds.jl"] ```@autodocs Modules = [Boscia] Pages = ["problem.jl"] -``` \ No newline at end of file +``` From 66f3171119d17dcb6cb63ce2da7f1946ab391eb9 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:01:22 +0200 Subject: [PATCH 29/49] Update custom.md --- docs/src/reference/custom.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/custom.md b/docs/src/reference/custom.md index 39a169f3e..d0bc0ce51 100644 --- a/docs/src/reference/custom.md +++ b/docs/src/reference/custom.md @@ -1,4 +1,4 @@ -# Bonobo +# Branch and Bound Tree functionality This section contains the customization of the branch-and-bound implementation of Bonobo.jl. From 36a63511d416bbb9e27f4b173ebd36a36c5810ff Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:06:03 +0200 Subject: [PATCH 30/49] Update 2_blmo_build.md --- docs/src/reference/2_blmo_build.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/src/reference/2_blmo_build.md b/docs/src/reference/2_blmo_build.md index dbbd4ed2c..77aa93f80 100644 --- a/docs/src/reference/2_blmo_build.md +++ b/docs/src/reference/2_blmo_build.md @@ -1,24 +1,26 @@ -# LMO +# The Bounded Linear Minimization Oracle (BLMO) -This section contains information about interface blmo_interface.jl , build_lmo.jl and time_tracking_lmo.jl +This section describes the general interface to the Bounded Linear Minimization Oracle and the preimplemented types of BLMO. -## BLMO_Interface +## General BLMO interface ```@autodocs Modules = [Boscia] Pages = ["blmo_interface.jl"] ``` -## Build LMO + +## Time Tracking Wrapper ```@autodocs Modules = [Boscia] -Pages = ["build_lmo.jl"] +Pages = ["time_tracking_lmo.jl"] ``` -## Time Tracking LMO +## Build LMO ```@autodocs Modules = [Boscia] -Pages = ["time_tracking_lmo.jl"] -``` \ No newline at end of file +Pages = ["build_lmo.jl"] +``` + From e5e9b06e68a0f06992a6734ba9881474e8a3d887 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:06:38 +0200 Subject: [PATCH 31/49] Update 0_reference.md --- docs/src/reference/0_reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/0_reference.md b/docs/src/reference/0_reference.md index c772c1e77..f1b8b9ec8 100644 --- a/docs/src/reference/0_reference.md +++ b/docs/src/reference/0_reference.md @@ -1,3 +1,3 @@ # API Reference -The pages in this section reference the documentation for specific types and functions. \ No newline at end of file +In this section we present the algorithm interface and the possible settings. From f9654cccbfada865d4bc8cc9e67c7a860fbe479e Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:06:57 +0200 Subject: [PATCH 32/49] Update 1_algorithms.md --- docs/src/reference/1_algorithms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/1_algorithms.md b/docs/src/reference/1_algorithms.md index d4c6679f2..33b59b02f 100644 --- a/docs/src/reference/1_algorithms.md +++ b/docs/src/reference/1_algorithms.md @@ -1,4 +1,4 @@ -# Algorithms +# Algorithm This section contains information about interface.jl From 876ec9bbd6317cd907e17c60ff9d760851820976 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:09:41 +0200 Subject: [PATCH 33/49] Update basics.md --- docs/src/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/basics.md b/docs/src/basics.md index 1a987c733..600647431 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -21,7 +21,7 @@ The classic FW algorithm's reliance on an LMO is inefficient for polytopes due t # Branch and Bound Techniques -In this section, we present the techniques derived from branch-and-bound techniques that can be used in our +In this section, we present the techniques derived from Frank Wolfe that can be used in our framework . ## FW gap based termination From a356e1321aafb3cfb9ba3c1c612dfdac7eaf4022 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:13:30 +0200 Subject: [PATCH 34/49] Update basics.md --- docs/src/basics.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/src/basics.md b/docs/src/basics.md index 600647431..448a02b43 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -1,6 +1,6 @@ # How does it work ? -Boscia focuses on mixed-integer convex problems in which the nonlinear constraints and objectives are convex and presents a new algorithmic framework for solving these problems that exploit recent advances in so-called Frank-Wolfe (FW) or Conditional Gradient (CG) methods. The problem class we consider is of the type: +Boscia focuses on mixed-integer convex problems in which the linear constraints and objectives are convex and presents a new algorithmic framework for solving these problems that exploit recent advances in so-called Frank-Wolfe (FW) or Conditional Gradient (CG) methods. The problem class we consider is of the type: $$ \min ( f(x) ) \text{ where } x \in \mathbb{R}^n, \, x \in X $$ @@ -30,7 +30,10 @@ Frank-Wolfe methods produce primal feasible iterates and an FW gap, offering man ## Tree stae-dependent termination and evolving error -We implement termination criteria in node processing to reduce iterations, prioritizing nodes with promising lower bounds. An adaptive Frank-Wolfe gap criterion increases precision with depth in the branch-and-bound tree. BPCG's convex combinations of integer extreme points ensure rapid convergence, even with reduced precision runs. This approach balances efficiency and accuracy in solving optimization problems. +We implement termination criteria in node processing to reduce iterations, prioritizing nodes with promising lower bounds. An adaptive Frank-Wolfe gap criterion increases precision with depth in the branch-and-bound tree. Frank Wolfe's convex combinations of integer extreme points ensure valid dual bound, even with reduced precision runs. This approach balances efficiency and accuracy in solving optimization problems.we have Hybrid branching which couples strong branching and most infeasible branching. Strong branching is very expensive to do for the whole tree and it's usually more beneficial to utilize strong branching only up to a certain depth. + + + ## Strong Branching From fa0b5ef398d8311d808b057af6e5c86e84d15328 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:14:51 +0200 Subject: [PATCH 35/49] Update int_sparse_reg.jl --- examples/int_sparse_reg.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/int_sparse_reg.jl b/examples/int_sparse_reg.jl index 0ca2e5641..99eacbb67 100644 --- a/examples/int_sparse_reg.jl +++ b/examples/int_sparse_reg.jl @@ -6,6 +6,7 @@ # The objective function f(x) represents the squared Euclidean distance between y and A * x. # The optimization problem is solved using the Boscia and FrankWolfe packages with the SCIP optimizer. # The final solution x and the result status are obtained using `Boscia.solve`.using Statistics + # The solve function is the one doing the heavy lifting, i.e. creating and solving the branch-and-bound tree! using Boscia From 2f1a67124bc946aad9322cbfc6e9ddbd8b106371 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:17:58 +0200 Subject: [PATCH 36/49] Update cube_blmo.jl --- examples/cube_blmo.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cube_blmo.jl b/examples/cube_blmo.jl index 1d2a75539..9714aede8 100644 --- a/examples/cube_blmo.jl +++ b/examples/cube_blmo.jl @@ -1,5 +1,5 @@ ## How to implement the BLMO Interface using the cube as an example -# The code provided is an implementation of a Bounded Linear Minimization Oracle (BLMO) for a cube, using the Julia programming language and leveraging the Boscia and Bonobo libraries. This BLMO is designed to work within the structure of these libraries to facilitate linear minimization problems within bounded constraints. +# The code provided is an implementation of a Bounded Linear Minimization Oracle (BLMO) for a cube, and leveraging the Boscia and Bonobo libraries. This BLMO is designed to work within the structure of these libraries to facilitate linear minimization problems within bounded constraints. # # Key Components and Functions: # @@ -190,4 +190,4 @@ end ## Logs function Boscia.get_BLMO_solve_data(blmo::CubeBLMO) return blmo.solving_time, 0.0, 0.0 -end \ No newline at end of file +end From e702c729027a94c40f1c4a3c73afac7559894ba1 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:18:59 +0200 Subject: [PATCH 37/49] Update birkhoff.jl --- examples/birkhoff.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/birkhoff.jl b/examples/birkhoff.jl index 84d07be91..049cdf692 100644 --- a/examples/birkhoff.jl +++ b/examples/birkhoff.jl @@ -1,4 +1,4 @@ -# This script demonstrates the use of Frank-Wolfe optimization on the Birkhoff polytope with +# This script demonstrates the use of Frank-Wolfe algorithm on the Birkhoff polytope with # permutation matrices. It utilizes the Boscia and SCIP packages to solve a bilinear optimization # problem. The objective is to minimize the Frobenius norm between a weighted sum of permutation # matrices and a given doubly stochastic matrix Xstar. The script defines the objective function From e3efc8a0ff5423f438be18d686c3b93ad54af0f3 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:20:28 +0200 Subject: [PATCH 38/49] Update HiGHS_example.jl --- examples/HiGHS_example.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/HiGHS_example.jl b/examples/HiGHS_example.jl index 321c1dd5e..08b892717 100644 --- a/examples/HiGHS_example.jl +++ b/examples/HiGHS_example.jl @@ -2,7 +2,7 @@ # The problem involves a 6-dimensional variable `x` that is constrained to be between 0 and 1 (inclusive) and is also restricted to binary values (0 or 1). # The objective function `f(x)` is a quadratic function representing the squared Euclidean distance from a vector `diffw` (which is set to 0.5 for all components). # The gradient of the objective function is computed by `grad!`. -# The `HiGHS.Optimizer` is used to handle the optimization problem and the `FrankWolfe.MathOptLMO` interface connects it to the Frank-Wolfe algorithm. +# The `HiGHS.Optimizer` is used to handle the linear minimization problem and the `FrankWolfe.MathOptLMO` interface connects it to the Frank-Wolfe algorithm. # The final solution `x` and the result status are obtained using `Boscia.solve`.using Boscia From 9e8740094156b0fde031257173fce64655682c9e Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:22:38 +0200 Subject: [PATCH 39/49] Update HiGHS_example.jl --- examples/HiGHS_example.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/HiGHS_example.jl b/examples/HiGHS_example.jl index 08b892717..614c7d064 100644 --- a/examples/HiGHS_example.jl +++ b/examples/HiGHS_example.jl @@ -1,4 +1,4 @@ -# This script sets up and solves a linear optimization problem using the Boscia and FrankWolfe packages in Julia. +# This script sets up and solves quadratic problem with linear constraints using the Boscia and FrankWolfe packages in Julia. # The problem involves a 6-dimensional variable `x` that is constrained to be between 0 and 1 (inclusive) and is also restricted to binary values (0 or 1). # The objective function `f(x)` is a quadratic function representing the squared Euclidean distance from a vector `diffw` (which is set to 0.5 for all components). # The gradient of the objective function is computed by `grad!`. From f0ef10dc7fa871a8545ee7d5ffa73f8a1ceb3d68 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:24:23 +0200 Subject: [PATCH 40/49] Update approx_planted_point.jl --- examples/approx_planted_point.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/approx_planted_point.jl b/examples/approx_planted_point.jl index 3431d0f08..a4b5c875a 100644 --- a/examples/approx_planted_point.jl +++ b/examples/approx_planted_point.jl @@ -1,9 +1,7 @@ # This script tests different methods for solving optimization problems involving approximate planted points # with integer and mixed integer constraints. The optimization methods used are SCIP, Cube LMO, and Cube Simple LMO. -# The script defines a function f(x) representing the objective function and its gradient. It then runs several -# test sets using these methods, checks the solution against expected values, and verifies the correctness of -# the results using `@test` assertions. +# The script defines a function f(x) representing the objective function and its gradient. # We first import the necessary packages, including SCIP and LinearAlgebra to visualize the feasible set. From 50a6fc9f02b3a46a245844db91d69aee144408ba Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:25:10 +0200 Subject: [PATCH 41/49] Update HiGHS_example.jl --- examples/HiGHS_example.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/HiGHS_example.jl b/examples/HiGHS_example.jl index 614c7d064..a878991b2 100644 --- a/examples/HiGHS_example.jl +++ b/examples/HiGHS_example.jl @@ -3,7 +3,9 @@ # The objective function `f(x)` is a quadratic function representing the squared Euclidean distance from a vector `diffw` (which is set to 0.5 for all components). # The gradient of the objective function is computed by `grad!`. # The `HiGHS.Optimizer` is used to handle the linear minimization problem and the `FrankWolfe.MathOptLMO` interface connects it to the Frank-Wolfe algorithm. -# The final solution `x` and the result status are obtained using `Boscia.solve`.using Boscia +# The final solution `x` and the result status are obtained using `Boscia.solve` + +using Boscia using FrankWolfe From 14757399b9b6d189dfde09e1f7bcd615a2fabf77 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:28:18 +0200 Subject: [PATCH 42/49] Update basics.md --- docs/src/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/basics.md b/docs/src/basics.md index 448a02b43..025dd2841 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -1,6 +1,6 @@ # How does it work ? -Boscia focuses on mixed-integer convex problems in which the linear constraints and objectives are convex and presents a new algorithmic framework for solving these problems that exploit recent advances in so-called Frank-Wolfe (FW) or Conditional Gradient (CG) methods. The problem class we consider is of the type: +Boscia focuses on mixed-integer convex problems in which the constraints linear , objective non linear are convex and presents a new algorithmic framework for solving these problems that exploit recent advances in so-called Frank-Wolfe (FW) or Conditional Gradient (CG) methods. The problem class we consider is of the type: $$ \min ( f(x) ) \text{ where } x \in \mathbb{R}^n, \, x \in X $$ From a16eca23a8efe99de6b8715ee4b8668185f5c0db Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Tue, 2 Jul 2024 13:15:10 +0200 Subject: [PATCH 43/49] Interface --- docs/.github/dependabot.yml | 7 ++ docs/.github/workflows/CompatHelper.yml | 16 +++ docs/.github/workflows/TagBot.yml | 15 +++ docs/.github/workflows/ci.yml | 45 +++++++ docs/make.jl | 2 +- docs/src/index.md | 12 +- docs/src/reference/2_blmo_build.md | 21 ++++ docs/src/reference/fw_variant.md | 4 +- src/interface.jl | 148 +++++++++++++++--------- 9 files changed, 205 insertions(+), 65 deletions(-) create mode 100644 docs/.github/dependabot.yml create mode 100644 docs/.github/workflows/CompatHelper.yml create mode 100644 docs/.github/workflows/TagBot.yml create mode 100644 docs/.github/workflows/ci.yml diff --git a/docs/.github/dependabot.yml b/docs/.github/dependabot.yml new file mode 100644 index 000000000..700707ced --- /dev/null +++ b/docs/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/docs/.github/workflows/CompatHelper.yml b/docs/.github/workflows/CompatHelper.yml new file mode 100644 index 000000000..cba9134c6 --- /dev/null +++ b/docs/.github/workflows/CompatHelper.yml @@ -0,0 +1,16 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} + run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/docs/.github/workflows/TagBot.yml b/docs/.github/workflows/TagBot.yml new file mode 100644 index 000000000..f49313b66 --- /dev/null +++ b/docs/.github/workflows/TagBot.yml @@ -0,0 +1,15 @@ +name: TagBot +on: + issue_comment: + types: + - created + workflow_dispatch: +jobs: + TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/docs/.github/workflows/ci.yml b/docs/.github/workflows/ci.yml new file mode 100644 index 000000000..2e16d7e33 --- /dev/null +++ b/docs/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI +on: + push: + branches: [master] + pull_request: + types: [opened, synchronize, reopened] +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - version: '1' + os: ubuntu-latest + arch: x64 + - version: '1.6' + os: ubuntu-latest + arch: x64 + - version: '1' + os: macOS-latest + arch: x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: actions/cache@v1 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v1 + with: + file: lcov.info diff --git a/docs/make.jl b/docs/make.jl index 058aa737f..800915f79 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,7 +6,7 @@ working_dir = @__DIR__ # Function to copy contents of README.md to index.md function copy_readme_to_index() readme_path = joinpath(working_dir, "..", "README.md") # Adjusted path - index_path = joinpath(working_dir, k"src", "index.md") + index_path = joinpath(working_dir, "src", "index.md") readme_content = read(readme_path, String) write(index_path, readme_content) end diff --git a/docs/src/index.md b/docs/src/index.md index cf418e430..160b1a72e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,8 +9,6 @@ A solver for Mixed-Integer Convex Optimization that uses Frank-Wolfe methods for The Boscia.jl solver uses a combination of a variant of the Frank-Wolfe algorithm and a branch-and-bound-like algorithm to solve mixed-integer convex optimization problems. These problems are of the form: **min_{x ∈ C, x_I ∈ Z^n} f(x)**, -where f is a differentiable convex function, C is a convex and compact set, and I is a set of indices for integer variables. - This approach is particularly effective if we can solve the mixed-integer linear minimization problem over C efficiently and handle the integer constraints. The set C is specified using the MathOptInterface API or any domain-specific language (DSL) like Julia for Mathematical Programming (**JuMP**) that implements this API. The paper presenting the package with mathematical explanations and numerous examples can be found here: @@ -26,10 +24,8 @@ Once you have installed Julia , From the Julia REPL, type ] to enter the Pkg REP pkg > add Boscia ``` -or alternatively you can do this - -Add the Boscia stable release with: +or alternatively via Pkg in any Julia code: ```julia import Pkg Pkg.add("Boscia") @@ -37,7 +33,10 @@ Pkg.add("Boscia") +Suggested change If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) + + If you want to use SCIP within Boscia and your OS is windows, you will have download SCIP separately, see SCIP.jl. Note that you do not necessarily have to download the binaries but can also use the installer provided by SCIP. @@ -55,7 +54,6 @@ Here is a simple example to get started. For more examples, see the examples fol ```julia - using Boscia using FrankWolfe using Random @@ -138,4 +136,4 @@ Search Statistics. LMO calls / sec: 1205.1724137931035 Nodes / sec: 218.96551724137933 LMO calls / node: 5.503937007874016 -``` \ No newline at end of file +``` diff --git a/docs/src/reference/2_blmo_build.md b/docs/src/reference/2_blmo_build.md index 77aa93f80..9bdf00d40 100644 --- a/docs/src/reference/2_blmo_build.md +++ b/docs/src/reference/2_blmo_build.md @@ -24,3 +24,24 @@ Modules = [Boscia] Pages = ["build_lmo.jl"] ``` +## MOI Oracle + +```@autodocs +Modules = [Boscia] +Pages = ["MOI_bounded_oracle.jl"] + +``` +## Managed BLMO + +```@autodocs +Modules = [Boscia] +Pages = ["managed_blmo.jl"] +``` + +## Polytopes + +```@autodocs +Modules = [Boscia] +Pages = ["polytope_blmos.jl"] +``` + diff --git a/docs/src/reference/fw_variant.md b/docs/src/reference/fw_variant.md index 79c0dbd2e..537951dda 100644 --- a/docs/src/reference/fw_variant.md +++ b/docs/src/reference/fw_variant.md @@ -1,8 +1,8 @@ +# Frank Wolfe Variant Here is the list with the currently supported Frank-Wolfe variants. - -## Frank Wolfe + ```@autodocs Modules = [Boscia] diff --git a/src/interface.jl b/src/interface.jl index 50ea13944..816224ecc 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,62 +1,100 @@ # Interface.jl """ - solve - -f - objective function oracle. -g - oracle for the gradient of the objective. -blmo - a MIP solver instance (e.g., SCIP) encoding the feasible region. Has to be of type `BoundedLinearMinimizationOracle` (see `lmo_wrapper.jl`). -traverse_strategy - encodes how to choose the next node for evaluation. - By default the node with the best lower bound is picked. -branching_strategy - by default we branch on the entry which is the farthest - away from being an integer. -variant - variant of FrankWolfe to be used to solve the node problem. - Options: FW -- Vanilla FrankWolfe - AFW -- Away FrankWolfe - BPCG -- Blended Pairwise Conditional Gradient -line_search - specifies the Line Search method used in the FrankWolfe variant. - Default is the Adaptive Line Search. For other types, check the FrankWolfe.jl package. -active_set - can be used to specify a starting point, e.g. if the feasible region is not completely - contained in the domain of the objective. By default, the direction (1,..,n) where n is - the size of the problem is used to find a start vertex. Beware that the active set may - only contain actual vertices of the feasible region. -lazy - specifies whether the lazification shoud be used. Per default true. - Beware that it has no effect with Vanilla Frank-Wolfe. -lazy_tolerance - decides how much progress is deemed enough to not have to call the LMO. -fw_epsilon - the tolerance for FrankWolfe in the root node. -verbose - if true, a log and solution statistics are printed. -dual_gap - if this absolute dual gap is reached, the algorithm stops. -rel_dual_gap - if this relative dual gap is reached, the algorithm stops. -time_limit - algorithm will stop if the time limit is reached. Depending on the problem - it is possible that no feasible solution has been found yet. -print_iter - encodes after how many proccessed nodes the current node and solution status - is printed. Will always print if a new integral solution has been found. -dual_gap_decay_factor - the FrankWolfe tolerance at a given level i in the tree is given by - fw_epsilon * dual_gap_decay_factor^i until we reach the min_node_fw_epsilon. -max_fw_iter - maximum number of iterations in a FrankWolfe run. -min_number_lower - If not Inf, evaluation of a node is stopped if at least min_number_lower nodes have a better - lower bound. -min_node_fw_epsilon - smallest fw epsilon possible, see dual_gap_decay_factor. -use_postsolve - Runs the specified Frank-Wolfe variant on the problem with the integral variables fixed to the solution, i.e. - it only optimizes over the continuous variables. This might improve the solution if one has many continuous variables. -min_fw_iterations - the minimum number of FrankWolfe iterations in the node evaluation. -max_iteration_post - maximum number of iterations in a FrankWolfe run during postsolve -dual_tightening - whether to use dual tightening techniques (make sure your function is convex!) -global_dual_tightening - dual tightening maintained globally valid (when new solutions are found) -bnb_callback - an optional callback called at every node of the tree, for example for heuristics -strong_convexity - strong convexity of the function, used for tighter dual bound at every node -domain_oracle - For a point x: returns true if x is in the domain of f, else false. Per default is true. - In case of the non trivial domain oracle, the starting point has to be feasible for f. Also, depending - on the Line Search method, you might have to provide the domain oracle to it, too. -start_solution - initial solution to start with an incumbent -fw_verbose - if true, FrankWolfe logs are printed -use_shadow_set - The shadow set is the set of discarded vertices which is inherited by the children nodes. - It is used to avoid recomputing of vertices in case the LMO is expensive. In case of a cheap LMO, - performance might improve by disabling this option. -custom_heuristics - List of custom heuristic from the user. -prob_rounding - The probability for calling the rounding heuristics. Since the feasibility has to be checked, it might - expensive to do this for every node. +``` Solve + +- 'f' - objective function oracle. + +- 'g' - oracle for the gradient of the objective. + +- 'blmo` - a MIP solver instance (e.g., SCIP) encoding the feasible region. + Has to be of type `BoundedLinearMinimizationOracle` (see `lmo_wrapper.jl`). + +- `traverse_strategy` - encodes how to choose the next node for evaluation. + By default, the node with the best lower bound is picked. + +- `branching_strategy` - by default we branch on the entry which is the farthest + away from being an integer. + +- `variant` - variant of FrankWolfe to be used to solve the node problem. + Options: + - `FW` Vanilla FrankWolfe + - `AFW` way FrankWolfe + - `BPCG` Blended Pairwise Conditional Gradient + +- `line_search` - specifies the Line Search method used in the FrankWolfe variant. + Default is the Adaptive Line Search. For other types, check the FrankWolfe.jl package. + +- `active_set` - can be used to specify a starting point, e.g., if the feasible region is not completely + contained in the domain of the objective. By default, the direction (1,..,n) where n is + the size of the problem is used to find a start vertex. Beware that the active set may + only contain actual vertices of the feasible region. + +- `lazy` - specifies whether the lazification should be used. Per default true. + Beware that it has no effect with Vanilla Frank-Wolfe. + +- `lazy_tolerance` - decides how much progress is deemed enough to not have to call the LMO. + +- `fw_epsilon` - the tolerance for FrankWolfe in the root node. + +- `verbose` - if true, a log and solution statistics are printed. + +- `dual_gap` - if this absolute dual gap is reached, the algorithm stops. + +- `rel_dual_gap` - if this relative dual gap is reached, the algorithm stops. + +- `time_limit` - algorithm will stop if the time limit is reached. Depending on the problem + it is possible that no feasible solution has been found yet. + +- `print_iter` - encodes after how many processed nodes the current node and solution status + is printed. Will always print if a new integral solution has been found. + +- `dual_gap_decay_factor` - the FrankWolfe tolerance at a given level `i` in the tree is given by + `fw_epsilon * dual_gap_decay_factor^i` until we reach the `min_node_fw_epsilon`. + +- `max_fw_iter` - maximum number of iterations in a FrankWolfe run. + +- `min_number_lower` - If not Inf, evaluation of a node is stopped if at least `min_number_lower` nodes have a better + lower bound. + +- `min_node_fw_epsilon` - smallest fw epsilon possible, see `dual_gap_decay_factor`. + +- `use_postsolve` - Runs the specified Frank-Wolfe variant on the problem with the integral variables fixed to the solution, + i.e., it only optimizes over the continuous variables. This might improve the solution if one has many continuous variables. + +- `min_fw_iterations` - the minimum number of FrankWolfe iterations in the node evaluation. + +- `max_iteration_post` - maximum number of iterations in a FrankWolfe run during postsolve + +- `dual_tightening` - whether to use dual tightening techniques (make sure your function is convex!) + +- `global_dual_tightening` - dual tightening maintained globally valid (when new solutions are found) + +- `bnb_callback` - an optional callback called at every node of the tree, for example for heuristics + +- `strong_convexity` - strong convexity of the function, used for tighter dual bound at every node + +- `domain_oracle` - For a point `x`: returns true if `x` is in the domain of `f`, else false. Per default is true. + In case of the non-trivial domain oracle, the starting point has to be feasible for `f`. Also, depending + on the Line Search method, you might have to provide the domain oracle to it, too. + +- `start_solution` - initial solution to start with an incumbent + +- `fw_verbose` - if true, FrankWolfe logs are printed + +- `use_shadow_set` - The shadow set is the set of discarded vertices which is inherited by the children nodes. + It is used to avoid recomputing of vertices in case the LMO is expensive. In case of a cheap LMO, + performance might improve by disabling this option. + +- `custom_heuristics` - List of custom heuristics from the user. + +- `prob_rounding` - The probability for calling the rounding heuristics. Since the feasibility has to be checked, it might + be expensive to do this for every node. + ``` """ + + + function solve( f, grad!, From eda9856a9978416d7a1588ddd59382d4152c2413 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:14:18 +0200 Subject: [PATCH 44/49] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 4db0c1a4d..16b7492b4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Saurabh Srivastava + Copyright (c) 2021 Zuse Institute Berlin Permission is hereby granted, free of charge, to any person obtaining a copy From 67e5e8454309e462532d5aeddf11137ce80c64b4 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:14:32 +0200 Subject: [PATCH 45/49] Update LICENSE --- LICENSE | 1 - 1 file changed, 1 deletion(-) diff --git a/LICENSE b/LICENSE index 16b7492b4..11ef1751e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,5 @@ MIT License - Copyright (c) 2021 Zuse Institute Berlin Permission is hereby granted, free of charge, to any person obtaining a copy From 1db1cb8b65e1a5338fde1db3583d51d7db7904c7 Mon Sep 17 00:00:00 2001 From: hbaruas <120647770+saurabhintoml@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:01:41 +0200 Subject: [PATCH 46/49] Update make.jl --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 800915f79..a6b1d87d9 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,7 +21,7 @@ copy_readme_to_index() makedocs( sitename = "Boscia.jl", modules = [Boscia], - format = Documenter.HTML(repolink = "https://github.com/saurabhintoml/Boscia.jl.git"), + format = Documenter.HTML(repolink = "https://github.com/ZIB-IOL/Boscia.jl"), pages = [ "Home" => "index.md", "How does it work?" => "basics.md", From a93dd8615f4a2fc56aa001aa4fe11039f8ce0ea4 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 10 Jul 2024 13:11:29 +0200 Subject: [PATCH 47/49] changes --- .github/workflows/CI.yml | 79 -------------------------------- .github/workflows/ci.yml | 45 ------------------ Manifest.toml | 7 --- README.md | 2 - docs/.github/workflows/ci.yml | 81 +++++++++++++++++++++++++++++++++ docs/src/basics.md | 12 +++-- examples/lasso.jl | 5 +- examples/low_dim_in_high_dim.jl | 4 +- examples/mps-example.jl | 4 +- 9 files changed, 95 insertions(+), 144 deletions(-) delete mode 100644 .github/workflows/CI.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 Manifest.toml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 1862fcd78..000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: CI -on: - push: - branches: - - master - tags: ['*'] - pull_request: - workflow_dispatch: -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} -jobs: - test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} - runs-on: ${{ matrix.os }} - timeout-minutes: 60 - permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created - actions: write - contents: read - strategy: - fail-fast: false - matrix: - version: - - '1.1' - - '1.10' - - '1.6' - - 'nightly' - os: - - ubuntu-latest - arch: - - x64 - steps: - - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v4 - with: - files: lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: false - docs: - name: Documentation - runs-on: ubuntu-latest - permissions: - actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created - contents: write - statuses: write - steps: - - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: '1' - - uses: julia-actions/cache@v1 - - name: Configure doc environment - shell: julia --project=docs --color=yes {0} - run: | - using Pkg - Pkg.develop(PackageSpec(path=pwd())) - Pkg.instantiate() - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-docdeploy@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} - - name: Run doctests - shell: julia --project=docs --color=yes {0} - run: | - using Documenter: DocMeta, doctest - using Boscia - DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) - doctest(Boscia) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 2e16d7e33..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: CI -on: - push: - branches: [master] - pull_request: - types: [opened, synchronize, reopened] -jobs: - test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - version: '1' - os: ubuntu-latest - arch: x64 - - version: '1.6' - os: ubuntu-latest - arch: x64 - - version: '1' - os: macOS-latest - arch: x64 - steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: actions/cache@v1 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ env.cache-name }}- - ${{ runner.os }}-test- - ${{ runner.os }}- - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 - with: - file: lcov.info diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index c3e1543b7..000000000 --- a/Manifest.toml +++ /dev/null @@ -1,7 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.10.3" -manifest_format = "2.0" -project_hash = "0815649208799fa12993731994e4db6bcf5e6067" - -[deps] diff --git a/README.md b/README.md index 160b1a72e..fab4701b2 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,6 @@ Pkg.add("Boscia") -Suggested change -If you don't have SCIP , you can on go this link and add SCIP as instructed ['SCIP'](https://github.com/scipopt/SCIP.jl) If you want to use SCIP within Boscia and your OS is windows, you will have download SCIP separately, see SCIP.jl. diff --git a/docs/.github/workflows/ci.yml b/docs/.github/workflows/ci.yml index 2e16d7e33..74d6093fb 100644 --- a/docs/.github/workflows/ci.yml +++ b/docs/.github/workflows/ci.yml @@ -43,3 +43,84 @@ jobs: - uses: codecov/codecov-action@v1 with: file: lcov.info + + name: CI +on: + push: + branches: + - master + tags: ['*'] + pull_request: + workflow_dispatch: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created + actions: write + contents: read + strategy: + fail-fast: false + matrix: + version: + - '1.1' + - '1.10' + - '1.6' + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v4 + with: + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + docs: + name: Documentation + runs-on: ubuntu-latest + permissions: + actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created + contents: write + statuses: write + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - uses: julia-actions/cache@v1 + - name: Configure doc environment + shell: julia --project=docs --color=yes {0} + run: | + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate() + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-docdeploy@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + - name: Run doctests + shell: julia --project=docs --color=yes {0} + run: | + using Documenter: DocMeta, doctest + using Boscia + DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) + doctest(Boscia) + diff --git a/docs/src/basics.md b/docs/src/basics.md index 025dd2841..eea6854b0 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -14,11 +14,6 @@ $$ $$ -## BPCG - - -The classic FW algorithm's reliance on an LMO is inefficient for polytopes due to expensive calls. The Blended Pairwise Conditional Gradient (BPCG) algorithm improves efficiency by combining FW steps with pairwise steps that avoid LMO calls. BPCG maintains a smaller active set of vertices, enhancing performance. We use a modified lazified BPCG from FrankWolfe.jl. - # Branch and Bound Techniques In this section, we present the techniques derived from Frank Wolfe that can be used in our @@ -46,5 +41,12 @@ In subproblems where variables are at bounds, our approach utilizes convexity an +## BPCG (used as default) + +The classic FW algorithm's reliance on an LMO is inefficient for polytopes due to expensive calls. The Blended Pairwise Conditional Gradient (BPCG) algorithm improves efficiency by combining FW steps with pairwise steps that avoid LMO calls. BPCG maintains a smaller active set of vertices, enhancing performance. We use a modified lazified BPCG from FrankWolfe.jl. + + + + diff --git a/examples/lasso.jl b/examples/lasso.jl index d24d85677..caa3ed996 100644 --- a/examples/lasso.jl +++ b/examples/lasso.jl @@ -5,8 +5,9 @@ # - z_i ∈ {0,1} for i = 1, ..., p # The objective function f(x) represents the squared Euclidean distance between y and A * β, with additional penalty terms. # The optimization problem is solved using the Boscia and FrankWolfe packages with the SCIP optimizer. - # The final solution x and the result status are obtained using `Boscia.solve`.using Statistics - + # The final solution x and the result status are obtained using `Boscia.solve`. + +using Statistics using Distributions using Boscia using FrankWolfe diff --git a/examples/low_dim_in_high_dim.jl b/examples/low_dim_in_high_dim.jl index b225942dc..7b824031f 100644 --- a/examples/low_dim_in_high_dim.jl +++ b/examples/low_dim_in_high_dim.jl @@ -2,9 +2,9 @@ # sets up two different optimization approaches: one using the SCIP optimizer and the other using a simple bounded linear minimization # oracle (BLMO) tailored for a cube. The function `f(x)` represents a quadratic function involving a matrix `W` and a reference point. # The optimization problem is solved using Boscia and FrankWolfe packages, and the final solution and the result status are obtained -# using `Boscia.solve`.using Boscia - +# using `Boscia.solve`. +using Boscia using FrankWolfe using Test using Random diff --git a/examples/mps-example.jl b/examples/mps-example.jl index 5c126ba89..0add6a898 100644 --- a/examples/mps-example.jl +++ b/examples/mps-example.jl @@ -2,9 +2,9 @@ # as the objective function. The number of variables is 429, with no integer variables. The code reads the MPS file, sets up # the SCIP optimizer, and uses the FrankWolfe method for optimization. It pushes the optimum towards the interior by utilizing # a trick and then defines the objective function and its gradient. Finally, it solves the optimization problem using Boscia -# and ensures the obtained solution is optimal.using Boscia - +# and ensures the obtained solution is optimal. +using Boscia using FrankWolfe using Test using Random From 075f2fb88b8e66ee07579aeccbf8281a404a7c00 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 10 Jul 2024 20:34:17 +0200 Subject: [PATCH 48/49] fresh changes --- {docs/.github => .github}/workflows/ci.yml | 0 docs/.github/dependabot.yml | 7 ------- docs/.github/workflows/CompatHelper.yml | 16 ---------------- docs/.github/workflows/TagBot.yml | 15 --------------- 4 files changed, 38 deletions(-) rename {docs/.github => .github}/workflows/ci.yml (100%) delete mode 100644 docs/.github/dependabot.yml delete mode 100644 docs/.github/workflows/CompatHelper.yml delete mode 100644 docs/.github/workflows/TagBot.yml diff --git a/docs/.github/workflows/ci.yml b/.github/workflows/ci.yml similarity index 100% rename from docs/.github/workflows/ci.yml rename to .github/workflows/ci.yml diff --git a/docs/.github/dependabot.yml b/docs/.github/dependabot.yml deleted file mode 100644 index 700707ced..000000000 --- a/docs/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/docs/.github/workflows/CompatHelper.yml b/docs/.github/workflows/CompatHelper.yml deleted file mode 100644 index cba9134c6..000000000 --- a/docs/.github/workflows/CompatHelper.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: CompatHelper -on: - schedule: - - cron: 0 0 * * * - workflow_dispatch: -jobs: - CompatHelper: - runs-on: ubuntu-latest - steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} - run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/docs/.github/workflows/TagBot.yml b/docs/.github/workflows/TagBot.yml deleted file mode 100644 index f49313b66..000000000 --- a/docs/.github/workflows/TagBot.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: TagBot -on: - issue_comment: - types: - - created - workflow_dispatch: -jobs: - TagBot: - if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' - runs-on: ubuntu-latest - steps: - - uses: JuliaRegistries/TagBot@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} From c34c200f5b1c99086a89d6f5fa7c2dd19e67dcc6 Mon Sep 17 00:00:00 2001 From: saurabhintoml Date: Wed, 10 Jul 2024 21:20:39 +0200 Subject: [PATCH 49/49] again ci --- .github/workflows/ci.yml | 81 ----------------------------- .github/workflows/documentation.yml | 31 +++++++++++ 2 files changed, 31 insertions(+), 81 deletions(-) create mode 100644 .github/workflows/documentation.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74d6093fb..2e16d7e33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,84 +43,3 @@ jobs: - uses: codecov/codecov-action@v1 with: file: lcov.info - - name: CI -on: - push: - branches: - - master - tags: ['*'] - pull_request: - workflow_dispatch: -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} -jobs: - test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} - runs-on: ${{ matrix.os }} - timeout-minutes: 60 - permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created - actions: write - contents: read - strategy: - fail-fast: false - matrix: - version: - - '1.1' - - '1.10' - - '1.6' - - 'nightly' - os: - - ubuntu-latest - arch: - - x64 - steps: - - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v4 - with: - files: lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: false - docs: - name: Documentation - runs-on: ubuntu-latest - permissions: - actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created - contents: write - statuses: write - steps: - - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: '1' - - uses: julia-actions/cache@v1 - - name: Configure doc environment - shell: julia --project=docs --color=yes {0} - run: | - using Pkg - Pkg.develop(PackageSpec(path=pwd())) - Pkg.instantiate() - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-docdeploy@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} - - name: Run doctests - shell: julia --project=docs --color=yes {0} - run: | - using Documenter: DocMeta, doctest - using Boscia - DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) - doctest(Boscia) - diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 000000000..e09b82d3f --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,31 @@ +docs: + name: Documentation + runs-on: ubuntu-latest + permissions: + actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created + contents: write + statuses: write + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - uses: julia-actions/cache@v1 + - name: Configure doc environment + shell: julia --project=docs --color=yes {0} + run: | + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate() + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-docdeploy@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + - name: Run doctests + shell: julia --project=docs --color=yes {0} + run: | + using Documenter: DocMeta, doctest + using Boscia + DocMeta.setdocmeta!(Boscia, :DocTestSetup, :(using Boscia); recursive=true) + doctest(Boscia)