86 ArgumentNullException.ThrowIfNull(problem);
91 for (var period = 0; period < problem.Horizon - 1; period++)
94 productionCosts[period] +
97 if (!
double.IsFinite(deliveredNext) ||
98 deliveredNext < productionCosts[period + 1])
114 CancellationToken cancellationToken =
default)
116 ArgumentNullException.ThrowIfNull(problem);
117 cancellationToken.ThrowIfCancellationRequested();
121 throw new NotSupportedException(
122 "BahlTajPlanningHorizonSolver requires " +
123 "p[t] + h[t] >= p[t+1] for every adjacent period.");
129 ArrayPool<double>.Shared.Rent(horizon + 1);
131 var predecessorBuffer =
132 ArrayPool<int>.Shared.Rent(horizon + 1);
134 var intervalCostBuffer =
135 ArrayPool<double>.Shared.Rent(horizon);
137 var deliveredCostBuffer =
138 ArrayPool<double>.Shared.Rent(horizon);
140 var cumulativeDemandBuffer =
141 ArrayPool<double>.Shared.Rent(horizon);
146 valueBuffer.AsSpan(0, horizon + 1);
149 predecessorBuffer.AsSpan(0, horizon + 1);
152 intervalCostBuffer.AsSpan(0, horizon);
155 deliveredCostBuffer.AsSpan(0, horizon);
157 var cumulativeDemand =
158 cumulativeDemandBuffer.AsSpan(0, horizon);
160 value.Fill(
double.PositiveInfinity);
161 predecessor.Fill(-1);
162 intervalCost.Clear();
163 deliveredCost.Clear();
164 cumulativeDemand.Clear();
176 var planningHorizonStart = 0;
178 for (var end = 0; end < horizon; end++)
180 if ((end & CancellationCheckMask) == 0)
182 cancellationToken.ThrowIfCancellationRequested();
188 deliveredCost[end] = productionCosts[end];
189 intervalCost[end] = 0.0;
190 cumulativeDemand[end] = 0.0;
192 if (demands[end] == 0.0)
200 value[end + 1] = value[end];
201 predecessor[end + 1] = end;
203 AdvanceDeliveredCosts(
204 planningHorizonStart,
212 var best =
double.PositiveInfinity;
215 for (var start = planningHorizonStart;
219 cumulativeDemand[start] = AddFinite(
220 cumulativeDemand[start],
222 "candidate cumulative demand");
224 intervalCost[start] = AddFinite(
228 deliveredCost[start],
229 "candidate delivered demand cost"),
230 "candidate regeneration-interval cost");
232 var regenerationCost = AddFinite(
235 "candidate setup plus regeneration cost");
237 var candidate = AddFinite(
240 "forward dynamic-programming candidate");
245 if (candidate < best ||
246 (candidate == best && start > bestStart))
253 if (!
double.IsFinite(best) ||
254 bestStart < planningHorizonStart ||
257 throw new ArithmeticException(
258 $
"No finite Bahl-Taj value was obtained for period {end}.");
261 value[end + 1] = best;
262 predecessor[end + 1] = bestStart;
265 planningHorizonStart = bestStart;
267 AdvanceDeliveredCosts(
268 planningHorizonStart,
274 cancellationToken.ThrowIfCancellationRequested();
284 ArrayPool<double>.Shared.Return(
288 ArrayPool<int>.Shared.Return(
292 ArrayPool<double>.Shared.Return(
296 ArrayPool<double>.Shared.Return(
300 ArrayPool<double>.Shared.Return(
301 cumulativeDemandBuffer,