use onnx_runtime_ir::Attribute;
use crate::context::InferenceContext;
use crate::error::ShapeInferError;
use crate::registry::InferenceRegistry;
fn is_reduction_none(ctx: &InferenceContext) -> bool {
ctx.node
.attr("reduction")
.and_then(Attribute::as_str)
.unwrap_or("mean")
== "none"
}
fn nll_loss(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(input) = ctx.input_type(0).cloned() else {
return Ok(());
};
let dtype = input.dtype;
let rank = input.shape.len();
if rank < 2 {
return Err(ShapeInferError::InvalidRank {
op: "NegativeLogLikelihoodLoss".into(),
index: 0,
rank,
detail: "input rank must be >= 2 (N, C, ...)".into(),
});
}
if is_reduction_none(ctx) {
let mut shape = Vec::with_capacity(rank - 1);
shape.push(input.shape[0].clone());
shape.extend(input.shape[2..].iter().cloned());
ctx.set_output(0, dtype, shape);
} else {
ctx.set_output(0, dtype, Vec::new());
}
Ok(())
}
fn softmax_cross_entropy_loss(ctx: &mut InferenceContext) -> Result<(), ShapeInferError> {
let Some(scores) = ctx.input_type(0).cloned() else {
return Ok(());
};
let dtype = scores.dtype;
if is_reduction_none(ctx) {
if let Some(labels) = ctx.input_shape(1).map(<[_]>::to_vec) {
ctx.set_output(0, dtype, labels);
}
} else {
ctx.set_output(0, dtype, Vec::new());
}
if ctx.num_outputs() >= 2 {
ctx.set_output(1, dtype, scores.shape.clone());
}
Ok(())
}
pub fn register(reg: &mut InferenceRegistry) {
reg.register("", "NegativeLogLikelihoodLoss", 12, nll_loss);
reg.register(
"",
"SoftmaxCrossEntropyLoss",
12,
softmax_cross_entropy_loss,
);
}