# CPSC 532P - Spring 2017 - http://cs.ubc.ca/~poole/cs532/2017
# This is not useful for much except answering this question for one predictor
# This requires Python 3 (get the latest version from http://python.org)

# Copyright David Poole, 2017
# This work is licensed under a Creative Commons
# Attribution-NonCommercial-ShareAlike 4.0 International License.
# See: http://creativecommons.org/licenses/by-nc-sa/4.0/deed.en

import random
import math

num_iter = 10000
num_test = 100

def laplace(n0,n1,c=1):
    "Laplace (pseudo-count 1)"
    return (n1+c)/(n0+n1+2*c)
def laplace2(n0,n1):
    "pseudo-count 2"
    return laplace(n0,n1,2)
def laplace05(n0,n1):
    "pseudo-count 0.5"
    return laplace(n0,n1,0.5)
def thresholded_ave(n0,n1):
    "thresholded ave"
    if n0==0:
        return 0.999
    elif n1==0:
        return 0.001
    else:
        return n1/(n0+n1)
def emp_ave(n0,n1):
    "empirical ave"
    return n1/(n0+n1)
def median(n0,n1):
    "median"
    if n1>n0:
        return 1
    elif n1<n0:
        return 0
    else:
        return 0.5


def sum_sq(actual,prediction):   # each term in sum as a function of actual and predicted
    """Sum of squares error"""
    return (actual-prediction)**2
def sum_abs(actual,prediction):
    """Sum of absolute error"""
    return abs(actual-prediction)
tiny = 0.5**1000  #Note: log(tiny,2)=1000 so the number of thousands is the number of errors
def log_loss(actual,prediction):
    """log loss"""    
    return -actual*math.log(prediction+tiny,2)-(1-actual)*math.log((1-prediction)+tiny,2)

for method in [sum_sq, sum_abs,log_loss]:
  print("\n",method.__doc__)
  for predictor in [emp_ave, median, thresholded_ave,  laplace, laplace2, laplace05 ]:
    print(predictor.__doc__, end='')
    for n in [1, 2, 3, 4, 5, 10, 20, 100, 1000]:
        error = 0.0
        for i in range(num_iter):
            p = random.random()
            n0, n1 = 0, 0
            for s in range(n):
                if random.random()<p :
                    n1 += 1
                else:
                    n0 += 1
            prediction = predictor(n0,n1)
            for tn in range(100):
                actual = 1 if random.random()<p else 0
                error += method(actual, prediction)
        print(' ||', "%.2f"%(error/num_iter), end='')
    print()  #adds end of line
                
                
