Skip to content

API Reference

Complete reference for every class and function in Neurogebra.

New here?

Start with the tutorials first, then come back here when you need details.


Quick Import Guide

# Core imports — you'll use these the most
from neurogebra import MathForge, Expression

# Autograd (manual neural networks)
from neurogebra.core.autograd import Value, Tensor

# Training
from neurogebra.core.trainer import Trainer

# Model building
from neurogebra.builders.model_builder import ModelBuilder

# Educational interface
from neurogebra.core.neurocraft import NeuroCraft

# Datasets
from neurogebra.datasets import Datasets, ExpandedDatasets

Core Classes

Expression

The fundamental building block — a mathematical expression with symbolic and numerical capabilities.

neurogebra.core.expression.Expression

Unified mathematical expression supporting symbolic, numerical, and trainable operations.

Attributes:

Name Type Description
name

Human-readable name of the expression

symbolic_expr

SymPy symbolic representation

params

Dictionary of parameters

trainable_params

List of parameter names that can be trained

metadata

Additional information about the expression

Source code in neurogebra/core/expression.py
class Expression:
    """
    Unified mathematical expression supporting symbolic, numerical,
    and trainable operations.

    Attributes:
        name: Human-readable name of the expression
        symbolic_expr: SymPy symbolic representation
        params: Dictionary of parameters
        trainable_params: List of parameter names that can be trained
        metadata: Additional information about the expression
    """

    def __init__(
        self,
        name: str,
        symbolic_expr: Union[str, sp.Expr],
        params: Optional[Dict[str, Any]] = None,
        trainable_params: Optional[List[str]] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize an Expression.

        Args:
            name: Name identifier for the expression
            symbolic_expr: Symbolic mathematical expression (string or SymPy)
            params: Dictionary of parameter values
            trainable_params: List of parameters that can be trained
            metadata: Additional information (description, usage, etc.)
        """
        self.name = name
        self.params = params or {}
        self.trainable_params = trainable_params or []
        self.metadata = metadata or {}

        # Convert string to SymPy expression
        if isinstance(symbolic_expr, str):
            self.symbolic_expr = sympify(symbolic_expr)
        else:
            self.symbolic_expr = symbolic_expr

        # Extract variables from symbolic expression
        self.variables = sorted(
            list(self.symbolic_expr.free_symbols), key=lambda s: s.name
        )

        # Create numerical function
        self._numerical_func = None
        self._compile_numerical()

    def _compile_numerical(self):
        """Compile symbolic expression to numerical function."""
        if self.variables:
            self._numerical_func = lambdify(
                self.variables, self.symbolic_expr, modules=["numpy"]
            )
        else:
            # Constant expression
            self._numerical_func = lambda: float(self.symbolic_expr)

    def eval(self, *args: Any, **kwargs: Any) -> Union[float, np.ndarray]:
        """
        Evaluate the expression numerically.

        Args:
            *args: Positional arguments for variables
            **kwargs: Keyword arguments for variables

        Returns:
            Numerical result (float or numpy array)

        Examples:
            >>> expr = Expression("quadratic", "a*x**2 + b*x + c")
            >>> result = expr.eval(x=2, a=1, b=2, c=1)
        """
        # Substitute parameters into expression
        expr_with_params = self.symbolic_expr.subs(self.params)

        # Determine remaining free symbols after parameter substitution
        remaining_vars = sorted(
            list(expr_with_params.free_symbols), key=lambda s: s.name
        )

        if not remaining_vars:
            return float(expr_with_params)

        # Create function with current parameters
        func = lambdify(remaining_vars, expr_with_params, modules=["numpy"])

        # Handle both positional and keyword arguments
        if args:
            return func(*args)
        elif kwargs:
            ordered_args = [kwargs.get(str(var), 0) for var in remaining_vars]
            return func(*ordered_args)
        else:
            return func()

    def gradient(self, var: Union[str, Symbol]) -> "Expression":
        """
        Compute symbolic gradient with respect to a variable.

        Args:
            var: Variable to differentiate with respect to

        Returns:
            New Expression representing the gradient
        """
        if isinstance(var, str):
            var = Symbol(var)

        grad_expr = sp.diff(self.symbolic_expr, var)

        return Expression(
            name=f"d({self.name})/d({var})",
            symbolic_expr=grad_expr,
            params=self.params.copy(),
            metadata={"parent": self.name, "gradient_var": str(var)},
        )

    def compose(self, other: "Expression") -> "Expression":
        """
        Compose two expressions: self(other(x)).

        Args:
            other: Expression to compose with

        Returns:
            New composed Expression
        """
        # Simple composition: self(other)
        if not self.variables:
            return Expression(
                name=f"{self.name}{other.name}",
                symbolic_expr=self.symbolic_expr,
                params={**self.params, **other.params},
                metadata={"composition": [self.name, other.name]},
            )

        composed_expr = self.symbolic_expr.subs(
            {self.variables[0]: other.symbolic_expr}
        )

        return Expression(
            name=f"{self.name}{other.name}",
            symbolic_expr=composed_expr,
            params={**self.params, **other.params},
            metadata={"composition": [self.name, other.name]},
        )

    def clone(self) -> "Expression":
        """
        Create a deep copy of this expression.

        Returns:
            New Expression with copied attributes
        """
        return Expression(
            name=self.name,
            symbolic_expr=self.symbolic_expr,
            params=self.params.copy(),
            trainable_params=self.trainable_params.copy(),
            metadata=self.metadata.copy(),
        )

    def visualize(
        self,
        x_range: Tuple[float, float] = (-5, 5),
        n_points: int = 500,
        interactive: bool = False,
        **kwargs: Any,
    ):
        """
        Visualize this expression.

        Args:
            x_range: Range for x-axis
            n_points: Number of points
            interactive: Use interactive plotly plot
            **kwargs: Additional plot parameters
        """
        from neurogebra.viz.plotting import plot_expression

        return plot_expression(self, x_range=x_range, n_points=n_points, **kwargs)

    def __call__(self, *args, **kwargs):
        """Allow expression to be called like a function."""
        return self.eval(*args, **kwargs)

    def __add__(self, other):
        """Add two expressions."""
        if isinstance(other, Expression):
            new_expr = self.symbolic_expr + other.symbolic_expr
            new_name = f"({self.name}+{other.name})"
            new_params = {**self.params, **other.params}
        else:
            new_expr = self.symbolic_expr + other
            new_name = f"({self.name}+{other})"
            new_params = self.params.copy()

        return Expression(new_name, new_expr, new_params)

    def __radd__(self, other):
        """Right addition."""
        if isinstance(other, (int, float)):
            new_expr = other + self.symbolic_expr
            new_name = f"({other}+{self.name})"
            return Expression(new_name, new_expr, self.params.copy())
        return NotImplemented

    def __sub__(self, other):
        """Subtract two expressions."""
        if isinstance(other, Expression):
            new_expr = self.symbolic_expr - other.symbolic_expr
            new_name = f"({self.name}-{other.name})"
            new_params = {**self.params, **other.params}
        else:
            new_expr = self.symbolic_expr - other
            new_name = f"({self.name}-{other})"
            new_params = self.params.copy()

        return Expression(new_name, new_expr, new_params)

    def __mul__(self, other):
        """Multiply two expressions."""
        if isinstance(other, Expression):
            new_expr = self.symbolic_expr * other.symbolic_expr
            new_name = f"({self.name}*{other.name})"
            new_params = {**self.params, **other.params}
        else:
            new_expr = self.symbolic_expr * other
            new_name = f"({self.name}*{other})"
            new_params = self.params.copy()

        return Expression(new_name, new_expr, new_params)

    def __rmul__(self, other):
        """Right multiplication."""
        if isinstance(other, (int, float)):
            new_expr = other * self.symbolic_expr
            new_name = f"({other}*{self.name})"
            return Expression(new_name, new_expr, self.params.copy())
        return NotImplemented

    def __truediv__(self, other):
        """Divide two expressions."""
        if isinstance(other, Expression):
            new_expr = self.symbolic_expr / other.symbolic_expr
            new_name = f"({self.name}/{other.name})"
            new_params = {**self.params, **other.params}
        else:
            new_expr = self.symbolic_expr / other
            new_name = f"({self.name}/{other})"
            new_params = self.params.copy()

        return Expression(new_name, new_expr, new_params)

    def __pow__(self, other):
        """Power of expression."""
        if isinstance(other, Expression):
            new_expr = self.symbolic_expr ** other.symbolic_expr
            new_name = f"({self.name}**{other.name})"
            new_params = {**self.params, **other.params}
        else:
            new_expr = self.symbolic_expr ** other
            new_name = f"({self.name}**{other})"
            new_params = self.params.copy()

        return Expression(new_name, new_expr, new_params)

    def __neg__(self):
        """Negate expression."""
        return Expression(
            f"(-{self.name})", -self.symbolic_expr, self.params.copy()
        )

    def __repr__(self):
        return f"Expression('{self.name}': {self.symbolic_expr})"

    def __str__(self):
        return str(self.symbolic_expr)

    @property
    def formula(self) -> str:
        """Get LaTeX representation of the expression."""
        return sp.latex(self.symbolic_expr)

    def simplify(self) -> "Expression":
        """
        Return a simplified version of the expression.

        Returns:
            New simplified Expression
        """
        simplified = sp.simplify(self.symbolic_expr)
        return Expression(
            name=f"simplified({self.name})",
            symbolic_expr=simplified,
            params=self.params.copy(),
            trainable_params=self.trainable_params.copy(),
            metadata=self.metadata.copy(),
        )

    def expand(self) -> "Expression":
        """
        Return an expanded version of the expression.

        Returns:
            New expanded Expression
        """
        expanded = sp.expand(self.symbolic_expr)
        return Expression(
            name=f"expanded({self.name})",
            symbolic_expr=expanded,
            params=self.params.copy(),
            trainable_params=self.trainable_params.copy(),
            metadata=self.metadata.copy(),
        )

    def integrate(self, var: Union[str, Symbol]) -> "Expression":
        """
        Compute symbolic integral with respect to a variable.

        Args:
            var: Variable to integrate with respect to

        Returns:
            New Expression representing the integral
        """
        if isinstance(var, str):
            var = Symbol(var)

        integral_expr = sp.integrate(self.symbolic_expr, var)

        return Expression(
            name=f"∫({self.name})d{var}",
            symbolic_expr=integral_expr,
            params=self.params.copy(),
            metadata={"parent": self.name, "integral_var": str(var)},
        )

    def explain(self, level: str = "intermediate") -> str:
        """
        Provide explanation of the expression.

        Args:
            level: Explanation level ('beginner', 'intermediate', 'advanced')

        Returns:
            Explanatory text
        """
        explanation = f"Expression: {self.name}\n"
        explanation += f"Formula: {self.formula}\n"
        explanation += f"Variables: {[str(v) for v in self.variables]}\n"

        if self.params:
            explanation += f"Parameters: {self.params}\n"

        if self.trainable_params:
            explanation += f"Trainable: {self.trainable_params}\n"

        if "description" in self.metadata:
            explanation += f"\nDescription: {self.metadata['description']}\n"

        if "usage" in self.metadata:
            explanation += f"\nUsage: {self.metadata['usage']}\n"

        if level in ("intermediate", "advanced"):
            if "pros" in self.metadata:
                explanation += f"\nPros: {', '.join(self.metadata['pros'])}\n"
            if "cons" in self.metadata:
                explanation += f"Cons: {', '.join(self.metadata['cons'])}\n"

        if level == "advanced":
            # Show gradient information
            for var in self.variables:
                grad = sp.diff(self.symbolic_expr, var)
                explanation += f"\n∂/∂{var} = {grad}\n"

        return explanation

Attributes

formula property

Get LaTeX representation of the expression.

Functions

__init__(name, symbolic_expr, params=None, trainable_params=None, metadata=None)

Initialize an Expression.

Parameters:

Name Type Description Default
name str

Name identifier for the expression

required
symbolic_expr Union[str, Expr]

Symbolic mathematical expression (string or SymPy)

required
params Optional[Dict[str, Any]]

Dictionary of parameter values

None
trainable_params Optional[List[str]]

List of parameters that can be trained

None
metadata Optional[Dict[str, Any]]

Additional information (description, usage, etc.)

None
Source code in neurogebra/core/expression.py
def __init__(
    self,
    name: str,
    symbolic_expr: Union[str, sp.Expr],
    params: Optional[Dict[str, Any]] = None,
    trainable_params: Optional[List[str]] = None,
    metadata: Optional[Dict[str, Any]] = None,
):
    """
    Initialize an Expression.

    Args:
        name: Name identifier for the expression
        symbolic_expr: Symbolic mathematical expression (string or SymPy)
        params: Dictionary of parameter values
        trainable_params: List of parameters that can be trained
        metadata: Additional information (description, usage, etc.)
    """
    self.name = name
    self.params = params or {}
    self.trainable_params = trainable_params or []
    self.metadata = metadata or {}

    # Convert string to SymPy expression
    if isinstance(symbolic_expr, str):
        self.symbolic_expr = sympify(symbolic_expr)
    else:
        self.symbolic_expr = symbolic_expr

    # Extract variables from symbolic expression
    self.variables = sorted(
        list(self.symbolic_expr.free_symbols), key=lambda s: s.name
    )

    # Create numerical function
    self._numerical_func = None
    self._compile_numerical()

eval(*args, **kwargs)

Evaluate the expression numerically.

Parameters:

Name Type Description Default
*args Any

Positional arguments for variables

()
**kwargs Any

Keyword arguments for variables

{}

Returns:

Type Description
Union[float, ndarray]

Numerical result (float or numpy array)

Examples:

>>> expr = Expression("quadratic", "a*x**2 + b*x + c")
>>> result = expr.eval(x=2, a=1, b=2, c=1)
Source code in neurogebra/core/expression.py
def eval(self, *args: Any, **kwargs: Any) -> Union[float, np.ndarray]:
    """
    Evaluate the expression numerically.

    Args:
        *args: Positional arguments for variables
        **kwargs: Keyword arguments for variables

    Returns:
        Numerical result (float or numpy array)

    Examples:
        >>> expr = Expression("quadratic", "a*x**2 + b*x + c")
        >>> result = expr.eval(x=2, a=1, b=2, c=1)
    """
    # Substitute parameters into expression
    expr_with_params = self.symbolic_expr.subs(self.params)

    # Determine remaining free symbols after parameter substitution
    remaining_vars = sorted(
        list(expr_with_params.free_symbols), key=lambda s: s.name
    )

    if not remaining_vars:
        return float(expr_with_params)

    # Create function with current parameters
    func = lambdify(remaining_vars, expr_with_params, modules=["numpy"])

    # Handle both positional and keyword arguments
    if args:
        return func(*args)
    elif kwargs:
        ordered_args = [kwargs.get(str(var), 0) for var in remaining_vars]
        return func(*ordered_args)
    else:
        return func()

gradient(var)

Compute symbolic gradient with respect to a variable.

Parameters:

Name Type Description Default
var Union[str, Symbol]

Variable to differentiate with respect to

required

Returns:

Type Description
Expression

New Expression representing the gradient

Source code in neurogebra/core/expression.py
def gradient(self, var: Union[str, Symbol]) -> "Expression":
    """
    Compute symbolic gradient with respect to a variable.

    Args:
        var: Variable to differentiate with respect to

    Returns:
        New Expression representing the gradient
    """
    if isinstance(var, str):
        var = Symbol(var)

    grad_expr = sp.diff(self.symbolic_expr, var)

    return Expression(
        name=f"d({self.name})/d({var})",
        symbolic_expr=grad_expr,
        params=self.params.copy(),
        metadata={"parent": self.name, "gradient_var": str(var)},
    )

compose(other)

Compose two expressions: self(other(x)).

Parameters:

Name Type Description Default
other Expression

Expression to compose with

required

Returns:

Type Description
Expression

New composed Expression

Source code in neurogebra/core/expression.py
def compose(self, other: "Expression") -> "Expression":
    """
    Compose two expressions: self(other(x)).

    Args:
        other: Expression to compose with

    Returns:
        New composed Expression
    """
    # Simple composition: self(other)
    if not self.variables:
        return Expression(
            name=f"{self.name}{other.name}",
            symbolic_expr=self.symbolic_expr,
            params={**self.params, **other.params},
            metadata={"composition": [self.name, other.name]},
        )

    composed_expr = self.symbolic_expr.subs(
        {self.variables[0]: other.symbolic_expr}
    )

    return Expression(
        name=f"{self.name}{other.name}",
        symbolic_expr=composed_expr,
        params={**self.params, **other.params},
        metadata={"composition": [self.name, other.name]},
    )

clone()

Create a deep copy of this expression.

Returns:

Type Description
Expression

New Expression with copied attributes

Source code in neurogebra/core/expression.py
def clone(self) -> "Expression":
    """
    Create a deep copy of this expression.

    Returns:
        New Expression with copied attributes
    """
    return Expression(
        name=self.name,
        symbolic_expr=self.symbolic_expr,
        params=self.params.copy(),
        trainable_params=self.trainable_params.copy(),
        metadata=self.metadata.copy(),
    )

visualize(x_range=(-5, 5), n_points=500, interactive=False, **kwargs)

Visualize this expression.

Parameters:

Name Type Description Default
x_range Tuple[float, float]

Range for x-axis

(-5, 5)
n_points int

Number of points

500
interactive bool

Use interactive plotly plot

False
**kwargs Any

Additional plot parameters

{}
Source code in neurogebra/core/expression.py
def visualize(
    self,
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    interactive: bool = False,
    **kwargs: Any,
):
    """
    Visualize this expression.

    Args:
        x_range: Range for x-axis
        n_points: Number of points
        interactive: Use interactive plotly plot
        **kwargs: Additional plot parameters
    """
    from neurogebra.viz.plotting import plot_expression

    return plot_expression(self, x_range=x_range, n_points=n_points, **kwargs)

__call__(*args, **kwargs)

Allow expression to be called like a function.

Source code in neurogebra/core/expression.py
def __call__(self, *args, **kwargs):
    """Allow expression to be called like a function."""
    return self.eval(*args, **kwargs)

__add__(other)

Add two expressions.

Source code in neurogebra/core/expression.py
def __add__(self, other):
    """Add two expressions."""
    if isinstance(other, Expression):
        new_expr = self.symbolic_expr + other.symbolic_expr
        new_name = f"({self.name}+{other.name})"
        new_params = {**self.params, **other.params}
    else:
        new_expr = self.symbolic_expr + other
        new_name = f"({self.name}+{other})"
        new_params = self.params.copy()

    return Expression(new_name, new_expr, new_params)

__radd__(other)

Right addition.

Source code in neurogebra/core/expression.py
def __radd__(self, other):
    """Right addition."""
    if isinstance(other, (int, float)):
        new_expr = other + self.symbolic_expr
        new_name = f"({other}+{self.name})"
        return Expression(new_name, new_expr, self.params.copy())
    return NotImplemented

__sub__(other)

Subtract two expressions.

Source code in neurogebra/core/expression.py
def __sub__(self, other):
    """Subtract two expressions."""
    if isinstance(other, Expression):
        new_expr = self.symbolic_expr - other.symbolic_expr
        new_name = f"({self.name}-{other.name})"
        new_params = {**self.params, **other.params}
    else:
        new_expr = self.symbolic_expr - other
        new_name = f"({self.name}-{other})"
        new_params = self.params.copy()

    return Expression(new_name, new_expr, new_params)

__mul__(other)

Multiply two expressions.

Source code in neurogebra/core/expression.py
def __mul__(self, other):
    """Multiply two expressions."""
    if isinstance(other, Expression):
        new_expr = self.symbolic_expr * other.symbolic_expr
        new_name = f"({self.name}*{other.name})"
        new_params = {**self.params, **other.params}
    else:
        new_expr = self.symbolic_expr * other
        new_name = f"({self.name}*{other})"
        new_params = self.params.copy()

    return Expression(new_name, new_expr, new_params)

__rmul__(other)

Right multiplication.

Source code in neurogebra/core/expression.py
def __rmul__(self, other):
    """Right multiplication."""
    if isinstance(other, (int, float)):
        new_expr = other * self.symbolic_expr
        new_name = f"({other}*{self.name})"
        return Expression(new_name, new_expr, self.params.copy())
    return NotImplemented

__truediv__(other)

Divide two expressions.

Source code in neurogebra/core/expression.py
def __truediv__(self, other):
    """Divide two expressions."""
    if isinstance(other, Expression):
        new_expr = self.symbolic_expr / other.symbolic_expr
        new_name = f"({self.name}/{other.name})"
        new_params = {**self.params, **other.params}
    else:
        new_expr = self.symbolic_expr / other
        new_name = f"({self.name}/{other})"
        new_params = self.params.copy()

    return Expression(new_name, new_expr, new_params)

__pow__(other)

Power of expression.

Source code in neurogebra/core/expression.py
def __pow__(self, other):
    """Power of expression."""
    if isinstance(other, Expression):
        new_expr = self.symbolic_expr ** other.symbolic_expr
        new_name = f"({self.name}**{other.name})"
        new_params = {**self.params, **other.params}
    else:
        new_expr = self.symbolic_expr ** other
        new_name = f"({self.name}**{other})"
        new_params = self.params.copy()

    return Expression(new_name, new_expr, new_params)

__neg__()

Negate expression.

Source code in neurogebra/core/expression.py
def __neg__(self):
    """Negate expression."""
    return Expression(
        f"(-{self.name})", -self.symbolic_expr, self.params.copy()
    )

simplify()

Return a simplified version of the expression.

Returns:

Type Description
Expression

New simplified Expression

Source code in neurogebra/core/expression.py
def simplify(self) -> "Expression":
    """
    Return a simplified version of the expression.

    Returns:
        New simplified Expression
    """
    simplified = sp.simplify(self.symbolic_expr)
    return Expression(
        name=f"simplified({self.name})",
        symbolic_expr=simplified,
        params=self.params.copy(),
        trainable_params=self.trainable_params.copy(),
        metadata=self.metadata.copy(),
    )

expand()

Return an expanded version of the expression.

Returns:

Type Description
Expression

New expanded Expression

Source code in neurogebra/core/expression.py
def expand(self) -> "Expression":
    """
    Return an expanded version of the expression.

    Returns:
        New expanded Expression
    """
    expanded = sp.expand(self.symbolic_expr)
    return Expression(
        name=f"expanded({self.name})",
        symbolic_expr=expanded,
        params=self.params.copy(),
        trainable_params=self.trainable_params.copy(),
        metadata=self.metadata.copy(),
    )

integrate(var)

Compute symbolic integral with respect to a variable.

Parameters:

Name Type Description Default
var Union[str, Symbol]

Variable to integrate with respect to

required

Returns:

Type Description
Expression

New Expression representing the integral

Source code in neurogebra/core/expression.py
def integrate(self, var: Union[str, Symbol]) -> "Expression":
    """
    Compute symbolic integral with respect to a variable.

    Args:
        var: Variable to integrate with respect to

    Returns:
        New Expression representing the integral
    """
    if isinstance(var, str):
        var = Symbol(var)

    integral_expr = sp.integrate(self.symbolic_expr, var)

    return Expression(
        name=f"∫({self.name})d{var}",
        symbolic_expr=integral_expr,
        params=self.params.copy(),
        metadata={"parent": self.name, "integral_var": str(var)},
    )

explain(level='intermediate')

Provide explanation of the expression.

Parameters:

Name Type Description Default
level str

Explanation level ('beginner', 'intermediate', 'advanced')

'intermediate'

Returns:

Type Description
str

Explanatory text

Source code in neurogebra/core/expression.py
def explain(self, level: str = "intermediate") -> str:
    """
    Provide explanation of the expression.

    Args:
        level: Explanation level ('beginner', 'intermediate', 'advanced')

    Returns:
        Explanatory text
    """
    explanation = f"Expression: {self.name}\n"
    explanation += f"Formula: {self.formula}\n"
    explanation += f"Variables: {[str(v) for v in self.variables]}\n"

    if self.params:
        explanation += f"Parameters: {self.params}\n"

    if self.trainable_params:
        explanation += f"Trainable: {self.trainable_params}\n"

    if "description" in self.metadata:
        explanation += f"\nDescription: {self.metadata['description']}\n"

    if "usage" in self.metadata:
        explanation += f"\nUsage: {self.metadata['usage']}\n"

    if level in ("intermediate", "advanced"):
        if "pros" in self.metadata:
            explanation += f"\nPros: {', '.join(self.metadata['pros'])}\n"
        if "cons" in self.metadata:
            explanation += f"Cons: {', '.join(self.metadata['cons'])}\n"

    if level == "advanced":
        # Show gradient information
        for var in self.variables:
            grad = sp.diff(self.symbolic_expr, var)
            explanation += f"\n∂/∂{var} = {grad}\n"

    return explanation

MathForge

Your gateway to a curated library of pre-built mathematical expressions organized by category.

neurogebra.core.forge.MathForge

Central hub for accessing mathematical expressions.

MathForge provides a unified interface to: - Get pre-built expressions - Create custom expressions - Search for expressions - Compose expressions - Train expressions

Examples:

>>> forge = MathForge()
>>> relu = forge.get("relu")
>>> result = relu.eval(x=-5)
>>> print(result)  # 0
Source code in neurogebra/core/forge.py
class MathForge:
    """
    Central hub for accessing mathematical expressions.

    MathForge provides a unified interface to:
    - Get pre-built expressions
    - Create custom expressions
    - Search for expressions
    - Compose expressions
    - Train expressions

    Examples:
        >>> forge = MathForge()
        >>> relu = forge.get("relu")
        >>> result = relu.eval(x=-5)
        >>> print(result)  # 0
    """

    def __init__(self):
        """Initialize MathForge with expression repository."""
        self._repository: Dict[str, Expression] = {}
        self._load_repository()

    def _load_repository(self):
        """Load all expressions from repository modules."""
        # Load activations
        self._repository.update(activations.get_activations())

        # Load losses
        self._repository.update(losses.get_losses())

        # Load regularizers
        self._repository.update(regularizers.get_regularizers())

        # Load algebra
        self._repository.update(algebra.get_algebra_expressions())

        # Load calculus
        self._repository.update(calculus.get_calculus_expressions())

        # Load statistics
        self._repository.update(statistics.get_statistics_expressions())

        # Load linear algebra
        self._repository.update(linalg.get_linalg_expressions())

        # Load optimization
        self._repository.update(optimization.get_optimization_expressions())

        # Load metrics
        self._repository.update(metrics.get_metrics_expressions())

        # Load transforms
        self._repository.update(transforms.get_transforms_expressions())

    def get(
        self,
        name: str,
        params: Optional[Dict[str, Any]] = None,
        trainable: bool = False,
    ) -> Expression:
        """
        Get an expression by name.

        Args:
            name: Name of the expression
            params: Parameter overrides
            trainable: Whether to make parameters trainable

        Returns:
            Expression instance

        Raises:
            KeyError: If expression not found

        Examples:
            >>> forge = MathForge()
            >>> sigmoid = forge.get("sigmoid")
            >>> custom = forge.get("leaky_relu", params={"alpha": 0.2})
        """
        if name not in self._repository:
            available = ", ".join(list(self._repository.keys())[:10])
            raise KeyError(
                f"Expression '{name}' not found. "
                f"Available: {available}..."
            )

        expr_template = self._repository[name]

        # Clone and customize
        expr = Expression(
            name=expr_template.name,
            symbolic_expr=expr_template.symbolic_expr,
            params=expr_template.params.copy(),
            trainable_params=(
                list(expr_template.params.keys())
                if trainable
                else expr_template.trainable_params.copy()
            ),
            metadata=expr_template.metadata.copy(),
        )

        # Override parameters
        if params:
            expr.params.update(params)

        return expr

    def register(self, name: str, expression: Expression) -> None:
        """
        Register a custom expression in the repository.

        Args:
            name: Name for the expression
            expression: Expression instance to register
        """
        self._repository[name] = expression

    def search(self, query: str) -> List[str]:
        """
        Search for expressions by name or description.

        Args:
            query: Search string

        Returns:
            List of matching expression names
        """
        query_lower = query.lower()
        results = []

        for name, expr in self._repository.items():
            # Search in name
            if query_lower in name.lower():
                results.append(name)
                continue

            # Search in metadata
            if "description" in expr.metadata:
                if query_lower in expr.metadata["description"].lower():
                    results.append(name)
                    continue

            # Search in category
            if "category" in expr.metadata:
                if query_lower in expr.metadata["category"].lower():
                    results.append(name)

        return results

    def list_all(self, category: Optional[str] = None) -> List[str]:
        """
        List all available expressions.

        Args:
            category: Filter by category (e.g., 'activation', 'loss')

        Returns:
            List of expression names
        """
        if category is None:
            return list(self._repository.keys())

        return [
            name
            for name, expr in self._repository.items()
            if expr.metadata.get("category") == category
        ]

    def compose(self, expression_str: str, **params: Any) -> Expression:
        """
        Compose expressions using string notation.

        Args:
            expression_str: Expression like "mse + 0.1*l2"
            **params: Parameters for composed expression

        Returns:
            Composed Expression

        Examples:
            >>> forge = MathForge()
            >>> loss = forge.compose("mse + 0.1*mae")
        """
        parts = expression_str.split("+")

        result = None
        for part in parts:
            part = part.strip()

            # Handle scalar multiplication
            if "*" in part:
                scalar_str, expr_name = part.split("*", 1)
                scalar = float(scalar_str.strip())
                expr = self.get(expr_name.strip())
                expr = expr * scalar
            else:
                expr = self.get(part)

            if result is None:
                result = expr
            else:
                result = result + expr

        if result is None:
            raise ValueError(f"Could not parse expression: {expression_str}")

        return result

    def explain(self, name: str, level: str = "intermediate") -> str:
        """
        Get explanation for an expression.

        Args:
            name: Name of the expression
            level: Detail level ('beginner', 'intermediate', 'advanced')

        Returns:
            Explanation string
        """
        expr = self.get(name)
        return expr.explain(level=level)

    def compare(self, names: List[str]) -> str:
        """
        Compare multiple expressions side by side.

        Args:
            names: List of expression names to compare

        Returns:
            Comparison table as string
        """
        lines = [f"{'Name':<20} {'Formula':<40} {'Category':<15}"]
        lines.append("-" * 75)

        for name in names:
            if name in self._repository:
                expr = self._repository[name]
                formula = str(expr.symbolic_expr)[:38]
                category = expr.metadata.get("category", "N/A")
                lines.append(f"{name:<20} {formula:<40} {category:<15}")

        return "\n".join(lines)

Functions

__init__()

Initialize MathForge with expression repository.

Source code in neurogebra/core/forge.py
def __init__(self):
    """Initialize MathForge with expression repository."""
    self._repository: Dict[str, Expression] = {}
    self._load_repository()

get(name, params=None, trainable=False)

Get an expression by name.

Parameters:

Name Type Description Default
name str

Name of the expression

required
params Optional[Dict[str, Any]]

Parameter overrides

None
trainable bool

Whether to make parameters trainable

False

Returns:

Type Description
Expression

Expression instance

Raises:

Type Description
KeyError

If expression not found

Examples:

>>> forge = MathForge()
>>> sigmoid = forge.get("sigmoid")
>>> custom = forge.get("leaky_relu", params={"alpha": 0.2})
Source code in neurogebra/core/forge.py
def get(
    self,
    name: str,
    params: Optional[Dict[str, Any]] = None,
    trainable: bool = False,
) -> Expression:
    """
    Get an expression by name.

    Args:
        name: Name of the expression
        params: Parameter overrides
        trainable: Whether to make parameters trainable

    Returns:
        Expression instance

    Raises:
        KeyError: If expression not found

    Examples:
        >>> forge = MathForge()
        >>> sigmoid = forge.get("sigmoid")
        >>> custom = forge.get("leaky_relu", params={"alpha": 0.2})
    """
    if name not in self._repository:
        available = ", ".join(list(self._repository.keys())[:10])
        raise KeyError(
            f"Expression '{name}' not found. "
            f"Available: {available}..."
        )

    expr_template = self._repository[name]

    # Clone and customize
    expr = Expression(
        name=expr_template.name,
        symbolic_expr=expr_template.symbolic_expr,
        params=expr_template.params.copy(),
        trainable_params=(
            list(expr_template.params.keys())
            if trainable
            else expr_template.trainable_params.copy()
        ),
        metadata=expr_template.metadata.copy(),
    )

    # Override parameters
    if params:
        expr.params.update(params)

    return expr

register(name, expression)

Register a custom expression in the repository.

Parameters:

Name Type Description Default
name str

Name for the expression

required
expression Expression

Expression instance to register

required
Source code in neurogebra/core/forge.py
def register(self, name: str, expression: Expression) -> None:
    """
    Register a custom expression in the repository.

    Args:
        name: Name for the expression
        expression: Expression instance to register
    """
    self._repository[name] = expression

search(query)

Search for expressions by name or description.

Parameters:

Name Type Description Default
query str

Search string

required

Returns:

Type Description
List[str]

List of matching expression names

Source code in neurogebra/core/forge.py
def search(self, query: str) -> List[str]:
    """
    Search for expressions by name or description.

    Args:
        query: Search string

    Returns:
        List of matching expression names
    """
    query_lower = query.lower()
    results = []

    for name, expr in self._repository.items():
        # Search in name
        if query_lower in name.lower():
            results.append(name)
            continue

        # Search in metadata
        if "description" in expr.metadata:
            if query_lower in expr.metadata["description"].lower():
                results.append(name)
                continue

        # Search in category
        if "category" in expr.metadata:
            if query_lower in expr.metadata["category"].lower():
                results.append(name)

    return results

list_all(category=None)

List all available expressions.

Parameters:

Name Type Description Default
category Optional[str]

Filter by category (e.g., 'activation', 'loss')

None

Returns:

Type Description
List[str]

List of expression names

Source code in neurogebra/core/forge.py
def list_all(self, category: Optional[str] = None) -> List[str]:
    """
    List all available expressions.

    Args:
        category: Filter by category (e.g., 'activation', 'loss')

    Returns:
        List of expression names
    """
    if category is None:
        return list(self._repository.keys())

    return [
        name
        for name, expr in self._repository.items()
        if expr.metadata.get("category") == category
    ]

compose(expression_str, **params)

Compose expressions using string notation.

Parameters:

Name Type Description Default
expression_str str

Expression like "mse + 0.1*l2"

required
**params Any

Parameters for composed expression

{}

Returns:

Type Description
Expression

Composed Expression

Examples:

>>> forge = MathForge()
>>> loss = forge.compose("mse + 0.1*mae")
Source code in neurogebra/core/forge.py
def compose(self, expression_str: str, **params: Any) -> Expression:
    """
    Compose expressions using string notation.

    Args:
        expression_str: Expression like "mse + 0.1*l2"
        **params: Parameters for composed expression

    Returns:
        Composed Expression

    Examples:
        >>> forge = MathForge()
        >>> loss = forge.compose("mse + 0.1*mae")
    """
    parts = expression_str.split("+")

    result = None
    for part in parts:
        part = part.strip()

        # Handle scalar multiplication
        if "*" in part:
            scalar_str, expr_name = part.split("*", 1)
            scalar = float(scalar_str.strip())
            expr = self.get(expr_name.strip())
            expr = expr * scalar
        else:
            expr = self.get(part)

        if result is None:
            result = expr
        else:
            result = result + expr

    if result is None:
        raise ValueError(f"Could not parse expression: {expression_str}")

    return result

explain(name, level='intermediate')

Get explanation for an expression.

Parameters:

Name Type Description Default
name str

Name of the expression

required
level str

Detail level ('beginner', 'intermediate', 'advanced')

'intermediate'

Returns:

Type Description
str

Explanation string

Source code in neurogebra/core/forge.py
def explain(self, name: str, level: str = "intermediate") -> str:
    """
    Get explanation for an expression.

    Args:
        name: Name of the expression
        level: Detail level ('beginner', 'intermediate', 'advanced')

    Returns:
        Explanation string
    """
    expr = self.get(name)
    return expr.explain(level=level)

compare(names)

Compare multiple expressions side by side.

Parameters:

Name Type Description Default
names List[str]

List of expression names to compare

required

Returns:

Type Description
str

Comparison table as string

Source code in neurogebra/core/forge.py
def compare(self, names: List[str]) -> str:
    """
    Compare multiple expressions side by side.

    Args:
        names: List of expression names to compare

    Returns:
        Comparison table as string
    """
    lines = [f"{'Name':<20} {'Formula':<40} {'Category':<15}"]
    lines.append("-" * 75)

    for name in names:
        if name in self._repository:
            expr = self._repository[name]
            formula = str(expr.symbolic_expr)[:38]
            category = expr.metadata.get("category", "N/A")
            lines.append(f"{name:<20} {formula:<40} {category:<15}")

    return "\n".join(lines)

Trainer

Fits trainable expressions to data using SGD or Adam optimization.

neurogebra.core.trainer.Trainer

Trainer for optimizing expression parameters.

Supports various optimization algorithms for fitting expressions to data.

Examples:

>>> from neurogebra.core.expression import Expression
>>> expr = Expression("linear", "a*x + b",
...                   params={"a": 0.0, "b": 0.0},
...                   trainable_params=["a", "b"])
>>> trainer = Trainer(expr, learning_rate=0.01)
>>> X = np.array([1, 2, 3, 4, 5])
>>> y = np.array([3, 5, 7, 9, 11])  # y = 2x + 1
>>> history = trainer.fit(X, y, epochs=100)
Source code in neurogebra/core/trainer.py
class Trainer:
    """
    Trainer for optimizing expression parameters.

    Supports various optimization algorithms for fitting expressions to data.

    Examples:
        >>> from neurogebra.core.expression import Expression
        >>> expr = Expression("linear", "a*x + b",
        ...                   params={"a": 0.0, "b": 0.0},
        ...                   trainable_params=["a", "b"])
        >>> trainer = Trainer(expr, learning_rate=0.01)
        >>> X = np.array([1, 2, 3, 4, 5])
        >>> y = np.array([3, 5, 7, 9, 11])  # y = 2x + 1
        >>> history = trainer.fit(X, y, epochs=100)
    """

    def __init__(
        self,
        expression: Expression,
        learning_rate: float = 0.01,
        optimizer: str = "sgd",
    ):
        """
        Initialize Trainer.

        Args:
            expression: Expression to train
            learning_rate: Learning rate for optimization
            optimizer: Optimization algorithm ('sgd', 'adam')
        """
        self.expression = expression
        self.learning_rate = learning_rate
        self.optimizer = optimizer
        self.history: Dict[str, List] = {"loss": [], "params": []}

        # Adam optimizer state
        self._m: Dict[str, float] = {}
        self._v: Dict[str, float] = {}
        self._t = 0

        for param_name in self.expression.trainable_params:
            self._m[param_name] = 0.0
            self._v[param_name] = 0.0

    def fit(
        self,
        X: np.ndarray,
        y: np.ndarray,
        epochs: int = 100,
        batch_size: Optional[int] = None,
        loss_fn: str = "mse",
        verbose: bool = True,
        callback: Optional[Callable] = None,
    ) -> Dict[str, List]:
        """
        Fit expression to data.

        Args:
            X: Input data (N,) or (N, D)
            y: Target data (N,)
            epochs: Number of training epochs
            batch_size: Mini-batch size (None = full batch)
            loss_fn: Loss function ('mse', 'mae')
            verbose: Print training progress
            callback: Optional callback function called each epoch

        Returns:
            Training history with loss and parameter values per epoch
        """
        X = np.asarray(X, dtype=np.float64)
        y = np.asarray(y, dtype=np.float64)

        if batch_size is None:
            batch_size = len(X)

        for epoch in range(epochs):
            # Shuffle data
            indices = np.random.permutation(len(X))
            X_shuffled = X[indices]
            y_shuffled = y[indices]

            epoch_loss = 0.0
            n_batches = 0

            # Mini-batch training
            for i in range(0, len(X), batch_size):
                X_batch = X_shuffled[i : i + batch_size]
                y_batch = y_shuffled[i : i + batch_size]

                # Forward pass
                predictions = self._forward(X_batch)

                # Compute loss
                loss = self._compute_loss(predictions, y_batch, loss_fn)
                epoch_loss += loss
                n_batches += 1

                # Backward pass (numerical gradients for MVP)
                self._backward(X_batch, y_batch, loss_fn)

            # Record history
            avg_loss = epoch_loss / n_batches
            self.history["loss"].append(avg_loss)
            self.history["params"].append(self.expression.params.copy())

            if verbose and (epoch % max(1, epochs // 10) == 0 or epoch == epochs - 1):
                print(f"Epoch {epoch:>4d}/{epochs}: Loss = {avg_loss:.6f}")

            if callback is not None:
                callback(epoch, avg_loss, self.expression.params.copy())

        return self.history

    def _forward(self, X: np.ndarray) -> np.ndarray:
        """Forward pass through expression."""
        results = []
        for x in X:
            result = self.expression.eval(x=float(x))
            results.append(float(result))
        return np.array(results)

    def _compute_loss(
        self, predictions: np.ndarray, targets: np.ndarray, loss_fn: str
    ) -> float:
        """Compute loss value."""
        if loss_fn == "mse":
            return float(np.mean((predictions - targets) ** 2))
        elif loss_fn == "mae":
            return float(np.mean(np.abs(predictions - targets)))
        elif loss_fn == "huber":
            delta = 1.0
            diff = np.abs(predictions - targets)
            return float(
                np.mean(
                    np.where(
                        diff <= delta,
                        0.5 * diff**2,
                        delta * diff - 0.5 * delta**2,
                    )
                )
            )
        else:
            raise ValueError(f"Unknown loss function: {loss_fn}")

    def _backward(self, X: np.ndarray, y: np.ndarray, loss_fn: str):
        """
        Backward pass using numerical gradients.

        For MVP, use finite differences. Later versions can use
        symbolic differentiation or autograd.
        """
        epsilon = 1e-5

        for param_name in self.expression.trainable_params:
            if param_name not in self.expression.params:
                continue

            original_value = self.expression.params[param_name]

            # Compute gradient via central finite difference
            self.expression.params[param_name] = original_value + epsilon
            pred_plus = self._forward(X)
            loss_plus = self._compute_loss(pred_plus, y, loss_fn)

            self.expression.params[param_name] = original_value - epsilon
            pred_minus = self._forward(X)
            loss_minus = self._compute_loss(pred_minus, y, loss_fn)

            # Restore original
            self.expression.params[param_name] = original_value

            # Gradient
            gradient = (loss_plus - loss_minus) / (2 * epsilon)

            # Update parameter based on optimizer
            if self.optimizer == "adam":
                self._adam_update(param_name, gradient)
            else:
                # SGD
                self.expression.params[param_name] = (
                    original_value - self.learning_rate * gradient
                )

    def _adam_update(
        self,
        param_name: str,
        gradient: float,
        beta1: float = 0.9,
        beta2: float = 0.999,
        eps: float = 1e-8,
    ):
        """Adam optimizer update step."""
        self._t += 1

        self._m[param_name] = beta1 * self._m[param_name] + (1 - beta1) * gradient
        self._v[param_name] = beta2 * self._v[param_name] + (1 - beta2) * gradient**2

        m_hat = self._m[param_name] / (1 - beta1**self._t)
        v_hat = self._v[param_name] / (1 - beta2**self._t)

        self.expression.params[param_name] -= (
            self.learning_rate * m_hat / (np.sqrt(v_hat) + eps)
        )

    def reset(self):
        """Reset trainer state."""
        self.history = {"loss": [], "params": []}
        self._t = 0
        for param_name in self.expression.trainable_params:
            self._m[param_name] = 0.0
            self._v[param_name] = 0.0

Functions

__init__(expression, learning_rate=0.01, optimizer='sgd')

Initialize Trainer.

Parameters:

Name Type Description Default
expression Expression

Expression to train

required
learning_rate float

Learning rate for optimization

0.01
optimizer str

Optimization algorithm ('sgd', 'adam')

'sgd'
Source code in neurogebra/core/trainer.py
def __init__(
    self,
    expression: Expression,
    learning_rate: float = 0.01,
    optimizer: str = "sgd",
):
    """
    Initialize Trainer.

    Args:
        expression: Expression to train
        learning_rate: Learning rate for optimization
        optimizer: Optimization algorithm ('sgd', 'adam')
    """
    self.expression = expression
    self.learning_rate = learning_rate
    self.optimizer = optimizer
    self.history: Dict[str, List] = {"loss": [], "params": []}

    # Adam optimizer state
    self._m: Dict[str, float] = {}
    self._v: Dict[str, float] = {}
    self._t = 0

    for param_name in self.expression.trainable_params:
        self._m[param_name] = 0.0
        self._v[param_name] = 0.0

fit(X, y, epochs=100, batch_size=None, loss_fn='mse', verbose=True, callback=None)

Fit expression to data.

Parameters:

Name Type Description Default
X ndarray

Input data (N,) or (N, D)

required
y ndarray

Target data (N,)

required
epochs int

Number of training epochs

100
batch_size Optional[int]

Mini-batch size (None = full batch)

None
loss_fn str

Loss function ('mse', 'mae')

'mse'
verbose bool

Print training progress

True
callback Optional[Callable]

Optional callback function called each epoch

None

Returns:

Type Description
Dict[str, List]

Training history with loss and parameter values per epoch

Source code in neurogebra/core/trainer.py
def fit(
    self,
    X: np.ndarray,
    y: np.ndarray,
    epochs: int = 100,
    batch_size: Optional[int] = None,
    loss_fn: str = "mse",
    verbose: bool = True,
    callback: Optional[Callable] = None,
) -> Dict[str, List]:
    """
    Fit expression to data.

    Args:
        X: Input data (N,) or (N, D)
        y: Target data (N,)
        epochs: Number of training epochs
        batch_size: Mini-batch size (None = full batch)
        loss_fn: Loss function ('mse', 'mae')
        verbose: Print training progress
        callback: Optional callback function called each epoch

    Returns:
        Training history with loss and parameter values per epoch
    """
    X = np.asarray(X, dtype=np.float64)
    y = np.asarray(y, dtype=np.float64)

    if batch_size is None:
        batch_size = len(X)

    for epoch in range(epochs):
        # Shuffle data
        indices = np.random.permutation(len(X))
        X_shuffled = X[indices]
        y_shuffled = y[indices]

        epoch_loss = 0.0
        n_batches = 0

        # Mini-batch training
        for i in range(0, len(X), batch_size):
            X_batch = X_shuffled[i : i + batch_size]
            y_batch = y_shuffled[i : i + batch_size]

            # Forward pass
            predictions = self._forward(X_batch)

            # Compute loss
            loss = self._compute_loss(predictions, y_batch, loss_fn)
            epoch_loss += loss
            n_batches += 1

            # Backward pass (numerical gradients for MVP)
            self._backward(X_batch, y_batch, loss_fn)

        # Record history
        avg_loss = epoch_loss / n_batches
        self.history["loss"].append(avg_loss)
        self.history["params"].append(self.expression.params.copy())

        if verbose and (epoch % max(1, epochs // 10) == 0 or epoch == epochs - 1):
            print(f"Epoch {epoch:>4d}/{epochs}: Loss = {avg_loss:.6f}")

        if callback is not None:
            callback(epoch, avg_loss, self.expression.params.copy())

    return self.history

reset()

Reset trainer state.

Source code in neurogebra/core/trainer.py
def reset(self):
    """Reset trainer state."""
    self.history = {"loss": [], "params": []}
    self._t = 0
    for param_name in self.expression.trainable_params:
        self._m[param_name] = 0.0
        self._v[param_name] = 0.0

Value (Autograd)

Scalar value with automatic differentiation — the engine behind backpropagation.

neurogebra.core.autograd.Value

Scalar value with automatic differentiation support.

Inspired by micrograd, this class wraps numerical values and tracks computational graphs for backpropagation.

Examples:

>>> a = Value(2.0)
>>> b = Value(3.0)
>>> c = a * b + a
>>> c.backward()
>>> print(a.grad)  # dc/da = b + 1 = 4.0
Source code in neurogebra/core/autograd.py
class Value:
    """
    Scalar value with automatic differentiation support.

    Inspired by micrograd, this class wraps numerical values and tracks
    computational graphs for backpropagation.

    Examples:
        >>> a = Value(2.0)
        >>> b = Value(3.0)
        >>> c = a * b + a
        >>> c.backward()
        >>> print(a.grad)  # dc/da = b + 1 = 4.0
    """

    def __init__(self, data: float, _children: Tuple = (), _op: str = ""):
        """
        Initialize a Value node.

        Args:
            data: Numerical value
            _children: Parent nodes in computation graph
            _op: Operation that created this node
        """
        self.data = float(data)
        self.grad = 0.0
        self._backward = lambda: None
        self._prev = set(_children)
        self._op = _op

    def __repr__(self):
        return f"Value(data={self.data}, grad={self.grad})"

    def __add__(self, other):
        """Addition with gradient tracking."""
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data + other.data, (self, other), "+")

        def _backward():
            self.grad += out.grad
            other.grad += out.grad

        out._backward = _backward

        return out

    def __mul__(self, other):
        """Multiplication with gradient tracking."""
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data * other.data, (self, other), "*")

        def _backward():
            self.grad += other.data * out.grad
            other.grad += self.data * out.grad

        out._backward = _backward

        return out

    def __pow__(self, other):
        """Power operation with gradient tracking."""
        assert isinstance(other, (int, float)), "only int/float powers supported"
        out = Value(self.data**other, (self,), f"**{other}")

        def _backward():
            self.grad += other * (self.data ** (other - 1)) * out.grad

        out._backward = _backward

        return out

    def relu(self):
        """ReLU activation with gradient."""
        out = Value(max(0, self.data), (self,), "ReLU")

        def _backward():
            self.grad += (out.data > 0) * out.grad

        out._backward = _backward

        return out

    def sigmoid(self):
        """Sigmoid activation with gradient."""
        sig = 1.0 / (1.0 + np.exp(-self.data))
        out = Value(sig, (self,), "sigmoid")

        def _backward():
            self.grad += sig * (1 - sig) * out.grad

        out._backward = _backward

        return out

    def tanh(self):
        """Tanh activation with gradient."""
        t = np.tanh(self.data)
        out = Value(t, (self,), "tanh")

        def _backward():
            self.grad += (1 - t**2) * out.grad

        out._backward = _backward

        return out

    def exp(self):
        """Exponential with gradient."""
        e = np.exp(self.data)
        out = Value(e, (self,), "exp")

        def _backward():
            self.grad += e * out.grad

        out._backward = _backward

        return out

    def log(self):
        """Natural logarithm with gradient."""
        out = Value(np.log(self.data), (self,), "log")

        def _backward():
            self.grad += (1.0 / self.data) * out.grad

        out._backward = _backward

        return out

    def backward(self):
        """
        Compute gradients via backpropagation.

        Performs topological sort and calls _backward on each node.
        """
        # Build topological order
        topo: List[Value] = []
        visited: Set[int] = set()

        def build_topo(v: Value):
            if id(v) not in visited:
                visited.add(id(v))
                for child in v._prev:
                    build_topo(child)
                topo.append(v)

        build_topo(self)

        # Backpropagate
        self.grad = 1.0
        for node in reversed(topo):
            node._backward()

    def zero_grad(self):
        """Reset gradient to zero."""
        self.grad = 0.0

    def __neg__(self):
        return self * -1

    def __radd__(self, other):
        return self + other

    def __sub__(self, other):
        return self + (-other)

    def __rsub__(self, other):
        return other + (-self)

    def __rmul__(self, other):
        return self * other

    def __truediv__(self, other):
        return self * other**-1

    def __rtruediv__(self, other):
        return other * self**-1

    def __lt__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        return self.data < other.data

    def __gt__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        return self.data > other.data

    def __eq__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        return self.data == other.data

    def __hash__(self):
        return id(self)

Functions

__init__(data, _children=(), _op='')

Initialize a Value node.

Parameters:

Name Type Description Default
data float

Numerical value

required
_children Tuple

Parent nodes in computation graph

()
_op str

Operation that created this node

''
Source code in neurogebra/core/autograd.py
def __init__(self, data: float, _children: Tuple = (), _op: str = ""):
    """
    Initialize a Value node.

    Args:
        data: Numerical value
        _children: Parent nodes in computation graph
        _op: Operation that created this node
    """
    self.data = float(data)
    self.grad = 0.0
    self._backward = lambda: None
    self._prev = set(_children)
    self._op = _op

__add__(other)

Addition with gradient tracking.

Source code in neurogebra/core/autograd.py
def __add__(self, other):
    """Addition with gradient tracking."""
    other = other if isinstance(other, Value) else Value(other)
    out = Value(self.data + other.data, (self, other), "+")

    def _backward():
        self.grad += out.grad
        other.grad += out.grad

    out._backward = _backward

    return out

__mul__(other)

Multiplication with gradient tracking.

Source code in neurogebra/core/autograd.py
def __mul__(self, other):
    """Multiplication with gradient tracking."""
    other = other if isinstance(other, Value) else Value(other)
    out = Value(self.data * other.data, (self, other), "*")

    def _backward():
        self.grad += other.data * out.grad
        other.grad += self.data * out.grad

    out._backward = _backward

    return out

__pow__(other)

Power operation with gradient tracking.

Source code in neurogebra/core/autograd.py
def __pow__(self, other):
    """Power operation with gradient tracking."""
    assert isinstance(other, (int, float)), "only int/float powers supported"
    out = Value(self.data**other, (self,), f"**{other}")

    def _backward():
        self.grad += other * (self.data ** (other - 1)) * out.grad

    out._backward = _backward

    return out

relu()

ReLU activation with gradient.

Source code in neurogebra/core/autograd.py
def relu(self):
    """ReLU activation with gradient."""
    out = Value(max(0, self.data), (self,), "ReLU")

    def _backward():
        self.grad += (out.data > 0) * out.grad

    out._backward = _backward

    return out

sigmoid()

Sigmoid activation with gradient.

Source code in neurogebra/core/autograd.py
def sigmoid(self):
    """Sigmoid activation with gradient."""
    sig = 1.0 / (1.0 + np.exp(-self.data))
    out = Value(sig, (self,), "sigmoid")

    def _backward():
        self.grad += sig * (1 - sig) * out.grad

    out._backward = _backward

    return out

tanh()

Tanh activation with gradient.

Source code in neurogebra/core/autograd.py
def tanh(self):
    """Tanh activation with gradient."""
    t = np.tanh(self.data)
    out = Value(t, (self,), "tanh")

    def _backward():
        self.grad += (1 - t**2) * out.grad

    out._backward = _backward

    return out

exp()

Exponential with gradient.

Source code in neurogebra/core/autograd.py
def exp(self):
    """Exponential with gradient."""
    e = np.exp(self.data)
    out = Value(e, (self,), "exp")

    def _backward():
        self.grad += e * out.grad

    out._backward = _backward

    return out

log()

Natural logarithm with gradient.

Source code in neurogebra/core/autograd.py
def log(self):
    """Natural logarithm with gradient."""
    out = Value(np.log(self.data), (self,), "log")

    def _backward():
        self.grad += (1.0 / self.data) * out.grad

    out._backward = _backward

    return out

backward()

Compute gradients via backpropagation.

Performs topological sort and calls _backward on each node.

Source code in neurogebra/core/autograd.py
def backward(self):
    """
    Compute gradients via backpropagation.

    Performs topological sort and calls _backward on each node.
    """
    # Build topological order
    topo: List[Value] = []
    visited: Set[int] = set()

    def build_topo(v: Value):
        if id(v) not in visited:
            visited.add(id(v))
            for child in v._prev:
                build_topo(child)
            topo.append(v)

    build_topo(self)

    # Backpropagate
    self.grad = 1.0
    for node in reversed(topo):
        node._backward()

zero_grad()

Reset gradient to zero.

Source code in neurogebra/core/autograd.py
def zero_grad(self):
    """Reset gradient to zero."""
    self.grad = 0.0

Tensor (Autograd)

Multi-dimensional array with gradient tracking for batch operations.

neurogebra.core.autograd.Tensor

Multi-dimensional array with autograd support.

Extends Value concept to tensors for mini-batch training.

Examples:

>>> t = Tensor([1.0, 2.0, 3.0], requires_grad=True)
>>> result = t.sum()
>>> result.backward()
>>> print(t.grad)  # [1.0, 1.0, 1.0]
Source code in neurogebra/core/autograd.py
class Tensor:
    """
    Multi-dimensional array with autograd support.

    Extends Value concept to tensors for mini-batch training.

    Examples:
        >>> t = Tensor([1.0, 2.0, 3.0], requires_grad=True)
        >>> result = t.sum()
        >>> result.backward()
        >>> print(t.grad)  # [1.0, 1.0, 1.0]
    """

    def __init__(self, data: Union[List, np.ndarray, Any], requires_grad: bool = False):
        """
        Initialize a Tensor.

        Args:
            data: Array-like data (list, numpy array, etc.)
            requires_grad: Whether to track gradients
        """
        self.data = np.array(data, dtype=np.float64)
        self.grad: Optional[np.ndarray] = None
        self.requires_grad = requires_grad
        self._grad_fn = None
        self._prev: Set["Tensor"] = set()

        if requires_grad:
            self.grad = np.zeros_like(self.data)

    @property
    def shape(self) -> Tuple:
        """Return the shape of the tensor."""
        return self.data.shape

    @property
    def ndim(self) -> int:
        """Return the number of dimensions."""
        return self.data.ndim

    def backward(self, gradient: Optional[np.ndarray] = None):
        """
        Compute gradients via backpropagation.

        Args:
            gradient: Upstream gradient. If None, uses ones.
        """
        if not self.requires_grad:
            return

        if gradient is None:
            gradient = np.ones_like(self.data)

        if self.grad is not None:
            self.grad += gradient
        else:
            self.grad = gradient.copy()

        if self._grad_fn is not None:
            self._grad_fn(gradient)

    def zero_grad(self):
        """Reset gradient to zero."""
        if self.requires_grad:
            self.grad = np.zeros_like(self.data)

    def sum(self) -> "Tensor":
        """Sum all elements."""
        out = Tensor(np.sum(self.data), requires_grad=self.requires_grad)
        out._prev = {self}

        def _grad_fn(gradient):
            self.backward(gradient * np.ones_like(self.data))

        out._grad_fn = _grad_fn
        return out

    def mean(self) -> "Tensor":
        """Mean of all elements."""
        n = self.data.size
        out = Tensor(np.mean(self.data), requires_grad=self.requires_grad)
        out._prev = {self}

        def _grad_fn(gradient):
            self.backward(gradient * np.ones_like(self.data) / n)

        out._grad_fn = _grad_fn
        return out

    def __add__(self, other):
        """Element-wise addition."""
        if isinstance(other, Tensor):
            out = Tensor(
                self.data + other.data,
                requires_grad=self.requires_grad or other.requires_grad,
            )
            out._prev = {self, other}

            def _grad_fn(gradient):
                if self.requires_grad:
                    self.backward(gradient)
                if other.requires_grad:
                    other.backward(gradient)

            out._grad_fn = _grad_fn
        else:
            out = Tensor(self.data + other, requires_grad=self.requires_grad)
            out._prev = {self}

            def _grad_fn(gradient):
                if self.requires_grad:
                    self.backward(gradient)

            out._grad_fn = _grad_fn

        return out

    def __mul__(self, other):
        """Element-wise multiplication."""
        if isinstance(other, Tensor):
            out = Tensor(
                self.data * other.data,
                requires_grad=self.requires_grad or other.requires_grad,
            )
            out._prev = {self, other}

            def _grad_fn(gradient):
                if self.requires_grad:
                    self.backward(gradient * other.data)
                if other.requires_grad:
                    other.backward(gradient * self.data)

            out._grad_fn = _grad_fn
        else:
            out = Tensor(self.data * other, requires_grad=self.requires_grad)
            out._prev = {self}

            def _grad_fn(gradient):
                if self.requires_grad:
                    self.backward(gradient * other)

            out._grad_fn = _grad_fn

        return out

    def __sub__(self, other):
        """Element-wise subtraction."""
        if isinstance(other, Tensor):
            return self + Tensor(-other.data, requires_grad=other.requires_grad)
        return self + (-other)

    def __neg__(self):
        """Negate tensor."""
        return self * (-1)

    def __rmul__(self, other):
        return self * other

    def __radd__(self, other):
        return self + other

    def __pow__(self, power):
        """Element-wise power."""
        out = Tensor(self.data**power, requires_grad=self.requires_grad)
        out._prev = {self}

        def _grad_fn(gradient):
            if self.requires_grad:
                self.backward(gradient * power * self.data ** (power - 1))

        out._grad_fn = _grad_fn
        return out

    def __repr__(self):
        return f"Tensor(shape={self.data.shape}, requires_grad={self.requires_grad})"

Attributes

shape property

Return the shape of the tensor.

ndim property

Return the number of dimensions.

Functions

__init__(data, requires_grad=False)

Initialize a Tensor.

Parameters:

Name Type Description Default
data Union[List, ndarray, Any]

Array-like data (list, numpy array, etc.)

required
requires_grad bool

Whether to track gradients

False
Source code in neurogebra/core/autograd.py
def __init__(self, data: Union[List, np.ndarray, Any], requires_grad: bool = False):
    """
    Initialize a Tensor.

    Args:
        data: Array-like data (list, numpy array, etc.)
        requires_grad: Whether to track gradients
    """
    self.data = np.array(data, dtype=np.float64)
    self.grad: Optional[np.ndarray] = None
    self.requires_grad = requires_grad
    self._grad_fn = None
    self._prev: Set["Tensor"] = set()

    if requires_grad:
        self.grad = np.zeros_like(self.data)

backward(gradient=None)

Compute gradients via backpropagation.

Parameters:

Name Type Description Default
gradient Optional[ndarray]

Upstream gradient. If None, uses ones.

None
Source code in neurogebra/core/autograd.py
def backward(self, gradient: Optional[np.ndarray] = None):
    """
    Compute gradients via backpropagation.

    Args:
        gradient: Upstream gradient. If None, uses ones.
    """
    if not self.requires_grad:
        return

    if gradient is None:
        gradient = np.ones_like(self.data)

    if self.grad is not None:
        self.grad += gradient
    else:
        self.grad = gradient.copy()

    if self._grad_fn is not None:
        self._grad_fn(gradient)

zero_grad()

Reset gradient to zero.

Source code in neurogebra/core/autograd.py
def zero_grad(self):
    """Reset gradient to zero."""
    if self.requires_grad:
        self.grad = np.zeros_like(self.data)

sum()

Sum all elements.

Source code in neurogebra/core/autograd.py
def sum(self) -> "Tensor":
    """Sum all elements."""
    out = Tensor(np.sum(self.data), requires_grad=self.requires_grad)
    out._prev = {self}

    def _grad_fn(gradient):
        self.backward(gradient * np.ones_like(self.data))

    out._grad_fn = _grad_fn
    return out

mean()

Mean of all elements.

Source code in neurogebra/core/autograd.py
def mean(self) -> "Tensor":
    """Mean of all elements."""
    n = self.data.size
    out = Tensor(np.mean(self.data), requires_grad=self.requires_grad)
    out._prev = {self}

    def _grad_fn(gradient):
        self.backward(gradient * np.ones_like(self.data) / n)

    out._grad_fn = _grad_fn
    return out

__add__(other)

Element-wise addition.

Source code in neurogebra/core/autograd.py
def __add__(self, other):
    """Element-wise addition."""
    if isinstance(other, Tensor):
        out = Tensor(
            self.data + other.data,
            requires_grad=self.requires_grad or other.requires_grad,
        )
        out._prev = {self, other}

        def _grad_fn(gradient):
            if self.requires_grad:
                self.backward(gradient)
            if other.requires_grad:
                other.backward(gradient)

        out._grad_fn = _grad_fn
    else:
        out = Tensor(self.data + other, requires_grad=self.requires_grad)
        out._prev = {self}

        def _grad_fn(gradient):
            if self.requires_grad:
                self.backward(gradient)

        out._grad_fn = _grad_fn

    return out

__mul__(other)

Element-wise multiplication.

Source code in neurogebra/core/autograd.py
def __mul__(self, other):
    """Element-wise multiplication."""
    if isinstance(other, Tensor):
        out = Tensor(
            self.data * other.data,
            requires_grad=self.requires_grad or other.requires_grad,
        )
        out._prev = {self, other}

        def _grad_fn(gradient):
            if self.requires_grad:
                self.backward(gradient * other.data)
            if other.requires_grad:
                other.backward(gradient * self.data)

        out._grad_fn = _grad_fn
    else:
        out = Tensor(self.data * other, requires_grad=self.requires_grad)
        out._prev = {self}

        def _grad_fn(gradient):
            if self.requires_grad:
                self.backward(gradient * other)

        out._grad_fn = _grad_fn

    return out

__sub__(other)

Element-wise subtraction.

Source code in neurogebra/core/autograd.py
def __sub__(self, other):
    """Element-wise subtraction."""
    if isinstance(other, Tensor):
        return self + Tensor(-other.data, requires_grad=other.requires_grad)
    return self + (-other)

__neg__()

Negate tensor.

Source code in neurogebra/core/autograd.py
def __neg__(self):
    """Negate tensor."""
    return self * (-1)

__pow__(power)

Element-wise power.

Source code in neurogebra/core/autograd.py
def __pow__(self, power):
    """Element-wise power."""
    out = Tensor(self.data**power, requires_grad=self.requires_grad)
    out._prev = {self}

    def _grad_fn(gradient):
        if self.requires_grad:
            self.backward(gradient * power * self.data ** (power - 1))

    out._grad_fn = _grad_fn
    return out

Builders

ModelBuilder

Keras-like interface for building neural network architectures layer by layer.

neurogebra.builders.model_builder.ModelBuilder

Build neural networks with an educational, intuitive interface.

ModelBuilder makes it easy for beginners to: - Understand what they're building - Get guidance on architecture choices - See what each layer does - Learn best practices

Examples:

>>> from neurogebra.builders import ModelBuilder
>>> builder = ModelBuilder()
>>> model = builder.Sequential([
...     builder.Dense(128, activation="relu"),
...     builder.Dropout(0.2),
...     builder.Dense(10, activation="softmax")
... ])
>>> model.summary()
Source code in neurogebra/builders/model_builder.py
class ModelBuilder:
    """
    Build neural networks with an educational, intuitive interface.

    ModelBuilder makes it easy for beginners to:
    - Understand what they're building
    - Get guidance on architecture choices
    - See what each layer does
    - Learn best practices

    Examples:
        >>> from neurogebra.builders import ModelBuilder
        >>> builder = ModelBuilder()
        >>> model = builder.Sequential([
        ...     builder.Dense(128, activation="relu"),
        ...     builder.Dropout(0.2),
        ...     builder.Dense(10, activation="softmax")
        ... ])
        >>> model.summary()
    """

    def __init__(self, craft: Optional[Any] = None):
        """
        Initialize ModelBuilder.

        Args:
            craft: NeuroCraft instance for expression access.
                   If None, a default one is created internally.
        """
        self.craft = craft
        self._templates = self._load_templates()

    def _get_craft(self):
        """Lazily initialize craft if needed."""
        if self.craft is None:
            from neurogebra.core.neurocraft import NeuroCraft

            self.craft = NeuroCraft(educational_mode=False)
        return self.craft

    def _load_templates(self) -> Dict[str, Dict[str, Any]]:
        """Load pre-built model templates for common tasks."""
        return {
            "simple_classifier": {
                "description": "Basic neural network for classification",
                "layers": [
                    {"type": "dense", "units": 128, "activation": "relu"},
                    {"type": "dropout", "rate": 0.2},
                    {"type": "dense", "units": 64, "activation": "relu"},
                    {"type": "dense", "units": 10, "activation": "softmax"},
                ],
                "good_for": [
                    "MNIST",
                    "CIFAR-10",
                    "Simple tabular data",
                ],
            },
            "image_classifier": {
                "description": "CNN for image classification",
                "layers": [
                    {
                        "type": "conv2d",
                        "filters": 32,
                        "kernel_size": 3,
                        "activation": "relu",
                    },
                    {"type": "maxpool2d", "pool_size": 2},
                    {
                        "type": "conv2d",
                        "filters": 64,
                        "kernel_size": 3,
                        "activation": "relu",
                    },
                    {"type": "maxpool2d", "pool_size": 2},
                    {"type": "flatten"},
                    {"type": "dense", "units": 128, "activation": "relu"},
                    {"type": "dense", "units": 10, "activation": "softmax"},
                ],
                "good_for": [
                    "Image recognition",
                    "Object classification",
                ],
            },
            "regression": {
                "description": "Network for predicting continuous values",
                "layers": [
                    {"type": "dense", "units": 64, "activation": "relu"},
                    {"type": "dense", "units": 32, "activation": "relu"},
                    {"type": "dense", "units": 1, "activation": "linear"},
                ],
                "good_for": [
                    "House price prediction",
                    "Stock prices",
                    "Any regression task",
                ],
            },
            "binary_classifier": {
                "description": "Network for binary yes/no classification",
                "layers": [
                    {"type": "dense", "units": 64, "activation": "relu"},
                    {"type": "dropout", "rate": 0.3},
                    {"type": "dense", "units": 32, "activation": "relu"},
                    {"type": "dense", "units": 1, "activation": "sigmoid"},
                ],
                "good_for": [
                    "Spam detection",
                    "Medical diagnosis",
                    "Sentiment analysis",
                ],
            },
        }

    # ---- Layer creation methods ----

    def Dense(
        self,
        units: int,
        activation: Optional[str] = None,
        input_shape: Optional[tuple] = None,
        **kwargs,
    ) -> Layer:
        """
        Create a fully connected (dense) layer.

        Args:
            units: Number of neurons
            activation: Activation function name
            input_shape: Shape of input (only for first layer)

        Returns:
            Layer instance

        Examples:
            >>> builder = ModelBuilder()
            >>> layer = builder.Dense(128, activation="relu")
            >>> layer.explain()  # Learn what it does
        """
        extra = {**kwargs}
        if input_shape is not None:
            extra["input_shape"] = input_shape
        return Layer("dense", units=units, activation=activation, **extra)

    def Conv2D(
        self,
        filters: int,
        kernel_size: int = 3,
        activation: Optional[str] = None,
        **kwargs,
    ) -> Layer:
        """
        Create a 2D convolutional layer for images.

        Args:
            filters: Number of filters / feature detectors
            kernel_size: Size of the filter window
            activation: Activation function

        Returns:
            Layer instance
        """
        return Layer(
            "conv2d",
            units=filters,
            activation=activation,
            kernel_size=kernel_size,
            **kwargs,
        )

    def Dropout(self, rate: float = 0.2) -> Layer:
        """
        Create a dropout layer for regularization.

        Args:
            rate: Fraction of neurons to drop (0.0 to 1.0)

        Returns:
            Layer instance
        """
        return Layer("dropout", rate=rate)

    def BatchNorm(self) -> Layer:
        """Create a batch normalization layer."""
        return Layer("batchnorm")

    def MaxPooling2D(self, pool_size: int = 2) -> Layer:
        """Create a max pooling layer."""
        return Layer("maxpool2d", pool_size=pool_size)

    def Flatten(self) -> Layer:
        """Create a flatten layer."""
        return Layer("flatten")

    # ---- Model construction ----

    def Sequential(
        self, layers: List[Layer], name: Optional[str] = None
    ) -> "Model":
        """
        Build a sequential model (layers stacked one after another).

        Args:
            layers: List of Layer instances
            name: Optional name for the model

        Returns:
            Model instance ready for compilation and training

        Examples:
            >>> builder = ModelBuilder()
            >>> model = builder.Sequential([
            ...     builder.Dense(128, activation="relu"),
            ...     builder.Dropout(0.2),
            ...     builder.Dense(10, activation="softmax")
            ... ])
            >>> model.summary()
        """
        return Model(layers=layers, name=name, craft=self._get_craft())

    def from_template(
        self,
        template_name: str,
        customize: Optional[Dict] = None,
    ) -> "Model":
        """
        Create a model from a pre-built template.

        Args:
            template_name: Name of template (e.g. 'simple_classifier')
            customize: Optional customizations (not yet implemented)

        Returns:
            Model instance

        Examples:
            >>> builder = ModelBuilder()
            >>> model = builder.from_template("simple_classifier")
            >>> model.explain_architecture()
        """
        if template_name not in self._templates:
            available = ", ".join(self._templates.keys())
            raise ValueError(
                f"Template '{template_name}' not found.\n"
                f"Available templates: {available}\n"
                f"Use builder.list_templates() to see details."
            )

        template = self._templates[template_name]

        # Deep-copy so repeated calls don't mutate the template
        layer_specs = copy.deepcopy(template["layers"])

        layers = []
        for layer_spec in layer_specs:
            layer_type = layer_spec.pop("type")

            if layer_type == "dense":
                layers.append(self.Dense(**layer_spec))
            elif layer_type == "conv2d":
                layers.append(self.Conv2D(**layer_spec))
            elif layer_type == "dropout":
                layers.append(self.Dropout(**layer_spec))
            elif layer_type == "maxpool2d":
                layers.append(self.MaxPooling2D(**layer_spec))
            elif layer_type == "flatten":
                layers.append(self.Flatten())
            elif layer_type == "batchnorm":
                layers.append(self.BatchNorm())

        model = self.Sequential(layers, name=template_name)
        model.template_info = template

        return model

    def list_templates(self):
        """Show all available model templates with descriptions."""
        print("\n🏗️  Available Model Templates:\n")

        for name, template in self._templates.items():
            print(f"  📦 {name}")
            print(f"     {template['description']}")
            print(f"     Good for: {', '.join(template['good_for'])}")
            print()

    def suggest_architecture(
        self,
        task: str,
        input_shape: tuple,
        output_size: int,
    ):
        """
        Get architecture suggestions based on your task.

        Args:
            task: 'classification', 'regression', 'image_classification', etc.
            input_shape: Shape of your input data
            output_size: Number of outputs

        Examples:
            >>> builder = ModelBuilder()
            >>> builder.suggest_architecture(
            ...     task="image_classification",
            ...     input_shape=(28, 28, 1),
            ...     output_size=10
            ... )
        """
        print(f"\n💡 Architecture Suggestions for {task}:\n")

        if task in ("classification", "image_classification"):
            if len(input_shape) == 3:
                print("   Recommended: Convolutional Neural Network (CNN)")
                print(
                    "   Why: CNNs are designed to detect patterns in images"
                )
                print("\n   Suggested architecture:")
                print("   1. Conv2D(32, kernel_size=3) + ReLU")
                print("   2. MaxPooling2D()")
                print("   3. Conv2D(64, kernel_size=3) + ReLU")
                print("   4. MaxPooling2D()")
                print("   5. Flatten()")
                print("   6. Dense(128) + ReLU")
                print("   7. Dropout(0.3)")
                print(f"   8. Dense({output_size}) + Softmax")
                print("\n   Use: builder.from_template('image_classifier')")
            else:
                print("   Recommended: Simple Feedforward Network")
                print("   Why: Efficient for structured/tabular data")
                print("\n   Suggested architecture:")
                print("   1. Dense(128) + ReLU")
                print("   2. Dropout(0.2)")
                print("   3. Dense(64) + ReLU")
                print(f"   4. Dense({output_size}) + Softmax")
                print(
                    "\n   Use: builder.from_template('simple_classifier')"
                )

        elif task == "regression":
            print("   Recommended: Regression Network")
            print("   Why: Predicts continuous values")
            print("\n   Suggested architecture:")
            print("   1. Dense(64) + ReLU")
            print("   2. Dense(32) + ReLU")
            print("   3. Dense(1) + Linear (no activation)")
            print("\n   Use: builder.from_template('regression')")

        elif task == "binary_classification":
            print("   Recommended: Binary Classifier")
            print("   Why: Two-class output with sigmoid")
            print("\n   Suggested architecture:")
            print("   1. Dense(64) + ReLU")
            print("   2. Dropout(0.3)")
            print("   3. Dense(32) + ReLU")
            print("   4. Dense(1) + Sigmoid")
            print(
                "\n   Use: builder.from_template('binary_classifier')"
            )

        print(
            "\n   💡 Tip: Start with these suggestions, then experiment!"
        )

Functions

__init__(craft=None)

Initialize ModelBuilder.

Parameters:

Name Type Description Default
craft Optional[Any]

NeuroCraft instance for expression access. If None, a default one is created internally.

None
Source code in neurogebra/builders/model_builder.py
def __init__(self, craft: Optional[Any] = None):
    """
    Initialize ModelBuilder.

    Args:
        craft: NeuroCraft instance for expression access.
               If None, a default one is created internally.
    """
    self.craft = craft
    self._templates = self._load_templates()

Dense(units, activation=None, input_shape=None, **kwargs)

Create a fully connected (dense) layer.

Parameters:

Name Type Description Default
units int

Number of neurons

required
activation Optional[str]

Activation function name

None
input_shape Optional[tuple]

Shape of input (only for first layer)

None

Returns:

Type Description
Layer

Layer instance

Examples:

>>> builder = ModelBuilder()
>>> layer = builder.Dense(128, activation="relu")
>>> layer.explain()  # Learn what it does
Source code in neurogebra/builders/model_builder.py
def Dense(
    self,
    units: int,
    activation: Optional[str] = None,
    input_shape: Optional[tuple] = None,
    **kwargs,
) -> Layer:
    """
    Create a fully connected (dense) layer.

    Args:
        units: Number of neurons
        activation: Activation function name
        input_shape: Shape of input (only for first layer)

    Returns:
        Layer instance

    Examples:
        >>> builder = ModelBuilder()
        >>> layer = builder.Dense(128, activation="relu")
        >>> layer.explain()  # Learn what it does
    """
    extra = {**kwargs}
    if input_shape is not None:
        extra["input_shape"] = input_shape
    return Layer("dense", units=units, activation=activation, **extra)

Conv2D(filters, kernel_size=3, activation=None, **kwargs)

Create a 2D convolutional layer for images.

Parameters:

Name Type Description Default
filters int

Number of filters / feature detectors

required
kernel_size int

Size of the filter window

3
activation Optional[str]

Activation function

None

Returns:

Type Description
Layer

Layer instance

Source code in neurogebra/builders/model_builder.py
def Conv2D(
    self,
    filters: int,
    kernel_size: int = 3,
    activation: Optional[str] = None,
    **kwargs,
) -> Layer:
    """
    Create a 2D convolutional layer for images.

    Args:
        filters: Number of filters / feature detectors
        kernel_size: Size of the filter window
        activation: Activation function

    Returns:
        Layer instance
    """
    return Layer(
        "conv2d",
        units=filters,
        activation=activation,
        kernel_size=kernel_size,
        **kwargs,
    )

Dropout(rate=0.2)

Create a dropout layer for regularization.

Parameters:

Name Type Description Default
rate float

Fraction of neurons to drop (0.0 to 1.0)

0.2

Returns:

Type Description
Layer

Layer instance

Source code in neurogebra/builders/model_builder.py
def Dropout(self, rate: float = 0.2) -> Layer:
    """
    Create a dropout layer for regularization.

    Args:
        rate: Fraction of neurons to drop (0.0 to 1.0)

    Returns:
        Layer instance
    """
    return Layer("dropout", rate=rate)

BatchNorm()

Create a batch normalization layer.

Source code in neurogebra/builders/model_builder.py
def BatchNorm(self) -> Layer:
    """Create a batch normalization layer."""
    return Layer("batchnorm")

MaxPooling2D(pool_size=2)

Create a max pooling layer.

Source code in neurogebra/builders/model_builder.py
def MaxPooling2D(self, pool_size: int = 2) -> Layer:
    """Create a max pooling layer."""
    return Layer("maxpool2d", pool_size=pool_size)

Flatten()

Create a flatten layer.

Source code in neurogebra/builders/model_builder.py
def Flatten(self) -> Layer:
    """Create a flatten layer."""
    return Layer("flatten")

Sequential(layers, name=None)

Build a sequential model (layers stacked one after another).

Parameters:

Name Type Description Default
layers List[Layer]

List of Layer instances

required
name Optional[str]

Optional name for the model

None

Returns:

Type Description
'Model'

Model instance ready for compilation and training

Examples:

>>> builder = ModelBuilder()
>>> model = builder.Sequential([
...     builder.Dense(128, activation="relu"),
...     builder.Dropout(0.2),
...     builder.Dense(10, activation="softmax")
... ])
>>> model.summary()
Source code in neurogebra/builders/model_builder.py
def Sequential(
    self, layers: List[Layer], name: Optional[str] = None
) -> "Model":
    """
    Build a sequential model (layers stacked one after another).

    Args:
        layers: List of Layer instances
        name: Optional name for the model

    Returns:
        Model instance ready for compilation and training

    Examples:
        >>> builder = ModelBuilder()
        >>> model = builder.Sequential([
        ...     builder.Dense(128, activation="relu"),
        ...     builder.Dropout(0.2),
        ...     builder.Dense(10, activation="softmax")
        ... ])
        >>> model.summary()
    """
    return Model(layers=layers, name=name, craft=self._get_craft())

from_template(template_name, customize=None)

Create a model from a pre-built template.

Parameters:

Name Type Description Default
template_name str

Name of template (e.g. 'simple_classifier')

required
customize Optional[Dict]

Optional customizations (not yet implemented)

None

Returns:

Type Description
'Model'

Model instance

Examples:

>>> builder = ModelBuilder()
>>> model = builder.from_template("simple_classifier")
>>> model.explain_architecture()
Source code in neurogebra/builders/model_builder.py
def from_template(
    self,
    template_name: str,
    customize: Optional[Dict] = None,
) -> "Model":
    """
    Create a model from a pre-built template.

    Args:
        template_name: Name of template (e.g. 'simple_classifier')
        customize: Optional customizations (not yet implemented)

    Returns:
        Model instance

    Examples:
        >>> builder = ModelBuilder()
        >>> model = builder.from_template("simple_classifier")
        >>> model.explain_architecture()
    """
    if template_name not in self._templates:
        available = ", ".join(self._templates.keys())
        raise ValueError(
            f"Template '{template_name}' not found.\n"
            f"Available templates: {available}\n"
            f"Use builder.list_templates() to see details."
        )

    template = self._templates[template_name]

    # Deep-copy so repeated calls don't mutate the template
    layer_specs = copy.deepcopy(template["layers"])

    layers = []
    for layer_spec in layer_specs:
        layer_type = layer_spec.pop("type")

        if layer_type == "dense":
            layers.append(self.Dense(**layer_spec))
        elif layer_type == "conv2d":
            layers.append(self.Conv2D(**layer_spec))
        elif layer_type == "dropout":
            layers.append(self.Dropout(**layer_spec))
        elif layer_type == "maxpool2d":
            layers.append(self.MaxPooling2D(**layer_spec))
        elif layer_type == "flatten":
            layers.append(self.Flatten())
        elif layer_type == "batchnorm":
            layers.append(self.BatchNorm())

    model = self.Sequential(layers, name=template_name)
    model.template_info = template

    return model

list_templates()

Show all available model templates with descriptions.

Source code in neurogebra/builders/model_builder.py
def list_templates(self):
    """Show all available model templates with descriptions."""
    print("\n🏗️  Available Model Templates:\n")

    for name, template in self._templates.items():
        print(f"  📦 {name}")
        print(f"     {template['description']}")
        print(f"     Good for: {', '.join(template['good_for'])}")
        print()

suggest_architecture(task, input_shape, output_size)

Get architecture suggestions based on your task.

Parameters:

Name Type Description Default
task str

'classification', 'regression', 'image_classification', etc.

required
input_shape tuple

Shape of your input data

required
output_size int

Number of outputs

required

Examples:

>>> builder = ModelBuilder()
>>> builder.suggest_architecture(
...     task="image_classification",
...     input_shape=(28, 28, 1),
...     output_size=10
... )
Source code in neurogebra/builders/model_builder.py
def suggest_architecture(
    self,
    task: str,
    input_shape: tuple,
    output_size: int,
):
    """
    Get architecture suggestions based on your task.

    Args:
        task: 'classification', 'regression', 'image_classification', etc.
        input_shape: Shape of your input data
        output_size: Number of outputs

    Examples:
        >>> builder = ModelBuilder()
        >>> builder.suggest_architecture(
        ...     task="image_classification",
        ...     input_shape=(28, 28, 1),
        ...     output_size=10
        ... )
    """
    print(f"\n💡 Architecture Suggestions for {task}:\n")

    if task in ("classification", "image_classification"):
        if len(input_shape) == 3:
            print("   Recommended: Convolutional Neural Network (CNN)")
            print(
                "   Why: CNNs are designed to detect patterns in images"
            )
            print("\n   Suggested architecture:")
            print("   1. Conv2D(32, kernel_size=3) + ReLU")
            print("   2. MaxPooling2D()")
            print("   3. Conv2D(64, kernel_size=3) + ReLU")
            print("   4. MaxPooling2D()")
            print("   5. Flatten()")
            print("   6. Dense(128) + ReLU")
            print("   7. Dropout(0.3)")
            print(f"   8. Dense({output_size}) + Softmax")
            print("\n   Use: builder.from_template('image_classifier')")
        else:
            print("   Recommended: Simple Feedforward Network")
            print("   Why: Efficient for structured/tabular data")
            print("\n   Suggested architecture:")
            print("   1. Dense(128) + ReLU")
            print("   2. Dropout(0.2)")
            print("   3. Dense(64) + ReLU")
            print(f"   4. Dense({output_size}) + Softmax")
            print(
                "\n   Use: builder.from_template('simple_classifier')"
            )

    elif task == "regression":
        print("   Recommended: Regression Network")
        print("   Why: Predicts continuous values")
        print("\n   Suggested architecture:")
        print("   1. Dense(64) + ReLU")
        print("   2. Dense(32) + ReLU")
        print("   3. Dense(1) + Linear (no activation)")
        print("\n   Use: builder.from_template('regression')")

    elif task == "binary_classification":
        print("   Recommended: Binary Classifier")
        print("   Why: Two-class output with sigmoid")
        print("\n   Suggested architecture:")
        print("   1. Dense(64) + ReLU")
        print("   2. Dropout(0.3)")
        print("   3. Dense(32) + ReLU")
        print("   4. Dense(1) + Sigmoid")
        print(
            "\n   Use: builder.from_template('binary_classifier')"
        )

    print(
        "\n   💡 Tip: Start with these suggestions, then experiment!"
    )

Repository

Pre-built expression collections organized by category.

Activations

neurogebra.repository.activations.get_activations()

Get dictionary of activation function expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/activations.py
def get_activations() -> Dict[str, Expression]:
    """
    Get dictionary of activation function expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    acts: Dict[str, Expression] = {}

    # ReLU
    acts["relu"] = Expression(
        name="relu",
        symbolic_expr="Max(0, x)",
        metadata={
            "category": "activation",
            "description": "Rectified Linear Unit - outputs x if x > 0, else 0",
            "usage": "Most common activation for hidden layers",
            "pros": ["Fast computation", "No vanishing gradient for positive values"],
            "cons": ["Dead neurons for negative values", "Not zero-centered"],
        },
    )

    # Sigmoid
    acts["sigmoid"] = Expression(
        name="sigmoid",
        symbolic_expr="1 / (1 + exp(-x))",
        metadata={
            "category": "activation",
            "description": "Sigmoid function - maps input to (0, 1)",
            "usage": "Binary classification output layer",
            "pros": ["Smooth gradient", "Bounded output (0, 1)"],
            "cons": ["Vanishing gradient", "Not zero-centered", "Expensive exp()"],
        },
    )

    # Tanh
    acts["tanh"] = Expression(
        name="tanh",
        symbolic_expr="tanh(x)",
        metadata={
            "category": "activation",
            "description": "Hyperbolic tangent - maps input to (-1, 1)",
            "usage": "Hidden layers when zero-centered outputs preferred",
            "pros": ["Zero-centered", "Smooth gradient"],
            "cons": ["Vanishing gradient for large values"],
        },
    )

    # Leaky ReLU
    acts["leaky_relu"] = Expression(
        name="leaky_relu",
        symbolic_expr="Max(alpha*x, x)",
        params={"alpha": 0.01},
        metadata={
            "category": "activation",
            "description": "ReLU with small slope for negative values",
            "usage": "Alternative to ReLU to prevent dead neurons",
            "pros": ["Prevents dead neurons", "Fast computation"],
            "cons": ["Inconsistent predictions for negative values"],
        },
    )

    # Swish (SiLU)
    acts["swish"] = Expression(
        name="swish",
        symbolic_expr="x / (1 + exp(-x))",
        metadata={
            "category": "activation",
            "description": "Self-gated activation function (x * sigmoid(x))",
            "usage": "Modern deep networks, often outperforms ReLU",
            "pros": ["Smooth", "Non-monotonic", "Self-gating property"],
            "cons": ["More expensive than ReLU"],
        },
    )

    # GELU
    acts["gelu"] = Expression(
        name="gelu",
        symbolic_expr="0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715*x**3)))",
        metadata={
            "category": "activation",
            "description": "Gaussian Error Linear Unit",
            "usage": "Transformers, BERT, and GPT models",
            "pros": ["Smooth", "Probabilistic interpretation", "State-of-the-art"],
            "cons": ["Expensive computation"],
        },
    )

    # Softplus
    acts["softplus"] = Expression(
        name="softplus",
        symbolic_expr="log(1 + exp(x))",
        metadata={
            "category": "activation",
            "description": "Smooth approximation of ReLU",
            "usage": "When a smooth differentiable version of ReLU is needed",
            "pros": ["Smooth everywhere", "Always positive output"],
            "cons": ["Slower than ReLU"],
        },
    )

    # ELU
    acts["elu"] = Expression(
        name="elu",
        symbolic_expr="Piecewise((x, x > 0), (alpha*(exp(x) - 1), True))",
        params={"alpha": 1.0},
        metadata={
            "category": "activation",
            "description": "Exponential Linear Unit",
            "usage": "When negative outputs improve robustness",
            "pros": ["Zero-centered for negative inputs", "Smooth"],
            "cons": ["Expensive exp() for negative values"],
        },
    )

    # SELU (Scaled ELU)
    acts["selu"] = Expression(
        name="selu",
        symbolic_expr=(
            "1.0507009873554805 * "
            "Piecewise((x, x > 0), "
            "(1.6732632423543772*(exp(x) - 1), True))"
        ),
        metadata={
            "category": "activation",
            "description": "Scaled Exponential Linear Unit - self-normalizing",
            "usage": "Self-normalizing neural networks (SNNs)",
            "pros": ["Self-normalizing", "Avoids vanishing/exploding gradients"],
            "cons": ["Only effective with specific architectures"],
        },
    )

    # Mish
    acts["mish"] = Expression(
        name="mish",
        symbolic_expr="x * tanh(log(1 + exp(x)))",
        metadata={
            "category": "activation",
            "description": "Mish activation - smooth, non-monotonic",
            "usage": "Computer vision models, YOLOv4",
            "pros": ["Smooth", "Non-monotonic", "Unbounded above"],
            "cons": ["Computationally expensive"],
        },
    )

    # Hard Sigmoid
    acts["hard_sigmoid"] = Expression(
        name="hard_sigmoid",
        symbolic_expr="Max(0, Min(1, (x + 3) / 6))",
        metadata={
            "category": "activation",
            "description": "Piecewise linear approximation of sigmoid",
            "usage": "Mobile/embedded models for efficiency",
            "pros": ["Very fast", "Good approximation of sigmoid"],
            "cons": ["Not smooth at transitions"],
        },
    )

    # Hard Swish
    acts["hard_swish"] = Expression(
        name="hard_swish",
        symbolic_expr="x * Max(0, Min(1, (x + 3) / 6))",
        metadata={
            "category": "activation",
            "description": "Efficient approximation of Swish",
            "usage": "MobileNetV3 and efficient mobile architectures",
            "pros": ["Fast", "Good approximation of Swish"],
            "cons": ["Not smooth at transitions"],
        },
    )

    # Softsign
    acts["softsign"] = Expression(
        name="softsign",
        symbolic_expr="x / (1 + Abs(x))",
        metadata={
            "category": "activation",
            "description": "Maps input to (-1, 1), similar to tanh but lighter tails",
            "usage": "Alternative to tanh for lighter-tail distribution",
            "pros": ["Lighter tails than tanh", "Smooth"],
            "cons": ["Slower convergence than tanh"],
        },
    )

    # Identity / Linear
    acts["linear"] = Expression(
        name="linear",
        symbolic_expr="x",
        metadata={
            "category": "activation",
            "description": "Identity/linear activation - no transformation",
            "usage": "Output layer for regression tasks",
            "pros": ["Simple", "No information loss"],
            "cons": ["No nonlinearity"],
        },
    )

    # Square
    acts["square"] = Expression(
        name="square",
        symbolic_expr="x**2",
        metadata={
            "category": "activation",
            "description": "Square activation function",
            "usage": "Polynomial networks, kernel approximation",
            "pros": ["Simple nonlinearity"],
            "cons": ["Unbounded", "Not monotonic"],
        },
    )

    return acts

Losses

neurogebra.repository.losses.get_losses()

Get dictionary of loss function expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/losses.py
def get_losses() -> Dict[str, Expression]:
    """
    Get dictionary of loss function expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    loss_fns: Dict[str, Expression] = {}

    # Mean Squared Error
    loss_fns["mse"] = Expression(
        name="mse",
        symbolic_expr="(y_pred - y_true)**2",
        metadata={
            "category": "loss",
            "description": "Mean Squared Error - standard regression loss",
            "usage": "Regression tasks",
            "pros": ["Smooth and convex", "Well-understood gradients"],
            "cons": ["Sensitive to outliers"],
        },
    )

    # Mean Absolute Error
    loss_fns["mae"] = Expression(
        name="mae",
        symbolic_expr="Abs(y_pred - y_true)",
        metadata={
            "category": "loss",
            "description": "Mean Absolute Error - robust regression loss",
            "usage": "Regression with outliers",
            "pros": ["Robust to outliers", "Interpretable"],
            "cons": ["Not differentiable at zero", "Slower convergence"],
        },
    )

    # Binary Cross-Entropy
    loss_fns["binary_crossentropy"] = Expression(
        name="binary_crossentropy",
        symbolic_expr="-y_true*log(y_pred) - (1-y_true)*log(1-y_pred)",
        metadata={
            "category": "loss",
            "description": "Binary cross-entropy for binary classification",
            "usage": "Binary classification with sigmoid output",
            "pros": ["Probabilistic interpretation", "Strong gradients"],
            "cons": ["Requires probability inputs (0, 1)"],
        },
    )

    # Huber Loss
    loss_fns["huber"] = Expression(
        name="huber",
        symbolic_expr=(
            "Piecewise("
            "(0.5*(y_pred - y_true)**2, Abs(y_pred - y_true) <= delta), "
            "(delta*Abs(y_pred - y_true) - 0.5*delta**2, True)"
            ")"
        ),
        params={"delta": 1.0},
        metadata={
            "category": "loss",
            "description": "Huber loss - MSE for small errors, MAE for large errors",
            "usage": "Robust regression, reinforcement learning",
            "pros": ["Best of MSE and MAE", "Differentiable everywhere"],
            "cons": ["Extra hyperparameter (delta)"],
        },
    )

    # Log-Cosh Loss
    loss_fns["log_cosh"] = Expression(
        name="log_cosh",
        symbolic_expr="log(cosh(y_pred - y_true))",
        metadata={
            "category": "loss",
            "description": "Logarithm of hyperbolic cosine loss",
            "usage": "Smooth alternative to Huber loss",
            "pros": ["Smooth", "Approximately MSE for small errors"],
            "cons": ["Less commonly used"],
        },
    )

    # Hinge Loss
    loss_fns["hinge"] = Expression(
        name="hinge",
        symbolic_expr="Max(0, 1 - y_true * y_pred)",
        metadata={
            "category": "loss",
            "description": "Hinge loss for SVM-style classification",
            "usage": "Support vector machines, maximum margin classifiers",
            "pros": ["Encourages margin", "Sparse solutions"],
            "cons": ["Not differentiable at hinge point"],
        },
    )

    # Squared Hinge Loss
    loss_fns["squared_hinge"] = Expression(
        name="squared_hinge",
        symbolic_expr="Max(0, 1 - y_true * y_pred)**2",
        metadata={
            "category": "loss",
            "description": "Squared hinge loss - smoother version of hinge",
            "usage": "Smooth SVM classification",
            "pros": ["Smooth", "Differentiable"],
            "cons": ["Penalizes outliers more"],
        },
    )

    # Quantile Loss
    loss_fns["quantile"] = Expression(
        name="quantile",
        symbolic_expr=(
            "Piecewise("
            "(q * (y_true - y_pred), y_true >= y_pred), "
            "((1 - q) * (y_pred - y_true), True)"
            ")"
        ),
        params={"q": 0.5},
        metadata={
            "category": "loss",
            "description": "Quantile loss for quantile regression",
            "usage": "Predicting specific quantiles of distribution",
            "pros": ["Asymmetric penalty", "Quantile estimation"],
            "cons": ["Not differentiable at zero"],
        },
    )

    return loss_fns

Regularizers

neurogebra.repository.regularizers.get_regularizers()

Get dictionary of regularization expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/regularizers.py
def get_regularizers() -> Dict[str, Expression]:
    """
    Get dictionary of regularization expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    regs: Dict[str, Expression] = {}

    # ----------------------------------------------------------------
    # Classic Norm-Based Regularizers
    # ----------------------------------------------------------------

    regs["l1"] = Expression(
        name="l1",
        symbolic_expr="lambda_reg * Abs(w)",
        params={"lambda_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "L1 regularization (Lasso) - promotes sparsity",
            "usage": "Feature selection, sparse models",
            "pros": ["Promotes sparsity", "Feature selection"],
            "cons": ["Not differentiable at zero"],
            "formula_latex": r"\lambda |w|",
        },
    )

    regs["l2"] = Expression(
        name="l2",
        symbolic_expr="lambda_reg * w**2",
        params={"lambda_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "L2 regularization (Ridge/Weight Decay)",
            "usage": "Prevent overfitting, weight decay",
            "pros": ["Smooth", "Well-understood", "Convex"],
            "cons": ["Does not promote sparsity"],
            "formula_latex": r"\lambda w^2",
        },
    )

    regs["elastic_net"] = Expression(
        name="elastic_net",
        symbolic_expr=(
            "alpha * lambda_reg * Abs(w) "
            "+ (1 - alpha) * lambda_reg * w**2"
        ),
        params={"lambda_reg": 0.01, "alpha": 0.5},
        metadata={
            "category": "regularizer",
            "description": "Elastic Net - combination of L1 and L2",
            "usage": "When both sparsity and grouping are desired",
            "pros": ["Combines L1 and L2 benefits"],
            "cons": ["Extra hyperparameter"],
        },
    )

    regs["weight_decay"] = Expression(
        name="weight_decay",
        symbolic_expr="0.5 * lambda_reg * w**2",
        params={"lambda_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "Weight decay - direct weight shrinkage (½λw²)",
            "usage": "AdamW optimizer, modern training",
            "pros": ["Decoupled from loss", "Stable training"],
            "cons": ["Equivalent to L2 for SGD"],
        },
    )

    regs["l1_l2"] = Expression(
        name="l1_l2",
        symbolic_expr="l1_reg * Abs(w) + l2_reg * w**2",
        params={"l1_reg": 0.01, "l2_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "Independent L1 + L2 with separate coefficients",
            "usage": "Fine-grained control over sparsity & smoothness",
            "pros": ["Independent tuning of each term"],
            "cons": ["Two hyperparameters"],
        },
    )

    # ----------------------------------------------------------------
    # Smooth Sparsity Regularizers
    # ----------------------------------------------------------------

    regs["log_barrier"] = Expression(
        name="log_barrier",
        symbolic_expr="-lambda_reg * log(1 - w**2 + epsilon)",
        params={"lambda_reg": 0.01, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "Log-barrier penalty - keeps weights bounded",
            "usage": "Constrained optimization, bounded weights",
            "pros": ["Smooth", "Enforces weight bounds"],
            "cons": ["Undefined outside bounds"],
        },
    )

    regs["sqrt_reg"] = Expression(
        name="sqrt_reg",
        symbolic_expr="lambda_reg * sqrt(w**2 + epsilon)",
        params={"lambda_reg": 0.01, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "Smooth L1 approximation via √(w² + ε)",
            "usage": "Differentiable sparsity, compressed sensing",
            "pros": ["Smooth everywhere", "Approximates L1"],
            "cons": ["Less sparse than true L1"],
        },
    )

    regs["cauchy_reg"] = Expression(
        name="cauchy_reg",
        symbolic_expr="lambda_reg * log(1 + (w / sigma)**2)",
        params={"lambda_reg": 0.01, "sigma": 1.0},
        metadata={
            "category": "regularizer",
            "description": "Cauchy (Lorentzian) penalty - robust sparsity",
            "usage": "Robust regression, outlier-tolerant sparsity",
            "pros": ["Non-convex", "Strongly promotes sparsity"],
            "cons": ["Non-convex, harder to optimize"],
        },
    )

    # ----------------------------------------------------------------
    # Information-Theoretic Regularizers
    # ----------------------------------------------------------------

    regs["entropy_reg"] = Expression(
        name="entropy_reg",
        symbolic_expr=(
            "-lambda_reg * (p * log(p + epsilon) "
            "+ (1 - p) * log(1 - p + epsilon))"
        ),
        params={"lambda_reg": 0.01, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "Entropy regularization - encourages confidence",
            "usage": "Reinforcement learning, semi-supervised learning",
            "pros": ["Encourages confident predictions"],
            "cons": ["Can reduce exploration"],
        },
    )

    regs["kl_divergence_reg"] = Expression(
        name="kl_divergence_reg",
        symbolic_expr=(
            "lambda_reg * (p * log((p + epsilon)/(q + epsilon)) "
            "+ (1 - p) * log((1 - p + epsilon)/(1 - q + epsilon)))"
        ),
        params={"lambda_reg": 0.01, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "KL divergence penalty - keep distribution close to prior",
            "usage": "Variational autoencoders (VAE), Bayesian learning",
            "pros": ["Principled Bayesian regularization"],
            "cons": ["Asymmetric", "Requires prior q"],
        },
    )

    # ----------------------------------------------------------------
    # Gradient / Smoothness Regularizers
    # ----------------------------------------------------------------

    regs["gradient_penalty"] = Expression(
        name="gradient_penalty",
        symbolic_expr="lambda_reg * (grad_norm - 1)**2",
        params={"lambda_reg": 10.0},
        metadata={
            "category": "regularizer",
            "description": "Gradient penalty - enforce Lipschitz constraint",
            "usage": "WGAN-GP, Lipschitz-constrained networks",
            "pros": ["Stabilizes GAN training", "Enforces smoothness"],
            "cons": ["Expensive to compute"],
        },
    )

    regs["total_variation"] = Expression(
        name="total_variation",
        symbolic_expr="lambda_reg * Abs(w_i - w_j)",
        params={"lambda_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "Total variation - promote spatial smoothness",
            "usage": "Image denoising, signal smoothing",
            "pros": ["Preserves edges", "Removes noise"],
            "cons": ["Staircase artifacts"],
        },
    )

    regs["tikhonov"] = Expression(
        name="tikhonov",
        symbolic_expr="lambda_reg * (w - w_prior)**2",
        params={"lambda_reg": 0.01, "w_prior": 0.0},
        metadata={
            "category": "regularizer",
            "description": "Tikhonov regularization - shrink toward prior",
            "usage": "Ill-posed inverse problems, Bayesian MAP",
            "pros": ["Principled", "Centers weights around prior"],
            "cons": ["Requires choosing prior"],
        },
    )

    # ----------------------------------------------------------------
    # Sparsity-Inducing Penalty Functions
    # ----------------------------------------------------------------

    regs["group_lasso"] = Expression(
        name="group_lasso",
        symbolic_expr="lambda_reg * sqrt(w1**2 + w2**2 + epsilon)",
        params={"lambda_reg": 0.01, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "Group Lasso - sets entire feature groups to zero",
            "usage": "Feature group selection, structured sparsity",
            "pros": ["Structured sparsity", "Group selection"],
            "cons": ["Requires grouping definition"],
        },
    )

    regs["scad"] = Expression(
        name="scad",
        symbolic_expr=(
            "Piecewise("
            "(lambda_reg * Abs(w), Abs(w) <= lambda_reg), "
            "(-(w**2 - 2*a*lambda_reg*Abs(w) + lambda_reg**2) "
            "/ (2*(a - 1)), Abs(w) <= a*lambda_reg), "
            "(lambda_reg**2 * (a + 1) / 2, True)"
            ")"
        ),
        params={"lambda_reg": 0.01, "a": 3.7},
        metadata={
            "category": "regularizer",
            "description": "SCAD penalty - Smoothly Clipped Absolute Deviation",
            "usage": "Variable selection, unbiased estimation",
            "pros": ["Unbiased for large weights", "Oracle property"],
            "cons": ["Non-convex", "Extra hyperparameter a"],
        },
    )

    regs["mcp"] = Expression(
        name="mcp",
        symbolic_expr=(
            "Piecewise("
            "(lambda_reg * Abs(w) - w**2 / (2*gamma_mcp), "
            "Abs(w) <= gamma_mcp*lambda_reg), "
            "(gamma_mcp*lambda_reg**2 / 2, True)"
            ")"
        ),
        params={"lambda_reg": 0.01, "gamma_mcp": 3.0},
        metadata={
            "category": "regularizer",
            "description": "Minimax Concave Penalty (MCP)",
            "usage": "High-dimensional variable selection",
            "pros": ["Nearly unbiased", "Sparser than Lasso"],
            "cons": ["Non-convex", "Harder optimization"],
        },
    )

    # ----------------------------------------------------------------
    # Other Useful Regularizers
    # ----------------------------------------------------------------

    regs["max_norm"] = Expression(
        name="max_norm",
        symbolic_expr="Max(0, w**2 - max_val**2)",
        params={"max_val": 3.0},
        metadata={
            "category": "regularizer",
            "description": "Max-norm constraint penalty",
            "usage": "Dropout companion, bounded activations",
            "pros": ["Bounds weight magnitude", "Works well with dropout"],
            "cons": ["Non-smooth at boundary"],
        },
    )

    regs["orthogonal_reg"] = Expression(
        name="orthogonal_reg",
        symbolic_expr="lambda_reg * (w1*w2)**2",
        params={"lambda_reg": 0.01},
        metadata={
            "category": "regularizer",
            "description": "Orthogonal regularization - encourage decorrelated weights",
            "usage": "Improving gradient flow, preventing mode collapse",
            "pros": ["Better gradient flow", "Decorrelated features"],
            "cons": ["Pairwise computation O(n²)"],
        },
    )

    regs["label_smoothing"] = Expression(
        name="label_smoothing",
        symbolic_expr="(1 - epsilon_smooth) * y + epsilon_smooth / K",
        params={"epsilon_smooth": 0.1, "K": 10},
        metadata={
            "category": "regularizer",
            "description": "Label smoothing - soften hard targets",
            "usage": "Classification, knowledge distillation",
            "pros": ["Prevents overconfidence", "Improves calibration"],
            "cons": ["Slightly lower peak accuracy"],
        },
    )

    regs["confidence_penalty"] = Expression(
        name="confidence_penalty",
        symbolic_expr="-lambda_reg * p * log(p + epsilon)",
        params={"lambda_reg": 0.1, "epsilon": 1e-8},
        metadata={
            "category": "regularizer",
            "description": "Confidence penalty - discourages overconfident outputs",
            "usage": "Well-calibrated models, uncertainty estimation",
            "pros": ["Better calibrated probabilities"],
            "cons": ["Slightly lower accuracy"],
        },
    )

    return regs

Algebra

neurogebra.repository.algebra.get_algebra_expressions()

Get dictionary of algebraic expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/algebra.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
def get_algebra_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of algebraic expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    algebra: Dict[str, Expression] = {}

    # ================================================================
    # Polynomial Functions
    # ================================================================

    algebra["linear_eq"] = Expression(
        name="linear_eq",
        symbolic_expr="m*x + b",
        params={"m": 1, "b": 0},
        trainable_params=["m", "b"],
        metadata={
            "category": "algebra",
            "description": "Linear equation: y = mx + b",
            "usage": "Linear regression, line fitting",
        },
    )

    algebra["quadratic"] = Expression(
        name="quadratic",
        symbolic_expr="a*x**2 + b*x + c",
        params={"a": 1, "b": 0, "c": 0},
        trainable_params=["a", "b", "c"],
        metadata={
            "category": "algebra",
            "description": "Quadratic polynomial: ax² + bx + c",
            "usage": "Curve fitting, parabolic approximation",
        },
    )

    algebra["cubic"] = Expression(
        name="cubic",
        symbolic_expr="a*x**3 + b*x**2 + c*x + d",
        params={"a": 1, "b": 0, "c": 0, "d": 0},
        trainable_params=["a", "b", "c", "d"],
        metadata={
            "category": "algebra",
            "description": "Cubic polynomial: ax³ + bx² + cx + d",
            "usage": "Curve fitting, interpolation",
        },
    )

    algebra["quartic"] = Expression(
        name="quartic",
        symbolic_expr="a*x**4 + b*x**3 + c*x**2 + d*x + e",
        params={"a": 1, "b": 0, "c": 0, "d": 0, "e": 0},
        trainable_params=["a", "b", "c", "d", "e"],
        metadata={
            "category": "algebra",
            "description": "Quartic polynomial: ax⁴ + bx³ + cx² + dx + e",
            "usage": "Higher-order curve fitting",
        },
    )

    algebra["monomial"] = Expression(
        name="monomial",
        symbolic_expr="a * x**n",
        params={"a": 1.0, "n": 2.0},
        trainable_params=["a", "n"],
        metadata={
            "category": "algebra",
            "description": "Monomial: a·xⁿ, single-term power expression",
            "usage": "Basis for polynomial approximation",
        },
    )

    # ================================================================
    # Power & Root Functions
    # ================================================================

    algebra["power_law"] = Expression(
        name="power_law",
        symbolic_expr="a * x**n",
        params={"a": 1.0, "n": 2.0},
        trainable_params=["a", "n"],
        metadata={
            "category": "algebra",
            "description": "Power law: a·xⁿ",
            "usage": "Scaling laws, Zipf's law, allometry",
        },
    )

    algebra["inverse_square"] = Expression(
        name="inverse_square",
        symbolic_expr="a / (x**2 + epsilon)",
        params={"a": 1.0, "epsilon": 1e-8},
        metadata={
            "category": "algebra",
            "description": "Inverse square law: a/x²",
            "usage": "Gravity, electric fields, light intensity",
        },
    )

    algebra["square_root"] = Expression(
        name="square_root",
        symbolic_expr="a * sqrt(Abs(x) + epsilon)",
        params={"a": 1.0, "epsilon": 1e-8},
        metadata={
            "category": "algebra",
            "description": "Square root function: a·√|x|",
            "usage": "Sublinear growth, diminishing returns",
        },
    )

    algebra["nth_root"] = Expression(
        name="nth_root",
        symbolic_expr="(Abs(x) + epsilon)**(1/n)",
        params={"n": 3.0, "epsilon": 1e-8},
        metadata={
            "category": "algebra",
            "description": "Nth root: x^(1/n)",
            "usage": "Root extraction, power normalization",
        },
    )

    # ================================================================
    # Exponential & Logarithmic Functions
    # ================================================================

    algebra["exp_decay"] = Expression(
        name="exp_decay",
        symbolic_expr="A * exp(-k * x)",
        params={"A": 1.0, "k": 1.0},
        trainable_params=["A", "k"],
        metadata={
            "category": "algebra",
            "description": "Exponential decay: A·e^(−kx)",
            "usage": "Radioactive decay, learning rate schedules",
        },
    )

    algebra["exp_growth"] = Expression(
        name="exp_growth",
        symbolic_expr="A * exp(k * x)",
        params={"A": 1.0, "k": 0.1},
        trainable_params=["A", "k"],
        metadata={
            "category": "algebra",
            "description": "Exponential growth: A·e^(kx)",
            "usage": "Population growth, compound interest",
        },
    )

    algebra["double_exponential"] = Expression(
        name="double_exponential",
        symbolic_expr="A * exp(-Abs(x - mu) / b)",
        params={"A": 1.0, "mu": 0.0, "b": 1.0},
        trainable_params=["A", "mu", "b"],
        metadata={
            "category": "algebra",
            "description": "Laplace distribution / double exponential",
            "usage": "Robust statistics, L1 regularization prior",
        },
    )

    algebra["logarithmic"] = Expression(
        name="logarithmic",
        symbolic_expr="a * log(x + epsilon) + b",
        params={"a": 1.0, "b": 0.0, "epsilon": 1e-8},
        trainable_params=["a", "b"],
        metadata={
            "category": "algebra",
            "description": "Logarithmic function: a·ln(x) + b",
            "usage": "Diminishing returns, Weber-Fechner law",
        },
    )

    algebra["log_linear"] = Expression(
        name="log_linear",
        symbolic_expr="a * log(1 + exp(b * x))",
        params={"a": 1.0, "b": 1.0},
        trainable_params=["a", "b"],
        metadata={
            "category": "algebra",
            "description": "Log-linear (softplus family): a·log(1+e^(bx))",
            "usage": "Smooth ramp functions, positive outputs",
        },
    )

    # ================================================================
    # Sigmoid & Growth Curves
    # ================================================================

    algebra["logistic"] = Expression(
        name="logistic",
        symbolic_expr="L / (1 + exp(-k * (x - x0)))",
        params={"L": 1.0, "k": 1.0, "x0": 0.0},
        trainable_params=["L", "k", "x0"],
        metadata={
            "category": "algebra",
            "description": "Logistic growth curve (S-curve)",
            "usage": "Population growth, adoption curves",
        },
    )

    algebra["gompertz"] = Expression(
        name="gompertz",
        symbolic_expr="a * exp(-b * exp(-c * x))",
        params={"a": 1.0, "b": 1.0, "c": 1.0},
        trainable_params=["a", "b", "c"],
        metadata={
            "category": "algebra",
            "description": "Gompertz curve - asymmetric sigmoid growth",
            "usage": "Tumor growth, product adoption, mortality",
        },
    )

    algebra["richards_curve"] = Expression(
        name="richards_curve",
        symbolic_expr="K / (1 + exp(-r * (x - x0)))**(1/nu)",
        params={"K": 1.0, "r": 1.0, "x0": 0.0, "nu": 1.0},
        trainable_params=["K", "r", "x0", "nu"],
        metadata={
            "category": "algebra",
            "description": "Richards / generalized logistic curve",
            "usage": "Flexible growth modeling, epidemiology",
        },
    )

    algebra["probit"] = Expression(
        name="probit",
        symbolic_expr="0.5 * (1 + erf(x / sqrt(2)))",
        metadata={
            "category": "algebra",
            "description": "Probit function (Gaussian CDF Φ(x))",
            "usage": "Probit regression, dose-response curves",
        },
    )

    algebra["hill_equation"] = Expression(
        name="hill_equation",
        symbolic_expr="V_max * x**n / (K**n + x**n)",
        params={"V_max": 1.0, "K": 0.5, "n": 2.0},
        trainable_params=["V_max", "K", "n"],
        metadata={
            "category": "algebra",
            "description": "Hill equation - cooperative binding sigmoid",
            "usage": "Biochemistry, pharmacology, dose-response",
        },
    )

    algebra["michaelis_menten"] = Expression(
        name="michaelis_menten",
        symbolic_expr="V_max * x / (K_m + x)",
        params={"V_max": 1.0, "K_m": 0.5},
        trainable_params=["V_max", "K_m"],
        metadata={
            "category": "algebra",
            "description": "Michaelis-Menten enzyme kinetics",
            "usage": "Enzyme kinetics, saturation modeling",
        },
    )

    # ================================================================
    # Probability Distribution Functions
    # ================================================================

    algebra["gaussian"] = Expression(
        name="gaussian",
        symbolic_expr="A * exp(-(x - mu)**2 / (2 * sigma**2))",
        params={"A": 1.0, "mu": 0.0, "sigma": 1.0},
        trainable_params=["A", "mu", "sigma"],
        metadata={
            "category": "algebra",
            "description": "Gaussian (normal) distribution bell curve",
            "usage": "Probability density, Gaussian processes, RBF",
        },
    )

    algebra["cauchy_distribution"] = Expression(
        name="cauchy_distribution",
        symbolic_expr="1 / (pi * gamma_param * (1 + ((x - x0) / gamma_param)**2))",
        params={"x0": 0.0, "gamma_param": 1.0},
        metadata={
            "category": "algebra",
            "description": "Cauchy (Lorentzian) distribution - heavy tails",
            "usage": "Robust statistics, spectral line shapes",
        },
    )

    algebra["student_t"] = Expression(
        name="student_t",
        symbolic_expr=(
            "(1 + x**2/nu)**(-(nu + 1)/2) "
            "* gamma_func((nu + 1)/2) "
            "/ (sqrt(nu * pi) * gamma_func(nu/2))"
        ),
        params={"nu": 5.0},
        metadata={
            "category": "algebra",
            "description": "Student's t-distribution kernel (unnormalized)",
            "usage": "Small sample inference, robust regression",
        },
    )

    algebra["rayleigh"] = Expression(
        name="rayleigh",
        symbolic_expr="(x / sigma**2) * exp(-x**2 / (2 * sigma**2))",
        params={"sigma": 1.0},
        metadata={
            "category": "algebra",
            "description": "Rayleigh distribution density",
            "usage": "Signal processing, wind speed modeling",
        },
    )

    algebra["laplace_distribution"] = Expression(
        name="laplace_distribution",
        symbolic_expr="(1/(2*b)) * exp(-Abs(x - mu)/b)",
        params={"mu": 0.0, "b": 1.0},
        metadata={
            "category": "algebra",
            "description": "Laplace distribution density",
            "usage": "Sparse priors, L1 loss connection, robustness",
        },
    )

    algebra["beta_distribution"] = Expression(
        name="beta_distribution",
        symbolic_expr="x**(alpha - 1) * (1 - x)**(beta_param - 1)",
        params={"alpha": 2.0, "beta_param": 5.0},
        metadata={
            "category": "algebra",
            "description": "Beta distribution kernel (unnormalized)",
            "usage": "Probability on [0,1], Bayesian conjugate prior",
        },
    )

    # ================================================================
    # Trigonometric & Periodic Functions
    # ================================================================

    algebra["sinusoidal"] = Expression(
        name="sinusoidal",
        symbolic_expr="A * sin(omega * x + phi)",
        params={"A": 1.0, "omega": 1.0, "phi": 0.0},
        trainable_params=["A", "omega", "phi"],
        metadata={
            "category": "algebra",
            "description": "Sinusoidal wave: A·sin(ωx + φ)",
            "usage": "Signal processing, periodic data fitting",
        },
    )

    algebra["damped_oscillation"] = Expression(
        name="damped_oscillation",
        symbolic_expr="A * exp(-gamma_d * x) * cos(omega * x + phi)",
        params={"A": 1.0, "gamma_d": 0.1, "omega": 1.0, "phi": 0.0},
        trainable_params=["A", "gamma_d", "omega", "phi"],
        metadata={
            "category": "algebra",
            "description": "Damped oscillation: A·e^(-γx)·cos(ωx+φ)",
            "usage": "Spring-mass systems, RLC circuits, decay",
        },
    )

    algebra["fourier_term"] = Expression(
        name="fourier_term",
        symbolic_expr="a0 + a1*cos(x) + b1*sin(x) + a2*cos(2*x) + b2*sin(2*x)",
        params={"a0": 0.0, "a1": 1.0, "b1": 0.0, "a2": 0.0, "b2": 0.0},
        trainable_params=["a0", "a1", "b1", "a2", "b2"],
        metadata={
            "category": "algebra",
            "description": "Fourier series (2 terms): a₀ + Σ(aₙcos(nx) + bₙsin(nx))",
            "usage": "Periodic function approximation, spectral analysis",
        },
    )

    algebra["sawtooth_approx"] = Expression(
        name="sawtooth_approx",
        symbolic_expr=(
            "0.5 - (1/pi) * (sin(x) + sin(2*x)/2 + sin(3*x)/3)"
        ),
        metadata={
            "category": "algebra",
            "description": "Sawtooth wave (Fourier approx, 3 terms)",
            "usage": "Signal processing, waveform generation",
        },
    )

    algebra["square_wave_approx"] = Expression(
        name="square_wave_approx",
        symbolic_expr=(
            "(4/pi) * (sin(x) + sin(3*x)/3 + sin(5*x)/5)"
        ),
        metadata={
            "category": "algebra",
            "description": "Square wave (Fourier approx, 3 terms)",
            "usage": "Digital signals, pulse train approximation",
        },
    )

    # ================================================================
    # Kernel Functions (ML / Gaussian Processes)
    # ================================================================

    algebra["rbf_kernel"] = Expression(
        name="rbf_kernel",
        symbolic_expr="exp(-gamma_k * (x - y)**2)",
        params={"gamma_k": 1.0},
        metadata={
            "category": "algebra",
            "description": "RBF / Gaussian kernel: exp(−γ(x−y)²)",
            "usage": "SVM, Gaussian processes, kernel methods",
        },
    )

    algebra["polynomial_kernel"] = Expression(
        name="polynomial_kernel",
        symbolic_expr="(alpha_k * x * y + c_k)**d_k",
        params={"alpha_k": 1.0, "c_k": 1.0, "d_k": 3.0},
        metadata={
            "category": "algebra",
            "description": "Polynomial kernel: (αxy + c)^d",
            "usage": "SVM, polynomial feature maps",
        },
    )

    algebra["laplacian_kernel"] = Expression(
        name="laplacian_kernel",
        symbolic_expr="exp(-gamma_k * Abs(x - y))",
        params={"gamma_k": 1.0},
        metadata={
            "category": "algebra",
            "description": "Laplacian kernel: exp(−γ|x−y|)",
            "usage": "SVM, non-smooth data, Gaussian processes",
        },
    )

    algebra["rational_quadratic_kernel"] = Expression(
        name="rational_quadratic_kernel",
        symbolic_expr="(1 + (x - y)**2 / (2*alpha_rq * length_scale**2))**(-alpha_rq)",
        params={"alpha_rq": 1.0, "length_scale": 1.0},
        metadata={
            "category": "algebra",
            "description": "Rational quadratic kernel (infinite RBF mixture)",
            "usage": "Gaussian processes, multi-scale modeling",
        },
    )

    algebra["matern_12_kernel"] = Expression(
        name="matern_12_kernel",
        symbolic_expr="sigma_k**2 * exp(-Abs(x - y) / length_scale)",
        params={"sigma_k": 1.0, "length_scale": 1.0},
        metadata={
            "category": "algebra",
            "description": "Matérn kernel (ν=1/2) - equivalent to Laplacian",
            "usage": "Gaussian processes for rough functions",
        },
    )

    algebra["matern_32_kernel"] = Expression(
        name="matern_32_kernel",
        symbolic_expr=(
            "sigma_k**2 * (1 + sqrt(3)*Abs(x - y)/length_scale) "
            "* exp(-sqrt(3)*Abs(x - y)/length_scale)"
        ),
        params={"sigma_k": 1.0, "length_scale": 1.0},
        metadata={
            "category": "algebra",
            "description": "Matérn kernel (ν=3/2) - once differentiable",
            "usage": "Gaussian processes, moderate smoothness",
        },
    )

    algebra["periodic_kernel"] = Expression(
        name="periodic_kernel",
        symbolic_expr="sigma_k**2 * exp(-2 * sin(pi * Abs(x - y) / period)**2 / length_scale**2)",
        params={"sigma_k": 1.0, "length_scale": 1.0, "period": 1.0},
        metadata={
            "category": "algebra",
            "description": "Periodic kernel for repeating patterns",
            "usage": "Gaussian processes on periodic data",
        },
    )

    # ================================================================
    # Rational Functions
    # ================================================================

    algebra["rational"] = Expression(
        name="rational",
        symbolic_expr="(a*x + b) / (c*x + d + epsilon)",
        params={"a": 1.0, "b": 0.0, "c": 0.0, "d": 1.0, "epsilon": 1e-8},
        trainable_params=["a", "b", "c", "d"],
        metadata={
            "category": "algebra",
            "description": "Rational function (ax+b)/(cx+d)",
            "usage": "Padé approximation, Möbius transforms",
        },
    )

    algebra["lorentzian"] = Expression(
        name="lorentzian",
        symbolic_expr="A / (1 + ((x - x0)/gamma_l)**2)",
        params={"A": 1.0, "x0": 0.0, "gamma_l": 1.0},
        trainable_params=["A", "x0", "gamma_l"],
        metadata={
            "category": "algebra",
            "description": "Lorentzian / Cauchy peak function",
            "usage": "Spectral line fitting, resonance curves",
        },
    )

    # ================================================================
    # Special Functions & Other
    # ================================================================

    algebra["heaviside"] = Expression(
        name="heaviside",
        symbolic_expr="Piecewise((0, x < 0), (1, True))",
        metadata={
            "category": "algebra",
            "description": "Heaviside step function H(x)",
            "usage": "Signal processing, threshold operations",
        },
    )

    algebra["ramp"] = Expression(
        name="ramp",
        symbolic_expr="Max(0, x)",
        metadata={
            "category": "algebra",
            "description": "Ramp function (same as ReLU)",
            "usage": "Piecewise linear modeling, half-wave rectifier",
        },
    )

    algebra["absolute_value"] = Expression(
        name="absolute_value",
        symbolic_expr="Abs(x)",
        metadata={
            "category": "algebra",
            "description": "Absolute value |x|",
            "usage": "Distance, L1 norm, error magnitude",
        },
    )

    algebra["sign_function"] = Expression(
        name="sign_function",
        symbolic_expr="Piecewise((-1, x < 0), (0, Eq(x, 0)), (1, True))",
        metadata={
            "category": "algebra",
            "description": "Sign / signum function: sgn(x)",
            "usage": "Direction indicator, binary quantization",
        },
    )

    algebra["clamp"] = Expression(
        name="clamp",
        symbolic_expr="Max(low, Min(high, x))",
        params={"low": 0.0, "high": 1.0},
        metadata={
            "category": "algebra",
            "description": "Clamp function: clip x to [low, high]",
            "usage": "Gradient clipping, bounded outputs",
        },
    )

    algebra["lerp"] = Expression(
        name="lerp",
        symbolic_expr="a_val + t * (b_val - a_val)",
        params={"a_val": 0.0, "b_val": 1.0},
        metadata={
            "category": "algebra",
            "description": "Linear interpolation: a + t(b − a)",
            "usage": "Animation, EMA, parameter mixing",
        },
    )

    algebra["smoothstep"] = Expression(
        name="smoothstep",
        symbolic_expr="3*t**2 - 2*t**3",
        metadata={
            "category": "algebra",
            "description": "Smoothstep (Hermite interpolation): 3t² − 2t³",
            "usage": "Smooth transitions, animation easing",
        },
    )

    algebra["smootherstep"] = Expression(
        name="smootherstep",
        symbolic_expr="6*t**5 - 15*t**4 + 10*t**3",
        metadata={
            "category": "algebra",
            "description": "Smootherstep (Ken Perlin): 6t⁵ − 15t⁴ + 10t³",
            "usage": "Perlin noise, ultra-smooth transitions",
        },
    )

    return algebra

Calculus

neurogebra.repository.calculus.get_calculus_expressions()

Get dictionary of common calculus expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/calculus.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
def get_calculus_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of common calculus expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    calc: Dict[str, Expression] = {}

    # ================================================================
    # Elementary Functions
    # ================================================================

    calc["exp"] = Expression(
        name="exp",
        symbolic_expr="exp(x)",
        metadata={
            "category": "calculus",
            "description": "Natural exponential function e^x",
            "usage": "Growth models, probability distributions",
            "derivative": "exp(x)",
            "integral": "exp(x)",
        },
    )

    calc["ln"] = Expression(
        name="ln",
        symbolic_expr="log(x)",
        metadata={
            "category": "calculus",
            "description": "Natural logarithm ln(x)",
            "usage": "Log-scale transformations, information theory",
            "derivative": "1/x",
            "integral": "x*ln(x) - x",
        },
    )

    calc["log2"] = Expression(
        name="log2",
        symbolic_expr="log(x) / log(2)",
        metadata={
            "category": "calculus",
            "description": "Base-2 logarithm log₂(x)",
            "usage": "Information theory, bits, binary entropy",
        },
    )

    calc["log10"] = Expression(
        name="log10",
        symbolic_expr="log(x) / log(10)",
        metadata={
            "category": "calculus",
            "description": "Base-10 (common) logarithm log₁₀(x)",
            "usage": "Decibels, pH scale, order of magnitude",
        },
    )

    calc["reciprocal"] = Expression(
        name="reciprocal",
        symbolic_expr="1 / x",
        metadata={
            "category": "calculus",
            "description": "Reciprocal function 1/x",
            "usage": "Inverse transformations, harmonic series",
            "derivative": "-1/x²",
        },
    )

    calc["sqrt"] = Expression(
        name="sqrt",
        symbolic_expr="sqrt(x)",
        metadata={
            "category": "calculus",
            "description": "Square root function √x",
            "usage": "Normalization, distance computation",
            "derivative": "1/(2√x)",
        },
    )

    calc["cbrt"] = Expression(
        name="cbrt",
        symbolic_expr="x**(Rational(1, 3))",
        metadata={
            "category": "calculus",
            "description": "Cube root function x^(1/3)",
            "usage": "Volume extraction, cubic equations",
        },
    )

    calc["abs_val"] = Expression(
        name="abs_val",
        symbolic_expr="Abs(x)",
        metadata={
            "category": "calculus",
            "description": "Absolute value |x|",
            "usage": "Distance, L1 norm, magnitude",
        },
    )

    # ================================================================
    # Trigonometric Functions
    # ================================================================

    calc["sin"] = Expression(
        name="sin",
        symbolic_expr="sin(x)",
        metadata={
            "category": "calculus",
            "description": "Sine function",
            "usage": "Trigonometric operations, signal processing",
            "derivative": "cos(x)",
            "integral": "-cos(x)",
        },
    )

    calc["cos"] = Expression(
        name="cos",
        symbolic_expr="cos(x)",
        metadata={
            "category": "calculus",
            "description": "Cosine function",
            "usage": "Trigonometric operations, positional encoding",
            "derivative": "-sin(x)",
            "integral": "sin(x)",
        },
    )

    calc["tan"] = Expression(
        name="tan",
        symbolic_expr="tan(x)",
        metadata={
            "category": "calculus",
            "description": "Tangent function tan(x) = sin(x)/cos(x)",
            "usage": "Trigonometry, angle computation",
            "derivative": "sec²(x) = 1 + tan²(x)",
        },
    )

    calc["sec"] = Expression(
        name="sec",
        symbolic_expr="1 / cos(x)",
        metadata={
            "category": "calculus",
            "description": "Secant function sec(x) = 1/cos(x)",
            "usage": "Advanced trigonometry, integration techniques",
        },
    )

    calc["csc"] = Expression(
        name="csc",
        symbolic_expr="1 / sin(x)",
        metadata={
            "category": "calculus",
            "description": "Cosecant function csc(x) = 1/sin(x)",
            "usage": "Advanced trigonometry",
        },
    )

    calc["cot"] = Expression(
        name="cot",
        symbolic_expr="cos(x) / sin(x)",
        metadata={
            "category": "calculus",
            "description": "Cotangent function cot(x) = cos(x)/sin(x)",
            "usage": "Advanced trigonometry",
        },
    )

    # ================================================================
    # Inverse Trigonometric Functions
    # ================================================================

    calc["arcsin"] = Expression(
        name="arcsin",
        symbolic_expr="asin(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse sine (arcsine) function",
            "usage": "Angle recovery, trigonometric inversion",
            "derivative": "1/√(1 − x²)",
        },
    )

    calc["arccos"] = Expression(
        name="arccos",
        symbolic_expr="acos(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse cosine (arccosine) function",
            "usage": "Angle computation from dot products",
            "derivative": "−1/√(1 − x²)",
        },
    )

    calc["arctan"] = Expression(
        name="arctan",
        symbolic_expr="atan(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse tangent (arctangent) function",
            "usage": "Angle computation, smooth saturation",
            "derivative": "1/(1 + x²)",
        },
    )

    calc["arctan2"] = Expression(
        name="arctan2",
        symbolic_expr="atan2(y, x)",
        metadata={
            "category": "calculus",
            "description": "Two-argument arctangent atan2(y, x)",
            "usage": "Full-circle angle computation in [−π, π]",
        },
    )

    # ================================================================
    # Hyperbolic Functions
    # ================================================================

    calc["sinh"] = Expression(
        name="sinh",
        symbolic_expr="sinh(x)",
        metadata={
            "category": "calculus",
            "description": "Hyperbolic sine: (eˣ − e⁻ˣ)/2",
            "usage": "Catenary curves, special relativity",
            "derivative": "cosh(x)",
        },
    )

    calc["cosh"] = Expression(
        name="cosh",
        symbolic_expr="cosh(x)",
        metadata={
            "category": "calculus",
            "description": "Hyperbolic cosine: (eˣ + e⁻ˣ)/2",
            "usage": "Catenary curves, distance in hyperbolic space",
            "derivative": "sinh(x)",
        },
    )

    calc["tanh_func"] = Expression(
        name="tanh_func",
        symbolic_expr="tanh(x)",
        metadata={
            "category": "calculus",
            "description": "Hyperbolic tangent: sinh(x)/cosh(x)",
            "usage": "Neural network activations, bounded output",
            "derivative": "1 − tanh²(x) = sech²(x)",
        },
    )

    calc["sech"] = Expression(
        name="sech",
        symbolic_expr="1 / cosh(x)",
        metadata={
            "category": "calculus",
            "description": "Hyperbolic secant sech(x) = 1/cosh(x)",
            "usage": "Soliton solutions, sech² potential",
        },
    )

    # ================================================================
    # Inverse Hyperbolic Functions
    # ================================================================

    calc["arcsinh"] = Expression(
        name="arcsinh",
        symbolic_expr="asinh(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse hyperbolic sine: ln(x + √(x²+1))",
            "usage": "Smooth logarithm-like transform for all x",
            "derivative": "1/√(x² + 1)",
        },
    )

    calc["arccosh"] = Expression(
        name="arccosh",
        symbolic_expr="acosh(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse hyperbolic cosine: ln(x + √(x²−1))",
            "usage": "Hyperbolic geometry, distance metrics",
        },
    )

    calc["arctanh"] = Expression(
        name="arctanh",
        symbolic_expr="atanh(x)",
        metadata={
            "category": "calculus",
            "description": "Inverse hyperbolic tangent: ½ln((1+x)/(1−x))",
            "usage": "Fisher z-transform, correlation analysis",
            "derivative": "1/(1 − x²)",
        },
    )

    # ================================================================
    # Special Functions
    # ================================================================

    calc["erf"] = Expression(
        name="erf",
        symbolic_expr="erf(x)",
        metadata={
            "category": "calculus",
            "description": "Error function erf(x) = (2/√π) ∫₀ˣ e^(−t²) dt",
            "usage": "GELU activation, Gaussian CDF, statistics",
            "derivative": "(2/√π)·e^(−x²)",
        },
    )

    calc["erfc"] = Expression(
        name="erfc",
        symbolic_expr="erfc(x)",
        metadata={
            "category": "calculus",
            "description": "Complementary error function: 1 − erf(x)",
            "usage": "Tail probabilities, Q-function",
        },
    )

    calc["gamma_func"] = Expression(
        name="gamma_func",
        symbolic_expr="gamma(x)",
        metadata={
            "category": "calculus",
            "description": "Gamma function Γ(x) - generalized factorial",
            "usage": "Distributions, combinatorics, Γ(n) = (n−1)!",
        },
    )

    calc["digamma"] = Expression(
        name="digamma",
        symbolic_expr="digamma(x)",
        metadata={
            "category": "calculus",
            "description": "Digamma function ψ(x) = d/dx ln Γ(x)",
            "usage": "Bayesian inference, sufficient statistics",
        },
    )

    calc["beta_func"] = Expression(
        name="beta_func",
        symbolic_expr="gamma(a_param) * gamma(b_param) / gamma(a_param + b_param)",
        params={"a_param": 1.0, "b_param": 1.0},
        metadata={
            "category": "calculus",
            "description": "Beta function B(a,b) = Γ(a)Γ(b)/Γ(a+b)",
            "usage": "Beta distribution normalization, Bayesian priors",
        },
    )

    calc["sinc"] = Expression(
        name="sinc",
        symbolic_expr="Piecewise((1, Eq(x, 0)), (sin(pi*x)/(pi*x), True))",
        metadata={
            "category": "calculus",
            "description": "Sinc function: sin(πx)/(πx)",
            "usage": "Signal reconstruction, Fourier analysis",
        },
    )

    # ================================================================
    # Series Approximations (educational)
    # ================================================================

    calc["taylor_exp"] = Expression(
        name="taylor_exp",
        symbolic_expr="1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120",
        metadata={
            "category": "calculus",
            "description": "Taylor series for eˣ (5 terms)",
            "usage": "Educational: showing how series approximate functions",
        },
    )

    calc["taylor_sin"] = Expression(
        name="taylor_sin",
        symbolic_expr="x - x**3/6 + x**5/120 - x**7/5040",
        metadata={
            "category": "calculus",
            "description": "Taylor series for sin(x) (4 terms)",
            "usage": "Educational: polynomial approximation of sin",
        },
    )

    calc["taylor_cos"] = Expression(
        name="taylor_cos",
        symbolic_expr="1 - x**2/2 + x**4/24 - x**6/720",
        metadata={
            "category": "calculus",
            "description": "Taylor series for cos(x) (4 terms)",
            "usage": "Educational: polynomial approximation of cos",
        },
    )

    calc["taylor_ln1px"] = Expression(
        name="taylor_ln1px",
        symbolic_expr="x - x**2/2 + x**3/3 - x**4/4 + x**5/5",
        metadata={
            "category": "calculus",
            "description": "Taylor series for ln(1+x) (5 terms)",
            "usage": "Educational: approximation valid for |x| < 1",
        },
    )

    calc["taylor_arctan"] = Expression(
        name="taylor_arctan",
        symbolic_expr="x - x**3/3 + x**5/5 - x**7/7",
        metadata={
            "category": "calculus",
            "description": "Taylor series for atan(x) (4 terms)",
            "usage": "Educational: Leibniz formula for π/4",
        },
    )

    # ================================================================
    # Fundamental Calculus Operations
    # ================================================================

    calc["gradient_descent_step"] = Expression(
        name="gradient_descent_step",
        symbolic_expr="w - lr * gradient",
        params={"lr": 0.01},
        metadata={
            "category": "calculus",
            "description": "Single gradient descent update: w ← w − η·∇L",
            "usage": "Foundation of all neural network training",
        },
    )

    calc["chain_rule"] = Expression(
        name="chain_rule",
        symbolic_expr="df_du * du_dx",
        metadata={
            "category": "calculus",
            "description": "Chain rule: d/dx f(u(x)) = f'(u)·u'(x)",
            "usage": "Backpropagation, composite function derivatives",
        },
    )

    calc["product_rule"] = Expression(
        name="product_rule",
        symbolic_expr="f_val * dg_dx + g_val * df_dx",
        metadata={
            "category": "calculus",
            "description": "Product rule: (fg)' = f·g' + g·f'",
            "usage": "Derivatives of products, attention gradients",
        },
    )

    calc["quotient_rule"] = Expression(
        name="quotient_rule",
        symbolic_expr="(g_val * df_dx - f_val * dg_dx) / (g_val**2 + epsilon)",
        params={"epsilon": 1e-8},
        metadata={
            "category": "calculus",
            "description": "Quotient rule: (f/g)' = (g·f' − f·g')/g²",
            "usage": "Derivatives of ratios, softmax gradients",
        },
    )

    calc["finite_difference"] = Expression(
        name="finite_difference",
        symbolic_expr="(f_plus - f_minus) / (2 * h)",
        params={"h": 1e-5},
        metadata={
            "category": "calculus",
            "description": "Central finite difference: (f(x+h) − f(x−h))/(2h)",
            "usage": "Numerical differentiation, gradient checking",
        },
    )

    calc["trapezoidal_rule"] = Expression(
        name="trapezoidal_rule",
        symbolic_expr="h * (f_a + f_b) / 2",
        metadata={
            "category": "calculus",
            "description": "Trapezoidal rule: h·(f(a) + f(b))/2",
            "usage": "Numerical integration, area approximation",
        },
    )

    calc["simpsons_rule"] = Expression(
        name="simpsons_rule",
        symbolic_expr="h * (f_a + 4*f_m + f_b) / 6",
        metadata={
            "category": "calculus",
            "description": "Simpson's rule: h·(f(a) + 4f(m) + f(b))/6",
            "usage": "Accurate numerical integration",
        },
    )

    # ================================================================
    # Integral Transforms (simplified scalar forms)
    # ================================================================

    calc["laplace_kernel"] = Expression(
        name="laplace_kernel",
        symbolic_expr="exp(-s * t)",
        metadata={
            "category": "calculus",
            "description": "Laplace transform kernel e^(−st)",
            "usage": "Control theory, differential equation solutions",
        },
    )

    calc["fourier_kernel_real"] = Expression(
        name="fourier_kernel_real",
        symbolic_expr="cos(omega * t)",
        metadata={
            "category": "calculus",
            "description": "Real part of Fourier kernel cos(ωt)",
            "usage": "Frequency analysis, spectral decomposition",
        },
    )

    calc["fourier_kernel_imag"] = Expression(
        name="fourier_kernel_imag",
        symbolic_expr="-sin(omega * t)",
        metadata={
            "category": "calculus",
            "description": "Imaginary part of Fourier kernel −sin(ωt)",
            "usage": "Frequency analysis, phase information",
        },
    )

    calc["wavelet_morlet"] = Expression(
        name="wavelet_morlet",
        symbolic_expr="exp(-x**2/2) * cos(5*x)",
        metadata={
            "category": "calculus",
            "description": "Morlet wavelet: e^(−x²/2)·cos(5x)",
            "usage": "Time-frequency analysis, wavelet transforms",
        },
    )

    calc["wavelet_mexican_hat"] = Expression(
        name="wavelet_mexican_hat",
        symbolic_expr="(2/sqrt(3)) * pi**(-Rational(1,4)) * (1 - x**2) * exp(-x**2/2)",
        metadata={
            "category": "calculus",
            "description": "Mexican hat (Ricker) wavelet: (1−x²)e^(−x²/2)",
            "usage": "Edge detection, seismology, wavelet analysis",
        },
    )

    return calc

