formally_smt/solver.rs
1//
2// ::formally - the open-source formal methods toolchain
3//
4// Copyright (c) 2025 Nicola Gigante
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23//
24
25use crate::formally;
26use formally::{
27 smt::{backends::Backend, logics::Logic, *},
28 support::*,
29};
30
31use std::{
32 cell::RefCell,
33 collections::HashSet,
34 fmt::{Debug, Formatter},
35 rc::Rc,
36 sync::Arc,
37};
38
39/// Configuration for SMT solvers.
40///
41/// This is a simple struct holding some parameters used to instantiate SMT solvers. [Config]
42/// instances are usually constructed from the default one by changing the desired parameters and
43/// then passed to [Solver::new()] and related constructors. Fields are public and can be set
44/// arbitrarily but a builder method for each parameter is also provided, for convenience.
45///
46/// Example:
47/// ```
48/// # mod formally {
49/// # pub extern crate formally_smt as smt;
50/// # pub extern crate formally_support as support;
51/// # }
52/// # use formally::{smt::{*, backends::z3::Z3}, support::*};
53/// # fn main() -> Result<()> {
54/// let config =
55/// Config::default()
56/// .logic("LIA")
57/// .produce_models(true);
58///
59/// let solver = Solver::new(&config)?;
60/// // ...
61/// # Ok(())
62/// # }
63/// ```
64#[derive(Debug, Clone, Default)]
65pub struct Config {
66 /// The name of the logic to instantiate the solver for.
67 ///
68 /// A [None] value is the default, which means a logic is not selected and the solver will
69 /// accept symbols from all the known theories, as if "ALL" had been specified (such as
70 /// with the `(set-logic ALL)` command in SMT-LIBv2).
71 ///
72 /// Note that the `"ALL"` string itself has no special meaning, but is looked up as any other
73 /// logic name (and probably not found).
74 pub logic: Option<Identifier<'static>>,
75 /// Whether the solver has to activate the machinery for generating models for satisfiable
76 /// instances.
77 pub produce_models: bool,
78}
79
80impl Config {
81 /// Create a new [Config] object with default values.
82 pub fn new() -> Config {
83 Config::default()
84 }
85
86 /// Set the `logic` fied.
87 pub fn logic<'a>(self, logic: impl Into<Identifier<'a>>) -> Config {
88 Config {
89 logic: Some(logic.into().into_owned()),
90 ..self
91 }
92 }
93
94 /// Set the `produce_models` fied.
95 pub fn produce_models(self, produce_models: bool) -> Config {
96 Config {
97 produce_models,
98 ..self
99 }
100 }
101}
102
103/// Wrap two [Scope] objects for declared/defined functions and sorts.
104///
105/// SMT-LIBv2 specifies two different namespaces for function and sort symbols in a module.
106/// It is therefore common, especially in backends, to keep around two `Scope<Function>` objects
107/// keeping track of the two namespaces. [Env] is a small utility to keep two scopes together and
108/// perform lookups in the right one as needed.
109#[derive(Default, Clone)]
110pub struct Env {
111 pub functions: Scope<Function>,
112 pub sorts: Scope<Function>,
113}
114
115impl Env {
116 /// Creates an [Env] with empty scopes.
117 pub fn new() -> Self {
118 Env::default()
119 }
120}
121
122/// Select which scope to use in [Env::lookup()] and [Resolve::resolve()].
123#[derive(Clone, Copy, Hash, PartialEq, Eq)]
124pub enum Role {
125 /// Lookup/resolve a function.
126 Function,
127 /// Lookup/resolve a sort.
128 Sort,
129}
130
131impl Env {
132 /// Calls [lookup()](Scope::lookup) on the functions or the sorts scope depending on `role`.
133 ///
134 /// If `role` is `Role::Function`, the call goes to the functions scope. Otherwise
135 /// (`Role::Sort`), to the sorts scope.
136 pub fn lookup<'i>(&self, name: Identifier<'i>, role: Role) -> LookupSet<'_, 'i, Function> {
137 match role {
138 Role::Function => self.functions.lookup(name),
139 Role::Sort => self.sorts.lookup(name),
140 }
141 }
142
143 /// Creates a new [Env] from a parent one using the two scopes of the parent as parents of each
144 /// single scope.
145 pub fn with_parent(self, parent: Env) -> Env {
146 Env {
147 functions: self.functions.with_parent(parent.functions),
148 sorts: self.sorts.with_parent(parent.sorts),
149 }
150 }
151}
152
153/// Type that manages the backend's "term manager" (or "context") and subterm sharing.
154///
155/// Most SMT APIs separate the "term manager" (also called "context"), responsible for managing
156/// subterm sharing and the creation and lifetime management of term objects, from the "solver",
157/// which is responsible for the actual reasoning.
158///
159/// [formally::smt] does the same, where [TermManager] manages subterm sharing (through an
160/// underlying instance of [TermPool]) and a handle to the underlying backend's term manager (e.g.
161/// [TermManager](cvc5_sys::TermManager) in cvc5 or [Z3_context](z3_sys::Z3_context) in Z3).
162///
163/// The handle to the underlying backend's manager performs the conversion from [Term] to the
164/// internal representation of terms of the backend (e.g. [Z3_ast](z3_sys::Z3_ast) in Z3).
165///
166/// A term manager is mainly used by giving a reference to it to one or more [Solver] instances.
167/// Solvers built on the same manager can use the same terms which will be converted to the
168/// underlying backend's representation only once, saving time.
169///
170/// Example:
171/// ```
172/// # mod formally {
173/// # pub extern crate formally_smt as smt;
174/// # pub extern crate formally_support as support;
175/// # }
176/// use formally::{smt::{*, backends::z3::Z3}};
177/// # use formally::support::*;
178///
179/// # use std::rc::Rc;
180/// #
181/// # fn main() -> Result<()> {
182/// let manager = Rc::new(TermManager::with_backend(Z3)?);
183/// let config = Config::new();
184/// let mut slv1 = Solver::with_manager(&config, manager.clone())?;
185/// let mut slv2 = Solver::with_manager(&config, manager)?;
186///
187/// let x1 = slv1.declare(Declaration::constant("x", sort!(Int)))?;
188/// let x2 = slv2.declare(Declaration::constant("x", sort!(Int)))?;
189///
190/// // Equivalent declarations/definitions result in the same `Declared`/`Defined` objects
191/// assert_eq!(x1, x2);
192///
193/// let t1 = slv1.lookup(term!(* x x), Role::Function)?;
194/// let t2 = slv2.lookup(term!(* x x), Role::Function)?;
195///
196/// // Equivalent terms result into the same [Term] objects
197/// assert_eq!(t1, t2);
198///
199/// # Ok(())
200/// # }
201/// ```
202///
203/// Since most SMT APIs are not threadsafe, [TermManager] is not [Send] nor [Sync] and must
204/// therefore be accessed and used by a single thread only. However, one can construct it using
205/// [TermManager::with_pool()] and passing a [DashPool] instance, which is a concurrent [TermPool]
206/// that allows to at least share the same terms among different threads.
207#[derive(Clone)]
208pub struct TermManager {
209 backend_manager: Rc<dyn backends::Manager>,
210 pool: Rc<dyn TermPool>,
211 decls: RefCell<HashSet<Arc<Declaration<Sort, Sort>>>>,
212 defs: RefCell<HashSet<Arc<Definition<Sort, Sort, Term>>>>,
213}
214
215impl TermManager {
216 fn decl(&self, decl: Declaration<Sort, Sort>) -> Declared {
217 if let Some(decl) = self.decls.borrow().get(&decl) {
218 return Declared(Nominal(decl.clone()));
219 }
220
221 let arc = Arc::new(decl);
222 self.decls.borrow_mut().insert(arc.clone());
223
224 Declared(Nominal(arc))
225 }
226
227 fn def(&self, def: Definition<Sort, Sort, Term>) -> Defined {
228 if let Some(def) = self.defs.borrow().get(&def) {
229 return Defined(Nominal(def.clone()));
230 }
231
232 let arc = Arc::new(def);
233 self.defs.borrow_mut().insert(arc.clone());
234
235 Defined(Nominal(arc))
236 }
237}
238
239impl Debug for TermManager {
240 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
241 write!(f, "TermManager {{ }}")
242 }
243}
244
245impl TermManager {
246 /// Construct a [TermManager] with the default backend (if any is available).
247 pub fn new() -> Result<TermManager> {
248 TermManager::with_backend(backends::Register::default_backend()?)
249 }
250
251 /// Construct a [TermManager] over a given [Backend] looked up by name, if it exists.
252 pub fn with_backend_name<'a>(backend: impl Into<Identifier<'a>>) -> Result<TermManager> {
253 let backend = backends::Register::backend(backend)?;
254
255 TermManager::with_backend(backend)
256 }
257
258 /// Construct a [TermManager] over a given [Backend].
259 pub fn with_backend(backend: impl Backend) -> Result<TermManager> {
260 Ok(TermManager {
261 backend_manager: Rc::from(backend.manager()?),
262 pool: Rc::new(HashPool::new()),
263 decls: RefCell::default(),
264 defs: RefCell::default(),
265 })
266 }
267
268 /// Construct a [TermManager] over a given [Backend] and [TermPool].
269 pub fn with_pool(backend: impl Backend, pool: Rc<dyn TermPool>) -> Result<TermManager> {
270 Ok(TermManager {
271 backend_manager: Rc::from(backend.manager()?),
272 pool,
273 decls: RefCell::default(),
274 defs: RefCell::default(),
275 })
276 }
277
278 /// Return a reference to the underlying [TermPool].
279 pub fn pool(&self) -> &dyn TermPool {
280 &*self.pool
281 }
282}
283
284/// Main interface to SMT solvers.
285///
286/// The [Solver] type is at the core of [formally::smt], providing high-level access to backend
287/// solvers.
288///
289/// One can create a [Solver] in three ways, all accepting a [Config] reference to configure the
290/// solver:
291/// - [Solver::new()] creates a solver with a default backend and a dedicated [TermManager].
292/// - [Solver::with_backend()] creates a solver with a given backend and a dedicated [TermManager].
293/// - [Solver::with_manager()] creates a solver over the given [TermManager].
294///
295/// [Solver] is responsible to implement name lookup on terms used in definitions and assertions.
296/// A term such as `term!(> p q)` contains three unresolved symbols, `">"`, `"p"`, and `"q"`.
297/// Without name resolution the term would be unusable by the backends.
298///
299/// [Solver] implements [Stack] to provide the usual incremental interface of most SAT/SMT solvers.
300pub struct Solver {
301 backend_solver: Box<dyn backends::Solver>,
302 env: Env,
303 manager: Rc<TermManager>,
304}
305
306impl Solver {
307 /// Create a [Solver] over a given [TermManager].
308 ///
309 /// Use this constructor to share the same [TermManager] among different solvers.
310 pub fn with_manager(config: &Config, manager: impl Into<Rc<TermManager>>) -> Result<Solver> {
311 let manager = manager.into();
312 let backend_solver = manager
313 .backend_manager
314 .backend()
315 .solver(config, manager.backend_manager.clone())?;
316 Ok(Solver {
317 env: Env::new().with_parent(backend_solver.logic().theory().env()),
318 backend_solver,
319 manager,
320 })
321 }
322
323 /// Create a [Solver] over a given [Backend].
324 ///
325 /// Backends provided by this crate are defined in [formally::smt::backend](backends).
326 pub fn with_backend(config: &Config, backend: impl Backend) -> Result<Solver> {
327 Solver::with_manager(config, TermManager::with_backend(backend)?)
328 }
329
330 /// Create a [Solver] over the given [Backend] specified by name, if it exists.
331 ///
332 /// Backends provided by this crate are defined in [formally::smt::backend](backends).
333 pub fn with_backend_name<'a>(
334 config: &Config,
335 backend: impl Into<Identifier<'a>>,
336 ) -> Result<Solver> {
337 Solver::with_manager(config, TermManager::with_backend_name(backend)?)
338 }
339
340 /// Create a [Solver] with a default backend and a dedicated [TermManager].
341 pub fn new(config: &Config) -> Result<Solver> {
342 Solver::with_manager(config, TermManager::new()?)
343 }
344
345 /// Get the currently selected [Logic].
346 pub fn logic(&self) -> &dyn Logic {
347 self.backend_solver.logic()
348 }
349
350 /// Set a new configuration for the solver.
351 ///
352 /// Note that the [logic](Config::logic) field is ignored when changing configuration after
353 /// construction.
354 pub fn config(&self, config: &Config) -> Result<()> {
355 Ok(self.backend_solver.config(config)?)
356 }
357
358 /// Get the [Env] object holding the current scopes for functions and sorts declared and defined
359 /// in the solver.
360 pub fn env(&self) -> &Env {
361 &self.env
362 }
363
364 /// Return a reference to the [TermPool] of the underlying [TermManager].
365 pub fn pool(&self) -> &dyn TermPool {
366 self.manager.pool()
367 }
368
369 /// Convert a [ToTerm] object into a [Term] including name resolution and type checking.
370 ///
371 /// This method uniques a [ToTerm] object into a [Term] using the [TermPool] of the underlying
372 /// [TermManager] and then performs name resolution and type checking on the term.
373 ///
374 /// The resulting term is guaranteed to be well typed and fully resolved.
375 ///
376 /// Since terms can describe sorts as well, and SMT-LIBv2 defines two different namespaces for
377 /// sorts and functions, the [Role] argument is needed to tell whether the term has to be
378 /// interpreted as a sort or as a value term.
379 pub fn lookup<T: ToTerm>(&self, term: T, role: Role) -> Result<Term> {
380 let interned = term.into_term_in(self.manager.pool());
381 let resolved = interned.resolve(self.env(), self.manager.pool(), role)?;
382 resolved.type_check()?;
383
384 Ok(resolved)
385 }
386
387 /// Convert a [ToSort] object into a [Sort] including name resolution and type checking.
388 ///
389 /// This method uniques a [ToSort] object into a [Sort] and then performs name resolution and
390 /// type checking on the sort.
391 ///
392 /// The resulting sort is guaranteed to be well-formed and fully resolved.
393 pub fn lookup_sort<S: ToSort>(&self, sort: &S) -> Result<Sort> {
394 let resolved = sort.resolve(self.env(), self.manager.pool(), Role::Sort)?;
395 resolved.type_check()?;
396
397 Ok(resolved.try_into()?)
398 }
399
400 /// Create a fully resolved [Variable] by looking up its sort.
401 ///
402 /// This convenience method is equivalent to constructing a new [Variable] instance by
403 /// calling [Solver::lookup_sort()] on the variable's sort.
404 ///
405 /// This method is needed to obtain a fully resolved [Variable] that can be used without further
406 /// name resolution, but it is *not* needed when normally using variables to define functions
407 /// that then refer to them by name.
408 ///
409 /// Example:
410 /// ```
411 /// # mod formally {
412 /// # pub extern crate formally_smt as smt;
413 /// # pub extern crate formally_support as support;
414 /// # }
415 /// use formally::smt::*;
416 /// # use formally::support::*;
417 ///
418 /// # fn main() -> Result<()> {
419 /// let config = Config::default();
420 /// let mut solver = Solver::new(&config)?;
421 ///
422 /// // we construct the variable directly.
423 /// let x = Variable::new("x", sort!(Int));
424 ///
425 /// // here the variable `x` is moved and `lookup_var()` is called internally.
426 /// let f = solver.define(Definition::function("f", [x], sort!(Int), term!(* x 2)));
427 ///
428 /// // here the variable is #expanded in the term so we need to do name resolution earlier
429 /// let y = solver.lookup_var(&Variable::new("y", sort!(Int)))?;
430 /// let g = solver.define(Definition::function("g", [y.clone()], sort!(Int), term!(* #y 2)));
431 ///
432 /// solver.require(term!(distinct (f 21) (g 21) 42))?;
433 ///
434 /// assert_eq!(solver.check()?, Answer::No);
435 ///
436 /// # Ok(())
437 /// # }
438 /// ```
439 pub fn lookup_var<S: ToSort>(&self, variable: &Variable<S>) -> Result<Variable> {
440 Ok(
441 Variable::new(variable.name().clone(), self.lookup_sort(variable.sort())?)
442 .over(variable.span()),
443 )
444 }
445
446 /// Create a fully resolved [Binding] by looking up its sort and term.
447 ///
448 /// This convenience method is useful to infer automatically the sort of the binding from its
449 /// defining term. It is designed to be used in pair with [Binding::new()].
450 pub fn lookup_binding<T: ToTerm>(&self, binding: Binding<Infer, T>) -> Result<Binding> {
451 let def = self.lookup(&binding.def, Role::Function)?;
452 let sort = Sort::of(&def)?;
453
454 let variable =
455 Variable::new(binding.variable.name().clone(), sort).over(binding.variable.span());
456
457 Ok(Binding {
458 variable,
459 def,
460 span: binding.span,
461 })
462 }
463
464 /// Declare a function (or a constant, or a sort).
465 ///
466 /// As explained in the [overview](formally::smt), [declare()](Solver::declare) registers a
467 /// [Declaration] value in the suitable scope and returns a [Declared] object that immutably
468 /// points to the registered [Declaration] object. The registered [Declaration] object is
469 /// not equal to the one passed as argument, in general, because name
470 /// resolution occurs.
471 ///
472 /// Remember that constants are seen as functions with no arguments, and sorts as constants of
473 /// the special sort [Sort::sort()]. See also [Declaration::function()],
474 /// [Declaration::constant()], and [Declaration::sort()] for details.
475 pub fn declare<R: ToSort, B: ToSort>(&mut self, decl: Declaration<R, B>) -> Result<Declared> {
476 let mut domain = Vec::with_capacity(decl.domain.len());
477 for d in &decl.domain {
478 domain.push(self.lookup_sort(d)?);
479 }
480
481 let decl = Declaration {
482 name: decl.name,
483 domain,
484 range: self.lookup_sort(&decl.range)?,
485 span: decl.span,
486 };
487
488 let decl = self.manager.decl(decl);
489 self.backend_solver
490 .logic()
491 .check_function(&decl.clone().into())?;
492
493 if decl.range == Sort::sort() {
494 self.env.sorts.add(&decl.name, decl.clone().into());
495 } else {
496 self.env.functions.add(&decl.name, decl.clone().into());
497 }
498 self.backend_solver.declare(decl.clone())?;
499
500 Ok(decl)
501 }
502
503 /// Define a function (or a constant, or a sort).
504 ///
505 /// As explained in the [overview](formally::smt), [define()](Solver::define) registers a
506 /// [Definition] value in the suitable scope and returns a [Defined] object that immutably
507 /// points to the registered [Definition] object. The registered [Definition] object is not
508 /// equal to the one passed as argument, in general, because name
509 /// resolution occurs.
510 ///
511 /// Remember that constants are seen as functions with no arguments, and sorts as constants of
512 /// the special sort [Sort::sort()]. See also [Definition::function()],
513 /// [Definition::constant()], and [Definition::sort()] for details.
514 ///
515 /// See also the documentation of [Solver::lookup_var()] to know when name lookup is needed to
516 /// be done manually on the variables used in a definition.
517 pub fn define<V: ToSort, R: ToSort, B: ToTerm>(
518 &mut self,
519 def: Definition<V, R, B>,
520 ) -> Result<Defined> {
521 let mut nested = Env::new().with_parent(self.env().clone());
522 let mut domain = Vec::with_capacity(def.domain.len());
523 for var in &def.domain {
524 let var = self.lookup_var(var)?;
525 domain.push(var.clone());
526 nested
527 .functions
528 .add(var.name(), Function::Variable(var.clone()));
529 }
530
531 let range = self.lookup_sort(&def.range)?;
532 let body = def.body.into_term_in(self.manager.pool()).resolve(
533 &nested,
534 self.manager.pool(),
535 Role::Function,
536 )?;
537 let bodysort = body.type_check()?;
538
539 if range == Sort::sort() {
540 if bodysort != Sort::sort() {
541 error!(
542 body.span(),
543 "expected sort in definition of `{}`, found `{}`", def.name, bodysort
544 );
545 return Err(DiagnosticEmitted);
546 }
547 } else {
548 if bodysort != range {
549 error!(
550 body.span(),
551 "expected term of sort `{}` in definition of `{}`, found `{}`",
552 range,
553 def.name,
554 bodysort
555 );
556 return Err(DiagnosticEmitted);
557 }
558 }
559
560 let def = Definition {
561 name: def.name,
562 domain,
563 range,
564 body,
565 span: None,
566 };
567
568 let def = self.manager.def(def);
569 self.backend_solver
570 .logic()
571 .check_function(&def.clone().into())?;
572
573 if def.range == Sort::sort() {
574 self.env.sorts.add(&def.name, def.clone().into());
575 } else {
576 self.env.functions.add(&def.name, def.clone().into());
577 }
578 self.backend_solver.define(def.clone())?;
579
580 Ok(def)
581 }
582
583 /// Assert the given term to the current assertion stack
584 ///
585 /// The term undergoes [name resolution](Resolve::resolve()) and must be well-typed and be of
586 /// sort [Core::Bool()](theories::Core::Bool()).
587 pub fn require<T: ToTerm>(&mut self, term: T) -> Result<()> {
588 let term = self.lookup(term, Role::Function)?;
589 self.backend_solver.logic().check_term(&term)?;
590 let sort = Sort::of(&term)?;
591
592 if sort != theories::Core::Bool() {
593 error!(term.span(), "can only assert Boolean terms");
594 note!(term.span(), "asserted term is of sort `{}`", sort);
595 return Err(DiagnosticEmitted);
596 }
597
598 self.backend_solver.require(&term)?;
599
600 Ok(())
601 }
602
603 /// Check the satisfiability of the current assertions.
604 ///
605 /// The return value distinguishes between errors occurred during the solving process (`Err(_)`)
606 /// and the case where no error occurred but the solver gave up on finding an answer
607 /// (`Ok(Answer::Unknown)`).
608 pub fn check(&mut self) -> Result<Answer> {
609 match self.backend_solver.check()? {
610 Some(true) => Ok(Answer::Yes),
611 Some(false) => Ok(Answer::No),
612 None => Ok(Answer::Unknown),
613 }
614 }
615
616 /// Get a model of the current assertions, if they were found to be satisfiable.
617 ///
618 /// The return value distinguishes between errors occurred while extracting the model from the
619 /// backend (`Err(_)`), and the case where no error occurred but there is no model available
620 /// (`Ok(None)`), for example because the last call to [check()](Solver::check()) did not
621 /// return `Ok(Answer::Yes)` or because model production was turned off in the solver's
622 /// [Config].
623 ///
624 /// Note that the representation of model values is still incomplete and only Boolean and
625 /// numerical values can be currently extracted.
626 pub fn model(&self) -> Result<Option<Model<'_>>> {
627 match self.backend_solver.model()? {
628 Some(provider) => Ok(Some(Model {
629 solver: self,
630 provider,
631 })),
632 None => Ok(None),
633 }
634 }
635}
636
637impl Stack for Solver {
638 /// Push a new frame in the assertions stack.
639 ///
640 /// The assertion stack includes asserted terms and declared/defined entities.
641 /// Currently, the `:global-declarations` option of SMT-LIBv2 is not supported.
642 fn push(&mut self) -> Result<()> {
643 Ok(self.backend_solver.push()?)
644 }
645
646 /// Pops a frame from the assertions stack, doing nothing if there is no frame to remove.
647 ///
648 /// The assertion stack includes asserted terms and declared/defined entities.
649 /// Currently, the `:global-declarations` option of SMT-LIBv2 is not supported.
650 fn pop_n(&mut self, n: usize) -> Result<()> {
651 Ok(self.backend_solver.pop_n(n)?)
652 }
653}
654
655/// A trait for types that can provide model values.
656pub trait ModelProvider {
657 fn value(&self, term: &Term, pool: &dyn TermPool) -> Option<Term>;
658}
659
660/// The answer to a call to [Solver::check()].
661#[derive(Clone, Copy, Default, PartialEq, Eq)]
662pub enum Answer {
663 /// The assertions are satisfiable.
664 Yes,
665 /// The assertions are unsatisfiable.
666 No,
667 /// A definite answer could not be found.
668 #[default]
669 Unknown,
670}
671
672impl Debug for Answer {
673 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
674 match self {
675 Answer::Yes => write!(f, "sat"),
676 Answer::No => write!(f, "unsat"),
677 Answer::Unknown => write!(f, "unknown"),
678 }
679 }
680}
681
682/// A model to the current set of assertions of a [Solver].
683pub struct Model<'s> {
684 solver: &'s Solver,
685 provider: Box<dyn 's + ModelProvider>,
686}
687
688impl Model<'_> {
689 /// Get the value of a [ToTerm] object (after name lookup and type checking) in the model.
690 pub fn value(&self, term: impl ToTerm) -> Result<Option<Term>> {
691 Ok(self.provider.value(
692 &self.solver.lookup(term, Role::Function)?,
693 self.solver.pool(),
694 ))
695 }
696}