/*************************************************************************
* Project: Library of Evolutionary Algoriths
*************************************************************************
* Author: Changhe Li & Ming Yang
* Email: changhe.lw@google.com Or yangming0702@gmail.com
* Language: C++
*************************************************************************
*  This file is part of EAlib. This library is free software;
*  you can redistribute it and/or modify it under the terms of the
*  GNU General Public License as published by the Free Software
*  Foundation; either version 2, or (at your option) any later version.
*************************************************************************/
// Created: 30 Nov 2011
// Last modified:

#include "SLSwarm.h"

#ifdef DEMON_EALIB
#include "../../../buffer/buffer.h"
#endif
int SLSwarm::ms_updateFre=-1;
double SLSwarm::ms_learnRatio=-1;
float SLSwarm::ms_ratioLearnToGbest=-1;


SLSwarm::SLSwarm():Swarm<SelfLearnParticle>()
{
    //ctor

}


SLSwarm::SLSwarm(const int rSize,bool mode):Swarm<SelfLearnParticle>(rSize,mode)
{
    //ctor
    setParameters();
    setID(ALG_SLPSO);
    strcpy(ma_name,"self-learning PSO");

    m_algPar<<"Population size: "<<m_popsize;

}

SLSwarm::~SLSwarm()
{
    //dtor
}



void SLSwarm::updateParameters(){

	int *rindex=new int[m_popsize];

	gInitializeRandomArray(rindex,m_popsize);

	if(ms_learnRatio<0)
		for(int i=0;i<m_popsize;i++){
			mp_pop[rindex[i]].m_learnRatio=(1-exp(-pow(i*1.6/m_popsize,4)))>0.05?(1-exp(-pow(i*1.6/m_popsize,4))):0.05;
		}
	else
		for(int i=0;i<m_popsize;i++){
			mp_pop[i].m_learnRatio=ms_learnRatio;
		}

	gInitializeRandomArray(rindex,m_popsize);

	if(ms_updateFre==-1)
		for(int i=0;i<m_popsize;i++){
			mp_pop[rindex[i]].m_updateFre=10*exp(-pow(i*1.6/m_popsize,4))>1?10*exp(-pow(i*1.6/m_popsize,4)):1;
		}
	else
		for(int i=0;i<m_popsize;i++){
			mp_pop[i].m_updateFre=ms_updateFre;
		}

	delete [] rindex;
	rindex=0;
	updateLearnToGbest();
}
void SLSwarm::setParameters(){

    for(int i=0;i<m_popsize;i++){
        if(ms_learnRatio<0)
        mp_pop[i].m_learnRatio=(1-exp(-pow(i*1.6/m_popsize,4)))>0.05?(1-exp(-pow(i*1.6/m_popsize,4))):0.05;
        else
            mp_pop[i].m_learnRatio=ms_learnRatio;

        if(ms_updateFre==-1)
        mp_pop[i].m_updateFre=10*exp(-pow(i*1.6/m_popsize,4))>1?10*exp(-pow(i*1.6/m_popsize,4)):1;
        else
            mp_pop[i].m_updateFre=ms_updateFre;
    }
    m_numLearnToGbest=m_popsize*ms_ratioLearnToGbest;
    MRandToBest(m_numLearnToGbest);
    for(int i=0;i<m_popsize;i++) mp_pop[i].setSelRatio();


}
void SLSwarm::calculateNumLearning(const int sfes){
	if(m_popsize<=0) return;
	if(ms_ratioLearnToGbest<0){
	m_numLearnToGbest=(int)(m_popsize*(1-exp(-100*pow((double)(Global::gp_problem->getEvaluations()-sfes)/(Global::g_tEvals-sfes),3))));
	}
}

SLSwarm &SLSwarm::operator= ( SLSwarm &s){
	if(this==&s) return *this;
	m_numLearnToGbest=s.m_numLearnToGbest;
	return *this;
}


void SLSwarm::updateLearnToGbest(){

	int PrenumLearnToGbest=0,CurnumLearnToGbest=0;


	for(int i=0;i<m_popsize;i++) if(mp_pop[i].m_flag) PrenumLearnToGbest++;
	bool *preLearnToGbestInfor=new bool[m_popsize];
	for(int i=0;i<m_popsize;i++) preLearnToGbestInfor[i]=mp_pop[i].m_flag;
	int *preLearnToGbest=0;
	int k=0;
	if(PrenumLearnToGbest>0){
		preLearnToGbest=new int[PrenumLearnToGbest];

		 k=0;
		for(int i=0;i<m_popsize;i++){
			if(mp_pop[i].m_flag) preLearnToGbest[k++]=i;
			if(k>=PrenumLearnToGbest) break;
		}
	}


	MRandToBest(m_numLearnToGbest);

	for(int i=0;i<m_popsize;i++) if(mp_pop[i].m_flag) CurnumLearnToGbest++;
	int *curLearnToGbest=0;

	if(CurnumLearnToGbest>0){
		curLearnToGbest=new int[CurnumLearnToGbest];
		k=0;
		for(int i=0;i<m_popsize;i++){
			if(mp_pop[i].m_flag) curLearnToGbest[k++]=i;
			if(k>=CurnumLearnToGbest) break;
		}
	}


	for(int i=0;i<PrenumLearnToGbest;i++){
		if(!mp_pop[preLearnToGbest[i]].m_flag) 		mp_pop[preLearnToGbest[i]].learnToNonLearn();

	}
	for(int i=0;i<CurnumLearnToGbest;i++){
		if(!preLearnToGbestInfor[curLearnToGbest[i]])	mp_pop[curLearnToGbest[i]].nonLearnToLearn();

	}


	if(preLearnToGbest){
	delete [] preLearnToGbest;
	preLearnToGbest=0;
	}
	if(curLearnToGbest){
	delete []  curLearnToGbest;
	curLearnToGbest=0;
	}

	delete [] preLearnToGbestInfor;
	preLearnToGbestInfor=0;


}

