Back to blog
ResearchModelsMirageModel ArchitectureHConvGLU

HConvGLU for Channel Mixing in Image Encoders

The forward pass of Mirage's channel mixer. Fixed Walsh transforms, three learned bands and a GELU gate, with the indexing, arithmetic and implementation details.

KM

Karen Mosoyan

HN

Henry Ndubuaku

11 min read

Mirage's image encoder uses 512 channels to describe each cell of a 16×1616 \times 16 image grid. Mixing those channels with a conventional feed-forward layer of hidden width 2048 would require about 2.1 million weights per block. Repeating that across the encoder would spend much of the model's storage on channel mixing alone.

HConvGLU uses a fixed Walsh transform to put each feature vector into another basis, mixes nearby coefficients with learned bands, then transforms back. A GELU gate makes the result depend nonlinearly on the input. The released encoder uses 28 of these mixers, each with 100,352 parameters. This article derives the forward pass, explains the restriction imposed by the bands and counts the work the operator actually performs.

What a dense mixer spends

For a column vector x∈Rdx \in \mathbb{R}^{d}, a two-matrix feed-forward layer with expansion ratio four has the form

FFN⁡(x)=W2 ϕ(W1x),W1∈R4d×d,W2∈Rd×4d.\operatorname{FFN}(x)=W_2\,\phi(W_1x), \qquad W_1\in\mathbb{R}^{4d\times d}, \quad W_2\in\mathbb{R}^{d\times4d}.

Ignoring biases, it stores 8d28d^2 weights and uses 8d28d^2 multiply-accumulates per vector. At d=512d=512, both counts are 2,097,152. These are the costs of this specific dense baseline, not of every possible feed-forward layer or a matched GLU.

A channel mixer operates within one feature vector. Spatial convolutions and attention move information between image locations. Keeping those jobs separate lets us ask a narrower question about the channel map. How much freedom do we need to give every pair of channels, and how much can a fixed transform supply?

The Hadamard MLP in Needle learns structured Kronecker mixing factors. HConvGLU keeps the basis fixed and spends its learned parameters on bands inside that basis. The two constructions share a connection to Walsh-Hadamard transforms, but they define different families of functions.

Gabor initialisation supplies a separate spatial bias to the encoder's convolutions. It changes their starting weights. HConvGLU defines the channel operator used throughout inference.

The exact Walsh basis

For n=2qn=2^q, the normalised Sylvester construction is

H1=[1],H2n=12[HnHnHn−Hn].H_1=[1], \qquad H_{2n}=\frac{1}{\sqrt2}\begin{bmatrix}H_n&H_n\\H_n&-H_n\end{bmatrix}.

Every entry is ±n−1/2\pm n^{-1/2} and the rows are orthonormal. The sequency of a row is its number of sign changes when read from left to right. Sorting the rows by that number gives the sequency-ordered matrix WW used in HConvGLU.

For an explicit indexing rule, let rev⁡q\operatorname{rev}_q reverse the lowest qq bits and let popcount⁡\operatorname{popcount} count set bits. With indices starting at zero,

s(j)=rev⁡q(j⊕(j ⁣≫ ⁣1)),Wjk=(−1)popcount⁡(s(j)&k)n.s(j)=\operatorname{rev}_q\big(j\mathbin{\oplus}(j\!\gg\!1)\big), \qquad W_{jk}=\frac{(-1)^{\operatorname{popcount}(s(j)\mathbin{\&}k)}}{\sqrt n}.

The symbols ⊕\oplus, &\& and ≫\gg denote bitwise XOR, AND and right shift. Row jj of this matrix has exactly jj sign changes.

Natural order

0157831241111469213510

The labels count sign changes along each row.

Row 0 is constant. Row 1 changes once. Row 15 changes at every step.

+1/√n
−1/√n
The same 16 orthogonal rows, reordered by their number of sign changes. Adjacent sequencies remain orthogonal; adjacency here describes sign-change counts, not similarity between vectors.

With this ordering, WW is symmetric as well as orthonormal,

WT=W,WTW=I,W−1=W.W^T=W, \qquad W^TW=I, \qquad W^{-1}=W.

The same operation therefore maps a vector into the Walsh basis and back. An arbitrary permutation of an orthogonal matrix's rows would remain orthogonal, but would not necessarily remain symmetric. The ordering is part of the operator, so a port should not substitute a natural-order transform without accounting for the permutation.

Sequency orders the patterns by sign changes. It does not make neighbouring rows close in Euclidean distance. Distinct normalised rows remain orthogonal, at distance 2\sqrt2. Nor does channel index represent a spatial direction in the image. Locality along the sequency axis is the bias we impose on the channel mixer.

A band in the transformed coordinates

Let u=Wxu=Wx. For an odd band width k=2r+1k=2r+1, define

(Bu)j=∑t=−rrwj,t+r uj+t,ui=0 when i∉[0,n−1].(Bu)_j=\sum_{t=-r}^{r}w_{j,t+r}\,u_{j+t}, \qquad u_i=0\text{ when }i\notin[0,n-1].

