Skip to content

Rust bindings

The fznso crate covers both sides: Library loads a solver and drives it, and fznso_export exposes one you have written.

See Using a solver and Implementing a solver for the walkthroughs.

46 items

macro_rules! fznso_exportsource

Generate all required C ABI functions for a Solver type.

Invoke this macro once in the root of a cdylib crate to expose the solver as a dynamically loaded library loadable by Library:

use fznso::{Model, Solution, Solver, SolverType, Status, Value};
struct MySolution;
impl Solution for MySolution {
fn value(&self, _: usize) -> Value<'_> { todo!() }
fn statistic(&self, _: &str) -> Value<'_> { todo!() }
}
struct MySolver;
// What an instance can do.
impl Solver for MySolver {
type Solution<'s> = MySolution where MySolver: 's;
fn option_get(&self, _: &str) -> Value<'_> { todo!() }
fn option_set(&mut self, _: &str, _: Value<'_>) -> Result<(), String> { todo!() }
fn run<M, F, G, H>(
&mut self,
_model: &M,
_on_solution: &mut F,
// `None` means nobody is listening, so skip building diagnostics.
_on_message: Option<&mut G>,
// `None` means the caller never cancels, so skip polling.
_should_stop: Option<&H>,
) -> Status
where
M: Model + Sync,
F: for<'s> FnMut(&'s MySolution) + Send,
G: FnMut(&str, Value<'_>) + Send,
H: Fn() -> bool + Send + Sync,
{ todo!() }
}
// What the type declares. Every list defaults to empty, so a solver that
// supports nothing yet need only provide `new`.
impl SolverType for MySolver {
fn new() -> Self { MySolver }
}
fznso::fznso_export!(MySolver, "mysolver");
fn main() {}

The second argument is the library’s name: every exported symbol is named fznso_<name>_... (for example fznso_mysolver_solver_run), so that several solvers can be linked into one binary without their entry points colliding.

It must match the base name of the built library filemysolver for libmysolver.so / libmysolver.2.1.dylib / mysolver.dll — so that Library can recover it when loading. The rules a name must satisfy, and where the version goes, are specified at https://fznso.minizinc.dev/spec/naming-and-versioning/.

A failing option_set or a Status::Error from run stores its message on the solver instance, and the generated fznso_<name>_solver_read_error returns it, so the caller sees the reason for the failure.

trait Annotationsource

Helper trait for types that can be used as annotations in a Model.

Implement this trait and use ann_ref to convert values into the AnnotationRef type returned by Model annotation methods.

methodfn argument(&self, index: usize) -> Value<'_>

The annotation argument at the given zero-based index.

methodfn argument_len(&self) -> usize

The number of arguments this annotation carries.

methodfn ident(&self) -> &str

The annotation’s identifier string (e.g. "output_var").

trait Modelsource

Read-only view of a constraint/optimization problem model.

The model is organised into layers that correspond to the incremental solving concept in the FZnSO protocol. Solvers receive a &dyn Model (or a concrete implementation via ModelRefAdapter(adapter::ModelRefAdapter)) and may query decisions, constraints, and the objective through this trait.

Rather than calling the low-level index/len primitives directly, prefer the provided methods, which return view types with richer APIs and iterators:

use fznso::Model;
fn print_model<M: Model>(model: &M) {
for layer in model.layers() {
for dec in layer.decisions() {
println!("{:?} domain={:?}", dec.name(), dec.domain().kind());
}
for con in layer.constraints() {
println!("{} ({})", con.ident(), con.argument_len());
}
}
let obj = model.objective();
if !obj.is_satisfy() {
println!("objective: {} {:?}", obj.ident(), obj.arg().kind());
}
}
methodfn constraint_annotation(&self, con: ConstraintIdx, index: usize) -> AnnotationRef<'_>

The index-th annotation on the given constraint.

methodfn constraint_annotation_len(&self, con: ConstraintIdx) -> usize

Number of annotations on the given constraint.

methodfn constraint_argument(&self, con: ConstraintIdx, index: usize) -> Value<'_>

The index-th argument of the given constraint.

methodfn constraint_argument_len(&self, con: ConstraintIdx) -> usize

Number of arguments of the given constraint.

methodfn constraint_defines(&self, con: ConstraintIdx) -> Option<DecisionIdx>

The decision variable defined by this constraint, if any.

methodfn constraint_ident(&self, con: ConstraintIdx) -> &str

The identifier of the given constraint (e.g. "int_le").

methodfn constraint_layer_end(&self, layer: usize) -> usize

One-past-the-end global constraint index for constraints in layers 0..=layer.

methodfn constraint_len(&self) -> usize

Total number of constraints across all layers.

methodfn decision_annotation(&self, decision: DecisionIdx, index: usize) -> AnnotationRef<'_>

The index-th annotation on the given decision variable.

methodfn decision_annotation_len(&self, decision: DecisionIdx) -> usize

Number of annotations on the given decision variable.

methodfn decision_defined(&self, decision: DecisionIdx) -> bool

Whether the given decision variable is defined by some constraint.

methodfn decision_in_solution(&self, decision: DecisionIdx) -> bool

Whether a solution may be asked for the given decision variable’s value.

A solver must give every such variable a value in every solution it reports, and is free to leave the others open. Having a name does not make a variable needed: names exist only for debugging.

methodfn decision_domain(&self, decision: DecisionIdx) -> Value<'_>

The domain of the given decision variable.

methodfn decision_layer_end(&self, layer: usize) -> usize

One-past-the-end global decision index for variables in layers 0..=layer.

methodfn decision_len(&self) -> usize

Total number of decision variables across all layers.

methodfn decision_name(&self, decision: DecisionIdx) -> Option<&str>

The optional name of the given decision variable.

methodfn decision_type(&self, decision: DecisionIdx) -> FznsoType

The type of the given decision variable.

What the variable is, as opposed to Model::decision_domain, which says which values it may take. list_of is always clear and decision always set, so only base, set_of and opt carry information.

methodfn layer_len(&self) -> usize

Total number of layers currently in the model.

methodfn layer_permanent(&self) -> usize

Number of layers that are permanently committed (will not be popped).

methodfn layer_redundant_index(&self, index: usize) -> usize

The global layer index of the index-th redundant permanent layer.

methodfn layer_redundant_len(&self) -> usize

Number of permanent layers marked as redundant.

methodfn layer_unchanged(&self) -> usize

Number of layers whose content is unchanged since the last solver run.

methodfn objective_annotation(&self, index: usize) -> AnnotationRef<'_>

The index-th annotation on the objective.

methodfn objective_annotation_len(&self) -> usize

Number of annotations on the objective.

methodfn objective_arg(&self) -> Value<'_>

The argument of the objective function.

methodfn objective_ident(&self) -> &str

Identifier of the objective function (e.g. "minimize"), or "" for satisfaction.

methodfn constraint(&self, idx: ConstraintIdx) -> ConstraintView<'_, Self>

A view into the constraint at idx.

methodfn constraints(&self) -> impl Iterator

Iterator over all constraints in the model, ordered by layer.

methodfn decision(&self, idx: DecisionIdx) -> DecisionView<'_, Self>

A view into the decision variable at idx.

methodfn decisions(&self) -> impl Iterator

Iterator over all decision variables in the model, ordered by layer.

methodfn layer(&self, idx: usize) -> LayerView<'_, Self>

A view into the layer at idx.

methodfn layers(&self) -> impl Iterator

Iterator over all layers, from the oldest (base) to the newest.

methodfn objective(&self) -> ObjectiveView<'_, Self>

A view into the model’s objective.

trait Solutionsource

Trait implemented by solution types produced by a Solver(crate::Solver).

Implement this alongside Solver(crate::Solver) to describe the values and statistics emitted for each solution found.

methodfn statistic(&self, name: &str) -> Value<'_>

Retrieve a named statistic.

methodfn value(&self, decision_idx: usize) -> Value<'_>

Retrieve the value assigned to a decision variable by its index.

trait Solversource

The unified solver interface.

Implement this trait to write a Rust-native solver and then use fznso_export(crate::fznso_export) to expose it as a dynamically loaded library loadable by Library.

See the fznso_export(crate::fznso_export) documentation for a complete worked example.

assoctype Solution

The concrete solution type emitted by this solver.

The lifetime parameter 's reflects that a solution may borrow from the solver or the current search state, lasting only for the duration of a single run(Self::run) callback invocation.

methodfn option_get(&self, name: &str) -> Value<'_>

Get the current value of a named solver option.

methodfn option_set(&mut self, name: &str, value: Value<'_>) -> Result<(), String>

Set the value of a named solver option. Returns Err with a human-readable message if the option is unknown or the value is invalid.

methodfn statistic(&self, name: &str) -> Value<'_>

The current value of a named solver-level statistic.

Only statistics this solver declares with the solver flag set in SolverType::STATISTIC_LIST are readable here; statistics carrying only the solution flag are read from a Solution instead. An unknown name yields the absent value, which is what the default implementation returns — a solver with no solver-level statistics need not override it.

methodfn run<M, F, G, H>(&mut self, model: &M, on_solution: &mut F, on_message: Option<&mut G>, should_stop: Option<&H>) -> Status

Run the solver against the given model, invoking on_solution for each solution found. Returns the completion Status.

on_message receives non-fatal diagnostics the solver wants to report: warnings, progress, search information. scope names the kind of message and is dot-separated so that consumers can filter on a prefix (warn, warn.domain, progress.objective); see the README for the scopes solvers are encouraged to use. Failures are not reported here — those surface as Status::Error.

A solver may search on several threads, so the contract is defined in terms of them:

  • Callbacks are invoked serially. The solver must never call on_solution and on_message concurrently — not with themselves, and not with each other. It may call them from any thread (for example the worker that found a solution), as long as it synchronises so that each call happens-before the next. That is why the callbacks need only be Send, not Sync: an ordinary closure that is not safe to call concurrently is enough. For an optimising solver this falls out of the incumbent update it already serialises, and it keeps solutions in the improving order that intermediate/progress.bound rely on.
  • The model may be queried concurrently. The model is a read-only view for the duration of the run, so the solver may query it from many threads at once (the search hot path). That is why the model must be Sync.

A consumer that wants to handle solutions in parallel does so behind a serial callback (push to a channel, fan out downstream); it never has to make the callback itself thread-safe.

should_stop is the exception: the solver may poll it from several threads at once, so it is Fn + Sync rather than FnMut. Reading an atomic — the usual implementation — already satisfies that.

on_message and should_stop are both Options, and None is a signal a solver should act on rather than paper over. Absent ones are spelled ignore_messages() and dont_interrupt(), which supply the concrete type a bare None cannot infer:

  • on_message: None means nobody is listening to diagnostics, so a solver should skip building them — not format a message and hand it to a sink that discards it.
  • should_stop: None means the caller will never ask to stop, so a solver need not poll at all.

A caller that wants only a deadline should set the time_limit option rather than polling a clock through should_stop: a solver told its deadline up front can plan its search around it.

on_solution has no None case — reporting solutions is what a run is for.

should_stop is how a caller abandons a run it has already started, and the only way to stop one: return true and the solver gives up as soon as it can, yielding Status::Incomplete. It covers both “one solution is enough” — trip it from inside on_solution — and cancelling a long search that has produced nothing, by tripping it from another thread.

An implementation must poll it before starting and at least once after each on_solution returns, and should otherwise poll as often as is reasonable. The answer is monotone, so it may be cached once true.

trait SolverTypesource

What a solver implementation declares about itself, independently of any particular instance.

Solver describes what an individual solver object can do; this trait describes what the solver type supports, which is fixed at compile time. The C entry points for these (fznso_constraint_list and friends) take no solver instance, which is why they live here rather than on Solver, and why fznso_export(crate::fznso_export) needs only the type that implements this trait and the library’s name — fznso_export!(MySolver, "mysolver"). See that macro’s documentation for a worked example.

A DynSolver deliberately does not implement this trait: a solver loaded at runtime cannot answer these statically, so it exposes the same information through inherent methods that ask its Library instead.

Every list defaults to empty, so an implementation need only declare the capabilities it actually has.

assoctype CONSTRAINT_LIST

The constraints this solver accepts, exported as fznso_constraint_list.

assoctype DECISION_LIST

The decision-variable types this solver accepts, exported as fznso_decision_list.

assoctype OBJECTIVE_LIST

The objective strategies this solver supports, exported as fznso_objective_list.

assoctype OPTION_LIST

The options this solver accepts, exported as fznso_option_list.

assoctype STATISTIC_LIST

The statistics this solver reports, exported as fznso_statistic_list.

methodfn new() -> Self

Create a new solver instance, exported as fznso_solver_create.

trait ValueExt<'a>source

Adds view(ValueExt::view) to Value.

Value is an alias for the ABI’s own handle type, so this cannot be an inherent method. Import the trait to use it.

methodfn view(&self) -> ValueView<'a>

Resolve this value into its payload.

See ValueView for the full list of shapes and an example.

struct ConstraintView<'m, M>source

A view into a single constraint within a Model.

Obtained via Model::constraint or by iterating Model::constraints.

methodfn annotation_len(&self) -> usize

Number of annotations on this constraint.

methodfn annotations(&self) -> impl Iterator + 'm

Iterator over all annotations on this constraint.

methodfn argument(&self, i: usize) -> Value<'m>

The i-th argument of this constraint.

methodfn argument_len(&self) -> usize

Number of arguments of this constraint.

methodfn arguments(&self) -> impl Iterator + 'm

Iterator over all arguments of this constraint.

methodfn defines(&self) -> Option<DecisionIdx>

The decision variable functionally defined by this constraint, if any.

methodfn ident(&self) -> &'m str

The identifier of this constraint (e.g. "int_le").

methodfn idx(&self) -> ConstraintIdx

The global index of this constraint.

struct DecisionView<'m, M>source

A view into a single decision variable within a Model.

Obtained via Model::decision or by iterating Model::decisions.

methodfn annotation_len(&self) -> usize

Number of annotations on this decision variable.

methodfn annotations(&self) -> impl Iterator + 'm

Iterator over all annotations on this decision variable.

methodfn defined(&self) -> bool

Whether this decision variable is functionally defined by some constraint.

methodfn domain(&self) -> Value<'m>

The domain of this decision variable.

methodfn idx(&self) -> DecisionIdx

The global index of this decision variable.

methodfn in_solution(&self) -> bool

Whether a solution may be asked for this decision variable’s value.

methodfn name(&self) -> Option<&'m str>

The optional name of this decision variable.

methodfn ty(&self) -> FznsoType

The type of this decision variable.

struct Discoveredsource

A solver library found on the search path, reported without loading it.

fieldname: String

The solver’s name, derived from the file name.

fieldversion: String

The version, parsed from the file name, or empty if the file carries none. Compared component-wise, so 10 is newer than 9.

fieldpath: std::path::PathBuf

The full path to the library.

struct DynSolution<'a>source

A solution emitted by a dynamically-loaded solver (DynSolver(crate::DynSolver)).

Implements Solution and provides direct access to the inner FznsoSolutionRef.

struct DynSolversource

A solver instance from a dynamically loaded library, via Library.

Created by Library::create_solver.

methodfn constraint_types(&self) -> ConstraintList<'_>

The constraints the loaded solver accepts.

methodfn decision_types(&self) -> TypeList<'_>

The decision-variable types the loaded solver accepts.

methodfn objective_list(&self) -> ObjectiveList<'_>

The objective strategies the loaded solver supports.

methodfn option_list(&self) -> OptionList<'_>

The options the loaded solver accepts.

methodfn statistic_list(&self) -> StatisticList<'_>

The statistics the loaded solver reports.

struct FloatRanges<'a>source

The ranges of a float set, borrowed from the value that produced them.

methodfn get(&self, index: usize) -> (f64, f64)

The inclusive (min, max) range at index.

Panics if index is not less than len(Self::len).

methodfn is_empty(&self) -> bool

Whether the set is empty.

methodfn iter(&self) -> impl Iterator + 'a

Iterator over the inclusive (min, max) ranges, in order.

methodfn len(&self) -> usize

The number of ranges in the set.

struct IntRanges<'a>source

The ranges of an integer set, borrowed from the value that produced them.

methodfn get(&self, index: usize) -> (i64, i64)

The inclusive (min, max) range at index.

Panics if index is not less than len(Self::len).

methodfn is_empty(&self) -> bool

Whether the set is empty.

methodfn iter(&self) -> impl Iterator + 'a

Iterator over the inclusive (min, max) ranges, in order.

methodfn len(&self) -> usize

The number of ranges in the set.

struct LayeredModelsource

An in-memory, multi-layer implementation of Model.

Layers correspond to the incremental-solving concept in the FZnSO protocol: decisions and constraints are added layer-by-layer. Layers can be pushed, popped, marked permanent, and marked redundant.

The model is created with one permanent base layer already in place. Additional layers can be pushed and popped on top of it; only the base layer (and any layers subsequently committed via mark_permanent) can never be popped.

The objective is global (not per-layer) and can be set at any time.

use fznso::{LayeredModel, OwnedValue, Type, TypeBase};
use rangelist::RangeList;
let mut m = LayeredModel::default();
let d = m.add_decision(
Type::new(TypeBase::FznsoTypeBaseInt).decision(true),
OwnedValue::IntSet(RangeList::from(-10i64..=10)),
Some("x".into()),
false,
true,
vec![],
);
m.add_constraint("int_le", vec![OwnedValue::Int(-10), OwnedValue::Decision(d)], None, vec![]);
m.set_objective(Some("minimize"), OwnedValue::Decision(d), vec![]);

mark_permanent: LayeredModel::mark_permanent

methodfn add_constraint(&mut self, ident: impl Into, arguments: Vec<OwnedValue>, defines: Option<DecisionIdx>, annotations: Vec<OwnedAnnotation>) -> ConstraintIdx

Add a constraint to the current (top) layer.

Panics if there are no layers.

methodfn add_decision(&mut self, ty: FznsoType, domain: OwnedValue, name: Option<String>, defined: bool, in_solution: bool, annotations: Vec<OwnedAnnotation>) -> DecisionIdx

Add a decision variable to the current (top) layer.

ty says what the variable is (see decision_type(Model::decision_type)); domain says which values it may take, and may be OwnedValue::Absent for a variable with no explicit domain.

Panics if there are no layers.

methodfn mark_permanent(&mut self)

Mark all current layers as permanent.

methodfn mark_redundant(&mut self, layer: usize)

Mark a permanent layer as redundant.

layer must be a valid permanent layer index (0..permanent).

methodfn pop_layer(&mut self)

Pop the top layer from the model.

Panics if there are no layers, or if the top layer is permanent.

methodfn push_layer(&mut self)

Push a new (empty) layer onto the model.

methodfn set_objective(&mut self, ident: Option<impl Into>, arg: OwnedValue, annotations: Vec<OwnedAnnotation>)

Set the model’s objective.

Pass None for ident to clear the objective (satisfy-only).

methodfn set_unchanged(&mut self, n: usize)

Set the number of layers considered unchanged since the last solver run.

struct LayerView<'m, M>source

A view into a single layer within a Model.

Obtained via Model::layer or by iterating Model::layers.

methodfn constraint_len(&self) -> usize

Number of constraints in this layer.

methodfn constraints(&self) -> impl Iterator + 'm

Iterator over constraints belonging to this layer.

methodfn decision_len(&self) -> usize

Number of decision variables in this layer.

methodfn decisions(&self) -> impl Iterator + 'm

Iterator over decision variables belonging to this layer.

methodfn idx(&self) -> usize

The index of this layer within the model.

methodfn is_permanent(&self) -> bool

Whether this layer is permanently committed (will never be popped).

methodfn is_redundant(&self) -> bool

Whether this permanent layer has been marked redundant.

methodfn is_unchanged(&self) -> bool

Whether this layer’s content is unchanged since the last solver run.

struct Librarysource

A dynamically loaded FZnSO solver library.

Holds libloading symbols for every required ABI entry point. Normally obtained via Library::new and held behind an Arc so that DynSolver instances can share ownership.

methodfn constraint_types(&self) -> FznsoConstraintList<'_>

Get the list of constraints supported by solvers from this library.

methodfn create_solver(&Arc<Self>) -> DynSolver

Creates a new DynSolver instance from this library.

methodfn decision_types(&self) -> FznsoTypeList<'_>

Get the list of decision-variable types supported by solvers from this library.

methodfn discover() -> Vec<Discovered>

List the solver libraries on the search path, without loading any.

Directories are visited in search_paths(Library::search_paths) order and, within one directory, the highest version of a solver comes first. Nothing is opened, so the ABI version is not checked here — a directory may hold solvers built for another ABI, and find(Library::find) skips those.

methodunsafe fn find(name: &str) -> Result<Arc<Self>, LoadError>

Load a solver by name from the search path, choosing the newest version.

Candidates are tried in discover(Library::discover) order, so the highest version in the earliest directory wins. Directory precedence comes first — a solver placed in $FZNSO_SOLVER_PATH or the per-user directory overrides a system one even if the system one is newer — and the newest version is chosen only among candidates of equal precedence. To pin a version regardless, use find_version(Library::find_version).

A candidate built for a different ABI version is skipped and the search continues, since solvers for several ABI versions can share one directory.

The libraries found on the search path must be valid FZnSO solvers; see Library::new.

methodunsafe fn find_version(name: &str, version: &str) -> Result<Arc<Self>, LoadError>

Load a solver by name and version from the search path.

version selects by whole dotted components, so "6" matches 6.2.1 and "6.2" matches 6.2.1 but not 6.1.0; an empty string matches any version, i.e. behaves like find(Library::find). Among the matches the newest is chosen, in discover(Library::discover) order.

The libraries found on the search path must be valid FZnSO solvers; see Library::new.

methodunsafe fn new<S>(filename: S) -> Result<Arc<Self>, LoadError>

Dynamically load a FZnSO solver library from filename.

A solver’s name is the identifier pasted into its entry points (fznso_<name>_solver_run and friends). It has to satisfy both C and the platform’s library naming at once, so:

  1. It must be a valid C identifier: ASCII letters, digits and underscores, not starting with a digit ([A-Za-z_][A-Za-z0-9_]*). Lowercase is the convention. No -, no ., nothing non-ASCII — those cannot appear in a symbol name.
  2. It must not begin with lib. On Unix the file is lib<name>.so, so a name that itself began with lib could not be told apart from the platform prefix on a platform that has none: libssat would be read back as libssat from liblibssat.so but as ssat from libssat.dll.
  3. The file’s base name must be <name>, optionally prefixed with lib, followed by any version and extension components. Everything from the first . is ignored, so <name> must not contain a ..

The name is recovered from the file name by stripping a leading lib and everything from the first ., so libgecode.6.2.1.so, libgecode.6.2.1.dylib and gecode.6.2.1.dll all load a solver whose entry points are prefixed fznso_gecode_. This is the only supported naming, so the file name and the exported symbols must agree. A file name that yields something unusable is rejected with LoadError::InvalidName rather than failing later as a missing symbol.

The version goes between the name and the extension on every platformlibgecode.6.2.1.so, libgecode.6.2.1.dylib, gecode.6.2.1.dll — rather than following each platform’s native library convention, so that one rule covers all three and Windows is not a special case.

Nothing ever links against a solver: it is opened by path from a directory that is deliberately off the linker search path, so no soname is resolved and ldconfig never sees it. The ELF libfoo.so.MAJOR scheme therefore buys nothing here, and putting the version first is what Python does for extension modules (foo.cpython-312-x86_64-linux-gnu.so) for the same reason. The native form is still parsed, so a distribution-packaged libgecode.so.6 also loads.

Several versions of one solver can sit side by side — libgecode.6.so and libgecode.7.so both name gecode. They export the same fznso_gecode_* symbols, but the library is opened with RTLD_LOCAL (and Windows resolves per module), so more than one can be loaded in a single process without colliding. Which one is used is the caller’s choice: pass the exact path, or select by version with find_version(Library::find_version).

The solver’s fznso_<name>_abi_version is checked first: loading fails with LoadError::AbiMismatch if it does not equal fznso_types::FZNSO_ABI_VERSION, guarding against a solver built against an incompatible version of the ABI.

The library at filename must export valid implementations of all thirteen FZnSO ABI functions under the fznso_<name>_ prefix (abi_version, constraint_list, decision_list, objective_list, option_list, statistic_list, solver_create, solver_free, solver_option_get, solver_option_set, solver_read_error, solver_run, solver_statistic). Behaviour is undefined if any symbol is missing or does not conform to the FZnSO ABI.

methodfn objective_list(&self) -> FznsoObjectiveList<'_>

Get the list of objective strategies supported by solvers from this library.

methodfn option_list(&self) -> FznsoOptionList<'static>

Get the list of options accepted by solvers from this library.

methodfn search_paths() -> Vec<std::path::PathBuf>

The directories searched for solver libraries, in search order.

Solvers live in a directory of their own rather than on the normal library path: a FZnSO shim is usually named after the solver it wraps, so libgecode.so would otherwise collide with real Gecode, and a dedicated directory is what makes discover(Library::discover) possible at all.

The order is

  1. $FZNSO_SOLVER_PATH, split on the platform’s path separator,
  2. the per-user directory ($XDG_DATA_HOME/fznso, ~/Library/Application Support/fznso, or %LOCALAPPDATA%\fznso),
  3. the system directories (/usr/local/lib/fznso, /usr/lib/fznso, or fznso beside the running executable on Windows).
methodfn statistic_list(&self) -> FznsoStatisticList<'_>

Get the list of statistics produced by solvers from this library.

struct ObjectiveView<'m, M>source

A view into the objective of a Model.

Obtained via Model::objective.

methodfn annotation_len(&self) -> usize

Number of annotations on the objective.

methodfn annotations(&self) -> impl Iterator + 'm

Iterator over all annotations on the objective.

methodfn arg(&self) -> Value<'m>

The argument of the objective function.

methodfn ident(&self) -> &'m str

The objective function identifier (e.g. "minimize"), or "" for satisfaction problems.

methodfn is_satisfy(&self) -> bool

Whether this is a satisfaction problem (no objective).

struct OwnedAnnotationsource

An annotation for use with LayeredModel.

methodfn new(ident: impl Into, arguments: Vec<OwnedValue>) -> Self

Create a new annotation with the given identifier (must be valid UTF-8 and contain no interior null bytes) and arguments.

struct ValueList<'a>source

The elements of a list value, borrowed from the value that produced them.

methodfn get(&self, index: usize) -> Value<'a>

The element at index.

Panics if index is not less than len(Self::len).

methodfn is_empty(&self) -> bool

Whether the list is empty.

methodfn iter(&self) -> impl Iterator + 'a

Iterator over the elements, in order.

methodfn len(&self) -> usize

The number of elements in the list.

enum LoadErrorsource

Why loading a solver library through Library::new failed.

variantLibrary

The library could not be opened, or a required symbol was missing.

variantInvalidName

The file name does not yield a usable solver name.

variantNotFound

No solver of the requested name was found on the search path.

variantAbiMismatch

The solver reports an ABI version this library cannot use.

enum OwnedValuesource

An owned value for use with LayeredModel.

Mirrors all variants of FznsoValueKind but owns the contained data.

variantAbsent

The absent / null value.

variantBool

A Boolean value.

variantInt

A 64-bit signed integer value.

variantFloat

A 64-bit IEEE 754 double-precision float value.

variantString

A UTF-8 string value.

variantDecision

A reference to a decision variable by its global index.

variantConstraint

A reference to a constraint by its global index.

variantIntSet

Integer set as a list of inclusive [min, max] ranges.

variantFloatSet

Float set as a list of inclusive [min, max] ranges.

variantList

An ordered list of nested values.

enum Statussource

Status enumeration that represents the outcome of Solver::run.

This is an extended version of fznso_types::FznsoStatus, which does not include the actual error message when an error occurs.

variantComplete

The solver explored the full search space.

variantIncomplete

The solver stopped early (timeout or other termination).

variantError

An error occurred; the string contains the error message.

enum ValueView<'a>source

A Value resolved into its payload.

Obtained from ValueExt::view. Matching on this is the safe alternative to checking the value’s kind and then calling the matching get_* accessor, which panics if the two disagree.

Sets and lists keep borrowing the original value rather than being collected, so viewing a value never allocates.

use fznso::{Value, ValueExt, ValueView};
fn describe(value: &Value<'_>) -> String {
match value.view() {
ValueView::Absent => "absent".to_owned(),
ValueView::Bool(b) => format!("{b}"),
ValueView::Int(i) => format!("{i}"),
ValueView::Float(f) => format!("{f}"),
ValueView::Str(s) => s.to_owned(),
ValueView::Decision(d) => format!("x{}", d.0),
ValueView::Constraint(c) => format!("c{}", c.0),
ValueView::IntSet(set) => format!("{:?}", set.iter().collect::<Vec<_>>()),
ValueView::FloatSet(set) => format!("{:?}", set.iter().collect::<Vec<_>>()),
ValueView::List(list) => format!("{} elements", list.len()),
}
}
assert_eq!(describe(&(&42_i64).into()), "42");
assert_eq!(describe(&(&()).into()), "absent");
variantAbsent

No value is present.

variantBool

A Boolean.

variantConstraint

A reference to a constraint by index.

variantDecision

A reference to a decision variable by index.

variantFloat

A 64-bit float.

variantFloatSet

A set of floats, as an ordered list of inclusive ranges.

variantInt

A 64-bit signed integer.

variantIntSet

A set of integers, as an ordered list of inclusive ranges.

variantList

An ordered list of values.

variantStr

A UTF-8 string.

fn ann_ref<'a, A>(ann: &'a A) -> crate::AnnotationRef<'a>source

Convert a reference to an Annotation implementation into an AnnotationRef suitable for returning from Model trait methods.

fn dont_interrupt<'a>() -> Option<&'a NoInterrupt>source

Pass as Solver::run’s should_stop when the caller will never interrupt the run, so a solver can drop the check from its search loop.

This says nothing about whether the solver stops — it will still finish, or hit its time_limit. It only promises that no interruption arrives from this side.

For a plain deadline prefer the time_limit option over a predicate that watches a clock: a solver told its budget up front can plan around it.

fn ignore_messages<'a>() -> Option<&'a mut IgnoredMessages>source

Pass as Solver::run’s on_message when the caller wants no diagnostics.

Nothing is merely discarded: the solver is told the sink is absent, so it can skip building the messages in the first place.

type AnnotationRef<'a> = fznso_types::FznsoAnnotationRef<'a>source

A reference to an annotation; see fznso_types::FznsoAnnotationRef.

type ConstraintIdx = fznso_types::FznsoConstraintIdxsource

Index of a constraint; see fznso_types::FznsoConstraintIdx.

type ConstraintList<'a> = fznso_types::FznsoConstraintList<'a>source

A list of constraint types; see fznso_types::FznsoConstraintList.

type ConstraintType<'a> = fznso_types::FznsoConstraintType<'a>source

A single constraint type descriptor; see fznso_types::FznsoConstraintType.

type DecisionIdx = fznso_types::FznsoDecisionIdxsource

Index of a decision variable; see fznso_types::FznsoDecisionIdx.

type IgnoredMessages = fn(&str, crate::Value<'_>)source

A stand-in message-sink type, for callers that want no diagnostics.

Never called; it exists only to give ignore_messages a concrete type, since a bare None leaves Solver::run’s sink parameter unconstrained.

type NoInterrupt = fn() -> boolsource

A stand-in stop-predicate type, for callers that never interrupt a run. See dont_interrupt.

type Objective<'a> = fznso_types::FznsoObjective<'a>source

A single objective descriptor; see fznso_types::FznsoObjective.

type ObjectiveList<'a> = fznso_types::FznsoObjectiveList<'a>source

A list of objectives; see fznso_types::FznsoObjectiveList.

type OptionDef<'a> = fznso_types::FznsoOption<'a>source

A single option descriptor; see fznso_types::FznsoOption.

Named OptionDef rather than Option so that it does not shadow std::option::Option.

type OptionList<'a> = fznso_types::FznsoOptionList<'a>source

A list of options; see fznso_types::FznsoOptionList.

type Statistic<'a> = fznso_types::FznsoStatistic<'a>source

A single statistic descriptor; see fznso_types::FznsoStatistic.

type StatisticList<'a> = fznso_types::FznsoStatisticList<'a>source

A list of statistics; see fznso_types::FznsoStatisticList.

type Str<'a> = fznso_types::FznsoStr<'a>source

A borrowed UTF-8 string passed across the interface; see fznso_types::FznsoStr.

type Type = fznso_types::FznsoTypesource

A value type descriptor; see fznso_types::FznsoType.

type TypeBase = fznso_types::FznsoTypeBasesource

The base of a value type descriptor; see fznso_types::FznsoTypeBase.

type TypeList<'a> = fznso_types::FznsoTypeList<'a>source

A list of value type descriptors; see fznso_types::FznsoTypeList.

type Value<'a> = fznso_types::FznsoValueRef<'a>source

A reference to a value; see fznso_types::FznsoValueRef.

type ValueKind = fznso_types::FznsoValueKindsource

Which payload a Value holds; see fznso_types::FznsoValueKind.

Prefer matching on ValueView, which carries the payload with it.