/** * Gershgorin circle theorem * by Kazuki Maeda * Last-Modified: May 28, 2013 */ var WIDTH = 950, HEIGHT = 450; var interval = 50; var axisColor = "#D3D7CF"; var tickColor = "#83877F"; var circleColor = "#FFD700"; var circleAlpha = 0.4; var eigpColor = "#FF0000"; var eigpR = 3; // the size of matrices var N = 4; var M = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]; var D, A; var rootSVG; var circle = {}; var eigp = {}; var matrixin, matrixout, eigout; window.onload = function(){ init(); } function init(){ // Create canvas (SVG) document.getElementById("canvas").removeChild(document.getElementById("canvas").firstChild); rootSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg"); rootSVG.setAttribute("width", WIDTH); rootSVG.setAttribute("height", HEIGHT); document.getElementById("canvas").appendChild(rootSVG); matrixin = document.getElementById("matrixin"); matrixout = document.getElementById("matrixout"); eigout = document.getElementById("eigout"); document.getElementById("inputbutton").addEventListener("click", matrixIn, true); document.getElementById("identitybutton").addEventListener("click", identity, true); // draw axes drawLine(0, WIDTH, HEIGHT/2, HEIGHT/2, axisColor, 2); drawLine(WIDTH/2, WIDTH/2, 0, HEIGHT, axisColor, 2); // ticks & labels var i = 1; for(var x = WIDTH/2+interval; x <= WIDTH; x += interval){ drawLine(x, x, 0, HEIGHT, tickColor, 1); drawLabel(x-(i>=0?10:16), calcY(0)+16, "8pt", axisColor, ""+i++); } i = -1; for(var x = WIDTH/2-interval; x >= 0; x -= interval){ drawLine(x, x, 0, HEIGHT, tickColor, 1); drawLabel(x-(i>=0?10:16), calcY(0)+16, "8pt", axisColor, ""+i--); } i = -1; for(var y = HEIGHT/2+interval; y <= HEIGHT; y += interval){ drawLine(0, WIDTH, y, y, tickColor, 1); drawLabel(calcX(0)-(i>=0?12:16), y+16, "8pt", axisColor, ""+i--); } i = 1; for(var y = HEIGHT/2-interval; y >= 0; y -= interval){ drawLine(0, WIDTH, y, y, tickColor, 1); drawLabel(calcX(0)-(i>=0?12:16), y+16, "8pt", axisColor, ""+i++); } drawLabel(WIDTH/2-12, HEIGHT/2+16, "8pt", axisColor, "0"); // circles & eig points for(var n = 0; n < N; ++n) circle[n] = createCircle(calcY(0), 0, circleColor, circleAlpha); for(var n = 0; n < N; ++n) eigp[n] = createCircle(calcY(0), eigpR, eigpColor, 1.0); // Slider (jQuery) $("#slider").slider({ min: 0, max: 1000, value: 0, slide: function(event, ui) { $("#valueoft").val(floatToString(parseFloat(ui.value)/1000, 4)); draw(); } }); $("#valueoft").val("0.000"); matrixIn(); } function calcX(x){ return WIDTH/2+interval*x; } function calcY(y){ return HEIGHT/2-interval*y; } function drawLine(x1, x2, y1, y2, stroke, strokeWidth){ var tmp = document.createElementNS("http://www.w3.org/2000/svg", "line"); tmp.setAttribute("x1", ""+x1); tmp.setAttribute("x2", ""+x2); tmp.setAttribute("y1", ""+y1); tmp.setAttribute("y2", ""+y2); tmp.setAttribute("stroke", stroke); tmp.setAttribute("stroke-width", ""+strokeWidth); rootSVG.appendChild(tmp); } function drawLabel(x, y, fontSize, fill, text){ var tmp = document.createElementNS("http://www.w3.org/2000/svg", "text"); tmp.setAttribute("x", ""+x); tmp.setAttribute("y", ""+y); tmp.setAttribute("font-size", fontSize); tmp.setAttribute("fill", fill); tmp.textContent = text; rootSVG.appendChild(tmp); } function createCircle(cy, r, fill, alpha){ var ret = document.createElementNS("http://www.w3.org/2000/svg", "circle"); ret.setAttribute("cy", ""+cy); ret.setAttribute("r", ""+r); ret.setAttribute("fill", fill); ret.setAttribute("fill-opacity", ""+alpha); rootSVG.appendChild(ret); return ret; } function floatToString(f, prec){ var ret = f.toString(); if(ret.indexOf(".") == -1) ret += "."; var len = ret.length-1-(f<0?1:0); for(var i = 0; i < prec-len; ++i) ret += "0"; if(ret.length > prec+(f<0?2:1)) ret = ret.substring(0, prec+(f<0?2:1)); return ret; } function draw(){ var Mt = numeric["+"](D, numeric["*"](parseFloat(document.getElementById("valueoft").value), A)); // calculate the radius of circles for(var i = 0; i < N; ++i){ var r = 0; for(var j = 0; j < N; ++j) if(j != i) r += Math.abs(Mt[i][j]); circle[i].setAttribute("r", ""+(r*interval)); } var eigo = numeric.eig(Mt).lambda; var eig = []; for(var n = 0; n < N; ++n){ eig[n] = {}; eig[n].x = eigo.x[n]; eig[n].y = (eigo.y === undefined ? 0 : eigo.y[n]); } eig.sort(function(a, b){ return b.x-a.x; }); for(var n = 0; n < N; ++n){ eigp[n].setAttribute("cx", ""+calcX(eig[n].x)); eigp[n].setAttribute("cy", ""+calcY(eig[n].y)); } // output matrix var output = "" for(var i = 0; i < N; ++i){ for(var j = 0; j < N; ++j){ output += (Mt[i][j]<0?" ":" ") + floatToString(Mt[i][j], 4); if(j != N-1) output += ","; } output += "\n"; } matrixout.value = output; // output eigenvalues output = "" for(var n = 0; n < N; ++n){ if(eig[n].x >= 0) output += " "; output += floatToString(eig[n].x, 16); var tmp = floatToString(eig[n].y, 16); if(tmp.length > 17) tmp = tmp.substring(1, tmp.length); output += (eig[n].y<0?" - ":" + ") + tmp + "i"; output += '\n'; } eigout.value = output; } function identity(){ matrixin.value = " 1.0, 0.0, 0.0, 0.0\n 0.0, 1.0, 0.0, 0.0\n 0.0, 0.0, 1.0, 0.0\n 0.0, 0.0, 0.0, 1.0"; } function matrixIn(){ var inM = numeric.parseCSV(matrixin.value); for(var i = 0; i < N; ++i){ for(var j = 0; j < N; ++j){ if(inM[i] === undefined || inM[i][j] === undefined || typeof inM[i][j] != "number" || isNaN(inM[i][j])) M[i][j] = 0; else M[i][j] = inM[i][j]; } } D = numeric.diag([M[0][0], M[1][1], M[2][2], M[3][3]]); A = numeric["-"](M, D); for(var n = 0; n < N; ++n) circle[n].setAttribute("cx", ""+calcX(D[n][n])); draw(); }