From a262f907060dc588720406be46b7b409b0e13c95 Mon Sep 17 00:00:00 2001 From: Abel Soares Siqueira Date: Fri, 31 May 2024 17:24:10 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Apply=20COPIERTemplate?= =?UTF-8?q?=20v0.5.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applied the template and made some manual changes, such as changing the docs names, fixing the README and removing some older workflows. ✅ Closes: #143 --- .JuliaFormatter.toml | 21 ++- .all-contributorsrc | 5 + .cirrus.yml | 32 ---- .copier-answers.yml | 11 ++ .editorconfig | 10 ++ .github/ISSUE_TEMPLATE/10-bug-report.yml | 61 +++++++ .github/ISSUE_TEMPLATE/20-feature-request.yml | 37 ++++ .github/ISSUE_TEMPLATE/30-usage.yml | 24 +++ .github/ISSUE_TEMPLATE/99-general.yml | 22 +++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/PULL_REQUEST_TEMPLATE.md | 28 +++ .github/dependabot.yml | 7 + .github/workflows/CompatHelper.yml | 4 +- .github/workflows/Docs.yml | 53 ++++++ .github/workflows/Documentation.yml | 23 --- .github/workflows/Lint.yml | 55 ++++++ .github/workflows/PreCommitUpdate.yml | 35 ++++ .github/workflows/ReusableTest.yml | 52 ++++++ .github/workflows/TagBot.yml | 52 ++++-- .github/workflows/Test.yml | 48 ++++++ .github/workflows/TestOnPRs.yml | 29 ++++ .github/workflows/ci.yml | 54 ------ .github/workflows/format_pr.yml | 32 ---- .gitignore | 6 + .markdownlint.json | 13 ++ .pre-commit-config.yaml | 49 ++++++ .yamllint.yml | 2 + CODE_OF_CONDUCT.md | 133 ++++++++++++++ README.md | 65 +++---- docs/Project.toml | 1 + docs/make.jl | 53 ++++-- docs/src/{tutorial.md => 10-tutorial.md} | 7 +- docs/src/{nls.md => 20-nls.md} | 7 +- docs/src/{qp.md => 30-qp.md} | 2 +- docs/src/{resolve.md => 40-resolve.md} | 16 +- docs/src/{benchmark.md => 50-benchmark.md} | 6 +- docs/src/{speed-up.md => 60-speed-up.md} | 23 +-- docs/src/90-contributing.md | 25 +++ docs/src/90-developer.md | 163 ++++++++++++++++++ docs/src/90-reference.md | 17 ++ docs/src/assets/style.css | 4 +- docs/src/index.md | 117 ++----------- docs/src/reference.md | 17 -- lychee.toml | 11 ++ src/multi-start.jl | 66 +++++-- src/optimizers.jl | 2 +- src/solve-model.jl | 6 +- src/solvers/ripqp_solve.jl | 6 +- test/multi-start-test.jl | 15 +- test/qp_tests.jl | 24 +-- test/runtests.jl | 38 ++-- 51 files changed, 1177 insertions(+), 417 deletions(-) create mode 100644 .all-contributorsrc delete mode 100644 .cirrus.yml create mode 100644 .copier-answers.yml create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/10-bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/20-feature-request.yml create mode 100644 .github/ISSUE_TEMPLATE/30-usage.yml create mode 100644 .github/ISSUE_TEMPLATE/99-general.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/Docs.yml delete mode 100644 .github/workflows/Documentation.yml create mode 100644 .github/workflows/Lint.yml create mode 100644 .github/workflows/PreCommitUpdate.yml create mode 100644 .github/workflows/ReusableTest.yml create mode 100644 .github/workflows/Test.yml create mode 100644 .github/workflows/TestOnPRs.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/format_pr.yml create mode 100644 .markdownlint.json create mode 100644 .pre-commit-config.yaml create mode 100644 .yamllint.yml create mode 100644 CODE_OF_CONDUCT.md rename docs/src/{tutorial.md => 10-tutorial.md} (96%) rename docs/src/{nls.md => 20-nls.md} (94%) rename docs/src/{qp.md => 30-qp.md} (99%) rename docs/src/{resolve.md => 40-resolve.md} (98%) rename docs/src/{benchmark.md => 50-benchmark.md} (96%) rename docs/src/{speed-up.md => 60-speed-up.md} (91%) create mode 100644 docs/src/90-contributing.md create mode 100644 docs/src/90-developer.md create mode 100644 docs/src/90-reference.md delete mode 100644 docs/src/reference.md create mode 100644 lychee.toml diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index 15fa654..15a73f2 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -1,7 +1,18 @@ -margin = 100 +align_assignment = true +align_matrix = true +align_pair_arrow = true +align_struct_field = true +always_for_in = true +annotate_untyped_fields_with_any = false +conditional_to_if = true +for_in_replacement = "in" +format_docstrings = false +format_markdown = false +import_to_using = true indent = 2 -whitespace_typedefs = true -whitespace_ops_in_indices = true +margin = 100 +normalize_line_endings = "unix" remove_extra_newlines = true -annotate_untyped_fields_with_any = false -normalize_line_endings = "unix" \ No newline at end of file +separate_kwargs_with_semicolon = true +whitespace_ops_in_indices = true +whitespace_typedefs = true diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 0000000..fd2f151 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,5 @@ +{ + "projectName": "JSOSuite", + "projectOwner": "JuliaSmoothOptimizers", + "files": ["README.md", "docs/src/index.md"] +} diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index e8a90ba..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,32 +0,0 @@ -task: - matrix: - - name: FreeBSD - freebsd_instance: - image_family: freebsd-13-3 - env: - matrix: - - JULIA_VERSION: 1.6 - - JULIA_VERSION: 1 - - name: MacOS M1 - macos_instance: - image: ghcr.io/cirruslabs/macos-ventura-base:latest - env: - - JULIA_VERSION: 1 - install_script: | - URL="https://raw.githubusercontent.com/ararslan/CirrusCI.jl/master/bin/install.sh" - set -x - if [ "$(uname -s)" = "Linux" ] && command -v apt; then - apt update - apt install -y curl - fi - if command -v curl; then - sh -c "$(curl ${URL})" - elif command -v wget; then - sh -c "$(wget ${URL} -q -O-)" - elif command -v fetch; then - sh -c "$(fetch ${URL} -o -)" - fi - build_script: - - cirrusjl build - test_script: - - cirrusjl test diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..aef8533 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,11 @@ +# Changes here will be overwritten by Copier +AnswerStrategy: recommended +AuthorEmail: tangi.migot@gmail.com +AuthorName: Tangi Migot +JuliaMinVersion: '1.6' +License: MPL-2.0 +PackageName: JSOSuite +PackageOwner: JuliaSmoothOptimizers +PackageUUID: ed6ae0be-a024-11e9-2788-05dbf8cd15d9 +_commit: v0.5.3 +_src_path: https://github.com/abelsiqueira/COPIERTemplate.jl diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9f5c55f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# https://editorconfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true diff --git a/.github/ISSUE_TEMPLATE/10-bug-report.yml b/.github/ISSUE_TEMPLATE/10-bug-report.yml new file mode 100644 index 0000000..033a011 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/10-bug-report.yml @@ -0,0 +1,61 @@ +name: Bug Report +description: File a bug report related to running the package +title: "[Bug] " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please, before submitting, make sure that: + + - There is not an [existing issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues) with the same question + - You have read the [contributing guide](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/docs/src/90-contributing.md/) + - You are following the [code of conduct](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CODE_OF_CONDUCT.md) + + The form below should help you in filling out this issue. + - type: textarea + id: description + attributes: + label: Description + description: Describe the bug + validations: + required: true + - type: input + id: pkg-version + attributes: + label: Package Version + description: What version of the package are you running? + validations: + required: true + - type: input + id: version + attributes: + label: Julia Version + description: What version of Julia are you running? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction steps + description: What steps led to the bug happening? + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: dropdown + id: os + attributes: + label: "Operating System" + description: What is the impacted environment? + multiple: true + options: + - Windows + - Linux + - Mac diff --git a/.github/ISSUE_TEMPLATE/20-feature-request.yml b/.github/ISSUE_TEMPLATE/20-feature-request.yml new file mode 100644 index 0000000..1978d06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/20-feature-request.yml @@ -0,0 +1,37 @@ +name: "Feature Request" +description: Suggest a new feature for the package +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please, before submitting, make sure that: + + - There is not an [existing issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues) with the same question + - You have read the [contributing guide](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/docs/src/90-contributing.md/) + - You are following the [code of conduct](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CODE_OF_CONDUCT.md) + + The form below should help you in filling out this issue. + - type: textarea + id: description + attributes: + label: Description + description: Describe the requested feature + validations: + required: true + - type: textarea + id: motivation + attributes: + label: Motivation + description: Explain why this feature is relevant + - type: textarea + id: target + attributes: + label: Target audience + description: Tell more about the users of this feature, or where it could be useful + - type: textarea + id: can-help + attributes: + label: Can you help + description: Can you help developing this feature? diff --git a/.github/ISSUE_TEMPLATE/30-usage.yml b/.github/ISSUE_TEMPLATE/30-usage.yml new file mode 100644 index 0000000..f641d16 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/30-usage.yml @@ -0,0 +1,24 @@ +name: "Usage question" +description: Questions related to the usage +labels: ["documentation"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please, before submitting, make sure that: + + - You have checked the [documentation](https://JuliaSmoothOptimizers.github.io/JSOSuite.jl) and haven't found enough information + - There is not an [existing issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues) with the same question + - You have read the [contributing guide](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/docs/src/90-contributing.md/) + - You are following the [code of conduct](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CODE_OF_CONDUCT.md) + + The form below should help you in filling out this issue. + - type: textarea + id: description + attributes: + label: Description + description: Write your question + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/99-general.yml b/.github/ISSUE_TEMPLATE/99-general.yml new file mode 100644 index 0000000..d939ecc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/99-general.yml @@ -0,0 +1,22 @@ +name: "General issue" +description: In case none of the others templates apply +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please, before submitting, make sure that: + + - There is not an [existing issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues) with the same question + - You have read the [contributing guide](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/docs/src/90-contributing.md/) + - You are following the [code of conduct](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CODE_OF_CONDUCT.md) + + The form below should help you in filling out this issue. + - type: textarea + id: description + attributes: + label: Description + description: Describe the issue + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..5f421db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discussions + url: https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/discussions + about: Create and follow discussions here diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9efeda2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ + + +## Related issues + + + + +Closes # + + + + +## Checklist + + + +- [ ] I am following the [contributing guidelines](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/docs/src/90-contributing.md) +- [ ] Tests are passing +- [ ] Lint workflow is passing +- [ ] Docs were updated and workflow is passing + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..700707c --- /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/CompatHelper.yml b/.github/workflows/CompatHelper.yml index 34613bb..210e56f 100644 --- a/.github/workflows/CompatHelper.yml +++ b/.github/workflows/CompatHelper.yml @@ -3,7 +3,7 @@ name: CompatHelper on: schedule: - - cron: 0 0 * * * + - cron: 0 0 * * * # Every day at 00:00 UTC workflow_dispatch: permissions: @@ -24,6 +24,8 @@ jobs: version: "1" arch: ${{ runner.arch }} if: steps.julia_in_path.outcome != 'success' + - name: Use Julia cache + uses: julia-actions/cache@v2 - name: "Add the General registry via Git" run: | import Pkg diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml new file mode 100644 index 0000000..846240c --- /dev/null +++ b/.github/workflows/Docs.yml @@ -0,0 +1,53 @@ +name: Docs + +on: + push: + branches: + - main + paths: + - "docs/**" + - "src/**" + - "*.toml" + tags: ["*"] + pull_request: + branches: + - main + paths: + - "docs/**" + - "src/**" + - "*.toml" + types: [opened, synchronize, reopened] + +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: + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: "1" + - name: Use Julia cache + uses: julia-actions/cache@v2 + - run: | + julia --project=docs -e ' + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate()' + - run: | + julia --project=docs -e ' + using Documenter: DocMeta, doctest + using JSOSuite + DocMeta.setdocmeta!(JSOSuite, :DocTestSetup, :(using JSOSuite); recursive=true) + doctest(JSOSuite)' + - run: julia --project=docs docs/make.jl + env: + JULIA_PKG_SERVER: "" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml deleted file mode 100644 index be0b865..0000000 --- a/.github/workflows/Documentation.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Documentation -on: - push: - branches: - - main - tags: '*' - pull_request: - types: [opened, synchronize, reopened] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@latest - with: - version: '1' - - name: Install dependencies - run: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - - name: Build and deploy - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} - run: julia --project=docs --color=yes docs/make.jl diff --git a/.github/workflows/Lint.yml b/.github/workflows/Lint.yml new file mode 100644 index 0000000..d7ba1d1 --- /dev/null +++ b/.github/workflows/Lint.yml @@ -0,0 +1,55 @@ +name: Lint + +on: + push: + branches: + - main + tags: ["*"] + pull_request: + +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: + lint: + name: Linting + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v4 + - name: Setup Julia + uses: julia-actions/setup-julia@v2 + with: + version: "1" + - name: Use Julia cache + uses: julia-actions/cache@v2 + - name: Install JuliaFormatter.jl + run: julia -e 'using Pkg; pkg"add JuliaFormatter"' + - name: Setup Python + uses: actions/setup-python@v5 + with: + cache: "pip" + - name: Cache pre-commit + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: ${{ runner.os }}-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }} + - name: Install pre-commit + run: pip install pre-commit + - name: Run pre-commit + run: SKIP=no-commit-to-branch pre-commit run -a + + link-checker: + name: Link checker + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@v1 + with: + fail: true diff --git a/.github/workflows/PreCommitUpdate.yml b/.github/workflows/PreCommitUpdate.yml new file mode 100644 index 0000000..c1bcef1 --- /dev/null +++ b/.github/workflows/PreCommitUpdate.yml @@ -0,0 +1,35 @@ +name: pre-commit Update + +on: + schedule: + - cron: "0 7 1/7 * *" # At 7:00 every 7 days + workflow_dispatch: + +jobs: + pre-commit-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + cache: pip + - name: Install pre-commit + run: pip install pre-commit + - name: Run pre-commit's autoupdate + run: | + # ignore exit code + pre-commit autoupdate || true + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + commit-message: "chore: :robot: pre-commit update" + title: "[AUTO] pre-commit update" + branch: auto-pre-commit-update + delete-branch: true + labels: chore + - name: Check outputs + run: | + echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" + echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" diff --git a/.github/workflows/ReusableTest.yml b/.github/workflows/ReusableTest.yml new file mode 100644 index 0000000..12431f2 --- /dev/null +++ b/.github/workflows/ReusableTest.yml @@ -0,0 +1,52 @@ +name: Reusable test + +on: + workflow_call: + inputs: + version: + required: false + type: string + default: "1" + os: + required: false + type: string + default: ubuntu-latest + arch: + required: false + type: string + default: x64 + allow_failure: + required: false + type: boolean + default: false + run_codecov: + required: false + type: boolean + default: false + secrets: + codecov_token: + required: true + +jobs: + test: + name: Julia ${{ inputs.version }} - ${{ inputs.os }} - ${{ inputs.arch }} - ${{ github.event_name }} + runs-on: ${{ inputs.os }} + continue-on-error: ${{ inputs.allow_failure }} + + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ inputs.version }} + arch: ${{ inputs.arch }} + - name: Use Julia cache + uses: julia-actions/cache@v2 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + if: ${{ inputs.run_codecov }} + - uses: codecov/codecov-action@v4 + if: ${{ inputs.run_codecov }} + with: + file: lcov.info + token: ${{ secrets.codecov_token }} diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 083baf9..dab9b0b 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,15 +1,37 @@ -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 }} +name: TagBot + +on: + issue_comment: + types: + - created + workflow_dispatch: + inputs: + lookback: + type: number + 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 }} + # Edit the following line to reflect the actual name of the GitHub Secret containing your private key + ssh: ${{ secrets.DOCUMENTER_KEY }} + # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml new file mode 100644 index 0000000..cc8feed --- /dev/null +++ b/.github/workflows/Test.yml @@ -0,0 +1,48 @@ +name: Test + +on: + push: + branches: + - main + tags: ["*"] + + workflow_dispatch: + +jobs: + test: + uses: ./.github/workflows/ReusableTest.yml + with: + os: ${{ matrix.os }} + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + allow_failure: ${{ matrix.allow_failure }} + run_codecov: ${{ matrix.version == '1' && matrix.os == 'ubuntu-latest' }} + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} + strategy: + fail-fast: false + matrix: + version: + - "1.6" + - "1" + os: + - ubuntu-latest + - macOS-latest + - windows-latest + arch: + - x64 + allow_failure: [false] + + include: + - version: "nightly" + os: ubuntu-latest + arch: x64 + allow_failure: true + - version: "nightly" + os: macOS-latest + arch: x64 + allow_failure: true + - version: "nightly" + os: windows-latest + arch: x64 + allow_failure: true diff --git a/.github/workflows/TestOnPRs.yml b/.github/workflows/TestOnPRs.yml new file mode 100644 index 0000000..19a1f5a --- /dev/null +++ b/.github/workflows/TestOnPRs.yml @@ -0,0 +1,29 @@ +name: Test on PRs + +on: + pull_request: + branches: + - main + paths: + - "src/**" + - "test/**" + - "*.toml" + types: [opened, synchronize, reopened] + +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: + uses: ./.github/workflows/ReusableTest.yml + with: + os: ubuntu-latest + version: "1" + arch: x64 + allow_failure: false + run_codecov: true + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 409e0d1..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: CI -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened] -jobs: - test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} - runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.allow_failure }} - strategy: - fail-fast: false - matrix: - version: ['1.6', '1'] - os: [ubuntu-latest, macOS-latest, windows-latest] - arch: [x64] - allow_failure: [false] - include: - - version: 'nightly' - os: ubuntu-latest - arch: x64 - allow_failure: true - - version: 'nightly' - os: macOS-latest - arch: x64 - allow_failure: true - - version: 'nightly' - os: windows-latest - arch: x64 - allow_failure: true - 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/.github/workflows/format_pr.yml b/.github/workflows/format_pr.yml deleted file mode 100644 index c8f5308..0000000 --- a/.github/workflows/format_pr.yml +++ /dev/null @@ -1,32 +0,0 @@ -# https://github.com/julia-actions/julia-format/blob/master/workflows/format_pr.yml -name: format-pr -on: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install JuliaFormatter and format - run: | - julia -e 'import Pkg; Pkg.add("JuliaFormatter")' - julia -e 'using JuliaFormatter; format(".")' - # https://github.com/marketplace/actions/create-pull-request - # https://github.com/peter-evans/create-pull-request#reference-example - - name: Create Pull Request - id: cpr - uses: peter-evans/create-pull-request@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ":robot: Format .jl files" - title: '[AUTO] JuliaFormatter.jl run' - branch: auto-juliaformatter-pr - delete-branch: true - labels: formatting, automated pr, no changelog - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" \ No newline at end of file diff --git a/.gitignore b/.gitignore index ba39cc5..c3dfb74 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ +*.jl.*.cov +*.jl.cov +*.jl.mem Manifest.toml +docs/build/ +*.rej +node_modules diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..8bf958a --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,13 @@ +{ + "MD007": { + "indent": 2, + "start_indented": false + }, + "MD013": { + "line_length": 1000, + "tables": false + }, + "MD033": false, + "MD041": false, + "default": true +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a36f806 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,49 @@ +repos: + - repo: local + hooks: + # Prevent committing .rej files + - id: forbidden-files + name: forbidden files + entry: found Copier update rejection files; review them and remove them + language: fail + files: "\\.rej$" + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-json + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: file-contents-sorter + files: .JuliaFormatter.toml + args: [--unique] + - id: mixed-line-ending + args: [--fix=lf] + - id: no-commit-to-branch + - id: pretty-format-json + args: [--autofix, --indent=2] + - id: trailing-whitespace + - id: check-merge-conflict + args: [--assume-in-merge] + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.41.0 + hooks: + - id: markdownlint-fix + - repo: https://github.com/citation-file-format/cffconvert + rev: 054bda51dbe278b3e86f27c890e3f3ac877d616c + hooks: + - id: validate-cff + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v4.0.0-alpha.8" # Use the sha or tag you want to point at + hooks: + - id: prettier + types_or: [yaml, json] + exclude: ".copier-answers.yml" + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + - repo: https://github.com/domluna/JuliaFormatter.jl + rev: v1.0.56 + hooks: + - id: julia-formatter diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..5e16e5f --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,2 @@ +rules: + indentation: { spaces: 2 } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..927910c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +`tangi.migot@gmail.com`. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/README.md b/README.md index 32fc3d6..76696d9 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,36 @@ -# JSOSuite.jl - -[![docs-stable][docs-stable-img]][docs-stable-url] [![docs-dev][docs-dev-img]][docs-dev-url] [![build-ci][build-ci-img]][build-ci-url] [![codecov][codecov-img]][codecov-url] [![release][release-img]][release-url] - -[docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg -[docs-stable-url]: https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/stable -[docs-dev-img]: https://img.shields.io/badge/docs-dev-purple.svg -[docs-dev-url]: https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/dev -[build-ci-img]: https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/workflows/CI/badge.svg?branch=main -[build-ci-url]: https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions -[codecov-img]: https://codecov.io/gh/JuliaSmoothOptimizers/JSOSuite.jl/branch/main/graph/badge.svg -[codecov-url]: https://codecov.io/gh/JuliaSmoothOptimizers/JSOSuite.jl -[release-img]: https://img.shields.io/github/v/release/JuliaSmoothOptimizers/JSOSuite.jl.svg?style=flat-square -[release-url]: https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/releases - -One stop solutions for all things optimization. +# JSOSuite + +[![Stable Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/stable) +[![In development documentation](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/dev) +[![Build Status](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/workflows/Test/badge.svg)](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions) +[![Test workflow status](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Test.yml/badge.svg?branch=main)](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Test.yml?query=branch%3Amain) +[![Lint workflow Status](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Lint.yml/badge.svg?branch=main)](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Lint.yml?query=branch%3Amain) +[![Docs workflow Status](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Docs.yml/badge.svg?branch=main)](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/actions/workflows/Docs.yml?query=branch%3Amain) +[![Build Status](https://api.cirrus-ci.com/github/JuliaSmoothOptimizers/JSOSuite.jl.svg)](https://cirrus-ci.com/github/JuliaSmoothOptimizers/JSOSuite.jl) +[![Coverage](https://codecov.io/gh/JuliaSmoothOptimizers/JSOSuite.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/JuliaSmoothOptimizers/JSOSuite.jl) +[![DOI](https://zenodo.org/badge/DOI/FIXME)](https://doi.org/FIXME) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) +[![All Contributors](https://img.shields.io/github/all-contributors/JuliaSmoothOptimizers/JSOSuite.jl?labelColor=5e1ec7&color=c0ffee&style=flat-square))](#contributors) ## How to Cite -If you use JSOSuite.jl in your work, please cite using the format given in [CITATION.cff](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CITATION.cff). - -## Installation - -``` -] add JSOSuite -``` +If you use JSOSuite.jl in your work, please cite using the reference given in [CITATION.cff](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/main/CITATION.cff). -## Examples +## Contributing -```julia -using JSOSuite +If you want to make contributions of any kind, please first that a look into our [contributing guide directly on GitHub](docs/src/90-contributing.md) or the [contributing page on the website](https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/dev/contributing/). -# Rosenbrock -x0 = [-1.2; 1.0] -f = x -> 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2 -stats = minimize(f, x0) +### Contributors -# Unconstrained problem in Float32 -stats = minimize(f, Float32.(x0)) + + + -# Constrained problem -c = x -> [x[1] + x[2] - 1] -stats = minimize(f, x0, c, [0.0], [0.0]) -``` + + -# Bug reports and discussions + -If you think you found a bug, feel free to open an [issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues). -Focused suggestions and requests can also be opened as issues. Before opening a pull request, start an issue or a discussion on the topic, please. +--- -If you want to ask a question not suited for a bug report, feel free to start a discussion [here](https://github.com/JuliaSmoothOptimizers/Organization/discussions). This forum is for general discussion about this repository and the [JuliaSmoothOptimizers](https://github.com/JuliaSmoothOptimizers), so questions about any of our packages are welcome. +This repo was created with the [COPIERTemplate.jl](https://github.com/abelsiqueira/COPIERTemplate.jl) package. diff --git a/docs/Project.toml b/docs/Project.toml index ad403b5..29d9bbc 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -7,6 +7,7 @@ GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" JSOSolvers = "10dff2fc-5484-5881-a0e0-c90441020f8a" JSOSuite = "ed6ae0be-a024-11e9-2788-05dbf8cd15d9" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71" diff --git a/docs/make.jl b/docs/make.jl index f2e3c70..9ae3015 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,28 +1,43 @@ -using Documenter, JSOSuite +using JSOSuite +using Documenter -makedocs( +DocMeta.setdocmeta!(JSOSuite, :DocTestSetup, :(using JSOSuite); recursive = true) + +const page_rename = Dict( + "developer.md" => "Developer docs", + "nls.md" => "Nonlinear Least Squares", + "qp.md" => "Quadratic models with linear constraints", + "resolve.md" => "Re-solve and in-place solve", + "speed-up.md" => "Speed up Solvers Tips", +) # Without the numbers + +function nice_name(file) + file = replace(file, r"^[0-9]*-" => "") + if haskey(page_rename, file) + return page_rename[file] + end + return splitext(file)[1] |> x -> replace(x, "-" => " ") |> titlecase +end + +makedocs(; modules = [JSOSuite], doctest = true, - linkcheck = false, - format = Documenter.HTML( + linkcheck = false, # Rely on Lint.yml/lychee for the links + authors = "Tangi Migot and contributors", + repo = "https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/blob/{commit}{path}#{line}", + sitename = "JSOSuite.jl", + format = Documenter.HTML(; + prettyurls = true, + canonical = "https://JuliaSmoothOptimizers.github.io/JSOSuite.jl", assets = ["assets/style.css"], - prettyurls = get(ENV, "CI", nothing) == "true", ), - sitename = "JSOSuite.jl", pages = [ - "Home" => "index.md", - "Tutorial" => "tutorial.md", - "Nonlinear Least Squares" => "nls.md", - "Quadratic models with linear constraints" => "qp.md", - "Re-solve and in-place solve" => "resolve.md", - "Benchmarking" => "benchmark.md", - "Speed up Solvers Tips" => "speed-up.md", - "Reference" => "reference.md", + "Home" => "index.md" + [ + nice_name(file) => file for + file in readdir(joinpath(@__DIR__, "src")) if file != "index.md" && splitext(file)[2] == ".md" + ] ], ) -deploydocs( - repo = "github.com/JuliaSmoothOptimizers/JSOSuite.jl.git", - push_preview = true, - devbranch = "main", -) +deploydocs(; repo = "github.com/JuliaSmoothOptimizers/JSOSuite.jl", push_preview = true) diff --git a/docs/src/tutorial.md b/docs/src/10-tutorial.md similarity index 96% rename from docs/src/tutorial.md rename to docs/src/10-tutorial.md index 275fc8b..2392368 100644 --- a/docs/src/tutorial.md +++ b/docs/src/10-tutorial.md @@ -18,6 +18,7 @@ All these optimizers rely on the `NLPModel API` from [NLPModels.jl](https://gith ``` The function `minimize` accepts as an argument any model `nlp` subtype of `AbstractNLPModel`. + ```julia output = minimize(nlpmodel::AbstractNLPModel; kwargs...) ``` @@ -42,7 +43,7 @@ We refer to [`JuMP tutorial`](https://jump.dev/JuMP.jl/stable/) for more on mode ### NLPModel with Automatic Differentiation -We refer to [`ADNLPModel`](https://jso.dev/ADNLPModels.jl/dev/reference/#ADNLPModels.ADNLPModel-Union{Tuple{S},%20Tuple{Any,%20S}}%20where%20S) for the description of the different constructors. +We refer to [`ADNLPModel`](https://jso.dev/ADNLPModels.jl/dev/reference/) for the description of the different constructors. #### Unconstrained @@ -61,7 +62,7 @@ nlp = ADNLPModel(f, x0) stats = minimize(nlp) ``` -One of the main advantages of this constructor is the possibility to run computations in different arithmetics. +One of the main advantages of this constructor is the possibility to run computations in different arithmetics. ```@example using JSOSuite @@ -139,6 +140,7 @@ JSOSuite.optimizers ``` Required information can be extracted by simple `DataFrame` manipulations. For instance, the list of optimizers handled by this package + ```@example ex1 JSOSuite.optimizers.name ``` @@ -146,6 +148,7 @@ JSOSuite.optimizers.name ### Select optimizers The function [`JSOSuite.select_optimizers`](@ref) returns a list of compatible optimizers. + ```@example using ADNLPModels, JSOSuite f = x -> 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2 diff --git a/docs/src/nls.md b/docs/src/20-nls.md similarity index 94% rename from docs/src/nls.md rename to docs/src/20-nls.md index 9dc2699..2e492fa 100644 --- a/docs/src/nls.md +++ b/docs/src/20-nls.md @@ -16,19 +16,21 @@ Although the problem can be solved using only ``f``, knowing ``F`` independent ## Model and solve NLS In this tutorial, we consider the following equality-constrained problem + ```math \begin{aligned} \min \quad & f(x):=\tfrac{1}{2}(10 * (x[2] - x[1]^2))^2 + \tfrac{1}{2}(x[1] - 1)^2 \\ & 1 \leq x[1] * x[2] \leq 1, \end{aligned} ``` + where ``1 \leq x[1] x[2] \leq 1`` implies that ``x[1] x[2] = 1``. In the rest of this tutorial, we will see two ways to model this problem exploiting the knowledge of the structure of the problem. ### NLS using automatic differentiation -Using the package [ADNLPModels.jl](https://github.com/JuliaSmoothOptimizers/ADNLPModels.jl]), the problem can be model as an `ADNLSModel` which will use automatic-differentiation to compute the derivatives. +Using the package [ADNLPModels.jl](https://github.com/JuliaSmoothOptimizers/ADNLPModels.jl), the problem can be model as an `ADNLSModel` which will use automatic-differentiation to compute the derivatives. ```@example ex1 using ADNLPModels, JSOSuite @@ -39,6 +41,7 @@ c = x -> [x[1] * x[2]] l = [1.] nls = ADNLSModel(F, x0, nres, c, l, l, name="AD-Rosenbrock") ``` + Note that the length of the residual function is given explictly to avoid any superfluous evaluation of this (potentially very large) function. ```@example ex1 @@ -81,7 +84,7 @@ stats = minimize(nls) ## Find a feasible point of an optimization problem or solve a nonlinear system -We show here how to find the feasible point of a given model. +We show here how to find the feasible point of a given model. ```math \begin{aligned} diff --git a/docs/src/qp.md b/docs/src/30-qp.md similarity index 99% rename from docs/src/qp.md rename to docs/src/30-qp.md index 58b61b0..c19e5c0 100644 --- a/docs/src/qp.md +++ b/docs/src/30-qp.md @@ -10,7 +10,7 @@ The quadratic model with linear constraints is another specific case where the o \end{aligned} ``` -This problem is convex whenever the matrix `Q` is positive semi-definite. A key aspect here is the modeling of the matrices `Q` and `A`. +This problem is convex whenever the matrix `Q` is positive semi-definite. A key aspect here is the modeling of the matrices `Q` and `A`. The main data structure available in Julia are: `LinearAlgebra.Matrix`, `SparseArrays.sparse`, `SparseMatricesCOO.sparse`, `LinearOperators.LinearOperator`. In JuliaSmoothOptimizers, the package [`QuadraticModels.jl`](https://github.com/JuliaSmoothOptimizers/QuadraticModels.jl) can be used to access the `NLPModel API` for such instance. diff --git a/docs/src/resolve.md b/docs/src/40-resolve.md similarity index 98% rename from docs/src/resolve.md rename to docs/src/40-resolve.md index 76ebd6c..4342675 100644 --- a/docs/src/resolve.md +++ b/docs/src/40-resolve.md @@ -3,22 +3,27 @@ It is very convenient to pre-allocate the memory used during the optimization of a given problem either for improved memory management or re-solving the same or a similar problem. Let us consider the following 2-dimensional unconstrained problem + ```math \begin{aligned} -\min_x \quad & f(x):= x_2^2 \exp(x_1^2) +\min_x \quad & f(x):= x_2^2 \exp(x_1^2) \end{aligned} ``` + Using `JSOSuite`’s `minimize` function, the problem can be solved as follows + ```@example ex1 using JSOSuite f(x) = x[2]^2 * exp(x[1]^2) stats = minimize(f, ones(2)) ; ``` + Using L-BFGS, the problem is locally solved. Note that when passing Julia functions as input to `minimize`, the problem is modeled as an [`ADNLPModel`](https://github.com/JuliaSmoothOptimizers/ADNLPModels.jl). So, the following would be equivalent: + ```@example ex2 using ADNLPModels, JSOSuite f(x) = x[2]^2 * exp(x[1]^2) @@ -27,6 +32,7 @@ stats = minimize(nlp) ``` The procedure is similar with `JuMP` models. + ```@example 3 using JuMP, JSOSuite model = Model() @@ -38,6 +44,7 @@ stats = minimize(model) ## In-place solve If we want to solve the same problem several times, for instance, for several initial guesses, it is recommended to use an in-place solve. + ```@example ex1 using ADNLPModels, JSOSolvers, SolverCore f(x) = x[2]^2 * exp(x[1]^2) @@ -46,14 +53,18 @@ solver = JSOSolvers.LBFGSSolver(nlp) stats = SolverCore.GenericExecutionStats(nlp) solve!(solver, nlp, stats, x = ones(2)) ``` + This deserves more explanations. The name of the solver structure and the corresponding package can be accessed via the DataFrame `JSOSuite.optimizers`. + ```@example ex1 JSOSuite.optimizers[!, [:name_solver, :name_pkg]] ``` + In our example, the solver L-BFGS is implemented in [`JSOSolvers.jl`](https://github.com/JuliaSmoothOptimizers/JSOSolvers.jl) and the solver structure is `LBFGSSolver`. Now, it is possible to reuse the memory allocated for the first solve for another round: + ```@example ex1 # NLPModels.reset!(nlp) # would also reset the evaluation counters of the model SolverCore.reset!(solver) @@ -64,6 +75,7 @@ solve!(solver, nlp, stats, x = new_x0) # new solve with existing solver object ## In-place solve of a different problem It is also possible to reuse the allocated memory to solve another problem with the same number of variables and constraints: + ```@example ex1 f2(x) = x[2]^2 + exp(x[1]^2) nlp = ADNLPModel(f2, ones(2)) # or use JuMP @@ -74,6 +86,7 @@ solve!(solver, nlp, stats) ## Allocation-free solvers In order to measure, the amount of memory allocated by the solvers, the package [`NLPModelsTest.jl`](https://github.com/JuliaSmoothOptimizers/NLPModelsTest.jl) defines a set of test problems that are allocation free. + ```@example ex1 using NLPModelsTest, SolverCore, JSOSolvers nlp = BROWNDEN(Float64) @@ -82,4 +95,5 @@ stats = GenericExecutionStats(nlp) solve!(solver, nlp, stats) @allocated solve!(solver, nlp, stats) ``` + Several of the pure Julia solvers available in JSOSuite have this property. diff --git a/docs/src/benchmark.md b/docs/src/50-benchmark.md similarity index 96% rename from docs/src/benchmark.md rename to docs/src/50-benchmark.md index 579dc0e..1d4ffbb 100644 --- a/docs/src/benchmark.md +++ b/docs/src/50-benchmark.md @@ -5,6 +5,7 @@ Benchmarking is very important when researching new algorithms or selecting the The package [`SolverBenchmark`](https://github.com/JuliaSmoothOptimizers/SolverBenchmark.jl) exports the function [`bmark_solvers`](https://github.com/JuliaSmoothOptimizers/SolverBenchmark.jl/blob/main/src/bmark_solvers.jl) that runs a set of optimizers on a set of problems. `JSOSuite.jl` specialize this function, see `bmark_solvers`. The [JuliaSmoothOptimizers organization](https://jso.dev) contains several packages of test problems ready to use for benchmarking. The main ones are + - [`OptimizationProblems.jl`](https://github.com/JuliaSmoothOptimizers/OptimizationProblems.jl): This package provides a collection of optimization problems in JuMP and ADNLPModels syntax; - [`CUTEst.jl`](https://github.com/JuliaSmoothOptimizers/CUTEst.jl); - [`NLSProblems.jl`](https://github.com/JuliaSmoothOptimizers/NLSProblems.jl). @@ -27,7 +28,7 @@ selected_meta = selected_meta[.!selected_meta.has_bounds .&& (selected_meta.ncon list = selected_meta[!, :name] ``` -Then, we generate the list of problems using [`ADNLPModel`](https://jso.dev/ADNLPModels.jl/dev/reference/#ADNLPModels.ADNLPModel-Union{Tuple{S},%20Tuple{Any,%20S}}%20where%20S). +Then, we generate the list of problems using [`ADNLPModel`](https://jso.dev/ADNLPModels.jl/dev/reference/). ```@example op ad_problems = [ @@ -47,11 +48,12 @@ selected_optimizers[selected_optimizers.is_available, :] # optimizers available ``` For the purpose of this example, we will consider 3 optimizers. + ```@example op select = ["IPOPT", "TRUNK", "LBFGS"] ``` -Once the problems and optimizers are chosen, the function `bmark_solvers` runs the benchmark. +Once the problems and optimizers are chosen, the function `bmark_solvers` runs the benchmark. ```@example op using SolverBenchmark diff --git a/docs/src/speed-up.md b/docs/src/60-speed-up.md similarity index 91% rename from docs/src/speed-up.md rename to docs/src/60-speed-up.md index 2935db1..eb119d2 100644 --- a/docs/src/speed-up.md +++ b/docs/src/60-speed-up.md @@ -5,6 +5,7 @@ The following contains a list of tips to speed up the solver selection and usage ## Derivatives The optimizers available in `JSOSuite.jl` are all using first and sometines second-order derivatives. There are mainly three categories: + - 1st order methods use only gradient information; - 1st order quasi-Newton methods require only gradient information, and uses it to build an approximation of the Hessian; - 2nd order methods: Those are using gradients and Hessian information. @@ -24,10 +25,10 @@ This classification is straightforwardly extended to handling constraints with t ## Find a better initial guess -The majority of derivative-based optimizers are local methods whose performance are dependent of the initial guess. +The majority of derivative-based optimizers are local methods whose performance are dependent of the initial guess. This usually relies on specific knowledge of the problem. -The function [`feasible_point`](@ref) computes a point satisfying the constraints of the problem that can be used as an initial guess. +The function [`feasible_point`](@ref) computes a point satisfying the constraints of the problem that can be used as an initial guess. An alternative is to solve a simpler version of the problem and reuse the solution as an initial guess for the more complex one. ## Use the structure of the problem @@ -45,20 +46,20 @@ Note that all optimizers presented here have been carefully optimized. All have ### Unconstrained -##### LBFGS (1st order) +#### LBFGS (1st order) - `mem::Int = 5`: memory parameter of the `lbfgs` algorithm; - `τ₁::T = T(0.9999)`: slope factor in the Wolfe condition when performing the line search; - `bk_max:: Int = 25`: maximum number of backtracks when performing the line search. -##### R2 (1st order) +#### R2 (1st order) - `η1 = eps(T)^(1/4)`, `η2 = T(0.95)`: step acceptance parameters; - `γ1 = T(1/2)`, `γ2 = 1/γ1`: regularization update parameters; - `σmin = eps(T)`: step parameter for R2 algorithm; - `β = T(0) ∈ [0,1]` is the constant in the momentum term. If `β == 0`, R2 does not use momentum. -##### TRUNK (matrix-free) +#### TRUNK (matrix-free) - `bk_max::Int = 10`: algorithm parameter; - `monotone::Bool = true`: algorithm parameter; @@ -66,7 +67,7 @@ Note that all optimizers presented here have been carefully optimized. All have ### Bound-constrained (matrix-free) -##### TRON +#### TRON - `μ₀::T = T(1e-2)`: algorithm parameter in (0, 0.5); - `μ₁::T = one(T)`: algorithm parameter in (0, +∞); @@ -76,27 +77,27 @@ Note that all optimizers presented here have been carefully optimized. All have ### Constrained -##### RipQP (quadratic with linear constraints) +#### RipQP (quadratic with linear constraints) See [RipQP.jl tutorial](https://jso.dev/tutorials/introduction-to-ripqp/) and [RipQP.jl API](https://jso.dev/RipQP.jl/stable/API/). -##### CaNNOLeS (NLS with nonlinear equality constraints) +#### CaNNOLeS (NLS with nonlinear equality constraints) - `linsolve::Symbol = :ma57`: solver to compute LDLt factorization. Available methods are: `:ma57`, `:ldlfactorizations`; - `method::Symbol = :Newton`: available methods `:Newton, :LM, :Newton_noFHess`, and `:Newton_vanishing`; See [CaNNOLeS.jl tutorial](https://jso.dev/CaNNOLeS.jl/dev/tutorial/). -##### DCISolver (nonlinear equality constraints) +#### DCISolver (nonlinear equality constraints) - `linear_solver = :ldlfact`: Solver for the factorization. options: `:ma57` if `HSL.jl` available. See the [`fine-tuneDCI tutorial`](https://jso.dev/DCISolver.jl/dev/fine-tuneDCI/). -##### FletcherPenaltySolver (nonlinear equality constraints) +#### FletcherPenaltySolver (nonlinear equality constraints) See the [`fine-tuneFPS tutorial`](https://jso.dev/FletcherPenaltySolver.jl/dev/fine-tuneFPS/). -##### Percival +#### Percival - `μ::Real = T(10.0)`: Starting value of the penalty parameter. diff --git a/docs/src/90-contributing.md b/docs/src/90-contributing.md new file mode 100644 index 0000000..051d777 --- /dev/null +++ b/docs/src/90-contributing.md @@ -0,0 +1,25 @@ +# [Contributing guidelines](@id contributing) + +First of all, thanks for the interest! + +We welcome all kinds of contribution, including, but not limited to code, documentation, examples, configuration, issue creating, etc. + +Be polite and respectful, and follow the code of conduct. + +## Bug reports and discussions + +If you think you found a bug, feel free to open an [issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues). +Focused suggestions and requests can also be opened as issues. +Before opening a pull request, start an issue or a discussion on the topic, please. + +## Working on an issue + +If you found an issue that interests you, comment on that issue what your plans are. +If the solution to the issue is clear, you can immediately create a pull request (see below). +Otherwise, say what your proposed solution is and wait for a discussion around it. + +!!! tip + Feel free to ping us after a few days if there are no responses. + +If your solution involves code (or something that requires running the package locally), check the [developer documentation](90-developer.md). +Otherwise, you can use the GitHub interface directly to create your pull request. diff --git a/docs/src/90-developer.md b/docs/src/90-developer.md new file mode 100644 index 0000000..47b05b0 --- /dev/null +++ b/docs/src/90-developer.md @@ -0,0 +1,163 @@ +# [Developer documentation](@id dev_docs) + +!!! note "Contributing guidelines" + If you haven't, please read the [Contributing guidelines](90-contributing.md) first. + +If you want to make contributions to this package that involves code, then this guide is for you. + +## First time clone + +!!! tip "If you have writing rights" + If you have writing rights, you don't have to fork. Instead, simply clone and skip ahead. Whenever **upstream** is mentioned, use **origin** instead. + +If this is the first time you work with this repository, follow the instructions below to clone the repository. + +1. Fork this repo +2. Clone your repo (this will create a `git remote` called `origin`) +3. Add this repo as a remote: + + ```bash + git remote add upstream https://github.com/JuliaSmoothOptimizers/JSOSuite.jl + ``` + +This will ensure that you have two remotes in your git: `origin` and `upstream`. +You will create branches and push to `origin`, and you will fetch and update your local `main` branch from `upstream`. + +## Linting and formatting + +Install a plugin on your editor to use [EditorConfig](https://editorconfig.org). +This will ensure that your editor is configured with important formatting settings. + +We use [https://pre-commit.com](https://pre-commit.com) to run the linters and formatters. +In particular, the Julia code is formatted using [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl), so please install it globally first: + +```julia-repl +julia> # Press ] +pkg> activate +pkg> add JuliaFormatter +``` + +To install `pre-commit`, we recommend using [pipx](https://pipx.pypa.io) as follows: + +```bash +# Install pipx following the link +pipx install pre-commit +``` + +With `pre-commit` installed, activate it as a pre-commit hook: + +```bash +pre-commit install +``` + +To run the linting and formatting manually, enter the command below: + +```bash +pre-commit run -a +``` + +**Now, you can only commit if all the pre-commit tests pass**. + +## Testing + +As with most Julia packages, you can just open Julia in the repository folder, activate the environment, and run `test`: + +```julia-repl +julia> # press ] +pkg> activate . +pkg> test +``` + +## Working on a new issue + +We try to keep a linear history in this repo, so it is important to keep your branches up-to-date. + +1. Fetch from the remote and fast-forward your local main + + ```bash + git fetch upstream + git switch main + git merge --ff-only upstream/main + ``` + +2. Branch from `main` to address the issue (see below for naming) + + ```bash + git switch -c 42-add-answer-universe + ``` + +3. Push the new local branch to your personal remote repository + + ```bash + git push -u origin 42-add-answer-universe + ``` + +4. Create a pull request to merge your remote branch into the org main. + +### Branch naming + +- If there is an associated issue, add the issue number. +- If there is no associated issue, **and the changes are small**, add a prefix such as "typo", "hotfix", "small-refactor", according to the type of update. +- If the changes are not small and there is no associated issue, then create the issue first, so we can properly discuss the changes. +- Use dash separated imperative wording related to the issue (e.g., `14-add-tests`, `15-fix-model`, `16-remove-obsolete-files`). + +### Commit message + +- Use imperative or present tense, for instance: *Add feature* or *Fix bug*. +- Have informative titles. +- When necessary, add a body with details. +- If there are breaking changes, add the information to the commit message. + +### Before creating a pull request + +!!! tip "Atomic git commits" + Try to create "atomic git commits" (recommended reading: [The Utopic Git History](https://blog.esciencecenter.nl/the-utopic-git-history-d44b81c09593)). + +- Make sure the tests pass. +- Make sure the pre-commit tests pass. +- Fetch any `main` updates from upstream and rebase your branch, if necessary: + + ```bash + git fetch upstream + git rebase upstream/main BRANCH_NAME + ``` + +- Then you can open a pull request and work with the reviewer to address any issues. + +## Building and viewing the documentation locally + +Following the latest suggestions, we recommend using `LiveServer` to build the documentation. +Here is how you do it: + +1. Run `julia --project=docs` to open Julia in the environment of the docs. +1. If this is the first time building the docs + 1. Press `]` to enter `pkg` mode + 1. Run `pkg> dev .` to use the development version of your package + 1. Press backspace to leave `pkg` mode +1. Run `julia> using LiveServer` +1. Run `julia> servedocs()` + +## Making a new release + +To create a new release, you can follow these simple steps: + +- Create a branch `release-x.y.z` +- Update `version` in `Project.toml` +- Update the `CHANGELOG.md`: + - Rename the section "Unreleased" to "[x.y.z] - yyyy-mm-dd" (i.e., version under brackets, dash, and date in ISO format) + - Add a new section on top of it named "Unreleased" + - Add a new link in the bottom for version "x.y.z" + - Change the "[unreleased]" link to use the latest version - end of line, `vx.y.z ... HEAD`. +- Create a commit "Release vx.y.z", push, create a PR, wait for it to pass, merge the PR. +- Go back to main screen and click on the latest commit (link: ) +- At the bottom, write `@JuliaRegistrator register` + +After that, you only need to wait and verify: + +- Wait for the bot to comment (should take < 1m) with a link to a RP to the registry +- Follow the link and wait for a comment on the auto-merge +- The comment should said all is well and auto-merge should occur shortly +- After the merge happens, TagBot will trigger and create a new GitHub tag. Check on +- After the release is create, a "docs" GitHub action will start for the tag. +- After it passes, a deploy action will run. +- After that runs, the [stable docs](https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/stable) should be updated. Check them and look for the version number. diff --git a/docs/src/90-reference.md b/docs/src/90-reference.md new file mode 100644 index 0000000..da4c94a --- /dev/null +++ b/docs/src/90-reference.md @@ -0,0 +1,17 @@ +# [Reference](@id reference) + +## Contents + +```@contents +Pages = ["90-reference.md"] +``` + +## Index + +```@index +Pages = ["90-reference.md"] +``` + +```@autodocs +Modules = [JSOSuite] +``` diff --git a/docs/src/assets/style.css b/docs/src/assets/style.css index 7f1fab0..f2cf50d 100644 --- a/docs/src/assets/style.css +++ b/docs/src/assets/style.css @@ -2,7 +2,7 @@ html.theme--documenter-dark #documenter .docs-sidebar { border-right: 4px solid #640000; background-color: #8c1515; - color: #fff; + color: #fff; } .mi, .mo, .mn { @@ -24,4 +24,4 @@ a:hover { nav.toc .logo { max-width: 256px; max-height: 256px; -} \ No newline at end of file +} diff --git a/docs/src/index.md b/docs/src/index.md index f1fc2e7..a8458bb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,113 +1,20 @@ -# JSOSuite.jl - -`JSOSuite` is a unique solution to access all the solvers available in the [JuliaSmoothOptimizers](https://github.com/JuliaSmoothOptimizers) organization. - -All these solvers rely on the `NLPModel API` from [NLPModels.jl](https://github.com/JuliaSmoothOptimizers/NLPModels.jl) for general nonlinear optimization problems of the form - -```math -\begin{aligned} -\min \quad & f(x) \\ -& c_L \leq c(x) \leq c_U \\ -& c_A \leq Ax \leq l_A, \\ -& \ell \leq x \leq u. -\end{aligned} -``` - -The package `JSOSuite` exports a function [`minimize`](@ref): -``` -output = minimize(args...; kwargs...) -``` -where the arguments define the problem, see [Tutorial](@ref tutorial-section). - -It is also possible to define an `NLPModel` or a `JuMP` model representing the problem, and then call `minimize`: -``` -output = minimize(nlpmodel; kwargs...) -output = minimize(jump; kwargs...) -``` - -The `NLPModel API` is a general API for solvers to interact with models by providing flexible data types to represent the objective and constraint functions to evaluate their derivatives, and to provide essentially any information that a solver might request from a model. [JuliaSmoothOrganization's website jso.dev](https://jso.dev) or [NLPModels.jl's documentation](https://jso.dev/NLPModels.jl/dev/) provide more tutorials on this topic. - -### NLPModel - -JuliaSmoothOptimizers' compliant solvers accept any model compatible with the `NLPModel API`. See the [Tutorial](@ref tutorial-section) section for examples. - -Depending on the origin of the problem several modeling tools are available. The following generic modeling tools are accepted: -- `JuMP` models are internally made compatible with NLPModel via [NLPModelsJuMP.jl](https://github.com/JuliaSmoothOptimizers/NLPModelsJuMP.jl); -- `Ampl` models stored in a `.nl` file can be instantiated with `AmplModel("name_of_file.nl")` using [AmplNLReader.jl](https://github.com/JuliaSmoothOptimizers/AmplNLReader.jl); -- [QPSReader.jl](https://github.com/JuliaSmoothOptimizers/QPSReader.jl) reads linear problems in MPS format and quadratic problems in QPS format; -- Models using automatic differentiation can be generated using [ADNLPModels.jl](https://github.com/JuliaSmoothOptimizers/ADNLPModels.jl); -- Models with manually input derivatives can be defined using [ManualNLPModels.jl](https://github.com/JuliaSmoothOptimizers/ManualNLPModels.jl). - -It is also possible to define your `NLPModel` variant. Several examples are available within JuliaSmoothOptimizers' umbrella: -- [KnetNLPModels.jl](https://github.com/JuliaSmoothOptimizers/KnetNLPModels.jl): An NLPModels Interface to Knet. -- [PDENLPModels.jl](https://github.com/JuliaSmoothOptimizers/PDENLPModels.jl): A NLPModel API for optimization problems with PDE-constraints. - -A nonlinear least squares problem is a special case with the objective function defined as ``f(x) = \tfrac{1}{2}\|F(x)\|^2_2``. -Although the problem can be solved using only ``f``, knowing ``F`` independently allows the development of more efficient methods. -See the [Nonlinear Least Squares](@ref nls-section) for more on the special treatment of these problems. - -### Output - -The value returned is a [`GenericExecutionStats`](https://jso.dev/SolverCore.jl/dev/reference/#SolverCore.GenericExecutionStats), which is a structure containing the available information at the end of the execution, such as a solver status, the objective function value, the norm of the residuals, the elapsed time, etc. - -It contains the following fields: -- `status`: Indicates the output of the solver. Use `show_statuses()` for the full list; -- `solution`: The final approximation returned by the solver (default: an uninitialized vector like `nlp.meta.x0`); -- `objective`: The objective value at `solution` (default: `Inf`); -- `dual_feas`: The dual feasibility norm at `solution` (default: `Inf`); -- `primal_feas`: The primal feasibility norm at `solution` (default: `0.0` if unconstrained, `Inf` otherwise); -- `multipliers`: The Lagrange multipliers wrt to the constraints (default: an uninitialized vector like `nlp.meta.y0`); -- `multipliers_L`: The Lagrange multipliers wrt to the lower bounds on the variables (default: an uninitialized vector like `nlp.meta.x0` if there are bounds, or a zero-length vector if not); -- `multipliers_U`: The Lagrange multipliers wrt to the upper bounds on the variables (default: an uninitialized vector like `nlp.meta.x0` if there are bounds, or a zero-length vector if not); -- `iter`: The number of iterations computed by the solver (default: `-1`); -- `elapsed_time`: The elapsed time computed by the solver (default: `Inf`); -- `solver_specific::Dict{Symbol,Any}`: A solver specific dictionary. - -The list of statuses is available via the function `SolverCore.show_statuses`: -```@example -using SolverCore -show_statuses() +```@meta +CurrentModule = JSOSuite ``` -### Keyword Arguments - -All the keyword arguments are passed to the selected solver. -Keywords available for all the solvers are given below: - -- `atol::T = √eps(T)`: absolute tolerance; -- `rtol::T = √eps(T)`: relative tolerance; -- `max_time::Float64 = 300.0`: maximum number of seconds; -- `max_iter::Int = typemax(Int)`: maximum number of iterations; -- `max_eval::Int = 10 000`: maximum number of constraint and objective functions evaluations; -- `callback = (args...) -> nothing`: callback called at each iteration; -- `verbose::Int = 0`: if > 0, display iteration details for every `verbose` iteration. - -The expected signature of the callback is `callback(nlp, solver, stats)`, and its output is ignored. -Changing any of the input arguments will affect the subsequent iterations. -In particular, setting `stats.status = :user` will stop the algorithm. -All relevant information should be available in `nlp` and `solver`. +# JSOSuite -The following are specific to nonlinear least squares: +Documentation for [JSOSuite](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl). -- `Fatol::T = √eps(T)`: absolute tolerance on the residual; -- `Frtol::T = eps(T)`: relative tolerance on the residual, the algorithm stops when ‖F(xᵏ)‖ ≤ Fatol + Frtol * ‖F(x⁰)‖. +## Contributors -Further possible options are documented in each solver's documentation. +```@raw html + + + -## Installation + + + ``` -] add JSOSuite -``` - -## Table of Contents - -```@contents -``` - -# Bug reports and discussions - -If you think you found a bug, feel free to open an [issue](https://github.com/JuliaSmoothOptimizers/JSOSuite.jl/issues). -Focused suggestions and requests can also be opened as issues. Before opening a pull request, start an issue or a discussion on the topic, please. - -If you want to ask a question not suited for a bug report, feel free to start a discussion [here](https://github.com/JuliaSmoothOptimizers/Organization/discussions). This forum is for general discussion about this repository and the [JuliaSmoothOptimizers](https://github.com/JuliaSmoothOptimizers), so questions about any of our packages are welcome. diff --git a/docs/src/reference.md b/docs/src/reference.md deleted file mode 100644 index 00cca18..0000000 --- a/docs/src/reference.md +++ /dev/null @@ -1,17 +0,0 @@ -# Reference -​ -## Contents -​ -```@contents -Pages = ["reference.md"] -``` -​ -## Index -​ -```@index -Pages = ["reference.md"] -``` -​ -```@autodocs -Modules = [JSOSuite] -``` \ No newline at end of file diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 0000000..4f187c5 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,11 @@ +exclude = [ + "@ref", + "^https://github.com/.*/releases/tag/v.*$", + "^https://doi.org/FIXME$", + "^https://JuliaSmoothOptimizers.github.io/JSOSuite.jl/stable$", + "zenodo.org/badge/DOI/FIXME$" +] + +exclude_path = [ + "docs/build" +] diff --git a/src/multi-start.jl b/src/multi-start.jl index b599394..037d7f8 100644 --- a/src/multi-start.jl +++ b/src/multi-start.jl @@ -33,12 +33,12 @@ function multi_start( verbose::Integer = 0, solver_verbose::Integer = 0, strategy::Symbol = :random, - kwargs... + kwargs..., ) where {T, S} best_x = get_x0(nlp) dom = Vector{RealInterval{Float64}}(undef, get_nvar(nlp)) new_x0, best_x = S(undef, get_nvar(nlp)), S(undef, get_nvar(nlp)) - for i=1:get_nvar(nlp) + for i in 1:get_nvar(nlp) dom[i] = RealInterval(nlp.meta.lvar[i], nlp.meta.uvar[i]) end new_x0 .= get_x0(nlp) @@ -69,19 +69,49 @@ function multi_start( [Int, T, T, T, Symbol], hdr_override = Dict(:f => "f(x)", :normx => "‖x‖", :normx0 => "‖x₀‖"), ) - best_obj = run_solver!(best_x, best_obj, solvers, nlp, new_x0, verbose, solver_verbose, max_time; kwargs...) + best_obj = run_solver!( + best_x, + best_obj, + solvers, + nlp, + new_x0, + verbose, + solver_verbose, + max_time; + kwargs..., + ) - for i=1:N + for i in 1:N get_next_x0!(Val(strategy), new_x0, i, dom, best_x) el_time = time() - start_time - best_obj = run_solver!(best_x, best_obj, solvers, nlp, new_x0, verbose, solver_verbose, max_time - el_time; kwargs...) + best_obj = run_solver!( + best_x, + best_obj, + solvers, + nlp, + new_x0, + verbose, + solver_verbose, + max_time - el_time; + kwargs..., + ) end return best_x end -function run_solver!(best_x, best_obj, solvers::Vector{String}, nlp, new_x0, verbose, solver_verbose, max_time; kwargs...) +function run_solver!( + best_x, + best_obj, + solvers::Vector{String}, + nlp, + new_x0, + verbose, + solver_verbose, + max_time; + kwargs..., +) for solver_name in solvers - stats = minimize(solver_name, nlp, x = new_x0, verbose = solver_verbose; kwargs...) + stats = minimize(solver_name, nlp; x = new_x0, verbose = solver_verbose, kwargs...) if (stats.status == :first_order) && (stats.objective < best_obj) best_obj = stats.objective best_x .= stats.solution @@ -91,8 +121,18 @@ function run_solver!(best_x, best_obj, solvers::Vector{String}, nlp, new_x0, ver return best_obj end -function run_solver!(best_x, best_obj, solver_name::String, nlp, new_x0, verbose, solver_verbose, max_time; kwargs...) - stats = minimize(solver_name, nlp, x = new_x0, verbose = solver_verbose; kwargs...) +function run_solver!( + best_x, + best_obj, + solver_name::String, + nlp, + new_x0, + verbose, + solver_verbose, + max_time; + kwargs..., +) + stats = minimize(solver_name, nlp; x = new_x0, verbose = solver_verbose, kwargs...) if (stats.status == :first_order) && (stats.objective < best_obj) best_obj = stats.objective best_x .= stats.solution @@ -101,9 +141,13 @@ function run_solver!(best_x, best_obj, solver_name::String, nlp, new_x0, verbose return best_obj end -function Random.rand!(rng::AbstractRNG, next_x0::AbstractArray{T}, dom::Vector{RealInterval{T}}) where {T} +function Random.rand!( + rng::AbstractRNG, + next_x0::AbstractArray{T}, + dom::Vector{RealInterval{T}}, +) where {T} # check that length(next_x0) == length(dom) - for i=1:length(next_x0) + for i in 1:length(next_x0) next_x0[i] = rand(rng, dom[i]) end return next_x0 diff --git a/src/optimizers.jl b/src/optimizers.jl index cb1042c..bd730a7 100644 --- a/src/optimizers.jl +++ b/src/optimizers.jl @@ -19,7 +19,7 @@ For each solver, the following are available: - `double_precision_only::Bool`: `true` if the solver only handles double precision (`Float64`); - `highest_derivative::Int`: order of the highest derivative used by the algorithm. """ -optimizers = DataFrame( +optimizers = DataFrame(; name = String[], name_solver = Symbol[], name_pkg = String[], diff --git a/src/solve-model.jl b/src/solve-model.jl index c707e0b..dd73fa2 100644 --- a/src/solve-model.jl +++ b/src/solve-model.jl @@ -182,7 +182,7 @@ function QuadraticModel( x0 = fill!(S(undef, length(c)), zero(T)), name::String = "Generic", ) where {T, S <: AbstractVector{T}} - return QuadraticModel(c, H, lvar = lvar, uvar = uvar, c0 = c0, x0 = x0, name = name) + return QuadraticModel(c, H; lvar = lvar, uvar = uvar, c0 = c0, x0 = x0, name = name) end function QuadraticModel( @@ -195,7 +195,7 @@ function QuadraticModel( x0 = fill!(S(undef, length(c)), zero(T)), name::String = "Generic", ) where {T, S <: AbstractVector{T}} - return QuadraticModel(c, H, A = A, lcon = lcon, ucon = ucon, c0 = c0, x0 = x0, name = name) + return QuadraticModel(c, H; A = A, lcon = lcon, ucon = ucon, c0 = c0, x0 = x0, name = name) end function QuadraticModel( @@ -212,7 +212,7 @@ function QuadraticModel( ) where {T, S <: AbstractVector{T}} return QuadraticModel( c, - H, + H; A = A, lcon = lcon, ucon = ucon, diff --git a/src/solvers/ripqp_solve.jl b/src/solvers/ripqp_solve.jl index b9c4105..18cf917 100644 --- a/src/solvers/ripqp_solve.jl +++ b/src/solvers/ripqp_solve.jl @@ -16,7 +16,7 @@ function minimize( delete!(keywords, :atol) delete!(keywords, :rtol) RipQP.InputTol( - T0, + T0; ϵ_pdd = ϵ_pdd, ϵ_rb = ϵ_rb, ϵ_rc = ϵ_rc, @@ -28,7 +28,7 @@ function minimize( ϵ_rb = ϵ_rc = T0(keywords[:atol]) delete!(keywords, :atol) RipQP.InputTol( - T0, + T0; ϵ_pdd = ϵ_pdd, ϵ_rb = ϵ_rb, ϵ_rc = ϵ_rc, @@ -40,7 +40,7 @@ function minimize( ϵ_rb = ϵ_rc = T0(keywords[:atol]) delete!(keywords, :rtol) RipQP.InputTol( - T0, + T0; ϵ_pdd = ϵ_pdd, ϵ_rb = ϵ_rb, ϵ_rc = ϵ_rc, diff --git a/test/multi-start-test.jl b/test/multi-start-test.jl index dc1e58f..d56f6b1 100644 --- a/test/multi-start-test.jl +++ b/test/multi-start-test.jl @@ -2,23 +2,30 @@ using ADNLPModels, NLPModels, NLPModelsTest, JSOSuite, LinearAlgebra @info "Test 1" nlp = BROWNDEN() -JSOSuite.multi_start(nlp, verbose = 1) +JSOSuite.multi_start(nlp; verbose = 1) # Test 2 d = 5 function f(x; d = d) - return sum(x[i]^2 / 4000 - prod(cos(x[i] / sqrt(i)) for i=1:d) + 1 for i=1:d) + return sum(x[i]^2 / 4000 - prod(cos(x[i] / sqrt(i)) for i in 1:d) + 1 for i in 1:d) end T = Float64 @info "Test 2" nlp = ADNLPModel(f, 300 * ones(T, d), -600 * ones(T, d), 600 * ones(T, d)) -ultimate_x = JSOSuite.multi_start(nlp, N = 50, verbose = 10) +ultimate_x = JSOSuite.multi_start(nlp; N = 50, verbose = 10) norm(grad(nlp, ultimate_x)), obj(nlp, ultimate_x) @info "Test 3" nlp = ADNLPModel(f, 300 * ones(T, d)) -ultimate_x = JSOSuite.multi_start(nlp, N = 10, verbose = 1, solver_verbose = 0, multi_solvers = true, skip_solvers = ["Percival"]) +ultimate_x = JSOSuite.multi_start( + nlp; + N = 10, + verbose = 1, + solver_verbose = 0, + multi_solvers = true, + skip_solvers = ["Percival"], +) norm(grad(nlp, ultimate_x)), obj(nlp, ultimate_x) diff --git a/test/qp_tests.jl b/test/qp_tests.jl index bcb861e..40a1f1e 100644 --- a/test/qp_tests.jl +++ b/test/qp_tests.jl @@ -7,12 +7,12 @@ H = sparse(Hrows, Hcols, Hvals) c0 = 1.0 x0 = [-1.2; 1.0] - qp_model = QuadraticModel(c, H, c0 = c0, x0 = x0, name = "uncqp_QP") + qp_model = QuadraticModel(c, H; c0 = c0, x0 = x0, name = "uncqp_QP") stats = minimize(qp_model) @test true - minimize(c, H, c0 = c0, x0 = x0, name = "uncqp_QP") + minimize(c, H; c0 = c0, x0 = x0, name = "uncqp_QP") @test true - minimize("RipQP", c, H, c0 = c0, x0 = x0, name = "uncqp_QP") + minimize("RipQP", c, H; c0 = c0, x0 = x0, name = "uncqp_QP") @test true end @@ -22,12 +22,12 @@ uvar = [1.0; 1.0] lvar = [0.0; 0.0] x0 = [0.5; 0.5] - qp_model = QuadraticModel(c, H, lvar, uvar, x0 = x0, name = "bndqp_QP") + qp_model = QuadraticModel(c, H, lvar, uvar; x0 = x0, name = "bndqp_QP") stats = minimize(qp_model) @test true - minimize(c, H, lvar, uvar, x0 = x0, name = "bndqp_QP") + minimize(c, H, lvar, uvar; x0 = x0, name = "bndqp_QP") @test true - minimize("RipQP", c, H, lvar, uvar, x0 = x0, name = "bndqp_QP") + minimize("RipQP", c, H, lvar, uvar; x0 = x0, name = "bndqp_QP") @test true end @@ -39,12 +39,12 @@ A = ones(1, n) lcon = [1.0] ucon = [1.0] - qp_model = QuadraticModel(c, H, A, lcon, ucon, name = "eqconqp_QP") + qp_model = QuadraticModel(c, H, A, lcon, ucon; name = "eqconqp_QP") stats = minimize(qp_model) @test true - minimize(c, H, A, lcon, ucon, name = "eqconqp_QP") + minimize(c, H, A, lcon, ucon; name = "eqconqp_QP") @test true - minimize("RipQP", c, H, A, lcon, ucon, name = "eqconqp_QP") + minimize("RipQP", c, H, A, lcon, ucon; name = "eqconqp_QP") @test true end @@ -62,12 +62,12 @@ lcon = [0.0; -Inf; -1.0] ucon = [Inf; 0.0; 1.0] x0 = ones(2) - qp_model = QuadraticModel(c, H, A, lcon, ucon, c0 = c0, x0 = x0, name = "ineqconqp_QP") + qp_model = QuadraticModel(c, H, A, lcon, ucon; c0 = c0, x0 = x0, name = "ineqconqp_QP") stats = minimize(qp_model) @test true - minimize(c, H, A, lcon, ucon, c0 = c0, x0 = x0, name = "ineqconqp_QP") + minimize(c, H, A, lcon, ucon; c0 = c0, x0 = x0, name = "ineqconqp_QP") @test true - minimize("RipQP", c, H, A, lcon, ucon, c0 = c0, x0 = x0, name = "ineqconqp_QP") + minimize("RipQP", c, H, A, lcon, ucon; c0 = c0, x0 = x0, name = "ineqconqp_QP") @test true end end diff --git a/test/runtests.jl b/test/runtests.jl index 538aa66..67ede6a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -73,7 +73,7 @@ end test_in_place_solve(nlp, solver_name) test_in_place_solve(model, solver_name) elseif is_available[1] && spec_nls[1] # NLS - nls = OptimizationProblems.ADNLPProblems.arglina(use_nls = true) + nls = OptimizationProblems.ADNLPProblems.arglina(; use_nls = true) test_in_place_solve(nls, solver_name) elseif is_available[1] # RipQP nlp_qm = QuadraticModel(nlp, nlp.meta.x0) @@ -85,15 +85,15 @@ end include("qp_tests.jl") @testset "Test `Float32`" begin - nlp = OptimizationProblems.ADNLPProblems.genrose(type = Val(Float32)) + nlp = OptimizationProblems.ADNLPProblems.genrose(; type = Val(Float32)) atol, rtol = √eps(Float32), √eps(Float32) for solver in eachrow(JSOSuite.select_optimizers(nlp)) if solver.nonlinear_obj - minimize(solver.name, nlp, verbose = 0, atol = atol, rtol = rtol) + minimize(solver.name, nlp; verbose = 0, atol = atol, rtol = rtol) @test true else nlp_qm = QuadraticModel(nlp, nlp.meta.x0) - minimize(solver.name, nlp_qm, verbose = 0, atol = atol, rtol = rtol) + minimize(solver.name, nlp_qm; verbose = 0, atol = atol, rtol = rtol) @test true end end @@ -104,47 +104,47 @@ end jum = MathOptNLPModel(model) @test JSOSuite.select_optimizers(model) == JSOSuite.select_optimizers(jum) for solver in eachrow(JSOSuite.select_optimizers(model)) - minimize(solver.name, model, verbose = 0) + minimize(solver.name, model; verbose = 0) @test true end end @testset "Benchmark on unconstrained problems" begin ad_problems = [ - OptimizationProblems.ADNLPProblems.eval(Meta.parse(problem))() for problem ∈ + OptimizationProblems.ADNLPProblems.eval(Meta.parse(problem))() for problem in first(meta[(5 .<= meta.nvar .<= 10) .& (meta.ncon .== 0) .& (.!meta.has_bounds), :name], 5) ] select = JSOSuite.optimizers[ JSOSuite.optimizers.can_solve_nlp .& JSOSuite.optimizers.is_available, :name, ] - stats = bmark_solvers(ad_problems, select, atol = 1e-3, max_time = 10.0, verbose = 0) + stats = bmark_solvers(ad_problems, select; atol = 1e-3, max_time = 10.0, verbose = 0) @test true # just test that it runs end @testset "Basic solve tests" begin f = x -> 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2 - stats = minimize(f, [-1.2; 1.0], verbose = 0) + stats = minimize(f, [-1.2; 1.0]; verbose = 0) @test stats.status_reliable && (stats.status == :first_order) - stats = minimize("DCISolver", f, [-1.2; 1.0], verbose = 0) + stats = minimize("DCISolver", f, [-1.2; 1.0]; verbose = 0) @test stats.status_reliable && (stats.status == :first_order) F = x -> [10 * (x[2] - x[1]^2); x[1] - 1] - stats = minimize(F, [-1.2; 1.0], 2, verbose = 0) + stats = minimize(F, [-1.2; 1.0], 2; verbose = 0) @test stats.status_reliable && (stats.status == :first_order) - stats = minimize("DCISolver", F, [-1.2; 1.0], 2, verbose = 0) + stats = minimize("DCISolver", F, [-1.2; 1.0], 2; verbose = 0) @test stats.status_reliable && (stats.status == :first_order) end @testset "Test solve OptimizationProblems: $name" for name in first(meta[meta.nvar .< 10, :name], 5) name in ["bennett5", "channel", "hs253", "hs73", "misra1c"] && continue nlp = OptimizationProblems.ADNLPProblems.eval(Meta.parse(name))() - minimize(nlp, verbose = 0) + minimize(nlp; verbose = 0) @test true model = OptimizationProblems.PureJuMP.eval(Meta.parse(name))() - minimize(model, verbose = 0) + minimize(model; verbose = 0) @test true end @@ -164,7 +164,7 @@ for solver in eachrow(JSOSuite.optimizers) if solver.nonlinear_obj minimize( solver.name, - nlp, + nlp; atol = 1e-5, rtol = 1e-5, max_time = 12.0, @@ -176,7 +176,7 @@ for solver in eachrow(JSOSuite.optimizers) nlp_qm = QuadraticModel(nlp, nlp.meta.x0) minimize( solver.name, - nlp_qm, + nlp_qm; atol = 1e-5, rtol = 1e-5, max_time = 12.0, @@ -191,7 +191,7 @@ end @testset "Test kwargs in optimizers on $model" for model in (:arglina, :hs6) nlp = OptimizationProblems.ADNLPProblems.eval(model)() - nls = OptimizationProblems.ADNLPProblems.eval(model)(use_nls = true) + nls = OptimizationProblems.ADNLPProblems.eval(model)(; use_nls = true) callback = (args...) -> nothing for solver in eachrow(JSOSuite.optimizers) @testset "Test options in $(solver.name)" begin @@ -201,7 +201,7 @@ end if solver.can_solve_nlp minimize( solver.name, - nlp, + nlp; atol = 1e-5, rtol = 1e-5, max_time = 12.0, @@ -214,7 +214,7 @@ end elseif solver.specialized_nls minimize( solver.name, - nls, + nls; atol = 1e-5, rtol = 1e-5, Fatol = 1e-5, @@ -230,7 +230,7 @@ end nlp_qm = QuadraticModel(nlp, nlp.meta.x0) minimize( solver.name, - nlp_qm, + nlp_qm; atol = 1e-5, rtol = 1e-5, max_time = 12.0,