LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
InventoryBalanceConstraintFamilyBuilder.cs
Go to the documentation of this file.
1using System;
2using System.Linq;
3using System.Threading;
4using System.Threading.Tasks;
5using LotSizingDataModel.Core.LogicalModel;
6using LotSizingDataModel.Core.PhysicalModel;
7using LotSizingDataModel.Core.Relationships;
8using LotSizingDataModel.Instance;
12
14
15/// <summary>
16/// Builds the physical inventory-flow balance for every
17/// item-warehouse inventory relationship and planning period.
18/// </summary>
19/// <remarks>
20/// The variable part of every balance is built through
21/// <see cref="InventoryBalanceConstraintBuilder"/>, preserving
22/// the corrected centralized sign convention:
23/// <code>
24/// inventory(t) - inventory(t-1)
25/// - variable inflows + variable outflows = fixed inflows.
26/// </code>
27/// The first-period right-hand side also contains the initial
28/// inventory. Scheduled receipts are fixed inflows.
29/// Production and supplier receipts are shifted by their lead
30/// times. Transport leaves the origin in its decision period and
31/// reaches the destination after the lane lead time.
32/// Production of a parent item consumes its BOM components in
33/// the production-start period.
34/// </remarks>
37{
38 /// <summary>
39 /// Gets the unique constraint-family identifier.
40 /// </summary>
41 public override string ConstraintFamilyId =>
42 "inventoryBalance";
43
44 /// <summary>
45 /// Determines whether inventory balances are required.
46 /// </summary>
47 public override bool IsEnabled(
48 LotSizingInstance instance,
50 {
51 ArgumentNullException.ThrowIfNull(instance);
52 ArgumentNullException.ThrowIfNull(options);
53
54 return instance.SupplyChain.Inventories.Count > 0;
55 }
56
57 /// <summary>
58 /// Builds all inventory-flow balance constraints.
59 /// </summary>
60 protected override ValueTask BuildConstraintsAsync(
61 LotSizingInstance instance,
64 CancellationToken cancellationToken)
65 {
66 foreach (Inventory inventory in instance.SupplyChain.Inventories)
67 {
68 for (int period = 1;
69 period <= instance.PlanningHorizon;
70 period++)
71 {
72 cancellationToken.ThrowIfCancellationRequested();
73
74 MathematicalVariable currentInventory =
75 context.GetVariable(
77 .CreateInventoryKey(
79 inventory.ItemId,
80 inventory.Warehouse,
81 period));
82
83 MathematicalVariable? previousInventory =
84 period > 1
85 ? context.GetVariable(
87 .CreateInventoryKey(
89 inventory.ItemId,
90 inventory.Warehouse,
91 period - 1))
92 : inventory.InitialInventoryDecisionMode ==
93 InitialInventoryDecisionMode.VariableDecision
94 ? context.GetVariable(
96 inventory))
97 : null;
98
99 var balance =
101 .SetCurrentState(currentInventory)
102 .SetPreviousState(previousInventory);
103
104 AddProductionFlows(
105 instance,
106 context,
107 inventory,
108 period,
109 balance,
110 options);
111
112 AddSupplierFlows(
113 instance,
114 context,
115 inventory,
116 period,
117 balance,
118 options);
119
120 AddTransportFlows(
121 instance,
122 context,
123 inventory,
124 period,
125 balance,
126 options);
127
128 AddDistributionOutflows(
129 instance,
130 context,
131 inventory,
132 period,
133 balance);
134
135 double fixedInflow =
136 inventory.ScheduledReceipt?[period] ??
137 0.0;
138
139 if (period == 1 &&
140 inventory.InitialInventoryDecisionMode !=
141 InitialInventoryDecisionMode.FixedParameter &&
142 inventory.InitialInventory != 0.0)
143 {
144 throw new InvalidOperationException(
145 "DLS/DLSI initial-stock semantics require the fixed " +
146 "InitialInventory value to be zero.");
147 }
148
149 double rightHandSide =
150 fixedInflow +
151 (period == 1 &&
152 inventory.InitialInventoryDecisionMode ==
153 InitialInventoryDecisionMode.FixedParameter
154 ? inventory.InitialInventory
155 : 0.0);
156
157 context.AddConstraint(
158 $"inventoryBalance_i{inventory.ItemId}" +
159 $"_w{inventory.Warehouse.ReferenceId}_t{period}",
160 balance.BuildExpression(),
162 rightHandSide,
163 description:
164 "Corrected physical inventory-flow " +
165 "balance.");
166 }
167 }
168
169 return ValueTask.CompletedTask;
170 }
171
172 private static void AddProductionFlows(
173 LotSizingInstance instance,
175 Inventory inventory,
176 int period,
179 {
180 if (inventory.Warehouse.Kind !=
181 WarehouseReferenceKind.PlantWarehouse)
182 {
183 return;
184 }
185
186 int plantId =
187 inventory.Warehouse.ReferenceId;
188
189 foreach (ProductionRouting routing
190 in instance.SupplyChain.ProductionRoutings)
191 {
192 if (routing.PlantId != plantId)
193 {
194 continue;
195 }
196
197 if (routing.ItemId == inventory.ItemId)
198 {
199 int startPeriod =
200 period - routing.LeadTime;
201
202 if (startPeriod >= 1)
203 {
204 balance.AddInflow(
205 context.GetVariable(
206 StandardFormulationVariableKeyFactory
207 .CreateProductionKey(
208 routing.Id,
209 startPeriod)));
210 }
211 }
212
213 if (!options.IncludeBillOfMaterials)
214 {
215 continue;
216 }
217
218 foreach (ComponentRequirement requirement
219 in instance.SupplyChain.ComponentRequirements)
220 {
221 if (requirement.ComponentItemId != inventory.ItemId ||
222 requirement.ParentItemId != routing.ItemId)
223 {
224 continue;
225 }
226
227 balance.AddOutflow(
228 context.GetVariable(
229 StandardFormulationVariableKeyFactory
230 .CreateProductionKey(
231 routing.Id,
232 period)),
233 requirement.Quantity);
234 }
235 }
236 }
237
238 private static void AddSupplierFlows(
239 LotSizingInstance instance,
240 MathematicalModelBuildContext context,
241 Inventory inventory,
242 int period,
243 InventoryBalanceConstraintBuilder balance,
244 StandardLotSizingFormulationOptions options)
245 {
246 if (!options.IncludeProcurement)
247 {
248 return;
249 }
250
251 foreach (SupplierDelivery delivery
252 in instance.SupplyChain.SupplierDeliveries)
253 {
254 if (delivery.ItemId != inventory.ItemId ||
255 !StandardFormulationDomainKeyFactory.AreSameWarehouse(
256 delivery.Warehouse,
257 inventory.Warehouse))
258 {
259 continue;
260 }
261
262 int orderPeriod =
263 period - delivery.LeadTime;
264
265 if (orderPeriod < 1)
266 {
267 continue;
268 }
269
270 balance.AddInflow(
271 context.GetVariable(
272 StandardFormulationVariableKeyFactory
273 .CreateProcurementKey(
274 delivery.SupplierId,
275 delivery.ItemId,
276 delivery.Warehouse,
277 orderPeriod)));
278 }
279 }
280
281 private static void AddTransportFlows(
282 LotSizingInstance instance,
283 MathematicalModelBuildContext context,
284 Inventory inventory,
285 int period,
286 InventoryBalanceConstraintBuilder balance,
287 StandardLotSizingFormulationOptions options)
288 {
289 if (!options.IncludeTransport)
290 {
291 return;
292 }
293
294 foreach (TransportCharacteristic characteristic
295 in instance.SupplyChain.TransportCharacteristics)
296 {
297 if (characteristic.ItemId != inventory.ItemId)
298 {
299 continue;
300 }
301
302 TransportResource resource =
303 instance.SupplyChain.TransportResources
304 .First(
305 candidate =>
306 candidate.Id ==
307 characteristic.TransportResourceId);
308
309 foreach (AssignedTransportLane lane in instance.SupplyChain.GetTransportLanes(resource.Id))
310 {
311 if (StandardFormulationDomainKeyFactory.AreSameWarehouse(
312 lane.Origin,
313 inventory.Warehouse))
314 {
315 balance.AddOutflow(
316 context.GetVariable(
317 StandardFormulationVariableKeyFactory
318 .CreateTransportKey(
319 inventory.ItemId,
320 resource.Id,
321 lane.Origin,
322 lane.Destination,
323 period)));
324 }
325
326 if (!StandardFormulationDomainKeyFactory.AreSameWarehouse(
327 lane.Destination,
328 inventory.Warehouse))
329 {
330 continue;
331 }
332
333 int departurePeriod =
334 period - lane.LeadTime;
335
336 if (departurePeriod < 1)
337 {
338 continue;
339 }
340
341 balance.AddInflow(
342 context.GetVariable(
343 StandardFormulationVariableKeyFactory
344 .CreateTransportKey(
345 inventory.ItemId,
346 resource.Id,
347 lane.Origin,
348 lane.Destination,
349 departurePeriod)));
350 }
351 }
352 }
353
354 private static void AddDistributionOutflows(
355 LotSizingInstance instance,
356 MathematicalModelBuildContext context,
357 Inventory inventory,
358 int period,
359 InventoryBalanceConstraintBuilder balance)
360 {
361 foreach (DistributionCenterSourcing sourcing
362 in instance.SupplyChain.DistributionCenterSourcings)
363 {
364 if (sourcing.ItemId != inventory.ItemId ||
365 !StandardFormulationDomainKeyFactory.AreSameWarehouse(
366 sourcing.Warehouse,
367 inventory.Warehouse))
368 {
369 continue;
370 }
371
372 balance.AddOutflow(
373 context.GetVariable(
374 StandardFormulationVariableKeyFactory
375 .CreateDistributionKey(
376 MathematicalDecisionCategory.Delivery,
377 sourcing.DistributionCenterId,
378 sourcing.ItemId,
379 sourcing.Warehouse,
380 period)));
381 }
382 }
383}
Builds a corrected inventory-balance constraint for one item, location, and period.
InventoryBalanceConstraintBuilder AddInflow(MathematicalVariable variable, double coefficient=1.0)
Adds an inflow variable to the balance.
InventoryBalanceConstraintBuilder AddOutflow(MathematicalVariable variable, double coefficient=1.0)
Adds an outflow variable to the balance.
InventoryBalanceConstraintBuilder SetCurrentState(MathematicalVariable inventory, MathematicalVariable? backlog=null, MathematicalVariable? shortage=null)
Assigns the current-period inventory state variables.
InventoryBalanceConstraintBuilder SetPreviousState(MathematicalVariable? inventory, MathematicalVariable? backlog=null, MathematicalVariable? shortage=null)
Assigns the previous-period inventory state variables.
Provides the shared state required while constructing a solver-independent mathematical model.
MathematicalVariable GetVariable(string domainKey)
Gets a required mathematical variable from its business-domain key.
Builds the physical inventory-flow balance for every item-warehouse inventory relationship and planni...
override ValueTask BuildConstraintsAsync(LotSizingInstance instance, MathematicalModelBuildContext context, StandardLotSizingFormulationOptions options, CancellationToken cancellationToken)
Builds all inventory-flow balance constraints.
override bool IsEnabled(LotSizingInstance instance, StandardLotSizingFormulationOptions options)
Determines whether inventory balances are required.
Creates canonical variable domain keys used by the standard formulation constraint builders.
Provides common validation and helper methods for one constraint family of the standard lot-sizing fo...
Configures the standard solver-independent mixed-integer lot-sizing formulation.
Provides the canonical mathematical domain-key categories used to identify lot-sizing decision famili...
const string Inventory
Identifies inventory-level decision variables.
Represents one decision variable in a mathematical optimization model.
MathematicalConstraintSense
Identifies the relational sense of a mathematical constraint.