mlflow-optimization 0.1.0__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.
- mlflow_optimization/__init__.py +50 -0
- mlflow_optimization/_bootstrap.py +129 -0
- mlflow_optimization/common/diagnostics.py +19 -0
- mlflow_optimization/common/patching.py +82 -0
- mlflow_optimization/features/annotations/annotate.py +97 -0
- mlflow_optimization/features/annotations/types.py +22 -0
- mlflow_optimization/features/artifacts/policy.py +16 -0
- mlflow_optimization/features/artifacts/ports.py +17 -0
- mlflow_optimization/features/autolog/ports.py +49 -0
- mlflow_optimization/features/autolog/service.py +147 -0
- mlflow_optimization/features/autolog/types.py +31 -0
- mlflow_optimization/features/metadata/collect.py +52 -0
- mlflow_optimization/features/metadata/types.py +42 -0
- mlflow_optimization/features/metrics/normalize.py +121 -0
- mlflow_optimization/features/metrics/types.py +54 -0
- mlflow_optimization/features/outcomes/classify.py +53 -0
- mlflow_optimization/features/outcomes/types.py +40 -0
- mlflow_optimization/features/runs/ports.py +30 -0
- mlflow_optimization/features/runs/record.py +231 -0
- mlflow_optimization/features/runs/types.py +43 -0
- mlflow_optimization/integrations/cp_sat/__init__.py +5 -0
- mlflow_optimization/integrations/cp_sat/adapter.py +464 -0
- mlflow_optimization/integrations/gurobipy/__init__.py +5 -0
- mlflow_optimization/integrations/gurobipy/adapter.py +474 -0
- mlflow_optimization/integrations/mathopt/__init__.py +1 -0
- mlflow_optimization/integrations/mathopt/adapter.py +939 -0
- mlflow_optimization/integrations/mlflow/artifacts.py +44 -0
- mlflow_optimization/integrations/mlflow/runs.py +480 -0
- mlflow_optimization/integrations/pyomo/__init__.py +5 -0
- mlflow_optimization/integrations/pyomo/adapter.py +1499 -0
- mlflow_optimization/integrations/pyscipopt/__init__.py +5 -0
- mlflow_optimization/integrations/pyscipopt/adapter.py +657 -0
- mlflow_optimization/integrations/routing/__init__.py +5 -0
- mlflow_optimization/integrations/routing/adapter.py +429 -0
- mlflow_optimization/py.typed +0 -0
- mlflow_optimization-0.1.0.dist-info/METADATA +52 -0
- mlflow_optimization-0.1.0.dist-info/RECORD +40 -0
- mlflow_optimization-0.1.0.dist-info/WHEEL +4 -0
- mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-APACHE +176 -0
- mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-MIT +21 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
"""The OR-Tools Routing solver integration."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from inspect import BoundArguments, signature
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import ortools
|
|
9
|
+
from ortools.constraint_solver import pywrapcp
|
|
10
|
+
|
|
11
|
+
from mlflow_optimization.common.patching import Patch, receiver_of
|
|
12
|
+
from mlflow_optimization.features.autolog.ports import (
|
|
13
|
+
ObservationError,
|
|
14
|
+
SolveObserver,
|
|
15
|
+
SolverIntegration,
|
|
16
|
+
)
|
|
17
|
+
from mlflow_optimization.features.autolog.types import SolveObservation
|
|
18
|
+
from mlflow_optimization.features.metadata.types import (
|
|
19
|
+
ParameterOrigin,
|
|
20
|
+
ReportedMetadata,
|
|
21
|
+
SolverParameter,
|
|
22
|
+
)
|
|
23
|
+
from mlflow_optimization.features.metrics.types import (
|
|
24
|
+
MissingReason,
|
|
25
|
+
ObjectiveSense,
|
|
26
|
+
ReportedMetrics,
|
|
27
|
+
)
|
|
28
|
+
from mlflow_optimization.features.outcomes.types import ReportedOutcome, Termination
|
|
29
|
+
|
|
30
|
+
_SDK_EXCEPTION: type[Exception] = Exception
|
|
31
|
+
_STATUSES: dict[int, tuple[str, Termination]] = {
|
|
32
|
+
0: ("ROUTING_NOT_SOLVED", Termination.UNKNOWN),
|
|
33
|
+
1: ("ROUTING_SUCCESS", Termination.FEASIBLE),
|
|
34
|
+
2: ("ROUTING_PARTIAL_SUCCESS_LOCAL_OPTIMUM_NOT_REACHED", Termination.FEASIBLE),
|
|
35
|
+
3: ("ROUTING_FAIL", Termination.UNKNOWN),
|
|
36
|
+
4: ("ROUTING_FAIL_TIMEOUT", Termination.TIME_LIMIT),
|
|
37
|
+
5: ("ROUTING_INVALID", Termination.ERROR),
|
|
38
|
+
6: ("ROUTING_INFEASIBLE", Termination.INFEASIBLE),
|
|
39
|
+
7: ("ROUTING_OPTIMAL", Termination.OPTIMAL),
|
|
40
|
+
}
|
|
41
|
+
_FAILED_STATUS_CODES: frozenset[int] = frozenset({3, 4, 5, 6})
|
|
42
|
+
_ENTRY_POINTS: tuple[str, ...] = (
|
|
43
|
+
"SolveWithParameters",
|
|
44
|
+
"Solve",
|
|
45
|
+
"SolveFromAssignmentWithParameters",
|
|
46
|
+
"SolveFromAssignmentsWithParameters",
|
|
47
|
+
"SolveWithIteratedLocalSearch",
|
|
48
|
+
"FastSolveFromAssignmentWithParameters",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _explicit_parameters(
|
|
53
|
+
actual: Any,
|
|
54
|
+
defaults: Any,
|
|
55
|
+
prefix: str = "",
|
|
56
|
+
) -> list[SolverParameter]:
|
|
57
|
+
"""Scalar values that differ from OR-Tools search defaults."""
|
|
58
|
+
parameters: list[SolverParameter] = []
|
|
59
|
+
try:
|
|
60
|
+
fields: Any = actual.DESCRIPTOR.fields
|
|
61
|
+
except _SDK_EXCEPTION:
|
|
62
|
+
return parameters
|
|
63
|
+
for field in fields:
|
|
64
|
+
try:
|
|
65
|
+
field_name: str = field.name
|
|
66
|
+
except _SDK_EXCEPTION:
|
|
67
|
+
continue
|
|
68
|
+
parameter_name: str = f"{prefix}.{field_name}" if prefix else field_name
|
|
69
|
+
try:
|
|
70
|
+
actual_value: Any = getattr(actual, field_name)
|
|
71
|
+
except _SDK_EXCEPTION:
|
|
72
|
+
continue
|
|
73
|
+
try:
|
|
74
|
+
default_value: Any = getattr(defaults, field_name)
|
|
75
|
+
except _SDK_EXCEPTION:
|
|
76
|
+
continue
|
|
77
|
+
try:
|
|
78
|
+
is_repeated: bool = bool(field.is_repeated)
|
|
79
|
+
except _SDK_EXCEPTION:
|
|
80
|
+
continue
|
|
81
|
+
if is_repeated:
|
|
82
|
+
try:
|
|
83
|
+
actual_values: list[Any] = list(actual_value)
|
|
84
|
+
except _SDK_EXCEPTION:
|
|
85
|
+
continue
|
|
86
|
+
try:
|
|
87
|
+
default_values: list[Any] = list(default_value)
|
|
88
|
+
except _SDK_EXCEPTION:
|
|
89
|
+
continue
|
|
90
|
+
if actual_values != default_values:
|
|
91
|
+
try:
|
|
92
|
+
actual_values_string: str = str(actual_values)
|
|
93
|
+
except _SDK_EXCEPTION:
|
|
94
|
+
continue
|
|
95
|
+
parameters.append(
|
|
96
|
+
SolverParameter(
|
|
97
|
+
parameter_name,
|
|
98
|
+
actual_values_string,
|
|
99
|
+
ParameterOrigin.EXPLICIT,
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
continue
|
|
103
|
+
try:
|
|
104
|
+
message_type: Any | None = field.message_type
|
|
105
|
+
except _SDK_EXCEPTION:
|
|
106
|
+
continue
|
|
107
|
+
if message_type is not None:
|
|
108
|
+
parameters.extend(
|
|
109
|
+
_explicit_parameters(actual_value, default_value, parameter_name)
|
|
110
|
+
)
|
|
111
|
+
continue
|
|
112
|
+
try:
|
|
113
|
+
differs_from_default: bool = actual_value != default_value
|
|
114
|
+
except _SDK_EXCEPTION:
|
|
115
|
+
continue
|
|
116
|
+
if differs_from_default:
|
|
117
|
+
try:
|
|
118
|
+
enum_type: Any | None = field.enum_type
|
|
119
|
+
except _SDK_EXCEPTION:
|
|
120
|
+
continue
|
|
121
|
+
value: int | float | bool | str = actual_value
|
|
122
|
+
if enum_type is not None:
|
|
123
|
+
try:
|
|
124
|
+
enum_value: Any = enum_type.values_by_number.get(actual_value)
|
|
125
|
+
except _SDK_EXCEPTION:
|
|
126
|
+
continue
|
|
127
|
+
if enum_value is not None:
|
|
128
|
+
try:
|
|
129
|
+
value = enum_value.name
|
|
130
|
+
except _SDK_EXCEPTION:
|
|
131
|
+
continue
|
|
132
|
+
if isinstance(value, (int, float, bool, str)):
|
|
133
|
+
parameters.append(
|
|
134
|
+
SolverParameter(parameter_name, value, ParameterOrigin.EXPLICIT)
|
|
135
|
+
)
|
|
136
|
+
return parameters
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class RoutingAdapter(SolverIntegration):
|
|
140
|
+
"""The OR-Tools Routing solver integration."""
|
|
141
|
+
|
|
142
|
+
api: str = "routing"
|
|
143
|
+
|
|
144
|
+
def __init__(self) -> None:
|
|
145
|
+
self._patches: list[Patch] = []
|
|
146
|
+
|
|
147
|
+
def install(self, observer: SolveObserver) -> None:
|
|
148
|
+
"""The solve hooks installed on the Routing model."""
|
|
149
|
+
if self._patches:
|
|
150
|
+
return
|
|
151
|
+
for entry_point in _ENTRY_POINTS:
|
|
152
|
+
if hasattr(pywrapcp.RoutingModel, entry_point):
|
|
153
|
+
patch: Patch = Patch(pywrapcp.RoutingModel, entry_point)
|
|
154
|
+
patch.install(self._patch_factory(observer, entry_point))
|
|
155
|
+
self._patches.append(patch)
|
|
156
|
+
|
|
157
|
+
def uninstall(self) -> None:
|
|
158
|
+
"""The original solve hooks after uninstall."""
|
|
159
|
+
for patch in reversed(self._patches):
|
|
160
|
+
patch.uninstall()
|
|
161
|
+
self._patches.clear()
|
|
162
|
+
|
|
163
|
+
def _patch_factory(
|
|
164
|
+
self,
|
|
165
|
+
observer: SolveObserver,
|
|
166
|
+
entry_point: str,
|
|
167
|
+
) -> Callable[[Any], Any]:
|
|
168
|
+
"""The wrapper factory for one Routing solve entry point."""
|
|
169
|
+
|
|
170
|
+
def factory(original: Any) -> Any:
|
|
171
|
+
@wraps(original)
|
|
172
|
+
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
173
|
+
call_observer: SolveObserver = observer
|
|
174
|
+
routing_model: pywrapcp.RoutingModel = receiver_of(
|
|
175
|
+
original, args, kwargs
|
|
176
|
+
)
|
|
177
|
+
if routing_model is None:
|
|
178
|
+
return original(*args, **kwargs)
|
|
179
|
+
counter_snapshot: tuple[float | None, float | None] = (
|
|
180
|
+
self._counter_snapshot(routing_model)
|
|
181
|
+
)
|
|
182
|
+
search_parameters: Any | None = self._search_parameters(
|
|
183
|
+
original,
|
|
184
|
+
args,
|
|
185
|
+
kwargs,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def call() -> Any:
|
|
189
|
+
return original(*args, **kwargs)
|
|
190
|
+
|
|
191
|
+
def extract(
|
|
192
|
+
result: Any | None,
|
|
193
|
+
error: BaseException | None,
|
|
194
|
+
) -> SolveObservation:
|
|
195
|
+
return self._extract(
|
|
196
|
+
routing_model,
|
|
197
|
+
result,
|
|
198
|
+
error,
|
|
199
|
+
search_parameters,
|
|
200
|
+
counter_snapshot,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return call_observer.observe(self.api, call, extract)
|
|
204
|
+
|
|
205
|
+
return wrapped
|
|
206
|
+
|
|
207
|
+
return factory
|
|
208
|
+
|
|
209
|
+
def _search_parameters(
|
|
210
|
+
self,
|
|
211
|
+
original: Callable[..., Any],
|
|
212
|
+
arguments: tuple[Any, ...],
|
|
213
|
+
keyword_arguments: dict[str, Any],
|
|
214
|
+
) -> Any | None:
|
|
215
|
+
"""Search parameters for one Routing solve call."""
|
|
216
|
+
bound_arguments: BoundArguments
|
|
217
|
+
try:
|
|
218
|
+
bound_arguments = signature(original).bind_partial(
|
|
219
|
+
*arguments, **keyword_arguments
|
|
220
|
+
)
|
|
221
|
+
except (TypeError, ValueError):
|
|
222
|
+
return None
|
|
223
|
+
return bound_arguments.arguments.get("search_parameters")
|
|
224
|
+
|
|
225
|
+
def _counter_snapshot(
|
|
226
|
+
self,
|
|
227
|
+
routing_model: pywrapcp.RoutingModel,
|
|
228
|
+
) -> tuple[float | None, float | None]:
|
|
229
|
+
"""The branch and failure counters for one Routing model."""
|
|
230
|
+
branches: float | None = None
|
|
231
|
+
try:
|
|
232
|
+
branches = float(routing_model.solver().Branches())
|
|
233
|
+
except _SDK_EXCEPTION:
|
|
234
|
+
branches = None
|
|
235
|
+
failures: float | None = None
|
|
236
|
+
try:
|
|
237
|
+
failures = float(routing_model.solver().Failures())
|
|
238
|
+
except _SDK_EXCEPTION:
|
|
239
|
+
failures = None
|
|
240
|
+
return branches, failures
|
|
241
|
+
|
|
242
|
+
def _extract(
|
|
243
|
+
self,
|
|
244
|
+
routing_model: pywrapcp.RoutingModel,
|
|
245
|
+
result: Any | None,
|
|
246
|
+
error: BaseException | None,
|
|
247
|
+
search_parameters: Any | None,
|
|
248
|
+
counter_snapshot: tuple[float | None, float | None],
|
|
249
|
+
) -> SolveObservation:
|
|
250
|
+
"""The plain observation values reported by one Routing solve."""
|
|
251
|
+
try:
|
|
252
|
+
return self._build_observation(
|
|
253
|
+
routing_model,
|
|
254
|
+
result,
|
|
255
|
+
error,
|
|
256
|
+
search_parameters,
|
|
257
|
+
counter_snapshot,
|
|
258
|
+
)
|
|
259
|
+
except ObservationError:
|
|
260
|
+
raise
|
|
261
|
+
except _SDK_EXCEPTION as extraction_error:
|
|
262
|
+
raise ObservationError(
|
|
263
|
+
"Could not read a Routing observation."
|
|
264
|
+
) from extraction_error
|
|
265
|
+
|
|
266
|
+
def _build_observation(
|
|
267
|
+
self,
|
|
268
|
+
routing_model: pywrapcp.RoutingModel,
|
|
269
|
+
result: Any | None,
|
|
270
|
+
error: BaseException | None,
|
|
271
|
+
search_parameters: Any | None,
|
|
272
|
+
counter_snapshot: tuple[float | None, float | None],
|
|
273
|
+
) -> SolveObservation:
|
|
274
|
+
"""The Routing solve observation."""
|
|
275
|
+
status_code: int | None = None
|
|
276
|
+
raw_status: str = "unknown"
|
|
277
|
+
termination: Termination = Termination.UNKNOWN
|
|
278
|
+
has_solution: bool | None = None
|
|
279
|
+
reported_result: Any | None = result
|
|
280
|
+
if error is not None:
|
|
281
|
+
termination = (
|
|
282
|
+
Termination.INTERRUPTED
|
|
283
|
+
if isinstance(error, KeyboardInterrupt)
|
|
284
|
+
else Termination.ERROR
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
try:
|
|
288
|
+
status_code = int(routing_model.status())
|
|
289
|
+
except _SDK_EXCEPTION:
|
|
290
|
+
status_code = None
|
|
291
|
+
if status_code is not None:
|
|
292
|
+
status_name: str
|
|
293
|
+
termination = Termination.UNKNOWN
|
|
294
|
+
status_name, termination = _STATUSES.get(
|
|
295
|
+
status_code, ("UNKNOWN", Termination.UNKNOWN)
|
|
296
|
+
)
|
|
297
|
+
raw_status = f"{status_name} ({status_code})"
|
|
298
|
+
has_solution = result is not None
|
|
299
|
+
if status_code in _FAILED_STATUS_CODES:
|
|
300
|
+
has_solution = False
|
|
301
|
+
reported_result = None
|
|
302
|
+
elif result is not None:
|
|
303
|
+
termination = (
|
|
304
|
+
Termination.OPTIMAL if status_code == 7 else Termination.FEASIBLE
|
|
305
|
+
)
|
|
306
|
+
if error is not None:
|
|
307
|
+
metrics: ReportedMetrics = ReportedMetrics()
|
|
308
|
+
else:
|
|
309
|
+
metrics = self._metrics(
|
|
310
|
+
routing_model,
|
|
311
|
+
reported_result,
|
|
312
|
+
counter_snapshot,
|
|
313
|
+
)
|
|
314
|
+
problem: dict[str, int] = {}
|
|
315
|
+
num_locations: int | None = None
|
|
316
|
+
try:
|
|
317
|
+
num_locations = int(routing_model.nodes())
|
|
318
|
+
except _SDK_EXCEPTION:
|
|
319
|
+
num_locations = None
|
|
320
|
+
if num_locations is not None:
|
|
321
|
+
problem["num_locations"] = num_locations
|
|
322
|
+
vehicle_count: int | None = None
|
|
323
|
+
try:
|
|
324
|
+
vehicle_count = int(routing_model.vehicles())
|
|
325
|
+
except _SDK_EXCEPTION:
|
|
326
|
+
vehicle_count = None
|
|
327
|
+
if vehicle_count is not None:
|
|
328
|
+
problem["num_vehicles"] = vehicle_count
|
|
329
|
+
parameters: list[SolverParameter] = self._reported_parameters(search_parameters)
|
|
330
|
+
api_version: str | None = None
|
|
331
|
+
try:
|
|
332
|
+
api_version = str(ortools.__version__)
|
|
333
|
+
except _SDK_EXCEPTION:
|
|
334
|
+
api_version = None
|
|
335
|
+
observation: SolveObservation = SolveObservation(
|
|
336
|
+
api=self.api,
|
|
337
|
+
outcome=ReportedOutcome(
|
|
338
|
+
raw_status,
|
|
339
|
+
termination,
|
|
340
|
+
has_solution,
|
|
341
|
+
),
|
|
342
|
+
metrics=metrics,
|
|
343
|
+
metadata=ReportedMetadata(
|
|
344
|
+
solver="routing",
|
|
345
|
+
api_version=api_version,
|
|
346
|
+
solver_version=api_version,
|
|
347
|
+
problem=problem,
|
|
348
|
+
parameters=parameters,
|
|
349
|
+
wall_time_includes_problem_build=False,
|
|
350
|
+
),
|
|
351
|
+
)
|
|
352
|
+
return observation
|
|
353
|
+
|
|
354
|
+
def _metrics(
|
|
355
|
+
self,
|
|
356
|
+
routing_model: pywrapcp.RoutingModel,
|
|
357
|
+
result: Any | None,
|
|
358
|
+
counter_snapshot: tuple[float | None, float | None],
|
|
359
|
+
) -> ReportedMetrics:
|
|
360
|
+
"""The Routing metrics reported for a successful solve call."""
|
|
361
|
+
objective_value: float | MissingReason
|
|
362
|
+
if result is None:
|
|
363
|
+
objective_value = MissingReason.NO_SOLUTION
|
|
364
|
+
else:
|
|
365
|
+
try:
|
|
366
|
+
objective_value = float(result.ObjectiveValue())
|
|
367
|
+
except _SDK_EXCEPTION:
|
|
368
|
+
objective_value = MissingReason.FAILED
|
|
369
|
+
solver_time_seconds: float | MissingReason = MissingReason.UNSUPPORTED
|
|
370
|
+
statistics: dict[str, float | MissingReason] = {}
|
|
371
|
+
native: dict[str, float] = {}
|
|
372
|
+
branches_before: float | None = counter_snapshot[0]
|
|
373
|
+
failures_before: float | None = counter_snapshot[1]
|
|
374
|
+
branches_after: float | None
|
|
375
|
+
failures_after: float | None
|
|
376
|
+
branches_after, failures_after = self._counter_snapshot(routing_model)
|
|
377
|
+
if branches_before is not None and branches_after is not None:
|
|
378
|
+
branches_elapsed: float = branches_after - branches_before
|
|
379
|
+
if branches_elapsed >= 0:
|
|
380
|
+
native["branches"] = branches_elapsed
|
|
381
|
+
if failures_before is not None and failures_after is not None:
|
|
382
|
+
failures_elapsed: float = failures_after - failures_before
|
|
383
|
+
if failures_elapsed >= 0:
|
|
384
|
+
native["failures"] = failures_elapsed
|
|
385
|
+
vehicle_count: int | None = None
|
|
386
|
+
try:
|
|
387
|
+
vehicle_count = int(routing_model.vehicles())
|
|
388
|
+
except _SDK_EXCEPTION:
|
|
389
|
+
vehicle_count = None
|
|
390
|
+
if result is not None and vehicle_count is not None:
|
|
391
|
+
vehicles_used: int = 0
|
|
392
|
+
vehicle_reads_succeeded: bool = True
|
|
393
|
+
for vehicle_index in range(vehicle_count):
|
|
394
|
+
try:
|
|
395
|
+
is_vehicle_used: bool = bool(
|
|
396
|
+
routing_model.IsVehicleUsed(result, vehicle_index)
|
|
397
|
+
)
|
|
398
|
+
except _SDK_EXCEPTION:
|
|
399
|
+
vehicle_reads_succeeded = False
|
|
400
|
+
continue
|
|
401
|
+
if is_vehicle_used:
|
|
402
|
+
vehicles_used += 1
|
|
403
|
+
if vehicle_reads_succeeded:
|
|
404
|
+
native["vehicles_used"] = float(vehicles_used)
|
|
405
|
+
return ReportedMetrics(
|
|
406
|
+
has_objective=True,
|
|
407
|
+
sense=ObjectiveSense.MINIMIZE,
|
|
408
|
+
objective_value=objective_value,
|
|
409
|
+
dual_bound=MissingReason.UNSUPPORTED,
|
|
410
|
+
solver_time_seconds=solver_time_seconds,
|
|
411
|
+
statistics=statistics,
|
|
412
|
+
native=native,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
def _reported_parameters(
|
|
416
|
+
self,
|
|
417
|
+
search_parameters: Any | None,
|
|
418
|
+
) -> list[SolverParameter]:
|
|
419
|
+
"""The explicit Routing search parameters reported for one call."""
|
|
420
|
+
parameters: list[SolverParameter] = []
|
|
421
|
+
if search_parameters is None:
|
|
422
|
+
return parameters
|
|
423
|
+
defaults: Any | None = None
|
|
424
|
+
try:
|
|
425
|
+
defaults = pywrapcp.DefaultRoutingSearchParameters()
|
|
426
|
+
except _SDK_EXCEPTION:
|
|
427
|
+
return parameters
|
|
428
|
+
parameters = _explicit_parameters(search_parameters, defaults)
|
|
429
|
+
return parameters
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mlflow-optimization
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automatic MLflow logging of mathematical optimization solves
|
|
5
|
+
Author: CoffeeGiNu
|
|
6
|
+
License-Expression: MIT OR Apache-2.0
|
|
7
|
+
License-File: LICENSE-APACHE
|
|
8
|
+
License-File: LICENSE-MIT
|
|
9
|
+
Requires-Dist: mlflow-skinny>=3.18.0
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Project-URL: Repository, https://github.com/CoffeeGiNu/mlflow-optimization
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# mlflow-optimization
|
|
15
|
+
|
|
16
|
+
Automatic MLflow logging of mathematical optimization solves.
|
|
17
|
+
|
|
18
|
+
Supported libraries: [gurobipy, PySCIPOpt, Pyomo, OR-Tools (MathOpt, CP-SAT, Routing)]
|
|
19
|
+
|
|
20
|
+
## install
|
|
21
|
+
|
|
22
|
+
Log to a tracking server (`MLFLOW_TRACKING_URI`):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install mlflow-optimization
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> [!NOTE]
|
|
29
|
+
> `mlflow-optimization` installs `mlflow-skinny`, which lacks MLflow's default local store. To log without a tracking server, install `mlflow` as well:
|
|
30
|
+
>
|
|
31
|
+
> ```bash
|
|
32
|
+
> pip install mlflow-optimization mlflow
|
|
33
|
+
> ```
|
|
34
|
+
|
|
35
|
+
## usage
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import mlflow_optimization
|
|
39
|
+
|
|
40
|
+
mlflow_optimization.autolog()
|
|
41
|
+
|
|
42
|
+
with mlflow_optimization.context(instance="knapsack-50", tags={"team": "ops"}):
|
|
43
|
+
model.optimize()
|
|
44
|
+
|
|
45
|
+
run = mlflow_optimization.last_solve()
|
|
46
|
+
if run is not None:
|
|
47
|
+
run.log_metric("gap_to_best_known", 0.02)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
Either of Apache License 2.0 or MIT license, at your option.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
mlflow_optimization/__init__.py,sha256=kxDiSW89nogx7q-p8PGXLFzhhuC8Uxg8WvI0bnuvZdc,1803
|
|
2
|
+
mlflow_optimization/_bootstrap.py,sha256=A75VvfRQvAQA3XWmxI_Ww3wkgGvKeMAr5qXKf8aDYx8,5094
|
|
3
|
+
mlflow_optimization/common/diagnostics.py,sha256=YYnGULBqMPB5WblnF2lxdLC89zbuqwJIzXfipKdTjxo,607
|
|
4
|
+
mlflow_optimization/common/patching.py,sha256=cy2hf0tKq9F_KBeW9VMaD5seWWeJFs_KPBPTByLslug,2818
|
|
5
|
+
mlflow_optimization/features/annotations/annotate.py,sha256=ePD0nLaNY4doM28RXkCO86PqucPkvsC0rPMhd0rCzQA,3494
|
|
6
|
+
mlflow_optimization/features/annotations/types.py,sha256=IWAkLb4OA2InBL5J6eP56nxTq4_QVKNi_d_KritjLFw,513
|
|
7
|
+
mlflow_optimization/features/artifacts/policy.py,sha256=V2WQ_738a4_iBg306_LYahwMiesUi9VAOBsC--S7cOI,570
|
|
8
|
+
mlflow_optimization/features/artifacts/ports.py,sha256=U1Og-zdDIw6biLxVLlwF7UQ0O2zZFNKCVO7vY5Oqqi0,409
|
|
9
|
+
mlflow_optimization/features/autolog/ports.py,sha256=6lxdrZL-qNjFf2cR-N4BzJq1Zehbs57way-ZfgLTCrs,1311
|
|
10
|
+
mlflow_optimization/features/autolog/service.py,sha256=_R_eTb8PHVmhpxdY6GtIQYGdSiKLCXxNkCERk0xfAcc,5448
|
|
11
|
+
mlflow_optimization/features/autolog/types.py,sha256=zMa15Jqs7e-PIUl7Z9yOpeNDMeoQ8BiauPIh3NtEsKQ,921
|
|
12
|
+
mlflow_optimization/features/metadata/collect.py,sha256=n-t18xI3D6zeUI1mbQdSwrgoGWvtc4AFpvX6gS7V7Zc,1976
|
|
13
|
+
mlflow_optimization/features/metadata/types.py,sha256=HtATxAUnWBv-WmvqPojxPtnipov6ac-Lc9306BKXQss,1135
|
|
14
|
+
mlflow_optimization/features/metrics/normalize.py,sha256=x32hbdg5DxmjawyC5a7P9nVNQGN1mmQS7QsqDsn6fss,4820
|
|
15
|
+
mlflow_optimization/features/metrics/types.py,sha256=IrXJFbHBRuJh1hKEp6AUGZfqM-Ydh7_LDAwosunXpwI,1490
|
|
16
|
+
mlflow_optimization/features/outcomes/classify.py,sha256=WNPha0pGZWFNTYNG2TF2JWt9HofNYIGhVncJmy01ofY,2144
|
|
17
|
+
mlflow_optimization/features/outcomes/types.py,sha256=aOm_HYSYznX9u2km3ogRp97MDhslWTp0DMsBLqXWfYo,956
|
|
18
|
+
mlflow_optimization/features/runs/ports.py,sha256=H_h4sekLIU8j7K5iKuISO3XhhPLXBMKD3D5dQdYIK8g,871
|
|
19
|
+
mlflow_optimization/features/runs/record.py,sha256=f3Wq3v1mzC8Wh8JN7dCpIMF-GjbSk3QFUOd2U7ou-58,8257
|
|
20
|
+
mlflow_optimization/features/runs/types.py,sha256=66P8NGthLcrBov9KD2hfGMWXFu2NXjCLQ5lQdOtPz9o,907
|
|
21
|
+
mlflow_optimization/integrations/cp_sat/__init__.py,sha256=kuf1ttQ_4EO3UPQIUbBq_G-fEIg0ICM_CPLqsxZxqUI,98
|
|
22
|
+
mlflow_optimization/integrations/cp_sat/adapter.py,sha256=v8THE0udqADGaxdtLQqa0VaK4I_eagnrtw9qDSJRxXQ,16736
|
|
23
|
+
mlflow_optimization/integrations/gurobipy/__init__.py,sha256=y0e7qyoHs2uVr-9whq_bfcoRa1sgp1ism2cyXEfe2D4,99
|
|
24
|
+
mlflow_optimization/integrations/gurobipy/adapter.py,sha256=t-vUJ1PPsUgR6j7UEg-7FTovBj_z6HWVY-BL6FS6uG0,16723
|
|
25
|
+
mlflow_optimization/integrations/mathopt/__init__.py,sha256=DYBecnlkXi2vb2HieuVZ5UEcRnisjAWPcsXNuKHEL6g,34
|
|
26
|
+
mlflow_optimization/integrations/mathopt/adapter.py,sha256=DwdwlBj2hkqKE2uiyFQh_95GmZsfgRbAcspMndTOYio,38035
|
|
27
|
+
mlflow_optimization/integrations/mlflow/artifacts.py,sha256=HTf8csrskx5gCVKDTmgWci7zUX_MuT6t8OGawN_Hjak,1323
|
|
28
|
+
mlflow_optimization/integrations/mlflow/runs.py,sha256=WqYuwcbBA9TcnP1Um2GDQl603VnYIZdRGDAogVjVotQ,16779
|
|
29
|
+
mlflow_optimization/integrations/pyomo/__init__.py,sha256=YX0WoaMbebdE5-DgPvvnsV2oSwX3uonfGwtCNnOBccY,95
|
|
30
|
+
mlflow_optimization/integrations/pyomo/adapter.py,sha256=mBWMr2oehxnqpRlvOwWOylq8crtV3l9LGSCL4QMtOvI,60712
|
|
31
|
+
mlflow_optimization/integrations/pyscipopt/__init__.py,sha256=cLh7p-Zk5AAKZAJaNRwsDjccztCY9qNO_hYxsPmJXQI,119
|
|
32
|
+
mlflow_optimization/integrations/pyscipopt/adapter.py,sha256=klJH3aIGxCI-OjYymJcMJRnfYK4L1mWsiKblSP0uDeI,23129
|
|
33
|
+
mlflow_optimization/integrations/routing/__init__.py,sha256=iHdDiuiFYL_PBsn0ztb-CWMXBvMK4xsHuvnTtn3LVoU,107
|
|
34
|
+
mlflow_optimization/integrations/routing/adapter.py,sha256=t0wzraXkUFoQF0-UT3dWLixzhyoE2I4FiI0psgdJrkw,15478
|
|
35
|
+
mlflow_optimization/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-APACHE,sha256=Yseh419WQGiW16p8pS0MwNJyrAIrXSeW59aQXbijY2o,9723
|
|
37
|
+
mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-MIT,sha256=Ox_Hk47qXBxtvKKcbjkhWgF6EmE3QfYyU25bKKhwN7E,1067
|
|
38
|
+
mlflow_optimization-0.1.0.dist-info/WHEEL,sha256=3NO1oYsF7mANFUWVdyEQLM-zmecczWWEmmF5rEbXrWk,80
|
|
39
|
+
mlflow_optimization-0.1.0.dist-info/METADATA,sha256=p4gUfATVTTFfkQOnTLJlyLZXOO9bD8snXgzUeVT-gl8,1290
|
|
40
|
+
mlflow_optimization-0.1.0.dist-info/RECORD,,
|