ferrotherm 0.12.0

Thermodynamic computing in pure Rust: sparse energy-based models, chromatic block-Gibbs, parallel tempering, thermodynamic linear algebra, stochastic differentiable programs, a variational compiler onto device topologies, exact inference by variable elimination, planted instances with known optima, sampler certificates, and a first-class joules ledger. std-only, zero dependencies, wasm-clean, deterministic by seed.
Documentation
name: ci
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Install icarus-verilog (RTL bit-exactness gate)
        run: sudo apt-get update && sudo apt-get install -y iverilog
      - name: One version across every package
        run: scripts/check-versions.sh
      - name: Every C ABI symbol reaches every binding, or the gap is written down
        run: scripts/check-parity.sh
      # Parity proves the names exist; this proves they compute the same thing. Bindings whose
      # toolchain the runner lacks skip; a binding that is present and produces nothing FAILS.
      - name: Every binding compiles one model to the same bytes
        run: scripts/check-semantics.sh
      # Parity proves a symbol is DECLARED in each binding. For Julia that is an @cfn line, which
      # says nothing about whether a function wraps it -- both OMMX functions were once declared and
      # exported with no body in between, and the module loaded fine.
      - name: Every exported binding name resolves
        run: scripts/check-exports.sh
      - name: Unit tests
        run: cargo test --release
      # The GPU crate builds everywhere and its tests skip where there is no adapter, so this
      # catches an API break on a runner with no driver -- which is every hosted runner.
      - name: GPU backend builds, and its tests skip rather than fail without an adapter
        run: cargo test --release -p ferrotherm-gpu
      # Same shape: no macmon on a Linux runner, so the measuring tests skip and the parsing and
      # refusal logic -- which is most of the crate -- still runs.
      - name: Power meter builds, and its tests skip rather than fail without a backend
        run: cargo test --release -p ferrotherm-meter
      # The OMMX bridge hand-rolls protobuf, so the check that matters is the REFERENCE
      # implementation reading what it writes. Installed here on purpose; the test skips without it.
      - name: OMMX bridge, checked against the reference implementation
        run: |
          python3 -m pip install --quiet ommx
          OMMX_PYTHON=python3 cargo test --release --test ommx_reference
      - name: Examples build
        run: cargo build --release --examples
      - name: Verification examples (exact-physics gates)
        run: |
          cargo run --release --example ring_tv
          cargo run --release --example onsager
  c-abi:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Build the library the header describes
        run: cargo build --release
      - name: The header compiles and links against it
        run: |
          cc -I include include/check.c -L target/release -lferrotherm -o /tmp/check
          LD_LIBRARY_PATH=target/release /tmp/check

  python:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python: ["3.11", "3.12", "3.13"]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: actions/setup-python@v5
        with:
          # The range we CLAIM, not one point in it. requires-python said >=3.9 while CI tested a
          # single version, so the floor was a claim nobody had ever run.
          python-version: "${{ matrix.python }}"
      - name: Build the library the bindings load
        run: cargo build --release
      - name: Python tests
        run: |
          pip install pytest
          python -m pytest python/test_model.py -q

  wasm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown
      - name: wasm cdylib builds clean
        run: cargo build --release --lib --target wasm32-unknown-unknown
      # Not a byte comparison against a fresh build: a wasm binary is not reproducible across
      # toolchain versions, so that fails on a runner whose rustc differs from the author's and
      # says nothing about whether the artefact works. What matters is that every symbol the pages
      # reach for is present -- a missing export makes the call `undefined`, and calling undefined
      # in a click handler leaves the page looking fine and doing nothing.
      - name: the committed wasm exports everything the pages call
        run: scripts/check-wasm-exports.sh

  zig:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: mlugg/setup-zig@v2
        with:
          version: 0.16.0
      # `zig build test` runs cargo itself and links with an rpath, so there is one command here
      # rather than a hand-written link line that can drift from what a consumer would use.
      - name: Zig tests
        working-directory: zig
        run: zig build test --summary all

  julia:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: julia-actions/setup-julia@v2
        with:
          version: "1.12"
      - name: Build the library the binding loads
        run: cargo build --release
      - name: Julia tests
        working-directory: julia/Ferrotherm
        env:
          FERROTHERM_LIB: ${{ github.workspace }}/target/release/libferrotherm.so
        run: julia --project=. -e 'using Pkg; Pkg.test()'

  editor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
      - name: Install
        working-directory: web-tests
        run: npm install && npx playwright install --with-deps chromium
      - name: Browser tests
        working-directory: web-tests
        run: npm test