Source code for mojolearn._residual_dropout_impl

# SPDX-License-Identifier: Apache-2.0
"""Explicit residual-dropout layer: argument admission and native buffers only."""
import operator

from . import _training_impl
from ._buffer import addr, addr_ro, empty
from ._bufcheck import is_native_f32, probe


def _operand(value, name):
    view = probe(value)
    if not is_native_f32(view.format) or not view.c_contiguous:
        raise ValueError(name + ' must be a C-contiguous native float32 buffer')
    count = 1
    for extent in view.shape:  # glue: shape metadata, no tensor elements
        count *= extent
    if count > (1 << 31) - 1:
        raise ValueError(name + ' exceeds the native element index range')
    return view.shape, count


def _uint(value, name, bits):
    if isinstance(value, bool):
        raise TypeError(name + ' must be an unsigned integer')
    value = operator.index(value)
    if value < 0 or value >= 1 << bits:
        raise ValueError(name + ' is outside its unsigned integer range')
    return value


def _params(count, p, seed, stream, offset):
    seed = _uint(seed, 'seed', 64)
    stream = _uint(stream, 'stream', 32)
    offset = _uint(offset, 'offset', 63)
    if offset > (1 << 63) - 1 - count:
        raise ValueError('offset plus element count exceeds the native Philox range')
    if isinstance(p, bool):
        raise TypeError('p must be a real dropout probability')
    # p admission, FP32 rounding and scale arithmetic are native Mojo.
    return [count, offset, seed & 0xffffffff, seed >> 32, stream, float(p)]


def _entry(name):
    binding = _training_impl._load('identical')
    entry = getattr(binding, name, None)
    if not callable(entry):
        raise ImportError('residual dropout requires updated training binding: missing ' + name)
    return entry


[docs] def residual_dropout(values, residual, *, p=0.5, seed=0, stream=0, offset=0): """Return ``residual + dropout(values)`` as one explicit IDENTICAL layer. Inputs must have identical shapes and C-contiguous float32 storage. The native implementation admits finite values and p in [0,1), computes scale=1/(1-p), and preserves the rounded dropout intermediate before add. RNG coordinates are (64-bit seed, 32-bit stream, offset+flat_index). NN59 selects the fused schedule; OFF/ALL_OFF use two native stages. No existing model is implicitly changed and no validation is claimed. """ shape, count = _operand(values, 'values') residual_shape, residual_count = _operand(residual, 'residual') if residual_shape != shape or residual_count != count: raise ValueError('values and residual must have identical shapes') params = _params(count, p, seed, stream, offset) result = empty(shape, '<f4') written = _entry('residual_dropout')( addr_ro(values, name='values') if count else 0, addr_ro(residual, name='residual') if count else 0, addr(result, name='result') if count else 0, params) if written != count: raise RuntimeError('residual_dropout returned an incomplete output') return result
[docs] def residual_dropout_backward(gradient, *, p=0.5, seed=0, stream=0, offset=0): """Return ``(d_values, d_residual)`` for the explicit residual layer. Pass exactly the forward p/seed/stream/offset to regenerate its mask. d_values applies the same rounded keep-and-scale operation to gradient; d_residual preserves the upstream gradient words. This is an explicit backward operation, not an automatic differentiation registration. """ shape, count = _operand(gradient, 'gradient') params = _params(count, p, seed, stream, offset) d_values = empty(shape, '<f4') d_residual = empty(shape, '<f4') written = _entry('residual_dropout_backward')( addr_ro(gradient, name='gradient') if count else 0, addr(d_values, name='d_values') if count else 0, addr(d_residual, name='d_residual') if count else 0, params) if written != count: raise RuntimeError('residual_dropout_backward returned incomplete gradients') return d_values, d_residual