Skip to main content

FHOPS — Forest Harvesting Operations Planning System

FHOPS is a Python package and CLI for building, solving, and evaluating forest harvesting operations plans. It provides:

  • A data contract (Pydantic models) for blocks, machines, landings, calendars.
  • A deterministic MIP builder using Pyomo, with HiGHS as the default solver (optional Gurobi support when installed/licensed).
  • A metaheuristic engine (Simulated Annealing v0.1) with pluggable operators.
  • A CLI (fhops) to validate data, solve with MIP or heuristics, and evaluate results.

Installation

pip install fhops==1.0.1

FHOPS 1.0.1 requires Python ≥ 3.11 and, among others, pyomo>=6.9.2 (HiGHS resolution and MILP warm starts), highspy>=1.8.1, and typer>=0.15.4 (older Typer releases cannot build the CLI with current Click). These floors were verified on Python 3.11 and 3.12 (see CHANGE_LOG.md, #118). Scenario files that use initial_state or locked_assignments[].shift_id require fhops>=1.0.1; see docs/howto/data_contract.rst.

For local development or release verification, use Hatch to mirror the CI suite:

pip install hatch
hatch run dev:suite

Quick start (development install)

The PyPI wheel installs the fhops package and CLI. The sample scenario paths below live in the source repository, so clone this repository first when following the quickstart exactly.

# inside a fresh virtual environment (Python 3.11 or 3.12 recommended)
pip install -e .[dev]
# optional extras for spatial IO
pip install .[geo]
# optional extras for commercial MIP backends
# (requires a Gurobi install + license)
pip install .[gurobi]

Optional: Gurobi setup (Linux)

HiGHS remains the default open-source MIP solver. If you have an academic or commercial Gurobi licence and want to use it with FHOPS:

# install gurobipy alongside FHOPS
pip install fhops[gurobi]

# download the licence tools bundle (version shown as example)
wget https://packages.gurobi.com/lictools/licensetools13.0.0_linux64.tar.gz
tar xvfz licensetools13.0.0_linux64.tar.gz

# request your licence key (replace XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)
./grbgetkey XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

# accept the default install path (typically $HOME/gurobi.lic) or specify a custom location.
# if stored elsewhere, point gurobipy at it:
export GRB_LICENSE_FILE=/path/to/gurobi.lic

# quick sanity check
python -c "import gurobipy as gp; m = gp.Model(); m.setParam('OutputFlag', 0); m.optimize()"

After the licence is active you can run the operational MILP with fhops solve-mip-operational ... --solver gurobi (HiGHS is the default solver).

Validate & Evaluate

fhops validate examples/tiny7/scenario.yaml
fhops solve-mip-operational examples/tiny7/scenario.yaml --out examples/tiny7/out/mip_solution.csv
fhops solve-heur examples/tiny7/scenario.yaml --out examples/tiny7/out/sa_solution.csv
fhops evaluate examples/tiny7/scenario.yaml --assignments examples/tiny7/out/mip_solution.csv

fhops solve-mip-operational solves the operational MILP documented in the FHOPS paper (fhops solve-mip is a deprecated alias of it since 1.0.1; the legacy day-level MIP it used to run is infeasible for scenarios with loader roles).

Heuristic objectives for examples/tiny7 and examples/small21 use built-in objective-weight overrides (soft landing capacity, lower mobilisation weight; AUTO_OBJECTIVE_WEIGHT_OVERRIDES), so they are not on the MILP objective's scale: the heuristic commands print a notice, and fhops benchmark / fhops bench suite also report the heuristic plan scored under the scenario's own weights. With a hard landing capacity (landing_surplus weight 0, the default) heuristic plans never overload a landing. See docs/howto/heuristic_presets.rst ("Objective Weights, Automatic Overrides, and Hard Violations").

fhops solve-heur tests/fixtures/regression/regression.yaml --out /tmp/regression_sa.csv
fhops evaluate tests/fixtures/regression/regression.yaml --assignments /tmp/regression_sa.csv

Expected evaluation output includes sequencing_violation_count=0. Mobilisation costs are exercised in tests/test_regression_integration.py, which injects machine parameters before running the CLI.

Analytics notebooks & dashboards

Executed analytics notebooks live under docs/examples/analytics/ and are published to the documentation site. They showcase deterministic playback, stochastic robustness, telemetry diagnostics, and benchmarking workflows. Regenerate them locally with:

python scripts/run_analytics_notebooks.py --light

The --light flag mirrors CI: it sets FHOPS_ANALYTICS_LIGHT=1, trimming stochastic sample counts so the suite finishes quickly. Drop the flag (or unset the environment variable) when you want the full ensemble versions.

Live dashboards (auto-published after every main build and the weekly full notebook run) live at https://ubc-fresh.github.io/fhops/reference/dashboards.html. Highlights:

  • Telemetry history trends and per-scenario leaderboards.
  • Latest tuner reports, comparison tables, and win-rate leaderboards.
  • Difficulty indices per bundle/tier and weekly notebook metadata archives.

Each dashboard entry includes regeneration commands so you can reproduce the artefacts locally.

Tuned heuristic presets

Release tuning runs are recorded in notes/release_tuning_results.md; the best operator weights and configurations per scenario/algorithm are serialized in notes/release_tuned_presets.json. Use these records when reproducing benchmarks or seeding custom presets, e.g.

python -c "import json; cfg=json.load(open('notes/release_tuned_presets.json')); print(cfg[0])"
# feed operator weights into fhops tune-random --operator-weight swap=... --operator-weight move=...

Quick demos

Show off the tuning harness or heuristics in one command:

python scripts/run_tuning_benchmarks.py \
  --bundle synthetic-small \
  --out-dir tmp/demo-synth \
  --random-runs 1 --random-iters 400 \
  --grid-iters 400 --grid-preset explore \
  --bayes-trials 2 --bayes-iters 400 \
  --max-workers 8 \
&& column -t -s'|' tmp/demo-synth/tuner_report.md | sed 's/^/  /'

or run eight random restarts per heuristic on the baseline bundle:

python scripts/run_tuning_benchmarks.py \
  --bundle baseline \
  --out-dir tmp/demo-restarts \
  --tuner random --tuner ils --tuner tabu \
  --random-runs 8 --random-iters 400 \
  --ils-runs 8 --ils-iters 400 \
  --tabu-runs 8 --tabu-iters 2000 \
  --max-workers 8 \
&& column -t -s'|' tmp/demo-restarts/tuner_summary.md | sed 's/^/  /'

Watching heuristics (live dashboard)

Most solver commands accept a --watch/--no-watch flag that renders a Rich dashboard while the run is in progress (long-running example: 200 000 SA iterations on med42 take several minutes):

fhops solve-heur examples/med42/scenario.yaml \
  --out tmp/med42_sa_watch.csv \
  --iters 200000 \
  --cooling-rate 0.99999 \
  --restart-interval 500 \
  --watch \
  --watch-refresh 0.5

The table shows shared metrics (scenario, solver, iteration, best/current/rolling objective, runtime, restarts/workers) while the line below it displays solver-specific details (e.g., SA temperature/acceptance, ILS perturbations, Tabu tenure). The dashboard refreshes only when FHOPS detects an interactive terminal; in CI/non-TTY contexts it emits a single warning (Watch mode disabled: not running in an interactive terminal.) and continues with the normal CLI output.

The flag is available on:

  • fhops solve-heur, fhops solve-ils, fhops solve-tabu
  • fhops tune-random, fhops tune-grid, fhops tune-bayes
  • fhops bench suite --watch

Adjust --watch-refresh <seconds> (default 0.5 s) to trade off responsiveness vs. terminal churn. When using --parallel-workers remember that scoring still uses Python threads today, so the table’s workers column reflects the requested worker count even though the GIL may limit true parallelism; for real multi-core use prefer --parallel-multistart or process-level orchestration.

FAQ – Watch Mode

  • “Watch mode disabled: not running in an interactive terminal.” The dashboard only renders when stdout is a TTY. Wrap the command with script (or run it inside a terminal multiplexer) when you need both the live table and a log:

    script -q -c "fhops solve-heur ... --watch" /tmp/fhops_watch.log
    

    The TTY sees the dashboard, while the log captures the standard CLI output after the run.

  • How can I record a screenshot/GIF? Run a short command (e.g., fhops solve-heur examples/tiny7/scenario.yaml --out tmp/tiny7_sa.csv --watch --iters 500) and use your preferred terminal recorder (asciinema, ttystudio, etc.). The sparkline now renders below the main table so column widths stay stable while recording.

Metadata

Release files for fhops 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fhops 1.0.1
File Size Uploaded
fhops-1.0.1.tar.gz 4.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for fhops 1.0.1
File Interpreter ABI Platform
fhops-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 4.7 MB

Release files / fhops-1.0.1.tar.gz

Download URL fhops-1.0.1.tar.gz
Size 4.1 MB
Tags Source
SHA-256 checksum
How to use checksums
d82afd523ebd9604ce8e59207bc00175d7e727029bf35a3dd87bfa638713cb63
BLAKE2b-256 checksum
How to use checksums
61246b2e4b4a653c0af26693aebba5b215684b85400d41dff7be6a040a51f782
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / fhops-1.0.1-py3-none-any.whl

Download URL fhops-1.0.1-py3-none-any.whl
Size 579.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d06e78bb315ef9a579386229acf32d479934c034184dce104e9508c87c7970a2
BLAKE2b-256 checksum
How to use checksums
77f006038a6e221eed58a19c40d0d9801c333ee76d198f2b009e51472d5b095d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page