nx_upsample.nx
buildroot/runtime/nx_upsample.nx
about
nx_upsample.nx -- 2x upsample for 4D feature maps.
Closes the missing-dep gap for VAE decoder + UNet up-blocks.
Modern image-gen architectures use 2x nearest-neighbor or
2x bilinear upsample inside the decoder stages:
VAE decoder: 4 stages of 2x upsample (8x total = 64x64 latent
-> 512x512 image)
UNet up-path: 2x upsample at each scale level
Super-resolution networks: 2x or 4x upsample
L3 canonical primitive composing:
NxTensor (L1, 4D NCHW input/output)
nx_loop.LoopVerdict (control)
-- no other deps; pure pixel-replication / linear-interp math
===== Tensor layout =============================================
Input: [N, C, H, W] NCHW Q10
Output: [N, C, 2H, 2W] 2x upscaled in spatial dims; channels
unchanged
===== Filter modes ==============================================
NX_UP_NEAREST -- each output pixel = nearest input pixel.
Blocky but cheap. Common in VAE decoder when
followed by a 3x3 conv that smooths the
artifacts.
NX_UP_BILINEAR -- 4-tap bilinear from the 4 nearest input pixels.
Smoother; slightly more expensive.
===== Pixel-center convention ===================================
Output pixel (oy, ox) maps to input pixel at:
iy = (oy + 0.5) / 2 - 0.5
ix = (ox + 0.5) / 2 - 0.5
This is the "align_corners=False" convention from PyTorch -- the
standard for VAE-class upsampling. In Q10 fixed-point:
iy_q10 = (oy + 1) * Q10 / 2 - Q10 / 2 ; if (... ) ... simplifies
dependencies 4 imports · 1 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nxnx_tensor.nx
imported by: nx_vae_decode_stage.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 60 | const NX_UP_Q10: nx_int = 1024 |
| 64 | const NX_UP_NEAREST: nx_int = 0 |
| 65 | const NX_UP_BILINEAR: nx_int = 1 |
| 66 | const NX_UP_FILTER_N: nx_int = 2 |
| 76 | const NX_UP_OK: nx_int = 0 |
| 77 | const NX_UP_ERR_BAD_DTYPE: nx_int = 1 |
| 78 | const NX_UP_ERR_BAD_NDIM: nx_int = 2 |
| 79 | const NX_UP_ERR_SHAPE_MISMATCH: nx_int = 3 |
| 80 | const NX_UP_ERR_NOT_CONTIGUOUS: nx_int = 4 |
| 81 | const NX_UP_ERR_BAD_FILTER: nx_int = 5 |
| 82 | const NX_UP_N_VERDICTS: nx_int = 6 |
functions
| 68 | func nx_up_filter_is_valid(k: nx_int) -> nx_int called by 1: main |
| 84 | func nx_up_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 92 | func _up_nearest(input: *NxTensor, output: *NxTensor) -> nx_int called by 1: nx_upsample_2x |
| 162 | func _up_bilinear(input: *NxTensor, output: *NxTensor) -> nx_int called by 1: nx_upsample_2x |
| 254 | func nx_upsample_2x(input: *NxTensor, output: *NxTensor, filter: nx_int) -> nx_int |
| 286 | func main() -> i64 |