38 LotSizingInstance instance,
39 LotSizingSolution solution,
42 ArgumentNullException.ThrowIfNull(instance);
43 ArgumentNullException.ThrowIfNull(solution);
44 ArgumentNullException.ThrowIfNull(options);
55 AreVariableDomainsValid =
59 IsObjectiveConsistent =
65 AddFilteredExistingValidationIssues(
75 result.AddIssue(schedulingIssue);
78 CompareExpectedDecisionStructure(
83 catch (Exception exception)
93 "Structural solution checking failed: " +
98 result.IsStructurallyValid =
107 private static void AddFilteredExistingValidationIssues(
108 LotSizingInstance instance,
109 LotSizingSolution solution,
114 new LotSizingSolutionValidator(
117 IReadOnlyList<SolutionValidationIssue> sourceIssues =
120 instance.SupplyChain);
123 SolutionValidationIssue sourceIssue
126 if (!IsStructuralIssue(
136 sourceIssue.Severity ==
137 SolutionValidationSeverity.Error
145 string.IsNullOrWhiteSpace(
151 $
"{sourceIssue.Code}: " +
157 private static bool IsStructuralIssue(
160 if (
string.IsNullOrWhiteSpace(code))
199 StringComparison.Ordinal) &&
202 StringComparison.Ordinal))
212 return code.StartsWith(
214 StringComparison.Ordinal);
217 private static void CompareExpectedDecisionStructure(
218 LotSizingInstance instance,
219 LotSizingSolution solution,
220 SolutionCheckResult result)
222 LotSizingSolution expected =
223 LotSizingSolution.CreateFor(
224 instance.SupplyChain,
226 solution.InstanceIdentifier,
228 "Expected checker structure",
230 solution.InstanceFingerprint,
236 expected.ProductionDecisions.Select(
238 solution.ProductionDecisions.Select(
244 expected.InventoryDecisions.Select(
246 solution.InventoryDecisions.Select(
252 expected.TransportDecisions.Select(
254 solution.TransportDecisions.Select(
260 expected.PurchaseDecisions.Select(
262 solution.PurchaseDecisions.Select(
268 expected.DistributionDecisions.Select(
270 solution.DistributionDecisions.Select(
295 private static void CompareKeySets(
297 IEnumerable<string> expectedKeys,
298 IEnumerable<string> actualKeys,
299 SolutionCheckResult result)
301 HashSet<string> expected =
302 expectedKeys.ToHashSet(
303 StringComparer.Ordinal);
305 HashSet<string> actual =
306 actualKeys.ToHashSet(
307 StringComparer.Ordinal);
314 StringComparer.Ordinal)
317 StringComparer.Ordinal))
320 new SolutionCheckIssue
332 $
"Missing {familyName} decision " +
342 StringComparer.Ordinal)
345 StringComparer.Ordinal))
348 new SolutionCheckIssue
360 $
"Unexpected {familyName} decision " +
361 $
"'{unexpectedKey}'."
366 private static string GetProductionKey(
367 ProductionDecision decision)
370 $
"production|routing={decision.RoutingId}";
373 private static string GetInventoryKey(
374 InventoryDecision decision)
377 $
"inventory|item={decision.ItemId}" +
378 $
"|warehouse={GetWarehouseKey(decision.Warehouse)}";
381 private static string GetTransportKey(
382 TransportDecision decision)
385 $
"transport|item={decision.ItemId}" +
386 $
"|resource={decision.TransportResourceId}" +
387 $
"|origin={GetWarehouseKey(decision.Origin)}" +
388 $
"|destination={GetWarehouseKey(decision.Destination)}";
391 private static string GetPurchaseKey(
392 PurchaseDecision decision)
395 $
"purchase|supplier={decision.SupplierId}" +
396 $
"|item={decision.ItemId}" +
398 $
"{GetWarehouseKey(decision.DestinationWarehouse)}";
401 private static string GetDistributionKey(
402 DistributionDecision decision)
405 $
"distribution|center={decision.DistributionCenterId}" +
406 $
"|item={decision.ItemId}" +
407 $
"|warehouse={GetWarehouseKey(decision.Warehouse)}";
410 private static string GetWarehouseKey(
411 LotSizingDataModel.Core.PhysicalModel.WarehouseReference warehouse)
414 $
"{warehouse.Kind}:{warehouse.ReferenceId}";
SolutionCheckResult Check(LotSizingInstance instance, LotSizingSolution solution, SolutionCheckOptions options)
Checks solution structure, decision keys, planning horizons, duplicate decisions, entity references,...