Skip to main content

sim_lib_numbers_tensor/implementation/
domain.rs

1//! Tensor number-domain registration: the `TensorNumbersLib` that installs the
2//! tensor domain, its value class, and its constructor operations.
3
4use std::sync::Arc;
5
6use sim_kernel::{
7    AbiVersion, DefaultFactory, Dependency, Export, Expr, Factory, Lib, LibManifest, LibTarget,
8    Linker, NumberDomain, Object, Result, Symbol, Value, Version,
9};
10use sim_lib_numbers_core::{
11    DomainNumberValueShape, NumberDomainTableSpec, domains, number_domain_table,
12};
13use sim_shape::shape_value;
14
15use super::{
16    citizen::{register_tensor_value_class, tensor_value_class_symbol},
17    function::{
18        TensorFunction, index_symbol, map_symbol, mat_symbol, reshape_symbol, scalar_symbol,
19        slice_symbol, tensor_symbol, vec_symbol,
20    },
21};
22
23/// The symbol naming the tensor number domain (`numbers/tensor`).
24pub fn number_domain() -> Symbol {
25    domains::tensor()
26}
27
28fn literal_class_symbol() -> Symbol {
29    domains::literal_class("tensor")
30}
31
32fn literal_instance_shape_symbol() -> Symbol {
33    Symbol::qualified(literal_class_symbol().to_string(), "instance-shape")
34}
35
36fn value_shape_symbol() -> Symbol {
37    sim_lib_numbers_core::value_shape_symbol(&number_domain())
38}
39
40#[sim_citizen_derive::non_citizen(
41    reason = "numbers/tensor number-domain marker; reconstruct by loading the tensor number lib",
42    kind = "marker",
43    descriptor = "numbers/tensor"
44)]
45struct TensorNumberDomain;
46
47impl NumberDomain for TensorNumberDomain {
48    fn symbol(&self) -> Symbol {
49        number_domain()
50    }
51
52    fn parse_priority(&self) -> i32 {
53        -200
54    }
55
56    fn parse_literal(&self, _cx: &mut sim_kernel::Cx, _text: &str) -> Result<Option<Value>> {
57        Ok(None)
58    }
59
60    fn encode_literal(
61        &self,
62        _cx: &mut sim_kernel::Cx,
63        _value: Value,
64    ) -> Result<Option<sim_kernel::NumberLiteral>> {
65        Ok(None)
66    }
67}
68
69impl Object for TensorNumberDomain {
70    fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
71        Ok("#<number-domain numbers/tensor>".to_owned())
72    }
73
74    fn as_any(&self) -> &dyn std::any::Any {
75        self
76    }
77}
78
79impl sim_kernel::ObjectCompat for TensorNumberDomain {
80    fn class(&self, cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
81        sim_lib_numbers_core::number_domain_class_stub(cx)
82    }
83    fn as_expr(&self, _cx: &mut sim_kernel::Cx) -> Result<Expr> {
84        Ok(Expr::Symbol(number_domain()))
85    }
86    fn as_table(&self, cx: &mut sim_kernel::Cx) -> Result<Value> {
87        let literal_class = cx
88            .registry()
89            .class_by_symbol(&literal_class_symbol())
90            .cloned()
91            .unwrap_or(cx.factory().symbol(literal_class_symbol())?);
92        let instance_shape = cx
93            .registry()
94            .shape_by_symbol(&literal_instance_shape_symbol())
95            .cloned()
96            .unwrap_or(cx.factory().symbol(literal_instance_shape_symbol())?);
97        let value_shape = cx
98            .registry()
99            .shape_by_symbol(&value_shape_symbol())
100            .cloned()
101            .unwrap_or(cx.factory().symbol(value_shape_symbol())?);
102        number_domain_table(
103            cx,
104            NumberDomainTableSpec::new(
105                number_domain(),
106                "tensor",
107                "value-only",
108                -200,
109                literal_class,
110                instance_shape,
111                value_shape,
112            ),
113        )
114    }
115    fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
116        Some(self)
117    }
118}
119
120struct TensorLiteralShape;
121
122impl sim_shape::Shape for TensorLiteralShape {
123    fn check_value(
124        &self,
125        _cx: &mut sim_kernel::Cx,
126        _value: Value,
127    ) -> Result<sim_shape::ShapeMatch> {
128        Ok(sim_shape::ShapeMatch::reject(
129            "numbers/tensor has no parsed literal surface".to_owned(),
130        ))
131    }
132
133    fn check_expr(&self, _cx: &mut sim_kernel::Cx, _expr: &Expr) -> Result<sim_shape::ShapeMatch> {
134        Ok(sim_shape::ShapeMatch::reject(
135            "numbers/tensor has no parsed literal surface".to_owned(),
136        ))
137    }
138
139    fn describe(&self, _cx: &mut sim_kernel::Cx) -> Result<sim_shape::ShapeDoc> {
140        Ok(sim_shape::ShapeDoc::new("TensorLiteral")
141            .with_detail("placeholder literal shape for the numbers/tensor domain")
142            .with_detail("tensor values are constructed by functions rather than parsed literals"))
143    }
144}
145
146#[sim_citizen_derive::non_citizen(
147    reason = "numbers/tensor literal class marker; tensor values use the numbers/Tensor citizen descriptor",
148    kind = "marker",
149    descriptor = "numbers/Tensor"
150)]
151struct TensorLiteralClass;
152
153impl Object for TensorLiteralClass {
154    fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
155        Ok(format!("#<class {}>", literal_class_symbol()))
156    }
157
158    fn as_any(&self) -> &dyn std::any::Any {
159        self
160    }
161}
162
163impl sim_kernel::ObjectCompat for TensorLiteralClass {
164    fn class(&self, cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
165        if let Some(value) = cx
166            .registry()
167            .class_by_symbol(&Symbol::qualified("core", "Class"))
168        {
169            return Ok(value.clone());
170        }
171        DefaultFactory.class_stub(
172            sim_kernel::CORE_CLASS_CLASS_ID,
173            Symbol::qualified("core", "Class"),
174        )
175    }
176    fn as_expr(&self, _cx: &mut sim_kernel::Cx) -> Result<Expr> {
177        Ok(Expr::Symbol(literal_class_symbol()))
178    }
179}
180
181/// Registered number-domain library that installs the `numbers/tensor` domain.
182///
183/// Loading this [`Lib`] registers the tensor number domain and its value class,
184/// the placeholder literal and value shapes, and the tensor constructor
185/// operations (`tensor`, `scalar`, `vec`, `mat`, `index`, `reshape`, `slice`,
186/// `map`). Specialized element-type backends layer on top through the
187/// [`SpecTensor`](crate::SpecTensor) interface.
188pub struct TensorNumbersLib;
189
190impl TensorNumbersLib {
191    /// Creates the tensor domain library. The value is stateless; the domain,
192    /// classes, shapes, and functions are installed when it is loaded into a
193    /// [`Cx`](sim_kernel::Cx).
194    pub fn new() -> Self {
195        Self
196    }
197}
198
199impl Default for TensorNumbersLib {
200    fn default() -> Self {
201        Self::new()
202    }
203}
204
205impl Lib for TensorNumbersLib {
206    fn manifest(&self) -> LibManifest {
207        LibManifest {
208            id: number_domain(),
209            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
210            abi: AbiVersion { major: 0, minor: 1 },
211            target: LibTarget::HostRegistered,
212            requires: Vec::<Dependency>::new(),
213            capabilities: Vec::new(),
214            exports: vec![
215                Export::NumberDomain {
216                    symbol: number_domain(),
217                    number_domain_id: None,
218                },
219                Export::Class {
220                    symbol: literal_class_symbol(),
221                    class_id: None,
222                },
223                Export::Class {
224                    symbol: tensor_value_class_symbol(),
225                    class_id: None,
226                },
227                Export::Shape {
228                    symbol: literal_instance_shape_symbol(),
229                    shape_id: None,
230                },
231                Export::Shape {
232                    symbol: value_shape_symbol(),
233                    shape_id: None,
234                },
235                export_function(tensor_symbol()),
236                export_function(scalar_symbol()),
237                export_function(vec_symbol()),
238                export_function(mat_symbol()),
239                export_function(index_symbol()),
240                export_function(reshape_symbol()),
241                export_function(slice_symbol()),
242                export_function(map_symbol()),
243            ],
244        }
245    }
246
247    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
248        linker.number_domain_value(
249            number_domain(),
250            DefaultFactory
251                .opaque(Arc::new(TensorNumberDomain))
252                .expect("tensor domain should be boxable"),
253        )?;
254        linker.class_value(
255            literal_class_symbol(),
256            DefaultFactory
257                .opaque(Arc::new(TensorLiteralClass))
258                .expect("tensor literal class should be boxable"),
259        )?;
260        register_tensor_value_class(linker)?;
261        linker.shape_value(
262            literal_instance_shape_symbol(),
263            shape_value(
264                literal_instance_shape_symbol(),
265                Arc::new(TensorLiteralShape),
266            ),
267        )?;
268        linker.shape_value(
269            value_shape_symbol(),
270            shape_value(
271                value_shape_symbol(),
272                Arc::new(DomainNumberValueShape::new(
273                    number_domain(),
274                    "TensorValue",
275                    [
276                        "number value in the numbers/tensor domain",
277                        "accepts tensor-shaped collections of scalar number cells",
278                    ],
279                )),
280            ),
281        )?;
282
283        for symbol in [
284            tensor_symbol(),
285            scalar_symbol(),
286            vec_symbol(),
287            mat_symbol(),
288            index_symbol(),
289            reshape_symbol(),
290            slice_symbol(),
291            map_symbol(),
292        ] {
293            linker.function_value(
294                symbol.clone(),
295                DefaultFactory
296                    .opaque(Arc::new(TensorFunction { symbol }))
297                    .expect("tensor function should be boxable"),
298            )?;
299        }
300        Ok(())
301    }
302}
303
304fn export_function(symbol: Symbol) -> Export {
305    Export::Function {
306        symbol,
307        function_id: None,
308    }
309}