diff --git a/ortools/sat/csharp/CpSolver.cs b/ortools/sat/csharp/CpSolver.cs index 0b6c0dbe71..e050c9d454 100644 --- a/ortools/sat/csharp/CpSolver.cs +++ b/ortools/sat/csharp/CpSolver.cs @@ -26,238 +26,168 @@ namespace Google.OrTools.Sat * variables in the best solution, as well as general statistics of the search. * */ -public class CpSolver +public sealed class CpSolver : IDisposable { - /** Solves the given model, and returns the solve status. */ - public CpSolverStatus Solve(CpModel model, SolutionCallback cb = null) + private LogCallback? _log_callback; + private BestBoundCallback? _best_bound_callback; + private SolveWrapper? _solve_wrapper; + private Queue _terms; + private bool _disposed = false; + + public double ObjectiveValue => Response!.ObjectiveValue; + + public double BestObjectiveBound => Response!.BestObjectiveBound; + + public string? StringParameters { get; set; } + + public CpSolverResponse? Response { get; private set; } + + public CpSolverStatus Solve(CpModel model, SolutionCallback? cb = null) { // Setup search. CreateSolveWrapper(); - if (string_parameters_ is not null) + if (StringParameters is not null) { - solve_wrapper_.SetStringParameters(string_parameters_); - } - if (log_callback_ is not null) - { - solve_wrapper_.AddLogCallbackFromClass(log_callback_); - } - if (best_bound_callback_ is not null) - { - solve_wrapper_.AddBestBoundCallbackFromClass(best_bound_callback_); - } - if (cb is not null) - { - solve_wrapper_.AddSolutionCallback(cb); + _solve_wrapper!.SetStringParameters(StringParameters); } - response_ = solve_wrapper_.Solve(model.Model); + if (_log_callback is not null) + { + _solve_wrapper!.AddLogCallbackFromClass(_log_callback); + } + + if (_best_bound_callback is not null) + { + _solve_wrapper!.AddBestBoundCallbackFromClass(_best_bound_callback); + } + + if (cb is not null) + { + _solve_wrapper!.AddSolutionCallback(cb); + } + + Response = _solve_wrapper!.Solve(model.Model); // Cleanup search. if (cb is not null) { - solve_wrapper_.ClearSolutionCallback(cb); + _solve_wrapper.ClearSolutionCallback(cb); } + ReleaseSolveWrapper(); - return response_.Status; - } - - /** Deprecated, use Solve() instead. */ - [ObsoleteAttribute("This method is obsolete. Call Solve instead.", false)] - public CpSolverStatus SolveWithSolutionCallback(CpModel model, SolutionCallback cb) - { - return Solve(model, cb); - } - - /** Deprecated, use Solve() instead. */ - [ObsoleteAttribute("This method is obsolete. Call Solve instead with the enumerate_all_solutions parameter.", - false)] - public CpSolverStatus SearchAllSolutions(CpModel model, SolutionCallback cb) - { - string old_parameters = string_parameters_; - string_parameters_ += " enumerate_all_solutions:true"; - Solve(model, cb); - string_parameters_ = old_parameters; - return response_.Status; + return Response.Status; } /** Stops the search asynchronously. */ [MethodImpl(MethodImplOptions.Synchronized)] - public void StopSearch() - { - if (solve_wrapper_ is not null) - { - solve_wrapper_.StopSearch(); - } - } + public void StopSearch() => _solve_wrapper?.StopSearch(); - [MethodImpl(MethodImplOptions.Synchronized)] - private void CreateSolveWrapper() - { - solve_wrapper_ = new SolveWrapper(); - } - - [MethodImpl(MethodImplOptions.Synchronized)] - private void ReleaseSolveWrapper() - { - solve_wrapper_ = null; - } - - /** Statistics on the solution found as a string.*/ - public String ResponseStats() - { - return CpSatHelper.SolverResponseStats(response_); - } - - /** The best objective value found during search.*/ - public double ObjectiveValue - { - get { - return response_.ObjectiveValue; - } - } - - /** - * - * The best lower bound found when minimizing, of the best upper bound found when maximizing - * - */ - public double BestObjectiveBound - { - get { - return response_.BestObjectiveBound; - } - } - - /** The parameters of the search, stored as a string */ - public string StringParameters - { - get { - return string_parameters_; - } - set { - string_parameters_ = value; - } - } + public string ResponseStats() => CpSatHelper.SolverResponseStats(Response); public void SetLogCallback(StringToVoidDelegate del) { - log_callback_ = new LogCallbackDelegate(del); + _log_callback?.Dispose(); + _log_callback = new LogCallbackDelegate(del); } public void ClearLogCallback() { - log_callback_ = null; + _log_callback?.Dispose(); + _log_callback = null; } public void SetBestBoundCallback(DoubleToVoidDelegate del) { - best_bound_callback_ = new BestBoundCallbackDelegate(del); + _best_bound_callback?.Dispose(); + _best_bound_callback = new BestBoundCallbackDelegate(del); } public void ClearBestBoundCallback() { - best_bound_callback_ = null; + _best_bound_callback?.Dispose(); + _best_bound_callback = null; } - public CpSolverResponse Response - { - get { - return response_; - } - } - - /** - * - * Returns the value of an integer variable in the last solution found. - * - */ public long Value(IntVar intVar) { - int index = intVar.GetIndex(); - long value = index >= 0 ? response_.Solution[index] : -response_.Solution[-index - 1]; + var index = intVar.GetIndex(); + var value = index >= 0 ? Response!.Solution[index] : -Response!.Solution[-index - 1]; return value; } - /** - * - * Returns the value of a linear expression in the last solution found. - * - */ public long Value(LinearExpr e) { long constant = 0; long coefficient = 1; - LinearExpr expr = e; - if (terms_ is null) + var expr = e; + if (_terms is null) { - terms_ = new Queue(); + _terms = new Queue(); } else { - terms_.Clear(); + _terms.Clear(); } do { switch (expr) { - case LinearExprBuilder a: - constant += coefficient * a.Offset; - if (coefficient == 1) - { - foreach (Term sub in a.Terms) + case LinearExprBuilder a: + constant += coefficient * a.Offset; + if (coefficient == 1) { - terms_.Enqueue(sub); + foreach (var sub in a.Terms) + { + _terms.Enqueue(sub); + } } - } - else - { - foreach (Term sub in a.Terms) + else { - terms_.Enqueue(new Term(sub.expr, sub.coefficient * coefficient)); + foreach (var sub in a.Terms) + { + _terms.Enqueue(new Term(sub.expr, sub.coefficient * coefficient)); + } } - } - break; - case IntVar intVar: - int index = intVar.GetIndex(); - long value = index >= 0 ? response_.Solution[index] : -response_.Solution[-index - 1]; - constant += coefficient * value; - break; - case NotBoolVar: - throw new ArgumentException("Cannot evaluate a literal in an integer expression."); - default: - throw new ArgumentException("Cannot evaluate '" + expr + "' in an integer expression"); + + break; + case IntVar intVar: + var index = intVar.GetIndex(); + var value = index >= 0 ? Response!.Solution[index] : -Response!.Solution[-index - 1]; + constant += coefficient * value; + break; + case NotBoolVar: + throw new ArgumentException("Cannot evaluate a literal in an integer expression."); + default: + throw new ArgumentException("Cannot evaluate '" + expr + "' in an integer expression"); } - if (!terms_.TryDequeue(out var term)) + if (!_terms.TryDequeue(out var term)) { break; } + expr = term.expr; coefficient = term.coefficient; - } while (true); + } + while (true); return constant; } - /** - * - * Returns the Boolean value of a literal in the last solution found. - * - */ - public Boolean BooleanValue(ILiteral literal) + public bool BooleanValue(ILiteral literal) { if (literal is BoolVar || literal is NotBoolVar) { - int index = literal.GetIndex(); + var index = literal.GetIndex(); if (index >= 0) { - return response_.Solution[index] != 0; + return Response!.Solution[index] != 0; } else { - return response_.Solution[-index - 1] == 0; + return Response!.Solution[-index - 1] == 0; } } else @@ -266,76 +196,61 @@ public class CpSolver } } - /** Returns the number of branches explored during search. */ - public long NumBranches() + public long NumBranches() => Response!.NumBranches; + + public long NumConflicts() => Response!.NumConflicts; + + public double WallTime() => Response!.WallTime; + + public IList SufficientAssumptionsForInfeasibility() => Response!.SufficientAssumptionsForInfeasibility; + + public string SolutionInfo() => Response!.SolutionInfo; + + public void Dispose() { - return response_.NumBranches; + if (_disposed) + { + return; + } + + _best_bound_callback?.Dispose(); + _log_callback?.Dispose(); + ReleaseSolveWrapper(); + _disposed = true; } - /** Returns the number of conflicts created during search. */ - public long NumConflicts() + [MethodImpl(MethodImplOptions.Synchronized)] + private void CreateSolveWrapper() { - return response_.NumConflicts; + _solve_wrapper?.Dispose(); + _solve_wrapper = new SolveWrapper(); } - /** Returns the wall time of the search. */ - public double WallTime() + [MethodImpl(MethodImplOptions.Synchronized)] + private void ReleaseSolveWrapper() { - return response_.WallTime; + _solve_wrapper?.Dispose(); + _solve_wrapper = null; } - - public IList SufficientAssumptionsForInfeasibility() - { - return response_.SufficientAssumptionsForInfeasibility; - } - - /** - * - * Returns some information on how the solution was found, or the reason why the model or the - * parameters are invalid. - * - */ - public String SolutionInfo() - { - return response_.SolutionInfo; - } - - private CpSolverResponse response_; - private LogCallback log_callback_; - private BestBoundCallback best_bound_callback_; - private string string_parameters_; - private SolveWrapper solve_wrapper_; - private Queue terms_; } class LogCallbackDelegate : LogCallback { - public LogCallbackDelegate(StringToVoidDelegate del) - { - this.delegate_ = del; - } + private readonly StringToVoidDelegate _delegate_; - public override void NewMessage(string message) - { - delegate_(message); - } + public LogCallbackDelegate(StringToVoidDelegate del) => _delegate_ = del; - private StringToVoidDelegate delegate_; + public override void NewMessage(string message) => _delegate_(message); } class BestBoundCallbackDelegate : BestBoundCallback { - public BestBoundCallbackDelegate(DoubleToVoidDelegate del) - { - this.delegate_ = del; - } + private readonly DoubleToVoidDelegate _delegate; - public override void NewBestBound(double bound) - { - delegate_(bound); - } + public BestBoundCallbackDelegate(DoubleToVoidDelegate del) => _delegate = del; + + public override void NewBestBound(double bound) => _delegate(bound); - private DoubleToVoidDelegate delegate_; } } // namespace Google.OrTools.Sat