Statistics

neurogebra.repository.statistics.get_statistics_expressions()

Get dictionary of statistical expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/statistics.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
def get_statistics_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of statistical expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    stats: Dict[str, Expression] = {}

    # ================================================================
    # Probability Density Functions
    # ================================================================

    stats["normal_pdf"] = Expression(
        name="normal_pdf",
        symbolic_expr="(1 / (sigma * sqrt(2 * pi))) * exp(-(x - mu)**2 / (2 * sigma**2))",
        params={"mu": 0, "sigma": 1},
        trainable_params=["mu", "sigma"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Normal (Gaussian) probability density function",
            "usage": "Central limit theorem, Bayesian priors, noise modeling",
            "formula_latex": r"\frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{(x-\mu)^2}{2\sigma^2}}",
        },
    )

    stats["standard_normal_pdf"] = Expression(
        name="standard_normal_pdf",
        symbolic_expr="(1 / sqrt(2 * pi)) * exp(-x**2 / 2)",
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Standard normal PDF (μ=0, σ=1)",
            "usage": "Z-scores, standard statistics",
            "formula_latex": r"\frac{1}{\sqrt{2\pi}} e^{-x^2/2}",
        },
    )

    stats["uniform_pdf"] = Expression(
        name="uniform_pdf",
        symbolic_expr="1 / (b - a)",
        params={"a": 0, "b": 1},
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Uniform distribution PDF on [a, b]",
            "usage": "Random initialization, prior distributions",
            "formula_latex": r"\frac{1}{b - a}",
        },
    )

    stats["exponential_pdf"] = Expression(
        name="exponential_pdf",
        symbolic_expr="lambda_param * exp(-lambda_param * x)",
        params={"lambda_param": 1},
        trainable_params=["lambda_param"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Exponential distribution PDF",
            "usage": "Waiting times, survival analysis, Poisson processes",
            "formula_latex": r"\lambda e^{-\lambda x}",
        },
    )

    stats["laplace_pdf"] = Expression(
        name="laplace_pdf",
        symbolic_expr="(1 / (2 * b)) * exp(-Abs(x - mu) / b)",
        params={"mu": 0, "b": 1},
        trainable_params=["mu", "b"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Laplace distribution PDF",
            "usage": "Robust statistics, L1 regularization prior",
            "formula_latex": r"\frac{1}{2b} e^{-|x-\mu|/b}",
        },
    )

    stats["cauchy_pdf"] = Expression(
        name="cauchy_pdf",
        symbolic_expr="1 / (pi * gamma_param * (1 + ((x - x0) / gamma_param)**2))",
        params={"x0": 0, "gamma_param": 1},
        trainable_params=["x0", "gamma_param"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Cauchy (Lorentzian) distribution PDF",
            "usage": "Heavy-tailed modeling, spectral lines",
            "formula_latex": r"\frac{1}{\pi\gamma[1+(\frac{x-x_0}{\gamma})^2]}",
        },
    )

    stats["log_normal_pdf"] = Expression(
        name="log_normal_pdf",
        symbolic_expr="(1 / (x * sigma * sqrt(2 * pi))) * exp(-(log(x) - mu)**2 / (2 * sigma**2))",
        params={"mu": 0, "sigma": 1},
        trainable_params=["mu", "sigma"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Log-normal distribution PDF",
            "usage": "Income distributions, stock prices, multiplicative processes",
            "formula_latex": r"\frac{1}{x\sigma\sqrt{2\pi}} e^{-\frac{(\ln x - \mu)^2}{2\sigma^2}}",
        },
    )

    stats["rayleigh_pdf"] = Expression(
        name="rayleigh_pdf",
        symbolic_expr="(x / sigma**2) * exp(-x**2 / (2 * sigma**2))",
        params={"sigma": 1},
        trainable_params=["sigma"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Rayleigh distribution PDF",
            "usage": "Wind speed modeling, signal amplitude",
            "formula_latex": r"\frac{x}{\sigma^2} e^{-x^2/(2\sigma^2)}",
        },
    )

    stats["gumbel_pdf"] = Expression(
        name="gumbel_pdf",
        symbolic_expr="(1 / beta_param) * exp(-(z + exp(-z)))",
        params={"mu": 0, "beta_param": 1},
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Gumbel distribution PDF (z = (x-μ)/β)",
            "usage": "Extreme value theory, max/min modeling",
            "note": "z = (x - mu) / beta; substitute before eval",
            "formula_latex": r"\frac{1}{\beta} e^{-(z + e^{-z})}",
        },
    )

    stats["beta_pdf"] = Expression(
        name="beta_pdf",
        symbolic_expr="x**(alpha - 1) * (1 - x)**(beta_param - 1)",
        params={"alpha": 2, "beta_param": 5},
        trainable_params=["alpha", "beta_param"],
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Beta distribution PDF (unnormalized, x ∈ [0,1])",
            "usage": "Bayesian priors for probabilities, A/B testing",
            "formula_latex": r"x^{\alpha-1}(1-x)^{\beta-1}",
        },
    )

    stats["chi_squared_kernel"] = Expression(
        name="chi_squared_kernel",
        symbolic_expr="x**(k/2 - 1) * exp(-x/2)",
        params={"k": 2},
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Chi-squared distribution kernel (unnormalized)",
            "usage": "Hypothesis testing, goodness-of-fit",
            "formula_latex": r"x^{k/2 - 1} e^{-x/2}",
        },
    )

    stats["student_t_kernel"] = Expression(
        name="student_t_kernel",
        symbolic_expr="(1 + x**2 / nu)**(-(nu + 1) / 2)",
        params={"nu": 3},
        metadata={
            "category": "statistics",
            "subcategory": "distribution",
            "description": "Student's t-distribution kernel (unnormalized)",
            "usage": "Small sample inference, robust regression",
            "formula_latex": r"(1 + x^2/\nu)^{-(\nu+1)/2}",
        },
    )

    # ================================================================
    # Cumulative Distribution & Survival Functions
    # ================================================================

    stats["logistic_cdf"] = Expression(
        name="logistic_cdf",
        symbolic_expr="1 / (1 + exp(-(x - mu) / s))",
        params={"mu": 0, "s": 1},
        metadata={
            "category": "statistics",
            "subcategory": "cdf",
            "description": "Logistic CDF (sigmoid with location/scale)",
            "usage": "Binary classification, logistic regression",
            "formula_latex": r"\frac{1}{1 + e^{-(x-\mu)/s}}",
        },
    )

    stats["exponential_cdf"] = Expression(
        name="exponential_cdf",
        symbolic_expr="1 - exp(-lambda_param * x)",
        params={"lambda_param": 1},
        metadata={
            "category": "statistics",
            "subcategory": "cdf",
            "description": "Exponential CDF",
            "usage": "Survival analysis, reliability engineering",
            "formula_latex": r"1 - e^{-\lambda x}",
        },
    )

    stats["survival_exponential"] = Expression(
        name="survival_exponential",
        symbolic_expr="exp(-lambda_param * x)",
        params={"lambda_param": 1},
        metadata={
            "category": "statistics",
            "subcategory": "survival",
            "description": "Exponential survival function S(x) = 1 - F(x)",
            "usage": "Survival analysis, reliability, time-to-event",
            "formula_latex": r"e^{-\lambda x}",
        },
    )

    stats["weibull_survival"] = Expression(
        name="weibull_survival",
        symbolic_expr="exp(-(x / lambda_param)**k)",
        params={"lambda_param": 1, "k": 1.5},
        metadata={
            "category": "statistics",
            "subcategory": "survival",
            "description": "Weibull survival function",
            "usage": "Failure analysis, wind speed distribution",
            "formula_latex": r"e^{-(x/\lambda)^k}",
        },
    )

    # ================================================================
    # Information Theory
    # ================================================================

    stats["binary_entropy"] = Expression(
        name="binary_entropy",
        symbolic_expr="-(p * log(p + 1e-15) + (1 - p) * log(1 - p + 1e-15))",
        metadata={
            "category": "statistics",
            "subcategory": "information_theory",
            "description": "Binary entropy H(p) for Bernoulli variable",
            "usage": "Decision trees, information gain, uncertainty",
            "formula_latex": r"-[p\ln p + (1-p)\ln(1-p)]",
        },
    )

    stats["cross_entropy_elem"] = Expression(
        name="cross_entropy_elem",
        symbolic_expr="-(y * log(p + 1e-15) + (1 - y) * log(1 - p + 1e-15))",
        metadata={
            "category": "statistics",
            "subcategory": "information_theory",
            "description": "Element-wise binary cross-entropy",
            "usage": "Classification loss, KL divergence component",
            "formula_latex": r"-[y\ln p + (1-y)\ln(1-p)]",
        },
    )

    stats["kl_divergence_elem"] = Expression(
        name="kl_divergence_elem",
        symbolic_expr="p * log((p + 1e-15) / (q + 1e-15))",
        metadata={
            "category": "statistics",
            "subcategory": "information_theory",
            "description": "Element-wise KL divergence D_KL(p||q)",
            "usage": "VAE loss, distribution matching, Bayesian inference",
            "formula_latex": r"p \ln\frac{p}{q}",
        },
    )

    stats["js_divergence_elem"] = Expression(
        name="js_divergence_elem",
        symbolic_expr="(1/2) * (p * log((2*p) / (p + q + 1e-15)) + q * log((2*q) / (p + q + 1e-15)))",
        metadata={
            "category": "statistics",
            "subcategory": "information_theory",
            "description": "Element-wise Jensen-Shannon divergence",
            "usage": "GAN training, symmetric distribution comparison",
            "formula_latex": r"\frac{1}{2}[p\ln\frac{2p}{p+q} + q\ln\frac{2q}{p+q}]",
        },
    )

    stats["mutual_info_bound"] = Expression(
        name="mutual_info_bound",
        symbolic_expr="log(1 + exp(x))",
        metadata={
            "category": "statistics",
            "subcategory": "information_theory",
            "description": "Donsker-Varadhan/NWJ lower bound kernel for MI",
            "usage": "Mutual information estimation, representation learning",
            "formula_latex": r"\ln(1 + e^x)",
        },
    )

    # ================================================================
    # Descriptive Statistics (scalar expressions)
    # ================================================================

    stats["z_score"] = Expression(
        name="z_score",
        symbolic_expr="(x - mu) / sigma",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "statistics",
            "subcategory": "descriptive",
            "description": "Z-score standardization",
            "usage": "Normalization, hypothesis testing, outlier detection",
            "formula_latex": r"z = \frac{x - \mu}{\sigma}",
        },
    )

    stats["t_statistic"] = Expression(
        name="t_statistic",
        symbolic_expr="(x - mu) / (s / sqrt(n))",
        params={"mu": 0, "s": 1, "n": 30},
        metadata={
            "category": "statistics",
            "subcategory": "descriptive",
            "description": "t-statistic for one-sample t-test",
            "usage": "Hypothesis testing, confidence intervals",
            "formula_latex": r"t = \frac{\bar{x} - \mu}{s/\sqrt{n}}",
        },
    )

    stats["coefficient_of_variation"] = Expression(
        name="coefficient_of_variation",
        symbolic_expr="sigma / mu",
        params={"mu": 1, "sigma": 0.1},
        metadata={
            "category": "statistics",
            "subcategory": "descriptive",
            "description": "Coefficient of variation (relative std dev)",
            "usage": "Compare variability across scales",
            "formula_latex": r"CV = \frac{\sigma}{\mu}",
        },
    )

    stats["pooled_variance"] = Expression(
        name="pooled_variance",
        symbolic_expr="((n1 - 1) * s1**2 + (n2 - 1) * s2**2) / (n1 + n2 - 2)",
        params={"n1": 30, "s1": 1, "n2": 30, "s2": 1},
        metadata={
            "category": "statistics",
            "subcategory": "descriptive",
            "description": "Pooled variance for two-sample t-test",
            "usage": "Comparing two groups, ANOVA",
            "formula_latex": r"s_p^2 = \frac{(n_1-1)s_1^2 + (n_2-1)s_2^2}{n_1+n_2-2}",
        },
    )

    stats["standard_error"] = Expression(
        name="standard_error",
        symbolic_expr="sigma / sqrt(n)",
        params={"sigma": 1, "n": 30},
        metadata={
            "category": "statistics",
            "subcategory": "descriptive",
            "description": "Standard error of the mean",
            "usage": "Confidence intervals, significance testing",
            "formula_latex": r"SE = \frac{\sigma}{\sqrt{n}}",
        },
    )

    # ================================================================
    # Bayesian Statistics
    # ================================================================

    stats["bayes_posterior_kernel"] = Expression(
        name="bayes_posterior_kernel",
        symbolic_expr="exp(-((x - mu_prior)**2) / (2 * sigma_prior**2)) * exp(-((y - x)**2) / (2 * sigma_lik**2))",
        params={"mu_prior": 0, "sigma_prior": 1, "sigma_lik": 1},
        metadata={
            "category": "statistics",
            "subcategory": "bayesian",
            "description": "Gaussian prior × Gaussian likelihood (posterior kernel)",
            "usage": "Bayesian inference, conjugate priors",
            "formula_latex": r"\mathcal{N}(x|\mu_0,\sigma_0^2) \cdot \mathcal{N}(y|x,\sigma_l^2)",
        },
    )

    stats["log_prior_normal"] = Expression(
        name="log_prior_normal",
        symbolic_expr="-(x - mu)**2 / (2 * sigma**2) - log(sigma) - log(2 * pi) / 2",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "statistics",
            "subcategory": "bayesian",
            "description": "Log of normal prior density",
            "usage": "Log-space Bayesian computation, weight priors in BNNs",
            "formula_latex": r"-\frac{(x-\mu)^2}{2\sigma^2} - \ln\sigma - \frac{\ln 2\pi}{2}",
        },
    )

    stats["evidence_lower_bound"] = Expression(
        name="evidence_lower_bound",
        symbolic_expr="log_likelihood - kl_term",
        params={"log_likelihood": 0, "kl_term": 0},
        metadata={
            "category": "statistics",
            "subcategory": "bayesian",
            "description": "Evidence Lower Bound (ELBO) = E[log p(x|z)] - KL(q||p)",
            "usage": "Variational autoencoders, variational inference",
            "formula_latex": r"ELBO = \mathbb{E}[\log p(x|z)] - D_{KL}(q(z|x) \| p(z))",
        },
    )

    # ================================================================
    # Regression & Correlation (scalar forms)
    # ================================================================

    stats["pearson_r_component"] = Expression(
        name="pearson_r_component",
        symbolic_expr="(x - mu_x) * (y - mu_y) / (sigma_x * sigma_y)",
        params={"mu_x": 0, "mu_y": 0, "sigma_x": 1, "sigma_y": 1},
        metadata={
            "category": "statistics",
            "subcategory": "correlation",
            "description": "Element-wise Pearson correlation component",
            "usage": "Correlation analysis, feature selection",
            "formula_latex": r"\frac{(x-\mu_x)(y-\mu_y)}{\sigma_x \sigma_y}",
        },
    )

    stats["linear_regression_pred"] = Expression(
        name="linear_regression_pred",
        symbolic_expr="beta_0 + beta_1 * x",
        params={"beta_0": 0, "beta_1": 1},
        trainable_params=["beta_0", "beta_1"],
        metadata={
            "category": "statistics",
            "subcategory": "regression",
            "description": "Simple linear regression prediction",
            "usage": "Regression, trend estimation",
            "formula_latex": r"\hat{y} = \beta_0 + \beta_1 x",
        },
    )

    stats["logistic_regression_pred"] = Expression(
        name="logistic_regression_pred",
        symbolic_expr="1 / (1 + exp(-(beta_0 + beta_1 * x)))",
        params={"beta_0": 0, "beta_1": 1},
        trainable_params=["beta_0", "beta_1"],
        metadata={
            "category": "statistics",
            "subcategory": "regression",
            "description": "Logistic regression prediction (binary)",
            "usage": "Binary classification, probability estimation",
            "formula_latex": r"\sigma(\beta_0 + \beta_1 x) = \frac{1}{1+e^{-(\beta_0+\beta_1 x)}}",
        },
    )

    # ================================================================
    # Moment Generating & Characteristic Functions
    # ================================================================

    stats["mgf_normal"] = Expression(
        name="mgf_normal",
        symbolic_expr="exp(mu * t + sigma**2 * t**2 / 2)",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "statistics",
            "subcategory": "generating_function",
            "description": "Moment generating function of Normal distribution",
            "usage": "Deriving moments, proving CLT",
            "formula_latex": r"M(t) = e^{\mu t + \sigma^2 t^2/2}",
        },
    )

    stats["mgf_exponential"] = Expression(
        name="mgf_exponential",
        symbolic_expr="lambda_param / (lambda_param - t)",
        params={"lambda_param": 1},
        metadata={
            "category": "statistics",
            "subcategory": "generating_function",
            "description": "Moment generating function of Exponential distribution",
            "usage": "Computing exponential moments",
            "formula_latex": r"M(t) = \frac{\lambda}{\lambda - t}",
        },
    )

    stats["characteristic_normal"] = Expression(
        name="characteristic_normal",
        symbolic_expr="exp(I * mu * t - sigma**2 * t**2 / 2)",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "statistics",
            "subcategory": "generating_function",
            "description": "Characteristic function of Normal distribution",
            "usage": "Fourier analysis of distributions, CLT proofs",
            "formula_latex": r"\varphi(t) = e^{i\mu t - \sigma^2 t^2/2}",
        },
    )

    return stats

