use std::fmt;
use tenferro_ops::ext_op::ExtensionOp;
use tenferro_ops::ShapeExtent;
use tenferro_tensor::{DType, DotGeneralConfig};
use crate::exec::{ExecInstruction, ExecOp, ExecProgram};
#[derive(Clone, Copy)]
pub struct GraphProgramLoweringView<'a> {
exec: &'a ExecProgram,
}
impl fmt::Debug for GraphProgramLoweringView<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GraphProgramLoweringView")
.field("slot_count", &self.slot_count())
.field("input_count", &self.input_slots().len())
.field("output_count", &self.output_slots().len())
.field("instruction_count", &self.exec.instructions.len())
.finish()
}
}
impl<'a> GraphProgramLoweringView<'a> {
pub(crate) fn new(exec: &'a ExecProgram) -> Self {
Self { exec }
}
pub fn slot_count(&self) -> usize {
self.exec.n_slots
}
pub fn input_slots(&self) -> &'a [usize] {
&self.exec.input_slots
}
pub fn output_slots(&self) -> &'a [usize] {
&self.exec.output_slots
}
pub fn instructions(&self) -> impl ExactSizeIterator<Item = GraphInstructionView<'a>> + '_ {
self.exec.instructions.iter().map(GraphInstructionView::new)
}
}
#[derive(Clone, Copy)]
pub struct GraphInstructionView<'a> {
inst: &'a ExecInstruction,
}
impl fmt::Debug for GraphInstructionView<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GraphInstructionView")
.field("op", &self.op_name())
.field("input_count", &self.input_slots().len())
.field("output_count", &self.output_slots().len())
.field("dtype", &self.dtype())
.finish()
}
}
impl<'a> GraphInstructionView<'a> {
fn new(inst: &'a ExecInstruction) -> Self {
Self { inst }
}
pub fn op(&self) -> GraphOpView<'a> {
match &self.inst.op {
ExecOp::Constant { dtype, bytes } => GraphOpView::Constant {
dtype: *dtype,
bytes,
},
ExecOp::Add => GraphOpView::Add,
ExecOp::Multiply => GraphOpView::Multiply,
ExecOp::Negate => GraphOpView::Negate,
ExecOp::Divide => GraphOpView::Divide,
ExecOp::Abs => GraphOpView::Abs,
ExecOp::Exp => GraphOpView::Exp,
ExecOp::Log => GraphOpView::Log,
ExecOp::Sin => GraphOpView::Sin,
ExecOp::Cos => GraphOpView::Cos,
ExecOp::Tanh => GraphOpView::Tanh,
ExecOp::Sqrt => GraphOpView::Sqrt,
ExecOp::Rsqrt => GraphOpView::Rsqrt,
ExecOp::Pow => GraphOpView::Pow,
ExecOp::Expm1 => GraphOpView::Expm1,
ExecOp::Log1p => GraphOpView::Log1p,
ExecOp::Convert { to } => GraphOpView::Convert { to: *to },
ExecOp::Reshape { .. } => GraphOpView::Reshape,
ExecOp::BroadcastInDim { dims, .. } => GraphOpView::BroadcastInDim { dims },
ExecOp::Transpose { perm } => GraphOpView::Transpose { perm },
ExecOp::ReduceSum { axes } => GraphOpView::ReduceSum { axes },
ExecOp::DotGeneral(config) => GraphOpView::DotGeneral { config },
ExecOp::Extension(op) => GraphOpView::Extension { op: op.as_ref() },
other => GraphOpView::Unsupported {
name: exec_op_name(other),
},
}
}
pub fn op_name(&self) -> &'static str {
self.op().name()
}
pub fn input_slots(&self) -> &'a [usize] {
&self.inst.input_slots
}
pub fn output_slots(&self) -> &'a [usize] {
&self.inst.output_slots
}
pub fn dtype(&self) -> DType {
self.inst.dtype
}
pub fn static_output_shape(
&self,
output_index: usize,
input_shapes: &[&[usize]],
) -> std::result::Result<Vec<usize>, GraphProgramLoweringShapeError> {
let extents = self.inst.output_extents.get(output_index).ok_or(
GraphProgramLoweringShapeError::MissingOutput {
op: self.op_name(),
output_index,
},
)?;
let mut shape = Vec::with_capacity(extents.len());
for (axis, extent) in extents.iter().enumerate() {
match extent {
ShapeExtent::Exact(dim) => shape.push(dim.eval(input_shapes).map_err(|err| {
GraphProgramLoweringShapeError::InvalidDimExpr {
op: self.op_name(),
output_index,
axis,
source: err,
}
})?),
ShapeExtent::UpperBound(_) => {
return Err(GraphProgramLoweringShapeError::NonStatic {
op: self.op_name(),
output_index,
axis,
kind: "an upper bound",
});
}
ShapeExtent::Unknown => {
return Err(GraphProgramLoweringShapeError::NonStatic {
op: self.op_name(),
output_index,
axis,
kind: "unknown",
});
}
}
}
Ok(shape)
}
}
#[derive(Clone, Copy)]
pub enum GraphOpView<'a> {
Constant { dtype: DType, bytes: &'a [u8] },
Add,
Multiply,
Negate,
Divide,
Abs,
Exp,
Log,
Sin,
Cos,
Tanh,
Sqrt,
Rsqrt,
Pow,
Expm1,
Log1p,
Convert { to: DType },
Reshape,
BroadcastInDim { dims: &'a [usize] },
Transpose { perm: &'a [usize] },
ReduceSum { axes: &'a [usize] },
DotGeneral { config: &'a DotGeneralConfig },
Extension { op: &'a dyn ExtensionOp },
Unsupported { name: &'static str },
}
impl fmt::Debug for GraphOpView<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Constant { dtype, bytes } => f
.debug_struct("Constant")
.field("dtype", dtype)
.field("byte_len", &bytes.len())
.finish(),
Self::Add => f.write_str("Add"),
Self::Multiply => f.write_str("Multiply"),
Self::Negate => f.write_str("Negate"),
Self::Divide => f.write_str("Divide"),
Self::Abs => f.write_str("Abs"),
Self::Exp => f.write_str("Exp"),
Self::Log => f.write_str("Log"),
Self::Sin => f.write_str("Sin"),
Self::Cos => f.write_str("Cos"),
Self::Tanh => f.write_str("Tanh"),
Self::Sqrt => f.write_str("Sqrt"),
Self::Rsqrt => f.write_str("Rsqrt"),
Self::Pow => f.write_str("Pow"),
Self::Expm1 => f.write_str("Expm1"),
Self::Log1p => f.write_str("Log1p"),
Self::Convert { to } => f.debug_struct("Convert").field("to", to).finish(),
Self::Reshape => f.write_str("Reshape"),
Self::BroadcastInDim { dims } => f
.debug_struct("BroadcastInDim")
.field("dims", dims)
.finish(),
Self::Transpose { perm } => f.debug_struct("Transpose").field("perm", perm).finish(),
Self::ReduceSum { axes } => f.debug_struct("ReduceSum").field("axes", axes).finish(),
Self::DotGeneral { config } => f
.debug_struct("DotGeneral")
.field("config", config)
.finish(),
Self::Extension { op } => f
.debug_struct("Extension")
.field("family_id", &op.family_id())
.finish(),
Self::Unsupported { name } => {
f.debug_struct("Unsupported").field("name", name).finish()
}
}
}
}
impl GraphOpView<'_> {
pub fn name(&self) -> &'static str {
match self {
Self::Constant { .. } => "Constant",
Self::Add => "Add",
Self::Multiply => "Multiply",
Self::Negate => "Negate",
Self::Divide => "Divide",
Self::Abs => "Abs",
Self::Exp => "Exp",
Self::Log => "Log",
Self::Sin => "Sin",
Self::Cos => "Cos",
Self::Tanh => "Tanh",
Self::Sqrt => "Sqrt",
Self::Rsqrt => "Rsqrt",
Self::Pow => "Pow",
Self::Expm1 => "Expm1",
Self::Log1p => "Log1p",
Self::Convert { .. } => "Convert",
Self::Reshape => "Reshape",
Self::BroadcastInDim { .. } => "BroadcastInDim",
Self::Transpose { .. } => "Transpose",
Self::ReduceSum { .. } => "ReduceSum",
Self::DotGeneral { .. } => "DotGeneral",
Self::Extension { .. } => "Extension",
Self::Unsupported { name } => name,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum GraphProgramLoweringShapeError {
#[error("ExecOp::{op} missing output_extents for output {output_index}")]
MissingOutput {
op: &'static str,
output_index: usize,
},
#[error("ExecOp::{op} output {output_index} axis {axis} has non-static extent: {kind}")]
NonStatic {
op: &'static str,
output_index: usize,
axis: usize,
kind: &'static str,
},
#[error(
"ExecOp::{op} output {output_index} axis {axis} has invalid dimension expression: {source}"
)]
InvalidDimExpr {
op: &'static str,
output_index: usize,
axis: usize,
source: tenferro_ops::dim_expr::DimExprEvalError,
},
}
fn exec_op_name(op: &ExecOp) -> &'static str {
match op {
ExecOp::Transpose { .. } => "Transpose",
ExecOp::Reshape { .. } => "Reshape",
ExecOp::BroadcastInDim { .. } => "BroadcastInDim",
ExecOp::Convert { .. } => "Convert",
ExecOp::Constant { .. } => "Constant",
ExecOp::DotGeneral(_) => "DotGeneral",
ExecOp::DotGeneralWithConj { .. } => "DotGeneralWithConj",
ExecOp::ReduceSum { .. } => "ReduceSum",
ExecOp::ExtractDiag { .. } => "ExtractDiag",
ExecOp::EmbedDiag { .. } => "EmbedDiag",
ExecOp::Tril { .. } => "Tril",
ExecOp::Triu { .. } => "Triu",
ExecOp::Add => "Add",
ExecOp::Multiply => "Multiply",
ExecOp::Negate => "Negate",
ExecOp::Conj => "Conj",
ExecOp::Divide => "Divide",
ExecOp::Abs => "Abs",
ExecOp::Sign => "Sign",
ExecOp::Maximum => "Maximum",
ExecOp::Minimum => "Minimum",
ExecOp::Compare(_) => "Compare",
ExecOp::Select => "Select",
ExecOp::Clamp => "Clamp",
ExecOp::Exp => "Exp",
ExecOp::Log => "Log",
ExecOp::Sin => "Sin",
ExecOp::Cos => "Cos",
ExecOp::Tanh => "Tanh",
ExecOp::Sqrt => "Sqrt",
ExecOp::Rsqrt => "Rsqrt",
ExecOp::Pow => "Pow",
ExecOp::Expm1 => "Expm1",
ExecOp::Log1p => "Log1p",
ExecOp::Gather(_) => "Gather",
ExecOp::GatherDynamicSliceSizes { .. } => "GatherDynamicSliceSizes",
ExecOp::Scatter(_) => "Scatter",
ExecOp::Slice(_) => "Slice",
ExecOp::DynamicSlice { .. } => "DynamicSlice",
ExecOp::DynamicUpdateSlice => "DynamicUpdateSlice",
ExecOp::Pad(_) => "Pad",
ExecOp::Concatenate { .. } => "Concatenate",
ExecOp::Reverse { .. } => "Reverse",
ExecOp::ShapeOf { .. } => "ShapeOf",
ExecOp::DynamicTruncate { .. } => "DynamicTruncate",
ExecOp::PadToMatch { .. } => "PadToMatch",
ExecOp::ReduceProd { .. } => "ReduceProd",
ExecOp::ReduceMax { .. } => "ReduceMax",
ExecOp::ReduceMin { .. } => "ReduceMin",
ExecOp::Extension(_) => "Extension",
}
}
#[cfg(test)]
mod tests;