LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
LotSizingInstanceFactory.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Security.Cryptography;
4using System.Text;
5using System.Xml;
6using System.Xml.Serialization;
7using LotSizingDataModel.Core;
12
14
15/// <summary>
16/// Creates lot-sizing problem instances and initializes their
17/// automatically derived metadata.
18/// </summary>
19/// <remarks>
20/// The factory can:
21/// <list type="bullet">
22/// <item>
23/// <description>
24/// create a root <see cref="LotSizingInstance"/> object;
25/// </description>
26/// </item>
27/// <item>
28/// <description>
29/// calculate a reproducible fingerprint of the supply-chain
30/// data;
31/// </description>
32/// </item>
33/// <item>
34/// <description>
35/// analyze the bill-of-materials graph;
36/// </description>
37/// </item>
38/// <item>
39/// <description>
40/// classify the problem using a known-problem-type catalog.
41/// </description>
42/// </item>
43/// </list>
44///
45/// Known results are not created automatically. An instance
46/// containing no known result remains valid.
47/// </remarks>
48public static class LotSizingInstanceFactory
49{
50 /// <summary>
51 /// Gets the identifier of the fingerprint algorithm and
52 /// serialization convention used by this factory.
53 /// </summary>
54 public const string FingerprintScheme =
55 "SHA256-XML-1";
56
57 /// <summary>
58 /// Creates and automatically analyzes a lot-sizing
59 /// problem instance.
60 /// </summary>
61 /// <param name="instanceId">
62 /// Stable identifier of the instance.
63 /// </param>
64 /// <param name="supplyChain">
65 /// Supply-chain model defining the problem data.
66 /// </param>
67 /// <param name="name">
68 /// Optional human-readable name of the instance.
69 /// </param>
70 /// <param name="declaredProductStructureType">
71 /// Optional product-structure type declared by the
72 /// instance author or source.
73 /// </param>
74 /// <param name="catalog">
75 /// Optional problem-type catalog.
76 ///
77 /// When this argument is <see langword="null"/> and
78 /// classification is enabled, the standard catalog is
79 /// created automatically.
80 /// </param>
81 /// <param name="analyzeProductStructure">
82 /// Value indicating whether the bill-of-materials graph
83 /// must be analyzed automatically.
84 /// </param>
85 /// <param name="classifyProblem">
86 /// Value indicating whether the problem must be classified
87 /// automatically.
88 /// </param>
89 /// <param name="createdBy">
90 /// Optional name of the person, organization or software
91 /// component creating the instance.
92 /// </param>
93 /// <param name="numericalTolerance">
94 /// Non-negative finite tolerance used during numerical
95 /// feature extraction.
96 /// </param>
97 /// <returns>
98 /// Newly created lot-sizing instance.
99 /// </returns>
100 /// <exception cref="ArgumentException">
101 /// Thrown when <paramref name="instanceId"/> is empty.
102 /// </exception>
103 /// <exception cref="ArgumentNullException">
104 /// Thrown when <paramref name="supplyChain"/> is
105 /// <see langword="null"/>.
106 /// </exception>
107 /// <exception cref="ArgumentOutOfRangeException">
108 /// Thrown when <paramref name="numericalTolerance"/> is
109 /// negative or not finite.
110 /// </exception>
111 /// <exception cref="InvalidOperationException">
112 /// Thrown when the supply chain cannot be serialized for
113 /// fingerprint generation or when the selected catalog is
114 /// invalid.
115 /// </exception>
117 string instanceId,
118 SupplyChain supplyChain,
119 string name = "",
120 ProductStructureType declaredProductStructureType =
121 ProductStructureType.Unknown,
122 KnownProblemTypeCatalog? catalog = null,
123 bool analyzeProductStructure = true,
124 bool classifyProblem = true,
125 string createdBy = "",
126 double numericalTolerance =
129 {
130 if (string.IsNullOrWhiteSpace(instanceId))
131 {
132 throw new ArgumentException(
133 "A lot-sizing instance identifier is required.",
134 nameof(instanceId));
135 }
136
137 ArgumentNullException.ThrowIfNull(supplyChain);
138
139 ValidateNumericalTolerance(
140 numericalTolerance);
141
142 string supplyChainFingerprint =
144 supplyChain);
145
146 var instance =
148 instanceId:
149 instanceId,
150
151 supplyChain:
152 supplyChain,
153
154 name:
155 name)
156 {
157 CreatedBy =
158 createdBy?.Trim() ??
159 string.Empty,
160
161 ProductStructure =
163 declaredProductStructureType)
164 };
165
166 if (analyzeProductStructure)
167 {
169 supplyChain:
170 instance.SupplyChain,
171
172 descriptor:
173 instance.ProductStructure,
174
175 supplyChainFingerprint:
176 supplyChainFingerprint);
177 }
178
179 if (classifyProblem)
180 {
181 KnownProblemTypeCatalog effectiveCatalog =
182 catalog ??
185
186 instance.ProblemClassification =
188 supplyChain:
189 instance.SupplyChain,
190
191 catalog:
192 effectiveCatalog,
193
194 supplyChainFingerprint:
195 supplyChainFingerprint,
196
197 numericalTolerance:
198 numericalTolerance);
199 }
200
201 return instance;
202 }
203
204 /// <summary>
205 /// Creates a lot-sizing instance without automatically
206 /// analyzing or classifying it.
207 /// </summary>
208 /// <param name="instanceId">
209 /// Stable identifier of the instance.
210 /// </param>
211 /// <param name="supplyChain">
212 /// Supply-chain model defining the problem data.
213 /// </param>
214 /// <param name="name">
215 /// Optional human-readable name of the instance.
216 /// </param>
217 /// <param name="declaredProductStructureType">
218 /// Optional product-structure type declared by the source.
219 /// </param>
220 /// <param name="createdBy">
221 /// Optional creator information.
222 /// </param>
223 /// <returns>
224 /// Newly created, unanalyzed lot-sizing instance.
225 /// </returns>
227 string instanceId,
228 SupplyChain supplyChain,
229 string name = "",
230 ProductStructureType declaredProductStructureType =
231 ProductStructureType.Unknown,
232 string createdBy = "")
233 {
234 return Create(
235 instanceId:
236 instanceId,
237
238 supplyChain:
239 supplyChain,
240
241 name:
242 name,
243
244 declaredProductStructureType:
245 declaredProductStructureType,
246
247 catalog:
248 null,
249
250 analyzeProductStructure:
251 false,
252
253 classifyProblem:
254 false,
255
256 createdBy:
257 createdBy);
258 }
259
260 /// <summary>
261 /// Recalculates the product-structure analysis and problem
262 /// classification of an existing instance.
263 /// </summary>
264 /// <param name="instance">
265 /// Instance whose derived data must be refreshed.
266 /// </param>
267 /// <param name="catalog">
268 /// Optional problem-type catalog.
269 ///
270 /// When this argument is <see langword="null"/> and
271 /// classification is enabled, the standard catalog is
272 /// used.
273 /// </param>
274 /// <param name="analyzeProductStructure">
275 /// Value indicating whether the persistent
276 /// product-structure descriptor must be updated.
277 /// </param>
278 /// <param name="classifyProblem">
279 /// Value indicating whether the problem classification
280 /// must be recalculated.
281 /// </param>
282 /// <param name="numericalTolerance">
283 /// Non-negative finite tolerance used during numerical
284 /// feature extraction.
285 /// </param>
286 /// <returns>
287 /// Fingerprint calculated for the current supply-chain
288 /// data.
289 /// </returns>
290 /// <remarks>
291 /// When one of the two analyses is not refreshed, an
292 /// existing result based on a different fingerprint is
293 /// marked as outdated.
294 ///
295 /// Known results are not modified.
296 /// </remarks>
297 public static string RefreshDerivedData(
298 LotSizingInstance instance,
299 KnownProblemTypeCatalog? catalog = null,
300 bool analyzeProductStructure = true,
301 bool classifyProblem = true,
302 double numericalTolerance =
304 .DefaultNumericalTolerance)
305 {
306 ArgumentNullException.ThrowIfNull(instance);
307
308 ValidateNumericalTolerance(
309 numericalTolerance);
310
311 string supplyChainFingerprint =
313 instance.SupplyChain);
314
315 if (analyzeProductStructure)
316 {
318 supplyChain:
319 instance.SupplyChain,
320
321 descriptor:
322 instance.ProductStructure,
323
324 supplyChainFingerprint:
325 supplyChainFingerprint);
326 }
327 else
328 {
329 MarkProductStructureAsOutdatedWhenRequired(
330 instance.ProductStructure,
331 supplyChainFingerprint);
332 }
333
334 if (classifyProblem)
335 {
336 KnownProblemTypeCatalog effectiveCatalog =
337 catalog ??
340
341 instance.ProblemClassification =
343 supplyChain:
344 instance.SupplyChain,
345
346 catalog:
347 effectiveCatalog,
348
349 supplyChainFingerprint:
350 supplyChainFingerprint,
351
352 numericalTolerance:
353 numericalTolerance);
354 }
355 else
356 {
357 MarkClassificationAsOutdatedWhenRequired(
358 instance.ProblemClassification,
359 supplyChainFingerprint);
360 }
361
363
364 instance.ModifiedAtUtc =
365 DateTime.UtcNow;
366
367 return supplyChainFingerprint;
368 }
369
370 /// <summary>
371 /// Recalculates only the persistent product-structure
372 /// analysis of an instance.
373 /// </summary>
374 /// <param name="instance">
375 /// Instance to analyze.
376 /// </param>
377 /// <returns>
378 /// Detailed product-structure analysis.
379 /// </returns>
380 public static ProductStructureAnalysis
382 LotSizingInstance instance)
383 {
384 ArgumentNullException.ThrowIfNull(instance);
385
386 string fingerprint =
388 instance.SupplyChain);
389
390 ProductStructureAnalysis analysis =
392 supplyChain:
393 instance.SupplyChain,
394
395 descriptor:
396 instance.ProductStructure,
397
398 supplyChainFingerprint:
399 fingerprint);
400
402
403 instance.ModifiedAtUtc =
404 DateTime.UtcNow;
405
406 return analysis;
407 }
408
409 /// <summary>
410 /// Recalculates only the problem classification of an
411 /// instance.
412 /// </summary>
413 /// <param name="instance">
414 /// Instance to classify.
415 /// </param>
416 /// <param name="catalog">
417 /// Optional problem-type catalog.
418 ///
419 /// When omitted, the standard catalog is used.
420 /// </param>
421 /// <param name="numericalTolerance">
422 /// Non-negative finite tolerance used during numerical
423 /// feature extraction.
424 /// </param>
425 /// <returns>
426 /// Newly generated problem classification.
427 /// </returns>
430 LotSizingInstance instance,
431 KnownProblemTypeCatalog? catalog = null,
432 double numericalTolerance =
434 .DefaultNumericalTolerance)
435 {
436 ArgumentNullException.ThrowIfNull(instance);
437
438 ValidateNumericalTolerance(
439 numericalTolerance);
440
441 KnownProblemTypeCatalog effectiveCatalog =
442 catalog ??
445
446 string fingerprint =
448 instance.SupplyChain);
449
450 LotSizingProblemClassification classification =
452 supplyChain:
453 instance.SupplyChain,
454
455 catalog:
456 effectiveCatalog,
457
458 supplyChainFingerprint:
459 fingerprint,
460
461 numericalTolerance:
462 numericalTolerance);
463
464 instance.ProblemClassification =
465 classification;
466
468
469 instance.ModifiedAtUtc =
470 DateTime.UtcNow;
471
472 return classification;
473 }
474
475 /// <summary>
476 /// Calculates a SHA-256 fingerprint of a supply-chain
477 /// object using its compact XML serialization.
478 /// </summary>
479 /// <param name="supplyChain">
480 /// Supply-chain object whose fingerprint must be
481 /// calculated.
482 /// </param>
483 /// <returns>
484 /// Fingerprint formatted as
485 /// <c>SHA256-XML-1:HEXADECIMAL_HASH</c>.
486 /// </returns>
487 /// <remarks>
488 /// Collection order is significant. Two semantically
489 /// equivalent models whose collections are ordered
490 /// differently may therefore produce different
491 /// fingerprints.
492 ///
493 /// This behavior is intentional because collection order
494 /// is part of the serialized instance representation.
495 /// </remarks>
496 /// <exception cref="ArgumentNullException">
497 /// Thrown when <paramref name="supplyChain"/> is
498 /// <see langword="null"/>.
499 /// </exception>
500 /// <exception cref="InvalidOperationException">
501 /// Thrown when the supply chain cannot be serialized.
502 /// </exception>
503 public static string ComputeSupplyChainFingerprint(
504 SupplyChain supplyChain)
505 {
506 ArgumentNullException.ThrowIfNull(supplyChain);
507
508 byte[] serializedData =
509 SerializeSupplyChainForFingerprint(
510 supplyChain);
511
512 byte[] hash;
513
514 using (SHA256 sha256 =
515 SHA256.Create())
516 {
517 hash =
518 sha256.ComputeHash(
519 serializedData);
520 }
521
522 return
523 $"{FingerprintScheme}:" +
524 Convert.ToHexString(hash);
525 }
526
527 private static byte[]
528 SerializeSupplyChainForFingerprint(
529 SupplyChain supplyChain)
530 {
531 var serializer =
532 new XmlSerializer(
533 typeof(SupplyChain));
534
535 var namespaces =
536 new XmlSerializerNamespaces();
537
538 namespaces.Add(
539 string.Empty,
540 string.Empty);
541
542 var settings =
543 new XmlWriterSettings
544 {
545 Encoding =
546 new UTF8Encoding(
547 encoderShouldEmitUTF8Identifier:
548 false),
549
550 Indent =
551 false,
552
553 OmitXmlDeclaration =
554 false,
555
556 NewLineHandling =
557 NewLineHandling.None,
558
559 CloseOutput =
560 false
561 };
562
563 try
564 {
565 using var stream =
566 new MemoryStream();
567
568 using (XmlWriter writer =
569 XmlWriter.Create(
570 stream,
571 settings))
572 {
573 serializer.Serialize(
574 writer,
575 supplyChain,
576 namespaces);
577 }
578
579 return stream.ToArray();
580 }
581 catch (InvalidOperationException exception)
582 {
583 throw new InvalidOperationException(
584 "The supply-chain model could not be " +
585 "serialized for fingerprint generation.",
586 exception);
587 }
588 }
589
590 private static void
591 MarkProductStructureAsOutdatedWhenRequired(
592 ProductStructureDescriptor descriptor,
593 string currentFingerprint)
594 {
595 if (!descriptor.HasBeenAnalyzed &&
596 !descriptor.HasDetectedType)
597 {
598 return;
599 }
600
601 bool fingerprintMatches =
602 !string.IsNullOrWhiteSpace(
603 descriptor.SupplyChainFingerprint) &&
604 string.Equals(
605 descriptor.SupplyChainFingerprint,
606 currentFingerprint,
607 StringComparison.Ordinal);
608
609 if (!fingerprintMatches)
610 {
611 descriptor.MarkAsOutdated();
612 }
613 }
614
615 private static void
616 MarkClassificationAsOutdatedWhenRequired(
617 LotSizingProblemClassification classification,
618 string currentFingerprint)
619 {
620 if (classification.Status ==
621 ProblemClassificationStatus.NotAnalyzed)
622 {
623 return;
624 }
625
626 if (!classification
627 .MatchesSupplyChainFingerprint(
628 currentFingerprint))
629 {
630 classification.MarkAsOutdated();
631 }
632 }
633
634 private static void ValidateNumericalTolerance(
635 double numericalTolerance)
636 {
637 if (!double.IsFinite(numericalTolerance) ||
638 numericalTolerance < 0.0)
639 {
640 throw new ArgumentOutOfRangeException(
641 nameof(numericalTolerance),
642 numericalTolerance,
643 "The numerical tolerance must be finite " +
644 "and non-negative.");
645 }
646 }
647}
Represents the detailed result of an automatic analysis of a product bill-of-materials graph.
Analyzes the bill-of-materials graph of a supply-chain instance and determines its product-structure ...
static ProductStructureAnalysis AnalyzeAndUpdate(SupplyChain supplyChain, ProductStructureDescriptor descriptor, string supplyChainFingerprint="")
Analyzes a supply-chain product structure and applies the result to a persistent descriptor.
Creates predefined catalogs of known lot-sizing problem-family definitions.
static KnownProblemTypeCatalog CreateStandardCatalog()
Creates the standard catalog of known lot-sizing problem families.
Stores known lot-sizing problem-family definitions and the reusable rules used to recognize them.
Stores the persistent result of the automatic classification of a lot-sizing problem instance.
ProblemClassificationStatus Status
Gets or sets the current global classification status.
Classifies lot-sizing supply-chain instances using a catalog of known problem-family definitions.
static LotSizingProblemClassification Classify(SupplyChain supplyChain, KnownProblemTypeCatalog catalog, string supplyChainFingerprint="", double numericalTolerance=LotSizingProblemFeatureExtractor .DefaultNumericalTolerance)
Classifies a supply-chain instance using a known problem-type catalog.
Extracts factual lot-sizing problem features from a supply-chain instance.
const double DefaultNumericalTolerance
Default tolerance used when comparing numerical time-series values.
Creates lot-sizing problem instances and initializes their automatically derived metadata.
static LotSizingInstance CreateUnanalyzed(string instanceId, SupplyChain supplyChain, string name="", ProductStructureType declaredProductStructureType=ProductStructureType.Unknown, string createdBy="")
Creates a lot-sizing instance without automatically analyzing or classifying it.
static ProductStructureAnalysis RefreshProductStructure(LotSizingInstance instance)
Recalculates only the persistent product-structure analysis of an instance.
const string FingerprintScheme
Gets the identifier of the fingerprint algorithm and serialization convention used by this factory.
static LotSizingInstance Create(string instanceId, SupplyChain supplyChain, string name="", ProductStructureType declaredProductStructureType=ProductStructureType.Unknown, KnownProblemTypeCatalog? catalog=null, bool analyzeProductStructure=true, bool classifyProblem=true, string createdBy="", double numericalTolerance=LotSizingProblemFeatureExtractor .DefaultNumericalTolerance)
Creates and automatically analyzes a lot-sizing problem instance.
static string RefreshDerivedData(LotSizingInstance instance, KnownProblemTypeCatalog? catalog=null, bool analyzeProductStructure=true, bool classifyProblem=true, double numericalTolerance=LotSizingProblemFeatureExtractor .DefaultNumericalTolerance)
Recalculates the product-structure analysis and problem classification of an existing instance.
static string ComputeSupplyChainFingerprint(SupplyChain supplyChain)
Calculates a SHA-256 fingerprint of a supply-chain object using its compact XML serialization.
static LotSizingProblemClassification RefreshProblemClassification(LotSizingInstance instance, KnownProblemTypeCatalog? catalog=null, double numericalTolerance=LotSizingProblemFeatureExtractor .DefaultNumericalTolerance)
Recalculates only the problem classification of an instance.
Adds explicit closed-loop return streams to a lot-sizing instance without changing the historical Sup...
LotSizingProblemClassification ProblemClassification
Gets or sets the automatic lot-sizing problem classification.
void ClearSolutionMethodRecommendationReport()
Clears the complete solution-method recommendation report.
SupplyChain SupplyChain
Gets or sets the supply-chain model represented by the instance.
ProductStructureDescriptor ProductStructure
Gets or sets the product-structure descriptor associated with the supply-chain model.
Describes the declared and automatically detected structure of the product bill-of-materials graph of...
void MarkAsOutdated()
Marks the recorded automatic analysis as outdated.
string SupplyChainFingerprint
Gets or sets the fingerprint of the supply-chain data used during the product-structure analysis.
bool HasDetectedType
Gets a value indicating whether an automatic analysis detected a product-structure type.
bool HasBeenAnalyzed
Gets a value indicating whether an automatic analysis date has been recorded.
ProblemClassificationStatus
Indicates the current status of the automatic classification of a lot-sizing problem instance.
ProductStructureType
Identifies the structural category of a product bill-of-materials graph.