package org.nd4j.linalg.lossfunctions.impl;

import lombok.EqualsAndHashCode;
import org.apache.commons.math3.util.Pair;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.lossfunctions.ILossFunction;

/**
 * Created by susaneraly on 8/15/16.
 */
@EqualsAndHashCode
public class LossMAPE implements ILossFunction {
    public INDArray scoreArray(INDArray labels, INDArray preOutput, String activationFn, INDArray mask) {
        INDArray scoreArr;
        INDArray postOutput = Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform(activationFn, preOutput.dup()));
        scoreArr = postOutput.sub(labels);
        scoreArr.divi(labels);
        scoreArr.muli(100);
        Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform("abs", scoreArr));
        if (mask != null) scoreArr.muliColumnVector(mask);
        return scoreArr;
    }

    @Override
    public double computeScore(INDArray labels, INDArray preOutput, String activationFn, INDArray mask, boolean average) {
        INDArray scoreArr = scoreArray(labels, preOutput, activationFn, mask);

        double score = scoreArr.sumNumber().doubleValue();

        if (average) score /= scoreArr.size(0);

        return score;
    }

    @Override
    public INDArray computeScoreArray(INDArray labels, INDArray preOutput, String activationFn, INDArray mask) {
        INDArray scoreArr = scoreArray(labels, preOutput, activationFn, mask);
        return scoreArr.sum(1);
    }

    @Override
    public INDArray computeGradient(INDArray labels, INDArray preOutput, String activationFn, INDArray mask) {
        INDArray postOutput = Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform(activationFn, preOutput.dup()));
        INDArray postOutDer = Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform(activationFn, preOutput.dup()).derivative());

        INDArray gradients = postOutput.sub(labels);
        Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform("abs", gradients));
        if (gradients.maxNumber().doubleValue() < Nd4j.EPS_THRESHOLD) return gradients;
        gradients.divi(postOutput.sub(labels)); //this gets me the sign +/- of the difference
        gradients.muli(postOutDer);
        gradients.divi(labels.addi(Nd4j.EPS_THRESHOLD)); //workaround for division by zero -> can be cleaner

        if(mask != null){
            gradients.muliColumnVector(mask);
        }

        return gradients;
    }

    @Override
    public org.apache.commons.math3.util.Pair<Double, INDArray> computeGradientAndScore(INDArray labels, INDArray preOutput, String activationFn, INDArray mask, boolean average) {
        //TODO: probably a more efficient way to do this...
        //Yes - will implement in round two. Just want to get done now.

        return new Pair<>(
                computeScore(labels, preOutput, activationFn, mask, average),
                computeGradient(labels, preOutput, activationFn, mask));
    }

    @Override
    public String toString() {
        return "LossMAPE()";
    }

}

@@ -159,16 +159,121 @@
 DArray;%0A
+import org.nd4j.linalg.api.ops.impl.transforms.Abs;%0Aimport org.nd4j.linalg.api.ops.impl.transforms.Sign;%0A
 import o
@@ -609,37 +609,33 @@
        INDArray 
-postO
+o
 utput = Nd4j.get
@@ -750,38 +750,36 @@
 reArr = 
-postO
+o
 utput.
+r
 sub
+i
 (labels)
 ;%0A      
@@ -774,66 +774,20 @@
 els)
-;%0A        scoreArr.divi(labels);%0A        scoreArr.muli(100
+.divi(labels
 );%0A 
@@ -880,24 +880,69 @@
 scoreArr));%0A
+        scoreArr.muli(100.0/labels.size(1));%0A
         if (
@@ -1762,21 +1762,17 @@
 NDArray 
-postO
+o
 utput = 
@@ -1893,26 +1893,27 @@
 NDArray 
-postOutDer
+sigmaPrimeZ
  = Nd4j.
@@ -2047,325 +2047,226 @@
 ray 
-gradients = postOutput.sub(labels);%0A        Nd4j.getExecutioner().execAndReturn(Nd4j.getOpFactory().createTransform(%22abs%22, gradients));%0A        if (gradients.maxNumber().doubleValue() %3C Nd4j.EPS_THRESHOLD) return gradients;%0A        gradients.divi(postOutput.sub(labels)); //this gets me the sign +/- of the difference
+actSubPredicted = labels.sub(output);%0A        INDArray gradient = Nd4j.getExecutioner().execAndReturn(new Sign(actSubPredicted));%0A        INDArray absLabels = Nd4j.getExecutioner().execAndReturn(new Abs(labels.dup()));
 %0A   
@@ -2282,136 +2282,71 @@
 ient
-s
 .muli(
-postOutDer);%0A        gradients
+sigmaPrimeZ)
 .divi(
-l
+absL
 abels
-.addi(Nd4j.EPS_THRESHOLD)); //workaround for division by zero -%3E can be cleaner
+).muli(-100.0/labels.size(1));
 %0A%0A  
@@ -2389,17 +2389,16 @@
 gradient
-s
 .muliCol
@@ -2448,17 +2448,16 @@
 gradient
-s
 ;%0A    %7D%0A

Fix LossMAPE gradient score and gradient