LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
KnownResult.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Globalization;
4using System.Linq;
5using System.Xml.Serialization;
6using LotSizingDataModel.Core.Common;
8using LotSizingDataModel.Solution;
9using LotSizingDataModel.Solution.Common;
10
12
13/// <summary>
14/// Represents a known result associated with a lot-sizing
15/// problem instance.
16/// </summary>
17/// <remarks>
18/// A known result may contain:
19/// <list type="bullet">
20/// <item>
21/// <description>
22/// only an objective value reported by an external source;
23/// </description>
24/// </item>
25/// <item>
26/// <description>
27/// feasibility or optimality claims without detailed
28/// decisions;
29/// </description>
30/// </item>
31/// <item>
32/// <description>
33/// a complete solver-independent
34/// <see cref="LotSizingSolution"/>;
35/// </description>
36/// </item>
37/// <item>
38/// <description>
39/// source, authorship and verification information.
40/// </description>
41/// </item>
42/// </list>
43///
44/// A detailed solution is deliberately optional. Published
45/// benchmark results frequently provide only an objective
46/// value and a description of the method used.
47/// </remarks>
48[Serializable]
49[XmlType(TypeName = "knownResult")]
50public sealed class KnownResult : ModelObject
51{
52 private string _resultId =
53 string.Empty;
54
55 private string _name =
56 string.Empty;
57
58 private double? _reportedObjectiveValue;
59
60 private string _objectiveName =
61 string.Empty;
62
63 private string _objectiveUnit =
64 string.Empty;
65
66 /*
67 * The first member of each enumeration is treated as the
68 * default state indicating that no information has been
69 * recorded.
70 */
71 private FeasibilityStatus _feasibilityStatus =
72 default;
73
74 private OptimalityStatus _optimalityStatus =
75 default;
76
77 private KnownResultVerificationStatus _verificationStatus =
79
80 private LotSizingSolution? _detailedSolution;
81
82 private string _methodName =
83 string.Empty;
84
85 private string _sourceTitle =
86 string.Empty;
87
88 private string _sourceReference =
89 string.Empty;
90
91 private string _sourceUri =
92 string.Empty;
93
94 private string _digitalObjectIdentifier =
95 string.Empty;
96
97 private DateTime? _obtainedAtUtc;
98
99 private DateTime? _recordedAtUtc;
100
101 private string _supplyChainFingerprint =
102 string.Empty;
103
104 private string _supersededByResultId =
105 string.Empty;
106
107 private string _comment =
108 string.Empty;
109
110 /// <summary>
111 /// Initializes an empty known result.
112 /// </summary>
113 /// <remarks>
114 /// This constructor is required for XML serialization.
115 /// </remarks>
116 public KnownResult()
117 {
118 }
119
120 /// <summary>
121 /// Initializes a known result with a stable identifier.
122 /// </summary>
123 /// <param name="resultId">
124 /// Stable identifier of the known result.
125 /// </param>
126 /// <exception cref="ArgumentException">
127 /// Thrown when <paramref name="resultId"/> is null, empty
128 /// or composed only of white-space characters.
129 /// </exception>
131 string resultId)
132 {
133 if (string.IsNullOrWhiteSpace(resultId))
134 {
135 throw new ArgumentException(
136 "A known-result identifier is required.",
137 nameof(resultId));
138 }
139
140 ResultId =
141 resultId;
142 }
143
144 /// <summary>
145 /// Initializes a known result containing a reported
146 /// objective value.
147 /// </summary>
148 /// <param name="resultId">
149 /// Stable identifier of the known result.
150 /// </param>
151 /// <param name="reportedObjectiveValue">
152 /// Objective value reported for the instance.
153 /// </param>
154 /// <param name="name">
155 /// Optional human-readable result name.
156 /// </param>
157 /// <exception cref="ArgumentException">
158 /// Thrown when <paramref name="resultId"/> is null, empty
159 /// or composed only of white-space characters.
160 /// </exception>
161 /// <exception cref="ArgumentOutOfRangeException">
162 /// Thrown when <paramref name="reportedObjectiveValue"/>
163 /// is not finite.
164 /// </exception>
166 string resultId,
167 double reportedObjectiveValue,
168 string name = "")
169 : this(resultId)
170 {
172 reportedObjectiveValue;
173
174 Name =
175 name;
176 }
177
178 /// <summary>
179 /// Gets or sets the stable identifier of this result.
180 /// </summary>
181 /// <remarks>
182 /// The identifier is used by
183 /// <c>LotSizingInstance.BestKnownResultId</c> and by links
184 /// between superseded results.
185 /// </remarks>
186 [XmlAttribute("resultId")]
187 public string ResultId
188 {
189 get => _resultId;
190 set
191 {
192 if (SetProperty(
193 ref _resultId,
194 value?.Trim() ?? string.Empty))
195 {
196 NotifyIdentityProperties();
197 }
198 }
199 }
200
201 /// <summary>
202 /// Gets or sets the human-readable name of the result.
203 /// </summary>
204 /// <remarks>
205 /// Examples include <c>Best value reported in 2025</c>,
206 /// <c>CPLEX reference solution</c> and
207 /// <c>Author-provided solution</c>.
208 /// </remarks>
209 [XmlAttribute("name")]
210 public string Name
211 {
212 get => _name;
213 set
214 {
215 if (SetProperty(
216 ref _name,
217 value?.Trim() ?? string.Empty))
218 {
219 OnPropertyChanged(
220 nameof(HasName));
221 }
222 }
223 }
224
225 /// <summary>
226 /// Gets or sets the objective value explicitly reported
227 /// for this result.
228 /// </summary>
229 /// <remarks>
230 /// This property is independent from the optional
231 /// detailed solution.
232 ///
233 /// It may therefore be populated even when
234 /// <see cref="DetailedSolution"/> is
235 /// <see langword="null"/>.
236 /// </remarks>
237 /// <exception cref="ArgumentOutOfRangeException">
238 /// Thrown when the supplied value is not finite.
239 /// </exception>
240 [XmlElement(
241 "reportedObjectiveValue",
242 IsNullable = true)]
244 {
245 get => _reportedObjectiveValue;
246 set
247 {
248 if (value.HasValue &&
249 !double.IsFinite(value.Value))
250 {
251 throw new ArgumentOutOfRangeException(
252 nameof(value),
253 value,
254 "The reported objective value must " +
255 "be finite.");
256 }
257
258 if (SetProperty(
259 ref _reportedObjectiveValue,
260 value))
261 {
262 NotifyResultDataProperties();
263 }
264 }
265 }
266
267 /// <summary>
268 /// Gets or sets the name of the reported objective.
269 /// </summary>
270 /// <remarks>
271 /// Examples include <c>TotalCost</c>,
272 /// <c>NetPresentValue</c> and <c>Makespan</c>.
273 ///
274 /// This property is useful when the instance supports
275 /// several possible evaluation criteria.
276 /// </remarks>
277 [XmlAttribute("objectiveName")]
278 public string ObjectiveName
279 {
280 get => _objectiveName;
281 set
282 {
283 if (SetProperty(
284 ref _objectiveName,
285 value?.Trim() ?? string.Empty))
286 {
287 OnPropertyChanged(
288 nameof(HasObjectiveName));
289 }
290 }
291 }
292
293 /// <summary>
294 /// Gets or sets the unit associated with the reported
295 /// objective value.
296 /// </summary>
297 /// <remarks>
298 /// Examples include <c>EUR</c>, <c>USD</c>,
299 /// <c>hours</c> and <c>dimensionless</c>.
300 /// </remarks>
301 [XmlAttribute("objectiveUnit")]
302 public string ObjectiveUnit
303 {
304 get => _objectiveUnit;
305 set
306 {
307 if (SetProperty(
308 ref _objectiveUnit,
309 value?.Trim() ?? string.Empty))
310 {
311 OnPropertyChanged(
312 nameof(HasObjectiveUnit));
313 }
314 }
315 }
316
317 /// <summary>
318 /// Gets or sets the reported or verified feasibility
319 /// status of the result.
320 /// </summary>
321 /// <remarks>
322 /// This status may represent a claim from an external
323 /// source when no detailed solution is available.
324 ///
325 /// The verification level of the claim is described by
326 /// <see cref="VerificationStatus"/>.
327 ///
328 /// The default enumeration value is interpreted as the
329 /// absence of feasibility information.
330 /// </remarks>
331 [XmlAttribute("feasibilityStatus")]
333 {
334 get => _feasibilityStatus;
335 set
336 {
337 if (SetProperty(
338 ref _feasibilityStatus,
339 value))
340 {
341 OnPropertyChanged(
343 }
344 }
345 }
346
347 /// <summary>
348 /// Gets or sets the reported or verified optimality
349 /// status of the result.
350 /// </summary>
351 /// <remarks>
352 /// An external source may claim optimality even when the
353 /// detailed solution or proof is unavailable.
354 ///
355 /// The default enumeration value is interpreted as the
356 /// absence of optimality information.
357 /// </remarks>
358 [XmlAttribute("optimalityStatus")]
360 {
361 get => _optimalityStatus;
362 set
363 {
364 if (SetProperty(
365 ref _optimalityStatus,
366 value))
367 {
368 OnPropertyChanged(
370 }
371 }
372 }
373
374 /// <summary>
375 /// Gets or sets the level of verification applied to
376 /// this result.
377 /// </summary>
378 [XmlAttribute("verificationStatus")]
380 {
381 get => _verificationStatus;
382 set
383 {
384 if (SetProperty(
385 ref _verificationStatus,
386 value))
387 {
388 NotifyVerificationProperties();
389 }
390 }
391 }
392
393 /// <summary>
394 /// Gets or sets the optional detailed solution associated
395 /// with this known result.
396 /// </summary>
397 /// <remarks>
398 /// A null value is valid and commonly represents a
399 /// published result for which only an objective value is
400 /// available.
401 ///
402 /// When present, the detailed solution contains the
403 /// production, inventory, purchasing, transportation,
404 /// distribution and capacity decisions.
405 /// </remarks>
406 [XmlElement(
407 "detailedSolution",
408 IsNullable = true)]
409 public LotSizingSolution? DetailedSolution
410 {
411 get => _detailedSolution;
412 set
413 {
414 if (SetProperty(
415 ref _detailedSolution,
416 value))
417 {
418 NotifyResultDataProperties();
419 }
420 }
421 }
422
423 /// <summary>
424 /// Gets or sets the name of the algorithm, solver or
425 /// procedure that produced the result.
426 /// </summary>
427 /// <remarks>
428 /// Examples include <c>Dynamic programming</c>,
429 /// <c>Branch-and-cut</c>, <c>CPLEX 22.1</c> and
430 /// <c>Author heuristic</c>.
431 /// </remarks>
432 [XmlElement("methodName")]
433 public string MethodName
434 {
435 get => _methodName;
436 set
437 {
438 if (SetProperty(
439 ref _methodName,
440 value?.Trim() ?? string.Empty))
441 {
442 OnPropertyChanged(
443 nameof(HasMethodInformation));
444 }
445 }
446 }
447
448 /// <summary>
449 /// Gets or sets the title of the publication, report,
450 /// repository or other source reporting the result.
451 /// </summary>
452 [XmlElement("sourceTitle")]
453 public string SourceTitle
454 {
455 get => _sourceTitle;
456 set
457 {
458 if (SetProperty(
459 ref _sourceTitle,
460 value?.Trim() ?? string.Empty))
461 {
462 NotifySourceProperties();
463 }
464 }
465 }
466
467 /// <summary>
468 /// Gets or sets a bibliographic or documentary reference
469 /// for the source reporting the result.
470 /// </summary>
471 /// <remarks>
472 /// This property may contain a formatted citation,
473 /// technical-report identifier, benchmark name or
474 /// repository reference.
475 /// </remarks>
476 [XmlElement("sourceReference")]
477 public string SourceReference
478 {
479 get => _sourceReference;
480 set
481 {
482 if (SetProperty(
483 ref _sourceReference,
484 value?.Trim() ?? string.Empty))
485 {
486 NotifySourceProperties();
487 }
488 }
489 }
490
491 /// <summary>
492 /// Gets or sets an optional URI identifying the source
493 /// from which the result was obtained.
494 /// </summary>
495 /// <remarks>
496 /// The URI is stored as text because benchmark sources
497 /// may use HTTP addresses, repository identifiers or
498 /// other URI schemes.
499 /// </remarks>
500 [XmlElement("sourceUri")]
501 public string SourceUri
502 {
503 get => _sourceUri;
504 set
505 {
506 if (SetProperty(
507 ref _sourceUri,
508 value?.Trim() ?? string.Empty))
509 {
510 NotifySourceProperties();
511 }
512 }
513 }
514
515 /// <summary>
516 /// Gets or sets the digital object identifier of the
517 /// publication reporting the result.
518 /// </summary>
519 /// <remarks>
520 /// The value should normally be stored without a
521 /// <c>https://doi.org/</c> prefix.
522 /// </remarks>
523 [XmlAttribute("doi")]
525 {
526 get => _digitalObjectIdentifier;
527 set
528 {
529 if (SetProperty(
530 ref _digitalObjectIdentifier,
531 NormalizeDigitalObjectIdentifier(value)))
532 {
533 NotifySourceProperties();
534 }
535 }
536 }
537
538 /// <summary>
539 /// Gets the names of persons or organizations credited
540 /// with obtaining or discovering the result.
541 /// </summary>
542 /// <remarks>
543 /// These names may differ from the persons who imported
544 /// the result into the current data file.
545 /// </remarks>
546 [XmlArray("discoverers")]
547 [XmlArrayItem("discoverer")]
548 public List<string> Discoverers { get; } =
549 new();
550
551 /// <summary>
552 /// Gets the authors or organizations associated with the
553 /// source reporting the result.
554 /// </summary>
555 [XmlArray("sourceAuthors")]
556 [XmlArrayItem("author")]
557 public List<string> SourceAuthors { get; } =
558 new();
559
560 /// <summary>
561 /// Gets or sets the UTC date and time at which the result
562 /// was obtained.
563 /// </summary>
564 /// <remarks>
565 /// A null value means that the result date is unknown.
566 ///
567 /// For results extracted from publications, this property
568 /// may represent the known publication or computation
569 /// date when a more precise timestamp is unavailable.
570 /// </remarks>
571 [XmlElement(
572 "obtainedAtUtc",
573 IsNullable = true)]
574 public DateTime? ObtainedAtUtc
575 {
576 get => _obtainedAtUtc;
577 set => SetUtcDateTime(
578 ref _obtainedAtUtc,
579 value,
580 nameof(ObtainedAtUtc));
581 }
582
583 /// <summary>
584 /// Gets or sets the UTC date and time at which the result
585 /// was recorded in the instance data.
586 /// </summary>
587 [XmlElement(
588 "recordedAtUtc",
589 IsNullable = true)]
590 public DateTime? RecordedAtUtc
591 {
592 get => _recordedAtUtc;
593 set => SetUtcDateTime(
594 ref _recordedAtUtc,
595 value,
596 nameof(RecordedAtUtc));
597 }
598
599 /// <summary>
600 /// Gets or sets the fingerprint of the supply-chain data
601 /// to which this result applies.
602 /// </summary>
603 /// <remarks>
604 /// A fingerprint prevents a result obtained for a
605 /// previous instance version from being silently treated
606 /// as valid for modified data.
607 /// </remarks>
608 [XmlAttribute("supplyChainFingerprint")]
610 {
611 get => _supplyChainFingerprint;
612 set
613 {
614 if (SetProperty(
615 ref _supplyChainFingerprint,
616 value?.Trim() ?? string.Empty))
617 {
618 OnPropertyChanged(
620 }
621 }
622 }
623
624 /// <summary>
625 /// Gets or sets the identifier of a newer result that
626 /// supersedes this result.
627 /// </summary>
628 /// <remarks>
629 /// The superseded result is retained for historical
630 /// traceability.
631 ///
632 /// A superseded result should not normally be selected as
633 /// the current best known result.
634 /// </remarks>
635 [XmlAttribute("supersededByResultId")]
637 {
638 get => _supersededByResultId;
639 set
640 {
641 if (SetProperty(
642 ref _supersededByResultId,
643 value?.Trim() ?? string.Empty))
644 {
645 NotifyVerificationProperties();
646 }
647 }
648 }
649
650 /// <summary>
651 /// Gets or sets an optional explanatory comment about the
652 /// result, its source or its verification.
653 /// </summary>
654 [XmlElement("comment")]
655 public string Comment
656 {
657 get => _comment;
658 set
659 {
660 if (SetProperty(
661 ref _comment,
662 value?.Trim() ?? string.Empty))
663 {
664 OnPropertyChanged(
665 nameof(HasComment));
666 }
667 }
668 }
669
670 /// <summary>
671 /// Gets a value indicating whether the result has a
672 /// stable identifier.
673 /// </summary>
674 [XmlIgnore]
675 public bool HasResultId =>
676 !string.IsNullOrWhiteSpace(ResultId);
677
678 /// <summary>
679 /// Gets a value indicating whether the result has a
680 /// human-readable name.
681 /// </summary>
682 [XmlIgnore]
683 public bool HasName =>
684 !string.IsNullOrWhiteSpace(Name);
685
686 /// <summary>
687 /// Gets a value indicating whether an explicit objective
688 /// value has been reported.
689 /// </summary>
690 [XmlIgnore]
692 ReportedObjectiveValue.HasValue;
693
694 /// <summary>
695 /// Gets a value indicating whether the objective name has
696 /// been recorded.
697 /// </summary>
698 [XmlIgnore]
699 public bool HasObjectiveName =>
700 !string.IsNullOrWhiteSpace(ObjectiveName);
701
702 /// <summary>
703 /// Gets a value indicating whether the objective unit has
704 /// been recorded.
705 /// </summary>
706 [XmlIgnore]
707 public bool HasObjectiveUnit =>
708 !string.IsNullOrWhiteSpace(ObjectiveUnit);
709
710 /// <summary>
711 /// Gets a value indicating whether feasibility
712 /// information has been recorded.
713 /// </summary>
714 /// <remarks>
715 /// The default enumeration value is treated as the state
716 /// in which no feasibility information is available.
717 /// </remarks>
718 [XmlIgnore]
720 !EqualityComparer<FeasibilityStatus>
721 .Default
722 .Equals(
724 default);
725
726 /// <summary>
727 /// Gets a value indicating whether optimality
728 /// information has been recorded.
729 /// </summary>
730 /// <remarks>
731 /// The default enumeration value is treated as the state
732 /// in which no optimality information is available.
733 /// </remarks>
734 [XmlIgnore]
736 !EqualityComparer<OptimalityStatus>
737 .Default
738 .Equals(
740 default);
741
742 /// <summary>
743 /// Gets a value indicating whether a detailed solution is
744 /// associated with the result.
745 /// </summary>
746 [XmlIgnore]
747 public bool HasDetailedSolution =>
748 DetailedSolution is not null;
749
750 /// <summary>
751 /// Gets a value indicating whether information about the
752 /// solution method has been recorded.
753 /// </summary>
754 [XmlIgnore]
755 public bool HasMethodInformation =>
756 !string.IsNullOrWhiteSpace(MethodName);
757
758 /// <summary>
759 /// Gets a value indicating whether at least one source
760 /// field has been recorded.
761 /// </summary>
762 [XmlIgnore]
763 public bool HasSourceInformation =>
764 !string.IsNullOrWhiteSpace(SourceTitle) ||
765 !string.IsNullOrWhiteSpace(SourceReference) ||
766 !string.IsNullOrWhiteSpace(SourceUri) ||
767 !string.IsNullOrWhiteSpace(
769 SourceAuthors.Count > 0;
770
771 /// <summary>
772 /// Gets a value indicating whether at least one
773 /// discoverer has been recorded.
774 /// </summary>
775 [XmlIgnore]
776 public bool HasDiscoverers =>
777 Discoverers.Count > 0;
778
779 /// <summary>
780 /// Gets a value indicating whether a supply-chain
781 /// fingerprint has been recorded.
782 /// </summary>
783 [XmlIgnore]
785 !string.IsNullOrWhiteSpace(
787
788 /// <summary>
789 /// Gets a value indicating whether an explanatory comment
790 /// has been recorded.
791 /// </summary>
792 [XmlIgnore]
793 public bool HasComment =>
794 !string.IsNullOrWhiteSpace(Comment);
795
796 /// <summary>
797 /// Gets a value indicating whether this result has been
798 /// superseded by another result.
799 /// </summary>
800 [XmlIgnore]
801 public bool IsSuperseded =>
802 !string.IsNullOrWhiteSpace(
804
805 /// <summary>
806 /// Gets a value indicating whether the result has been
807 /// disputed.
808 /// </summary>
809 [XmlIgnore]
810 public bool IsDisputed =>
813
814 /// <summary>
815 /// Gets a value indicating whether the result has been
816 /// invalidated.
817 /// </summary>
818 [XmlIgnore]
819 public bool IsInvalidated =>
822
823 /// <summary>
824 /// Gets a value indicating whether the result contains
825 /// usable numerical or detailed solution data.
826 /// </summary>
827 [XmlIgnore]
828 public bool HasResultData =>
831
832 /// <summary>
833 /// Gets a value indicating whether this result may be
834 /// considered when selecting the best known result.
835 /// </summary>
836 /// <remarks>
837 /// This property only determines eligibility. Comparing
838 /// objective values still requires knowledge of the
839 /// objective direction and meaning.
840 /// </remarks>
841 [XmlIgnore]
843 HasResultId &&
845 !IsSuperseded &&
846 !IsDisputed &&
848
849 /// <summary>
850 /// Replaces the persons or organizations credited with
851 /// discovering or obtaining the result.
852 /// </summary>
853 /// <param name="discoverers">
854 /// Discoverer names.
855 /// </param>
857 IEnumerable<string> discoverers)
858 {
859 ReplaceNormalizedStrings(
861 discoverers,
862 nameof(discoverers));
863
864 OnPropertyChanged(
865 nameof(Discoverers));
866
867 OnPropertyChanged(
868 nameof(HasDiscoverers));
869 }
870
871 /// <summary>
872 /// Replaces the authors or organizations associated with
873 /// the result source.
874 /// </summary>
875 /// <param name="sourceAuthors">
876 /// Source-author names.
877 /// </param>
879 IEnumerable<string> sourceAuthors)
880 {
881 ReplaceNormalizedStrings(
883 sourceAuthors,
884 nameof(sourceAuthors));
885
886 OnPropertyChanged(
887 nameof(SourceAuthors));
888
889 OnPropertyChanged(
890 nameof(HasSourceInformation));
891 }
892
893 /// <summary>
894 /// Removes the explicitly reported objective value and
895 /// its descriptive metadata.
896 /// </summary>
898 {
900 null;
901
903 string.Empty;
904
906 string.Empty;
907 }
908
909 /// <summary>
910 /// Removes the detailed solution while preserving all
911 /// reported objective and source information.
912 /// </summary>
914 {
916 null;
917 }
918
919 /// <summary>
920 /// Marks this result as superseded by another result.
921 /// </summary>
922 /// <param name="newResultId">
923 /// Identifier of the newer result.
924 /// </param>
925 /// <exception cref="ArgumentException">
926 /// Thrown when <paramref name="newResultId"/> is empty or
927 /// identifies this result itself.
928 /// </exception>
930 string newResultId)
931 {
932 if (string.IsNullOrWhiteSpace(newResultId))
933 {
934 throw new ArgumentException(
935 "A superseding result identifier is required.",
936 nameof(newResultId));
937 }
938
939 string normalizedResultId =
940 newResultId.Trim();
941
942 if (string.Equals(
943 ResultId,
944 normalizedResultId,
945 StringComparison.OrdinalIgnoreCase))
946 {
947 throw new ArgumentException(
948 "A result cannot supersede itself.",
949 nameof(newResultId));
950 }
951
953 normalizedResultId;
954 }
955
956 /// <summary>
957 /// Removes the link indicating that this result has been
958 /// superseded.
959 /// </summary>
961 {
963 string.Empty;
964 }
965
966 /// <summary>
967 /// Determines whether this result was obtained for the
968 /// supplied supply-chain fingerprint.
969 /// </summary>
970 /// <param name="currentFingerprint">
971 /// Current supply-chain fingerprint.
972 /// </param>
973 /// <returns>
974 /// <see langword="true"/> when both fingerprints are
975 /// present and equal; otherwise, <see langword="false"/>.
976 /// </returns>
978 string currentFingerprint)
979 {
981 string.IsNullOrWhiteSpace(currentFingerprint))
982 {
983 return false;
984 }
985
986 return string.Equals(
988 currentFingerprint.Trim(),
989 StringComparison.Ordinal);
990 }
991
992 /// <inheritdoc/>
993 /// <inheritdoc/>
994 public override string ToString()
995 {
996 string resultDescription =
997 HasName
998 ? Name
999 : ResultId;
1000
1001 string objectiveDescription =
1003 {
1004 double objectiveValue =>
1005 objectiveValue.ToString(
1006 CultureInfo.InvariantCulture),
1007
1008 null when HasDetailedSolution =>
1009 "detailed solution",
1010
1011 _ =>
1012 "no result data"
1013 };
1014
1015 return
1016 $"{resultDescription}; " +
1017 $"{objectiveDescription}; " +
1018 $"{VerificationStatus}";
1019 }
1020
1021 private static string
1022 NormalizeDigitalObjectIdentifier(
1023 string? value)
1024 {
1025 string normalizedValue =
1026 value?.Trim() ??
1027 string.Empty;
1028
1029 const string httpsPrefix =
1030 "https://doi.org/";
1031
1032 const string httpPrefix =
1033 "http://doi.org/";
1034
1035 const string doiPrefix =
1036 "doi:";
1037
1038 if (normalizedValue.StartsWith(
1039 httpsPrefix,
1040 StringComparison.OrdinalIgnoreCase))
1041 {
1042 return normalizedValue[
1043 httpsPrefix.Length..].Trim();
1044 }
1045
1046 if (normalizedValue.StartsWith(
1047 httpPrefix,
1048 StringComparison.OrdinalIgnoreCase))
1049 {
1050 return normalizedValue[
1051 httpPrefix.Length..].Trim();
1052 }
1053
1054 if (normalizedValue.StartsWith(
1055 doiPrefix,
1056 StringComparison.OrdinalIgnoreCase))
1057 {
1058 return normalizedValue[
1059 doiPrefix.Length..].Trim();
1060 }
1061
1062 return normalizedValue;
1063 }
1064
1065 private static void ReplaceNormalizedStrings(
1066 ICollection<string> destination,
1067 IEnumerable<string> source,
1068 string parameterName)
1069 {
1070 ArgumentNullException.ThrowIfNull(source);
1071
1072 string[] normalizedValues =
1073 source
1074 .Where(
1075 value =>
1076 !string.IsNullOrWhiteSpace(value))
1077 .Select(
1078 value =>
1079 value.Trim())
1080 .Distinct(
1081 StringComparer.OrdinalIgnoreCase)
1082 .OrderBy(
1083 value =>
1084 value,
1085 StringComparer.OrdinalIgnoreCase)
1086 .ToArray();
1087
1088 destination.Clear();
1089
1090 foreach (string value in normalizedValues)
1091 {
1092 destination.Add(value);
1093 }
1094 }
1095
1096 private static DateTime ConvertToUtc(
1097 DateTime value)
1098 {
1099 return value.Kind switch
1100 {
1101 DateTimeKind.Utc =>
1102 value,
1103
1104 DateTimeKind.Local =>
1105 value.ToUniversalTime(),
1106
1107 _ =>
1108 DateTime.SpecifyKind(
1109 value,
1110 DateTimeKind.Utc)
1111 };
1112 }
1113
1114 private void SetUtcDateTime(
1115 ref DateTime? storage,
1116 DateTime? value,
1117 string propertyName)
1118 {
1119 DateTime? normalizedValue =
1120 value.HasValue
1121 ? ConvertToUtc(value.Value)
1122 : null;
1123
1124 SetProperty(
1125 ref storage,
1126 normalizedValue,
1127 propertyName);
1128 }
1129
1130 private void NotifyIdentityProperties()
1131 {
1132 OnPropertyChanged(
1133 nameof(HasResultId));
1134
1135 OnPropertyChanged(
1137 }
1138
1139 private void NotifyResultDataProperties()
1140 {
1141 OnPropertyChanged(
1143
1144 OnPropertyChanged(
1145 nameof(HasDetailedSolution));
1146
1147 OnPropertyChanged(
1148 nameof(HasResultData));
1149
1150 OnPropertyChanged(
1152 }
1153
1154 private void NotifySourceProperties()
1155 {
1156 OnPropertyChanged(
1157 nameof(HasSourceInformation));
1158 }
1159
1160 private void NotifyVerificationProperties()
1161 {
1162 OnPropertyChanged(
1163 nameof(IsSuperseded));
1164
1165 OnPropertyChanged(
1166 nameof(IsDisputed));
1167
1168 OnPropertyChanged(
1169 nameof(IsInvalidated));
1170
1171 OnPropertyChanged(
1173 }
1174}
string SourceUri
Gets or sets an optional URI identifying the source from which the result was obtained.
KnownResult()
Initializes an empty known result.
List< string > Discoverers
Gets the names of persons or organizations credited with obtaining or discovering the result.
DateTime? RecordedAtUtc
Gets or sets the UTC date and time at which the result was recorded in the instance data.
bool CanBeSelectedAsBestKnownResult
Gets a value indicating whether this result may be considered when selecting the best known result.
bool HasObjectiveUnit
Gets a value indicating whether the objective unit has been recorded.
bool HasFeasibilityInformation
Gets a value indicating whether feasibility information has been recorded.
bool MatchesSupplyChainFingerprint(string currentFingerprint)
Determines whether this result was obtained for the supplied supply-chain fingerprint.
void ClearSupersedingResult()
Removes the link indicating that this result has been superseded.
OptimalityStatus OptimalityStatus
Gets or sets the reported or verified optimality status of the result.
LotSizingSolution? DetailedSolution
Gets or sets the optional detailed solution associated with this known result.
string Name
Gets or sets the human-readable name of the result.
void ClearDetailedSolution()
Removes the detailed solution while preserving all reported objective and source information.
bool HasReportedObjectiveValue
Gets a value indicating whether an explicit objective value has been reported.
bool HasObjectiveName
Gets a value indicating whether the objective name has been recorded.
string ResultId
Gets or sets the stable identifier of this result.
bool HasOptimalityInformation
Gets a value indicating whether optimality information has been recorded.
KnownResultVerificationStatus VerificationStatus
Gets or sets the level of verification applied to this result.
List< string > SourceAuthors
Gets the authors or organizations associated with the source reporting the result.
string MethodName
Gets or sets the name of the algorithm, solver or procedure that produced the result.
string SourceTitle
Gets or sets the title of the publication, report, repository or other source reporting the result.
void ReplaceDiscoverers(IEnumerable< string > discoverers)
Replaces the persons or organizations credited with discovering or obtaining the result.
bool IsInvalidated
Gets a value indicating whether the result has been invalidated.
bool HasDiscoverers
Gets a value indicating whether at least one discoverer has been recorded.
string SupersededByResultId
Gets or sets the identifier of a newer result that supersedes this result.
string ObjectiveName
Gets or sets the name of the reported objective.
bool HasSupplyChainFingerprint
Gets a value indicating whether a supply-chain fingerprint has been recorded.
void MarkAsSupersededBy(string newResultId)
Marks this result as superseded by another result.
FeasibilityStatus FeasibilityStatus
Gets or sets the reported or verified feasibility status of the result.
string DigitalObjectIdentifier
Gets or sets the digital object identifier of the publication reporting the result.
bool HasMethodInformation
Gets a value indicating whether information about the solution method has been recorded.
bool HasResultData
Gets a value indicating whether the result contains usable numerical or detailed solution data.
DateTime? ObtainedAtUtc
Gets or sets the UTC date and time at which the result was obtained.
string SupplyChainFingerprint
Gets or sets the fingerprint of the supply-chain data to which this result applies.
KnownResult(string resultId)
Initializes a known result with a stable identifier.
bool HasSourceInformation
Gets a value indicating whether at least one source field has been recorded.
double? ReportedObjectiveValue
Gets or sets the objective value explicitly reported for this result.
KnownResult(string resultId, double reportedObjectiveValue, string name="")
Initializes a known result containing a reported objective value.
void ClearReportedObjective()
Removes the explicitly reported objective value and its descriptive metadata.
string Comment
Gets or sets an optional explanatory comment about the result, its source or its verification.
bool HasComment
Gets a value indicating whether an explanatory comment has been recorded.
bool IsSuperseded
Gets a value indicating whether this result has been superseded by another result.
string SourceReference
Gets or sets a bibliographic or documentary reference for the source reporting the result.
string ObjectiveUnit
Gets or sets the unit associated with the reported objective value.
bool HasName
Gets a value indicating whether the result has a human-readable name.
bool HasDetailedSolution
Gets a value indicating whether a detailed solution is associated with the result.
void ReplaceSourceAuthors(IEnumerable< string > sourceAuthors)
Replaces the authors or organizations associated with the result source.
bool HasResultId
Gets a value indicating whether the result has a stable identifier.
bool IsDisputed
Gets a value indicating whether the result has been disputed.
KnownResultVerificationStatus
Indicates the level of verification associated with a known result for a lot-sizing problem instance.