Telemetry Logging
Simulated annealing runs can emit structured telemetry so that future tuning (manual, LLM-assisted,
or automated) can analyse performance. Use --telemetry-log PATH with either
fhops solve-heur or fhops bench suite to append newline-delimited JSON records:
fhops solve-heur examples/tiny7/scenario.yaml --out tmp/result.csv \
--telemetry-log tmp/telemetry.jsonl --show-operator-stats
Schema
Each JSON record includes the following fields:
timestamp(str)ISO8601 UTC timestamp when the entry was written.
source(str)Command that generated the entry (
solve-heurorbench-suite).scenario(str) /scenario_path(str)Scenario name and file path.
solver(str)Identifies the solver (
sa,ils,tabu). When omitted the record came fromsolve-heurwithout specifying an algorithm (legacy).seed(int),iterations(int)Parameters used for the run.
objective(float)Final objective reported by the solver.
kpis(object)Snapshot of computed KPIs (mobilisation cost, total production, etc.).
operators_config(object)Final operator weight configuration used for the run.
operators_stats(object)Per-operator telemetry with the following fields:
proposals: number of neighbour proposals emitted.accepted: number of accepted neighbours.skipped: times the operator returnedNone(e.g., infeasible move).weight: effective weight used for selection.acceptance_rate:accepted / proposals(0 when proposals is 0).
Example
{
"timestamp": "2025-11-09T05:31:42.972801",
"source": "solve-heur",
"scenario": "FHOPS Tiny7",
"scenario_path": "examples/tiny7/scenario.yaml",
"solver": "sa",
"seed": 42,
"iterations": 200,
"objective": 13.0,
"kpis": {"total_production": 45.5, "mobilisation_cost": 65.0, "...": "..."},
"operators_config": {"swap": 1.0, "move": 1.0},
"operators_stats": {
"swap": {
"proposals": 200.0,
"accepted": 200.0,
"skipped": 0.0,
"weight": 1.0,
"acceptance_rate": 1.0
},
"move": {
"proposals": 200.0,
"accepted": 200.0,
"skipped": 0.0,
"weight": 1.0,
"acceptance_rate": 1.0
}
}
}
Solver-specific fields
ILS entries echo diversification metadata:
perturbations,restarts,improvement_steps,stall_limit,perturbation_strength, and hybrid flags (hybrid_use_mip,hybrid_mip_time_limit).Tabu entries include
tabu_tenureandtabu_stall_limit.
Usage Notes
Logs are append-only; use tooling such as
jqor pandas to analyse historical performance.Operators with frequently low acceptance rates may warrant weight adjustments or new presets.
Combine logs with the hyperparameter tuning plan (
notes/metaheuristic_hyperparam_tuning.md) to drive future ML/LLM-based schedulers.Parallel options add
batch_size/max_workersfields to single-run records. Multi-start telemetry logs per-run entries withrun_id/presetand a summary record containingtype: multi_start_summary,best_run_id,best_objective, andruns_executed.
CLI Reporting
Use the fhops telemetry report sub-command to aggregate the mirrored SQLite
store into CSV/Markdown summaries without re-running the tuners (CI and the
weekly analytics workflow both invoke this before deploying GitHub Pages):
fhops telemetry report telemetry/runs.sqlite \
--out-csv tmp/tuner_report.csv \
--out-markdown tmp/tuner_report.md
The command scans runs, run_metrics, run_kpis, and
tuner_summaries tables to surface best/mean objective values per algorithm
and scenario. See Telemetry-Driven Tuning for a step-by-step guide and
docs/reference/dashboards for the live links generated from these files.
Historical Trends
Each CI run uploads three ready-made history artefacts under the
telemetry-report bundle:
history_summary.csv— tabular history of best/mean objectives for dated snapshots.history_summary.md— Markdown rendering of the same table.history_summary.html— Altair chart plotting best objective trends.history_delta.{csv,md}— latest vs. previous snapshot diff across objectives and key KPIs.
Download those files (or rerun analyze_tuner_reports.py --history-dir on a
local archive) to inspect performance trends without regenerating telemetry.