<?php

namespace Rubix\ML\NeuralNet\ActivationFunctions;

use Tensor\Matrix;

use function exp;

/**
 * SiLU
 *
 * Sigmoid Linear Units are smooth and non-monotonic rectified activation functions. Their inputs are weighted by
 * the [Sigmoid](sigmoid.md) activation function acting as a self-gating mechanism.
 *
 * References:
 * [1] S. Elwing et al. (2017). Sigmoid-Weighted Linear Units for Neural Network Function Approximation in
 * Reinforcement Learning.
 *
 * @category    Machine Learning
 * @package     Rubix/ML
 * @author      Andrew DalPino
 */
class SiLU implements ActivationFunction
{
    /**
     * Compute the activation.
     *
     * @internal
     *
     * @param \Tensor\Matrix $input
     * @return \Tensor\Matrix
     */
    public function activate(Matrix $input) : Matrix
    {
        return $input->map([$this, '_compute']);
    }

    /**
     * Calculate the derivative of the activation.
     *
     * @internal
     *
     * @param \Tensor\Matrix $input
     * @param \Tensor\Matrix $output
     * @return \Tensor\Matrix
     */
    public function differentiate(Matrix $input, Matrix $output) : Matrix
    {
        $ones = Matrix::ones(...$output->shape());

        return $output->divide($input)
            ->multiply($ones->subtract($output))
            ->add($output);
    }

    /**
     * @internal
     *
     * @param float $input
     * @return float
     */
    public function _compute(float $input) : float
    {
        return $input / (1.0 + exp(-$input));
    }

    /**
     * Return the string representation of the object.
     *
     * @return string
     */
    public function __toString() : string
    {
        return 'SiLU';
    }
}
