47 private const double ScoreComparisonTolerance =
82 SupplyChain supplyChain,
84 string supplyChainFingerprint =
"",
85 double numericalTolerance =
87 .DefaultNumericalTolerance)
89 ArgumentNullException.ThrowIfNull(supplyChain);
90 ArgumentNullException.ThrowIfNull(catalog);
101 productStructureAnalysis,
107 supplyChainFingerprint,
109 productStructureAnalysis.
Errors);
142 string supplyChainFingerprint =
"")
144 ArgumentNullException.ThrowIfNull(features);
145 ArgumentNullException.ThrowIfNull(catalog);
152 supplyChainFingerprint,
153 Array.Empty<
string>(),
154 Array.Empty<
string>());
160 string supplyChainFingerprint,
161 IEnumerable<string> initialWarnings,
162 IEnumerable<string> initialErrors)
179 SupplyChainFingerprint =
180 supplyChainFingerprint?.Trim() ??
185 NormalizeMessages(initialWarnings);
188 NormalizeMessages(initialErrors);
193 "The extracted feature profile does not " +
194 "contain the minimum structural information " +
195 "required for problem classification.");
198 if (errors.Count > 0)
200 classification.ReplaceWarnings(warnings);
201 classification.ReplaceErrors(errors);
203 classification.Status =
206 return classification;
209 var evaluatedMatches =
210 new List<EvaluatedProblemTypeMatch>();
212 foreach (KnownProblemTypeDefinition definition
215 EvaluatedProblemTypeMatch? evaluatedMatch =
222 if (evaluatedMatch is not
null)
224 evaluatedMatches.Add(
229 if (errors.Count > 0)
231 classification.ReplaceWarnings(warnings);
232 classification.ReplaceErrors(errors);
234 classification.Status =
237 return classification;
240 KnownProblemTypeMatch[] orderedMatches =
244 evaluatedMatch.Match)
247 GetMatchDisplayOrder(
254 match.ProblemTypeCode,
255 StringComparer.OrdinalIgnoreCase)
258 classification.ReplaceMatches(
261 SelectPrimaryMatchAndStatus(
265 classification.ReplaceWarnings(
268 classification.ReplaceErrors(
271 return classification;
274 private static EvaluatedProblemTypeMatch?
276 KnownProblemTypeDefinition definition,
277 KnownProblemTypeCatalog catalog,
278 LotSizingProblemFeatures features,
279 ICollection<string> errors)
282 new List<ClassificationEvidence>();
284 var additionalFeatureCodes =
286 StringComparer.OrdinalIgnoreCase);
288 bool evaluationFailed =
291 EvaluateStandardRuleCategory(
292 definition.RequiredRuleCodes,
298 ref evaluationFailed);
300 EvaluateStandardRuleCategory(
301 definition.OptionalRuleCodes,
307 ref evaluationFailed);
309 EvaluateExtensionRules(
310 definition.ExtensionRuleCodes,
314 additionalFeatureCodes,
316 ref evaluationFailed);
318 EvaluateExclusionRules(
319 definition.ExclusionRuleCodes,
324 ref evaluationFailed);
326 if (evaluationFailed)
332 new KnownProblemTypeMatch(
343 definition.DefaultScope)
346 definition.DefinitionVersion
349 match.ReplaceEvidence(
352 match.ReplaceAdditionalFeatureCodes(
353 additionalFeatureCodes);
355 match.UpdateScoreFromEvidence();
357 if (!match.HasBlockingMismatches)
360 match.HasAdditionalFeatures
361 ? ProblemMatchKind.KnownExtension
364 return new EvaluatedProblemTypeMatch(
369 if (match.Score + ScoreComparisonTolerance <
370 definition.ClosestMatchThreshold)
378 return new EvaluatedProblemTypeMatch(
383 private static void EvaluateStandardRuleCategory(
384 IEnumerable<string> ruleCodes,
385 KnownProblemTypeCatalog catalog,
386 LotSizingProblemFeatures features,
388 ICollection<ClassificationEvidence> evidence,
389 ICollection<string> errors,
390 ref
bool evaluationFailed)
392 foreach (
string ruleCode
in ruleCodes)
394 KnownProblemRuleDefinition? rule =
395 catalog.FindRule(ruleCode);
400 $
"Classification rule '{ruleCode}' " +
401 "could not be resolved.");
403 evaluationFailed =
true;
408 KnownProblemRuleEvaluator.TryEvaluate(
412 out ClassificationEvidence?
414 out
string errorMessage);
417 evaluatedEvidence is
null)
420 $
"Rule '{ruleCode}' could not be " +
421 $
"evaluated: {errorMessage}");
423 evaluationFailed =
true;
432 private static void EvaluateExtensionRules(
433 IEnumerable<string> ruleCodes,
434 KnownProblemTypeCatalog catalog,
435 LotSizingProblemFeatures features,
436 ICollection<ClassificationEvidence> evidence,
437 ISet<string> additionalFeatureCodes,
438 ICollection<string> errors,
439 ref
bool evaluationFailed)
441 foreach (
string ruleCode
in ruleCodes)
443 KnownProblemRuleDefinition? rule =
444 catalog.FindRule(ruleCode);
449 $
"Extension rule '{ruleCode}' could not " +
452 evaluationFailed =
true;
457 KnownProblemRuleEvaluator.TryEvaluate(
461 out ClassificationEvidence?
463 out
string errorMessage);
466 extensionEvidence is
null)
469 $
"Extension rule '{ruleCode}' could not " +
470 $
"be evaluated: {errorMessage}");
472 evaluationFailed =
true;
480 extensionEvidence.Weight = 0.0;
482 if (extensionEvidence.IsSatisfied)
484 additionalFeatureCodes.Add(
487 extensionEvidence.Comment =
488 "The condition identifies an additional " +
489 "feature not included in the classical " +
490 "problem-family definition.";
494 extensionEvidence.Comment =
495 "The possible extension is not present " +
496 "in the analyzed instance.";
504 private static void EvaluateExclusionRules(
505 IEnumerable<string> ruleCodes,
506 KnownProblemTypeCatalog catalog,
507 LotSizingProblemFeatures features,
508 ICollection<ClassificationEvidence> evidence,
509 ICollection<string> errors,
510 ref
bool evaluationFailed)
512 foreach (
string ruleCode
in ruleCodes)
514 KnownProblemRuleDefinition? rule =
515 catalog.FindRule(ruleCode);
520 $
"Exclusion rule '{ruleCode}' could not " +
523 evaluationFailed =
true;
528 KnownProblemRuleEvaluator.TryEvaluate(
532 out ClassificationEvidence?
534 out
string errorMessage);
540 $
"Exclusion rule '{ruleCode}' could not " +
541 $
"be evaluated: {errorMessage}");
543 evaluationFailed =
true;
554 var exclusionEvidence =
555 new ClassificationEvidence(
557 rawEvidence.FeatureCode,
560 "exclusion condition absent",
563 rawEvidence.IsSatisfied
564 ?
"exclusion condition present"
565 :
"exclusion condition absent",
568 !rawEvidence.IsSatisfied,
574 string.IsNullOrWhiteSpace(
575 rawEvidence.Description)
576 ?
"The exclusion condition must " +
578 : rawEvidence.Description)
581 rawEvidence.RuleCode,
587 $
"Evaluated exclusion condition: " +
588 $
"expected '{rawEvidence.ExpectedValue}', " +
589 $
"observed '{rawEvidence.ObservedValue}'."
597 private static void SelectPrimaryMatchAndStatus(
598 LotSizingProblemClassification classification,
599 IReadOnlyCollection<EvaluatedProblemTypeMatch>
602 EvaluatedProblemTypeMatch[] directCandidates =
608 .CanBePrimaryMatch &&
614 .AppliesToCompleteProblem)
617 GetDirectMatchQuality(
618 evaluatedMatch.Match))
621 evaluatedMatch.Match.Score)
637 StringComparer.OrdinalIgnoreCase)
640 if (directCandidates.Length == 0)
642 bool hasRecognizedPartialStructure =
643 classification.Matches.Any(
647 .RecognizedRelaxation ||
650 .RecognizedSubproblem);
652 classification.Status =
653 hasRecognizedPartialStructure
662 EvaluatedProblemTypeMatch bestCandidate =
665 EvaluatedProblemTypeMatch[] equivalentCandidates =
669 AreEquivalentPrimaryCandidates(
674 if (equivalentCandidates.Length > 1)
676 classification.Status =
679 classification.Comment =
680 "Several known problem families have the " +
681 "same primary-selection rank: " +
684 equivalentCandidates.Select(
693 classification.SetPrimaryMatch(
694 bestCandidate.Match);
696 classification.ReplaceUnclassifiedFeatureCodes(
698 .AdditionalFeatureCodes);
700 classification.Status =
701 bestCandidate.Match.MatchKind ==
702 ProblemMatchKind.Exact &&
703 !classification.HasUnclassifiedFeatures
704 ? ProblemClassificationStatus.Classified
706 .PartiallyClassified;
709 private static bool AreEquivalentPrimaryCandidates(
710 EvaluatedProblemTypeMatch first,
711 EvaluatedProblemTypeMatch second)
714 GetDirectMatchQuality(first.Match) ==
715 GetDirectMatchQuality(second.Match) &&
719 second.Match.Score) <=
720 ScoreComparisonTolerance &&
722 first.Definition.RequiredRuleCodes.Count ==
723 second.Definition.RequiredRuleCodes.Count &&
725 first.Definition.Priority ==
726 second.Definition.Priority;
729 private static int GetDirectMatchQuality(
730 KnownProblemTypeMatch match)
732 return match.MatchKind
switch
734 ProblemMatchKind.Exact =>
737 ProblemMatchKind.KnownExtension =>
745 private static int GetMatchDisplayOrder(
748 return matchKind
switch
750 ProblemMatchKind.Exact =>
753 ProblemMatchKind.KnownExtension =>
757 .RecognizedSubproblem =>
761 .RecognizedRelaxation =>
765 .ClosestKnownFamily =>
773 private static List<string> NormalizeMessages(
774 IEnumerable<string> messages)
776 ArgumentNullException.ThrowIfNull(messages);
781 !
string.IsNullOrWhiteSpace(
787 StringComparer.Ordinal)
791 StringComparer.Ordinal)
795 private sealed class EvaluatedProblemTypeMatch
797 public EvaluatedProblemTypeMatch(
798 KnownProblemTypeDefinition definition,
799 KnownProblemTypeMatch match)
803 throw new ArgumentNullException(
808 throw new ArgumentNullException(
812 public KnownProblemTypeDefinition Definition
817 public KnownProblemTypeMatch Match