Linear Algebra

neurogebra.repository.linalg.get_linalg_expressions()

Get dictionary of linear algebra expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/linalg.py
def get_linalg_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of linear algebra expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    la: Dict[str, Expression] = {}

    # ================================================================
    # Vector Norms (element-wise components)
    # ================================================================

    la["l1_norm_elem"] = Expression(
        name="l1_norm_elem",
        symbolic_expr="Abs(x)",
        metadata={
            "category": "linalg",
            "subcategory": "norm",
            "description": "L1 norm element |xᵢ| (sum over elements for vector norm)",
            "usage": "Sparsity, Manhattan distance, LASSO",
            "formula_latex": r"|x_i|",
        },
    )

    la["l2_norm_elem"] = Expression(
        name="l2_norm_elem",
        symbolic_expr="x**2",
        metadata={
            "category": "linalg",
            "subcategory": "norm",
            "description": "L2 norm element xᵢ² (sum then sqrt for vector norm)",
            "usage": "Euclidean distance, weight decay, Ridge",
            "formula_latex": r"x_i^2",
        },
    )

    la["lp_norm_elem"] = Expression(
        name="lp_norm_elem",
        symbolic_expr="Abs(x)**p",
        params={"p": 2},
        metadata={
            "category": "linalg",
            "subcategory": "norm",
            "description": "General Lp norm element |xᵢ|^p",
            "usage": "Generalized norms, robust statistics",
            "formula_latex": r"|x_i|^p",
        },
    )

    la["huber_norm_elem"] = Expression(
        name="huber_norm_elem",
        symbolic_expr="Piecewise((x**2 / 2, Abs(x) <= delta), (delta * (Abs(x) - delta / 2), True))",
        params={"delta": 1},
        metadata={
            "category": "linalg",
            "subcategory": "norm",
            "description": "Huber norm element (smooth L1/L2 transition)",
            "usage": "Robust regression, smooth optimization",
            "formula_latex": r"\begin{cases} x^2/2 & |x|\le\delta \\ \delta(|x|-\delta/2) & \text{otherwise} \end{cases}",
        },
    )

    # ================================================================
    # Inner Products & Similarities
    # ================================================================

    la["dot_product_elem"] = Expression(
        name="dot_product_elem",
        symbolic_expr="x * y",
        metadata={
            "category": "linalg",
            "subcategory": "inner_product",
            "description": "Dot product element xᵢ·yᵢ (sum for full dot product)",
            "usage": "Similarity, projections, attention scores",
            "formula_latex": r"x_i \cdot y_i",
        },
    )

    la["cosine_similarity"] = Expression(
        name="cosine_similarity",
        symbolic_expr="dot_xy / (norm_x * norm_y + 1e-8)",
        params={"dot_xy": 1, "norm_x": 1, "norm_y": 1},
        metadata={
            "category": "linalg",
            "subcategory": "similarity",
            "description": "Cosine similarity cos(θ) = (x·y) / (‖x‖‖y‖)",
            "usage": "Text similarity, recommendation, embeddings",
            "formula_latex": r"\cos\theta = \frac{\mathbf{x}\cdot\mathbf{y}}{\|\mathbf{x}\|\|\mathbf{y}\|}",
        },
    )

    la["scaled_dot_product"] = Expression(
        name="scaled_dot_product",
        symbolic_expr="dot_qk / sqrt(d_k)",
        params={"dot_qk": 1, "d_k": 64},
        metadata={
            "category": "linalg",
            "subcategory": "inner_product",
            "description": "Scaled dot-product (Transformer attention core)",
            "usage": "Self-attention, cross-attention in Transformers",
            "formula_latex": r"\frac{Q \cdot K^T}{\sqrt{d_k}}",
        },
    )

    # ================================================================
    # Distance Metrics
    # ================================================================

    la["euclidean_dist_elem"] = Expression(
        name="euclidean_dist_elem",
        symbolic_expr="(x - y)**2",
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "Squared Euclidean distance element (sum then sqrt)",
            "usage": "K-means, KNN, distance-based clustering",
            "formula_latex": r"(x_i - y_i)^2",
        },
    )

    la["manhattan_dist_elem"] = Expression(
        name="manhattan_dist_elem",
        symbolic_expr="Abs(x - y)",
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "Manhattan distance element |xᵢ - yᵢ|",
            "usage": "High-dimensional data, grid movement",
            "formula_latex": r"|x_i - y_i|",
        },
    )

    la["minkowski_dist_elem"] = Expression(
        name="minkowski_dist_elem",
        symbolic_expr="Abs(x - y)**p",
        params={"p": 2},
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "Minkowski distance element |xᵢ - yᵢ|^p",
            "usage": "Generalized distance (p=1 Manhattan, p=2 Euclidean)",
            "formula_latex": r"|x_i - y_i|^p",
        },
    )

    la["chebyshev_dist"] = Expression(
        name="chebyshev_dist",
        symbolic_expr="Max(Abs(x - y), Abs(a - b))",
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "Chebyshev distance max|xᵢ - yᵢ| (2D demo)",
            "usage": "Chess-board distance, L∞ norm",
            "formula_latex": r"\max_i |x_i - y_i|",
        },
    )

    la["mahalanobis_1d"] = Expression(
        name="mahalanobis_1d",
        symbolic_expr="Abs(x - mu) / sigma",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "1D Mahalanobis distance (generalized z-score)",
            "usage": "Outlier detection, multivariate anomaly detection",
            "formula_latex": r"\frac{|x - \mu|}{\sigma}",
        },
    )

    la["canberra_dist_elem"] = Expression(
        name="canberra_dist_elem",
        symbolic_expr="Abs(x - y) / (Abs(x) + Abs(y) + 1e-8)",
        metadata={
            "category": "linalg",
            "subcategory": "distance",
            "description": "Canberra distance element",
            "usage": "Sensitive to small values, ecology data",
            "formula_latex": r"\frac{|x_i - y_i|}{|x_i| + |y_i|}",
        },
    )

    # ================================================================
    # Projections & Decompositions
    # ================================================================

    la["vector_projection_scalar"] = Expression(
        name="vector_projection_scalar",
        symbolic_expr="dot_ab / (norm_b**2 + 1e-8)",
        params={"dot_ab": 1, "norm_b": 1},
        metadata={
            "category": "linalg",
            "subcategory": "projection",
            "description": "Scalar projection coefficient (a·b / ‖b‖²)",
            "usage": "Gram-Schmidt, PCA components, projections",
            "formula_latex": r"\frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{b}\|^2}",
        },
    )

    la["orthogonal_residual_elem"] = Expression(
        name="orthogonal_residual_elem",
        symbolic_expr="a - proj_coeff * b",
        params={"proj_coeff": 0},
        metadata={
            "category": "linalg",
            "subcategory": "projection",
            "description": "Orthogonal residual element aᵢ - (proj coeff)·bᵢ",
            "usage": "Gram-Schmidt orthogonalization, residual computation",
            "formula_latex": r"a_i - \frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{b}\|^2} b_i",
        },
    )

    # ================================================================
    # Matrix Operations (2×2 scalar forms)
    # ================================================================

    la["determinant_2x2"] = Expression(
        name="determinant_2x2",
        symbolic_expr="a * d - b * c",
        metadata={
            "category": "linalg",
            "subcategory": "matrix",
            "description": "Determinant of 2×2 matrix [[a,b],[c,d]]",
            "usage": "Invertibility, area scaling, eigenvalue computation",
            "formula_latex": r"\det = ad - bc",
        },
    )

    la["trace_2x2"] = Expression(
        name="trace_2x2",
        symbolic_expr="a + d",
        metadata={
            "category": "linalg",
            "subcategory": "matrix",
            "description": "Trace of 2×2 matrix [[a,b],[c,d]]",
            "usage": "Sum of eigenvalues, matrix regularization",
            "formula_latex": r"\text{tr}(A) = a + d",
        },
    )

    la["eigenvalue_2x2"] = Expression(
        name="eigenvalue_2x2",
        symbolic_expr="(a + d) / 2 + sqrt(((a - d) / 2)**2 + b * c)",
        metadata={
            "category": "linalg",
            "subcategory": "matrix",
            "description": "Larger eigenvalue of 2×2 matrix [[a,b],[c,d]]",
            "usage": "PCA, spectral analysis, stability analysis",
            "formula_latex": r"\lambda_1 = \frac{a+d}{2} + \sqrt{(\frac{a-d}{2})^2 + bc}",
        },
    )

    la["frobenius_elem"] = Expression(
        name="frobenius_elem",
        symbolic_expr="x**2",
        metadata={
            "category": "linalg",
            "subcategory": "matrix",
            "description": "Frobenius norm element (sum all aᵢⱼ², then sqrt)",
            "usage": "Matrix norm, regularization of weight matrices",
            "formula_latex": r"a_{ij}^2",
        },
    )

    # ================================================================
    # Kernel Functions (additional kernels; see also algebra.py)
    # ================================================================

    la["sigmoid_kernel"] = Expression(
        name="sigmoid_kernel",
        symbolic_expr="tanh(alpha * x * y + c_coeff)",
        params={"alpha": 1, "c_coeff": 0},
        metadata={
            "category": "linalg",
            "subcategory": "kernel",
            "description": "Sigmoid (hyperbolic tangent) kernel",
            "usage": "Neural network-inspired kernel, SVM",
            "formula_latex": r"K(x,y) = \tanh(\alpha x y + c)",
        },
    )

    # ================================================================
    # Attention & Transformer Components
    # ================================================================

    la["softmax_score"] = Expression(
        name="softmax_score",
        symbolic_expr="exp(x) / (exp(x) + exp(y) + 1e-8)",
        metadata={
            "category": "linalg",
            "subcategory": "attention",
            "description": "2-class softmax probability",
            "usage": "Attention weights, classification head",
            "formula_latex": r"\frac{e^{x_i}}{\sum_j e^{x_j}}",
        },
    )

    la["log_softmax_score"] = Expression(
        name="log_softmax_score",
        symbolic_expr="x - log(exp(x) + exp(y) + 1e-8)",
        metadata={
            "category": "linalg",
            "subcategory": "attention",
            "description": "Log-softmax (numerically stable, 2-class)",
            "usage": "NLLLoss input, cross-entropy computation",
            "formula_latex": r"x_i - \ln\sum_j e^{x_j}",
        },
    )

    la["layer_norm_elem"] = Expression(
        name="layer_norm_elem",
        symbolic_expr="gamma_ln * (x - mu) / (sigma + 1e-5) + beta_ln",
        params={"mu": 0, "sigma": 1, "gamma_ln": 1, "beta_ln": 0},
        trainable_params=["gamma_ln", "beta_ln"],
        metadata={
            "category": "linalg",
            "subcategory": "normalization",
            "description": "Layer normalization element",
            "usage": "Transformers, stabilizing training",
            "formula_latex": r"\gamma \frac{x - \mu}{\sigma + \epsilon} + \beta",
        },
    )

    la["batch_norm_elem"] = Expression(
        name="batch_norm_elem",
        symbolic_expr="gamma_bn * (x - mu_batch) / (sqrt(var_batch + 1e-5)) + beta_bn",
        params={"mu_batch": 0, "var_batch": 1, "gamma_bn": 1, "beta_bn": 0},
        trainable_params=["gamma_bn", "beta_bn"],
        metadata={
            "category": "linalg",
            "subcategory": "normalization",
            "description": "Batch normalization element",
            "usage": "CNNs, stabilizing deep network training",
            "formula_latex": r"\gamma \frac{x - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} + \beta",
        },
    )

    return la

