# Training

> UForm for C

Training is GPU-only.
`uform_mesh_bootstrap` joins this process to a run once, through files in a directory every host sees, and after it processes talk only over NVLink and InfiniBand.
The mesh is the process-wide half of the transport, which every trainer borrows; `uform_mesh_describe` reports the topology every process agreed on, its multimem tier, and the `gpu_class` every GPU of the run dispatches as, the executor's pin or the devices' own.
Every process bootstraps with the same count, multicast extent, directory, job and GPU class, or the call reports `uform_status_config_mismatch_k`.

`uform_trainer_init_vram` makes a trainer on a VRAM executor, owning one replica of the tower per device, its gradients, the Adafactor moments, the reconstruction heads and the learnable temperatures.
One `uform_trainer_step` is one optimizer step over this process's share of the global batch: forward, the losses, backward, the world gradient reduction, and the update.
Items arrive tagged with a `uform_modality_t`, grouped into bags that travel together, with attract, repel and ignore relations indexing the step's own items.
Beside the masked and pooled contrastive losses, a trainer relaxes two optional terms, each read when its weight is positive:

- `late_interaction` trains the token head with hard MaxSim scores between the items each step's `sides` names a query, a document or both.
- `choices` trains the byte decoder on the legal bytes each step's `choices` tape gives, the tape `uform_text_choose` reads, and their target probabilities in the `choice_targets` tape, shaped as the probabilities it lands, within the batch's `max_choices_per_item`.

`uform_pair_loss_form_t` picks how a contrastive term turns a row of scores into forces: `balanced`, `ranked` or SigLIP's `sigmoid`, where `derived` keeps sigmoid for pooled embeddings and ranked for token vectors.
The step's stats report every loss as its world mean, with `late_interaction_loss` and `choices_loss` beside the contrastive and masked terms, the items `truncated` at a row's width, and the segments `dropped` whole for want of room.
A training call that fails after a GPU recorded a fault, such as a peer missing a multimem barrier or an InfiniBand round past its deadline, carries that fault as its outcome message.
`uform_trainer_save` writes the safetensors checkpoint the model verbs load.
Every path, the mesh's `directory`, a `resume` checkpoint and a save's `path`, arrives as the operating system's own bytes, not necessarily UTF-8, and one holding a NUL byte is refused with `uform_status_invalid_argument_k`; the mesh's `job` is a UTF-8 name.

```c
uform_mesh_t mesh = NULL;
uform_mesh_bootstrap(&(uform_mesh_bootstrap_t){.mesh = &mesh, .executor = executor, .outcome = &outcome,
    .options = {.host_domain_index = node, .host_domain_count = nodes, .directory = {exchange, exchange_length},
                .job = {job, job_length}}});
uform_trainer_t trainer = NULL;
uform_trainer_init_vram(&(uform_trainer_init_vram_t){
    .trainer = &trainer, .executor = executor, .outcome = &outcome, .mesh = mesh, .depth = 12, .hidden = 512,
    .batch = {.max_rows_per_device = 64, .max_segments_per_device = 256, .max_columns_per_row = 1024,
              .max_bags_per_device = 128, .image = {.patch_side = 16}},
    .objective = uform_objective_joint_k, .schedule = {.steps = 100000}, .seed = 42});
```

`#include <uform/train.h>`

## uform_modality_t

What the bytes of one training item hold, in the order the engine packs a batch.

```c
enum uform_modality_t
```

- `uform_modality_text_k` = `0`: UTF-8 text, a byte per token.
- `uform_modality_image_k` = `1`: A JPEG, PNG or WebP file.
- `uform_modality_video_k` = `2`: An MP4 or WebM file.
- `uform_modality_audio_k` = `3`: A WAV, FLAC, MP3 or Opus file.

Spellings: ```
char const * uform_modality_name(uform_modality_t modality);
```

```
uform_modality_t uform_modality_from_name(uform_bytes_view_t name);
```

## uform_image_shape_t

