ULSAlgorithms 1.1.0-g3e5595996d
High-performance exact and heuristic algorithms for uncapacitated lot sizing
Loading...
Searching...
No Matches
JacobsKhumawalaBranchAndBoundSolver.cs
Go to the documentation of this file.
1using System.Buffers;
7
9
10/// <summary>
11/// Exact single-level lot-sizing procedure expressed as the simplified
12/// branch-and-bound/subproblem scheme of Jacobs and Khumawala.
13/// </summary>
14/// <remarks>
15/// <para>
16/// Jacobs and Khumawala (1987) present a simple branch-and-bound procedure for
17/// single-item, single-level lot sizing. Their abstract describes the method as
18/// computationally equivalent to Wagner-Whitin, but easier to apply through a
19/// graphical branch-and-bound representation. Efficiency is obtained by
20/// dividing the problem into subproblems and proving that some subproblems
21/// cannot lead to an optimum.
22/// </para>
23/// <para>
24/// This modern reconstruction represents each boundary period as a subproblem.
25/// Branches are regeneration intervals. For every boundary, only the cheapest
26/// label is retained; any more expensive branch reaching the same subproblem is
27/// dominated and is discarded. A feasible Lot-for-Lot incumbent supplies an
28/// additional global upper bound.
29/// </para>
30/// <para>
31/// Because boundaries are processed in topological order, every retained label
32/// is final when expanded. The resulting algorithm is exact, runs in O(T²)
33/// time, and uses O(T) auxiliary working memory.
34/// </para>
35/// <para>
36/// Reference:
37/// F. R. Jacobs and B. M. Khumawala,
38/// "A Simplified Procedure for Optimal Single-Level Lot Sizing",
39/// Production and Inventory Management 28(3), 39-43, 1987.
40/// </para>
41/// <para>
42/// This is a C# reconstruction of the published branch/subproblem architecture,
43/// not a transliteration of the original graphical worksheet.
44/// </para>
45/// </remarks>
47{
48 public string Name =>
49 "Jacobs-Khumawala simplified branch-and-bound";
50
52 UlsSolverKind.Exact;
53
55 UlsProblem problem,
56 CancellationToken cancellationToken = default)
57 {
58 ArgumentNullException.ThrowIfNull(problem);
59 cancellationToken.ThrowIfCancellationRequested();
60
61 var horizon =
62 problem.Horizon;
63
64 var bestLabelBuffer =
65 ArrayPool<double>.Shared.Rent(
66 horizon + 1);
67
68 var predecessorBuffer =
69 ArrayPool<int>.Shared.Rent(
70 horizon + 1);
71
72 try
73 {
74 var bestLabel =
75 bestLabelBuffer.AsSpan(
76 0,
77 horizon + 1);
78
79 var predecessor =
80 predecessorBuffer.AsSpan(
81 0,
82 horizon + 1);
83
84 bestLabel.Fill(
85 double.PositiveInfinity);
86
87 predecessor.Fill(-1);
88
89 bestLabel[0] = 0.0;
90
91 var arc =
92 new UlsRegenerationCost(problem);
93
94 var incumbent =
95 ComputeLotForLotUpperBound(
96 problem);
97
98 for (var start = 0;
99 start < horizon;
100 start++)
101 {
102 cancellationToken.ThrowIfCancellationRequested();
103
104 var startLabel =
105 bestLabel[start];
106
107 if (!double.IsFinite(startLabel) ||
108 startLabel > incumbent)
109 {
110 continue;
111 }
112
113 // A zero-demand state may be transferred to the next
114 // subproblem without opening an order.
115 if (problem.Demands[start] == 0.0 &&
116 startLabel <
117 bestLabel[start + 1])
118 {
119 bestLabel[start + 1] =
120 startLabel;
121
122 predecessor[start + 1] =
123 start;
124 }
125
126 for (var end = start;
127 end < horizon;
128 end++)
129 {
130 if (arc.GetDemand(
131 start,
132 end) == 0.0)
133 {
134 continue;
135 }
136
137 var candidate =
138 startLabel +
139 arc.GetCost(
140 start,
141 end);
142
143 if (!double.IsFinite(candidate))
144 {
145 throw new ArithmeticException(
146 "Numerical overflow in Jacobs-Khumawala branch cost.");
147 }
148
149 // Incumbent fathoming.
150 if (candidate > incumbent)
151 {
152 continue;
153 }
154
155 var boundary =
156 end + 1;
157
158 // Subproblem dominance:
159 // only the cheapest branch reaching the same boundary
160 // can belong to an optimal continuation.
161 if (candidate <
162 bestLabel[boundary] ||
163 (candidate ==
164 bestLabel[boundary] &&
165 start >
166 predecessor[boundary]))
167 {
168 bestLabel[boundary] =
169 candidate;
170
171 predecessor[boundary] =
172 start;
173
174 if (boundary == horizon &&
175 candidate < incumbent)
176 {
177 incumbent =
178 candidate;
179 }
180 }
181 }
182 }
183
184 if (!double.IsFinite(
185 bestLabel[horizon]))
186 {
187 throw new ArithmeticException(
188 "Jacobs-Khumawala failed to obtain a finite incumbent.");
189 }
190
192 problem,
193 predecessor,
194 Name,
195 cancellationToken);
196 }
197 finally
198 {
199 ArrayPool<double>.Shared.Return(
200 bestLabelBuffer,
201 clearArray: false);
202
203 ArrayPool<int>.Shared.Return(
204 predecessorBuffer,
205 clearArray: false);
206 }
207 }
208
209 private static double ComputeLotForLotUpperBound(
210 UlsProblem problem)
211 {
212 var demands = problem.Demands;
213 var setupCosts = problem.SetupCosts;
214 var productionCosts =
215 problem.UnitProductionCosts;
216
217 var cost = 0.0;
218
219 for (var period = 0;
220 period < problem.Horizon;
221 period++)
222 {
223 if (demands[period] == 0.0)
224 {
225 continue;
226 }
227
228 cost +=
229 setupCosts[period] +
230 demands[period] *
231 productionCosts[period];
232
233 if (!double.IsFinite(cost))
234 {
235 throw new ArithmeticException(
236 "Numerical overflow in Lot-for-Lot upper bound.");
237 }
238 }
239
240 return cost;
241 }
242}
O(1) regeneration-interval cost evaluator for the uncapacitated zero-inventory-ordering structure.
Exact single-level lot-sizing procedure expressed as the simplified branch-and-bound/subproblem schem...
UlsSolveResult Solve(UlsProblem problem, CancellationToken cancellationToken=default)
Solves an uncapacitated lot-sizing problem.
Reconstructs a zero-inventory-order ULS solution from shortest-path predecessors.
static UlsSolveResult Build(UlsProblem problem, ReadOnlySpan< int > predecessor, string solverName, CancellationToken cancellationToken)
Represents a validated classical uncapacitated lot-sizing problem.
Definition UlsProblem.cs:23
ReadOnlySpan< double > UnitProductionCosts
Gets unit production costs by period.
int Horizon
Gets the number of planning periods.
Definition UlsProblem.cs:82
ReadOnlySpan< double > Demands
Gets demand by period.
Definition UlsProblem.cs:92
ReadOnlySpan< double > SetupCosts
Gets fixed setup costs by period.
Definition UlsProblem.cs:97
Represents the outcome returned by a ULS solution strategy.
Defines the common strategy contract implemented by every ULS solver.
Definition IUlsSolver.cs:14
UlsSolverKind
Identifies the broad family of a ULS solution strategy.