Table of Contents

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:

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:

In the following example, we create a solver with a solution callback. We print the objective value and the value of interval variable 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:

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:

In the following example, we create a solver with a solution callback. We print the objective value and the value of interval variable 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

Func<string, Task>

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:

OnLog

Callback for log messages from the solver.

public Func<string, Task>? OnLog { get; set; }

Property Value

Func<string, Task>

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:

OnObjectiveBound

Callback for objective bound events from the solver.

public Func<ObjectiveBoundEntry, Task>? OnObjectiveBound { get; set; }

Property Value

Func<ObjectiveBoundEntry, Task>

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:
    • value (double): The new bound value.
    • solveTime (double): Time when the bound was proved (seconds since solve start).

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:

OnSolution

Callback for solution events from the solver.

public Func<SolutionEvent, Task>? OnSolution { get; set; }

Property Value

Func<SolutionEvent, Task>

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:
    • solution (Solution): The solution object with variable values.
    • solveTime (double): Time when the solution was found (seconds since solve start).
    • valid (bool?): Solution verification result if enabled, null otherwise.

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:

OnWarning

Callback for warning messages from the solver.

public Func<string, Task>? OnWarning { get; set; }

Property Value

Func<string, Task>

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:

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

parameters Parameters

Parameters 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.solver is set, its value is returned (path or URL).

  • If the OPTALCP_SOLVER environment variable is set, then it is used.

  • Finally, if nothing from the above works, the method assumes that optalcp is in the PATH.

// 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:

SendSolution(Solution)

Send an external solution to the solver.

public void SendSolution(Solution solution)

Parameters

solution Solution

The 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

model Model

The model to solve

params Parameters

The parameters for the solver

warmStart Solution

An 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

reason string

The 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:

In the following example, we issue a stop command 1 minute after the first solution is found.
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

model Model

The model to convert

parameters Parameters

Optional solver parameters (included in generated code)

warmStart Solution

Optional initial solution to include

Returns

Task<string>

JavaScript code representing the model.

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:

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

model Model

The model to convert

parameters Parameters

Optional solver parameters

warmStart Solution

Optional initial solution to include

Returns

Task<string>

Text representation of the model.

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: