1use std::sync::Arc;
2
3use computegraph::GraphOperation;
4use tenferro_ops::dim_expr::DimExpr;
5use tenferro_ops::ext_op::ExtensionOp;
6use tenferro_ops::std_tensor_op::StdTensorOp;
7use tenferro_tensor::{
8 CompareDir, DType, DotGeneralConfig, GatherConfig, PadConfig, ScatterConfig, SliceConfig,
9};
10
11use super::metadata::SemanticProvenance;
12use super::{
13 Alias, Effect, ProgramValue, SemanticPlacementConstraint, SemanticProvenanceView, ShapeGuard,
14};
15
16#[derive(Clone, Debug, PartialEq)]
18#[non_exhaustive]
19pub enum CoreSemanticOp {
20 Add,
21 Sub,
22 Mul,
23 Neg,
24 Conj,
25 DotGeneral {
26 config: DotGeneralConfig,
27 },
28 Transpose {
29 perm: Vec<usize>,
30 },
31 Reshape {
32 to_shape: Vec<DimExpr>,
33 },
34 BroadcastInDim {
35 shape: Vec<DimExpr>,
36 dims: Vec<usize>,
37 },
38 Convert {
39 from: DType,
40 to: DType,
41 },
42 Constant {
43 dtype: DType,
44 bytes: Vec<u8>,
45 },
46 ReduceSum {
47 axes: Vec<usize>,
48 },
49 ReduceSumSquares {
50 axes: Vec<usize>,
51 },
52 Div,
53 Rem,
54 Abs,
55 Sign,
56 Maximum,
57 Minimum,
58 Compare(CompareDir),
59 Select,
60 Clamp,
61 Exp,
62 Log,
63 Sin,
64 Cos,
65 Tanh,
66 Sqrt,
67 Rsqrt,
68 Pow,
69 Expm1,
70 Log1p,
71 ExtractDiag {
72 axis_a: usize,
73 axis_b: usize,
74 },
75 EmbedDiag {
76 axis_a: usize,
77 axis_b: usize,
78 },
79 Tril {
80 k: i64,
81 },
82 Triu {
83 k: i64,
84 },
85 Gather(GatherConfig),
86 GatherDynamicSliceSizes {
87 offset_dims: Vec<usize>,
88 collapsed_slice_dims: Vec<usize>,
89 start_index_map: Vec<usize>,
90 index_vector_dim: usize,
91 slice_sizes: Vec<DimExpr>,
92 },
93 Scatter(ScatterConfig),
94 Slice(SliceConfig),
95 DynamicSlice {
96 slice_sizes: Vec<usize>,
97 },
98 DynamicUpdateSlice,
99 Pad(PadConfig),
100 Concatenate {
101 axis: usize,
102 input_count: usize,
103 },
104 Reverse {
105 axes: Vec<usize>,
106 },
107 ShapeOf {
108 axis: usize,
109 },
110 DynamicTruncate {
111 axis: usize,
112 },
113 PadToMatch {
114 axis: usize,
115 },
116 ReduceProd {
117 axes: Vec<usize>,
118 },
119 ReduceMax {
120 axes: Vec<usize>,
121 },
122 ReduceMin {
123 axes: Vec<usize>,
124 },
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
130pub enum CoreSemanticOpConversionError {
131 #[error("extension operations are not core semantic operations")]
134 ExtensionCarrier,
135}
136
137impl CoreSemanticOp {
138 pub(crate) fn input_count(&self) -> usize {
139 let standard = StdTensorOp::from(self);
140 GraphOperation::input_count(&standard)
141 }
142
143 pub(crate) fn output_count(&self) -> usize {
144 let standard = StdTensorOp::from(self);
145 GraphOperation::output_count(&standard)
146 }
147}
148
149impl TryFrom<&StdTensorOp> for CoreSemanticOp {
150 type Error = CoreSemanticOpConversionError;
151
152 fn try_from(op: &StdTensorOp) -> Result<Self, Self::Error> {
153 Ok(match op {
154 StdTensorOp::Add => Self::Add,
155 StdTensorOp::Sub => Self::Sub,
156 StdTensorOp::Mul => Self::Mul,
157 StdTensorOp::Neg => Self::Neg,
158 StdTensorOp::Conj => Self::Conj,
159 StdTensorOp::DotGeneral { config } => Self::DotGeneral {
160 config: config.clone(),
161 },
162 StdTensorOp::Transpose { perm } => Self::Transpose { perm: perm.clone() },
163 StdTensorOp::Reshape { to_shape } => Self::Reshape {
164 to_shape: to_shape.clone(),
165 },
166 StdTensorOp::BroadcastInDim { shape, dims } => Self::BroadcastInDim {
167 shape: shape.clone(),
168 dims: dims.clone(),
169 },
170 StdTensorOp::Convert { from, to } => Self::Convert {
171 from: *from,
172 to: *to,
173 },
174 StdTensorOp::Constant { dtype, bytes } => Self::Constant {
175 dtype: *dtype,
176 bytes: bytes.clone(),
177 },
178 StdTensorOp::ReduceSum { axes } => Self::ReduceSum { axes: axes.clone() },
179 StdTensorOp::ReduceSumSquares { axes } => Self::ReduceSumSquares { axes: axes.clone() },
180 StdTensorOp::Div => Self::Div,
181 StdTensorOp::Rem => Self::Rem,
182 StdTensorOp::Abs => Self::Abs,
183 StdTensorOp::Sign => Self::Sign,
184 StdTensorOp::Maximum => Self::Maximum,
185 StdTensorOp::Minimum => Self::Minimum,
186 StdTensorOp::Compare(direction) => Self::Compare(direction.clone()),
187 StdTensorOp::Select => Self::Select,
188 StdTensorOp::Clamp => Self::Clamp,
189 StdTensorOp::Exp => Self::Exp,
190 StdTensorOp::Log => Self::Log,
191 StdTensorOp::Sin => Self::Sin,
192 StdTensorOp::Cos => Self::Cos,
193 StdTensorOp::Tanh => Self::Tanh,
194 StdTensorOp::Sqrt => Self::Sqrt,
195 StdTensorOp::Rsqrt => Self::Rsqrt,
196 StdTensorOp::Pow => Self::Pow,
197 StdTensorOp::Expm1 => Self::Expm1,
198 StdTensorOp::Log1p => Self::Log1p,
199 StdTensorOp::ExtractDiag { axis_a, axis_b } => Self::ExtractDiag {
200 axis_a: *axis_a,
201 axis_b: *axis_b,
202 },
203 StdTensorOp::EmbedDiag { axis_a, axis_b } => Self::EmbedDiag {
204 axis_a: *axis_a,
205 axis_b: *axis_b,
206 },
207 StdTensorOp::Tril { k } => Self::Tril { k: *k },
208 StdTensorOp::Triu { k } => Self::Triu { k: *k },
209 StdTensorOp::Gather(config) => Self::Gather(config.clone()),
210 StdTensorOp::GatherDynamicSliceSizes {
211 offset_dims,
212 collapsed_slice_dims,
213 start_index_map,
214 index_vector_dim,
215 slice_sizes,
216 } => Self::GatherDynamicSliceSizes {
217 offset_dims: offset_dims.clone(),
218 collapsed_slice_dims: collapsed_slice_dims.clone(),
219 start_index_map: start_index_map.clone(),
220 index_vector_dim: *index_vector_dim,
221 slice_sizes: slice_sizes.clone(),
222 },
223 StdTensorOp::Scatter(config) => Self::Scatter(config.clone()),
224 StdTensorOp::Slice(config) => Self::Slice(config.clone()),
225 StdTensorOp::DynamicSlice { slice_sizes } => Self::DynamicSlice {
226 slice_sizes: slice_sizes.clone(),
227 },
228 StdTensorOp::DynamicUpdateSlice => Self::DynamicUpdateSlice,
229 StdTensorOp::Pad(config) => Self::Pad(config.clone()),
230 StdTensorOp::Concatenate { axis, input_count } => Self::Concatenate {
231 axis: *axis,
232 input_count: *input_count,
233 },
234 StdTensorOp::Reverse { axes } => Self::Reverse { axes: axes.clone() },
235 StdTensorOp::ShapeOf { axis } => Self::ShapeOf { axis: *axis },
236 StdTensorOp::DynamicTruncate { axis } => Self::DynamicTruncate { axis: *axis },
237 StdTensorOp::PadToMatch { axis } => Self::PadToMatch { axis: *axis },
238 StdTensorOp::ReduceProd { axes } => Self::ReduceProd { axes: axes.clone() },
239 StdTensorOp::ReduceMax { axes } => Self::ReduceMax { axes: axes.clone() },
240 StdTensorOp::ReduceMin { axes } => Self::ReduceMin { axes: axes.clone() },
241 StdTensorOp::Extension(_) => {
242 return Err(CoreSemanticOpConversionError::ExtensionCarrier);
243 }
244 })
245 }
246}
247
248impl From<&CoreSemanticOp> for StdTensorOp {
249 fn from(op: &CoreSemanticOp) -> Self {
250 match op {
251 CoreSemanticOp::Add => Self::Add,
252 CoreSemanticOp::Sub => Self::Sub,
253 CoreSemanticOp::Mul => Self::Mul,
254 CoreSemanticOp::Neg => Self::Neg,
255 CoreSemanticOp::Conj => Self::Conj,
256 CoreSemanticOp::DotGeneral { config } => Self::DotGeneral {
257 config: config.clone(),
258 },
259 CoreSemanticOp::Transpose { perm } => Self::Transpose { perm: perm.clone() },
260 CoreSemanticOp::Reshape { to_shape } => Self::Reshape {
261 to_shape: to_shape.clone(),
262 },
263 CoreSemanticOp::BroadcastInDim { shape, dims } => Self::BroadcastInDim {
264 shape: shape.clone(),
265 dims: dims.clone(),
266 },
267 CoreSemanticOp::Convert { from, to } => Self::Convert {
268 from: *from,
269 to: *to,
270 },
271 CoreSemanticOp::Constant { dtype, bytes } => Self::Constant {
272 dtype: *dtype,
273 bytes: bytes.clone(),
274 },
275 CoreSemanticOp::ReduceSum { axes } => Self::ReduceSum { axes: axes.clone() },
276 CoreSemanticOp::ReduceSumSquares { axes } => {
277 Self::ReduceSumSquares { axes: axes.clone() }
278 }
279 CoreSemanticOp::Div => Self::Div,
280 CoreSemanticOp::Rem => Self::Rem,
281 CoreSemanticOp::Abs => Self::Abs,
282 CoreSemanticOp::Sign => Self::Sign,
283 CoreSemanticOp::Maximum => Self::Maximum,
284 CoreSemanticOp::Minimum => Self::Minimum,
285 CoreSemanticOp::Compare(direction) => Self::Compare(direction.clone()),
286 CoreSemanticOp::Select => Self::Select,
287 CoreSemanticOp::Clamp => Self::Clamp,
288 CoreSemanticOp::Exp => Self::Exp,
289 CoreSemanticOp::Log => Self::Log,
290 CoreSemanticOp::Sin => Self::Sin,
291 CoreSemanticOp::Cos => Self::Cos,
292 CoreSemanticOp::Tanh => Self::Tanh,
293 CoreSemanticOp::Sqrt => Self::Sqrt,
294 CoreSemanticOp::Rsqrt => Self::Rsqrt,
295 CoreSemanticOp::Pow => Self::Pow,
296 CoreSemanticOp::Expm1 => Self::Expm1,
297 CoreSemanticOp::Log1p => Self::Log1p,
298 CoreSemanticOp::ExtractDiag { axis_a, axis_b } => Self::ExtractDiag {
299 axis_a: *axis_a,
300 axis_b: *axis_b,
301 },
302 CoreSemanticOp::EmbedDiag { axis_a, axis_b } => Self::EmbedDiag {
303 axis_a: *axis_a,
304 axis_b: *axis_b,
305 },
306 CoreSemanticOp::Tril { k } => Self::Tril { k: *k },
307 CoreSemanticOp::Triu { k } => Self::Triu { k: *k },
308 CoreSemanticOp::Gather(config) => Self::Gather(config.clone()),
309 CoreSemanticOp::GatherDynamicSliceSizes {
310 offset_dims,
311 collapsed_slice_dims,
312 start_index_map,
313 index_vector_dim,
314 slice_sizes,
315 } => Self::GatherDynamicSliceSizes {
316 offset_dims: offset_dims.clone(),
317 collapsed_slice_dims: collapsed_slice_dims.clone(),
318 start_index_map: start_index_map.clone(),
319 index_vector_dim: *index_vector_dim,
320 slice_sizes: slice_sizes.clone(),
321 },
322 CoreSemanticOp::Scatter(config) => Self::Scatter(config.clone()),
323 CoreSemanticOp::Slice(config) => Self::Slice(config.clone()),
324 CoreSemanticOp::DynamicSlice { slice_sizes } => Self::DynamicSlice {
325 slice_sizes: slice_sizes.clone(),
326 },
327 CoreSemanticOp::DynamicUpdateSlice => Self::DynamicUpdateSlice,
328 CoreSemanticOp::Pad(config) => Self::Pad(config.clone()),
329 CoreSemanticOp::Concatenate { axis, input_count } => Self::Concatenate {
330 axis: *axis,
331 input_count: *input_count,
332 },
333 CoreSemanticOp::Reverse { axes } => Self::Reverse { axes: axes.clone() },
334 CoreSemanticOp::ShapeOf { axis } => Self::ShapeOf { axis: *axis },
335 CoreSemanticOp::DynamicTruncate { axis } => Self::DynamicTruncate { axis: *axis },
336 CoreSemanticOp::PadToMatch { axis } => Self::PadToMatch { axis: *axis },
337 CoreSemanticOp::ReduceProd { axes } => Self::ReduceProd { axes: axes.clone() },
338 CoreSemanticOp::ReduceMax { axes } => Self::ReduceMax { axes: axes.clone() },
339 CoreSemanticOp::ReduceMin { axes } => Self::ReduceMin { axes: axes.clone() },
340 }
341 }
342}
343
344pub(crate) enum SemanticOp {
345 Core(CoreSemanticOp),
346 Extension(Arc<dyn ExtensionOp>),
347}
348
349pub(crate) struct SemanticOperation {
350 pub(crate) op: SemanticOp,
351 pub(crate) inputs: Box<[ProgramValue]>,
352 pub(crate) outputs: Box<[ProgramValue]>,
353 pub(crate) effects: Box<[Effect]>,
354 pub(crate) aliases: Box<[Alias]>,
355 pub(crate) shape_guards: Box<[ShapeGuard]>,
356 pub(crate) placement: SemanticPlacementConstraint,
357 pub(crate) provenance: SemanticProvenance,
358}
359
360#[derive(Clone, Copy)]
362#[non_exhaustive]
363pub enum SemanticOpRef<'a> {
364 Core(&'a CoreSemanticOp),
366 Extension(&'a dyn ExtensionOp),
368}
369
370impl std::fmt::Debug for SemanticOpRef<'_> {
371 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 match self {
373 Self::Core(_) => formatter.write_str("SemanticOpRef::Core(<bounded>)"),
374 Self::Extension(op) => formatter
375 .debug_tuple("SemanticOpRef::Extension")
376 .field(&op.family_id())
377 .finish(),
378 }
379 }
380}
381
382#[derive(Clone, Copy)]
384pub struct SemanticOperationView<'a> {
385 operation: &'a SemanticOperation,
386}
387
388impl<'a> SemanticOperationView<'a> {
389 pub(crate) const fn new(operation: &'a SemanticOperation) -> Self {
390 Self { operation }
391 }
392
393 pub fn op(self) -> SemanticOpRef<'a> {
395 match &self.operation.op {
396 SemanticOp::Core(op) => SemanticOpRef::Core(op),
397 SemanticOp::Extension(op) => SemanticOpRef::Extension(op.as_ref()),
398 }
399 }
400
401 pub fn inputs(self) -> &'a [ProgramValue] {
403 &self.operation.inputs
404 }
405
406 pub fn outputs(self) -> &'a [ProgramValue] {
408 &self.operation.outputs
409 }
410
411 pub fn effects(self) -> &'a [Effect] {
413 &self.operation.effects
414 }
415
416 pub fn aliases(self) -> &'a [Alias] {
418 &self.operation.aliases
419 }
420
421 pub fn shape_guards(self) -> &'a [ShapeGuard] {
423 &self.operation.shape_guards
424 }
425
426 pub fn provenance(self) -> SemanticProvenanceView<'a> {
428 self.operation.provenance.view()
429 }
430
431 pub fn placement(self) -> SemanticPlacementConstraint {
433 self.operation.placement
434 }
435}
436
437impl std::fmt::Debug for SemanticOperationView<'_> {
438 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 formatter
440 .debug_struct("SemanticOperationView")
441 .field("op", &self.op())
442 .field("inputs", &self.inputs().len())
443 .field("outputs", &self.outputs().len())
444 .field("effects", &self.effects().len())
445 .field("aliases", &self.aliases().len())
446 .field("shape_guards", &self.shape_guards().len())
447 .finish()
448 }
449}