Synthetic Datasets

FHOPS ships with a small library of synthetic scenarios that mirror the structure of our teaching and benchmarking datasets. Each bundle is a self-contained directory under examples/synthetic/ with a scenario.yaml and the CSV tables that back it. The bundles are generated by fhops.scenario.synthetic.generate_random_dataset() so they follow the same data contract validation as user-supplied scenarios.

Reference bundles

Three ready-to-use bundles are versioned with the project. The table below summarises their scale; see examples/synthetic/metadata.yaml for the full metadata (random seeds, blackout windows, and shift calendars).

Reference bundle statistics

Bundle

Blocks

Machines

Landings

Days

Shifts/day

Seed / Notes

small

4

2

1

6

1

Seed 101 — single-shift schedule without blackouts.

medium

8

4

2

12

1

Seed 202 — includes short blackout windows for downtime realism.

large

16

6

3

18

2

Seed 303 — two-shift calendar with extended blackout periods.

The example README (examples/synthetic/README.md) gives a short overview and links back to the metadata file if you need exact counts or blackout windows for reproducibility.

Block enrichment, crews, and blackouts

Each bundle ships with richer signals to support evaluation/benchmarking:

  • blocks.csv now contains terrain and prescription columns sampled from tier-aware pools (e.g., gentle/mixed for the small instance, steep/snow for the large one).

  • machines.csv adds a crew column; the generated crew_assignments.csv table records the crew → machine mapping with capability notes so validators and scenario loaders can reconstruct assignments.

  • Crew capabilities are sampled from the capability_pool while respecting the tier-specific span (single-skill crews in small, multi-skill teams in large).

  • Blackout windows are produced with tier defaults: zero-probability for small bundles, short gaps for medium, and multi-day outages for large. Metadata captures the sampled windows so smoke tests can diff future changes.

  • Weighted terrain/prescription distributions and blackout bias windows can be overridden via SyntheticDatasetConfig (see terrain_weights / prescription_weights and blackout_biases) to concentrate sampling on seasonal or topographical hotspots. Harvest-system mixes are also recorded when bundled systems are supplied.

If you regenerate the library, the metadata YAML under each tier (and the aggregate metadata file in examples/synthetic/) records the terrain/prescription mix, crew capabilities, blackout windows, and the seed that produced the dataset.

CLI walkthrough

Use the bundles anywhere a normal scenario is accepted. Typical entry points:

  • Validate the data contract:

    fhops validate examples/synthetic/medium/scenario.yaml
    
  • Run the simulated annealing baseline and capture the schedule:

    fhops solve-heur examples/synthetic/medium/scenario.yaml --out /tmp/medium_sa.csv --seed 99
    
  • Generate shift/day summaries with KPI exports:

    fhops eval-playback examples/synthetic/medium/scenario.yaml \\
                        --assignments /tmp/medium_sa.csv \\
                        --shift-out /tmp/medium_shift.csv \\
                        --day-out /tmp/medium_day.csv \\
                        --summary-md /tmp/medium_playback.md
    
  • Benchmark solver presets across the reference datasets:

    fhops bench suite --scenario examples/synthetic/large/scenario.yaml \\
                      --operator-preset explore \\
                      --out-dir tmp/bench_synth_large
    
  • Generate a fresh bundle (preview or write to disk):

    fhops synth generate tmp/synth_medium_custom --tier medium --seed 321 --blocks 10:14 --preview
    

    Drop --preview (and optionally add --overwrite) to write the generated CSV/YAML bundle. The command accepts YAML/TOML configs via --config when you need full control over the SyntheticDatasetConfig fields.

    Common flags you can mix in:

    • --machines 6:8 / --landings 2 / --days 10:14 to resample sizes.

    • --shifts-per-day 2 to simulate multi-shift calendars.

    • --machine-daily-hours 20 when you want the generated machines.csv to reflect partial-day availability (default is 24, matching the data-contract baseline).

    Note

    When you write directly to examples/synthetic/<tier> the CLI automatically refreshes the aggregate examples/synthetic/metadata.yaml so documentation and automation stay in sync.

  • Create several bundles via a batch plan:

    fhops synth batch docs/examples/synthetic_batch_plan.yaml --overwrite
    

    The plan file can be YAML/TOML/JSON and contains a list of entries, each mirroring the options accepted by fhops synth generate (tier, seed, config, overrides, etc.). This is handy when preparing datasets for experiments or CI warmups. See docs/examples/synthetic_batch_plan.yaml (and the referenced synthetic_batch_custom.yaml) for a ready-to-run example.

These commands reuse the same CLI surfaces already documented in Evaluation Workflows and CLI Reference, but the synthetic bundles keep the inputs lightweight enough for quick iteration and teaching exercises.

The loader automatically ingests crew_assignments.csv when present, so downstream analytics can group KPIs by crew. Pair the bundle metadata with playback exports to slice utilisation or downtime by terrain/prescription class.

Regenerating bundles

The generator can be scripted to refresh or extend the library. Seeds are recorded in metadata.yaml so you can rebuild a bundle exactly:

from pathlib import Path

from fhops.scenario.synthetic import SyntheticDatasetConfig, generate_random_dataset

config = SyntheticDatasetConfig(
    name="synthetic-medium",
    tier="medium",
    num_blocks=(8, 10),
    num_days=12,
    num_machines=4,
    num_landings=2,
    shifts_per_day=1,
    shift_hours=(9.5, 10.5),
    terrain_pool=["rolling", "mixed", "steep"],
    crew_pool=["crew-alpha", "crew-beta", "crew-gamma"],
    capability_pool=["harvester", "forwarder", "processor"],
    crew_capability_span=(1, 2),
)

bundle = generate_random_dataset(config, seed=202)
bundle.write(Path("examples/synthetic/medium"))

You can customise the config ranges (blocks, production rates, blackout probabilities, and role assignments) or pass a systems dictionary to incorporate predefined harvest system templates via generate_random_dataset(..., systems=default_system_registry()).

For small teaching examples, the deterministic helper fhops.scenario.synthetic.generate_basic() creates a scenario with a single landing and no randomisation; for system-aware datasets use fhops.scenario.synthetic.generate_with_systems().

Where to go next

  • tests/test_synthetic_dataset.py illustrates the expected invariants and is a good starting point if you extend the generator.

  • The benchmarking how-to (Benchmarking Harness) explains how to wire new scenarios into the benchmarking harness once you are happy with the dataset characteristics.