LotSizingDataModel.Solver.Console 2.0.1
Console application for executing solvers and validating produced solutions.
Loading...
Searching...
No Matches
MathematicalModelTextExporter.cs
Go to the documentation of this file.
1using System.Globalization;
2using System.Text;
3using LotSizingDataModel.Solver.Modeling;
4
6
7/// <summary>
8/// Writes a complete human-readable representation of a
9/// solver-independent mathematical model.
10/// </summary>
12{
13 /// <summary>
14 /// Writes the supplied mathematical model to a text file.
15 /// </summary>
16 /// <param name="model">Mathematical model to export.</param>
17 /// <param name="filePath">Destination text-file path.</param>
18 public static void Write(
19 MathematicalModel model,
20 string filePath)
21 {
22 ArgumentNullException.ThrowIfNull(model);
23 ArgumentException.ThrowIfNullOrWhiteSpace(filePath);
24
25 model.EnsureValid();
26
27 string? directory =
28 Path.GetDirectoryName(filePath);
29
30 if (!string.IsNullOrWhiteSpace(directory))
31 {
32 Directory.CreateDirectory(directory);
33 }
34
35 using var writer =
36 new StreamWriter(
37 filePath,
38 append: false,
39 new UTF8Encoding(
40 encoderShouldEmitUTF8Identifier: true));
41
42 WriteHeader(
43 writer,
44 model);
45
46 WriteVariables(
47 writer,
48 model);
49
50 WriteObjective(
51 writer,
52 model);
53
54 WriteObjectiveByCategory(
55 writer,
56 model);
57
58 WriteConstraints(
59 writer,
60 model);
61 }
62
63 private static void WriteHeader(
64 TextWriter writer,
65 MathematicalModel model)
66 {
67 writer.WriteLine(
68 "LOT SIZING MATHEMATICAL MODEL DUMP");
69 writer.WriteLine(
70 "==================================");
71 writer.WriteLine();
72
73 writer.WriteLine(
74 $"Model : {model.Name}");
75 writer.WriteLine(
76 $"Variables : {model.VariableCount}");
77 writer.WriteLine(
78 $" continuous : {model.ContinuousVariableCount}");
79 writer.WriteLine(
80 $" integer : {model.IntegerVariableCount}");
81 writer.WriteLine(
82 $" binary : {model.BinaryVariableCount}");
83 writer.WriteLine(
84 $"Enabled constraints : {model.EnabledConstraintCount}");
85 writer.WriteLine(
86 $"Objective sense : {model.Objective.Sense}");
87 writer.WriteLine(
88 $"Objective terms : {model.Objective.Expression.TermCount}");
89 writer.WriteLine(
90 $"Objective constant : {Format(model.Objective.Expression.Constant)}");
91 writer.WriteLine();
92 }
93
94 private static void WriteVariables(
95 TextWriter writer,
96 MathematicalModel model)
97 {
98 writer.WriteLine(
99 "VARIABLES");
100 writer.WriteLine(
101 "=========");
102 writer.WriteLine();
103
104 foreach (
105 MathematicalVariable variable
106 in model.Variables.OrderBy(
107 variable =>
108 variable.Id))
109 {
110 writer.WriteLine(
111 $"[{variable.Id}] {variable.Name}");
112
113 writer.WriteLine(
114 $" Type : {variable.VariableType}");
115
116 writer.WriteLine(
117 $" Bounds : [{FormatBound(variable.LowerBound)}, " +
118 $"{FormatBound(variable.UpperBound)}]");
119
120 writer.WriteLine(
121 $" DomainKey : {variable.DomainKey}");
122
123 double objectiveCoefficient =
124 GetObjectiveCoefficient(
125 model,
126 variable.Id);
127
128 writer.WriteLine(
129 $" ObjCoeff : {Format(objectiveCoefficient)}");
130
131 writer.WriteLine();
132 }
133 }
134
135 private static void WriteObjective(
136 TextWriter writer,
137 MathematicalModel model)
138 {
139 writer.WriteLine(
140 "OBJECTIVE");
141 writer.WriteLine(
142 "=========");
143 writer.WriteLine();
144
145 writer.WriteLine(
146 $"{model.Objective.Sense} {model.Objective.Name}");
147
148 writer.WriteLine();
149
150 foreach (
151 LinearTerm term
152 in model.Objective.Expression.Terms)
153 {
154 MathematicalVariable? variable =
155 model.FindVariableById(
156 term.VariableId);
157
158 writer.WriteLine(
159 $" {FormatSigned(term.Coefficient)} " +
160 $"{FormatVariable(variable, term.VariableId)}");
161 }
162
163 if (model.Objective.Expression.Constant != 0.0)
164 {
165 writer.WriteLine(
166 $" {FormatSigned(model.Objective.Expression.Constant)}");
167 }
168
169 writer.WriteLine();
170 }
171
172 private static void WriteObjectiveByCategory(
173 TextWriter writer,
174 MathematicalModel model)
175 {
176 writer.WriteLine(
177 "OBJECTIVE COEFFICIENTS BY DOMAIN CATEGORY");
178 writer.WriteLine(
179 "=========================================");
180 writer.WriteLine();
181
182 var rows =
183 model.Objective.Expression.Terms
184 .Select(
185 term =>
186 {
187 MathematicalVariable? variable =
188 model.FindVariableById(
189 term.VariableId);
190
191 string category =
192 GetCategory(
193 variable?.DomainKey);
194
195 return new
196 {
197 Category = category,
198 Term = term,
199 Variable = variable
200 };
201 })
202 .GroupBy(
203 row =>
204 row.Category,
205 StringComparer.OrdinalIgnoreCase)
206 .OrderBy(
207 group =>
208 group.Key,
209 StringComparer.OrdinalIgnoreCase);
210
211 foreach (var group in rows)
212 {
213 writer.WriteLine(
214 $"[{group.Key}]");
215
216 writer.WriteLine(
217 $" term count : {group.Count()}");
218
219 writer.WriteLine(
220 $" coefficient sum : " +
221 $"{Format(group.Sum(row => row.Term.Coefficient))}");
222
223 foreach (var row in group)
224 {
225 writer.WriteLine(
226 $" {FormatSigned(row.Term.Coefficient)} " +
227 $"{FormatVariable(row.Variable, row.Term.VariableId)}");
228 }
229
230 writer.WriteLine();
231 }
232 }
233
234 private static void WriteConstraints(
235 TextWriter writer,
236 MathematicalModel model)
237 {
238 writer.WriteLine(
239 "CONSTRAINTS");
240 writer.WriteLine(
241 "===========");
242 writer.WriteLine();
243
244 foreach (
245 LinearConstraint constraint
246 in model.Constraints
247 .Where(
248 constraint =>
249 constraint.IsEnabled)
250 .OrderBy(
251 constraint =>
252 constraint.Id))
253 {
254 writer.WriteLine(
255 $"[{constraint.Id}] {constraint.Name}");
256
257 writer.WriteLine(
258 $" DomainKey : {constraint.DomainKey}");
259
260 writer.Write(
261 " Equation : ");
262
263 bool first =
264 true;
265
266 foreach (
267 LinearTerm term
268 in constraint.LeftHandSide.Terms)
269 {
270 MathematicalVariable? variable =
271 model.FindVariableById(
272 term.VariableId);
273
274 if (!first)
275 {
276 writer.Write(
277 term.Coefficient >= 0.0
278 ? " + "
279 : " - ");
280 }
281 else if (term.Coefficient < 0.0)
282 {
283 writer.Write("-");
284 }
285
286 writer.Write(
287 $"{Format(Math.Abs(term.Coefficient))}*" +
288 $"{FormatVariable(variable, term.VariableId)}");
289
290 first =
291 false;
292 }
293
294 if (constraint.LeftHandSide.Constant != 0.0 ||
295 first)
296 {
297 double constant =
298 constraint.LeftHandSide.Constant;
299
300 if (!first)
301 {
302 writer.Write(
303 constant >= 0.0
304 ? " + "
305 : " - ");
306 }
307 else if (constant < 0.0)
308 {
309 writer.Write("-");
310 }
311
312 writer.Write(
313 Format(
314 Math.Abs(constant)));
315 }
316
317 writer.Write(
318 $" {FormatSense(constraint.Sense)} " +
319 $"{Format(constraint.RightHandSide)}");
320
321 writer.WriteLine();
322 writer.WriteLine();
323 }
324 }
325
326 private static double GetObjectiveCoefficient(
327 MathematicalModel model,
328 int variableId)
329 {
330 LinearTerm? term =
331 model.Objective.Expression.Terms.FirstOrDefault(
332 term =>
333 term.VariableId == variableId);
334
335 return term?.Coefficient ?? 0.0;
336 }
337
338 private static string GetCategory(
339 string? domainKey)
340 {
341 if (string.IsNullOrWhiteSpace(domainKey))
342 {
343 return "(none)";
344 }
345
346 int separatorIndex =
347 domainKey.IndexOf('|');
348
349 if (separatorIndex < 0)
350 {
351 return domainKey;
352 }
353
354 return domainKey[..separatorIndex];
355 }
356
357 private static string FormatVariable(
358 MathematicalVariable? variable,
359 int variableId)
360 {
361 if (variable is null)
362 {
363 return $"<unknown:{variableId}>";
364 }
365
366 return
367 $"{variable.Name} [id={variable.Id}; key={variable.DomainKey}]";
368 }
369
370 private static string FormatSense(
371 MathematicalConstraintSense sense)
372 {
373 return sense switch
374 {
375 MathematicalConstraintSense.LessThanOrEqual =>
376 "<=",
377
378 MathematicalConstraintSense.Equal =>
379 "=",
380
381 MathematicalConstraintSense.GreaterThanOrEqual =>
382 ">=",
383
384 _ =>
385 sense.ToString()
386 };
387 }
388
389 private static string Format(
390 double value)
391 {
392 return value.ToString(
393 "G17",
394 CultureInfo.InvariantCulture);
395 }
396
397 private static string FormatBound(
398 double value)
399 {
400 if (double.IsPositiveInfinity(value))
401 {
402 return "+inf";
403 }
404
405 if (double.IsNegativeInfinity(value))
406 {
407 return "-inf";
408 }
409
410 return Format(value);
411 }
412
413 private static string FormatSigned(
414 double value)
415 {
416 string sign =
417 value >= 0.0
418 ? "+"
419 : "-";
420
421 return
422 $"{sign} {Format(Math.Abs(value))}";
423 }
424}
Writes a complete human-readable representation of a solver-independent mathematical model.
static void Write(MathematicalModel model, string filePath)
Writes the supplied mathematical model to a text file.