├── .gitignore ├── example ├── w1.png ├── gen.png ├── decoder.png ├── catalog ├── hdec_sparsity.csv ├── henc_sparsity.csv ├── toyhist.csv └── valid_logp.csv ├── utils.js ├── demo ├── cslab_environ.py ├── mnist.py ├── logger.py └── vae.py ├── index.html ├── lib ├── stream_layers.js ├── nv.d3.min.css.map ├── nv.d3.min.css ├── nv.d3.css └── jquery-2.1.4.min.js ├── LICENSE ├── default.css ├── README.md ├── merge_expr.py └── dashboard.js /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *.$py.class 4 | *.so 5 | -------------------------------------------------------------------------------- /example/w1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renmengye/deep-dashboard/HEAD/example/w1.png -------------------------------------------------------------------------------- /example/gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renmengye/deep-dashboard/HEAD/example/gen.png -------------------------------------------------------------------------------- /example/decoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renmengye/deep-dashboard/HEAD/example/decoder.png -------------------------------------------------------------------------------- /example/catalog: -------------------------------------------------------------------------------- 1 | filename,type,name 2 | raw-20160211-005535.log,plain,Raw logs 3 | w1.png,image,W1 visualization 4 | decoder.png,image,Decoder reconstruction 5 | gen.png,image,Generated digits 6 | valid_logp.csv,csv,Validation log prob 7 | henc_sparsity.csv,csv,Encoder hidden activation sparsity 8 | hdec_sparsity.csv,csv,Decoder hidden activation sparsity 9 | train_logp.csv,csv,Train log prob 10 | step_time.csv,csv,step time (ms) 11 | toyhist.csv,histogram,Toy Histogram 12 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | var getSearchParameters = function() { 2 | var prmstr = window.location.search.substr(1); 3 | return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {}; 4 | } 5 | 6 | var transformToAssocArray = function(prmstr) { 7 | var params = {}; 8 | var prmarr = prmstr.split("&"); 9 | for ( var i = 0; i < prmarr.length; i++) { 10 | var tmparr = prmarr[i].split("="); 11 | params[tmparr[0]] = tmparr[1]; 12 | } 13 | return params; 14 | } 15 | -------------------------------------------------------------------------------- /demo/cslab_environ.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import socket 4 | 5 | if os.path.exists('/u/mren'): 6 | sys.path.insert( 7 | 0, '/pkgs/tensorflow-gpu-0.5.0/lib/python2.7/site-packages') 8 | hostname = socket.gethostname() 9 | if hostname.startswith('guppy'): 10 | sys.path.insert( 11 | 0, 12 | '/u/mren/code/img-count/third_party/tensorflow-gpu/_python_build') 13 | else: 14 | sys.path.insert( 15 | 0, '/u/mren/code/img-count/third_party/tensorflow/_python_build') 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/stream_layers.js: -------------------------------------------------------------------------------- 1 | /* Inspired by Lee Byron's test data generator. */ 2 | function stream_layers(n, m, o) { 3 | if (arguments.length < 3) o = 0; 4 | function bump(a) { 5 | var x = 1 / (.1 + Math.random()), 6 | y = 2 * Math.random() - .5, 7 | z = 10 / (.1 + Math.random()); 8 | for (var i = 0; i < m; i++) { 9 | var w = (i / m - y) * z; 10 | a[i] += x * Math.exp(-w * w); 11 | } 12 | } 13 | return d3.range(n).map(function() { 14 | var a = [], i; 15 | for (i = 0; i < m; i++) a[i] = o + o * Math.random(); 16 | for (i = 0; i < 5; i++) bump(a); 17 | return a.map(stream_index); 18 | }); 19 | } 20 | 21 | /* Another layer generator using gamma distributions. */ 22 | function stream_waves(n, m) { 23 | return d3.range(n).map(function(i) { 24 | return d3.range(m).map(function(j) { 25 | var x = 20 * j / m - i / 3; 26 | return 2 * x * Math.exp(-.5 * x); 27 | }).map(stream_index); 28 | }); 29 | } 30 | 31 | function stream_index(d, i) { 32 | return {x: i, y: Math.max(0, d)}; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Mengye Ren 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /default.css: -------------------------------------------------------------------------------- 1 | text { 2 | font: 12px sans-serif; 3 | } 4 | 5 | h1 { 6 | font: 20px sans-serif; 7 | font-weight: 700; 8 | } 9 | 10 | h2 { 11 | font: 18px sans-serif; 12 | font-weight: 700; 13 | } 14 | 15 | h3 { 16 | font: 14px sans-serif; 17 | font-weight: 500; 18 | font-style: italic; 19 | margin-left: 10px; 20 | color: #888; 21 | } 22 | 23 | a:link { 24 | color: #000; 25 | text-decoration: none; 26 | } 27 | 28 | a:visited { 29 | color: #000; 30 | text-decoration: none; 31 | } 32 | 33 | a:hover { 34 | color: #888; 35 | text-decoration: none; 36 | } 37 | 38 | a:active { 39 | color: #888; 40 | text-decoration: none; 41 | } 42 | 43 | svg { 44 | display: block; 45 | } 46 | 47 | html, body { 48 | margin: 0px; 49 | padding: 0px; 50 | height: 100%; 51 | width: 100%; 52 | font: 12px sans-serif; 53 | } 54 | 55 | .dashed { 56 | stroke-dasharray: 5,5; 57 | } 58 | 59 | .panel { 60 | width: 100%; 61 | margin: 10px 0 50px 0; 62 | } 63 | 64 | .chart_panel { 65 | height: 300px; 66 | } 67 | 68 | .chart_control { 69 | margin-left: 20px; 70 | } 71 | 72 | .raw_log { 73 | margin-left: 20px; 74 | } 75 | 76 | .img_log { 77 | width: 100%; 78 | } 79 | 80 | .experiment { 81 | border-bottom: 2px solid #ccc; 82 | } 83 | 84 | .timestamp { 85 | float: right; 86 | } 87 | 88 | #content { 89 | width: 800px; 90 | margin: auto; 91 | } 92 | 93 | #settings { 94 | margin-bottom: 20px; 95 | } 96 | 97 | .title { 98 | font: 25px sans-serif; 99 | font-weight: 700; 100 | margin: 20px 0px 20px 0px; 101 | } 102 | -------------------------------------------------------------------------------- /example/hdec_sparsity.csv: -------------------------------------------------------------------------------- 1 | step,time,hdec sparsity 2 | 0,2016-02-11T00:55:42.417560,0.505302 3 | 500,2016-02-11T00:55:56.551022,2.4e-05 4 | 1000,2016-02-11T00:56:09.835471,0.000228 5 | 1500,2016-02-11T00:56:23.245147,0.000213 6 | 2000,2016-02-11T00:56:38.694239,0.000127 7 | 2500,2016-02-11T00:56:56.078995,3.1e-05 8 | 3000,2016-02-11T00:57:14.448969,3e-05 9 | 3500,2016-02-11T00:57:33.081141,0.000104 10 | 4000,2016-02-11T00:57:51.156075,0.000207 11 | 4500,2016-02-11T00:58:09.203511,5.6e-05 12 | 5000,2016-02-11T00:58:29.978176,0.000253 13 | 5500,2016-02-11T00:58:48.988085,0.000416 14 | 6000,2016-02-11T00:59:05.403753,0.018198 15 | 6500,2016-02-11T00:59:22.109633,0.060368 16 | 7000,2016-02-11T00:59:40.740266,0.070843 17 | 7500,2016-02-11T00:59:56.611000,0.083831 18 | 8000,2016-02-11T01:00:12.854554,0.093753 19 | 8500,2016-02-11T01:00:33.869910,0.103516 20 | 9000,2016-02-11T01:00:50.458145,0.130494 21 | 9500,2016-02-11T01:01:06.602950,0.147626 22 | 10000,2016-02-11T01:01:23.433178,0.160772 23 | 10500,2016-02-11T01:01:43.384897,0.180775 24 | 11000,2016-02-11T01:01:58.796116,0.211917 25 | 11500,2016-02-11T01:02:15.020169,0.228921 26 | 12000,2016-02-11T01:02:31.636323,0.237045 27 | 12500,2016-02-11T01:02:54.179437,0.24526 28 | 13000,2016-02-11T01:03:10.403967,0.256322 29 | 13500,2016-02-11T01:03:26.904927,0.266719 30 | 14000,2016-02-11T01:03:44.573391,0.275294 31 | 14500,2016-02-11T01:04:05.734581,0.283793 32 | 15000,2016-02-11T01:04:25.694286,0.2915 33 | 15500,2016-02-11T01:04:47.050107,0.296735 34 | 16000,2016-02-11T01:05:03.339184,0.304615 35 | 16500,2016-02-11T01:05:20.314884,0.308935 36 | 17000,2016-02-11T01:05:36.878588,0.314991 37 | 17500,2016-02-11T01:05:53.176309,0.318622 38 | 18000,2016-02-11T01:06:09.768582,0.319498 39 | 18500,2016-02-11T01:06:27.619950,0.319748 40 | 19000,2016-02-11T01:06:50.774968,0.323343 41 | 19500,2016-02-11T01:07:07.109977,0.327239 42 | 20000,2016-02-11T01:07:23.466550,0.328832 43 | 20500,2016-02-11T01:07:40.002775,0.329383 44 | 21000,2016-02-11T01:07:56.552463,0.331962 45 | 21500,2016-02-11T01:08:13.032436,0.334853 46 | 22000,2016-02-11T01:08:28.810628,0.335561 47 | 22500,2016-02-11T01:08:45.447830,0.336808 48 | 23000,2016-02-11T01:09:37.931584,0.340278 49 | 23500,2016-02-11T01:09:55.452073,0.341563 50 | 24000,2016-02-11T01:10:12.097980,0.342431 51 | 24500,2016-02-11T01:10:28.616936,0.341859 52 | 25000,2016-02-11T01:10:45.454687,0.344729 53 | 25500,2016-02-11T01:11:02.618750,0.345555 54 | 26000,2016-02-11T01:11:19.686561,0.346831 55 | 26500,2016-02-11T01:11:37.034679,0.346298 56 | 27000,2016-02-11T01:11:55.376893,0.346912 57 | 27500,2016-02-11T01:12:12.308374,0.347792 58 | 28000,2016-02-11T01:14:58.581285,0.348521 59 | 28500,2016-02-11T01:15:20.840515,0.348668 60 | 29000,2016-02-11T01:15:38.637232,0.346623 61 | 29500,2016-02-11T01:15:56.125118,0.352238 62 | 30000,2016-02-11T01:16:12.740632,0.351898 63 | 30500,2016-02-11T01:16:29.350734,0.349849 64 | 31000,2016-02-11T01:16:46.885288,0.350181 65 | -------------------------------------------------------------------------------- /example/henc_sparsity.csv: -------------------------------------------------------------------------------- 1 | step,time,henc sparsity 2 | 0,2016-02-11T00:55:42.417477,0.537671 3 | 500,2016-02-11T00:55:56.550959,0.140596 4 | 1000,2016-02-11T00:56:09.835418,0.168366 5 | 1500,2016-02-11T00:56:23.245085,0.184838 6 | 2000,2016-02-11T00:56:38.694198,0.203149 7 | 2500,2016-02-11T00:56:56.078950,0.221949 8 | 3000,2016-02-11T00:57:14.448927,0.229711 9 | 3500,2016-02-11T00:57:33.081086,0.270143 10 | 4000,2016-02-11T00:57:51.156034,0.298622 11 | 4500,2016-02-11T00:58:09.203469,0.310716 12 | 5000,2016-02-11T00:58:29.978107,0.329055 13 | 5500,2016-02-11T00:58:48.988043,0.362139 14 | 6000,2016-02-11T00:59:05.403711,0.35957 15 | 6500,2016-02-11T00:59:22.109589,0.339169 16 | 7000,2016-02-11T00:59:40.740224,0.364515 17 | 7500,2016-02-11T00:59:56.610921,0.386189 18 | 8000,2016-02-11T01:00:12.854498,0.41551 19 | 8500,2016-02-11T01:00:33.869869,0.416796 20 | 9000,2016-02-11T01:00:50.458103,0.378423 21 | 9500,2016-02-11T01:01:06.602887,0.377172 22 | 10000,2016-02-11T01:01:23.433136,0.381982 23 | 10500,2016-02-11T01:01:43.384818,0.376279 24 | 11000,2016-02-11T01:01:58.796075,0.351293 25 | 11500,2016-02-11T01:02:15.020114,0.343456 26 | 12000,2016-02-11T01:02:31.636254,0.344448 27 | 12500,2016-02-11T01:02:54.179379,0.342075 28 | 13000,2016-02-11T01:03:10.403913,0.341615 29 | 13500,2016-02-11T01:03:26.904884,0.338724 30 | 14000,2016-02-11T01:03:44.573348,0.334104 31 | 14500,2016-02-11T01:04:05.734525,0.33111 32 | 15000,2016-02-11T01:04:25.694227,0.330945 33 | 15500,2016-02-11T01:04:47.050052,0.329533 34 | 16000,2016-02-11T01:05:03.339139,0.330127 35 | 16500,2016-02-11T01:05:20.314829,0.331579 36 | 17000,2016-02-11T01:05:36.878432,0.331174 37 | 17500,2016-02-11T01:05:53.176244,0.330401 38 | 18000,2016-02-11T01:06:09.768530,0.325761 39 | 18500,2016-02-11T01:06:27.619882,0.32312 40 | 19000,2016-02-11T01:06:50.774912,0.32034 41 | 19500,2016-02-11T01:07:07.109924,0.318384 42 | 20000,2016-02-11T01:07:23.466489,0.316344 43 | 20500,2016-02-11T01:07:40.002733,0.31426 44 | 21000,2016-02-11T01:07:56.552418,0.313225 45 | 21500,2016-02-11T01:08:13.032394,0.312957 46 | 22000,2016-02-11T01:08:28.810587,0.309598 47 | 22500,2016-02-11T01:08:45.447788,0.308123 48 | 23000,2016-02-11T01:09:37.931397,0.308775 49 | 23500,2016-02-11T01:09:55.452018,0.307165 50 | 24000,2016-02-11T01:10:12.097904,0.308044 51 | 24500,2016-02-11T01:10:28.616875,0.307892 52 | 25000,2016-02-11T01:10:45.454645,0.307439 53 | 25500,2016-02-11T01:11:02.618710,0.307786 54 | 26000,2016-02-11T01:11:19.686504,0.306801 55 | 26500,2016-02-11T01:11:37.034628,0.308399 56 | 27000,2016-02-11T01:11:55.376840,0.308335 57 | 27500,2016-02-11T01:12:12.308327,0.309184 58 | 28000,2016-02-11T01:14:58.581093,0.308773 59 | 28500,2016-02-11T01:15:20.840410,0.30885 60 | 29000,2016-02-11T01:15:38.637177,0.309169 61 | 29500,2016-02-11T01:15:56.125076,0.309302 62 | 30000,2016-02-11T01:16:12.740580,0.3093 63 | 30500,2016-02-11T01:16:29.350679,0.3097 64 | 31000,2016-02-11T01:16:46.885233,0.310845 65 | -------------------------------------------------------------------------------- /example/toyhist.csv: -------------------------------------------------------------------------------- 1 | x,0.0720908711172,0.52840124081,0.501501133398,-0.216497244484,0.0488643808484,0.812678692472,0.43415160266,-0.160115171873,-0.13077082696,0.373049298437,0.402420943464,-0.0510384092334,-0.598691420928,0.660212406948,0.660522751243,0.126458335549,-0.639347162282,0.393452419275,0.715917558823,0.260250374131,0.249444505295,0.202359290004,0.288479166253,0.61974898055,0.425296281729,-0.192951948641,0.630317456119,0.214631707002,0.0369556436041,-0.00117652422689,0.430916208744,0.59870301097,0.299680337,-0.238174899676,0.155858133181,0.926633745316,0.590902799395,-0.0283633916421,0.437306908126,0.457674392044,0.180339863863,0.789600384231,0.163256582133,0.294192916573,0.194360680733,0.353491579876,0.18355774205,0.303329530652,0.00670266704342,1.23186549224,0.299895122648,0.209452643614,-0.754866552693,0.581813486091,0.0501443381336,0.160287208987,0.452374678832,-0.0733998899057,0.0350586552074,0.559153906574,0.424193982702,0.283935897801,0.664064322506,0.780519580472,0.0105036186835,0.320793421788,-0.20083517846,-0.334784874166,0.33407463254,0.269047413113,-0.118896283683,0.129309249171,0.550075363241,0.291470682168,0.705967134258,-0.60679174681,0.0185541154528,0.296642755667,-0.0979257923463,0.740108334847,-0.783208123751,0.422650241664,-0.147601692269,0.0242631913245,0.214405049415,0.246476769219,-0.0817640921943,0.14852474827,-0.218119657837,-0.0810163309287,-0.305588887966,0.738013894106,0.0233427748663,0.651877769268,0.462468488099,-0.47952958041,0.562874122775,-0.0990488399176,-0.229936506281,0.122013664535 2 | y,0.908597668879,0.448256404108,0.604616819259,0.637479639461,0.517637806059,0.355841519108,1.06411211862,0.0979747336194,1.09587476824,0.432035199201,0.841258259426,0.518454710818,0.169024346064,0.8482647739,0.739583564769,0.476594472354,0.508040249177,0.736469984971,0.604477088219,0.319952706423,0.730021894764,0.694340295023,0.557017699228,0.924236207098,1.12295146465,0.252640096324,0.576826299293,0.679885472696,0.699325994408,0.486776306952,0.501483701321,0.531967752131,0.545623237901,0.791368667338,0.960273792301,1.059137013,0.544187541102,0.169925964904,0.7436724357,1.1451677378,0.566322863719,0.468347172174,0.514919068313,0.807002878917,0.420810553281,0.0790648105041,0.884162868001,0.856958283554,0.155259988871,0.70685136184,1.38991899235,0.181544494837,0.6382840509,0.641408670117,0.681770330351,0.707266849155,0.586871033034,0.140159675625,0.439982098804,0.553585892235,0.678917779849,0.534402921918,0.543293721428,0.493837065701,0.454328664053,0.6757213704,0.42184417592,0.55464289428,0.00293613473339,0.925800074056,0.217748586107,0.310623269646,0.762222146235,0.721766085137,0.772818918119,0.770137562016,0.743440868057,1.14777686403,0.72861290229,0.357856631642,0.593330676811,0.528257592522,0.455346188881,0.773927913416,0.29781556338,0.314488188281,0.42475241213,0.619341708968,0.881248463361,0.384748033222,0.616814821794,0.267882741829,0.879315362319,-0.173013681814,0.548219112811,0.525763873201,0.618643239021,0.496965982695,0.370980606786,0.739188467579 3 | -------------------------------------------------------------------------------- /example/valid_logp.csv: -------------------------------------------------------------------------------- 1 | step,time,valid logp 2 | 0,2016-02-11T00:55:42.417299,-543.412349854 3 | 500,2016-02-11T00:55:56.550831,-234.835267639 4 | 1000,2016-02-11T00:56:09.835298,-219.7842099 5 | 1500,2016-02-11T00:56:23.244972,-213.745237427 6 | 2000,2016-02-11T00:56:38.694086,-210.727046051 7 | 2500,2016-02-11T00:56:56.078791,-208.792633514 8 | 3000,2016-02-11T00:57:14.448812,-207.713288574 9 | 3500,2016-02-11T00:57:33.080968,-206.923294983 10 | 4000,2016-02-11T00:57:51.155920,-206.227645111 11 | 4500,2016-02-11T00:58:09.203357,-205.203223724 12 | 5000,2016-02-11T00:58:29.977953,-204.179029694 13 | 5500,2016-02-11T00:58:48.987931,-202.604453278 14 | 6000,2016-02-11T00:59:05.403597,-198.543881836 15 | 6500,2016-02-11T00:59:22.109476,-193.503899384 16 | 7000,2016-02-11T00:59:40.740109,-190.516235352 17 | 7500,2016-02-11T00:59:56.610779,-188.175942383 18 | 8000,2016-02-11T01:00:12.854383,-185.855839844 19 | 8500,2016-02-11T01:00:33.869756,-182.79936615 20 | 9000,2016-02-11T01:00:50.457988,-178.708313141 21 | 9500,2016-02-11T01:01:06.602724,-175.963180542 22 | 10000,2016-02-11T01:01:23.433023,-173.479467468 23 | 10500,2016-02-11T01:01:43.384690,-170.287695465 24 | 11000,2016-02-11T01:01:58.795960,-163.456819916 25 | 11500,2016-02-11T01:02:15.019977,-157.913713989 26 | 12000,2016-02-11T01:02:31.636137,-154.241869507 27 | 12500,2016-02-11T01:02:54.179259,-151.706841125 28 | 13000,2016-02-11T01:03:10.403797,-149.634828949 29 | 13500,2016-02-11T01:03:26.904761,-147.609477844 30 | 14000,2016-02-11T01:03:44.573235,-145.884052124 31 | 14500,2016-02-11T01:04:05.734405,-144.112550354 32 | 15000,2016-02-11T01:04:25.694105,-142.590622864 33 | 15500,2016-02-11T01:04:47.049909,-140.863491974 34 | 16000,2016-02-11T01:05:03.339019,-139.108470459 35 | 16500,2016-02-11T01:05:20.314711,-137.351234741 36 | 17000,2016-02-11T01:05:36.878009,-135.591091537 37 | 17500,2016-02-11T01:05:53.176124,-133.728122482 38 | 18000,2016-02-11T01:06:09.768412,-131.854151688 39 | 18500,2016-02-11T01:06:27.619756,-130.178781509 40 | 19000,2016-02-11T01:06:50.774798,-128.867138443 41 | 19500,2016-02-11T01:07:07.109809,-127.650250244 42 | 20000,2016-02-11T01:07:23.466368,-126.423716431 43 | 20500,2016-02-11T01:07:40.002615,-125.432377548 44 | 21000,2016-02-11T01:07:56.552301,-124.327060852 45 | 21500,2016-02-11T01:08:13.032282,-123.200733795 46 | 22000,2016-02-11T01:08:28.810472,-122.179623032 47 | 22500,2016-02-11T01:08:45.447671,-121.300531769 48 | 23000,2016-02-11T01:09:37.931164,-120.209877319 49 | 23500,2016-02-11T01:09:55.451888,-119.353180695 50 | 24000,2016-02-11T01:10:12.097783,-118.461716919 51 | 24500,2016-02-11T01:10:28.616751,-117.54067955 52 | 25000,2016-02-11T01:10:45.454526,-116.670044022 53 | 25500,2016-02-11T01:11:02.618606,-115.84668045 54 | 26000,2016-02-11T01:11:19.686387,-115.017327118 55 | 26500,2016-02-11T01:11:37.034515,-114.171150894 56 | 27000,2016-02-11T01:11:55.376720,-113.500768967 57 | 27500,2016-02-11T01:12:12.308202,-112.748794098 58 | 28000,2016-02-11T01:14:58.580608,-112.092018967 59 | 28500,2016-02-11T01:15:20.748294,-111.452630157 60 | 29000,2016-02-11T01:15:38.637054,-111.024089661 61 | 29500,2016-02-11T01:15:56.124963,-110.414999924 62 | 30000,2016-02-11T01:16:12.740461,-109.871440277 63 | 30500,2016-02-11T01:16:29.350538,-109.356283646 64 | 31000,2016-02-11T01:16:46.885100,-108.873785553 65 | -------------------------------------------------------------------------------- /lib/nv.d3.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["build/nv.d3.css"],"names":[],"mappings":"AAqBA,oBAfA,oBAgBI,KAAM,KA4VN,gBAAiB,WAtErB,kBAqDA,uBA5UA,oBAfA,oBA4WI,gBAAiB,WAgErB,UAAW,UAJX,mBA7aA,eA4ZA,uBA8BA,uCACI,eAAgB,KA+DpB,WAhQA,aAuQI,QAAS,MAST,sBAAuB,KAEvB,mBAAoB,KA5gBxB,eAEI,QAAS,EAuCb,2BA+IA,0DACI,QAAS,EApLb,oBAEI,OAAQ,KACR,eAAgB,IAIpB,2BACI,eAAgB,IAGpB,gCACI,eAAgB,EAGpB,oBAEI,OAAQ,QAIZ,0BACI,0BACA,eAAgB,IAGpB,mCACI,YAAa,IAGjB,sCACA,uCACA,uCACI,YAAa,OAOjB,oBACI,aAAc,IAEd,WAAY,aAAa,MAAM,OAC/B,gBAAiB,aAAa,MAAM,OACpC,mBAAoB,aAAa,MAAM,OAG3C,0BACI,aAAc,EAGlB,2BACI,KAAM,QAGV,oBACI,KAAM,YAGV,2BACI,KAAM,cAKV,sCAFA,mCACA,6CAEI,eAAgB,EAEhB,WAAY,aAAa,MAAM,OAC/B,gBAAiB,aAAa,MAAM,OACpC,mBAAoB,aAAa,MAAM,OAK3C,8CACA,4CAHA,yCACA,mDAGI,aAAc,EAGlB,sCACA,6CACI,YAAa,IACb,KAAM,cACN,OAAQ,YAIZ,yBACE,aAAc,GAGhB,+BAIA,6BAHE,aAAc,EAOhB,6BACE,OAAQ,KAGV,uBACE,aAAc,MAGhB,gBAAkB,KAAM,KAAK,WAC7B,4BAA8B,aAAc,GAC5C,kCAAoC,aAAc,EAClD,2BAA6B,OAAQ,KAAM,aAAc,IACzD,mCAAqC,OAAQ,KAAM,KAAM,KAAM,aAAc,MAC7E,8BAAgC,OAAQ,KAAM,aAAc,KAC5D,gCAAkC,KAAM,KACxC,gCAAkC,KAAM,KACxC,gCAAkC,KAAM,KACxC,0BAA4B,UAAW,KAAM,YAAa,IAC1D,6BAA+B,KAAM,KAGrC,0BACI,KAAM,QACN,aAAc,GAElB,gCACI,aAAc,GAGlB,2CACI,aAAc,IAGlB,iDACI,aAAc,IAGlB,yDACI,OAAQ,QACR,KAAM,QAGV,yDACI,OAAQ,QACR,KAAM,QAGV,wDACI,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAIxE,uCACI,OAAQ,KAQZ,4BACI,aAAa,EACb,aAAa,EAGjB,8BACI,aAAa,EACb,aAAa,EAGjB,qDACI,aAAa,EACb,eAAe,EAQnB,kCACI,aAAc,IAGlB,wCACI,aAAc,EAElB,8BACI,KAAM,KAGV,8BACI,OAAQ,KAGZ,oDACI,aAAc,EACd,eAAgB,EAGpB,sDACI,aAAc,aACd,eAAgB,aAIpB,6CACI,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAKxE,iCADA,4CAEI,aAAc,IACd,aAAc,cACd,eAAgB,cAIpB,2BACI,OAAQ,KACR,eAAgB,EAChB,KAAM,KACN,aAAc,EAKlB,oBACI,OAAQ,UAUZ,aAEI,oBAAqB,KAErB,gBAAiB,KACjB,iBAAkB,KAClB,YAAa,KAEb,MAAM,KACN,OAAO,KAMX,0BAA2B,2BACvB,gBAAiB,EAAE,IAAI,KAAK,eAC5B,mBAAoB,EAAE,IAAI,KAAK,eAC/B,WAAY,EAAE,IAAI,KAAK,eAEvB,sBAAuB,IACvB,mBAAoB,IACpB,cAAe,IAInB,WACI,KAAM,IAAO,KAAK,MAGtB,aACI,KAAM,IAAK,KAAK,MAGpB,qBACI,KAAM,KACN,aAAc,EAGlB,gBACI,UAAW,KACX,YAAa,IAQjB,kBACI,aAAc,KAIlB,uBACI,KAAM,KACN,OAAQ,KAQZ,4BACI,OAAQ,QAGZ,qCACI,aAAc,EAIlB,wBACI,aAAc,YAGlB,+BACI,OAAQ,KACR,aAAc,GACd,KAAM,KACN,aAAc,GAIlB,oCACI,aAAc,IAGlB,0CACI,aAAc,IAGlB,6CACI,OAAQ,QAGZ,6CACI,OAAQ,QAIZ,uBACI,KAAM,KACN,OAAQ,KACR,eAAgB,GAIpB,uBACI,KAAM,KACN,eAAgB,GAGpB,4CAEI,KAAM,KACN,aAAc,GACd,OAAQ,KACR,gBAAiB,WAGrB,qCACI,aAAc,EACjB,aAAc,IAIf,8BACE,KAAM,KACN,OAAQ,KACR,aAAc,EACd,eAAgB,EAChB,iBAAkB,EAAG,EAUvB,2BACI,UAAW,KACX,KAAM,qBAGV,4BACI,OAAQ,KACR,aAAc,EAGlB,kBAhBI,WAAY,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OACvF,gBAAiB,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5F,mBAAoB,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OAe/F,OAAQ,KACR,aAAc,IACd,eAAgB,EAGpB,yBACI,aAAc,GAKlB,4BACI,aAAc,EACd,eAAgB,EAIpB,iCACI,aAAc,KACd,eAAgB,GAGpB,kCACI,aAAc,EAWlB,wBACI,KAAM,KAOV,2CACI,OAAQ,KACR,aAAc,MAGlB,uBACA,yBACI,eAAgB,IAwLpB,+BAnIA,WAoII,eAAe,KAtLnB,oBACI,aAAc,EACd,eAAgB,EAGpB,kCACA,kCACI,aAAc,EACd,UAAW,KACX,YAAa,IAGjB,kCACI,OAAQ,KAGZ,oCACI,OAAQ,QACR,KAAM,QAGV,oCACI,OAAQ,QACR,KAAM,QAGV,wCACI,YAAa,IACb,UAAW,MAsEf,cAsCA,wBACI,YAAa,IA1GjB,kCACI,aAAc,GACd,eAAgB,EAChB,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAGxE,wCACI,aAAc,GAIlB,0CACI,eAAgB,EAChB,aAAc,EAIlB,WACI,SAAU,SAEV,MAAO,cACP,QAAS,IAET,QAAS,MAGT,YAAa,MACb,UAAW,KACX,WAAY,KAGZ,YAAa,OAGb,oBAAqB,KAErB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KAIb,WAAY,qBACZ,OAAQ,IAAI,MAAM,eAClB,cAAe,IAqBnB,cAgBA,aACI,OAAQ,EAER,WAAY,OAlChB,4BAA6B,6BACzB,WAAY,QAAQ,KAAK,OACzB,gBAAiB,QAAQ,KAAK,OAC9B,mBAAoB,QAAQ,KAAK,OAEjC,iBAAkB,MAClB,sBAAuB,MACvB,yBAA0B,MAG9B,uBACA,uBACI,QAAS,IAGb,cAEI,QAAS,IAAI,KACb,YAAa,KAEb,iBAAkB,sBAClB,MAAO,cAGP,cAAe,IAAI,MAAM,QAEzB,sBAAuB,IAAI,IAAI,EAAE,EACjC,mBAAoB,IAAI,IAAI,EAAE,EAC9B,cAAe,IAAI,IAAI,EAAE,EAG7B,aAEI,QAAS,IAAI,KAIjB,gBACI,QAAS,aACT,OAAQ,IAAI,EAGhB,iBACI,OAAQ,IACR,eAAe,EAInB,oBACI,QAAS,IAAI,IAAI,IAAI,EACrB,eAAgB,OAMpB,8BACI,YAAa,IAEjB,0BACI,WAAY,MACZ,YAAa,IAGjB,iCACI,QAAS,IAAI,IAAI,IAAI,EACrB,oBAAqB,MACrB,oBAAqB,IACrB,iBAAkB,MAClB,iBAAkB,IAGtB,2CAGI,eAAgB,OAIhB,MAAO,KACP,OAAQ,KACR,OAAQ,IAAI,MAAM,KAGtB,mBACI,QAAS,IACT,WAAY,OAGhB,2BACI,eAAgB,KAChB,QAAS,KAUb,wBACI,OAAQ"} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep Dashboard 2 | A better visualization tool for training machine learning models. 3 | 4 | ## Introduction 5 | 6 | Tired of watching un-informative command line console? 7 | 8 | Tired of the line limit of ssh screen? 9 | 10 | Deep dashboard helps you visualize the training process better, and provides 11 | with more diagnostics. 12 | 13 | It currently supports displaying three types of visualizations: 14 | - Time series data, in CSV format 15 | - Raw plain text data 16 | - Image data 17 | 18 | ### Benefits of Deep Dashboard 19 | - Completely static, so it works in any server environment (no need to open 20 | a new HTTP port). 21 | - Visualizes training process in real-time. 22 | - Allows you to check model training on any browser, laptop, cell phone, etc. 23 | - Supports all machine learning libraries as backend. 24 | 25 | ## Installation 26 | 27 | * Install [Apache 2](https://httpd.apache.org/docs/2.4/install.html) 28 | 29 | For Ubuntu, 30 | ```shell 31 | sudo apt-get install apache2 32 | ``` 33 | 34 | * Clone this repository under Apache root folder 35 | * It is usually in */var/www* or */var/www/html*. 36 | * For Toronto users, your Apache root is */u/$USER/public_html*). 37 | 38 | ```shell 39 | set APACHE_ROOT=/var/www/html 40 | cd $APACHE_ROOT 41 | git clone https://github.com/renmengye/deep-dashboard.git 42 | mkdir results 43 | cp -R deep-dashboard/example results 44 | chmod -R +777 deep-dashboard 45 | chmod -R +777 results 46 | ``` 47 | 48 | * Alternatively, you can create soft links in the Apache folder. Note to 49 | Toronto users: soft links won't work in /u/$USER/public_html. 50 | 51 | * Now browse [http://localhost/deep-dashboard?id=example](http://localhost/deep-dashboard?id=example) 52 | 53 | ## Real-time demo 54 | This repository includes a demo for training a variational auto-encoder. 55 | To run this demo, you will need some extra dependencies: 56 | * [Tensorflow](https://github.com/tensorflow/tensorflow) 57 | * numpy 58 | * matplotlib 59 | 60 | Now run the demo. 61 | ```shell 62 | cd demo 63 | python vae.py -logs $APACHE_ROOT/results 64 | ``` 65 | 66 | The command line will print the web address to visualize the training process 67 | like below: 68 | ``` 69 | INFO: 13:59:36 vae.py:263 Dashboard: http://localhost/deep-dashboard?id=vae_mnist-20160211135936 70 | ``` 71 | 72 | ## Couple with your training program 73 | 74 | Now you are ready to add your own job to the dashboard! This section will 75 | brief you through the architecture of dashboard, so you know what is going on 76 | under the hood. 77 | 78 | ### File structure 79 | 80 | - */var/www/html/* 81 | - *deep-dashboard*: javascripts, css, and html. 82 | - *lib* 83 | - jquery 84 | - nvd3 85 | - d3 86 | - *index.html*: Simple frontend. 87 | - *dashboard.js*: Main dashboard code. 88 | - *utils.js*: Utility functions. 89 | 90 | - *results*: all your experiment files 91 | - *catalog*: CSV file listing all the folders here. 92 | - *experiment_id_1* 93 | - *catalog*: CSV file listing all the files here. 94 | - *raw.log*: Plain text file to display as plain text. 95 | - *curve.csv*: CSV file to display as a time series curve. 96 | - *plot.png*: Image file to display as an image. 97 | - *experiment_id_2* 98 | - *catalog* 99 | - *raw.log* 100 | - *curve.csv* 101 | - *plot.png* 102 | 103 | To visualize experiment_id_1, you can always go to [http://localhost/deep-dashboard?id=experiment_id_1](http://localhost/deep-dashboard?id=experiment_id_1) 104 | 105 | ### Frontend (javascript) 106 | 107 | You can customize the dashboard through modifying *index.html*. The following 108 | code is currently in *index.html* to initialize the dashboard object. 109 | 110 | ```javascript 111 | $(function(){ 112 | var params = getSearchParameters(); 113 | var dashboard = new Dashboard("../results/", params.id, "#content", { 114 | xKey: "time", 115 | timeout: 5000, 116 | maxLines: 500, 117 | maxDatapoints: 500 118 | }); 119 | }); 120 | ``` 121 | 122 | There are four arguments to initialize a new dashboard object. 123 | - Root folder where all the experiments are stored. 124 | - Experiment ID 125 | - DOM selector, where to place the dashboard. 126 | - Extra options 127 | - xKey: the key name for the x-axis, "step" or "time". "step" will display 128 | the step number, and "time" will display the absolute time. 129 | - timeout: automatic refresh rate, in milliseconds. 130 | - maxLines: maximum number of lines to display to a plain text file. 131 | - maxDatapoints: maximum number of data points to display in one time 132 | series curve. 133 | 134 | ### Backend (your program) 135 | 136 | Last step to hook up the dashboard is to modify your training program. In 137 | short, to add your own job, you simply need to write some files to the right 138 | place, e.g. the training data points to a CSV file. 139 | 140 | #### Catalog format 141 | Each experiment folder contains a *catalog* file. It is in the following CSV 142 | format. 143 | 144 | ```csv 145 | filename,type,name 146 | xent1.csv,csv,training cross entropy 147 | xent2.csv,csv,validation cross entropy 148 | img.png,image,output 149 | ... 150 | ``` 151 | 152 | Each row contains three columns, the path to the file, the type of the file, 153 | and the name of the visualization. 154 | 155 | The type of the file can be one of the three: 156 | - *image* 157 | - *csv* 158 | - *plain* 159 | 160 | Every time you add a new visualization, 161 | remember to register it in the catalog. 162 | 163 | #### Adding time series visualization 164 | 1. Register in catalog. 165 | 2. Write a CSV file of the following format: 166 | 167 | ```csv 168 | step,time,$y-axis$ 169 | 0,2016-02-09T22:46:10.788732,0.886523685455 170 | 1,2016-02-09T22:46:12.329840,0.884593292039 171 | ... 172 | ``` 173 | 174 | Replace $y-axis$ with the name of the y-axis. 175 | 176 | #### Add plain text visualization 177 | 1. Register in catalog. 178 | 2. Write a plain text file of any content. 179 | 180 | #### Add image visualization 181 | 1. Register in catalog. 182 | 2. Write a image file. 183 | 184 | Once you update the files existing in the catalog, the dashboard will soon 185 | refresh to the newest version of the file. 186 | -------------------------------------------------------------------------------- /demo/mnist.py: -------------------------------------------------------------------------------- 1 | """Functions for downloading and reading MNIST data.""" 2 | import gzip 3 | import os 4 | import urllib 5 | 6 | import numpy 7 | 8 | SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/' 9 | 10 | 11 | def maybe_download(filename, work_directory): 12 | """Download the data from Yann's website, unless it's already here.""" 13 | if not os.path.exists(work_directory): 14 | os.mkdir(work_directory) 15 | filepath = os.path.join(work_directory, filename) 16 | if not os.path.exists(filepath): 17 | filepath, _ = urllib.urlretrieve(SOURCE_URL + filename, filepath) 18 | statinfo = os.stat(filepath) 19 | print 'Succesfully downloaded', filename, statinfo.st_size, 'bytes.' 20 | return filepath 21 | 22 | 23 | def _read32(bytestream): 24 | dt = numpy.dtype(numpy.uint32).newbyteorder('>') 25 | return numpy.frombuffer(bytestream.read(4), dtype=dt) 26 | 27 | 28 | def extract_images(filename): 29 | """Extract the images into a 4D uint8 numpy array [index, y, x, depth].""" 30 | print 'Extracting', filename 31 | with gzip.open(filename) as bytestream: 32 | magic = _read32(bytestream) 33 | if magic != 2051: 34 | raise ValueError( 35 | 'Invalid magic number %d in MNIST image file: %s' % 36 | (magic, filename)) 37 | num_images = _read32(bytestream) 38 | rows = _read32(bytestream) 39 | cols = _read32(bytestream) 40 | buf = bytestream.read(rows * cols * num_images) 41 | data = numpy.frombuffer(buf, dtype=numpy.uint8) 42 | data = data.reshape(num_images, rows, cols, 1) 43 | return data 44 | 45 | 46 | def dense_to_one_hot(labels_dense, num_classes=10): 47 | """Convert class labels from scalars to one-hot vectors.""" 48 | num_labels = labels_dense.shape[0] 49 | index_offset = numpy.arange(num_labels) * num_classes 50 | labels_one_hot = numpy.zeros((num_labels, num_classes)) 51 | labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1 52 | return labels_one_hot 53 | 54 | 55 | def extract_labels(filename, one_hot=False): 56 | """Extract the labels into a 1D uint8 numpy array [index].""" 57 | print 'Extracting', filename 58 | with gzip.open(filename) as bytestream: 59 | magic = _read32(bytestream) 60 | if magic != 2049: 61 | raise ValueError( 62 | 'Invalid magic number %d in MNIST label file: %s' % 63 | (magic, filename)) 64 | num_items = _read32(bytestream) 65 | buf = bytestream.read(num_items) 66 | labels = numpy.frombuffer(buf, dtype=numpy.uint8) 67 | if one_hot: 68 | return dense_to_one_hot(labels) 69 | return labels 70 | 71 | 72 | class DataSet(object): 73 | 74 | def __init__(self, images, labels, fake_data=False): 75 | if fake_data: 76 | self._num_examples = 10000 77 | else: 78 | assert images.shape[0] == labels.shape[0], ( 79 | "images.shape: %s labels.shape: %s" % (images.shape, 80 | labels.shape)) 81 | self._num_examples = images.shape[0] 82 | 83 | # Convert shape from [num examples, rows, columns, depth] 84 | # to [num examples, rows*columns] (assuming depth == 1) 85 | assert images.shape[3] == 1 86 | images = images.reshape(images.shape[0], 87 | images.shape[1] * images.shape[2]) 88 | # Convert from [0, 255] -> [0.0, 1.0]. 89 | images = images.astype(numpy.float32) 90 | images = numpy.multiply(images, 1.0 / 255.0) 91 | self._images = images 92 | self._labels = labels 93 | self._epochs_completed = 0 94 | self._index_in_epoch = 0 95 | 96 | @property 97 | def images(self): 98 | return self._images 99 | 100 | @property 101 | def labels(self): 102 | return self._labels 103 | 104 | @property 105 | def num_examples(self): 106 | return self._num_examples 107 | 108 | @property 109 | def epochs_completed(self): 110 | return self._epochs_completed 111 | 112 | def next_batch(self, batch_size, fake_data=False): 113 | """Return the next `batch_size` examples from this data set.""" 114 | if fake_data: 115 | fake_image = [1.0 for _ in xrange(784)] 116 | fake_label = 0 117 | return [fake_image for _ in xrange(batch_size)], [ 118 | fake_label for _ in xrange(batch_size)] 119 | start = self._index_in_epoch 120 | self._index_in_epoch += batch_size 121 | if self._index_in_epoch > self._num_examples: 122 | # Finished epoch 123 | self._epochs_completed += 1 124 | # Shuffle the data 125 | perm = numpy.arange(self._num_examples) 126 | numpy.random.shuffle(perm) 127 | self._images = self._images[perm] 128 | self._labels = self._labels[perm] 129 | # Start next epoch 130 | start = 0 131 | self._index_in_epoch = batch_size 132 | assert batch_size <= self._num_examples 133 | end = self._index_in_epoch 134 | return self._images[start:end], self._labels[start:end] 135 | 136 | 137 | def read_data_sets(train_dir, fake_data=False, one_hot=False): 138 | class DataSets(object): 139 | pass 140 | data_sets = DataSets() 141 | 142 | if fake_data: 143 | data_sets.train = DataSet([], [], fake_data=True) 144 | data_sets.validation = DataSet([], [], fake_data=True) 145 | data_sets.test = DataSet([], [], fake_data=True) 146 | return data_sets 147 | 148 | TRAIN_IMAGES = 'train-images-idx3-ubyte.gz' 149 | TRAIN_LABELS = 'train-labels-idx1-ubyte.gz' 150 | TEST_IMAGES = 't10k-images-idx3-ubyte.gz' 151 | TEST_LABELS = 't10k-labels-idx1-ubyte.gz' 152 | VALIDATION_SIZE = 5000 153 | 154 | local_file = maybe_download(TRAIN_IMAGES, train_dir) 155 | train_images = extract_images(local_file) 156 | 157 | local_file = maybe_download(TRAIN_LABELS, train_dir) 158 | train_labels = extract_labels(local_file, one_hot=one_hot) 159 | 160 | local_file = maybe_download(TEST_IMAGES, train_dir) 161 | test_images = extract_images(local_file) 162 | 163 | local_file = maybe_download(TEST_LABELS, train_dir) 164 | test_labels = extract_labels(local_file, one_hot=one_hot) 165 | 166 | validation_images = train_images[:VALIDATION_SIZE] 167 | validation_labels = train_labels[:VALIDATION_SIZE] 168 | train_images = train_images[VALIDATION_SIZE:] 169 | train_labels = train_labels[VALIDATION_SIZE:] 170 | 171 | data_sets.train = DataSet(train_images, train_labels) 172 | data_sets.validation = DataSet(validation_images, validation_labels) 173 | data_sets.test = DataSet(test_images, test_labels) 174 | 175 | return data_sets 176 | -------------------------------------------------------------------------------- /demo/logger.py: -------------------------------------------------------------------------------- 1 | """ 2 | A python logger 3 | 4 | Usage: 5 | # Set logger verbose level. 6 | import os 7 | os.environ['VERBOSE'] = 1 8 | 9 | import logger 10 | log = logger.get('../logs/sample_log') 11 | 12 | log.info('Hello world!') 13 | log.info('Hello again!', verbose=2) 14 | log.warning('Something might be wrong.') 15 | log.error('Something is wrong.') 16 | log.fatal('Failed.') 17 | """ 18 | 19 | from __future__ import print_function 20 | import datetime 21 | import inspect 22 | import os 23 | import sys 24 | import traceback 25 | 26 | TERM_COLOR = { 27 | 'normal': '\033[0m', 28 | 'bright': '\033[1m', 29 | 'invert': '\033[7m', 30 | 'black': '\033[30m', 31 | 'red': '\033[31m', 32 | 'green': '\033[32m', 33 | 'yellow': '\033[33m', 34 | 'blue': '\033[34m', 35 | 'magenta': '\033[35m', 36 | 'cyan': '\033[36m', 37 | 'white': '\033[37m', 38 | 'default': '\033[39m' 39 | } 40 | 41 | log = None 42 | 43 | 44 | def get(fname=None): 45 | """ 46 | Returns a logger instance, with optional log file output. 47 | """ 48 | global log 49 | if log is not None and fname is None: 50 | return log 51 | 52 | # fname = os.environ.get('LOGTO', None) 53 | # if fname is None: 54 | # fname = default_fname 55 | else: 56 | log = Logger(fname) 57 | 58 | return log 59 | 60 | 61 | class Logger(object): 62 | 63 | def __init__(self, filename=None, default_verbose=0): 64 | """ 65 | Constructs a logger with optional log file output. 66 | 67 | Args: 68 | filename: optional log file output. If None, nothing will be 69 | written to file 70 | """ 71 | now = datetime.datetime.now() 72 | self.verbose_thresh = int(os.environ.get('VERBOSE', 0)) 73 | self.default_verbose = default_verbose 74 | if filename is not None: 75 | self.filename = \ 76 | '{}-{:04d}{:02d}{:02d}-{:02d}{:02d}{:02d}.log'.format( 77 | filename, 78 | now.year, now.month, now.day, 79 | now.hour, now.minute, now.second) 80 | dirname = os.path.dirname(self.filename) 81 | if not os.path.exists(dirname): 82 | os.makedirs(dirname) 83 | open(self.filename, 'w').close() 84 | self.info('Log written to {}'.format( 85 | os.path.abspath(self.filename))) 86 | else: 87 | self.filename = None 88 | 89 | pass 90 | 91 | @staticmethod 92 | def get_time_str(t=None): 93 | """ 94 | Returns a formatted time string. 95 | 96 | Args: 97 | t: datetime, default now. 98 | """ 99 | if t is None: 100 | t = datetime.datetime.now() 101 | 102 | # timestr = '{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format( 103 | # t.year, t.month, t.day, t.hour, t.minute, t.second) 104 | # timestr = '{:04d}{:02d}{:02d}{:02d}{:02d}{:02d}'.format( 105 | # t.year, t.month, t.day, t.hour, t.minute, t.second) 106 | timestr = '{:02d}:{:02d}:{:02d}'.format(t.hour, t.minute, t.second) 107 | 108 | return timestr 109 | 110 | def log(self, message, typ='info', verbose=None): 111 | """ 112 | Writes a message. 113 | 114 | Args: 115 | message: string, message content. 116 | typ: string, type of the message. info, warning, error, or fatal. 117 | verbose: number, verbose level of the message. If lower than the 118 | environment variable, then the message will be logged to standard 119 | output and log output file (if set). 120 | """ 121 | if typ == 'info': 122 | typstr_print = '{0}INFO:{1}'.format( 123 | TERM_COLOR['green'], TERM_COLOR['default']) 124 | typstr_log = 'INFO:' 125 | elif typ == 'warning': 126 | typstr_print = '{0}WARNING:{1}'.format( 127 | TERM_COLOR['yellow'], TERM_COLOR['default']) 128 | typstr_log = 'WARNING:' 129 | elif typ == 'error': 130 | typstr_print = '{0}ERROR:{1}'.format( 131 | TERM_COLOR['red'], TERM_COLOR['default']) 132 | typstr_log = 'ERROR:' 133 | elif typ == 'fatal': 134 | typstr_print = '{0}FATAL:{1}'.format( 135 | TERM_COLOR['red'], TERM_COLOR['default']) 136 | typstr_log = 'FATAL:' 137 | else: 138 | raise Exception('Unknown log type: {0}'.format(typ)) 139 | timestr = self.get_time_str() 140 | for (frame, filename, line_number, function_name, lines, index) in \ 141 | inspect.getouterframes(inspect.currentframe()): 142 | fn = os.path.basename(filename) 143 | if fn != 'logger.py': 144 | break 145 | cwd = os.getcwd() 146 | if filename.startswith(cwd): 147 | filename = filename[len(cwd):] 148 | filename = filename.lstrip('/') 149 | callerstr = '{0}:{1}'.format(filename, line_number) 150 | printstr = '{0} {1} {2} {3}'.format( 151 | typstr_print, timestr, callerstr, message) 152 | logstr = '{0} {1} {2} {3}'.format( 153 | typstr_log, timestr, callerstr, message) 154 | 155 | if verbose is None: 156 | verbose = self.default_verbose 157 | 158 | if self.verbose_thresh >= verbose: 159 | print(printstr) 160 | 161 | if self.filename is not None: 162 | with open(self.filename, 'a') as f: 163 | f.write(logstr) 164 | f.write('\n') 165 | 166 | pass 167 | 168 | def info(self, message, verbose=None): 169 | """ 170 | Writes an info message. 171 | 172 | Args: 173 | message: string, message content. 174 | verbose: number, verbose level. 175 | """ 176 | self.log(message, typ='info', verbose=verbose) 177 | 178 | pass 179 | 180 | def warning(self, message, verbose=None): 181 | """ 182 | Writes a warning message. 183 | 184 | Args: 185 | message: string, message content. 186 | verbose: number, verbose level. 187 | """ 188 | self.log(message, typ='warning', verbose=verbose) 189 | 190 | pass 191 | 192 | def error(self, message, verbose=None): 193 | """ 194 | Writes an info message. 195 | 196 | Args: 197 | message: string, message content. 198 | verbose: number, verbose level. 199 | """ 200 | self.log(message, typ='error', verbose=verbose) 201 | 202 | pass 203 | 204 | def fatal(self, message, verbose=None): 205 | """ 206 | Writes a fatal message, and exits the program. 207 | 208 | Args: 209 | message: string, message content. 210 | verbose: number, verbose level. 211 | """ 212 | self.log(message, typ='fatal', verbose=verbose) 213 | sys.exit(0) 214 | 215 | pass 216 | 217 | def log_args(self, verbose=None): 218 | self.info('Command: {}'.format(' '.join(sys.argv))) 219 | 220 | pass 221 | 222 | def log_exception(self, exception): 223 | tb_str = traceback.format_exc(exception) 224 | self.error(tb_str) 225 | 226 | pass 227 | 228 | def verbose_level(self, level): 229 | 230 | class VerboseScope(): 231 | 232 | def __init__(self, logger, new_level): 233 | self._new_level = new_level 234 | self._logger = logger 235 | 236 | pass 237 | 238 | def __enter__(self): 239 | self._restore = self._logger.default_verbose 240 | self._logger.default_verbose = self._new_level 241 | 242 | pass 243 | 244 | def __exit__(self, type, value, traceback): 245 | self._logger.default_verbose = self._restore 246 | 247 | pass 248 | 249 | return VerboseScope(self, level) 250 | -------------------------------------------------------------------------------- /merge_expr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merge multiple experiments to show it in one dashboard. 3 | Do this after each experiment is finished. 4 | 5 | Usage: 6 | python merge_expr.py \ 7 | --results {results_folder} \ 8 | --input {name}:{exp_id},{name}:{exp_id},... \ 9 | --output {new_exp_id} 10 | 11 | Example: 12 | """ 13 | from __future__ import print_function 14 | import argparse 15 | import os 16 | import shutil 17 | import sys 18 | 19 | if sys.version[0] == "2": 20 | input = raw_input 21 | 22 | 23 | def parse_args(): 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument('--results', default=None, type=str) 26 | parser.add_argument('--input', default=None, type=str) 27 | parser.add_argument('--output', default=None, type=str) 28 | parser.add_argument('--max_step', default=None, type=int) 29 | return parser.parse_args() 30 | 31 | 32 | def get_inputs(inp_str): 33 | exp_str = inp_str.split(',') 34 | exp_pair = [_estr.split(':') for _estr in exp_str] 35 | return exp_pair 36 | 37 | 38 | def merge(input_pairs, output_folder, max_step=None): 39 | data = [] 40 | filename_name_map = {} 41 | filename_type_map = {} 42 | for ii in xrange(len(input_pairs)): 43 | data_ii = {} # filename maps to data_dict 44 | name = input_pairs[ii][0] 45 | folder = input_pairs[ii][1] 46 | # Read catalog 47 | catalog = os.path.join(folder, 'catalog') 48 | with open(catalog, 'r') as catalog_file: 49 | for line in catalog_file: 50 | parts = line.strip('\n').split(',') 51 | # Only merge CSV file. 52 | if parts[1] != 'csv' and parts[1] != 'histogram': 53 | continue 54 | if parts[0] not in filename_name_map: 55 | filename_name_map[parts[0]] = parts[2] 56 | if parts[0] not in filename_type_map: 57 | filename_type_map[parts[0]] = parts[1] 58 | filename = os.path.join(folder, parts[0]) 59 | 60 | if parts[1] == 'csv': 61 | data_ = [] 62 | names_ = [] 63 | with open(filename, 'r') as data_file: 64 | for line2 in data_file: 65 | parts2 = line2.strip('\n').split(',') 66 | if len(data_) == 0: 67 | for pp in parts2: 68 | names_.append(pp) 69 | data_.append([]) 70 | else: 71 | do_add = False 72 | if max_step is not None: 73 | if int(parts2[0]) < max_step: 74 | do_add = True 75 | else: 76 | do_add = True 77 | if do_add: 78 | for jj, pp in enumerate(parts2): 79 | if jj == 0: 80 | data_[jj].append(int(pp)) 81 | else: 82 | data_[jj].append(pp) 83 | 84 | elif parts[1] == 'histogram': 85 | data_ = [] 86 | names_ = [] 87 | with open(filename, 'r') as data_file: 88 | for line2 in data_file: 89 | parts2 = line2.strip('\n').split(',') 90 | names_.append(parts2[0]) 91 | data_.append(parts2[1:]) 92 | 93 | data_dict = {} # For each stats file: key: values. 94 | for data_jj, name_jj in zip(data_, names_): 95 | data_dict[name_jj] = data_jj 96 | data_ii[parts[0]] = data_dict 97 | data.append(data_ii) 98 | pass 99 | 100 | merge_data = {} 101 | for data_ii in data: 102 | for kk in data_ii.keys(): 103 | if kk not in merge_data: 104 | if filename_type_map[kk] == 'csv': 105 | merge_data[kk] = [] 106 | elif filename_type_map[kk] == 'histogram': 107 | merge_data[kk] = {} 108 | 109 | for ii, data_ii in enumerate(data): # Experiments. 110 | for kk in data_ii.keys(): # Filename. 111 | 112 | if filename_type_map[kk] == 'csv': 113 | for jj in xrange(len(data_ii[kk].values()[0])): # Time series. 114 | item = {} 115 | for ll in data_ii[kk].keys(): 116 | if ll != 'step' and ll != 'time': 117 | if len(data_ii[kk].keys()) == 3: 118 | ll2 = input_pairs[ii][0] 119 | else: 120 | ll2 = input_pairs[ii][0] + ' ' + ll 121 | else: 122 | ll2 = ll 123 | item[ll2] = data_ii[kk][ll][jj] 124 | merge_data[kk].append(item) 125 | 126 | elif filename_type_map[kk] == 'histogram': 127 | for jj in data_ii[kk].keys(): 128 | if len(data_ii[kk].keys()) == 1: 129 | ll = input_pairs[ii][0] 130 | else: 131 | ll = input_pairs[ii][0] + ' ' + jj 132 | merge_data[kk][ll] = data_ii[kk][jj] 133 | 134 | for kk in merge_data.keys(): 135 | if filename_type_map[kk] == 'csv': 136 | merge_data[kk] = sorted(merge_data[kk], key=lambda x: x['step']) 137 | 138 | merge_catalog = os.path.join(output_folder, 'catalog') 139 | with open(merge_catalog, 'w') as f: 140 | f.write('filename,type,name\n') 141 | for kk in merge_data.keys(): 142 | filename = os.path.join(output_folder, kk) 143 | 144 | if filename_type_map[kk] == 'csv': 145 | all_keys = [] 146 | for jj in xrange(len(merge_data[kk])): 147 | for ll in merge_data[kk][jj].keys(): 148 | if ll not in all_keys: 149 | all_keys.append(ll) 150 | with open(filename, 'w') as f: 151 | f.write('step,time') 152 | for ll in all_keys: 153 | if ll != 'step' and ll != 'time': 154 | f.write(',{}'.format(ll)) 155 | f.write('\n') 156 | for jj in xrange(len(merge_data[kk])): 157 | ss = '{},{}'.format(merge_data[kk][jj]['step'], 158 | merge_data[kk][jj]['time']) 159 | for ll in all_keys: 160 | if ll != 'step' and ll != 'time': 161 | ss += ',' 162 | if ll in merge_data[kk][jj]: 163 | ss += '{}'.format(merge_data[kk][jj][ll]) 164 | f.write('{}\n'.format(ss)) 165 | 166 | elif filename_type_map[kk] == 'histogram': 167 | with open(filename, 'w') as f: 168 | for jj in sorted(merge_data[kk].keys()): 169 | f.write(jj + ',' + ','.join(merge_data[kk][jj]) + '\n') 170 | 171 | with open(os.path.join(output_folder, 'catalog'), 'a') as f: 172 | f.write('{},{},{}\n'.format( 173 | kk, filename_type_map[kk], filename_name_map[kk])) 174 | 175 | 176 | def main(): 177 | args = parse_args() 178 | if args.input is None: 179 | raise Exception('Must provide input experiment ID') 180 | if args.results is None: 181 | raise Exception('Must provide results folder') 182 | if args.output is None: 183 | raise Exception('Must provide output experiment ID') 184 | input_pairs = get_inputs(args.input) 185 | for inp in input_pairs: 186 | inp[1] = os.path.join(args.results, inp[1]) 187 | output_folder = os.path.join(args.results, args.output) 188 | if not os.path.exists(output_folder): 189 | os.makedirs(output_folder) 190 | else: 191 | confirm = input( 192 | 'Folder "{}" exists, remove and continue? [Y/n] '.format( 193 | output_folder)) 194 | if confirm != 'n': 195 | shutil.rmtree(output_folder) 196 | os.makedirs(output_folder) 197 | else: 198 | print('Folder "{}" not removed.'.format(output_folder)) 199 | return 200 | merge(input_pairs, output_folder, max_step=args.max_step) 201 | pass 202 | 203 | if __name__ == '__main__': 204 | main() 205 | -------------------------------------------------------------------------------- /lib/nv.d3.min.css: -------------------------------------------------------------------------------- 1 | .nvd3 .nv-axis line,.nvd3 .nv-axis path{fill:none;shape-rendering:crispEdges}.nv-brush .extent,.nvd3 .background path,.nvd3 .nv-axis line,.nvd3 .nv-axis path{shape-rendering:crispEdges}.nv-distx,.nv-disty,.nv-noninteractive,.nvd3 .nv-axis,.nvd3.nv-pie .nv-label,.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvtooltip,svg.nvd3-svg{display:block;-webkit-touch-callout:none;-khtml-user-select:none}.nvd3 .nv-axis{opacity:1}.nvd3 .nv-axis.nv-disabled,.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3 .nv-axis path{stroke:#000;stroke-opacity:.75}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{stroke:#e5e5e5}.nvd3 .nv-axis .zero line, .nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:transparent}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-discretebar .nv-groups rect,.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover,.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:transparent}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover,.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3 .nv-groups .nv-point.hover,.nvd3.nv-scatter .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-interactiveGuideLine,.nvtooltip{pointer-events:none}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvtooltip h3,.nvtooltip table td.key{font-weight:400}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;color:rgba(0,0,0,1);padding:1px;z-index:10000;font-family:Arial;font-size:13px;text-align:left;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip h3,.nvtooltip p{margin:0;text-align:center}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{padding:4px 14px;line-height:18px;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{padding:5px 14px}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key.total{font-weight:700}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{vertical-align:middle;width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 line.nv-guideline{stroke:#ccc} 2 | /*# sourceMappingURL=nv.d3.min.css.map */ -------------------------------------------------------------------------------- /demo/vae.py: -------------------------------------------------------------------------------- 1 | """ 2 | This code implements VAE (Variational Autoencoder) [1] on MNIST. 3 | 4 | Author: Mengye Ren (mren@cs.toronto.edu) 5 | 6 | Usage: python vae_mnist.py -logs {path to log folder} 7 | 8 | Reference: 9 | [1] D.P. Kingma, M. Welling. Auto-encoding variational Bayes. ICLR 2014. 10 | """ 11 | import cslab_environ 12 | 13 | import argparse 14 | import datetime 15 | import logger 16 | import matplotlib 17 | matplotlib.use('Agg') 18 | import matplotlib.cm as cm 19 | import matplotlib.pyplot as plt 20 | import mnist 21 | import numpy as np 22 | import os 23 | import tensorflow as tf 24 | import time 25 | 26 | 27 | def log_register(filename, typ, name): 28 | """Register a new file in the catalog 29 | 30 | Args: 31 | filename: string, path to the log file. 32 | typ: string, file type, "csv" or "plain" or "image". 33 | name: string, name of the visualization. 34 | """ 35 | folder = os.path.dirname(filename) 36 | if not os.path.exists(folder): 37 | os.makedirs(folder) 38 | catalog = os.path.join(folder, 'catalog') 39 | basename = os.path.basename(filename) 40 | if not os.path.exists(catalog): 41 | with open(catalog, 'w') as f: 42 | f.write('filename,type,name\n') 43 | f.write('{},{},{}\n'.format(basename, typ, name)) 44 | else: 45 | with open(catalog, 'a') as f: 46 | f.write('{},{},{}\n'.format(basename, typ, name)) 47 | 48 | 49 | class TimeSeriesLogger(): 50 | """Log time series data to CSV file.""" 51 | 52 | def __init__(self, filename, labels, name=None, buffer_size=100): 53 | """ 54 | Args: 55 | label: list of string 56 | name: string 57 | """ 58 | self.filename = filename 59 | self.written_catalog = False 60 | if type(labels) != list: 61 | labels = [labels] 62 | if name is None: 63 | self.name = labels[0] 64 | else: 65 | self.name = name 66 | self.labels = labels 67 | self.buffer = [] 68 | self.buffer.append('step,time,{}\n'.format(','.join(self.labels))) 69 | self.buffer_size = buffer_size 70 | log.info('Time series data "{}" log to "{}"'.format(labels, filename)) 71 | pass 72 | 73 | def add(self, step, values): 74 | """Add an entry. 75 | 76 | Args: 77 | step: int 78 | value: list of numbers 79 | """ 80 | t = datetime.datetime.utcnow() 81 | if type(values) != list: 82 | values = [values] 83 | self.buffer.append('{:d},{},{}\n'.format( 84 | step, t.isoformat(), ','.join([str(v) for v in values]))) 85 | if len(self.buffer) >= self.buffer_size: 86 | self.flush() 87 | pass 88 | 89 | def flush(self): 90 | """Write the buffer to file.""" 91 | 92 | if not self.written_catalog: 93 | log_register(self.filename, 'csv', self.name) 94 | self.written_catalog = True 95 | 96 | if not os.path.exists(self.filename): 97 | mode = 'w' 98 | else: 99 | mode = 'a' 100 | with open(self.filename, mode) as f: 101 | f.write(''.join(self.buffer)) 102 | self.buffer = [] 103 | pass 104 | 105 | def close(self): 106 | """Flush the rest.""" 107 | self.flush() 108 | pass 109 | 110 | 111 | def weight_variable(shape, wd=None, name=None): 112 | """Initialize weights.""" 113 | initial = tf.truncated_normal(shape, stddev=0.01) 114 | var = tf.Variable(initial, name=name) 115 | if wd: 116 | weight_decay = tf.nn.l2_loss(var) * wd 117 | tf.add_to_collection('losses', weight_decay) 118 | 119 | return var 120 | 121 | 122 | def get_decoder(opt, train_model, device='/cpu:0'): 123 | """Decoder for inference""" 124 | num_hid = opt['num_hid'] 125 | nl = eval(opt['non_linear']) 126 | 127 | with tf.device(device): 128 | z = tf.placeholder('float', [None, num_hid], name='z') 129 | w_4 = train_model['w_4'] 130 | b_4 = train_model['b_4'] 131 | h_dec = nl(tf.matmul(z, w_4) + b_4, name='h_dec') 132 | w_5 = train_model['w_5'] 133 | b_5 = train_model['b_5'] 134 | mu_x = tf.sigmoid(tf.matmul(h_dec, w_5) + b_5, name='mu_x') 135 | 136 | return {'z': z, 'mu_x': mu_x} 137 | 138 | 139 | def get_train_model(opt, device='/cpu:0'): 140 | """VAE""" 141 | num_inp = opt['num_inp'] 142 | num_hid_enc = opt['num_hid_enc'] 143 | num_hid = opt['num_hid'] 144 | num_hid_dec = opt['num_hid_dec'] 145 | wd = opt['weight_decay'] 146 | nl = eval(opt['non_linear']) 147 | 148 | with tf.device(device): 149 | x = tf.placeholder('float', [None, num_inp], name='x') 150 | w_1 = weight_variable([num_inp, num_hid_enc], wd=wd) 151 | b_1 = weight_variable([num_hid_enc], wd=wd) 152 | h_enc = nl(tf.matmul(x, w_1) + b_1) 153 | w_2 = weight_variable([num_hid_enc, num_hid], wd=wd) 154 | b_2 = weight_variable([num_hid], wd=wd) 155 | mu_enc = tf.matmul(h_enc, w_2) + b_2 156 | w_3 = weight_variable([num_hid_enc, num_hid], wd=wd) 157 | b_3 = weight_variable([num_hid], wd=wd) 158 | log_sigma_enc = tf.matmul(h_enc, w_3) + b_3 159 | t = tf.placeholder('float', [None, num_hid]) 160 | z = mu_enc + tf.exp(log_sigma_enc) * t 161 | kl_qzx_pz = -0.5 * tf.reduce_sum( 162 | 1 + 2 * log_sigma_enc - mu_enc * mu_enc - 163 | tf.exp(2 * log_sigma_enc)) 164 | w_4 = weight_variable([num_hid, num_hid_dec], wd=wd) 165 | b_4 = weight_variable([num_hid_dec], wd=wd) 166 | h_dec = nl(tf.matmul(z, w_4) + b_4) 167 | w_5 = weight_variable([num_hid_dec, num_inp], wd=wd) 168 | b_5 = weight_variable([num_inp], wd=wd) 169 | mu_dec = tf.sigmoid(tf.matmul(h_dec, w_5) + b_5) 170 | log_pxz = tf.reduce_sum(x * tf.log(mu_dec + 1e-7) + 171 | (1 - x) * tf.log((1 - mu_dec + 1e-7))) 172 | num_ex = tf.shape(x) 173 | w_kl = 1.0 174 | w_logp = 1.0 175 | log_px_lb = (-w_kl * kl_qzx_pz + w_logp * log_pxz) / \ 176 | (w_kl + w_logp) * 2.0 / tf.to_float(num_ex[0]) 177 | tf.add_to_collection('losses', -log_px_lb) 178 | total_loss = tf.add_n(tf.get_collection('losses')) 179 | 180 | lr = 1e-4 181 | train_step = tf.train.AdamOptimizer(lr).minimize(total_loss) 182 | 183 | return {'x': x, 't': t, 'w_1': w_1, 'h_enc': h_enc, 'h_dec': h_dec, 184 | 'log_px_lb': log_px_lb, 'train_step': train_step, 185 | 'w_4': w_4, 'b_4': b_4, 'w_5': w_5, 'b_5': b_5, 'mu_dec': mu_dec} 186 | 187 | 188 | def preprocess(x, opt): 189 | return (x > 0.5).astype('float32').reshape([-1, 28 * 28]) 190 | 191 | 192 | def get_model_id(task_name): 193 | time_obj = datetime.datetime.now() 194 | model_id = timestr = '{}-{:04d}{:02d}{:02d}{:02d}{:02d}{:02d}'.format( 195 | task_name, time_obj.year, time_obj.month, time_obj.day, 196 | time_obj.hour, time_obj.minute, time_obj.second) 197 | 198 | return model_id 199 | 200 | 201 | def plot_digits(fname, data, num_row, num_col): 202 | f, axarr = plt.subplots(num_row, num_col, figsize=(10, num_row)) 203 | 204 | for ii in xrange(num_row): 205 | for jj in xrange(num_col): 206 | axarr[ii, jj].set_axis_off() 207 | idx = ii * num_col + jj 208 | axarr[ii, jj].imshow(data[idx], cmap=cm.Greys_r) 209 | 210 | plt.tight_layout(pad=0.0, w_pad=0.0, h_pad=0.0) 211 | plt.savefig(fname, dpi=80) 212 | 213 | 214 | def parse_args(): 215 | """Parse input arguments.""" 216 | parser = argparse.ArgumentParser(description='Train VAE') 217 | parser.add_argument('-logs', default='../logs', 218 | help='Training curve logs folder') 219 | args = parser.parse_args() 220 | 221 | return args 222 | 223 | if __name__ == '__main__': 224 | # Command-line arguments 225 | args = parse_args() 226 | 227 | # Model ID 228 | model_id = get_model_id('vae_mnist') 229 | 230 | # Log folder 231 | logs_folder = args.logs 232 | logs_folder = os.path.join(logs_folder, model_id) 233 | 234 | # Plain text logger 235 | log = logger.get(os.path.join(logs_folder, 'raw')) 236 | log.log_args() 237 | log_register(log.filename, 'plain', 'Raw logs') 238 | 239 | # Create time series loggers 240 | logp_logger = TimeSeriesLogger( 241 | os.path.join(logs_folder, 'logp.csv'), 242 | labels=['train', 'valid'], 243 | name='Log prob lowerbound', 244 | buffer_size=1) 245 | sparsity_logger = TimeSeriesLogger( 246 | os.path.join(logs_folder, 'sparsity.csv'), 247 | labels=['encoder', 'decoder'], 248 | name='Hidden layer sparsity', 249 | buffer_size=1) 250 | step_time_logger = TimeSeriesLogger( 251 | os.path.join(logs_folder, 'step_time.csv'), 252 | labels='step time (ms)', 253 | buffer_size=3) 254 | 255 | # Image loggers 256 | w1_image_fname = os.path.join(logs_folder, 'w1.png') 257 | decoder_image_fname = os.path.join(logs_folder, 'decoder.png') 258 | gen_image_fname = os.path.join(logs_folder, 'gen.png') 259 | log_register(w1_image_fname, 'image', 'W1 visualization') 260 | log_register(decoder_image_fname, 'image', 'Decoder reconstruction') 261 | log_register(gen_image_fname, 'image', 'Generated digits') 262 | 263 | # Dashboard info 264 | log.info('Dashboard: http://localhost/deep-dashboard?id={}'.format(model_id)) 265 | 266 | # Model options 267 | opt = { 268 | 'num_inp': 28 * 28, 269 | 'num_hid_enc': 100, 270 | 'num_hid': 20, 271 | 'num_hid_dec': 100, 272 | 'non_linear': 'tf.nn.relu', 273 | 'weight_decay': 5e-5 274 | } 275 | 276 | # MNIST dataset 277 | dataset = mnist.read_data_sets("../MNIST_data/", one_hot=True) 278 | 279 | # Create models 280 | m = get_train_model(opt) 281 | m_dec = get_decoder(opt, m) 282 | 283 | # RNG 284 | random = np.random.RandomState(2) 285 | 286 | # Start session 287 | sess = tf.Session() 288 | sess.run(tf.initialize_all_variables()) 289 | 290 | # Train loop 291 | step = 0 292 | while step < 50000: 293 | # Validation 294 | valid_log_px_lb = 0.0 295 | henc_sparsity = 0.0 296 | hdec_sparsity = 0.0 297 | log.info('Running validation') 298 | for ii in xrange(100): 299 | batch = dataset.test.next_batch(100) 300 | x = preprocess(batch[0], opt) 301 | t = random.normal(0, 1, [x.shape[0], opt['num_hid']]) 302 | log_px_lb, henc, hdec = sess.run([ 303 | m['log_px_lb'], 304 | m['h_enc'], 305 | m['h_dec']], 306 | feed_dict={ 307 | m['x']: x, 308 | m['t']: t 309 | }) 310 | henc_sparsity += (henc == 0.0).sum() / float(henc.size) / 100.0 311 | hdec_sparsity += (hdec == 0.0).sum() / float(hdec.size) / 100.0 312 | valid_log_px_lb += log_px_lb / 100.0 313 | log.info('step {:d}, valid logp {:.4f}'.format(step, valid_log_px_lb)) 314 | 315 | num_plot = 50 316 | x = dataset.test.images[: num_plot] 317 | t = random.normal(0, 1, [x.shape[0], opt['num_hid']]) 318 | w1, x_rec = sess.run([m['w_1'], m['mu_dec']], feed_dict={ 319 | m['x']: x, 320 | m['t']: t 321 | }) 322 | w1 = w1.transpose().reshape([-1, 28, 28]) 323 | z = random.normal(0, 1, [x.shape[0], opt['num_hid']]) 324 | x_ = sess.run(m_dec['mu_x'], feed_dict={ 325 | m_dec['z']: z}).reshape([-1, 28, 28]) 326 | x_comb = np.array([x, x_rec]).transpose( 327 | [1, 0, 2]).reshape([-1, 28, 28]) 328 | 329 | plot_digits(w1_image_fname, w1, 3, 10) 330 | plot_digits(decoder_image_fname, x_comb, 3, 10) 331 | plot_digits(gen_image_fname, x_, 3, 10) 332 | logp_logger.add(step, ['', valid_log_px_lb]) 333 | sparsity_logger.add(step, [henc_sparsity, hdec_sparsity]) 334 | 335 | # Train 336 | for ii in xrange(500): 337 | batch = dataset.train.next_batch(100) 338 | x = preprocess(batch[0], opt) 339 | t = random.normal(0, 1, [x.shape[0], opt['num_hid']]) 340 | st = time.time() 341 | r = sess.run([m['log_px_lb'], m['train_step']], feed_dict={ 342 | m['x']: x, 343 | m['t']: random.normal(0, 1, [x.shape[0], opt['num_hid']]) 344 | }) 345 | if step % 10 == 0: 346 | log_px_lb = r[0] 347 | step_time = (time.time() - st) * 1000 348 | log.info('{:d} logp {:.4f} t {:.2f}ms'.format( 349 | step, log_px_lb, step_time)) 350 | logp_logger.add(step, [log_px_lb, '']) 351 | step_time_logger.add(step, step_time) 352 | 353 | step += 1 354 | 355 | # Clean up 356 | logp_logger.close() 357 | sparsity_logger.close() 358 | step_time_logger.close() 359 | sess.close() 360 | -------------------------------------------------------------------------------- /lib/nv.d3.css: -------------------------------------------------------------------------------- 1 | /* nvd3 version 1.8.1-dev (https://github.com/novus/nvd3) 2015-09-10 */ 2 | .nvd3 .nv-axis { 3 | pointer-events:none; 4 | opacity: 1; 5 | } 6 | 7 | .nvd3 .nv-axis path { 8 | fill: none; 9 | stroke: #000; 10 | stroke-opacity: .75; 11 | shape-rendering: crispEdges; 12 | } 13 | 14 | .nvd3 .nv-axis path.domain { 15 | stroke-opacity: .75; 16 | } 17 | 18 | .nvd3 .nv-axis.nv-x path.domain { 19 | stroke-opacity: 0; 20 | } 21 | 22 | .nvd3 .nv-axis line { 23 | fill: none; 24 | stroke: #e5e5e5; 25 | shape-rendering: crispEdges; 26 | } 27 | 28 | .nvd3 .nv-axis .zero line, 29 | /*this selector may not be necessary*/ .nvd3 .nv-axis line.zero { 30 | stroke-opacity: .75; 31 | } 32 | 33 | .nvd3 .nv-axis .nv-axisMaxMin text { 34 | font-weight: bold; 35 | } 36 | 37 | .nvd3 .x .nv-axis .nv-axisMaxMin text, 38 | .nvd3 .x2 .nv-axis .nv-axisMaxMin text, 39 | .nvd3 .x3 .nv-axis .nv-axisMaxMin text { 40 | text-anchor: middle 41 | } 42 | 43 | .nvd3 .nv-axis.nv-disabled { 44 | opacity: 0; 45 | } 46 | 47 | .nvd3 .nv-bars rect { 48 | fill-opacity: .75; 49 | 50 | transition: fill-opacity 250ms linear; 51 | -moz-transition: fill-opacity 250ms linear; 52 | -webkit-transition: fill-opacity 250ms linear; 53 | } 54 | 55 | .nvd3 .nv-bars rect.hover { 56 | fill-opacity: 1; 57 | } 58 | 59 | .nvd3 .nv-bars .hover rect { 60 | fill: lightblue; 61 | } 62 | 63 | .nvd3 .nv-bars text { 64 | fill: rgba(0,0,0,0); 65 | } 66 | 67 | .nvd3 .nv-bars .hover text { 68 | fill: rgba(0,0,0,1); 69 | } 70 | 71 | .nvd3 .nv-multibar .nv-groups rect, 72 | .nvd3 .nv-multibarHorizontal .nv-groups rect, 73 | .nvd3 .nv-discretebar .nv-groups rect { 74 | stroke-opacity: 0; 75 | 76 | transition: fill-opacity 250ms linear; 77 | -moz-transition: fill-opacity 250ms linear; 78 | -webkit-transition: fill-opacity 250ms linear; 79 | } 80 | 81 | .nvd3 .nv-multibar .nv-groups rect:hover, 82 | .nvd3 .nv-multibarHorizontal .nv-groups rect:hover, 83 | .nvd3 .nv-candlestickBar .nv-ticks rect:hover, 84 | .nvd3 .nv-discretebar .nv-groups rect:hover { 85 | fill-opacity: 1; 86 | } 87 | 88 | .nvd3 .nv-discretebar .nv-groups text, 89 | .nvd3 .nv-multibarHorizontal .nv-groups text { 90 | font-weight: bold; 91 | fill: rgba(0,0,0,1); 92 | stroke: rgba(0,0,0,0); 93 | } 94 | 95 | /* boxplot CSS */ 96 | .nvd3 .nv-boxplot circle { 97 | fill-opacity: 0.5; 98 | } 99 | 100 | .nvd3 .nv-boxplot circle:hover { 101 | fill-opacity: 1; 102 | } 103 | 104 | .nvd3 .nv-boxplot rect:hover { 105 | fill-opacity: 1; 106 | } 107 | 108 | .nvd3 line.nv-boxplot-median { 109 | stroke: black; 110 | } 111 | 112 | .nv-boxplot-tick:hover { 113 | stroke-width: 2.5px; 114 | } 115 | /* bullet */ 116 | .nvd3.nv-bullet { font: 10px sans-serif; } 117 | .nvd3.nv-bullet .nv-measure { fill-opacity: .8; } 118 | .nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; } 119 | .nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; } 120 | .nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; } 121 | .nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; } 122 | .nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; } 123 | .nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; } 124 | .nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; } 125 | .nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; } 126 | .nvd3.nv-bullet .nv-subtitle { fill: #999; } 127 | 128 | 129 | .nvd3.nv-bullet .nv-range { 130 | fill: #bababa; 131 | fill-opacity: .4; 132 | } 133 | .nvd3.nv-bullet .nv-range:hover { 134 | fill-opacity: .7; 135 | } 136 | 137 | .nvd3.nv-candlestickBar .nv-ticks .nv-tick { 138 | stroke-width: 1px; 139 | } 140 | 141 | .nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover { 142 | stroke-width: 2px; 143 | } 144 | 145 | .nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect { 146 | stroke: #2ca02c; 147 | fill: #2ca02c; 148 | } 149 | 150 | .nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect { 151 | stroke: #d62728; 152 | fill: #d62728; 153 | } 154 | 155 | .with-transitions .nv-candlestickBar .nv-ticks .nv-tick { 156 | transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 157 | -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 158 | -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 159 | 160 | } 161 | 162 | .nvd3.nv-candlestickBar .nv-ticks line { 163 | stroke: #333; 164 | } 165 | 166 | 167 | .nvd3 .nv-legend .nv-disabled rect { 168 | /*fill-opacity: 0;*/ 169 | } 170 | 171 | .nvd3 .nv-check-box .nv-box { 172 | fill-opacity:0; 173 | stroke-width:2; 174 | } 175 | 176 | .nvd3 .nv-check-box .nv-check { 177 | fill-opacity:0; 178 | stroke-width:4; 179 | } 180 | 181 | .nvd3 .nv-series.nv-disabled .nv-check-box .nv-check { 182 | fill-opacity:0; 183 | stroke-opacity:0; 184 | } 185 | 186 | .nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check { 187 | opacity: 0; 188 | } 189 | 190 | /* line plus bar */ 191 | .nvd3.nv-linePlusBar .nv-bar rect { 192 | fill-opacity: .75; 193 | } 194 | 195 | .nvd3.nv-linePlusBar .nv-bar rect:hover { 196 | fill-opacity: 1; 197 | } 198 | .nvd3 .nv-groups path.nv-line { 199 | fill: none; 200 | } 201 | 202 | .nvd3 .nv-groups path.nv-area { 203 | stroke: none; 204 | } 205 | 206 | .nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point { 207 | fill-opacity: 0; 208 | stroke-opacity: 0; 209 | } 210 | 211 | .nvd3.nv-scatter.nv-single-point .nv-groups .nv-point { 212 | fill-opacity: .5 !important; 213 | stroke-opacity: .5 !important; 214 | } 215 | 216 | 217 | .with-transitions .nvd3 .nv-groups .nv-point { 218 | transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 219 | -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 220 | -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; 221 | 222 | } 223 | 224 | .nvd3.nv-scatter .nv-groups .nv-point.hover, 225 | .nvd3 .nv-groups .nv-point.hover { 226 | stroke-width: 7px; 227 | fill-opacity: .95 !important; 228 | stroke-opacity: .95 !important; 229 | } 230 | 231 | 232 | .nvd3 .nv-point-paths path { 233 | stroke: #aaa; 234 | stroke-opacity: 0; 235 | fill: #eee; 236 | fill-opacity: 0; 237 | } 238 | 239 | 240 | 241 | .nvd3 .nv-indexLine { 242 | cursor: ew-resize; 243 | } 244 | 245 | /******************** 246 | * SVG CSS 247 | */ 248 | 249 | /******************** 250 | Default CSS for an svg element nvd3 used 251 | */ 252 | svg.nvd3-svg { 253 | -webkit-touch-callout: none; 254 | -webkit-user-select: none; 255 | -khtml-user-select: none; 256 | -ms-user-select: none; 257 | -moz-user-select: none; 258 | user-select: none; 259 | display: block; 260 | width:100%; 261 | height:100%; 262 | } 263 | 264 | /******************** 265 | Box shadow and border radius styling 266 | */ 267 | .nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip { 268 | -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); 269 | -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); 270 | box-shadow: 0 5px 10px rgba(0,0,0,.2); 271 | 272 | -webkit-border-radius: 5px; 273 | -moz-border-radius: 5px; 274 | border-radius: 5px; 275 | } 276 | 277 | 278 | .nvd3 text { 279 | font: normal 12px Arial; 280 | } 281 | 282 | .nvd3 .title { 283 | font: bold 14px Arial; 284 | } 285 | 286 | .nvd3 .nv-background { 287 | fill: white; 288 | fill-opacity: 0; 289 | } 290 | 291 | .nvd3.nv-noData { 292 | font-size: 18px; 293 | font-weight: bold; 294 | } 295 | 296 | 297 | /********** 298 | * Brush 299 | */ 300 | 301 | .nv-brush .extent { 302 | fill-opacity: .125; 303 | shape-rendering: crispEdges; 304 | } 305 | 306 | .nv-brush .resize path { 307 | fill: #eee; 308 | stroke: #666; 309 | } 310 | 311 | 312 | /********** 313 | * Legend 314 | */ 315 | 316 | .nvd3 .nv-legend .nv-series { 317 | cursor: pointer; 318 | } 319 | 320 | .nvd3 .nv-legend .nv-disabled circle { 321 | fill-opacity: 0; 322 | } 323 | 324 | /* focus */ 325 | .nvd3 .nv-brush .extent { 326 | fill-opacity: 0 !important; 327 | } 328 | 329 | .nvd3 .nv-brushBackground rect { 330 | stroke: #000; 331 | stroke-width: .4; 332 | fill: #fff; 333 | fill-opacity: .7; 334 | } 335 | 336 | 337 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick { 338 | stroke-width: 1px; 339 | } 340 | 341 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover { 342 | stroke-width: 2px; 343 | } 344 | 345 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive { 346 | stroke: #2ca02c; 347 | } 348 | 349 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative { 350 | stroke: #d62728; 351 | } 352 | 353 | 354 | .nvd3 .background path { 355 | fill: none; 356 | stroke: #EEE; 357 | stroke-opacity: .4; 358 | shape-rendering: crispEdges; 359 | } 360 | 361 | .nvd3 .foreground path { 362 | fill: none; 363 | stroke-opacity: .7; 364 | } 365 | 366 | .nvd3 .nv-parallelCoordinates-brush .extent 367 | { 368 | fill: #fff; 369 | fill-opacity: .6; 370 | stroke: gray; 371 | shape-rendering: crispEdges; 372 | } 373 | 374 | .nvd3 .nv-parallelCoordinates .hover { 375 | fill-opacity: 1; 376 | stroke-width: 3px; 377 | } 378 | 379 | 380 | .nvd3 .missingValuesline line { 381 | fill: none; 382 | stroke: black; 383 | stroke-width: 1; 384 | stroke-opacity: 1; 385 | stroke-dasharray: 5, 5; 386 | } 387 | .nvd3.nv-pie path { 388 | stroke-opacity: 0; 389 | transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; 390 | -moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; 391 | -webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; 392 | 393 | } 394 | 395 | .nvd3.nv-pie .nv-pie-title { 396 | font-size: 24px; 397 | fill: rgba(19, 196, 249, 0.59); 398 | } 399 | 400 | .nvd3.nv-pie .nv-slice text { 401 | stroke: #000; 402 | stroke-width: 0; 403 | } 404 | 405 | .nvd3.nv-pie path { 406 | stroke: #fff; 407 | stroke-width: 1px; 408 | stroke-opacity: 1; 409 | } 410 | 411 | .nvd3.nv-pie .hover path { 412 | fill-opacity: .7; 413 | } 414 | .nvd3.nv-pie .nv-label { 415 | pointer-events: none; 416 | } 417 | .nvd3.nv-pie .nv-label rect { 418 | fill-opacity: 0; 419 | stroke-opacity: 0; 420 | } 421 | 422 | /* scatter */ 423 | .nvd3 .nv-groups .nv-point.hover { 424 | stroke-width: 20px; 425 | stroke-opacity: .5; 426 | } 427 | 428 | .nvd3 .nv-scatter .nv-point.hover { 429 | fill-opacity: 1; 430 | } 431 | .nv-noninteractive { 432 | pointer-events: none; 433 | } 434 | 435 | .nv-distx, .nv-disty { 436 | pointer-events: none; 437 | } 438 | 439 | /* sparkline */ 440 | .nvd3.nv-sparkline path { 441 | fill: none; 442 | } 443 | 444 | .nvd3.nv-sparklineplus g.nv-hoverValue { 445 | pointer-events: none; 446 | } 447 | 448 | .nvd3.nv-sparklineplus .nv-hoverValue line { 449 | stroke: #333; 450 | stroke-width: 1.5px; 451 | } 452 | 453 | .nvd3.nv-sparklineplus, 454 | .nvd3.nv-sparklineplus g { 455 | pointer-events: all; 456 | } 457 | 458 | .nvd3 .nv-hoverArea { 459 | fill-opacity: 0; 460 | stroke-opacity: 0; 461 | } 462 | 463 | .nvd3.nv-sparklineplus .nv-xValue, 464 | .nvd3.nv-sparklineplus .nv-yValue { 465 | stroke-width: 0; 466 | font-size: .9em; 467 | font-weight: normal; 468 | } 469 | 470 | .nvd3.nv-sparklineplus .nv-yValue { 471 | stroke: #f66; 472 | } 473 | 474 | .nvd3.nv-sparklineplus .nv-maxValue { 475 | stroke: #2ca02c; 476 | fill: #2ca02c; 477 | } 478 | 479 | .nvd3.nv-sparklineplus .nv-minValue { 480 | stroke: #d62728; 481 | fill: #d62728; 482 | } 483 | 484 | .nvd3.nv-sparklineplus .nv-currentValue { 485 | font-weight: bold; 486 | font-size: 1.1em; 487 | } 488 | /* stacked area */ 489 | .nvd3.nv-stackedarea path.nv-area { 490 | fill-opacity: .7; 491 | stroke-opacity: 0; 492 | transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; 493 | -moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; 494 | -webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; 495 | } 496 | 497 | .nvd3.nv-stackedarea path.nv-area.hover { 498 | fill-opacity: .9; 499 | } 500 | 501 | 502 | .nvd3.nv-stackedarea .nv-groups .nv-point { 503 | stroke-opacity: 0; 504 | fill-opacity: 0; 505 | } 506 | 507 | 508 | .nvtooltip { 509 | position: absolute; 510 | background-color: rgba(255,255,255,1.0); 511 | color: rgba(0,0,0,1.0); 512 | padding: 1px; 513 | border: 1px solid rgba(0,0,0,.2); 514 | z-index: 10000; 515 | display: block; 516 | 517 | font-family: Arial; 518 | font-size: 13px; 519 | text-align: left; 520 | pointer-events: none; 521 | 522 | white-space: nowrap; 523 | 524 | -webkit-touch-callout: none; 525 | -webkit-user-select: none; 526 | -khtml-user-select: none; 527 | -moz-user-select: none; 528 | -ms-user-select: none; 529 | user-select: none; 530 | } 531 | 532 | .nvtooltip { 533 | background: rgba(255,255,255, 0.8); 534 | border: 1px solid rgba(0,0,0,0.5); 535 | border-radius: 4px; 536 | } 537 | 538 | /*Give tooltips that old fade in transition by 539 | putting a "with-transitions" class on the container div. 540 | */ 541 | .nvtooltip.with-transitions, .with-transitions .nvtooltip { 542 | transition: opacity 50ms linear; 543 | -moz-transition: opacity 50ms linear; 544 | -webkit-transition: opacity 50ms linear; 545 | 546 | transition-delay: 200ms; 547 | -moz-transition-delay: 200ms; 548 | -webkit-transition-delay: 200ms; 549 | } 550 | 551 | .nvtooltip.x-nvtooltip, 552 | .nvtooltip.y-nvtooltip { 553 | padding: 8px; 554 | } 555 | 556 | .nvtooltip h3 { 557 | margin: 0; 558 | padding: 4px 14px; 559 | line-height: 18px; 560 | font-weight: normal; 561 | background-color: rgba(247,247,247,0.75); 562 | color: rgba(0,0,0,1.0); 563 | text-align: center; 564 | 565 | border-bottom: 1px solid #ebebeb; 566 | 567 | -webkit-border-radius: 5px 5px 0 0; 568 | -moz-border-radius: 5px 5px 0 0; 569 | border-radius: 5px 5px 0 0; 570 | } 571 | 572 | .nvtooltip p { 573 | margin: 0; 574 | padding: 5px 14px; 575 | text-align: center; 576 | } 577 | 578 | .nvtooltip span { 579 | display: inline-block; 580 | margin: 2px 0; 581 | } 582 | 583 | .nvtooltip table { 584 | margin: 6px; 585 | border-spacing:0; 586 | } 587 | 588 | 589 | .nvtooltip table td { 590 | padding: 2px 9px 2px 0; 591 | vertical-align: middle; 592 | } 593 | 594 | .nvtooltip table td.key { 595 | font-weight: normal; 596 | } 597 | .nvtooltip table td.key.total { 598 | font-weight: bold; 599 | } 600 | .nvtooltip table td.value { 601 | text-align: right; 602 | font-weight: bold; 603 | } 604 | 605 | .nvtooltip table tr.highlight td { 606 | padding: 1px 9px 1px 0; 607 | border-bottom-style: solid; 608 | border-bottom-width: 1px; 609 | border-top-style: solid; 610 | border-top-width: 1px; 611 | } 612 | 613 | .nvtooltip table td.legend-color-guide div { 614 | width: 8px; 615 | height: 8px; 616 | vertical-align: middle; 617 | } 618 | 619 | .nvtooltip table td.legend-color-guide div { 620 | width: 12px; 621 | height: 12px; 622 | border: 1px solid #999; 623 | } 624 | 625 | .nvtooltip .footer { 626 | padding: 3px; 627 | text-align: center; 628 | } 629 | 630 | .nvtooltip-pending-removal { 631 | pointer-events: none; 632 | display: none; 633 | } 634 | 635 | 636 | /**** 637 | Interactive Layer 638 | */ 639 | .nvd3 .nv-interactiveGuideLine { 640 | pointer-events:none; 641 | } 642 | .nvd3 line.nv-guideline { 643 | stroke: #ccc; 644 | } 645 | -------------------------------------------------------------------------------- /dashboard.js: -------------------------------------------------------------------------------- 1 | Array.min = function(array) { 2 | return Math.min.apply(Math, array); 3 | }; 4 | 5 | Array.max = function(array ) { 6 | return Math.max.apply(Math, array); 7 | }; 8 | 9 | Date.prototype.stdTimezoneOffset = function() { 10 | var jan = new Date(this.getFullYear(), 0, 1); 11 | var jul = new Date(this.getFullYear(), 6, 1); 12 | return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); 13 | } 14 | 15 | Date.prototype.dst = function() { 16 | return this.getTimezoneOffset() < this.stdTimezoneOffset(); 17 | } 18 | 19 | var Dashboard = function(rootFolder, experimentId, placeholder, options) { 20 | this.rootFolder = rootFolder; 21 | var dashboard = this; 22 | var place = d3.select(placeholder); 23 | // Default options. 24 | if (!options.xKey) { 25 | options.xKey = "step"; 26 | } 27 | if (!options.maxToDisplay) { 28 | options.maxToDisplay = 10; 29 | } 30 | if (!options.maxLines) { 31 | options.maxLines = 500; 32 | } 33 | if (!options.maxDatapoints) { 34 | options.maxDatapoints = 500; 35 | } 36 | if (!options.autoRefresh) { 37 | options.autoRefresh = false; 38 | } 39 | 40 | // Add title. 41 | place.append("div") 42 | .attr("class", "title") 43 | .text("Deep Dashboard"); 44 | 45 | // Add settings panel. 46 | place.append("div") 47 | .attr("id", "settings") 48 | .call(function() { 49 | d3.select("#settings") 50 | .append("div") 51 | .text("Auto-refresh: ") 52 | .append("input") 53 | .attr("type", "checkbox") 54 | .attr("id", "check_auto_refresh") 55 | .call(function() { 56 | document.getElementById("check_auto_refresh") 57 | .onchange = dashboard.autoRefresh.bind(dashboard); 58 | 59 | document.getElementById("check_auto_refresh") 60 | .checked = options.autoRefresh; 61 | }); 62 | d3.select("#settings") 63 | .append("div") 64 | .text("Display on x-axis: ") 65 | .append("select") 66 | .attr("id", "select_xaxis") 67 | .call(function() { 68 | var opt = {} 69 | opt.step = d3.select("#select_xaxis") 70 | .append("option") 71 | .attr("value", "step") 72 | .text("step"); 73 | opt.abs_time = d3.select("#select_xaxis") 74 | .append("option") 75 | .attr("value", "abs_time") 76 | .text("absolute time"); 77 | opt.rel_time = d3.select("#select_xaxis") 78 | .append("option") 79 | .attr("value", "rel_time") 80 | .text("relative time"); 81 | opt[options.xKey].attr("selected", true); 82 | document.getElementById("select_xaxis") 83 | .onchange = dashboard.refreshChart.bind(dashboard); 84 | }); 85 | }); 86 | 87 | if (experimentId) { 88 | // d3.csv(this.rootFolder + "catalog", function(error, csvData) { 89 | // csvData.forEach(function(elem, idx, arr) { 90 | // if (elem.id == experimentId) { 91 | 92 | // } 93 | // }); 94 | // }); 95 | this.addExperiment(place, experimentId, false); 96 | } else { 97 | d3.csv(this.rootFolder + "catalog", function(error, csvData) { 98 | if (error) throw error; 99 | // TODO: sort by last modified date. 100 | displen = Math.min(csvData.length, options.maxToDisplay); 101 | if (displen != csvData.length) { 102 | csvData = csvData.slice(0, displen); 103 | } 104 | csvData.reverse(); 105 | csvData.forEach(function(elem, idx, arr) { 106 | setTimeout(function() { 107 | dashboard.addExperiment(place, elem.id, true, elem.desc); 108 | }, 50 * idx); 109 | }); 110 | }); 111 | } 112 | 113 | this.allPanels = {}; 114 | this.options = options; 115 | 116 | // Set event listener. 117 | this.active = true; 118 | window.addEventListener("mousemove", function() { 119 | dashboard.active = true; 120 | }, false); 121 | window.addEventListener("blur", function() { 122 | dashboard.active = false; 123 | }, false); 124 | }; 125 | 126 | Dashboard.prototype.autoRefresh = function() { 127 | this.options.autoRefresh = 128 | document.getElementById("check_auto_refresh").checked; 129 | } 130 | 131 | Dashboard.prototype.getXKeyFormat = function(xKey) { 132 | var floatFormatter = d3.format(",.2f"); 133 | var absTimeFormatter = d3.time.format("%Y/%m/%d %H:%M"); 134 | var relTimeFormatter = d3.time.format("D%d %H:%M"); 135 | if (this.options.xKey === "step") { 136 | return d3.format(",d"); 137 | } else if (this.options.xKey === "abs_time") { 138 | return function(d) { 139 | return absTimeFormatter(new Date(d)); 140 | }; 141 | } else if (this.options.xKey === "rel_time") { 142 | return function(d) { 143 | return relTimeFormatter(new Date(d)); 144 | }; 145 | } 146 | }; 147 | 148 | Dashboard.prototype.getYKeyFormat = function(yKey) { 149 | var floatFormatter = d3.format(",.4f"); 150 | return function(d) { 151 | return floatFormatter(d); 152 | }; 153 | }; 154 | 155 | Dashboard.prototype.getXAxis = function(xKey) { 156 | if (xKey === "step") { 157 | return "step"; 158 | } else if (xKey === "abs_time" || xKey === "rel_time") { 159 | return "time"; 160 | } 161 | } 162 | 163 | Dashboard.prototype.addExperiment = function(placeholder, experimentId, titleOnly, subtitle) { 164 | var experimentFolder = this.rootFolder + experimentId + "/"; 165 | var dashboard = this; 166 | d3.csv(experimentFolder + "catalog", function(error, csvData) { 167 | if (error) { 168 | d3.select("#content") 169 | .append("h1") 170 | .html("Experiment " + experimentId + " Not Found"); 171 | throw error; 172 | } 173 | var experimentName = dashboard.getPanelId(experimentId); 174 | var divId = "exp_" + experimentName; 175 | if (typeof subtitle == 'undefined') { 176 | subtitle = ''; 177 | } 178 | // Set title. 179 | placeholder 180 | .append("div") 181 | .attr("id", divId) 182 | .attr("class", "experiment") 183 | .append("h1") 184 | .html(" " + experimentId + "") 185 | .append("h3") 186 | .html(subtitle) 187 | .call(function(){ 188 | if (!titleOnly) { 189 | d3.select("#" + divId) 190 | .append("div") 191 | .attr("id", "menu_" + experimentName) 192 | .append("h2") 193 | .text("Navigation").call(function() { 194 | for (var ii = 0; ii < csvData.length; ++ii) { 195 | var fname = experimentFolder + csvData[ii].filename; 196 | var name = csvData[ii].name; 197 | d3.select("#menu_" + experimentName) 198 | .append("span") 199 | .html("

" + 201 | name + "

"); 202 | } 203 | }); 204 | for (var ii = 0; ii < csvData.length; ++ii) { 205 | var fname = experimentFolder + csvData[ii].filename; 206 | var name = csvData[ii].name; 207 | var place = d3.select("#" + divId); 208 | var panel = dashboard.addPanel(place, fname, name); 209 | panel.type = csvData[ii].type; 210 | 211 | if (!csvData[ii].type) { 212 | csvData[ii].type = "csv"; 213 | } 214 | if (csvData[ii].type === "csv") { 215 | dashboard.addChart(panel); 216 | } else if (csvData[ii].type === "plain") { 217 | dashboard.addPlainLog(panel); 218 | } else if (csvData[ii].type === "image") { 219 | dashboard.addImage(panel); 220 | } else if (csvData[ii].type === "histogram") { 221 | dashboard.addHistogram(panel); 222 | } else { 223 | console.log("Unknown type " + csvData[ii].type) 224 | } 225 | } 226 | } 227 | }); 228 | 229 | 230 | }); 231 | }; 232 | 233 | Dashboard.prototype.getSubsampleRate = function(len) { 234 | if (len < this.options.maxDatapoints) { 235 | return 1; 236 | } else { 237 | return Math.floor(len / this.options.maxDatapoints); 238 | } 239 | }; 240 | 241 | Dashboard.prototype.parseData = function(csvData) { 242 | // Look up the data key. 243 | var yKeys = {}; 244 | var data = []; 245 | var col = 0; 246 | var colors = ["#2980b9", "#16a085", "#c0392b", "#8e44ad", "#d35400", 247 | "#2c3e50", "#3d315b", "#708b75", "#011627", "#9bc53d", "#5bc0eb", "#fde74c", 248 | "#e55934", "#da7921"]; 249 | for (var key in csvData[0]) { 250 | if (key !== "step" && key !== "time") { 251 | yKeys[key] = col; 252 | data.push({ 253 | values: [], 254 | key: key, 255 | // color: colors[col % colors.length] 256 | }); 257 | col++; 258 | } 259 | } 260 | 261 | var d = new Date() 262 | var offset = (d.getTimezoneOffset()) * 60000; 263 | if (d.dst()) { 264 | offset += 3600000 265 | } 266 | var time_start = Date.parse(csvData[0].time); 267 | var count = {}; 268 | for (var yKey in yKeys) { 269 | count[yKey] = 0; 270 | } 271 | 272 | for (var ii = 0; ii < csvData.length; ++ii) { 273 | for (var yKey in yKeys) { 274 | if (csvData[ii][yKey] !== "") { 275 | count[yKey]++; 276 | } 277 | } 278 | } 279 | var subsample = {}; 280 | for (var yKey in yKeys){ 281 | subsample[yKey] = this.getSubsampleRate(count[yKey]); 282 | count[yKey] = 0; 283 | } 284 | 285 | var getXVal = function(data, xKey) { 286 | if (xKey === "abs_time") { 287 | return Date.parse(data.time); 288 | } else if (xKey === "rel_time") { 289 | return Date.parse(data.time) - time_start + offset; 290 | } else { 291 | return data[xKey]; 292 | } 293 | } 294 | for (var ii = 0; ii < csvData.length; ++ii) { 295 | for (var yKey in yKeys) { 296 | if (csvData[ii][yKey] !== "") { 297 | if (count[yKey] % subsample[yKey] == 0) { 298 | var col = yKeys[yKey]; 299 | data[col].values.push({ 300 | "x": getXVal(csvData[ii], this.options.xKey), 301 | "y": csvData[ii][yKey] 302 | }); 303 | } 304 | count[yKey]++; 305 | } 306 | } 307 | } 308 | return data; 309 | }; 310 | 311 | Dashboard.prototype.getXYLimit = function(data) { 312 | var minX, maxX, minY, maxY; 313 | for (var ii = 0; ii < data.length; ++ii) { 314 | var xValues = data[ii].values.map(function(item) {return item.x}); 315 | var yValues = data[ii].values.map(function(item) {return item.y}); 316 | if (ii == 0){ 317 | minX = Array.min(xValues); 318 | maxX = Array.max(xValues); 319 | minY = Array.min(yValues); 320 | maxY = Array.max(yValues); 321 | } else { 322 | minX = Math.min(minX, Array.min(xValues)); 323 | maxX = Math.max(maxX, Array.max(xValues)); 324 | minY = Math.min(minY, Array.min(yValues)); 325 | maxY = Math.max(maxY, Array.max(yValues)); 326 | } 327 | } 328 | return [minX, maxX, minY, maxY]; 329 | }; 330 | 331 | Dashboard.prototype.updateChart = function(panel) { 332 | var chart = panel.chart; 333 | var dashboard = this; 334 | 335 | d3.csv(panel.filename, function(error, csvData) { 336 | if (error) throw error; 337 | var data = dashboard.parseData(csvData); 338 | var limits = dashboard.getXYLimit(data); 339 | console.log(chart) 340 | chart.xDomain([limits[0], limits[1]]).yDomain([limits[2], limits[3]]); 341 | d3.select("#svg_" + panel.id).datum(data); 342 | chart.xAxis.axisLabel(dashboard.getXAxis(dashboard.options.xKey)) 343 | .tickFormat(dashboard.getXKeyFormat(dashboard.options.xKey)); 344 | if(document.getElementById("check_ylog_" + panel.id).checked) { 345 | chart.yScale(d3.scale.log()); 346 | } else { 347 | chart.yScale(d3.scale.linear()); 348 | } 349 | chart.update(); 350 | dashboard.updateLastModified(panel, false); 351 | }); 352 | }; 353 | 354 | Dashboard.prototype.refreshChart = function() { 355 | this.options.xKey = $( "#select_xaxis" ).val(); 356 | for (var panelId in this.allPanels) { 357 | var panel = this.allPanels[panelId]; 358 | if (panel.type === "csv") { 359 | this.updateChart(panel); 360 | } 361 | } 362 | }; 363 | 364 | Dashboard.prototype.updateLastModified = function(panel, add) { 365 | $.ajax({ 366 | type: "GET", 367 | async: true, 368 | timeout: 5000, 369 | url: panel.filename, 370 | dataType : "text", 371 | success: function(data, textStatus, request){ 372 | var lastModified = request.getResponseHeader("Last-Modified"); 373 | panel.lastModified = lastModified; 374 | if (add) { 375 | d3.select("#panel_" + panel.id) 376 | .append("div") 377 | .attr("id", "ts_" + panel.id) 378 | .attr("class", "timestamp") 379 | .html("Last updated: " + lastModified); 380 | } else { 381 | d3.select("#ts_" + panel.id) 382 | .html("Last updated: " + lastModified); 383 | } 384 | }, 385 | error: function(e) {throw e;} 386 | }); 387 | }; 388 | 389 | Dashboard.prototype.addChart = function(panel) { 390 | var dashboard = this; 391 | nv.addGraph(function() { 392 | // Load data 393 | d3.csv(panel.filename, function(error, csvData) { 394 | if (error) throw error; 395 | var data = dashboard.parseData(csvData); 396 | var limits = dashboard.getXYLimit(data); 397 | 398 | // Initialize chart. 399 | var chart = nv.models.lineChart() 400 | .options({ 401 | transitionDuration: 300, 402 | useInteractiveGuideline: true 403 | }) 404 | .xDomain([limits[0], limits[1]]) 405 | .yDomain([limits[2], limits[3]]); 406 | 407 | chart.xAxis 408 | .axisLabel(dashboard.getXAxis(dashboard.options.xKey)) 409 | .tickFormat(dashboard.getXKeyFormat(dashboard.options.xKey)); 410 | 411 | chart.yAxis 412 | .axisLabel("") 413 | .tickFormat(dashboard.getYKeyFormat(dashboard.options.yKey)); 414 | 415 | panel.placeholder.append("div") 416 | .attr("id", "chart_panel_" + panel.id) 417 | .attr("class", "chart_panel") 418 | .attr("id", "svg_" + panel.id) 419 | .append("svg") 420 | .datum(data) 421 | .call(chart) 422 | .call(function() { 423 | dashboard.updateLastModified(panel, true); 424 | }) 425 | .call(function() { 426 | panel.placeholder.append("div") 427 | .attr("class", "chart_control") 428 | .text("y log scale") 429 | .append("input") 430 | .attr("type", "checkbox") 431 | .attr("id", "check_ylog_" + panel.id) 432 | .call(function() { 433 | document.getElementById("check_ylog_" + panel.id) 434 | .onchange = function() { 435 | dashboard.updateChart(panel); 436 | }.bind(dashboard); 437 | }); 438 | }); 439 | panel.chart = chart; 440 | dashboard.updateChart(panel); 441 | setInterval(dashboard.schedule( 442 | function() {dashboard.updateChart(panel)}), 443 | dashboard.options.timeout); 444 | }); 445 | }); 446 | }; 447 | 448 | 449 | Dashboard.prototype.addImage = function(panel) { 450 | var dashboard = this; 451 | panel.placeholder.append("img") 452 | .attr("class", "img_log") 453 | .attr("src", panel.filename) 454 | .attr("id", "img_" + panel.id) 455 | .call(function() { 456 | dashboard.updateLastModified(panel, true); 457 | }); 458 | 459 | var update = function() { 460 | var date = new Date(); 461 | var img = d3.select("#" + "img_" + panel.id)[0][0]; 462 | img.src = panel.filename + "?ts=" + date.getTime(); 463 | dashboard.updateLastModified(panel, false); 464 | }; 465 | update(); 466 | setInterval(dashboard.schedule(update), dashboard.options.timeout); 467 | }; 468 | 469 | Dashboard.prototype.getPanelId = function(filename) { 470 | var filenameArr = filename.split("/"); 471 | var filename2 = filenameArr[filenameArr.length - 1]; 472 | var filename2Arr = filename2.split("."); 473 | var panelId = filename2Arr[0]; 474 | panelId = panelId.replace(/ /g, "_").replace(/\(/g, "_").replace(/\)/g, "_").replace(/\//g, "_").replace(/-/g, "_"); 475 | return panelId; 476 | }; 477 | 478 | Dashboard.prototype.addPanel = function(placeholder, filename, name) { 479 | var panel = {}; 480 | panel.id = this.getPanelId(filename); 481 | panel.filename = filename; 482 | panel.name = name; 483 | placeholder.append("div") 484 | .attr("id", "panel_" + panel.id) 485 | .attr("class", "panel"); 486 | panelplace = d3.select("#panel_" + panel.id); 487 | // panelplace.append("h3").html(name + " ^"); 488 | panelplace.append("h2").html(name); 489 | panel.placeholder = panelplace; 490 | this.allPanels[panel.id] = panel; 491 | 492 | return panel; 493 | }; 494 | 495 | // Add a raw log panel. 496 | Dashboard.prototype.addPlainLog = function(panel, timeout) { 497 | var dashboard = this; 498 | panel.placeholder.append("textarea") 499 | .attr("class", "raw_log") 500 | .attr("cols", "94") 501 | .attr("rows", "20") 502 | .attr("id", "textarea_" + panel.id) 503 | .call(function() { 504 | dashboard.updateLastModified(panel, true); 505 | }); 506 | 507 | panel.placeholder.append("div") 508 | .attr("id", "check_div_" + panel.id) 509 | .attr("class", "raw_log") 510 | .call(function() { 511 | var div = d3.select("#check_div_" + panel.id); 512 | div.append("input") 513 | .attr("type", "checkbox") 514 | .attr("id", "check_" + panel.id) 515 | .attr("checked", true); 516 | div.append("label") 517 | .text("Auto-scroll to bottom"); 518 | }); 519 | 520 | var update = function() { 521 | $.ajax({ 522 | type: "GET", 523 | async: true, 524 | timeout: 5000, 525 | url: panel.filename, 526 | dataType : "text", 527 | success: function(data, textStatus, request){ 528 | var lines = data.split("\n"); 529 | // Maximum 500 lines. 530 | var lines = lines.slice( 531 | Math.max(0, lines.length - dashboard.options.maxLines)); 532 | var log = lines.join("\n") 533 | d3.select("#textarea_" + panel.id) 534 | .html(log) 535 | .call(function() { 536 | dashboard.updateLastModified(panel, false); 537 | }); 538 | 539 | 540 | // Scroll to bottom. 541 | if (document.getElementById("check_" + panel.id).checked) { 542 | var textarea = document.getElementById( 543 | "textarea_" + panel.id); 544 | textarea.scrollTop = textarea.scrollHeight; 545 | } 546 | 547 | }, 548 | error: function(e) {throw e;} 549 | }); 550 | }; 551 | update(); 552 | setInterval(dashboard.schedule(update), dashboard.options.timeout); 553 | }; 554 | 555 | Dashboard.prototype.parseHistogram = function(data) { 556 | function hist(data, dmin, dmax, nbin) { 557 | if (typeof dmin == 'undefined') { 558 | dmin = Array.min(data); 559 | } 560 | if (typeof dmax == 'undefined') { 561 | dmax = Array.max(data) + 0.0001; 562 | } 563 | if (!nbin) { 564 | nbin = data.length / 100; 565 | } 566 | var bins = []; 567 | step = (dmax - dmin) / nbin; 568 | for (var ii = 0; ii < nbin; ii++) { 569 | bins.push({ 570 | x: ii * step + dmin, 571 | y: 0 572 | }); 573 | } 574 | function addtobin(x) { 575 | idx = Math.floor((x - dmin) / step); 576 | bins[idx].y += 1; 577 | } 578 | data.map(addtobin); 579 | return bins; 580 | } 581 | 582 | var series = data.split('\n'); 583 | var dmax = 0; 584 | var dmin = 0; 585 | var keys = []; 586 | var values = []; 587 | for (var ii = 0; ii < series.length; ii++) { 588 | var sdata = series[ii].split(','); 589 | if (sdata.length > 1) { 590 | keys.push(sdata[0]); 591 | var val = []; 592 | for (var jj = 1; jj < sdata.length; jj++) { 593 | val.push(parseFloat(sdata[jj])); 594 | } 595 | values.push(val); 596 | if (ii == 0) { 597 | dmax = Array.max(val) + 0.0001; 598 | dmin = Array.min(val); 599 | } else { 600 | dmax = Math.max(Array.max(val) + 0.0001, dmax); 601 | dmin = Math.min(Array.min(val), dmin); 602 | } 603 | } 604 | } 605 | 606 | var parsed_data = []; 607 | var num_max = 0; 608 | for (var ii = 0; ii < keys.length; ii++) { 609 | parsed_data[ii] = {}; 610 | parsed_data[ii].key = keys[ii]; 611 | 612 | var bins = hist(values[ii], dmin, dmax); 613 | for (var jj = 0; jj < bins.length; jj++) { 614 | bins[jj].series = ii; 615 | num_max = Math.max(bins[jj].y, num_max) 616 | } 617 | parsed_data[ii].values = bins; 618 | parsed_data[ii].xmax = dmax; 619 | parsed_data[ii].xmin = dmin; 620 | parsed_data[ii].ymax = num_max; 621 | parsed_data[ii].ymin = 0; 622 | } 623 | return parsed_data; 624 | }; 625 | 626 | Dashboard.prototype.updateHistogram = function(panel) { 627 | var chart = panel.chart; 628 | var dashboard = this; 629 | 630 | d3.text(panel.filename, function(error, data) { 631 | if (error) throw error; 632 | var parsed_data = dashboard.parseHistogram(data); 633 | // chart.xDomain([parsed_data[0].xmin, parsed_data[0].xmax]) 634 | // .yDomain([parsed_data[0].ymin, parsed_data[0].ymax]); 635 | d3.select("#svg_" + panel.id).datum(parsed_data); 636 | // chart.xAxis.tickFormat(d3.format(',.2f')); 637 | chart.update(); 638 | dashboard.updateLastModified(panel, false); 639 | }); 640 | }; 641 | 642 | Dashboard.prototype.addHistogram = function(panel) { 643 | var dashboard = this; 644 | nv.addGraph(function() { 645 | 646 | d3.text(panel.filename, function(error, data) { 647 | parsed_data = dashboard.parseHistogram(data); 648 | 649 | var chart = nv.models.multiBarChart() 650 | .reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered. 651 | .rotateLabels(0) //Angle to rotate x-axis labels. 652 | .showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode. 653 | .groupSpacing(0.1) //Distance between each group of bars. 654 | ; 655 | 656 | chart.xAxis 657 | .tickFormat(d3.format(',.2f')); 658 | 659 | chart.yAxis 660 | .tickFormat(d3.format(',.2f')); 661 | 662 | panel.placeholder.append("div") 663 | .attr("id", "chart_panel_" + panel.id) 664 | .attr("class", "chart_panel") 665 | .append("svg") 666 | .attr("id", "svg_" + panel.id) 667 | // .datum(getDataToy()) 668 | .datum(parsed_data) 669 | .call(chart) 670 | .call(function() { 671 | dashboard.updateLastModified(panel, true); 672 | } 673 | ); 674 | panel.chart = chart; 675 | dashboard.updateHistogram(panel); 676 | setInterval(dashboard.schedule( 677 | function() {dashboard.updateHistogram(panel)}), 678 | dashboard.options.timeout); 679 | }); 680 | 681 | }); 682 | }; 683 | 684 | Dashboard.prototype.schedule = function(callback) { 685 | var dashboard = this; 686 | return function() { 687 | if (dashboard.active && dashboard.options.autoRefresh) { 688 | callback(); 689 | } 690 | }; 691 | }; 692 | -------------------------------------------------------------------------------- /lib/jquery-2.1.4.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ 2 | !function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ 3 | return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("