Table of contents

Authoring a prepared context's weight payload

Kokoro-Hexagon 0a03be39Updated 2026-09-23

A serialized QNN context is an executable template whose numeric payload we can write from logical coordinates, with no compile and no prepare library.

Result

PING_32.QNN (45,056 bytes, graph PING_32_GRAPH, in PING_32_X [1,32], out PING_32_Y [1,32]) with its entire 2,048-byte FP16 weight region replaced by an identity matrix authored through the V73 K-pair fold, then executed on SM8550 / Hexagon V73 through contextCreateFromBinary + graphRetrieve + graphExecute from PowerShell:

LoadMs=48  ContextBytes=45056  ExecuteMs=18.1,2.6,1.2
Out[PING_32_Y]  NonFinite=0  MaxAbs=1.1921E-07  SnrDb=138.47  MeanAbs=1
Passed=True

X is all ones, so Y[n] = sum_k W[k,n] = 1 for an identity. MaxAbs 1.19e-7 is one fp32 ULP at 1.0. The fold is therefore confirmed by execution, not only by inspection:

half = (k >> 1) * (N * 2) + n * 2 + (k & 1)      file = base + 2 * half

No ONNX, no Python, no C++ worker, and no libQnnHtpPrepare.so — prepare is needed to build a graph, not to run a finished context.

What this corrects

Patch-QnnWeights.ps1 in the old repository was written for exactly this experiment in 2026-08 and its output, PATCHED_32.QNN, is on disk. Decoding that artifact shows its weight region is entirely FP16 zero: 2,045 of 2,048 bytes differ from the template, and none of the 1,024 halfwords is non-zero.

The cause is in the script:

param([int]$K = 32, [int]$N = 32)
for ($k = 0; $k -lt $K; $k++) { for ($n = 0; $n -lt $N; $n++) { ... } }

PowerShell variable names are case-insensitive, so $k is $K and $n is $N. Both conditions evaluate 0 -lt 0, neither body runs, the packed array stays zero, and the script then writes 2,048 zero bytes over the weights and executes without any error. So the experiment appeared to run and proved nothing.

tools/Author-QnnWeights.ps1 uses loop variables that cannot shadow their bounds and asserts both the element count and the expected number of non-zero halfwords before writing.

Scope

This authors the numeric payload of an existing topology. Synthesising a new graph structure would mean writing the FlatBuffers section of the context, which is not decoded. Those compose, though: build a topology once through the C API, serialize it with contextGetBinary, then author weights into it indefinitely at zero compile cost.