int SLSwarm::evolve(){

	if(m_popsize<=0)	return 0;

	#ifdef DEMON_EALIB
	Buffer<SelfLearnParticle>::updateBuffer(this);
#endif

	Chromosome  t_p;
	double *avg_v=new double[Global::g_dimNumber];

	for(int j=0;j<Global::g_dimNumber;j++){
		avg_v[j]=0;
		for(int i=0;i<m_popsize;i++){
			avg_v[j]+=fabs(mp_pop[i].mp_vel[j]);
		}
		avg_v[j]/=m_popsize;

	}
	bool f_return=false;
	int k;
	int *rindex=new int[m_popsize];
	gInitializeRandomArray(rindex,m_popsize);

	for(int j=0;j<m_popsize;j++){
		int i=rindex[j];
		k=i;
		if(mp_pop[i].m_itersUnimpr>mp_pop[i].m_updateFre){

			mp_pop[i].updateSelectionRatioProg();
			mp_pop[i].m_itersUnimpr=0;
			//mp_pop[i].updateSelectionRatioMonitor();

		}
		int sel=mp_pop[i].selectOperator();
		t_p=mp_pop[i].m_pself;
		int nearest;
		switch (sel+1){
		case 1:
			mp_pop[i].move(mp_pop[i].m_pbest,m_W,m_C1);
			break;
		case 2:
			mp_pop[i].NormalMutation(avg_v);
			break;
		case 3:
			nearest=gRandInt(0,m_popsize);
			if(m_popsize>1){
				while(nearest==i) nearest=gRandInt(0,m_popsize);
			}
			if(mp_pop[i].m_pbest>mp_pop[nearest].m_pbest){
				i=nearest;
				t_p=mp_pop[i].m_pself;
				mp_pop[i].move(mp_pop[k].m_pbest,m_W,m_C1);
			}else
			mp_pop[i].move(mp_pop[nearest].m_pbest,m_W,m_C1);
			break;
		case 4:
			mp_pop[i].move(m_best,m_W,m_C1);
			break;
		default:
			Throw(Runtime_error("operator selection error"));
			break;
		}

		if(mp_pop[i].m_pself>(mp_pop[i].m_pbest)){
			//mp_pop[i].mp_monitor[sel].m_numSuccess++;
			//mp_pop[i].mp_monitor[sel].m_rewards+=fabs(mp_pop[i].m_pself.getObj()-mp_pop[i].m_pbest.getObj());
			mp_pop[i].m_pbest=mp_pop[i].m_pself;
			if(mp_pop[i].m_pself>(m_best)) {
				m_best=mp_pop[i].m_pself;

			}
		}

		mp_pop[i].mp_prog[sel].m_numSelected++;
		//mp_pop[i].mp_monitor[sel].m_numSelected++;

		if(mp_pop[i].m_pself>(t_p)){
			mp_pop[i].mp_prog[sel].m_numSuccess++;
			mp_pop[i].mp_prog[sel].m_rewards+=fabs(t_p.getObj()-mp_pop[i].m_pself.getObj());
		}else
			mp_pop[i].m_itersUnimpr++;

        if(gIsTerminate()){
				f_return=true;
				break ;
        }

		if(mp_pop[i].m_pself>(t_p)){
		    updateBest(i,mp_pop[i].m_learnRatio);

			if(gIsTerminate()){
				f_return=true;
				break ;
			}
		}
	}
	delete [] avg_v;
	avg_v=0;
	delete [] rindex;
	rindex=0;

	if(f_return==true) return 0;


	updateParameters();
	m_evoNum++;
	
	return 0;
}



int SLSwarm::getNumLearnTogbest(){

	int n=0;
	for(int i=0;i<m_popsize;i++) if(mp_pop[i].m_flag) n++;

	return n;
}




void SLSwarm::MRandToBest( int num){

	if(num<=0){
		for(int i=0;i<m_popsize;i++)	mp_pop[i].m_flag=false;
		return;
	}
	if(num>m_popsize) num=m_popsize;

	for(int i=0;i<m_popsize;i++)	mp_pop[i].m_flag=false;
	vector<int> l;
	for(int i=0;i<m_popsize;i++) l.push_back(i);

	for(int j=0;j<num;j++){
		int k=gRandInt(0,l.size()-1);
		mp_pop[l[k]].m_flag=true;
		l.erase(l.begin()+k);
	}
	l.clear();

}

int SLSwarm::run(){
     while(!gIsTerminate()){
        //cout<<Global::g_runIdx<<" "<<m_best.getObj()<<endl;
        calculateNumLearning(0);
        evolve();
		initializePara(m_maxW-(m_maxW-m_minW)*Global::gp_problem->getEvaluations()/Global::g_tEvals,m_C1,m_C2,false,m_maxW,m_minW);
    }
	 #ifdef DEMON_EALIB
	 Buffer<SelfLearnParticle>::updateBuffer(this);
#endif
        return 0;
}




