Mappers and modulation

Mappers turn a trainable latent vector into a flat generated descriptor. Modulation strategies change fixed mapping tensors as a function of that latent vector.

Fixed MLP mapper

MLPMapper uses orthogonally initialized matrices registered as buffers. They move with the module, appear in its state dict, and are never returned by parameters():

import torch

from mapping_networks import AdditiveModulation, MLPMapper

mapper = MLPMapper(
    latent_dim=16,
    output_dim=128,
    hidden_dims=(32,),
    modulation=AdditiveModulation(alpha=0.01),
)
latent = torch.randn(16, requires_grad=True)
descriptor = mapper(latent)
descriptor.sum().backward()

assert descriptor.shape == (128,)
assert list(mapper.parameters()) == []
assert latent.grad is not None

The optional hidden layers use GELU by default. Both hidden and output activations are replaceable with tensor callables. A mapper accepts one one-dimensional latent vector; batched latent mappings should be expressed explicitly by a generator so parameter ownership remains unambiguous.

ResidualMLPMapper adds same-width residual blocks between fixed input and output projections. It is useful when a deeper mapping is desired without discarding the fixed-buffer invariant.

Modulation strategies

All strategies implement BaseModulation.modulate(weights, latent) and avoid in-place mutation.

Additive

AdditiveModulation(alpha) implements the paper’s mapping-weight rule across matrix rows:

W' = W + alpha * z

The latent length must equal the matrix input width. alpha must be non-negative.

Affine

AffineModulation(scale, shift) applies a parameter-free per-column transformation:

W' = W * (1 + scale * tanh(z)) + shift * z

It is identity modulation at z = 0 and introduces no optimizer-visible parameters.

Low rank

LowRankModulation(rank, alpha) decodes the latent vector into factors A and B:

W' = W + (alpha / rank) * A @ B

For a matrix with shape (rows, columns), the required latent length is rank * (rows + columns), available through latent_dim_for(weights). This strategy is intended for generators that allocate a dedicated modulation latent of that size; it is not automatically compatible with an MLP input latent.

Extension contracts

Custom mappers subclass BaseMapper, validate a one-dimensional latent, and return exactly output_dim values. Custom modulation subclasses BaseModulation and returns a new tensor with the same shape as its input weights.

Transformer and explicitly trainable hypernetwork mappers are deferred. Their eventual implementations will use the same BaseMapper contract and will be clearly identified when they introduce trainable parameters beyond latent vectors.