Running a search
FznsoStatus fznso_NAME_solver_run( FznsoSolver *solver, FznsoModelRef model, void *context, void (*on_solution)(void *context, FznsoSolutionRef solution), void (*on_message)(void *context, FznsoStr scope, FznsoValueRef value), bool (*should_stop)(void *context));One call performs one search.
context is passed back to every callback untouched; the solver never interprets it.
model is borrowed for the duration of the call and is read-only throughout.
Everything the solver hands out is borrowed the same way; see Lifetimes & ownership.
Solutions
Section titled “Solutions”on_solution is called once per solution found.
It is not optional: a run with nowhere to report solutions would be pointless.
The FznsoSolutionRef is valid only for the duration of the callback; copy out anything you want to keep.
Which solutions are reported depends on the model’s objective and on the all_solutions and intermediate options:
- with no objective, every solution found is reported, until the search is complete or stopped;
- with an objective and
intermediateset, each improving solution is reported as it is found; - with an objective and
intermediateunset, only the final (optimal) solution is reported; - with
all_solutionsset, the search does not stop at the first optimal solution.
Stopping early
Section titled “Stopping early”If you want to set a time limit, set the time_limit option.
A solver told its budget up front can plan around it: sizing restart schedules, deciding when to stop diversifying and dive for a feasible solution, budgeting time between portfolio strategies.
For other scenarios, should_stop is a predicate the solver polls; a true answer means give up as soon as convenient and return FznsoIncomplete.
Use should_stop for what a deadline cannot express: a user pressing cancel, “five solutions are enough”, or a condition that depends on the answers received so far.
A solver is expected to poll it:
- once before starting, so that an application which has already asked to stop gets no search at all;
- at least once after each
on_solutionreturns, which is what makes “stop after this solution” reliable; - otherwise as often as is reasonable, so that a cancel is acted on promptly rather than at the end of the search.
should_stop may be null, which tells the solver the application will never ask to stop, so it can drop the check from its search loop entirely.
It is also the one callback an application must make thread-safe.
Diagnostics
Section titled “Diagnostics”While solving, a solver can report non-fatal diagnostics through on_message.
Each message carries a scope naming its kind and a value carrying its payload.
Scopes are dot-separated from least to most specific, so an application can filter on a prefix without knowing the full set; the common ones are in the registry.
Unlike options, constraints and statistics, scopes are deliberately not declared up front. Messages are pushed to the application rather than retrieved by name, so it never needs to know a scope in advance to receive one, and can always fall back to reporting an unrecognised scope verbatim.
on_message may be null, meaning nobody is listening.
A solver should then skip building diagnostics rather than formatting them and handing them to a sink that discards them. That is the point of making it nullable.
Status
Section titled “Status”| Status | Meaning |
|---|---|
FznsoComplete | The solver explored the full search space and yielded all relevant solutions. If any solution exists, one was reported; if none was reported, none exists. |
FznsoIncomplete | The search stopped early — a time limit, a should_stop, or any other termination condition. Additional or better solutions may exist. |
FznsoError | The solver could not continue. Read the reason with fznso_<name>_solver_read_error. |
FznsoComplete with an objective and no should_stop means the last solution reported is optimal.
FznsoIncomplete means only that the solutions reported are valid, not that they are the best.