# -*- coding:utf-8 -*-
# Claude TOUZET 03-2017
# CA-RCM : Carte auto-organisatrice pour la reconnaissance de chiffres manuscrits
# Prend 100 prototypes dans BA.TXT, fait l'apprentissage, sauve les poids, et affiche perf
# Prend les 190 prototypes et affiche perf
# Question 1 : influence du nombre d'itérations
# Question 2 : influence de l'initialisation des poids
# Question 3 : influence de l'ordre de présentation des exemples 


from __future__ import unicode_literals # pour que les accents s'affichent correctement en Python 2.7
from random import *
import os.path # pour pouvoir tester l'exstence du fichier BA1.py
from BA1 import BA # les 190 exemples de chiffres manuscrits

def appr (alpha, ex, index) : # Apprentissage : modification des poids d'un neurone
    global WSOM, BA
    for k in range (0, nb_entree) : 
        WSOM[index][k] = WSOM[index][k] + alpha*(BA[ex][k]-WSOM[index][k])
    return

nb_iterations= int(input("Nombre d'iterations (100) ? "))
init= int(input("Initialisation aléatoire (1) ou toujours la même (0) ? "))
ordre= int(input("Sélection des exemples d'apprentissage aléatoire (1) ou dans l'ordre de BA2 (0) ? "))

BA2 = [
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90], 
[1, 11, 21, 31, 41, 51, 61, 71, 81, 91], 
[2, 12, 22, 32, 42, 52, 62, 72, 82, 92], 
[3, 13, 23, 33, 43, 53, 63, 73, 83, 93], 
[4, 14, 24, 34, 44, 54, 64, 74, 84, 94], 
[5, 15, 25, 35, 45, 55, 65, 75, 85, 95], 
[6, 16, 26, 36, 46, 56, 66, 76, 86, 96], 
[7, 17, 27, 37, 47, 57, 67, 77, 87, 97], 
[8, 18, 28, 38, 48, 58, 68, 78, 88, 98], 
[9, 19, 29, 39, 49, 59, 69, 79, 89, 99]]
# BA2 contient les numeros des exemples de BA (taille 190) utilises pour la base d'apprentissage
# le premier vecteur contient les 10 exemples du 0, le suivant les 10 exemples du 1, etc .

nb_entree=15  # nb d'entrees sur chaque neurone
nb_neuron_som1D=10 # 1-D neurons of the self-organizing map
nb_neuron_som=nb_neuron_som1D*nb_neuron_som1D # 2-D neurons of the self-organizing map
nb_samples=190 # number of samples

chiffre = []
for i in range (nb_samples) : chiffre.append(i%10)
SOM = []; WSOM = []
for i in range (nb_neuron_som) :
  SOM.append(0)
  WSOM.append([])
  for j in range (0, nb_entree) :
      WSOM[i].append(5.0 + random()/10)

# Store the WSOM (également si n'existe pas)
if (init == 1) or (not(os.path.isfile('WSOM_init.dat'))) : 
    fich=open('WSOM_init.dat', 'w')
    for i in range (0, nb_neuron_som) : 
        for j in range (0, nb_entree) :
            fich.write (str(int(WSOM[i][j]*10000))),
            fich.write (' '), 
        fich.write('\n')
    fich.close()

fich = open('WSOM_init.dat', 'r')  # relit les poids
i=-1
for line in fich.readlines() :
    i=i+1; j=0
    word1 = line.rstrip('\r\n') # enleve retour chariot final
    word = word1.rsplit(' ') # decoupe selon ' '
    for nb in word :
        if (len(nb)>0) : 
            WSOM[i][j]=int (nb)/10000.
            j = j+1
fich.close()

