self organizing map

    source colors rgb

    number of source colors
    000
    001
    010
    011
    100
    101
    110
    111

    result square map


    Source /home/ch45859/web/wlkl.ch/public_html/inf/js/nn1.php

    <html>
     <head>
      <title> <?php echo basename(__file__, '.php'); ?> </title>
      <style type="text/css">
        #src { 
            display: block; width: 250px; height: 250px; text-align: center; 
        }
    /*    #c000, #c001, #c010, #c011, #c100, #c101, #c110, #c111  { 
            display: block; float:left; width: 60px; height: 60px; line-height: 60px; font-size: 12px;
        }
        #c000 { background-color: #000; color: #fff; }
        #c001 { background-color: #00f; color: #fff; }
        #c010 { background-color: #0f0; color: #fff; }
        #c011 { background-color: #0ff; color: #000; }
        #c100 { background-color: #f00; color: #fff; }
        #c101 { background-color: #f0f; color: #000; }
        #c110 { background-color: #ff0; color: #000; }
        #c111 { background-color: #fff; color: #000; } */
        #r {    clear: both;
            display: block; width: 500px; height: 500px; background-color: green; text-align: center; font-size: 12px;
        }
            
      </style>
        <script type="text/javascript">
        var rc = -1;
        var aa = []; // result array [row, col, r g b]
        var sc = []; // source colore [x, r g b]
        var it = 0;
    
        function numCols(nCo) { /* draw source colors */
            var co = parseInt(nCo);
            var h = '';
            sc = [];
            if (isNaN(co) || co < 2) {
                h = 'change num colors to illegal ' + nCo;
            } else {
                var w = Math.floor(250 / Math.ceil(Math.sqrt(co)));   /* width of one source cell */
                var d = Math.ceil(Math.cbrt(co));                      /* how many steps for each of the 3 base colors */    
                var d3 = d * d * d - 1;
                var st = 'style="display:block; float: left;width:' + w + 'px; height: ' + w + 'px; line-height: ' + w + 'px; ';
                var cf = 15 / (d-1);
                for (var x=0; x < co; x++) {
                    var cc = Math.round(d3 / (co-1) * x);            /* current color index */
                    var ca = [Math.round(Math.floor(cc / d / d) * cf), Math.round(Math.floor(cc / d) % d * cf), Math.round(cc % d % d * cf)]; /* rgb  fff value */
                    var ct = colStr(ca);
                    h += '<div ' + st + ' background-color: ' + ct + '; color: ' + (ca[0] + ca[1] + ca[2] < 24 ? '#fff' : '#000') + ';">'
                        + ct + '</div>';
                    sc.push(ca);
                }
            }
            document.getElementById("src").innerHTML = h;
            numRCcha(document.getElementById("numRC").value);
        }
    
        function colStr(c) { /* return the color string #fff from the rgb color c[0] c[1] c[2],  each  in [0, 15] */
            return '#' + Math.round(c[0]).toString(16) + Math.round(c[1]).toString(16) + Math.round(c[2]).toString(16);
        }
    
        function numRCcha(nRc) { /* randomly initialize the nRc*nRc map colors */
            rc = parseInt(nRc);
            if (isNaN(rc) || rc < 1) {
                alert('change num row/cols to illegal ' + nRc);
            } else {
                aa = [];
                for (var i = 0; i < rc; i++ ) {
                    rr = [];
                    for (var j = 0; j < rc; j++ ) {
                    //    rr.push([7,0,12]);
                        rr.push([Math.floor(Math.random() * 16), Math.floor(Math.random() * 16), Math.floor(Math.random() * 16)]);
                    }
                    aa.push(rr);        
                }
            //    alert('change num row/cols to legal ' + rc);
            }
            it = 0;
            draw();
            document.getElementById("rTxt").innerHTML = 'randomly initialised s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist);
            
        }
    
        var chg = 0;
    
        function iterate(num) { /* one iteration: move each map color near to the winner (by epW) and neiboughrs nearer by epN */
            if (rc < 1) {
                document.getElementById("rTxt").innerHTML = 'cannot iterate illegal rows/cols=' + rc;
                return;
                }
            else if (sc.length < 2) {
                document.getElementById("rTxt").innerHTML = 'cannot iterate empty src';
                return;
                }
        
            for ( var c=0; c < num; c++) {
                iterate1();
            }
            draw();
            document.getElementById("rTxt").innerHTML =  'iteration ' + it + ' s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist) + ' chg=' + chg;
        }
    
        function iterate1() {
            var epW = 0.003;
            var epN = epW/5;
            chg = 0;
            for (var x=0; x < sc.length; x++) {
                var y = x;
                y = Math.floor(Math.random() * sc.length)
                var bi = 0, bj = 0;                        
                for (var i = 0; i < rc; i++ )
                    for (var j = 0; j < rc; j++ ) {
                        if ( dist2(sc[y], aa[i][j]) < dist2(sc[y], aa[bi][bj])) {
                            bi = i;
                            bj = j;
                        }
                    }
                upd(aa[bi][bj], sc[y], epW); // update winner
                if (bi >= 1) upd(aa[bi-1][bj], sc[y], epN); // update neighbours
                if (bi < rc-1) upd(aa[bi+1][bj], sc[y], epN);
                if (bj >= 1) upd(aa[bi][bj-1], sc[y], epN);
                if (bj < rc-1) upd(aa[bi][bj+1], sc[y], epN);
            }
            it++;
        }
    
        function minToMax() {
            info();
            var ai = 0, aj=0, bi= 0, bj = 0;
            for (var i = 0; i < rc; i++ ) {
                for (var j = 0; j < rc; j++ ) {
                    if (aa[i][j][3].length < aa[ai][aj][3].length) {
                        ai = i;
                        aj = j;
                    }
                    if (aa[i][j][4] > aa[bi][bj][4] || ( aa[i][j][4] == aa[bi][bj][4] && aa[i][j][3].length > aa[bi][bj][3].length)) {
                        bi = i;
                        bj = j;
                    }
                }
            }
            if (ai == bi && aj == bj) {
                alert("max = min: " + ai + ',' + aj);
                return;
            } else {
                var m = 'moved ' + ai + ',' + aj + '(' + aa[ai][aj][3].length + ' ' + Math.round(aa[ai][aj][4]) + ') to '
                                 + bi + ',' + bj + '(' + aa[bi][bj][3].length + ' ' + Math.round(aa[bi][bj][4]) + ')';
                x = aa[bi][bj][3][Math.floor(Math.random() * aa[bi][bj][3].length)];
                aa[ai][aj] = [sc[x][0], sc[x][1], sc[x][2]];
                draw();
                document.getElementById("rTxt").innerHTML =  m + ' s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist) + ' chg=' + chg;
            }
        }
    
        function dist2(a, b) { /* return square of euclidean distance */
            return       (a[0] - b[0]) * (a[0] - b[0])
                + (a[1] - b[1]) * (a[1] - b[1])
                + (a[2] - b[2]) * (a[2] - b[2]);
        }
    
        function upd(u, o, eps) { /* update map color ==> move u on line u - o, to point 1-eps : eps obetween  */
            var fi = 1 - eps;
            var n = u[0] * fi + o[0] * eps;
            chg += (u[0] - n) * (u[0] - n);
            u[0] = n;
            n = u[1] * fi + o[1] * eps;
            chg += (u[1] - n) * (u[1] - n);
            u[1] = n;
            n = u[2] * fi + o[2] * eps;
            chg += (u[2] - n) * (u[2] - n);
            u[2] = n;
            return;
        }
    
        var mmDist = 0;  /* for source color find winner and distance  */
        var smDist = 0;
    
        function draw() { /* draw map */
            if (isNaN(rc) || rc < 1) {
                document.getElementById("r").innerHTML = 'change num row/cols to illegal ' + event.target.value;
                return -99;
            } 
            info();
            var h = '';
            var w = Math.trunc(500 / rc);
            var st = 'style="display:block; float: left;width:' + w + 'px; height: ' + w + 'px;'
            for (var i = 0; i < rc; i++ ) {
                for (var j = 0; j < rc; j++ ) {
                    var id = 'r' + i + ' c' + j;
                    var co = colStr(aa[i][j]);
                    h += '<div id="' + id + '" ' + st + ' background-color: ' + co 
                        + '; color: '  + (aa[i][j][0]+aa[i][j][1]+aa[i][j][2] < 24 ? '#fff' : '#000') + ';">'
                        + id + ' ' + co;
                    if (aa[i][j][3].length != 0)
                         h += '<br/>winner=' + aa[i][j][3].length + ' dist2=' + Math.round(aa[i][j][4]) + '<br/>' + aa[i][j][5];
                    h += '</div> ';   
                } 
            }
            document.getElementById("r").innerHTML = h;
        }
    
        function info() {
            mmDist = 0;
            smDist = 0;
            for (var i = 0; i < rc; i++ )
                for (var j = 0; j < rc; j++ ) {
                    aa[i][j][3] = [];
                    aa[i][j][4] = 0;
                    aa[i][j][5] = '';
                    if (i >= 1)
                        mmDist += dist2(aa[i-1][j], aa[i][j]);    
                    if (j >= 1)
                        mmDist += dist2(aa[i][j-1], aa[i][j]);    
            }
            for (var x=0; x < sc.length; x++) {
                var bi = 0, bj = 0;                        
                for (var i = 0; i < rc; i++ ) {
                    for (var j = 0; j < rc; j++ ) {
                        if ( dist2(sc[x], aa[i][j]) < dist2(sc[x], aa[bi][bj])) {
                            bi = i;
                            bj = j;
                        }
                    }
                }
                aa[bi][bj][3].push(x);
                var d = dist2(aa[bi][bj], sc[x]);
                smDist += d;
                aa[bi][bj][4] += d;
                aa[bi][bj][5] += colStr(sc[x]) + ' ';
            }
    
        }
    
        function onLoadDo() {
            numCols(document.getElementById("numCols").value);
        }
            
        </script>
     </head>
     <body onload="onLoadDo();">
    <ol>
    <h1>self organizing map</h1>
    <ul><li>select number of source colors
    </li><li>fill y*y map with random colors
    </li><li>iterate: for each source color
    <ul><li> find nearest map color = winner 
    </li><li>move winner nearer to source (by epW)
    </li><li>move neighbours of winner nearer to source (by epN typically &lt; epW)
    </li></ul>
    <li><strong>unfortuneatly</strong>, iteration does not improve over random. Better are small eps (0.01 or less)
    </li><li>possiply each map neuron should have the same number of winners! Verbesserungen?!Idee: minToMax: move map points with the fewest winners to a randomly chosen source color of the neurons with the top distanceSquare
    </li></ul>
    
    <h2>source colors rgb</h2>
    number of source colors <input type="text" id="numCols" value="20" size="2" onchange="numCols(event.target.value);">
    <div id="src">
    <div id="c000">000</div>
    <div id="c001">001</div>
    <div id="c010">010</div>
    <div id="c011">011</div>
    <div id="c100">100</div>
    <div id="c101">101</div>
    <div id="c110">110</div>
    <div id="c111">111</div>
    </div>
    <h2 style="clear: both;">result square map</h2> 
    <ul>
    <li>number of rows/cols <input type="text" id="numRC" value="2" size="2" onchange="numRCcha(event.target.value);"></li>
    <li> <input type="button" value="iterate1" onclick="iterate(1)" /> <input type="button" value="iterate100" onclick="iterate(100)" /> <input type="button" value="minToMax" onclick="minToMax()" /> </li>
    <li id="rTxt">initial</li></ul><br/>
    <div id="r">
    </div>
    <h1 style="clear: both;">Source <?php echo __file__; ?> </h1>
    <?php highlight_file(__file__) ?>
    
     </body>
    </html>