Source code for embedl_deploy._internal.lattice.patterns.conversions.activation
# Copyright (C) 2026 Embedl AB
"""Activation conversion patterns for Lattice."""
import logging
from torch import fx, nn
from embedl_deploy._internal.core.patterns.main import Pattern, Phase
from embedl_deploy._internal.core.tree.types import Graft, Tree, TreeMatch
from embedl_deploy._internal.core.tree.utils import get_module, resolve_module
from embedl_deploy._internal.lattice.modules.activation import (
LatticeLeakyReLU,
)
_LOG = logging.getLogger(__name__)
def _is_nonconforming_leaky_relu(node: fx.Node) -> bool:
"""Return ``True`` when `node` is a ``LeakyReLU`` that needs rewriting."""
mod = get_module(node)
if not isinstance(mod, nn.LeakyReLU):
return False
if isinstance(mod, LatticeLeakyReLU):
return False
return not LatticeLeakyReLU.is_compatible(mod)
def _make_lattice_leaky_relu(
tree_match: TreeMatch,
) -> tuple[nn.Module, ...]:
"""Build the replacement ``LeakyReLU`` for the matched node."""
node = tree_match.get_node(0)
act = resolve_module(node, nn.LeakyReLU)
new_act = LatticeLeakyReLU(act)
if act.negative_slope != new_act.negative_slope:
_LOG.warning(
"%s: LeakyReLU negative_slope snapped from %s to %s — "
"output will differ, retraining is recommended",
node.name,
act.negative_slope,
new_act.negative_slope,
)
return (new_act,)
[docs]
class LatticeLeakyReLUPattern(Pattern):
"""Snap a ``LeakyReLU`` negative slope to Lattice's supported value.
Lattice hardware implements leaky ReLU with a fixed negative slope of
``1/16`` (0.0625). Any other slope is replaced by that value and a warning
is logged advising retraining.
"""
is_numerically_equivalent = False
phase = Phase.CONVERSION
tree: Tree = (_is_nonconforming_leaky_relu,)
graft: Graft = (_make_lattice_leaky_relu,)
def _make_relu_from_activation(
tree_match: TreeMatch,
) -> tuple[nn.Module, ...]:
"""Build a ``ReLU`` replacement for the matched activation."""
node = tree_match.get_node(0)
act = resolve_module(node, nn.Module)
_LOG.warning(
"%s: %s replaced with ReLU — output will differ, retraining is recommended",
node.name,
type(act).__name__,
)
return (nn.ReLU(inplace=getattr(act, "inplace", False)),)
[docs]
class LatticeSiLUToReLUPattern(Pattern):
"""Replace ``SiLU`` with ``ReLU``.
Lattice hardware does not support ``SiLU`` activations. Any ``SiLU``
instance is replaced by a ``ReLU`` and a warning is logged advising
retraining.
"""
is_numerically_equivalent = False
phase = Phase.CONVERSION
tree: Tree = (lambda node: isinstance(get_module(node), nn.SiLU),)
graft: Graft = (_make_relu_from_activation,)
[docs]
class LatticeGELUToReLUPattern(Pattern):
"""Replace ``GELU`` with ``ReLU``.
Lattice hardware does not support ``GELU`` activations. Any ``GELU``
instance is replaced by a ``ReLU`` and a warning is logged advising
retraining.
"""
is_numerically_equivalent = False
phase = Phase.CONVERSION
tree: Tree = (lambda node: isinstance(get_module(node), nn.GELU),)
graft: Graft = (_make_relu_from_activation,)
[docs]
class LatticeReLU6ToReLUPattern(Pattern):
"""Replace ``ReLU6`` with ``ReLU``.
Lattice hardware does not support ``ReLU6`` (Clip) activations. Any
``ReLU6`` instance is replaced by a ``ReLU`` and a warning is logged
advising retraining.
"""
is_numerically_equivalent = False
phase = Phase.CONVERSION
tree: Tree = (lambda node: isinstance(get_module(node), nn.ReLU6),)
graft: Graft = (_make_relu_from_activation,)