print 'APPRENTISSAGE (', nb_iterations,'it.)'
mu1=1.0 
beta1=0.5 
for n in range (0, nb_iterations) : # iterations
    #print 'iteration = ',n
    mu = mu1 - mu1 * (n/nb_iterations)
    beta = beta1 - beta1 * (n/nb_iterations)
    for x in range (0, len(BA2)) :   
        for y in range (len(BA2[x])) :
            xx = int (random()*(len(BA2))); yy = int (random()*len(BA2[0])); a = BA2[xx][yy] # sélection aléatoire
            if (ordre==0) : a = BA2[x][y] # sélection dans l'ordre des ex d'appr
            for m in range (0, nb_neuron_som) : # compute distances 
                SOM[m]=0
                for k in range (0, nb_entree) : SOM[m] = SOM[m] + abs(BA[a][k]-WSOM[m][k]) 
            mini = SOM[0]; index = 0
            for m in range (1, nb_neuron_som) : # on cherche le plus proche 
                if (SOM[m] < mini) : mini = SOM[m]; index = m
            appr (mu, a, index) # gagnant
            if (((index%nb_neuron_som1D) != 0) and (m>0)) : appr (beta, a, index-1) # neighbour gauche
            if ((((index+1)%nb_neuron_som1D) != 0) and  ((index+1)<nb_neuron_som)) : appr (beta, a, index+1) # neighbour droit               
            if ((index-nb_neuron_som1D)>=0) : appr (beta, a, index-nb_neuron_som1D) # neighbour haut
            if ((index+nb_neuron_som1D)<(nb_neuron_som)) : appr (beta, a, index+nb_neuron_som1D) # neighbour bas

liste=[] # on repasse 1 fois pour obtenir les labels
for i in range (10) :
    liste.append([])
for x in range (0, len(BA2)) :   
    for y in range (len(BA2[x])) :
        a = BA2[x][y] # sélection dans l'ordre des ex d'appr
        for m in range (0, nb_neuron_som) : # compute distances 
            SOM[m]=0
            for k in range (0, nb_entree) : SOM[m] = SOM[m] + abs(BA[a][k]-WSOM[m][k]) 
            mini = SOM[0]; index = 0
            for m in range (1, nb_neuron_som) : # on cherche le plus proche 
                if (SOM[m] < mini) : mini = SOM[m]; index = m
        m=index; # winner
        liste[chiffre[a]].append(m) # ajouter ce neurone à la liste des gagnants pour ce chiffre
    
'''print 'Liste des numéros des neurones associés à chacun des 10 chiffres'
for i in range (10) :
     print 
     print i,' : ',
     for j in range (len(liste[i])) :
         print liste[i][j],
print
print
'''
'''
0  :  26 26 33 33 26 33 26 33 27 26
1  :  13 13 13 13 20 13 13 13 13 19
2  :  15 15 15 15 15 15 15 15 15 18
3  :  29 10 10 4 10 6 10 10 10 10
4  :  12 12 12 11 11 11 11 12 12 12
5  :  9 9 9 2 9 9 9 9 8 3
6  :  1 2 2 1 7 2 1 2 7 1
7  :  22 24 24 28 16 28 28 28 23 22
8  :  16 28 16 16 15 15 15 15 16 17
9  :  6 6 6 6 6 6 6 6 6 5
'''
neurone=[]
pb=[]
pb1=[]
for i in range (nb_neuron_som) :
  neurone.append([])
  pb.append(-1)
  pb1.append(-1)
for i in range (10) :
  for j in range (len(liste[i])) :
    neurone[liste[i][j]].append(i)
'''print 'Liste des chiffres associés à chaque neurone'
for i in range (nb_neuron_som) :
  print i, ' : ', neurone [i]
print
'''
'''
0  :  [1, 1, 1]
1  :  [6, 6, 6, 6, 6]
2  :  [6, 6, 6]
3  :  []
4  :  [0, 0, 0, 0, 0, 0]
5  :  [0, 0, 0, 0]
6  :  [5]
'''
courant = -1
for i in range (nb_neuron_som) :
  for j in range (1, len(neurone[i])) :
    if (neurone[i][j-1] != neurone[i][j]) :
      if pb[i] == -1 :
        courant = courant +1 #lettre suivante
        pb[i] = courant
        pb1[i] = i # le neurone
print 'Pour chaque neurone de la carte, le(s) chiffre(s) associé(s)'
AZ=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
#affichage de la carte avec à la place des neuroens, le chiffre associé (s'il existe) 
for i in range (nb_neuron_som) :  # pour chaque neurone
    if (i%nb_neuron_som1D) == 0 : # retour à la ligne
        print
    if (pb[i] == -1) :
        if (len(neurone[i]) > 0) :
          print ' ',neurone[i][0],# le 1er de la liste (de chiffres identiques)
        else :
          print ' ','-',
    else :
        print ' ',AZ[pb[i]],#indice
