skip to content

Reference

Architecture support

copy markdown

Poros maintains an explicit registry of validated transformer architectures. An architecture is "validated" only after it passes the 0.00e+00 bitwise parity gate: resident-vs-blockwise comparison at atol=0, rtol=0 on both loss and adapter weights.

Parity evidence comes in three tiers, and each family's row below names the tier it actually has (all committed under docs/validation/):

  1. Full campaign — 200-step gates on all four surfaces (loss, weights, gradient norms, optimizer state).
  2. Architecture gate — a bitwise gate over 5–50 optimizer steps (parity is structural, so a short gate is strong evidence, but it is not the campaign bar; the contributor minimum is 48 steps).
  3. Structural smoke — load + streamed-load byte-identity only; no parity claim.

Validated architectures

Familymodel_typeNF4 parityEvidence tierNotes
Qwen2.5qwen20.00e+00full campaign (200-step)7B–72B validated. Also covers DeepSeek-R1-Distill-Qwen (dense qwen2).
Qwen3qwen30.00e+0050-step gate32B gated.
Qwen3.5 / Qwen3.6 (dense)qwen3_5 / qwen3_5_text0.00e+00short gate (5-step)0.8B–27B; gated on Qwen3.5-4B. Below the 48-step contributor bar; scheduled for a 48+ step gate.
Gemma 3gemma3 / gemma3_text0.00e+0050-step gate27B gated (gemma-3-27b-pt).
Gemma 4 (dense)gemma4 / gemma4_text0.00e+0050-step gate31B gated (gemma-4-31B-it). Sliding-window + global hybrid attention handled by per-layer kwargs capture/replay.

Note: Qwen3.6 dense ships model_type: "qwen3_5" in its config.json (the same pattern as Qwen2.5 shipping qwen2), so both generations resolve to the one validated registry entry — there is no separate qwen3_6 type to add.

GPU validation matrix

Each architecture's bitwise parity gate (pytest -m gpu tests/test_gpu_parity.py, torch.equal) has passed on:

Parity gates per the paper's parity matrix (Table 3). A cell is PASS only where that family's bitwise gate was actually recorded on that GPU; means not gated there (not a failure).

GPU (sm)qwen2qwen3qwen3_5gemma3gemma4Benchmarks
RTX 5090 (sm_120)PASS (7B, 32B)PASS (32B)PASS (27B)PASS (31B)
NVIDIA B300 SXM6 (sm_103)PASS (32B, 72B)PASS (32B)official-b300-results.md
NVIDIA A100 80GB PCIe (sm_80)PASS (32B)official-a100-results.md
RTX PRO 6000 (sm_120)PASS (32B)
RTX 4090 (sm_89)PASS (4B)
RTX 3090 (sm_86)PASS (7B)

Torch-stack validation matrix

The same bitwise gate, re-run per PyTorch stack via scripts/parity_torch_versions.sh on A100 80GB PCIe, using the smallest validated checkpoint per family (Qwen2.5-0.5B, Qwen3-0.6B, gemma-3-1b-pt, gemma-4-31B-it). Raw rows: validation/torch_versions_parity.txt.

Stackqwen2qwen3gemma3gemma4
torch 2.11.0+cu128PASSPASSPASSPASS
torch 2.12.0+cu130PASSPASSPASSPASS

The full-size checkpoints are gated separately, not by this sweep: Qwen2.5-32B and Qwen3-32B on the A100/B300/PRO 6000 campaigns, and Gemma3-27B and Gemma4-31B on the RTX 5090 streamed-load sweep (torch 2.11.0+cu128, 50 steps — validation/streamed-load/sweep/).

Execution modes (SDPA backend selection, TF32, bf16 autocast, torch.compile) do not change parity either — measured as a resident-vs-blockwise forward gate plus a reproducibility check, not a full training gate; see parity-modes.md.

Note: google/gemma-4-12B-it reports model_type: gemma4_unified, which is a distinct architecture from the validated gemma4 — the registry correctly refuses it (PorosArchNotValidatedError). It is a roadmap target.

Architecture coverage targets

FamilyStatusNotes
Gemma 4 unified (gemma4_unified, e.g. gemma-4-12B-it)TargetDistinct model_type from the validated dense gemma4; needs its own parity run.
Gemma 4 (MoE)TargetMoE routing under blockwise residency is unvalidated. Dense first; MoE marked roadmap until measured.

Check a model before you train

poros check Qwen/Qwen2.5-7B          # a Hub id (needs huggingface-hub)
poros check ./my-local-model         # a local dir with config.json
poros check ./my-local-model/config.json

