solverforge 0.6.9__tar.gz → 0.6.10__tar.gz
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.
- {solverforge-0.6.9 → solverforge-0.6.10}/Cargo.lock +1 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/Cargo.toml +1 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/PKG-INFO +5 -5
- {solverforge-0.6.9 → solverforge-0.6.10}/README.md +4 -4
- {solverforge-0.6.9 → solverforge-0.6.10}/pyproject.toml +5 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/__init__.py +1 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/_native.pyi +2 -6
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/config.py +1 -3
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/constraints.py +8 -25
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/decorators.py +2 -5
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/errors.py +0 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/events.py +0 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/fields.py +7 -22
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/groups.py +4 -13
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/model.py +6 -9
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/solver.py +1 -3
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/typing.py +0 -1
- {solverforge-0.6.9 → solverforge-0.6.10}/LICENSE +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/console.py +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/joiner.py +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/manager.py +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/py.typed +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/score.py +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/python/solverforge/ui.py +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/rust-toolchain.toml +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/bindings.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/callbacks/call.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/callbacks/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/callbacks/registry.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/callbacks/threading.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/config.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/compiled.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/evaluate.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/incremental.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/list_precedence.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/matches.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/state.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/constraints/stream_plan.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/descriptor/extractor.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/descriptor/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/descriptor/variable.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/error.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/intern.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/lib.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/manager/candidate_trace.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/manager/events.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/manager/jobs.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/manager/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/manager/provenance.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/proxy/entity.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/proxy/list.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/proxy/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/proxy/solution.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/candidate_metric.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/dynamic_assignment_group.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/dynamic_provider.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/list_slots.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/runtime/scalar_slots.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/build.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/compiled.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/parse.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/python.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/runtime_plan.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/types.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/schema/validate.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/score.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/api.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/console.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/progress.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/result.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/solver/solvable.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/callback_view.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/clone.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/entity_table.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/marshal.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/mod.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/solution.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/state/variables.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/ui.rs +0 -0
- {solverforge-0.6.9 → solverforge-0.6.10}/src/value.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solverforge
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.10
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -51,12 +51,12 @@ requirements, and other APIs that are not part of this package.
|
|
|
51
51
|
|
|
52
52
|
## Installation
|
|
53
53
|
|
|
54
|
-
The package version prepared by this checkout is `solverforge` `0.6.
|
|
54
|
+
The package version prepared by this checkout is `solverforge` `0.6.10`, targeting
|
|
55
55
|
the published and registry-locked SolverForge Rust `0.19.7` dependency base.
|
|
56
56
|
Install the matching package after its Python release with:
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
|
-
python3.14 -m pip install "solverforge==0.6.
|
|
59
|
+
python3.14 -m pip install "solverforge==0.6.10"
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
The installable wheel contains the core `solverforge` package, native extension,
|
|
@@ -76,12 +76,12 @@ make deliveries-run PORT=7861
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
The same source-checkout examples can be run against the matching published
|
|
79
|
-
package once `0.6.
|
|
79
|
+
package once `0.6.10` is released, without `PYTHONPATH` or an editable checkout:
|
|
80
80
|
|
|
81
81
|
```sh
|
|
82
82
|
python3.14 -m venv .venv-examples
|
|
83
83
|
. .venv-examples/bin/activate
|
|
84
|
-
python -m pip install "solverforge[examples]==0.6.
|
|
84
|
+
python -m pip install "solverforge[examples]==0.6.10"
|
|
85
85
|
python examples/nqueens.py
|
|
86
86
|
python -m examples.solverforge_hospital
|
|
87
87
|
python -m examples.solverforge_deliveries
|
|
@@ -20,12 +20,12 @@ requirements, and other APIs that are not part of this package.
|
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
|
-
The package version prepared by this checkout is `solverforge` `0.6.
|
|
23
|
+
The package version prepared by this checkout is `solverforge` `0.6.10`, targeting
|
|
24
24
|
the published and registry-locked SolverForge Rust `0.19.7` dependency base.
|
|
25
25
|
Install the matching package after its Python release with:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
python3.14 -m pip install "solverforge==0.6.
|
|
28
|
+
python3.14 -m pip install "solverforge==0.6.10"
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The installable wheel contains the core `solverforge` package, native extension,
|
|
@@ -45,12 +45,12 @@ make deliveries-run PORT=7861
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
The same source-checkout examples can be run against the matching published
|
|
48
|
-
package once `0.6.
|
|
48
|
+
package once `0.6.10` is released, without `PYTHONPATH` or an editable checkout:
|
|
49
49
|
|
|
50
50
|
```sh
|
|
51
51
|
python3.14 -m venv .venv-examples
|
|
52
52
|
. .venv-examples/bin/activate
|
|
53
|
-
python -m pip install "solverforge[examples]==0.6.
|
|
53
|
+
python -m pip install "solverforge[examples]==0.6.10"
|
|
54
54
|
python examples/nqueens.py
|
|
55
55
|
python -m examples.solverforge_hospital
|
|
56
56
|
python -m examples.solverforge_deliveries
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "solverforge"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.10"
|
|
8
8
|
description = "Dynamic Python bindings for the SolverForge constraint solver."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -51,6 +51,10 @@ strip = true
|
|
|
51
51
|
testpaths = ["tests/python"]
|
|
52
52
|
pythonpath = ["python"]
|
|
53
53
|
|
|
54
|
+
[tool.black]
|
|
55
|
+
line-length = 100
|
|
56
|
+
target-version = ["py314"]
|
|
57
|
+
|
|
54
58
|
[tool.ruff]
|
|
55
59
|
line-length = 100
|
|
56
60
|
target-version = "py314"
|
|
@@ -38,18 +38,14 @@ class SolverManager:
|
|
|
38
38
|
def _preflight_qualified_candidate_trace_provenance(
|
|
39
39
|
self,
|
|
40
40
|
*,
|
|
41
|
-
qualified_candidate_trace_provenance:
|
|
42
|
-
QualifiedCandidateTraceProvenance | None
|
|
43
|
-
) = None,
|
|
41
|
+
qualified_candidate_trace_provenance: QualifiedCandidateTraceProvenance | None = None,
|
|
44
42
|
) -> None: ...
|
|
45
43
|
def solve(
|
|
46
44
|
self,
|
|
47
45
|
solution: object,
|
|
48
46
|
schema: CompiledSchema,
|
|
49
47
|
*,
|
|
50
|
-
qualified_candidate_trace_provenance:
|
|
51
|
-
QualifiedCandidateTraceProvenance | None
|
|
52
|
-
) = None,
|
|
48
|
+
qualified_candidate_trace_provenance: QualifiedCandidateTraceProvenance | None = None,
|
|
53
49
|
) -> int: ...
|
|
54
50
|
def get_status(self, job_id: int) -> dict[str, object]: ...
|
|
55
51
|
def telemetry_detail(self, job_id: int) -> dict[str, object]: ...
|
|
@@ -162,9 +162,7 @@ def _normalize_phases(phases: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
|
162
162
|
dict(phase_copy["termination"])
|
|
163
163
|
).to_dict()
|
|
164
164
|
if "child_phases" in phase_copy:
|
|
165
|
-
phase_copy["child_phases"] = _normalize_phases(
|
|
166
|
-
list(phase_copy["child_phases"])
|
|
167
|
-
)
|
|
165
|
+
phase_copy["child_phases"] = _normalize_phases(list(phase_copy["child_phases"]))
|
|
168
166
|
normalized.append(phase_copy)
|
|
169
167
|
return normalized
|
|
170
168
|
|
|
@@ -163,10 +163,7 @@ class ListPrecedenceMakespanConstraintStream:
|
|
|
163
163
|
if plan.precedence_duration is None and plan.precedence_duration_field is None:
|
|
164
164
|
msg = f"{self.entity_type.__name__}.{self.variable_name} requires precedence_duration"
|
|
165
165
|
raise TypeError(msg)
|
|
166
|
-
if
|
|
167
|
-
plan.precedence_successors is None
|
|
168
|
-
and plan.precedence_successors_field is None
|
|
169
|
-
):
|
|
166
|
+
if plan.precedence_successors is None and plan.precedence_successors_field is None:
|
|
170
167
|
msg = f"{self.entity_type.__name__}.{self.variable_name} requires precedence_successors"
|
|
171
168
|
raise TypeError(msg)
|
|
172
169
|
if plan.element_owner is None and plan.element_owner_field is None:
|
|
@@ -382,9 +379,7 @@ class ConstraintFactory:
|
|
|
382
379
|
self.score_family = score_family
|
|
383
380
|
|
|
384
381
|
def for_each(self, entity_type: type[object]) -> UniConstraintStream:
|
|
385
|
-
return UniConstraintStream(
|
|
386
|
-
entity_type=entity_type, score_family=self.score_family
|
|
387
|
-
)
|
|
382
|
+
return UniConstraintStream(entity_type=entity_type, score_family=self.score_family)
|
|
388
383
|
|
|
389
384
|
def for_each_unassigned_element(
|
|
390
385
|
self,
|
|
@@ -409,14 +404,10 @@ class ConstraintFactory:
|
|
|
409
404
|
)
|
|
410
405
|
|
|
411
406
|
def join(self, *_args: Any, **_kwargs: Any) -> None:
|
|
412
|
-
raise NotImplementedError(
|
|
413
|
-
"dynamic join is implemented in the native stream planner"
|
|
414
|
-
)
|
|
407
|
+
raise NotImplementedError("dynamic join is implemented in the native stream planner")
|
|
415
408
|
|
|
416
409
|
def if_exists(self, *_args: Any, **_kwargs: Any) -> None:
|
|
417
|
-
raise NotImplementedError(
|
|
418
|
-
"dynamic if_exists is implemented in the native stream planner"
|
|
419
|
-
)
|
|
410
|
+
raise NotImplementedError("dynamic if_exists is implemented in the native stream planner")
|
|
420
411
|
|
|
421
412
|
def if_not_exists(self, *_args: Any, **_kwargs: Any) -> None:
|
|
422
413
|
raise NotImplementedError(
|
|
@@ -424,14 +415,10 @@ class ConstraintFactory:
|
|
|
424
415
|
)
|
|
425
416
|
|
|
426
417
|
def group_by(self, *_args: Any, **_kwargs: Any) -> None:
|
|
427
|
-
raise NotImplementedError(
|
|
428
|
-
"dynamic group_by is implemented in the native stream planner"
|
|
429
|
-
)
|
|
418
|
+
raise NotImplementedError("dynamic group_by is implemented in the native stream planner")
|
|
430
419
|
|
|
431
420
|
def flattened(self, *_args: Any, **_kwargs: Any) -> None:
|
|
432
|
-
raise NotImplementedError(
|
|
433
|
-
"dynamic flattened is implemented in the native stream planner"
|
|
434
|
-
)
|
|
421
|
+
raise NotImplementedError("dynamic flattened is implemented in the native stream planner")
|
|
435
422
|
|
|
436
423
|
|
|
437
424
|
def joiner_to_native(joiner: object) -> object:
|
|
@@ -462,9 +449,7 @@ def collector_to_native(collector: object) -> object:
|
|
|
462
449
|
return collector
|
|
463
450
|
|
|
464
451
|
|
|
465
|
-
def _list_variable_element_collection(
|
|
466
|
-
entity_type: type[object], variable_name: str
|
|
467
|
-
) -> str:
|
|
452
|
+
def _list_variable_element_collection(entity_type: type[object], variable_name: str) -> str:
|
|
468
453
|
field_info = _list_variable_metadata(entity_type, variable_name)
|
|
469
454
|
collection = field_info.get("element_collection")
|
|
470
455
|
if isinstance(collection, str) and collection:
|
|
@@ -473,9 +458,7 @@ def _list_variable_element_collection(
|
|
|
473
458
|
raise TypeError(msg)
|
|
474
459
|
|
|
475
460
|
|
|
476
|
-
def _list_variable_metadata(
|
|
477
|
-
entity_type: type[object], variable_name: str
|
|
478
|
-
) -> dict[str, object]:
|
|
461
|
+
def _list_variable_metadata(entity_type: type[object], variable_name: str) -> dict[str, object]:
|
|
479
462
|
metadata = getattr(entity_type, "__solverforge_entity__", None)
|
|
480
463
|
if not isinstance(metadata, dict):
|
|
481
464
|
msg = f"{entity_type.__name__} is not marked with @planning_entity"
|
|
@@ -143,8 +143,7 @@ def problem_fact(cls: T) -> T:
|
|
|
143
143
|
fields = _collect_fields(cls)
|
|
144
144
|
if any(field["kind"] == "planning_pin" for field in fields):
|
|
145
145
|
msg = (
|
|
146
|
-
f"{cls.__name__} is a problem fact; planning_pin is only valid on a "
|
|
147
|
-
"planning entity"
|
|
146
|
+
f"{cls.__name__} is a problem fact; planning_pin is only valid on a " "planning entity"
|
|
148
147
|
)
|
|
149
148
|
raise ModelValidationError(msg)
|
|
150
149
|
setattr(
|
|
@@ -220,9 +219,7 @@ def planning_solution(
|
|
|
220
219
|
shadow_variable_updates: dict[str, Any] | list[dict[str, Any]] | None = None,
|
|
221
220
|
) -> Callable[[T], T]:
|
|
222
221
|
def decorate(cls: T) -> T:
|
|
223
|
-
updates =
|
|
224
|
-
shadow_updates if shadow_updates is not None else shadow_variable_updates
|
|
225
|
-
)
|
|
222
|
+
updates = shadow_updates if shadow_updates is not None else shadow_variable_updates
|
|
226
223
|
if updates is None:
|
|
227
224
|
update_list: list[dict[str, Any]] = []
|
|
228
225
|
elif isinstance(updates, dict):
|
|
@@ -59,9 +59,7 @@ class SolutionCallback:
|
|
|
59
59
|
raise TypeError("SolutionCallback callback must be callable")
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
ListValueSource: TypeAlias =
|
|
63
|
-
RowField | SolutionField | EntityCallback | SolutionCallback
|
|
64
|
-
)
|
|
62
|
+
ListValueSource: TypeAlias = RowField | SolutionField | EntityCallback | SolutionCallback
|
|
65
63
|
ListFeasibilityFieldSource: TypeAlias = RowField | SolutionField
|
|
66
64
|
|
|
67
65
|
|
|
@@ -74,14 +72,10 @@ class CapacityRouteFeasibility:
|
|
|
74
72
|
|
|
75
73
|
def __post_init__(self) -> None:
|
|
76
74
|
if self.capacity is None or self.demand is None:
|
|
77
|
-
raise TypeError(
|
|
78
|
-
"CapacityRouteFeasibility requires capacity and demand sources"
|
|
79
|
-
)
|
|
75
|
+
raise TypeError("CapacityRouteFeasibility requires capacity and demand sources")
|
|
80
76
|
|
|
81
77
|
|
|
82
|
-
ListFeasibilitySource: TypeAlias =
|
|
83
|
-
EntityCallback | SolutionCallback | CapacityRouteFeasibility
|
|
84
|
-
)
|
|
78
|
+
ListFeasibilitySource: TypeAlias = EntityCallback | SolutionCallback | CapacityRouteFeasibility
|
|
85
79
|
|
|
86
80
|
|
|
87
81
|
@dataclass(frozen=True)
|
|
@@ -94,9 +88,7 @@ class ListRouteHooks:
|
|
|
94
88
|
|
|
95
89
|
def __post_init__(self) -> None:
|
|
96
90
|
if self.depot is None or self.distance is None or self.feasible is None:
|
|
97
|
-
raise TypeError(
|
|
98
|
-
"ListRouteHooks requires depot, distance, and feasible sources"
|
|
99
|
-
)
|
|
91
|
+
raise TypeError("ListRouteHooks requires depot, distance, and feasible sources")
|
|
100
92
|
|
|
101
93
|
|
|
102
94
|
@dataclass(frozen=True)
|
|
@@ -403,9 +395,7 @@ def _resolve_list_value_source(
|
|
|
403
395
|
solution_arity,
|
|
404
396
|
)
|
|
405
397
|
return source
|
|
406
|
-
raise TypeError(
|
|
407
|
-
f"{name} must be RowField, SolutionField, EntityCallback, or SolutionCallback"
|
|
408
|
-
)
|
|
398
|
+
raise TypeError(f"{name} must be RowField, SolutionField, EntityCallback, or SolutionCallback")
|
|
409
399
|
|
|
410
400
|
|
|
411
401
|
def _resolve_required_list_value_source(
|
|
@@ -455,9 +445,7 @@ def _resolve_list_feasibility_source(
|
|
|
455
445
|
solution_arity,
|
|
456
446
|
)
|
|
457
447
|
return source
|
|
458
|
-
raise TypeError(
|
|
459
|
-
f"{name} must be EntityCallback, SolutionCallback, or CapacityRouteFeasibility"
|
|
460
|
-
)
|
|
448
|
+
raise TypeError(f"{name} must be EntityCallback, SolutionCallback, or CapacityRouteFeasibility")
|
|
461
449
|
|
|
462
450
|
|
|
463
451
|
def _resolve_list_feasibility_field_source(
|
|
@@ -501,10 +489,7 @@ def _callback_accepts_arity(callback: Callable[..., object], arity: int) -> bool
|
|
|
501
489
|
required_positional_count += 1
|
|
502
490
|
elif parameter.kind is Parameter.VAR_POSITIONAL:
|
|
503
491
|
accepts_extra_positionals = True
|
|
504
|
-
elif
|
|
505
|
-
parameter.kind is Parameter.KEYWORD_ONLY
|
|
506
|
-
and parameter.default is Parameter.empty
|
|
507
|
-
):
|
|
492
|
+
elif parameter.kind is Parameter.KEYWORD_ONLY and parameter.default is Parameter.empty:
|
|
508
493
|
return False
|
|
509
494
|
|
|
510
495
|
if arity < required_positional_count:
|
|
@@ -89,17 +89,10 @@ class ScalarAssignmentGroup:
|
|
|
89
89
|
),
|
|
90
90
|
):
|
|
91
91
|
if callback is not None and field is not None:
|
|
92
|
-
raise TypeError(
|
|
93
|
-
f"{callback_name} and {field_name} cannot both be configured"
|
|
94
|
-
)
|
|
92
|
+
raise TypeError(f"{callback_name} and {field_name} cannot both be configured")
|
|
95
93
|
if field is not None and (not isinstance(field, str) or not field):
|
|
96
|
-
raise TypeError(
|
|
97
|
-
|
|
98
|
-
)
|
|
99
|
-
if (
|
|
100
|
-
self.assignment_rule is not None
|
|
101
|
-
and self.same_value_conflict_field is not None
|
|
102
|
-
):
|
|
94
|
+
raise TypeError(f"{field_name} must be a non-empty string when provided")
|
|
95
|
+
if self.assignment_rule is not None and self.same_value_conflict_field is not None:
|
|
103
96
|
raise TypeError(
|
|
104
97
|
"assignment_rule and same_value_conflict_field cannot both be configured"
|
|
105
98
|
)
|
|
@@ -107,9 +100,7 @@ class ScalarAssignmentGroup:
|
|
|
107
100
|
not isinstance(self.same_value_conflict_field, str)
|
|
108
101
|
or not self.same_value_conflict_field
|
|
109
102
|
):
|
|
110
|
-
raise TypeError(
|
|
111
|
-
"same_value_conflict_field must be a non-empty string when provided"
|
|
112
|
-
)
|
|
103
|
+
raise TypeError("same_value_conflict_field must be a non-empty string when provided")
|
|
113
104
|
|
|
114
105
|
def to_native(self) -> dict[str, Any]:
|
|
115
106
|
return {
|
|
@@ -90,9 +90,7 @@ def _schema_shape(value: object) -> tuple[SchemaShape, bool]:
|
|
|
90
90
|
|
|
91
91
|
def shape(item: object, *, callback_state: bool = False) -> SchemaShape:
|
|
92
92
|
nonlocal cacheable, next_marker
|
|
93
|
-
recursive = isinstance(
|
|
94
|
-
item, (dict, list, tuple, FunctionType, MethodType, partial)
|
|
95
|
-
)
|
|
93
|
+
recursive = isinstance(item, (dict, list, tuple, FunctionType, MethodType, partial))
|
|
96
94
|
item_id = id(item)
|
|
97
95
|
if recursive:
|
|
98
96
|
if item_id in active:
|
|
@@ -147,8 +145,7 @@ def _schema_shape(value: object) -> tuple[SchemaShape, bool]:
|
|
|
147
145
|
for cell in (item.__closure__ or ())
|
|
148
146
|
)
|
|
149
147
|
defaults = tuple(
|
|
150
|
-
shape(default, callback_state=True)
|
|
151
|
-
for default in (item.__defaults__ or ())
|
|
148
|
+
shape(default, callback_state=True) for default in (item.__defaults__ or ())
|
|
152
149
|
)
|
|
153
150
|
keyword_defaults = tuple(
|
|
154
151
|
(name, shape(default, callback_state=True))
|
|
@@ -241,7 +238,9 @@ def _scalar_groups(callbacks: object) -> list[dict[str, object]]:
|
|
|
241
238
|
callback = cast(Callable[..., object], item)
|
|
242
239
|
metadata = getattr(callback, "__solverforge_scalar_group__", None)
|
|
243
240
|
if not isinstance(metadata, dict):
|
|
244
|
-
msg =
|
|
241
|
+
msg = (
|
|
242
|
+
f"{callback!r} is not marked with @scalar_group and is not a ScalarAssignmentGroup"
|
|
243
|
+
)
|
|
245
244
|
raise ModelValidationError(msg)
|
|
246
245
|
name = metadata.get("name")
|
|
247
246
|
if not isinstance(name, str) or not name:
|
|
@@ -262,9 +261,7 @@ def _conflict_repairs(callbacks: object) -> list[dict[str, object]]:
|
|
|
262
261
|
if not isinstance(metadata, dict):
|
|
263
262
|
msg = f"{callback!r} is not marked with @conflict_repair"
|
|
264
263
|
raise ModelValidationError(msg)
|
|
265
|
-
constraint_names = list(
|
|
266
|
-
cast(Iterable[object], metadata.get("constraints") or [])
|
|
267
|
-
)
|
|
264
|
+
constraint_names = list(cast(Iterable[object], metadata.get("constraints") or []))
|
|
268
265
|
if not constraint_names or not all(
|
|
269
266
|
isinstance(name, str) and name for name in constraint_names
|
|
270
267
|
):
|
|
@@ -9,9 +9,7 @@ from .model import _compiled_schema_for_solution
|
|
|
9
9
|
|
|
10
10
|
class Solver:
|
|
11
11
|
@staticmethod
|
|
12
|
-
def solve(
|
|
13
|
-
solution: object, config: SolverConfig | dict[str, Any] | None = None
|
|
14
|
-
) -> object:
|
|
12
|
+
def solve(solution: object, config: SolverConfig | dict[str, Any] | None = None) -> object:
|
|
15
13
|
schema = _compiled_schema_for_solution(solution)
|
|
16
14
|
return _native.solve(solution, schema, _resolve_config(config))
|
|
17
15
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|