DA-Transformer / global.js
hzhwcmhf's picture
init
976cf27
() => {
var script = document.createElement("script");
script.src = "https://visjs.github.io/vis-network/standalone/umd/vis-network.min.js";
document.head.appendChild(script);
// var current_options;
// var node_pass_prob, prelen, max_path, node_tokens, node_probs, links, tarlen;
// var visible_nodes, map_max_path;
// var visible_edges, sorted_links_out, sorted_links_in;
// var nodes, edges, network;
// var disable_edge_select;
showing_node = undefined;
showing_edge = undefined;
function obtain_node_object(node_id) {
var title = "Node " + node_id.toString() + " (Pass Prob = " + node_pass_prob[node_id].toFixed(2) + ")\n";
title += "==========\n"
for(var j=0;j<5;j++){
if(node_probs[node_id][j]> 0.01){
title += node_tokens[node_id][j] + "\t" + node_probs[node_id][j].toFixed(2) + "\n";
}
}
var currentNode = {"id": node_id, "label": node_tokens[node_id][0], "value":node_pass_prob[node_id], "title": title};
if (current_options.show_node_detail){
currentNode.label = title;
}
if(map_max_path[node_id] !== undefined){
currentNode.x = map_max_path[node_id].x;
currentNode.y = map_max_path[node_id].y;
currentNode.fixed = true;
currentNode.mass = 5;
currentNode.color = {border: "red", background: "orange", highlight: {border: "red", background: "#ffcc66"}};
}
return currentNode;
}
function set_node_visibility(node_id, flag){
if(visible_nodes[node_id] == flag) return;
visible_nodes[node_id] = flag;
if(flag){
nodes.add(obtain_node_object(node_id));
}else{
nodes.remove(node_id);
}
}
function update_visible_nodes(clear_state=false){
if(typeof visible_nodes === "undefined" || clear_state){
visible_nodes = [...Array(prelen)].map((_, __) => false); // size: [prelen]
map_max_path = {}; // {node_id -> position on max_path}
var accumulated_x = 0;
for (var i=0;i<tarlen;i++){
accumulated_x += node_tokens[max_path[i]][0].length * 5
var y = Math.floor(Math.random() * 3) * 100 - 100;
map_max_path[max_path[i]] = {position: i, x: accumulated_x, y:y};
accumulated_x += node_tokens[max_path[i]][0].length * 5 + 100;
}
nodes = new vis.DataSet();
}
for (var i=0;i<prelen;i++){
if(node_pass_prob[i] >= current_options.minimum_node_pass_prob || map_max_path[i] !== undefined){
set_node_visibility(i, true);
}else{
set_node_visibility(i, false);
}
}
}
function update_node_details(){
for(var i=0;i<prelen;i++) if (visible_nodes[i]){
currentNode = obtain_node_object(i);
nodes.updateOnly(currentNode);
}
}
function obtain_edge_object(i, j){
var edge_id = i.toString() + "-" + j.toString();
var label = links[i][j].toFixed(2);
var pass_label = (node_pass_prob[i] * links[i][j]).toFixed(2);
var title = "From Node " + i.toString() + " to Node " + j.toString() + "\n" + "Transition Probability:" + label + "\nPassing Probability:" + pass_label;
var currentEdge = {id: edge_id,
from: i, to: j, value: links[i][j] * node_pass_prob[i], title: title};
if (map_max_path[i] !== undefined && map_max_path[j] !== undefined && map_max_path[i].position + 1 == map_max_path[j].position){
currentEdge.color = "red";
}
if(current_options.show_edge_label){
currentEdge.label = label;
}else{
currentEdge.label = " ";
}
return currentEdge;
}
function set_edge_visibility(i, j, flag){
if(visible_edges[i][j] == flag) return;
visible_edges[i][j] = flag;
if(flag){
edges.add(obtain_edge_object(i, j));
}else{
var edge_id = i.toString() + "-" + j.toString();
edges.remove(edge_id);
}
}
function update_visible_edges(clear_state=false){
if(typeof visible_edges === "undefined" || clear_state){
visible_edges = [...Array(prelen)].map((_, __) => {return [...Array(prelen)].map((_, __) => false);}); // size: [prelen, prelen]
sorted_links_out = []; // size: [prelen, prelen]
for (var i=0;i<prelen;i++){
// sort the out edge according to their prob
sorted_links_out.push(links[i].map((val, idx) => {return {'idx': idx, 'val': val};}).
sort((v1, v2) => v2.val - v1.val)
);
}
sorted_links_in = []; // size: [prelen, prelen], element {idx, val}, from big to small
for (var i=0;i<prelen;i++){
links_in = []
for(var j=0;j<prelen;j++) links_in.push({idx: j, val: links[j][i] * node_pass_prob[j]})
sorted_links_in.push(links_in.sort((v1, v2) => v2.val - v1.val));
}
edges = new vis.DataSet();
}
var next_visible_edges = [...Array(prelen)].map((_, __) => {return [...Array(prelen)].map((_, __) => false);}); // size: [prelen, prelen]
var links_in_num = [...Array(prelen)].map((_, __) => 0);
for (var i=0;i<prelen - 1;i++){
if(!visible_nodes[i]) continue;
// select visible out edge of node i
var left_visible_edge_num = current_options.max_out_edge_num;
var left_visible_edge_prob = current_options.max_out_edge_prob;
for(var j=0; j<prelen;j++){
var idx = sorted_links_out[i][j]['idx'];
if (sorted_links_out[i][j]['val'] < current_options.minimum_edge_prob) break;
if (visible_nodes[idx]){
links_in_num[idx]++;
next_visible_edges[i][idx] = true;
left_visible_edge_num--;
left_visible_edge_prob -= sorted_links_out[i][j]['val'];
if (left_visible_edge_num==0 || left_visible_edge_prob < 0){
break;
}
}
}
}
if(current_options.force_in_edge){
// add at least one in edge per node
for (var i=0;i<prelen;i++){
if(i == 0 || !visible_nodes[i] || links_in_num[i] > 0) continue;
for(var j=0; j<prelen;j++){
var idx = sorted_links_in[i][j]['idx'];
if (visible_nodes[idx]){
next_visible_edges[idx][i] = true;
break;
}
}
}
}
for(var i=0;i<prelen;i++){
for(var j=i+1;j<prelen;j++){
set_edge_visibility(i, j, next_visible_edges[i][j]);
}
}
}
function update_edge_label(){
for(var i=0;i<prelen;i++){
for(var j=i+1;j<prelen;j++) if (visible_edges[i][j]){
currentEdge = obtain_edge_object(i, j);
edges.updateOnly(currentEdge);
}
}
}
function customScalingFunction(min,max,total,value) {
min = 0;
var scale = 1 / (max - min);
return Math.max(0,(value - min)*scale);
}
function escapeHtml(unsafe)
{
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function get_jumpable_node(idx){
if(visible_nodes[idx]){
return "<a href=\"javascript:network.selectNodes(["+ idx.toString() +"]);show_hint_node(" + idx.toString() + ");\">" + idx.toString() + "</a>";
}else{
return "<a class=\"invisible\" href=\"javascript:show_hint_node(["+ idx.toString() +"]);network.unselectAll();\">" + idx.toString() + "</a>";
}
}
function get_jumpable_edge(i, j, label){
var edge_id = i.toString() + "-" + j.toString();
if(visible_edges[i][j]){
return "<a href=\"javascript:network.selectEdges(['"+ edge_id +"']);show_hint_edge('" + edge_id + "');\">" + label + "</a>";
}else{
return "<a class=\"invisible\" href=\"javascript:show_hint_edge('" + edge_id + "');network.unselectAll();\">" + label + "</a>";
}
}
show_hint_node = function(node_id){
showing_node = node_id;
showing_edge = undefined;
var title = "<p>You selected <b>Node " + node_id.toString() + "</b> ";
if (visible_nodes[node_id]){
title += "(<a href=\"javascript:network.fit({nodes:[" + node_id.toString() + "],animation:true});\">Find Me!</a>). "
}else{
title += "(Not shown). "
}
title += "Passing Probability: <b>" + node_pass_prob[node_id].toFixed(2) + "</b>. You can click the links below to jump to other edges or nodes.</p>";
document.getElementById("hintsupper").innerHTML = title;
title = "<table><thead><tr><th>Rank</th><th>Candidate</th><th>Probability</th></tr></thead><tbody>";
for (var j=0;j<5;j++){
title += "<tr><td>#" + (j+1).toString() + "</td><td>" + escapeHtml(node_tokens[node_id][j]) + "</td><td>" + node_probs[node_id][j].toFixed(2) + "</td></tr>";
}
title += "</tbody>"
title += "<p>Top-5 Token Candidates: </p>";
document.getElementById("hintsleft").innerHTML = title;
title = "<table><thead><tr><th>Rank</th><th>To</th><th>Transition Prob.</th><th>Passing Prob.</th></tr></thead><tbody>";
for (var j=0;j<prelen;j++){
var idx = sorted_links_out[node_id][j].idx;
if(j < 5 || visible_edges[node_id][idx]){
title += "<tr><td>" + get_jumpable_edge(node_id, idx, "#" + (j+1).toString()) + "</td><td>" + get_jumpable_node(idx) + "</td><td>" + links[node_id][idx].toFixed(2) + "</td><td>" +
(node_pass_prob[node_id] * links[node_id][idx]).toFixed(2) + "</td></tr>";
}
}
title += "</tbody>"
title += "<p>Top Outgoing Edges: </p>"
document.getElementById("hintscenter").innerHTML = title;
title = "<table><thead><tr><th>Rank</th><th>From</th><th>Transition Prob.</th><th>Passing Prob.</th></tr></thead><tbody>";
for (var j=0;j<prelen;j++){
var idx = sorted_links_in[node_id][j].idx;
if(j < 5 || visible_edges[idx][node_id]){
title += "<tr><td>" + get_jumpable_edge(idx, node_id, "#" + (j+1).toString()) + "</td><td>" + get_jumpable_node(idx) + "</td><td>" + links[idx][node_id].toFixed(2) + "</td><td>" +
(node_pass_prob[idx] * links[idx][node_id]).toFixed(2) + "</td></tr>";
}
}
title += "</tbody>"
title += "<p>Top Incoming Edges: </p>"
document.getElementById("hintsright").innerHTML = title;
document.getElementById("hintsbottom").innerHTML =
"<br>"+
"Passing probability of a node V represents how likely the node will be choosen in a random path, i.e., P(V \\in A). <br>" +
"Passing probability of an edge from U to V represents how likely the node V follows the node U in a random path, i.e., P(a_i = U && a_{i+1} = V). <br>" +
"Token probability represents how likely a token is predicted on the given node, i.e., P(y_i| v_{a_{i}}). <br>" +
"Transition probability represents how likely a specific node is following the given node, i.e. P(a_{i+1} | a_{i}).<br>"
}
show_hint_edge = function(edge_id){
showing_edge = edge_id;
showing_node = undefined;
var i = parseInt(edge_id.split("-")[0]);
var j = parseInt(edge_id.split("-")[1]);
var label = links[i][j].toFixed(2);
var passing_label = (links[i][j] * node_pass_prob[i]).toFixed(2);
var title = "You selected an edge from <b>Node " + get_jumpable_node(i) + " to Node " + get_jumpable_node(j) + "</b>."
if (visible_edges[i][j]){
title += "(<a href=\"javascript:network.fit({nodes:[" + i.toString() + "," + j.toString() + "],animation:true});\">Find Me!</a>). "
}else{
title += "(Not shown). "
}
title += "<br> You can click the links above to jump to the nodes. <br><br>"
title += "Transition Probability:<b>" + label + "</b><br>";
title += "Passing Probability:<b>" + passing_label + "</b><br>";
title += "<br>" +
"Transition probability represents how likely a specific node is following the given node, i.e. P(a_{i+1} | a_{i}).<br>" +
"Passing probability of an edge from U to V represents how likely the node V follows the node U in a random path, i.e., P(a_i = U && a_{i+1} = V). <br>"
document.getElementById("hintsupper").innerHTML = title;
document.getElementById("hintsleft").innerHTML = "";
document.getElementById("hintsright").innerHTML = "";
document.getElementById("hintscenter").innerHTML = "";
document.getElementById("hintsbottom").innerHTML = "";
}
function clear_hint(){
showing_node = undefined;
showing_edge = undefined;
document.getElementById("hintsupper").innerHTML = "Use scroll to zoom in or out. Select or Hover over nodes and edges for more information ... (Try dragging nodes to replace them.)";
document.getElementById("hintsleft").innerHTML = "";
document.getElementById("hintsright").innerHTML = "";
document.getElementById("hintscenter").innerHTML = "";
document.getElementById("hintsbottom").innerHTML = "";
}
startNetwork = function(graph_info, options) {
current_options = options;
global_graph_info = graph_info;
node_pass_prob = graph_info['node_pass_prob'][0] // size: [prelen]
prelen = node_pass_prob.length
max_path = graph_info['max_paths'][0] // size: [tarlen]
tarlen = max_path.length
node_tokens = graph_info['node_tokens'][0] // size: [prelen, 5]
node_probs = graph_info['node_probs'][0] // size: [prelen, 5]
links = graph_info['links'][0] // size: [prelen, prelen]
update_visible_nodes(true);
update_visible_edges(true);
// create a network
var container = document.getElementById("daggraph");
var data = {
nodes: nodes,
edges: edges,
};
network_options = {
nodes: {
shape: "ellipse",
scaling: {
label: {
min: 8,
max: 20,
},
customScalingFunction: customScalingFunction,
},
},
edges: {
arrowStrikethrough: false,
arrows: "to",
smooth: {
type: "continuous"
},
color: "#2B7CE9",
font: { align: "bottom" },
length: 120,
scaling: {
min: 0.5,
max: 3,
label: {
min: 8,
max: 15,
},
customScalingFunction: customScalingFunction,
}
}
};
network = new vis.Network(container, data, network_options);
network.off("dragStart");
network.on("dragStart", function (params) {
var idx = this.getNodeAt(params.pointer.DOM);
if (idx !== undefined) {
// console.log("dragstart " + idx.toString());
if (map_max_path[idx] !== undefined){
data.nodes.update({id: idx, fixed: false});
}
}
});
network.off("dragEnd");
network.on("dragEnd", function (params) {
var idx = this.getNodeAt(params.pointer.DOM);
if (idx !== undefined){
// console.log("dragend " + idx.toString());
if (map_max_path[idx] !== undefined){
data.nodes.update({id: idx, fixed: true});
map_max_path[idx].x = params.pointer.canvas.x;
map_max_path[idx].y = params.pointer.canvas.y;
}
}
});
disable_edge_select = false;
network.off("selectNode");
network.on("selectNode", function (params) {
var node_id = params.nodes[0];
show_hint_node(node_id);
disable_edge_select = true;
setTimeout(() => {disable_edge_select=false;}, 200);
});
network.off("selectEdge");
network.on("selectEdge", function (params) {
if(disable_edge_select) return;
var edge_id = params.edges[0];
show_hint_edge(edge_id);
});
network.off("deselectNode");
network.on("deselectNode", function (params) {
clear_hint();
showing_node = undefined;
showing_edge = undefined;
});
network.off("deselectEdge");
network.on("deselectEdge", function (params) {
clear_hint();
});
}
updateNetwork = function(options) {
if(typeof node_pass_prob === "undefined") return;
old_options = current_options;
current_options = options;
if(options.minimum_node_pass_prob != old_options.minimum_node_pass_prob){
update_visible_nodes();
}
if(options.minimum_node_pass_prob != old_options.minimum_node_pass_prob ||
options.minimum_edge_prob != old_options.minimum_edge_prob ||
options.max_out_edge_num != old_options.max_out_edge_num ||
options.max_out_edge_prob != old_options.max_out_edge_prob ||
options.force_in_edge != old_options.force_in_edge){
update_visible_edges();
}
if(options.show_node_detail != old_options.show_node_detail){
if(options.show_node_detail) {
network_options.nodes.shape = "dot";
network_options.nodes.scaling.label.min=10;
network_options.nodes.scaling.label.max=10;
}else{
network_options.nodes.shape = "ellipse";
network_options.nodes.scaling.label.min=8;
network_options.nodes.scaling.label.max=20;
}
network.setOptions(network_options);
update_node_details();
}
if(options.show_edge_label != old_options.show_edge_label){
update_edge_label();
}
if(showing_node != undefined){
show_hint_node(showing_node);
}
if(showing_edge != undefined){
show_hint_edge(showing_edge);
}
}
}