use super::{Node, NodeCodegen};
use crate::burn::{BurnImports, Scope, TensorKind, ToTokens, Type};
use burn::record::PrecisionSettings;
use proc_macro2::TokenStream;
use quote::quote;
use std::rc::Rc;
type FnPointer = Rc<dyn Fn(TokenStream) -> TokenStream>;
#[derive(Clone, new)]
pub struct UnaryNode {
pub input: Type,
pub output: Type,
pub kind: UnaryNodeKind,
function: FnPointer,
}
#[derive(Clone)]
pub enum UnaryNodeKind {
Cast(Option<TensorKind>, Option<TensorKind>),
Cos,
Erf,
Exp,
Flatten,
Gelu,
LeakyRelu,
HardSigmoid,
Log,
LogSoftmax,
Neg,
Not,
ReduceMax,
ReduceMin,
ReduceMean,
ReduceProd,
ReduceSum,
Reciprocal,
Relu,
Shape,
Sigmoid,
Sin,
Softmax,
Sqrt,
Tanh,
Transpose,
Sign,
}
impl UnaryNodeKind {
pub fn as_str(&self) -> &str {
match self {
Self::Cast(..) => "cast",
Self::Cos => "cos",
Self::Erf => "erf",
Self::Exp => "exp",
Self::Flatten => "flatten",
Self::Gelu => "gelu",
Self::LeakyRelu => "leaky_relu",
Self::HardSigmoid => "hard_sigmoid",
Self::Log => "log",
Self::LogSoftmax => "log_softmax",
Self::Neg => "neg",
Self::Not => "not",
Self::ReduceMax => "reduce_max",
Self::ReduceMin => "reduce_min",
Self::ReduceMean => "reduce_mean",
Self::ReduceProd => "reduce_prod",
Self::ReduceSum => "reduce_sum",
Self::Reciprocal => "reciprocal",
Self::Relu => "relu",
Self::Shape => "shape",
Self::Sigmoid => "sigmoid",
Self::Sin => "sin",
Self::Softmax => "softmax",
Self::Sqrt => "sqrt",
Self::Tanh => "tanh",
Self::Transpose => "transpose",
Self::Sign => "sign",
}
}
}
impl std::fmt::Debug for UnaryNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(
format!(
"UnaryNode {{ input: {:?}, output: {:?}, name: {} }}",
self.input,
self.output,
self.kind.as_str()
)
.as_str(),
)
}
}
impl<PS: PrecisionSettings> NodeCodegen<PS> for UnaryNode {
fn output_types(&self) -> Vec<Type> {
vec![self.output.clone()]
}
fn input_types(&self) -> Vec<Type> {
vec![self.input.clone()]
}
fn forward(&self, scope: &mut Scope, node_position: usize) -> TokenStream {
let input = match &self.input {
Type::Tensor(tensor) => scope.tensor_use_owned(tensor, node_position),
Type::Scalar(scalar) => {
let name = scalar.name.clone();
quote! { #name }
}
_ => panic!("lhs must be a tensor or scalar"),
};
let output = &self.output.name();
let function = (self.function)(input);
match &self.output {
Type::Shape(ref shape_type) => {
let dim = shape_type.dim.to_tokens();
quote! {
let #output: [usize;#dim] = #function.try_into().unwrap();
}
}
_ => {
quote! {
let #output = #function;
}
}
}
}
fn into_node(self) -> Node<PS> {
Node::Unary(self)
}
fn register_imports(&self, imports: &mut BurnImports) {
match self.kind {
UnaryNodeKind::Neg => {
imports.register("core::ops::Neg");
}
UnaryNodeKind::Not => {
imports.register("burn::tensor::Bool");
}
UnaryNodeKind::Cast(Some(input_kind), Some(output_kind)) => {
if input_kind == TensorKind::Bool || output_kind == TensorKind::Bool {
imports.register("burn::tensor::Bool");
}
if input_kind == TensorKind::Int || output_kind == TensorKind::Int {
imports.register("burn::tensor::Int");
}
}
_ => {}
}
}
}
impl UnaryNode {
pub(crate) fn erf(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.erf() };
Self::new(input, output, UnaryNodeKind::Erf, Rc::new(function))
}
pub(crate) fn flatten(input: Type, output: Type, start_dim: usize, end_dim: usize) -> Self {
let start_dim = start_dim.to_tokens();
let end_dim = end_dim.to_tokens();
let function = move |input| quote! { #input.flatten(#start_dim, #end_dim) };
Self::new(input, output, UnaryNodeKind::Flatten, Rc::new(function))
}
pub(crate) fn relu(input: Type, output: Type) -> Self {
let function = move |input| quote! { burn::tensor::activation::relu(#input) };
Self::new(input, output, UnaryNodeKind::Relu, Rc::new(function))
}
pub(crate) fn leaky_relu(input: Type, output: Type, alpha: f64) -> Self {
let alpha = alpha.to_tokens();
let function = move |input| quote! { burn::tensor::activation::leaky_relu(#input, #alpha) };
Self::new(input, output, UnaryNodeKind::Relu, Rc::new(function))
}
pub(crate) fn sigmoid(input: Type, output: Type) -> Self {
let function = move |input| quote! { burn::tensor::activation::sigmoid(#input) };
Self::new(input, output, UnaryNodeKind::Sigmoid, Rc::new(function))
}
pub(crate) fn hard_sigmoid(input: Type, output: Type, alpha: f64, beta: f64) -> Self {
let alpha = alpha.to_tokens();
let beta = beta.to_tokens();
let function =
move |input| quote! { burn::tensor::activation::hard_sigmoid(#input, #alpha, #beta) };
Self::new(input, output, UnaryNodeKind::HardSigmoid, Rc::new(function))
}
pub(crate) fn log_softmax(input: Type, output: Type, dim: usize) -> Self {
let dim = dim.to_tokens();
let function = move |input| quote! { burn::tensor::activation::log_softmax(#input, #dim) };
Self::new(input, output, UnaryNodeKind::LogSoftmax, Rc::new(function))
}
pub(crate) fn softmax(input: Type, output: Type, dim: usize) -> Self {
let dim = dim.to_tokens();
let function = move |input| quote! { burn::tensor::activation::softmax(#input, #dim) };
Self::new(input, output, UnaryNodeKind::Softmax, Rc::new(function))
}
pub(crate) fn sqrt(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.sqrt()};
Self::new(input, output, UnaryNodeKind::Sqrt, Rc::new(function))
}
pub(crate) fn tanh(input: Type, output: Type) -> Self {
let function = move |input| quote! { burn::tensor::activation::tanh(#input)};
Self::new(input, output, UnaryNodeKind::Tanh, Rc::new(function))
}
pub(crate) fn transpose(input: Type, output: Type, perm: Vec<i64>) -> Self {
let perm = perm.to_tokens();
let function = move |input| quote! { #input.permute(#perm) };
Self::new(input, output, UnaryNodeKind::Transpose, Rc::new(function))
}
pub(crate) fn reciprocal(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.recip() };
Self::new(input, output, UnaryNodeKind::Reciprocal, Rc::new(function))
}
pub(crate) fn cos(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.cos()};
Self::new(input, output, UnaryNodeKind::Cos, Rc::new(function))
}
pub(crate) fn sin(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.sin()};
Self::new(input, output, UnaryNodeKind::Sin, Rc::new(function))
}
pub(crate) fn exp(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.exp()};
Self::new(input, output, UnaryNodeKind::Exp, Rc::new(function))
}
pub(crate) fn gelu(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.gelu()};
Self::new(input, output, UnaryNodeKind::Gelu, Rc::new(function))
}
pub(crate) fn log(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.log()};
Self::new(input, output, UnaryNodeKind::Log, Rc::new(function))
}
pub(crate) fn neg(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.neg()};
Self::new(input, output, UnaryNodeKind::Neg, Rc::new(function))
}
pub(crate) fn not(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.bool_not()};
Self::new(input, output, UnaryNodeKind::Not, Rc::new(function))
}
pub(crate) fn cast(input: Type, output: Type) -> Self {
match (input.clone(), output.clone()) {
(Type::Scalar(input_scalar), Type::Scalar(output_scalar)) => {
if input_scalar.kind == output_scalar.kind {
Self::new(
input,
output,
UnaryNodeKind::Cast(None, None),
Rc::new(|input| input),
)
} else {
let ty = output_scalar.ty();
Self::new(
input,
output,
UnaryNodeKind::Cast(None, None),
Rc::new(move |input| quote! { #input as #ty }),
)
}
}
(Type::Tensor(input_tensor), Type::Tensor(output_tensor)) => {
if input_tensor.kind == output_tensor.kind {
Self::new(
input,
output,
UnaryNodeKind::Cast(Some(input_tensor.kind), Some(output_tensor.kind)),
Rc::new(|input| input),
)
} else {
let function = match output_tensor.kind {
TensorKind::Bool => move |input| quote! { #input.bool()},
TensorKind::Int => move |input| quote! { #input.int()},
TensorKind::Float => move |input| quote! { #input.float()},
};
Self::new(
input,
output,
UnaryNodeKind::Cast(Some(input_tensor.kind), Some(output_tensor.kind)),
Rc::new(function),
)
}
}
_ => panic!("output must be a tensor or scalar"),
}
}
pub(crate) fn reduce_max(input: Type, output: Type, dim: Option<usize>) -> Self {
if let Type::Tensor(ref tensor) = output {
if let Some(dim) = dim {
if tensor.kind == TensorKind::Bool {
panic!("ReduceMax is not supported for boolean");
}
let dim = dim.to_tokens();
Self::new(
input,
output,
UnaryNodeKind::ReduceMax,
Rc::new(move |input| quote! { #input.max_dim(#dim) }),
)
} else {
Self::new(
input,
output,
UnaryNodeKind::ReduceMax,
Rc::new(move |input| quote! { #input.max() }),
)
}
} else {
panic!("ReduceMax only supports tensor output");
}
}
pub(crate) fn reduce_min(input: Type, output: Type, dim: Option<usize>) -> Self {
if let Type::Tensor(ref tensor) = output {
if let Some(dim) = dim {
if tensor.kind == TensorKind::Bool {
panic!("ReduceMin is not supported for boolean");
}
let dim = dim.to_tokens();
Self::new(
input,
output,
UnaryNodeKind::ReduceMin,
Rc::new(move |input| quote! { #input.min_dim(#dim) }),
)
} else {
Self::new(
input,
output,
UnaryNodeKind::ReduceMin,
Rc::new(move |input| quote! { #input.min() }),
)
}
} else {
panic!("ReduceMin only supports tensor output");
}
}
pub(crate) fn reduce_mean(input: Type, output: Type, dim: Option<usize>) -> Self {
if let Type::Tensor(_) = output {
if let Some(dim) = dim {
let dim = dim.to_tokens();
Self::new(
input,
output,
UnaryNodeKind::ReduceMean,
Rc::new(move |input| quote! { #input.mean_dim(#dim) }),
)
} else {
Self::new(
input,
output,
UnaryNodeKind::ReduceMean,
Rc::new(move |input| quote! { #input.mean() }),
)
}
} else {
panic!("ReduceMean only supports tensor output");
}
}
pub(crate) fn reduce_prod(input: Type, output: Type, dim: Option<usize>) -> Self {
if let Type::Tensor(ref tensor) = output {
if let Some(dim) = dim {
if tensor.kind == TensorKind::Bool {
panic!("ReduceProd is not supported for boolean");
}
let dim = dim.to_tokens();
Self::new(
input,
output,
UnaryNodeKind::ReduceProd,
Rc::new(move |input| quote! { #input.prod_dim(#dim) }),
)
} else {
Self::new(
input,
output,
UnaryNodeKind::ReduceProd,
Rc::new(move |input| quote! { #input.prod() }),
)
}
} else {
panic!("ReduceProd only supports tensor output");
}
}
pub(crate) fn reduce_sum(input: Type, output: Type, dim: Option<usize>) -> Self {
if let Type::Tensor(ref tensor) = output {
if let Some(dim) = dim {
if tensor.kind == TensorKind::Bool {
panic!("ReduceSum is not supported for boolean");
}
let dim = dim.to_tokens();
Self::new(
input,
output,
UnaryNodeKind::ReduceSum,
Rc::new(move |input| quote! { #input.sum_dim(#dim) }),
)
} else {
Self::new(
input,
output,
UnaryNodeKind::ReduceSum,
Rc::new(move |input| quote! { #input.sum() }),
)
}
} else {
panic!("ReduceSum only supports tensor output");
}
}
pub(crate) fn shape(input: Type, output: Type, start_dim: usize, end_dim: usize) -> Self {
let start_dim = start_dim.to_tokens();
let end_dim = end_dim.to_tokens();
let function = move |input| {
quote! {
#input.dims()[#start_dim..#end_dim]
}
};
Self::new(input, output, UnaryNodeKind::Shape, Rc::new(function))
}
pub(crate) fn sign(input: Type, output: Type) -> Self {
let function = move |input| quote! { #input.sign()};
Self::new(input, output, UnaryNodeKind::Sign, Rc::new(function))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::burn::node::tests::one_node_graph;
use crate::burn::{ScalarKind, ScalarType, ShapeType, TensorType};
#[test]
fn test_unary_codegen_flatten() {
one_node_graph(
UnaryNode::flatten(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
1,
2,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.flatten(1, 2);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_erf() {
one_node_graph(
UnaryNode::erf(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.erf();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_relu() {
one_node_graph(
UnaryNode::relu(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::relu(tensor1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_leaky_relu() {
one_node_graph(
UnaryNode::leaky_relu(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
0.1,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::leaky_relu(tensor1, 0.1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_sigmoid() {
one_node_graph(
UnaryNode::sigmoid(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::sigmoid(tensor1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_hard_sigmoid() {
one_node_graph(
UnaryNode::hard_sigmoid(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
0.2,
0.5,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::hard_sigmoid(tensor1, 0.2, 0.5);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_log_softmax() {
one_node_graph(
UnaryNode::log_softmax(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
1,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::log_softmax(tensor1, 1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_softmax() {
one_node_graph(
UnaryNode::softmax(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
1,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::softmax(tensor1, 1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_tanh() {
one_node_graph(
UnaryNode::tanh(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = burn::tensor::activation::tanh(tensor1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_transpose() {
one_node_graph(
UnaryNode::transpose(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
vec![0, 3, 1, 2],
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.permute([0, 3, 1, 2]);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reduce_max() {
one_node_graph(
UnaryNode::reduce_max(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
Some(1),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.max_dim(1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::reduce_max(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 1)),
None,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 1> {
let tensor2 = tensor1.max();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reduce_min() {
one_node_graph(
UnaryNode::reduce_min(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
Some(1),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.min_dim(1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::reduce_min(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 1)),
None,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 1> {
let tensor2 = tensor1.min();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reduce_mean() {
one_node_graph(
UnaryNode::reduce_mean(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
Some(1),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.mean_dim(1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::reduce_mean(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 1)),
None,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 1> {
let tensor2 = tensor1.mean();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reduce_prod() {
one_node_graph(
UnaryNode::reduce_prod(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
Some(1),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.prod_dim(1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::reduce_prod(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 1)),
None,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 1> {
let tensor2 = tensor1.prod();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reduce_sum() {
one_node_graph(
UnaryNode::reduce_sum(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
Some(1),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.sum_dim(1);
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::reduce_sum(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 1)),
None,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 1> {
let tensor2 = tensor1.sum();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_reciprocal() {
one_node_graph(
UnaryNode::reciprocal(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.recip();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_cast() {
one_node_graph(
UnaryNode::cast(
Type::Scalar(ScalarType::new("scalar1", ScalarKind::Float64)),
Type::Scalar(ScalarType::new("scalar2", ScalarKind::Float32)),
),
quote! {
pub fn forward(&self, scalar1: f64) -> f32 {
let scalar2 = scalar1 as f32;
scalar2
}
},
vec!["scalar1".to_string()],
vec!["scalar2".to_string()],
);
one_node_graph(
UnaryNode::cast(
Type::Scalar(ScalarType::new("scalar1", ScalarKind::Float32)),
Type::Scalar(ScalarType::new("scalar2", ScalarKind::Float64)),
),
quote! {
pub fn forward(&self, scalar1: f32) -> f64 {
let scalar2 = scalar1 as f64;
scalar2
}
},
vec!["scalar1".to_string()],
vec!["scalar2".to_string()],
);
one_node_graph(
UnaryNode::cast(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_int("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4, Int> {
let tensor2 = tensor1.int();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::cast(
Type::Tensor(TensorType::new_int("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4, Int>) -> Tensor<B, 4> {
let tensor2 = tensor1.float();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
one_node_graph(
UnaryNode::cast(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_bool("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4, Bool> {
let tensor2 = tensor1.bool();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_cos() {
one_node_graph(
UnaryNode::cos(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.cos();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_sin() {
one_node_graph(
UnaryNode::sin(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.sin();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_exp() {
one_node_graph(
UnaryNode::exp(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.exp();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_gelu() {
one_node_graph(
UnaryNode::gelu(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.gelu();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_log() {
one_node_graph(
UnaryNode::log(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.log();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_neg_scalar() {
one_node_graph(
UnaryNode::neg(
Type::Scalar(ScalarType::new("scalar1", ScalarKind::Float64)),
Type::Scalar(ScalarType::new("scalar2", ScalarKind::Float64)),
),
quote! {
pub fn forward(&self, scalar1: f64) -> f64 {
let scalar2 = scalar1.neg();
scalar2
}
},
vec!["scalar1".to_string()],
vec!["scalar2".to_string()],
);
}
#[test]
fn test_unary_neg_tensor() {
one_node_graph(
UnaryNode::neg(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.neg();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_not() {
one_node_graph(
UnaryNode::not(
Type::Tensor(TensorType::new_bool("tensor1", 4)),
Type::Tensor(TensorType::new_bool("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4, Bool>) -> Tensor<B, 4, Bool> {
let tensor2 = tensor1.bool_not();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
#[test]
fn test_unary_codegen_shape() {
one_node_graph(
UnaryNode::shape(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Shape(ShapeType::new("shape1", 4)),
1,
3,
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> [usize; 4] {
let shape1: [usize; 4] = tensor1.dims()[1..3].try_into().unwrap();
shape1
}
},
vec!["tensor1".to_string()],
vec!["shape1".to_string()],
);
}
#[test]
fn test_unary_sign_tensor() {
one_node_graph(
UnaryNode::sign(
Type::Tensor(TensorType::new_float("tensor1", 4)),
Type::Tensor(TensorType::new_float("tensor2", 4)),
),
quote! {
pub fn forward(&self, tensor1: Tensor<B, 4>) -> Tensor<B, 4> {
let tensor2 = tensor1.sign();
tensor2
}
},
vec!["tensor1".to_string()],
vec!["tensor2".to_string()],
);
}
}