omnibias.keras.growable
Keras 3 operator layers and activation paths implemented with keras.ops for TensorFlow, JAX, or Torch.
import omnibias.keras.growableModule contract & mathematical conventions
GrowableOperatorMultiBiasUnit (Keras backend).
An OMBU whose bias arity K can grow during training without changing
its output shape. Mirrors
omnibias.torch.growable.GrowableOperatorMultiBiasUnit.
Two growth strategies preserve the unit's output at the moment of growth:
"pair"(default; any base activation). Adds two biases at an existing location with signs(+eta, -eta)(sum zero, output unchanged)."saturate"(only for activations that vanish asz -> -inf: sigmoid, softplus, gaussian, exp, relu, huber). Adds one bias at-bigso the new term contributes ~0.
Biases / signs are pre-allocated at K_max; the forward only sums over
the first active_K columns.
Shared formulas do not imply universal floating-point identity. Verify dtype, shape, derivative-order support, and the stated numerical assumptions for the path you use.
GrowStrategy
omnibias.keras.GrowStrategySource ↗GrowStrategy = Literal['pair', 'saturate']This exported constant is defined by the expression above. Its exact value and annotation come from the pinned source; use it with the module contract.
Read the implementation
GrowStrategy = Literal["pair", "saturate"]Other public import paths
omnibias.keras.GrowStrategyomnibias.keras.growable.GrowStrategy
GrowableOperatorMultiBiasUnit
omnibias.keras.GrowableOMBUSource ↗class GrowableOperatorMultiBiasUnit(layers.Layer)A multi-bias unit whose arity K can grow during training.
Parameters
num_channels : int
init_K : int, default 1
K_max : int, default 8
base : str or ActivationSpec, default "sigmoid"
init_bias : float, default 0.0
learnable_biases : bool, default True
learnable_signs : bool, default True
pair_eta : float, default 0.05
saturate_big : float, default 25.0
Parameters
| Name | Type & default | Contract |
|---|---|---|
num_channelspositional-or-keyword | intRequired | int |
init_Kpositional-or-keyword | intDefault: 1 | int, default 1 |
K_maxpositional-or-keyword | intDefault: 8 | int, default 8 |
basepositional-or-keyword | str | ActivationSpec[Any]Default: 'sigmoid' | str or ActivationSpec, default "sigmoid" |
init_biaspositional-or-keyword | floatDefault: 0.0 | float, default 0.0 |
learnable_biasespositional-or-keyword | boolDefault: True | bool, default True |
learnable_signspositional-or-keyword | boolDefault: True | bool, default True |
pair_etapositional-or-keyword | floatDefault: 0.05 | float, default 0.05 |
saturate_bigpositional-or-keyword | floatDefault: 25.0 | float, default 25.0 |
kwargsvariadic keyword | AnyRequired | See the operation contract and implementation below. |
Executed regression · grow beyond kmax raises
Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_grow_beyond_kmax_raises() -> None:
unit = GrowableOMBU(num_channels=2, init_K=1, K_max=2, base="sigmoid")
with pytest.raises(RuntimeError):
unit.grow("pair")python -m pytest "packages/omnibias-keras/tests/test_growable.py::test_grow_beyond_kmax_raises" -qRegression example · saturate growth friendly only
This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_saturate_growth_friendly_only() -> None:
unit = GrowableOMBU(num_channels=2, init_K=1, K_max=4, base="sigmoid")
unit.grow("saturate")
assert unit.active_K == 2
bad = GrowableOMBU(num_channels=2, init_K=1, K_max=4, base="tanh")
with pytest.raises(ValueError):
bad.grow("saturate")python -m pytest packages/omnibias-keras/tests/test_growable.py -k test_saturate_growth_friendly_only -qFields & methods
__init__method
__init__(self, num_channels: int, init_K: int=1, K_max: int=8, base: str | ActivationSpec[Any]='sigmoid', init_bias: float=0.0, learnable_biases: bool=True, learnable_signs: bool=True, pair_eta: float=0.05, saturate_big: float=25.0, **kwargs: Any) -> NoneSee the class contract above and the exact method implementation below.
Parameters
| Name | Type & default | Contract |
|---|---|---|
num_channelspositional-or-keyword | intRequired | See the operation contract and implementation below. |
init_Kpositional-or-keyword | intDefault: 1 | See the operation contract and implementation below. |
K_maxpositional-or-keyword | intDefault: 8 | See the operation contract and implementation below. |
basepositional-or-keyword | str | ActivationSpec[Any]Default: 'sigmoid' | See the operation contract and implementation below. |
init_biaspositional-or-keyword | floatDefault: 0.0 | See the operation contract and implementation below. |
learnable_biasespositional-or-keyword | boolDefault: True | See the operation contract and implementation below. |
learnable_signspositional-or-keyword | boolDefault: True | See the operation contract and implementation below. |
pair_etapositional-or-keyword | floatDefault: 0.05 | See the operation contract and implementation below. |
saturate_bigpositional-or-keyword | floatDefault: 25.0 | See the operation contract and implementation below. |
kwargsvariadic keyword | AnyRequired | See the operation contract and implementation below. |
Returns: None
Validation & failure cases
- When
num_channels < 1, raisesValueError(f'num_channels must be >= 1, got {num_channels}'). - When
K_max < 1, raisesValueError(f'K_max must be >= 1, got {K_max}'). - When
not 1 <= init_K <= K_max, raisesValueError(f'init_K must be in [1, K_max={K_max}], got {init_K}'). - When
pair_eta <= 0, raisesValueError(f'pair_eta must be > 0, got {pair_eta}'). - When
saturate_big <= 0, raisesValueError(f'saturate_big must be > 0, got {saturate_big}').
Executed regression · grow beyond kmax raises
Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_grow_beyond_kmax_raises() -> None:
unit = GrowableOMBU(num_channels=2, init_K=1, K_max=2, base="sigmoid")
with pytest.raises(RuntimeError):
unit.grow("pair")python -m pytest "packages/omnibias-keras/tests/test_growable.py::test_grow_beyond_kmax_raises" -qMethod implementation
def __init__(
self,
num_channels: int,
init_K: int = 1,
K_max: int = 8,
base: str | ActivationSpec[Any] = "sigmoid",
init_bias: float = 0.0,
learnable_biases: bool = True,
learnable_signs: bool = True,
pair_eta: float = 0.05,
saturate_big: float = 25.0,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
if num_channels < 1:
raise ValueError(f"num_channels must be >= 1, got {num_channels}")
if K_max < 1:
raise ValueError(f"K_max must be >= 1, got {K_max}")
if not (1 <= init_K <= K_max):
raise ValueError(f"init_K must be in [1, K_max={K_max}], got {init_K}")
if pair_eta <= 0:
raise ValueError(f"pair_eta must be > 0, got {pair_eta}")
if saturate_big <= 0:
raise ValueError(f"saturate_big must be > 0, got {saturate_big}")
self.num_channels = num_channels
self.K_max = K_max
self._base_name = base if isinstance(base, str) else base.name
self.spec = base if isinstance(base, ActivationSpec) else get_activation(base)
self.pair_eta = float(pair_eta)
self.saturate_big = float(saturate_big)
self.init_bias = init_bias
self.learnable_biases = learnable_biases
self.learnable_signs = learnable_signs
dtype = self.variable_dtype
biases_np = np.full((num_channels, K_max), float(init_bias), dtype=dtype)
signs_np = np.zeros((num_channels, K_max), dtype=dtype)
signs_np[:, :init_K] = identity_signs(init_K, dtype=dtype)[None, :]
self.biases = self.add_weight(
name="biases",
shape=(num_channels, K_max),
initializer="zeros",
trainable=learnable_biases,
dtype=dtype,
)
self.signs = self.add_weight(
name="signs",
shape=(num_channels, K_max),
initializer="zeros",
trainable=learnable_signs,
dtype=dtype,
)
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K = int(init_K)
self.built = Trueactive_Kproperty
active_K(self) -> intSee the class contract above and the exact method implementation below.
Returns: int
Executed regression · saturate growth friendly only
Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_saturate_growth_friendly_only() -> None:
unit = GrowableOMBU(num_channels=2, init_K=1, K_max=4, base="sigmoid")
unit.grow("saturate")
assert unit.active_K == 2
bad = GrowableOMBU(num_channels=2, init_K=1, K_max=4, base="tanh")
with pytest.raises(ValueError):
bad.grow("saturate")python -m pytest "packages/omnibias-keras/tests/test_growable.py::test_saturate_growth_friendly_only" -qMethod implementation
def active_K(self) -> int:
return self._active_Kcan_growproperty
can_grow(self) -> boolSee the class contract above and the exact method implementation below.
Returns: bool
Method implementation
def can_grow(self) -> bool:
return self._active_K < self.K_maxcallmethod
call(self, z: Any) -> AnyLiteral multi-bias forward over the active K columns only.
Parameters
| Name | Type & default | Contract |
|---|---|---|
zpositional-or-keyword | AnyRequired | See the operation contract and implementation below. |
Returns: Any
Executed regression · pair growth preserves output
Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_pair_growth_preserves_output() -> None:
z = np.random.default_rng(0).normal(size=(8, 4))
unit = GrowableOMBU(num_channels=4, init_K=1, K_max=5, base="tanh")
before = _np(unit(_t(z)))
added = unit.grow("pair")
assert added == 2
assert unit.active_K == 3
after = _np(unit(_t(z)))
np.testing.assert_allclose(after, before, rtol=1e-10, atol=1e-12)python -m pytest "packages/omnibias-keras/tests/test_growable.py::test_pair_growth_preserves_output" -qMethod implementation
def call(self, z: Any) -> Any:
"""Literal multi-bias forward over the active K columns only."""
K = self._active_K
biases = self.biases[:, :K]
signs = self.signs[:, :K]
return multibias_literal_forward(z, biases, signs, self.spec.forward)growmethod
grow(self, strategy: GrowStrategy='pair', *, channel: int | None=None, anchor_value: float | np.ndarray | None=None) -> intGrow active_K. Returns the number of newly-activated columns.
Parameters
| Name | Type & default | Contract |
|---|---|---|
strategypositional-or-keyword | GrowStrategyDefault: 'pair' | See the operation contract and implementation below. |
channelkeyword-only | int | NoneDefault: None | See the operation contract and implementation below. |
anchor_valuekeyword-only | float | np.ndarray | NoneDefault: None | See the operation contract and implementation below. |
Returns: int
Validation & failure cases
- When
K + 2 > self.K_max, raisesRuntimeError(f'grow(pair) would exceed K_max={self.K_max}: active_K={K}'). - When
anchor_value.shape != (self.num_channels,), raisesValueError(f'anchor_value array must have shape ({self.num_channels},), got {anchor_value.shape}'). - When
not 0 <= j < K, raisesValueError(f'channel must be in [0, {K}), got {j}'). - When
K + 1 > self.K_max, raisesRuntimeError(f'grow(saturate) would exceed K_max={self.K_max}: active_K={K}'). - When
self.spec.name not in _SATURATE_FRIENDLY, raisesValueError(f"strategy='saturate' is only safe for activations that vanish at z -> -infinity: one of {sorted(_SATURATE_FRIENDLY)}. Got {self.spec.name!r}; use strategy='pair' instead.").
Executed regression · grow beyond kmax raises
Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.
def test_grow_beyond_kmax_raises() -> None:
unit = GrowableOMBU(num_channels=2, init_K=1, K_max=2, base="sigmoid")
with pytest.raises(RuntimeError):
unit.grow("pair")python -m pytest "packages/omnibias-keras/tests/test_growable.py::test_grow_beyond_kmax_raises" -qMethod implementation
def grow(
self,
strategy: GrowStrategy = "pair",
*,
channel: int | None = None,
anchor_value: float | np.ndarray | None = None,
) -> int:
"""Grow ``active_K``. Returns the number of newly-activated columns."""
K = self._active_K
biases_np = np.asarray(ops.convert_to_numpy(self.biases.value))
signs_np = np.asarray(ops.convert_to_numpy(self.signs.value))
if strategy == "pair":
if K + 2 > self.K_max:
raise RuntimeError(f"grow(pair) would exceed K_max={self.K_max}: active_K={K}")
if anchor_value is not None:
if isinstance(anchor_value, np.ndarray):
if anchor_value.shape != (self.num_channels,):
raise ValueError(
f"anchor_value array must have shape ({self.num_channels},), "
f"got {anchor_value.shape}"
)
anchor = anchor_value.astype(biases_np.dtype)
else:
anchor = np.full(self.num_channels, float(anchor_value), dtype=biases_np.dtype)
else:
j = 0 if channel is None else int(channel)
if not (0 <= j < K):
raise ValueError(f"channel must be in [0, {K}), got {j}")
anchor = biases_np[:, j].copy()
biases_np[:, K] = anchor
biases_np[:, K + 1] = anchor
signs_np[:, K] = +self.pair_eta
signs_np[:, K + 1] = -self.pair_eta
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K += 2
return 2
if strategy == "saturate":
if K + 1 > self.K_max:
raise RuntimeError(f"grow(saturate) would exceed K_max={self.K_max}: active_K={K}")
if self.spec.name not in _SATURATE_FRIENDLY:
raise ValueError(
f"strategy='saturate' is only safe for activations that vanish "
f"at z -> -infinity: one of {sorted(_SATURATE_FRIENDLY)}. "
f"Got {self.spec.name!r}; use strategy='pair' instead."
)
biases_np[:, K] = -self.saturate_big
signs_np[:, K] = 0.0
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K += 1
return 1
raise ValueError(f"Unknown strategy {strategy!r}; expected 'pair' or 'saturate'.")active_parametersmethod
active_parameters(self) -> dict[str, Any]See the class contract above and the exact method implementation below.
Returns: dict[str, Any]
Method implementation
def active_parameters(self) -> dict[str, Any]:
K = self._active_K
return {"biases": self.biases[:, :K], "signs": self.signs[:, :K]}get_configmethod
get_config(self) -> dict[str, Any]See the class contract above and the exact method implementation below.
Returns: dict[str, Any]
Method implementation
def get_config(self) -> dict[str, Any]:
config = super().get_config()
config.update(
{
"num_channels": self.num_channels,
"init_K": self._active_K,
"K_max": self.K_max,
"base": self._base_name,
"init_bias": self.init_bias,
"learnable_biases": self.learnable_biases,
"learnable_signs": self.learnable_signs,
"pair_eta": self.pair_eta,
"saturate_big": self.saturate_big,
}
)
return configspecattribute
spec = base if isinstance(base, ActivationSpec) else get_activation(base)Public instance attribute assigned by __init__. The source assignment is spec = base if isinstance(base, ActivationSpec) else get_activation(base); it describes instance state, not a literal constructor default.
biasesattribute
biases = self.add_weight(name='biases', shape=(num_channels, K_max), initializer='zeros', trainable=learnable_biases, dtype=dtype)Public instance attribute assigned by __init__. The source assignment is biases = self.add_weight(name='biases', shape=(num_channels, K_max), initializer='zeros', trainable=learnable_biases, dtype=dtype); it describes instance state, not a literal constructor default.
signsattribute
signs = self.add_weight(name='signs', shape=(num_channels, K_max), initializer='zeros', trainable=learnable_signs, dtype=dtype)Public instance attribute assigned by __init__. The source assignment is signs = self.add_weight(name='signs', shape=(num_channels, K_max), initializer='zeros', trainable=learnable_signs, dtype=dtype); it describes instance state, not a literal constructor default.
builtattribute
built = TruePublic instance attribute assigned by __init__. The source assignment is built = True; it describes instance state, not a literal constructor default.
Read the implementation
class GrowableOperatorMultiBiasUnit(layers.Layer):
"""A multi-bias unit whose arity ``K`` can grow during training.
Parameters
----------
num_channels : int
init_K : int, default 1
K_max : int, default 8
base : str or :class:`ActivationSpec`, default ``"sigmoid"``
init_bias : float, default 0.0
learnable_biases : bool, default True
learnable_signs : bool, default True
pair_eta : float, default 0.05
saturate_big : float, default 25.0
"""
def __init__(
self,
num_channels: int,
init_K: int = 1,
K_max: int = 8,
base: str | ActivationSpec[Any] = "sigmoid",
init_bias: float = 0.0,
learnable_biases: bool = True,
learnable_signs: bool = True,
pair_eta: float = 0.05,
saturate_big: float = 25.0,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
if num_channels < 1:
raise ValueError(f"num_channels must be >= 1, got {num_channels}")
if K_max < 1:
raise ValueError(f"K_max must be >= 1, got {K_max}")
if not (1 <= init_K <= K_max):
raise ValueError(f"init_K must be in [1, K_max={K_max}], got {init_K}")
if pair_eta <= 0:
raise ValueError(f"pair_eta must be > 0, got {pair_eta}")
if saturate_big <= 0:
raise ValueError(f"saturate_big must be > 0, got {saturate_big}")
self.num_channels = num_channels
self.K_max = K_max
self._base_name = base if isinstance(base, str) else base.name
self.spec = base if isinstance(base, ActivationSpec) else get_activation(base)
self.pair_eta = float(pair_eta)
self.saturate_big = float(saturate_big)
self.init_bias = init_bias
self.learnable_biases = learnable_biases
self.learnable_signs = learnable_signs
dtype = self.variable_dtype
biases_np = np.full((num_channels, K_max), float(init_bias), dtype=dtype)
signs_np = np.zeros((num_channels, K_max), dtype=dtype)
signs_np[:, :init_K] = identity_signs(init_K, dtype=dtype)[None, :]
self.biases = self.add_weight(
name="biases",
shape=(num_channels, K_max),
initializer="zeros",
trainable=learnable_biases,
dtype=dtype,
)
self.signs = self.add_weight(
name="signs",
shape=(num_channels, K_max),
initializer="zeros",
trainable=learnable_signs,
dtype=dtype,
)
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K = int(init_K)
self.built = True
@property
def active_K(self) -> int:
return self._active_K
@property
def can_grow(self) -> bool:
return self._active_K < self.K_max
def call(self, z: Any) -> Any:
"""Literal multi-bias forward over the active K columns only."""
K = self._active_K
biases = self.biases[:, :K]
signs = self.signs[:, :K]
return multibias_literal_forward(z, biases, signs, self.spec.forward)
def grow(
self,
strategy: GrowStrategy = "pair",
*,
channel: int | None = None,
anchor_value: float | np.ndarray | None = None,
) -> int:
"""Grow ``active_K``. Returns the number of newly-activated columns."""
K = self._active_K
biases_np = np.asarray(ops.convert_to_numpy(self.biases.value))
signs_np = np.asarray(ops.convert_to_numpy(self.signs.value))
if strategy == "pair":
if K + 2 > self.K_max:
raise RuntimeError(f"grow(pair) would exceed K_max={self.K_max}: active_K={K}")
if anchor_value is not None:
if isinstance(anchor_value, np.ndarray):
if anchor_value.shape != (self.num_channels,):
raise ValueError(
f"anchor_value array must have shape ({self.num_channels},), "
f"got {anchor_value.shape}"
)
anchor = anchor_value.astype(biases_np.dtype)
else:
anchor = np.full(self.num_channels, float(anchor_value), dtype=biases_np.dtype)
else:
j = 0 if channel is None else int(channel)
if not (0 <= j < K):
raise ValueError(f"channel must be in [0, {K}), got {j}")
anchor = biases_np[:, j].copy()
biases_np[:, K] = anchor
biases_np[:, K + 1] = anchor
signs_np[:, K] = +self.pair_eta
signs_np[:, K + 1] = -self.pair_eta
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K += 2
return 2
if strategy == "saturate":
if K + 1 > self.K_max:
raise RuntimeError(f"grow(saturate) would exceed K_max={self.K_max}: active_K={K}")
if self.spec.name not in _SATURATE_FRIENDLY:
raise ValueError(
f"strategy='saturate' is only safe for activations that vanish "
f"at z -> -infinity: one of {sorted(_SATURATE_FRIENDLY)}. "
f"Got {self.spec.name!r}; use strategy='pair' instead."
)
biases_np[:, K] = -self.saturate_big
signs_np[:, K] = 0.0
self.biases.assign(biases_np)
self.signs.assign(signs_np)
self._active_K += 1
return 1
raise ValueError(f"Unknown strategy {strategy!r}; expected 'pair' or 'saturate'.")
def active_parameters(self) -> dict[str, Any]:
K = self._active_K
return {"biases": self.biases[:, :K], "signs": self.signs[:, :K]}
def get_config(self) -> dict[str, Any]:
config = super().get_config()
config.update(
{
"num_channels": self.num_channels,
"init_K": self._active_K,
"K_max": self.K_max,
"base": self._base_name,
"init_bias": self.init_bias,
"learnable_biases": self.learnable_biases,
"learnable_signs": self.learnable_signs,
"pair_eta": self.pair_eta,
"saturate_big": self.saturate_big,
}
)
return configOther public import paths
omnibias.keras.GrowableOMBUomnibias.keras.GrowableOperatorMultiBiasUnitomnibias.keras.growable.GrowableOperatorMultiBiasUnit