pub struct Linear { /* private fields */ }Expand description
Minimal inlined Linear layer, lazy.
Computes y = x @ W + b, delegating to
WeightStorage::apply_linear —
the same builder every lazy model in fuel-core uses. That buys F32, BF16,
Q4_0 and LoRA weights for free; a hand-rolled const_f32_like + matmul
here would have been f32-only.
§Weight layout — CHANGED from the eager version
[in_features, out_features], applied as x @ W.
The eager Linear this replaces stored [out_features, in_features] and
computed x @ W.t() (PyTorch’s nn.Linear convention). WeightStorage
uses the transposed layout, and adopting it is what makes the quantized and
LoRA paths reachable. The difference is silent — feeding
[out, in] data to a square layer produces a wrong answer rather than an
error — so it is called out here rather than left to be discovered.
fuel-parallel has no consumers today, so nothing in-tree breaks.
§Why host storage rather than a Tensor weight
Tensor is graph-affine: two tensors combine iff they share a
graph. A Linear holding a Tensor weight could only ever be applied
to inputs on that one graph — useless for a model that builds a fresh graph
per step, which is exactly what Fuel’s lazy decode path does.
WeightStorage materializes onto the input’s graph at forward time,
so one Linear serves any number of graphs.
Implementations§
Source§impl Linear
impl Linear
Sourcepub fn new(
weight: WeightStorage,
in_features: usize,
out_features: usize,
bias: Option<Arc<[f32]>>,
) -> Result<Self>
pub fn new( weight: WeightStorage, in_features: usize, out_features: usize, bias: Option<Arc<[f32]>>, ) -> Result<Self>
Build from a WeightStorage laid out [in_features, out_features].
Returns Err on a size mismatch rather than deferring it: the
apply_linear path this delegates to reports mismatches via
assert_eq!/unwrap(), which would be a panic on a production path.
Validating here keeps Fuel’s never-panic rule intact.
The element-count check applies to F32/BF16 only — a Q4_0 weight’s
elem_count counts packed u32 words, not logical elements, so its
dimensions are checked by WeightStorage itself against the values
stored in the variant.
Sourcepub fn from_f32(
weight: impl Into<Arc<[f32]>>,
in_features: usize,
out_features: usize,
bias: Option<Arc<[f32]>>,
) -> Result<Self>
pub fn from_f32( weight: impl Into<Arc<[f32]>>, in_features: usize, out_features: usize, bias: Option<Arc<[f32]>>, ) -> Result<Self>
Convenience constructor for a plain f32 weight, [in_features, out_features].
Sourcepub fn forward(&self, x: &Tensor) -> Result<Tensor>
pub fn forward(&self, x: &Tensor) -> Result<Tensor>
Forward: y = x @ W + b, built on x’s graph.
The eager original branched on x.is_contiguous() and hand-picked
between a reshape-then-matmul and a broadcast matmul, “to avoid a
broadcasted matmul — it’s much slower”. That is a dispatch-time
optimisation, and in a lazy DAG choosing it here would be the model layer
making a strategy decision that belongs to the optimizer. Lazy matmul
is N-D batched with rank-2 broadcasting, so the whole dance collapses to
one delegated call and the optimizer keeps its job.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Linear
impl RefUnwindSafe for Linear
impl Send for Linear
impl Sync for Linear
impl Unpin for Linear
impl UnsafeUnpin for Linear
impl UnwindSafe for Linear
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more