use std::fmt::Write;
use smallvec::SmallVec;
use crate::{MapOperation, Shape};
use super::Symbol;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Opcode {
Leaf,
Parameter,
Input,
Add,
Sub,
Mul,
Div,
Neg,
Map {
operation: MapOperation,
},
Powf,
Maximum,
Step,
MatMul,
Sum,
SumAlong {
axis: usize,
},
Broadcast {
shape: Shape,
},
BroadcastAlong {
axis: usize,
extent: usize,
},
Reshape {
shape: Shape,
},
Permute {
order: SmallVec<[usize; 4]>,
},
Narrow {
axis: usize,
start: usize,
len: usize,
},
Pad {
axis: usize,
start: usize,
full_extent: usize,
},
Unfold {
axis: usize,
size: usize,
step: usize,
dilation: usize,
},
Fold {
axis: usize,
size: usize,
step: usize,
dilation: usize,
extent: usize,
},
Gather,
Scatter,
LogSoftmax {
axis: usize,
},
LogSumExp {
axis: usize,
},
}
impl Opcode {
pub fn name(&self) -> &'static str {
match self {
Opcode::Leaf => "Leaf",
Opcode::Parameter => "Parameter",
Opcode::Input => "Input",
Opcode::Add => "Add",
Opcode::Sub => "Sub",
Opcode::Mul => "Mul",
Opcode::Div => "Div",
Opcode::Neg => "Neg",
Opcode::Map { operation } => match operation {
MapOperation::Exp => "Exp",
MapOperation::Ln => "Ln",
MapOperation::Sqrt => "Sqrt",
MapOperation::Tanh => "Tanh",
MapOperation::Sin => "Sin",
MapOperation::Cos => "Cos",
MapOperation::Log1p => "Log1p",
MapOperation::Expm1 => "Expm1",
MapOperation::Erf => "Erf",
MapOperation::ErfDerivative => "ErfDerivative",
},
Opcode::Powf => "Powf",
Opcode::Maximum => "Maximum",
Opcode::Step => "Step",
Opcode::MatMul => "MatMul",
Opcode::Sum => "Sum",
Opcode::SumAlong { .. } => "SumAlong",
Opcode::Broadcast { .. } => "Broadcast",
Opcode::BroadcastAlong { .. } => "BroadcastAlong",
Opcode::Reshape { .. } => "Reshape",
Opcode::Permute { .. } => "Permute",
Opcode::Narrow { .. } => "Narrow",
Opcode::Pad { .. } => "Pad",
Opcode::Unfold { .. } => "Unfold",
Opcode::Fold { .. } => "Fold",
Opcode::Gather => "Gather",
Opcode::Scatter => "Scatter",
Opcode::LogSoftmax { .. } => "LogSoftmax",
Opcode::LogSumExp { .. } => "LogSumExp",
}
}
pub fn arity(&self) -> usize {
match self {
Opcode::Leaf | Opcode::Parameter | Opcode::Input => 0,
Opcode::Neg
| Opcode::Map { .. }
| Opcode::Sum
| Opcode::SumAlong { .. }
| Opcode::Broadcast { .. }
| Opcode::BroadcastAlong { .. }
| Opcode::Reshape { .. }
| Opcode::Permute { .. }
| Opcode::Narrow { .. }
| Opcode::Pad { .. }
| Opcode::Unfold { .. }
| Opcode::Fold { .. }
| Opcode::LogSoftmax { .. }
| Opcode::LogSumExp { .. } => 1,
Opcode::Add
| Opcode::Sub
| Opcode::Mul
| Opcode::Div
| Opcode::Powf
| Opcode::Maximum
| Opcode::Step
| Opcode::MatMul
| Opcode::Gather
| Opcode::Scatter => 2,
}
}
pub fn is_source(&self) -> bool {
matches!(self, Opcode::Leaf | Opcode::Parameter | Opcode::Input)
}
pub(crate) fn parameter_text(&self) -> String {
match self {
Opcode::SumAlong { axis }
| Opcode::LogSoftmax { axis }
| Opcode::LogSumExp { axis } => format!("axis={axis}"),
Opcode::Broadcast { shape } | Opcode::Reshape { shape } => format!("shape={shape}"),
Opcode::BroadcastAlong { axis, extent } => format!("axis={axis} extent={extent}"),
Opcode::Permute { order } => {
let axes: Vec<String> = order.iter().map(usize::to_string).collect();
format!("order=[{}]", axes.join(", "))
}
Opcode::Narrow { axis, start, len } => {
format!("axis={axis} start={start} len={len}")
}
Opcode::Pad {
axis,
start,
full_extent,
} => format!("axis={axis} start={start} full_extent={full_extent}"),
Opcode::Unfold {
axis,
size,
step,
dilation,
} => format!("axis={axis} size={size} step={step} dilation={dilation}"),
Opcode::Fold {
axis,
size,
step,
dilation,
extent,
} => format!("axis={axis} size={size} step={step} dilation={dilation} extent={extent}"),
_ => String::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct Node {
pub(crate) symbol: Symbol,
pub(crate) opcode: Opcode,
pub(crate) shape: Shape,
pub(crate) operands: SmallVec<[Symbol; 2]>,
}
impl Node {
pub fn symbol(&self) -> Symbol {
self.symbol
}
pub fn opcode(&self) -> &Opcode {
&self.opcode
}
pub fn name(&self) -> &'static str {
self.opcode.name()
}
pub fn shape(&self) -> &Shape {
&self.shape
}
pub fn operands(&self) -> &[Symbol] {
&self.operands
}
pub fn is_source(&self) -> bool {
self.opcode.is_source()
}
pub(crate) fn spec_line(&self) -> String {
let mut detail = String::new();
let operands: Vec<String> = self
.operands
.iter()
.map(|operand| operand.id.index().to_string())
.collect();
detail.push_str(&operands.join(", "));
let parameters = self.opcode.parameter_text();
if !parameters.is_empty() {
if !detail.is_empty() {
detail.push_str(" ");
}
detail.push_str(¶meters);
}
let mut line = String::new();
let _ = write!(
line,
"{:4} {:<14} {:<18} {}",
self.symbol.id.index(),
self.name(),
detail,
self.shape,
);
line
}
}
impl std::fmt::Display for Node {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "{}", self.spec_line())
}
}
#[cfg(test)]
#[path = "tests/opcode_tests.rs"]
mod tests;