ULSAlgorithms 1.1.0-g3e5595996d
High-performance exact and heuristic algorithms for uncapacitated lot sizing
Loading...
Searching...
No Matches
LinearModelSolutionValidator.cs
Go to the documentation of this file.
2
4
5/// <summary>
6/// Independently checks solver-returned values against the portable model.
7/// </summary>
8public static class LinearModelSolutionValidator
9{
10 /// <summary>
11 /// Validates bounds, integrality, constraints and objective reconstruction.
12 /// </summary>
13 /// <remarks>
14 /// Constraint feasibility uses a mixed absolute/relative policy. The
15 /// configured feasibility tolerance is multiplied by the scale of each
16 /// row, where the scale is the maximum of 1, the absolute right-hand side,
17 /// and the absolute sum of evaluated row terms. This keeps small rows
18 /// protected by the absolute tolerance while avoiding false rejection of
19 /// numerically valid solver output on larger rows.
20 /// </remarks>
22 LinearModel model,
23 IReadOnlyDictionary<int, double> values,
24 double feasibilityTolerance,
25 double integralityTolerance)
26 {
27 ArgumentNullException.ThrowIfNull(model);
28 ArgumentNullException.ThrowIfNull(values);
29
30 if (!double.IsFinite(feasibilityTolerance) ||
31 feasibilityTolerance <= 0.0)
32 {
33 throw new ArgumentOutOfRangeException(
34 nameof(feasibilityTolerance));
35 }
36
37 if (!double.IsFinite(integralityTolerance) ||
38 integralityTolerance <= 0.0)
39 {
40 throw new ArgumentOutOfRangeException(
41 nameof(integralityTolerance));
42 }
43
44 var diagnostics =
45 new List<string>();
46
47 double maxBoundViolation = 0.0;
48 double maxIntegralityViolation = 0.0;
49 double maxConstraintViolation = 0.0;
50 double maxConstraintNormalizedViolation = 0.0;
51
52 foreach (LinearVariable variable in model.Variables)
53 {
54 if (!values.TryGetValue(
55 variable.Id,
56 out double value) ||
57 !double.IsFinite(value))
58 {
59 diagnostics.Add(
60 $"Variable '{variable.Name}' has no finite returned value.");
61
62 maxBoundViolation =
63 double.PositiveInfinity;
64
65 continue;
66 }
67
68 double lowerViolation =
69 Math.Max(
70 0.0,
71 variable.LowerBound - value);
72
73 double upperViolation =
74 double.IsPositiveInfinity(variable.UpperBound)
75 ? 0.0
76 : Math.Max(
77 0.0,
78 value - variable.UpperBound);
79
80 maxBoundViolation =
81 Math.Max(
82 maxBoundViolation,
83 Math.Max(
84 lowerViolation,
85 upperViolation));
86
87 if (variable.Type != LinearVariableType.Continuous)
88 {
89 double integerViolation =
90 Math.Abs(
91 value -
92 Math.Round(
93 value,
94 MidpointRounding.AwayFromZero));
95
96 maxIntegralityViolation =
97 Math.Max(
98 maxIntegralityViolation,
99 integerViolation);
100 }
101 }
102
103 foreach (LinearConstraint constraint in model.Constraints)
104 {
105 double activity =
106 EvaluateTerms(
107 constraint.Terms,
108 values);
109
110 double violation =
111 constraint.Sense switch
112 {
114 Math.Max(
115 0.0,
116 activity - constraint.RightHandSide),
117
119 Math.Abs(
120 activity - constraint.RightHandSide),
121
123 Math.Max(
124 0.0,
125 constraint.RightHandSide - activity),
126
127 _ =>
128 throw new NotSupportedException(
129 $"Unsupported constraint sense '{constraint.Sense}'.")
130 };
131
132 double rowScale =
133 EvaluateConstraintScale(
134 constraint,
135 values);
136
137 double normalizedViolation =
138 double.IsFinite(violation) &&
139 double.IsFinite(rowScale) &&
140 rowScale > 0.0
141 ? violation / rowScale
142 : double.PositiveInfinity;
143
144 maxConstraintViolation =
145 Math.Max(
146 maxConstraintViolation,
147 violation);
148
149 maxConstraintNormalizedViolation =
150 Math.Max(
151 maxConstraintNormalizedViolation,
152 normalizedViolation);
153 }
154
155 double objective =
156 model.Objective.Constant +
157 EvaluateTerms(
158 model.Objective.Terms,
159 values);
160
161 bool feasible =
162 double.IsFinite(objective) &&
163 maxBoundViolation <= feasibilityTolerance &&
164 maxConstraintNormalizedViolation <= feasibilityTolerance &&
165 maxIntegralityViolation <= integralityTolerance;
166
167 if (maxBoundViolation > feasibilityTolerance)
168 {
169 diagnostics.Add(
170 $"Maximum bound violation is {maxBoundViolation:R}.");
171 }
172
173 if (maxConstraintNormalizedViolation > feasibilityTolerance)
174 {
175 diagnostics.Add(
176 $"Maximum constraint violation is " +
177 $"{maxConstraintViolation:R}; normalized violation is " +
178 $"{maxConstraintNormalizedViolation:R}.");
179 }
180
181 if (maxIntegralityViolation > integralityTolerance)
182 {
183 diagnostics.Add(
184 $"Maximum integrality violation is " +
185 $"{maxIntegralityViolation:R}.");
186 }
187
188 if (!double.IsFinite(objective))
189 {
190 diagnostics.Add(
191 "The independently reconstructed objective is not finite.");
192 }
193
195 feasible,
196 objective,
197 maxBoundViolation,
198 maxIntegralityViolation,
199 maxConstraintViolation,
200 diagnostics);
201 }
202
203 private static double EvaluateConstraintScale(
204 LinearConstraint constraint,
205 IReadOnlyDictionary<int, double> values)
206 {
207 double absoluteTermSum = 0.0;
208
209 foreach (LinearTerm term in constraint.Terms)
210 {
211 if (!values.TryGetValue(
212 term.VariableId,
213 out double value) ||
214 !double.IsFinite(value))
215 {
216 return double.NaN;
217 }
218
219 absoluteTermSum +=
220 Math.Abs(
221 term.Coefficient *
222 value);
223 }
224
225 return Math.Max(
226 1.0,
227 Math.Max(
228 Math.Abs(
229 constraint.RightHandSide),
230 absoluteTermSum));
231 }
232
233 private static double EvaluateTerms(
234 IEnumerable<LinearTerm> terms,
235 IReadOnlyDictionary<int, double> values)
236 {
237 double result = 0.0;
238
239 foreach (LinearTerm term in terms)
240 {
241 if (!values.TryGetValue(
242 term.VariableId,
243 out double value) ||
244 !double.IsFinite(value))
245 {
246 return double.NaN;
247 }
248
249 result +=
250 term.Coefficient *
251 value;
252 }
253
254 return result;
255 }
256}
Independent solver-agnostic validation of a returned variable assignment.
Independently checks solver-returned values against the portable model.
static LinearModelSolutionValidation Validate(LinearModel model, IReadOnlyDictionary< int, double > values, double feasibilityTolerance, double integralityTolerance)
Validates bounds, integrality, constraints and objective reconstruction.
Describes one portable linear constraint.
IReadOnlyList< LinearTerm > Terms
Gets the nonzero terms.
Immutable solver-independent linear or mixed-integer linear model.
Definition LinearModel.cs:7
Describes one variable in a portable linear mathematical model.
LinearVariableType Type
Gets the variable domain.
int Id
Gets the stable zero-based variable identifier.
LinearVariableType
Identifies the domain of a variable in a portable linear mathematical model.
Stores one coefficient of a portable linear expression.
Definition LinearTerm.cs:7
int VariableId
Gets the referenced variable identifier.
Definition LinearTerm.cs:31
double Coefficient
Gets the coefficient.
Definition LinearTerm.cs:34