sim_lib_logic/model.rs
1//! Tuning values for the logic organ: search strategy, occurs-check policy,
2//! resource limits, and the aggregate [`LogicConfig`].
3//!
4//! See the [`README`](https://docs.rs/sim-runtime) for how the logic organ
5//! layers concrete resolution over the kernel `Shape` contracts.
6
7use sim_kernel::Symbol;
8
9/// Order in which the resolver explores pending goal states.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum SearchStrategy {
12 /// Depth-first search: explore the most recent state first (the default).
13 #[default]
14 Dfs,
15 /// Breadth-first search: explore the oldest pending state first.
16 Bfs,
17 /// Fair search: round-robin over pending states to avoid starvation.
18 Fair,
19}
20
21impl SearchStrategy {
22 /// Parses a strategy from its surface symbol (`dfs`, `bfs`, or `fair`).
23 ///
24 /// Returns `None` for any other symbol name.
25 pub fn from_symbol(symbol: &Symbol) -> Option<Self> {
26 match symbol.name.as_ref() {
27 "dfs" => Some(Self::Dfs),
28 "bfs" => Some(Self::Bfs),
29 "fair" => Some(Self::Fair),
30 _ => None,
31 }
32 }
33
34 /// Returns the surface symbol for this strategy (the inverse of
35 /// [`SearchStrategy::from_symbol`]).
36 pub fn as_symbol(self) -> Symbol {
37 Symbol::new(match self {
38 Self::Dfs => "dfs",
39 Self::Bfs => "bfs",
40 Self::Fair => "fair",
41 })
42 }
43}
44
45/// Whether the unifier runs the occurs check when binding a variable.
46#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
47pub enum OccursCheck {
48 /// Always run the occurs check, rejecting bindings that build cyclic terms
49 /// (the safe default).
50 #[default]
51 Always,
52 /// Skip the occurs check, trusting that terms stay acyclic.
53 TrustedAcyclic,
54}
55
56/// Resource ceilings that bound a single logic query.
57///
58/// Each limit aborts the query with an error rather than looping forever.
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub struct LogicLimits {
61 /// Maximum resolution depth before the query is aborted.
62 pub max_depth: usize,
63 /// Maximum number of answers to collect, or `None` for unbounded.
64 pub max_answers: Option<usize>,
65 /// Maximum number of pending goals allowed in a single state.
66 pub max_goals: usize,
67 /// Maximum number of candidate clauses scanned across the whole query.
68 pub max_clause_scan: usize,
69}
70
71impl Default for LogicLimits {
72 fn default() -> Self {
73 Self {
74 max_depth: 128,
75 max_answers: Some(256),
76 max_goals: 1024,
77 max_clause_scan: 8192,
78 }
79 }
80}
81
82/// Aggregate tuning for the logic organ, threaded through every query.
83///
84/// Combines [`LogicLimits`], the [`SearchStrategy`], the [`OccursCheck`]
85/// policy, and the indexing/tabling switches.
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct LogicConfig {
88 /// Resource ceilings applied to each query.
89 pub limits: LogicLimits,
90 /// Channel buffer size for streamed answers.
91 pub stream_buffer: usize,
92 /// Goal-exploration order.
93 pub strategy: SearchStrategy,
94 /// Occurs-check policy used by the unifier.
95 pub occurs_check: OccursCheck,
96 /// Whether first-argument clause indexing is enabled.
97 pub enable_indexing: bool,
98 /// Whether goal tabling (loop detection) is enabled.
99 pub enable_tabling: bool,
100}
101
102impl Default for LogicConfig {
103 fn default() -> Self {
104 Self {
105 limits: LogicLimits::default(),
106 stream_buffer: 64,
107 strategy: SearchStrategy::Dfs,
108 occurs_check: OccursCheck::Always,
109 enable_indexing: true,
110 enable_tabling: true,
111 }
112 }
113}