aidos-rot13-onxx

A byte-level ROT13 "model" in ONNX. It is a smoke-test fixture, not a trained network: the only weight is a hand-built 256×256 permutation matrix, so the correct output is known exactly for every possible input. Any wrong byte is a runtime bug, never model drift.

Built for Aidos, a local-first AI agent project, as a fixture for testing its model-loading and inference runtime against a model whose correct output is known in advance for every input.

File rot13.onnx (258 KB)
Opset / IR 13 / 8 — the floor mobile ONNX Runtime builds still accept
Inputs input_idsint64[batch, seq], byte values 0..255
Outputs output_idsint64[batch, seq]; logitsfloat32[batch, seq, 256]
Contract output_ids[b][i] == rot13(input_ids[b][i])

batch and seq are dynamic. Both dimensions may be any size ≥ 1.

Why ROT13

A smoke test needs a model whose correct output is known in advance, for every possible input. Real models do not offer that: their outputs shift with quantization, sampling, threading, and version bumps, so a test can only assert something vague and ends up passing on broken runtimes.

ROT13 over bytes gives a total, exactly-specified function on all 256 inputs. This fixture computes it through genuine model machinery — a float32 GEMM instead of a lookup table — so loading the file exercises what a real model exercises: dynamic shapes, float matmul, argmax, and the int64 ↔ float boundary, while staying small enough to check into git.

The model is not trained. The only weight is a hand-built permutation matrix; see the derivation below.

Why it is shaped this way

input_ids  ──OneHot(depth=256)──▶ float32[batch, seq, 256]
           ──MatMul(P)─────────▶ logits
           ──ArgMax(axis=-1)───▶ output_ids

P[t] is the one-hot row for rot13(t), so logits is a clean one-hot: exactly one 1.0 per position and 0.0 elsewhere. A smoke test can assert on that directly when it wants to check float tensor plumbing rather than just the decoded bytes.

Properties worth asserting

  • Total. Defined for all 256 byte values, not just ASCII letters.
  • Involution. ROT13 is its own inverse, so feeding output_ids back in returns the original input. A round-trip check needs no expected-value table.
  • Position- and batch-independent. Each position is computed on its own, so a row's result must not change with sequence length or batch composition. A batched result that differs from the same row run alone is a bug.
  • Exact. The logit gap is 1.0 vs 0.0; no tolerance tuning is needed.

Inputs outside 0..255 are a contract violation. ONNX defines OneHot to emit an all-zero row for an out-of-range index, so the model returns 0 rather than raising — callers must range-check.

Use

import numpy as np, onnxruntime as ort

session = ort.InferenceSession("rot13.onnx", providers=["CPUExecutionProvider"])
ids = np.frombuffer(b"Hello, World!", dtype=np.uint8).astype(np.int64)[None, :]
output_ids, logits = session.run(None, {"input_ids": ids})
print(bytes(output_ids[0].astype(np.uint8)))   # b'Uryyb, Jbeyq!'

Regenerate and verify

Source, build.py, and verify.py live in the Aidos monorepo:

pip install onnx onnxruntime numpy
python3 build.py       # rewrites rot13.onnx
python3 verify.py      # exits non-zero on any mismatch

verify.py checks all 256 byte values individually and as one batch, several text fixtures, the involution, batch independence, and the shape and one-hot structure of logits. It derives expected values from the standard library's rot13 codec rather than from build.py, so a bug in the builder cannot make the verifier agree with it.

See also: aidos-rot13-gguf, the same fixture as a full autoregressive llama-architecture transformer.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support