LotSizingDataModel.Checker 2.0.1
Reusable validation engine for structural, mathematical and objective consistency checks.
Loading...
Searching...
No Matches
LotSizingSolutionBatchVerificationService.cs
Go to the documentation of this file.
4
6
7/// <summary>
8/// Executes independent lot-sizing solution verification over a collection of
9/// candidates with bounded parallelism and deterministic result ordering.
10/// </summary>
11/// <remarks>
12/// <para>
13/// Unexpected exceptions are isolated per candidate and converted into failed
14/// batch items. They do not stop verification of unrelated candidates.
15/// </para>
16/// <para>
17/// Cancellation is different: when the supplied cancellation token is
18/// cancelled, the operation stops cooperatively and propagates
19/// <see cref="OperationCanceledException"/> to the caller. A cancelled batch is
20/// therefore never presented as a completed validation campaign.
21/// </para>
22/// </remarks>
24{
25 private readonly Func<LotSizingSolutionVerificationService>
26 _verificationServiceFactory;
27
28 /// <summary>
29 /// Initializes the batch service with the standard verification facade.
30 /// </summary>
32 : this(
33 static () =>
35 {
36 }
37
38 /// <summary>
39 /// Initializes the batch service with a factory creating an independent
40 /// verification facade for each concurrently processed candidate.
41 /// </summary>
42 /// <param name="verificationServiceFactory">
43 /// Factory used to create verification facades. It may be used
44 /// concurrently and must return a non-null service on every call.
45 /// </param>
47 Func<LotSizingSolutionVerificationService>
48 verificationServiceFactory)
49 {
50 _verificationServiceFactory =
51 verificationServiceFactory ??
52 throw new ArgumentNullException(
53 nameof(verificationServiceFactory));
54 }
55
56 /// <summary>
57 /// Verifies all candidates with bounded parallelism.
58 /// </summary>
59 /// <param name="candidates">Candidates to verify.</param>
60 /// <param name="options">Optional batch execution configuration.</param>
61 /// <param name="cancellationToken">Cancellation token.</param>
62 /// <returns>
63 /// Per-candidate results in input order plus a checker-only batch summary.
64 /// </returns>
65 public Task<SolutionVerificationBatchResult> VerifyAsync(
66 IEnumerable<SolutionVerificationBatchCandidate> candidates,
68 CancellationToken cancellationToken = default)
69 {
70 return VerifyAsync(
71 candidates,
72 options,
73 progress: null,
74 cancellationToken);
75 }
76
77 /// <summary>
78 /// Verifies all candidates with bounded parallelism and reports completed
79 /// candidate counts while preserving deterministic result ordering.
80 /// </summary>
81 /// <param name="candidates">Candidates to verify.</param>
82 /// <param name="options">Optional batch execution configuration.</param>
83 /// <param name="progress">Optional progress sink.</param>
84 /// <param name="cancellationToken">Cancellation token.</param>
85 /// <returns>
86 /// Per-candidate results in input order plus a checker-only batch summary.
87 /// </returns>
88 public async Task<SolutionVerificationBatchResult> VerifyAsync(
89 IEnumerable<SolutionVerificationBatchCandidate> candidates,
91 IProgress<SolutionVerificationBatchProgress>? progress,
92 CancellationToken cancellationToken = default)
93 {
94 ArgumentNullException.ThrowIfNull(candidates);
95
96 options ??=
98
99 options.EnsureValid();
100
101 cancellationToken.ThrowIfCancellationRequested();
102
103 SolutionVerificationBatchCandidate[] materializedCandidates =
104 MaterializeAndValidateCandidates(candidates);
105
106 ReportProgress(
107 progress,
108 totalCandidateCount: materializedCandidates.Length,
109 completedCandidateCount: 0,
110 lastItem: null);
111
112 if (materializedCandidates.Length == 0)
113 {
115 {
116 Items =
118 CheckSummary =
120 };
121 }
122
123 var results =
125 materializedCandidates.Length];
126
127 int completedCandidateCount =
128 0;
129
130 var parallelOptions =
131 new ParallelOptions
132 {
133 CancellationToken =
134 cancellationToken,
135 MaxDegreeOfParallelism =
136 options.MaxDegreeOfParallelism
137 };
138
139 await Parallel.ForEachAsync(
140 Enumerable.Range(
141 0,
142 materializedCandidates.Length),
143 parallelOptions,
144 async (index, token) =>
145 {
147 materializedCandidates[index];
148
150 await VerifyCandidateAsync(
151 index,
152 candidate,
153 options.VerificationOptions,
154 token)
155 .ConfigureAwait(false);
156
157 results[index] =
158 item;
159
160 int completed =
161 Interlocked.Increment(
162 ref completedCandidateCount);
163
164 ReportProgress(
165 progress,
166 materializedCandidates.Length,
167 completed,
168 item);
169 })
170 .ConfigureAwait(false);
171
172 cancellationToken.ThrowIfCancellationRequested();
173
174 SolutionCheckBatchSummary checkSummary =
175 BuildCheckSummary(results);
176
178 {
179 Items =
180 Array.AsReadOnly(results),
181 CheckSummary =
182 checkSummary
183 };
184 }
185
186 private static void ReportProgress(
187 IProgress<SolutionVerificationBatchProgress>? progress,
188 int totalCandidateCount,
189 int completedCandidateCount,
191 {
192 progress?.Report(
194 {
195 TotalCandidateCount =
196 totalCandidateCount,
197 CompletedCandidateCount =
198 completedCandidateCount,
199 LastCompletedCandidateIndex =
200 lastItem?.Index ?? -1,
201 LastCompletedCandidateKey =
202 lastItem?.CandidateKey,
203 LastExecutionSucceeded =
204 lastItem?.ExecutionSucceeded,
205 LastCandidateIsValid =
206 lastItem is null
207 ? null
208 : lastItem.IsValid
209 });
210 }
211
212 private async Task<SolutionVerificationBatchItemResult>
213 VerifyCandidateAsync(
214 int index,
215 SolutionVerificationBatchCandidate candidate,
216 SolutionVerificationOptions verificationOptions,
217 CancellationToken cancellationToken)
218 {
219 try
220 {
221 cancellationToken.ThrowIfCancellationRequested();
222
223 LotSizingSolutionVerificationService verificationService =
224 _verificationServiceFactory() ??
225 throw new InvalidOperationException(
226 "The verification-service factory returned null.");
227
228 SolutionVerificationOptions candidateOptions =
229 CloneVerificationOptions(
230 verificationOptions);
231
232 LotSizingSolutionVerificationResult verificationResult =
233 candidate.Kind switch
234 {
235 SolutionVerificationBatchCandidateKind.StandaloneSolution =>
236 await verificationService.VerifyAsync(
237 candidate.Instance,
238 candidate.Solution ??
239 throw new InvalidOperationException(
240 "The standalone batch candidate has no solution."),
241 candidateOptions,
242 cancellationToken)
243 .ConfigureAwait(false),
244
245 SolutionVerificationBatchCandidateKind.KnownResult =>
246 await verificationService.VerifyKnownResultAsync(
247 candidate.Instance,
248 candidate.KnownResult ??
249 throw new InvalidOperationException(
250 "The known-result batch candidate has no known result."),
251 candidateOptions,
252 cancellationToken)
253 .ConfigureAwait(false),
254
255 _ =>
256 throw new InvalidOperationException(
257 "The batch candidate kind is not supported.")
258 };
259
260 cancellationToken.ThrowIfCancellationRequested();
261
262 return new SolutionVerificationBatchItemResult
263 {
264 Index =
265 index,
266 CandidateKey =
267 candidate.CandidateKey,
268 CandidateName =
269 candidate.CandidateName,
270 Kind =
271 candidate.Kind,
272 ExecutionSucceeded =
273 true,
274 VerificationResult =
275 verificationResult,
276 Summary =
278 verificationResult.CheckResult)
279 };
280 }
281 catch (OperationCanceledException)
282 when (cancellationToken.IsCancellationRequested)
283 {
284 throw;
285 }
286 catch (Exception exception)
287 {
288 return new SolutionVerificationBatchItemResult
289 {
290 Index =
291 index,
292 CandidateKey =
293 candidate.CandidateKey,
294 CandidateName =
295 candidate.CandidateName,
296 Kind =
297 candidate.Kind,
298 ExecutionSucceeded =
299 false,
300 FailureExceptionType =
301 exception.GetType().FullName ??
302 exception.GetType().Name,
303 FailureMessage =
304 exception.Message
305 };
306 }
307 }
308
309 private static SolutionVerificationBatchCandidate[]
310 MaterializeAndValidateCandidates(
311 IEnumerable<SolutionVerificationBatchCandidate> candidates)
312 {
313 SolutionVerificationBatchCandidate[] materialized =
314 candidates.ToArray();
315
316 for (int index = 0;
317 index < materialized.Length;
318 index++)
319 {
320 if (materialized[index] is null)
321 {
322 throw new ArgumentException(
323 $"The candidate at index {index} is null.",
324 nameof(candidates));
325 }
326 }
327
328 string? duplicateKey =
329 materialized
330 .GroupBy(
331 candidate =>
332 candidate.CandidateKey,
333 StringComparer.Ordinal)
334 .Where(group =>
335 group.Count() > 1)
336 .Select(group =>
337 group.Key)
338 .OrderBy(key => key, StringComparer.Ordinal)
339 .FirstOrDefault();
340
341 if (duplicateKey is not null)
342 {
343 throw new ArgumentException(
344 $"The candidate key '{duplicateKey}' occurs more than once. " +
345 "Batch candidate keys must be unique.",
346 nameof(candidates));
347 }
348
349 return materialized;
350 }
351
352 private static SolutionVerificationOptions CloneVerificationOptions(
353 SolutionVerificationOptions source)
354 {
355 return new SolutionVerificationOptions
356 {
357 ApplyToSolutionEvaluation =
359 UpdateKnownResultFeasibility =
361 PromoteFullyVerifiedKnownResult =
363 EvaluatorName =
364 source.EvaluatorName,
365 EvaluatorVersion =
366 source.EvaluatorVersion,
367 CheckOptions =
368 CloneCheckOptions(
369 source.CheckOptions)
370 };
371 }
372
373 private static SolutionCheckOptions CloneCheckOptions(
374 SolutionCheckOptions source)
375 {
376 return new SolutionCheckOptions
377 {
378 Level =
379 source.Level,
380 FeasibilityTolerance =
382 ZeroTolerance =
383 source.ZeroTolerance,
384 IntegralityTolerance =
386 ObjectiveAbsoluteTolerance =
388 ObjectiveRelativeTolerance =
390 ReportedObjectiveValueOverride =
392 IgnoreDisabledConstraints =
394 ContinueAfterStructuralErrors =
395 source.ContinueAfterStructuralErrors
396 };
397 }
398
399 private static SolutionCheckBatchSummary BuildCheckSummary(
400 IReadOnlyList<SolutionVerificationBatchItemResult> results)
401 {
402 var aggregator =
403 new SolutionCheckBatchAggregator();
404
405 foreach (
406 SolutionVerificationBatchItemResult item
407 in results)
408 {
409 if (!item.ExecutionSucceeded ||
410 item.VerificationResult is null)
411 {
412 continue;
413 }
414
415 aggregator.Add(
416 item.CandidateKey,
417 item.VerificationResult.CheckResult,
418 item.CandidateName);
419 }
420
421 return aggregator.BuildSummary();
422 }
423}
async Task< SolutionVerificationBatchResult > VerifyAsync(IEnumerable< SolutionVerificationBatchCandidate > candidates, SolutionVerificationBatchOptions? options, IProgress< SolutionVerificationBatchProgress >? progress, CancellationToken cancellationToken=default)
Verifies all candidates with bounded parallelism and reports completed candidate counts while preserv...
LotSizingSolutionBatchVerificationService(Func< LotSizingSolutionVerificationService > verificationServiceFactory)
Initializes the batch service with a factory creating an independent verification facade for each con...
LotSizingSolutionBatchVerificationService()
Initializes the batch service with the standard verification facade.
Task< SolutionVerificationBatchResult > VerifyAsync(IEnumerable< SolutionVerificationBatchCandidate > candidates, SolutionVerificationBatchOptions? options=null, CancellationToken cancellationToken=default)
Verifies all candidates with bounded parallelism.
Contains the execution and checker result for one batch candidate.
int Index
Gets the zero-based position of the candidate in the original input.
string CandidateKey
Gets the stable caller-defined candidate identifier.
bool ExecutionSucceeded
Gets whether verification execution completed without an unexpected per-candidate exception.
Configures execution of a batch solution-verification campaign.
SolutionVerificationOptions VerificationOptions
Gets or sets the verification policy applied independently to every candidate.
Reports deterministic progress information for a running batch-verification operation.
Contains the deterministic result of a complete batch-verification run.
double ObjectiveRelativeTolerance
Gets or sets the relative tolerance used when comparing objective values.
bool IgnoreDisabledConstraints
Gets or sets whether disabled mathematical constraints must be ignored.
double ZeroTolerance
Gets or sets the absolute tolerance used to identify numerical zero.
double FeasibilityTolerance
Gets or sets the absolute tolerance used for constraint feasibility.
double ObjectiveAbsoluteTolerance
Gets or sets the absolute tolerance used when comparing objective values.
SolutionCheckLevel Level
Gets or sets the requested checking depth.
double IntegralityTolerance
Gets or sets the tolerance used for integer and binary values.
double? ReportedObjectiveValueOverride
Gets or sets an optional externally reported objective value that takes precedence over the value sto...
Configures the high-level verification facade and the application of checker results back to domain o...
SolutionCheckOptions CheckOptions
Gets or sets the options used by the underlying solution checker.
bool UpdateKnownResultFeasibility
Gets or sets whether an explicitly supplied known result receives the independently established feasi...
bool ApplyToSolutionEvaluation
Gets or sets whether the computed checker result is written to LotSizingSolution.Evaluation.
string EvaluatorVersion
Gets or sets the evaluator version written to the solution evaluation.
string EvaluatorName
Gets or sets the evaluator name written to the solution evaluation.
bool PromoteFullyVerifiedKnownResult
Gets or sets whether a known result is promoted to AutomaticallyVerified when a complete Full check s...
Contains both the independent checker result and information about the application of that result to ...
required SolutionCheckResult CheckResult
Gets the independent checker result.
High-level facade for checking a lot-sizing solution and, when requested, recording the independent e...
Task< LotSizingSolutionVerificationResult > VerifyKnownResultAsync(LotSizingInstance instance, KnownResult knownResult, SolutionVerificationOptions? options=null, CancellationToken cancellationToken=default)
Checks the detailed solution attached to a known result and optionally updates both the detailed solu...
Task< LotSizingSolutionVerificationResult > VerifyAsync(LotSizingInstance instance, LotSizingSolution solution, SolutionVerificationOptions? options=null, CancellationToken cancellationToken=default)
Checks a standalone candidate solution and optionally writes the resulting evaluation into the candid...
Contains aggregated metrics for a collection of checked solutions.
Builds compact summaries from complete checker results.
static SolutionCheckSummary Create(SolutionCheckResult result)
Creates a compact machine-oriented summary.