Skip to main content

Class: Solver

Remarks

The solver provides asynchronous communication with the solver.

Unlike function 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.

Solver inherits from EventEmitter class from Node.js. You can subscribe to events using the standard Node.js on method. The following events are emitted:

  • error: Emits an instance of Error (standard Node.js class) when an error occurs.
  • warning: Emits a string for every issued warning.
  • log: Emits a string for every log message.
  • trace: Emits a string for every trace message.
  • solution: Emits a SolutionEvent when a solution is found.
  • objectiveBound: Emits a ObjectiveBoundEntry when a new lower bound is proved.
  • summary: Emits SolveSummary at the end of the solve.
  • close: Emits void. It is always the last event emitted.

The solver output (log, trace, and warnings) is printed on the console by default. It can be redirected to a file or a stream, or suppressed completely using the Parameters.printLog parameter.

Example

In the following example, we run a solver asynchronously. We subscribe to the solution event to print the objective value of the solution and the value of interval variable x. After finding the first solution, we request the solver to stop. We also subscribe to the summary event to print statistics about the solve.

import * as CP from '@scheduleopt/optalcp';

...
let model = new CP.Model();
let x = model.intervalVar({ ... });
...

// Create a new solver:
let solver = new CP.Solver;

// Subscribe to "solution" events:
solver.on("solution", (msg: CP.SolutionEvent) => {
// Get Solution from SolutionEvent:
let solution = msg.solution;
console.log("At time " + msg.solveTime + ", solution found with objective " + solution.getObjective());
// Print value of interval variable x:
if (solution.isAbsent(x))
console.log(" Interval variable x is absent");
else
console.log(" Interval variable x: [" + solution.getStart(x) + " -- " + solution.getEnd(x) + "]");
// Request the solver to stop as soon as possible
// (the message is only informative):
solver.stop("We are happy with the first solution found.");
});

// Subscribe to "summary" events:
solver.on("summary", (msg: CP.SolveSummary) => {
// Print the statistics. The statistics doesn't exist if an error occurred.
console.log("Total duration of solve: " + msg.duration);
console.log("Number of branches: " + msg.nbBranches);
});

try {
await solver.solve(model, { timeLimit: 60 });
console.log("All done");
} catch (e) {
// We did not subscribe to "error" events. So an exception is thrown in
// case of an error.
console.error("Exception caught: ", (e as Error).message);
}

Constructors

new Solver()

new Solver(): Solver

Returns

Solver

Solving

findSolver()

static findSolver(parameters: Parameters): string

Find path to the optalcp binary.

Parameters

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

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

  • If npm package @scheduleopt/optalcp-bin is installed then it is searched for the optalcp executable.

  • If npm package @scheduleopt/optalcp-bin-academic is installed then it is searched too.

  • If npm package optalcp-bin-preview is installed then it is searched too.

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

import * as cp from '@scheduleopt/optalcp';

// Check what solver would be used
const solver = cp.Solver.findSolver();
console.log("Solver:", solver);

// Or check with custom parameters
const custom = cp.Solver.findSolver({ solver: '/custom/path/optalcp' });

See

Parameters.solver to specify the solver.

Other

onError

Get Signature

get onError(): undefined | (msg: string) => void | (msg: string) => Promise<void>

Callback for error messages from the solver.

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.

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

// Synchronous callback
solver.onError = (msg) => console.error(`ERROR: ${msg}`);

// Asynchronous callback
solver.onError = async (msg: string) => {
await asyncLogger.error(msg);
};

An error message indicates that the solve is closing. However, other messages (log, warning, solution) may still arrive after the error.

See
Returns

undefined | (msg: string) => void | (msg: string) => Promise<void>

Set Signature

