sim_lib_numbers_tensor/implementation/
value.rs1use std::sync::Arc;
5
6use sim_kernel::{
7 ClassRef, Cx, DefaultFactory, Error, Expr, Factory, NumberValue, Object, ObjectCompat,
8 ObjectEncode, ObjectEncoding, Result, Symbol, Value,
9};
10
11use super::citizen::tensor_value_class_symbol;
12use super::domain::number_domain;
13use super::storage::{BoxedTensorStorage, TensorLocation, TensorStorage};
14use super::validation::{
15 choose_dtype, validate_cells, validate_dtype_accepts_cells, validate_exact_cell_dtype,
16 validate_shape_and_data_len,
17};
18
19#[derive(Clone)]
27pub struct Tensor {
28 shape: Arc<[usize]>,
30 dtype: Symbol,
33 storage: Arc<dyn TensorStorage>,
35}
36
37impl Tensor {
38 pub fn new_checked(
41 cx: &mut Cx,
42 shape: Vec<usize>,
43 dtype: Symbol,
44 data: Vec<Value>,
45 ) -> Result<Self> {
46 validate_shape_and_data_len(&shape, data.len())?;
47 validate_cells(cx, &data)?;
48 validate_dtype_accepts_cells(cx, &dtype, &data)?;
49 Self::from_storage(
50 shape,
51 dtype.clone(),
52 Arc::new(BoxedTensorStorage::new(dtype, data)),
53 )
54 }
55
56 pub fn new_exact(shape: Vec<usize>, dtype: Symbol, data: Vec<Value>) -> Result<Self> {
62 validate_shape_and_data_len(&shape, data.len())?;
63 validate_exact_cell_dtype(&dtype, &data)?;
64 Self::from_storage(
65 shape,
66 dtype.clone(),
67 Arc::new(BoxedTensorStorage::new(dtype, data)),
68 )
69 }
70
71 pub fn from_storage(
77 shape: Vec<usize>,
78 dtype: Symbol,
79 storage: Arc<dyn TensorStorage>,
80 ) -> Result<Self> {
81 validate_shape_and_data_len(&shape, storage.len())?;
82 if storage.dtype() != &dtype {
83 return Err(Error::Eval(format!(
84 "tensor dtype {dtype} does not match storage dtype {}",
85 storage.dtype()
86 )));
87 }
88 Ok(Self {
89 shape: shape.into(),
90 dtype,
91 storage,
92 })
93 }
94
95 pub fn shape(&self) -> &[usize] {
97 &self.shape
98 }
99
100 pub fn dtype(&self) -> &Symbol {
102 &self.dtype
103 }
104
105 pub fn location(&self) -> TensorLocation {
107 self.storage.location()
108 }
109
110 pub fn storage(&self) -> &Arc<dyn TensorStorage> {
116 &self.storage
117 }
118
119 pub fn len(&self) -> usize {
121 self.storage.len()
122 }
123
124 pub fn is_empty(&self) -> bool {
126 self.storage.is_empty()
127 }
128
129 pub fn cell(&self, index: usize) -> Result<Value> {
131 if index >= self.len() {
132 return Err(Error::Eval(
133 "tensor cell index was out of bounds".to_owned(),
134 ));
135 }
136 self.storage.cell(index)
137 }
138
139 pub fn materialize(&self) -> Result<Arc<dyn TensorStorage>> {
141 let storage = if self.storage.location() == TensorLocation::Host {
142 self.storage.clone()
143 } else {
144 self.storage.materialize()?
145 };
146 if storage.location() != TensorLocation::Host {
147 return Err(Error::Eval(
148 "tensor materialization did not produce host storage".to_owned(),
149 ));
150 }
151 if storage.dtype() != &self.dtype {
152 return Err(Error::Eval(format!(
153 "materialized tensor dtype {} does not match {}",
154 storage.dtype(),
155 self.dtype
156 )));
157 }
158 if storage.len() != self.len() {
159 return Err(Error::Eval(format!(
160 "materialized tensor length {} does not match {}",
161 storage.len(),
162 self.len()
163 )));
164 }
165 Ok(storage)
166 }
167
168 pub fn cells(&self) -> Result<Arc<[Value]>> {
174 let storage = self.materialize()?;
175 if let Some(boxed) = storage.as_any().downcast_ref::<BoxedTensorStorage>() {
176 return Ok(boxed.cells());
177 }
178 (0..storage.len())
179 .map(|index| storage.cell(index))
180 .collect::<Result<Vec<_>>>()
181 .map(Arc::from)
182 }
183
184 pub fn data(&self) -> Arc<[Value]> {
192 self.cells()
193 .expect("Tensor::data requires host-observable cells; use Tensor::cells for fallible observation")
194 }
195
196 pub fn rank(&self) -> usize {
199 self.shape.len()
200 }
201
202 pub fn flat_offset(shape: &[usize], indices: &[usize]) -> Result<usize> {
221 if shape.len() != indices.len() {
222 return Err(Error::Eval("tensor index rank mismatch".to_owned()));
223 }
224 let mut stride = 1usize;
225 let mut offset = 0usize;
226 for (dim, index) in shape.iter().rev().zip(indices.iter().rev()) {
227 if *index >= *dim {
228 return Err(Error::Eval("tensor index was out of bounds".to_owned()));
229 }
230 offset += index * stride;
231 stride = stride.saturating_mul(*dim);
232 }
233 Ok(offset)
234 }
235
236 pub fn coordinates(shape: &[usize]) -> Vec<Vec<usize>> {
239 if shape.is_empty() {
240 return vec![Vec::new()];
241 }
242 if shape.contains(&0) {
243 return Vec::new();
244 }
245 let mut out = Vec::new();
246 let mut coord = vec![0usize; shape.len()];
247 loop {
248 out.push(coord.clone());
249 let mut axis = shape.len();
250 while axis > 0 {
251 axis -= 1;
252 coord[axis] += 1;
253 if coord[axis] < shape[axis] {
254 break;
255 }
256 coord[axis] = 0;
257 if axis == 0 {
258 return out;
259 }
260 }
261 }
262 }
263}
264
265impl Object for Tensor {
266 fn display(&self, cx: &mut Cx) -> Result<String> {
267 match self.as_expr(cx)? {
268 Expr::Call { .. } => Ok(format!("{}<{:?}>", tensor_display_name(), self.shape)),
269 expr => Ok(format!("{expr:?}")),
270 }
271 }
272
273 fn as_any(&self) -> &dyn std::any::Any {
274 self
275 }
276}
277
278impl sim_kernel::ObjectCompat for Tensor {
279 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
280 if let Some(value) = cx.registry().class_by_symbol(&tensor_value_class_symbol()) {
281 return Ok(value.clone());
282 }
283 if let Some(value) = cx
284 .registry()
285 .class_by_symbol(&Symbol::qualified("core", "Number"))
286 {
287 return Ok(value.clone());
288 }
289 DefaultFactory.class_stub(
290 sim_kernel::CORE_NUMBER_CLASS_ID,
291 Symbol::qualified("core", "Number"),
292 )
293 }
294 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
295 let cells = self.cells()?;
296 match self.rank() {
297 0 => Ok(Expr::Call {
298 operator: Box::new(Expr::Symbol(Symbol::new("scalar"))),
299 args: vec![
300 cells
301 .first()
302 .ok_or_else(|| Error::Eval("scalar tensor is missing its cell".to_owned()))?
303 .object()
304 .as_expr(cx)?,
305 ],
306 }),
307 1 => Ok(Expr::Vector(exprs(cx, &cells)?)),
308 2 => {
309 let width = self.shape[1];
310 let rows = if width == 0 {
311 vec![Expr::Vector(Vec::new()); self.shape[0]]
312 } else {
313 cells
314 .chunks(width)
315 .map(|row| exprs(cx, row).map(Expr::Vector))
316 .collect::<Result<Vec<_>>>()?
317 };
318 Ok(Expr::Vector(rows))
319 }
320 _ => Ok(Expr::Call {
321 operator: Box::new(Expr::Symbol(Symbol::new("tensor"))),
322 args: vec![
323 Expr::Vector(
324 self.shape
325 .iter()
326 .map(|dim| Expr::String(dim.to_string()))
327 .collect(),
328 ),
329 Expr::Symbol(self.dtype.clone()),
330 Expr::Vector(exprs(cx, &cells)?),
331 ],
332 }),
333 }
334 }
335 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
336 let shape = cx.factory().list(
337 self.shape
338 .iter()
339 .map(|dim| cx.factory().string(dim.to_string()))
340 .collect::<Result<Vec<_>>>()?,
341 )?;
342 let data = cx.factory().list(self.cells()?.to_vec())?;
343 cx.factory().table(vec![
344 (
345 Symbol::new("kind"),
346 cx.factory().string("tensor".to_owned())?,
347 ),
348 (Symbol::new("shape"), shape),
349 (
350 Symbol::new("dtype"),
351 cx.factory().symbol(self.dtype.clone())?,
352 ),
353 (Symbol::new("data"), data),
354 ])
355 }
356 fn as_number_value(&self) -> Option<&dyn NumberValue> {
357 Some(self)
358 }
359
360 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
361 Some(self)
362 }
363}
364
365impl NumberValue for Tensor {
366 fn number_domain(&self, _cx: &mut Cx) -> Result<Symbol> {
367 Ok(number_domain())
368 }
369}
370
371impl ObjectEncode for Tensor {
372 fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
373 let cells = self.cells()?;
374 Ok(ObjectEncoding::Constructor {
375 class: tensor_value_class_symbol(),
376 args: vec![
377 Expr::Symbol(Symbol::new("v1")),
378 Expr::List(
379 self.shape
380 .iter()
381 .map(|dim| {
382 Expr::Number(sim_kernel::NumberLiteral {
383 domain: Symbol::qualified("citizen", "int"),
384 canonical: dim.to_string(),
385 })
386 })
387 .collect(),
388 ),
389 Expr::List(exprs(cx, &cells)?),
390 Expr::Symbol(self.dtype.clone()),
391 ],
392 })
393 }
394}
395
396impl sim_citizen::Citizen for Tensor {
397 fn citizen_symbol() -> Symbol {
398 tensor_value_class_symbol()
399 }
400
401 fn citizen_version() -> u32 {
402 1
403 }
404
405 fn citizen_arity() -> usize {
406 3
407 }
408
409 fn citizen_fields() -> &'static [&'static str] {
410 &["shape", "data", "domain"]
411 }
412}
413
414pub fn build_tensor_value(
422 cx: &mut Cx,
423 shape: Vec<usize>,
424 dtype_hint: Option<Symbol>,
425 data: Vec<Value>,
426) -> Result<Value> {
427 validate_shape_and_data_len(&shape, data.len())?;
428 let dtype = choose_dtype(cx, dtype_hint, &data)?;
429 let tensor = Tensor::new_checked(cx, shape, dtype, data)?;
430 cx.factory().opaque(Arc::new(tensor))
431}
432
433pub fn build_scalar_tensor_value(cx: &mut Cx, value: Value) -> Result<Value> {
435 build_tensor_value(cx, Vec::new(), None, vec![value])
436}
437
438pub fn tensor_value_ref(value: &Value) -> Option<&Tensor> {
440 value.object().downcast_ref::<Tensor>()
441}
442
443pub fn tensor_dtype(tensor: &Tensor) -> &Symbol {
445 tensor.dtype()
446}
447
448pub fn flatten_tensor_scalar_cells(tensor: &Tensor) -> Vec<Value> {
453 tensor.data().iter().cloned().collect()
454}
455
456pub fn tensor_display_name() -> &'static str {
457 "tensor"
458}
459
460fn exprs(cx: &mut Cx, data: &[Value]) -> Result<Vec<Expr>> {
461 data.iter()
462 .map(|value| value.object().as_expr(cx))
463 .collect()
464}