Skip to main content

sim_lib_numbers_numeric/
function.rs

1//! The numeric domain library and its operation symbols (`numeric-diff`,
2//! `integrate`, `integrate-adapt`, `ode-solve`, `numeric/compose`, and
3//! `numeric/run-composed`) registered into the runtime.
4
5use std::{any::Any, sync::Arc};
6
7use sim_kernel::{
8    AbiVersion, Args, Callable, ClassRef, Cx, DefaultFactory, Dependency, Export, Expr, Factory,
9    Lib, LibManifest, LibTarget, Linker, Object, RawArgs, Result, Symbol, Value, Version,
10};
11use sim_lib_numbers_core::domains;
12
13use super::runtime;
14
15/// Returns the symbol bound to the `numeric-diff` operation.
16///
17/// # Examples
18///
19/// ```
20/// use sim_lib_numbers_numeric::{
21///     integrate_adapt_symbol, integrate_symbol, numeric_diff_symbol, ode_solve_symbol,
22/// };
23///
24/// assert_eq!(numeric_diff_symbol().to_string(), "numeric-diff");
25/// assert_eq!(integrate_symbol().to_string(), "integrate");
26/// assert_eq!(integrate_adapt_symbol().to_string(), "integrate-adapt");
27/// assert_eq!(ode_solve_symbol().to_string(), "ode-solve");
28/// ```
29pub fn numeric_diff_symbol() -> Symbol {
30    Symbol::new("numeric-diff")
31}
32
33/// Returns the symbol bound to the fixed-rule `integrate` operation.
34pub fn integrate_symbol() -> Symbol {
35    Symbol::new("integrate")
36}
37
38/// Returns the symbol bound to the adaptive `integrate-adapt` operation.
39pub fn integrate_adapt_symbol() -> Symbol {
40    Symbol::new("integrate-adapt")
41}
42
43/// Returns the symbol bound to the `ode-solve` operation.
44pub fn ode_solve_symbol() -> Symbol {
45    Symbol::new("ode-solve")
46}
47
48/// Returns the symbol bound to the `numeric/compose` operation.
49pub fn numeric_compose_symbol() -> Symbol {
50    Symbol::qualified("numeric", "compose")
51}
52
53/// Returns the symbol bound to the `numeric/run-composed` operation.
54pub fn numeric_run_composed_symbol() -> Symbol {
55    Symbol::qualified("numeric", "run-composed")
56}
57
58fn function_symbols() -> [Symbol; 6] {
59    [
60        numeric_diff_symbol(),
61        integrate_symbol(),
62        integrate_adapt_symbol(),
63        ode_solve_symbol(),
64        numeric_compose_symbol(),
65        numeric_run_composed_symbol(),
66    ]
67}
68
69#[derive(Clone)]
70struct NumericFunction {
71    symbol: Symbol,
72}
73
74impl Object for NumericFunction {
75    fn display(&self, _cx: &mut Cx) -> Result<String> {
76        Ok(format!("#<function {}>", self.symbol))
77    }
78
79    fn as_any(&self) -> &dyn Any {
80        self
81    }
82}
83
84impl sim_kernel::ObjectCompat for NumericFunction {
85    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
86        if let Some(value) = cx
87            .registry()
88            .class_by_symbol(&Symbol::qualified("core", "Function"))
89        {
90            return Ok(value.clone());
91        }
92        DefaultFactory.class_stub(
93            sim_kernel::CORE_FUNCTION_CLASS_ID,
94            Symbol::qualified("core", "Function"),
95        )
96    }
97    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
98        Ok(Expr::Symbol(self.symbol.clone()))
99    }
100    fn as_callable(&self) -> Option<&dyn Callable> {
101        Some(self)
102    }
103}
104
105impl Callable for NumericFunction {
106    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
107        if self.symbol == numeric_diff_symbol() {
108            runtime::call_numeric_diff(cx, args)
109        } else if self.symbol == integrate_symbol() {
110            runtime::call_integrate(cx, args)
111        } else if self.symbol == integrate_adapt_symbol() {
112            runtime::call_integrate_adapt(cx, args)
113        } else if self.symbol == numeric_compose_symbol() {
114            runtime::call_numeric_compose(cx, args)
115        } else if self.symbol == numeric_run_composed_symbol() {
116            runtime::call_numeric_run_composed(cx, args)
117        } else {
118            runtime::call_ode_solve(cx, args)
119        }
120    }
121
122    fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
123        let args = args.into_exprs();
124        if self.symbol == numeric_diff_symbol() {
125            runtime::call_numeric_diff_exprs(cx, args)
126        } else if self.symbol == integrate_symbol() {
127            runtime::call_integrate_exprs(cx, args)
128        } else if self.symbol == integrate_adapt_symbol() {
129            runtime::call_integrate_adapt_exprs(cx, args)
130        } else if self.symbol == numeric_compose_symbol() {
131            runtime::call_numeric_compose_exprs(cx, args)
132        } else if self.symbol == numeric_run_composed_symbol() {
133            runtime::call_numeric_run_composed_exprs(cx, args)
134        } else {
135            runtime::call_ode_solve_exprs(cx, args)
136        }
137    }
138}
139
140/// Library that installs the numeric evaluation surface: the `numeric-diff`,
141/// `integrate`, `integrate-adapt`, `ode-solve`, `numeric/compose`, and
142/// `numeric/run-composed` callables that dispatch to the registered numeric
143/// backends.
144pub struct NumericNumbersLib;
145
146impl NumericNumbersLib {
147    /// Creates a new `NumericNumbersLib` ready to be loaded into a runtime.
148    pub fn new() -> Self {
149        Self
150    }
151}
152
153impl Default for NumericNumbersLib {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159impl Lib for NumericNumbersLib {
160    fn manifest(&self) -> LibManifest {
161        LibManifest {
162            id: domains::numeric(),
163            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
164            abi: AbiVersion { major: 0, minor: 1 },
165            target: LibTarget::HostRegistered,
166            requires: Vec::<Dependency>::new(),
167            capabilities: Vec::new(),
168            exports: function_symbols()
169                .into_iter()
170                .map(|symbol| Export::Function {
171                    symbol,
172                    function_id: None,
173                })
174                .collect(),
175        }
176    }
177
178    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
179        for symbol in function_symbols() {
180            linker.function_value(
181                symbol.clone(),
182                DefaultFactory.opaque(Arc::new(NumericFunction { symbol }))?,
183            )?;
184        }
185        Ok(())
186    }
187}