The image slice of a batch envelope; a zero `patch_side` leaves images out of the run.

```c
struct uform_image_shape_t
```

- `patch_side` `size_t`: The square patch side images tile into, in pixels.
- `channels` `size_t`: Channels every pixel carries; zero derives four, RGBA end to end.

## uform_video_shape_t

The video slice of a batch envelope; a zero `patch_side` leaves video out of the run.

```c
struct uform_video_shape_t
```

- `patch_side` `size_t`: The square patch side one tube tiles into, in pixels.
- `tube_frames` `size_t`: Frames one tube folds ahead of its patch; zero derives two.
- `frame_stride` `size_t`: Source frames between sampled ones; zero derives one, every frame.
- `channels` `size_t`: Channels every pixel carries; zero derives three.

## uform_audio_shape_t

The audio slice of a batch envelope; a zero `chunk_samples` leaves audio out of the run.

```c
struct uform_audio_shape_t
```

- `chunk_samples` `size_t`: Raw samples per frame, the fan-in of the learned frontend.
- `sample_rate` `size_t`: The mono rate every recording is resampled to; zero derives 16000.

## uform_batch_shape_t

What one device's step carries at most, whatever the modality mix: capacity fixed at creation, with padding through the data.

```c
struct uform_batch_shape_t
```

Text always takes part; a media modality takes part when its slice is live.

- `max_rows_per_device` `size_t`: Rows one device's step packs.
- `max_segments_per_device` `size_t`: Items one device's step carries across all its rows, which is also its pooling axis.
- `max_columns_per_row` `size_t`: Positions one row spans, the sequence length.
- `max_bags_per_device` `size_t`: Bags one device's step carries; zero keeps every item its own routing unit.
- `max_choices_per_item` `size_t`: Legal bytes one item offers to choose from at most; zero makes no choices.
- `image` `uform_image_shape_t`: The image geometry.
- `video` `uform_video_shape_t`: The video geometry.
- `audio` `uform_audio_shape_t`: The audio geometry.

## uform_objective_t

Which losses a step relaxes, one enumerator per combination the engine trains.

```c
enum uform_objective_t
```

- `uform_objective_masked_k` = `0`: Masked reconstruction alone, as the Base pretrains.
- `uform_objective_contrastive_k` = `1`: The pairwise contrastive loss alone, as the Embedder trains.
- `uform_objective_joint_k` = `2`: Both, joined at the final-norm states.

Spellings: ```
char const * uform_objective_name(uform_objective_t objective);
```

```
uform_objective_t uform_objective_from_name(uform_bytes_view_t name);
```

## uform_masking_t

How the masked objective corrupts a batch and weighs its heads; every zero derives the default named.

```c
struct uform_masking_t
```

- `token_mask_probability` `float`: Fraction of text positions covered by spans; 0.15.
- `token_mean_span` `float`: Mean geometric span length in bytes; 16.
- `replace_fraction` `float`: Of covered positions, the share shown as MASK; 0.8.
- `random_fraction` `float`: Of covered positions, the share replaced by a random byte, the rest kept clean; 0.1.
- `image_mask_ratio` `float`: Fraction of image patches masked; 0.75.
- `video_mask_ratio` `float`: Fraction of video tubes masked, tubed across time; 0.75.
- `audio_mask_ratio` `float`: Fraction of audio frames masked; 0.75.
- `text_weight` `float`: The weight λ of the text head; 1.
- `image_weight` `float`: The weight λ of the image head; 1.
- `video_weight` `float`: The weight λ of the video head; 1.
- `audio_weight` `float`: The weight λ of the audio head; 1.

## uform_negatives_t

Which pairs the contrastive loss pushes apart.

```c
enum uform_negatives_t
```

- `uform_negatives_ambient_k` = `0`: Repel edges, and every pair no edge names, at `ambient_repulsion_weight`.
- `uform_negatives_named_k` = `1`: Repel edges alone.

Spellings: ```
char const * uform_negatives_name(uform_negatives_t negatives);
```