Metrics

neurogebra.repository.metrics.get_metrics_expressions()

Get dictionary of evaluation metric expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/metrics.py
def get_metrics_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of evaluation metric expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    met: Dict[str, Expression] = {}

    # ================================================================
    # Classification Metrics (element-wise / scalar forms)
    # ================================================================

    met["accuracy_elem"] = Expression(
        name="accuracy_elem",
        symbolic_expr="Piecewise((1, Eq(y_pred, y_true)), (0, True))",
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Element-wise accuracy indicator (1 if correct)",
            "usage": "Classification evaluation, per-sample correctness",
            "formula_latex": r"\mathbb{1}[\hat{y}_i = y_i]",
        },
    )

    met["precision_formula"] = Expression(
        name="precision_formula",
        symbolic_expr="tp / (tp + fp + 1e-8)",
        params={"tp": 1, "fp": 0},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Precision = TP / (TP + FP)",
            "usage": "When false positives are costly (spam detection)",
            "formula_latex": r"\text{Precision} = \frac{TP}{TP + FP}",
        },
    )

    met["recall_formula"] = Expression(
        name="recall_formula",
        symbolic_expr="tp / (tp + fn + 1e-8)",
        params={"tp": 1, "fn": 0},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Recall (Sensitivity) = TP / (TP + FN)",
            "usage": "When false negatives are costly (medical diagnosis)",
            "formula_latex": r"\text{Recall} = \frac{TP}{TP + FN}",
        },
    )

    met["f1_score_formula"] = Expression(
        name="f1_score_formula",
        symbolic_expr="2 * prec * rec / (prec + rec + 1e-8)",
        params={"prec": 1, "rec": 1},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "F1 score: harmonic mean of precision and recall",
            "usage": "Balanced classification evaluation",
            "formula_latex": r"F_1 = \frac{2 \cdot P \cdot R}{P + R}",
        },
    )

    met["fbeta_score_formula"] = Expression(
        name="fbeta_score_formula",
        symbolic_expr="(1 + beta_f**2) * prec * rec / (beta_f**2 * prec + rec + 1e-8)",
        params={"prec": 1, "rec": 1, "beta_f": 1},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Fβ score: weighted harmonic mean (β>1 favors recall)",
            "usage": "Custom precision-recall tradeoff",
            "formula_latex": r"F_\beta = \frac{(1+\beta^2) P R}{\beta^2 P + R}",
        },
    )

    met["specificity_formula"] = Expression(
        name="specificity_formula",
        symbolic_expr="tn / (tn + fp + 1e-8)",
        params={"tn": 1, "fp": 0},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Specificity (True Negative Rate) = TN / (TN + FP)",
            "usage": "Complementary to recall for binary classification",
            "formula_latex": r"\text{Specificity} = \frac{TN}{TN + FP}",
        },
    )

    met["balanced_accuracy"] = Expression(
        name="balanced_accuracy",
        symbolic_expr="(sensitivity + specificity) / 2",
        params={"sensitivity": 1, "specificity": 1},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Balanced accuracy (average of TPR and TNR)",
            "usage": "Imbalanced datasets",
            "formula_latex": r"\frac{TPR + TNR}{2}",
        },
    )

    met["matthews_corr"] = Expression(
        name="matthews_corr",
        symbolic_expr="(tp * tn - fp * fn) / (sqrt((tp + fp)*(tp + fn)*(tn + fp)*(tn + fn)) + 1e-8)",
        params={"tp": 1, "tn": 1, "fp": 0, "fn": 0},
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Matthews Correlation Coefficient (MCC)",
            "usage": "Balanced metric even with imbalanced classes",
            "formula_latex": r"MCC = \frac{TP \cdot TN - FP \cdot FN}{\sqrt{(TP+FP)(TP+FN)(TN+FP)(TN+FN)}}",
        },
    )

    met["log_loss_elem"] = Expression(
        name="log_loss_elem",
        symbolic_expr="-(y * log(p + 1e-15) + (1 - y) * log(1 - p + 1e-15))",
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Element-wise log loss (binary cross-entropy)",
            "usage": "Probabilistic classification evaluation",
            "formula_latex": r"-[y\ln p + (1-y)\ln(1-p)]",
        },
    )

    met["brier_score_elem"] = Expression(
        name="brier_score_elem",
        symbolic_expr="(p - y)**2",
        metadata={
            "category": "metric",
            "subcategory": "classification",
            "description": "Brier score element (probability calibration)",
            "usage": "Assessing calibration of probabilistic predictions",
            "formula_latex": r"(p_i - y_i)^2",
        },
    )

    # ================================================================
    # Regression Metrics
    # ================================================================

    met["mse_elem"] = Expression(
        name="mse_elem",
        symbolic_expr="(y_pred - y_true)**2",
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Mean Squared Error element",
            "usage": "Standard regression evaluation",
            "formula_latex": r"(\hat{y}_i - y_i)^2",
        },
    )

    met["mae_elem"] = Expression(
        name="mae_elem",
        symbolic_expr="Abs(y_pred - y_true)",
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Mean Absolute Error element",
            "usage": "Robust regression evaluation",
            "formula_latex": r"|\hat{y}_i - y_i|",
        },
    )

    met["rmse_formula"] = Expression(
        name="rmse_formula",
        symbolic_expr="sqrt(mse_val)",
        params={"mse_val": 1},
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Root Mean Squared Error from MSE value",
            "usage": "Regression metric in original units",
            "formula_latex": r"RMSE = \sqrt{MSE}",
        },
    )

    met["r_squared"] = Expression(
        name="r_squared",
        symbolic_expr="1 - ss_res / (ss_tot + 1e-8)",
        params={"ss_res": 0, "ss_tot": 1},
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "R² coefficient of determination",
            "usage": "Proportion of variance explained by model",
            "formula_latex": r"R^2 = 1 - \frac{SS_{res}}{SS_{tot}}",
        },
    )

    met["adjusted_r_squared"] = Expression(
        name="adjusted_r_squared",
        symbolic_expr="1 - (1 - r2) * (n_samples - 1) / (n_samples - n_features - 1)",
        params={"r2": 0.9, "n_samples": 100, "n_features": 5},
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Adjusted R² (penalizes extra features)",
            "usage": "Feature selection, model comparison",
            "formula_latex": r"R^2_{adj} = 1 - (1-R^2)\frac{n-1}{n-p-1}",
        },
    )

    met["mape_elem"] = Expression(
        name="mape_elem",
        symbolic_expr="Abs((y_true - y_pred) / (y_true + 1e-8))",
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Mean Absolute Percentage Error element",
            "usage": "Relative error metric, forecasting",
            "formula_latex": r"\left|\frac{y_i - \hat{y}_i}{y_i}\right|",
        },
    )

    met["smape_elem"] = Expression(
        name="smape_elem",
        symbolic_expr="Abs(y_pred - y_true) / ((Abs(y_pred) + Abs(y_true)) / 2 + 1e-8)",
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Symmetric MAPE element",
            "usage": "Balanced percentage error, avoids MAPE asymmetry",
            "formula_latex": r"\frac{|\hat{y}-y|}{(|\hat{y}|+|y|)/2}",
        },
    )

    met["huber_loss_elem"] = Expression(
        name="huber_loss_elem",
        symbolic_expr="Piecewise(((y_pred - y_true)**2 / 2, Abs(y_pred - y_true) <= delta), (delta * (Abs(y_pred - y_true) - delta / 2), True))",
        params={"delta": 1.0},
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Huber loss element (robust to outliers)",
            "usage": "When data has outliers, DQN training",
            "formula_latex": r"\begin{cases} \frac{(y-\hat{y})^2}{2} & |y-\hat{y}|\le\delta \\ \delta(|y-\hat{y}|-\delta/2) & \text{otherwise} \end{cases}",
        },
    )

    met["explained_variance_ratio"] = Expression(
        name="explained_variance_ratio",
        symbolic_expr="lambda_i / (total_variance + 1e-8)",
        params={"lambda_i": 1, "total_variance": 10},
        metadata={
            "category": "metric",
            "subcategory": "regression",
            "description": "Explained variance ratio (PCA eigenvalue fraction)",
            "usage": "PCA component importance, dimensionality reduction",
            "formula_latex": r"\frac{\lambda_i}{\sum_j \lambda_j}",
        },
    )

    # ================================================================
    # Similarity & Set Metrics
    # ================================================================

    met["jaccard_index"] = Expression(
        name="jaccard_index",
        symbolic_expr="n_intersect / (n_union + 1e-8)",
        params={"n_intersect": 1, "n_union": 1},
        metadata={
            "category": "metric",
            "subcategory": "similarity",
            "description": "Jaccard index (Intersection over Union)",
            "usage": "Object detection (IoU), set similarity",
            "formula_latex": r"J = \frac{|A \cap B|}{|A \cup B|}",
        },
    )

    met["dice_coefficient"] = Expression(
        name="dice_coefficient",
        symbolic_expr="2 * n_intersect / (size_a + size_b + 1e-8)",
        params={"n_intersect": 1, "size_a": 1, "size_b": 1},
        metadata={
            "category": "metric",
            "subcategory": "similarity",
            "description": "Dice coefficient (F1 for sets)",
            "usage": "Image segmentation, medical imaging",
            "formula_latex": r"DSC = \frac{2|A \cap B|}{|A| + |B|}",
        },
    )

    met["cosine_distance_metric"] = Expression(
        name="cosine_distance_metric",
        symbolic_expr="1 - dot_ab / (norm_a * norm_b + 1e-8)",
        params={"dot_ab": 1, "norm_a": 1, "norm_b": 1},
        metadata={
            "category": "metric",
            "subcategory": "similarity",
            "description": "Cosine distance = 1 - cosine similarity",
            "usage": "NLP embedding distance, recommendation",
            "formula_latex": r"d = 1 - \frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{a}\|\|\mathbf{b}\|}",
        },
    )

    # ================================================================
    # Ranking Metrics
    # ================================================================

    met["reciprocal_rank"] = Expression(
        name="reciprocal_rank",
        symbolic_expr="1 / rank",
        params={"rank": 1},
        metadata={
            "category": "metric",
            "subcategory": "ranking",
            "description": "Reciprocal Rank: 1/rank of first relevant result",
            "usage": "Information retrieval, search engine evaluation",
            "formula_latex": r"RR = \frac{1}{\text{rank}}",
        },
    )

    met["dcg_elem"] = Expression(
        name="dcg_elem",
        symbolic_expr="rel / log(pos + 1, 2)",
        params={"rel": 1, "pos": 1},
        metadata={
            "category": "metric",
            "subcategory": "ranking",
            "description": "DCG element: relevance / log₂(position + 1)",
            "usage": "Discounted Cumulative Gain for ranking",
            "formula_latex": r"\frac{rel_i}{\log_2(i+1)}",
        },
    )

    met["ndcg_formula"] = Expression(
        name="ndcg_formula",
        symbolic_expr="dcg / (idcg + 1e-8)",
        params={"dcg": 1, "idcg": 1},
        metadata={
            "category": "metric",
            "subcategory": "ranking",
            "description": "Normalized DCG = DCG / ideal DCG",
            "usage": "Comparing ranked lists, search quality",
            "formula_latex": r"nDCG = \frac{DCG}{IDCG}",
        },
    )

    # ================================================================
    # Information Criteria (Model Selection)
    # ================================================================

    met["aic"] = Expression(
        name="aic",
        symbolic_expr="2 * k - 2 * log_likelihood",
        params={"k": 1, "log_likelihood": 0},
        metadata={
            "category": "metric",
            "subcategory": "model_selection",
            "description": "Akaike Information Criterion",
            "usage": "Model selection, complexity-accuracy tradeoff",
            "formula_latex": r"AIC = 2k - 2\ln(\hat{L})",
        },
    )

    met["bic"] = Expression(
        name="bic",
        symbolic_expr="k * log(n_obs) - 2 * log_likelihood",
        params={"k": 1, "n_obs": 100, "log_likelihood": 0},
        metadata={
            "category": "metric",
            "subcategory": "model_selection",
            "description": "Bayesian Information Criterion",
            "usage": "Model selection (stronger penalty for complexity)",
            "formula_latex": r"BIC = k\ln(n) - 2\ln(\hat{L})",
        },
    )

    return met

