LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalSolutionMappingContext.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using LotSizingDataModel.Instance;
7
9
10/// <summary>
11/// Provides the shared state and lookup services required while
12/// mapping a mathematical solver result back to the lot-sizing
13/// domain model.
14/// </summary>
16{
17 private readonly Dictionary<int, MathematicalVariable>
18 _variablesById;
19
20 private readonly Dictionary<int, MathematicalVariableValue>
21 _valuesByVariableId;
22
23 private readonly Dictionary<string, MathematicalVariableValue>
24 _valuesByDomainKey;
25
26 /// <summary>
27 /// Initializes a mathematical-solution mapping context.
28 /// </summary>
29 /// <param name="instance">
30 /// Source lot-sizing instance.
31 /// </param>
32 /// <param name="model">
33 /// Solver-independent mathematical model.
34 /// </param>
35 /// <param name="solveResult">
36 /// Generic mathematical-model solve result.
37 /// </param>
38 /// <exception cref="ArgumentNullException">
39 /// Thrown when one of the required arguments is
40 /// <see langword="null"/>.
41 /// </exception>
42 /// <exception cref="InvalidOperationException">
43 /// Thrown when the model and solver result are inconsistent.
44 /// </exception>
46 LotSizingInstance instance,
49 : this(
50 instance,
51 model,
52 solveResult,
54 {
55 }
56
57 /// <summary>
58 /// Initializes a mathematical-solution mapping context with
59 /// explicit mapping options.
60 /// </summary>
61 /// <param name="instance">
62 /// Source lot-sizing instance.
63 /// </param>
64 /// <param name="model">
65 /// Solver-independent mathematical model.
66 /// </param>
67 /// <param name="solveResult">
68 /// Generic mathematical-model solve result.
69 /// </param>
70 /// <param name="options">
71 /// Mapping options to apply.
72 /// </param>
73 /// <exception cref="ArgumentNullException">
74 /// Thrown when one of the required arguments is
75 /// <see langword="null"/>.
76 /// </exception>
77 /// <exception cref="InvalidOperationException">
78 /// Thrown when the model, solver result, or options are
79 /// inconsistent.
80 /// </exception>
82 LotSizingInstance instance,
86 {
87 ArgumentNullException.ThrowIfNull(
88 instance);
89
90 ArgumentNullException.ThrowIfNull(
91 model);
92
93 ArgumentNullException.ThrowIfNull(
94 solveResult);
95
96 ArgumentNullException.ThrowIfNull(
97 options);
98
99 model.EnsureValid();
100 solveResult.EnsureValid();
101
102 MathematicalSolutionMappingOptions normalizedOptions =
103 options.Clone();
104
105 normalizedOptions.EnsureValid();
106
107 Instance =
108 instance;
109
110 Model =
111 model;
112
114 solveResult;
115
116 Options =
117 normalizedOptions;
118
121 normalizedOptions.ZeroTolerance,
126
127 _variablesById =
128 model.Variables.ToDictionary(
129 variable =>
130 variable.Id);
131
132 _valuesByVariableId =
133 solveResult.VariableValues.ToDictionary(
134 variableValue =>
135 variableValue.VariableId);
136
137 _valuesByDomainKey =
138 new Dictionary<string, MathematicalVariableValue>(
139 StringComparer.OrdinalIgnoreCase);
140
141 BuildIndexes();
142 }
143
144 /// <summary>
145 /// Gets the source lot-sizing instance.
146 /// </summary>
147 public LotSizingInstance Instance
148 {
149 get;
150 }
151
152 /// <summary>
153 /// Gets the solver-independent mathematical model.
154 /// </summary>
156 {
157 get;
158 }
159
160 /// <summary>
161 /// Gets the generic mathematical-model solve result.
162 /// </summary>
164 {
165 get;
166 }
167
168 /// <summary>
169 /// Gets the normalized mapping options used by this context.
170 /// </summary>
172 {
173 get;
174 }
175
176 /// <summary>
177 /// Gets the numerical normalizer used for all mathematical
178 /// variable values exposed to decision mappers.
179 /// </summary>
184
185 /// <summary>
186 /// Gets a mathematical variable by identifier.
187 /// </summary>
188 /// <param name="variableId">
189 /// Mathematical-variable identifier.
190 /// </param>
191 /// <returns>
192 /// Mathematical variable.
193 /// </returns>
194 /// <exception cref="KeyNotFoundException">
195 /// Thrown when the identifier is unknown.
196 /// </exception>
198 int variableId)
199 {
200 if (_variablesById.TryGetValue(
201 variableId,
202 out MathematicalVariable? variable))
203 {
204 return variable;
205 }
206
207 throw new KeyNotFoundException(
208 $"No mathematical variable exists for identifier " +
209 $"'{variableId}'.");
210 }
211
212 /// <summary>
213 /// Gets a solver value by mathematical-variable identifier.
214 /// </summary>
215 /// <param name="variableId">
216 /// Mathematical-variable identifier.
217 /// </param>
218 /// <returns>
219 /// Solver value.
220 /// </returns>
221 /// <exception cref="KeyNotFoundException">
222 /// Thrown when no solver value exists for the identifier.
223 /// </exception>
225 int variableId)
226 {
227 if (_valuesByVariableId.TryGetValue(
228 variableId,
229 out MathematicalVariableValue? variableValue))
230 {
231 return variableValue;
232 }
233
234 throw new KeyNotFoundException(
235 $"No solver value exists for mathematical variable " +
236 $"identifier '{variableId}'.");
237 }
238
239 /// <summary>
240 /// Gets a solver value by business-domain key.
241 /// </summary>
242 /// <param name="domainKey">
243 /// Business-domain key.
244 /// </param>
245 /// <returns>
246 /// Solver value associated with the domain key.
247 /// </returns>
248 /// <exception cref="ArgumentException">
249 /// Thrown when <paramref name="domainKey"/> is empty.
250 /// </exception>
251 /// <exception cref="KeyNotFoundException">
252 /// Thrown when no value is associated with the domain key.
253 /// </exception>
255 string domainKey)
256 {
257 string normalizedDomainKey =
258 NormalizeDomainKey(
259 domainKey);
260
261 if (_valuesByDomainKey.TryGetValue(
262 normalizedDomainKey,
263 out MathematicalVariableValue? variableValue))
264 {
265 return variableValue;
266 }
267
268 throw new KeyNotFoundException(
269 $"No solver value exists for domain key " +
270 $"'{normalizedDomainKey}'.");
271 }
272
273 /// <summary>
274 /// Attempts to get a solver value by business-domain key.
275 /// </summary>
276 /// <param name="domainKey">
277 /// Business-domain key.
278 /// </param>
279 /// <param name="variableValue">
280 /// Solver value when found.
281 /// </param>
282 /// <returns>
283 /// <see langword="true"/> when a value is found; otherwise,
284 /// <see langword="false"/>.
285 /// </returns>
286 public bool TryGetValue(
287 string domainKey,
288 out MathematicalVariableValue? variableValue)
289 {
290 string normalizedDomainKey =
291 NormalizeDomainKey(
292 domainKey);
293
294 return _valuesByDomainKey.TryGetValue(
295 normalizedDomainKey,
296 out variableValue);
297 }
298
299 /// <summary>
300 /// Returns solver values whose domain-key category matches
301 /// the supplied category.
302 /// </summary>
303 /// <param name="category">
304 /// Domain-key category.
305 /// </param>
306 /// <param name="includeZeroValues">
307 /// Indicates whether values considered equal to zero must be
308 /// included.
309 /// </param>
310 /// <param name="zeroTolerance">
311 /// Absolute tolerance below which a value is considered zero.
312 /// </param>
313 /// <returns>
314 /// Matching mathematical-variable values.
315 /// </returns>
316 public IReadOnlyList<MathematicalVariableValue>
318 string category,
319 bool includeZeroValues,
320 double zeroTolerance = 1.0e-9)
321 {
322 if (string.IsNullOrWhiteSpace(
323 category))
324 {
325 throw new ArgumentException(
326 "A domain-key category is required.",
327 nameof(category));
328 }
329
330 if (double.IsNaN(
331 zeroTolerance) ||
332 double.IsInfinity(
333 zeroTolerance) ||
334 zeroTolerance < 0.0)
335 {
336 throw new ArgumentOutOfRangeException(
337 nameof(zeroTolerance),
338 zeroTolerance,
339 "Zero tolerance must be finite and non-negative.");
340 }
341
342 string normalizedCategory =
343 category.Trim();
344
345 return _valuesByDomainKey
346 .Where(
347 pair =>
348 {
350 pair.Key,
351 out MathematicalDomainKey? key) ||
352 !string.Equals(
353 key!.Category,
354 normalizedCategory,
355 StringComparison.OrdinalIgnoreCase))
356 {
357 return false;
358 }
359
360 return includeZeroValues ||
361 Math.Abs(
362 pair.Value.Value) >
363 zeroTolerance;
364 })
365 .Select(
366 pair =>
367 {
368 MathematicalVariable? variable =
369 Model.FindVariableById(
370 pair.Value.VariableId);
371
372 if (variable is null)
373 {
374 throw new InvalidOperationException(
375 $"No mathematical variable with identifier " +
376 $"'{pair.Value.VariableId}' exists in the model.");
377 }
378
379 return ValueNormalizer.Normalize(
380 variable,
381 pair.Value);
382 })
383 .ToArray();
384 }
385
386 /// <summary>
387 /// Returns all non-zero solver values whose domain-key
388 /// category matches the supplied category.
389 /// </summary>
390 /// <param name="category">
391 /// Domain-key category.
392 /// </param>
393 /// <param name="zeroTolerance">
394 /// Absolute tolerance below which a value is considered zero.
395 /// </param>
396 /// <returns>
397 /// Matching mathematical-variable values.
398 /// </returns>
399 public IReadOnlyList<MathematicalVariableValue>
401 string category,
402 double zeroTolerance = 1.0e-9)
403 {
404 return GetValuesByCategory(
405 category,
406 includeZeroValues: false,
407 zeroTolerance);
408 }
409
410 private void BuildIndexes()
411 {
412 foreach (
413 MathematicalVariableValue variableValue
415 {
416 if (!_variablesById.TryGetValue(
417 variableValue.VariableId,
418 out MathematicalVariable? variable))
419 {
420 throw new InvalidOperationException(
421 $"Solver result references unknown " +
422 $"mathematical variable identifier " +
423 $"'{variableValue.VariableId}'.");
424 }
425
426 string domainKey =
427 !string.IsNullOrWhiteSpace(
428 variableValue.DomainKey)
429 ? variableValue.DomainKey.Trim()
430 : variable.DomainKey?.Trim() ??
431 string.Empty;
432
433 if (string.IsNullOrWhiteSpace(
434 domainKey))
435 {
436 continue;
437 }
438
439 if (!string.IsNullOrWhiteSpace(
440 variable.DomainKey) &&
441 !string.Equals(
442 variable.DomainKey.Trim(),
443 domainKey,
444 StringComparison.OrdinalIgnoreCase))
445 {
446 throw new InvalidOperationException(
447 $"Solver-result domain key '{domainKey}' does " +
448 $"not match model domain key " +
449 $"'{variable.DomainKey}' for variable " +
450 $"identifier '{variable.Id}'.");
451 }
452
453 variableValue.DomainKey =
454 domainKey;
455
456 if (!_valuesByDomainKey.TryAdd(
457 domainKey,
458 variableValue))
459 {
460 throw new InvalidOperationException(
461 $"Domain key '{domainKey}' appears more than " +
462 "once in the mathematical solver result.");
463 }
464 }
465 }
466
467 private static string NormalizeDomainKey(
468 string domainKey)
469 {
470 if (string.IsNullOrWhiteSpace(
471 domainKey))
472 {
473 throw new ArgumentException(
474 "A mathematical domain key is required.",
475 nameof(domainKey));
476 }
477
478 return domainKey.Trim();
479 }
480}
Stores the solver result for a solver-independent mathematical model.
void EnsureValid()
Validates the mathematical-model solve result.
List< MathematicalVariableValue > VariableValues
Gets the mathematical-variable values returned by the solver.
Stores the value returned by a solver for one mathematical decision variable.
int VariableId
Gets or sets the identifier of the mathematical variable.
string DomainKey
Gets or sets the business-domain key associated with the mathematical variable.
Represents a parsed mathematical-model business-domain key.
static bool TryParse(string domainKey, out MathematicalDomainKey? result)
Attempts to parse a canonical mathematical domain key.
MathematicalSolutionMappingContext(LotSizingInstance instance, MathematicalModel model, MathematicalModelSolveResult solveResult)
Initializes a mathematical-solution mapping context.
MathematicalVariableValueNormalizer ValueNormalizer
Gets the numerical normalizer used for all mathematical variable values exposed to decision mappers.
MathematicalModel Model
Gets the solver-independent mathematical model.
MathematicalSolutionMappingOptions Options
Gets the normalized mapping options used by this context.
IReadOnlyList< MathematicalVariableValue > GetNonZeroValuesByCategory(string category, double zeroTolerance=1.0e-9)
Returns all non-zero solver values whose domain-key category matches the supplied category.
MathematicalVariableValue GetValue(int variableId)
Gets a solver value by mathematical-variable identifier.
bool TryGetValue(string domainKey, out MathematicalVariableValue? variableValue)
Attempts to get a solver value by business-domain key.
MathematicalVariable GetVariable(int variableId)
Gets a mathematical variable by identifier.
MathematicalSolutionMappingContext(LotSizingInstance instance, MathematicalModel model, MathematicalModelSolveResult solveResult, MathematicalSolutionMappingOptions options)
Initializes a mathematical-solution mapping context with explicit mapping options.
MathematicalModelSolveResult SolveResult
Gets the generic mathematical-model solve result.
IReadOnlyList< MathematicalVariableValue > GetValuesByCategory(string category, bool includeZeroValues, double zeroTolerance=1.0e-9)
Returns solver values whose domain-key category matches the supplied category.
MathematicalVariableValue GetValue(string domainKey)
Gets a solver value by business-domain key.
Defines the options used when mapping a mathematical solver result back to a normalized lot-sizing so...
MathematicalSolutionMappingOptions Clone()
Creates an independent copy of the mapping options.
double ZeroTolerance
Gets or sets the absolute tolerance below which a solver value is considered equal to zero.
Normalizes raw mathematical-variable values before they are exposed through domain decision objects o...
const double DefaultNearIntegerTolerance
Gets the default tolerance used to clean continuous values that are numerically indistinguishable fro...
const double DefaultIntegralityTolerance
Gets the default tolerance used for integer-domain values.
Represents a solver-independent mathematical optimization model.
List< MathematicalVariable > Variables
Gets the mathematical variables.
void EnsureValid()
Validates the complete mathematical model.
Represents one decision variable in a mathematical optimization model.