use onnx_runtime_ir::{Attribute, DataType};
use crate::context::InferenceContext;
use crate::dim_expr::DimExpr;
use crate::error::ShapeInferError;
use crate::registry::InferenceRegistry;
fn dtype_attr(ctx: &InferenceContext) -> Option<DataType> {
let raw = ctx.node.attr("dtype").and_then(Attribute::as_int)?;
i32::try_from(raw).ok().and_then(DataType::from_onnx)
}
fn random(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let dtype = dtype_attr(ctx).unwrap_or(DataType::Float32);
let Some(shape) = ctx
.node
.attr("shape")
.and_then(Attribute::as_ints)
.map(<[i64]>::to_vec)
else {
return Ok(());
};
let dims = shape
.into_iter()
.map(|extent| {
if extent >= 0 {
DimExpr::constant(extent)
} else {
ctx.fresh_dim()
}
})
.collect();
ctx.set_output(0, dtype, dims);
Ok(())
}
fn random_like(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(input) = ctx.input_type(0).cloned() else {
return Ok(());
};
let dtype = dtype_attr(ctx).unwrap_or(input.dtype);
ctx.set_output(0, dtype, input.shape);
Ok(())
}
fn multinomial(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(input) = ctx.input_shape(0).map(<[DimExpr]>::to_vec) else {
return Ok(());
};
if input.len() != 2 {
return Err(ShapeInferError::InvalidRank {
op: "Multinomial".into(),
index: 0,
rank: input.len(),
detail: "expected [batch, classes]".into(),
});
}
let dtype = dtype_attr(ctx).unwrap_or(DataType::Int32);
let sample_size = ctx
.node
.attr("sample_size")
.and_then(Attribute::as_int)
.unwrap_or(1);
let samples = if sample_size >= 0 {
DimExpr::constant(sample_size)
} else {
ctx.fresh_dim()
};
ctx.set_output(0, dtype, vec![input[0].clone(), samples]);
Ok(())
}
pub fn register(reg: &mut InferenceRegistry) {
reg.register("", "RandomNormal", 1, random);
reg.register("", "RandomUniform", 1, random);
reg.register("", "RandomNormalLike", 1, random_like);
reg.register("", "RandomUniformLike", 1, random_like);
reg.register("", "Bernoulli", 15, random_like);
reg.register("", "Multinomial", 7, multinomial);
}