ClikaRT::nn::Linear
class
Header: ClikaRT/nn/linear.h
Inherits: ClikaRT::nn::Module
Inherited by: ClikaRT::nn::QLinear, ClikaRT::nn::QLinearWoQ
Affine transformation of the trailing dimension: .
The module owns its weights: construct with make(...), or declare shapes and bind a checkpoint via load_state_dict. After the first forward (or initialize()) the weight lives ONLY in the backend's packed form (one resident copy); to(dtype) restores, casts, and repacks on the next forward.
Shape: input [*, in_features] (any leading batch dims); weight [out_features, in_features]; bias [out_features], optional; output [*, out_features].
auto lin = ClikaRT::nn::Linear::make(W, b);
auto y = lin->forward(x); // [batch, in] -> [batch, out]
Static member functions
make(Tensor, OptionalTensor, LinearOptions)
static std::shared_ptr<Linear> make(
Tensor weight,
OptionalTensor bias = {},
LinearOptions options = {}
)
Construct FROM tensors (a static factory, so a bad config is a clean error), THE posture dispatch: geometry, dtype and device are all read off weight, the slots are declared AND bound in this one call (an optional bias [out] binds beside it; a bias slot exists exactly when bias is passed; options.bias is not consulted here), and the returned object's dynamic type follows the file doc's dispatch rule: options.output_quant ⇒ QLinear; weight.is_quantized() ⇒ QLinearWoQ (its logical shape read in the loader's [in, out] order); else Linear (the HF [out, in] dense layout). options.device overrides the placement (the payloads move there); options.dtype pins a dtype (dense payloads cast to it; a quantized payload's codes are never cast). The first forward packs. Raises ClikaRT::Error on an undefined / non-2-D weight.
Declared in ClikaRT/nn/linear.h, line 176
make(int64_t, int64_t, LinearOptions)
static std::shared_ptr<Linear> make(
std::int64_t in_features,
std::int64_t out_features,
LinearOptions options = {}
)
Construct FROM tensors (a static factory, so a bad config is a clean error), THE posture dispatch: geometry, dtype and device are all read off weight, the slots are declared AND bound in this one call (an optional bias [out] binds beside it; a bias slot exists exactly when bias is passed; options.bias is not consulted here), and the returned object's dynamic type follows the file doc's dispatch rule: options.output_quant ⇒ QLinear; weight.is_quantized() ⇒ QLinearWoQ (its logical shape read in the loader's [in, out] order); else Linear (the HF [out, in] dense layout). options.device overrides the placement (the payloads move there); options.dtype pins a dtype (dense payloads cast to it; a quantized payload's codes are never cast). The first forward packs. Raises ClikaRT::Error on an undefined / non-2-D weight.
Declared in ClikaRT/nn/linear.h, line 196
fuse()
static std::shared_ptr<Linear> fuse(
ClikaRT::Span<const std::shared_ptr<Linear>> parts,
LinearOptions options = {}
)
Fuse sibling projections (a QKV group, a gate‖up pair) into ONE module: every part bound and un-packed, uniform bias arity, equal in_features. Dense siblings fuse into one packed GEMM over the concatenated weight; quantized BLOCK-stream siblings under one scheme stack their payloads (out-channels concatenate; the scales ride inside the blocks). A MIXED-scheme quantized group fuses its maximal ADJACENT same-scheme (and same-global-scale) subsets and serves the rest per part; adjacency preserves the output column order, so parts are never regrouped across a mismatched neighbour. A group the mechanics cannot fuse at all returns the SAME static type serving per-part forwards + a concat, never an error, never silent (the outcome is logged). options.activation applies on EVERY path: the fully-fused path serves it inside the GEMM program (where the backend has one); a declined or partially-grouped group composes it on the concat result inside its own forward. So forward's result (shape and values) never depends on whether the fuse engaged; a group the mechanics cannot fuse serves per-part through the same handle, and only the dispatch count differs. Weight slots of the parts a fuse consumed release to declared fakes (the fused module owns that subset's single resident copy); standalone parts keep their own pack lifecycle; every part stays registered for state-dict naming. options configures the fused module. A GATED activation (SwiGlu/GeGlu/ReGlu): out_features keeps naming the GEMM width (the parts' concatenated out-features, the checkpoint-facing weight shape) while forward emits the post-gate HALF width on every path, exactly the gate/up serving convention. A gated group must be BIAS-FREE (the gate/up projection carries no bias; biased parts raise a typed refusal naming that constraint) and its total out-features EVEN (the halves must split). output_quant and non-Packed residency are refused here. Raises ClikaRT::Error on an empty group, an unbound or already-packed part, or refused options.
Declared in ClikaRT/nn/linear.h, line 278
Member functions
~Linear()
~Linear() override
Releases the module's packed weights.
Declared in ClikaRT/nn/linear.h, line 203
kind()
virtual LinearKind kind() const noexcept
The module's SERVING POSTURE (see LinearKind; posture, not class identity). A plain Linear reports Dense until a quantized payload binds, then WeightQuantized; QLinearWoQ / QLinear report their pinned posture constantly.
Declared in ClikaRT/nn/linear.h, line 209
residency()
WeightResidency residency() const noexcept
The weight residency as currently RESOLVED: the constructed value until the pack point, then Auto's decision (Packed or Borrowed).
Declared in ClikaRT/nn/linear.h, line 213
packed_parameters()
virtual std::vector<std::pair<std::string, Tensor>> packed_parameters() const override
The dense weight, reconstructed on demand from the packed serving form once the layer has packed ({{"weight", tensor}}; value-exact, a fresh owning copy; the pack stays the single resident copy). Empty while un-packed (the registry slot still enumerates), under Borrowed residency (same), and for the quantized / fused-group postures (their reconstruction is not served yet; checkpoint those before the first forward). See Module::packed_parameters().
Declared in ClikaRT/nn/linear.h, line 222
share_weight()
void share_weight(Tensor weight)
Adopt weight as this module's weight WITHOUT a copy: two handles, one storage (the tie: e.g. an lm head serving off the embedding table). Total and idempotent: a module whose weight already landed (a checkpoint bind, a prior share, a live pack) ignores the call and returns ok. The residency option then governs the pack point as documented on LinearOptions; under Borrowed (or an Auto that declines) the shared storage stays the ONE resident copy and forward serves off it; a shared parameter enumerates once, under its first registered name. Raises ClikaRT::Error on a cross-device share or a pinned dtype the table does not already carry (either would force the copy the tie exists to avoid). NOTE: a placement move (to(device)) re-homes each registry slot individually, so a tied pair moved across devices unties into two copies; re-tie with share_weight after a cross-device move. A dtype move on a shared weight is refused for the same reason (cast the SOURCE instead).
Declared in ClikaRT/nn/linear.h, line 240
set_weights()
void set_weights(Tensor weight, OptionalTensor bias = {})
Bind the declared slots positionally: weight, dense in EITHER orientation (the HF [out, in] or the transposed [in, out], validated against the declared feature counts; the ambiguous square case is read as [out, in]), or a QUANTIZED payload (its scheme rides the tensor; the serving posture follows it), and, when the module was declared with one, bias ([out]). The declared placement wins (the tensors move to it); an unpinned slot adopts the payload's dtype, a pinned one casts a DENSE payload to the pin (quantized codes never cast). Re-binding after a pack drops the pack; the next forward re-packs from the new weights. Raises ClikaRT::Error on a geometry mismatch or a bias without a declared bias slot.
Declared in ClikaRT/nn/linear.h, line 295
initialize_impl()
virtual Result<void> initialize_impl() override
Optional warm-up: run the first-forward pack NOW (idempotent, thread-safe) so a serving process pays it at load time instead of on the first token. Every declared slot must hold a real (loaded) tensor; a still-fake slot is a clean error. After the pack the weight slot is released (the packed form is the single resident copy, no raw+packed doubling), so named_parameters() enumerates only the unpacked slots (the bias); a re-bind re-inserts and the next pack re-releases.
Declared in ClikaRT/nn/linear.h, line 306
to_impl(StreamOrDevice)
virtual Result<void> to_impl(StreamOrDevice where) override
Move to a placement (a Device, or a Stream, the lane this Linear computes on and lands its outputs on) or cast to a dtype. Before the pack this moves the registry slots (a still-fake slot re-declares its metadata there). Once packed, a placement move REBUILDS the pack on the target; a dtype cast restores the raw weight from the pack, casts, and re-packs on the next forward, never a stale packed form computing on the old placement. A dtype cast PINS that dtype for every later re-bind, whether it runs before or after the first bind (an explicit cast is a dtype choice; a subsequent payload must not silently revert it). On a quantized posture (kind() != Dense) a dtype cast is Unsupported: the weight stays quantized at rest; dequantize explicitly if a dense copy is wanted.
Declared in ClikaRT/nn/linear.h, line 320
to_impl(DataType)
Declared in ClikaRT/nn/linear.h, line 321
forward()
Apply the bound weight: x [*, in] -> [*, out] (then bias + activation if configured). The first call packs the weight into the backend kernel layout (once, thread-safe); every later call reuses the pack: the dense matmul or the weight-only-quantized matmul, per the bound payload.
Throws
ClikaRT::Error: on a shape/dtype mismatch or a still-fake slot.
Declared in ClikaRT/nn/linear.h, line 330
Protected member functions
apply_weight_residency()
virtual void apply_weight_residency(
WeightResidency residency,
std::uint64_t& stamped,
std::uint64_t& class_pinned
) override
The load-scope residency stamp (nn::LoadOptions::weight_residency). Adopts the override as this module's requested residency, exactly as if it had been constructed with it, unless the class pins its residency (the quantized postures serve Packed alone), in which case the pin holds and the module counts in class_pinned. Applies to packs that have not yet run; residency() reports the outcome.
Declared in ClikaRT/nn/linear.h, line 339
Linear()
CLIKART_LOCAL Linear()
Declared in ClikaRT/nn/linear.h, line 342
Linear(void)
explicit CLIKART_LOCAL Linear(void* impl) noexcept
Subclass construction: adopts a derived detail::LinearImpl as the one pImpl. The parameter is void*, NOT the typed impl pointer, on purpose: a protected member of an exported class IS exported on PE (dllexport exports every member), and an exported signature must not name a detail:: type; do not "fix" this to the typed pointer.
Declared in ClikaRT/nn/linear.h, line 348
Protected data members
impl_
std::unique_ptr<detail::LinearImpl> impl_
Declared in ClikaRT/nn/linear.h, line 349