use onnx_runtime_ir::{Attribute, DataType};
use crate::context::InferenceContext;
use crate::dim_expr::DimExpr;
use crate::error::ShapeInferError;
use crate::registry::InferenceRegistry;
use crate::shape_data::ShapeData;
pub fn shape(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(input) = ctx.input_shape(0).map(<[DimExpr]>::to_vec) else {
return Ok(());
};
let rank = input.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(
ctx.node
.attr("start")
.and_then(Attribute::as_int)
.unwrap_or(0),
);
let end = clamp(
ctx.node
.attr("end")
.and_then(Attribute::as_int)
.unwrap_or(rank),
);
let elems: Vec<DimExpr> = input.get(start..end.max(start)).unwrap_or(&[]).to_vec();
ctx.set_output(
0,
DataType::Int64,
vec![DimExpr::constant(elems.len() as i64)],
);
ctx.set_output_shape_data(0, ShapeData::vector(DataType::Int64, elems));
Ok(())
}
pub fn size(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(input) = ctx.input_shape(0).map(<[DimExpr]>::to_vec) else {
return Ok(());
};
let total = DimExpr::product(&input);
ctx.set_output(0, DataType::Int64, Vec::new());
ctx.set_output_shape_data(0, ShapeData::scalar(DataType::Int64, total));
Ok(())
}
pub fn constant(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let node = ctx.node;
if let Some(Attribute::Tensor(t)) = node.attr("value") {
let shape = t
.dims
.iter()
.map(|&d| DimExpr::constant(d as i64))
.collect();
ctx.set_output(0, t.dtype, shape);
if let Some(sd) = ShapeData::from_tensor(t.dtype, &t.dims, &t.data) {
ctx.set_output_shape_data(0, sd);
}
return Ok(());
}
if let Some(i) = node.attr("value_int").and_then(Attribute::as_int) {
ctx.set_output(0, DataType::Int64, Vec::new());
ctx.set_output_shape_data(0, ShapeData::scalar(DataType::Int64, DimExpr::constant(i)));
return Ok(());
}
if let Some(v) = node.attr("value_ints").and_then(Attribute::as_ints) {
let elems: Vec<DimExpr> = v.iter().map(|&x| DimExpr::constant(x)).collect();
ctx.set_output(0, DataType::Int64, vec![DimExpr::constant(v.len() as i64)]);
ctx.set_output_shape_data(0, ShapeData::vector(DataType::Int64, elems));
return Ok(());
}
if node.attr("value_float").is_some() {
ctx.set_output(0, DataType::Float32, Vec::new());
return Ok(());
}
if let Some(Attribute::Floats(v)) = node.attr("value_floats") {
ctx.set_output(
0,
DataType::Float32,
vec![DimExpr::constant(v.len() as i64)],
);
return Ok(());
}
Ok(())
}
pub fn constant_of_shape(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let (dtype, fill) = value_attr_scalar(ctx.node);
if let Some(sd) = ctx.input_shape_data(0).cloned() {
let shape = sd.as_shape();
let numel_known = shape.iter().all(|d| d.as_const().is_some());
ctx.set_output(0, dtype, shape.clone());
if let (Some(fill), true) = (
fill,
numel_known && (dtype.is_int() || dtype == DataType::Bool),
) {
let numel: Option<i64> = shape
.iter()
.filter_map(|d| d.as_const())
.try_fold(1i64, |acc, d| acc.checked_mul(d));
if let Some(numel) = numel
&& (0..=1024).contains(&numel)
{
let elems = vec![fill; numel as usize];
ctx.set_output_shape_data(0, ShapeData::vector(dtype, elems));
}
}
return Ok(());
}
if let Some(shape) = ctx.input_shape(0).map(<[DimExpr]>::to_vec)
&& shape.len() == 1
&& let Some(rank) = shape[0].as_const()
{
let out = (0..rank).map(|_| ctx.fresh_dim()).collect();
ctx.set_output(0, dtype, out);
}
Ok(())
}
pub fn cast(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(to) = ctx.node.attr("to").and_then(Attribute::as_int) else {
return Err(ShapeInferError::MissingAttribute {
op: "Cast".into(),
attr: "to".into(),
});
};
let Some(dtype) = DataType::from_onnx(to as i32) else {
return Err(ShapeInferError::Invalid {
op: "Cast".into(),
detail: format!("unknown target dtype {to}"),
});
};
if let Some(shape) = ctx.input_shape(0).map(<[DimExpr]>::to_vec) {
ctx.set_output(0, dtype, shape);
}
if (dtype.is_int() || dtype == DataType::Bool)
&& let Some(sd) = ctx.input_shape_data(0).cloned()
{
ctx.set_output_shape_data(
0,
ShapeData {
dtype,
dims: sd.dims,
elems: sd.elems,
float_elems: None,
},
);
}
Ok(())
}
pub fn identity(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
if let Some(t) = ctx.input_type(0).cloned() {
ctx.set_output_type(0, t);
}
if let Some(sd) = ctx.input_shape_data(0).cloned() {
ctx.set_output_shape_data(0, sd);
}
Ok(())
}
fn value_attr_scalar(node: &onnx_runtime_ir::Node) -> (DataType, Option<DimExpr>) {
if let Some(Attribute::Tensor(t)) = node.attr("value") {
let fill = ShapeData::from_tensor(t.dtype, &t.dims, &t.data)
.and_then(|sd| sd.elems.into_iter().next());
return (t.dtype, fill);
}
(DataType::Float32, None)
}
pub fn register(reg: &mut InferenceRegistry) {
reg.register("", "Shape", 1, shape);
reg.register("", "Size", 1, size);
reg.register("", "Constant", 1, constant);
reg.register("", "ConstantOfShape", 1, constant_of_shape);
reg.register("", "Cast", 1, cast);
reg.register("", "CastLike", 1, cast_like);
reg.register("", "Identity", 1, identity);
for version in [10, 13, 19, 21, 23, 25] {
reg.register("", "QuantizeLinear", version, quantize_linear);
reg.register("", "DequantizeLinear", version, dequantize_linear);
}
reg.register("", "DynamicQuantizeLinear", 11, dynamic_quantize_linear);
}
fn cast_like(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let dtype = ctx.input_dtype(1);
if let (Some(shape), Some(dtype)) = (ctx.input_shape(0).map(<[DimExpr]>::to_vec), dtype) {
ctx.set_output(0, dtype, shape);
}
Ok(())
}
fn quantize_linear(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let dtype = if ctx.has_input(2) {
ctx.input_dtype(2)
} else {
Some(DataType::Uint8)
};
if let (Some(shape), Some(dtype)) = (ctx.input_shape(0).map(<[DimExpr]>::to_vec), dtype) {
ctx.set_output(0, dtype, shape);
}
Ok(())
}
fn dequantize_linear(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
if let (Some(shape), Some(dtype)) = (
ctx.input_shape(0).map(<[DimExpr]>::to_vec),
ctx.input_dtype(1),
) {
ctx.set_output(0, dtype, shape);
}
Ok(())
}
fn dynamic_quantize_linear(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
if let Some(shape) = ctx.input_shape(0).map(<[DimExpr]>::to_vec) {
ctx.set_output(0, DataType::Uint8, shape);
ctx.set_output(1, DataType::Float32, Vec::new());
ctx.set_output(2, DataType::Uint8, Vec::new());
}
Ok(())
}