1use std::{fmt, sync::Arc};
4
5use sim_kernel::{
6 CapabilityName, ClassRef, Cx, DefaultFactory, Error, Factory, Object, Result, Symbol, Value,
7};
8
9use super::{
10 cast::cast_tensor,
11 elementwise::{
12 execute_elementwise_binary_request, execute_elementwise_unary_request,
13 is_elementwise_binary_op, is_elementwise_unary_op, tensor_elementwise_op_symbols,
14 },
15 execution_ops::{
16 execute_tensor_math_request, is_tensor_executor_math_op, tensor_executor_math_op_symbols,
17 },
18 value::{Tensor, build_tensor_value, tensor_value_ref},
19};
20
21pub fn tensor_executor_symbol() -> Symbol {
23 Symbol::qualified("tensor", "executor")
24}
25
26pub fn tensor_site_symbol() -> Symbol {
28 Symbol::new("site/tensor")
29}
30
31pub fn tensor_execute_capability() -> CapabilityName {
34 CapabilityName::new("tensor.execute")
35}
36
37pub fn active_tensor_executor(cx: &Cx) -> Option<Arc<dyn TensorExecutor>> {
39 cx.env().get(&tensor_executor_symbol()).and_then(|value| {
40 value
41 .object()
42 .downcast_ref::<TensorExecutorBinding>()
43 .map(TensorExecutorBinding::executor)
44 })
45}
46
47pub fn tensor_op_symbol() -> Symbol {
49 Symbol::qualified("tensor", "op/tensor")
50}
51
52pub fn scalar_op_symbol() -> Symbol {
54 Symbol::qualified("tensor", "op/scalar")
55}
56
57pub fn vec_op_symbol() -> Symbol {
59 Symbol::qualified("tensor", "op/vec")
60}
61
62pub fn mat_op_symbol() -> Symbol {
64 Symbol::qualified("tensor", "op/mat")
65}
66
67pub fn index_op_symbol() -> Symbol {
69 Symbol::qualified("tensor", "op/index")
70}
71
72pub fn reshape_op_symbol() -> Symbol {
74 Symbol::qualified("tensor", "op/reshape")
75}
76
77pub fn slice_op_symbol() -> Symbol {
79 Symbol::qualified("tensor", "op/slice")
80}
81
82pub fn map_op_symbol() -> Symbol {
84 Symbol::qualified("tensor", "op/map")
85}
86
87pub fn cast_op_symbol() -> Symbol {
89 Symbol::qualified("tensor", "op/cast")
90}
91
92#[derive(Clone, Debug, PartialEq, Eq)]
94pub struct TensorMeta {
95 shape: Arc<[usize]>,
96 dtype: Symbol,
97}
98
99impl TensorMeta {
100 pub fn new(shape: Vec<usize>, dtype: Symbol) -> Self {
102 Self {
103 shape: shape.into(),
104 dtype,
105 }
106 }
107
108 pub fn from_tensor(tensor: &Tensor) -> Self {
110 Self::new(tensor.shape().to_vec(), tensor.dtype().clone())
111 }
112
113 pub fn shape(&self) -> &[usize] {
115 &self.shape
116 }
117
118 pub fn dtype(&self) -> &Symbol {
120 &self.dtype
121 }
122}
123
124#[derive(Clone, Debug)]
126pub struct TensorOp {
127 pub symbol: Symbol,
129 pub attributes: Value,
131}
132
133impl TensorOp {
134 pub fn new(symbol: Symbol, attributes: Value) -> Self {
136 Self { symbol, attributes }
137 }
138
139 pub fn without_attributes(cx: &mut Cx, symbol: Symbol) -> Result<Self> {
141 Ok(Self::new(symbol, cx.factory().nil()?))
142 }
143}
144
145#[derive(Clone)]
147pub struct TensorRequest {
148 pub operation: TensorOp,
150 pub inputs: Arc<[Tensor]>,
152 pub output: TensorMeta,
154}
155
156impl TensorRequest {
157 pub fn new(operation: TensorOp, inputs: Vec<Tensor>, output: TensorMeta) -> Self {
159 Self {
160 operation,
161 inputs: inputs.into(),
162 output,
163 }
164 }
165}
166
167#[derive(Clone)]
169pub enum TensorExecution {
170 Complete(Tensor),
172 Unsupported {
174 reason: Arc<str>,
176 },
177}
178
179#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct TensorExecutorCard {
182 pub symbol: Symbol,
184 pub provider: String,
186 pub locality: Symbol,
188 pub operations: Arc<[Symbol]>,
190 pub device_capability: Option<CapabilityName>,
192}
193
194impl TensorExecutorCard {
195 pub fn new(
197 symbol: Symbol,
198 provider: impl Into<String>,
199 locality: Symbol,
200 operations: Vec<Symbol>,
201 device_capability: Option<CapabilityName>,
202 ) -> Self {
203 Self {
204 symbol,
205 provider: provider.into(),
206 locality,
207 operations: operations.into(),
208 device_capability,
209 }
210 }
211}
212
213#[derive(Clone, Debug, PartialEq, Eq)]
215pub struct SubmissionEvidence {
216 pub executor: Symbol,
218 pub accepted: usize,
220}
221
222impl SubmissionEvidence {
223 pub fn new(executor: Symbol, accepted: usize) -> Self {
225 Self { executor, accepted }
226 }
227}
228
229#[derive(Clone, Debug, PartialEq, Eq)]
231pub enum TensorExecError {
232 CapabilityDenied {
234 capability: CapabilityName,
236 },
237 InvalidRequest {
239 message: Arc<str>,
241 },
242 Unsupported {
244 operation: Symbol,
246 reason: Arc<str>,
248 },
249 Shape {
251 message: Arc<str>,
253 },
254 Eval {
256 message: Arc<str>,
258 },
259}
260
261impl TensorExecError {
262 pub(crate) fn invalid(message: impl Into<Arc<str>>) -> Self {
263 Self::InvalidRequest {
264 message: message.into(),
265 }
266 }
267
268 pub(crate) fn shape(message: impl Into<Arc<str>>) -> Self {
269 Self::Shape {
270 message: message.into(),
271 }
272 }
273
274 pub(crate) fn unsupported(operation: Symbol, reason: impl Into<Arc<str>>) -> Self {
275 Self::Unsupported {
276 operation,
277 reason: reason.into(),
278 }
279 }
280}
281
282impl fmt::Display for TensorExecError {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 match self {
285 Self::CapabilityDenied { capability } => {
286 write!(f, "capability denied: {capability}")
287 }
288 Self::InvalidRequest { message } => f.write_str(message),
289 Self::Unsupported { operation, reason } => {
290 write!(f, "unsupported tensor operation {operation}: {reason}")
291 }
292 Self::Shape { message } => f.write_str(message),
293 Self::Eval { message } => f.write_str(message),
294 }
295 }
296}
297
298impl std::error::Error for TensorExecError {}
299
300impl From<Error> for TensorExecError {
301 fn from(error: Error) -> Self {
302 match error {
303 Error::CapabilityDenied { capability } => Self::CapabilityDenied { capability },
304 Error::WrongShape { diagnostics, .. } => {
305 let message = diagnostics
306 .first()
307 .map(|diagnostic| diagnostic.message.clone())
308 .unwrap_or_else(|| "tensor result shape check failed".to_owned());
309 Self::Shape {
310 message: Arc::from(message),
311 }
312 }
313 other => Self::Eval {
314 message: Arc::from(other.to_string()),
315 },
316 }
317 }
318}
319
320impl From<TensorExecError> for Error {
321 fn from(error: TensorExecError) -> Self {
322 match error {
323 TensorExecError::CapabilityDenied { capability } => {
324 Error::CapabilityDenied { capability }
325 }
326 other => Error::Eval(other.to_string()),
327 }
328 }
329}
330
331pub trait TensorExecutor: Send + Sync + 'static {
333 fn card(&self) -> TensorExecutorCard;
335
336 fn execute(
338 &self,
339 cx: &mut Cx,
340 request: TensorRequest,
341 ) -> std::result::Result<TensorExecution, TensorExecError>;
342
343 fn flush(&self) -> std::result::Result<SubmissionEvidence, TensorExecError>;
345}
346
347pub fn execute_tensor_request(cx: &mut Cx, request: TensorRequest) -> Result<Tensor> {
349 let operation = request.operation.symbol.clone();
350 let executor = active_tensor_executor(cx).unwrap_or_else(|| Arc::new(CpuTensorExecutor::new()));
351 match executor.execute(cx, request).map_err(Error::from)? {
352 TensorExecution::Complete(tensor) => Ok(tensor),
353 TensorExecution::Unsupported { reason } => {
354 Err(Error::from(TensorExecError::unsupported(operation, reason)))
355 }
356 }
357}
358
359pub(crate) fn tensor_executor_value(executor: Arc<dyn TensorExecutor>) -> Result<Value> {
360 DefaultFactory.opaque(Arc::new(TensorExecutorBinding { executor }))
361}
362
363struct TensorExecutorBinding {
364 executor: Arc<dyn TensorExecutor>,
365}
366
367impl TensorExecutorBinding {
368 fn executor(&self) -> Arc<dyn TensorExecutor> {
369 self.executor.clone()
370 }
371}
372
373impl Object for TensorExecutorBinding {
374 fn display(&self, _cx: &mut Cx) -> Result<String> {
375 let card = self.executor.card();
376 Ok(format!("#<tensor-executor {}>", card.symbol))
377 }
378
379 fn as_any(&self) -> &dyn std::any::Any {
380 self
381 }
382}
383
384impl sim_kernel::ObjectCompat for TensorExecutorBinding {
385 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
386 if let Some(value) = cx
387 .registry()
388 .class_by_symbol(&Symbol::qualified("core", "Function"))
389 {
390 return Ok(value.clone());
391 }
392 DefaultFactory.class_stub(
393 sim_kernel::CORE_FUNCTION_CLASS_ID,
394 Symbol::qualified("core", "Function"),
395 )
396 }
397}
398
399#[derive(Clone, Debug, Default)]
401pub struct CpuTensorExecutor;
402
403impl CpuTensorExecutor {
404 pub fn new() -> Self {
406 Self
407 }
408}
409
410impl TensorExecutor for CpuTensorExecutor {
411 fn card(&self) -> TensorExecutorCard {
412 TensorExecutorCard::new(
413 Symbol::qualified("tensor", "executor/cpu"),
414 "cpu",
415 Symbol::qualified("core", "local-fabric"),
416 vec![
417 tensor_op_symbol(),
418 scalar_op_symbol(),
419 vec_op_symbol(),
420 mat_op_symbol(),
421 reshape_op_symbol(),
422 cast_op_symbol(),
423 ]
424 .into_iter()
425 .chain(tensor_elementwise_op_symbols())
426 .chain(tensor_executor_math_op_symbols())
427 .collect(),
428 None,
429 )
430 }
431
432 fn execute(
433 &self,
434 cx: &mut Cx,
435 request: TensorRequest,
436 ) -> std::result::Result<TensorExecution, TensorExecError> {
437 let operation = request.operation.symbol.clone();
438 let result = if operation == tensor_op_symbol() || operation == vec_op_symbol() {
439 execute_tensor(cx, &request)?
440 } else if operation == scalar_op_symbol() {
441 execute_scalar(&request)?
442 } else if operation == mat_op_symbol() {
443 execute_mat(cx, &request)?
444 } else if operation == reshape_op_symbol() {
445 execute_reshape(cx, &request)?
446 } else if operation == cast_op_symbol() {
447 execute_cast(&request)?
448 } else if operation == index_op_symbol() {
449 return Err(TensorExecError::unsupported(
450 operation,
451 "index returns a scalar value, not a tensor",
452 ));
453 } else if is_elementwise_binary_op(&operation) {
454 execute_elementwise_binary_request(cx, &request)?
455 } else if is_elementwise_unary_op(&operation) {
456 execute_elementwise_unary_request(cx, &request)?
457 } else if is_tensor_executor_math_op(&operation) {
458 execute_tensor_math_request(cx, &request)?
459 } else {
460 return Ok(TensorExecution::Unsupported {
461 reason: Arc::from("unknown tensor operation"),
462 });
463 };
464 check_output(&request.output, &result)?;
465 Ok(TensorExecution::Complete(result))
466 }
467
468 fn flush(&self) -> std::result::Result<SubmissionEvidence, TensorExecError> {
469 Ok(SubmissionEvidence::new(
470 Symbol::qualified("tensor", "executor/cpu"),
471 0,
472 ))
473 }
474}
475
476impl Object for CpuTensorExecutor {
477 fn display(&self, _cx: &mut Cx) -> Result<String> {
478 Ok("#<tensor-executor cpu>".to_owned())
479 }
480
481 fn as_any(&self) -> &dyn std::any::Any {
482 self
483 }
484}
485
486impl sim_kernel::ObjectCompat for CpuTensorExecutor {
487 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
488 if let Some(value) = cx
489 .registry()
490 .class_by_symbol(&Symbol::qualified("core", "Function"))
491 {
492 return Ok(value.clone());
493 }
494 DefaultFactory.class_stub(
495 sim_kernel::CORE_FUNCTION_CLASS_ID,
496 Symbol::qualified("core", "Function"),
497 )
498 }
499}
500
501fn execute_tensor(
502 cx: &mut Cx,
503 request: &TensorRequest,
504) -> std::result::Result<Tensor, TensorExecError> {
505 let cells = request
506 .inputs
507 .iter()
508 .map(|tensor| {
509 if tensor.rank() == 0 {
510 tensor.cell(0)
511 } else {
512 Err(Error::Eval(
513 "tensor op/tensor expects scalar tensor inputs as cells".to_owned(),
514 ))
515 }
516 })
517 .collect::<Result<Vec<_>>>()
518 .map_err(TensorExecError::from)?;
519 build_tensor_value(
520 cx,
521 request.output.shape().to_vec(),
522 Some(request.output.dtype().clone()),
523 cells,
524 )
525 .map_err(TensorExecError::from)
526 .and_then(|value| tensor_from_value(&value))
527}
528
529fn execute_scalar(request: &TensorRequest) -> std::result::Result<Tensor, TensorExecError> {
530 let [tensor] = request.inputs.as_ref() else {
531 return Err(TensorExecError::invalid(
532 "scalar operation expects exactly one tensor input",
533 ));
534 };
535 if tensor.rank() != 0 {
536 return Err(TensorExecError::invalid(
537 "scalar operation expects a rank-0 tensor input",
538 ));
539 }
540 Ok(tensor.clone())
541}
542
543fn execute_mat(
544 cx: &mut Cx,
545 request: &TensorRequest,
546) -> std::result::Result<Tensor, TensorExecError> {
547 if request.output.shape().len() != 2 {
548 return Err(TensorExecError::invalid(
549 "matrix operation expects rank-2 output metadata",
550 ));
551 }
552 let row_width = request.output.shape()[1];
553 let mut cells = Vec::new();
554 for row in request.inputs.iter() {
555 if row.shape() != [row_width] {
556 return Err(TensorExecError::invalid(
557 "matrix operation inputs must be rank-1 rows matching output width",
558 ));
559 }
560 cells.extend(row.cells().map_err(TensorExecError::from)?.iter().cloned());
561 }
562 build_tensor_value(
563 cx,
564 request.output.shape().to_vec(),
565 Some(request.output.dtype().clone()),
566 cells,
567 )
568 .map_err(TensorExecError::from)
569 .and_then(|value| tensor_from_value(&value))
570}
571
572fn execute_reshape(
573 cx: &mut Cx,
574 request: &TensorRequest,
575) -> std::result::Result<Tensor, TensorExecError> {
576 let [tensor] = request.inputs.as_ref() else {
577 return Err(TensorExecError::invalid(
578 "reshape operation expects exactly one tensor input",
579 ));
580 };
581 build_tensor_value(
582 cx,
583 request.output.shape().to_vec(),
584 Some(request.output.dtype().clone()),
585 tensor
586 .cells()
587 .map_err(TensorExecError::from)?
588 .iter()
589 .cloned()
590 .collect(),
591 )
592 .map_err(TensorExecError::from)
593 .and_then(|value| tensor_from_value(&value))
594}
595
596fn execute_cast(request: &TensorRequest) -> std::result::Result<Tensor, TensorExecError> {
597 let [tensor] = request.inputs.as_ref() else {
598 return Err(TensorExecError::invalid(
599 "cast operation expects exactly one tensor input",
600 ));
601 };
602 cast_tensor(tensor, request.output.dtype().clone()).map_err(TensorExecError::from)
603}
604
605fn tensor_from_value(value: &Value) -> std::result::Result<Tensor, TensorExecError> {
606 tensor_value_ref(value)
607 .cloned()
608 .ok_or_else(|| TensorExecError::invalid("tensor executor produced a non-tensor value"))
609}
610
611fn check_output(
612 expected: &TensorMeta,
613 result: &Tensor,
614) -> std::result::Result<(), TensorExecError> {
615 if expected.shape() != result.shape() {
616 return Err(TensorExecError::shape(format!(
617 "tensor result shape {:?} did not match {:?}",
618 result.shape(),
619 expected.shape()
620 )));
621 }
622 if expected.dtype() != result.dtype() {
623 return Err(TensorExecError::shape(format!(
624 "tensor result dtype {} did not match {}",
625 result.dtype(),
626 expected.dtype()
627 )));
628 }
629 Ok(())
630}