ULSAlgorithms 1.1.0-g3e5595996d
High-performance exact and heuristic algorithms for uncapacitated lot sizing
Loading...
Searching...
No Matches
ZangwillNetworkSolver.cs
Go to the documentation of this file.
1using System.Buffers;
7
9
10/// <summary>
11/// Exact ULS solver using Zangwill's acyclic network representation.
12/// </summary>
13/// <remarks>
14/// <para>
15/// A node represents a zero-inventory boundary between periods. An arc from
16/// node <c>i</c> to node <c>j</c> represents one replenishment in period
17/// <c>i</c> covering periods <c>i..j-1</c>. The ULS problem is therefore a
18/// shortest-path problem in a directed acyclic network.
19/// </para>
20/// <para>
21/// This implementation performs the shortest-path recursion backwards from
22/// node T to node 0 and evaluates every arc in O(1) using cumulative arrays.
23/// Time complexity is O(T²); auxiliary working memory is O(T).
24/// </para>
25/// <para>
26/// Reference:
27/// W. I. Zangwill,
28/// "A Backlogging Model and a Multi-Echelon Model of a Dynamic Economic Lot
29/// Size Production System—A Network Approach",
30/// Management Science 15(9), 506-527, 1969.
31/// </para>
32/// <para>
33/// The present class solves the no-backlogging single-echelon specialization
34/// represented by <see cref="UlsProblem"/>.
35/// </para>
36/// </remarks>
37public sealed class ZangwillNetworkSolver : IUlsSolver
38{
39 public string Name =>
40 "Zangwill acyclic network";
41
43 UlsSolverKind.Exact;
44
46 UlsProblem problem,
47 CancellationToken cancellationToken = default)
48 {
49 ArgumentNullException.ThrowIfNull(problem);
50 cancellationToken.ThrowIfCancellationRequested();
51
52 var horizon = problem.Horizon;
53
54 var costToGoBuffer =
55 ArrayPool<double>.Shared.Rent(
56 horizon + 1);
57
58 var successorBuffer =
59 ArrayPool<int>.Shared.Rent(
60 horizon + 1);
61
62 var predecessorBuffer =
63 ArrayPool<int>.Shared.Rent(
64 horizon + 1);
65
66 try
67 {
68 var costToGo =
69 costToGoBuffer.AsSpan(
70 0,
71 horizon + 1);
72
73 var successor =
74 successorBuffer.AsSpan(
75 0,
76 horizon + 1);
77
78 var predecessor =
79 predecessorBuffer.AsSpan(
80 0,
81 horizon + 1);
82
83 costToGo.Fill(
84 double.PositiveInfinity);
85
86 successor.Fill(-1);
87 predecessor.Fill(-1);
88
89 costToGo[horizon] = 0.0;
90 successor[horizon] = horizon;
91
92 var arc =
93 new UlsRegenerationCost(problem);
94
95 var demands =
96 problem.Demands;
97
98 for (var start = horizon - 1;
99 start >= 0;
100 start--)
101 {
102 cancellationToken.ThrowIfCancellationRequested();
103
104 var best =
105 double.PositiveInfinity;
106
107 var bestNext = -1;
108
109 // Zero-demand periods may simply be crossed with no setup.
110 if (demands[start] == 0.0)
111 {
112 best =
113 costToGo[start + 1];
114
115 bestNext =
116 start + 1;
117 }
118
119 for (var end = start;
120 end < horizon;
121 end++)
122 {
123 if (arc.GetDemand(
124 start,
125 end) == 0.0)
126 {
127 continue;
128 }
129
130 var candidate =
131 arc.GetCost(
132 start,
133 end) +
134 costToGo[end + 1];
135
136 if (!double.IsFinite(candidate))
137 {
138 throw new ArithmeticException(
139 "Numerical overflow in Zangwill shortest path.");
140 }
141
142 if (candidate < best ||
143 (candidate == best &&
144 end + 1 < bestNext))
145 {
146 best = candidate;
147 bestNext = end + 1;
148 }
149 }
150
151 if (!double.IsFinite(best) ||
152 bestNext <= start)
153 {
154 throw new ArithmeticException(
155 $"No finite Zangwill path from node {start}.");
156 }
157
158 costToGo[start] = best;
159 successor[start] = bestNext;
160 }
161
162 var node = 0;
163
164 while (node < horizon)
165 {
166 cancellationToken.ThrowIfCancellationRequested();
167
168 var next =
169 successor[node];
170
171 if (next <= node ||
172 next > horizon)
173 {
174 throw new InvalidOperationException(
175 "Invalid Zangwill successor chain.");
176 }
177
178 if (arc.GetDemand(
179 node,
180 next - 1) > 0.0)
181 {
182 predecessor[next] =
183 node;
184 }
185 else
186 {
187 predecessor[next] =
188 next - 1;
189 }
190
191 node = next;
192 }
193
195 problem,
196 predecessor,
197 Name,
198 cancellationToken);
199 }
200 finally
201 {
202 ArrayPool<double>.Shared.Return(
203 costToGoBuffer,
204 clearArray: false);
205
206 ArrayPool<int>.Shared.Return(
207 successorBuffer,
208 clearArray: false);
209
210 ArrayPool<int>.Shared.Return(
211 predecessorBuffer,
212 clearArray: false);
213 }
214 }
215}
O(1) regeneration-interval cost evaluator for the uncapacitated zero-inventory-ordering structure.
Reconstructs a zero-inventory-order ULS solution from shortest-path predecessors.
static UlsSolveResult Build(UlsProblem problem, ReadOnlySpan< int > predecessor, string solverName, CancellationToken cancellationToken)
Exact ULS solver using Zangwill's acyclic network representation.
string Name
Gets the stable human-readable name of the solver.
UlsSolverKind Kind
Gets the broad family of the solver.
UlsSolveResult Solve(UlsProblem problem, CancellationToken cancellationToken=default)
Solves an uncapacitated lot-sizing problem.
Represents a validated classical uncapacitated lot-sizing problem.
Definition UlsProblem.cs:23
int Horizon
Gets the number of planning periods.
Definition UlsProblem.cs:82
ReadOnlySpan< double > Demands
Gets demand by period.
Definition UlsProblem.cs:92
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.