Transforms

neurogebra.repository.transforms.get_transforms_expressions()

Get dictionary of transform expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/transforms.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
def get_transforms_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of transform expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    tf: Dict[str, Expression] = {}

    # ================================================================
    # Normalization Transforms
    # ================================================================

    tf["min_max_normalize"] = Expression(
        name="min_max_normalize",
        symbolic_expr="(x - x_min) / (x_max - x_min + 1e-8)",
        params={"x_min": 0, "x_max": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "Min-Max normalization to [0, 1]",
            "usage": "Feature scaling, image pixel normalization",
            "formula_latex": r"\frac{x - x_{min}}{x_{max} - x_{min}}",
        },
    )

    tf["min_max_to_range"] = Expression(
        name="min_max_to_range",
        symbolic_expr="a_range + (x - x_min) * (b_range - a_range) / (x_max - x_min + 1e-8)",
        params={"x_min": 0, "x_max": 1, "a_range": -1, "b_range": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "Min-Max normalization to [a, b]",
            "usage": "Scaling to arbitrary range (e.g., [-1, 1])",
            "formula_latex": r"a + \frac{(x - x_{min})(b - a)}{x_{max} - x_{min}}",
        },
    )

    tf["z_score_normalize"] = Expression(
        name="z_score_normalize",
        symbolic_expr="(x - mu) / (sigma + 1e-8)",
        params={"mu": 0, "sigma": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "Z-score standardization (mean=0, std=1)",
            "usage": "Most common preprocessing, SVM, logistic regression",
            "formula_latex": r"\frac{x - \mu}{\sigma}",
        },
    )

    tf["robust_scale"] = Expression(
        name="robust_scale",
        symbolic_expr="(x - median_val) / (iqr + 1e-8)",
        params={"median_val": 0, "iqr": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "Robust scaling (median and IQR)",
            "usage": "Data with outliers, robust preprocessing",
            "formula_latex": r"\frac{x - \text{median}}{IQR}",
        },
    )

    tf["l2_normalize"] = Expression(
        name="l2_normalize",
        symbolic_expr="x / (norm_l2 + 1e-8)",
        params={"norm_l2": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "L2 (unit vector) normalization",
            "usage": "Embedding normalization, cosine similarity prep",
            "formula_latex": r"\frac{x}{\|x\|_2}",
        },
    )

    tf["max_abs_scale"] = Expression(
        name="max_abs_scale",
        symbolic_expr="x / (max_abs + 1e-8)",
        params={"max_abs": 1},
        metadata={
            "category": "transform",
            "subcategory": "normalization",
            "description": "MaxAbs scaling (preserves sparsity)",
            "usage": "Sparse data, data already centered at zero",
            "formula_latex": r"\frac{x}{\max|x|}",
        },
    )

    # ================================================================
    # Logarithmic & Power Transforms
    # ================================================================

    tf["log_transform"] = Expression(
        name="log_transform",
        symbolic_expr="log(x + 1)",
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "Log(1+x) transform for right-skewed data",
            "usage": "Count data, income, prices",
            "formula_latex": r"\ln(x + 1)",
        },
    )

    tf["log10_transform"] = Expression(
        name="log10_transform",
        symbolic_expr="log(x + 1, 10)",
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "Log base-10 transform",
            "usage": "Orders of magnitude, scientific data",
            "formula_latex": r"\log_{10}(x + 1)",
        },
    )

    tf["sqrt_transform"] = Expression(
        name="sqrt_transform",
        symbolic_expr="sqrt(x)",
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "Square root transform (mild right-skew fix)",
            "usage": "Count data, variance stabilization",
            "formula_latex": r"\sqrt{x}",
        },
    )

    tf["box_cox_approx"] = Expression(
        name="box_cox_approx",
        symbolic_expr="Piecewise(((x**lambda_bc - 1) / lambda_bc, lambda_bc != 0), (log(x), True))",
        params={"lambda_bc": 0.5},
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "Box-Cox transform (power family for normality)",
            "usage": "Making data more Gaussian, regression assumption",
            "formula_latex": r"\frac{x^\lambda - 1}{\lambda}",
        },
    )

    tf["yeo_johnson_positive"] = Expression(
        name="yeo_johnson_positive",
        symbolic_expr="((x + 1)**lambda_yj - 1) / lambda_yj",
        params={"lambda_yj": 0.5},
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "Yeo-Johnson transform (positive x, λ≠0)",
            "usage": "Handles zero and negative values (unlike Box-Cox)",
            "formula_latex": r"\frac{(x+1)^\lambda - 1}{\lambda}",
        },
    )

    tf["power_transform"] = Expression(
        name="power_transform",
        symbolic_expr="x**p_exp",
        params={"p_exp": 2.0},
        metadata={
            "category": "transform",
            "subcategory": "power",
            "description": "General power transform x^p",
            "usage": "Feature engineering, polynomial features",
            "formula_latex": r"x^p",
        },
    )

    # ================================================================
    # Activation-Style Transforms (used in preprocessing)
    # ================================================================

    tf["sigmoid_transform"] = Expression(
        name="sigmoid_transform",
        symbolic_expr="1 / (1 + exp(-k_sig * (x - x0_sig)))",
        params={"k_sig": 1, "x0_sig": 0},
        metadata={
            "category": "transform",
            "subcategory": "nonlinear",
            "description": "Sigmoid squashing to (0, 1)",
            "usage": "Probability calibration, soft thresholding",
            "formula_latex": r"\frac{1}{1 + e^{-k(x-x_0)}}",
        },
    )

    tf["tanh_transform"] = Expression(
        name="tanh_transform",
        symbolic_expr="tanh(x)",
        metadata={
            "category": "transform",
            "subcategory": "nonlinear",
            "description": "Tanh squashing to (-1, 1)",
            "usage": "Centering with bounded output",
            "formula_latex": r"\tanh(x)",
        },
    )

    tf["softplus_transform"] = Expression(
        name="softplus_transform",
        symbolic_expr="log(1 + exp(x))",
        metadata={
            "category": "transform",
            "subcategory": "nonlinear",
            "description": "Softplus: smooth approximation to ReLU",
            "usage": "Ensuring positive outputs, smooth activation",
            "formula_latex": r"\ln(1 + e^x)",
        },
    )

    tf["softsign_transform"] = Expression(
        name="softsign_transform",
        symbolic_expr="x / (1 + Abs(x))",
        metadata={
            "category": "transform",
            "subcategory": "nonlinear",
            "description": "Softsign: slower saturation than tanh",
            "usage": "Alternative to tanh, polynomial decay",
            "formula_latex": r"\frac{x}{1 + |x|}",
        },
    )

    # ================================================================
    # Clipping & Quantization
    # ================================================================

    tf["clip_transform"] = Expression(
        name="clip_transform",
        symbolic_expr="Max(low, Min(x, high))",
        params={"low": 0, "high": 1},
        metadata={
            "category": "transform",
            "subcategory": "clipping",
            "description": "Clip (clamp) values to [low, high]",
            "usage": "Gradient clipping, pixel range enforcement",
            "formula_latex": r"\text{clip}(x, a, b)",
        },
    )

    tf["winsorize"] = Expression(
        name="winsorize",
        symbolic_expr="Max(lower_pct, Min(x, upper_pct))",
        params={"lower_pct": -3, "upper_pct": 3},
        metadata={
            "category": "transform",
            "subcategory": "clipping",
            "description": "Winsorize (clip to percentile bounds)",
            "usage": "Outlier handling, robust statistics",
            "formula_latex": r"\text{clip}(x, q_{lo}, q_{hi})",
        },
    )

    # ================================================================
    # Encoding Transforms
    # ================================================================

    tf["thermometer_bit"] = Expression(
        name="thermometer_bit",
        symbolic_expr="Piecewise((1, x >= threshold), (0, True))",
        params={"threshold": 0.5},
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Thermometer encoding bit (1 if x ≥ threshold)",
            "usage": "Thermometer encoding, binary discretization",
            "formula_latex": r"\mathbb{1}[x \ge \theta]",
        },
    )

    tf["gaussian_basis"] = Expression(
        name="gaussian_basis",
        symbolic_expr="exp(-((x - center)**2) / (2 * width**2))",
        params={"center": 0, "width": 1},
        trainable_params=["center", "width"],
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Gaussian radial basis function",
            "usage": "RBF feature encoding, kernel approximation",
            "formula_latex": r"e^{-\frac{(x-c)^2}{2w^2}}",
        },
    )

    tf["fourier_feature_sin"] = Expression(
        name="fourier_feature_sin",
        symbolic_expr="sin(2 * pi * freq * x)",
        params={"freq": 1},
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Sine component of random Fourier feature",
            "usage": "Random Fourier features, positional encoding",
            "formula_latex": r"\sin(2\pi f x)",
        },
    )

    tf["fourier_feature_cos"] = Expression(
        name="fourier_feature_cos",
        symbolic_expr="cos(2 * pi * freq * x)",
        params={"freq": 1},
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Cosine component of random Fourier feature",
            "usage": "Random Fourier features, positional encoding",
            "formula_latex": r"\cos(2\pi f x)",
        },
    )

    tf["positional_enc_sin"] = Expression(
        name="positional_enc_sin",
        symbolic_expr="sin(pos / (10000**(2*i / d_model)))",
        params={"pos": 1, "i": 0, "d_model": 512},
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Transformer positional encoding (sine component)",
            "usage": "Transformer input embeddings",
            "formula_latex": r"PE(pos,2i) = \sin\frac{pos}{10000^{2i/d}}",
        },
    )

    tf["positional_enc_cos"] = Expression(
        name="positional_enc_cos",
        symbolic_expr="cos(pos / (10000**(2*i / d_model)))",
        params={"pos": 1, "i": 0, "d_model": 512},
        metadata={
            "category": "transform",
            "subcategory": "encoding",
            "description": "Transformer positional encoding (cosine component)",
            "usage": "Transformer input embeddings",
            "formula_latex": r"PE(pos,2i+1) = \cos\frac{pos}{10000^{2i/d}}",
        },
    )

    # ================================================================
    # Signal Processing Transforms
    # ================================================================

    tf["moving_average_weight"] = Expression(
        name="moving_average_weight",
        symbolic_expr="1 / window_size",
        params={"window_size": 5},
        metadata={
            "category": "transform",
            "subcategory": "signal",
            "description": "Uniform moving average weight",
            "usage": "Smoothing time series, noise reduction",
            "formula_latex": r"\frac{1}{k}",
        },
    )

    tf["exponential_smoothing"] = Expression(
        name="exponential_smoothing",
        symbolic_expr="alpha_es * x + (1 - alpha_es) * s_prev",
        params={"alpha_es": 0.3, "s_prev": 0},
        metadata={
            "category": "transform",
            "subcategory": "signal",
            "description": "Exponential smoothing update",
            "usage": "Time series forecasting, EMA indicators",
            "formula_latex": r"s_t = \alpha x_t + (1-\alpha) s_{t-1}",
        },
    )

    tf["difference_transform"] = Expression(
        name="difference_transform",
        symbolic_expr="x - x_prev",
        params={"x_prev": 0},
        metadata={
            "category": "transform",
            "subcategory": "signal",
            "description": "First-order differencing",
            "usage": "Making time series stationary, detrending",
            "formula_latex": r"\Delta x_t = x_t - x_{t-1}",
        },
    )

    tf["log_return"] = Expression(
        name="log_return",
        symbolic_expr="log(x / (x_prev + 1e-8))",
        params={"x_prev": 1},
        metadata={
            "category": "transform",
            "subcategory": "signal",
            "description": "Log return (financial time series)",
            "usage": "Stock prices, financial modeling",
            "formula_latex": r"r_t = \ln\frac{P_t}{P_{t-1}}",
        },
    )

    # ================================================================
    # Weight Initialization Formulas
    # ================================================================

    tf["xavier_uniform_bound"] = Expression(
        name="xavier_uniform_bound",
        symbolic_expr="sqrt(6 / (fan_in + fan_out))",
        params={"fan_in": 256, "fan_out": 256},
        metadata={
            "category": "transform",
            "subcategory": "initialization",
            "description": "Xavier/Glorot uniform init bound",
            "usage": "Linear/sigmoid layer initialization",
            "formula_latex": r"\text{bound} = \sqrt{\frac{6}{n_{in}+n_{out}}}",
        },
    )

    tf["xavier_normal_std"] = Expression(
        name="xavier_normal_std",
        symbolic_expr="sqrt(2 / (fan_in + fan_out))",
        params={"fan_in": 256, "fan_out": 256},
        metadata={
            "category": "transform",
            "subcategory": "initialization",
            "description": "Xavier/Glorot normal init standard deviation",
            "usage": "Linear/sigmoid layer initialization",
            "formula_latex": r"\sigma = \sqrt{\frac{2}{n_{in}+n_{out}}}",
        },
    )

    tf["he_uniform_bound"] = Expression(
        name="he_uniform_bound",
        symbolic_expr="sqrt(6 / fan_in)",
        params={"fan_in": 256},
        metadata={
            "category": "transform",
            "subcategory": "initialization",
            "description": "He/Kaiming uniform init bound",
            "usage": "ReLU layer initialization",
            "formula_latex": r"\text{bound} = \sqrt{\frac{6}{n_{in}}}",
        },
    )

    tf["he_normal_std"] = Expression(
        name="he_normal_std",
        symbolic_expr="sqrt(2 / fan_in)",
        params={"fan_in": 256},
        metadata={
            "category": "transform",
            "subcategory": "initialization",
            "description": "He/Kaiming normal init standard deviation",
            "usage": "ReLU layer initialization",
            "formula_latex": r"\sigma = \sqrt{\frac{2}{n_{in}}}",
        },
    )

    tf["lecun_normal_std"] = Expression(
        name="lecun_normal_std",
        symbolic_expr="sqrt(1 / fan_in)",
        params={"fan_in": 256},
        metadata={
            "category": "transform",
            "subcategory": "initialization",
            "description": "LeCun normal init standard deviation",
            "usage": "SELU activation, self-normalizing networks",
            "formula_latex": r"\sigma = \sqrt{\frac{1}{n_{in}}}",
        },
    )

    return tf

