LotSizingDataModel.Checker 2.0.1
Reusable validation engine for structural, mathematical and objective consistency checks.
Loading...
Searching...
No Matches
ClosedLoopSolutionFeasibilityChecker.cs
Go to the documentation of this file.
1using LotSizingDataModel.Instance;
2using LotSizingDataModel.Instance.ClosedLoop;
3using LotSizingDataModel.Solution;
4using LotSizingDataModel.Solution.Common;
5using LotSizingDataModel.Solution.Decisions;
6
8
9/// <summary>
10/// Independent semantic checker for normalized closed-loop
11/// solution decisions.
12/// </summary>
14{
16 LotSizingInstance instance,
17 LotSizingSolution solution,
18 double tolerance = 1.0e-6)
19 {
20 ArgumentNullException.ThrowIfNull(instance);
21 ArgumentNullException.ThrowIfNull(solution);
22
23 if (!double.IsFinite(tolerance) ||
24 tolerance < 0.0)
25 {
26 throw new ArgumentOutOfRangeException(
27 nameof(tolerance));
28 }
29
30 var diagnostics =
31 new List<MathematicalFeasibilityDiagnostic>();
32
33 bool partial = false;
34
35 foreach (ClosedLoopReturnStream stream
36 in instance.ClosedLoopReturnStreams)
37 {
38 ClosedLoopDecision[] decisions =
39 solution.ClosedLoopDecisions
40 .Where(
41 candidate =>
42 candidate.ReturnStreamId ==
43 stream.Id)
44 .ToArray();
45
46 if (decisions.Length == 0)
47 {
48 partial = true;
49
50 diagnostics.Add(
52 "LSDM-FEAS-CL-001",
53 $"Closed-loop decision for return stream '{stream.Id}' is missing."));
54
55 continue;
56 }
57
58 if (decisions.Length > 1)
59 {
60 diagnostics.Add(
62 "LSDM-FEAS-CL-002",
63 $"Closed-loop return stream '{stream.Id}' has duplicate solution decisions."));
64
65 continue;
66 }
67
68 ClosedLoopDecision decision =
69 decisions[0];
70
71 if (decision.PlanningHorizon !=
72 instance.PlanningHorizon)
73 {
74 diagnostics.Add(
76 "LSDM-FEAS-CL-003",
77 $"Closed-loop decision for stream '{stream.Id}' has an inconsistent planning horizon."));
78
79 continue;
80 }
81
82 for (int period = 1;
83 period <= instance.PlanningHorizon;
84 period++)
85 {
86 double recovery =
87 decision.RecoveryInputs[period];
88
89 double disposal =
90 decision.DisposalQuantities[period];
91
92 double recovered =
93 decision.RecoveredOutputs[period];
94
95 if (Math.Abs(
96 recovery +
97 disposal -
98 stream.ReturnQuantity[period]) >
99 tolerance)
100 {
101 diagnostics.Add(
103 "LSDM-FEAS-CL-004",
104 $"Return conservation is violated for stream '{stream.Id}', period {period}."));
105 }
106
107 if (Math.Abs(
108 recovered -
109 stream.RecoveryYield *
110 recovery) >
111 tolerance)
112 {
113 diagnostics.Add(
115 "LSDM-FEAS-CL-005",
116 $"Recovery-yield identity is violated for stream '{stream.Id}', period {period}."));
117 }
118
119 if (stream.RecoveryCapacity is not null &&
120 recovery >
121 stream.RecoveryCapacity[period] +
122 tolerance)
123 {
124 diagnostics.Add(
126 "LSDM-FEAS-CL-006",
127 $"Recovery capacity is violated for stream '{stream.Id}', period {period}."));
128 }
129 }
130 }
131
132 var knownStreamIds =
133 instance.ClosedLoopReturnStreams
134 .Select(
135 stream =>
136 stream.Id)
137 .ToHashSet();
138
139 foreach (ClosedLoopDecision decision
140 in solution.ClosedLoopDecisions)
141 {
142 if (!knownStreamIds.Contains(
143 decision.ReturnStreamId))
144 {
145 diagnostics.Add(
147 "LSDM-FEAS-CL-007",
148 $"Solution contains unknown closed-loop return stream '{decision.ReturnStreamId}'."));
149 }
150 }
151
152 bool infeasible =
153 diagnostics.Any(
154 diagnostic =>
155 diagnostic.Code !=
156 "LSDM-FEAS-CL-001");
157
158 FeasibilityStatus status =
159 infeasible
160 ? FeasibilityStatus.Infeasible
161 : partial
162 ? FeasibilityStatus.PartiallyEvaluated
163 : FeasibilityStatus.Feasible;
164
166 status,
167 diagnostics);
168 }
169}
Independent semantic checker for normalized closed-loop solution decisions.
MathematicalFeasibilityCheckResult Check(LotSizingInstance instance, LotSizingSolution solution, double tolerance=1.0e-6)