set onError(handler: undefined | (msg: string) => void | (msg: string) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (msg: string) => void | (msg: string) => Promise<void>
Returns

void


onLog

Get Signature

get onLog(): undefined | (msg: string) => void | (msg: string) => Promise<void>

Callback for log messages from the solver.

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.

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

// Synchronous callback
solver.onLog = (msg) => myLogger.info(msg);

// Or with a named function
solver.onLog = (msg: string) => {
console.log(`LOG: ${msg}`);
};

// Asynchronous callback
solver.onLog = async (msg: string) => {
await asyncLogger.info(msg);
};

The amount of log messages and their periodicity can be controlled by Parameters.logLevel and Parameters.logPeriod.

See
Returns

undefined | (msg: string) => void | (msg: string) => Promise<void>

Set Signature

set onLog(handler: undefined | (msg: string) => void | (msg: string) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (msg: string) => void | (msg: string) => Promise<void>
Returns

void


onObjectiveBound

Get Signature

get onObjectiveBound(): undefined | (event: ObjectiveBoundEntry) => void | (event: ObjectiveBoundEntry) => Promise<void>

Callback for objective bound events from the solver.

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

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

// Synchronous callback
solver.onObjectiveBound = (event) => console.log(`Bound: ${event.value}`);

// Asynchronous callback
solver.onObjectiveBound = async (event: CP.ObjectiveBoundEntry) => {
await updateDashboard(event.value);
};
See
Returns

undefined | (event: ObjectiveBoundEntry) => void | (event: ObjectiveBoundEntry) => Promise<void>

Set Signature

set onObjectiveBound(handler: undefined | (event: ObjectiveBoundEntry) => void | (event: ObjectiveBoundEntry) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (event: ObjectiveBoundEntry) => void | (event: ObjectiveBoundEntry) => Promise<void>
Returns

void


onSolution

Get Signature

get onSolution(): undefined | (event: SolutionEvent) => void | (event: SolutionEvent) => Promise<void>

Callback for solution events from the solver.

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 (number): Time when the solution was found (seconds since solve start).
    • valid (boolean | undefined): Solution verification result if enabled, undefined otherwise.

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

solver.onSolution = (event: CP.SolutionEvent) => {
const sol = event.solution;
const time = event.solveTime;
console.log(`Solution found at ${time.toFixed(2)}s, objective=${sol.getObjective()}`);
};

// Asynchronous callback
solver.onSolution = async (event: CP.SolutionEvent) => {
const sol = event.solution;
await saveToDatabase(sol);
};
See
Returns

undefined | (event: SolutionEvent) => void | (event: SolutionEvent) => Promise<void>

Set Signature

set onSolution(handler: undefined | (event: SolutionEvent) => void | (event: SolutionEvent) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (event: SolutionEvent) => void | (event: SolutionEvent) => Promise<void>
Returns

void


onSummary

Get Signature

get onSummary(): undefined | (summary: SolveSummary) => void | (summary: SolveSummary) => Promise<void>

Callback for solve completion event.

Remarks

The callback is called once when the solve completes, providing final statistics. The default is no callback.

The callback receives one argument:

  • summary (SolveSummary): Solve statistics with properties including:
    • nbSolutions (number): Number of solutions found.
    • duration (number): Total solve time in seconds.
    • nbBranches (number): Number of branches explored.
    • objective (number | undefined): Best objective value, or undefined if no solution found.
    • Plus many other statistics (see SolveSummary).

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

solver.onSummary = (summary: CP.SolveSummary) => {
console.log(`Solve completed: ${summary.nbSolutions} solutions`);
console.log(`Time: ${summary.duration.toFixed(2)}s`);
if (summary.objective !== undefined)
console.log(`Best objective: ${summary.objective}`);
};

// Asynchronous callback
solver.onSummary = async (summary: CP.SolveSummary) => {
await saveStatsToDb(summary);
};
See

SolveSummary for the complete list of statistics.

Returns

undefined | (summary: SolveSummary) => void | (summary: SolveSummary) => Promise<void>

Set Signature

set onSummary(handler: undefined | (summary: SolveSummary) => void | (summary: SolveSummary) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (summary: SolveSummary) => void | (summary: SolveSummary) => Promise<void>
Returns

void


onWarning

Get Signature

get onWarning(): undefined | (msg: string) => void | (msg: string) => Promise<void>

Callback for warning messages from the solver.

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.

The callback can be either synchronous or asynchronous. If the callback raises 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.

const solver = new CP.Solver();

// Synchronous callback
solver.onWarning = (msg) => console.warn(msg);

// Asynchronous callback
solver.onWarning = async (msg: string) => {
await asyncLogger.warning(msg);
};

The amount of warning messages can be configured using Parameters.warningLevel.

See
Returns

undefined | (msg: string) => void | (msg: string) => Promise<void>

Set Signature

set onWarning(handler: undefined | (msg: string) => void | (msg: string) => Promise<void>): void

Parameters
ParameterType
handlerundefined | (msg: string) => void | (msg: string) => Promise<void>
Returns

void


sendSolution()

sendSolution(solution: Solution): Promise<void>

Send an external solution to the solver.

Parameters

ParameterTypeDescription
solutionSolutionThe solution to send. It must be compatible with the model; otherwise, an error is raised

Returns

Promise<void>

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.

The solution is sent to the solver asynchronously. Unless parameter Parameters.logLevel is set to 0, the solver will log a message when it receives the solution.


solve()

solve(model: Model, params?: Parameters, warmStart?: Solution): Promise<SolveResult>

Solves a model with the specified parameters.

Parameters

ParameterTypeDescription
modelModelThe model to solve
params?ParametersThe parameters for the solver
warmStart?SolutionAn initial solution to start the solver with

Returns

Promise<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()

stop(reason: string): Promise<void>

Requests the solver to stop as soon as possible.

Parameters

ParameterTypeDescription
reasonstringThe reason why to stop. The reason will appear in the log

Returns

Promise<void>

Remarks

This method only initiates the stop; it returns immediately without waiting for the solver to actually stop. The solver will stop as soon as possible and will send a summary event. However, other events may be sent before the summary event (e.g., another solution found or a log message).

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.

let solver = new CP.Solver;
timerStarted = false;
solver.on('solution', (_: SolutionEvent) => {
// 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:
setTimeout(() => {
console.log("Requesting solver to stop");
solver.stop("Stop because I said so!");
}, 60); // The timeout is 60 seconds
}
});
let result = await solver.solve(model, { timeLimit: 300 });