pub struct SaeAssignment {
pub logits: Array2<f64>,
pub coords: Vec<LatentCoordValues>,
pub mode: AssignmentMode,
pub ungated: Vec<bool>,
pub frozen_logits: Option<Array2<f64>>,
}Expand description
Per-row latent assignment state.
The stored assignment parameter is logits; non-negative assignments are
derived by row-wise softmax, independent IBP-MAP sigmoid active indicators,
or JumpReLU gates. Softmax logits are canonicalized to the reference chart
logits[K - 1] = 0, so the row-local Newton coordinates contain only the
first K - 1 logits (0 coordinates for K = 1). Gate-style modes keep
all K logits as identifiable scalar parameters. coords[k] holds
t_{.,k} for atom k.
Fields§
§logits: Array2<f64>§coords: Vec<LatentCoordValues>§mode: AssignmentMode§ungated: Vec<bool>#1026 — per-atom UNGATED flag (length K, default all-false). An
ungated atom is the dense linear/background tier: its per-row gate is
fixed at a_k ≡ 1 (it contributes γ_k(t_k) to EVERY row, unweighted),
it is excluded from the other atoms’ gate (for the column-separable
IBP / JumpReLU modes the remaining atoms are computed independently, so
they are unaffected), and its logit is NOT a free parameter — its
logit-JVP, sparsity-prior gradient/curvature, and softmax majorizer
contributions are all zero, leaving its logit slot an inert
(ridge-regularized) null direction in the per-row Newton block. This lets
the linear tier carry FULL-RANK reconstructible variance
(fitted = γ_ungated(x) + Σ_{gated} a_k·γ_k(x)) so a linear SAE can reach
the rank-(K·d) PCA ceiling, while the gated curved atoms still add sparse
structure on the residual (#1026 routing-bound finding).
frozen_logits: Option<Array2<f64>>#1033 — AMORTIZED / FROZEN routing. When Some, this (n, K) matrix is a
ρ-INVARIANT predicted routing (the amortized x → logits map distilled
from the frozen dictionary): the gates are computed from THESE logits
instead of the free self.logits, and the logits are NOT optimized by the
inner Newton (their gradient/curvature/prior contributions are zeroed,
exactly as for Self::ungated). This is the generalization of an ungated
atom from “pin the gate at 1” to “pin the gate at the predicted value”: it
makes the per-row routing a fixed function of x + the frozen dictionary,
so the outer ρ-search reuses ONE routing instead of re-solving per-row
gates every outer eval — the n-independent-outer-loop lever (#1033). None
is the historical free-logit path (bit-identical).
Implementations§
Source§impl SaeAssignment
impl SaeAssignment
pub fn new( logits: Array2<f64>, coords: Vec<LatentCoordValues>, temperature: f64, ) -> Result<Self, String>
pub fn with_mode( logits: Array2<f64>, coords: Vec<LatentCoordValues>, mode: AssignmentMode, ) -> Result<Self, String>
Sourcepub fn with_frozen_routing(
self,
predicted: Option<Array2<f64>>,
) -> Result<Self, String>
pub fn with_frozen_routing( self, predicted: Option<Array2<f64>>, ) -> Result<Self, String>
#1033 — install a ρ-INVARIANT FROZEN routing (the amortized predicted
logits; see SaeAssignment::frozen_logits). predicted must be
(n, K). With routing frozen, the gates are computed from predicted and
the logits are excluded from the inner Newton (their gradient/curvature are
inert, like an ungated atom’s). Passing None restores the free-logit
path.
Sourcepub fn routing_is_frozen(&self) -> bool
pub fn routing_is_frozen(&self) -> bool
Whether the per-row routing is FROZEN (amortized) rather than free-logit.
Sourcepub fn freeze_routing_from_current_logits(self) -> Result<Self, String>
pub fn freeze_routing_from_current_logits(self) -> Result<Self, String>
#1033 — install the simplest faithful AMORTIZED routing predictor: a
fixed-form DISTILL of the current dictionary’s routing, namely the current
(converged) logits SNAPSHOTTED as the ρ-invariant frozen routing. This is
the x → logits map “evaluated once at the frozen dictionary” — the
routing the dictionary already expresses — held fixed so the outer ρ-search
reuses it instead of re-optimizing the gates at every ρ. (A richer
predictor that recomputes logits from x via the encode-atlas chart
geometry is a later refinement; snapshotting the converged routing is the
exact fixed-point it would target at the frozen dictionary.) Rejected for
Softmax for the same simplex-coupling reason as Self::with_frozen_routing.
Sourcepub fn freeze_routing_in_place(&mut self) -> Result<(), String>
pub fn freeze_routing_in_place(&mut self) -> Result<(), String>
#1033 — in-place variant of Self::freeze_routing_from_current_logits
for callers holding &mut SaeAssignment (e.g. inside a SaeManifoldTerm),
where moving the assignment out is awkward. Same contract: snapshot the
current logits as the ρ-invariant frozen routing; reject Softmax.
Sourcepub fn set_frozen_routing_in_place(
&mut self,
predicted: Array2<f64>,
) -> Result<(), String>
pub fn set_frozen_routing_in_place( &mut self, predicted: Array2<f64>, ) -> Result<(), String>
#1033 — install an explicit predicted routing in place (the
RoutingPredictor::ChartGeometry output), &mut self variant of
Self::with_frozen_routing. predicted must be (n, K); rejects Softmax
(separable-mode contract) and non-finite predictions.
Sourcepub fn thaw_routing(&mut self)
pub fn thaw_routing(&mut self)
#1033 — lift the frozen routing, restoring the free-logit search path.
Sourcepub fn with_ungated(self, flags: Vec<bool>) -> Result<Self, String>
pub fn with_ungated(self, flags: Vec<bool>) -> Result<Self, String>
#1026 — designate which atoms are UNGATED (the dense linear/background
tier; see SaeAssignment::ungated). flags must have length K.
Ungating is defined for the COLUMN-SEPARABLE gate modes (IBP-MAP and
JumpReLU): each atom’s gate is an independent per-atom function of its own
logit, so pinning one atom to a_k ≡ 1 leaves every other atom’s gate
exactly as computed. Softmax is a coupled simplex (Σ_k a_k = 1 over all
K), so a unit gate for one atom is only well defined relative to a
gated-subset renormalization that must also be reflected in the logit-JVP
and the entropy majorizer; this constructor’s contract is restricted to
the separable modes, and an ungated atom under Softmax is REJECTED here so
the inner solve never runs on a value/gradient-mismatched gate. Callers
wanting a dense background tier under Softmax route it as an IBP-MAP or
JumpReLU atom.
Sourcepub fn has_ungated(&self) -> bool
pub fn has_ungated(&self) -> bool
Whether any atom is ungated (the #1026 background tier is engaged).
pub fn n_obs(&self) -> usize
pub fn k_atoms(&self) -> usize
pub fn total_coord_dim(&self) -> usize
pub fn assignment_coord_dim(&self) -> usize
pub fn row_block_dim(&self) -> usize
pub fn coord_offsets(&self) -> Vec<usize>
pub fn assignments(&self) -> Array2<f64>
pub fn assignments_row(&self, row: usize) -> Array1<f64>
pub fn try_assignments_row(&self, row: usize) -> Result<Array1<f64>, String>
Sourcepub fn flatten_ext_coords(&self) -> Array1<f64>
pub fn flatten_ext_coords(&self) -> Array1<f64>
Flatten extension coordinates in row-major SAE layout:
(assignment chart_i, t_i0[0..d_0], ..., t_iK[0..d_K]) for every row.
Softmax contributes the first K - 1 reference logits and omits the
fixed reference logit; gate-style assignment modes contribute all K
logits.
pub fn from_blocks_with_mode( logits: Array2<f64>, coord_blocks: Vec<Array2<f64>>, mode: AssignmentMode, ) -> Result<Self, String>
pub fn from_blocks_with_mode_and_manifolds( logits: Array2<f64>, coord_blocks: Vec<Array2<f64>>, manifolds: Vec<LatentManifold>, mode: AssignmentMode, ) -> Result<Self, String>
Trait Implementations§
Source§impl Clone for SaeAssignment
impl Clone for SaeAssignment
Source§fn clone(&self) -> SaeAssignment
fn clone(&self) -> SaeAssignment
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for SaeAssignment
impl RefUnwindSafe for SaeAssignment
impl Send for SaeAssignment
impl Sync for SaeAssignment
impl Unpin for SaeAssignment
impl UnsafeUnpin for SaeAssignment
impl UnwindSafe for SaeAssignment
Blanket Implementations§
impl<T> Allocation for T
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,
Source§impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T, U> Imply<T> for U
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 moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.