Skip to content

feat(io-onnx): import LeakyRelu, Silu, Clip, BatchNormalization and Pad as real ops - #1466

Merged
michalharakal merged 1 commit into
developfrom
feature/1400-onnx-real-ops
Oct 31, 2026
Merged

michalharakal merged 1 commit into
developfrom
feature/1400-onnx-real-ops

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

Summary

Implements the op-mapping lanes of #1400. These ONNX node types imported as OnnxPlaceholderOperation, so the graph built but threw on execution. They now map to operations the ComputeGraphExecutor dispatches, and each one-node graph imports and executes with PyTorch/ONNX-spec semantics:

ONNX node Mapped to Notes
LeakyRelu (#1401) leakyRelu alpha → negativeSlope, default 0.01
Silu (#1402) silu
Clip (#1403) clamp min/max from attributes (opset < 11) or folded from initializer inputs (opset ≥ 11); dynamic min/max tensors keep the placeholder so execution fails loudly instead of silently not clipping
BatchNormalization (#1404) batchNorm executor decomposes inference-mode BN into primitives with per-channel (1, C, 1, …) broadcasts; epsilon honoured
Pad (#1405) pad2d mappable form only (constant mode, zero fill, 4D, spatial-only pads); pads from attribute or INT64 initializer; proper H/W output-shape inference; other forms keep the placeholder

Design notes:

  • The importer's existing pattern is followed: operations are local BaseOperation subclasses whose name matches the executor's dispatch (same as OnnxConcatOperation), so no public lang-core API changes.
  • Ops that fold trailing initializer inputs into parameters (Clip min/max, Pad pads) wire only the data input as an edge — the folded initializers never reach execution.
  • ComputeGraphExecutor gains dispatch for leakyRelu, clamp, pad2d, batchNorm, and treats onnx_initializer nodes as input-like: values bind by node id or tensor name, and unused initializers no longer break execution.
  • ConvTranspose ([Lane 2 · kotlin-core] ONNX importer — import ONNX ConvTranspose as a real op #1406) is deliberately not mapped here — it needs the convTranspose2d op that lands with feat(ops): implement convTranspose2d — CPU kernel, backward and StableHLO lowering #1464; it stays a loud placeholder until that merges.

Tests

OnnxRealOpsExecuteTest (jvmTest, with a new backend-cpu test dependency): builds one-node ONNX models with pbandk protos, imports them and runs them on the real CPU ops — value assertions for every op (BN against the closed-form reference), both Clip forms (attribute + folded initializers, with an edge-count assertion), both Pad forms (attribute + INT64 initializer, incl. output-shape inference), and the fail-loud paths (dynamic Clip bound, reflect-mode Pad).

Closes #1401, closes #1402, closes #1403, closes #1404, closes #1405. Part of #1400.

…ad as real ops (#1400)

The ONNX importer mapped these node types to OnnxPlaceholderOperation,
which made the graph import but throw on execution.

- LeakyRelu -> leakyRelu (alpha attribute -> negativeSlope, default 0.01)
- Silu -> silu
- Clip -> clamp; min/max from attributes (opset < 11) or folded from
  initializer inputs (opset >= 11). Dynamic min/max tensors keep the
  placeholder so execution fails loudly instead of silently not clipping.
- BatchNormalization -> batchNorm with its epsilon attribute; the executor
  decomposes inference-mode BN into primitive ops with per-channel
  (1, C, 1, ...) broadcast reshapes.
- Pad -> pad2d for the mappable form (constant mode, zero fill, 4D input,
  spatial-only pads), pads from the attribute (opset < 11) or an INT64
  initializer input (opset >= 11), with proper H/W output-shape inference.
  Other modes/layouts keep the placeholder.

Supporting changes:
- Ops whose trailing initializer inputs fold into parameters at import
  (Clip min/max, Pad pads) wire only the data input as a graph edge.
- ComputeGraphExecutor: dispatch leakyRelu, clamp, pad2d and batchNorm;
  treat onnx_initializer nodes as input-like so their values bind by node
  id or tensor name and unused initializers no longer break execution.
- skainet-io-onnx jvmTest gains a backend-cpu dependency so the new tests
  import AND execute each one-node graph against the real CPU ops.

ConvTranspose (#1406) is intentionally not mapped here: it needs the
convTranspose2d op that lands with #1464.

Closes #1401, closes #1402, closes #1403, closes #1404, closes #1405.
Part of #1400.
@michalharakal
michalharakal merged commit a62a3df into develop Oct 31, 2026
17 checks passed
@michalharakal
michalharakal deleted the feature/1400-onnx-real-ops branch October 31, 2026 20:50
@michalharakal michalharakal mentioned this pull request Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment