LotSizingDataModel.Checker 2.0.1
Reusable validation engine for structural, mathematical and objective consistency checks.
Loading...
Searching...
No Matches
ScientificLotSizingSolvePipeline.cs
Go to the documentation of this file.
3using LotSizingDataModel.Instance.Scientific;
4using LotSizingDataModel.Solution;
5using LotSizingDataModel.Solution.Metadata.Scientific;
6using LotSizingDataModel.Solver.Execution;
7using LotSizingDataModel.Solver.Formulation;
8using LotSizingDataModel.Solver.Formulation.Scientific;
9using LotSizingDataModel.Solver.Mapping.Scientific;
10using LotSizingDataModel.Solver.Resolution.Scientific;
11
13
14/// <summary>
15/// End-to-end scientific solve orchestration.
16/// </summary>
17/// <remarks>
18/// The pipeline never reimplements the technical solver workflow. It delegates
19/// model build, solver selection/execution and solution mapping to the existing
20/// <see cref="ILotSizingSolverService"/> after scientific preflight has pinned
21/// one verified formulation.
22///
23/// Numerical solution verification and scientific provenance verification are
24/// retained as separate result channels.
25/// </remarks>
28{
29 private readonly ILotSizingSolverService _solverService;
30
31 private readonly MathematicalModelFormulationRegistry
32 _formulationRegistry;
33
34 private readonly ScientificClassificationEngine
35 _classificationEngine;
36
37 private readonly ScientificFormulationSelectionService
38 _formulationSelectionService;
39
40 private readonly ScientificResolutionPlanner
41 _resolutionPlanner;
42
44 _solutionVerificationService;
45
47 _provenanceChecker;
48
50 ILotSizingSolverService solverService,
51 MathematicalModelFormulationRegistry formulationRegistry)
52 : this(
53 solverService,
54 formulationRegistry,
55 new ScientificClassificationEngine(),
56 new ScientificFormulationSelectionService(),
57 new ScientificResolutionPlanner(),
60 {
61 }
62
64 ILotSizingSolverService solverService,
65 MathematicalModelFormulationRegistry formulationRegistry,
66 ScientificClassificationEngine classificationEngine,
67 ScientificFormulationSelectionService formulationSelectionService,
68 ScientificResolutionPlanner resolutionPlanner,
69 LotSizingSolutionVerificationService solutionVerificationService,
71 {
72 _solverService =
73 solverService ??
74 throw new ArgumentNullException(nameof(solverService));
75
76 _formulationRegistry =
77 formulationRegistry ??
78 throw new ArgumentNullException(nameof(formulationRegistry));
79
80 _classificationEngine =
81 classificationEngine ??
82 throw new ArgumentNullException(nameof(classificationEngine));
83
84 _formulationSelectionService =
85 formulationSelectionService ??
86 throw new ArgumentNullException(
87 nameof(formulationSelectionService));
88
89 _resolutionPlanner =
90 resolutionPlanner ??
91 throw new ArgumentNullException(nameof(resolutionPlanner));
92
93 _solutionVerificationService =
94 solutionVerificationService ??
95 throw new ArgumentNullException(
96 nameof(solutionVerificationService));
97
98 _provenanceChecker =
99 provenanceChecker ??
100 throw new ArgumentNullException(nameof(provenanceChecker));
101 }
102
103 public async ValueTask<ScientificSolvePipelineResult> SolveAsync(
105 CancellationToken cancellationToken = default)
106 {
107 ArgumentNullException.ThrowIfNull(request);
108
109 SolverRequest sourceRequest =
110 request.SolverRequest;
111
112 sourceRequest.EnsureValid();
113
114 ScientificClassificationResult classification =
115 _classificationEngine.Analyze(
116 sourceRequest.Instance!,
117 request.ClassificationRequest);
118
119 bool callerRequestedSpecificFormulation =
120 !string.IsNullOrWhiteSpace(
121 sourceRequest.FormulationName);
122
123 ScientificFormulationSelectionResult selection =
124 _formulationSelectionService.Select(
125 classification,
126 _formulationRegistry,
127 requestedFormulationId:
128 sourceRequest.FormulationName ?? string.Empty,
129 allowFallback:
130 !callerRequestedSpecificFormulation);
131
132 ScientificResolutionPlan resolutionPlan =
133 _resolutionPlanner.Create(
134 classification,
135 selection,
136 sourceRequest.PreferredSolver);
137
138 var diagnostics =
139 new List<ScientificSolvePipelineDiagnostic>();
140
141 if (
142 classification.IsBlocked ||
143 !selection.IsSuccessful ||
144 selection.Formulation is null ||
145 !resolutionPlan.IsReady ||
146 resolutionPlan.SelectedMethod is null)
147 {
148 diagnostics.Add(
149 Diagnostic(
150 "LSDM-PIPE-001",
152 "scientificPreflight",
153 classification.IsBlocked
154 ? "Scientific classification is blocked."
155 : !selection.IsSuccessful
156 ? "No scientifically selectable mathematical " +
157 "formulation is available."
158 : "No executable scientific resolution plan is " +
159 "available."));
160
161 return Result(
162 ScientificSolvePipelineStatus.PreflightRejected,
163 classification,
164 selection,
165 resolutionPlan,
166 solverRun: null,
167 provenance: null,
168 numericalVerification: null,
169 provenanceVerification: null,
170 diagnostics);
171 }
172
173 if (
174 !selection.Formulation.CanBuild(
175 sourceRequest.Instance!))
176 {
177 diagnostics.Add(
178 Diagnostic(
179 "LSDM-PIPE-002",
181 "scientificPreflight.formulation",
182 $"Scientifically selected formulation " +
183 $"'{selection.Formulation.FormulationId}' failed its " +
184 "technical CanBuild(instance) contract."));
185
186 return Result(
187 ScientificSolvePipelineStatus.PreflightRejected,
188 classification,
189 selection,
190 resolutionPlan,
191 solverRun: null,
192 provenance: null,
193 numericalVerification: null,
194 provenanceVerification: null,
195 diagnostics);
196 }
197
198 string selectedFormulationId =
199 selection.Formulation.FormulationId;
200
201 SolverRequest delegatedRequest =
203 sourceRequest,
204 selectedFormulationId);
205
206 // Critical invariant: scientific selection pins the technical request.
207 if (
208 !delegatedRequest.FormulationName.Equals(
209 selectedFormulationId,
210 StringComparison.OrdinalIgnoreCase))
211 {
212 throw new InvalidOperationException(
213 "Internal scientific solver-request pinning failed.");
214 }
215
216 SolverRunResult solverRun =
217 await _solverService.SolveAsync(
218 delegatedRequest,
219 cancellationToken)
220 .ConfigureAwait(false);
221
222 cancellationToken.ThrowIfCancellationRequested();
223
224 if (!solverRun.HasSolution)
225 {
226 diagnostics.Add(
227 Diagnostic(
228 "LSDM-PIPE-010",
229 solverRun.IsSuccessful
232 "solverRun",
233 $"Solver run completed with termination reason " +
234 $"'{solverRun.TerminationReason}' and produced no " +
235 "normalized LotSizingSolution."));
236
237 return Result(
238 ScientificSolvePipelineStatus.CompletedWithoutSolution,
239 classification,
240 selection,
241 resolutionPlan,
242 solverRun,
243 provenance: null,
244 numericalVerification: null,
245 provenanceVerification: null,
246 diagnostics);
247 }
248
249 if (
250 !solverRun.FormulationName.Equals(
251 selectedFormulationId,
252 StringComparison.OrdinalIgnoreCase))
253 {
254 diagnostics.Add(
255 Diagnostic(
256 "LSDM-PIPE-011",
258 "solverRun.formulationName",
259 $"Scientific preflight selected formulation " +
260 $"'{selectedFormulationId}', but the solver run reports " +
261 $"'{solverRun.FormulationName}'."));
262
263 return Result(
264 ScientificSolvePipelineStatus.FormulationDrift,
265 classification,
266 selection,
267 resolutionPlan,
268 solverRun,
269 provenance: null,
270 numericalVerification: null,
271 provenanceVerification: null,
272 diagnostics);
273 }
274
275 if (
276 resolutionPlan.SelectedMethod!.Method.RequiresMilpBackend &&
277 (
278 ScientificSolverBackendCatalog.Find(
279 solverRun.SolverKind) is not
280 ScientificSolverBackendDefinition actualBackend ||
281 !actualBackend.Supports(
282 resolutionPlan.SelectedMethod.Method)
283 ))
284 {
285 diagnostics.Add(
286 Diagnostic(
287 "LSDM-PIPE-012",
289 "solverRun.solverKind",
290 $"Solver backend '{solverRun.SolverKind}' is not " +
291 $"compatible with scientific solution method " +
292 $"'{resolutionPlan.SelectedMethod.Method.MethodId}'."));
293
294 return Result(
296 classification,
297 selection,
298 resolutionPlan,
299 solverRun,
300 provenance: null,
301 numericalVerification: null,
302 provenanceVerification: null,
303 diagnostics);
304 }
305
306 if (
307 sourceRequest.PreferredSolver !=
308 LotSizingDataModel.Solver.Common.SolverKind.Automatic &&
309 solverRun.SolverKind !=
310 sourceRequest.PreferredSolver)
311 {
312 diagnostics.Add(
313 Diagnostic(
314 "LSDM-PIPE-013",
316 "solverRun.solverKind",
317 $"Caller requested solver backend " +
318 $"'{sourceRequest.PreferredSolver}', but the solver run " +
319 $"reports '{solverRun.SolverKind}'."));
320
321 return Result(
323 classification,
324 selection,
325 resolutionPlan,
326 solverRun,
327 provenance: null,
328 numericalVerification: null,
329 provenanceVerification: null,
330 diagnostics);
331 }
332
333 LotSizingSolution solution =
334 solverRun.Solution!;
335
336 SolutionScientificProvenance provenance;
337
338 try
339 {
340 provenance =
341 ScientificSolutionProvenanceMapper.Apply(
342 solution,
343 selection,
344 resolutionPlan.SelectedMethod!.Method,
345 solverRun.SolverKind);
346 }
347 catch (Exception exception)
348 {
349 diagnostics.Add(
350 Diagnostic(
351 "LSDM-PIPE-020",
353 "solution.provenance",
354 "Scientific provenance capture failed: " +
355 exception.Message));
356
357 return Result(
358 ScientificSolvePipelineStatus.ProvenanceCaptureFailed,
359 classification,
360 selection,
361 resolutionPlan,
362 solverRun,
363 provenance: null,
364 numericalVerification: null,
365 provenanceVerification: null,
366 diagnostics);
367 }
368
369 LotSizingSolutionVerificationResult? numericalVerification =
370 null;
371
372 if (request.VerifyNumerically)
373 {
374 numericalVerification =
375 await _solutionVerificationService.VerifyAsync(
376 sourceRequest.Instance!,
377 solution,
378 cancellationToken:
379 cancellationToken)
380 .ConfigureAwait(false);
381
382 if (!numericalVerification.IsValid)
383 {
384 diagnostics.Add(
385 Diagnostic(
386 "LSDM-PIPE-030",
388 "solution.numericalVerification",
389 "The solver produced a normalized solution and " +
390 "scientific provenance was captured, but independent " +
391 "numerical solution verification did not validate all " +
392 "requested checks."));
393 }
394 }
395
396 SolutionScientificProvenanceCheckResult? provenanceVerification =
397 null;
398
399 if (request.VerifyProvenance)
400 {
401 provenanceVerification =
402 _provenanceChecker.Check(
403 sourceRequest.Instance!,
404 solution);
405
406 if (!provenanceVerification.IsCoherent)
407 {
408 diagnostics.Add(
409 Diagnostic(
410 "LSDM-PIPE-031",
411 provenanceVerification.HasErrors
414 "solution.provenanceVerification",
415 $"Scientific provenance verification returned " +
416 $"'{provenanceVerification.Kind}'."));
417 }
418 }
419
420 if (diagnostics.Count == 0)
421 {
422 diagnostics.Add(
423 Diagnostic(
424 "LSDM-PIPE-100",
426 "pipeline",
427 "Scientific preflight, solver execution, provenance " +
428 "capture and requested independent verifications " +
429 "completed coherently."));
430 }
431
432 return Result(
434 classification,
435 selection,
436 resolutionPlan,
437 solverRun,
438 provenance,
439 numericalVerification,
440 provenanceVerification,
441 diagnostics);
442 }
443
444 public void RequestStop()
445 {
446 _solverService.RequestStop();
447 }
448
449 private static ScientificSolvePipelineDiagnostic Diagnostic(
450 string code,
452 string path,
453 string message) =>
454 new(
455 code,
456 severity,
457 path,
458 message);
459
460 private static ScientificSolvePipelineResult Result(
462 ScientificClassificationResult classification,
463 ScientificFormulationSelectionResult selection,
464 ScientificResolutionPlan resolutionPlan,
465 SolverRunResult? solverRun,
466 SolutionScientificProvenance? provenance,
467 LotSizingSolutionVerificationResult? numericalVerification,
468 SolutionScientificProvenanceCheckResult? provenanceVerification,
469 IEnumerable<ScientificSolvePipelineDiagnostic> diagnostics) =>
470 new(
471 status,
472 classification,
473 selection,
474 resolutionPlan,
475 solverRun,
476 provenance,
477 numericalVerification,
478 provenanceVerification,
479 diagnostics);
480}
Contains both the independent checker result and information about the application of that result to ...
bool IsValid
Gets whether the requested independent checks all succeeded.
High-level facade for checking a lot-sizing solution and, when requested, recording the independent e...
ScientificLotSizingSolvePipeline(ILotSizingSolverService solverService, MathematicalModelFormulationRegistry formulationRegistry, ScientificClassificationEngine classificationEngine, ScientificFormulationSelectionService formulationSelectionService, ScientificResolutionPlanner resolutionPlanner, LotSizingSolutionVerificationService solutionVerificationService, SolutionScientificProvenanceChecker provenanceChecker)
ScientificLotSizingSolvePipeline(ILotSizingSolverService solverService, MathematicalModelFormulationRegistry formulationRegistry)
async ValueTask< ScientificSolvePipelineResult > SolveAsync(ScientificSolvePipelineRequest request, CancellationToken cancellationToken=default)
Stable structured diagnostic emitted by the end-to-end scientific solve pipeline.
Keeps every evidence channel of one end-to-end scientific solve separate.
Creates the delegated technical solver request while preserving the caller's original request object.
static SolverRequest CreateDelegated(SolverRequest source, string formulationId)
Re-evaluates a solution's recorded scientific provenance against the current instance and current for...
Executes scientific preflight, technical solve, provenance capture and independent verification as on...
ScientificSolvePipelineStatus
High-level status of the end-to-end scientific solve pipeline.