32 ScientificClassificationEngine classificationEngine,
33 UniversalNotationMatcher notationMatcher,
34 ScientificFormulationCompatibilityService
35 formulationCompatibilityService)
37 _classificationEngine =
38 classificationEngine ??
39 throw new ArgumentNullException(nameof(classificationEngine));
43 throw new ArgumentNullException(nameof(notationMatcher));
45 _formulationCompatibilityService =
46 formulationCompatibilityService ??
47 throw new ArgumentNullException(
48 nameof(formulationCompatibilityService));
52 LotSizingInstance instance,
53 LotSizingSolution solution)
55 ArgumentNullException.ThrowIfNull(instance);
56 ArgumentNullException.ThrowIfNull(solution);
58 SolutionScientificProvenanceReadResult read =
59 SolutionScientificProvenanceCodec.Read(
60 solution.GenerationMetadata);
64 SolutionScientificProvenanceReadKind.Missing)
74 "solution.generationMetadata",
75 "Scientific solution provenance is not recorded."));
80 SolutionScientificProvenanceReadKind.Invalid)
90 "solution.generationMetadata",
94 SolutionScientificProvenance provenance =
97 ScientificClassificationResult classification =
98 _classificationEngine.Analyze(instance);
101 classification.IsBlocked ||
102 classification.Descriptor is
null)
113 "Current instance scientific classification is blocked."));
117 new List<SolutionScientificProvenanceDiagnostic>();
122 UniversalNotationMatchResult notationMatch;
127 _notationMatcher.Match(
128 classification.Descriptor,
129 provenance.DetectedNotation);
131 catch (Exception exception)
133 exception is FormatException or
134 NotSupportedException or
145 "solution.provenance.detectedNotation",
146 "Recorded detected notation is invalid: " +
150 switch (notationMatch.Kind)
152 case UniversalNotationMatchKind.Exact:
155 case UniversalNotationMatchKind.Compatible:
165 "solution.provenance.detectedNotation",
166 "Recorded notation remains compatible but is less " +
167 "specific than current detected semantics."));
170 case UniversalNotationMatchKind.Incomplete:
180 "solution.provenance.detectedNotation",
181 "Current analysis lacks information required to fully " +
182 "revalidate the recorded notation."));
185 case UniversalNotationMatchKind.Contradiction:
195 "solution.provenance.detectedNotation",
196 "Recorded detected notation contradicts current " +
197 "instance semantics."));
201 throw new ArgumentOutOfRangeException();
204 if (classification.PrimaryProblemClass is
null)
215 "classification.problemClass",
216 "Current instance has no unique canonical problem class."));
219 !classification.PrimaryProblemClass.Definition.Code.Equals(
220 provenance.CanonicalProblemClassCode,
221 StringComparison.OrdinalIgnoreCase))
232 "solution.provenance.problemClassCode",
233 $
"Recorded problem class " +
234 $
"'{provenance.CanonicalProblemClassCode}' differs from " +
236 $
"'{classification.PrimaryProblemClass.Definition.Code}'."));
239 !classification.PrimaryProblemClass.Kind.ToString().Equals(
240 provenance.ProblemClassMatchKind,
241 StringComparison.Ordinal))
252 "solution.provenance.problemClassMatchKind",
253 "Canonical problem class is unchanged, but core/extension " +
254 "match status has changed."));
257 ScientificFormulationCompatibilityResult compatibility =
258 _formulationCompatibilityService.Assess(
260 provenance.FormulationId);
262 switch (compatibility.Kind)
264 case ScientificFormulationCompatibilityKind.Compatible:
267 case ScientificFormulationCompatibilityKind.Undetermined:
268 case ScientificFormulationCompatibilityKind.Blocked:
278 "solution.provenance.formulationId",
279 "Current scientific formulation compatibility cannot " +
280 "be established completely."));
283 case ScientificFormulationCompatibilityKind.Incompatible:
293 "solution.provenance.formulationId",
294 "Recorded formulation is scientifically incompatible " +
295 "with the current instance classification."));
299 throw new ArgumentOutOfRangeException();
303 compatibility.Profile is not
null &&
304 !
string.IsNullOrWhiteSpace(provenance.FormulationFamily) &&
305 !compatibility.Profile.FormulationFamily.Equals(
306 provenance.FormulationFamily,
307 StringComparison.Ordinal))
318 "solution.provenance.formulationFamily",
319 "The formulation identifier is unchanged but its current " +
320 "scientific family label differs from the recorded one."));
323 if (provenance.IsLegacySchema)
334 "solution.provenance.schemaVersion",
335 "Legacy scientific provenance schema v1 has no explicit " +
336 "solution-method/backend evidence."));
340 ScientificSolutionMethodDefinition? method =
341 ScientificSolutionMethodCatalog.Find(
342 provenance.SolutionMethodId);
355 "solution.provenance.solutionMethodId",
356 $
"Recorded solution method " +
357 $
"'{provenance.SolutionMethodId}' is not present in " +
358 "the current scientific method catalog."));
361 classification.PrimaryProblemClass is not
null &&
362 !method.IsApplicableTo(
363 classification.PrimaryProblemClass.Definition.Id))
374 "solution.provenance.solutionMethodId",
375 $
"Recorded solution method '{method.MethodId}' is not " +
376 "applicable to the current canonical problem class."));
381 provenance.SolverBackendKind,
383 out SolverKind backendKind))
394 "solution.provenance.solverBackendKind",
395 $
"Recorded solver backend " +
396 $
"'{provenance.SolverBackendKind}' is unknown."));
398 else if (method is not
null)
400 ScientificSolverBackendDefinition? backend =
401 ScientificSolverBackendCatalog.Find(
405 method.RequiresMilpBackend &&
408 !backend.Supports(method)
420 "solution.provenance.solverBackendKind",
421 $
"Recorded backend '{backendKind}' is not " +
422 $
"compatible with recorded solution method " +
423 $
"'{method.MethodId}'."));
428 if (diagnostics.Count == 0)
434 "solution.provenance",
435 "Recorded scientific provenance is coherent with the " +
436 "current instance and formulation capability catalog."));