LotSizingDataModel.Solver.Xpress 2.0.1
Xpress solver adapter for the LotSizingDataModel solver abstraction.
Loading...
Searching...
No Matches
XpressAsciiSolutionValueParser.cs
Go to the documentation of this file.
1using System.Globalization;
2using System.Text.RegularExpressions;
3
5
6/// <summary>
7/// Parses Xpress ASCII solution rows written with the standard Optimizer
8/// solution layout.
9/// </summary>
10/// <remarks>
11/// In the documented ASCII layout, field 2 is the column name and field 5 is
12/// the activity value. The parser also contains a conservative fallback for
13/// minor formatting differences between Xpress releases.
14/// </remarks>
15public static partial class XpressAsciiSolutionValueParser
16{
17 /// <summary>
18 /// Parses variable activities keyed by the portable mathematical variable
19 /// identifier embedded in names of the form <c>v_&lt;id&gt;</c>.
20 /// </summary>
21 /// <param name="path">Xpress ASCII solution file.</param>
22 /// <returns>Parsed finite activities keyed by mathematical variable ID.</returns>
23 public static IReadOnlyDictionary<int, double> ParseFile(
24 string path)
25 {
26 if (string.IsNullOrWhiteSpace(path) ||
27 !File.Exists(path))
28 {
29 return new Dictionary<int, double>();
30 }
31
32 return ParseLines(
33 File.ReadLines(path));
34 }
35
36 /// <summary>
37 /// Parses Xpress ASCII solution lines.
38 /// </summary>
39 /// <param name="lines">Solution lines.</param>
40 /// <returns>Parsed activities.</returns>
41 public static IReadOnlyDictionary<int, double> ParseLines(
42 IEnumerable<string> lines)
43 {
44 ArgumentNullException.ThrowIfNull(lines);
45
46 var result =
47 new Dictionary<int, double>();
48
49 foreach (string? line in lines)
50 {
51 string currentLine =
52 line ?? string.Empty;
53
54 Match nameMatch =
55 VariableNameRegex().Match(
56 currentLine);
57
58 if (!nameMatch.Success ||
59 !int.TryParse(
60 nameMatch.Groups[1].Value,
61 NumberStyles.Integer,
62 CultureInfo.InvariantCulture,
63 out int variableId))
64 {
65 continue;
66 }
67
68 string[] fields =
69 currentLine
70 .Split(
71 (char[]?)null,
72 StringSplitOptions.RemoveEmptyEntries |
73 StringSplitOptions.TrimEntries);
74
75 int nameIndex =
76 Array.FindIndex(
77 fields,
78 field =>
79 string.Equals(
80 field,
81 nameMatch.Value,
82 StringComparison.Ordinal));
83
84 if (nameIndex >= 0 &&
85 nameIndex + 3 < fields.Length &&
86 TryParseFinite(
87 fields[nameIndex + 3],
88 out double documentedActivity))
89 {
90 result[variableId] =
91 documentedActivity;
92 continue;
93 }
94
95 string suffix =
96 currentLine[(nameMatch.Index + nameMatch.Length)..];
97
98 MatchCollection numbers =
99 NumberRegex().Matches(suffix);
100
101 if (numbers.Count == 0)
102 {
103 continue;
104 }
105
106 // The activity is normally the third field after the name. If
107 // formatting has collapsed fields, the final finite number on the
108 // row is the safest fallback for a standard column solution row.
109 for (int index = numbers.Count - 1; index >= 0; index--)
110 {
111 if (TryParseFinite(
112 numbers[index].Value,
113 out double fallbackActivity))
114 {
115 result[variableId] =
116 fallbackActivity;
117 break;
118 }
119 }
120 }
121
122 return result;
123 }
124
125 private static bool TryParseFinite(
126 string text,
127 out double value)
128 {
129 return double.TryParse(
130 text,
131 NumberStyles.Float,
132 CultureInfo.InvariantCulture,
133 out value) &&
134 double.IsFinite(value);
135 }
136
137 [GeneratedRegex(
138 @"\bv_(\d+)\b",
139 RegexOptions.CultureInvariant)]
140 private static partial Regex VariableNameRegex();
141
142 [GeneratedRegex(
143 @"[+\-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+\-]?\d+)?",
144 RegexOptions.CultureInvariant)]
145 private static partial Regex NumberRegex();
146}
Parses Xpress ASCII solution rows written with the standard Optimizer solution layout.
static IReadOnlyDictionary< int, double > ParseLines(IEnumerable< string > lines)
Parses Xpress ASCII solution lines.
static IReadOnlyDictionary< int, double > ParseFile(string path)
Parses variable activities keyed by the portable mathematical variable identifier embedded in names o...