use onnx_runtime_ep_api::{EpError, Kernel, KernelFactory, Result, TensorMut, TensorView};
use onnx_runtime_ir::{DataType, Node};
use super::{check_arity, write_dense_bytes};
pub struct ShapeKernel {
start: i64,
end: Option<i64>,
}
pub struct ShapeFactory;
impl KernelFactory for ShapeFactory {
fn create(&self, node: &Node, _shapes: &[Vec<usize>]) -> Result<Box<dyn Kernel>> {
let start = node.attr("start").and_then(|a| a.as_int()).unwrap_or(0);
let end = node.attr("end").and_then(|a| a.as_int());
Ok(Box::new(ShapeKernel { start, end }))
}
}
impl Kernel for ShapeKernel {
fn execute(&self, inputs: &[TensorView], outputs: &mut [TensorMut]) -> Result<()> {
check_arity("Shape", inputs, outputs, 1, 1, 1)?;
if outputs[0].dtype != DataType::Int64 {
return Err(EpError::KernelFailed(format!(
"Shape: output must be Int64, got {:?}",
outputs[0].dtype
)));
}
let shape = inputs[0].shape;
let rank = shape.len() as i64;
let clamp = |v: i64| -> usize {
let v = if v < 0 { v + rank } else { v };
v.clamp(0, rank) as usize
};
let start = clamp(self.start);
let end = clamp(self.end.unwrap_or(rank));
let dims = shape.get(start..end.max(start)).unwrap_or(&[]);
let mut bytes = Vec::with_capacity(dims.len() * 8);
for &d in dims {
bytes.extend_from_slice(&(d as i64).to_le_bytes());
}
write_dense_bytes(&mut outputs[0], &bytes)
}
fn supports_strided_input(&self, _input_idx: usize) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernels::testutil::Owned;
fn shape_kernel(start: i64, end: Option<i64>) -> ShapeKernel {
ShapeKernel { start, end }
}
#[test]
fn shape_of_3d_tensor() {
let x = Owned::f32(&[2, 3, 4], &[0.0; 24]);
let mut out = Owned::zeros(DataType::Int64, &[3]);
shape_kernel(0, None)
.execute(&[x.view()], &mut [out.view_mut()])
.unwrap();
assert_eq!(out.to_i64(), vec![2, 3, 4]);
}
#[test]
fn shape_of_scalar_is_empty() {
let x = Owned::f32(&[], &[7.0]);
let mut out = Owned::zeros(DataType::Int64, &[0]);
shape_kernel(0, None)
.execute(&[x.view()], &mut [out.view_mut()])
.unwrap();
assert_eq!(out.to_i64(), Vec::<i64>::new());
}
#[test]
fn shape_start_end_slices_dims() {
let x = Owned::f32(&[2, 3, 4, 8], &[0.0; 192]);
let mut out = Owned::zeros(DataType::Int64, &[1]);
shape_kernel(0, Some(1))
.execute(&[x.view()], &mut [out.view_mut()])
.unwrap();
assert_eq!(out.to_i64(), vec![2]);
}
#[test]
fn shape_negative_start_end() {
let x = Owned::f32(&[2, 3, 4, 8], &[0.0; 192]);
let mut out = Owned::zeros(DataType::Int64, &[1]);
shape_kernel(-2, Some(-1))
.execute(&[x.view()], &mut [out.view_mut()])
.unwrap();
assert_eq!(out.to_i64(), vec![4]);
}
#[test]
fn shape_start_only_to_end() {
let x = Owned::f32(&[2, 3, 4], &[0.0; 24]);
let mut out = Owned::zeros(DataType::Int64, &[2]);
shape_kernel(1, None)
.execute(&[x.view()], &mut [out.view_mut()])
.unwrap();
assert_eq!(out.to_i64(), vec![3, 4]);
}
}