Optimization

neurogebra.repository.optimization.get_optimization_expressions()

Get dictionary of optimization expressions.

Returns:

Type Description
Dict[str, Expression]

Dictionary mapping names to Expression instances

Source code in neurogebra/repository/optimization.py
def get_optimization_expressions() -> Dict[str, Expression]:
    """
    Get dictionary of optimization expressions.

    Returns:
        Dictionary mapping names to Expression instances
    """
    opt: Dict[str, Expression] = {}

    # ================================================================
    # Gradient-Based Optimizer Update Rules
    # ================================================================

    opt["sgd_step"] = Expression(
        name="sgd_step",
        symbolic_expr="w - lr * grad",
        params={"lr": 0.01},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Stochastic Gradient Descent: w ← w - η∇L",
            "usage": "Basic optimization, baseline training",
            "pros": ["Simple", "Low memory", "Well-understood"],
            "cons": ["Slow convergence", "Sensitive to learning rate"],
            "formula_latex": r"w_{t+1} = w_t - \eta \nabla L",
        },
    )

    opt["momentum_step"] = Expression(
        name="momentum_step",
        symbolic_expr="w - (mu_momentum * v + lr * grad)",
        params={"lr": 0.01, "mu_momentum": 0.9, "v": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "SGD with Momentum: v ← μv + η∇L, w ← w - v",
            "usage": "Faster convergence, escaping local minima",
            "pros": ["Accelerates in consistent gradient direction"],
            "cons": ["Extra hyperparameter μ"],
            "formula_latex": r"v_{t+1} = \mu v_t + \eta \nabla L;\; w_{t+1} = w_t - v_{t+1}",
        },
    )

    opt["nesterov_step"] = Expression(
        name="nesterov_step",
        symbolic_expr="w - (mu_momentum * v + lr * grad_lookahead)",
        params={"lr": 0.01, "mu_momentum": 0.9, "v": 0, "grad_lookahead": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Nesterov Accelerated Gradient",
            "usage": "Improved momentum, better convergence",
            "pros": ["Look-ahead gradient correction", "Faster convergence"],
            "formula_latex": r"v_{t+1} = \mu v_t + \eta \nabla L(w_t - \mu v_t);\; w_{t+1} = w_t - v_{t+1}",
        },
    )

    opt["adagrad_step"] = Expression(
        name="adagrad_step",
        symbolic_expr="w - lr * grad / (sqrt(G + 1e-8))",
        params={"lr": 0.01, "G": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "AdaGrad: adaptive per-parameter learning rate",
            "usage": "Sparse data, NLP embeddings",
            "pros": ["Adapts to parameter frequency", "Good for sparse gradients"],
            "cons": ["Learning rate monotonically decreases"],
            "formula_latex": r"w_{t+1} = w_t - \frac{\eta}{\sqrt{G_t + \epsilon}} \nabla L",
        },
    )

    opt["rmsprop_step"] = Expression(
        name="rmsprop_step",
        symbolic_expr="w - lr * grad / (sqrt(v_rms + 1e-8))",
        params={"lr": 0.001, "v_rms": 0, "rho": 0.9},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "RMSProp: running average of squared gradients",
            "usage": "RNNs, non-stationary objectives",
            "pros": ["Fixes AdaGrad decay", "Adapts to recent gradients"],
            "formula_latex": r"v_t = \rho v_{t-1} + (1-\rho)(\nabla L)^2;\; w_{t+1} = w_t - \frac{\eta}{\sqrt{v_t+\epsilon}} \nabla L",
        },
    )

    opt["adam_step"] = Expression(
        name="adam_step",
        symbolic_expr="w - lr * m_hat / (sqrt(v_hat) + 1e-8)",
        params={"lr": 0.001, "m_hat": 0, "v_hat": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Adam: adaptive moments (most popular optimizer)",
            "usage": "Default choice for deep learning",
            "pros": ["Adaptive LR", "Momentum", "Bias correction"],
            "formula_latex": r"w_{t+1} = w_t - \frac{\eta \hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}",
        },
    )

    opt["adamw_step"] = Expression(
        name="adamw_step",
        symbolic_expr="w * (1 - lr * lambda_wd) - lr * m_hat / (sqrt(v_hat) + 1e-8)",
        params={"lr": 0.001, "m_hat": 0, "v_hat": 0, "lambda_wd": 0.01},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "AdamW: Adam with decoupled weight decay",
            "usage": "Transformers, modern deep learning",
            "pros": ["Better generalization than Adam", "Correct weight decay"],
            "formula_latex": r"w_{t+1} = w_t(1-\eta\lambda) - \frac{\eta\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon}",
        },
    )

    opt["adam_m_update"] = Expression(
        name="adam_m_update",
        symbolic_expr="beta1 * m + (1 - beta1) * grad",
        params={"beta1": 0.9, "m": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Adam first moment (mean) update",
            "usage": "Component of Adam optimizer",
            "formula_latex": r"m_t = \beta_1 m_{t-1} + (1-\beta_1)\nabla L",
        },
    )

    opt["adam_v_update"] = Expression(
        name="adam_v_update",
        symbolic_expr="beta2 * v_adam + (1 - beta2) * grad**2",
        params={"beta2": 0.999, "v_adam": 0},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Adam second moment (variance) update",
            "usage": "Component of Adam optimizer",
            "formula_latex": r"v_t = \beta_2 v_{t-1} + (1-\beta_2)(\nabla L)^2",
        },
    )

    opt["adam_bias_correction_m"] = Expression(
        name="adam_bias_correction_m",
        symbolic_expr="m / (1 - beta1**t)",
        params={"beta1": 0.9, "m": 0, "t": 1},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Adam first moment bias correction",
            "usage": "Component of Adam optimizer",
            "formula_latex": r"\hat{m}_t = \frac{m_t}{1-\beta_1^t}",
        },
    )

    opt["adam_bias_correction_v"] = Expression(
        name="adam_bias_correction_v",
        symbolic_expr="v_adam / (1 - beta2**t)",
        params={"beta2": 0.999, "v_adam": 0, "t": 1},
        metadata={
            "category": "optimization",
            "subcategory": "optimizer",
            "description": "Adam second moment bias correction",
            "usage": "Component of Adam optimizer",
            "formula_latex": r"\hat{v}_t = \frac{v_t}{1-\beta_2^t}",
        },
    )

    # ================================================================
    # Learning Rate Schedules
    # ================================================================

    opt["constant_lr"] = Expression(
        name="constant_lr",
        symbolic_expr="lr_init",
        params={"lr_init": 0.001},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Constant learning rate (baseline)",
            "usage": "Simple training, debugging",
            "formula_latex": r"\eta_t = \eta_0",
        },
    )

    opt["step_decay_lr"] = Expression(
        name="step_decay_lr",
        symbolic_expr="lr_init * gamma_decay**floor(epoch / step_size)",
        params={"lr_init": 0.01, "gamma_decay": 0.1, "step_size": 30},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Step decay: reduce LR by factor every N epochs",
            "usage": "Image classification (ResNet schedule)",
            "formula_latex": r"\eta_t = \eta_0 \cdot \gamma^{\lfloor t/s \rfloor}",
        },
    )

    opt["exponential_decay_lr"] = Expression(
        name="exponential_decay_lr",
        symbolic_expr="lr_init * exp(-k_decay * epoch)",
        params={"lr_init": 0.01, "k_decay": 0.01},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Exponential decay learning rate",
            "usage": "Smooth LR reduction over training",
            "formula_latex": r"\eta_t = \eta_0 e^{-kt}",
        },
    )

    opt["cosine_annealing_lr"] = Expression(
        name="cosine_annealing_lr",
        symbolic_expr="lr_min + (lr_max - lr_min) * (1 + cos(pi * epoch / T_max)) / 2",
        params={"lr_min": 1e-6, "lr_max": 0.01, "T_max": 100},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Cosine annealing schedule (warm restarts capable)",
            "usage": "State-of-the-art training, SGDR",
            "formula_latex": r"\eta_t = \eta_{min} + \frac{\eta_{max}-\eta_{min}}{2}(1+\cos\frac{\pi t}{T_{max}})",
        },
    )

    opt["warmup_linear_lr"] = Expression(
        name="warmup_linear_lr",
        symbolic_expr="lr_target * Min(1, epoch / warmup_steps)",
        params={"lr_target": 0.001, "warmup_steps": 1000},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Linear warmup (ramp up, then constant)",
            "usage": "Transformers, large batch training",
            "formula_latex": r"\eta_t = \eta_{target} \cdot \min(1, t/t_{warmup})",
        },
    )

    opt["polynomial_decay_lr"] = Expression(
        name="polynomial_decay_lr",
        symbolic_expr="(lr_init - lr_end) * (1 - epoch / max_epochs)**power_lr + lr_end",
        params={"lr_init": 0.01, "lr_end": 1e-6, "max_epochs": 100, "power_lr": 1},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Polynomial decay learning rate",
            "usage": "Object detection, semantic segmentation",
            "formula_latex": r"\eta_t = (\eta_0 - \eta_{end})(1 - t/T)^p + \eta_{end}",
        },
    )

    opt["inverse_sqrt_lr"] = Expression(
        name="inverse_sqrt_lr",
        symbolic_expr="lr_init / sqrt(Max(epoch, warmup_steps))",
        params={"lr_init": 0.01, "warmup_steps": 4000},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Inverse square root schedule (Transformer original)",
            "usage": "Attention Is All You Need schedule",
            "formula_latex": r"\eta_t = \frac{\eta_0}{\sqrt{\max(t, t_w)}}",
        },
    )

    opt["cyclical_lr"] = Expression(
        name="cyclical_lr",
        symbolic_expr="lr_min + (lr_max - lr_min) * Abs(2 * (epoch / (2 * step_size) - floor(epoch / (2 * step_size) + Rational(1,2))))",
        params={"lr_min": 1e-4, "lr_max": 0.01, "step_size": 2000},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "Cyclical learning rate (triangular policy)",
            "usage": "Super-convergence, finding optimal LR range",
            "formula_latex": r"\eta_t = \eta_{min} + (\eta_{max}-\eta_{min}) \cdot \text{tri}(t)",
        },
    )

    opt["one_cycle_approx_lr"] = Expression(
        name="one_cycle_approx_lr",
        symbolic_expr="lr_max * (1 + cos(pi * epoch / T_max)) / 2",
        params={"lr_max": 0.01, "T_max": 100},
        metadata={
            "category": "optimization",
            "subcategory": "lr_schedule",
            "description": "1-cycle policy approximation (cosine phase)",
            "usage": "Fast training with super-convergence",
            "formula_latex": r"\eta_t \approx \frac{\eta_{max}}{2}(1 + \cos\frac{\pi t}{T})",
        },
    )

    # ================================================================
    # Gradient Clipping & Processing
    # ================================================================

    opt["gradient_clip_value"] = Expression(
        name="gradient_clip_value",
        symbolic_expr="Max(Min(grad, clip_val), -clip_val)",
        params={"clip_val": 1.0},
        metadata={
            "category": "optimization",
            "subcategory": "gradient_processing",
            "description": "Gradient clipping by value",
            "usage": "Prevent exploding gradients in RNNs",
            "formula_latex": r"\text{clip}(g, -c, c)",
        },
    )

    opt["gradient_clip_norm"] = Expression(
        name="gradient_clip_norm",
        symbolic_expr="grad * Min(1, max_norm / (grad_norm + 1e-8))",
        params={"max_norm": 1.0, "grad_norm": 1.0},
        metadata={
            "category": "optimization",
            "subcategory": "gradient_processing",
            "description": "Gradient clipping by global norm",
            "usage": "Standard gradient clipping for deep networks",
            "formula_latex": r"g \cdot \min(1, \frac{c}{\|g\|})",
        },
    )

    opt["ema_update"] = Expression(
        name="ema_update",
        symbolic_expr="alpha_ema * param_new + (1 - alpha_ema) * param_ema",
        params={"alpha_ema": 0.001},
        metadata={
            "category": "optimization",
            "subcategory": "gradient_processing",
            "description": "Exponential Moving Average parameter update",
            "usage": "Model averaging, Polyak averaging, EMA models",
            "formula_latex": r"\bar{\theta}_t = \alpha\theta_t + (1-\alpha)\bar{\theta}_{t-1}",
        },
    )

    # ================================================================
    # Loss Landscape & Convergence
    # ================================================================

    opt["quadratic_bowl"] = Expression(
        name="quadratic_bowl",
        symbolic_expr="a_qb * (x - x_opt)**2 + b_qb * (y - y_opt)**2",
        params={"a_qb": 1, "b_qb": 1, "x_opt": 0, "y_opt": 0},
        metadata={
            "category": "optimization",
            "subcategory": "landscape",
            "description": "Quadratic bowl (simple convex surface)",
            "usage": "Visualizing optimizer trajectories",
            "formula_latex": r"f(x,y) = a(x-x^*)^2 + b(y-y^*)^2",
        },
    )

    opt["rosenbrock"] = Expression(
        name="rosenbrock",
        symbolic_expr="(a_rb - x)**2 + b_rb * (y - x**2)**2",
        params={"a_rb": 1, "b_rb": 100},
        metadata={
            "category": "optimization",
            "subcategory": "landscape",
            "description": "Rosenbrock function (banana function)",
            "usage": "Optimizer benchmarking, non-convex optimization testing",
            "formula_latex": r"f(x,y) = (a-x)^2 + b(y-x^2)^2",
        },
    )

    opt["rastrigin_2d"] = Expression(
        name="rastrigin_2d",
        symbolic_expr="20 + (x**2 - 10*cos(2*pi*x)) + (y**2 - 10*cos(2*pi*y))",
        metadata={
            "category": "optimization",
            "subcategory": "landscape",
            "description": "Rastrigin function (many local minima)",
            "usage": "Global optimization algorithms, swarm intelligence",
            "formula_latex": r"f(\mathbf{x}) = An + \sum[x_i^2 - A\cos(2\pi x_i)]",
        },
    )

    opt["ackley_2d"] = Expression(
        name="ackley_2d",
        symbolic_expr="-20 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - exp(0.5 * (cos(2*pi*x) + cos(2*pi*y))) + exp(1) + 20",
        metadata={
            "category": "optimization",
            "subcategory": "landscape",
            "description": "Ackley function (nearly flat outer region)",
            "usage": "Testing optimizer escape from flat regions",
            "formula_latex": r"-20e^{-0.2\sqrt{0.5(x^2+y^2)}} - e^{0.5(\cos 2\pi x + \cos 2\pi y)} + e + 20",
        },
    )

    return opt