poros check resolves the model_type from config.json without loading the model or importing torch and reports its registry status — validated, user_asserted (you registered it; runnable, no Poros parity guarantee), target (roadmap), or not-in-registry — plus what to do next. Exit code is 0 only for validated and 1 for everything else, including user_asserted, so a CI or agent script can never mistake your own assertion for a Poros guarantee.

The architecture registry

The registry is a single source of truth (_ARCHITECTURES in poros/core/arch_registry.py). Read it programmatically:

from poros.core.arch_registry import validated_arch_types, architectures

validated_arch_types()   # {"qwen2", "qwen3", "qwen3_5", "qwen3_5_text",
                         #  "gemma3", "gemma3_text", "gemma4", "gemma4_text"}
architectures()          # full {model_type: ArchSpec} map incl. targets

There are three ways to extend it, in increasing order of permanence. The first two create user_asserted entries — runnable, but carrying no Poros parity guarantee. Only the third enters the validated set:

import poros

# 1. At runtime, in your own code (no fork needed) -> user_asserted:
poros.register_architecture("phi3", notes="0.00e+00 on my own 200-step run")

# 2. Via environment, for an ops/CI override without code -> user_asserted:
#    POROS_USER_ASSERTED_ARCHS="phi3,starcoder2"

# 3. Permanently -> validated: add ONE line to _ARCHITECTURES, with a parity
#    run and a PR. This is the only path into the first-party evidence class.

The notes= field is your own record of what you measured; it does not change the status. poros check ./my-phi3-model (it takes a model id or directory, not a bare model_type) will report user_asserted and exit non-zero after either of the first two.

VALIDATED_BLOCKWISE_ARCH_TYPES remains as a back-compat snapshot of the built-in set. validated_arch_types() returns the same first-party set and is deliberately not affected by runtime registration or the environment, which is what makes it usable as the basis of a parity guarantee. For "what will Poros agree to run", use runnable_arch_types() (validated + user-asserted); for what you yourself added, user_asserted_arch_types().

The blockwise_arch_check config field controls behavior for unvalidated architectures:

  • "error" (default): raise PorosArchNotValidatedError naming the supported list. This is the safe default — it prevents training on an architecture that is neither validated nor user-asserted, where parity is unknown.
  • "warn": log a warning and proceed. Use only for validation runs when testing a new architecture.
  • "off": skip the check entirely. Not recommended for production.

Layer discovery

Poros searches multiple root candidates and layer paths to locate the transformer block container. This handles standard layouts (Qwen2, Qwen3) and nested/vision-language layouts (Gemma3, VLMs).

Root candidates:

"", "model", "base_model", "base_model.model",
"base_model.model.model", "language_model",
"model.language_model", "language_model.model"

Layer paths:

"model.layers", "language_model.layers", "gpt_neox.layers",
"transformer.h", "model.decoder.layers", "encoder.layer"

Plus get_base_model() as a dynamic root candidate for PEFT-wrapped models.

Adding a new architecture

To validate a new architecture family:

  1. Set blockwise_arch_check: "warn" in your config so the registry gate proceeds on the unvalidated family instead of raising.
  2. Run a full parity test (48+ steps) and confirm 0.00e+00 max diff on loss and adapter weights.
  3. If parity holds, add one line to _ARCHITECTURES in poros/core/arch_registry.py:
    "phi3": ArchSpec("phi3", "validated", "0.00e+00", "70B validated 2026-..."),
    (For a private/local validation you don't want to upstream, call poros.register_architecture("phi3", notes="parity 0.00e+00 on my run") at runtime, or set POROS_USER_ASSERTED_ARCHS — no fork required. Both create user_asserted entries: Poros will run the architecture, but the result carries no Poros parity guarantee and prepare() reports probed_not_validated. validated stays a first-party evidence class backed by a committed Caistro artifact, and step 3 above — a registry line plus a PR — is the only way in.)
  4. Add regression tests for the new family.
  5. Submit a PR with the parity test results.

Precision support

Precisions are registered the same way, in poros/core/precision.py (_PRECISIONS). Only nf4 is stable and carries the bitwise-parity guarantee in the validated matrix; bf16/fp16 are experimental with no parity claim; the separate bf16_bitwise runtime is also experimental but IS bitwise-gated (0.5B and 32B on A100), unoptimized and outside the official matrix; int8 is planned. poros schema config and selectable_precisions() reflect the registry, so adding int8 or a LoRA-specific precision in v2 is a one-line change here plus a load path — no scattering of string literals across the trainer, model loader, and config.

Design principle

The block manager owns residency only: load, stream, offload, and gradient routing. It does NOT reconstruct per-layer forward behavior (masks, rotary embeddings, attention dispatch). Those are pulled from the model's own modules per forward pass. This ensures any HF architecture's native behavior is preserved and prevents per-architecture mask/attention bugs.