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
Macros
Section titled “Macros”fznso_export
Section titled “fznso_export”macro_rules! fznso_exportsourceGenerate 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 file — mysolver 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.
Traits
Section titled “Traits”Annotation
Section titled “Annotation”trait AnnotationsourceHelper 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.
fn argument(&self, index: usize) -> Value<'_>The annotation argument at the given zero-based index.
fn argument_len(&self) -> usizeThe number of arguments this annotation carries.
fn ident(&self) -> &strThe annotation’s identifier string (e.g. "output_var").
trait ModelsourceRead-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.
Ergonomic access
Section titled “Ergonomic access”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()); }}fn constraint_annotation(&self, con: ConstraintIdx, index: usize) -> AnnotationRef<'_>The index-th annotation on the given constraint.
fn constraint_annotation_len(&self, con: ConstraintIdx) -> usizeNumber of annotations on the given constraint.
fn constraint_argument(&self, con: ConstraintIdx, index: usize) -> Value<'_>The index-th argument of the given constraint.
fn constraint_argument_len(&self, con: ConstraintIdx) -> usizeNumber of arguments of the given constraint.
fn constraint_defines(&self, con: ConstraintIdx) -> Option<DecisionIdx>The decision variable defined by this constraint, if any.
fn constraint_ident(&self, con: ConstraintIdx) -> &strThe identifier of the given constraint (e.g. "int_le").
fn constraint_layer_end(&self, layer: usize) -> usizeOne-past-the-end global constraint index for constraints in layers
0..=layer.
fn constraint_len(&self) -> usizeTotal number of constraints across all layers.
fn decision_annotation(&self, decision: DecisionIdx, index: usize) -> AnnotationRef<'_>The index-th annotation on the given decision variable.
fn decision_annotation_len(&self, decision: DecisionIdx) -> usizeNumber of annotations on the given decision variable.
fn decision_defined(&self, decision: DecisionIdx) -> boolWhether the given decision variable is defined by some constraint.
fn decision_in_solution(&self, decision: DecisionIdx) -> boolWhether 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.
fn decision_domain(&self, decision: DecisionIdx) -> Value<'_>The domain of the given decision variable.
fn decision_layer_end(&self, layer: usize) -> usizeOne-past-the-end global decision index for variables in layers
0..=layer.
fn decision_len(&self) -> usizeTotal number of decision variables across all layers.
fn decision_name(&self, decision: DecisionIdx) -> Option<&str>The optional name of the given decision variable.
fn decision_type(&self, decision: DecisionIdx) -> FznsoTypeThe 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.
fn layer_len(&self) -> usizeTotal number of layers currently in the model.
fn layer_permanent(&self) -> usizeNumber of layers that are permanently committed (will not be popped).
fn layer_redundant_index(&self, index: usize) -> usizeThe global layer index of the index-th redundant permanent layer.
fn layer_redundant_len(&self) -> usizeNumber of permanent layers marked as redundant.
fn layer_unchanged(&self) -> usizeNumber of layers whose content is unchanged since the last solver run.
fn objective_annotation(&self, index: usize) -> AnnotationRef<'_>The index-th annotation on the objective.
fn objective_annotation_len(&self) -> usizeNumber of annotations on the objective.
fn objective_arg(&self) -> Value<'_>The argument of the objective function.
fn objective_ident(&self) -> &strIdentifier of the objective function (e.g. "minimize"), or "" for
satisfaction.
fn constraint(&self, idx: ConstraintIdx) -> ConstraintView<'_, Self>A view into the constraint at idx.
fn constraints(&self) -> impl IteratorIterator over all constraints in the model, ordered by layer.
fn decision(&self, idx: DecisionIdx) -> DecisionView<'_, Self>A view into the decision variable at idx.
fn decisions(&self) -> impl IteratorIterator over all decision variables in the model, ordered by layer.
fn layer(&self, idx: usize) -> LayerView<'_, Self>A view into the layer at idx.
fn layers(&self) -> impl IteratorIterator over all layers, from the oldest (base) to the newest.
fn objective(&self) -> ObjectiveView<'_, Self>A view into the model’s objective.
Solution
Section titled “Solution”trait SolutionsourceTrait 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.
fn statistic(&self, name: &str) -> Value<'_>Retrieve a named statistic.
fn value(&self, decision_idx: usize) -> Value<'_>Retrieve the value assigned to a decision variable by its index.
Solver
Section titled “Solver”trait SolversourceThe 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.
type SolutionThe 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.
fn option_get(&self, name: &str) -> Value<'_>Get the current value of a named solver option.
fn 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.
fn 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.
fn run<M, F, G, H>(&mut self, model: &M, on_solution: &mut F, on_message: Option<&mut G>, should_stop: Option<&H>) -> StatusRun 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.
Threading
Section titled “Threading”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_solutionandon_messageconcurrently — 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 beSend, notSync: 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 thatintermediate/progress.boundrely 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.
Optional callbacks
Section titled “Optional callbacks”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: Nonemeans 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: Nonemeans 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.
Stopping early
Section titled “Stopping early”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.
SolverType
Section titled “SolverType”trait SolverTypesourceWhat 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.
type CONSTRAINT_LISTThe constraints this solver accepts, exported as
fznso_constraint_list.
type DECISION_LISTThe decision-variable types this solver accepts, exported as
fznso_decision_list.
type OBJECTIVE_LISTThe objective strategies this solver supports, exported as
fznso_objective_list.
type OPTION_LISTThe options this solver accepts, exported as fznso_option_list.
type STATISTIC_LISTThe statistics this solver reports, exported as fznso_statistic_list.
fn new() -> SelfCreate a new solver instance, exported as fznso_solver_create.
ValueExt
Section titled “ValueExt”trait ValueExt<'a>sourceAdds 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.
fn view(&self) -> ValueView<'a>Resolve this value into its payload.
See ValueView for the full list of shapes and an example.
Structs
Section titled “Structs”ConstraintView
Section titled “ConstraintView”struct ConstraintView<'m, M>sourceA view into a single constraint within a Model.
Obtained via Model::constraint or by iterating Model::constraints.
fn annotation_len(&self) -> usizeNumber of annotations on this constraint.
fn annotations(&self) -> impl Iterator + 'mIterator over all annotations on this constraint.
fn argument(&self, i: usize) -> Value<'m>The i-th argument of this constraint.
fn argument_len(&self) -> usizeNumber of arguments of this constraint.
fn arguments(&self) -> impl Iterator + 'mIterator over all arguments of this constraint.
fn defines(&self) -> Option<DecisionIdx>The decision variable functionally defined by this constraint, if any.
fn ident(&self) -> &'m strThe identifier of this constraint (e.g. "int_le").
fn idx(&self) -> ConstraintIdxThe global index of this constraint.
DecisionView
Section titled “DecisionView”struct DecisionView<'m, M>sourceA view into a single decision variable within a Model.
Obtained via Model::decision or by iterating Model::decisions.
fn annotation_len(&self) -> usizeNumber of annotations on this decision variable.
fn annotations(&self) -> impl Iterator + 'mIterator over all annotations on this decision variable.
fn defined(&self) -> boolWhether this decision variable is functionally defined by some constraint.
fn domain(&self) -> Value<'m>The domain of this decision variable.
fn idx(&self) -> DecisionIdxThe global index of this decision variable.
fn in_solution(&self) -> boolWhether a solution may be asked for this decision variable’s value.
fn name(&self) -> Option<&'m str>The optional name of this decision variable.
fn ty(&self) -> FznsoTypeThe type of this decision variable.
Discovered
Section titled “Discovered”struct DiscoveredsourceA solver library found on the search path, reported without loading it.
name: StringThe solver’s name, derived from the file name.
version: StringThe version, parsed from the file name, or empty if the file carries
none. Compared component-wise, so 10 is newer than 9.
path: std::path::PathBufThe full path to the library.
DynSolution
Section titled “DynSolution”struct DynSolution<'a>sourceA solution emitted by a dynamically-loaded solver
(DynSolver(crate::DynSolver)).
Implements Solution and provides direct access to the inner
FznsoSolutionRef.
DynSolver
Section titled “DynSolver”struct DynSolversourceA solver instance from a dynamically loaded library, via Library.
Created by Library::create_solver.
fn constraint_types(&self) -> ConstraintList<'_>The constraints the loaded solver accepts.
fn decision_types(&self) -> TypeList<'_>The decision-variable types the loaded solver accepts.
fn objective_list(&self) -> ObjectiveList<'_>The objective strategies the loaded solver supports.
fn option_list(&self) -> OptionList<'_>The options the loaded solver accepts.
fn statistic_list(&self) -> StatisticList<'_>The statistics the loaded solver reports.
FloatRanges
Section titled “FloatRanges”struct FloatRanges<'a>sourceThe ranges of a float set, borrowed from the value that produced them.
fn get(&self, index: usize) -> (f64, f64)The inclusive (min, max) range at index.
Panics
Section titled “Panics”Panics if index is not less than len(Self::len).
fn is_empty(&self) -> boolWhether the set is empty.
fn iter(&self) -> impl Iterator + 'aIterator over the inclusive (min, max) ranges, in order.
fn len(&self) -> usizeThe number of ranges in the set.
IntRanges
Section titled “IntRanges”struct IntRanges<'a>sourceThe ranges of an integer set, borrowed from the value that produced them.
fn get(&self, index: usize) -> (i64, i64)The inclusive (min, max) range at index.
Panics
Section titled “Panics”Panics if index is not less than len(Self::len).
fn is_empty(&self) -> boolWhether the set is empty.
fn iter(&self) -> impl Iterator + 'aIterator over the inclusive (min, max) ranges, in order.
fn len(&self) -> usizeThe number of ranges in the set.
LayeredModel
Section titled “LayeredModel”struct LayeredModelsourceAn 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.
Example
Section titled “Example”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
fn add_constraint(&mut self, ident: impl Into, arguments: Vec<OwnedValue>, defines: Option<DecisionIdx>, annotations: Vec<OwnedAnnotation>) -> ConstraintIdxAdd a constraint to the current (top) layer.
Panics
Section titled “Panics”Panics if there are no layers.
fn add_decision(&mut self, ty: FznsoType, domain: OwnedValue, name: Option<String>, defined: bool, in_solution: bool, annotations: Vec<OwnedAnnotation>) -> DecisionIdxAdd 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
Section titled “Panics”Panics if there are no layers.
fn mark_permanent(&mut self)Mark all current layers as permanent.
fn mark_redundant(&mut self, layer: usize)Mark a permanent layer as redundant.
layer must be a valid permanent layer index (0..permanent).
fn pop_layer(&mut self)Pop the top layer from the model.
Panics
Section titled “Panics”Panics if there are no layers, or if the top layer is permanent.
fn push_layer(&mut self)Push a new (empty) layer onto the model.
fn 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).
fn set_unchanged(&mut self, n: usize)Set the number of layers considered unchanged since the last solver run.
LayerView
Section titled “LayerView”struct LayerView<'m, M>sourceA view into a single layer within a Model.
Obtained via Model::layer or by iterating Model::layers.
fn constraint_len(&self) -> usizeNumber of constraints in this layer.
fn constraints(&self) -> impl Iterator + 'mIterator over constraints belonging to this layer.
fn decision_len(&self) -> usizeNumber of decision variables in this layer.
fn decisions(&self) -> impl Iterator + 'mIterator over decision variables belonging to this layer.
fn idx(&self) -> usizeThe index of this layer within the model.
fn is_permanent(&self) -> boolWhether this layer is permanently committed (will never be popped).
fn is_redundant(&self) -> boolWhether this permanent layer has been marked redundant.
fn is_unchanged(&self) -> boolWhether this layer’s content is unchanged since the last solver run.
Library
Section titled “Library”struct LibrarysourceA 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.
fn constraint_types(&self) -> FznsoConstraintList<'_>Get the list of constraints supported by solvers from this library.
fn create_solver(&Arc<Self>) -> DynSolverCreates a new DynSolver instance from this library.
fn decision_types(&self) -> FznsoTypeList<'_>Get the list of decision-variable types supported by solvers from this library.
fn 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.
unsafe 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.
Safety
Section titled “Safety”The libraries found on the search path must be valid FZnSO solvers; see
Library::new.
unsafe 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.
Safety
Section titled “Safety”The libraries found on the search path must be valid FZnSO solvers; see
Library::new.
unsafe fn new<S>(filename: S) -> Result<Arc<Self>, LoadError>Dynamically load a FZnSO solver library from filename.
Naming rules
Section titled “Naming rules”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:
- 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. - It must not begin with
lib. On Unix the file islib<name>.so, so a name that itself began withlibcould not be told apart from the platform prefix on a platform that has none:libssatwould be read back aslibssatfromliblibssat.sobut asssatfromlibssat.dll. - The file’s base name must be
<name>, optionally prefixed withlib, 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.
Versioning
Section titled “Versioning”The version goes between the name and the extension on every
platform — libgecode.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.
Safety
Section titled “Safety”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.
fn objective_list(&self) -> FznsoObjectiveList<'_>Get the list of objective strategies supported by solvers from this library.
fn option_list(&self) -> FznsoOptionList<'static>Get the list of options accepted by solvers from this library.
fn 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
$FZNSO_SOLVER_PATH, split on the platform’s path separator,- the per-user directory (
$XDG_DATA_HOME/fznso,~/Library/Application Support/fznso, or%LOCALAPPDATA%\fznso), - the system directories (
/usr/local/lib/fznso,/usr/lib/fznso, orfznsobeside the running executable on Windows).
fn statistic_list(&self) -> FznsoStatisticList<'_>Get the list of statistics produced by solvers from this library.
ObjectiveView
Section titled “ObjectiveView”struct ObjectiveView<'m, M>sourceA view into the objective of a Model.
Obtained via Model::objective.
fn annotation_len(&self) -> usizeNumber of annotations on the objective.
fn annotations(&self) -> impl Iterator + 'mIterator over all annotations on the objective.
fn arg(&self) -> Value<'m>The argument of the objective function.
fn ident(&self) -> &'m strThe objective function identifier (e.g. "minimize"), or "" for
satisfaction problems.
fn is_satisfy(&self) -> boolWhether this is a satisfaction problem (no objective).
OwnedAnnotation
Section titled “OwnedAnnotation”struct OwnedAnnotationsourceAn annotation for use with LayeredModel.
fn new(ident: impl Into, arguments: Vec<OwnedValue>) -> SelfCreate a new annotation with the given identifier (must be valid UTF-8 and contain no interior null bytes) and arguments.
ValueList
Section titled “ValueList”struct ValueList<'a>sourceThe elements of a list value, borrowed from the value that produced them.
fn get(&self, index: usize) -> Value<'a>The element at index.
Panics
Section titled “Panics”Panics if index is not less than len(Self::len).
fn is_empty(&self) -> boolWhether the list is empty.
fn iter(&self) -> impl Iterator + 'aIterator over the elements, in order.
fn len(&self) -> usizeThe number of elements in the list.
LoadError
Section titled “LoadError”enum LoadErrorsourceWhy loading a solver library through Library::new failed.
LibraryThe library could not be opened, or a required symbol was missing.
InvalidNameThe file name does not yield a usable solver name.
NotFoundNo solver of the requested name was found on the search path.
AbiMismatchThe solver reports an ABI version this library cannot use.
OwnedValue
Section titled “OwnedValue”enum OwnedValuesourceAn owned value for use with LayeredModel.
Mirrors all variants of FznsoValueKind but owns the contained data.
AbsentThe absent / null value.
BoolA Boolean value.
IntA 64-bit signed integer value.
FloatA 64-bit IEEE 754 double-precision float value.
StringA UTF-8 string value.
DecisionA reference to a decision variable by its global index.
ConstraintA reference to a constraint by its global index.
IntSetInteger set as a list of inclusive [min, max] ranges.
FloatSetFloat set as a list of inclusive [min, max] ranges.
ListAn ordered list of nested values.
Status
Section titled “Status”enum StatussourceStatus 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.
CompleteThe solver explored the full search space.
IncompleteThe solver stopped early (timeout or other termination).
ErrorAn error occurred; the string contains the error message.
ValueView
Section titled “ValueView”enum ValueView<'a>sourceA 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");AbsentNo value is present.
BoolA Boolean.
ConstraintA reference to a constraint by index.
DecisionA reference to a decision variable by index.
FloatA 64-bit float.
FloatSetA set of floats, as an ordered list of inclusive ranges.
IntA 64-bit signed integer.
IntSetA set of integers, as an ordered list of inclusive ranges.
ListAn ordered list of values.
StrA UTF-8 string.
Functions
Section titled “Functions”ann_ref
Section titled “ann_ref”fn ann_ref<'a, A>(ann: &'a A) -> crate::AnnotationRef<'a>sourceConvert a reference to an Annotation implementation into an
AnnotationRef suitable for returning from Model trait methods.
dont_interrupt
Section titled “dont_interrupt”fn dont_interrupt<'a>() -> Option<&'a NoInterrupt>sourcePass 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.
ignore_messages
Section titled “ignore_messages”fn ignore_messages<'a>() -> Option<&'a mut IgnoredMessages>sourcePass 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 aliases
Section titled “Type aliases”AnnotationRef
Section titled “AnnotationRef”type AnnotationRef<'a> = fznso_types::FznsoAnnotationRef<'a>sourceA reference to an annotation; see fznso_types::FznsoAnnotationRef.
ConstraintIdx
Section titled “ConstraintIdx”type ConstraintIdx = fznso_types::FznsoConstraintIdxsourceIndex of a constraint; see fznso_types::FznsoConstraintIdx.
ConstraintList
Section titled “ConstraintList”type ConstraintList<'a> = fznso_types::FznsoConstraintList<'a>sourceA list of constraint types; see fznso_types::FznsoConstraintList.
ConstraintType
Section titled “ConstraintType”type ConstraintType<'a> = fznso_types::FznsoConstraintType<'a>sourceA single constraint type descriptor; see
fznso_types::FznsoConstraintType.
DecisionIdx
Section titled “DecisionIdx”type DecisionIdx = fznso_types::FznsoDecisionIdxsourceIndex of a decision variable; see fznso_types::FznsoDecisionIdx.
IgnoredMessages
Section titled “IgnoredMessages”type IgnoredMessages = fn(&str, crate::Value<'_>)sourceA 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.
NoInterrupt
Section titled “NoInterrupt”type NoInterrupt = fn() -> boolsourceA stand-in stop-predicate type, for callers that never interrupt a run. See
dont_interrupt.
Objective
Section titled “Objective”type Objective<'a> = fznso_types::FznsoObjective<'a>sourceA single objective descriptor; see fznso_types::FznsoObjective.
ObjectiveList
Section titled “ObjectiveList”type ObjectiveList<'a> = fznso_types::FznsoObjectiveList<'a>sourceA list of objectives; see fznso_types::FznsoObjectiveList.
OptionDef
Section titled “OptionDef”type OptionDef<'a> = fznso_types::FznsoOption<'a>sourceA single option descriptor; see fznso_types::FznsoOption.
Named OptionDef rather than Option so that it does not shadow
std::option::Option.
OptionList
Section titled “OptionList”type OptionList<'a> = fznso_types::FznsoOptionList<'a>sourceA list of options; see fznso_types::FznsoOptionList.
Statistic
Section titled “Statistic”type Statistic<'a> = fznso_types::FznsoStatistic<'a>sourceA single statistic descriptor; see fznso_types::FznsoStatistic.
StatisticList
Section titled “StatisticList”type StatisticList<'a> = fznso_types::FznsoStatisticList<'a>sourceA list of statistics; see fznso_types::FznsoStatisticList.
type Str<'a> = fznso_types::FznsoStr<'a>sourceA borrowed UTF-8 string passed across the interface; see
fznso_types::FznsoStr.
type Type = fznso_types::FznsoTypesourceA value type descriptor; see fznso_types::FznsoType.
TypeBase
Section titled “TypeBase”type TypeBase = fznso_types::FznsoTypeBasesourceThe base of a value type descriptor; see fznso_types::FznsoTypeBase.
TypeList
Section titled “TypeList”type TypeList<'a> = fznso_types::FznsoTypeList<'a>sourceA list of value type descriptors; see fznso_types::FznsoTypeList.
type Value<'a> = fznso_types::FznsoValueRef<'a>sourceA reference to a value; see fznso_types::FznsoValueRef.
ValueKind
Section titled “ValueKind”type ValueKind = fznso_types::FznsoValueKindsourceWhich payload a Value holds; see fznso_types::FznsoValueKind.
Prefer matching on ValueView, which carries the payload with it.