The weight array has shape n×kn \times k. Its first index is the output channel and its second index is the tap. Every output has its own weights. Despite the name HConvGLU, this is not a translation-invariant convolution with a single kernel shared across the sequency axis. The edges are zero-padded, not circular.

Mirage uses n=512n=512, k=65k=65 and r=32r=32. Each output can mix a neighbourhood of 65 transformed coefficients, including itself. The matrix is banded in these coordinates, while the equivalent linear map in the original channels is WBWWBW.

A band can mix all channels

B · Walsh coordinates

WBW · original channels

This example uses 32 channels and a width-7 band. Its 224 stored slots contain 212 active coefficients. The chosen band becomes dense after the basis change. Other choices, including the identity, need not do so. Mirage uses width 65 on 512 channels.

A sparse band can produce a dense channel map because the basis vectors each span all input channels. This is a possibility, not a guarantee for every choice of weights. For example, B=IB=I gives WBW=IWBW=I. The change of basis also preserves rank,

rank⁡(WBW)=rank⁡(B).\operatorname{rank}(WBW)=\operatorname{rank}(B).

A full-rank band therefore gives a full-rank channel map. Full rank does not mean unrestricted expressivity. The linear family {WBW}\{WBW\} has only as many degrees of freedom as the active entries of BB, rather than n2n^2. HConvGLU accepts that restriction and adds an input-dependent gate.

The gated forward pass

The complete mixer has three bands and one bias,

u=Wx,a=Bgu+b,v=Bvu,u=Wx, \qquad a=B_gu+b, \qquad v=B_vu, h=ϕ(a)⊙v,y=WBoh.h=\phi(a)\odot v, \qquad y=WB_oh.

Here ⊙\odot is elementwise multiplication and ϕ\phi is GELU. This multiplicative construction follows gated linear units and the GELU-gated variant described by Shazeer. HConvGLU supplies the fixed transforms and banded projections around that gating operation.

The forward implementation uses the usual tanh approximation to GELU,

ϕ(z)≈z2[1+tanh⁡(2π(z+0.044715z3))].\phi(z)\approx\frac{z}{2}\left[1+\tanh\left(\sqrt{\frac{2}{\pi}}\left(z+0.044715z^3\right)\right)\right].

GELU is not a sigmoid gate. Its output can be negative or greater than one. It modulates the value branch rather than assigning it a probability.

The HConvGLU forward pass

WB_g u + bGELUB_v u⊙B_oWxy
The first transform gives u = Wx. Gate and value bands form a pointwise product, followed by the output band and the inverse transform. This is the mixer output y; normalisation and residual additions belong to the surrounding encoder block.

The gate is also why HConvGLU cannot be reduced to one fixed matrix. At a particular input, its Jacobian is