```
uform_negatives_t uform_negatives_from_name(uform_bytes_view_t name);
```

## uform_pair_loss_form_t

How a contrastive term turns a row of scores into forces, mirroring the trainer's pair-loss forms; zero keeps the term's own form: sigmoid for pooled vertices, ranked for token vectors.

```c
enum uform_pair_loss_form_t
```

- `uform_pair_loss_form_derived_k` = `0`: The term's own form.
- `uform_pair_loss_form_balanced_k` = `1`: The sigmoid cross-entropy of every cell, its attract and repel halves normalized apart.
- `uform_pair_loss_form_ranked_k` = `2`: The sigmoid cross-entropy of every positive's score over every negative's, where the bias cancels.
- `uform_pair_loss_form_sigmoid_k` = `3`: SigLIP's sigmoid cross-entropy of every cell, each repel weighed like an attract.

## uform_contrastive_t

How the contrastive loss scores the pooled embeddings; every zero derives the default named.

```c
struct uform_contrastive_t
```

- `matryoshka_dimensions` `size_t const *`: The __optional__ Matryoshka prefix widths, strictly ascending, each a multiple of 8, at most 8 of them, every one scored; `NULL` scores the full width alone.
- `matryoshka_dimensions_count` `size_t`: Entries in `matryoshka_dimensions`.
- `temperature_groups` `size_t`: Learnable (inverse temperature, bias) pairs, one per group a step's items select; one.
- `inverse_temperature` `float`: The starting inverse temperature of every group; 10 under the sigmoid form, SigLIP's, else 1.
- `bias` `float`: The starting logit bias of every group; −10 under the sigmoid form, SigLIP's, else zero.
- `negatives` `uform_negatives_t`: Which pairs repel.
- `ambient_repulsion_weight` `float`: The weight of every pair no edge names, under [`uform_negatives_ambient_k`](#uform_negatives_ambient_k); 1.
- `max_edges_per_device` `size_t`: Edges one device's step names per edge set at most; zero derives it from the segments and the largest bag.
- `pair_loss` `uform_pair_loss_form_t`: The pair loss over each anchor's row; sigmoid.

## uform_late_interaction_t

How the late-interaction loss scores the per-token vectors of query and document segments; every zero derives the default named.

```c
struct uform_late_interaction_t
```

- `weight` `float`: The loss weight; zero trains no token head.
- `width` `size_t`: The token vectors' width, a multiple of 8; 128.
- `inverse_temperature` `float`: The starting inverse temperature of every contrastive group; 1.
- `bias` `float`: The starting logit bias of every contrastive group; zero is zero.
- `max_query_span` `size_t`: Tokens one query segment contributes at most, its first past the prefix; 64.
- `max_query_tokens_per_device` `size_t`: Query token rows one device's step holds at most; `max_query_span` of every segment.
- `max_documents_per_device` `size_t`: Document segments one device's step holds at most; `max_segments_per_device`.
- `max_document_tokens_per_device` `size_t`: Document token rows one device's step holds at most; the batch's tokens per device.
- `pair_loss` `uform_pair_loss_form_t`: The pair loss over each query's row of hard MaxSim scores; ranked.

## uform_late_interaction_side_t

Which side of a late-interaction score an item takes, mirroring the trainer's sides: a query's tokens score a document's, and a symmetric pair plays both.

```c
enum uform_late_interaction_side_t
```

- `uform_late_interaction_side_none_k` = `0`: The item takes no part in late interaction.
- `uform_late_interaction_side_query_k` = `1`: The item's tokens score the documents it attracts and repels.
- `uform_late_interaction_side_document_k` = `2`: The item's tokens are scored by the queries naming it.
- `uform_late_interaction_side_query_and_document_k` = `3`: The item plays both sides.

## uform_schedule_t

The learning-rate schedule: a linear warmup to the peak, then a cosine decay to the floor.

```c
struct uform_schedule_t
```

- `learning_rate` `float`: The peak learning rate; zero derives 5e-4.
- `min_learning_rate` `float`: The floor the cosine settles at; zero derives 1e-5.
- `warmup_steps` `size_t`: Steps from zero to the peak; zero derives a tenth of `steps`, at most 400.
- `steps` `size_t`: Steps the run takes across resumes, which the cosine spans; zero is refused.

## uform_trainer_t

A training run: the tower replicas, gradients, optimizer moments, heads, temperatures and the collectives sized by them, on the executor's devices.

```c
typedef struct uform_trainer_handle_t * uform_trainer_t;
```

## uform_edges_view_t

One edge set over a step's items, as compressed sparse rows.

```c
struct uform_edges_view_t
```

- `offsets` `size_t const *`: The step's `count` + 1 non-decreasing offsets into `ids`, in host memory; `NULL` names no edges.
- `ids` `uint32_t const *`: Item `i` links to items `ids`[offsets[i]] up to `ids`[offsets[i+1]], indices into the step's items, ascending within each run.
- `weights` `float const *`: The __optional__ weight of each edge, paired with `ids`; `NULL` weighs every edge one.

## uform_relations_view_t

The contrastive targets of one step, resolved per pair in this order: a pair `ignore` names is skipped, one `attract` names pulls together, one `repel` names pushes apart, and every other pair follows the run's `negatives`.

```c
struct uform_relations_view_t
```

- `attract` `uform_edges_view_t`: Pairs pulled together.
- `repel` `uform_edges_view_t`: Pairs pushed apart.
- `ignore` `uform_edges_view_t`: Pairs skipped, like known false negatives; `weights` is not read.

## uform_trainer_step_stats_t

What a step reports, every loss its world mean.

```c
struct uform_trainer_step_stats_t
```

- `step` `size_t`: The optimizer step the call applied, counted from one across resumes.
- `learning_rate` `float`: The learning rate it applied.
- `loss` `float`: The total loss the step minimized.
- `contrastive_loss` `float`: The contrastive term; zero when the objective is masked alone.
- `masked_text_loss` `float`: The text reconstruction term, before its weight; zero when absent.
- `masked_image_loss` `float`: The image reconstruction term, before its weight; zero when absent.
- `masked_video_loss` `float`: The video reconstruction term, before its weight; zero when absent.
- `masked_audio_loss` `float`: The audio reconstruction term, before its weight; zero when absent.
- `late_interaction_loss` `float`: The late-interaction term, weighted; zero when absent.
- `choices_loss` `float`: The choices term over the items that made a choice, before its weight; zero when absent.
- `truncated` `size_t`: Items cut at a row's width.
- `dropped` `size_t`: Segments dropped whole because the batch had no room for them, since the last step that read its stats.

## uform_trainer_init_vram

Builds one replica per device, the NVLink domain its consumers reserve over the mesh, and starts or resumes the run; every process of the run calls it with the same geometry, objective and seed.

```c
void uform_trainer_init_vram(uform_trainer_init_vram_t *arguments);
```

- `trainer`: Output for the new trainer, written once the call completes.
- `executor`: A VRAM executor, every device of which holds one replica.
- `outcome`: Output for the outcome, final once its status leaves [`uform_status_deferred_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_deferred_k).
- `completion`: Whether the call blocks or returns once queued; zero blocks.
- `resume`: The __optional__ path of the checkpoint to continue from, at its saved step; empty starts from `seed`.
- `depth`: Transformer blocks stacked.
- `hidden`: Embedding width, a multiple of 128.
- `batch`: What one device's step carries at most.
- `objective`: Which losses a step relaxes.
- `masking`: The masked objective's corruption and weights, read unless the objective is contrastive alone.
- `contrastive`: The contrastive loss's scoring, read unless the objective is masked alone.
- `late_interaction`: The late-interaction loss beside the pooled one, read when its weight is positive.
- `choices`: The calibrated choices beside every other term, read when its weight is positive, which needs the batch's `max_choices_per_item`.
- `schedule`: The learning-rate schedule.
- `mesh`: The __optional__ mesh, borrowed until the trainer is freed and made over this same executor; `NULL` runs this process alone.
- `seed`: The base of every keyed draw: initialization, corruption and the shuffle.
- `progress`: The __optional__ handler, called with tensors placed on a resume.

## uform_trainer_step

Applies one optimizer step over this process's items and every peer's; every process of the run calls it once per step.

```c
void uform_trainer_step(uform_trainer_step_t *arguments);
```

- `trainer`: The run to advance.
- `executor`: The executor the trainer was created on.
- `outcome`: Output for the outcome, final once its status leaves [`uform_status_deferred_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_deferred_k).
- `completion`: Whether the call blocks or returns once queued; zero blocks.
- `count`: Items in this process's share of the step; more than its devices' envelopes hold reports [`uform_status_shape_mismatch_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_shape_mismatch_k).
- `modalities`: The modality of each item, `count` of them in host memory; a modality the run left out reports [`uform_status_unsupported_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_unsupported_k).
- `contents`: The items' bytes, one entry each, of `uform_scalar_u8_k:` UTF-8 text or an encoded file, as `modalities` tags it.
- `bags`: The __optional__ bag of each item, `count` of them in host memory; items sharing a bag land on one device.
- `temperature_groups`: The __optional__ temperature group of each item, `count` of them in host memory, each below the run's `temperature_groups`; `NULL` puts every item in group zero.
- `sides`: The __optional__ late-interaction side of each item, `count` of them in host memory; `NULL` makes every item [`uform_late_interaction_side_none_k`](#uform_late_interaction_side_none_k).
- `choices`: The __optional__ legal choices of each item, one byte each, [`uform_scalar_u8_k`](https://www.unum.cloud/docs/uform/c/encoding.md#uform_scalar_u8_k), as `[uform_text_choose_t](https://www.unum.cloud/docs/uform/c/choices.md#uform_text_choose)` reads them; `NULL` offsets make no choices, and an item offering none makes none.
- `choice_targets`: The target probability of each choice, [`uform_scalar_f32_k`](https://www.unum.cloud/docs/uform/c/encoding.md#uform_scalar_f32_k), in `choices` order, as `[uform_text_choose_t](https://www.unum.cloud/docs/uform/c/choices.md#uform_text_choose)` lands its probabilities: each item's entry as long as its entry of `choices`, summing to one.
- `relations`: The contrastive targets, read unless the objective is masked alone; a contrastive step naming no attract edge reports [`uform_status_invalid_argument_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_invalid_argument_k).
- `embeddings`: The __optional__ output for the pooled embedding of every item, as the contrastive loss scored it before the update, inside `arena`; `NULL` skips the copy.
- `arena`: Memory for `embeddings`, rewound by the call.
- `outputs_scalar`: The scalar type `embeddings` lands as.
- `stats`: The __optional__ report; `NULL` skips it, and with it the readback of the losses.

## uform_trainer_save

Writes the weights and the step, which [`uform_model_load_vram`](https://www.unum.cloud/docs/uform/c/models.md#uform_model_load_vram) and its siblings serve and [`uform_trainer_init_vram`](#uform_trainer_init_vram) resumes; only the process of index zero writes, the rest return at once.

```c
void uform_trainer_save(uform_trainer_save_t *arguments);
```

- `trainer`: The run to save.
- `executor`: The executor the trainer was created on.
- `outcome`: Output for the outcome, final once its status leaves [`uform_status_deferred_k`](https://www.unum.cloud/docs/uform/c/errors.md#uform_status_deferred_k).
- `completion`: Whether the call blocks or returns once queued; zero blocks.
- `path`: Destination path of the safetensors checkpoint, written whole and renamed into place.
- `progress`: The __optional__ handler, called with tensors written.

## uform_trainer_free

Frees a trainer and leaves the mesh; `NULL` is safe, and it cannot fail, so takes no outcome.

```c
void uform_trainer_free(uform_trainer_t trainer);
```

Every call using it must be final.

