from numpy import array
# from random import random
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt

import numpy as np


class Particle:
    pass
        
 
def optimize(self, opts={}):
#     pass

# def a():
    u = self.utilityFunction    
    
    dimensions = self.portfolio.weights.count()
    pop_size = opts.get("pop", int(0.045*dimensions**2+0.3*dimensions+9.29)) #20+2*np.sqrt(dimensions)
    iter_max = opts.get("iterations", 1000) # dimensions*20    
    verbose = opts.get("verbose", False) # dimensions*20  
    c1 = opts.get("c1", 1.49618) # dimensions*20    
    c2 = opts.get("c2", 1.49618) # dimensions*20    
    w = opts.get("w", 0.7298) # dimensions*20    0.7298
    speed_factor = opts.get("speed_factor", 1.0/dimensions) # dimensions*20        
    alive_speed = speed_factor*0.001     
   
    #initialize the particles
    particles = []
    for i in range(pop_size):
        p = Particle()
        p.params = np.random.random(dimensions)
        p.utility = 999999.9        
        p.v = speed_factor*np.random.random(dimensions)
        particles.append(p)

    # first few particles will have better positions
    particles[0].params = array([1.0 for j in range(dimensions)])
    particles[1].params = self.portfolio['sortino'].values
    particles[2].params = self.portfolio['sharpe'].values
    particles[3].params = self.portfolio['mean'].values
    

    gbest = particles[0]
    err = 999999999
    i=0

    # plt.ion()
    # plt.show()
    # plt.clf()
    
    
    g = [] #u(particles[0].params)
    while i < iter_max :        
        excitement = 0.0
        for p in particles:
            utility = u(p.params)
            if utility < p.utility:
                p.utility = utility
                p.best = p.params                

            if utility < gbest.utility:                
                gbest = p
                if np.linalg.norm(p.v) > alive_speed:
                    excitement = 0.0
                    continue

            p.v = (w+excitement)*p.v + (c1+excitement) * np.random.random(1) * (p.best - p.params) \
                    + (c2+excitement) * np.random.random(1) * (gbest.params - p.params)
            p.params = p.params + p.v
                
        i  += 1
        if i>iter_max: #utilityChange<0.000001 and 
            break

        if pd.Series(map (lambda x : np.linalg.norm(x), [px.v for px in particles]) ).mean() < alive_speed:
            print "Saturated"
            break

        #progress bar. '.' = 10%
        if verbose and i % 10 == 0:
            print gbest.utility, g[-1]
            # plt.plot(g, 'g')
            # plt.pause(0.1)

        g.append( pd.Series(map (lambda x : np.linalg.norm(x), [px.v for px in particles]) ).mean())
    
        # print particles[1].v

    # pd.Series(g).plot()    
    # plt.xlabel('Iteracia')
    # plt.ylabel('f(x)')    
    # plt.show()
        
    if verbose:
        print '\nParticle Swarm Optimisation\n'
        print 'PARAMETERS\n','-'*9
        print 'Population size : ', pop_size
        print 'Speed factor    : ', speed_factor
        print 'Dimensions      : ', dimensions

        print 'c1              : ', c1
        print 'c2              : ', c2
        print 'w               : ', w

        print 'RESULTS\n', '-'*7
        print 'gbest utility   : ', gbest.utility        
        print 'iterations      : ', i+1

    # use hill climber for best particle
    print "starting hill climber"
    hill = opt.minimize(self.utilityFunction, gbest.params, options={'disp': True})

    # run utility function one last time to set best weights
    self.utilityFunction(hill.x) #gbest.params

    return pd.Series(g) # returns list of utility function 



if  __name__ =='__main__':
    optimize(None)