# -*- coding:Utf-8 -*-
from random import *
# Claude TOUZET 03-2018
# CA-RCM : Carte auto-organisatrice pour la reconnaissance de chiffres manuscrits
# Apprentissage : 100 prototypes dans BA.TXT (BA2), fait l'apprentissage
# Test : 190 (100 de BA + 90 nouveaux) prototypes et affiche perf
learning = 1

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 .

BA = [
[12,42,39,35,7,39,29,0,41,38,0,39,18,41,26],
[0,28,0,12,55,0,29,41,0,0,35,0,10,62,42],
[42,34,0,19,34,0,3,35,0,30,11,0,54,36,31],
[21,27,11,0,25,8,8,27,20,0,0,39,6,21,27],
[16,19,0,36,9,0,41,20,44,5,22,36,0,11,30],
[18,34,23,43,0,0,30,34,7,0,14,35,26,34,9],
[1,38,17,30,13,0,48,0,0,50,27,35,20,40,60],
[31,49,39,4,20,33,11,54,44,7,55,6,4,53,0],
[18,46,35,47,1,57,17,46,37,1,43,55,11,49,32],
[31,45,46,46,9,63,42,44,58,0,0,49,6,43,30],
[37,38,3,37,11,47,43,0,53,40,0,45,22,45,29],
[0,44,0,5,58,0,36,52,0,0,45,0,30,60,49],
[33,41,19,46,22,26,6,43,7,36,18,0,26,38,33],
[28,45,16,9,34,11,0,39,41,0,19,33,28,37,2],
[9,39,0,40,11,3,45,42,44,2,50,5,4,46,0],
[34,43,30,52,0,0,41,39,18,0,14,40,35,48,15],
[13,36,2,34,13,0,44,15,3,44,19,42,24,36,37],
[17,28,36,0,18,26,35,50,45,16,39,3,31,23,0],
[35,53,23,51,13,52,17,55,26,33,40,52,52,42,10],
[33,55,28,53,23,64,33,36,50,0,17,55,48,38,6],
[15,59,29,35,10,51,41,0,49,48,2,50,26,65,31],
[0,48,0,1,52,0,68,49,0,0,45,1,42,75,55],
[26,51,3,23,41,9,8,52,0,44,13,16,55,52,48],
[48,49,11,17,42,9,15,56,47,3,2,54,11,51,26],
[25,33,0,47,7,11,60,42,60,5,43,25,0,47,10],
[34,49,28,49,5,0,61,35,23,10,20,50,34,45,6],
[0,40,17,19,39,0,47,35,13,52,22,59,51,50,28],
[27,38,23,0,28,19,40,61,44,16,38,1,35,16,0],
[31,54,43,46,17,30,32,61,9,30,41,27,27,46,17],
[40,53,19,57,23,47,31,39,54,0,2,59,52,59,18],
[1,23,38,38,29,56,43,0,50,49,1,50,19,44,36],
[0,42,6,0,56,2,38,62,0,0,54,0,16,67,47],
[25,64,9,48,46,7,12,50,0,50,9,1,50,49,35],
[25,53,11,40,40,6,18,58,36,6,7,47,12,45,21],
[14,46,1,42,25,11,25,45,50,0,30,38,0,42,19],
[13,38,26,46,1,0,38,18,7,2,19,42,24,41,15],
[0,25,24,11,41,3,34,17,1,47,32,52,29,36,39],
[32,51,42,0,45,23,28,66,13,30,41,13,35,22,0],
[37,39,35,41,26,32,18,48,17,22,42,26,26,44,12],
[45,39,3,54,46,42,23,31,60,0,2,58,34,52,27],
[26,43,16,53,17,40,43,0,47,52,0,57,34,41,34],
[0,17,27,0,27,34,28,48,28,0,21,22,20,54,33],
[21,47,7,51,32,15,10,50,2,32,24,0,32,36,30],
[46,45,18,11,51,9,1,25,45,0,0,55,27,45,24],
[19,31,0,51,17,21,18,37,47,0,33,33,0,45,14],
[23,38,33,53,2,0,31,28,7,0,18,39,34,42,6],
[0,18,26,7,41,5,25,30,0,34,37,29,17,37,36],
[15,40,49,0,22,40,20,56,15,32,43,16,40,19,0],
[14,39,51,18,51,39,23,63,5,56,33,22,29,54,15],
[16,64,20,37,56,55,7,29,59,0,3,60,39,53,26],
[11,62,38,43,13,53,52,0,53,56,1,55,47,59,20],
[0,48,10,0,60,2,37,75,0,19,58,0,35,71,42],
[26,60,22,54,38,30,9,62,4,50,22,0,56,51,46],
[11,59,27,14,51,22,0,20,56,0,11,57,41,55,10],
[18,26,0,45,12,25,42,39,56,1,32,38,0,33,28],
[15,44,41,43,24,0,23,23,0,1,24,49,40,49,23],
[7,41,0,24,33,0,38,32,7,39,37,54,24,50,46],
[38,43,52,0,9,50,24,58,25,20,44,23,29,31,0],
[31,45,42,41,42,19,30,41,0,45,36,12,19,54,16],
[14,62,19,57,40,70,26,40,56,0,0,70,46,59,34],
[13,54,10,43,17,45,54,0,52,53,4,56,19,49,22],
[0,32,17,1,56,12,30,59,3,1,54,7,31,64,26],
[29,45,11,43,38,17,10,50,2,40,18,0,33,40,38],
[38,41,7,14,45,5,21,33,19,0,10,48,33,41,15],
[15,36,0,46,19,7,36,40,52,0,36,33,3,50,7],
[28,48,28,49,3,0,56,17,2,14,30,50,21,51,22],
[0,33,18,14,37,1,41,25,0,48,34,28,21,38,49],
[34,45,38,0,21,36,25,51,21,20,39,8,21,29,0],
[38,46,40,45,38,27,34,39,0,49,46,1,28,41,0],
[0,41,37,0,56,63,0,5,55,0,0,51,38,46,45],
[21,72,29,59,11,57,49,0,56,53,0,64,34,59,31],
[0,40,7,0,52,10,49,70,2,5,56,6,24,64,39],
[23,51,2,35,54,6,9,52,0,53,20,10,50,56,51],
[36,51,3,23,42,7,8,61,20,2,11,53,7,56,40],
[20,22,0,49,9,15,50,18,58,13,32,56,0,4,49],
[7,40,46,21,22,1,18,42,6,0,8,54,32,46,33],
[3,44,10,27,31,0,47,41,25,52,13,55,34,46,45],
[26,29,28,0,6,53,28,40,34,14,45,8,12,35,0],
[24,41,37,31,39,40,13,49,8,44,32,40,27,41,22],
[9,39,47,28,29,51,7,37,48,0,4,53,44,52,24],
[10,71,32,46,11,57,55,0,48,47,1,61,22,59,45],
[0,29,19,23,61,13,20,58,0,0,58,8,17,64,52],
[42,37,0,25,50,0,12,42,0,42,19,22,41,53,38],
[43,49,2,12,58,3,30,52,22,0,9,55,41,48,18],
[20,29,0,56,14,8,59,44,70,3,51,39,0,43,30],
[18,43,58,48,32,1,44,21,1,3,42,32,51,55,11],
[0,19,22,13,41,5,32,28,3,45,39,47,30,41,34],
[28,45,38,0,14,43,17,50,24,30,48,23,21,33,0],
[44,35,55,48,12,49,10,63,31,21,46,55,13,56,38],
[18,53,43,41,13,63,18,49,57,0,6,60,41,57,22],
[10,55,13,40,17,40,43,0,38,28,0,46,13,53,28],
[0,38,17,36,58,22,13,23,16,0,24,10,3,60,37],
[34,46,7,40,13,23,2,40,10,17,37,5,2,48,52],
[42,44,0,10,42,6,0,44,34,0,6,45,37,59,15],
[20,17,0,32,9,3,40,17,44,15,40,29,0,10,25],
[41,52,14,40,0,0,24,49,17,0,6,50,26,55,34],
[0,24,30,13,39,1,37,21,2,42,33,39,15,37,45],
[49,55,37,0,2,46,21,39,51,12,59,13,0,40,0],
[15,39,20,36,24,38,26,36,18,12,39,28,1,33,41],
[20,49,10,50,39,25,34,35,42,0,0,48,33,58,44],
[0,35,32,19,36,43,46,1,47,35,10,44,46,61,10],
[0,35,8,28,57,2,11,40,0,31,54,32,51,26,29],
[18,49,0,25,60,2,27,36,0,49,9,20,42,58,28],
[20,42,8,11,37,8,5,50,18,0,11,45,33,53,19],
[30,16,0,41,4,31,38,46,38,1,34,19,0,39,7],
[15,51,8,50,17,0,47,38,16,7,18,63,43,63,40],
[0,19,34,14,39,3,41,40,22,45,18,54,27,45,32],
[37,47,38,0,24,37,43,55,38,45,15,0,34,0,0],
[18,36,23,7,33,40,0,29,33,15,39,40,30,39,15],
[25,55,1,42,40,33,36,69,53,2,34,82,81,71,22],
[0,51,16,30,44,44,54,0,45,45,7,58,51,63,14],
[0,35,3,33,60,3,29,48,0,6,50,34,42,59,35],
[41,25,0,31,33,0,39,13,0,45,0,2,38,61,41],
[32,49,6,23,56,5,12,56,20,0,13,52,33,57,26],
[14,28,0,34,9,0,32,31,45,7,34,30,0,15,24],
[25,52,23,44,12,0,33,26,18,3,23,62,10,55,26],
[2,43,18,26,32,0,45,6,0,50,51,37,29,47,56],
[26,49,26,0,23,28,35,57,40,18,28,0,31,13,0],
[24,34,46,46,36,39,29,52,3,23,49,32,8,49,40],
[35,49,20,57,30,61,27,48,66,0,9,67,68,76,25],
[7,44,13,32,20,29,37,0,35,30,4,46,16,48,24],
[0,30,0,31,45,0,15,38,0,3,43,14,29,51,37],
[39,36,0,11,42,2,28,40,0,46,17,30,28,53,22],
[57,45,0,22,58,0,25,51,54,0,7,61,31,64,24],
[9,44,0,44,9,32,31,44,50,0,50,13,0,39,0],
[16,40,21,49,22,0,31,48,30,0,13,56,26,53,17],
[0,21,45,8,48,7,34,23,0,46,36,24,13,42,31],
[47,74,2,4,57,0,48,65,51,59,30,11,38,0,0],
[24,47,37,44,25,46,15,56,20,43,34,50,50,49,29],
[19,65,25,50,11,61,31,59,50,10,13,60,63,68,18],
[9,69,20,53,40,43,58,0,61,52,10,63,35,65,18],
[0,36,0,9,63,0,58,59,0,19,63,51,64,36,10],
[24,39,7,37,28,27,3,46,9,42,27,3,38,43,48],
[34,46,0,27,55,0,23,53,14,0,16,64,26,50,23],
[15,27,0,37,17,15,40,26,48,7,32,33,0,36,9],
[16,44,43,49,21,0,30,50,31,0,7,58,57,52,17],
[0,25,13,14,46,3,35,43,18,55,17,55,34,47,35],
[32,47,29,0,30,29,45,56,39,38,13,0,40,0,0],
[24,0,67,49,9,57,9,58,35,0,56,47,0,53,44],
[26,65,11,59,21,57,35,56,64,0,1,69,31,70,37],
[27,67,25,56,15,50,52,0,50,50,1,56,36,47,26],
[0,44,4,16,65,10,26,58,5,0,57,9,2,75,40],
[27,48,14,12,44,23,15,59,2,54,13,0,37,44,50],
[37,39,5,16,51,6,12,40,32,0,10,51,30,45,10],
[26,27,0,46,15,6,43,35,53,5,40,40,0,48,24],
[8,53,27,47,17,0,28,53,38,0,13,62,47,62,12],
[10,53,7,40,18,1,58,35,20,58,38,58,40,39,16],
[33,48,21,0,39,31,4,58,5,53,61,36,21,25,0],
[45,53,15,0,54,43,17,62,50,52,19,63,38,62,30],
[0,60,4,0,92,41,0,49,55,0,0,68,58,82,64],
[3,56,41,37,10,46,39,0,43,40,0,41,57,32,39],
[0,6,31,0,30,29,20,36,18,0,28,18,31,44,32],
[20,48,4,27,39,5,3,40,0,32,17,0,22,40,32],
[28,44,0,10,40,0,23,49,53,11,6,45,4,43,7],
[7,37,1,30,21,0,38,31,44,3,38,29,2,46,9],
[19,39,36,39,2,0,58,40,25,2,9,44,22,48,6],
[0,42,23,17,39,0,40,22,6,57,26,65,45,43,26],
[28,51,17,0,34,11,23,62,32,13,40,0,24,18,0],
[24,41,45,49,8,48,43,43,12,37,57,8,36,50,7],
[13,54,48,47,13,56,26,38,55,0,1,51,31,45,25],
[0,18,47,10,52,65,49,6,56,57,1,53,48,53,16],
[0,24,19,5,55,2,29,56,0,3,46,17,54,55,36],
[10,62,8,7,57,12,4,48,0,40,14,10,56,57,44],
[8,51,28,20,38,24,0,43,62,0,2,57,44,48,15],
[11,26,0,44,4,27,49,33,48,6,34,30,0,39,8],
[0,44,16,9,55,1,0,33,61,0,5,52,45,51,6],
[0,12,42,1,46,4,26,54,30,53,17,58,28,41,47],
[9,47,39,3,26,39,21,62,34,34,52,16,27,24,0],
[31,51,30,50,45,13,25,54,25,42,14,53,29,40,29],
[6,60,16,45,40,62,40,51,57,2,16,58,48,52,11],
[17,93,43,58,23,66,76,0,69,65,10,61,67,70,9],
[0,3,50,1,35,46,31,56,22,4,55,3,18,36,0],
[37,35,0,38,49,0,25,34,0,49,7,25,48,58,26],
[5,55,30,25,37,37,0,26,54,0,4,59,44,53,17],
[13,40,1,26,32,18,54,39,60,58,59,41,0,32,24],
[0,22,55,21,55,2,8,62,27,0,14,53,58,60,10],
[0,29,15,12,48,0,42,34,18,61,41,60,51,51,16],
[23,47,21,0,36,24,27,65,33,33,37,0,30,16,0],
[27,55,57,52,10,54,31,59,6,30,78,3,64,42,0],
[51,40,7,60,49,38,19,36,61,0,1,68,46,59,30],
[9,59,32,50,25,70,56,0,57,53,0,67,34,47,31],
[0,26,14,16,47,16,10,48,1,0,56,21,30,46,20],
[13,54,21,30,38,33,2,55,12,22,38,7,43,53,43],
[31,66,1,15,61,13,0,34,62,0,23,45,57,45,1],
[6,41,0,32,19,27,54,35,51,20,54,28,0,43,9],
[26,33,36,41,19,0,23,37,19,0,3,53,36,43,24],
[0,42,31,13,47,0,40,38,1,52,43,26,48,59,17],
[33,42,24,4,26,26,21,44,20,22,42,10,14,35,0],
[7,52,54,50,22,52,18,73,9,35,56,22,47,64,10],
[19,74,20,59,37,79,35,60,68,0,12,72,69,88,14]]

