15 double correctionFactor,
16 bool useLookAheadLookBack,
17 CancellationToken cancellationToken)
19 ArgumentNullException.ThrowIfNull(problem);
20 cancellationToken.ThrowIfCancellationRequested();
26 if (correctionFactor < 0.0 ||
27 correctionFactor > 0.5 ||
28 !
double.IsFinite(correctionFactor))
30 throw new ArgumentOutOfRangeException(
31 nameof(correctionFactor));
34 if (useLookAheadLookBack &&
35 !HasStrictlyPositiveDemand(problem))
37 throw new NotSupportedException(
38 $
"{solverName} conservatively requires strictly positive " +
39 "demand in every period when Look-Ahead/Look-Back is enabled.");
43 var buffer = ArrayPool<int>.Shared.Rent(horizon);
47 var cycleEnds = buffer.AsSpan(0, horizon);
63 while (start < horizon)
65 cancellationToken.ThrowIfCancellationRequested();
67 var end = SelectPpbCycleEnd(
74 if (useLookAheadLookBack)
76 end = ApplyLookAheadLookBack(
85 cycleEnds[start] = end;
101 ArrayPool<int>.Shared.Return(
107 private static int SelectPpbCycleEnd(
108 ReadOnlySpan<double> demands,
112 double correctionFactor)
114 if (holdingCost == 0.0)
116 return demands.Length - 1;
119 var epp = setupCost / holdingCost;
121 var adjustedPartPeriods =
125 "corrected part-period quantity");
129 Math.Abs(epp - adjustedPartPeriods);
131 for (var end = start + 1;
132 end < demands.Length;
135 adjustedPartPeriods = AddFinite(
138 end - start + correctionFactor,
140 "corrected part-period quantity"),
141 "corrected cumulative part-period quantity");
144 Math.Abs(epp - adjustedPartPeriods);
146 if (difference < bestDifference ||
147 (difference == bestDifference &&
150 bestDifference = difference;
154 if (adjustedPartPeriods >= epp)
163 private static int ApplyLookAheadLookBack(
164 ReadOnlySpan<double> demands,
169 double correctionFactor)
173 if (next >= demands.Length)
178 var coverage = end - start + 1;
183 if (next + 1 < demands.Length)
185 var incrementalCurrentLotCost =
191 "Look-Ahead gate quantity"),
192 "Look-Ahead gate cost");
194 if (incrementalCurrentLotCost <= setupCost)
199 var currentPatternCost =
206 "Look-Ahead current cost"),
208 1.0 + correctionFactor,
210 "Look-Ahead current cost"),
211 "Look-Ahead current cost"),
212 "Look-Ahead current cost");
214 var shiftedPatternCost =
219 coverage + correctionFactor,
221 "Look-Ahead shifted cost"),
225 "Look-Ahead shifted cost"),
226 "Look-Ahead shifted cost"),
227 "Look-Ahead shifted cost");
229 if (shiftedPatternCost < currentPatternCost)
241 var currentPatternCost =
246 coverage - 1.0 + correctionFactor,
248 "Look-Back current cost"),
252 "Look-Back current cost"),
253 "Look-Back current cost"),
254 "Look-Back current cost");
256 var shiftedPatternCost =
263 "Look-Back shifted cost"),
265 1.0 + correctionFactor,
267 "Look-Back shifted cost"),
268 "Look-Back shifted cost"),
269 "Look-Back shifted cost");
271 if (shiftedPatternCost < currentPatternCost)
280 private static bool HasStrictlyPositiveDemand(
286 period < demands.Length;
289 if (!(demands[period] > 0.0))
298 private static double AddFinite(
303 var value = left + right;
305 if (!
double.IsFinite(value))
307 throw new ArithmeticException(
308 $
"Numerical overflow while computing {operation}.");
314 private static double MultiplyFinite(
319 var value = left * right;
321 if (!
double.IsFinite(value))
323 throw new ArithmeticException(
324 $
"Numerical overflow while computing {operation}.");