Class Solver
- Namespace
- OptalCP
- Assembly
- OptalCP.dll
Async solver with event callbacks for solutions, bounds, and log messages.
public class Solver : IDisposable
- Inheritance
-
Solver
- Implements
- Inherited Members
Remarks
Provides event-driven communication with the solver subprocess.
Unlike Model.Solve, Solver allows the user to process individual events
during the solve and to stop the solver at any time. If you're
interested in the final result only, use Model.Solve instead.
To solve a model, create a new Solver object and call its method Solver.Solve.
Event callbacks are set as properties before calling Solve:
- Solver.OnError: Called with a string message when an error occurs.
- Solver.OnWarning: Called with a string for every issued warning.
- Solver.OnLog: Called with a string for every log message.
- Solver.OnSolution: Called with a SolutionEvent when a solution is found.
- Solver.OnObjectiveBound: Called with an
ObjectiveBoundEntrywhen a new bound is proved.
The solver output (log, trace, and warnings) is printed to console by default. It can be redirected to a file or suppressed using the Parameters.printLog parameter.
Example:
x.
After finding the first solution, we request the solver to stop.
var model = new Model();
var x = model.IntervalVar(length: 10, name: "x");
model.Minimize(x.End());
var solver = new Solver();
// Define solution handler:
solver.OnSolution = (evt) =>
{
var solution = evt.solution;
Console.WriteLine($"At time {evt.solveTime:F2}, objective = {solution.GetObjective()}");
if (solution.IsPresent(x))
Console.WriteLine($" x: [{solution.GetStart(x)} -- {solution.GetEnd(x)}]");
else
Console.WriteLine(" x is absent");
// Request the solver to stop:
solver.Stop("We are happy with the first solution.");
return Task.CompletedTask;
};
var result = await solver.Solve(model, new Parameters { timeLimit = 60 });
Console.WriteLine($"Duration: {result.duration}");
Constructors
Solver()
Async solver with event callbacks for solutions, bounds, and log messages.
public Solver()
Remarks
Provides event-driven communication with the solver subprocess.
Unlike Model.Solve, Solver allows the user to process individual events
during the solve and to stop the solver at any time. If you're
interested in the final result only, use Model.Solve instead.
To solve a model, create a new Solver object and call its method Solver.Solve.
Event callbacks are set as properties before calling Solve:
- Solver.OnError: Called with a string message when an error occurs.
- Solver.OnWarning: Called with a string for every issued warning.
- Solver.OnLog: Called with a string for every log message.
- Solver.OnSolution: Called with a SolutionEvent when a solution is found.
- Solver.OnObjectiveBound: Called with an
ObjectiveBoundEntrywhen a new bound is proved.
The solver output (log, trace, and warnings) is printed to console by default. It can be redirected to a file or suppressed using the Parameters.printLog parameter.
Example:
x.
After finding the first solution, we request the solver to stop.
var model = new Model();
var x = model.IntervalVar(length: 10, name: "x");
model.Minimize(x.End());
var solver = new Solver();
// Define solution handler:
solver.OnSolution = (evt) =>
{
var solution = evt.solution;
Console.WriteLine($"At time {evt.solveTime:F2}, objective = {solution.GetObjective()}");
if (solution.IsPresent(x))
Console.WriteLine($" x: [{solution.GetStart(x)} -- {solution.GetEnd(x)}]");
else
Console.WriteLine(" x is absent");
// Request the solver to stop:
solver.Stop("We are happy with the first solution.");
return Task.CompletedTask;
};
var result = await solver.Solve(model, new Parameters { timeLimit = 60 });
Console.WriteLine($"Duration: {result.duration}");
Properties
OnError
Callback for error messages from the solver.
public Func<string?, Task>? OnError { get; set; }
Property Value
Remarks
The callback is called for each error message. The default is no callback.
The callback receives one argument:
msg(string): The error message text.
In C#, callbacks must return Task. Use Task.CompletedTask for synchronous implementations.
If the callback throws an exception, the solve is aborted with that error.
Note: This property cannot be changed while a solve is in progress. Attempting to set it during an active solve raises an error.
var solver = new Solver();
// Synchronous callback
solver.OnError = (msg) => {
Console.Error.WriteLine($"ERROR: {msg}");
return Task.CompletedTask;
};
// Asynchronous callback
solver.OnError = async (msg) => {
await asyncLogger.ErrorAsync(msg);
};
An error message indicates that the solve is closing. However, other messages (log, warning, solution) may still arrive after the error.
See also:
- Solver.OnWarning — for warning messages.
- Solver.OnLog — for log messages.
OnLog
Callback for log messages from the solver.
public Func<string, Task>? OnLog { get; set; }
Property Value
Remarks
The callback is called for each log message, after the message is written to the output stream (see Parameters.printLog). The default is no callback.
The callback receives one argument:
msg(string): The log message text.
In C#, callbacks must return Task. Use Task.CompletedTask for synchronous implementations.
If the callback throws an exception, the solve is aborted with that error.
Note: This property cannot be changed while a solve is in progress. Attempting to set it during an active solve raises an error.
var solver = new Solver();
// Synchronous callback
solver.OnLog = (msg) => {
Console.WriteLine($"LOG: {msg}");
return Task.CompletedTask;
};
// Asynchronous callback
solver.OnLog = async (msg) => {
await asyncLogger.InfoAsync(msg);
};
The amount of log messages and their periodicity can be controlled by Parameters.logLevel and Parameters.logPeriod.
See also:
- Parameters.printLog — for redirecting output to a stream.
- Solver.OnWarning — for warning messages.
OnObjectiveBound
Callback for objective bound events from the solver.
public Func<ObjectiveBoundEntry, Task>? OnObjectiveBound { get; set; }
Property Value
Remarks
The callback is called when the solver improves the bound on the objective (lower bound for minimization, upper bound for maximization). The default is no callback.
The callback receives one argument:
event(ObjectiveBoundEntry): An object with the following fields:
In C#, callbacks must return Task. Use Task.CompletedTask for synchronous implementations.
If the callback throws an exception, the solve is aborted with that error.
Note: This property cannot be changed while a solve is in progress. Attempting to set it during an active solve raises an error.
var solver = new Solver();
// Synchronous callback
solver.OnObjectiveBound = (e) => {
Console.WriteLine($"Bound: {e.value}");
return Task.CompletedTask;
};
// Asynchronous callback
solver.OnObjectiveBound = async (e) => {
await UpdateDashboard(e.value);
};
See also:
- ObjectiveBoundEntry — for the event structure.
- Solver.OnSolution — for solution events with objective values.
OnSolution
Callback for solution events from the solver.
public Func<SolutionEvent, Task>? OnSolution { get; set; }
Property Value
Remarks
The callback is called each time the solver finds a new solution. The default is no callback.
The callback receives one argument:
event(SolutionEvent): An object with the following fields:
In C#, callbacks must return Task. Use Task.CompletedTask for synchronous implementations.
If the callback throws an exception, the solve is aborted with that error.
Note: This property cannot be changed while a solve is in progress. Attempting to set it during an active solve raises an error.
var solver = new Solver();
// Synchronous callback
solver.OnSolution = (e) => {
var sol = e.solution;
var time = e.solveTime;
Console.WriteLine($"Solution found at {time:F2}s, objective={sol.GetObjective()}");
return Task.CompletedTask;
};
// Asynchronous callback
solver.OnSolution = async (e) => {
await SaveToDatabase(e.solution);
};
See also:
- SolutionEvent — for the event structure.
- Solution — for accessing variable values.
- Solver.OnObjectiveBound — for objective bound updates.
OnWarning
Callback for warning messages from the solver.
public Func<string, Task>? OnWarning { get; set; }
Property Value
Remarks
The callback is called for each warning message, after the message is written to the output stream (see Parameters.printLog). The default is no callback.
The callback receives one argument:
msg(string): The warning message text.
In C#, callbacks must return Task. Use Task.CompletedTask for synchronous implementations.
If the callback throws an exception, the solve is aborted with that error.
Note: This property cannot be changed while a solve is in progress. Attempting to set it during an active solve raises an error.
var solver = new Solver();
// Synchronous callback
solver.OnWarning = (msg) => {
Console.WriteLine($"WARNING: {msg}");
return Task.CompletedTask;
};
// Asynchronous callback
solver.OnWarning = async (msg) => {
await asyncLogger.WarningAsync(msg);
};
The amount of warning messages can be configured using Parameters.warningLevel.
See also:
- Parameters.printLog — for redirecting output to a stream.
- Solver.OnLog — for log messages.
- Solver.OnError — for error messages.
Methods
Dispose()
Disposes the solver and any active process.
public void Dispose()
FindSolver(Parameters?)
Find path to the optalcp binary.
public static string FindSolver(Parameters? parameters = null)
Parameters
parametersParametersParameters object that may contain the solver path or URL
Returns
- string
The path to the solver executable or WebSocket URL
Remarks
This static method is used to find the solver for Model.Solve. Usually, there's no need to call this method directly. However, it could be used to check what solver would be used.
The method works as follows:
If
parameters.solveris set, its value is returned (path or URL).If the
OPTALCP_SOLVERenvironment variable is set, then it is used.Finally, if nothing from the above works, the method assumes that
optalcpis in thePATH.
// Check what solver would be used
var solver = Solver.FindSolver();
Console.WriteLine($"Solver: {solver}");
// Or check with custom parameters
var custom = Solver.FindSolver(new Parameters { solver = "/custom/path/optalcp" });
See also:
- Parameters.solver — to specify the solver.
SendSolution(Solution)
Send an external solution to the solver.
public void SendSolution(Solution solution)
Parameters
solutionSolutionThe solution to send. It must be compatible with the model; otherwise, an error is raised
Remarks
This function can be used to send an external solution to the solver, e.g. found by another solver, a heuristic, or a user. The solver will take advantage of the solution to speed up the search: it will search only for better solutions (if it is a minimization or maximization problem). The solver may try to improve the provided solution by Large Neighborhood Search.
The solution does not have to be better than the current best solution found by the solver. It is up to the solver whether or not it will use the solution in this case.
Sending a solution to a solver that has already stopped has no effect.
If the function is called before the model is sent to the solver, the solution is queued internally and sent automatically once the model transfer completes. Unless parameter Parameters.logLevel is set to 0, the solver will log a message when it receives the solution.
Solve(Model, Parameters?, Solution?)
Solves a model with the specified parameters.
public Task<SolveResult> Solve(Model model, Parameters? @params = null, Solution? warmStart = null)
Parameters
modelModelThe model to solve
paramsParametersThe parameters for the solver
warmStartSolutionAn initial solution to start the solver with
Returns
- Task<SolveResult>
The result of the solve when finished.
Remarks
The solving process runs asynchronously. Use await to wait for the
solver to finish. During the solve, the solver emits events that can be
intercepted using callback properties like Solver.OnSolution,
Solver.OnLog, etc.
Communication with the solver subprocess happens through the event loop.
The user code must yield control (using await or waiting for an event)
for the solver to receive commands and send updates.
Warm start and external solutions
If the warmStart parameter is specified, the solver will start with the
given solution. The solution must be compatible with the model; otherwise
an error is raised. The solver will take advantage of the
solution to speed up the search: it will search only for better solutions
(if it is a minimization or maximization problem). The solver may try to
improve the provided solution by Large Neighborhood Search.
There are two ways to pass a solution to the solver: using warmStart
parameter and using function Solver.SendSolution.
The difference is that warmStart is guaranteed to be used by the solver
before the solve starts. On the other hand, sendSolution can be called
at any time during the solve.
Parameter Parameters.lnsUseWarmStartOnly controls whether the solver should only use the warm start solution (and not search for other initial solutions). If no warm start is provided, the solver searches for its own initial solution as usual.
Stop(string)
Requests the solver to stop as soon as possible.
public void Stop(string reason = "User requested")
Parameters
reasonstringThe reason why to stop. The reason will appear in the log
Remarks
This method only initiates the stop; it returns immediately without waiting
for the solver to actually stop. Because the solver runs asynchronously,
more events (such as additional solutions or log messages) may still arrive
after stop is called. The solver will stop as soon as possible, but the
exact timing depends on what it is currently doing.
Requesting a stop on a solver that has already stopped has no effect.
Example:
var solver = new Solver();
bool timerStarted = false;
solver.OnSolution = (SolutionEvent e) => {
// We just found a solution. Set a timeout if there isn't any.
if (!timerStarted) {
timerStarted = true;
// Register a function to be called after 60 seconds:
Task.Delay(60000).ContinueWith(_ => {
Console.WriteLine("Requesting solver to stop");
solver.Stop("Stop because I said so!");
});
}
return Task.CompletedTask;
};
var result = await solver.Solve(model, new Parameters { timeLimit = 300 });
ToJS(Model, Parameters?, Solution?)
Converts a model to JavaScript code (async version).
public Task<string> ToJS(Model model, Parameters? parameters = null, Solution? warmStart = null)
Parameters
modelModelThe model to convert
parametersParametersOptional solver parameters (included in generated code)
warmStartSolutionOptional initial solution to include
Returns
Remarks
Async version of Model.ToJS. This method communicates with the solver process to generate the JavaScript output.
The output is human-readable, executable with Node.js, and can be stored in a file.
This feature is experimental and the result is not guaranteed to be valid in all cases.
var model = new Model();
var x = model.IntervalVar(length: 10, name: "task_x");
model.Minimize(x.End());
var solver = new Solver();
var jsCode = await solver.ToJS(model);
Console.WriteLine(jsCode);
See also:
- Model.ToJS — for synchronous usage.
- Solver.ToText — for text format export.
ToText(Model, Parameters?, Solution?)
Converts a model to text format (async version).
public Task<string> ToText(Model model, Parameters? parameters = null, Solution? warmStart = null)
Parameters
modelModelThe model to convert
parametersParametersOptional solver parameters
warmStartSolutionOptional initial solution to include
Returns
Remarks
Async version of Model.ToText. This method communicates with the solver process to generate the text output.
The output is human-readable and similar to the IBM CP Optimizer file format.
var model = new Model();
var x = model.IntervalVar(length: 10, name: "task_x");
model.Minimize(x.End());
var solver = new Solver();
var text = await solver.ToText(model);
Console.WriteLine(text);
See also:
- Model.ToText — for synchronous usage.
- Solver.ToJS — for JavaScript export.