// Strategie evolutiva de tip mu+lambda
//
// Parametri:
//   mu:  numarul de parinti
//   lambda: numarul de urmasi 
//   n: dimensiunea fiecarui element
//   gen:  numar de generatii
//   [a,b]: domeniul de definitie al functiei de optimizat (minimizat)
//          (in cazul retelei neuronale e domeniul de valori pt ponderi)
//   sigma: parametrul care controleaza marimea perturbatiei
//   rho: numarul de parinti utilizati in recombinare
//   selectie: numele functiei de selectie
//   functie: numele functiei de minimizat
//   nrHidden: numar de unitati ascunse
// Exemplu de apel: SEretea(20,20,100,-1,1,0.5,2,@SE_trunchiere,@SE_XOR,5);

function [pop,f]=SEretea(mu,lambda,gen,a,b,sigma,rho,selectie,functie,nrHidden)
n=4*nrHidden+1;
pop1=SE_initpop(mu,n,a,b); //initializarea populatiei
pop2=zeros(lambda,n); //alocare spatiu pentru populatia de copii obtinuta dupa recombinare si  mutatie
m=mu+lambda;
f=zeros(1,m);      //alocare spatiu pentru valorile elementelor populatiei reunite
for k=1:gen
   // generare populatie de urmasi prin recombinare si mutatie  
   for i=1:lambda
      pop2(i,:)=SE_recombinare(pop1,rho);
      pop2(i,:)=SE_mutatie(pop2(i,:),sigma);
   end
   pop=[pop1;pop2];  // concatenarea populatiei curente si a populatiei de copii
   // evaluarea tuturor elementelor
   for i=1:m
      f(1,i)=functie(pop(i,:),nrHidden);     
   end   
   // selectie urmasi
   pop1=selectie(pop,f,mu); 
   disp(min(f));  // afisarea valorii celui mai bun element din populatie
   if (min(f)==0)
       break;
   end;    
end 
 disp("Nr generatii "); disp(k); 
 errMin=min(f);
 disp('Eroare:'); disp(errMin); 
endfunction

// Strategie evolutiva:  initializarea populatiei (m vectori avand cate n componente)
// fiecare componenta se genereaza uniform aleator in intervalul [a,b]
function pop = SE_initpop(m,n,a,b)
pop=zeros(m,n);
for i=1:m
   for j=1:n
      pop(i,j)=grand(1,1,"unf",a,b);
   end   
end   
endfunction

// Strategie evolutiva: recombinare
// pornind de la rho parinti selectati aleator se genereaza un fiu: media aritmetica a parintilor
function copil=SE_recombinare(p, rho)
m=size(p,1); 
n=size(p,2);
copil=zeros(1,n);
for k=1:rho
   ind=grand(1,1,"uin",1,m);  // selectare indice parinte
   copil=copil+p(ind,:); 
end
copil=copil/rho;
endfunction

// Strategie evolutiva: mutatie
// genereaza un nou element prin aplicarea unei perturbatii normale de medie 0 si abatere standard sigma
function copil=SE_mutatie(parinte,sigma)
n=size(parinte,2);
copil=parinte;
for i=1:n
   copil(i)=copil(i)+grand(1,1,"nor",0,sigma);
end   
endfunction

// selectie de tip turneu
function p2=SE_turneu(p1,f1,mu)
m=size(p1,1);
p2=[];
sel=zeros(1,m);
for i=1:mu
   j1=grand(1,1,'uin',1,m);
   j2=grand(1,1,'uin',1,m);
   if f1(j1)<f1(j2)
      p2(i,:)=p1(j1,:);
   else   
      p2(i,:)=p1(j2,:);  
   end   
end   
endfunction

// selectie prin trunchiere
function p2=SE_trunchiere(p1,f1,mu)
m=size(p1,1); 
n=size(p1,2);
p2=zeros(mu,n);
sel=zeros(1,m); 
for i=1:mu
   jmin=1;
   for j=2:m
      if sel(j)==0 
         if f1(j)<f1(jmin)
            jmin=j;
         end   
      end
   end   
   sel(jmin)=1;
   p2(i,:)=p1(jmin,:);
end  
endfunction


function v=H(u)
if u<=0
    v=0;
else
    v=1;
end
endfunction

function err=SE_XOR(x,k)
b=[-1 -1 -1 -1; 0 0 1 1; 0 1 0 1];
d=[0 1 1 0];
w1=zeros(k,3); w2=zeros(1,k);
n=size(x,2); disp(n,"n=");
w1(:,1)=x(1:k)'; disp(w1(:,1),"w1=");
w1(:,2)=x(k+1:2*k)'; disp(w1(:,2),"w2=");
w1(:,3)=x(2*k+1:(3*k))'; disp(w1(:,3),"w3=");
w2=x(3*k+1:(4*k+1));
err=0;
for j=1:4
   x1=w1*b(:,j);
   for i=1:k
       y1(i)=H(x1(i));
   end    
//   disp(y1,"y1=");
   x2=[-1 y1']';
   y=H(w2*x2);
//   disp('y');
//   disp(y);
//   disp('d');
//   disp(d(j));
   err=err+(d(j)-y)^2;
end
disp(err,"err=");
endfunction

SEretea(20,20,100,-1,1,0.5,2,SE_trunchiere,SE_XOR,5)










