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>