Visualization

Plotting

neurogebra.viz.plotting

Plotting utilities for Neurogebra.

Provides static plotting functions for expressions using matplotlib.

Classes

Functions

plot_expression(expression, x_range=(-5, 5), n_points=500, title=None, xlabel='x', ylabel='f(x)', figsize=(8, 5), show_grid=True, show_formula=True, ax=None, **eval_kwargs)

Plot a single expression.

Parameters:

Name Type Description Default
expression Expression

Expression to plot

required
x_range Tuple[float, float]

(min, max) range for x-axis

(-5, 5)
n_points int

Number of points to evaluate

500
title Optional[str]

Plot title (defaults to expression name)

None
xlabel str

X-axis label

'x'
ylabel str

Y-axis label

'f(x)'
figsize Tuple[int, int]

Figure size

(8, 5)
show_grid bool

Whether to show grid

True
show_formula bool

Whether to show formula in legend

True
ax Optional[Axes]

Optional matplotlib Axes to plot on

None
**eval_kwargs Any

Additional keyword arguments for eval

{}

Returns:

Type Description
Figure

matplotlib Figure

Source code in neurogebra/viz/plotting.py
def plot_expression(
    expression: Expression,
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    title: Optional[str] = None,
    xlabel: str = "x",
    ylabel: str = "f(x)",
    figsize: Tuple[int, int] = (8, 5),
    show_grid: bool = True,
    show_formula: bool = True,
    ax: Optional[plt.Axes] = None,
    **eval_kwargs: Any,
) -> plt.Figure:
    """
    Plot a single expression.

    Args:
        expression: Expression to plot
        x_range: (min, max) range for x-axis
        n_points: Number of points to evaluate
        title: Plot title (defaults to expression name)
        xlabel: X-axis label
        ylabel: Y-axis label
        figsize: Figure size
        show_grid: Whether to show grid
        show_formula: Whether to show formula in legend
        ax: Optional matplotlib Axes to plot on
        **eval_kwargs: Additional keyword arguments for eval

    Returns:
        matplotlib Figure
    """
    x = np.linspace(x_range[0], x_range[1], n_points)

    # Evaluate expression
    try:
        y = np.array([expression.eval(x=xi, **eval_kwargs) for xi in x])
    except Exception:
        y = np.vectorize(lambda xi: expression.eval(x=xi, **eval_kwargs))(x)

    # Create figure if no axes provided
    if ax is None:
        fig, ax = plt.subplots(figsize=figsize)
    else:
        fig = ax.figure

    label = f"${expression.formula}$" if show_formula else expression.name
    ax.plot(x, y, label=label, linewidth=2)

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_title(title or f"{expression.name}")
    ax.legend(fontsize=9)

    if show_grid:
        ax.grid(True, alpha=0.3)
        ax.axhline(y=0, color="k", linewidth=0.5)
        ax.axvline(x=0, color="k", linewidth=0.5)

    fig.tight_layout()
    return fig

plot_comparison(expressions, x_range=(-5, 5), n_points=500, title='Expression Comparison', figsize=(10, 6), show_grid=True)

Plot multiple expressions on the same axes for comparison.

Parameters:

Name Type Description Default
expressions List[Expression]

List of Expressions to compare

required
x_range Tuple[float, float]

(min, max) range for x-axis

(-5, 5)
n_points int

Number of points

500
title str

Plot title

'Expression Comparison'
figsize Tuple[int, int]

Figure size

(10, 6)
show_grid bool

Whether to show grid

True

Returns:

Type Description
Figure

matplotlib Figure

Source code in neurogebra/viz/plotting.py
def plot_comparison(
    expressions: List[Expression],
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    title: str = "Expression Comparison",
    figsize: Tuple[int, int] = (10, 6),
    show_grid: bool = True,
) -> plt.Figure:
    """
    Plot multiple expressions on the same axes for comparison.

    Args:
        expressions: List of Expressions to compare
        x_range: (min, max) range for x-axis
        n_points: Number of points
        title: Plot title
        figsize: Figure size
        show_grid: Whether to show grid

    Returns:
        matplotlib Figure
    """
    fig, ax = plt.subplots(figsize=figsize)
    x = np.linspace(x_range[0], x_range[1], n_points)

    for expr in expressions:
        try:
            y = np.array([expr.eval(x=xi) for xi in x])
            ax.plot(x, y, label=expr.name, linewidth=2)
        except Exception as e:
            print(f"Warning: Could not plot {expr.name}: {e}")

    ax.set_xlabel("x")
    ax.set_ylabel("f(x)")
    ax.set_title(title)
    ax.legend(fontsize=9)

    if show_grid:
        ax.grid(True, alpha=0.3)
        ax.axhline(y=0, color="k", linewidth=0.5)
        ax.axvline(x=0, color="k", linewidth=0.5)

    fig.tight_layout()
    return fig

plot_gradient(expression, var='x', x_range=(-5, 5), n_points=500, figsize=(10, 5))

Plot expression alongside its gradient.

Parameters:

Name Type Description Default
expression Expression

Expression to plot

required
var str

Variable to differentiate with respect to

'x'
x_range Tuple[float, float]

(min, max) range for x-axis

(-5, 5)
n_points int

Number of points

500
figsize Tuple[int, int]

Figure size

(10, 5)

Returns:

Type Description
Figure

matplotlib Figure

Source code in neurogebra/viz/plotting.py
def plot_gradient(
    expression: Expression,
    var: str = "x",
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    figsize: Tuple[int, int] = (10, 5),
) -> plt.Figure:
    """
    Plot expression alongside its gradient.

    Args:
        expression: Expression to plot
        var: Variable to differentiate with respect to
        x_range: (min, max) range for x-axis
        n_points: Number of points
        figsize: Figure size

    Returns:
        matplotlib Figure
    """
    grad_expr = expression.gradient(var)

    fig, axes = plt.subplots(1, 2, figsize=figsize)

    x = np.linspace(x_range[0], x_range[1], n_points)

    # Plot original
    y = np.array([expression.eval(x=xi) for xi in x])
    axes[0].plot(x, y, "b-", linewidth=2)
    axes[0].set_title(f"{expression.name}")
    axes[0].set_xlabel("x")
    axes[0].set_ylabel("f(x)")
    axes[0].grid(True, alpha=0.3)

    # Plot gradient
    dy = np.array([grad_expr.eval(x=xi) for xi in x])
    axes[1].plot(x, dy, "r-", linewidth=2)
    axes[1].set_title(f"d({expression.name})/d{var}")
    axes[1].set_xlabel("x")
    axes[1].set_ylabel("f'(x)")
    axes[1].grid(True, alpha=0.3)

    fig.tight_layout()
    return fig

plot_training_history(history, figsize=(12, 4))

Plot training history (loss and accuracy).

Parameters:

Name Type Description Default
history Dict[str, List]

Training history dict with 'loss', 'val_loss', 'accuracy', 'val_accuracy' keys

required
figsize Tuple[int, int]

Figure size

(12, 4)

Returns:

Type Description
Figure

matplotlib Figure