∂y∂x=WBo[diag⁡(v⊙ϕ′(a))Bg+diag⁡(ϕ(a))Bv]W.\frac{\partial y}{\partial x} =W B_o\left[ \operatorname{diag}\big(v\odot\phi'(a)\big)B_g +\operatorname{diag}\big(\phi(a)\big)B_v \right]W.

The two diagonal factors depend on xx. They alter the effective channel mixing for each input while the three bands constrain the learned projections. This gives a more precise picture of the tradeoff than saying the transform makes a small layer equivalent to an arbitrary dense one.

For a width dd that is not a power of two, let PP pad to n=2⌈log⁡2d⌉n=2^{\lceil\log_2d\rceil} and CC keep the first dd output coordinates. The general expression is

y=CWBo[ϕ(BgWPx+b)⊙BvWPx].y=CWB_o\left[\phi(B_gWPx+b)\odot B_vWPx\right].

Mirage uses d=n=512d=n=512, so padding and cropping are unnecessary.

Computing the transform

A dense matrix multiplication is unnecessary for WW. The following NumPy implementation uses the same sequency butterfly as Mirage. It accepts any leading batch dimensions and transforms the last axis.

import numpy as np


def walsh_sequency(x):
    x = np.array(x, copy=True)
    n = x.shape[-1]
    if n < 1 or n & (n - 1):
        raise ValueError("the last dimension must be a power of two")
    width = 1
    while width < n:
        blocks = x.reshape(*x.shape[:-1], n // (2 * width), 2, width)
        left = blocks[..., 0, :]
        signs = np.where(np.arange(width) % 2 == 0, 1, -1)
        right = blocks[..., 1, :] * signs
        x = np.stack((left + right, left - right), axis=-1).reshape(x.shape)
        width *= 2
    return x / np.sqrt(n)

Each stage interleaves sums and differences, with alternating signs on the right branch. There are log⁡2n\log_2n stages and nn additions or subtractions per stage. A normalised transform also needs the final scale, unless it is folded into adjacent weights.

A useful port check is to compare the butterfly with the explicit matrix above, then check W(Wx)=xW(Wx)=x within the chosen floating-point tolerance. Testing only the inverse property would miss a different self-inverse ordering. The band operation also needs an edge test so an out-of-range tap cannot wrap onto the opposite end.

Counting storage and arithmetic

The archive stores all nknk taps of each band. At the boundaries, some of those taps multiply zero and do not affect the result. For r<nr<n, the number of active coefficients in one band is

a(n,k)=nk−r(r+1).a(n,k)=nk-r(r+1).

The two edges omit 1+2+⋯+r1+2+\cdots+r coefficients each. At n=512n=512 and k=65k=65, there are 33,280 stored tap slots and 32,224 active coefficients per band.

ComponentStored parametersMultiply-accumulates per vector
Gate band33,28032,224
Value band33,28032,224
Output band33,28032,224
Gate bias5120
Total100,35296,672

The bias requires 512 additions. Two transforms require another 2nlog⁡2n=9,2162n\log_2n=9,216 additions or subtractions. GELU, the elementwise product and any transform scaling are additional work. Normalisation and the surrounding residual block are outside this operator count. A kernel that multiplies every stored tap, including the padded edge taps, would instead perform 99,840 band multiply-accumulates.

Operator counts at 512 channels

Stored parameters per mixer

Dense 4d FFN
2,097,152
HConvGLU
100,352

Matrix multiply-accumulates per vector

Dense 4d FFN
2,097,152
HConvGLU
96,672

Stored parameters across 28 mixers

Dense 4d FFN
58,720,256
HConvGLU
2,809,856
Linear scales within each panel. The dense baseline has hidden width 2048 and excludes biases. HConvGLU includes its gate bias in storage. Arithmetic bars count matrix multiply-accumulates only; HConvGLU also needs 9,216 butterfly additions, the gate, bias addition and elementwise product.

Against the specified dense baseline, HConvGLU stores about 20.9 times fewer parameters and uses about 21.7 times fewer band multiply-accumulates. Across 28 mixers the stored parameter counts are 2,809,856 versus 58,720,256. Neither ratio is an end-to-end latency ratio or an accuracy comparison. Memory access, transforms, activation functions and hardware utilisation determine the actual runtime.

Where it sits in Mirage

Five stride-two stem stages reduce the 512-pixel canvas to a 16×1616 \times 16 grid. The encoder then has 16 convolutional body blocks and 12 attention blocks. Each contains one HConvGLU at width 512.

In a body block, a gated depthwise convolution first mixes neighbouring spatial cells. A normalised feature vector enters HConvGLU, and global response normalisation acts on its output before the residual addition. The attention blocks instead follow their attention residual with normalisation, HConvGLU and a residual addition, without that response-normalisation wrapper. Valid-image masks keep padded cells out of the feature computation.

HConvGLU itself works on one feature vector at a time. Attention, spatial convolutions and the surrounding response normalisation supply the interactions between locations. Four register tokens participate in the attention stack and are removed from the image grid before the downstream heads use it. The Mirage architecture diagram and maths show these surrounding operations in full.

The inference implementation

The CPU engine processes small tiles of image cells. It computes the gate and value bands together to reuse the transformed input, evaluates the output band, and applies the second butterfly. It never constructs a dense 512×512512 \times 512 matrix for the band operation.

The HConvGLU bands and gate bias are stored in FP16 in the released archive and expanded to FP32 by the CPU loader. Mirage's 2-bit designation describes its main quantised matrices, not every tensor. A file-size estimate or a port needs to preserve this distinction.

The engine also folds the transform scales into the bands. Let S=n WS=\sqrt n\,W be the unnormalised butterfly. It loads

B~g=Bg/n,B~v=Bv/n,B~o=Bo/n,\widetilde B_g=B_g/\sqrt n, \qquad \widetilde B_v=B_v/\sqrt n, \qquad \widetilde B_o=B_o/\sqrt n,

and evaluates

y=SB~o[ϕ(B~gSx+b)⊙B~vSx].y=S\widetilde B_o\left[\phi(\widetilde B_gSx+b)\odot\widetilde B_vSx\right].

The bias is not rescaled. This equals the normalised expression above. A port using normalised butterflies should use the original bands, while one using the folded weights should use the unnormalised butterfly. Applying both scales would change the operator.

HConvGLU limits the learned channel map to make the encoder smaller. Structured visuo-semantic perception imposes structure at another level, by reducing the language decoder's responsibilities. Both choices are explicit restrictions on the model. Their practical value is whether the resulting model performs the required perception tasks within the device's storage and compute budget. The Mirage release puts the complete system and its outputs in context.

Cite this post

@misc{mosoyan2026hconvglu,
  title        = {HConvGLU for Channel Mixing in Image Encoders},
  author       = {Mosoyan, Karen and Ndubuaku, Henry},
  year         = {2026},
  month        = oct,
  howpublished = {Cactus Compute blog},
  url          = {https://cactuscompute.com/blog/hconvglu}
}