1use std::sync::Arc;
10
11use tenferro_ops::dim_expr::DimExpr;
12use tenferro_ops::ext_op::ExtensionOp;
13use tenferro_ops::shape_extent::ShapeExtent;
14use tenferro_ops::std_tensor_op::StdTensorOp;
15use tenferro_tensor::{DType, IntoShapeVec, Tensor, TensorScalar, TensorValue};
16
17use crate::checkpoint::RetainedValue;
18use crate::error::{Error, ErrorPhase, Result};
19use crate::program::{CoreSemanticOp, ProgramBuildError, ProgramInputSpec, ProgramValueMetadata};
20use crate::sym_dim::SymDim;
21use crate::trace::{TraceContext, TraceValue};
22use crate::traced::TracedTensor;
23
24pub(crate) fn core_op(op: &StdTensorOp) -> Result<CoreSemanticOp> {
31 CoreSemanticOp::try_from(op).map_err(|source| {
32 Error::runtime_state_source("semantic core operation", ErrorPhase::GraphBuild, source)
33 })
34}
35
36pub(crate) fn append_core(
38 ctx: &TraceContext,
39 op: &StdTensorOp,
40 inputs: &[TraceValue],
41) -> Result<Box<[TraceValue]>> {
42 let core = core_op(op)?;
43 ctx.add_op(core, inputs)
44 .map_err(|source| build_error("semantic standard operation", source))
45}
46
47pub(crate) fn append_extension(
49 ctx: &TraceContext,
50 op: Arc<dyn ExtensionOp>,
51 inputs: &[TraceValue],
52) -> Result<Box<[TraceValue]>> {
53 ctx.add_extension(op, inputs)
54 .map_err(|source| build_error("semantic extension operation", source))
55}
56
57pub(crate) fn finish_value(
63 ctx: &TraceContext,
64 value: TraceValue,
65 rank: usize,
66 dtype: DType,
67 hint: Option<Vec<SymDim>>,
68) -> Result<TracedTensor> {
69 let metadata = ctx
70 .value_metadata(value)
71 .map_err(|source| build_error("semantic value metadata", source))?;
72 debug_assert_eq!(
73 rank,
74 metadata.shape().len(),
75 "cached rank must match the appended value"
76 );
77 debug_assert_eq!(
78 dtype,
79 metadata.dtype(),
80 "cached dtype must match the appended value"
81 );
82 let derived = metadata_sym_shape(&metadata);
83 let symbolic_id = next_symbolic_id();
84 let hint = if crate::shape_infer::extent_precision(metadata.shape().iter())
88 == crate::shape_infer::ExtentPrecision::Exact
89 {
90 hint
91 } else {
92 None
93 };
94 Ok(TracedTensor {
95 ctx: ctx.clone(),
96 value,
97 rank: metadata.shape().len(),
98 dtype: metadata.dtype(),
99 shape_hint: restamp_hint(derived.or(hint), symbolic_id),
100 data: None,
101 symbolic_id,
102 })
103}
104
105fn finish_leaf(
107 ctx: &TraceContext,
108 value: TraceValue,
109 rank: usize,
110 dtype: DType,
111 hint: Option<Vec<SymDim>>,
112 data: Option<Arc<RetainedValue>>,
113) -> Result<TracedTensor> {
114 let mut tensor = finish_value(ctx, value, rank, dtype, hint)?;
115 tensor.data = data;
116 Ok(tensor)
117}
118
119pub(crate) fn finish_derived(ctx: &TraceContext, value: TraceValue) -> Result<TracedTensor> {
121 let metadata = ctx
122 .value_metadata(value)
123 .map_err(|source| build_error("semantic value metadata", source))?;
124 let rank = metadata.shape().len();
125 let dtype = metadata.dtype();
126 let hint = metadata_sym_shape(&metadata);
127 finish_value(ctx, value, rank, dtype, hint)
128}
129
130pub(crate) fn finish_bound_value(
132 ctx: &TraceContext,
133 value: TraceValue,
134 data: Arc<RetainedValue>,
135) -> Result<TracedTensor> {
136 let metadata = ctx
137 .value_metadata(value)
138 .map_err(|source| build_error("semantic value metadata", source))?;
139 let rank = metadata.shape().len();
140 let dtype = metadata.dtype();
141 let hint = metadata_sym_shape(&metadata);
142 let mut tensor = finish_value(ctx, value, rank, dtype, hint)?;
143 tensor.data = Some(data);
144 Ok(tensor)
145}
146
147#[doc(hidden)]
156pub fn dim_expr_to_sym_dim_public(expr: &DimExpr) -> Option<SymDim> {
157 dim_expr_to_sym_dim(expr)
158}
159
160pub(crate) fn dim_expr_to_sym_dim(expr: &DimExpr) -> Option<SymDim> {
161 Some(match expr {
162 DimExpr::Const(value) => SymDim::from(*value),
163 DimExpr::InputDim { input_idx, axis } => SymDim::tensor_axis(*input_idx as u64, *axis),
164 DimExpr::Add(lhs, rhs) => dim_expr_to_sym_dim(lhs)? + dim_expr_to_sym_dim(rhs)?,
165 DimExpr::Sub(lhs, rhs) => dim_expr_to_sym_dim(lhs)? - dim_expr_to_sym_dim(rhs)?,
166 DimExpr::Mul(lhs, rhs) => dim_expr_to_sym_dim(lhs)? * dim_expr_to_sym_dim(rhs)?,
167 DimExpr::FloorDiv(lhs, rhs) => dim_expr_to_sym_dim(lhs)? / dim_expr_to_sym_dim(rhs)?,
168 DimExpr::Min(lhs, rhs) => dim_expr_to_sym_dim(lhs)?.min(dim_expr_to_sym_dim(rhs)?),
169 DimExpr::Max(lhs, rhs) => dim_expr_to_sym_dim(lhs)?.max(dim_expr_to_sym_dim(rhs)?),
170 })
171}
172
173fn next_symbolic_id() -> u64 {
179 static NEXT_SYMBOLIC_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
180 NEXT_SYMBOLIC_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
181}
182
183fn restamp_hint(hint: Option<Vec<SymDim>>, own_id: u64) -> Option<Vec<SymDim>> {
189 hint.map(|shape| {
190 shape
191 .into_iter()
192 .enumerate()
193 .map(|(axis, dim)| {
194 if dim.constant_value().is_some() {
195 dim
196 } else {
197 SymDim::tensor_axis(own_id, axis)
198 }
199 })
200 .collect()
201 })
202}
203
204fn metadata_sym_shape(metadata: &ProgramValueMetadata) -> Option<Vec<SymDim>> {
206 metadata
207 .shape()
208 .iter()
209 .map(|extent| match extent {
210 ShapeExtent::Exact(expr) => dim_expr_to_sym_dim(expr),
211 _ => None,
212 })
213 .collect()
214}
215
216pub(crate) fn build_error(op: &'static str, source: ProgramBuildError) -> Error {
217 match source {
218 ProgramBuildError::Metadata { source } => *source,
219 other => Error::runtime_state_source(op, ErrorPhase::GraphBuild, other),
220 }
221}
222
223fn concrete_spec(
224 dtype: DType,
225 shape: &[usize],
226 identity: Option<&'static str>,
227) -> ProgramInputSpec {
228 let metadata = ProgramValueMetadata::new(dtype, shape.iter().map(|&size| DimExpr::Const(size)));
229 let spec = ProgramInputSpec::from_metadata(metadata);
230 match identity {
231 Some(identity) => spec.with_scalar_identity(identity),
232 None => spec,
233 }
234}
235
236fn concrete_hint(shape: &[usize]) -> Option<Vec<SymDim>> {
237 Some(shape.iter().map(|&size| SymDim::from(size)).collect())
238}
239
240fn symbolic_spec(dtype: DType, rank: usize, input_idx: usize) -> ProgramInputSpec {
241 ProgramInputSpec::new(
242 dtype,
243 (0..rank).map(|axis| DimExpr::InputDim { input_idx, axis }),
244 )
245}
246
247impl TraceContext {
248 pub fn input_concrete_shape(&self, dtype: DType, shape: &[usize]) -> Result<TracedTensor> {
268 self.input_concrete_shape_with_identity(dtype, shape, None)
269 }
270
271 pub fn input_concrete_shape_declaring_scalar(
281 &self,
282 dtype: DType,
283 shape: &[usize],
284 identity: &'static str,
285 ) -> Result<TracedTensor> {
286 self.input_concrete_shape_with_identity(dtype, shape, Some(identity))
287 }
288
289 fn input_concrete_shape_with_identity(
290 &self,
291 dtype: DType,
292 shape: &[usize],
293 identity: Option<&'static str>,
294 ) -> Result<TracedTensor> {
295 let spec = concrete_spec(dtype, shape, identity);
296 let value = self
297 .input(spec)
298 .map_err(|source| build_error("trace input", source))?;
299 finish_leaf(self, value, shape.len(), dtype, concrete_hint(shape), None)
300 }
301
302 pub fn input_symbolic_shape(&self, dtype: DType, rank: usize) -> Result<TracedTensor> {
321 let index = self.input_count();
322 let spec = symbolic_spec(dtype, rank, index);
323 let value = self
324 .input(spec)
325 .map_err(|source| build_error("trace input", source))?;
326 finish_leaf(self, value, rank, dtype, metadata_hint(self, value)?, None)
327 }
328
329 pub fn from_tensor_concrete_shape(&self, tensor: Tensor) -> Result<TracedTensor> {
349 self.tensor_concrete_shape_with_identity(tensor, None)
350 }
351
352 pub fn from_tensor_concrete_shape_declaring_scalar(
362 &self,
363 tensor: Tensor,
364 identity: &'static str,
365 ) -> Result<TracedTensor> {
366 self.tensor_concrete_shape_with_identity(tensor, Some(identity))
367 }
368
369 fn tensor_concrete_shape_with_identity(
370 &self,
371 tensor: Tensor,
372 identity: Option<&'static str>,
373 ) -> Result<TracedTensor> {
374 let dtype = tensor.dtype();
375 let shape = tensor.shape().to_vec();
376 let spec = concrete_spec(dtype, &shape, identity);
377 let data = Arc::new(RetainedValue::from_tensor(tensor));
378 let value = self
379 .input_bound(spec, Arc::clone(&data))
380 .map_err(|source| build_error("trace constant", source))?;
381 finish_leaf(
382 self,
383 value,
384 shape.len(),
385 dtype,
386 concrete_hint(&shape),
387 Some(data),
388 )
389 }
390
391 pub fn from_tensor_symbolic_shape(&self, tensor: Tensor) -> Result<TracedTensor> {
397 let data = Arc::new(RetainedValue::from_tensor(tensor));
398 self.from_shared_tensor_value_symbolic_shape(data)
399 }
400
401 #[doc(hidden)]
408 pub fn from_tensor_value_symbolic_shape(&self, value: TensorValue) -> Result<TracedTensor> {
409 let data = Arc::new(RetainedValue::from_tensor_value(value)?);
410 self.from_shared_tensor_value_symbolic_shape(data)
411 }
412
413 #[doc(hidden)]
419 pub fn from_shared_tensor_value_symbolic_shape(
420 &self,
421 data: Arc<RetainedValue>,
422 ) -> Result<TracedTensor> {
423 let rank = data.shape().len();
424 let dtype = data.dtype();
425 let index = self.input_count();
426 let spec = symbolic_spec(dtype, rank, index);
427 let value = self
428 .input_bound(spec, Arc::clone(&data))
429 .map_err(|source| build_error("trace constant", source))?;
430 finish_leaf(
431 self,
432 value,
433 rank,
434 dtype,
435 metadata_hint(self, value)?,
436 Some(data),
437 )
438 }
439
440 pub fn from_vec_col_major<T: TensorScalar>(
447 &self,
448 shape: impl IntoShapeVec,
449 data: Vec<T>,
450 ) -> Result<TracedTensor> {
451 let tensor = Tensor::from_vec_col_major(shape, data)?;
452 self.from_tensor_concrete_shape(tensor)
453 }
454}
455
456fn metadata_hint(ctx: &TraceContext, value: TraceValue) -> Result<Option<Vec<SymDim>>> {
457 let metadata = ctx
458 .value_metadata(value)
459 .map_err(|source| build_error("semantic value metadata", source))?;
460 Ok(metadata_sym_shape(&metadata))
461}