solverpilot 0.1__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.
- solverpilot/__init__.py +250 -0
- solverpilot/_immutability.py +27 -0
- solverpilot/_version.py +3 -0
- solverpilot/applications/__init__.py +3 -0
- solverpilot/applications/routing/__init__.py +19 -0
- solverpilot/applications/routing/compile_milp.py +321 -0
- solverpilot/applications/routing/diagnostics.py +78 -0
- solverpilot/applications/routing/errors.py +14 -0
- solverpilot/applications/routing/heuristics.py +167 -0
- solverpilot/applications/routing/model.py +259 -0
- solverpilot/applications/routing/reference.py +81 -0
- solverpilot/applications/routing/solution.py +89 -0
- solverpilot/applications/routing/timeline.py +79 -0
- solverpilot/applications/routing/validation.py +169 -0
- solverpilot/applications/tsp/__init__.py +41 -0
- solverpilot/applications/tsp/compile_cp.py +121 -0
- solverpilot/applications/tsp/compile_milp.py +139 -0
- solverpilot/applications/tsp/distance.py +60 -0
- solverpilot/applications/tsp/errors.py +17 -0
- solverpilot/applications/tsp/heuristics.py +112 -0
- solverpilot/applications/tsp/model.py +175 -0
- solverpilot/applications/tsp/reference.py +121 -0
- solverpilot/applications/tsp/solution.py +65 -0
- solverpilot/applications/tsp/validation.py +92 -0
- solverpilot/backends/__init__.py +51 -0
- solverpilot/backends/_casadi_highs_worker.py +92 -0
- solverpilot/backends/base.py +28 -0
- solverpilot/backends/bundled_capi.py +910 -0
- solverpilot/backends/casadi_conic.py +303 -0
- solverpilot/backends/health.py +240 -0
- solverpilot/backends/highspy_native.py +217 -0
- solverpilot/backends/nlopt_native.py +237 -0
- solverpilot/backends/osqp_native.py +297 -0
- solverpilot/backends/protocol_v2.py +119 -0
- solverpilot/backends/pyscipopt_native.py +200 -0
- solverpilot/backends/registry.py +38 -0
- solverpilot/backends/scipy_highs.py +138 -0
- solverpilot/backends/scipy_highs_lp.py +195 -0
- solverpilot/backends/scipy_slsqp_qp.py +133 -0
- solverpilot/backends/scipy_vendored_highs_dev.py +363 -0
- solverpilot/benchmark/__init__.py +23 -0
- solverpilot/benchmark/acquire.py +199 -0
- solverpilot/benchmark/baselines.py +70 -0
- solverpilot/benchmark/bundle.py +37 -0
- solverpilot/benchmark/campaign.py +150 -0
- solverpilot/benchmark/environment.py +115 -0
- solverpilot/benchmark/integrity.py +46 -0
- solverpilot/benchmark/model.py +146 -0
- solverpilot/benchmark/policy.py +182 -0
- solverpilot/benchmark/registry.py +38 -0
- solverpilot/benchmark/report.py +44 -0
- solverpilot/benchmark/runner.py +516 -0
- solverpilot/benchmark/slurm.py +55 -0
- solverpilot/benchmark/specs.py +75 -0
- solverpilot/benchmark/splits.py +181 -0
- solverpilot/benchmark/summary.py +384 -0
- solverpilot/benchmark/verify.py +142 -0
- solverpilot/benchmark/worker.py +71 -0
- solverpilot/bridges/__init__.py +19 -0
- solverpilot/bridges/engine.py +44 -0
- solverpilot/bridges/indicator.py +232 -0
- solverpilot/bridges/registry.py +78 -0
- solverpilot/bridges/transformation-tape.schema.json +53 -0
- solverpilot/bridges/types.py +149 -0
- solverpilot/bridges/validation.py +110 -0
- solverpilot/capabilities/__init__.py +32 -0
- solverpilot/capabilities/backend-capability-v2.schema.json +21 -0
- solverpilot/capabilities/conformance.py +151 -0
- solverpilot/capabilities/enums.py +27 -0
- solverpilot/capabilities/manifest.py +19 -0
- solverpilot/capabilities/ontology.py +73 -0
- solverpilot/capabilities/requirements.py +37 -0
- solverpilot/capabilities/resolve.py +30 -0
- solverpilot/capabilities/v2.py +711 -0
- solverpilot/cli/__init__.py +1 -0
- solverpilot/cli/backend_health.py +77 -0
- solverpilot/cli/benchmark.py +331 -0
- solverpilot/cli/benchmark_worker.py +33 -0
- solverpilot/cli/capabilities.py +65 -0
- solverpilot/conic/__init__.py +11 -0
- solverpilot/conic/backend.py +263 -0
- solverpilot/conic/compiler.py +310 -0
- solverpilot/conic/conformance.py +60 -0
- solverpilot/conic/conic-ir-v1.schema.json +24 -0
- solverpilot/conic/ir.py +299 -0
- solverpilot/conic/validation.py +149 -0
- solverpilot/cp/__init__.py +7 -0
- solverpilot/cp/conformance.py +32 -0
- solverpilot/cp/cp-ir-v1.schema.json +13 -0
- solverpilot/cp/ir.py +170 -0
- solverpilot/cp/model.py +143 -0
- solverpilot/cp/ortools_backend.py +423 -0
- solverpilot/cp/ortools_worker.py +75 -0
- solverpilot/cp/reference.py +51 -0
- solverpilot/cp/serialization.py +117 -0
- solverpilot/cp/validate.py +83 -0
- solverpilot/diagnose/__init__.py +33 -0
- solverpilot/diagnose/infeasibility.py +463 -0
- solverpilot/diagnose/model.py +87 -0
- solverpilot/evaluation/__init__.py +53 -0
- solverpilot/evaluation/metrics.py +150 -0
- solverpilot/evaluation/oracle.py +288 -0
- solverpilot/evaluation/proof.py +53 -0
- solverpilot/evaluation/references.py +73 -0
- solverpilot/evaluation/regret.py +281 -0
- solverpilot/exceptions.py +34 -0
- solverpilot/experimental/__init__.py +32 -0
- solverpilot/extensions/__init__.py +56 -0
- solverpilot/extensions/entrypoints.py +34 -0
- solverpilot/extensions/errors.py +35 -0
- solverpilot/extensions/manager.py +651 -0
- solverpilot/extensions/manifest.py +232 -0
- solverpilot/extensions/model.py +101 -0
- solverpilot/extensions/versioning.py +136 -0
- solverpilot/history/__init__.py +22 -0
- solverpilot/history/errors.py +15 -0
- solverpilot/history/model.py +129 -0
- solverpilot/history/sqlite.py +651 -0
- solverpilot/inspect/__init__.py +3 -0
- solverpilot/inspect/fingerprint.py +199 -0
- solverpilot/intelligence/__init__.py +36 -0
- solverpilot/intelligence/errors.py +16 -0
- solverpilot/intelligence/fingerprint.py +108 -0
- solverpilot/intelligence/leakage.py +69 -0
- solverpilot/intelligence/schema.py +314 -0
- solverpilot/intelligence/shift.py +208 -0
- solverpilot/io/__init__.py +41 -0
- solverpilot/io/binary64.py +49 -0
- solverpilot/io/csv.py +230 -0
- solverpilot/io/errors.py +25 -0
- solverpilot/io/hashing.py +111 -0
- solverpilot/io/json.py +90 -0
- solverpilot/io/mapping.py +195 -0
- solverpilot/io/model.py +407 -0
- solverpilot/io/numeric.py +108 -0
- solverpilot/io/provenance.py +114 -0
- solverpilot/io/result.py +45 -0
- solverpilot/io/source.py +104 -0
- solverpilot/io/strict_json.py +133 -0
- solverpilot/minlp/__init__.py +6 -0
- solverpilot/minlp/compiler.py +66 -0
- solverpilot/minlp/conformance.py +34 -0
- solverpilot/minlp/convexity.py +85 -0
- solverpilot/minlp/ir.py +35 -0
- solverpilot/minlp/minlp-ir-v1.schema.json +1 -0
- solverpilot/minlp/orchestrator.py +220 -0
- solverpilot/minlp/validation.py +28 -0
- solverpilot/model/__init__.py +27 -0
- solverpilot/model/compiler.py +1263 -0
- solverpilot/model/errors.py +22 -0
- solverpilot/model/expression.py +373 -0
- solverpilot/model/model.py +716 -0
- solverpilot/model/sets.py +60 -0
- solverpilot/model/types.py +36 -0
- solverpilot/nlp/__init__.py +7 -0
- solverpilot/nlp/ad.py +166 -0
- solverpilot/nlp/backend.py +113 -0
- solverpilot/nlp/compiler.py +116 -0
- solverpilot/nlp/conformance.py +58 -0
- solverpilot/nlp/ir.py +70 -0
- solverpilot/nlp/nlp-ir-v1.schema.json +1 -0
- solverpilot/nlp/validation.py +60 -0
- solverpilot/plan/__init__.py +33 -0
- solverpilot/plan/model.py +79 -0
- solverpilot/plan/planner.py +201 -0
- solverpilot/plan/production.py +627 -0
- solverpilot/plan/selective_lp.py +65 -0
- solverpilot/problem/__init__.py +16 -0
- solverpilot/problem/enums.py +20 -0
- solverpilot/problem/hashing.py +88 -0
- solverpilot/problem/linear.py +196 -0
- solverpilot/problem/mps.py +447 -0
- solverpilot/problem/quadratic.py +192 -0
- solverpilot/reporting/__init__.py +27 -0
- solverpilot/reporting/explain.py +327 -0
- solverpilot/reporting/model.py +264 -0
- solverpilot/reporting/render.py +55 -0
- solverpilot/runtime/__init__.py +19 -0
- solverpilot/runtime/auto.py +193 -0
- solverpilot/runtime/budgeting.py +43 -0
- solverpilot/runtime/executor.py +126 -0
- solverpilot/runtime/portfolio.py +197 -0
- solverpilot/runtime/result.py +72 -0
- solverpilot/session/__init__.py +29 -0
- solverpilot/session/conformance_v2.py +239 -0
- solverpilot/session/mutations.py +115 -0
- solverpilot/session/persistent-session-trace.schema.json +45 -0
- solverpilot/session/persistent.py +491 -0
- solverpilot/session/reuse.py +81 -0
- solverpilot/session/session.py +236 -0
- solverpilot/trace/__init__.py +3 -0
- solverpilot/trace/schema.py +47 -0
- solverpilot/validate/__init__.py +10 -0
- solverpilot/validate/core.py +179 -0
- solverpilot/validate/result.py +55 -0
- solverpilot-0.1.dist-info/METADATA +554 -0
- solverpilot-0.1.dist-info/RECORD +201 -0
- solverpilot-0.1.dist-info/WHEEL +5 -0
- solverpilot-0.1.dist-info/entry_points.txt +4 -0
- solverpilot-0.1.dist-info/licenses/LICENSE +202 -0
- solverpilot-0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
import itertools, math
|
|
6
|
+
from solverpilot._immutability import deep_freeze
|
|
7
|
+
from .ir import *
|
|
8
|
+
from .validate import validate_cp_solution, complete_assignment
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class CPSolveResult:
|
|
12
|
+
status:str
|
|
13
|
+
assignment:Mapping[int,int]|None
|
|
14
|
+
objective:int|None
|
|
15
|
+
validation:object
|
|
16
|
+
optimality_proven:bool
|
|
17
|
+
backend:str
|
|
18
|
+
raw_statistics:Mapping[str,Any]
|
|
19
|
+
|
|
20
|
+
def __post_init__(self):
|
|
21
|
+
if self.assignment is not None:
|
|
22
|
+
object.__setattr__(self, 'assignment', deep_freeze(dict(self.assignment)))
|
|
23
|
+
object.__setattr__(self, 'raw_statistics', deep_freeze(dict(self.raw_statistics)))
|
|
24
|
+
|
|
25
|
+
class ReferenceCPBackend:
|
|
26
|
+
name='reference-cp-enumeration'
|
|
27
|
+
verification_only=True
|
|
28
|
+
def __init__(self,max_states=1_000_000): self.max_states=int(max_states)
|
|
29
|
+
def is_available(self): return True
|
|
30
|
+
def solve(self,problem:CPProblem,*,max_states=None):
|
|
31
|
+
limit=self.max_states if max_states is None else int(max_states)
|
|
32
|
+
base=problem.base_variables
|
|
33
|
+
states=math.prod(len(v.domain) for v in base)
|
|
34
|
+
if states>limit:
|
|
35
|
+
return CPSolveResult('unknown_state_limit',None,None,validate_cp_solution(problem,{}),False,self.name,{'states_total':states,'states_examined':0,'limit':limit})
|
|
36
|
+
best=None; best_obj=None; examined=0; feasible=0
|
|
37
|
+
for values in itertools.product(*(v.domain for v in base)):
|
|
38
|
+
examined+=1; a={v.var_id:int(x) for v,x in zip(base,values)}; a=complete_assignment(problem,a)
|
|
39
|
+
rep=validate_cp_solution(problem,a)
|
|
40
|
+
if not rep.valid: continue
|
|
41
|
+
feasible+=1; obj=rep.objective_recomputed
|
|
42
|
+
if problem.objective is None:
|
|
43
|
+
best=a; best_obj=None; break
|
|
44
|
+
if best is None or (problem.objective_sense is CPObjectiveSense.MINIMIZE and obj<best_obj) or (problem.objective_sense is CPObjectiveSense.MAXIMIZE and obj>best_obj):
|
|
45
|
+
best=a.copy(); best_obj=obj
|
|
46
|
+
if best is None:
|
|
47
|
+
# If objective-free search stops at first feasible only when found; no feasible means full enumeration.
|
|
48
|
+
return CPSolveResult('infeasible',None,None,validate_cp_solution(problem,{}),True,self.name,{'states_total':states,'states_examined':examined,'feasible_seen':feasible})
|
|
49
|
+
rep=validate_cp_solution(problem,best,best_obj)
|
|
50
|
+
proof = problem.objective is None or examined==states
|
|
51
|
+
return CPSolveResult('optimal' if problem.objective is not None and proof else 'feasible',best,best_obj,rep,proof,self.name,{'states_total':states,'states_examined':examined,'feasible_seen':feasible})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping
|
|
4
|
+
|
|
5
|
+
from .ir import (
|
|
6
|
+
CPAllDifferentIR,
|
|
7
|
+
CPCircuitIR,
|
|
8
|
+
CPCumulativeIR,
|
|
9
|
+
CPElementIR,
|
|
10
|
+
CPExactlyOneIR,
|
|
11
|
+
CPIntVarIR,
|
|
12
|
+
CPIntervalIR,
|
|
13
|
+
CPLinearConstraintIR,
|
|
14
|
+
CPLinearExprIR,
|
|
15
|
+
CPNoOverlapIR,
|
|
16
|
+
CPObjectiveSense,
|
|
17
|
+
CPProblem,
|
|
18
|
+
CPTableIR,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _expr_from_dict(payload: Mapping[str, Any]) -> CPLinearExprIR:
|
|
23
|
+
return CPLinearExprIR(
|
|
24
|
+
terms=tuple((int(v), int(c)) for v, c in payload.get("terms", ())),
|
|
25
|
+
constant=int(payload.get("constant", 0)),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def problem_from_canonical_dict(payload: Mapping[str, Any]) -> CPProblem:
|
|
30
|
+
"""Reconstruct canonical CP IR from :meth:`CPProblem.canonical_dict` output.
|
|
31
|
+
|
|
32
|
+
This deliberately accepts only the closed P9 schema families. It is used by
|
|
33
|
+
the isolated CP-SAT worker so no pickle/arbitrary-code serialization crosses
|
|
34
|
+
the process boundary.
|
|
35
|
+
"""
|
|
36
|
+
variables = tuple(
|
|
37
|
+
CPIntVarIR(
|
|
38
|
+
var_id=int(v["id"]),
|
|
39
|
+
name=str(v["name"]),
|
|
40
|
+
domain=tuple(int(x) for x in v["domain"]),
|
|
41
|
+
kind=str(v.get("kind", "int")),
|
|
42
|
+
derived_from=None
|
|
43
|
+
if v.get("derived_from") is None
|
|
44
|
+
else (int(v["derived_from"][0]), int(v["derived_from"][1])),
|
|
45
|
+
)
|
|
46
|
+
for v in payload.get("variables", ())
|
|
47
|
+
)
|
|
48
|
+
intervals = tuple(
|
|
49
|
+
CPIntervalIR(
|
|
50
|
+
interval_id=int(i["id"]),
|
|
51
|
+
name=str(i["name"]),
|
|
52
|
+
start_var=int(i["start_var"]),
|
|
53
|
+
size=int(i["size"]),
|
|
54
|
+
end_var=int(i["end_var"]),
|
|
55
|
+
)
|
|
56
|
+
for i in payload.get("intervals", ())
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
constraints = []
|
|
60
|
+
for c in payload.get("constraints", ()):
|
|
61
|
+
kind = c["kind"]
|
|
62
|
+
if kind == "linear":
|
|
63
|
+
constraints.append(
|
|
64
|
+
CPLinearConstraintIR(
|
|
65
|
+
_expr_from_dict(c["expr"]),
|
|
66
|
+
None if c.get("lower") is None else int(c["lower"]),
|
|
67
|
+
None if c.get("upper") is None else int(c["upper"]),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
elif kind == "all_different":
|
|
71
|
+
constraints.append(CPAllDifferentIR(tuple(int(x) for x in c["var_ids"])))
|
|
72
|
+
elif kind == "exactly_one":
|
|
73
|
+
constraints.append(CPExactlyOneIR(tuple(int(x) for x in c["literal_ids"])))
|
|
74
|
+
elif kind == "table":
|
|
75
|
+
constraints.append(
|
|
76
|
+
CPTableIR(
|
|
77
|
+
tuple(int(x) for x in c["var_ids"]),
|
|
78
|
+
tuple(tuple(int(x) for x in row) for row in c["allowed_tuples"]),
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
elif kind == "element":
|
|
82
|
+
constraints.append(
|
|
83
|
+
CPElementIR(
|
|
84
|
+
int(c["index_var"]),
|
|
85
|
+
tuple(int(x) for x in c["values"]),
|
|
86
|
+
int(c["target_var"]),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
elif kind == "circuit":
|
|
90
|
+
constraints.append(
|
|
91
|
+
CPCircuitIR(tuple(tuple(int(x) for x in arc) for arc in c["arcs"]))
|
|
92
|
+
)
|
|
93
|
+
elif kind == "no_overlap":
|
|
94
|
+
constraints.append(CPNoOverlapIR(tuple(int(x) for x in c["interval_ids"])))
|
|
95
|
+
elif kind == "cumulative":
|
|
96
|
+
constraints.append(
|
|
97
|
+
CPCumulativeIR(
|
|
98
|
+
tuple(int(x) for x in c["interval_ids"]),
|
|
99
|
+
tuple(int(x) for x in c["demands"]),
|
|
100
|
+
int(c["capacity"]),
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError(f"unsupported CP constraint kind in isolated payload: {kind!r}")
|
|
105
|
+
|
|
106
|
+
objective_payload = payload.get("objective")
|
|
107
|
+
objective = None if objective_payload is None else _expr_from_dict(objective_payload)
|
|
108
|
+
return CPProblem(
|
|
109
|
+
variables=variables,
|
|
110
|
+
intervals=intervals,
|
|
111
|
+
constraints=tuple(constraints),
|
|
112
|
+
objective=objective,
|
|
113
|
+
objective_sense=CPObjectiveSense(payload.get("objective_sense", "minimize")),
|
|
114
|
+
name=payload.get("name"),
|
|
115
|
+
metadata=dict(payload.get("metadata", {})),
|
|
116
|
+
schema_version=str(payload.get("schema_version", "solverpilot.cp-ir.v1")),
|
|
117
|
+
)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Mapping
|
|
4
|
+
import math
|
|
5
|
+
from .ir import *
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class CPValidationReport:
|
|
9
|
+
valid:bool
|
|
10
|
+
domain_violations:tuple[int,...]
|
|
11
|
+
constraint_violations:tuple[int,...]
|
|
12
|
+
derived_violations:tuple[int,...]
|
|
13
|
+
objective_recomputed:int|None
|
|
14
|
+
objective_reported:int|None
|
|
15
|
+
objective_consistent:bool|None
|
|
16
|
+
|
|
17
|
+
def _expr(e, a): return e.constant + sum(c*a[v] for v,c in e.terms)
|
|
18
|
+
|
|
19
|
+
def complete_assignment(problem,assignment):
|
|
20
|
+
a={int(k):int(v) for k,v in dict(assignment).items()}
|
|
21
|
+
pending=True
|
|
22
|
+
while pending:
|
|
23
|
+
pending=False
|
|
24
|
+
for v in problem.variables:
|
|
25
|
+
if v.var_id in a: continue
|
|
26
|
+
if v.derived_from is not None:
|
|
27
|
+
s,size=v.derived_from
|
|
28
|
+
if s in a: a[v.var_id]=a[s]+size; pending=True
|
|
29
|
+
return a
|
|
30
|
+
|
|
31
|
+
def _circuit_ok(c,a):
|
|
32
|
+
nodes=set(); selected=[]
|
|
33
|
+
for t,h,l in c.arcs:
|
|
34
|
+
nodes.add(t); nodes.add(h)
|
|
35
|
+
if a[l]==1: selected.append((t,h))
|
|
36
|
+
# CP-SAT semantics: incident nodes exactly one selected incoming/outgoing.
|
|
37
|
+
for n in nodes:
|
|
38
|
+
if sum(1 for t,h in selected if t==n)!=1: return False
|
|
39
|
+
if sum(1 for t,h in selected if h==n)!=1: return False
|
|
40
|
+
nonself=[e for e in selected if e[0]!=e[1]]
|
|
41
|
+
if not nonself: return True
|
|
42
|
+
active=set(x for e in nonself for x in e)
|
|
43
|
+
start=next(iter(active)); seen={start}; cur=start
|
|
44
|
+
for _ in range(len(active)):
|
|
45
|
+
nxts=[h for t,h in nonself if t==cur]
|
|
46
|
+
if len(nxts)!=1: return False
|
|
47
|
+
cur=nxts[0]
|
|
48
|
+
if cur==start: break
|
|
49
|
+
if cur in seen: return False
|
|
50
|
+
seen.add(cur)
|
|
51
|
+
return cur==start and seen==active
|
|
52
|
+
|
|
53
|
+
def validate_cp_solution(problem:CPProblem,assignment:Mapping[int,int],objective_reported=None):
|
|
54
|
+
a=complete_assignment(problem,assignment); dviol=[]; derived=[]; cviol=[]
|
|
55
|
+
for v in problem.variables:
|
|
56
|
+
if v.var_id not in a or a[v.var_id] not in v.domain: dviol.append(v.var_id)
|
|
57
|
+
if v.derived_from is not None and v.var_id in a:
|
|
58
|
+
s,size=v.derived_from
|
|
59
|
+
if s not in a or a[v.var_id] != a[s]+size: derived.append(v.var_id)
|
|
60
|
+
imap=problem.interval_map
|
|
61
|
+
for idx,c in enumerate(problem.constraints):
|
|
62
|
+
ok=True
|
|
63
|
+
try:
|
|
64
|
+
if isinstance(c,CPLinearConstraintIR):
|
|
65
|
+
val=_expr(c.expr,a); ok=(c.lower is None or val>=c.lower) and (c.upper is None or val<=c.upper)
|
|
66
|
+
elif isinstance(c,CPAllDifferentIR): ok=len({a[v] for v in c.var_ids})==len(c.var_ids)
|
|
67
|
+
elif isinstance(c,CPExactlyOneIR): ok=sum(a[v] for v in c.literal_ids)==1
|
|
68
|
+
elif isinstance(c,CPTableIR): ok=tuple(a[v] for v in c.var_ids) in set(c.allowed_tuples)
|
|
69
|
+
elif isinstance(c,CPElementIR):
|
|
70
|
+
ix=a[c.index_var]; ok=0<=ix<len(c.values) and a[c.target_var]==c.values[ix]
|
|
71
|
+
elif isinstance(c,CPCircuitIR): ok=_circuit_ok(c,a)
|
|
72
|
+
elif isinstance(c,CPNoOverlapIR):
|
|
73
|
+
spans=[(a[imap[i].start_var],a[imap[i].end_var]) for i in c.interval_ids]
|
|
74
|
+
ok=all(e1<=s2 or e2<=s1 for j,(s1,e1) in enumerate(spans) for s2,e2 in spans[j+1:])
|
|
75
|
+
elif isinstance(c,CPCumulativeIR):
|
|
76
|
+
items=[(a[imap[i].start_var],a[imap[i].end_var],d) for i,d in zip(c.interval_ids,c.demands)]
|
|
77
|
+
points=sorted({s for s,e,d in items if s<e})
|
|
78
|
+
ok=c.capacity>=0 and all(sum(d for s,e,d in items if s<=t<e)<=c.capacity for t in points)
|
|
79
|
+
except KeyError: ok=False
|
|
80
|
+
if not ok: cviol.append(idx)
|
|
81
|
+
obj=None if problem.objective is None or any(v not in a for v,c in problem.objective.terms) else _expr(problem.objective,a)
|
|
82
|
+
consistent=None if objective_reported is None or obj is None else int(objective_reported)==int(obj)
|
|
83
|
+
return CPValidationReport(not dviol and not derived and not cviol and consistent is not False,tuple(dviol),tuple(cviol),tuple(derived),obj,None if objective_reported is None else int(objective_reported),consistent)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .infeasibility import (
|
|
2
|
+
deletion_filter_conflict,
|
|
3
|
+
diagnose_infeasibility,
|
|
4
|
+
elastic_relaxation,
|
|
5
|
+
find_static_infeasibility,
|
|
6
|
+
)
|
|
7
|
+
from .model import (
|
|
8
|
+
ConflictAtom,
|
|
9
|
+
ConflictSetResult,
|
|
10
|
+
DiagnosticIssue,
|
|
11
|
+
ElasticRelaxationResult,
|
|
12
|
+
EvidenceKind,
|
|
13
|
+
InfeasibilityReport,
|
|
14
|
+
NormalizedIISEvidence,
|
|
15
|
+
RelaxationViolation,
|
|
16
|
+
ViolationKind,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"ConflictAtom",
|
|
21
|
+
"ConflictSetResult",
|
|
22
|
+
"DiagnosticIssue",
|
|
23
|
+
"ElasticRelaxationResult",
|
|
24
|
+
"EvidenceKind",
|
|
25
|
+
"InfeasibilityReport",
|
|
26
|
+
"NormalizedIISEvidence",
|
|
27
|
+
"RelaxationViolation",
|
|
28
|
+
"ViolationKind",
|
|
29
|
+
"deletion_filter_conflict",
|
|
30
|
+
"diagnose_infeasibility",
|
|
31
|
+
"elastic_relaxation",
|
|
32
|
+
"find_static_infeasibility",
|
|
33
|
+
]
|