33 public string Name =>
"Wagner-Whitin classical";
41 CancellationToken cancellationToken =
default)
43 ArgumentNullException.ThrowIfNull(problem);
44 cancellationToken.ThrowIfCancellationRequested();
47 var arcCosts =
new double[checked(horizon * horizon)];
49 BuildArcCostMatrix(problem, arcCosts, cancellationToken);
51 var value =
new double[horizon + 1];
52 var predecessor =
new int[horizon + 1];
54 Array.Fill(value,
double.PositiveInfinity);
55 Array.Fill(predecessor, -1);
58 for (var end = 1; end <= horizon; end++)
60 cancellationToken.ThrowIfCancellationRequested();
62 var best =
double.PositiveInfinity;
65 for (var start = 0; start < end; start++)
67 var arcCost = arcCosts[(start * horizon) + (end - 1)];
68 var candidate = value[start] + arcCost;
77 if (!
double.IsFinite(best) || bestStart < 0)
79 throw new ArithmeticException(
80 $
"No finite Wagner-Whitin value was obtained for horizon prefix {end}.");
84 predecessor[end] = bestStart;
94 private static void BuildArcCostMatrix(
96 Span<double> arcCosts,
97 CancellationToken cancellationToken)
105 for (var start = 0; start < horizon; start++)
107 cancellationToken.ThrowIfCancellationRequested();
109 var deliveredUnitCost = productionCosts[start];
111 var cumulativeDemand = 0.0;
113 for (var end = start; end < horizon; end++)
115 cumulativeDemand += demands[end];
117 batchCost += demands[end] * deliveredUnitCost;
119 if (!
double.IsFinite(batchCost))
121 throw new ArithmeticException(
122 "Numerical overflow while computing a Wagner-Whitin arc cost.");
125 arcCosts[(start * horizon) + end] =
126 cumulativeDemand > 0.0
127 ? setupCosts[start] + batchCost
130 if (!
double.IsFinite(arcCosts[(start * horizon) + end]))
132 throw new ArithmeticException(
133 "Numerical overflow while computing a Wagner-Whitin regeneration interval.");
136 if (end < horizon - 1)
138 deliveredUnitCost += holdingCosts[end];
140 if (!
double.IsFinite(deliveredUnitCost))
142 throw new ArithmeticException(
143 "Numerical overflow while accumulating delivered unit cost.");