LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
LotSizingSolverService.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Linq;
5using System.Threading;
6using System.Threading.Tasks;
12
14
15/// <summary>
16/// Orchestrates the complete solution of a lot-sizing instance,
17/// from formulation selection to normalized solution mapping.
18/// </summary>
19public sealed class LotSizingSolverService :
21{
22 private readonly IMathematicalModelBuildService
23 _modelBuildService;
24
26 _solverExecutionService;
27
29 _solutionMappingService;
30
32 _formulationRegistry;
33
34 private readonly SolverAdapterRegistry
35 _solverAdapterRegistry;
36
37 private readonly IReadOnlyList<SolverAvailabilityInfo>
38 _availabilityInformation;
39
40 private readonly SolverSelectionOptions
41 _solverSelectionOptions;
42
44 _mappingOptions;
45
46 /// <summary>
47 /// Initializes the high-level lot-sizing solver service
48 /// using the standard service implementations.
49 /// </summary>
50 /// <param name="formulationRegistry">
51 /// Registry containing the mathematical formulations
52 /// available to the application.
53 /// </param>
54 /// <param name="solverAdapterRegistry">
55 /// Registry containing the loaded solver adapters.
56 /// </param>
57 /// <param name="availabilityInformation">
58 /// Availability information for the detected solvers.
59 /// </param>
60 /// <exception cref="ArgumentNullException">
61 /// Thrown when one of the required dependencies is
62 /// <see langword="null"/>.
63 /// </exception>
65 MathematicalModelFormulationRegistry formulationRegistry,
66 SolverAdapterRegistry solverAdapterRegistry,
67 IEnumerable<SolverAvailabilityInfo> availabilityInformation)
68 : this(
73 formulationRegistry,
74 solverAdapterRegistry,
75 availabilityInformation,
78 {
79 }
80
81 /// <summary>
82 /// Initializes the high-level lot-sizing solver service with
83 /// explicitly supplied service implementations and options.
84 /// </summary>
85 /// <param name="modelBuildService">
86 /// Mathematical-model construction service.
87 /// </param>
88 /// <param name="solverExecutionService">
89 /// Mathematical-model solver execution service.
90 /// </param>
91 /// <param name="solutionMappingService">
92 /// Mathematical-solution mapping service.
93 /// </param>
94 /// <param name="formulationRegistry">
95 /// Registry containing available mathematical formulations.
96 /// </param>
97 /// <param name="solverAdapterRegistry">
98 /// Registry containing loaded solver adapters.
99 /// </param>
100 /// <param name="availabilityInformation">
101 /// Availability information associated with detected
102 /// solvers.
103 /// </param>
104 /// <param name="solverSelectionOptions">
105 /// Solver-selection options.
106 /// </param>
107 /// <param name="mappingOptions">
108 /// Mathematical-solution mapping options.
109 /// </param>
110 /// <exception cref="ArgumentNullException">
111 /// Thrown when one of the supplied dependencies is
112 /// <see langword="null"/>.
113 /// </exception>
115 IMathematicalModelBuildService modelBuildService,
116 IMathematicalModelSolverExecutionService solverExecutionService,
117 IMathematicalSolutionMappingService solutionMappingService,
118 MathematicalModelFormulationRegistry formulationRegistry,
119 SolverAdapterRegistry solverAdapterRegistry,
120 IEnumerable<SolverAvailabilityInfo> availabilityInformation,
121 SolverSelectionOptions solverSelectionOptions,
123 {
124 ArgumentNullException.ThrowIfNull(
125 modelBuildService);
126
127 ArgumentNullException.ThrowIfNull(
128 solverExecutionService);
129
130 ArgumentNullException.ThrowIfNull(
131 solutionMappingService);
132
133 ArgumentNullException.ThrowIfNull(
134 formulationRegistry);
135
136 ArgumentNullException.ThrowIfNull(
137 solverAdapterRegistry);
138
139 ArgumentNullException.ThrowIfNull(
140 availabilityInformation);
141
142 ArgumentNullException.ThrowIfNull(
143 solverSelectionOptions);
144
145 ArgumentNullException.ThrowIfNull(
146 mappingOptions);
147
148 _modelBuildService =
149 modelBuildService;
150
151 _solverExecutionService =
152 solverExecutionService;
153
154 _solutionMappingService =
155 solutionMappingService;
156
157 _formulationRegistry =
158 formulationRegistry;
159
160 _solverAdapterRegistry =
161 solverAdapterRegistry;
162
163 _availabilityInformation =
164 availabilityInformation.ToArray();
165
166 _solverSelectionOptions =
167 solverSelectionOptions;
168
169 _mappingOptions =
170 mappingOptions;
171 }
172
173 /// <summary>
174 /// Solves a complete lot-sizing instance.
175 /// </summary>
176 /// <param name="request">
177 /// Normalized solver request.
178 /// </param>
179 /// <param name="cancellationToken">
180 /// Token used to cancel the complete solution workflow.
181 /// </param>
182 /// <returns>
183 /// Normalized solver-run result.
184 /// </returns>
185 /// <exception cref="ArgumentNullException">
186 /// Thrown when <paramref name="request"/> is
187 /// <see langword="null"/>.
188 /// </exception>
189 public async ValueTask<SolverRunResult> SolveAsync(
190 SolverRequest request,
191 CancellationToken cancellationToken = default)
192 {
193 ArgumentNullException.ThrowIfNull(
194 request);
195
196 request.EnsureValid();
197
198 DateTime startedAtUtc =
199 DateTime.UtcNow;
200
201 var stopwatch =
202 Stopwatch.StartNew();
203
204 MathematicalModelBuildResult? buildResult =
205 null;
206
207 MathematicalModelSolveResult? solveResult =
208 null;
209
210 MathematicalSolutionMappingResult? mappingResult =
211 null;
212
213 try
214 {
215 cancellationToken.ThrowIfCancellationRequested();
216
217 var buildOptions =
219 {
220 RequestedFormulationId =
221 request.FormulationName ?? string.Empty,
222
223 AllowFallback =
224 string.IsNullOrWhiteSpace(
225 request.FormulationName),
226
227 ValidateGeneratedModel =
228 true,
229
230 CloneGeneratedModel =
231 false
232 };
233
234 buildResult =
235 await _modelBuildService.BuildAsync(
236 request.Instance!,
237 _formulationRegistry,
238 buildOptions,
239 cancellationToken);
240
241 if (!buildResult.IsSuccessful ||
242 buildResult.Model is null)
243 {
244 stopwatch.Stop();
245
246 return CreateWorkflowFailure(
247 request,
248 startedAtUtc,
249 stopwatch.Elapsed,
250 SolverTerminationReason.ModelError,
251 buildResult.FailureMessage,
252 buildResult.Diagnostics);
253 }
254
255 var mathematicalRequest =
257 {
258 Model =
259 buildResult.Model,
260
261 RunName =
262 request.RunName ?? string.Empty,
263
264 FormulationId =
265 buildResult.SelectedFormulationId,
266
267 Parameters =
268 request.Parameters
269 };
270
271 foreach (
272 var observer
273 in request.ProgressObservers)
274 {
275 mathematicalRequest.ProgressObservers.Add(
276 observer);
277 }
278
279 mathematicalRequest.EnsureValid();
280
281 solveResult =
282 await _solverExecutionService.SolveAsync(
283 mathematicalRequest,
284 request.PreferredSolver,
285 _solverAdapterRegistry,
286 _availabilityInformation,
287 _solverSelectionOptions,
288 cancellationToken);
289
290 if (!solveResult.HasFeasibleSolution)
291 {
292 stopwatch.Stop();
293
294 var failedMapping =
296 "No feasible mathematical solution was " +
297 "available for normalized solution mapping.",
298 TimeSpan.Zero);
299
300 SolverRunResult result =
302 solveResult,
303 failedMapping,
304 startedAtUtc,
305 stopwatch.Elapsed);
306
307 AddDiagnostics(
308 result,
309 buildResult.Diagnostics);
310
311 return result;
312 }
313
314 mappingResult =
315 await _solutionMappingService.MapAsync(
316 request.Instance!,
317 buildResult.Model,
318 solveResult,
319 _mappingOptions,
320 cancellationToken);
321
322 stopwatch.Stop();
323
324 SolverRunResult finalResult =
326 solveResult,
327 mappingResult,
328 startedAtUtc,
329 stopwatch.Elapsed);
330
331 AddDiagnostics(
332 finalResult,
333 buildResult.Diagnostics);
334
335 if (mappingResult.IsSuccessful &&
336 mappingResult.Solution is not null)
337 {
339 objectiveCheck =
341 buildResult.Model,
342 solveResult,
343 _mappingOptions);
344
345 finalResult.RecomputedObjectiveValue =
346 objectiveCheck.RecomputedObjectiveValue;
347
348 finalResult.ObjectiveDifference =
349 objectiveCheck.AbsoluteDifference;
350
351 finalResult.ObjectiveVerificationStatus =
352 objectiveCheck.Status;
353
354 foreach (
355 string diagnostic
356 in objectiveCheck.Diagnostics)
357 {
358 finalResult.AddDiagnostic(
359 diagnostic);
360 }
361
362 if (objectiveCheck.RecomputedObjectiveValue.HasValue)
363 {
364 mappingResult.Solution.Evaluation.ObjectiveValue =
365 objectiveCheck.RecomputedObjectiveValue.Value;
366
367 mappingResult.Solution.Evaluation.ObjectiveValueSource =
368 "Recomputed from normalized mathematical " +
369 "decision-variable values";
370
371 mappingResult.Solution.Evaluation.EvaluatorName =
373
374 mappingResult.Solution.Evaluation.EvaluatorVersion =
375 "1.0";
376
377 mappingResult.Solution.Evaluation.EvaluatedAtUtc =
378 DateTime.UtcNow;
379 }
380 }
381
382 if (!mappingResult.IsSuccessful)
383 {
384 finalResult.AddDiagnostic(
385 "The solver produced a feasible mathematical " +
386 "solution, but mapping to LotSizingSolution " +
387 "failed.");
388 }
389
390 return finalResult;
391 }
392 catch (OperationCanceledException)
393 {
394 stopwatch.Stop();
395
396 return CreateWorkflowFailure(
397 request,
398 startedAtUtc,
399 stopwatch.Elapsed,
400 SolverTerminationReason.UserInterrupted,
401 "The lot-sizing solve workflow was cancelled.",
402 buildResult?.Diagnostics);
403 }
404 catch (Exception exception)
405 {
406 stopwatch.Stop();
407
408 SolverRunResult result =
409 CreateWorkflowFailure(
410 request,
411 startedAtUtc,
412 stopwatch.Elapsed,
413 SolverTerminationReason.InternalError,
414 exception.Message,
415 buildResult?.Diagnostics);
416
417 result.AddDiagnostic(
418 exception.ToString());
419
420 return result;
421 }
422 }
423
424 /// <summary>
425 /// Requests interruption of the currently running native
426 /// mathematical solver.
427 /// </summary>
428 public void RequestStop()
429 {
430 _solverExecutionService.RequestStop();
431 }
432
433 private static SolverRunResult CreateWorkflowFailure(
434 SolverRequest request,
435 DateTime startedAtUtc,
436 TimeSpan elapsed,
437 SolverTerminationReason terminationReason,
438 string? message,
439 IEnumerable<string>? diagnostics)
440 {
441 var result =
443 {
444 RunName =
445 request.RunName ?? string.Empty,
446
447 SolverKind =
448 request.PreferredSolver,
449
450 FormulationName =
451 request.FormulationName ?? string.Empty,
452
453 StartedAtUtc =
454 startedAtUtc,
455
456 CompletedAtUtc =
457 startedAtUtc + elapsed,
458
459 ElapsedSeconds =
460 elapsed.TotalSeconds,
461
462 TerminationReason =
463 terminationReason,
464
465 SolutionCount =
466 0,
467
468 Solution =
469 null
470 };
471
472 AddDiagnostics(
473 result,
474 diagnostics);
475
476 if (!string.IsNullOrWhiteSpace(
477 message))
478 {
479 result.AddDiagnostic(
480 message);
481 }
482
483 return result;
484 }
485
486 private static void AddDiagnostics(
487 SolverRunResult target,
488 IEnumerable<string>? diagnostics)
489 {
490 if (diagnostics is null)
491 {
492 return;
493 }
494
495 foreach (
496 string diagnostic
497 in diagnostics)
498 {
499 if (!string.IsNullOrWhiteSpace(
500 diagnostic))
501 {
502 target.AddDiagnostic(
503 diagnostic);
504 }
505 }
506 }
507}
Stores solver adapters that were loaded successfully and provides lookup services by adapter identifi...
Configures how a solver adapter is selected when automatic solver selection is requested.
double? AbsoluteDifference
Gets or sets the absolute difference between the two objective values.
double? RecomputedObjectiveValue
Gets or sets the objective value recomputed from normalized mathematical-variable values.
Recomputes the objective from the exact normalized numerical representation used by the business solu...
static MathematicalObjectiveRecalculationResult Recalculate(MathematicalModel model, MathematicalModelSolveResult solveResult, MathematicalSolutionMappingOptions mappingOptions)
Recomputes and verifies the objective value.
LotSizingSolverService(MathematicalModelFormulationRegistry formulationRegistry, SolverAdapterRegistry solverAdapterRegistry, IEnumerable< SolverAvailabilityInfo > availabilityInformation)
Initializes the high-level lot-sizing solver service using the standard service implementations.
LotSizingSolverService(IMathematicalModelBuildService modelBuildService, IMathematicalModelSolverExecutionService solverExecutionService, IMathematicalSolutionMappingService solutionMappingService, MathematicalModelFormulationRegistry formulationRegistry, SolverAdapterRegistry solverAdapterRegistry, IEnumerable< SolverAvailabilityInfo > availabilityInformation, SolverSelectionOptions solverSelectionOptions, MathematicalSolutionMappingOptions mappingOptions)
Initializes the high-level lot-sizing solver service with explicitly supplied service implementations...
void RequestStop()
Requests interruption of the currently running native mathematical solver.
async ValueTask< SolverRunResult > SolveAsync(SolverRequest request, CancellationToken cancellationToken=default)
Solves a complete lot-sizing instance.
Describes a request to solve an already built, solver-independent mathematical model.
void EnsureValid()
Validates the mathematical-model solve request.
Stores the solver result for a solver-independent mathematical model.
bool HasFeasibleSolution
Gets or sets a value indicating whether the solver produced at least one feasible solution.
Selects a compatible mathematical-model solver adapter and executes an already built solver-independe...
Creates the public solver-run result from the generic mathematical-model result and the normalized so...
static SolverRunResult Create(MathematicalModelSolveResult solveResult, MathematicalSolutionMappingResult mappingResult, DateTime startedAtUtc, TimeSpan elapsed)
Creates a normalized solver-run result.
Describes a request to solve a lot-sizing instance.
string FormulationName
Gets or sets the formulation name to use.
void EnsureValid()
Validates the solver request.
IList< ISolverProgressObserver > ProgressObservers
Gets the progress observers attached to this request.
SolverKind PreferredSolver
Gets or sets the preferred solver.
LotSizingInstance? Instance
Gets or sets the lot-sizing instance to solve.
Represents the normalized result of a solver execution.
void AddDiagnostic(string message)
Adds a diagnostic message.
Defines the options used to select a formulation and build a solver-independent mathematical model.
Describes the result of building a solver-independent mathematical model from a lot-sizing instance.
List< string > Diagnostics
Gets the diagnostic messages produced during selection and model construction.
string SelectedFormulationId
Gets or sets the selected formulation identifier.
MathematicalModel? Model
Gets or sets the generated mathematical model.
bool IsSuccessful
Gets or sets a value indicating whether model construction succeeded.
Selects a compatible formulation and builds the associated solver-independent mathematical model.
Creates preconfigured mathematical decision-mapper registries for the standard lot-sizing solution mo...
Defines the options used when mapping a mathematical solver result back to a normalized lot-sizing so...
Describes the result of mapping a mathematical solver result back to a normalized lot-sizing solution...
bool IsSuccessful
Gets or sets a value indicating whether mapping succeeded.
LotSizingSolution? Solution
Gets or sets the normalized lot-sizing solution produced by the mapping process.
static MathematicalSolutionMappingResult Failure(string failureMessage, TimeSpan mappingDuration)
Creates a failed mathematical-solution mapping result.
Maps a generic mathematical solver result to a normalized lot-sizing solution and reports mapping dia...
Defines the high-level service used to solve a complete lot-sizing instance from a normalized solver ...
Defines a service that selects a compatible solver adapter and executes an already built solver-indep...
Defines a service that selects a formulation and builds a solver-independent mathematical model.
Defines a service that maps a generic mathematical solver result to a normalized lot-sizing solution.
SolverTerminationReason
Describes why a solver execution terminated.
SolverKind
Identifies a supported mathematical optimization solver.
Definition SolverKind.cs:12