fhops 0.1.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fhops/__init__.py +5 -0
- fhops/cli/_utils.py +140 -0
- fhops/cli/benchmarks.py +797 -0
- fhops/cli/geospatial.py +32 -0
- fhops/cli/main.py +2162 -0
- fhops/cli/profiles.py +268 -0
- fhops/cli/synthetic.py +433 -0
- fhops/cli/telemetry.py +366 -0
- fhops/core/types.py +36 -0
- fhops/data/loaders.py +35 -0
- fhops/eval/kpis.py +15 -0
- fhops/evaluation/__init__.py +83 -0
- fhops/evaluation/metrics/__init__.py +1 -0
- fhops/evaluation/metrics/aggregates.py +117 -0
- fhops/evaluation/metrics/kpis.py +292 -0
- fhops/evaluation/playback/__init__.py +56 -0
- fhops/evaluation/playback/adapters.py +252 -0
- fhops/evaluation/playback/aggregates.py +153 -0
- fhops/evaluation/playback/core.py +327 -0
- fhops/evaluation/playback/events.py +102 -0
- fhops/evaluation/playback/exporters.py +132 -0
- fhops/evaluation/playback/stochastic.py +318 -0
- fhops/evaluation/reporting/__init__.py +1 -0
- fhops/model/pyomo_builder.py +15 -0
- fhops/optimization/__init__.py +6 -0
- fhops/optimization/constraints/__init__.py +1 -0
- fhops/optimization/heuristics/__init__.py +21 -0
- fhops/optimization/heuristics/ils.py +393 -0
- fhops/optimization/heuristics/multistart.py +217 -0
- fhops/optimization/heuristics/registry.py +422 -0
- fhops/optimization/heuristics/sa.py +650 -0
- fhops/optimization/heuristics/tabu.py +306 -0
- fhops/optimization/mip/__init__.py +6 -0
- fhops/optimization/mip/builder.py +258 -0
- fhops/optimization/mip/constraints/system_sequencing.py +96 -0
- fhops/optimization/mip/highs_driver.py +263 -0
- fhops/scenario/__init__.py +26 -0
- fhops/scenario/contract/__init__.py +25 -0
- fhops/scenario/contract/models.py +403 -0
- fhops/scenario/io/__init__.py +5 -0
- fhops/scenario/io/loaders.py +180 -0
- fhops/scenario/io/mobilisation.py +71 -0
- fhops/scenario/synthetic/__init__.py +25 -0
- fhops/scenario/synthetic/generator.py +805 -0
- fhops/scheduling/__init__.py +13 -0
- fhops/scheduling/geospatial.py +173 -0
- fhops/scheduling/mobilisation/__init__.py +15 -0
- fhops/scheduling/mobilisation/models.py +75 -0
- fhops/scheduling/systems/__init__.py +5 -0
- fhops/scheduling/systems/models.py +121 -0
- fhops/scheduling/timeline/__init__.py +5 -0
- fhops/scheduling/timeline/models.py +71 -0
- fhops/solve/heuristics/sa.py +15 -0
- fhops/solve/highs_mip.py +15 -0
- fhops/telemetry/__init__.py +6 -0
- fhops/telemetry/jsonl.py +60 -0
- fhops/telemetry/run_logger.py +222 -0
- fhops/telemetry/sqlite_store.py +227 -0
- fhops-0.1.0a1.dist-info/METADATA +173 -0
- fhops-0.1.0a1.dist-info/RECORD +63 -0
- fhops-0.1.0a1.dist-info/WHEEL +4 -0
- fhops-0.1.0a1.dist-info/entry_points.txt +2 -0
- fhops-0.1.0a1.dist-info/licenses/LICENSE +21 -0
fhops/__init__.py
ADDED
fhops/cli/_utils.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""CLI helper utilities for FHOPS."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
7
|
+
OPERATOR_PRESETS: dict[str, dict[str, float]] = {
|
|
8
|
+
"balanced": {
|
|
9
|
+
"swap": 1.0,
|
|
10
|
+
"move": 1.0,
|
|
11
|
+
"block_insertion": 0.0,
|
|
12
|
+
"cross_exchange": 0.0,
|
|
13
|
+
"mobilisation_shake": 0.0,
|
|
14
|
+
},
|
|
15
|
+
"swap-only": {
|
|
16
|
+
"swap": 1.0,
|
|
17
|
+
"move": 0.0,
|
|
18
|
+
"block_insertion": 0.0,
|
|
19
|
+
"cross_exchange": 0.0,
|
|
20
|
+
"mobilisation_shake": 0.0,
|
|
21
|
+
},
|
|
22
|
+
"move-only": {
|
|
23
|
+
"swap": 0.0,
|
|
24
|
+
"move": 1.0,
|
|
25
|
+
"block_insertion": 0.0,
|
|
26
|
+
"cross_exchange": 0.0,
|
|
27
|
+
"mobilisation_shake": 0.0,
|
|
28
|
+
},
|
|
29
|
+
"swap-heavy": {
|
|
30
|
+
"swap": 2.0,
|
|
31
|
+
"move": 0.5,
|
|
32
|
+
"block_insertion": 0.0,
|
|
33
|
+
"cross_exchange": 0.0,
|
|
34
|
+
"mobilisation_shake": 0.0,
|
|
35
|
+
},
|
|
36
|
+
"diversify": {
|
|
37
|
+
"swap": 1.5,
|
|
38
|
+
"move": 1.5,
|
|
39
|
+
"block_insertion": 0.0,
|
|
40
|
+
"cross_exchange": 0.0,
|
|
41
|
+
"mobilisation_shake": 0.0,
|
|
42
|
+
},
|
|
43
|
+
"explore": {
|
|
44
|
+
"swap": 1.0,
|
|
45
|
+
"move": 1.0,
|
|
46
|
+
"block_insertion": 0.6,
|
|
47
|
+
"cross_exchange": 0.6,
|
|
48
|
+
"mobilisation_shake": 0.2,
|
|
49
|
+
},
|
|
50
|
+
"mobilisation": {
|
|
51
|
+
"swap": 0.8,
|
|
52
|
+
"move": 0.8,
|
|
53
|
+
"block_insertion": 0.4,
|
|
54
|
+
"cross_exchange": 0.4,
|
|
55
|
+
"mobilisation_shake": 1.2,
|
|
56
|
+
},
|
|
57
|
+
"stabilise": {
|
|
58
|
+
"swap": 0.5,
|
|
59
|
+
"move": 1.5,
|
|
60
|
+
"block_insertion": 0.2,
|
|
61
|
+
"cross_exchange": 0.2,
|
|
62
|
+
"mobilisation_shake": 0.0,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
OPERATOR_PRESET_DESCRIPTIONS: dict[str, str] = {
|
|
67
|
+
"balanced": "Default swap/move weights (1.0 each).",
|
|
68
|
+
"swap-only": "Disable move and rely solely on swap moves.",
|
|
69
|
+
"move-only": "Disable swap and allow only move operations.",
|
|
70
|
+
"swap-heavy": "Bias toward swap moves while keeping move available.",
|
|
71
|
+
"diversify": "Encourage both operators equally with higher weights.",
|
|
72
|
+
"explore": "Activate block insertion/cross exchange with moderate mobilisation shake to diversify neighbourhood search.",
|
|
73
|
+
"mobilisation": "Emphasise mobilisation_shake to escape local minima in distance-constrained scenarios.",
|
|
74
|
+
"stabilise": "Favour move operations to consolidate plans while keeping advanced operators at low weights.",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def parse_operator_weights(weight_args: Sequence[str] | None) -> dict[str, float]:
|
|
79
|
+
"""Parse name=value weight strings into a dictionary."""
|
|
80
|
+
weights: dict[str, float] = {}
|
|
81
|
+
if not weight_args:
|
|
82
|
+
return weights
|
|
83
|
+
for arg in weight_args:
|
|
84
|
+
if "=" not in arg:
|
|
85
|
+
raise ValueError(f"Operator weight must be in name=value format (got '{arg}')")
|
|
86
|
+
name, raw_value = arg.split("=", 1)
|
|
87
|
+
name = name.strip()
|
|
88
|
+
if not name:
|
|
89
|
+
raise ValueError(f"Operator weight missing operator name in '{arg}'")
|
|
90
|
+
try:
|
|
91
|
+
value = float(raw_value)
|
|
92
|
+
except ValueError as exc:
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"Operator weight for '{name}' must be numeric (got '{raw_value}')"
|
|
95
|
+
) from exc
|
|
96
|
+
weights[name.lower()] = value
|
|
97
|
+
return weights
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def resolve_operator_presets(presets: Sequence[str] | None):
|
|
101
|
+
"""Resolve preset names into a list of operators and weight mapping."""
|
|
102
|
+
if not presets:
|
|
103
|
+
return None, {}
|
|
104
|
+
combined_weights: dict[str, float] = {}
|
|
105
|
+
for preset in presets:
|
|
106
|
+
key = preset.lower()
|
|
107
|
+
config = OPERATOR_PRESETS.get(key)
|
|
108
|
+
if config is None:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"Unknown operator preset '{preset}'. Available: {', '.join(sorted(OPERATOR_PRESETS))}"
|
|
111
|
+
)
|
|
112
|
+
for name, weight in config.items():
|
|
113
|
+
combined_weights[name.lower()] = float(weight)
|
|
114
|
+
operators = [name for name, weight in combined_weights.items() if weight > 0]
|
|
115
|
+
return operators or None, combined_weights
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def operator_preset_help() -> str:
|
|
119
|
+
return ", ".join(
|
|
120
|
+
f"{name} ({OPERATOR_PRESET_DESCRIPTIONS.get(name, '').strip()})".strip()
|
|
121
|
+
for name in sorted(OPERATOR_PRESETS)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def format_operator_presets() -> str:
|
|
126
|
+
lines = []
|
|
127
|
+
for name in sorted(OPERATOR_PRESETS):
|
|
128
|
+
weights = ", ".join(f"{op}={val}" for op, val in OPERATOR_PRESETS[name].items())
|
|
129
|
+
desc = OPERATOR_PRESET_DESCRIPTIONS.get(name, "")
|
|
130
|
+
lines.append(f"{name}: {weights}" + (f" — {desc}" if desc else ""))
|
|
131
|
+
return "\n".join(lines)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
"parse_operator_weights",
|
|
136
|
+
"resolve_operator_presets",
|
|
137
|
+
"operator_preset_help",
|
|
138
|
+
"format_operator_presets",
|
|
139
|
+
"OPERATOR_PRESETS",
|
|
140
|
+
]
|