tenferro_runtime/
trace.rs1use std::fmt;
2use std::sync::Arc;
3
4use tenferro_ops::ext_op::ExtensionOp;
5use tenferro_tensor::Tensor;
6
7use crate::extension_cache::ExtensionCacheStore;
8use crate::program::{
9 BindingKey, CoreSemanticOp, FrozenProgram, ProgramBindings, ProgramBuildError,
10 ProgramFinishError, ProgramInputSpec, ProgramValue, ProgramValueMetadata, SemanticProgram,
11 SemanticProgramBuilder,
12};
13
14pub struct TraceContext {
20 builder: SemanticProgramBuilder,
21 extension_caches: ExtensionCacheStore,
22}
23
24impl TraceContext {
25 pub fn new() -> Self {
27 Self {
28 builder: SemanticProgramBuilder::new(),
29 extension_caches: ExtensionCacheStore::new(),
30 }
31 }
32
33 pub fn input(&mut self, spec: ProgramInputSpec) -> Result<TraceValue, ProgramBuildError> {
40 let value = self.builder.input(spec)?;
41 Ok(TraceValue { value })
42 }
43
44 pub fn input_with_default(
53 &mut self,
54 spec: ProgramInputSpec,
55 tensor: Arc<Tensor>,
56 ) -> Result<TraceValue, ProgramBuildError> {
57 let input = self.input(spec)?;
58 self.bind_input(input, tensor)?;
59 Ok(input)
60 }
61
62 pub fn bind_input(
71 &mut self,
72 input: TraceValue,
73 tensor: Arc<Tensor>,
74 ) -> Result<BindingKey, ProgramBuildError> {
75 self.builder.bind_input(input.value, tensor)
76 }
77
78 pub fn add_op(
88 &mut self,
89 op: CoreSemanticOp,
90 inputs: &[TraceValue],
91 ) -> Result<Box<[TraceValue]>, ProgramBuildError> {
92 let inputs = program_values(inputs);
93 self.builder.add_op(op, &inputs).map(trace_values)
94 }
95
96 pub fn add_extension(
107 &mut self,
108 op: Arc<dyn ExtensionOp>,
109 inputs: &[TraceValue],
110 ) -> Result<Box<[TraceValue]>, ProgramBuildError> {
111 let inputs = program_values(inputs);
112 self.builder.add_extension(op, &inputs).map(trace_values)
113 }
114
115 pub fn value_metadata(
122 &self,
123 value: TraceValue,
124 ) -> Result<&ProgramValueMetadata, ProgramBuildError> {
125 self.builder.value_metadata(value.value)
126 }
127
128 pub fn extension_caches_mut(&mut self) -> &mut ExtensionCacheStore {
130 &mut self.extension_caches
131 }
132
133 pub fn finish(self, outputs: &[TraceValue]) -> Result<TracedGraph, ProgramFinishError> {
142 let outputs = program_values(outputs);
143 let frozen = self.builder.finish(&outputs)?;
144 Ok(TracedGraph { frozen })
145 }
146}
147
148impl Default for TraceContext {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[derive(Clone, Copy)]
156pub struct TraceValue {
157 value: ProgramValue,
158}
159
160impl fmt::Debug for TraceValue {
161 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162 formatter.write_str("TraceValue(<opaque>)")
163 }
164}
165
166#[derive(Clone)]
168pub struct TracedGraph {
169 frozen: FrozenProgram,
170}
171
172impl TracedGraph {
173 pub fn program(&self) -> &SemanticProgram {
175 &self.frozen.program
176 }
177
178 pub fn bindings(&self) -> &ProgramBindings {
180 &self.frozen.bindings
181 }
182
183 pub(crate) fn frozen(&self) -> &FrozenProgram {
184 &self.frozen
185 }
186}
187
188impl fmt::Debug for TracedGraph {
189 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
190 formatter
191 .debug_struct("TracedGraph")
192 .field("inputs", &self.program().inputs().len())
193 .field("outputs", &self.program().outputs().len())
194 .field("bindings", &self.bindings().len())
195 .field(
196 "semantic_fingerprint",
197 &self.program().semantic_fingerprint(),
198 )
199 .finish()
200 }
201}
202
203fn program_values(values: &[TraceValue]) -> Vec<ProgramValue> {
204 values.iter().map(|value| value.value).collect()
205}
206
207fn trace_values(values: Box<[ProgramValue]>) -> Box<[TraceValue]> {
208 values
209 .into_vec()
210 .into_iter()
211 .map(|value| TraceValue { value })
212 .collect()
213}
214
215#[cfg(test)]
216mod tests {
217 use tenferro_ops::ShapeExtent;
218 use tenferro_tensor::DType;
219
220 use super::*;
221
222 #[test]
223 fn unknown_input_extents_keep_ordered_semantic_inputs() {
224 let spec = || {
225 ProgramInputSpec::from_metadata(ProgramValueMetadata::from_extents(
226 DType::F64,
227 [ShapeExtent::Unknown],
228 ))
229 };
230 let mut context = TraceContext::new();
231 let first = context.input(spec()).unwrap();
232 let second = context.input(spec()).unwrap();
233 let graph = context.finish(&[first, second]).unwrap();
234
235 assert_eq!(graph.program().inputs().len(), 2);
236 for input in graph.program().inputs() {
237 assert_eq!(
238 graph.program().value_metadata(*input).unwrap().shape(),
239 [ShapeExtent::Unknown]
240 );
241 }
242 }
243}