LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalModelSolveResult.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Xml.Serialization;
6
8
9/// <summary>
10/// Stores the solver result for a solver-independent
11/// mathematical model.
12/// </summary>
13[Serializable]
14[XmlType(TypeName = "mathematicalModelSolveResult")]
16{
17 private readonly List<MathematicalVariableValue> _variableValues =
18 new();
19
20 private readonly List<string> _diagnostics =
21 new();
22
23 /// <summary>
24 /// Initializes an empty mathematical-model solve result.
25 /// </summary>
27 {
28 RunName =
29 string.Empty;
30
32 string.Empty;
33
35 string.Empty;
36
38 string.Empty;
39 }
40
41 /// <summary>
42 /// Gets or sets the solve-run name.
43 /// </summary>
44 [XmlAttribute("runName")]
45 public string RunName
46 {
47 get;
48 set;
49 }
50
51 /// <summary>
52 /// Gets or sets the identifier of the formulation that
53 /// generated the mathematical model.
54 /// </summary>
55 [XmlAttribute("formulationId")]
56 public string FormulationId
57 {
58 get;
59 set;
60 }
61
62 /// <summary>
63 /// Gets or sets the solver kind.
64 /// </summary>
65 [XmlAttribute("solverKind")]
67 {
68 get;
69 set;
70 }
71
72 /// <summary>
73 /// Gets or sets the solver name.
74 /// </summary>
75 [XmlAttribute("solverName")]
76 public string SolverName
77 {
78 get;
79 set;
80 }
81
82 /// <summary>
83 /// Gets or sets the solver version.
84 /// </summary>
85 [XmlAttribute("solverVersion")]
86 public string SolverVersion
87 {
88 get;
89 set;
90 }
91
92 /// <summary>
93 /// Gets or sets the termination reason.
94 /// </summary>
95 [XmlAttribute("terminationReason")]
97 {
98 get;
99 set;
100 }
101
102 /// <summary>
103 /// Gets or sets a value indicating whether the solver
104 /// produced at least one feasible solution.
105 /// </summary>
106 [XmlAttribute("hasFeasibleSolution")]
108 {
109 get;
110 set;
111 }
112
113 /// <summary>
114 /// Gets or sets a value indicating whether optimality was
115 /// proven.
116 /// </summary>
117 [XmlAttribute("isOptimal")]
118 public bool IsOptimal
119 {
120 get;
121 set;
122 }
123
124 /// <summary>
125 /// Gets or sets the objective value.
126 /// </summary>
127 [XmlAttribute("objectiveValue")]
128 public double? ObjectiveValue
129 {
130 get;
131 set;
132 }
133
134 /// <summary>
135 /// Gets or sets the best objective bound.
136 /// </summary>
137 [XmlAttribute("bestBound")]
138 public double? BestBound
139 {
140 get;
141 set;
142 }
143
144 /// <summary>
145 /// Gets or sets the relative optimality gap.
146 /// </summary>
147 [XmlAttribute("relativeGap")]
148 public double? RelativeGap
149 {
150 get;
151 set;
152 }
153
154 /// <summary>
155 /// Gets or sets the absolute optimality gap.
156 /// </summary>
157 [XmlAttribute("absoluteGap")]
158 public double? AbsoluteGap
159 {
160 get;
161 set;
162 }
163
164 /// <summary>
165 /// Gets or sets the solver wall-clock duration.
166 /// </summary>
167 [XmlIgnore]
168 public TimeSpan SolveDuration
169 {
170 get;
171 set;
172 }
173
174 /// <summary>
175 /// Gets or sets the solver wall-clock duration in
176 /// milliseconds for XML serialization.
177 /// </summary>
178 [XmlAttribute("solveDurationMilliseconds")]
180 {
181 get =>
182 SolveDuration.TotalMilliseconds;
183
184 set =>
186 TimeSpan.FromMilliseconds(
187 value);
188 }
189
190 /// <summary>
191 /// Gets or sets the number of explored branch-and-bound
192 /// nodes, when available.
193 /// </summary>
194 [XmlAttribute("exploredNodeCount")]
195 public long? ExploredNodeCount
196 {
197 get;
198 set;
199 }
200
201 /// <summary>
202 /// Gets or sets the number of simplex or barrier
203 /// iterations, when available.
204 /// </summary>
205 [XmlAttribute("iterationCount")]
206 public long? IterationCount
207 {
208 get;
209 set;
210 }
211
212 /// <summary>
213 /// Gets the mathematical-variable values returned by the
214 /// solver.
215 /// </summary>
216 [XmlArray("variableValues")]
217 [XmlArrayItem("variableValue")]
218 public List<MathematicalVariableValue> VariableValues =>
219 _variableValues;
220
221 /// <summary>
222 /// Gets diagnostic messages produced while solving.
223 /// </summary>
224 [XmlArray("diagnostics")]
225 [XmlArrayItem("diagnostic")]
226 public List<string> Diagnostics =>
227 _diagnostics;
228
229 /// <summary>
230 /// Adds a variable value.
231 /// </summary>
232 /// <param name="variableValue">
233 /// Variable value to add.
234 /// </param>
235 /// <exception cref="ArgumentNullException">
236 /// Thrown when <paramref name="variableValue"/> is
237 /// <see langword="null"/>.
238 /// </exception>
239 /// <exception cref="InvalidOperationException">
240 /// Thrown when the same mathematical variable identifier is
241 /// already present.
242 /// </exception>
243 public void AddVariableValue(
244 MathematicalVariableValue variableValue)
245 {
246 ArgumentNullException.ThrowIfNull(
247 variableValue);
248
249 variableValue.EnsureValid();
250
251 if (_variableValues.Any(
252 existingValue =>
253 existingValue.VariableId ==
254 variableValue.VariableId))
255 {
256 throw new InvalidOperationException(
257 $"A value for mathematical variable identifier " +
258 $"'{variableValue.VariableId}' is already stored.");
259 }
260
261 _variableValues.Add(
262 variableValue);
263 }
264
265 /// <summary>
266 /// Adds a non-empty diagnostic message.
267 /// </summary>
268 /// <param name="diagnostic">
269 /// Diagnostic message.
270 /// </param>
271 public void AddDiagnostic(
272 string diagnostic)
273 {
274 if (!string.IsNullOrWhiteSpace(
275 diagnostic))
276 {
277 _diagnostics.Add(
278 diagnostic.Trim());
279 }
280 }
281
282 /// <summary>
283 /// Finds a variable value by mathematical-variable
284 /// identifier.
285 /// </summary>
286 /// <param name="variableId">
287 /// Mathematical-variable identifier.
288 /// </param>
289 /// <returns>
290 /// Matching value, or <see langword="null"/> when no value is
291 /// stored for the supplied identifier.
292 /// </returns>
294 int variableId)
295 {
296 return _variableValues.FirstOrDefault(
297 variableValue =>
298 variableValue.VariableId ==
299 variableId);
300 }
301
302 /// <summary>
303 /// Validates the mathematical-model solve result.
304 /// </summary>
305 /// <exception cref="InvalidOperationException">
306 /// Thrown when the result contains inconsistent numerical
307 /// values or duplicate variable identifiers.
308 /// </exception>
309 public void EnsureValid()
310 {
311 EnsureFiniteNullable(
313 nameof(ObjectiveValue));
314
315 EnsureFiniteNullable(
316 BestBound,
317 nameof(BestBound));
318
319 EnsureFiniteNullable(
321 nameof(RelativeGap));
322
323 EnsureFiniteNullable(
325 nameof(AbsoluteGap));
326
327 if (SolveDuration < TimeSpan.Zero)
328 {
329 throw new InvalidOperationException(
330 "Solver duration cannot be negative.");
331 }
332
334 !ObjectiveValue.HasValue)
335 {
336 throw new InvalidOperationException(
337 "A feasible solver result must provide an " +
338 "objective value.");
339 }
340
341 if (IsOptimal &&
343 {
344 throw new InvalidOperationException(
345 "An optimal solver result must contain a feasible " +
346 "solution.");
347 }
348
349 var variableIds =
350 new HashSet<int>();
351
352 foreach (
353 MathematicalVariableValue variableValue
354 in _variableValues)
355 {
356 if (variableValue is null)
357 {
358 throw new InvalidOperationException(
359 "The variable-value collection cannot contain " +
360 "a null entry.");
361 }
362
363 variableValue.EnsureValid();
364
365 if (!variableIds.Add(
366 variableValue.VariableId))
367 {
368 throw new InvalidOperationException(
369 $"Mathematical variable identifier " +
370 $"'{variableValue.VariableId}' appears more " +
371 "than once in the solve result.");
372 }
373 }
374 }
375
376 private static void EnsureFiniteNullable(
377 double? value,
378 string propertyName)
379 {
380 if (value.HasValue &&
381 (double.IsNaN(
382 value.Value) ||
383 double.IsInfinity(
384 value.Value)))
385 {
386 throw new InvalidOperationException(
387 $"{propertyName} must be finite when specified.");
388 }
389 }
390}
void EnsureValid()
Validates the mathematical-model solve result.
MathematicalVariableValue? FindVariableValue(int variableId)
Finds a variable value by mathematical-variable identifier.
SolverTerminationReason TerminationReason
Gets or sets the termination reason.
long? IterationCount
Gets or sets the number of simplex or barrier iterations, when available.
List< MathematicalVariableValue > VariableValues
Gets the mathematical-variable values returned by the solver.
double SolveDurationMilliseconds
Gets or sets the solver wall-clock duration in milliseconds for XML serialization.
bool IsOptimal
Gets or sets a value indicating whether optimality was proven.
MathematicalModelSolveResult()
Initializes an empty mathematical-model solve result.
long? ExploredNodeCount
Gets or sets the number of explored branch-and-bound nodes, when available.
string FormulationId
Gets or sets the identifier of the formulation that generated the mathematical model.
void AddVariableValue(MathematicalVariableValue variableValue)
Adds a variable value.
bool HasFeasibleSolution
Gets or sets a value indicating whether the solver produced at least one feasible solution.
List< string > Diagnostics
Gets diagnostic messages produced while solving.
void AddDiagnostic(string diagnostic)
Adds a non-empty diagnostic message.
TimeSpan SolveDuration
Gets or sets the solver wall-clock duration.
Stores the value returned by a solver for one mathematical decision variable.
void EnsureValid()
Validates the mathematical-variable value.
int VariableId
Gets or sets the identifier of the mathematical variable.
SolverTerminationReason
Describes why a solver execution terminated.