dabke 0.83.0 → 0.84.0
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.
- package/CHANGELOG.md +21 -0
- package/README.md +22 -27
- package/dist/client.d.ts +39 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/client.schemas.d.ts +118 -18
- package/dist/client.schemas.d.ts.map +1 -1
- package/dist/client.schemas.js +110 -6
- package/dist/client.schemas.js.map +1 -1
- package/dist/client.types.d.ts +63 -7
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/cost.d.ts +5 -2
- package/dist/cpsat/cost.d.ts.map +1 -1
- package/dist/cpsat/cost.js +13 -6
- package/dist/cpsat/cost.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +42 -61
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +451 -56
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +19 -7
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +48 -9
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rule-descriptor.d.ts +250 -0
- package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
- package/dist/cpsat/rule-descriptor.js +51 -0
- package/dist/cpsat/rule-descriptor.js.map +1 -0
- package/dist/cpsat/rules/artifacts.d.ts +9 -0
- package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
- package/dist/cpsat/rules/artifacts.js +31 -0
- package/dist/cpsat/rules/artifacts.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +18 -20
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
- package/dist/cpsat/rules/assign-together.js +90 -54
- package/dist/cpsat/rules/assign-together.js.map +1 -1
- package/dist/cpsat/rules/assigned-day.d.ts +12 -0
- package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
- package/dist/cpsat/rules/assigned-day.js +43 -0
- package/dist/cpsat/rules/assigned-day.js.map +1 -0
- package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
- package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
- package/dist/cpsat/rules/assignment-priority.js +47 -50
- package/dist/cpsat/rules/assignment-priority.js.map +1 -1
- package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
- package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
- package/dist/cpsat/rules/assignment-terms.js +19 -0
- package/dist/cpsat/rules/assignment-terms.js.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.js +57 -0
- package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
- package/dist/cpsat/rules/cost-utils.d.ts +1 -3
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
- package/dist/cpsat/rules/cost-utils.js +0 -7
- package/dist/cpsat/rules/cost-utils.js.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +30 -24
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +30 -24
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/location-preference.d.ts +14 -21
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
- package/dist/cpsat/rules/location-preference.js +45 -45
- package/dist/cpsat/rules/location-preference.js.map +1 -1
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +67 -66
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/max-days-week.d.ts +17 -31
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-days-week.js +69 -81
- package/dist/cpsat/rules/max-days-week.js.map +1 -1
- package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +56 -72
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +60 -75
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +57 -73
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +123 -94
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/min-days-week.d.ts +13 -26
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-days-week.js +85 -74
- package/dist/cpsat/rules/min-days-week.js.map +1 -1
- package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +72 -54
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +75 -57
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +151 -163
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +5 -42
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -1
- package/dist/cpsat/rules/must-assign.js +43 -75
- package/dist/cpsat/rules/must-assign.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
- package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-eligibility.js +16 -0
- package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
- package/dist/cpsat/rules/pattern-time.d.ts +5 -0
- package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-time.js +20 -0
- package/dist/cpsat/rules/pattern-time.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +217 -30
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +98 -34
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.d.ts +9 -7
- package/dist/cpsat/rules/resolver.d.ts.map +1 -1
- package/dist/cpsat/rules/resolver.js +19 -18
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/role-preference.d.ts +24 -0
- package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/role-preference.js +72 -0
- package/dist/cpsat/rules/role-preference.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +90 -17
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +24 -11
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +14 -19
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/target-days-week.d.ts +31 -0
- package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/target-days-week.js +80 -0
- package/dist/cpsat/rules/target-days-week.js.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts +21 -57
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +114 -127
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/rules/variables.d.ts +4 -0
- package/dist/cpsat/rules/variables.d.ts.map +1 -0
- package/dist/cpsat/rules/variables.js +10 -0
- package/dist/cpsat/rules/variables.js.map +1 -0
- package/dist/cpsat/rules.d.ts +20 -3
- package/dist/cpsat/rules.d.ts.map +1 -1
- package/dist/cpsat/rules.js +19 -2
- package/dist/cpsat/rules.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +7 -6
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +15 -28
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +47 -4
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/types.js +24 -1
- package/dist/cpsat/types.js.map +1 -1
- package/dist/cpsat/utils.d.ts +23 -6
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +76 -15
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +6 -2
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +122 -10
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +21 -10
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/datetime.utils.d.ts +7 -13
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +29 -36
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +48 -60
- package/dist/schedule/cost.d.ts.map +1 -1
- package/dist/schedule/cost.js +15 -51
- package/dist/schedule/cost.js.map +1 -1
- package/dist/schedule/coverage.d.ts +57 -28
- package/dist/schedule/coverage.d.ts.map +1 -1
- package/dist/schedule/coverage.js +24 -1
- package/dist/schedule/coverage.js.map +1 -1
- package/dist/schedule/definition.d.ts +138 -19
- package/dist/schedule/definition.d.ts.map +1 -1
- package/dist/schedule/definition.js +99 -90
- package/dist/schedule/definition.js.map +1 -1
- package/dist/schedule/index.d.ts +4 -4
- package/dist/schedule/index.d.ts.map +1 -1
- package/dist/schedule/index.js +2 -2
- package/dist/schedule/index.js.map +1 -1
- package/dist/schedule/rules.d.ts +438 -113
- package/dist/schedule/rules.d.ts.map +1 -1
- package/dist/schedule/rules.js +310 -108
- package/dist/schedule/rules.js.map +1 -1
- package/dist/schedule/shift-patterns.d.ts +0 -17
- package/dist/schedule/shift-patterns.d.ts.map +1 -1
- package/dist/schedule/shift-patterns.js +0 -17
- package/dist/schedule/shift-patterns.js.map +1 -1
- package/dist/schedule/time-periods.d.ts +37 -27
- package/dist/schedule/time-periods.d.ts.map +1 -1
- package/dist/schedule/time-periods.js +37 -27
- package/dist/schedule/time-periods.js.map +1 -1
- package/dist/types.d.ts +67 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +64 -0
- package/dist/types.js.map +1 -1
- package/package.json +8 -9
- package/solver/src/solver/models.py +41 -4
- package/solver/src/solver/solver.py +546 -157
- package/solver/tests/test_solver.py +363 -7
- package/src/client.schemas.ts +127 -10
- package/src/client.ts +3 -0
- package/src/client.types.ts +73 -7
- package/src/cpsat/cost.ts +17 -7
- package/src/cpsat/model-builder.ts +614 -129
- package/src/cpsat/response.ts +57 -13
- package/src/cpsat/rule-descriptor.ts +311 -0
- package/src/cpsat/rules/artifacts.ts +56 -0
- package/src/cpsat/rules/assign-together.ts +104 -69
- package/src/cpsat/rules/assigned-day.ts +70 -0
- package/src/cpsat/rules/assignment-priority.ts +54 -58
- package/src/cpsat/rules/assignment-terms.ts +43 -0
- package/src/cpsat/rules/concurrent-intervals.ts +93 -0
- package/src/cpsat/rules/cost-utils.ts +1 -9
- package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
- package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
- package/src/cpsat/rules/index.ts +30 -24
- package/src/cpsat/rules/location-preference.ts +47 -52
- package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
- package/src/cpsat/rules/max-consecutive-days.ts +74 -91
- package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
- package/src/cpsat/rules/max-days-week.ts +89 -115
- package/src/cpsat/rules/max-hours-day.ts +64 -84
- package/src/cpsat/rules/max-hours-week.ts +74 -90
- package/src/cpsat/rules/max-shifts-day.ts +65 -85
- package/src/cpsat/rules/min-consecutive-days.ts +161 -127
- package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
- package/src/cpsat/rules/min-days-week.ts +93 -91
- package/src/cpsat/rules/min-hours-day.ts +79 -58
- package/src/cpsat/rules/min-hours-week.ts +87 -62
- package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
- package/src/cpsat/rules/minimize-cost.ts +175 -188
- package/src/cpsat/rules/must-assign.ts +44 -81
- package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
- package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
- package/src/cpsat/rules/pattern-eligibility.ts +26 -0
- package/src/cpsat/rules/pattern-time.ts +30 -0
- package/src/cpsat/rules/registry.ts +140 -62
- package/src/cpsat/rules/resolver.ts +58 -30
- package/src/cpsat/rules/role-preference.ts +92 -0
- package/src/cpsat/rules/rules.types.ts +89 -17
- package/src/cpsat/rules/scope.types.ts +31 -26
- package/src/cpsat/rules/target-days-week.ts +105 -0
- package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
- package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
- package/src/cpsat/rules/time-off.ts +154 -163
- package/src/cpsat/rules/variables.ts +11 -0
- package/src/cpsat/rules.ts +43 -17
- package/src/cpsat/semantic-time.ts +29 -43
- package/src/cpsat/types.ts +53 -4
- package/src/cpsat/utils.ts +92 -20
- package/src/cpsat/validation-reporter.ts +170 -19
- package/src/cpsat/validation.types.ts +22 -11
- package/src/datetime.utils.ts +45 -44
- package/src/index.ts +64 -12
- package/src/schedule/cost.ts +40 -61
- package/src/schedule/coverage.ts +57 -28
- package/src/schedule/definition.ts +565 -76
- package/src/schedule/index.ts +23 -3
- package/src/schedule/rules.ts +631 -132
- package/src/schedule/shift-patterns.ts +0 -17
- package/src/schedule/time-periods.ts +37 -27
- package/src/types.ts +114 -2
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from solver.models import (
|
|
4
|
+
Constraint,
|
|
5
|
+
Objective,
|
|
6
|
+
Options,
|
|
7
|
+
SolverObjectiveStage,
|
|
8
|
+
SolverRequest,
|
|
9
|
+
SolverStageResult,
|
|
10
|
+
Term,
|
|
11
|
+
Variable,
|
|
12
|
+
)
|
|
13
|
+
from solver.solver import _aggregate_stage_status, solve_request
|
|
3
14
|
|
|
4
15
|
|
|
5
16
|
def test_linear_feasible_solution():
|
|
@@ -134,11 +145,11 @@ def test_soft_constraint_reports_violation_when_id_provided():
|
|
|
134
145
|
response = solve_request(request)
|
|
135
146
|
|
|
136
147
|
assert response.status == "OPTIMAL"
|
|
137
|
-
assert response.
|
|
138
|
-
assert response.
|
|
139
|
-
assert response.
|
|
140
|
-
assert response.
|
|
141
|
-
assert response.
|
|
148
|
+
assert response.softConstraintViolations is not None
|
|
149
|
+
assert response.softConstraintViolations[0].constraintId == "coverage:test:2024-02-01:540"
|
|
150
|
+
assert response.softConstraintViolations[0].violationAmount == 1
|
|
151
|
+
assert response.softConstraintViolations[0].targetValue == 1
|
|
152
|
+
assert response.softConstraintViolations[0].actualValue == 0
|
|
142
153
|
|
|
143
154
|
|
|
144
155
|
def test_infeasible_solution_reports_solution_info():
|
|
@@ -154,3 +165,348 @@ def test_infeasible_solution_reports_solution_info():
|
|
|
154
165
|
|
|
155
166
|
assert response.status == "INFEASIBLE"
|
|
156
167
|
assert response.solutionInfo is not None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_infeasible_solution_reports_tracked_constraint_ids():
|
|
171
|
+
request = SolverRequest(
|
|
172
|
+
variables=[Variable(type="bool", name="x")],
|
|
173
|
+
options=Options(diagnostics="hard"),
|
|
174
|
+
constraints=[
|
|
175
|
+
Constraint(
|
|
176
|
+
type="linear",
|
|
177
|
+
terms=[Term(var="x", coeff=1)],
|
|
178
|
+
op="==",
|
|
179
|
+
rhs=0,
|
|
180
|
+
id="must-be-zero",
|
|
181
|
+
),
|
|
182
|
+
Constraint(
|
|
183
|
+
type="linear",
|
|
184
|
+
terms=[Term(var="x", coeff=1)],
|
|
185
|
+
op="==",
|
|
186
|
+
rhs=1,
|
|
187
|
+
id="must-be-one",
|
|
188
|
+
),
|
|
189
|
+
],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
response = solve_request(request)
|
|
193
|
+
|
|
194
|
+
assert response.status == "INFEASIBLE"
|
|
195
|
+
assert response.hardConstraintConflicts is not None
|
|
196
|
+
assert {conflict.constraintId for conflict in response.hardConstraintConflicts} == {
|
|
197
|
+
"must-be-zero",
|
|
198
|
+
"must-be-one",
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_infeasible_solution_omits_hard_conflicts_by_default():
|
|
203
|
+
request = SolverRequest(
|
|
204
|
+
variables=[Variable(type="bool", name="x")],
|
|
205
|
+
constraints=[
|
|
206
|
+
Constraint(type="linear", terms=[Term(var="x", coeff=1)], op="==", rhs=0, id="zero"),
|
|
207
|
+
Constraint(type="linear", terms=[Term(var="x", coeff=1)], op="==", rhs=1, id="one"),
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
response = solve_request(request)
|
|
212
|
+
|
|
213
|
+
assert response.status == "INFEASIBLE"
|
|
214
|
+
assert response.hardConstraintConflicts is None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_lexicographic_minimization_freezes_stage_1_before_stage_2():
|
|
218
|
+
request = SolverRequest(
|
|
219
|
+
variables=[Variable(type="bool", name="x")],
|
|
220
|
+
constraints=[],
|
|
221
|
+
objectiveStages=[
|
|
222
|
+
SolverObjectiveStage(id="minimize-x", sense="minimize", terms=[Term(var="x", coeff=1)]),
|
|
223
|
+
SolverObjectiveStage(id="maximize-x", sense="maximize", terms=[Term(var="x", coeff=1)]),
|
|
224
|
+
],
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
response = solve_request(request)
|
|
228
|
+
|
|
229
|
+
assert response.status == "OPTIMAL"
|
|
230
|
+
assert response.values == {"x": 0}
|
|
231
|
+
assert response.stageResults is not None
|
|
232
|
+
assert [stage.id for stage in response.stageResults] == ["minimize-x", "maximize-x"]
|
|
233
|
+
assert [stage.objectiveValue for stage in response.stageResults] == [0, 0]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_lexicographic_maximization_freezes_stage_1_before_stage_2():
|
|
237
|
+
request = SolverRequest(
|
|
238
|
+
variables=[Variable(type="bool", name="x")],
|
|
239
|
+
constraints=[],
|
|
240
|
+
objectiveStages=[
|
|
241
|
+
SolverObjectiveStage(id="maximize-x", sense="maximize", terms=[Term(var="x", coeff=1)]),
|
|
242
|
+
SolverObjectiveStage(id="minimize-x", sense="minimize", terms=[Term(var="x", coeff=1)]),
|
|
243
|
+
],
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
response = solve_request(request)
|
|
247
|
+
|
|
248
|
+
assert response.status == "OPTIMAL"
|
|
249
|
+
assert response.values == {"x": 1}
|
|
250
|
+
assert response.stageResults is not None
|
|
251
|
+
assert [stage.objectiveValue for stage in response.stageResults] == [1, 1]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_staged_soft_linear_slack_belongs_to_declared_stage():
|
|
255
|
+
request = SolverRequest(
|
|
256
|
+
variables=[Variable(type="bool", name="x")],
|
|
257
|
+
constraints=[
|
|
258
|
+
Constraint(
|
|
259
|
+
type="soft_linear",
|
|
260
|
+
terms=[Term(var="x", coeff=1)],
|
|
261
|
+
op=">=",
|
|
262
|
+
rhs=1,
|
|
263
|
+
penalty=10,
|
|
264
|
+
id="want-x",
|
|
265
|
+
stage="first",
|
|
266
|
+
),
|
|
267
|
+
Constraint(
|
|
268
|
+
type="soft_linear",
|
|
269
|
+
terms=[Term(var="x", coeff=1)],
|
|
270
|
+
op="<=",
|
|
271
|
+
rhs=0,
|
|
272
|
+
penalty=100,
|
|
273
|
+
id="avoid-x",
|
|
274
|
+
stage="second",
|
|
275
|
+
),
|
|
276
|
+
],
|
|
277
|
+
objectiveStages=[
|
|
278
|
+
SolverObjectiveStage(id="first", sense="minimize", terms=[]),
|
|
279
|
+
SolverObjectiveStage(id="second", sense="minimize", terms=[]),
|
|
280
|
+
],
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
response = solve_request(request)
|
|
284
|
+
|
|
285
|
+
assert response.status == "OPTIMAL"
|
|
286
|
+
assert response.values == {"x": 1}
|
|
287
|
+
assert response.stageResults is not None
|
|
288
|
+
assert [stage.objectiveValue for stage in response.stageResults] == [0, 100]
|
|
289
|
+
assert response.softConstraintViolations is not None
|
|
290
|
+
assert [violation.constraintId for violation in response.softConstraintViolations] == ["avoid-x"]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_unknown_soft_stage_is_rejected():
|
|
294
|
+
request = SolverRequest(
|
|
295
|
+
variables=[Variable(type="bool", name="x")],
|
|
296
|
+
constraints=[
|
|
297
|
+
Constraint(
|
|
298
|
+
type="soft_linear",
|
|
299
|
+
terms=[Term(var="x", coeff=1)],
|
|
300
|
+
op="<=",
|
|
301
|
+
rhs=0,
|
|
302
|
+
penalty=1,
|
|
303
|
+
stage="missing",
|
|
304
|
+
)
|
|
305
|
+
],
|
|
306
|
+
objectiveStages=[SolverObjectiveStage(id="known", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
response = solve_request(request)
|
|
310
|
+
|
|
311
|
+
assert response.status == "ERROR"
|
|
312
|
+
assert response.error is not None
|
|
313
|
+
assert "not declared" in response.error
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def test_missing_soft_stage_is_rejected_in_staged_mode():
|
|
317
|
+
request = SolverRequest(
|
|
318
|
+
variables=[Variable(type="bool", name="x")],
|
|
319
|
+
constraints=[
|
|
320
|
+
Constraint(
|
|
321
|
+
type="soft_linear",
|
|
322
|
+
terms=[Term(var="x", coeff=1)],
|
|
323
|
+
op="<=",
|
|
324
|
+
rhs=0,
|
|
325
|
+
penalty=1,
|
|
326
|
+
)
|
|
327
|
+
],
|
|
328
|
+
objectiveStages=[SolverObjectiveStage(id="known", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
response = solve_request(request)
|
|
332
|
+
|
|
333
|
+
assert response.status == "ERROR"
|
|
334
|
+
assert response.error is not None
|
|
335
|
+
assert "requires stage" in response.error
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_objective_and_objective_stages_are_rejected():
|
|
339
|
+
request = SolverRequest(
|
|
340
|
+
variables=[Variable(type="bool", name="x")],
|
|
341
|
+
constraints=[],
|
|
342
|
+
objective=Objective(sense="minimize", terms=[Term(var="x", coeff=1)]),
|
|
343
|
+
objectiveStages=[SolverObjectiveStage(id="stage", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
response = solve_request(request)
|
|
347
|
+
|
|
348
|
+
assert response.status == "ERROR"
|
|
349
|
+
assert response.error is not None
|
|
350
|
+
assert "cannot both be present" in response.error
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def test_satisfy_mode_ignores_soft_penalties_and_returns_hard_feasible_solution():
|
|
354
|
+
request = SolverRequest(
|
|
355
|
+
variables=[Variable(type="bool", name="x")],
|
|
356
|
+
constraints=[
|
|
357
|
+
Constraint(type="linear", terms=[Term(var="x", coeff=1)], op="==", rhs=1),
|
|
358
|
+
Constraint(
|
|
359
|
+
type="soft_linear",
|
|
360
|
+
terms=[Term(var="x", coeff=1)],
|
|
361
|
+
op="<=",
|
|
362
|
+
rhs=0,
|
|
363
|
+
penalty=100,
|
|
364
|
+
id="prefer-zero",
|
|
365
|
+
),
|
|
366
|
+
],
|
|
367
|
+
mode="satisfy",
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
response = solve_request(request)
|
|
371
|
+
|
|
372
|
+
assert response.status == "OPTIMAL"
|
|
373
|
+
assert response.values == {"x": 1}
|
|
374
|
+
assert response.softConstraintViolations == []
|
|
375
|
+
assert response.statistics is not None
|
|
376
|
+
assert "objectiveValue" not in response.statistics
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def test_satisfy_mode_validates_soft_constraints_with_unknown_variables():
|
|
380
|
+
request = SolverRequest(
|
|
381
|
+
variables=[Variable(type="bool", name="x")],
|
|
382
|
+
constraints=[
|
|
383
|
+
Constraint(
|
|
384
|
+
type="soft_linear",
|
|
385
|
+
terms=[Term(var="missing", coeff=1)],
|
|
386
|
+
op="<=",
|
|
387
|
+
rhs=0,
|
|
388
|
+
penalty=100,
|
|
389
|
+
),
|
|
390
|
+
],
|
|
391
|
+
mode="satisfy",
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
response = solve_request(request)
|
|
395
|
+
|
|
396
|
+
assert response.status == "ERROR"
|
|
397
|
+
assert response.error is not None
|
|
398
|
+
assert "Unknown variable missing" in response.error
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def test_satisfy_mode_validates_soft_constraint_required_fields():
|
|
402
|
+
request = SolverRequest(
|
|
403
|
+
variables=[Variable(type="bool", name="x")],
|
|
404
|
+
constraints=[
|
|
405
|
+
Constraint(
|
|
406
|
+
type="soft_linear",
|
|
407
|
+
terms=[Term(var="x", coeff=1)],
|
|
408
|
+
op="<=",
|
|
409
|
+
rhs=0,
|
|
410
|
+
),
|
|
411
|
+
],
|
|
412
|
+
mode="satisfy",
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
response = solve_request(request)
|
|
416
|
+
|
|
417
|
+
assert response.status == "ERROR"
|
|
418
|
+
assert response.error is not None
|
|
419
|
+
assert "requires terms, op, rhs, and penalty" in response.error
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def test_objective_stages_with_solution_limit_one_is_rejected():
|
|
423
|
+
request = SolverRequest(
|
|
424
|
+
variables=[Variable(type="bool", name="x")],
|
|
425
|
+
constraints=[],
|
|
426
|
+
objectiveStages=[SolverObjectiveStage(id="stage", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
427
|
+
options=Options(solutionLimit=1),
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
response = solve_request(request)
|
|
431
|
+
|
|
432
|
+
assert response.status == "ERROR"
|
|
433
|
+
assert response.error is not None
|
|
434
|
+
assert "solutionLimit=1" in response.error
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def test_stage_metadata_is_returned():
|
|
438
|
+
request = SolverRequest(
|
|
439
|
+
variables=[Variable(type="bool", name="x")],
|
|
440
|
+
constraints=[],
|
|
441
|
+
objectiveStages=[SolverObjectiveStage(id="stage", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
response = solve_request(request)
|
|
445
|
+
|
|
446
|
+
assert response.stageResults is not None
|
|
447
|
+
assert len(response.stageResults) == 1
|
|
448
|
+
stage_result = response.stageResults[0]
|
|
449
|
+
assert stage_result.id == "stage"
|
|
450
|
+
assert stage_result.status == "OPTIMAL"
|
|
451
|
+
assert stage_result.objectiveValue == 0
|
|
452
|
+
assert stage_result.bestObjectiveBound == 0
|
|
453
|
+
assert stage_result.solveTimeMs >= 0
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def test_staged_failure_omits_values_when_no_stage_has_succeeded():
|
|
457
|
+
request = SolverRequest(
|
|
458
|
+
variables=[Variable(type="bool", name="x")],
|
|
459
|
+
constraints=[
|
|
460
|
+
Constraint(type="linear", terms=[Term(var="x", coeff=1)], op="==", rhs=0),
|
|
461
|
+
Constraint(type="linear", terms=[Term(var="x", coeff=1)], op="==", rhs=1),
|
|
462
|
+
],
|
|
463
|
+
objectiveStages=[SolverObjectiveStage(id="stage", sense="minimize", terms=[Term(var="x", coeff=1)])],
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
response = solve_request(request)
|
|
467
|
+
|
|
468
|
+
assert response.status == "INFEASIBLE"
|
|
469
|
+
assert response.values is None
|
|
470
|
+
assert "values" not in response.model_dump(exclude_unset=True)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def test_aggregate_stage_status_returns_optimal_when_all_stages_are_optimal():
|
|
474
|
+
stage_results = [
|
|
475
|
+
SolverStageResult(id="stage-1", status="OPTIMAL", solveTimeMs=1),
|
|
476
|
+
SolverStageResult(id="stage-2", status="OPTIMAL", solveTimeMs=1),
|
|
477
|
+
]
|
|
478
|
+
|
|
479
|
+
assert _aggregate_stage_status(stage_results) == "OPTIMAL"
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def test_aggregate_stage_status_downgrades_when_later_stage_is_feasible():
|
|
483
|
+
stage_results = [
|
|
484
|
+
SolverStageResult(id="stage-1", status="OPTIMAL", solveTimeMs=1),
|
|
485
|
+
SolverStageResult(id="stage-2", status="FEASIBLE", solveTimeMs=1),
|
|
486
|
+
]
|
|
487
|
+
|
|
488
|
+
assert _aggregate_stage_status(stage_results) == "FEASIBLE"
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def test_aggregate_stage_status_downgrades_when_earlier_stage_is_feasible():
|
|
492
|
+
stage_results = [
|
|
493
|
+
SolverStageResult(id="stage-1", status="FEASIBLE", solveTimeMs=1),
|
|
494
|
+
SolverStageResult(id="stage-2", status="OPTIMAL", solveTimeMs=1),
|
|
495
|
+
]
|
|
496
|
+
|
|
497
|
+
assert _aggregate_stage_status(stage_results) == "FEASIBLE"
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def test_aggregate_stage_status_returns_first_terminal_status():
|
|
501
|
+
stage_results = [
|
|
502
|
+
SolverStageResult(id="stage-1", status="OPTIMAL", solveTimeMs=1),
|
|
503
|
+
SolverStageResult(id="stage-2", status="TIMEOUT", solveTimeMs=1),
|
|
504
|
+
SolverStageResult(id="stage-3", status="ERROR", solveTimeMs=1),
|
|
505
|
+
]
|
|
506
|
+
|
|
507
|
+
assert _aggregate_stage_status(stage_results) == "TIMEOUT"
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def test_aggregate_stage_status_rejects_empty_stage_results():
|
|
511
|
+
with pytest.raises(ValueError, match="empty stage results"):
|
|
512
|
+
_aggregate_stage_status([])
|
package/src/client.schemas.ts
CHANGED
|
@@ -55,6 +55,7 @@ export const LinearConstraintSchema = z.object({
|
|
|
55
55
|
terms: z.array(SolverTermSchema),
|
|
56
56
|
op: z.union([z.literal("<="), z.literal(">="), z.literal("==")]),
|
|
57
57
|
rhs: z.number(),
|
|
58
|
+
id: z.string().optional(),
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
export const SoftLinearConstraintSchema = z.object({
|
|
@@ -64,6 +65,7 @@ export const SoftLinearConstraintSchema = z.object({
|
|
|
64
65
|
rhs: z.number(),
|
|
65
66
|
penalty: z.number(),
|
|
66
67
|
id: z.string().optional(),
|
|
68
|
+
stage: z.string().optional(),
|
|
67
69
|
});
|
|
68
70
|
|
|
69
71
|
export const ExactlyOneConstraintSchema = z.object({
|
|
@@ -118,6 +120,13 @@ export const SolverObjectiveSchema = z.object({
|
|
|
118
120
|
terms: z.array(SolverTermSchema),
|
|
119
121
|
});
|
|
120
122
|
|
|
123
|
+
export const SolverObjectiveStageSchema = SolverObjectiveSchema.extend({
|
|
124
|
+
id: z.string(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export const SolverModeSchema = z.enum(["optimize", "satisfy"]);
|
|
128
|
+
export const SolverDiagnosticModeSchema = z.enum(["none", "hard"]);
|
|
129
|
+
|
|
121
130
|
// --------------------------------------------------------------------------
|
|
122
131
|
// Options schema
|
|
123
132
|
// --------------------------------------------------------------------------
|
|
@@ -125,20 +134,109 @@ export const SolverObjectiveSchema = z.object({
|
|
|
125
134
|
export const SolverOptionsSchema = z.object({
|
|
126
135
|
timeLimitSeconds: z.number().optional(),
|
|
127
136
|
solutionLimit: z.number().optional(),
|
|
137
|
+
diagnostics: SolverDiagnosticModeSchema.optional(),
|
|
128
138
|
});
|
|
129
139
|
|
|
130
140
|
// --------------------------------------------------------------------------
|
|
131
141
|
// Request/Response schemas
|
|
132
142
|
// --------------------------------------------------------------------------
|
|
133
143
|
|
|
134
|
-
export const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
export const SolverStatusSchema = z.enum(["OPTIMAL", "FEASIBLE", "INFEASIBLE", "TIMEOUT", "ERROR"]);
|
|
145
|
+
|
|
146
|
+
export const SolverStageResultSchema = z.object({
|
|
147
|
+
id: z.string(),
|
|
148
|
+
status: SolverStatusSchema,
|
|
149
|
+
objectiveValue: z.number().optional(),
|
|
150
|
+
bestObjectiveBound: z.number().optional(),
|
|
151
|
+
solveTimeMs: z.number(),
|
|
139
152
|
});
|
|
140
153
|
|
|
141
|
-
export const
|
|
154
|
+
export const SolverRequestSchema = z
|
|
155
|
+
.object({
|
|
156
|
+
variables: z.array(SolverVariableSchema),
|
|
157
|
+
constraints: z.array(SolverConstraintSchema),
|
|
158
|
+
objective: SolverObjectiveSchema.optional(),
|
|
159
|
+
objectiveStages: z.array(SolverObjectiveStageSchema).optional(),
|
|
160
|
+
mode: SolverModeSchema.optional(),
|
|
161
|
+
options: SolverOptionsSchema.optional(),
|
|
162
|
+
})
|
|
163
|
+
.superRefine((request, ctx) => {
|
|
164
|
+
if (request.objective && request.objectiveStages) {
|
|
165
|
+
ctx.addIssue({
|
|
166
|
+
code: "custom",
|
|
167
|
+
path: ["objectiveStages"],
|
|
168
|
+
message: "objective and objectiveStages cannot both be present",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (request.objectiveStages !== undefined) {
|
|
173
|
+
if (request.objectiveStages.length === 0) {
|
|
174
|
+
ctx.addIssue({
|
|
175
|
+
code: "custom",
|
|
176
|
+
path: ["objectiveStages"],
|
|
177
|
+
message: "objectiveStages cannot be empty",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const seenStageIds = new Set<string>();
|
|
182
|
+
for (const [index, stage] of request.objectiveStages.entries()) {
|
|
183
|
+
if (seenStageIds.has(stage.id)) {
|
|
184
|
+
ctx.addIssue({
|
|
185
|
+
code: "custom",
|
|
186
|
+
path: ["objectiveStages", index, "id"],
|
|
187
|
+
message: `Duplicate objective stage id ${stage.id}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
seenStageIds.add(stage.id);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (request.options?.solutionLimit === 1) {
|
|
194
|
+
ctx.addIssue({
|
|
195
|
+
code: "custom",
|
|
196
|
+
path: ["options", "solutionLimit"],
|
|
197
|
+
message: "objectiveStages cannot be used with solutionLimit=1",
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const [index, constraint] of request.constraints.entries()) {
|
|
202
|
+
if (constraint.type !== "soft_linear") continue;
|
|
203
|
+
|
|
204
|
+
if (constraint.stage === undefined) {
|
|
205
|
+
ctx.addIssue({
|
|
206
|
+
code: "custom",
|
|
207
|
+
path: ["constraints", index, "stage"],
|
|
208
|
+
message: "Soft linear constraint requires stage when objectiveStages are used",
|
|
209
|
+
});
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!seenStageIds.has(constraint.stage)) {
|
|
214
|
+
ctx.addIssue({
|
|
215
|
+
code: "custom",
|
|
216
|
+
path: ["constraints", index, "stage"],
|
|
217
|
+
message: `Soft linear constraint stage ${constraint.stage} is not declared`,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (request.mode === "satisfy") {
|
|
224
|
+
if (request.objective) {
|
|
225
|
+
ctx.addIssue({
|
|
226
|
+
code: "custom",
|
|
227
|
+
path: ["objective"],
|
|
228
|
+
message: 'mode "satisfy" cannot be used with objective',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (request.objectiveStages) {
|
|
232
|
+
ctx.addIssue({
|
|
233
|
+
code: "custom",
|
|
234
|
+
path: ["objectiveStages"],
|
|
235
|
+
message: 'mode "satisfy" cannot be used with objectiveStages',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
});
|
|
142
240
|
|
|
143
241
|
export const SolverStatisticsSchema = z.object({
|
|
144
242
|
solveTimeMs: z.number().optional(),
|
|
@@ -146,18 +244,37 @@ export const SolverStatisticsSchema = z.object({
|
|
|
146
244
|
branches: z.number().optional(),
|
|
147
245
|
});
|
|
148
246
|
|
|
149
|
-
export const
|
|
247
|
+
export const SolverSoftConstraintViolationSchema = z.object({
|
|
150
248
|
constraintId: z.string(),
|
|
151
249
|
violationAmount: z.number(),
|
|
152
250
|
targetValue: z.number(),
|
|
153
251
|
actualValue: z.number(),
|
|
154
252
|
});
|
|
155
253
|
|
|
156
|
-
export const
|
|
157
|
-
|
|
254
|
+
export const SolverHardConstraintConflictSchema = z.object({
|
|
255
|
+
constraintId: z.string(),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const SolverResponseBaseSchema = z.object({
|
|
158
259
|
values: z.record(z.string(), z.number()).optional(),
|
|
159
260
|
statistics: SolverStatisticsSchema.optional(),
|
|
160
261
|
error: z.string().optional(),
|
|
161
262
|
solutionInfo: z.string().optional(),
|
|
162
|
-
|
|
263
|
+
hardConstraintConflicts: z.array(SolverHardConstraintConflictSchema).optional(),
|
|
264
|
+
stageResults: z.array(SolverStageResultSchema).optional(),
|
|
163
265
|
});
|
|
266
|
+
|
|
267
|
+
const SuccessfulSolverResponseSchema = SolverResponseBaseSchema.extend({
|
|
268
|
+
status: z.union([z.literal("OPTIMAL"), z.literal("FEASIBLE")]),
|
|
269
|
+
softConstraintViolations: z.array(SolverSoftConstraintViolationSchema),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const NonSuccessfulSolverResponseSchema = SolverResponseBaseSchema.extend({
|
|
273
|
+
status: z.union([z.literal("INFEASIBLE"), z.literal("TIMEOUT"), z.literal("ERROR")]),
|
|
274
|
+
softConstraintViolations: z.array(SolverSoftConstraintViolationSchema).optional(),
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
export const SolverResponseSchema = z.union([
|
|
278
|
+
SuccessfulSolverResponseSchema,
|
|
279
|
+
NonSuccessfulSolverResponseSchema,
|
|
280
|
+
]);
|