14 private const int CancellationCheckMask = 255;
19 CancellationToken cancellationToken)
24 FedergruenTzurLinearMode.NoSpeculativeMotive,
31 CancellationToken cancellationToken)
36 FedergruenTzurLinearMode.NondecreasingSetupCosts,
43 FedergruenTzurLinearMode mode,
44 CancellationToken cancellationToken)
48 var valueBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
49 var predecessorBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
53 var value = valueBuffer.AsSpan(0, horizon + 1);
54 var predecessor = predecessorBuffer.AsSpan(0, horizon + 1);
64 var cumulativeDemand = 0.0;
65 var cumulativeHoldingBefore = 0.0;
66 var firstPeriodHoldingCost = 0.0;
68 using var candidates =
71 for (var period = 0; period < horizon; period++)
73 if ((period & CancellationCheckMask) == 0)
75 cancellationToken.ThrowIfCancellationRequested();
78 var previousCumulativeDemand = cumulativeDemand;
80 cumulativeDemand = AddFinite(
85 firstPeriodHoldingCost = AddFinite(
86 firstPeriodHoldingCost,
89 cumulativeHoldingBefore,
90 "first-period-order holding-cost transform"),
91 "first-period-order holding-cost transform");
95 var transformedVariableCost =
96 productionCosts[period] -
97 cumulativeHoldingBefore;
100 transformedVariableCost,
101 "transformed variable production cost");
103 var intercept = value[period];
105 intercept = AddFinite(
108 "candidate intercept");
110 intercept = AddFinite(
112 -firstPeriodHoldingCost,
113 "candidate intercept");
115 intercept = AddFinite(
119 cumulativeHoldingBefore,
120 "candidate intercept"),
121 "candidate intercept");
123 intercept = AddFinite(
126 productionCosts[period],
127 previousCumulativeDemand,
128 "candidate intercept"),
129 "candidate intercept");
131 var shouldInsert = candidates.IsEmpty;
136 mode == FedergruenTzurLinearMode.NoSpeculativeMotive ||
137 transformedVariableCost < candidates.LastSlope;
142 candidates.AddMonotone(
144 transformedVariableCost,
149 candidates.GetBestAndDiscardPast(
152 var bestLineValue = AddFinite(
153 candidates.BestIntercept,
155 candidates.BestSlope,
157 "candidate line evaluation"),
158 "candidate line evaluation");
160 var orderValue = AddFinite(
161 firstPeriodHoldingCost,
163 "forward dynamic-programming value");
165 if (demands[period] == 0.0 &&
166 value[period] <= orderValue)
168 value[period + 1] = value[period];
169 predecessor[period + 1] = period;
173 value[period + 1] = orderValue;
174 predecessor[period + 1] = bestPeriod;
177 cumulativeHoldingBefore = AddFinite(
178 cumulativeHoldingBefore,
179 holdingCosts[period],
180 "cumulative holding cost");
183 cancellationToken.ThrowIfCancellationRequested();
185 return ZeroInventoryOrderSolutionBuilder.Build(
193 ArrayPool<double>.Shared.Return(
197 ArrayPool<int>.Shared.Return(
203 private static double AddFinite(
208 var value = left + right;
209 EnsureFinite(value, operation);
213 private static double MultiplyFinite(
218 var value = left * right;
219 EnsureFinite(value, operation);
223 private static void EnsureFinite(
227 if (!
double.IsFinite(value))
229 throw new ArithmeticException(
230 $
"Numerical overflow while computing {operation}.");
234 private enum FedergruenTzurLinearMode
237 NondecreasingSetupCosts