MLLossFunctions
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description Methods for Loss functions.
library("MLLossFunctions")

import RicardoSantos/DebugConsole/6 as console
[__T, __C] = console.init(25)

// reference:
//      https://kobiso.github.io/research/research-loss-functions/
//      https://machinelearningmastery.com/loss-and-loss-functions-for-training-deep-learning-neural-networks/

// @function Mean Squared Error (MSE) " MSE = 1/N * sum[i=1:N]((y - y')^2) ".
// @param expects float array, expected values.
// @param predicts float array, prediction values.
// @returns float
export mse (float[] expects, float[] predicts) => //{
    int _size_e = array.size(expects)
    int _size_p = array.size(predicts)
    
    switch
        (_size_e < 1)        => runtime.error('MLLossFunctions -> mse(): "expects" has wrong size.')
        (_size_e != _size_p) => runtime.error('MLLossFunctions -> mse(): "expects" and "predicts" size does not match.')
    
    float _sum = 0.0
    for _i = 0 to _size_e - 1
        _sum += math.pow(array.get(expects, _i) - array.get(predicts, _i), 2.0)
	_sum / _size_e
//{ usage:
//{ remarks:
//}}}

// @function Binary Cross-Entropy Loss (log).
// @param expects float array, expected values.
// @param predicts float array, prediction values.
// @returns float
export binary_cross_entropy (float[] expects, float[] predicts) => //{
    int _size_e = array.size(expects)
    int _size_p = array.size(predicts)
    //
    switch
        (_size_e < 1)        => runtime.error('MLLossFunctions -> binary_cross_entropy(): "expects" has wrong size.')
        (_size_e != _size_p) => runtime.error('MLLossFunctions -> binary_cross_entropy(): "expects" and "predicts" size does not match.')
    //
    float _sum = 0.0
    for _i = 0 to _size_e - 1
        float _e = array.get(expects, _i) // y
        float _p = array.get(predicts, _i)// yhat
        _sum += _e * math.log(_p) + (1.0 - _e) * math.log(1.0 - _p)
	0.0 - ((1.0 / _size_e) * _sum)
//{ usage:
i1 = array.from(0.19858, 0.28559, 0.51583)
i2 = array.from(0.2698, 0.3223, 0.4078)
y = array.from(1.0, 0.0, 0.0) // expected
i3 = array.from(.5, .7, .2, .3, .5, .6)
y3 = array.from(0.0, 1, 0, 0, 0, 0)
var float ce1 = binary_cross_entropy(y, i1)
var float ce2 = binary_cross_entropy(y, i2)
var float ce3 = binary_cross_entropy(y3, i3) // expected: 0.53984624 //https://neptune.ai/blog/cross-entropy-loss-and-its-applications-in-deep-learning
console.queue_one(__C, str.format('{0}, {1}, {2}', ce1, ce2, ce3))
//{ remarks:
//  reference: https://sparrow.dev/binary-cross-entropy/
//}}}

console.update(__T, __C)