118 CancellationToken cancellationToken =
default)
120 ArgumentNullException.ThrowIfNull(problem);
121 cancellationToken.ThrowIfCancellationRequested();
125 throw new NotSupportedException(
126 "ChowdhuryBakiAzabSolver requires strictly positive demands, " +
127 "constant unit production costs, and a strictly positive " +
128 "stationary relevant holding cost.");
135 var predecessor =
new int[2];
146 var eventCapacity = checked((2 * horizon) + 8);
148 var gBuffer = ArrayPool<double>.Shared.Rent(horizon + 2);
149 var aBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
150 var bBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
151 var prefixDemandBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
152 var prefixWeightedDemandBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
154 var activePreviousBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
155 var activeNextBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
156 var bestSuccessorBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
157 var listHeadBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
158 var eventPeriodBuffer = ArrayPool<int>.Shared.Rent(eventCapacity);
159 var eventNextBuffer = ArrayPool<int>.Shared.Rent(eventCapacity);
160 var predecessorBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
164 var g = gBuffer.AsSpan(0, horizon + 2);
165 var a = aBuffer.AsSpan(0, horizon + 1);
166 var b = bBuffer.AsSpan(0, horizon + 1);
167 var prefixDemand = prefixDemandBuffer.AsSpan(0, horizon + 1);
168 var prefixWeightedDemand =
169 prefixWeightedDemandBuffer.AsSpan(0, horizon + 1);
172 activePreviousBuffer.AsSpan(0, horizon + 1);
174 activeNextBuffer.AsSpan(0, horizon + 1);
176 bestSuccessorBuffer.AsSpan(0, horizon + 1);
178 listHeadBuffer.AsSpan(0, horizon + 1);
180 eventPeriodBuffer.AsSpan(0, eventCapacity);
182 eventNextBuffer.AsSpan(0, eventCapacity);
184 predecessorBuffer.AsSpan(0, horizon + 1);
189 prefixDemand.Clear();
190 prefixWeightedDemand.Clear();
191 activePrevious.Clear();
193 bestSuccessor.Clear();
195 predecessor.Fill(-1);
201 for (var period = 1; period <= horizon; period++)
203 var demand = demands[period - 1];
205 prefixDemand[period] = AddFinite(
206 prefixDemand[period - 1],
208 "cumulative demand");
210 prefixWeightedDemand[period] = AddFinite(
211 prefixWeightedDemand[period - 1],
212 MultiplyFinite(period, demand,
"weighted demand"),
213 "weighted cumulative demand");
216 activePrevious[1] = 0;
219 for (var diagonal = 2; diagonal <= horizon; diagonal++)
221 activePrevious[diagonal] = diagonal - 1;
224 for (var diagonal = 1; diagonal < horizon; diagonal++)
226 activeNext[diagonal] = diagonal + 1;
229 g[horizon + 1] = 0.0;
231 g[horizon] = setupCosts[horizon - 1];
232 bestSuccessor[horizon] = horizon + 1;
234 var bestDiagonal = horizon - 1;
236 var cancellationCounter = 0;
238 for (var k = horizon - 1; k >= 1; k--)
240 if ((cancellationCounter++ & CancellationCheckMask) == 0)
242 cancellationToken.ThrowIfCancellationRequested();
251 "Algorithm 1 advantage");
253 EnsureFinite(a[k],
"Algorithm 1 advantage");
255 b[k] = MultiplyFinite(
258 "Algorithm 1 slope");
260 var u = ClampedCeilingRatio(
276 var eventIndex = listHead[k];
278 while (eventIndex >= 0)
280 if ((cancellationCounter++ & CancellationCheckMask) == 0)
282 cancellationToken.ThrowIfCancellationRequested();
285 var p = eventPeriod[eventIndex];
287 if (p <= bestDiagonal &&
288 activeNext[activePrevious[p]] == p)
295 "stack advantage shift");
297 var aggregatedSlope = b[p];
300 while (delta <= 0.0 &&
303 if ((cancellationCounter++ &
304 CancellationCheckMask) == 0)
306 cancellationToken.ThrowIfCancellationRequested();
309 if (p < bestDiagonal)
311 activeNext[activePrevious[p]] =
314 activePrevious[activeNext[p]] =
325 "stack advantage shift"),
326 "stack advantage aggregation");
328 aggregatedSlope = AddFinite(
331 "stack slope aggregation");
340 "stack compressed intercept"),
341 "stack compressed intercept");
343 b[p] = aggregatedSlope;
345 u = ClampedCeilingRatio(
365 activePrevious[stackHead];
370 eventIndex = eventNext[eventIndex];
373 bestSuccessor[k] = bestDiagonal + 2;
375 var holdingArcCost = ComputeHoldingArcCost(
380 prefixWeightedDemand);
385 "backward shortest-path cost");
390 "backward shortest-path cost");
395 while (node <= horizon)
397 cancellationToken.ThrowIfCancellationRequested();
399 var next = bestSuccessor[node];
404 throw new InvalidOperationException(
405 "The Chowdhury-Baki-Azab successor chain is inconsistent.");
408 predecessor[next - 1] = node - 1;
420 ArrayPool<double>.Shared.Return(gBuffer, clearArray:
false);
421 ArrayPool<double>.Shared.Return(aBuffer, clearArray:
false);
422 ArrayPool<double>.Shared.Return(bBuffer, clearArray:
false);
423 ArrayPool<double>.Shared.Return(prefixDemandBuffer, clearArray:
false);
424 ArrayPool<double>.Shared.Return(
425 prefixWeightedDemandBuffer,
428 ArrayPool<int>.Shared.Return(activePreviousBuffer, clearArray:
false);
429 ArrayPool<int>.Shared.Return(activeNextBuffer, clearArray:
false);
430 ArrayPool<int>.Shared.Return(bestSuccessorBuffer, clearArray:
false);
431 ArrayPool<int>.Shared.Return(listHeadBuffer, clearArray:
false);
432 ArrayPool<int>.Shared.Return(eventPeriodBuffer, clearArray:
false);
433 ArrayPool<int>.Shared.Return(eventNextBuffer, clearArray:
false);
434 ArrayPool<int>.Shared.Return(predecessorBuffer, clearArray:
false);