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.
Files changed (40) hide show
  1. mlflow_optimization/__init__.py +50 -0
  2. mlflow_optimization/_bootstrap.py +129 -0
  3. mlflow_optimization/common/diagnostics.py +19 -0
  4. mlflow_optimization/common/patching.py +82 -0
  5. mlflow_optimization/features/annotations/annotate.py +97 -0
  6. mlflow_optimization/features/annotations/types.py +22 -0
  7. mlflow_optimization/features/artifacts/policy.py +16 -0
  8. mlflow_optimization/features/artifacts/ports.py +17 -0
  9. mlflow_optimization/features/autolog/ports.py +49 -0
  10. mlflow_optimization/features/autolog/service.py +147 -0
  11. mlflow_optimization/features/autolog/types.py +31 -0
  12. mlflow_optimization/features/metadata/collect.py +52 -0
  13. mlflow_optimization/features/metadata/types.py +42 -0
  14. mlflow_optimization/features/metrics/normalize.py +121 -0
  15. mlflow_optimization/features/metrics/types.py +54 -0
  16. mlflow_optimization/features/outcomes/classify.py +53 -0
  17. mlflow_optimization/features/outcomes/types.py +40 -0
  18. mlflow_optimization/features/runs/ports.py +30 -0
  19. mlflow_optimization/features/runs/record.py +231 -0
  20. mlflow_optimization/features/runs/types.py +43 -0
  21. mlflow_optimization/integrations/cp_sat/__init__.py +5 -0
  22. mlflow_optimization/integrations/cp_sat/adapter.py +464 -0
  23. mlflow_optimization/integrations/gurobipy/__init__.py +5 -0
  24. mlflow_optimization/integrations/gurobipy/adapter.py +474 -0
  25. mlflow_optimization/integrations/mathopt/__init__.py +1 -0
  26. mlflow_optimization/integrations/mathopt/adapter.py +939 -0
  27. mlflow_optimization/integrations/mlflow/artifacts.py +44 -0
  28. mlflow_optimization/integrations/mlflow/runs.py +480 -0
  29. mlflow_optimization/integrations/pyomo/__init__.py +5 -0
  30. mlflow_optimization/integrations/pyomo/adapter.py +1499 -0
  31. mlflow_optimization/integrations/pyscipopt/__init__.py +5 -0
  32. mlflow_optimization/integrations/pyscipopt/adapter.py +657 -0
  33. mlflow_optimization/integrations/routing/__init__.py +5 -0
  34. mlflow_optimization/integrations/routing/adapter.py +429 -0
  35. mlflow_optimization/py.typed +0 -0
  36. mlflow_optimization-0.1.0.dist-info/METADATA +52 -0
  37. mlflow_optimization-0.1.0.dist-info/RECORD +40 -0
  38. mlflow_optimization-0.1.0.dist-info/WHEEL +4 -0
  39. mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-APACHE +176 -0
  40. 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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.13.6
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any