LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalSolutionMappingService.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Linq;
5using System.Threading;
6using System.Threading.Tasks;
7using LotSizingDataModel.Instance;
10
12
13/// <summary>
14/// Maps a generic mathematical solver result to a normalized
15/// lot-sizing solution and reports mapping diagnostics.
16/// </summary>
19{
21 _decisionMapperRegistry;
22
23 /// <summary>
24 /// Initializes a mathematical-solution mapping service.
25 /// </summary>
26 /// <param name="decisionMapperRegistry">
27 /// Registry containing the decision-family mappers.
28 /// </param>
29 /// <exception cref="ArgumentNullException">
30 /// Thrown when <paramref name="decisionMapperRegistry"/> is
31 /// <see langword="null"/>.
32 /// </exception>
34 MathematicalDecisionMapperRegistry decisionMapperRegistry)
35 {
36 ArgumentNullException.ThrowIfNull(
37 decisionMapperRegistry);
38
39 _decisionMapperRegistry =
40 decisionMapperRegistry;
41 }
42
43 /// <summary>
44 /// Maps a mathematical-model solve result to a normalized
45 /// lot-sizing solution.
46 /// </summary>
47 /// <param name="instance">
48 /// Source lot-sizing instance.
49 /// </param>
50 /// <param name="model">
51 /// Solver-independent mathematical model that was solved.
52 /// </param>
53 /// <param name="solveResult">
54 /// Generic mathematical-model solve result.
55 /// </param>
56 /// <param name="options">
57 /// Mathematical-solution mapping options.
58 /// </param>
59 /// <param name="cancellationToken">
60 /// Token used to cancel the mapping operation.
61 /// </param>
62 /// <returns>
63 /// Task returning the complete mapping result.
64 /// </returns>
65 /// <exception cref="ArgumentNullException">
66 /// Thrown when one of the required arguments is
67 /// <see langword="null"/>.
68 /// </exception>
69 public ValueTask<MathematicalSolutionMappingResult> MapAsync(
70 LotSizingInstance instance,
74 CancellationToken cancellationToken = default)
75 {
76 ArgumentNullException.ThrowIfNull(
77 instance);
78
79 ArgumentNullException.ThrowIfNull(
80 model);
81
82 ArgumentNullException.ThrowIfNull(
83 solveResult);
84
85 ArgumentNullException.ThrowIfNull(
86 options);
87
88 MathematicalSolutionMappingOptions normalizedOptions =
89 options.Clone();
90
91 normalizedOptions.EnsureValid();
92
93 cancellationToken.ThrowIfCancellationRequested();
94
95 var stopwatch =
96 Stopwatch.StartNew();
97
98 try
99 {
100 model.EnsureValid();
101 solveResult.EnsureValid();
102
103 if (!solveResult.HasFeasibleSolution)
104 {
105 stopwatch.Stop();
106
107 return new ValueTask<MathematicalSolutionMappingResult>(
109 "A solver result without a feasible " +
110 "solution cannot be mapped.",
111 stopwatch.Elapsed));
112 }
113
114 ValidateCompleteness(
115 model,
116 solveResult,
117 normalizedOptions);
118
119 cancellationToken.ThrowIfCancellationRequested();
120
121 IReadOnlyCollection<string> categories =
122 GetCategories(
123 solveResult);
124
125 ValidateCategories(
126 categories,
127 normalizedOptions);
128
129 var mapper =
131 _decisionMapperRegistry);
132
133 LotSizingDataModel.Solution.LotSizingSolution solution =
134 mapper.Map(
135 instance,
136 model,
137 solveResult,
138 normalizedOptions);
139
140 cancellationToken.ThrowIfCancellationRequested();
141
142 stopwatch.Stop();
143
146 solution,
147 stopwatch.Elapsed);
148
149 PopulateStatistics(
150 result,
151 solveResult,
152 categories,
153 normalizedOptions);
154
155 result.AddDiagnostic(
156 $"Mathematical solution mapping processed " +
157 $"{result.ProcessedValueCount} values across " +
158 $"{result.ProcessedCategoryCount} decision " +
159 "categories.");
160
161 result.EnsureValid();
162
163 return new ValueTask<MathematicalSolutionMappingResult>(
164 result);
165 }
166 catch (OperationCanceledException)
167 {
168 throw;
169 }
170 catch (Exception exception)
171 {
172 stopwatch.Stop();
173
176 exception.Message,
177 stopwatch.Elapsed);
178
179 result.AddDiagnostic(
180 exception.ToString());
181
182 result.EnsureValid();
183
184 return new ValueTask<MathematicalSolutionMappingResult>(
185 result);
186 }
187 }
188
189 private static IReadOnlyCollection<string> GetCategories(
191 {
192 var categories =
193 new HashSet<string>(
194 StringComparer.OrdinalIgnoreCase);
195
196 foreach (
197 MathematicalVariableValue variableValue
198 in solveResult.VariableValues)
199 {
200 if (string.IsNullOrWhiteSpace(
201 variableValue.DomainKey))
202 {
203 continue;
204 }
205
206 if (MathematicalDomainKey.TryParse(
207 variableValue.DomainKey,
208 out MathematicalDomainKey? domainKey))
209 {
210 categories.Add(
211 domainKey!.Category);
212 }
213 }
214
215 return categories;
216 }
217
218 private void ValidateCategories(
219 IEnumerable<string> categories,
220 MathematicalSolutionMappingOptions options)
221 {
222 if (!options.RequireKnownCategories)
223 {
224 return;
225 }
226
227 foreach (
228 string category
229 in categories)
230 {
231 if (!_decisionMapperRegistry.Contains(
232 category))
233 {
234 throw new InvalidOperationException(
235 $"No mathematical decision mapper is " +
236 $"registered for category '{category}'.");
237 }
238 }
239 }
240
241 private static void ValidateCompleteness(
242 MathematicalModel model,
243 MathematicalModelSolveResult solveResult,
244 MathematicalSolutionMappingOptions options)
245 {
246 if (!options.RequireCompleteVariableValues)
247 {
248 return;
249 }
250
251 var returnedVariableIds =
252 solveResult.VariableValues
253 .Select(
254 variableValue =>
255 variableValue.VariableId)
256 .ToHashSet();
257
258 foreach (
259 MathematicalVariable variable
260 in model.Variables)
261 {
262 if (!returnedVariableIds.Contains(
263 variable.Id))
264 {
265 throw new InvalidOperationException(
266 $"No solver value was returned for " +
267 $"mathematical variable '{variable.Name}' " +
268 $"with identifier '{variable.Id}'.");
269 }
270 }
271 }
272
273 private static void PopulateStatistics(
274 MathematicalSolutionMappingResult result,
275 MathematicalModelSolveResult solveResult,
276 IReadOnlyCollection<string> categories,
277 MathematicalSolutionMappingOptions options)
278 {
279 int processedValueCount =
280 0;
281
282 int ignoredValueCount =
283 0;
284
285 foreach (
286 MathematicalVariableValue variableValue
287 in solveResult.VariableValues)
288 {
289 bool isZero =
290 Math.Abs(
291 variableValue.Value) <=
292 options.ZeroTolerance;
293
294 if (isZero &&
295 !options.IncludeZeroValues)
296 {
297 ignoredValueCount++;
298
299 continue;
300 }
301
302 processedValueCount++;
303 }
304
305 result.ProcessedValueCount =
306 processedValueCount;
307
308 result.IgnoredValueCount =
309 ignoredValueCount;
310
311 result.ProcessedCategoryCount =
312 categories.Count;
313 }
314}
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.
bool HasFeasibleSolution
Gets or sets a value indicating whether the solver produced at least one feasible solution.
Stores the value returned by a solver for one mathematical decision variable.
int VariableId
Gets or sets the identifier of the mathematical variable.
double Value
Gets or sets the value returned by the solver.
string DomainKey
Gets or sets the business-domain key associated with the mathematical variable.
Stores and resolves mathematical decision mappers by business-domain category.
Maps a complete mathematical solver result to a normalized lot-sizing solution by delegating each dec...
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.
Describes the result of mapping a mathematical solver result back to a normalized lot-sizing solution...
void EnsureValid()
Validates the mathematical-solution mapping result.
static MathematicalSolutionMappingResult Failure(string failureMessage, TimeSpan mappingDuration)
Creates a failed mathematical-solution mapping result.
void AddDiagnostic(string? diagnostic)
Adds a non-empty diagnostic message.
static MathematicalSolutionMappingResult Success(LotSizingSolution solution, TimeSpan mappingDuration)
Creates a successful mathematical-solution mapping result.
MathematicalSolutionMappingService(MathematicalDecisionMapperRegistry decisionMapperRegistry)
Initializes a mathematical-solution mapping service.
ValueTask< MathematicalSolutionMappingResult > MapAsync(LotSizingInstance instance, MathematicalModel model, MathematicalModelSolveResult solveResult, MathematicalSolutionMappingOptions options, CancellationToken cancellationToken=default)
Maps a mathematical-model solve result to a normalized lot-sizing solution.
Represents a solver-independent mathematical optimization model.
List< MathematicalVariable > Variables
Gets the mathematical variables.
void EnsureValid()
Validates the complete mathematical model.
int Id
Gets or sets the unique variable identifier within the mathematical model.
Defines a service that maps a generic mathematical solver result to a normalized lot-sizing solution.