## Environment
- Hailo AI SW Suite 2025-10 (DFC 3.33.0), HailoRT 4.23.0, Hailo-8 (Raspberry Pi 5 + AI HAT)
- SSD-style model, 12 output layers (6 scales × cls/loc), all end nodes are plain convs
## Summary
When requesting 16-bit outputs, the optimizer applies the request correctly, but the **compiler silently demotes some outputs back to 8-bit**, and the resulting HEFs are internally inconsistent in two different ways depending on how the request was written. There is no warning at any point; the compile reports success.
## Fact 1: optimize stage is fine
With `quantization_param(output_layerN, precision_mode=a16_w16)` for all 12 outputs, the quantized HAR shows all 12 head convs at `a8_w8_a16`. So far so good.
## Fact 2: the compiler silently demotes a varying subset
`hailortcli parse-hef` on the compiled HEF shows some outputs as UINT16 and some as UINT8. The demoted subset **changes with the allocation** (three compilations — default, `resources_param`-constrained, and a different model-script syntax — produced three different demoted subsets). No warning or log message mentions the demotion.
## Fact 3a: with per-conv syntax → runtime failure
When 16-bit was requested via the conv layer names (`quantization_param(convNN, precision_mode=a16_w16)` — admittedly not the documented idiom), demotion hit only one half of a cls/loc output pair, and the resulting HEF **fails at runtime**:
```
[HailoRT] [error] CHECK failed - src_size must be: 18048, passed_size: 13728
[HailoRT] [error] Queue element PullQueueEl_demux6/conv62 run in thread function failed! status = HAILO_INVALID_ARGUMENT(2)
```
Neither 18048 nor 13728 corresponds to any user-facing frame size of the model, so HailoRT is rejecting its own internal demux configuration. Reproduced identically with the C++ `InferModel` API and pyhailort, and across two different allocations.
## Fact 3b: with the documented syntax → silently dead outputs
With the documented `output_layerN` idiom the HEF runs, but the demoted UINT8 streams keep **16-bit dequantization parameters**. Example from our HEF (printed via `InferModel::InferStream::get_quant_infos()`):
| Output (demoted to UINT8) | qp_scale | qp_zp | (0 − zp)·scale | Observed device min | 255·scale | Observed device span |
|—|—|—|—|—|—|—|
| conv54 (5x5x54) | 0.00584604 | 29897 | −174.78 | −174.78 | 1.49 | 1.49 |
| conv55 (5x5x24) | 0.000328704 | 17981 | −5.911 | −5.91 | 0.084 | 0.08 |
| conv81 (3x3x36) | 0.001608 | 28952 | −46.56 | −46.55 | 0.410 | 0.40 |
| conv82 (3x3x16) | 0.000136132 | 23675 | −3.223 | −3.22 | 0.035 | 0.03 |
A zero-point of ~30000 on a UINT8 stream is impossible; the entire uint8 code range is dequantized into a ~1-unit sliver at the bottom of the 16-bit range, so these outputs are effectively constant. In a detector this silently blinds the affected scales while the rest of the model works — the worst kind of failure, since nothing errors and overall results look plausible.
## Requests
1. Make precision demotion an explicit warning (or a hard error with an override flag)
2. Fix the dequantization parameters of demoted streams (Fact 3b) and the demux configuration for mixed-precision pairs (Fact 3a)
3. Document the constraints that make a 16-bit output layer ineligible — we could not find them (predecessors are plain convs, tensors are tiny)
## Workaround
Avoid mixed-precision outputs entirely: keep all outputs 8-bit and recover accuracy with manual activation clipping (`pre_quantization_optimization(activation_clipping, …)`) on the wide-range heads. This configuration works correctly on device (all outputs match emulation, corr 0.97–0.999).