110 CancellationToken cancellationToken =
default)
112 ArgumentNullException.ThrowIfNull(problem);
113 cancellationToken.ThrowIfCancellationRequested();
117 var effectiveWorkerLimit =
119 ? Math.Max(1, Environment.ProcessorCount)
122 var valueBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
123 var predecessorBuffer = ArrayPool<int>.Shared.Rent(horizon + 1);
124 var demandPrefixBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
125 var holdingPrefixBuffer = ArrayPool<double>.Shared.Rent(horizon + 1);
126 var weightedDemandPrefixBuffer =
127 ArrayPool<double>.Shared.Rent(horizon + 1);
129 var workerBestValueBuffer =
130 ArrayPool<double>.Shared.Rent(effectiveWorkerLimit);
131 var workerBestPredecessorBuffer =
132 ArrayPool<int>.Shared.Rent(effectiveWorkerLimit);
136 var value = valueBuffer;
137 var predecessor = predecessorBuffer;
138 var demandPrefix = demandPrefixBuffer;
139 var holdingPrefix = holdingPrefixBuffer;
140 var weightedDemandPrefix = weightedDemandPrefixBuffer;
144 double.PositiveInfinity,
154 demandPrefix[0] = 0.0;
155 holdingPrefix[0] = 0.0;
156 weightedDemandPrefix[0] = 0.0;
162 for (var period = 0; period < horizon; period++)
164 demandPrefix[period + 1] = AddFinite(
165 demandPrefix[period],
167 "cumulative demand");
169 weightedDemandPrefix[period + 1] = AddFinite(
170 weightedDemandPrefix[period],
173 holdingPrefix[period],
174 "demand-weighted holding prefix"),
175 "demand-weighted holding prefix");
177 holdingPrefix[period + 1] = AddFinite(
178 holdingPrefix[period],
179 holdingCosts[period],
180 "cumulative holding cost");
183 for (var end = 1; end <= horizon; end++)
185 cancellationToken.ThrowIfCancellationRequested();
191 effectiveWorkerLimit <= 1)
193 (best, bestStart) = FindBestSerial(
199 weightedDemandPrefix,
205 Math.Min(effectiveWorkerLimit, end);
207 var options =
new ParallelOptions
209 CancellationToken = cancellationToken,
213 global::System.Threading.Tasks.Parallel.For(
220 (end * worker) / workerCount;
223 (end * (worker + 1)) / workerCount;
225 var localBest =
double.PositiveInfinity;
226 var localBestStart = -1;
228 for (var start = first;
229 start < lastExclusive;
232 if ((start & 255) == 0)
234 cancellationToken.ThrowIfCancellationRequested();
237 var candidate = EvaluateCandidate(
244 weightedDemandPrefix);
246 if (candidate < localBest ||
247 (candidate == localBest &&
248 (localBestStart < 0 ||
249 start < localBestStart)))
251 localBest = candidate;
252 localBestStart = start;
256 workerBestValueBuffer[worker] = localBest;
257 workerBestPredecessorBuffer[worker] =
261 best =
double.PositiveInfinity;
265 worker < workerCount;
269 workerBestValueBuffer[worker];
272 workerBestPredecessorBuffer[worker];
274 if (candidate < best ||
275 (candidate == best &&
276 candidateStart >= 0 &&
278 candidateStart < bestStart)))
281 bestStart = candidateStart;
286 if (!
double.IsFinite(best) ||
289 throw new ArithmeticException(
290 $
"No finite Lyu-Lee value was obtained for state {end}.");
294 predecessor[end] = bestStart;
299 predecessor.AsSpan(0, horizon + 1),
305 ArrayPool<double>.Shared.Return(valueBuffer, clearArray:
false);
306 ArrayPool<int>.Shared.Return(predecessorBuffer, clearArray:
false);
307 ArrayPool<double>.Shared.Return(
310 ArrayPool<double>.Shared.Return(
313 ArrayPool<double>.Shared.Return(
314 weightedDemandPrefixBuffer,
316 ArrayPool<double>.Shared.Return(
317 workerBestValueBuffer,
319 ArrayPool<int>.Shared.Return(
320 workerBestPredecessorBuffer,