LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
UlsAlgorithmsExactModelContractExtractor.cs
Go to the documentation of this file.
1using LotSizingDataModel.Instance;
4
6
7/// <summary>
8/// Extracts the narrow canonical SI-ULS contract accepted by
9/// the alpha.34 exact adapters.
10/// </summary>
12{
13 private const double Tolerance = 1.0e-10;
14
16 LotSizingInstance instance,
18 {
19 ArgumentNullException.ThrowIfNull(
20 instance);
21
22 ArgumentNullException.ThrowIfNull(
23 model);
24
25 if (instance.PlanningHorizon <= 0)
26 {
27 throw new NotSupportedException(
28 "ULSAlgorithms exact adapters require a positive planning horizon.");
29 }
30
31 if (instance.SupplyChain.Demands.Count != 1 ||
32 instance.SupplyChain.ProductionRoutings.Count != 1 ||
33 instance.SupplyChain.Inventories.Count != 1)
34 {
35 throw new NotSupportedException(
36 "ULSAlgorithms exact adapters require exactly one demand, one production routing and one inventory.");
37 }
38
39 var routing =
40 instance.SupplyChain.ProductionRoutings[0];
41
42 var inventory =
43 instance.SupplyChain.Inventories[0];
44
45 if (routing.LeadTime != 0)
46 {
47 throw new NotSupportedException(
48 "The alpha.34 ULSAlgorithms bridge requires zero production lead time.");
49 }
50
51 if (Math.Abs(inventory.InitialInventory) >
52 Tolerance)
53 {
54 throw new NotSupportedException(
55 "The alpha.34 ULSAlgorithms bridge requires fixed zero initial inventory.");
56 }
57
58 if (inventory.ScheduledReceipt is not null)
59 {
60 for (int period = 1;
61 period <= instance.PlanningHorizon;
62 period++)
63 {
64 if (Math.Abs(
65 inventory.ScheduledReceipt[period]) >
66 Tolerance)
67 {
68 throw new NotSupportedException(
69 "Scheduled receipts are outside the alpha.34 canonical ULS contract.");
70 }
71 }
72 }
73
74 if (instance.SupplyChain.ComponentRequirements.Count != 0 ||
75 instance.SupplyChain.SupplierDeliveries.Count != 0 ||
76 instance.SupplyChain.TransportCharacteristics.Count != 0)
77 {
78 throw new NotSupportedException(
79 "BOM, procurement and transport extensions are outside the alpha.34 canonical ULS contract.");
80 }
81
82 if (model.Objective.Sense !=
83 ObjectiveSense.Minimize)
84 {
85 throw new NotSupportedException(
86 "ULSAlgorithms exact adapters require a minimization objective.");
87 }
88
89 if (Math.Abs(
91 Tolerance)
92 {
93 throw new NotSupportedException(
94 "The alpha.34 canonical ULS objective cannot contain an additive constant.");
95 }
96
97 AssertCanonicalConstraintShape(
98 model);
99
100 int horizon =
101 instance.PlanningHorizon;
102
103 double[] demands =
104 new double[horizon];
105
106 var demand =
107 instance.SupplyChain.Demands[0];
108
109 for (int period = 1;
110 period <= horizon;
111 period++)
112 {
113 demands[period - 1] =
114 demand.GetQuantity(
115 period);
116 }
117
118 double[] setupCosts =
119 new double[horizon];
120
121 double[] productionCosts =
122 new double[horizon];
123
124 double[] holdingCosts =
125 new double[horizon];
126
127 var coefficientByVariable =
128 model.Objective.Expression.Terms.ToDictionary(
129 term => term.VariableId,
130 term => term.Coefficient);
131
132 int[] productionCounts =
133 new int[horizon];
134
135 int[] setupCounts =
136 new int[horizon];
137
138 int[] inventoryCounts =
139 new int[horizon];
140
141 int[] deliveryCounts =
142 new int[horizon];
143
144 foreach (MathematicalVariable variable
145 in model.Variables)
146 {
148 variable.DomainKey,
149 out MathematicalDomainKey? key) ||
150 key is null)
151 {
152 throw new NotSupportedException(
153 $"Variable '{variable.Name}' has no canonical domain key.");
154 }
155
156 if (!key.TryGetInt32(
157 "period",
158 out int period) ||
159 period < 1 ||
160 period > horizon)
161 {
162 throw new NotSupportedException(
163 $"Variable '{variable.Name}' has no valid canonical planning period.");
164 }
165
166 int index =
167 period - 1;
168
169 coefficientByVariable.TryGetValue(
170 variable.Id,
171 out double coefficient);
172
173 switch (key.Category)
174 {
176 productionCounts[index]++;
177 productionCosts[index] += coefficient;
178 break;
179
181 setupCounts[index]++;
182 setupCosts[index] += coefficient;
183 break;
184
186 inventoryCounts[index]++;
187 holdingCosts[index] += coefficient;
188 break;
189
191 deliveryCounts[index]++;
192
193 if (Math.Abs(coefficient) >
194 Tolerance)
195 {
196 throw new NotSupportedException(
197 "Delivery-cost terms are outside the alpha.34 canonical ULS objective.");
198 }
199
200 break;
201
202 default:
203 throw new NotSupportedException(
204 $"Decision category '{key.Category}' is outside the alpha.34 canonical ULS model shape.");
205 }
206 }
207
208 for (int index = 0;
209 index < horizon;
210 index++)
211 {
212 if (productionCounts[index] != 1 ||
213 setupCounts[index] != 1 ||
214 inventoryCounts[index] != 1 ||
215 deliveryCounts[index] > 1)
216 {
217 throw new NotSupportedException(
218 $"Period {index + 1} does not contain exactly one production/setup/inventory variable or contains multiple delivery variables.");
219 }
220 }
221
223 demands,
224 setupCosts,
225 productionCosts,
226 holdingCosts);
227 }
228
229 private static void AssertCanonicalConstraintShape(
230 MathematicalModel model)
231 {
232 foreach (LinearConstraint constraint
233 in model.Constraints)
234 {
235 string name =
236 constraint.Name ?? string.Empty;
237
238 bool allowed =
239 name.StartsWith(
240 "inventoryBalance_",
241 StringComparison.Ordinal) ||
242 name.StartsWith(
243 "demand_",
244 StringComparison.Ordinal) ||
245 name.StartsWith(
246 "productionSetupLink_",
247 StringComparison.Ordinal);
248
249 if (!allowed)
250 {
251 throw new NotSupportedException(
252 $"Constraint '{name}' is outside the alpha.34 canonical ULS formulation shape.");
253 }
254 }
255 }
256}
Extracts the narrow canonical SI-ULS contract accepted by the alpha.34 exact adapters.
UlsAlgorithmsExactProblemData Extract(LotSizingInstance instance, MathematicalModel model)
Normalized deterministic uncapacitated single-item problem passed to ULSAlgorithms.
Provides the canonical mathematical domain-key categories used to identify lot-sizing decision famili...
const string Production
Identifies production-quantity decision variables.
const string Inventory
Identifies inventory-level decision variables.
const string Delivery
Identifies delivered-quantity decision variables.
const string Setup
Identifies production setup decision variables.
Represents a parsed mathematical-model business-domain key.
static bool TryParse(string domainKey, out MathematicalDomainKey? result)
Attempts to parse a canonical mathematical domain key.
Represents one linear constraint in a mathematical optimization model.
List< LinearTerm > Terms
Gets the linear terms of the expression.
double Constant
Gets or sets the constant value of the expression.
Represents a solver-independent mathematical optimization model.
MathematicalObjective Objective
Gets or sets the mathematical objective.
List< LinearConstraint > Constraints
Gets the linear constraints.
ObjectiveSense Sense
Gets or sets the optimization direction.
LinearExpression Expression
Gets or sets the linear objective expression.
Represents one decision variable in a mathematical optimization model.
string DomainKey
Gets or sets an optional domain key used to map the mathematical variable back to a lot-sizing decisi...
int Id
Gets or sets the unique variable identifier within the mathematical model.
ObjectiveSense
Identifies the optimization direction of a mathematical objective.