ULSAlgorithms 1.1.0-g3e5595996d
High-performance exact and heuristic algorithms for uncapacitated lot sizing
Loading...
Searching...
No Matches
XpressSolverAdapter.cs
Go to the documentation of this file.
1using System.Reflection;
2
4
5/// <summary>
6/// Detects and validates FICO Xpress through its optional Optimizer .NET
7/// assembly.
8/// </summary>
9public sealed class XpressSolverAdapter :
11{
12 private static readonly SemaphoreSlim RuntimeGate =
13 new(1, 1);
14
15 /// <summary>Initializes the Xpress adapter.</summary>
25
26 /// <inheritdoc />
27 public override string AdapterId =>
28 "ULSAlgorithms.Solver.Xpress";
29
30 /// <inheritdoc />
31 public override string AdapterName =>
32 "ULSAlgorithms Xpress Adapter";
33
34 /// <inheritdoc />
35 public override SolverKind SolverKind =>
36 SolverKind.Xpress;
37
38 /// <inheritdoc />
39 public override async ValueTask<SolverAvailabilityInfo>
41 CancellationToken cancellationToken = default)
42 {
43 cancellationToken.ThrowIfCancellationRequested();
44
45 Assembly? assembly;
46 string resolvedPath;
47
48 try
49 {
50 assembly =
52 out resolvedPath);
53 }
54 catch (Exception exception)
55 {
56 Exception effective =
57 Unwrap(exception);
58
59 return Failure(
60 ClassifyFailure(effective),
61 string.Empty,
62 effective.Message);
63 }
64
65 if (assembly is null)
66 {
67 return new SolverAvailabilityInfo(
68 SolverKind.Xpress,
69 SolverAvailabilityStatus.NotInstalled,
70 solverName: "FICO Xpress MP",
71 diagnostics:
72 [
73 "Optimizer.dll was not found. Define XPRESSDIR or set " +
74 "ULSALGORITHMS_XPRESS_OPTIMIZER_ASSEMBLY."
75 ]);
76 }
77
78 await RuntimeGate.WaitAsync(
79 cancellationToken);
80
81 try
82 {
83 Type xprsType =
84 assembly.GetType(
85 "Optimizer.XPRS",
86 throwOnError: true,
87 ignoreCase: false)!;
88
89 InvokeXpressInit(
90 xprsType);
91
92 try
93 {
94 string version =
95 TryGetStaticProperty(
96 xprsType,
97 "Version",
98 "VERSION")?
99 .ToString() ??
100 string.Empty;
101
102 return new SolverAvailabilityInfo(
103 SolverKind.Xpress,
104 SolverAvailabilityStatus.Available,
105 solverName: "FICO Xpress MP",
106 solverVersion: version,
107 installationPath:
108 ResolveInstallationDirectory(
109 resolvedPath),
110 managedAssemblyPath:
111 resolvedPath,
112 nativeLibraryPath:
113 Environment.GetEnvironmentVariable(
114 "XPRESSDIR") ??
115 string.Empty,
116 licenseInformation:
117 "XPRS.Init completed successfully.",
118 diagnostics:
119 [
120 $"FICO Xpress Optimizer runtime loaded from " +
121 $"'{resolvedPath}'.",
122 "XPRS.Init completed successfully, including " +
123 "native runtime/license initialization."
124 ]);
125 }
126 finally
127 {
128 TryInvokeStatic(
129 xprsType,
130 "Free");
131 }
132 }
133 catch (OperationCanceledException)
134 {
135 throw;
136 }
137 catch (Exception exception)
138 {
139 Exception effective =
140 Unwrap(exception);
141
142 return Failure(
143 ClassifyFailure(effective),
144 resolvedPath,
145 effective.Message);
146 }
147 finally
148 {
149 RuntimeGate.Release();
150 }
151 }
152
153 private static SolverAvailabilityInfo Failure(
155 string resolvedPath,
156 string diagnostic)
157 {
158 return new SolverAvailabilityInfo(
159 SolverKind.Xpress,
160 status,
161 solverName: "FICO Xpress MP",
162 installationPath:
163 ResolveInstallationDirectory(
164 resolvedPath),
165 managedAssemblyPath:
166 resolvedPath,
167 nativeLibraryPath:
168 Environment.GetEnvironmentVariable(
169 "XPRESSDIR") ??
170 string.Empty,
171 diagnostics: [diagnostic]);
172 }
173
174 private static string ResolveInstallationDirectory(
175 string path)
176 {
177 if (string.IsNullOrWhiteSpace(path) ||
178 string.Equals(
179 path,
180 "already loaded",
181 StringComparison.OrdinalIgnoreCase) ||
182 string.Equals(
183 path,
184 "assembly probing",
185 StringComparison.OrdinalIgnoreCase))
186 {
187 return Environment.GetEnvironmentVariable(
188 "XPRESSDIR") ??
189 string.Empty;
190 }
191
192 return Path.GetDirectoryName(path) ??
193 string.Empty;
194 }
195
196 private static object? TryGetStaticProperty(
197 Type type,
198 params string[] names)
199 {
200 foreach (string name in names)
201 {
202 PropertyInfo? property =
203 type.GetProperty(
204 name,
205 BindingFlags.Public |
206 BindingFlags.Static |
207 BindingFlags.IgnoreCase);
208
209 if (property is not null)
210 {
211 return property.GetValue(null);
212 }
213
214 FieldInfo? field =
215 type.GetField(
216 name,
217 BindingFlags.Public |
218 BindingFlags.Static |
219 BindingFlags.IgnoreCase);
220
221 if (field is not null)
222 {
223 return field.GetValue(null);
224 }
225 }
226
227 return null;
228 }
229
230 private static void InvokeXpressInit(
231 Type xprsType)
232 {
233 MethodInfo? withArgument =
234 FindStaticMethod(
235 xprsType,
236 "Init",
237 parameterCount: 1);
238
239 if (withArgument is not null)
240 {
241 withArgument.Invoke(
242 null,
243 [string.Empty]);
244 return;
245 }
246
247 MethodInfo? withoutArgument =
248 FindStaticMethod(
249 xprsType,
250 "Init",
251 parameterCount: 0);
252
253 if (withoutArgument is not null)
254 {
255 withoutArgument.Invoke(
256 null,
257 null);
258 return;
259 }
260
261 throw new MissingMethodException(
262 xprsType.FullName,
263 "Init");
264 }
265
266 private static void InvokeStatic(
267 Type type,
268 string methodName,
269 params object?[] arguments)
270 {
271 MethodInfo method =
272 FindStaticMethod(
273 type,
274 methodName,
275 arguments.Length) ??
276 throw new MissingMethodException(
277 type.FullName,
278 methodName);
279
280 method.Invoke(
281 null,
282 arguments);
283 }
284
285 private static void TryInvokeStatic(
286 Type type,
287 string methodName,
288 params object?[] arguments)
289 {
290 try
291 {
292 MethodInfo? method =
293 FindStaticMethod(
294 type,
295 methodName,
296 arguments.Length);
297
298 method?.Invoke(
299 null,
300 arguments);
301 }
302 catch
303 {
304 // Cleanup must not mask the original result/error.
305 }
306 }
307
308 private static MethodInfo? FindStaticMethod(
309 Type type,
310 string methodName,
311 int parameterCount)
312 {
313 return type
314 .GetMethods(
315 BindingFlags.Public |
316 BindingFlags.Static)
317 .FirstOrDefault(
318 method =>
319 string.Equals(
320 method.Name,
321 methodName,
322 StringComparison.OrdinalIgnoreCase) &&
323 method.GetParameters().Length ==
324 parameterCount);
325 }
326}
static Exception Unwrap(Exception exception)
Unwraps reflection invocation exceptions.
static SolverAvailabilityStatus ClassifyFailure(Exception exception)
Maps an exception to a stable availability state.
OptimizationSolverAdapterBase(params SolverCapability[] capabilities)
Initializes an adapter with its supported capabilities.
Locates and loads the optional FICO Xpress Optimizer managed assembly.
static ? Assembly TryLoad(out string resolvedPath)
Loads an already loaded Optimizer assembly, an explicitly located assembly, or an assembly resolvable...
override async ValueTask< SolverAvailabilityInfo > CheckAvailabilityAsync(CancellationToken cancellationToken=default)
Performs the adapter-specific machine discovery / load / license check.
override SolverKind SolverKind
Gets the concrete solver targeted by this adapter.
override string AdapterName
Gets the adapter display name.
override string AdapterId
Gets the stable adapter identifier.
Describes solver availability and the installation selected by an adapter.
SolverAvailabilityStatus
Describes whether a solver can currently be used on the current computer.
SolverCapability
Identifies an optional capability exposed by an optimization-solver adapter.
@ LinearProgramming
Continuous linear programming.
@ OptimalityGapReporting
Best-bound and optimality-gap reporting.
@ MixedIntegerLinearProgramming
Mixed-integer linear programming.
@ SearchStatistics
Search-node and iteration statistics.
SolverKind
Identifies a mathematical optimization solver that can be used by solver-backed ULS algorithms.
Definition SolverKind.cs:15