LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
ScientificClassificationEngine.cs
Go to the documentation of this file.
1using LotSizingDataModel.Core;
9
11
12/// <summary>
13/// Single entry point for current scientific classification capabilities.
14/// </summary>
15/// <remarks>
16/// This engine composes existing services; it does not duplicate their rules.
17///
18/// It deliberately does not infer planning paradigms, mathematical
19/// formulations or solution methods.
20/// </remarks>
22{
23 private readonly InstanceModelChecker _modelChecker;
24 private readonly UniversalNotationGenerator _notationGenerator;
25 private readonly UniversalNotationParser _notationParser;
26 private readonly UniversalNotationMatcher _notationMatcher;
27 private readonly LotSizingProblemClassDetector _problemClassDetector;
29 _historicalCapabilityAnalyzer;
30
41
43 InstanceModelChecker modelChecker,
44 UniversalNotationGenerator notationGenerator,
45 UniversalNotationParser notationParser,
46 UniversalNotationMatcher notationMatcher,
47 LotSizingProblemClassDetector problemClassDetector,
49 historicalCapabilityAnalyzer)
50 {
51 _modelChecker =
52 modelChecker ??
53 throw new ArgumentNullException(nameof(modelChecker));
54
55 _notationGenerator =
56 notationGenerator ??
57 throw new ArgumentNullException(nameof(notationGenerator));
58
59 _notationParser =
60 notationParser ??
61 throw new ArgumentNullException(nameof(notationParser));
62
63 _notationMatcher =
64 notationMatcher ??
65 throw new ArgumentNullException(nameof(notationMatcher));
66
67 _problemClassDetector =
68 problemClassDetector ??
69 throw new ArgumentNullException(nameof(problemClassDetector));
70
71 _historicalCapabilityAnalyzer =
72 historicalCapabilityAnalyzer ??
73 throw new ArgumentNullException(
74 nameof(historicalCapabilityAnalyzer));
75 }
76
79 ScientificClassificationRequest? request = null)
80 {
81 ArgumentNullException.ThrowIfNull(descriptor);
82
83 return AnalyzeDescriptorCore(
84 descriptor,
85 request ?? new ScientificClassificationRequest(),
86 instanceValidation: null,
87 initialDiagnostics:
89 }
90
93 ScientificClassificationRequest? request = null)
94 {
95 ArgumentNullException.ThrowIfNull(features);
96
97 return Analyze(
99 features),
100 request);
101 }
102
104 SupplyChain supplyChain,
105 ScientificClassificationRequest? request = null)
106 {
107 ArgumentNullException.ThrowIfNull(supplyChain);
108
109 // A transient wrapper lets SupplyChain analysis honor the exact same
110 // structural/model-validation gate as LotSizingInstance analysis
111 // without mutating or serializing the caller's model.
112 return Analyze(
114 supplyChain),
115 request);
116 }
117
119 LotSizingInstance instance,
120 ScientificClassificationRequest? request = null)
121 {
122 ArgumentNullException.ThrowIfNull(instance);
123
124 ScientificClassificationRequest effectiveRequest =
125 request ??
127
128 InstanceModelCheckResult validation =
129 _modelChecker.Check(instance);
130
131 if (!validation.Capabilities.CanClassify)
132 {
133 return CreateBlockedResult(
134 instanceValidation: validation,
135 diagnostics:
136 new[]
137 {
139 code: "LSDM-SCI-001",
140 severity:
142 .Error,
143 path: "instance",
144 message:
145 "Scientific classification is blocked by " +
146 "instance-model validation.")
147 },
148 declaredNotation:
149 effectiveRequest.DeclaredNotation);
150 }
151
152 return AnalyzeSupplyChain(
153 instance.SupplyChain,
154 effectiveRequest,
155 validation);
156 }
157
158 private ScientificClassificationResult AnalyzeSupplyChain(
159 SupplyChain supplyChain,
161 InstanceModelCheckResult? instanceValidation)
162 {
163 ProductStructureAnalysis productStructureAnalysis =
165 supplyChain);
166
167 LotSizingProblemFeatures features =
169 supplyChain,
170 productStructureAnalysis,
171 request.NumericalTolerance);
172
173 LotSizingProblemDescriptor descriptor =
175 features,
176 supplyChain);
177
178 var diagnostics =
179 new List<ScientificClassificationDiagnostic>();
180
181 diagnostics.AddRange(
182 productStructureAnalysis.Warnings.Select(
183 warning =>
185 code: "LSDM-SCI-002",
186 severity:
188 .Warning,
189 path: "descriptor.structure",
190 message: warning)));
191
192 diagnostics.AddRange(
193 productStructureAnalysis.Errors.Select(
194 error =>
196 code: "LSDM-SCI-003",
197 severity:
199 .Error,
200 path: "descriptor.structure",
201 message: error)));
202
203 if (productStructureAnalysis.Errors.Count > 0)
204 {
205 return CreateBlockedResult(
206 instanceValidation,
207 diagnostics,
208 request.DeclaredNotation,
209 descriptor);
210 }
211
212 return AnalyzeDescriptorCore(
213 descriptor,
214 request,
215 instanceValidation,
216 diagnostics);
217 }
218
219 private ScientificClassificationResult AnalyzeDescriptorCore(
221 ScientificClassificationRequest request,
222 InstanceModelCheckResult? instanceValidation,
223 IEnumerable<ScientificClassificationDiagnostic>
224 initialDiagnostics)
225 {
226 var diagnostics =
227 new List<ScientificClassificationDiagnostic>(
228 initialDiagnostics);
229
230 UniversalLotSizingNotation detectedNotation =
231 _notationGenerator.Generate(
232 descriptor,
233 request.DerivedSemantics);
234
235 IReadOnlyList<LotSizingProblemClassMatchResult>
236 problemClasses =
237 _problemClassDetector.Detect(
238 descriptor);
239
240 LotSizingProblemClassMatchResult? primaryProblemClass =
241 problemClasses.Count == 1
242 ? problemClasses[0]
243 : null;
244
245 if (problemClasses.Count == 0)
246 {
247 diagnostics.Add(
248 new ScientificClassificationDiagnostic(
249 code: "LSDM-SCI-020",
250 severity:
252 .Warning,
253 path: "classification.problemClasses",
254 message:
255 "No currently detectable canonical lot-sizing " +
256 "problem class matches this descriptor."));
257 }
258 else if (problemClasses.Count > 1)
259 {
260 diagnostics.Add(
261 new ScientificClassificationDiagnostic(
262 code: "LSDM-SCI-021",
263 severity:
265 .Warning,
266 path: "classification.problemClasses",
267 message:
268 "More than one canonical problem class is " +
269 "compatible; no primary class was selected."));
270 }
271
272 ScientificNotationComparison notationComparison =
273 CompareDeclaredNotation(
274 descriptor,
275 request,
276 diagnostics);
277
278 IReadOnlyList<HistoricalClassificationCapability>
279 historicalCapabilities =
280 _historicalCapabilityAnalyzer.Analyze(
281 descriptor);
282
283 return new ScientificClassificationResult(
284 isBlocked: false,
285 descriptor,
286 detectedNotation,
287 notationComparison,
288 problemClasses,
289 primaryProblemClass,
290 historicalCapabilities,
291 diagnostics,
292 instanceValidation);
293 }
294
295 private ScientificNotationComparison CompareDeclaredNotation(
296 LotSizingProblemDescriptor descriptor,
297 ScientificClassificationRequest request,
298 ICollection<ScientificClassificationDiagnostic> diagnostics)
299 {
300 if (!request.HasDeclaredNotation)
301 {
302 return new ScientificNotationComparison(
303 string.Empty,
305 }
306
307 UniversalProblemSpecification declaredSpecification;
308
309 try
310 {
311 UniversalLotSizingNotation parsed =
312 _notationParser.Parse(
313 request.DeclaredNotation);
314
315 declaredSpecification =
316 new UniversalProblemSpecification(
317 parsed);
318 }
319 catch (Exception exception)
320 when (
321 exception is FormatException or
322 NotSupportedException or
323 ArgumentException)
324 {
325 diagnostics.Add(
326 new ScientificClassificationDiagnostic(
327 code: "LSDM-SCI-010",
328 severity:
330 .Error,
331 path: "classification.declaredNotation",
332 message:
333 "Declared notation is invalid: " +
334 exception.Message));
335
336 return new ScientificNotationComparison(
337 request.DeclaredNotation,
339 .InvalidDeclaredNotation);
340 }
341
342 UniversalNotationMatchResult match =
343 _notationMatcher.Match(
344 descriptor,
345 declaredSpecification,
346 request.DerivedSemantics);
347
349 match.Kind switch
350 {
351 UniversalNotationMatchKind.Exact =>
353
354 UniversalNotationMatchKind.Compatible =>
356
357 UniversalNotationMatchKind.Incomplete =>
359
360 UniversalNotationMatchKind.Contradiction =>
362
363 _ => throw new ArgumentOutOfRangeException()
364 };
365
366 AppendDeclaredNotationDiagnostic(
367 kind,
368 diagnostics);
369
370 return new ScientificNotationComparison(
371 request.DeclaredNotation,
372 kind,
373 declaredSpecification,
374 match);
375 }
376
377 private static void AppendDeclaredNotationDiagnostic(
379 ICollection<ScientificClassificationDiagnostic> diagnostics)
380 {
381 switch (kind)
382 {
384 case ScientificNotationComparisonKind.NotDeclared:
385 return;
386
387 case ScientificNotationComparisonKind.Compatible:
388 diagnostics.Add(
389 new ScientificClassificationDiagnostic(
390 code: "LSDM-SCI-011",
391 severity:
394 path: "classification.declaredNotation",
395 message:
396 "Declared notation is compatible but less " +
397 "specific than detected instance semantics."));
398 return;
399
400 case ScientificNotationComparisonKind.Incomplete:
401 diagnostics.Add(
402 new ScientificClassificationDiagnostic(
403 code: "LSDM-SCI-012",
404 severity:
406 .Warning,
407 path: "classification.declaredNotation",
408 message:
409 "Declared notation requires semantic " +
410 "information that has not been analyzed."));
411 return;
412
413 case ScientificNotationComparisonKind.Contradiction:
414 diagnostics.Add(
415 new ScientificClassificationDiagnostic(
416 code: "LSDM-SCI-013",
417 severity:
419 .Error,
420 path: "classification.declaredNotation",
421 message:
422 "Declared notation contradicts detected " +
423 "instance semantics."));
424 return;
425
426 case ScientificNotationComparisonKind.InvalidDeclaredNotation:
427 return;
428
429 default:
430 throw new ArgumentOutOfRangeException(
431 nameof(kind),
432 kind,
433 "Unknown notation-comparison kind.");
434 }
435 }
436
437 private static ScientificClassificationResult CreateBlockedResult(
438 InstanceModelCheckResult? instanceValidation,
439 IEnumerable<ScientificClassificationDiagnostic> diagnostics,
440 string declaredNotation,
441 LotSizingProblemDescriptor? descriptor = null)
442 {
443 ScientificNotationComparison notationComparison =
444 string.IsNullOrWhiteSpace(declaredNotation)
445 ? new ScientificNotationComparison(
446 string.Empty,
448 : new ScientificNotationComparison(
449 declaredNotation,
451 .NotEvaluated);
452
453 return new ScientificClassificationResult(
454 isBlocked: true,
455 descriptor,
456 detectedNotation: null,
457 notationComparison,
458 problemClassMatches:
459 Array.Empty<LotSizingProblemClassMatchResult>(),
460 primaryProblemClass: null,
461 historicalCapabilities:
462 Array.Empty<HistoricalClassificationCapability>(),
463 diagnostics,
464 instanceValidation);
465 }
466}
Represents the detailed result of an automatic analysis of a product bill-of-materials graph.
IReadOnlyList< string > Errors
Gets the errors detected during the analysis.
IReadOnlyList< string > Warnings
Gets the non-fatal warnings detected during the analysis.
Analyzes the bill-of-materials graph of a supply-chain instance and determines its product-structure ...
static ProductStructureAnalysis Analyze(SupplyChain supplyChain)
Analyzes the product structure of a supply-chain instance.
Extracts factual lot-sizing problem features from a supply-chain instance.
static LotSizingProblemFeatures Extract(SupplyChain supplyChain, double numericalTolerance=DefaultNumericalTolerance)
Extracts lot-sizing problem features and automatically analyzes the product structure.
Describes the factual structural and modeling features detected in a lot-sizing supply-chain instance...
Typed structured description of the factual lot-sizing problem characteristics currently represented ...
static LotSizingProblemDescriptor FromLegacyFeatures(LotSizingProblemFeatures features)
Adds explicit closed-loop return streams to a lot-sizing instance without changing the historical Sup...
SupplyChain SupplyChain
Gets or sets the supply-chain model represented by the instance.
Matches a typed lot-sizing descriptor against a universal-notation problem specification.
Typed abstract syntax tree for the versioned alpha | beta | gamma scheme.
Generates notation scheme version 1 from typed problem descriptors.
Parses and canonicalizes notation scheme version 1.
Detects all executable canonical problem classes compatible with one descriptor.
Reports historical mapping/detection capability without inventing a historical code that cannot be de...
One structured diagnostic emitted by the scientific classification engine.
ScientificClassificationResult Analyze(SupplyChain supplyChain, ScientificClassificationRequest? request=null)
ScientificClassificationEngine(InstanceModelChecker modelChecker, UniversalNotationGenerator notationGenerator, UniversalNotationParser notationParser, UniversalNotationMatcher notationMatcher, LotSizingProblemClassDetector problemClassDetector, HistoricalClassificationCapabilityAnalyzer historicalCapabilityAnalyzer)
ScientificClassificationResult Analyze(LotSizingInstance instance, ScientificClassificationRequest? request=null)
ScientificClassificationResult Analyze(LotSizingProblemFeatures features, ScientificClassificationRequest? request=null)
ScientificClassificationResult Analyze(LotSizingProblemDescriptor descriptor, ScientificClassificationRequest? request=null)
string DeclaredNotation
Gets notation declared by a file, author, benchmark or caller. It is never overwritten by detected no...
Consolidated, UI-independent scientific classification result.
Represents the complete result of semantic instance validation.
InstanceValidationCapabilities Capabilities
Gets downstream-operation capabilities derived from the validation result.
Checks whether a LotSizingInstance represents a structurally valid and semantically meaningful lot-si...
bool CanClassify
Gets a value indicating whether automatic classification may safely use the instance.
@ NotEvaluated
A declaration exists but scientific classification was blocked before the declaration could be evalua...