1using System.Diagnostics;
2using System.Globalization;
3using System.Reflection;
16 private static readonly SemaphoreSlim RuntimeGate =
24 public async ValueTask<LinearModelSolveResult>
SolveAsync(
28 CancellationToken cancellationToken =
default)
30 ArgumentNullException.ThrowIfNull(model);
31 ArgumentNullException.ThrowIfNull(selection);
32 ArgumentNullException.ThrowIfNull(options);
36 throw new ArgumentException(
37 "The supplied selection does not target Xpress.",
49 await RuntimeGate.WaitAsync(
69 out
string resolvedPath);
80 "Optimizer.dll unavailable",
81 [
"Xpress was selected but Optimizer.dll could not be loaded."]);
102 using CancellationTokenRegistration cancellationRegistration =
103 cancellationToken.Register(
111 api.Optimize(problem),
112 CancellationToken.None);
120 IReadOnlyDictionary<int, double> values =
125 out
string mappingDiagnostic);
127 bool solutionExists =
130 string nativeStatus =
144 $
"Xpress runtime: {resolvedPath}."
147 if (!
string.IsNullOrWhiteSpace(
154 if (!
string.IsNullOrWhiteSpace(
158 $
"Xpress status: {nativeStatus}.");
175 if (problem is not
null)
184 catch (OperationCanceledException)
196 [
"Xpress execution was cancelled by the caller."],
201 catch (Exception exception)
205 Exception effective =
206 exception is TargetInvocationException invocation &&
207 invocation.InnerException is Exception inner
218 effective.GetType().Name,
226 RuntimeGate.Release();
242 StringComparison.OrdinalIgnoreCase) &&
245 StringComparison.OrdinalIgnoreCase))
252 StringComparison.OrdinalIgnoreCase))
259 StringComparison.OrdinalIgnoreCase))
266 StringComparison.OrdinalIgnoreCase))
287 if (objective.HasValue &&
295 Math.Abs(objective.Value)))
307internal sealed class XpressExecutionReflectionApi
309 private readonly Type _xprsType;
310 private readonly Type _problemType;
312 private XpressExecutionReflectionApi(
316 _xprsType = xprsType;
317 _problemType = problemType;
320 internal static XpressExecutionReflectionApi
Create(
331 "Optimizer.XPRSprob",
335 return new XpressExecutionReflectionApi(
342 MethodInfo? oneArgument =
349 if (oneArgument is not
null)
358 MethodInfo? zeroArgument =
365 zeroArgument?.Invoke(
369 if (oneArgument is
null &&
370 zeroArgument is
null)
372 throw new MissingMethodException(
399 return Activator.CreateInstance(
401 throw new InvalidOperationException(
402 "Unable to create Optimizer.XPRSprob.");
434 return InvokeRequired(
436 "GetSolution") as
double[];
448 out
string diagnostic)
450 diagnostic =
string.Empty;
452 if (solution is
null)
454 return new Dictionary<int, double>();
458 new Dictionary<int, double>();
460 bool allResolved =
true;
464 if (!TryGetColumnIndex(
470 index >= solution.Length)
476 byName[variable.
Id] =
481 byName.Count == model.VariableCount)
484 "Xpress solution values were mapped by portable variable name.";
489 if (solution.Length ==
493 new Dictionary<int, double>();
496 index < model.VariableCount;
500 model.Variables[index].Id] =
505 "Xpress used the LP column-order fallback because GetIndex " +
506 "could not map every portable variable name.";
512 "Xpress returned a solution vector that could not be mapped safely.";
514 return new Dictionary<int, double>();
520 return TryGetProperty(
531 params
string[] names)
548 CultureInfo.InvariantCulture);
550 return double.IsFinite(converted)
571 if (problem is IDisposable disposable)
573 disposable.Dispose();
581 foreach (
string name
in
615 private static bool TryGetColumnIndex(
622 MethodInfo? twoParameter =
629 if (twoParameter is not
null)
638 if (value is not
null)
643 CultureInfo.InvariantCulture);
654 MethodInfo? threeParameter =
661 if (threeParameter is not
null)
665 object?[] arguments =
668 threeParameter.Invoke(
675 CultureInfo.InvariantCulture);
688 private static object? TryGetProperty(
690 params
string[] names)
692 foreach (
string name
in names)
694 PropertyInfo?
property =
695 target.GetType().GetProperty(
697 BindingFlags.Instance |
698 BindingFlags.Public |
699 BindingFlags.IgnoreCase);
701 if (property is not
null)
703 return property.GetValue(
711 private static bool TrySetProperty(
716 PropertyInfo?
property =
717 target.GetType().GetProperty(
719 BindingFlags.Instance |
720 BindingFlags.Public |
721 BindingFlags.IgnoreCase);
723 if (property is
null ||
734 property.PropertyType,
735 CultureInfo.InvariantCulture);
749 private static object? InvokeRequired(
752 params
object?[] arguments)
759 parameterCount: arguments.Length) ??
760 throw new MissingMethodException(
761 target.GetType().FullName,
764 return method.Invoke(
769 private static MethodInfo? FindMethod(
776 BindingFlags.Public |
778 ? BindingFlags.Static
779 : BindingFlags.Instance);
788 StringComparison.OrdinalIgnoreCase) &&
789 method.GetParameters().Length ==
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...
Configures one solver-backed execution of a portable linear model.
bool KeepTemporaryFiles
Gets or sets whether temporary model/solution/log artifacts are retained.
Result of executing a solver-independent linear or mixed-integer model.
Writes a conservative LP representation understood by all four supported execution backends.
void Write(LinearModel model, string path)
Writes the supplied model to an LP file.
static string GetVariableName(int variableId)
Gets the stable portable variable name.
IReadOnlyDictionary< int, double > GetValues(object problem, LinearModel model, double[]? solution, out string diagnostic)
double? TryGetDoubleProperty(object problem, params string[] names)
double?[] TryGetSolution(object problem)
string TryGetStatus(object problem)
void ReadProblem(object problem, string modelPath)
void DestroyProblem(object problem)
static XpressExecutionReflectionApi Create(Assembly assembly)
void Optimize(object problem)
static void TryInterrupt(object problem)
Executes portable LP/MILP models through the optional FICO Xpress Optimizer .NET runtime loaded by re...
async ValueTask< LinearModelSolveResult > SolveAsync(LinearModel model, SolverSelectionResult selection, LinearModelSolveOptions options, CancellationToken cancellationToken=default)
Executes one portable model using an already selected solver.
SolverKind SolverKind
Gets the concrete solver implemented by this executor.
static LinearModelSolveResult BuildSolutionResult(LinearModel model, SolverSelectionResult selection, LinearModelSolveOptions options, LinearModelSolveStatus proposedStatus, IReadOnlyDictionary< int, double > returnedValues, bool hasCandidateSolution, TimeSpan duration, string nativeStatus, IEnumerable< string > diagnostics, string artifactDirectory)
static string CreateArtifactDirectory(SolverKind solverKind, LinearModelSolveOptions options)
static void DeleteArtifactsUnlessRetained(string directory, LinearModelSolveOptions options)
static void ExportModelIfRequested(string generatedModelPath, LinearModelSolveOptions options)
Immutable solver-independent linear or mixed-integer linear model.
string Name
Gets the model name.
Describes one variable in a portable linear mathematical model.
int Id
Gets the stable zero-based variable identifier.
Serializable-style immutable snapshot of the solver selected for one solver-backed ULS execution.
Describes the outcome of optimization-solver selection.
SolverKind SelectedSolver
Gets the selected solver kind, or Unknown when none was selected.
Executes the portable LinearModel with one concrete solver.
LinearModelSolveStatus
Describes the termination state of a solver-backed portable linear model.