1using System.Diagnostics;
38 if (
string.IsNullOrWhiteSpace(name))
40 throw new ArgumentException(
41 "A solver name is required.",
48 throw new ArgumentNullException(nameof(separator));
53 CloneExecutionOptions(
56 _cuttingPlaneOptions =
57 CloneCuttingPlaneOptions(
58 cuttingPlaneOptions ??
62 public string Name {
get; }
73 ArgumentNullException.ThrowIfNull(problem);
74 return _separator.IsApplicable(problem);
79 CancellationToken cancellationToken =
default)
81 ArgumentNullException.ThrowIfNull(problem);
82 cancellationToken.ThrowIfCancellationRequested();
89 .ConfigureAwait(
false),
90 CancellationToken.None)
97 CancellationToken cancellationToken =
default)
99 ArgumentNullException.ThrowIfNull(problem);
100 cancellationToken.ThrowIfCancellationRequested();
104 throw new NotSupportedException(
105 $
"{Name} is not applicable to the supplied ULS cost structure.");
108 var formulationBuilder =
112 formulationBuilder.Build(problem);
119 new List<(string Name, LsCutDefinition Cut)>();
123 StringComparer.Ordinal);
125 var iterationReports =
126 new List<CutIterationReport>();
128 var convergenceIterations =
129 new List<CuttingPlaneIterationStatistics>();
136 for (
int iteration = 0;
137 iteration < _cuttingPlaneOptions.MaximumIterations;
140 cancellationToken.ThrowIfCancellationRequested();
143 selectedSolver is
null
144 ? CloneExecutionOptions(_executionOptions)
145 : CreatePinnedExecutionOptions(
150 await _modelSolver.SolveAsync(
154 .ConfigureAwait(
false);
156 if (lpExecution.
Solver is not
null &&
157 selectedSolver is
null)
166 return BuildEarlyFailure(
170 convergenceIterations,
171 "Root LP relaxation could not be solved to a valid " +
176 Stopwatch.StartNew();
178 IReadOnlyList<LsSeparatedCut> candidates =
190 var eligibleIndices =
194 new Dictionary<int, (CutDisposition Disposition, string Reason)>();
197 new string[candidates.Count];
199 var seenThisIteration =
201 StringComparer.Ordinal);
204 index < candidates.Count;
217 _cuttingPlaneOptions.ViolationTolerance)
219 preDisposition[index] =
222 $
"Violation {candidate.Violation:G17} <= " +
224 $
"{_cuttingPlaneOptions.ViolationTolerance:G17}."
231 _cuttingPlaneOptions.MinimumEfficacy)
233 preDisposition[index] =
236 $
"Efficacy {candidate.Efficacy:G17} < minimum " +
237 $
"{_cuttingPlaneOptions.MinimumEfficacy:G17}."
243 if (knownCuts.Contains(key) ||
244 !seenThisIteration.Add(key))
246 preDisposition[index] =
249 "An equivalent (l,S) cut is already present."
255 eligibleIndices.Add(index);
258 HashSet<int> selectedIndices =
262 _cuttingPlaneOptions);
265 new List<(string Name, LsCutDefinition Cut)>();
267 int selectedCount = 0;
270 index < candidates.Count;
281 if (preDisposition.TryGetValue(
291 else if (!selectedIndices.Contains(index))
297 $
"Eligible violated cut not selected by policy " +
298 $
"'{_cuttingPlaneOptions.SelectionPolicy}'.";
306 $
"ls_{_separator.Method}_{iteration}_{sequence}";
309 $
"Selected by '{_cuttingPlaneOptions.SelectionPolicy}' " +
310 "and added to the portable LP model.";
337 var iterationReport =
343 iterationReports.Add(
346 double[] positiveViolations =
350 candidate.Violation > 0.0)
356 convergenceIterations.Add(
365 eligibleIndices.Count,
373 candidates.Count == 0
377 candidate.Violation),
378 meanPositiveViolation:
379 positiveViolations.Length == 0
381 : positiveViolations.Average(),
383 candidates.Count == 0
387 candidate.Efficacy)));
389 if (newlyAdded.Count == 0)
400 if (selectedSolver is
null)
406 "No optimization solver was selected during root separation.");
415 CreatePinnedExecutionOptions(
420 await _modelSolver.SolveAsync(
424 .ConfigureAwait(
false);
432 convergenceIterations,
435 var executionReport =
445 MapStatusWithoutSolution(
484 "ULS-domain validation rejected the reconstructed " +
485 "cutting-plane solution: " +
505 $
"Portable-model objective " +
506 $
"{finalExecution.ObjectiveValue.Value:G17} differs " +
507 $
"from reconstructed ULS objective " +
508 $
"{solution.TotalCost:G17}.");
512 finalExecution.Status ==
529 catch (Exception exception)
530 when (exception is not OperationCanceledException)
540 $
"ULS solution reconstruction failed: " +
541 $
"{exception.Message}");
548 IReadOnlyList<CutIterationReport> iterationReports,
549 IReadOnlyList<CuttingPlaneIterationStatistics> convergenceIterations,
552 if (selectedSolver is
null)
556 MapStatusWithoutSolution(
572 convergenceIterations,
573 finalMipObjective:
null);
577 MapStatusWithoutSolution(
613 private static bool ObjectivesAgree(
614 double modelObjective,
622 Math.Abs(modelObjective),
623 Math.Abs(ulsObjective)));
631 private static string BuildMessage(
632 LinearModelSolveResult execution,
633 CutGenerationReport cuts,
634 CuttingPlaneConvergenceReport convergence)
639 $
"Cuts generated: {cuts.CutsGenerated}",
640 $
"cuts added: {cuts.CutsAdded}",
641 $
"not selected: {cuts.NotSelected}",
642 $
"separation iterations: {cuts.IterationCount}"
648 $
"root bound improvement: " +
649 $
"{convergence.RootBoundImprovement.Value:G17}");
655 $
"root gap closed: " +
656 $
"{convergence.RootGapClosedFraction.Value:P2}");
659 if (execution.
Solver is not
null)
662 $
"Optimization engine: " +
663 $
"{execution.Solver.SolverName} " +
664 $
"{execution.Solver.SolverVersion}".Trim());
667 if (!
string.IsNullOrWhiteSpace(
671 $
"Native status: {execution.NativeStatus}");
681 !
string.IsNullOrWhiteSpace(part)));
684 private static LinearModelSolveOptions CreatePinnedExecutionOptions(
685 LinearModelSolveOptions source,
688 LinearModelSolveOptions result =
689 CloneExecutionOptions(source);
694 result.AllowFallbackWhenExplicit =
700 private static LinearModelSolveOptions CloneExecutionOptions(
701 LinearModelSolveOptions source)
705 return new LinearModelSolveOptions
708 AllowFallbackWhenExplicit =
710 FeasibilityTolerance =
714 IntegralityTolerance =
716 NearIntegerTolerance =
723 source.TemporaryRootPath
727 private static LsCuttingPlaneOptions CloneCuttingPlaneOptions(
728 LsCuttingPlaneOptions source)
732 return new LsCuttingPlaneOptions
742 MaximumCutsPerIteration =
743 source.MaximumCutsPerIteration
Complete cutting-plane traceability report for one solver-backed solve.
Traceability report for one cutting-plane iteration.
Trace record for one generated cutting-plane constraint.
Summarizes root-bound evolution and separation effort for one exact cut-and-solve execution.
double? RootGapClosedFraction
Gets the fraction of the initial LP-to-MIP gap closed by root cutting planes. Returns null when the d...
double? RootBoundImprovement
Gets absolute improvement of the root lower bound.
Combines solver-selection provenance, cutting-plane traceability and root convergence statistics.
Numerical convergence statistics for one root cutting-plane iteration.
static string Create(LsCutDefinition definition)
static LinearModel AddCuts(LinearModel source, IEnumerable<(string Name, LsCutDefinition Cut)> cuts)
static LinearModel CreateLpRelaxation(LinearModel source)
static HashSet< int > Select(IReadOnlyList< LsSeparatedCut > candidates, IReadOnlyCollection< int > eligibleIndices, LsCuttingPlaneOptions options)
Configures root LP (l,S) separation before the final exact MILP solve.
int MaximumIterations
Gets or sets the maximum number of root separation iterations.
CutSelectionPolicy SelectionPolicy
Gets or sets the cut-pool selection policy.
double MinimumEfficacy
Gets or sets the minimum efficacy required before a cut is eligible. The default zero preserves v0....
void EnsureValid()
Validates this option set.
double ViolationTolerance
Gets or sets the positive violation required before a cut is eligible.
One candidate (l,S) inequality produced by a separation procedure.
LsCutDefinition Definition
Gets the solver-independent inequality definition.
double Violation
Gets RHS - LHS for the canonical greater-than-or-equal form. Positive values are violated.
double Efficacy
Gets violation divided by the Euclidean coefficient norm.
bool IsApplicable(UlsProblem problem)
string Name
Gets the stable human-readable name of the solver.
UlsSolverKind Kind
Gets the broad family of the solver.
LsCuttingPlaneSolverBase(string name, ILsCutSeparator separator, LinearModelSolver? modelSolver=null, LinearModelSolveOptions? executionOptions=null, LsCuttingPlaneOptions? cuttingPlaneOptions=null)
async ValueTask< UlsSolveResult > SolveAsync(UlsProblem problem, CancellationToken cancellationToken=default)
Solves a ULS problem asynchronously.
UlsSolveResult Solve(UlsProblem problem, CancellationToken cancellationToken=default)
Solves an uncapacitated lot-sizing problem.
CutSeparationMethod SeparationMethod
Represents a validated classical uncapacitated lot-sizing problem.
Configures one solver-backed execution of a portable linear model.
SolverKind Solver
Gets or sets the requested solver. The default is automatic selection.
bool AllowFallbackWhenExplicit
Gets or sets whether an explicitly requested solver may fall back to another solver when unavailable.
double NearIntegerTolerance
Gets or sets the tolerance used to clean continuous values that are numerically indistinguishable fro...
double ZeroTolerance
Gets or sets the tolerance used to identify numerical zero before validation and objective reconstruc...
double IntegralityTolerance
Gets or sets the integrality tolerance used both for normalization and the independent solution check...
bool KeepTemporaryFiles
Gets or sets whether temporary model/solution/log artifacts are retained.
void EnsureValid()
Validates this option set.
string ExportModelPath
Gets or sets an optional path receiving the exact LP model submitted to the selected solver.
double FeasibilityTolerance
Gets or sets the feasibility tolerance used by the independent solution checker.
Result of executing a solver-independent linear or mixed-integer model.
SolverExecutionInfo? Solver
Gets selected-solver provenance, when a solver was selected.
bool HasFeasibleSolution
Gets whether the result contains an independently valid solution.
TimeSpan SolveDuration
Gets elapsed solver execution time.
string NativeStatus
Gets the provider-native status/log summary.
IReadOnlyList< string > Diagnostics
Gets execution and validation diagnostics.
IReadOnlyDictionary< int, double > VariableValues
Gets variable values keyed by portable variable id.
LinearModelSolveStatus Status
Gets the normalized solve status after independent validation.
double? ObjectiveValue
Gets the independently recomputed objective value.
High-level solver-independent execution service for portable linear models.
Immutable solver-independent linear or mixed-integer linear model.
Serializable-style immutable snapshot of the solver selected for one solver-backed ULS execution.
SolverKind SelectedSolver
Gets the selected concrete solver.
ULS result enriched with the complete (l,S) cut-generation report.
Represents a feasible production plan for a ULS problem.
double TotalCost
Gets the complete objective value.
Represents the outcome returned by a ULS solution strategy.
Independent ULS-domain validation report for one production plan.
IReadOnlyList< string > Diagnostics
Gets validation diagnostics.
bool IsFeasible
Gets whether the plan passed every ULS-domain check.
Independently verifies a ULS production plan against the original UlsProblem.
static UlsSolutionValidationResult Validate(UlsProblem problem, UlsSolution solution, double tolerance=1.0e-7)
Validates inventory balance, nonnegativity, setup linking, final inventory and all objective componen...
Optional asynchronous companion contract for ULS strategies whose implementation delegates work to an...
Defines the common strategy contract implemented by every ULS solver.
Separates classical ULS (l,S) inequalities from a fractional aggregate lot-sizing solution.
UlsSolverKind
Identifies the broad family of a ULS solution strategy.
CutSeparationMethod
Identifies the separation procedure that generated a cut.
CutDisposition
Describes what happened to a generated cut after separation.
LinearModelSolveStatus
Describes the termination state of a solver-backed portable linear model.
@ Infeasible
The model was proven infeasible.
@ Unknown
No reliable status was obtained.
@ Cancelled
The computation was cancelled by the caller.
@ Optimal
The model was solved to proven optimality.
@ SolverUnavailable
No usable optimization solver was available.
SolverKind
Identifies a mathematical optimization solver that can be used by solver-backed ULS algorithms.
UlsSolveStatus
Describes the mathematical status of a ULS solve.
@ Optimal
A globally optimal solution has been found.
@ Failed
The solver failed before producing a valid mathematical conclusion.