nb_iterations=100 # learning iterations : 100
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

SOM = []    
WSOM = []
nbproto = 190 # only for learning : test is after nbproto...
chiffre = []    
for i in range (nb_samples) : 
    chiffre.append(i%10)
for i in range (nb_neuron_som) :
  SOM.append(0)
if (learning==1) : # initialisation aléatoire des poids de la SOFM
    for i in range (0, nb_neuron_som) :
        WSOM.append([])
        for j in range (0, nb_entree) :
            WSOM[i].append(5.0 + random()/10)
            
# Self-organizing map  
mu1=1.0 
beta1=0.5 
if (learning==1) : #LEARNING
    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[0])) :
                xx = int (random()*(len(BA2)))
                yy = int (random()*len(BA2[0]))
                a = BA2[xx][yy]
                #print a
                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 = 100000
                for m in range (0, nb_neuron_som) : # compute distances 
                    if (SOM[m] < mini) :
                       mini = SOM[m]
                       index = m
                m=index; # winner
                #if (n == (nb_iterations-1)) :
                    #print 'winner : ',m
                for k in range (0, nb_entree) :
                        WSOM[m][k] = WSOM[m][k] + mu*(BA[a][k]-WSOM[m][k]) 
                
                # bord gauche
                m=index-1 # neighbour gauche
                #print m, (m/nb_neuron_som1D)*1.0, ceil (m/nb_neuron_som1D)
                if ((index%nb_neuron_som1D) != 0) :
                    if (m>0) :
                        #if (n == nb_iterations-1) :
                                #print 'gauche',m, index
                        for k in range (0,nb_entree) :
                                WSOM[m][k] = WSOM[m][k] + beta*(BA[a][k]-WSOM[m][k]) 
                 
                m=index+1 # neighbour droit
                if ((m%nb_neuron_som1D) != 0) :
                    if (m<nb_neuron_som) :
                        #if (n == nb_iterations-1) :
                                #print 'droit',index,m
                        for k in range (0,nb_entree) :
                                WSOM[m][k] = WSOM[m][k] + beta*(BA[a][k]-WSOM[m][k])
                                
                m=index-nb_neuron_som1D # neighbour haut
                if (m>=0) :
                        #if (n == nb_iterations-1) :
                                #print 'haut',index,m
                        for k in range (0,nb_entree) :
                                 WSOM[m][k] = WSOM[m][k] + beta*(BA[a][k]-WSOM[m][k])
     
                m=index+nb_neuron_som1D # neighbour bas
                if (m<(nb_neuron_som)) :
                        #if (n == nb_iterations-1) :
                                #print 'bas',index,m
                        for k in range (0,nb_entree) :
                                WSOM[m][k] = WSOM[m][k] + beta*(BA[a][k]-WSOM[m][k])

# généralisation
liste=[]
for i in range (10) :
    liste.append([])
for a in range (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 = 100000
    for m in range (0, nb_neuron_som) : # compute distances 
          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

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)

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
  
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],end=" "),# le 1er de la liste (de chiffres identiques)
        else :
          print (' ','-',end=" "),
    else :
        print (' ',AZ[pb[i]],end=" "),#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],' : ',end=" "),
      for j in range (len(neurone[i])) :
        print (neurone[i][j],end=" "),
        
liste1=[]
for i in range (nb_neuron_som) :
    liste1.append([]) # initialisation
for a in range (0, nbproto) :     
    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 = 100000
    for m in range (0, nb_neuron_som) : # compute distances 
          if (SOM[m] < mini) :
               mini = SOM[m]
               index = m
    m=index; # winner
    liste1[m].append(a) # associe cet exemple a ce gagnant
    
print ('\n\nles numéros des exemples pour chaque neurone')
for i in range (len(liste1)) :
    print (i," : ",liste1[i]) # les numéros des exemples pour ce neurone
 