Source code in neurogebra/viz/plotting.py
def plot_training_history(
    history: Dict[str, List],
    figsize: Tuple[int, int] = (12, 4),
) -> plt.Figure:
    """
    Plot training history (loss and accuracy).

    Args:
        history: Training history dict with 'loss', 'val_loss',
                 'accuracy', 'val_accuracy' keys
        figsize: Figure size

    Returns:
        matplotlib Figure
    """
    has_val = bool(history.get("val_loss"))
    has_acc = bool(history.get("accuracy"))
    n_plots = 1 + int(has_acc)

    fig, axes = plt.subplots(1, n_plots, figsize=figsize)
    if n_plots == 1:
        axes = [axes]

    epochs = range(1, len(history["loss"]) + 1)

    # Loss plot
    axes[0].plot(
        epochs, history["loss"], "b-", linewidth=2, label="Training Loss"
    )
    if has_val:
        axes[0].plot(
            epochs,
            history["val_loss"],
            "r--",
            linewidth=2,
            label="Validation Loss",
        )
    axes[0].set_xlabel("Epoch")
    axes[0].set_ylabel("Loss")
    axes[0].set_title("Training Progress")
    axes[0].legend()
    axes[0].grid(True, alpha=0.3)

    # Accuracy plot
    if has_acc:
        axes[1].plot(
            epochs,
            history["accuracy"],
            "b-",
            linewidth=2,
            label="Training Acc",
        )
        if history.get("val_accuracy"):
            axes[1].plot(
                epochs,
                history["val_accuracy"],
                "r--",
                linewidth=2,
                label="Validation Acc",
            )
        axes[1].set_xlabel("Epoch")
        axes[1].set_ylabel("Accuracy")
        axes[1].set_title("Model Accuracy")
        axes[1].legend()
        axes[1].grid(True, alpha=0.3)

    fig.tight_layout()
    return fig

Interactive

neurogebra.viz.interactive

Interactive visualization tools for Neurogebra.

Provides interactive plotting using plotly (optional dependency).

Classes

Functions

check_plotly()

Raise if plotly is not installed.

Source code in neurogebra/viz/interactive.py
def check_plotly():
    """Raise if plotly is not installed."""
    if not PLOTLY_AVAILABLE:
        raise ImportError(
            "Plotly is required for interactive visualizations. "
            "Install it with: pip install neurogebra[viz] "
            "or pip install plotly"
        )

interactive_plot(expression, x_range=(-5, 5), n_points=500, title=None)

Create an interactive plot of an expression using Plotly.

Parameters:

Name Type Description Default
expression Expression

Expression to plot

required
x_range Tuple[float, float]

(min, max) range for x values

(-5, 5)
n_points int

Number of evaluation points

500
title Optional[str]

Plot title

None

Returns:

Type Description
Figure

Plotly Figure object

Source code in neurogebra/viz/interactive.py
def interactive_plot(
    expression: Expression,
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    title: Optional[str] = None,
) -> "go.Figure":
    """
    Create an interactive plot of an expression using Plotly.

    Args:
        expression: Expression to plot
        x_range: (min, max) range for x values
        n_points: Number of evaluation points
        title: Plot title

    Returns:
        Plotly Figure object
    """
    check_plotly()

    x = np.linspace(x_range[0], x_range[1], n_points)
    y = np.array([expression.eval(x=xi) for xi in x])

    fig = go.Figure()
    fig.add_trace(
        go.Scatter(
            x=x,
            y=y,
            mode="lines",
            name=expression.name,
            line=dict(width=2),
            hovertemplate="x: %{x:.3f}<br>f(x): %{y:.3f}<extra></extra>",
        )
    )

    fig.update_layout(
        title=title or f"{expression.name}: {expression.formula}",
        xaxis_title="x",
        yaxis_title="f(x)",
        template="plotly_white",
        hovermode="x unified",
    )

    return fig

interactive_comparison(expressions, x_range=(-5, 5), n_points=500, title='Expression Comparison')

Interactive comparison of multiple expressions.

Parameters:

Name Type Description Default
expressions List[Expression]

List of Expressions to compare

required
x_range Tuple[float, float]

(min, max) range

(-5, 5)
n_points int

Number of points

500
title str

Plot title

'Expression Comparison'

Returns:

Type Description
Figure

Plotly Figure object

Source code in neurogebra/viz/interactive.py
def interactive_comparison(
    expressions: List[Expression],
    x_range: Tuple[float, float] = (-5, 5),
    n_points: int = 500,
    title: str = "Expression Comparison",
) -> "go.Figure":
    """
    Interactive comparison of multiple expressions.

    Args:
        expressions: List of Expressions to compare
        x_range: (min, max) range
        n_points: Number of points
        title: Plot title

    Returns:
        Plotly Figure object
    """
    check_plotly()

    fig = go.Figure()
    x = np.linspace(x_range[0], x_range[1], n_points)

    for expr in expressions:
        try:
            y = np.array([expr.eval(x=xi) for xi in x])
            fig.add_trace(
                go.Scatter(
                    x=x,
                    y=y,
                    mode="lines",
                    name=expr.name,
                    line=dict(width=2),
                )
            )
        except Exception as e:
            print(f"Warning: Could not plot {expr.name}: {e}")

    fig.update_layout(
        title=title,
        xaxis_title="x",
        yaxis_title="f(x)",
        template="plotly_white",
        hovermode="x unified",
    )

    return fig

Utilities

Helpers

neurogebra.utils.helpers

Helper utilities for Neurogebra.

Provides common utility functions used across the library.

Functions

validate_array(data, name='input')

Validate and convert input to numpy array.

Parameters:

Name Type Description Default
data Any

Input data (list, tuple, numpy array, or scalar)

required
name str

Name for error messages

'input'

Returns:

Type Description
ndarray

NumPy array

Raises:

Type Description
TypeError

If data cannot be converted

Source code in neurogebra/utils/helpers.py
def validate_array(data: Any, name: str = "input") -> np.ndarray:
    """
    Validate and convert input to numpy array.

    Args:
        data: Input data (list, tuple, numpy array, or scalar)
        name: Name for error messages

    Returns:
        NumPy array

    Raises:
        TypeError: If data cannot be converted
    """
    try:
        arr = np.asarray(data, dtype=np.float64)
        return arr
    except (ValueError, TypeError) as e:
        raise TypeError(f"Cannot convert {name} to numpy array: {e}")

clip_gradients(gradients, max_norm=1.0)

Clip gradients by global norm.

Parameters:

Name Type Description Default
gradients Dict[str, float]

Dictionary of parameter name -> gradient value

required
max_norm float

Maximum allowed gradient norm

1.0

Returns:

Type Description
Dict[str, float]

Clipped gradients

Source code in neurogebra/utils/helpers.py
def clip_gradients(
    gradients: Dict[str, float],
    max_norm: float = 1.0,
) -> Dict[str, float]:
    """
    Clip gradients by global norm.

    Args:
        gradients: Dictionary of parameter name -> gradient value
        max_norm: Maximum allowed gradient norm

    Returns:
        Clipped gradients
    """
    total_norm = np.sqrt(sum(g**2 for g in gradients.values()))

    if total_norm > max_norm:
        scale = max_norm / total_norm
        return {k: v * scale for k, v in gradients.items()}

    return gradients

numerical_gradient(func, x, epsilon=1e-05)

Compute numerical gradient using central differences.

Parameters:

Name Type Description Default
func Callable

Function to differentiate

required
x ndarray

Point at which to compute gradient

required
epsilon float

Step size for finite differences

1e-05

Returns:

Type Description
ndarray

Gradient array

Source code in neurogebra/utils/helpers.py
def numerical_gradient(
    func: Callable, x: np.ndarray, epsilon: float = 1e-5
) -> np.ndarray:
    """
    Compute numerical gradient using central differences.

    Args:
        func: Function to differentiate
        x: Point at which to compute gradient
        epsilon: Step size for finite differences

    Returns:
        Gradient array
    """
    x = np.asarray(x, dtype=np.float64)
    grad = np.zeros_like(x)

    for i in range(len(x)):
        x_plus = x.copy()
        x_minus = x.copy()
        x_plus[i] += epsilon
        x_minus[i] -= epsilon

        grad[i] = (func(x_plus) - func(x_minus)) / (2 * epsilon)

    return grad

normalize(data, method='minmax')

Normalize data.

Parameters:

Name Type Description Default
data ndarray

Input array

required
method str

Normalization method ('minmax', 'standard', 'l2')

'minmax'

Returns:

Type Description
ndarray

Normalized array

Source code in neurogebra/utils/helpers.py
def normalize(data: np.ndarray, method: str = "minmax") -> np.ndarray:
    """
    Normalize data.

    Args:
        data: Input array
        method: Normalization method ('minmax', 'standard', 'l2')

    Returns:
        Normalized array
    """
    data = np.asarray(data, dtype=np.float64)

    if method == "minmax":
        data_min = data.min()
        data_max = data.max()
        if data_max - data_min == 0:
            return np.zeros_like(data)
        return (data - data_min) / (data_max - data_min)

    elif method == "standard":
        mean = data.mean()
        std = data.std()
        if std == 0:
            return np.zeros_like(data)
        return (data - mean) / std

    elif method == "l2":
        norm = np.linalg.norm(data)
        if norm == 0:
            return np.zeros_like(data)
        return data / norm

    else:
        raise ValueError(f"Unknown normalization method: {method}")

generate_data(func, x_range=(-5, 5), n_points=100, noise_std=0.0, seed=None)

Generate synthetic data from a function.

Parameters:

Name Type Description Default
func Callable

Function to generate data from

required
x_range tuple

(min, max) range for x values

(-5, 5)
n_points int

Number of data points

100
noise_std float

Standard deviation of Gaussian noise

0.0
seed Optional[int]

Random seed for reproducibility

None

Returns:

Type Description
tuple

Tuple of (X, y) arrays

Source code in neurogebra/utils/helpers.py
def generate_data(
    func: Callable,
    x_range: tuple = (-5, 5),
    n_points: int = 100,
    noise_std: float = 0.0,
    seed: Optional[int] = None,
) -> tuple:
    """
    Generate synthetic data from a function.

    Args:
        func: Function to generate data from
        x_range: (min, max) range for x values
        n_points: Number of data points
        noise_std: Standard deviation of Gaussian noise
        seed: Random seed for reproducibility

    Returns:
        Tuple of (X, y) arrays
    """
    if seed is not None:
        np.random.seed(seed)

    X = np.linspace(x_range[0], x_range[1], n_points)
    y = np.array([func(x) for x in X], dtype=np.float64)

    if noise_std > 0:
        y += np.random.normal(0, noise_std, size=n_points)

    return X, y

Explain

neurogebra.utils.explain.ExpressionExplainer

Generates detailed explanations of mathematical expressions.

Supports multiple difficulty levels and output formats.

Source code in neurogebra/utils/explain.py
class ExpressionExplainer:
    """
    Generates detailed explanations of mathematical expressions.

    Supports multiple difficulty levels and output formats.
    """

    @staticmethod
    def explain(
        expression: Expression,
        level: str = "intermediate",
        format: str = "text",
    ) -> str:
        """
        Generate explanation for an expression.

        Args:
            expression: Expression to explain
            level: Detail level ('beginner', 'intermediate', 'advanced')
            format: Output format ('text', 'markdown', 'latex')

        Returns:
            Explanation string
        """
        if format == "markdown":
            return ExpressionExplainer._explain_markdown(expression, level)
        elif format == "latex":
            return ExpressionExplainer._explain_latex(expression, level)
        else:
            return expression.explain(level=level)

    @staticmethod
    def _explain_markdown(expression: Expression, level: str) -> str:
        """Generate Markdown-formatted explanation."""
        lines = []
        lines.append(f"## {expression.name}")
        lines.append("")
        lines.append(f"**Formula:** ${expression.formula}$")
        lines.append("")

        if "description" in expression.metadata:
            lines.append(f"**Description:** {expression.metadata['description']}")
            lines.append("")

        if "usage" in expression.metadata:
            lines.append(f"**Usage:** {expression.metadata['usage']}")
            lines.append("")

        if expression.params:
            lines.append("**Parameters:**")
            for k, v in expression.params.items():
                lines.append(f"- `{k}` = {v}")
            lines.append("")

        if level in ("intermediate", "advanced"):
            if "pros" in expression.metadata:
                lines.append("**Advantages:**")
                for pro in expression.metadata["pros"]:
                    lines.append(f"- ✅ {pro}")
                lines.append("")

            if "cons" in expression.metadata:
                lines.append("**Disadvantages:**")
                for con in expression.metadata["cons"]:
                    lines.append(f"- ⚠️ {con}")
                lines.append("")

        if level == "advanced":
            lines.append("**Derivatives:**")
            for var in expression.variables:
                grad = sp.diff(expression.symbolic_expr, var)
                lines.append(f"- $\\frac{{\\partial}}{{\\partial {var}}} = {sp.latex(grad)}$")
            lines.append("")

        return "\n".join(lines)

    @staticmethod
    def _explain_latex(expression: Expression, level: str) -> str:
        """Generate LaTeX-formatted explanation."""
        lines = []
        lines.append(f"\\section{{{expression.name}}}")
        lines.append(f"\\[{expression.formula}\\]")
        lines.append("")

        if "description" in expression.metadata:
            lines.append(f"{expression.metadata['description']}")
            lines.append("")

        if level == "advanced":
            lines.append("\\subsection{Derivatives}")
            for var in expression.variables:
                grad = sp.diff(expression.symbolic_expr, var)
                lines.append(
                    f"\\[\\frac{{\\partial}}{{\\partial {var}}} = {sp.latex(grad)}\\]"
                )

        return "\n".join(lines)

    @staticmethod
    def compare_expressions(
        expressions: list,
        format: str = "text",
    ) -> str:
        """
        Compare multiple expressions.

        Args:
            expressions: List of Expression instances
            format: Output format

        Returns:
            Comparison string
        """
        if format == "markdown":
            lines = ["| Name | Formula | Category |", "|------|---------|----------|"]
            for expr in expressions:
                cat = expr.metadata.get("category", "N/A")
                formula = str(expr.symbolic_expr)[:30]
                lines.append(f"| {expr.name} | ${formula}$ | {cat} |")
            return "\n".join(lines)
        else:
            lines = [f"{'Name':<20} {'Formula':<40} {'Category':<15}"]
            lines.append("-" * 75)
            for expr in expressions:
                cat = expr.metadata.get("category", "N/A")
                formula = str(expr.symbolic_expr)[:38]
                lines.append(f"{expr.name:<20} {formula:<40} {cat:<15}")
            return "\n".join(lines)

Functions

explain(expression, level='intermediate', format='text') staticmethod

Generate explanation for an expression.

Parameters:

Name Type Description Default
expression Expression

Expression to explain

required
level str

Detail level ('beginner', 'intermediate', 'advanced')

'intermediate'
format str

Output format ('text', 'markdown', 'latex')

'text'

Returns:

Type Description
str

Explanation string

Source code in neurogebra/utils/explain.py
@staticmethod
def explain(
    expression: Expression,
    level: str = "intermediate",
    format: str = "text",
) -> str:
    """
    Generate explanation for an expression.

    Args:
        expression: Expression to explain
        level: Detail level ('beginner', 'intermediate', 'advanced')
        format: Output format ('text', 'markdown', 'latex')

    Returns:
        Explanation string
    """
    if format == "markdown":
        return ExpressionExplainer._explain_markdown(expression, level)
    elif format == "latex":
        return ExpressionExplainer._explain_latex(expression, level)
    else:
        return expression.explain(level=level)

compare_expressions(expressions, format='text') staticmethod

Compare multiple expressions.

Parameters:

Name Type Description Default
expressions list

List of Expression instances

required
format str

Output format

'text'

Returns:

Type Description
str

Comparison string

Source code in neurogebra/utils/explain.py
@staticmethod
def compare_expressions(
    expressions: list,
    format: str = "text",
) -> str:
    """
    Compare multiple expressions.

    Args:
        expressions: List of Expression instances
        format: Output format

    Returns:
        Comparison string
    """
    if format == "markdown":
        lines = ["| Name | Formula | Category |", "|------|---------|----------|"]
        for expr in expressions:
            cat = expr.metadata.get("category", "N/A")
            formula = str(expr.symbolic_expr)[:30]
            lines.append(f"| {expr.name} | ${formula}$ | {cat} |")
        return "\n".join(lines)
    else:
        lines = [f"{'Name':<20} {'Formula':<40} {'Category':<15}"]
        lines.append("-" * 75)
        for expr in expressions:
            cat = expr.metadata.get("category", "N/A")
            formula = str(expr.symbolic_expr)[:38]
            lines.append(f"{expr.name:<20} {formula:<40} {cat:<15}")
        return "\n".join(lines)

Framework Bridges

Convert Neurogebra expressions to production frameworks.

PyTorch Bridge

neurogebra.bridges.pytorch_bridge

PyTorch bridge for Neurogebra expressions.

Converts Neurogebra expressions to PyTorch-compatible modules.

Classes

Functions

check_torch()

Raise if PyTorch is not installed.

Source code in neurogebra/bridges/pytorch_bridge.py
def check_torch():
    """Raise if PyTorch is not installed."""
    if not TORCH_AVAILABLE:
        raise ImportError(
            "PyTorch is required for this feature. "
            "Install it with: pip install neurogebra[frameworks] "
            "or pip install torch"
        )

to_pytorch(expression)

Convert a Neurogebra Expression to a PyTorch nn.Module.

Parameters:

Name Type Description Default
expression Expression

Neurogebra Expression instance

required

Returns:

Type Description
Module

PyTorch nn.Module that implements the expression and gradient flow.

Raises:

Type Description
ImportError

If PyTorch is not installed

ValueError

If expression has more than one runtime input variable

Notes

This bridge supports single-input expressions (for example, x -> f(x)). Trainable scalar parameters are supported and receive gradients using symbolic differentiation.

Source code in neurogebra/bridges/pytorch_bridge.py
def to_pytorch(expression: Expression) -> "nn.Module":
    """
    Convert a Neurogebra Expression to a PyTorch nn.Module.

    Args:
        expression: Neurogebra Expression instance

    Returns:
        PyTorch nn.Module that implements the expression and gradient flow.

    Raises:
        ImportError: If PyTorch is not installed
        ValueError: If expression has more than one runtime input variable

    Notes:
        This bridge supports single-input expressions (for example, x -> f(x)).
        Trainable scalar parameters are supported and receive gradients using
        symbolic differentiation.
    """
    check_torch()

    class ExpressionModule(nn.Module):
        def __init__(self, expr: Expression):
            super().__init__()
            self.expr_name = expr.name
            self._symbolic_expr = expr.symbolic_expr
            self._base_params = expr.params.copy()

            free_symbol_map = {
                symbol.name: symbol for symbol in self._symbolic_expr.free_symbols
            }
            param_names = set(self._base_params.keys())

            unresolved_inputs = sorted(
                name for name in free_symbol_map if name not in param_names
            )
            if len(unresolved_inputs) > 1:
                raise ValueError(
                    "to_pytorch currently supports single-input expressions. "
                    f"Found unresolved variables: {unresolved_inputs}"
                )
            self._input_symbol_name = unresolved_inputs[0] if unresolved_inputs else "x"

            self._ordered_symbol_names = sorted(free_symbol_map.keys())
            self._ordered_symbols = [
                free_symbol_map[name] for name in self._ordered_symbol_names
            ]

            # Register trainable scalar parameters on the module.
            self._trainable_names = []
            for param_name in expr.trainable_params:
                if param_name in self._base_params:
                    value = float(self._base_params[param_name])
                else:
                    value = 0.0
                    self._base_params[param_name] = value

                self._trainable_names.append(param_name)
                setattr(
                    self,
                    param_name,
                    nn.Parameter(torch.tensor(value, dtype=torch.float32)),
                )

            self._value_fn = sp.lambdify(
                self._ordered_symbols,
                self._symbolic_expr,
                modules=["numpy"],
            )

            x_symbol = free_symbol_map.get(
                self._input_symbol_name, sp.Symbol(self._input_symbol_name)
            )
            grad_x_expr = sp.diff(self._symbolic_expr, x_symbol)
            self._grad_x_fn = sp.lambdify(
                self._ordered_symbols,
                grad_x_expr,
                modules=["numpy"],
            )

            self._grad_param_fns = {}
            for param_name in self._trainable_names:
                param_symbol = free_symbol_map.get(param_name, sp.Symbol(param_name))
                grad_param_expr = sp.diff(self._symbolic_expr, param_symbol)
                self._grad_param_fns[param_name] = sp.lambdify(
                    self._ordered_symbols,
                    grad_param_expr,
                    modules=["numpy"],
                )

        def _build_symbol_values(self, x_np, param_values):
            symbol_values = {name: value for name, value in self._base_params.items()}
            symbol_values.update(param_values)
            symbol_values[self._input_symbol_name] = x_np
            return symbol_values

        def _evaluate_callable(self, fn, ordered_args, target_shape):
            if self._ordered_symbol_names:
                value = fn(*ordered_args)
            else:
                value = fn()

            value_np = np.asarray(value, dtype=np.float64)
            if value_np.shape == ():
                return np.full(target_shape, float(value_np), dtype=np.float64)
            if value_np.shape != target_shape:
                value_np = np.broadcast_to(value_np, target_shape)
            return np.asarray(value_np, dtype=np.float64)

        def _evaluate_with_gradients(self, x: "torch.Tensor", params):
            x_np = x.detach().cpu().numpy()

            param_values = {
                name: float(param.detach().cpu().item())
                for name, param in zip(self._trainable_names, params)
            }

            symbol_values = self._build_symbol_values(x_np, param_values)
            ordered_args = [
                symbol_values.get(name, 0.0) for name in self._ordered_symbol_names
            ]

            output_np = self._evaluate_callable(self._value_fn, ordered_args, x_np.shape)
            grad_x_np = self._evaluate_callable(self._grad_x_fn, ordered_args, x_np.shape)
            grad_param_nps = [
                self._evaluate_callable(self._grad_param_fns[name], ordered_args, x_np.shape)
                for name in self._trainable_names
            ]

            return output_np, grad_x_np, grad_param_nps

        def forward(self, x: "torch.Tensor") -> "torch.Tensor":
            module = self
            trainable_params = [getattr(self, name) for name in self._trainable_names]

            class SympyExpressionFunction(torch.autograd.Function):
                @staticmethod
                def forward(ctx, input_tensor, *params):
                    output_np, grad_x_np, grad_param_nps = (
                        module._evaluate_with_gradients(input_tensor, params)
                    )

                    grad_x_tensor = torch.tensor(
                        grad_x_np,
                        dtype=input_tensor.dtype,
                        device=input_tensor.device,
                    )
                    grad_param_tensors = [
                        torch.tensor(
                            grad_np,
                            dtype=input_tensor.dtype,
                            device=input_tensor.device,
                        )
                        for grad_np in grad_param_nps
                    ]

                    ctx.param_dtypes = [param.dtype for param in params]
                    ctx.save_for_backward(grad_x_tensor, *grad_param_tensors)

                    return torch.tensor(
                        output_np,
                        dtype=input_tensor.dtype,
                        device=input_tensor.device,
                    )

                @staticmethod
                def backward(ctx, grad_output):
                    saved_tensors = ctx.saved_tensors

                    grad_x = grad_output * saved_tensors[0]

                    grad_params = []
                    for index, grad_param in enumerate(saved_tensors[1:]):
                        grad_value = (grad_output * grad_param).sum()
                        grad_params.append(grad_value.to(ctx.param_dtypes[index]))

                    return (grad_x, *grad_params)

            return SympyExpressionFunction.apply(x, *trainable_params)

    return ExpressionModule(expression)

from_pytorch(module, name='pytorch_expr')

Create a Neurogebra Expression from a simple PyTorch activation.

Parameters:

Name Type Description Default
module Module

PyTorch module (e.g., nn.ReLU())

required
name str

Name for the expression

'pytorch_expr'

Returns:

Type Description
Expression

Neurogebra Expression

Note

This creates a numerical-only expression (no symbolic form).

Source code in neurogebra/bridges/pytorch_bridge.py
def from_pytorch(module: "nn.Module", name: str = "pytorch_expr") -> Expression:
    """
    Create a Neurogebra Expression from a simple PyTorch activation.

    Args:
        module: PyTorch module (e.g., nn.ReLU())
        name: Name for the expression

    Returns:
        Neurogebra Expression

    Note:
        This creates a numerical-only expression (no symbolic form).
    """
    check_torch()

    # Map known PyTorch modules to symbolic expressions
    module_map = {
        "ReLU": "Max(0, x)",
        "Sigmoid": "1 / (1 + exp(-x))",
        "Tanh": "tanh(x)",
        "Softplus": "log(1 + exp(x))",
        "ELU": "Piecewise((x, x > 0), (exp(x) - 1, True))",
    }

    class_name = module.__class__.__name__

    if class_name in module_map:
        return Expression(
            name=name,
            symbolic_expr=module_map[class_name],
            metadata={"source": "pytorch", "original_class": class_name},
        )

    # Fallback: create expression from numerical evaluation
    return Expression(
        name=name,
        symbolic_expr="x",  # Placeholder
        metadata={
            "source": "pytorch",
            "original_class": class_name,
            "warning": "Symbolic form not available, using placeholder",
        },
    )

TensorFlow Bridge

neurogebra.bridges.tensorflow_bridge

TensorFlow bridge for Neurogebra expressions.

Converts Neurogebra expressions to TensorFlow-compatible functions.

Classes

Functions

check_tensorflow()

Raise if TensorFlow is not installed.

Source code in neurogebra/bridges/tensorflow_bridge.py
def check_tensorflow():
    """Raise if TensorFlow is not installed."""
    if not TF_AVAILABLE:
        raise ImportError(
            "TensorFlow is required for this feature. "
            "Install it with: pip install neurogebra[frameworks] "
            "or pip install tensorflow"
        )

to_tensorflow(expression)

Convert a Neurogebra Expression to a TensorFlow function.

Parameters:

Name Type Description Default
expression Expression

Neurogebra Expression instance

required

Returns:

Type Description
Callable

TensorFlow-compatible function

Raises:

Type Description
ImportError

If TensorFlow is not installed

ValueError

If expression has more than one runtime input variable

Source code in neurogebra/bridges/tensorflow_bridge.py
def to_tensorflow(expression: Expression) -> Callable:
    """
    Convert a Neurogebra Expression to a TensorFlow function.

    Args:
        expression: Neurogebra Expression instance

    Returns:
        TensorFlow-compatible function

    Raises:
        ImportError: If TensorFlow is not installed
        ValueError: If expression has more than one runtime input variable
    """
    check_tensorflow()

    runtime_vars = [
        str(var)
        for var in expression.variables
        if str(var) not in expression.params
    ]
    if len(runtime_vars) > 1:
        raise ValueError(
            "to_tensorflow currently supports single-input expressions. "
            f"Found unresolved variables: {runtime_vars}"
        )
    input_var = runtime_vars[0] if runtime_vars else "x"

    def _evaluate_numpy(x_np: np.ndarray) -> np.ndarray:
        result = expression.eval(**{input_var: x_np})
        return np.asarray(result, dtype=x_np.dtype)

    def tf_expression(x: "tf.Tensor") -> "tf.Tensor":
        output = tf.numpy_function(_evaluate_numpy, [x], Tout=x.dtype)
        output.set_shape(x.shape)
        return output

    return tf_expression

to_keras_layer(expression, name=None)

Convert a Neurogebra Expression to a Keras Layer.

Parameters:

Name Type Description Default
expression Expression

Neurogebra Expression instance

required
name Optional[str]

Optional layer name

None

Returns:

Type Description
Any

Keras Lambda layer

Raises:

Type Description
ValueError

If expression has more than one runtime input variable

Source code in neurogebra/bridges/tensorflow_bridge.py
def to_keras_layer(expression: Expression, name: Optional[str] = None) -> Any:
    """
    Convert a Neurogebra Expression to a Keras Layer.

    Args:
        expression: Neurogebra Expression instance
        name: Optional layer name

    Returns:
        Keras Lambda layer

    Raises:
        ValueError: If expression has more than one runtime input variable
    """
    check_tensorflow()

    layer_name = name or f"neurogebra_{expression.name}"

    runtime_vars = [
        str(var)
        for var in expression.variables
        if str(var) not in expression.params
    ]
    if len(runtime_vars) > 1:
        raise ValueError(
            "to_keras_layer currently supports single-input expressions. "
            f"Found unresolved variables: {runtime_vars}"
        )
    input_var = runtime_vars[0] if runtime_vars else "x"

    def _evaluate_numpy(x_np: np.ndarray) -> np.ndarray:
        result = expression.eval(**{input_var: x_np})
        return np.asarray(result, dtype=np.float32)

    def layer_fn(x):
        output = tf.numpy_function(_evaluate_numpy, [x], tf.float32)
        output.set_shape(x.shape)
        return output

    return tf.keras.layers.Lambda(layer_fn, name=layer_name)

JAX Bridge

neurogebra.bridges.jax_bridge

JAX bridge for Neurogebra expressions.

Converts Neurogebra expressions to JAX-compatible functions.

Classes

Functions

check_jax()

Raise if JAX is not installed.

Source code in neurogebra/bridges/jax_bridge.py
def check_jax():
    """Raise if JAX is not installed."""
    if not JAX_AVAILABLE:
        raise ImportError(
            "JAX is required for this feature. "
            "Install it with: pip install jax jaxlib"
        )

to_jax(expression)

Convert a Neurogebra Expression to a JAX-compatible function.

Parameters:

Name Type Description Default
expression Expression

Neurogebra Expression instance

required

Returns:

Type Description
Callable

JAX-compatible function for eager evaluation.

Raises:

Type Description
ImportError

If JAX is not installed

ValueError

If expression has more than one runtime input variable

Notes

This bridge uses NumPy-backed evaluation under the hood. It is intended for interoperability, not traced JIT/grad execution.

Source code in neurogebra/bridges/jax_bridge.py
def to_jax(expression: Expression) -> Callable:
    """
    Convert a Neurogebra Expression to a JAX-compatible function.

    Args:
        expression: Neurogebra Expression instance

    Returns:
        JAX-compatible function for eager evaluation.

    Raises:
        ImportError: If JAX is not installed
        ValueError: If expression has more than one runtime input variable

    Notes:
        This bridge uses NumPy-backed evaluation under the hood. It is intended
        for interoperability, not traced JIT/grad execution.
    """
    check_jax()

    runtime_vars = [
        str(var)
        for var in expression.variables
        if str(var) not in expression.params
    ]
    if len(runtime_vars) > 1:
        raise ValueError(
            "to_jax currently supports single-input expressions. "
            f"Found unresolved variables: {runtime_vars}"
        )
    input_var = runtime_vars[0] if runtime_vars else "x"

    def jax_fn(x):
        x_np = np.asarray(x)
        result = expression.eval(**{input_var: x_np})
        return jnp.array(result)

    return jax_fn

to_jax_grad(expression, var='x')

Convert expression and return its gradient as a JAX function.

Parameters:

Name Type Description Default
expression Expression

Neurogebra Expression instance

required
var str

Variable to differentiate with respect to

'x'

Returns:

Type Description
Callable

JAX function computing the gradient

Source code in neurogebra/bridges/jax_bridge.py
def to_jax_grad(expression: Expression, var: str = "x") -> Callable:
    """
    Convert expression and return its gradient as a JAX function.

    Args:
        expression: Neurogebra Expression instance
        var: Variable to differentiate with respect to

    Returns:
        JAX function computing the gradient
    """
    check_jax()

    grad_expr = expression.gradient(var)
    return to_jax(grad_expr)