print '\n\nConfusions : '
# affichage de la liste des indices
courant = -1
for i in range (nb_neuron_som) :
  if (pb1[i] != -1) :
      courant = courant +1
      print '\n',AZ[courant],' : ',
      for j in range (len(neurone[i])) :
        print neurone[i][j],
'''
  1   6   6   -   0   0
  5   6   -   8   -   -
  a   -   1   2   -   7
  9   8   -   b   4   7
  c   8   3   d   -   5
  e   f   3   9   5   5

a  :  3 9 9 
b  :  3 4 4 4 4 4 4 4 
c  :  7 7 8 8 8 8 8 8 
d  :  3 3 9 
e  :  1 1 2 2 2 2 
f  :  1 2 2 2 2 2
'''

print '\n\nGENERALISATION'
liste=[]
for i in range (10) :
    liste.append([])
for a in range (0, nb_samples) :     
    for m in range (0, nb_neuron_som) : # compute distances 
        SOM[m]=0
        for k in range (0, nb_entree) :
            SOM[m] = SOM[m] + abs(BA[a][k]-WSOM[m][k]) 
        mini = SOM[0]; index = 0
        for m in range (1, nb_neuron_som) : # on cherche le plus proche 
            if (SOM[m] < mini) : mini = SOM[m]; index = m
    m=index; # winner
    liste[chiffre[a]].append(m) # ajouter ce neurone à la liste des gagnants pour ce chiffre
'''print '\nNeurones associés aux chiffres'
for i in range (10) :
     print 
     print i,' : ',
     for j in range (len(liste[i])) :
         print liste[i][j],
print
print
print 'Exemples associés à chacun des neurones\n'
'''
'''
0  :  26 26 33 33 26 33 26 33 27 26
1  :  13 13 13 13 20 13 13 13 13 19
2  :  15 15 15 15 15 15 15 15 15 18
3  :  29 10 10 4 10 6 10 10 10 10
4  :  12 12 12 11 11 11 11 12 12 12
5  :  9 9 9 2 9 9 9 9 8 3
6  :  1 2 2 1 7 2 1 2 7 1
7  :  22 24 24 28 16 28 28 28 23 22
8  :  16 28 16 16 15 15 15 15 16 17
9  :  6 6 6 6 6 6 6 6 6 5
'''

neurone=[]
pb=[]
pb1=[]
for i in range (nb_neuron_som) :
  neurone.append([])
  pb.append(-1)
  pb1.append(-1)
for i in range (10) :
  for j in range (len(liste[i])) :
    neurone[liste[i][j]].append(i)
'''for i in range (nb_neuron_som) :
  print i, ' : ', neurone [i]
print
print 'la carte avec les chiffres à la place des neurones. ( - pas exemple associé)'
'''
'''
0  :  [1, 1, 1]
1  :  [6, 6, 6, 6, 6]
2  :  [6, 6, 6]
3  :  []
4  :  [0, 0, 0, 0, 0, 0]
5  :  [0, 0, 0, 0]
6  :  [5]
'''
courant = -1
for i in range (nb_neuron_som) :
  for j in range (1, len(neurone[i])) :
    if (neurone[i][j-1] != neurone[i][j]) :
      if pb[i] == -1 :
        courant = courant +1 #lettre suivante
        pb[i] = courant
        pb1[i] = i # le neurone
print 'Pour chaque neurone de la carte, le(s) chiffre(s) associé(s)'  
AZ=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
#affichage de la carte avec à la place des neuroens, le chiffre associé (s'il existe) 
for i in range (nb_neuron_som) :  # pour chaque neurone
    if (i%nb_neuron_som1D) == 0 : # retour à la ligne
        print
    if (pb[i] == -1) :
        if (len(neurone[i]) > 0) :
          print ' ',neurone[i][0],# le 1er de la liste (de chiffres identiques)
        else :
          print ' ','-',
    else :
        print ' ',AZ[pb[i]],#indice
print
print '\nla liste des confusions'
# affichage de la liste des indices
courant = -1
for i in range (nb_neuron_som) :
  if (pb1[i] != -1) :
      courant = courant +1
      print '\n',AZ[courant],' : ',
      for j in range (len(neurone[i])) :
        print neurone[i][j],

