#!usr/bin/env python
# encoding: utf-8

########
# pyannotemod grid search
# HATA Kazuya. 2020.12.10
# This program which is designed for pyannote validation minimizes a given one-variable scalar function in a bounded domain by grid search.
# Each stage of this program will be as the following:
#  Stage 1: grid search with # of girdNum in a domain (leftX, rightX).
#  Stage 2-stageNum: grid search with # of gridNum around the optimum x of the previous stage (points outside of the domain will be skipped).
# History:
#  2020.12.10 HATA Kazuya. 1st version
#  2020.12.11 HATA Kazuya. Change the grid search into a grid search from coarser to finer.
########

class OptResult:
    x = 0
    fun = 0

def gridsearch1dimmin(func, leftX=0, rightX=1, gridNum=20, stageNum=4, firstGridNum=5, firstThreshold=0.3, epsilon=0.01):
    #logfile = 'am7data2/user/k.hata/data/log/pyannote/log-gridsearch/log-gridsearch-test.csv'
    ##logfile = '/mnt/c/Users/hata/programming/python/output/log-gridsearch-pyannote.csv'
    ##logfilefscoreepoch = '/mnt/c/Users/hata/programming/python/output/log-gridsearch-pyannote-fscore-epoch.csv'
#'../output/log-gridsearch-test-new.csv'
    
    ## print("args(func, gridNum, stageNum, leftX, rightX) = ", func, gridNum, stageNum, leftX, rightX)
    xlist = []
    scorelist = []

    gridnum = firstGridNum
    dx = float((rightX - leftX)/gridnum)
    xstart = leftX + dx
    xend = rightX - dx

    for stage in range(stageNum):
        print()
        print("stage = ", stage)


        #dx = float((rightedge - leftedge)/(gridNum*magnification))
        #print()
        print("xstart = ", xstart)
        print("xend = ", xend)
        localscorelist = []
        for i in range(gridnum - 1): #*(magnificationRate**(stage + 1))):
            x = xstart + i*dx
            if leftX < x and x < rightX:
                score = func(x)
                xlist.append(x)
                print("x, score = ", x, score)
                scorelist.append(score)
                localscorelist.append(score)
                ##with open(logfile, 'a') as f:
                ##    print(x, score, file=f)
    #print("xlist = ", xlist)
    #print("scorelist = ", scorelist)
                
                ##print("(gridnum, xstart, xend, dx) = ", gridnum, xstart, xend, dx)
                ##print("optimized result: (x, score) = ", xopt, scoreopt)
        localscoreopt = localscorelist[localscorelist.index(min(localscorelist))]
        xopt = xlist[scorelist.index(min(scorelist))]
        scoreopt = scorelist[scorelist.index(min(scorelist))]
        
        if stage == 0 and localscoreopt > firstThreshold:
            break
        elif stage > 0 and localscoreopt > prescoreopt - epsilon:
            break

        prescoreopt = scoreopt
        #### renew magnification and xstart and xend
        # magnification = magnificationRate
        #dx = float(dx/magnificationRate)
        gridnum = gridNum
        dx = float(2*dx/gridnum)
        xstart = xopt - float(dx*(gridnum/2.0 - 1.0))
        xend = xopt + float(dx*(gridnum/2.0 - 1.0))
        ###  xstart = xopt - float((dx/magnificationRate)*(gridnum/2.0 - 1.0))
        ### xend = xopt + float((dx/magnificationRate)*(gridnum/2.0 - 1.0))
        # dx = float(dx/magnificationRate)

        

#### Record History
    ##with open(logfile, 'a') as f:
    ##    print("optimized result: (x, score) = ", xopt, scoreopt, file=f)
    ##with open(logfilefscoreepoch, 'a') as fepoch:
    ##    print(xopt, 1 - scoreopt, file=fepoch)

    #fun = scoreopt
    OptResult.x = xopt
    OptResult.fun = scoreopt
    return OptResult

def testfunc(a):
    return - a*a + 0.5


def main():
    res = gridsearch1dimmin(testfunc)
    print("res = ", res)
    print(res.x, res.fun)

if __name__ == '__main__':
    main()

