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