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 rank(&self) -> usize {
187 self.shape.len()
188 }
189
190 pub fn flat_offset(shape: &[usize], indices: &[usize]) -> Result<usize> {
209 if shape.len() != indices.len() {
210 return Err(Error::Eval("tensor index rank mismatch".to_owned()));
211 }
212 let mut stride = 1usize;
213 let mut offset = 0usize;
214 for (dim, index) in shape.iter().rev().zip(indices.iter().rev()) {
215 if *index >= *dim {
216 return Err(Error::Eval("tensor index was out of bounds".to_owned()));
217 }
218 offset += index * stride;
219 stride = stride.saturating_mul(*dim);
220 }
221 Ok(offset)
222 }
223
224 pub fn coordinates(shape: &[usize]) -> Vec<Vec<usize>> {
227 if shape.is_empty() {
228 return vec![Vec::new()];
229 }
230 if shape.contains(&0) {
231 return Vec::new();
232 }
233 let mut out = Vec::new();
234 let mut coord = vec![0usize; shape.len()];
235 loop {
236 out.push(coord.clone());
237 let mut axis = shape.len();
238 while axis > 0 {
239 axis -= 1;
240 coord[axis] += 1;
241 if coord[axis] < shape[axis] {
242 break;
243 }
244 coord[axis] = 0;
245 if axis == 0 {
246 return out;
247 }
248 }
249 }
250 }
251}
252
253impl Object for Tensor {
254 fn display(&self, cx: &mut Cx) -> Result<String> {
255 match self.as_expr(cx)? {
256 Expr::Call { .. } => Ok(format!("{}<{:?}>", tensor_display_name(), self.shape)),
257 expr => Ok(format!("{expr:?}")),
258 }
259 }
260
261 fn as_any(&self) -> &dyn std::any::Any {
262 self
263 }
264}
265
266impl sim_kernel::ObjectCompat for Tensor {
267 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
268 if let Some(value) = cx.registry().class_by_symbol(&tensor_value_class_symbol()) {
269 return Ok(value.clone());
270 }
271 if let Some(value) = cx
272 .registry()
273 .class_by_symbol(&Symbol::qualified("core", "Number"))
274 {
275 return Ok(value.clone());
276 }
277 DefaultFactory.class_stub(
278 sim_kernel::CORE_NUMBER_CLASS_ID,
279 Symbol::qualified("core", "Number"),
280 )
281 }
282 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
283 let cells = self.cells()?;
284 match self.rank() {
285 0 => Ok(Expr::Call {
286 operator: Box::new(Expr::Symbol(Symbol::new("scalar"))),
287 args: vec![
288 cells
289 .first()
290 .ok_or_else(|| Error::Eval("scalar tensor is missing its cell".to_owned()))?
291 .object()
292 .as_expr(cx)?,
293 ],
294 }),
295 1 => Ok(Expr::Vector(exprs(cx, &cells)?)),
296 2 => {
297 let width = self.shape[1];
298 let rows = if width == 0 {
299 vec![Expr::Vector(Vec::new()); self.shape[0]]
300 } else {
301 cells
302 .chunks(width)
303 .map(|row| exprs(cx, row).map(Expr::Vector))
304 .collect::<Result<Vec<_>>>()?
305 };
306 Ok(Expr::Vector(rows))
307 }
308 _ => Ok(Expr::Call {
309 operator: Box::new(Expr::Symbol(Symbol::new("tensor"))),
310 args: vec![
311 Expr::Vector(
312 self.shape
313 .iter()
314 .map(|dim| Expr::String(dim.to_string()))
315 .collect(),
316 ),
317 Expr::Symbol(self.dtype.clone()),
318 Expr::Vector(exprs(cx, &cells)?),
319 ],
320 }),
321 }
322 }
323 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
324 let shape = cx.factory().list(
325 self.shape
326 .iter()
327 .map(|dim| cx.factory().string(dim.to_string()))
328 .collect::<Result<Vec<_>>>()?,
329 )?;
330 let data = cx.factory().list(self.cells()?.to_vec())?;
331 cx.factory().table(vec![
332 (
333 Symbol::new("kind"),
334 cx.factory().string("tensor".to_owned())?,
335 ),
336 (Symbol::new("shape"), shape),
337 (
338 Symbol::new("dtype"),
339 cx.factory().symbol(self.dtype.clone())?,
340 ),
341 (Symbol::new("data"), data),
342 ])
343 }
344 fn as_number_value(&self) -> Option<&dyn NumberValue> {
345 Some(self)
346 }
347
348 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
349 Some(self)
350 }
351}
352
353impl NumberValue for Tensor {
354 fn number_domain(&self, _cx: &mut Cx) -> Result<Symbol> {
355 Ok(number_domain())
356 }
357}
358
359impl ObjectEncode for Tensor {
360 fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
361 let cells = self.cells()?;
362 Ok(ObjectEncoding::Constructor {
363 class: tensor_value_class_symbol(),
364 args: vec![
365 Expr::Symbol(Symbol::new("v1")),
366 Expr::List(
367 self.shape
368 .iter()
369 .map(|dim| {
370 Expr::Number(sim_kernel::NumberLiteral {
371 domain: Symbol::qualified("citizen", "int"),
372 canonical: dim.to_string(),
373 })
374 })
375 .collect(),
376 ),
377 Expr::List(exprs(cx, &cells)?),
378 Expr::Symbol(self.dtype.clone()),
379 ],
380 })
381 }
382}
383
384impl sim_citizen::Citizen for Tensor {
385 fn citizen_symbol() -> Symbol {
386 tensor_value_class_symbol()
387 }
388
389 fn citizen_version() -> u32 {
390 1
391 }
392
393 fn citizen_arity() -> usize {
394 3
395 }
396
397 fn citizen_fields() -> &'static [&'static str] {
398 &["shape", "data", "domain"]
399 }
400}
401
402pub fn build_tensor_value(
410 cx: &mut Cx,
411 shape: Vec<usize>,
412 dtype_hint: Option<Symbol>,
413 data: Vec<Value>,
414) -> Result<Value> {
415 validate_shape_and_data_len(&shape, data.len())?;
416 let dtype = choose_dtype(cx, dtype_hint, &data)?;
417 let tensor = Tensor::new_checked(cx, shape, dtype, data)?;
418 cx.factory().opaque(Arc::new(tensor))
419}
420
421pub fn build_scalar_tensor_value(cx: &mut Cx, value: Value) -> Result<Value> {
423 build_tensor_value(cx, Vec::new(), None, vec![value])
424}
425
426pub fn tensor_value_ref(value: &Value) -> Option<&Tensor> {
428 value.object().downcast_ref::<Tensor>()
429}
430
431pub fn tensor_dtype(tensor: &Tensor) -> &Symbol {
433 tensor.dtype()
434}
435
436pub fn flatten_tensor_scalar_cells(tensor: &Tensor) -> Result<Arc<[Value]>> {
438 tensor.cells()
439}
440
441pub fn tensor_display_name() -> &'static str {
442 "tensor"
443}
444
445fn exprs(cx: &mut Cx, data: &[Value]) -> Result<Vec<Expr>> {
446 data.iter()
447 .map(|value| value.object().as_expr(cx))
448 .collect()
449}