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