#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Sep  5 23:29:38 2022

@author: ojacques
"""

import numpy as np
import matplotlib.pyplot as plt

x=np.arange(-2,2.01,0.01)
y=np.cos(20*x)-np.abs(x)/2+np.power(x,3)/4
plt.plot(x,y)
plt.grid('on')


#----------------------------------------------------------------
#
# Numero de genes, tamanho da população, probabilidade de mutação
#
#----------------------------------------------------------------
nBits=22  
nPop = 20
pMut = 0.05
intConvergencia = 0.0001
nGeracoesConv= 10

vMin=-2
vMax=2

#----------------------------------------------------------------
#
# Gerando uma população inicial
#
#----------------------------------------------------------------


#cromossomo=[] #vários cromossomos, vários indivíduos ou soluções
acromossomo = np.random.randint(0,2,(nPop,nBits))#array de bits
#for i in range(nPop):
#    cromossomo.append(''.join(str(x) for x in acromossomo[i]))

#processo semelhante ao acima com array    
pot=np.power(2,np.arange(nBits-1,-1,-1))#potências de 2²¹ a 2⁰

#print(acromossomo)

#transformando vetor de bits e reais no intervalo [vMin, vMax]
#dec=acromossomo@pot
#axr=vMin + (vMax-vMin)*dec/(2**nBits - 1)

fx=np.cos(20*axr)-np.abs(axr)/2+np.power(axr,3)/4


fitness = fx+4

solucaoAnterior=fx[np.argmin(fitness)] 
novaSolucao=fx[np.argmax(fitness)]
numConv=0
if abs(novaSolucao - solucaoAnterior) <= intConvergencia:
    numConv += 1
#while numConv<nGeracoesConv:
px=fitness/np.sum(fitness)
prbAc=np.cumsum(fitness)/np.sum(fitness)

pais=[]
#poderia gera apais
for i in range(10):
    r=np.random.uniform()
    id=np.argmax(prbAc>r)
    sel = acromossomo[id]  
    if i>0:
        while sel in pais: #evita seleção repetida
            r=np.random.uniform()
            id=np.argmax(prbAc>r)
            sel=acromossomo[id]
    pais.append(sel)
    
filhos=[]
for p1 in range(len(pais)-1):
    for p2 in range(p1+1,len(pais)):
        c=np.random.randint(1,20) #ponto de corte
          
        #afilho1[0:c+1]=pais[p2][0:c+1]
        #afilho1[c+1:]=pais[p1][c+1:]
        #afilho2[0:c+1]=pais[p1][0:c+1]
        #afilho2[c+1:]=pais[p2][c+1:]
        filho1=pais[p2][0:c+1]+pais[p1][c+1:]
        filho2=pais[p1][0:c+1]+pais[p2][c+1:]
        
        #evitar filhos repetidos
        while (filho1 in filhos) or (filho2 in filhos) or (filho1 in acromossomo) or (filho2 in acromossomo):
            c=np.random.randint(1,20)
            filho1=pais[p2][0:c+1]+pais[p1][c+1:]
            filho2=pais[p1][0:c+1]+pais[p2][c+1:]
            
        filhos.append(filho1)
        filhos.append(filho2) 
        
        
for i,f in enumerate(filhos):
    r=np.random.uniform()
    if r<pMut:
        bit=np.random.randint(0,nBits-1)
        if filhos[i,bit]==0:
            filhos[i,bit]=1
        else:
            filhos[i,bit]=0
novageracao=[]
novageracao=list(np.append(np.array(acromossomo),np.array(filhos)))    


fx = np.cos(20*axr)-np.abs(axr)/2+np.power(axr,3)/4
fitness = fx+4

indMax = np.argmax(fx)
maxX=axr[indMax]
maxF=fx[indMax]
print(f'Melhor solucão: f({maxX})={maxF}') 

x=np.arange(-2,2.01,0.01)
y=np.cos(20*x)-np.abs(x)/2+np.power(x,3)/4
plt.plot(x,y)
plt.plot(maxX,maxF,'ro')
plt.grid('on')
input()

ind = np.argsort(fitness)[-nPop:] #toma os nPop melhores indivíduos
ind = np.argsort(fx)[-nPop:]
cromossomo=[]
cromossomo = list(np.array(novageracao)[ind])

xr=np.array(list(map(bin2float,cromossomo)))  
fx = np.cos(20*xr)-np.abs(xr)/2+np.power(xr,3)/4

novaSolucao=fx[-1]
numConv += 1
if abs(novaSolucao - solucaoAnterior) > intConvergencia:
    numConv=0

#if __name__=='__main__': #chamado como prompt $ python algGen.py
    