//! Node representation for ONNX operations. use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::error::Result; use crate::onnx_proto::{AttributeProto, NodeProto}; /// ONNX operator kinds. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum NodeKind { // Arithmetic /// Element-wise addition of two tensors. Add, /// Element-wise subtraction of two tensors. Sub, /// Element-wise multiplication of two tensors. Mul, /// Element-wise division of two tensors. Div, /// Element-wise negation of a tensor. Neg, /// Element-wise absolute value of a tensor. Abs, /// Element-wise square root of a tensor. Sqrt, /// Element-wise power operation. Pow, /// Element-wise exponential function. Exp, /// Element-wise natural logarithm. Log, /// Element-wise floor rounding. Floor, /// Element-wise ceiling rounding. Ceil, /// Element-wise rounding to nearest integer. Round, // Matrix operations /// Matrix multiplication of two tensors. MatMul, /// General matrix multiplication with optional bias. Gemm, // Activations /// Rectified Linear Unit activation function. Relu, /// Leaky Rectified Linear Unit activation function. LeakyRelu, /// Sigmoid activation function. Sigmoid, /// Hyperbolic tangent activation function. Tanh, /// Softmax activation function. Softmax, /// Log-softmax activation function. LogSoftmax, /// Gaussian Error Linear Unit activation function. Gelu, /// Sigmoid Linear Unit (Swish) activation function. Silu, /// Hard sigmoid activation function. HardSigmoid, /// Hard swish activation function. HardSwish, /// Exponential Linear Unit activation function. Elu, /// Scaled Exponential Linear Unit activation function. Selu, /// Continuously Differentiable Exponential Linear Unit. Celu, /// Softplus activation function. Softplus, /// Softsign activation function. Softsign, /// Mish activation function. Mish, // Normalization /// Batch normalization operation. BatchNormalization, /// Layer normalization operation. LayerNormalization, /// Instance normalization operation. InstanceNormalization, /// Group normalization operation. GroupNormalization, /// Lp normalization operation. LpNormalization, // Convolution /// Convolution operation. Conv, /// Transposed convolution (deconvolution) operation. ConvTranspose, // Pooling /// Max pooling operation. MaxPool, /// Average pooling operation. AveragePool, /// Global average pooling over spatial dimensions. GlobalAveragePool, /// Global max pooling over spatial dimensions. GlobalMaxPool, /// Global Lp pooling over spatial dimensions. GlobalLpPool, // Reduction /// Sum reduction along specified axes. ReduceSum, /// Mean reduction along specified axes. ReduceMean, /// Max reduction along specified axes. ReduceMax, /// Min reduction along specified axes. ReduceMin, /// Product reduction along specified axes. ReduceProd, /// L1 norm reduction along specified axes. ReduceL1, /// L2 norm reduction along specified axes. ReduceL2, /// Log of sum reduction along specified axes. ReduceLogSum, /// Log of sum of exponentials reduction along specified axes. ReduceLogSumExp, /// Sum of squares reduction along specified axes. ReduceSumSquare, // Shape operations /// Reshape tensor to a new shape. Reshape, /// Transpose tensor dimensions. Transpose, /// Flatten tensor to 2D. Flatten, /// Remove dimensions of size 1. Squeeze, /// Insert dimensions of size 1. Unsqueeze, /// Concatenate tensors along an axis. Concat, /// Split tensor into multiple tensors. Split, /// Extract a slice from a tensor. Slice, /// Gather elements along an axis by index. Gather, /// Gather elements by index along specified axis. GatherElements, /// Gather slices from tensor using N-dimensional indices. GatherND, /// Scatter elements along an axis by index. Scatter, /// Scatter elements by index along specified axis. ScatterElements, /// Scatter slices into tensor using N-dimensional indices. ScatterND, /// Broadcast tensor to a larger shape. Expand, /// Tile tensor by repeating along dimensions. Tile, /// Pad tensor with constant or edge values. Pad, // Comparison /// Element-wise equality comparison. Equal, /// Element-wise greater-than comparison. Greater, /// Element-wise greater-than-or-equal comparison. GreaterOrEqual, /// Element-wise less-than comparison. Less, /// Element-wise less-than-or-equal comparison. LessOrEqual, /// Element-wise logical NOT. Not, /// Element-wise logical AND. And, /// Element-wise logical OR. Or, /// Element-wise logical XOR. Xor, /// Conditional selection based on a mask tensor. Where, // Type conversion /// Cast tensor to a different data type. Cast, /// Cast tensor to the same type as another tensor. CastLike, // Constants /// Produce a constant tensor. Constant, /// Generate a tensor of a given shape filled with a constant. ConstantOfShape, /// Get the shape of a tensor as a 1D tensor. Shape, /// Get the total number of elements in a tensor. Size, // RNN /// Long Short-Term Memory recurrent layer. LSTM, /// Gated Recurrent Unit recurrent layer. GRU, /// Simple recurrent neural network layer. RNN, // Attention /// Single-head attention mechanism. Attention, /// Multi-head attention mechanism. MultiHeadAttention, // Misc /// Dropout regularization layer. Dropout, /// Identity operation (pass-through). Identity, /// Clip values to a specified range. Clip, /// Gauss error function. Erf, /// Element-wise sine function. Sin, /// Element-wise cosine function. Cos, /// Element-wise tangent function. Tan, /// Element-wise arcsine function. Asin, /// Element-wise arccosine function. Acos, /// Element-wise arctangent function. Atan, /// Element-wise hyperbolic sine function. Sinh, /// Element-wise hyperbolic cosine function. Cosh, /// Element-wise inverse hyperbolic sine function. Asinh, /// Element-wise inverse hyperbolic cosine function. Acosh, /// Element-wise inverse hyperbolic tangent function. Atanh, /// Element-wise sign function. Sign, /// Element-wise reciprocal (1/x). Reciprocal, /// Element-wise minimum of input tensors. Min, /// Element-wise maximum of input tensors. Max, /// Element-wise mean of input tensors. Mean, /// Element-wise sum of input tensors. Sum, // Embedding /// Embedding lookup operation. Embedding, /// One-hot encoding operation. OneHot, // Resize /// Resize tensor using interpolation. Resize, /// Upsample tensor (deprecated, use Resize). Upsample, // Custom/Unknown /// Custom or unsupported operator with its name. Custom(String), } impl NodeKind { /// Parse from ONNX op_type string. pub fn from_op_type(op_type: &str) -> Self { match op_type { // Arithmetic "Add" => NodeKind::Add, "Sub" => NodeKind::Sub, "Mul" => NodeKind::Mul, "Div" => NodeKind::Div, "Neg" => NodeKind::Neg, "Abs" => NodeKind::Abs, "Sqrt" => NodeKind::Sqrt, "Pow" => NodeKind::Pow, "Exp" => NodeKind::Exp, "Log" => NodeKind::Log, "Floor" => NodeKind::Floor, "Ceil" => NodeKind::Ceil, "Round" => NodeKind::Round, // Matrix "MatMul" => NodeKind::MatMul, "Gemm" => NodeKind::Gemm, // Activations "Relu" => NodeKind::Relu, "LeakyRelu" => NodeKind::LeakyRelu, "Sigmoid" => NodeKind::Sigmoid, "Tanh" => NodeKind::Tanh, "Softmax" => NodeKind::Softmax, "LogSoftmax" => NodeKind::LogSoftmax, "Gelu" => NodeKind::Gelu, "Silu" => NodeKind::Silu, "HardSigmoid" => NodeKind::HardSigmoid, "HardSwish" => NodeKind::HardSwish, "Elu" => NodeKind::Elu, "Selu" => NodeKind::Selu, "Celu" => NodeKind::Celu, "Softplus" => NodeKind::Softplus, "Softsign" => NodeKind::Softsign, "Mish" => NodeKind::Mish, // Normalization "BatchNormalization" => NodeKind::BatchNormalization, "LayerNormalization" => NodeKind::LayerNormalization, "InstanceNormalization" => NodeKind::InstanceNormalization, "GroupNormalization" => NodeKind::GroupNormalization, "LpNormalization" => NodeKind::LpNormalization, // Convolution "Conv" => NodeKind::Conv, "ConvTranspose" => NodeKind::ConvTranspose, // Pooling "MaxPool" => NodeKind::MaxPool, "AveragePool" => NodeKind::AveragePool, "GlobalAveragePool" => NodeKind::GlobalAveragePool, "GlobalMaxPool" => NodeKind::GlobalMaxPool, "GlobalLpPool" => NodeKind::GlobalLpPool, // Reduction "ReduceSum" => NodeKind::ReduceSum, "ReduceMean" => NodeKind::ReduceMean, "ReduceMax" => NodeKind::ReduceMax, "ReduceMin" => NodeKind::ReduceMin, "ReduceProd" => NodeKind::ReduceProd, "ReduceL1" => NodeKind::ReduceL1, "ReduceL2" => NodeKind::ReduceL2, "ReduceLogSum" => NodeKind::ReduceLogSum, "ReduceLogSumExp" => NodeKind::ReduceLogSumExp, "ReduceSumSquare" => NodeKind::ReduceSumSquare, // Shape "Reshape" => NodeKind::Reshape, "Transpose" => NodeKind::Transpose, "Flatten" => NodeKind::Flatten, "Squeeze" => NodeKind::Squeeze, "Unsqueeze" => NodeKind::Unsqueeze, "Concat" => NodeKind::Concat, "Split" => NodeKind::Split, "Slice" => NodeKind::Slice, "Gather" => NodeKind::Gather, "GatherElements" => NodeKind::GatherElements, "GatherND" => NodeKind::GatherND, "Scatter" => NodeKind::Scatter, "ScatterElements" => NodeKind::ScatterElements, "ScatterND" => NodeKind::ScatterND, "Expand" => NodeKind::Expand, "Tile" => NodeKind::Tile, "Pad" => NodeKind::Pad, // Comparison "Equal" => NodeKind::Equal, "Greater" => NodeKind::Greater, "GreaterOrEqual" => NodeKind::GreaterOrEqual, "Less" => NodeKind::Less, "LessOrEqual" => NodeKind::LessOrEqual, "Not" => NodeKind::Not, "And" => NodeKind::And, "Or" => NodeKind::Or, "Xor" => NodeKind::Xor, "Where" => NodeKind::Where, // Type "Cast" => NodeKind::Cast, "CastLike" => NodeKind::CastLike, // Constants "Constant" => NodeKind::Constant, "ConstantOfShape" => NodeKind::ConstantOfShape, "Shape" => NodeKind::Shape, "Size" => NodeKind::Size, // RNN "LSTM" => NodeKind::LSTM, "GRU" => NodeKind::GRU, "RNN" => NodeKind::RNN, // Attention "Attention" => NodeKind::Attention, "MultiHeadAttention" => NodeKind::MultiHeadAttention, // Misc "Dropout" => NodeKind::Dropout, "Identity" => NodeKind::Identity, "Clip" => NodeKind::Clip, "Erf" => NodeKind::Erf, "Sin" => NodeKind::Sin, "Cos" => NodeKind::Cos, "Tan" => NodeKind::Tan, "Asin" => NodeKind::Asin, "Acos" => NodeKind::Acos, "Atan" => NodeKind::Atan, "Sinh" => NodeKind::Sinh, "Cosh" => NodeKind::Cosh, "Asinh" => NodeKind::Asinh, "Acosh" => NodeKind::Acosh, "Atanh" => NodeKind::Atanh, "Sign" => NodeKind::Sign, "Reciprocal" => NodeKind::Reciprocal, "Min" => NodeKind::Min, "Max" => NodeKind::Max, "Mean" => NodeKind::Mean, "Sum" => NodeKind::Sum, // Embedding "Embedding" => NodeKind::Embedding, "OneHot" => NodeKind::OneHot, // Resize "Resize" => NodeKind::Resize, "Upsample" => NodeKind::Upsample, // Unknown other => NodeKind::Custom(other.to_string()), } } /// Check if this is a supported operator. pub fn is_supported(&self) -> bool { !matches!(self, NodeKind::Custom(_)) } } /// Attribute value types. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum AttributeValue { /// Floating point value. Float(f32), /// Integer value. Int(i64), /// String value. String(String), /// List of floats. Floats(Vec), /// List of integers. Ints(Vec), /// List of strings. Strings(Vec), } /// A node in the ONNX graph. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Node { /// Node name (may be empty). pub name: String, /// Operator kind. pub kind: NodeKind, /// Input tensor names. pub inputs: Vec, /// Output tensor names. pub outputs: Vec, /// Node attributes. pub attributes: HashMap, } impl Node { /// Create from ONNX NodeProto. pub fn from_proto(proto: &NodeProto) -> Result { let name = proto.name.clone(); let kind = NodeKind::from_op_type(&proto.op_type); let inputs = proto.input.clone(); let outputs = proto.output.clone(); let attributes = Self::parse_attributes(&proto.attribute)?; Ok(Self { name, kind, inputs, outputs, attributes, }) } fn parse_attributes(attrs: &[AttributeProto]) -> Result> { let mut result = HashMap::new(); for attr in attrs { let value = Self::parse_attribute_value(attr)?; result.insert(attr.name.clone(), value); } Ok(result) } fn parse_attribute_value(attr: &AttributeProto) -> Result { // Check type field first, then fall back to presence of data match attr.r#type { 1 => Ok(AttributeValue::Float(attr.f)), 2 => Ok(AttributeValue::Int(attr.i)), 3 => Ok(AttributeValue::String( String::from_utf8_lossy(&attr.s).to_string(), )), 6 => Ok(AttributeValue::Floats(attr.floats.clone())), 7 => Ok(AttributeValue::Ints(attr.ints.clone())), 8 => Ok(AttributeValue::Strings( attr.strings .iter() .map(|s| String::from_utf8_lossy(s).to_string()) .collect(), )), _ => { // Fall back to checking which field has data if attr.f != 0.0 { Ok(AttributeValue::Float(attr.f)) } else if attr.i != 0 { Ok(AttributeValue::Int(attr.i)) } else if !attr.s.is_empty() { Ok(AttributeValue::String( String::from_utf8_lossy(&attr.s).to_string(), )) } else if !attr.floats.is_empty() { Ok(AttributeValue::Floats(attr.floats.clone())) } else if !attr.ints.is_empty() { Ok(AttributeValue::Ints(attr.ints.clone())) } else { // Default to int 0 Ok(AttributeValue::Int(0)) } } } } /// Get an integer attribute. pub fn get_int(&self, name: &str) -> Option { match self.attributes.get(name) { Some(AttributeValue::Int(v)) => Some(*v), _ => None, } } /// Get a float attribute. pub fn get_float(&self, name: &str) -> Option { match self.attributes.get(name) { Some(AttributeValue::Float(v)) => Some(*v), _ => None, } } /// Get a string attribute. pub fn get_string(&self, name: &str) -> Option<&str> { match self.attributes.get(name) { Some(AttributeValue::String(v)) => Some(v), _ => None, } } /// Get an integer list attribute. pub fn get_ints(&self, name: &str) -> Option<&[i64]> { match self.attributes.get(name) { Some(AttributeValue::Ints(v)) => Some(v), _ => None, } } /// Get a float list attribute. pub fn get_floats(&self, name: &str) -> Option<&[f32]> { match self.attributes.get(name) { Some(AttributeValue::Floats(v)) => Some(v), _ => None, } } }