microlp 0.5.0

A fast linear programming solver library.
Documentation
name: Continuous Integration
on:
  pull_request:
  push:
    branches:
      - main
      - master
    tags:
      - v*

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  conformance:
    name: Conformance
    runs-on: "ubuntu-latest"
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Cache Rust
        uses: actions/cache@v3
        with:
          key: ${{ runner.os }}-nightly-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'benchmark/Cargo.toml') }}
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: nightly
          components: rustfmt, clippy
          override: true

      - name: Rustfmt
        uses: actions-rs/cargo@v1
        with:
          command: fmt
          args: --check

      - name: Clippy
        uses: actions-rs/cargo@v1
        with:
          command: clippy
          args: --locked -- --deny warnings
  test:
    strategy:
      matrix:
        os:
          - windows-latest
          - macos-latest
          - ubuntu-latest

    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Cache Rust
        uses: actions/cache@v3
        with:
          key: ${{ runner.os }}-stable-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'benchmark/Cargo.toml') }}
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Test Rust (unit tests)
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --locked --release --lib

      - name: Doctests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --locked --release --doc

      - name: Exhaustive small-model oracles
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --locked --release --test exhaustive_mip

      # Problem-based correctness suite. Tiers are cumulative (easy < medium
      # < hard < xhard); --hard runs everything up to and including the hard
      # tier (long-running MILP benchmarks), with every single instance capped
      # at 5 minutes (--max-case-seconds 300, applied on top of each case's
      # own budget). The interrupt-tolerant hard cases pass cleanly at any
      # budget; nothing else needs anywhere near the cap. The xhard tier
      # (MILPBench instances beyond the solver's ceiling, 10-minute budgets)
      # stays manual: run it locally with
      # `cargo test --release --test suite -- --xhard`.
      # On Linux, additionally cross-check every result against HiGHS via
      # `--features highs`. The grounding is OS-independent, so it runs on one
      # runner only; building HiGHS needs CMake and a C++ compiler, both
      # preinstalled on ubuntu-latest.
      - name: Correctness suite
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --locked --release --test suite ${{ matrix.os == 'ubuntu-latest' && '--features highs' || '' }} -- --hard --parallel 4 --max-case-seconds 300

      - name: Test Rust with traces
        uses: actions-rs/cargo@v1
        env:
          RUST_LOG: "trace"
        with:
          command: test
          args: --locked --lib

  wasm:
    name: WASM
    runs-on: "ubuntu-latest"
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Cache Rust
        uses: actions/cache@v3
        with:
          key: ${{ runner.os }}-wasm-${{ hashFiles('Cargo.lock', 'Cargo.toml') }}
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          target: wasm32-unknown-unknown
          override: true

      # Compile guard: the library must build for wasm on its own. It can because
      # it reads the clock from `web-time`, not `std::time::Instant` (which panics
      # on wasm32-unknown-unknown). Library only: dev-dependencies and the
      # benchmark crate stay native.
      - name: Build library for wasm32-unknown-unknown
        uses: actions-rs/cargo@v1
        with:
          command: build
          args: --locked --release --lib --target wasm32-unknown-unknown

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install wasm-pack
        uses: jetli/wasm-pack-action@v0.4.0
        with:
          version: latest

      # Runtime guard: build the example to wasm and actually solve an LP and a
      # MILP under Node. Each solve sets a time limit, so this exercises the
      # web-time clock at runtime — catching a `std::time::Instant` regression
      # that the compile step above cannot (it compiles fine, then panics).
      - name: Run the wasm example under Node
        working-directory: wasm-example
        run: |
          wasm-pack build --target nodejs
          node test.js

  publish-crate:
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [conformance, test, wasm]

    name: Publish to Crates.io
    runs-on: "ubuntu-latest"
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Publish
        run: cargo publish --token ${REGISTRY_TOKEN}
        env:
          REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}