LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
LotSizingProblemFeatureExtractor.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using LotSizingDataModel.Core;
5using LotSizingDataModel.Core.PhysicalModel;
6using LotSizingDataModel.Core.DecisionModel.Objectives;
7using LotSizingDataModel.Core.DecisionModel.Scheduling;
9
11
12/// <summary>
13/// Extracts factual lot-sizing problem features from a
14/// supply-chain instance.
15/// </summary>
16/// <remarks>
17/// This extractor does not assign a known problem-family code.
18///
19/// It converts the data and optional model components contained
20/// in a <see cref="SupplyChain"/> into a feature profile that
21/// can subsequently be used by a problem classifier.
22/// </remarks>
24{
25 /// <summary>
26 /// Default tolerance used when comparing numerical
27 /// time-series values.
28 /// </summary>
29 public const double DefaultNumericalTolerance =
30 1e-9;
31
32 /// <summary>
33 /// Extracts lot-sizing problem features and automatically
34 /// analyzes the product structure.
35 /// </summary>
36 /// <param name="supplyChain">
37 /// Supply-chain instance to analyze.
38 /// </param>
39 /// <param name="numericalTolerance">
40 /// Non-negative finite tolerance used for numerical
41 /// comparisons.
42 /// </param>
43 /// <returns>
44 /// Extracted lot-sizing problem-feature profile.
45 /// </returns>
47 SupplyChain supplyChain,
48 double numericalTolerance =
50 {
51 ArgumentNullException.ThrowIfNull(supplyChain);
52
53 ValidateTolerance(
54 numericalTolerance);
55
57 productStructureAnalysis =
59 supplyChain);
60
61 return Extract(
62 supplyChain,
63 productStructureAnalysis,
64 numericalTolerance);
65 }
66
67 /// <summary>
68 /// Extracts lot-sizing problem features using an existing
69 /// product-structure analysis.
70 /// </summary>
71 /// <param name="supplyChain">
72 /// Supply-chain instance to analyze.
73 /// </param>
74 /// <param name="productStructureAnalysis">
75 /// Previously calculated product-structure analysis.
76 /// </param>
77 /// <param name="numericalTolerance">
78 /// Non-negative finite tolerance used for numerical
79 /// comparisons.
80 /// </param>
81 /// <returns>
82 /// Extracted lot-sizing problem-feature profile.
83 /// </returns>
85 SupplyChain supplyChain,
87 productStructureAnalysis,
88 double numericalTolerance =
90 {
91 ArgumentNullException.ThrowIfNull(supplyChain);
92
93 ArgumentNullException.ThrowIfNull(
94 productStructureAnalysis);
95
96 ValidateTolerance(
97 numericalTolerance);
98
99 WorkCenter[] workCenters =
100 EnumerateWorkCenters(
101 supplyChain)
102 .ToArray();
103
104 Warehouse[] warehouses =
105 EnumerateWarehouses(
106 supplyChain)
107 .ToArray();
108
109 bool hasTransportLanes =
110 supplyChain.TransportResources.Any(
111 resource =>
112 supplyChain.TransportAssignments.Any(a => a.TransportResourceId == resource.Id));
113
114 var features =
116 {
117 ItemCount =
118 supplyChain.Items.Count,
119
120 PlanningHorizon =
121 supplyChain.PlanningHorizon,
122
123 PlantCount =
124 supplyChain.Plants.Count,
125
126 WorkCenterCount =
127 workCenters.Length,
128
129 WarehouseCount =
130 warehouses.Length,
131
132 SupplierCount =
133 supplyChain.Suppliers.Count,
134
135 DistributionCenterCount =
136 supplyChain
137 .DistributionCenters
138 .Count,
139
140 TransportResourceCount =
141 supplyChain
142 .TransportResources
143 .Count,
144
145 ProductStructureRelationshipCount =
146 productStructureAnalysis
148
149 MaximumProductStructureDepth =
150 productStructureAnalysis
152
153 ProductStructureType =
154 productStructureAnalysis
156
157 HasDemand =
158 supplyChain.Demands.Count > 0,
159
160 /*
161 * The current Core model stores explicit
162 * deterministic demand quantities.
163 *
164 * It does not currently contain stochastic
165 * demand distributions or scenarios.
166 */
167 HasDeterministicDemand =
168 supplyChain.Demands.Count > 0,
169
170 HasTimeVaryingDemand =
171 supplyChain.Demands.Any(
172 demand =>
173 HasTimeVariation(
174 demand.Quantities,
175 numericalTolerance)),
176
177 HasInitialInventory =
178 supplyChain.Inventories.Any(
179 inventory =>
180 inventory.InitialInventory >
181 numericalTolerance),
182
183 HasSafetyStockRequirements =
184 supplyChain.Inventories.Any(
185 inventory =>
186 inventory.SafetyStock is not null),
187
188 HasBacklogging =
189 supplyChain
190 .DistributionCenterSourcings
191 .Any(
192 sourcing =>
193 sourcing.BacklogConstraint
194 is not null),
195
196 HasLostSales =
197 supplyChain
198 .DistributionCenterSourcings
199 .Any(
200 sourcing =>
201 sourcing.ShortageConstraint
202 is not null),
203
204 HasProduction =
205 supplyChain
206 .ProductionRoutings
207 .Count > 0 ||
208 supplyChain
209 .ProductionCharacteristics
210 .Count > 0,
211
212 HasProductionCapacityConstraints =
213 workCenters.Any(
214 workCenter =>
215 workCenter.CapacityConstraint
216 is not null),
217
218 HasSharedProductionCapacity =
219 DetectSharedProductionCapacity(
220 supplyChain),
221
222 HasTimeVaryingProductionCapacity =
223 workCenters.Any(
224 workCenter =>
225 workCenter.CapacityConstraint
226 is not null &&
227 HasTimeVariation(
228 workCenter
229 .CapacityConstraint
230 .Values,
231 numericalTolerance)),
232
233 HasSetupCosts =
234 supplyChain
235 .ProductionCharacteristics
236 .Any(
237 characteristic =>
238 characteristic.FixedSetupCost
239 is not null),
240
241 HasSetupTimes =
242 supplyChain
243 .ProductionCharacteristics
244 .Any(
245 characteristic =>
246 characteristic.SetupTime
247 is not null),
248
249 HasStartUpCosts =
250 supplyChain
251 .ProductionCharacteristics
252 .Any(
253 characteristic =>
254 characteristic.StartUpCost
255 is not null),
256
257 HasStartUpTimes =
258 supplyChain
259 .ProductionCharacteristics
260 .Any(
261 characteristic =>
262 characteristic.StartUpTime
263 is not null),
264
265 HasProductionSetupFamilies =
266 supplyChain.ProductionSetupFamilies.Count > 0,
267
268 HasProductionSetupFamilyTimes =
269 supplyChain.ProductionSetupFamilies.Any(
270 family => family.SetupTime is not null),
271
272 HasProductionLeadTimes =
273 supplyChain
274 .ProductionRoutings
275 .Any(
276 routing =>
277 routing.LeadTime > 0),
278
279 HasMinimumLotSizes =
280 supplyChain
281 .ProductionRoutings
282 .Any(
283 routing =>
284 routing.MinimumLotSize
285 is not null),
286
287 HasMaximumLotSizes =
288 supplyChain
289 .ProductionRoutings
290 .Any(
291 routing =>
292 routing.MaximumLotSize
293 is not null),
294
295 HasLotSizeMultiples =
296 supplyChain
297 .ProductionRoutings
298 .Any(
299 routing =>
300 routing.LotSizeMultiple
301 is not null),
302
303 HasGroupingConstraints =
304 supplyChain.ProductionRoutings.Any(
305 routing => routing.GroupingConstraint is not null),
306
307 HasAdditionalProductionCapacity =
308 workCenters.Any(
309 workCenter =>
310 workCenter.AdditionalCapacity
311 is not null),
312
313 HasPurchasing =
314 supplyChain
315 .SupplierDeliveries
316 .Count > 0,
317
318 HasSupplierCapacityConstraints =
319 supplyChain
320 .SupplierDeliveries
321 .Any(
322 delivery =>
323 delivery.CapacityConstraint
324 is not null),
325
326 HasSupplierLeadTimes =
327 supplyChain
328 .SupplierDeliveries
329 .Any(
330 delivery =>
331 delivery.LeadTime > 0),
332
333 HasTransportation =
334 hasTransportLanes ||
335 supplyChain
336 .TransportCharacteristics
337 .Count > 0,
338
339 HasTransportCapacityConstraints =
340 DetectTransportCapacityConstraints(
341 supplyChain),
342
343 HasTransportLeadTimes =
344 supplyChain
345 .TransportResources
346 .SelectMany(
347 resource =>
348 supplyChain.GetTransportLanes(resource.Id))
349 .Any(
350 lane =>
351 lane.LeadTime > 0),
352
353 HasAdditionalTransportCapacity =
354 DetectAdditionalTransportCapacity(
355 supplyChain),
356
357 HasDistribution =
358 supplyChain
359 .DistributionCenterSourcings
360 .Count > 0 ||
361 supplyChain
362 .Demands
363 .Count > 0,
364
365 HasWarehouseCapacityConstraints =
366 DetectWarehouseCapacityConstraints(
367 supplyChain,
368 warehouses),
369
370 HasAdditionalWarehouseCapacity =
371 DetectAdditionalWarehouseCapacity(
372 supplyChain,
373 warehouses),
374
375 /*
376 * Production and storage locations are used
377 * here to characterize the site structure.
378 *
379 * Suppliers and distribution centers are
380 * represented separately by their own flags.
381 */
382 IsMultiSite =
383 supplyChain.Plants.Count +
384 supplyChain
385 .StandaloneWarehouses
386 .Count >
387 1,
388
389 HasFinancialConstraints =
390 supplyChain.PeriodicOperatingExpenditureBudget
391 is not null,
392
393 HasMultipleObjectives =
394 supplyChain.ObjectivePolicy?
395 .HasMultipleEnabledCriteria ??
396 false,
397
398 ObjectiveCriterionCount =
399 supplyChain.ObjectivePolicy?
400 .EnabledCriterionCount ??
401 1,
402
403 PrimaryObjectiveKind =
404 supplyChain.ObjectivePolicy?
405 .PrimaryObjectiveKind ??
406 OptimizationObjectiveKind.Economic,
407
408 ObjectiveAggregationMode =
409 supplyChain.ObjectivePolicy?
410 .AggregationMode ??
411 ObjectiveAggregationMode.Single,
412
413 HasIntegratedScheduling =
414 supplyChain.WorkCenters.Any(
415 workCenter =>
416 workCenter.SchedulingProfile is not null),
417
418 SchedulingBucketMode =
419 supplyChain.WorkCenters
420 .Select(
421 workCenter =>
422 workCenter.SchedulingProfile?
423 .BucketMode ??
424 SchedulingBucketMode.Unspecified)
425 .FirstOrDefault(
426 mode =>
427 mode != SchedulingBucketMode.Unspecified),
428
429 HasInitialSetupState =
430 supplyChain.WorkCenters.Any(
431 workCenter =>
432 workCenter.SchedulingProfile?
433 .HasInitialSetupState == true),
434
435 HasSetupCarryOver =
436 supplyChain.WorkCenters.Any(
437 workCenter =>
438 workCenter.SchedulingProfile?
439 .HasSetupCarryOver == true),
440
441 SetupCarryOverPolicy =
442 supplyChain.WorkCenters
443 .Where(workCenter => workCenter.SchedulingProfile is not null)
444 .Select(workCenter => workCenter.SchedulingProfile!.SetupCarryOverPolicy)
445 .Distinct()
446 .Take(2)
447 .ToArray() is var carryOverPolicies &&
448 carryOverPolicies.Length == 1
449 ? carryOverPolicies[0]
450 : SetupCarryOverPolicy.Unspecified,
451
452 HasSequenceDependentChangeoverTimes =
453 supplyChain.WorkCenters.Any(
454 workCenter =>
455 workCenter.SchedulingProfile?
456 .HasSequenceDependentChangeoverTimes == true),
457
458 HasSequenceDependentChangeoverCosts =
459 supplyChain.WorkCenters.Any(
460 workCenter =>
461 workCenter.SchedulingProfile?
462 .HasSequenceDependentChangeoverCosts == true),
463
464 HasMaximumSetupCountConstraints =
465 supplyChain.WorkCenters.Any(
466 workCenter =>
467 workCenter.SchedulingProfile?
468 .MaximumSetupCount is not null),
469
470 SmallBucketProductionMode =
471 supplyChain.WorkCenters
472 .Where(
473 workCenter =>
474 workCenter.SchedulingProfile is not null)
475 .Select(
476 workCenter =>
477 workCenter.SchedulingProfile!
478 .SmallBucketProductionMode)
479 .Distinct()
480 .Take(2)
481 .ToArray() is var schedulingModes &&
482 schedulingModes.Length == 1
483 ? schedulingModes[0]
484 : SmallBucketProductionMode.Unspecified,
485
486 SchedulingResourceCount =
487 supplyChain.WorkCenters.Count(
488 workCenter =>
489 workCenter.SchedulingProfile is not null),
490
491 HasMaximumProducedItemCountConstraint =
492 supplyChain.WorkCenters.Any(
493 workCenter =>
494 workCenter.SchedulingProfile?
495 .MaximumProducedItemCount is not null),
496
497 MaximumProducedItemCountPerBucket =
498 supplyChain.WorkCenters
499 .Select(
500 workCenter =>
501 workCenter.SchedulingProfile?
502 .MaximumProducedItemCountPerBucket ?? 0)
503 .DefaultIfEmpty(0)
504 .Max(),
505
506 MaximumSetupTransitionsPerBucket =
507 supplyChain.WorkCenters
508 .Select(
509 workCenter =>
510 workCenter.SchedulingProfile?
511 .MaximumSetupTransitionsPerBucket ?? 0)
512 .DefaultIfEmpty(0)
513 .Max(),
514
515 MicroPeriodLengthMode =
516 supplyChain.WorkCenters
517 .Where(workCenter => workCenter.SchedulingProfile is not null)
518 .Select(workCenter => workCenter.SchedulingProfile!.MicroPeriodLengthMode)
519 .Distinct().Take(2).ToArray() is var microLengthModes &&
520 microLengthModes.Length == 1
521 ? microLengthModes[0]
522 : MicroPeriodLengthMode.Unspecified,
523
524 MicroPeriodAssignmentMode =
525 supplyChain.WorkCenters
526 .Where(workCenter => workCenter.SchedulingProfile is not null)
527 .Select(workCenter => workCenter.SchedulingProfile!.MicroPeriodAssignmentMode)
528 .Distinct().Take(2).ToArray() is var microAssignmentModes &&
529 microAssignmentModes.Length == 1
530 ? microAssignmentModes[0]
531 : MicroPeriodAssignmentMode.Unspecified,
532
533 HasExplicitMicroPeriodGrid =
534 supplyChain.WorkCenters.Any(workCenter => workCenter.SchedulingProfile?.HasExplicitMicroPeriodGrid == true),
535
536 TotalMicroPeriodCount =
537 supplyChain.WorkCenters.Sum(workCenter => workCenter.SchedulingProfile?.TotalMicroPeriodCount ?? 0),
538
539 MaximumMicroPeriodCountPerMacroPeriod =
540 supplyChain.WorkCenters.Select(workCenter => workCenter.SchedulingProfile?.MaximumMicroPeriodCountPerMacroPeriod ?? 0).DefaultIfEmpty(0).Max(),
541
542 HasVariableMicroPeriodCount =
543 supplyChain.WorkCenters.Any(workCenter => workCenter.SchedulingProfile?.HasVariableMicroPeriodCount == true)
544 };
545
546 return features;
547 }
548
549 private static bool
550 DetectSharedProductionCapacity(
551 SupplyChain supplyChain)
552 {
553 return supplyChain
554 .ProductionCharacteristics
555 .Where(
556 characteristic =>
557 characteristic.WorkCenter.PlantId > 0 &&
558 characteristic.WorkCenter.WorkCenterId > 0)
559 .GroupBy(
560 characteristic =>
561 (
562 characteristic.WorkCenter.PlantId,
563 characteristic.WorkCenter
564 .WorkCenterId
565 ))
566 .Any(
567 group =>
568 group
569 .Select(
570 characteristic =>
571 characteristic.ItemId)
572 .Distinct()
573 .Count() > 1);
574 }
575
576 private static bool
577 DetectTransportCapacityConstraints(
578 SupplyChain supplyChain)
579 {
580 bool hasGlobalCapacity =
581 supplyChain
582 .TransportResources
583 .Any(
584 resource =>
585 resource.CapacityConstraint
586 is not null);
587
588 bool hasItemSpecificCapacity =
589 supplyChain
590 .TransportCharacteristics
591 .Any(
592 characteristic =>
593 characteristic.CapacityConstraint
594 is not null);
595
596 return
597 hasGlobalCapacity ||
598 hasItemSpecificCapacity;
599 }
600
601 private static bool
602 DetectAdditionalTransportCapacity(
603 SupplyChain supplyChain)
604 {
605 bool hasGlobalAdditionalCapacity =
606 supplyChain
607 .TransportResources
608 .Any(
609 resource =>
610 resource.AdditionalCapacity
611 is not null);
612
613 bool hasItemSpecificAdditionalCapacity =
614 supplyChain
615 .TransportCharacteristics
616 .Any(
617 characteristic =>
618 characteristic.AdditionalCapacity
619 is not null);
620
621 return
622 hasGlobalAdditionalCapacity ||
623 hasItemSpecificAdditionalCapacity;
624 }
625
626 private static bool
627 DetectWarehouseCapacityConstraints(
628 SupplyChain supplyChain,
629 IEnumerable<Warehouse> warehouses)
630 {
631 bool hasGlobalWarehouseCapacity =
632 warehouses.Any(
633 warehouse =>
634 warehouse.CapacityConstraint
635 is not null);
636
637 bool hasItemSpecificWarehouseCapacity =
638 supplyChain.Inventories.Any(
639 inventory =>
640 inventory.CapacityConstraint
641 is not null);
642
643 return
644 hasGlobalWarehouseCapacity ||
645 hasItemSpecificWarehouseCapacity;
646 }
647
648 private static bool
649 DetectAdditionalWarehouseCapacity(
650 SupplyChain supplyChain,
651 IEnumerable<Warehouse> warehouses)
652 {
653 bool hasGlobalAdditionalCapacity =
654 warehouses.Any(
655 warehouse =>
656 warehouse.AdditionalCapacity
657 is not null);
658
659 bool hasItemSpecificAdditionalCapacity =
660 supplyChain.Inventories.Any(
661 inventory =>
662 inventory.AdditionalCapacity
663 is not null);
664
665 return
666 hasGlobalAdditionalCapacity ||
667 hasItemSpecificAdditionalCapacity;
668 }
669
670 private static IEnumerable<WorkCenter>
671 EnumerateWorkCenters(
672 SupplyChain supplyChain)
673 {
674 foreach (Plant plant
675 in supplyChain.Plants)
676 {
677 foreach (WorkCenter workCenter
678 in plant.WorkCenters)
679 {
680 yield return workCenter;
681 }
682 }
683 }
684
685 private static IEnumerable<Warehouse>
686 EnumerateWarehouses(
687 SupplyChain supplyChain)
688 {
689 foreach (Plant plant
690 in supplyChain.Plants)
691 {
692 yield return plant.Warehouse;
693 }
694
695 foreach (StandaloneWarehouse warehouse
696 in supplyChain.StandaloneWarehouses)
697 {
698 yield return warehouse;
699 }
700 }
701
702 private static bool HasTimeVariation(
703 IEnumerable<double> values,
704 double numericalTolerance)
705 {
706 ArgumentNullException.ThrowIfNull(values);
707
708 using IEnumerator<double> enumerator =
709 values.GetEnumerator();
710
711 if (!enumerator.MoveNext())
712 {
713 return false;
714 }
715
716 double referenceValue =
717 enumerator.Current;
718
719 while (enumerator.MoveNext())
720 {
721 double currentValue =
722 enumerator.Current;
723
724 if (!double.IsFinite(referenceValue) ||
725 !double.IsFinite(currentValue))
726 {
727 /*
728 * Invalid numerical data must not be treated
729 * as a stationary valid series.
730 */
731 return true;
732 }
733
734 if (Math.Abs(
735 currentValue -
736 referenceValue) >
737 numericalTolerance)
738 {
739 return true;
740 }
741 }
742
743 return false;
744 }
745
746 private static void ValidateTolerance(
747 double numericalTolerance)
748 {
749 if (!double.IsFinite(
750 numericalTolerance) ||
751 numericalTolerance < 0.0)
752 {
753 throw new ArgumentOutOfRangeException(
754 nameof(numericalTolerance),
755 numericalTolerance,
756 "The numerical tolerance must be finite " +
757 "and non-negative.");
758 }
759 }
760}
Represents the detailed result of an automatic analysis of a product bill-of-materials graph.
int RelationshipCount
Gets the number of distinct component-to-parent relationships.
int MaximumDepth
Gets the maximum number of relationships on a directed path from a leaf item to a root item.
ProductStructureType DetectedType
Gets the product-structure type detected by the analyzer.
Analyzes the bill-of-materials graph of a supply-chain instance and determines its product-structure ...
static ProductStructureAnalysis Analyze(SupplyChain supplyChain)
Analyzes the product structure of a supply-chain instance.
Extracts factual lot-sizing problem features from a supply-chain instance.
static LotSizingProblemFeatures Extract(SupplyChain supplyChain, double numericalTolerance=DefaultNumericalTolerance)
Extracts lot-sizing problem features and automatically analyzes the product structure.
static LotSizingProblemFeatures Extract(SupplyChain supplyChain, ProductStructureAnalysis productStructureAnalysis, double numericalTolerance=DefaultNumericalTolerance)
Extracts lot-sizing problem features using an existing product-structure analysis.
const double DefaultNumericalTolerance
Default tolerance used when comparing numerical time-series values.
Describes the factual structural and modeling features detected in a lot-sizing supply-chain instance...