提交 aae36ad4 authored 作者: carriepl's avatar carriepl

Merge pull request #3322 from cangermueller/master

d3viz: Interactive visualization of Theano compute graphs
......@@ -14,6 +14,8 @@
*.toc
*.vrb
.noseids
*.DS_Store
*.bak
Theano.egg-info
\#*\#
build
......@@ -26,12 +28,12 @@ doc/indexes/oplist.txt
doc/indexes/typelist.txt
html
pdf
pull.sh
setuptools-*.egg
theano/generated_version.py
theano/generated_version.py.out
distribute-*.egg
distribute-*.tar.gz
Theano.suo
*.DS_Store
*.bak
\ No newline at end of file
.ipynb_checkpoints
.pydevproject
......@@ -122,7 +122,7 @@ html_logo = 'images/theano_logo_allblue_200x46.png'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['.static', 'images']
html_static_path = ['.static', 'images', 'library/d3viz/examples']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
......
.d3-context-menu {
position: absolute;
display: none;
background-color: #f2f2f2;
border-radius: 4px;
font-family: Arial, sans-serif;
font-size: 14px;
min-width: 50px;
border: 1px solid #d4d4d4;
z-index:1200;
}
.d3-context-menu ul {
list-style-type: none;
margin: 4px 0px;
padding: 0px;
cursor: default;
}
.d3-context-menu ul li {
padding: 4px 16px;
}
.d3-context-menu ul li:hover {
background-color: #4677f8;
color: #fefefe;
}
svg {
margin-left:auto;
margin-right:auto;
display:block;
position: fixed;
border: 0px solid black;
top: 32px; left:0%; right:0%; bottom:0%;
}
.menuBar {
border-bottom: 1px solid black;
height: 22px;
}
input.menuBar {
}
.nodeRect {
stroke: black;
border: 3px solid black;
}
.nodeEllipse {
stroke: black;
border: 3px solid black;
}
.nodeText {
color: black;
}
.edge {
stroke-width: 3px;
cursor: pointer;
opacity: 0.4;
}
.edgeLabelRect {
stroke: black;
border: 1px solid black;
fill: skyblue;
opacity: 0.9;
}
.edgeLabelText {
fill: black;
text-anchor: start;
}
.arrowHead {
stroke: green;
stroke-width: 1px;
}
.arrowHead_n {
stroke: green;
}
.arrowHead_r {
stroke-width: 3px;
fill: red;
stroke: red;
}
.arrowHead_b {
stroke: dodgerblue;
}
.edgeTooltip {
position: absolute;
text-align: center;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
.nodeInfo {
position: absolute;
text-align: left;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
path.hull {
fill: lightsteelblue;
fill-opacity: 0.3;
}
\ No newline at end of file
d3.contextMenu = function (menu, openCallback) {
// create the div element that will hold the context menu
d3.selectAll('.d3-context-menu').data([1])
.enter()
.append('div')
.attr('class', 'd3-context-menu');
// close menu
d3.select('body').on('click.d3-context-menu', function() {
d3.select('.d3-context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
return function(data, index) {
var elm = this;
d3.selectAll('.d3-context-menu').html('');
var list = d3.selectAll('.d3-context-menu').append('ul');
list.selectAll('li').data(menu).enter()
.append('li')
.html(function(d) {
return d.title;
})
.on('click', function(d, i) {
d.action(elm, data, index);
d3.select('.d3-context-menu').style('display', 'none');
});
// the openCallback allows an action to fire before the menu is displayed
// an example usage would be closing a tooltip
if (openCallback) openCallback(data, index);
// display context menu
d3.select('.d3-context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
};
};
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Theano javascript library for interactive visualization.
*
* Author: Christof Angermueller <cangermueller@gmail.com>
*/
/*
* Checks if variable is defined.
*/
function exists(x) {
return typeof(x) != 'undefined';
}
/*
* Replace all patterns in string.
*/
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
}
/*
* Computes len equally spaces points between start and end.
*/
function linspace(start, end, len) {
var d = (end - start) / (len - 1);
var rv = [start];
for (i = 1; i < len; ++i) {
rv.push(rv[i - 1] + d);
}
return rv;
}
/*
* Converts string to list
*/
function str2List(s) {
s = s.split('\t');
return s;
}
/*
* Flips y-scale such that (0, 0) points to top-left corner.
*/
function flipAxes(nodes) {
var size = [0, 0];
for (var i in nodes) {
var node = nodes[i];
size[0] = Math.max(size[0], node.pos[0] + node.width);
size[1] = Math.max(size[1], node.pos[1] + node.height);
}
for (var i in nodes) {
var node = nodes[i];
node.pos[1] = size[1] - (node.pos[1] + node.height);
}
}
/*
* Preprocesses raw dotGraph
*/
function processDotGraph(dotGraph) {
// Ignore cluster nodes
dotGraph.rnodes = {};
for (var nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.id = nodeId;
node.isCluster = nodeId.substr(0, 7) == 'cluster';
if (!node.isCluster) {
dotGraph.rnodes[nodeId] = node;
}
}
// Precompute attributes
var i = 0;
for (var nodeId in dotGraph.rnodes) {
var node = dotGraph._nodes[nodeId];
node.pos = node.pos.split(',').map(function(d) {return parseInt(d);});
var size = textSize(node.label, {'class': 'nodeText'});
node.width = size.width + 2 * pad;
node.height = size.height + 2 * pad;
node.cx = node.width / 2;
node.cy = node.height / 2;
node.hasChilds = exists(node.subg);
node.showChilds = false;
if (exists(node.profile)) {
node.profile = parseProfile(node.profile);
isProfiled = true;
}
if (exists(node.tag)) {
node.tag = str2List(node.tag);
}
if (exists(node.subg_map_inputs)) {
node.subg_map_inputs = eval(node.subg_map_inputs)
}
if (exists(node.subg_map_outputs)) {
node.subg_map_outputs = eval(node.subg_map_outputs)
}
}
flipAxes(dotGraph.rnodes);
// Offset and scale positions
var posMin = [Infinity, Infinity];
for (var i in dotGraph.rnodes) {
var node = dotGraph._nodes[i];
posMin[0] = Math.min(posMin[0], node.pos[0]);
posMin[1] = Math.min(posMin[1], node.pos[1]);
}
for (var i in dotGraph.rnodes) {
var node = dotGraph._nodes[i];
var pos = node.pos;
pos[0] -= posMin[0];
pos[1] -= posMin[1];
pos[0] = 1.2 * pos[0];
pos[1] = 1.2 * pos[1];
}
// Preprocess edges
var edges = dotGraph.edges();
for (var i in edges) {
var edge = dotGraph.edge(edges[i]);
var size = textSize(edge.label, {'class': 'edgeLabelText'});
edge.width = size.width + 2 * pad;
edge.height = size.height + 2 * pad;
if (!exists(edge.color)) {
edge.color = 'black';
}
switch (edge.color) {
case 'dodgerblue':
edge.type = 'b';
break;
case 'red':
edge.type = 'r';
break;
default:
edge.type = 'n';
}
}
}
/*
* Extracts profiling information from string.
*/
function parseProfile(s) {
var p = str2List(s);
p = p.map(function(x) { return parseFloat(x); });
return p;
}
/*
* Preprocesses DOT nodes for front-end visualization.
* Assigns all children of parent (root of graph if not specified)
* to the same group and calls function recursively on children.
*
*/
function traverseChilds(dotGraph, nodes, groups, parent) {
var preId = '';
var ref = undefined;
// Create new group with parent as parent
var group = {'id': groups.length, 'nodes': [], 'parent': parent};
if (exists(parent)) {
ref = parent.value.subg;
group.parent = parent;
group.nodes.push(parent);
parent.group = group;
}
groups.push(group);
// Loop over all children
var childs = dotGraph.children(ref);
for (var i in childs) {
var child = dotGraph.node(childs[i]);
if (child.isCluster) {
continue;
}
var node = {
'id': child.id,
'value': child,
'index': nodes.length,
'fixed': fixOnInit,
'group': group,
'isParent': child.showChilds,
'parent': parent
};
nodes.push(node);
if (child.showChilds) {
// Recurse if child is root of subcluster that should be expandend
traverseChilds(dotGraph, nodes, groups, node);
} else {
group.nodes.push(node);
}
}
// Groups appended to groups after group are group children.
group.childs = [];
for (var i = group.id + 1; i < groups.length; ++i) {
group.childs.push(groups[i].id);
}
}
/*
* Computes width and height of group of nodes.
*/
function groupSize(nodes) {
var minPos = [Infinity, Infinity];
var maxPos = [-Infinity, -Infinity];
for (var i in nodes) {
var node = nodes[i];
if (node.isParent) {
continue;
}
minPos[0] = Math.min(minPos[0], node.value.pos[0]);
minPos[1] = Math.min(minPos[1], node.value.pos[1]);
maxPos[0] = Math.max(maxPos[0], node.value.pos[0] + node.value.width);
maxPos[1] = Math.max(maxPos[1], node.value.pos[1] + node.value.height);
}
return [maxPos[0] - minPos[0], maxPos[1] - minPos[1]];
}
/*
* Creates front-end graph for visualizing from DOT graph.
*/
function frontEndGraph(dotGraph, prevGraph) {
var graph = {'nodes': [], 'groups': []};
traverseChilds(dotGraph, graph.nodes, graph.groups);
// Dictionary to access nodes by id
graph.nodesd = {};
for (var i in graph.nodes) {
var node = graph.nodes[i];
graph.nodesd[node.id] = node;
}
// Dictionary to access groups by id
graph.groupsd = {};
for (var i in graph.groups) {
var group = graph.groups[i];
graph.groupsd[group.id] = group;
}
// Parent nodes
graph.nodesp = graph.nodes.filter(function(d) {return d.isParent;});
// Non-parent nodes
graph.nodesn = graph.nodes.filter(function(d) {return !d.isParent;});
// Compute size of groups
for (i in graph.groups) {
var group = graph.groups[i];
group.size = groupSize(group.nodes);
var parent = group.parent;
if (exists(parent)) {
var prevParent = prevGraph.nodesd[group.parent.id];
if (exists(prevParent)) {
// Restore previous group position if given
group.pos = [prevParent.x, prevParent.y];
} else {
// Use position of parent otherwise
group.pos = parent.value.pos.slice(0);
}
group.pos[0] += parent.value.cx;
group.pos[1] += parent.value.cy;
} else {
group.pos = [group.size[0] / 2, group.size[1] / 2];
}
// Offset nodes on group center
var min = [Infinity, Infinity];
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
min[0] = Math.min(min[0], node.value.pos[0]);
min[1] = Math.min(min[0], node.value.pos[1]);
}
}
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
node.x = group.pos[0] - group.size[0] / 2 + node.value.pos[0] - min[0];
node.y = group.pos[1] - group.size[1] / 2 + node.value.pos[1] - min[1];
}
}
}
graph.size = graph.groups[0].size;
// Reuse previous positions
if (exists(prevGraph)) {
for (var i in graph.nodes) {
var node = graph.nodes[i];
var prevNode;
prevNode = prevGraph.nodesd[node.id];
if (exists(prevNode)) {
node.x = prevNode.x;
node.y = prevNode.y;
node.fixed = prevNode.fixed;
} else {
for (var j in prevGraph.groups) {
var group = prevGraph.groups[j];
if (exists(group.parent) && group.parent.id == node.id) {
node.x = group.pos[0] + group.size[0] / 2;
node.y = group.pos[1] + group.size[1] / 2;
}
}
}
}
}
// Edges
graph.edges = [];
for (var i in graph.nodesn) {
for (var j in graph.nodesn) {
var source = graph.nodesn[i];
var target = graph.nodesn[j];
var dotEdge = dotGraph.edge(source.value.id, target.value.id);
if (exists(dotEdge)) {
var edge = {};
edge.source = parseInt(source.index);
edge.target = parseInt(target.index);
edge.value = dotEdge;
graph.edges.push(edge);
}
// Redirect edges to subgraph
function redirectEdges(map, dotEdge) {
for (var k in map) {
var kmap = map[k];
if (kmap[0] == source.id && kmap[1] == target.id) {
var edge = {};
edge.source = parseInt(source.index);
edge.target = parseInt(target.index);
edge.value = dotEdge;
graph.edges.push(edge);
}
}
}
var map = undefined;
if (exists(target.parent)) {
var parent = target.parent;
var dotEdge = dotGraph.edge(source.id, parent.id);
if (exists(dotEdge)) {
map = parent.value.subg_map_inputs;
redirectEdges(map, dotEdge);
}
}
if (exists(source.parent)) {
var parent = source.parent;
var dotEdge = dotGraph.edge(parent.id, target.id);
if (exists(dotEdge)) {
map = parent.value.subg_map_outputs;
redirectEdges(map, dotEdge);
}
}
}
}
return graph;
}
/*
* Computes d3.js convex hull surrounding nodes that
* belong to the same group.
*/
function convexHulls(graph, offset) {
var hulls = [];
offset = offset || 20;
for (var i in graph.groups) {
var group = graph.groups[i];
if (!exists(group.parent)) {
continue;
}
var points = [];
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
points.push([node.x - node.value.cx - offset, node.y - node.value.cy - offset]);
points.push([node.x - node.value.cx - offset, node.y + node.value.cy + offset]);
points.push([node.x + node.value.cx + offset, node.y - node.value.cy - offset]);
points.push([node.x + node.value.cx + offset, node.y + node.value.cy + offset]);
}
}
for (var k in group.childs) {
var nodes = graph.groupsd[group.childs[k]].nodes;
for (var j in nodes) {
var node = nodes[j];
if (!node.isParent) {
points.push([node.x - node.value.cx - offset, node.y - node.value.cy - offset]);
points.push([node.x - node.value.cx - offset, node.y + node.value.cy + offset]);
points.push([node.x + node.value.cx + offset, node.y - node.value.cy - offset]);
points.push([node.x + node.value.cx + offset, node.y + node.value.cy + offset]);
}
}
}
hulls.push({group: i, path: d3.geom.hull(points)});
}
return hulls;
}
/*
* Draws convex hull.
*/
function drawConvexHull(d) {
var curve = d3.svg.line()
.interpolate("cardinal-closed")
.tension(.85);
return curve(d.path);
}
/*
* Creates skeleton for graph visualization.
* Positions will be updated by updateGraph().
*/
function drawGraph() {
d3.select('body').select('#menu').select('#toggleColors').remove();
if (isProfiled) {
d3.select('body').select('#menu').append('input')
.attr('id', 'toggleColors')
.attr('type', 'button')
.attr('value', 'Toggle profile colors')
.attr('onclick', "toggleNodeColors()");
maxProfilePer = 0;
for (i in graph.nodes) {
var p = graph.nodes[i].value.profile;
if (exists(p)) {
maxProfilePer = Math.max(maxProfilePer, p[0] / p[1]);
}
}
}
var isEdgeOver = false;
var isEdgeLabelOver = false;
// Event handler for dragging groups
var dragHulls = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
forceLayout.stop();
})
.on("drag", function dragged(d) {
// Shift all group members
var group = graph.groups[d.group];
for (var i in group.nodes) {
var node = group.nodes[i];
node.x += d3.event.dx;
node.y += d3.event.dy;
node.px += d3.event.dx;
node.py += d3.event.dy;
}
group.pos[0] += d3.event.dx;
group.pos[1] += d3.event.dy;
// Shift all members of sub groups
for (var k in group.childs) {
var cgroup = graph.groupsd[group.childs[k]];
var nodes = cgroup.nodes;
for (var j in nodes) {
var node = nodes[j];
node.x += d3.event.dx;
node.y += d3.event.dy;
node.px += d3.event.dx;
node.py += d3.event.dy;
cgroup.pos[0] += d3.event.dx;
cgroup.pos[1] += d3.event.dy;
}
}
updateGraph();
})
.on('dragend', function(d) {forceLayout.resume();});
// Draw convex hull surrounding group of nodes
graph.hulls = convexHulls(graph);
hulls = pane.selectAll('#hulls').remove();
hulls = pane.append('g').attr('id', 'hulls')
.selectAll('path')
.data(graph.hulls).enter()
.append('path')
.attr('class', 'hull')
.attr('d', drawConvexHull)
.call(dragHulls);
// Event handler to open/close groups
hulls.on('dblclick', function(d) {
var group = graph.groups[d.group];
group.parent.value.showChilds = !group.parent.value.showChilds;
if (!group.parent.value.showChilds) {
for (i in group.childs) {
var child = graph.groupsd[group.childs[i]];
child.parent.value.showChilds = false;
}
}
graph = frontEndGraph(dotGraph, graph);
drawGraph();
});
// Add edges
edges = pane.selectAll('#edges').remove();
edges = pane.append('g').attr('id', 'edges')
.selectAll('path').data(graph.edges).enter().append('path')
.attr('class', 'edge')
.attr('stroke', function(d) {return d.value.color;})
.attr('marker-mid', function(d) { return 'url(#edgeArrow_' + d.value.type + ')';});
edges.on('mouseover', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 1.0);
edgeDiv.transition()
.duration(200)
.style('opacity', .9);
edgeDiv
.html(d.value.label)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
});
edges.on('mouseout', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 0.4);
edgeDiv.transition()
.duration(200)
.style('opacity', 0);
});
// Add nodes
pane.selectAll('#nodes').remove();
nodes = pane.append('g').attr('id', 'nodes')
.selectAll('g').data(graph.nodesn).enter().append('g');
updateNodes();
updateGraph();
nodes.on('dblclick', function(d) {
if (d.value.hasChilds) {
d.value.showChilds = !d.value.showChilds;
graph = frontEndGraph(dotGraph, graph);
if (!fixOnInit && d.value.showChilds) {
var n = dotGraph.neighbors(d.id);
for (i in n) {
graph.nodesd[n[i]].fixed = false;
}
}
drawGraph();
}
});
nodes.on('mouseover', function(node) {
// Highlight incoming edges
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source == node || d.target == node) {
edge.transition()
.duration(200)
.style('opacity', 1.0);
}
});
// Show node details
if (!isEditNode) {
nodeInfo.transition()
.duration(200)
.style('opacity', .9);
nodeInfo
.html(formatNodeInfos(node))
.style('left', (d3.event.pageX) + 30 + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
}
});
nodes.on('mouseout', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source.index == node.index || d.target.index == node.index) {
edge.transition()
.duration(200)
.style('opacity', 0.4);
}
});
hideNodeInfo();
});
nodes.on('contextmenu', d3.contextMenu(menuItems));
forceLayout = d3.layout.force()
.nodes(graph.nodes)
.links(graph.edges)
.size(graph.size)
.linkDistance(200)
.charge(-1000)
.linkStrength(0.2)
.gravity(0.05)
.friction(0.5)
.on('tick', updateGraph)
.start();
// Drag behavour
var drag = forceLayout.drag()
.on('dragstart', function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
d.fixed = true;
});
nodes.call(drag);
}
/*
* Computes weighted average between two points.
*/
function avgPos(x1, y1, x2, y2, c) {
x = (1 - c) * x1 + c * x2;
y = (1 - c) * y1 + c * y2;
p = x + ',' + y;
return p;
}
/*
* Checks for collisions in d3.js quadtree.
* See http://bl.ocks.org/mbostock/3231298 for more details.
*/
function collide(node) {
var eps = 10;
var nx1 = node.x - node.value.cx - eps;
var nx2 = node.x + node.value.cx + eps;
var ny1 = node.y - node.value.cy - eps;
var ny2 = node.y + node.value.cy + eps;
return function(quad, x1, y1, x2, y2) {
var point = quad.point;
if (point && (point != node) && !point.fixed && ! node.fixed) {
var px1 = point.x - point.value.cx;
var px2 = point.x + point.value.cx;
var py1 = point.y - point.value.cy;
var py2 = point.y + point.value.cy;
if (!(px1 > nx2 || px2 < nx1 || py1 >= ny2 || py2 <= ny1)) {
var eta = 0.1;
if (px1 < nx1) {
// move quad to left
var d = eta * (px2 - nx1);
point.x -= d;
node.x += d;
} else {
var d = eta * (nx2 - px1);
point.x += d;
node.x -= d;
}
if (py1 < ny1) {
// move quad to top
var d = eta * (py2 - ny1);
point.y -= d;
node.y += d;
} else {
var d = eta * (ny2 - py1);
point.y += d;
node.y -= d;
}
}
}
return x1 > nx2 || x2 < nx1 || y1 >= ny2 || y2 <= ny1;
};
}
/*
* Computes euclidean distance between points.
*/
function distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}
/*
* Updates graph visualization.
*/
function updateGraph() {
// Avoid collisions
var q = d3.geom.quadtree(graph.nodes);
for (var i in graph.nodes) {
q.visit(collide(graph.nodes[i]));
}
graph.hulls = convexHulls(graph);
hulls.data(graph.hulls)
.attr('d', drawConvexHull);
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; });
// Update edges
edges.attr('d', function(d) {
var dist = 100;
var l = distance(d.source.x, d.source.y, d.target.x, d.target.y);
var n = Math.max(2, Math.floor(l / dist));
var marker = [];
for (var i = 1; i < n; ++i) {
marker.push(i / n);
}
var markerPos = marker.map(function(c) {
return avgPos(d.source.x, d.source.y, d.target.x, d.target.y, c);});
var markerPos = ' L' + markerPos.join(' L');
return 'M' + d.source.x + ',' + d.source.y + markerPos + ' L' + d.target.x + ',' + d.target.y;
});
}
/*
* Toggles between usual nodes colors and profiling colors
*/
function toggleNodeColors() {
useProfileColors = !useProfileColors;
updateNodes();
updateGraph();
}
/*
* Computes bounding box that fits text of a certain length.
*/
function textSize(text, attr) {
var t = svg.append('text').text(text);
if (typeof(attr) != 'undefined') {
for (a in attr) {
t.attr(a, attr[a]);
}
}
var bbox = t.node().getBBox();
t.remove();
return bbox;
}
/*
* Computes profiling color.
*/
function profileColor(per) {
var s = d3.scale.linear()
.domain(linspace(0, maxProfilePer, profileColors.length))
.range(profileColors)
.interpolate(d3.interpolateRgb);
return s(per);
}
/*
* Retuns node fill color.
*/
function nodeFillColor(d) {
if (useProfileColors) {
var p = d.value.profile;
if (d.value.node_type == 'apply' && exists(p)) {
return profileColor(d.value.profile[0] / d.value.profile[1]);
} else {
return 'white';
}
} else {
return typeof(d.value.fillcolor) == 'undefined' ? 'white' : d.value.fillcolor;
}
}
/*
* Formats profiling timing information.
*/
function formatTime(sec) {
var s;
if (sec < 0.1) {
s = (sec * 1000).toFixed(1) + ' ms';
} else {
s = sec.toFixed(1) + ' s';
}
return s;
}
/*
* Formats node details.
*/
function formatNodeInfos(node) {
var v = node.value;
var s = '<b><center>' + v.label + '</center></b><hr>';
s += '<b>Node:</b> ' + replaceAll(v.node_type, '_', ' ') + ' node';
if (exists(v.dtype)) {
s += '</br>';
s += '<b>Type:</b> <source>' + v.dtype + '</source>';
}
if (exists(v.apply_op)) {
s += '</br>';
s += '<b>Apply:</b> <source>' + v.apply_op + '</source>';
}
if (exists(v.tag)) {
s += '<p>';
s += '<b>Location:</b> <source>' + v.tag[1] + ': ' + v.tag[0] + '</source><br>';
s += '<b>Definition:</b> <source>' + v.tag[2] + '</source><br>';
s += '</p>';
}
var p = v.profile;
if (exists(p)) {
s += '<p>';
s += '<b>Time:</b> ' + formatTime(p[0]);
s += ' / ' + (p[0] / p[1] * 100).toFixed(1) + ' %';
s += '</p>';
}
return s;
}
/*
* Updates node visualization.
*/
function updateNode(d, node) {
var shape;
if (d.value.shape == 'ellipse') {
node.selectAll('ellipse').remove();
shape = node.append('ellipse')
.attr('class', 'nodeEllipse')
.attr('cx', d.value.cx)
.attr('cy', d.value.cy)
.attr('rx', d.value.width * 0.6)
.attr('ry', d.value.height * 0.6);
} else {
node.selectAll('rect').remove();
shape = node.append('rect')
.attr('class', 'nodeRect')
.attr('width', d.value.width)
.attr('height', d.value.height);
}
shape.attr('fill', nodeFillColor(d));
node.selectAll('text').remove();
var text = node.append('text')
.attr('class', 'nodeText')
.attr('x', pad)
.attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;});
if (d.value.hasChilds) {
node.style('cursor', 'pointer');
}
}
/*
* Updates visualization of all nodes.
*/
function updateNodes() {
nodes.each(function(d) {
var node = d3.select(this);
updateNode(d, node);
});
}
/*
* Hides node information field.
*/
function hideNodeInfo() {
nodeInfo.transition()
.duration(200)
.style('opacity', 0);
}
/*
* Adjusts node size.
*/
function setNodeSize(node) {
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
}
/*
* Event handler for editing nodes.
*/
function editNode(elm, d) {
var node = d3.select(elm);
var pos = elm.getBBox();
if (d3.event.defaultPrevented) return;
isEditNode = true;
hideNodeInfo();
var form = node.append('foreignObject')
.attr('x', pos.x)
.attr('y', pos.y)
.attr('width', d.value.width)
.attr('height', 25);
var input = form.append('xhtml:form').append('input')
.attr('style', 'width: ' + d.value.width + 'px')
.attr('value', function() {
this.focus();
return d.value.label;
})
.on('blur', function() {
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
isEditNode = false;
})
.on('keypress', function() {
if (!d3.event) {
d3.event = window.event;
}
var event = d3.event;
if (event.keyCode == 13) {
if (typeof(event.cancelBubble)) {
event.cancelBubble = true;
}
if (event.stopPropagation) {
event.stopPropagation();
}
event.preventDefault();
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
isEditNode = false;
}
});
}
/*
* Release node from fixed positions.
*/
function releaseNode(d) {
d.fixed = false;
forceLayout.start();
}
/*
* Releases positions of all nodes.
*/
function releaseNodes() {
graph['nodes'].forEach (function (d) {
d.fixed = false;
});
forceLayout.start();
}
/*
* Restores original node positions.
*/
function resetNodes() {
graph = frontEndGraph(dotGraph);
drawGraph();
}
(function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var j=typeof require=="function"&&require;if(!h&&j)return j(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}var f=typeof require=="function"&&require;for(var g=0;g<d.length;g++)e(d[g]);return e})({1:[function(a,b,c){var d=self;d.dagreD3=a("./index")},{"./index":2}],2:[function(a,b,c){b.exports={Digraph:a("graphlib").Digraph,Renderer:a("./lib/Renderer"),json:a("graphlib").converter.json,layout:a("dagre").layout,version:a("./lib/version")}},{"./lib/Renderer":3,"./lib/version":4,dagre:11,graphlib:28}],3:[function(a,b,c){function g(){this._layout=d(),this.drawNodes(k),this.drawEdgeLabels(l),this.drawEdgePaths(m),this.positionNodes(n),this.positionEdgeLabels(o),this.positionEdgePaths(p),this.transition(q),this.postLayout(r),this.postRender(s),this.edgeInterpolate("bundle"),this.edgeTension(.95)}function h(a){var b=a.copy();return b.nodes().forEach(function(a){var c=b.node(a);c===undefined&&(c={},b.node(a,c)),"label"in c||(c.label="")}),b.edges().forEach(function(a){var c=b.edge(a);c===undefined&&(c={},b.edge(a,c)),"label"in c||(c.label="")}),b}function i(a,b){var c=a.getBBox();b.width=c.width,b.height=c.height}function j(a,b){var c=b.run(a);return a.eachNode(function(a,b){c.node(a).label=b.label}),a.eachEdge(function(a,b,d,e){c.edge(a).label=e.label}),c}function k(a,b){var c=a.nodes().filter(function(b){return!z(a,b)}),d=b.selectAll("g.node").classed("enter",!1).data(c,function(a){return a});return d.selectAll("*").remove(),d.enter().append("g").style("opacity",0).attr("class","node enter"),d.each(function(b){t(a.node(b),e.select(this),10,10)}),this._transition(d.exit()).style("opacity",0).remove(),d}function l(a,b){var c=b.selectAll("g.edgeLabel").classed("enter",!1).data(a.edges(),function(a){return a});return c.selectAll("*").remove(),c.enter().append("g").style("opacity",0).attr("class","edgeLabel enter"),c.each(function(b){t(a.edge(b),e.select(this),0,0)}),this._transition(c.exit()).style("opacity",0).remove(),c}function n(a,b,c){function d(b){var c=a.node(b);return"translate("+c.x+","+c.y+")"}b.filter(".enter").attr("transform",d),this._transition(b).style("opacity",1).attr("transform",d)}function o(a,b){function c(b){var c=a.edge(b),d=x(c.points);return"translate("+d.x+","+d.y+")"}b.filter(".enter").attr("transform",c),this._transition(b).style("opacity",1).attr("transform",c)}function p(a,b){function f(b){var f=a.edge(b),g=a.node(a.incidentNodes(b)[0]),h=a.node(a.incidentNodes(b)[1]),i=f.points.slice(),j=i.length===0?h:i[0],k=i.length===0?g:i[i.length-1];return i.unshift(y(g,j)),i.push(y(h,k)),e.svg.line().x(function(a){return a.x}).y(function(a){return a.y}).interpolate(c).tension(d)(i)}var c=this._edgeInterpolate,d=this._edgeTension;b.filter(".enter").selectAll("path").attr("d",f),this._transition(b.selectAll("path")).attr("d",f).style("opacity",1)}function q(a){return a}function r(){}function s(a,b){a.isDirected()&&b.select("#arrowhead").empty()&&b.append("svg:defs").append("svg:marker").attr("id","arrowhead").attr("viewBox","0 0 10 10").attr("refX",8).attr("refY",5).attr("markerUnits","strokewidth").attr("markerWidth",8).attr("markerHeight",5).attr("orient","auto").attr("style","fill: #333").append("svg:path").attr("d","M 0 0 L 10 5 L 0 10 z")}function t(a,b,c,d){var e=a.label,f=b.append("rect"),g=b.append("g");e[0]==="<"?(u(e,g),c=d=0):v(e,g,Math.floor(a.labelCols),a.labelCut);var h=b.node().getBBox();g.attr("transform","translate("+ -h.width/2+","+ -h.height/2+")"),f.attr("rx",5).attr("ry",5).attr("x",-(h.width/2+c)).attr("y",-(h.height/2+d)).attr("width",h.width+2*c).attr("height",h.height+2*d)}function u(a,b){var c=b.append("foreignObject").attr("width","100000"),d,e;c.append("xhtml:div").style("float","left").html(function(){return a}).each(function(){d=this.clientWidth,e=this.clientHeight}),c.attr("width",d).attr("height",e)}function v(a,b,c,d){d===undefined&&(d="false"),d=d.toString().toLowerCase()==="true";var e=b.append("text").attr("text-anchor","left");a=a.replace(/\\n/g,"\n");var f=c?w(a,c,d):a;f=f.split("\n");for(var g=0;g<f.length;g++)e.append("tspan").attr("dy","1em").attr("x","1").text(f[g])}function w(a,b,c,d){d=d||"\n",b=b||75,c=c||!1;if(!a)return a;var e=".{1,"+b+"}(\\s|$)"+(c?"|.{"+b+"}|.+$":"|\\S+?(\\s|$)");return a.match(RegExp(e,"g")).join(d)}function x(a){var b=a.length/2;if(a.length%2)return a[Math.floor(b)];var c=a[b-1],d=a[b];return{x:(c.x+d.x)/2,y:(c.y+d.y)/2}}function y(a,b){var c=a.x,d=a.y,e=b.x-c,f=b.y-d,g=a.width/2,h=a.height/2,i,j;return Math.abs(f)*g>Math.abs(e)*h?(f<0&&(h=-h),i=f===0?0:h*e/f,j=h):(e<0&&(g=-g),i=g,j=e===0?0:g*f/e),{x:c+i,y:d+j}}function z(a,b){return"children"in a&&a.children(b).length}function A(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}}var d=a("dagre").layout,e;try{e=a("d3")}catch(f){e=window.d3}b.exports=g,g.prototype.layout=function(a){return arguments.length?(this._layout=a,this):this._layout},g.prototype.drawNodes=function(a){return arguments.length?(this._drawNodes=A(a,this),this):this._drawNodes},g.prototype.drawEdgeLabels=function(a){return arguments.length?(this._drawEdgeLabels=A(a,this),this):this._drawEdgeLabels},g.prototype.drawEdgePaths=function(a){return arguments.length?(this._drawEdgePaths=A(a,this),this):this._drawEdgePaths},g.prototype.positionNodes=function(a){return arguments.length?(this._positionNodes=A(a,this),this):this._positionNodes},g.prototype.positionEdgeLabels=function(a){return arguments.length?(this._positionEdgeLabels=A(a,this),this):this._positionEdgeLabels},g.prototype.positionEdgePaths=function(a){return arguments.length?(this._positionEdgePaths=A(a,this),this):this._positionEdgePaths},g.prototype.transition=function(a){return arguments.length?(this._transition=A(a,this),this):this._transition},g.prototype.postLayout=function(a){return arguments.length?(this._postLayout=A(a,this),this):this._postLayout},g.prototype.postRender=function(a){return arguments.length?(this._postRender=A(a,this),this):this._postRender},g.prototype.edgeInterpolate=function(a){return arguments.length?(this._edgeInterpolate=a,this):this._edgeInterpolate},g.prototype.edgeTension=function(a){return arguments.length?(this._edgeTension=a,this):this._edgeTension},g.prototype.run=function(a,b){a=h(a),b.selectAll("g.edgePaths, g.edgeLabels, g.nodes").data(["edgePaths","edgeLabels","nodes"]).enter().append("g").attr("class",function(a){return a});var c=this._drawNodes(a,b.select("g.nodes")),d=this._drawEdgeLabels(a,b.select("g.edgeLabels"));c.each(function(b){i(this,a.node(b))}),d.each(function(b){i(this,a.edge(b))});var e=j(a,this._layout);this._postLayout(e,b);var f=this._drawEdgePaths(a,b.select("g.edgePaths"));return this._positionNodes(e,c),this._positionEdgeLabels(e,d),this._positionEdgePaths(e,f),this._postRender(e,b),e};var m=function(a,b){var c=b.selectAll("g.edgePath").classed("enter",!1).data(a.edges(),function(a){return a});return c.enter().append("g").attr("class","edgePath enter").append("path").style("opacity",0).attr("marker-end","url(#arrowhead)"),this._transition(c.exit()).style("opacity",0).remove(),c}},{d3:10,dagre:11}],4:[function(a,b,c){b.exports="0.1.5"},{}],5:[function(a,b,c){c.Set=a("./lib/Set"),c.PriorityQueue=a("./lib/PriorityQueue"),c.version=a("./lib/version")},{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(a,b,c){function d(){this._arr=[],this._keyIndices={}}b.exports=d,d.prototype.size=function(){return this._arr.length},d.prototype.keys=function(){return this._arr.map(function(a){return a.key})},d.prototype.has=function(a){return a in this._keyIndices},d.prototype.priority=function(a){var b=this._keyIndices[a];if(b!==undefined)return this._arr[b].priority},d.prototype.min=function(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key},d.prototype.add=function(a,b){var c=this._keyIndices;if(a in c)return!1;var d=this._arr,e=d.length;return c[a]=e,d.push({key:a,priority:b}),this._decrease(e),!0},d.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var a=this._arr.pop();return delete this._keyIndices[a.key],this._heapify(0),a.key},d.prototype.decrease=function(a,b){var c=this._keyIndices[a];if(b>this._arr[c].priority)throw new Error("New priority is greater than current priority. Key: "+a+" Old: "+this._arr[c].priority+" New: "+b);this._arr[c].priority=b,this._decrease(c)},d.prototype._heapify=function(a){var b=this._arr,c=2*a,d=c+1,e=a;c<b.length&&(e=b[c].priority<b[e].priority?c:e,d<b.length&&(e=b[d].priority<b[e].priority?d:e),e!==a&&(this._swap(a,e),this._heapify(e)))},d.prototype._decrease=function(a){var b=this._arr,c=b[a].priority,d;while(a!==0){d=a>>1;if(b[d].priority<c)break;this._swap(a,d),a=d}},d.prototype._swap=function(a,b){var c=this._arr,d=this._keyIndices,e=c[a],f=c[b];c[a]=f,c[b]=e,d[f.key]=a,d[e.key]=b}},{}],7:[function(a,b,c){function e(a){this._size=0,this._keys={};if(a)for(var b=0,c=a.length;b<c;++b)this.add(a[b])}function f(a){var b=Object.keys(a),c=b.length,d=new Array(c),e;for(e=0;e<c;++e)d[e]=a[b[e]];return d}var d=a("./util");b.exports=e,e.intersect=function(a){if(a.length===0)return new e;var b=new e(d.isArray(a[0])?a[0]:a[0].keys());for(var c=1,f=a.length;c<f;++c){var g=b.keys(),h=d.isArray(a[c])?new e(a[c]):a[c];for(var i=0,j=g.length;i<j;++i){var k=g[i];h.has(k)||b.remove(k)}}return b},e.union=function(a){var b=d.reduce(a,function(a,b){return a+(b.size?b.size():b.length)},0),c=new Array(b),f=0;for(var g=0,h=a.length;g<h;++g){var i=a[g],j=d.isArray(i)?i:i.keys();for(var k=0,l=j.length;k<l;++k)c[f++]=j[k]}return new e(c)},e.prototype.size=function(){return this._size},e.prototype.keys=function(){return f(this._keys)},e.prototype.has=function(a){return a in this._keys},e.prototype.add=function(a){return a in this._keys?!1:(this._keys[a]=a,++this._size,!0)},e.prototype.remove=function(a){return a in this._keys?(delete this._keys[a],--this._size,!0):!1}},{"./util":8}],8:[function(a,b,c){Array.isArray?c.isArray=Array.isArray:c.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"},"function"!=typeof Array.prototype.reduce?c.reduce=function(a,b,c){"use strict";if(null===a||"undefined"==typeof a)throw new TypeError("Array.prototype.reduce called on null or undefined");if("function"!=typeof b)throw new TypeError(b+" is not a function");var d,e,f=a.length>>>0,g=!1;1<arguments.length&&(e=c,g=!0);for(d=0;f>d;++d)a.hasOwnProperty(d)&&(g?e=b(e,a[d],d,a):(e=a[d],g=!0));if(!g)throw new TypeError("Reduce of empty array with no initial value");return e}:c.reduce=function(a,b,c){return a.reduce(b,c)}},{}],9:[function(a,b,c){b.exports="1.1.3"},{}],10:[function(a,b,c){a("./d3"),b.exports=d3,function(){delete this.d3}()},{}],11:[function(a,b,c){c.Digraph=a("graphlib").Digraph,c.Graph=a("graphlib").Graph,c.layout=a("./lib/layout"),c.version=a("./lib/version")},{"./lib/layout":12,"./lib/version":27,graphlib:28}],12:[function(a,b,c){var d=a("./util"),e=a("./rank"),f=a("./order"),g=a("graphlib").CGraph,h=a("graphlib").CDigraph;b.exports=function(){function j(a){var c=new h;a.eachNode(function(a,b){b===undefined&&(b={}),c.addNode(a,{width:b.width,height:b.height}),b.hasOwnProperty("rank")&&(c.node(a).prefRank=b.rank)}),a.parent&&a.nodes().forEach(function(b){c.parent(b,a.parent(b))}),a.eachEdge(function(a,b,d,e){e===undefined&&(e={});var f={e:a,minLen:e.minLen||1,width:e.width||0,height:e.height||0,points:[]};c.addEdge(null,b,d,f)});var d=a.graph()||{};return c.graph({rankDir:d.rankDir||b.rankDir,orderRestarts:d.orderRestarts}),c}function k(a){var g=i.rankSep(),h;try{return h=d.time("initLayoutGraph",j)(a),h.order()===0?h:(h.eachEdge(function(a,b,c,d){d.minLen*=2}),i.rankSep(g/2),d.time("rank.run",e.run)(h,b.rankSimplex),d.time("normalize",l)(h),d.time("order",f)(h,b.orderMaxSweeps),d.time("position",c.run)(h),d.time("undoNormalize",m)(h),d.time("fixupEdgePoints",n)(h),d.time("rank.restoreEdges",e.restoreEdges)(h),d.time("createFinalGraph",o)(h,a.isDirected()))}finally{i.rankSep(g)}}function l(a){var b=0;a.eachEdge(function(c,d,e,f){var g=a.node(d).rank,h=a.node(e).rank;if(g+1<h){for(var i=d,j=g+1,k=0;j<h;++j,++k){var l="_D"+ ++b,m={width:f.width,height:f.height,edge:{id:c,source:d,target:e,attrs:f},rank:j,dummy:!0};k===0?m.index=0:j+1===h&&(m.index=1),a.addNode(l,m),a.addEdge(null,i,l,{}),i=l}a.addEdge(null,i,e,{}),a.delEdge(c)}})}function m(a){a.eachNode(function(b,c){if(c.dummy){if("index"in c){var d=c.edge;a.hasEdge(d.id)||a.addEdge(d.id,d.source,d.target,d.attrs);var e=a.edge(d.id).points;e[c.index]={x:c.x,y:c.y,ul:c.ul,ur:c.ur,dl:c.dl,dr:c.dr}}a.delNode(b)}})}function n(a){a.eachEdge(function(a,b,c,d){d.reversed&&d.points.reverse()})}function o(a,b){var c=b?new h:new g;c.graph(a.graph()),a.eachNode(function(a,b){c.addNode(a,b)}),a.eachNode(function(b){c.parent(b,a.parent(b))}),a.eachEdge(function(a,b,d,e){c.addEdge(e.e,b,d,e)});var d=0,e=0;return a.eachNode(function(b,c){a.children(b).length||(d=Math.max(d,c.x+c.width/2),e=Math.max(e,c.y+c.height/2))}),a.eachEdge(function(a,b,c,f){var g=Math.max.apply(Math,f.points.map(function(a){return a.x})),h=Math.max.apply(Math,f.points.map(function(a){return a.y}));d=Math.max(d,g+f.width/2),e=Math.max(e,h+f.height/2)}),c.graph().width=d,c.graph().height=e,c}function p(a){return function(){return arguments.length?(a.apply(null,arguments),i):a()}}var b={debugLevel:0,orderMaxSweeps:f.DEFAULT_MAX_SWEEPS,rankSimplex:!1,rankDir:"TB"},c=a("./position")(),i={};return i.orderIters=d.propertyAccessor(i,b,"orderMaxSweeps"),i.rankSimplex=d.propertyAccessor(i,b,"rankSimplex"),i.nodeSep=p(c.nodeSep),i.edgeSep=p(c.edgeSep),i.universalSep=p(c.universalSep),i.rankSep=p(c.rankSep),i.rankDir=d.propertyAccessor(i,b,"rankDir"),i.debugAlignment=p(c.debugAlignment),i.debugLevel=d.propertyAccessor(i,b,"debugLevel",function(a){d.log.level=a,c.debugLevel(a)}),i.run=d.time("Total layout",k),i._normalize=l,i}},{"./order":13,"./position":18,"./rank":19,"./util":26,graphlib:28}],13:[function(a,b,c){function k(a,b){function o(){a.eachNode(function(a,b){m[a]=b.order})}arguments.length<2&&(b=j);var c=a.graph().orderRestarts||0,h=f(a);h.forEach(function(b){b=b.filterNodes(function(b){return!a.children(b).length})});var i=0,k,l=Number.MAX_VALUE,m={};for(var p=0;p<Number(c)+1&&l!==0;++p){k=Number.MAX_VALUE,g(a,c>0),d.log(2,"Order phase start cross count: "+a.graph().orderInitCC);var q,r,s;for(q=0,r=0;r<4&&q<b&&k>0;++q,++r,++i)n(a,h,q),s=e(a),s<k&&(r=0,k=s,s<l&&(o(),l=s)),d.log(3,"Order phase start "+p+" iter "+q+" cross count: "+s)}Object.keys(m).forEach(function(b){if(!a.children||!a.children(b).length)a.node(b).order=m[b]}),a.graph().orderCC=l,d.log(2,"Order iterations: "+i),d.log(2,"Order phase best cross count: "+a.graph().orderCC)}function l(a,b){var c={};return b.forEach(function(b){c[b]=a.inEdges(b).map(function(b){return a.node(a.source(b)).order})}),c}function m(a,b){var c={};return b.forEach(function(b){c[b]=a.outEdges(b).map(function(b){return a.node(a.target(b)).order})}),c}function n(a,b,c){c%2===0?o(a,b,c):p(a,b,c)}function o(a,b){var c;for(i=1;i<b.length;++i)c=h(b[i],c,l(a,b[i].nodes()))}function p(a,b){var c;for(i=b.length-2;i>=0;--i)h(b[i],c,m(a,b[i].nodes()))}var d=a("./util"),e=a("./order/crossCount"),f=a("./order/initLayerGraphs"),g=a("./order/initOrder"),h=a("./order/sortLayer");b.exports=k;var j=24;k.DEFAULT_MAX_SWEEPS=j},{"./order/crossCount":14,"./order/initLayerGraphs":15,"./order/initOrder":16,"./order/sortLayer":17,"./util":26}],14:[function(a,b,c){function e(a){var b=0,c=d.ordering(a);for(var e=1;e<c.length;++e)b+=f(a,c[e-1],c[e]);return b}function f(a,b,c){var d=[];b.forEach(function(b){var c=[];a.outEdges(b).forEach(function(b){c.push(a.node(a.target(b)).order)}),c.sort(function(a,b){return a-b}),d=d.concat(c)});var e=1;while(e<c.length)e<<=1;var f=2*e-1;e-=1;var g=[];for(var h=0;h<f;++h)g[h]=0;var i=0;return d.forEach(function(a){var b=a+e;++g[b];while(b>0)b%2&&(i+=g[b+1]),b=b-1>>1,++g[b]}),i}var d=a("../util");b.exports=e},{"../util":26}],15:[function(a,b,c){function f(a){function c(d){if(d===null){a.children(d).forEach(function(a){c(a)});return}var f=a.node(d);f.minRank="rank"in f?f.rank:Number.MAX_VALUE,f.maxRank="rank"in f?f.rank:Number.MIN_VALUE;var h=new e;return a.children(d).forEach(function(b){var d=c(b);h=e.union([h,d]),f.minRank=Math.min(f.minRank,a.node(b).minRank),f.maxRank=Math.max(f.maxRank,a.node(b).maxRank)}),"rank"in f&&h.add(f.rank),h.keys().forEach(function(a){a in b||(b[a]=[]),b[a].push(d)}),h}var b=[];c(null);var f=[];return b.forEach(function(b,c){f[c]=a.filterNodes(d(b))}),f}var d=a("graphlib").filter.nodesFromList,e=a("cp-data").Set;b.exports=f},{"cp-data":5,graphlib:28}],16:[function(a,b,c){function f(a,b){var c=[];a.eachNode(function(b,d){var e=c[d.rank];if(a.children&&a.children(b).length>0)return;e||(e=c[d.rank]=[]),e.push(b)}),c.forEach(function(c){b&&e.shuffle(c),c.forEach(function(b,c){a.node(b).order=c})});var f=d(a);a.graph().orderInitCC=f,a.graph().orderCC=Number.MAX_VALUE}var d=a("./crossCount"),e=a("../util");b.exports=f},{"../util":26,"./crossCount":14}],17:[function(a,b,c){function e(a,b,c){var e=[],f={};a.eachNode(function(a,b){e[b.order]=a;var g=c[a];g.length&&(f[a]=d.sum(g)/g.length)});var g=a.nodes().filter(function(a){return f[a]!==undefined});g.sort(function(b,c){return f[b]-f[c]||a.node(b).order-a.node(c).order});for(var h=0,i=0,j=g.length;i<j;++h)f[e[h]]!==undefined&&(a.node(g[i++]).order=h)}var d=a("../util");b.exports=e},{"../util":26}],18:[function(a,b,c){var d=a("./util");b.exports=function(){function c(b){b=b.filterNodes(d.filterNonSubgraphs(b));var c=d.ordering(b),e=f(b,c),i={};["u","d"].forEach(function(d){d==="d"&&c.reverse(),["l","r"].forEach(function(f){f==="r"&&m(c);var j=d+f,k=g(b,c,e,d==="u"?"predecessors":"successors");i[j]=h(b,c,k.pos,k.root,k.align),a.debugLevel>=3&&t(d+f,b,c,i[j]),f==="r"&&l(i[j]),f==="r"&&m(c)}),d==="d"&&c.reverse()}),k(b,c,i),b.eachNode(function(a){var c=[];for(var d in i){var e=i[d][a];r(d,b,a,e),c.push(e)}c.sort(function(a,b){return a-b}),q(b,a,(c[1]+c[2])/2)});var j=0,p=b.graph().rankDir==="BT"||b.graph().rankDir==="RL";c.forEach(function(c){var e=d.max(c.map(function(a){return o(b,a)}));j+=e/2,c.forEach(function(a){s(b,a,p?-j:j)}),j+=e/2+a.rankSep});var u=d.min(b.nodes().map(function(a){return q(b,a)-n(b,a)/2})),v=d.min(b.nodes().map(function(a){return s(b,a)-o(b,a)/2}));b.eachNode(function(a){q(b,a,q(b,a)-u),s(b,a,s(b,a)-v)})}function e(a,b){return a<b?a.toString().length+":"+a+"-"+b:b.toString().length+":"+b+"-"+a}function f(a,b){function k(a){var b=d[a];if(b<h||b>j)c[e(g[i],a)]=!0}var c={},d={},f,g,h,i,j;if(b.length<=2)return c;b[1].forEach(function(a,b){d[a]=b});for(var l=1;l<b.length-1;++l){f=b[l],g=b[l+1],h=0,i=0;for(var m=0;m<g.length;++m){var n=g[m];d[n]=m,j=undefined;if(a.node(n).dummy){var o=a.predecessors(n)[0];o!==undefined&&a.node(o).dummy&&(j=d[o])}j===undefined&&m===g.length-1&&(j=f.length-1);if(j!==undefined){for(;i<=m;++i)a.predecessors(g[i]).forEach(k);h=j}}}return c}function g(a,b,c,d){var f={},g={},h={};return b.forEach(function(a){a.forEach(function(a,b){g[a]=a,h[a]=a,f[a]=b})}),b.forEach(function(b){var i=-1;b.forEach(function(b){var j=a[d](b),k;j.length>0&&(j.sort(function(a,b){return f[a]-f[b]}),k=(j.length-1)/2,j.slice(Math.floor(k),Math.ceil(k)+1).forEach(function(a){h[b]===b&&!c[e(a,b)]&&i<f[a]&&(h[a]=b,h[b]=g[b]=g[a],i=f[a])}))})}),{pos:f,root:g,align:h}}function h(a,b,c,e,f){function l(a,b,c){b in h[a]?h[a][b]=Math.min(h[a][b],c):h[a][b]=c}function m(b){if(!(b in k)){k[b]=0;var d=b;do{if(c[d]>0){var h=e[j[d]];m(h),g[b]===b&&(g[b]=g[h]);var i=p(a,j[d])+p(a,d);g[b]!==g[h]?l(g[h],g[b],k[b]-k[h]-i):k[b]=Math.max(k[b],k[h]+i)}d=f[d]}while(d!==b)}}var g={},h={},i={},j={},k={};return b.forEach(function(a){a.forEach(function(b,c){g[b]=b,h[b]={},c>0&&(j[b]=a[c-1])})}),d.values(e).forEach(function(a){m(a)}),b.forEach(function(a){a.forEach(function(a){k[a]=k[e[a]];if(a===e[a]&&a===g[a]){var b=0;a in h&&Object.keys(h[a]).length>0&&(b=d.min(Object.keys(h[a]).map(function(b){return h[a][b]+(b in i?i[b]:0)}))),i[a]=b}})}),b.forEach(function(a){a.forEach(function(a){k[a]+=i[g[e[a]]]||0})}),k}function i(a,b,c){return d.min(b.map(function(a){var b=a[0];return c[b]}))}function j(a,b,c){return d.max(b.map(function(a){var b=a[a.length-1];return c[b]}))}function k(a,b,c){function h(a){c[l][a]+=g[l]}var d={},e={},f,g={},k=Number.POSITIVE_INFINITY;for(var l in c){var m=c[l];d[l]=i(a,b,m),e[l]=j(a,b,m);var n=e[l]-d[l];n<k&&(k=n,f=l)}["u","d"].forEach(function(a){["l","r"].forEach(function(b){var c=a+b;g[c]=b==="l"?d[f]-d[c]:e[f]-e[c]})});for(l in c)a.eachNode(h)}function l(a){for(var b in a)a[b]=-a[b]}function m(a){a.forEach(function(a){a.reverse()})}function n(a,b){switch(a.graph().rankDir){case"LR":return a.node(b).height;case"RL":return a.node(b).height;default:return a.node(b).width}}function o(a,b){switch(a.graph().rankDir){case"LR":return a.node(b).width;case"RL":return a.node(b).width;default:return a.node(b).height}}function p(b,c){if(a.universalSep!==null)return a.universalSep;var d=n(b,c),e=b.node(c).dummy?a.edgeSep:a.nodeSep;return(d+e)/2}function q(a,b,c){if(a.graph().rankDir==="LR"||a.graph().rankDir==="RL"){if(arguments.length<3)return a.node(b).y;a.node(b).y=c}else{if(arguments.length<3)return a.node(b).x;a.node(b).x=c}}function r(a,b,c,d){if(b.graph().rankDir==="LR"||b.graph().rankDir==="RL"){if(arguments.length<3)return b.node(c)[a];b.node(c)[a]=d}else{if(arguments.length<3)return b.node(c)[a];b.node(c)[a]=d}}function s(a,b,c){if(a.graph().rankDir==="LR"||a.graph().rankDir==="RL"){if(arguments.length<3)return a.node(b).x;a.node(b).x=c}else{if(arguments.length<3)return a.node(b).y;a.node(b).y=c}}function t(a,b,c,d){c.forEach(function(c,e){var f,g;c.forEach(function(c){var h=d[c];if(f){var i=p(b,f)+p(b,c);h-g<i&&console.log("Position phase: sep violation. Align: "+a+". Layer: "+e+". "+"U: "+f+" V: "+c+". Actual sep: "+(h-g)+" Expected sep: "+i)}f=c,g=h})})}var a={nodeSep:50,edgeSep:10,universalSep:null,rankSep:30},b={};return b.nodeSep=d.propertyAccessor(b,a,"nodeSep"),b.edgeSep=d.propertyAccessor(b,a,"edgeSep"),b.universalSep=d.propertyAccessor(b,a,"universalSep"),b.rankSep=d.propertyAccessor(b,a,"rankSep"),b.debugLevel=d.propertyAccessor(b,a,"debugLevel"),b.run=c,b}},{"./util":26}],19:[function(a,b,c){function l(a,b){n(a),d.time("constraints.apply",h.apply)(a),o(a),d.time("acyclic",e)(a);var c=a.filterNodes(d.filterNonSubgraphs(a));f(c),j(c).forEach(function(a){var d=c.filterNodes(k.nodesFromList(a));r(d,b)}),d.time("constraints.relax",h.relax(a)),d.time("reorientEdges",q)(a)}function m(a){e.undo(a)}function n(a){a.eachEdge(function(b,c,d,e){if(c===d){var f=p(a,b,c,d,e,0,!1),g=p(a,b,c,d,e,1,!0),h=p(a,b,c,d,e,2,!1);a.addEdge(null,f,c,{minLen:1,selfLoop:!0}),a.addEdge(null,f,g,{minLen:1,selfLoop:!0}),a.addEdge(null,c,h,{minLen:1,selfLoop:!0}),a.addEdge(null,g,h,{minLen:1,selfLoop:!0}),a.delEdge(b)}})}function o(a){a.eachEdge(function(b,c,d,e){if(c===d){var f=e.originalEdge,g=p(a,f.e,f.u,f.v,f.value,0,!0);a.addEdge(null,c,g,{minLen:1}),a.addEdge(null,g,d,{minLen:1}),a.delEdge(b)}})}function p(a,b,c,d,e,f,g){return a.addNode(null,{width:g?e.width:0,height:g?e.height:0,edge:{id:b,source:c,target:d,attrs:e},dummy:!0,index:f})}function q(a){a.eachEdge(function(b,c,d,e){a.node(c).rank>a.node(d).rank&&(a.delEdge(b),e.reversed=!0,a.addEdge(b,d,c,e))})}function r(a,b){var c=g(a);b&&(d.log(1,"Using network simplex for ranking"),i(a,c)),s(a)}function s(a){var b=d.min(a.nodes().map(function(b){return a.node(b).rank}));a.eachNode(function(a,c){c.rank-=b})}var d=a("./util"),e=a("./rank/acyclic"),f=a("./rank/initRank"),g=a("./rank/feasibleTree"),h=a("./rank/constraints"),i=a("./rank/simplex"),j=a("graphlib").alg.components,k=a("graphlib").filter;c.run=l,c.restoreEdges=m},{"./rank/acyclic":20,"./rank/constraints":21,"./rank/feasibleTree":22,"./rank/initRank":23,"./rank/simplex":25,"./util":26,graphlib:28}],20:[function(a,b,c){function e(a){function f(d){if(d in c)return;c[d]=b[d]=!0,a.outEdges(d).forEach(function(c){var h=a.target(c),i;d===h?console.error('Warning: found self loop "'+c+'" for node "'+d+'"'):h in b?(i=a.edge(c),a.delEdge(c),i.reversed=!0,++e,a.addEdge(c,h,d,i)):f(h)}),delete b[d]}var b={},c={},e=0;return a.eachNode(function(a){f(a)}),d.log(2,"Acyclic Phase: reversed "+e+" edge(s)"),e}function f(a){a.eachEdge(function(b,c,d,e){e.reversed&&(delete e.reversed,a.delEdge(b),a.addEdge(b,d,c,e))})}var d=a("../util");b.exports=e,b.exports.undo=f},{"../util":26}],21:[function(a,b,c){function d(a){return a!=="min"&&a!=="max"&&a.indexOf("same_")!==0?(console.error("Unsupported rank type: "+a),!1):!0}function e(a,b,c,d){a.inEdges(b).forEach(function(b){var e=a.edge(b),f;e.originalEdge?f=e:f={originalEdge:{e:b,u:a.source(b),v:a.target(b),value:e},minLen:a.edge(b).minLen},e.selfLoop&&(d=!1),d?(a.addEdge(null,c,a.source(b),f),f.reversed=!0):a.addEdge(null,a.source(b),c,f)})}function f(a,b,c,d){a.outEdges(b).forEach(function(b){var e=a.edge(b),f;e.originalEdge?f=e:f={originalEdge:{e:b,u:a.source(b),v:a.target(b),value:e},minLen:a.edge(b).minLen},e.selfLoop&&(d=!1),d?(a.addEdge(null,a.target(b),c,f),f.reversed=!0):a.addEdge(null,c,a.target(b),f)})}function g(a,b,c){c!==undefined&&a.children(b).forEach(function(b){b!==c&&!a.outEdges(c,b).length&&!a.node(b).dummy&&a.addEdge(null,c,b,{minLen:0})})}function h(a,b,c){c!==undefined&&a.children(b).forEach(function(b){b!==c&&!a.outEdges(b,c).length&&!a.node(b).dummy&&a.addEdge(null,b,c,{minLen:0})})}c.apply=function(a){function b(c){var i={};a.children(c).forEach(function(g){if(a.children(g).length){b(g);return}var h=a.node(g),j=h.prefRank;if(j!==undefined){if(!d(j))return;j in i?i.prefRank.push(g):i.prefRank=[g];var k=i[j];k===undefined&&(k=i[j]=a.addNode(null,{originalNodes:[]}),a.parent(k,c)),e(a,g,k,j==="min"),f(a,g,k,j==="max"),a.node(k).originalNodes.push({u:g,value:h,parent:c}),a.delNode(g)}}),g(a,c,i.min),h(a,c,i.max)}b(null)},c.relax=function(a){var b=[];a.eachEdge(function(a,c,d,e){var f=e.originalEdge;f&&b.push(f)}),a.eachNode(function(b,c){var d=c.originalNodes;d&&(d.forEach(function(b){b.value.rank=c.rank,a.addNode(b.u,b.value),a.parent(b.u,b.parent)}),a.delNode(b))}),b.forEach(function(b){a.addEdge(b.e,b.u,b.v,b.value)})}},{}],22:[function(a,b,c){function g(a){function g(d){var e=!0;return a.predecessors(d).forEach(function(f){b.has(f)&&!h(a,f,d)&&(b.has(d)&&(c.addNode(d,{}),b.remove(d),c.graph({root:d})),c.addNode(f,{}),c.addEdge(null,f,d,{reversed:!0}),b.remove(f),g(f),e=!1)}),a.successors(d).forEach(function(f){b.has(f)&&!h(a,d,f)&&(b.has(d)&&(c.addNode(d,{}),b.remove(d),c.graph({root:d})),c.addNode(f,{}),c.addEdge(null,d,f,{}),b.remove(f),g(f),e=!1)}),e}function i(){var d=Number.MAX_VALUE;b.keys().forEach(function(c){a.predecessors(c).forEach(function(e){if(!b.has(e)){var f=h(a,e,c);Math.abs(f)<Math.abs(d)&&(d=-f)}}),a.successors(c).forEach(function(e){if(!b.has(e)){var f=h(a,c,e);Math.abs(f)<Math.abs(d)&&(d=f)}})}),c.eachNode(function(b){a.node(b).rank-=d})}var b=new d(a.nodes()),c=new e;if(b.size()===1){var f=a.nodes()[0];return c.addNode(f,{}),c.graph({root:f}),c}while(b.size()){var j=c.order()?c.nodes():b.keys();for(var k=0,l=j.length;k<l&&g(j[k]);++k);b.size()&&i()}return c}function h(a,b,c){var d=a.node(c).rank-a.node(b).rank,e=f.max(a.outEdges(b,c).map(function(b){return a.edge(b).minLen}));return d-e}var d=a("cp-data").Set,e=a("graphlib").Digraph,f=a("../util");b.exports=g},{"../util":26,"cp-data":5,graphlib:28}],23:[function(a,b,c){function f(a){var b=e(a);b.forEach(function(b){var c=a.inEdges(b);if(c.length===0){a.node(b).rank=0;return}var e=c.map(function(b){return a.node(a.source(b)).rank+a.edge(b).minLen});a.node(b).rank=d.max(e)})}var d=a("../util"),e=a("graphlib").alg.topsort;b.exports=f},{"../util":26,graphlib:28}],24:[function(a,b,c){function d(a,b,c,d){return Math.abs(a.node(b).rank-a.node(c).rank)-d}b.exports={slack:d}},{}],25:[function(a,b,c){function f(a,b){g(a,b);for(;;){var c=k(b);if(c===null)break;var d=l(a,b,c);m(a,b,c,d)}}function g(a,b){function c(d){var e=b.successors(d);for(var f in e){var g=e[f];c(g)}d!==b.graph().root&&i(a,b,d)}h(b),b.eachEdge(function(a,b,c,d){d.cutValue=0}),c(b.graph().root)}function h(a){function c(d){var e=a.successors(d),f=b;for(var g in e){var h=e[g];c(h),f=Math.min(f,a.node(h).low)}a.node(d).low=f,a.node(d).lim=b++}var b=0;c(a.graph().root)}function i(a,b,c){var d=b.inEdges(c)[0],e=[],f=b.outEdges(c);for(var g in f)e.push(b.target(f[g]));var h=0,i=0,k=0,l=0,m=0,n=a.outEdges(c),o;for(var p in n){var q=a.target(n[p]);for(o in e)j(b,q,e[o])&&i++;j(b,q,c)||l++}var r=a.inEdges(c);for(var s in r){var t=a.source(r[s]);for(o in e)j(b,t,e[o])&&k++;j(b,t,c)||m++}var u=0;for(o in e){var v=b.edge(f[o]).cutValue;b.edge(f[o]).reversed?u-=v:u+=v}b.edge(d).reversed?h-=u-i+k-l+m:h+=u-i+k-l+m,b.edge(d).cutValue=h}function j(a,b,c){return a.node(c).low<=a.node(b).lim&&a.node(b).lim<=a.node(c).lim}function k(a){var b=a.edges();for(var c in b){var d=b[c],e=a.edge(d);if(e.cutValue<0)return d}return null}function l(a,b,c){var d=b.source(c),f=b.target(c),g=b.node(f).lim<b.node(d).lim?f:d,h=!b.edge(c).reversed,i=Number.POSITIVE_INFINITY,k;h?a.eachEdge(function(d,f,h,l){if(d!==c&&j(b,f,g)&&!j(b,h,g)){var m=e.slack(a,f,h,l.minLen);m<i&&(i=m,k=d)}}):a.eachEdge(function(d,f,h,l){if(d!==c&&!j(b,f,g)&&j(b,h,g)){var m=e.slack(a,f,h,l.minLen);m<i&&(i=m,k=d)}});if(k===undefined){var l=[],m=[];throw a.eachNode(function(a){j(b,a,g)?m.push(a):l.push(a)}),new Error("No edge found from outside of tree to inside")}return k}function m(a,b,c,d){function h(a){var c=b.inEdges(a);for(var d in c){var e=c[d],f=b.source(e),g=b.edge(e);h(f),b.delEdge(e),g.reversed=!g.reversed,b.addEdge(e,a,f,g)}}b.delEdge(c);var e=a.source(d),f=a.target(d);h(f);var i=e,j=b.inEdges(i);while(j.length>0)i=b.source(j[0]),j=b.inEdges(i);b.graph().root=i,b.addEdge(null,e,f,{cutValue:0}),g(a,b),n(a,b)}function n(a,b){function c(d){var e=b.successors(d);e.forEach(function(b){var e=o(a,d,b);a.node(b).rank=a.node(d).rank+e,c(b)})}c(b.graph().root)}function o(a,b,c){var e=a.outEdges(b,c);if(e.length>0)return d.max(e.map(function(b){return a.edge(b).minLen}));var f=a.inEdges(b,c);if(f.length>0)return-d.max(f.map(function(b){return a.edge(b).minLen}))}var d=a("../util"),e=a("./rankUtil");b.exports=f},{"../util":26,"./rankUtil":24}],26:[function(a,b,c){function d(a,b){return function(){var c=(new Date).getTime();try{return b.apply(null,arguments)}finally{e(1,a+" time: "+((new Date).getTime()-c)+"ms")}}}function e(a){e.level>=a&&console.log.apply(console,Array.prototype.slice.call(arguments,1))}c.min=function(a){return Math.min.apply(Math,a)},c.max=function(a){return Math.max.apply(Math,a)},c.all=function(a,b){for(var c=0;c<a.length;++c)if(!b(a[c]))return!1;return!0},c.sum=function(a){return a.reduce(function(a,b){return a+b},0)},c.values=function(a){return Object.keys(a).map(function(b){return a[b]})},c.shuffle=function(a){for(i=a.length-1;i>0;--i){var b=Math.floor(Math.random()*(i+1)),c=a[b];a[b]=a[i],a[i]=c}},c.propertyAccessor=function(a,b,c,d){return function(e){return arguments.length?(b[c]=e,d&&d(e),a):b[c]}},c.ordering=function(a){var b=[];return a.eachNode(function(a,c){var d=b[c.rank]||(b[c.rank]=[]);d[c.order]=a}),b},c.filterNonSubgraphs=function(a){return function(b){return a.children(b).length===0}},d.enabled=!1,c.time=d,e.level=0,c.log=e},{}],27:[function(a,b,c){b.exports="0.4.5"},{}],28:[function(a,b,c){c.Graph=a("./lib/Graph"),c.Digraph=a("./lib/Digraph"),c.CGraph=a("./lib/CGraph"),c.CDigraph=a("./lib/CDigraph"),a("./lib/graph-converters"),c.alg={isAcyclic:a("./lib/alg/isAcyclic"),components:a("./lib/alg/components"),dijkstra:a("./lib/alg/dijkstra"),dijkstraAll:a("./lib/alg/dijkstraAll"),findCycles:a("./lib/alg/findCycles"),floydWarshall:a("./lib/alg/floydWarshall"),postorder:a("./lib/alg/postorder"),preorder:a("./lib/alg/preorder"),prim:a("./lib/alg/prim"),tarjan:a("./lib/alg/tarjan"),topsort:a("./lib/alg/topsort")},c.converter={json:a("./lib/converter/json.js")};var d=a("./lib/filter");c.filter={all:d.all,nodesFromList:d.nodesFromList},c.version=a("./lib/version")},{"./lib/CDigraph":30,"./lib/CGraph":31,"./lib/Digraph":32,"./lib/Graph":33,"./lib/alg/components":34,"./lib/alg/dijkstra":35,"./lib/alg/dijkstraAll":36,"./lib/alg/findCycles":37,"./lib/alg/floydWarshall":38,"./lib/alg/isAcyclic":39,"./lib/alg/postorder":40,"./lib/alg/preorder":41,"./lib/alg/prim":42,"./lib/alg/tarjan":43,"./lib/alg/topsort":44,"./lib/converter/json.js":46,"./lib/filter":47,"./lib/graph-converters":48,"./lib/version":50}],29:[function(a,b,c){function e(){this._value=undefined,this._nodes={},this._edges={},this._nextId=0}function f(a,b,c){(a[b]||(a[b]=new d)).add(
c)}function g(a,b,c){var d=a[b];d.remove(c),d.size()===0&&delete a[b]}var d=a("cp-data").Set;b.exports=e,e.prototype.order=function(){return Object.keys(this._nodes).length},e.prototype.size=function(){return Object.keys(this._edges).length},e.prototype.graph=function(a){if(arguments.length===0)return this._value;this._value=a},e.prototype.hasNode=function(a){return a in this._nodes},e.prototype.node=function(a,b){var c=this._strictGetNode(a);if(arguments.length===1)return c.value;c.value=b},e.prototype.nodes=function(){var a=[];return this.eachNode(function(b){a.push(b)}),a},e.prototype.eachNode=function(a){for(var b in this._nodes){var c=this._nodes[b];a(c.id,c.value)}},e.prototype.hasEdge=function(a){return a in this._edges},e.prototype.edge=function(a,b){var c=this._strictGetEdge(a);if(arguments.length===1)return c.value;c.value=b},e.prototype.edges=function(){var a=[];return this.eachEdge(function(b){a.push(b)}),a},e.prototype.eachEdge=function(a){for(var b in this._edges){var c=this._edges[b];a(c.id,c.u,c.v,c.value)}},e.prototype.incidentNodes=function(a){var b=this._strictGetEdge(a);return[b.u,b.v]},e.prototype.addNode=function(a,b){if(a===undefined||a===null){do a="_"+ ++this._nextId;while(this.hasNode(a))}else if(this.hasNode(a))throw new Error("Graph already has node '"+a+"'");return this._nodes[a]={id:a,value:b},a},e.prototype.delNode=function(a){this._strictGetNode(a),this.incidentEdges(a).forEach(function(a){this.delEdge(a)},this),delete this._nodes[a]},e.prototype._addEdge=function(a,b,c,d,e,g){this._strictGetNode(b),this._strictGetNode(c);if(a===undefined||a===null){do a="_"+ ++this._nextId;while(this.hasEdge(a))}else if(this.hasEdge(a))throw new Error("Graph already has edge '"+a+"'");return this._edges[a]={id:a,u:b,v:c,value:d},f(e[c],b,a),f(g[b],c,a),a},e.prototype._delEdge=function(a,b,c){var d=this._strictGetEdge(a);g(b[d.v],d.u,a),g(c[d.u],d.v,a),delete this._edges[a]},e.prototype.copy=function(){var a=new this.constructor;return a.graph(this.graph()),this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(b,c,d,e)}),a._nextId=this._nextId,a},e.prototype.filterNodes=function(a){var b=new this.constructor;return b.graph(this.graph()),this.eachNode(function(c,d){a(c)&&b.addNode(c,d)}),this.eachEdge(function(a,c,d,e){b.hasNode(c)&&b.hasNode(d)&&b.addEdge(a,c,d,e)}),b},e.prototype._strictGetNode=function(a){var b=this._nodes[a];if(b===undefined)throw new Error("Node '"+a+"' is not in graph");return b},e.prototype._strictGetEdge=function(a){var b=this._edges[a];if(b===undefined)throw new Error("Edge '"+a+"' is not in graph");return b}},{"cp-data":5}],30:[function(a,b,c){var d=a("./Digraph"),e=a("./compoundify"),f=e(d);b.exports=f,f.fromDigraph=function(a){var b=new f,c=a.graph();return c!==undefined&&b.graph(c),a.eachNode(function(a,c){c===undefined?b.addNode(a):b.addNode(a,c)}),a.eachEdge(function(a,c,d,e){e===undefined?b.addEdge(null,c,d):b.addEdge(null,c,d,e)}),b},f.prototype.toString=function(){return"CDigraph "+JSON.stringify(this,null,2)}},{"./Digraph":32,"./compoundify":45}],31:[function(a,b,c){var d=a("./Graph"),e=a("./compoundify"),f=e(d);b.exports=f,f.fromGraph=function(a){var b=new f,c=a.graph();return c!==undefined&&b.graph(c),a.eachNode(function(a,c){c===undefined?b.addNode(a):b.addNode(a,c)}),a.eachEdge(function(a,c,d,e){e===undefined?b.addEdge(null,c,d):b.addEdge(null,c,d,e)}),b},f.prototype.toString=function(){return"CGraph "+JSON.stringify(this,null,2)}},{"./Graph":33,"./compoundify":45}],32:[function(a,b,c){function g(){e.call(this),this._inEdges={},this._outEdges={}}var d=a("./util"),e=a("./BaseGraph"),f=a("cp-data").Set;b.exports=g,g.prototype=new e,g.prototype.constructor=g,g.prototype.isDirected=function(){return!0},g.prototype.successors=function(a){return this._strictGetNode(a),Object.keys(this._outEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.predecessors=function(a){return this._strictGetNode(a),Object.keys(this._inEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.neighbors=function(a){return f.union([this.successors(a),this.predecessors(a)]).keys()},g.prototype.sources=function(){var a=this;return this._filterNodes(function(b){return a.inEdges(b).length===0})},g.prototype.sinks=function(){var a=this;return this._filterNodes(function(b){return a.outEdges(b).length===0})},g.prototype.source=function(a){return this._strictGetEdge(a).u},g.prototype.target=function(a){return this._strictGetEdge(a).v},g.prototype.inEdges=function(a,b){this._strictGetNode(a);var c=f.union(d.values(this._inEdges[a])).keys();return arguments.length>1&&(this._strictGetNode(b),c=c.filter(function(a){return this.source(a)===b},this)),c},g.prototype.outEdges=function(a,b){this._strictGetNode(a);var c=f.union(d.values(this._outEdges[a])).keys();return arguments.length>1&&(this._strictGetNode(b),c=c.filter(function(a){return this.target(a)===b},this)),c},g.prototype.incidentEdges=function(a,b){return arguments.length>1?f.union([this.outEdges(a,b),this.outEdges(b,a)]).keys():f.union([this.inEdges(a),this.outEdges(a)]).keys()},g.prototype.toString=function(){return"Digraph "+JSON.stringify(this,null,2)},g.prototype.addNode=function(a,b){return a=e.prototype.addNode.call(this,a,b),this._inEdges[a]={},this._outEdges[a]={},a},g.prototype.delNode=function(a){e.prototype.delNode.call(this,a),delete this._inEdges[a],delete this._outEdges[a]},g.prototype.addEdge=function(a,b,c,d){return e.prototype._addEdge.call(this,a,b,c,d,this._inEdges,this._outEdges)},g.prototype.delEdge=function(a){e.prototype._delEdge.call(this,a,this._inEdges,this._outEdges)},g.prototype._filterNodes=function(a){var b=[];return this.eachNode(function(c){a(c)&&b.push(c)}),b}},{"./BaseGraph":29,"./util":49,"cp-data":5}],33:[function(a,b,c){function g(){e.call(this),this._incidentEdges={}}var d=a("./util"),e=a("./BaseGraph"),f=a("cp-data").Set;b.exports=g,g.prototype=new e,g.prototype.constructor=g,g.prototype.isDirected=function(){return!1},g.prototype.neighbors=function(a){return this._strictGetNode(a),Object.keys(this._incidentEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.incidentEdges=function(a,b){return this._strictGetNode(a),arguments.length>1?(this._strictGetNode(b),b in this._incidentEdges[a]?this._incidentEdges[a][b].keys():[]):f.union(d.values(this._incidentEdges[a])).keys()},g.prototype.toString=function(){return"Graph "+JSON.stringify(this,null,2)},g.prototype.addNode=function(a,b){return a=e.prototype.addNode.call(this,a,b),this._incidentEdges[a]={},a},g.prototype.delNode=function(a){e.prototype.delNode.call(this,a),delete this._incidentEdges[a]},g.prototype.addEdge=function(a,b,c,d){return e.prototype._addEdge.call(this,a,b,c,d,this._incidentEdges,this._incidentEdges)},g.prototype.delEdge=function(a){e.prototype._delEdge.call(this,a,this._incidentEdges,this._incidentEdges)}},{"./BaseGraph":29,"./util":49,"cp-data":5}],34:[function(a,b,c){function e(a){function e(b,d){c.has(b)||(c.add(b),d.push(b),a.neighbors(b).forEach(function(a){e(a,d)}))}var b=[],c=new d;return a.nodes().forEach(function(a){var c=[];e(a,c),c.length>0&&b.push(c)}),b}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],35:[function(a,b,c){function e(a,b,c,e){function h(b){var d=a.incidentNodes(b),e=d[0]!==i?d[0]:d[1],h=f[e],k=c(b),l=j.distance+k;if(k<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+b+" Weight: "+k);l<h.distance&&(h.distance=l,h.predecessor=i,g.decrease(e,l))}var f={},g=new d;c=c||function(){return 1},e=e||(a.isDirected()?function(b){return a.outEdges(b)}:function(b){return a.incidentEdges(b)}),a.eachNode(function(a){var c=a===b?0:Number.POSITIVE_INFINITY;f[a]={distance:c},g.add(a,c)});var i,j;while(g.size()>0){i=g.removeMin(),j=f[i];if(j.distance===Number.POSITIVE_INFINITY)break;e(i).forEach(h)}return f}var d=a("cp-data").PriorityQueue;b.exports=e},{"cp-data":5}],36:[function(a,b,c){function e(a,b,c){var e={};return a.eachNode(function(f){e[f]=d(a,f,b,c)}),e}var d=a("./dijkstra");b.exports=e},{"./dijkstra":35}],37:[function(a,b,c){function e(a){return d(a).filter(function(a){return a.length>1})}var d=a("./tarjan");b.exports=e},{"./tarjan":43}],38:[function(a,b,c){function d(a,b,c){var d={},e=a.nodes();return b=b||function(){return 1},c=c||(a.isDirected()?function(b){return a.outEdges(b)}:function(b){return a.incidentEdges(b)}),e.forEach(function(f){d[f]={},d[f][f]={distance:0},e.forEach(function(a){f!==a&&(d[f][a]={distance:Number.POSITIVE_INFINITY})}),c(f).forEach(function(c){var e=a.incidentNodes(c),h=e[0]!==f?e[0]:e[1],i=b(c);i<d[f][h].distance&&(d[f][h]={distance:i,predecessor:f})})}),e.forEach(function(a){var b=d[a];e.forEach(function(c){var f=d[c];e.forEach(function(c){var d=f[a],e=b[c],g=f[c],h=d.distance+e.distance;h<g.distance&&(g.distance=h,g.predecessor=e.predecessor)})})}),d}b.exports=d},{}],39:[function(a,b,c){function e(a){try{d(a)}catch(b){if(b instanceof d.CycleException)return!1;throw b}return!0}var d=a("./topsort");b.exports=e},{"./topsort":44}],40:[function(a,b,c){function e(a,b,c){function f(b,d){if(e.has(b))throw new Error("The input graph is not a tree: "+a);e.add(b),a.neighbors(b).forEach(function(a){a!==d&&f(a,b)}),c(b)}var e=new d;if(a.isDirected())throw new Error("This function only works for undirected graphs");f(b)}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],41:[function(a,b,c){function e(a,b,c){function f(b,d){if(e.has(b))throw new Error("The input graph is not a tree: "+a);e.add(b),c(b),a.neighbors(b).forEach(function(a){a!==d&&f(a,b)})}var e=new d;if(a.isDirected())throw new Error("This function only works for undirected graphs");f(b)}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],42:[function(a,b,c){function f(a,b){function i(c){var d=a.incidentNodes(c),e=d[0]!==h?d[0]:d[1],i=g.priority(e);if(i!==undefined){var j=b(c);j<i&&(f[e]=h,g.decrease(e,j))}}var c=new d,f={},g=new e,h;if(a.order()===0)return c;a.eachNode(function(a){g.add(a,Number.POSITIVE_INFINITY),c.addNode(a)}),g.decrease(a.nodes()[0],0);var j=!1;while(g.size()>0){h=g.removeMin();if(h in f)c.addEdge(null,h,f[h]);else{if(j)throw new Error("Input graph is not connected: "+a);j=!0}a.incidentEdges(h).forEach(i)}return c}var d=a("../Graph"),e=a("cp-data").PriorityQueue;b.exports=f},{"../Graph":33,"cp-data":5}],43:[function(a,b,c){function d(a){function f(h){var i=d[h]={onStack:!0,lowlink:b,index:b++};c.push(h),a.successors(h).forEach(function(a){a in d?d[a].onStack&&(i.lowlink=Math.min(i.lowlink,d[a].index)):(f(a),i.lowlink=Math.min(i.lowlink,d[a].lowlink))});if(i.lowlink===i.index){var j=[],k;do k=c.pop(),d[k].onStack=!1,j.push(k);while(h!==k);e.push(j)}}if(!a.isDirected())throw new Error("tarjan can only be applied to a directed graph. Bad input: "+a);var b=0,c=[],d={},e=[];return a.nodes().forEach(function(a){a in d||f(a)}),e}b.exports=d},{}],44:[function(a,b,c){function d(a){function f(g){if(g in c)throw new e;g in b||(c[g]=!0,b[g]=!0,a.predecessors(g).forEach(function(a){f(a)}),delete c[g],d.push(g))}if(!a.isDirected())throw new Error("topsort can only be applied to a directed graph. Bad input: "+a);var b={},c={},d=[],g=a.sinks();if(a.order()!==0&&g.length===0)throw new e;return a.sinks().forEach(function(a){f(a)}),d}function e(){}b.exports=d,d.CycleException=e,e.prototype.toString=function(){return"Graph has at least one cycle"}},{}],45:[function(a,b,c){function e(a){function b(){a.call(this),this._parents={},this._children={},this._children[null]=new d}return b.prototype=new a,b.prototype.constructor=b,b.prototype.parent=function(a,b){this._strictGetNode(a);if(arguments.length<2)return this._parents[a];if(a===b)throw new Error("Cannot make "+a+" a parent of itself");b!==null&&this._strictGetNode(b),this._children[this._parents[a]].remove(a),this._parents[a]=b,this._children[b].add(a)},b.prototype.children=function(a){return a!==null&&this._strictGetNode(a),this._children[a].keys()},b.prototype.addNode=function(b,c){return b=a.prototype.addNode.call(this,b,c),this._parents[b]=null,this._children[b]=new d,this._children[null].add(b),b},b.prototype.delNode=function(b){var c=this.parent(b);return this._children[b].keys().forEach(function(a){this.parent(a,c)},this),this._children[c].remove(b),delete this._parents[b],delete this._children[b],a.prototype.delNode.call(this,b)},b.prototype.copy=function(){var b=a.prototype.copy.call(this);return this.nodes().forEach(function(a){b.parent(a,this.parent(a))},this),b},b.prototype.filterNodes=function(b){function f(a){var b=c.parent(a);return b===null||d.hasNode(b)?(e[a]=b,b):b in e?e[b]:f(b)}var c=this,d=a.prototype.filterNodes.call(this,b),e={};return d.eachNode(function(a){d.parent(a,f(a))}),d},b}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],46:[function(a,b,c){function h(a){return Object.prototype.toString.call(a).slice(8,-1)}var d=a("../Graph"),e=a("../Digraph"),f=a("../CGraph"),g=a("../CDigraph");c.decode=function(a,b,c){c=c||e;if(h(a)!=="Array")throw new Error("nodes is not an Array");if(h(b)!=="Array")throw new Error("edges is not an Array");if(typeof c=="string")switch(c){case"graph":c=d;break;case"digraph":c=e;break;case"cgraph":c=f;break;case"cdigraph":c=g;break;default:throw new Error("Unrecognized graph type: "+c)}var i=new c;return a.forEach(function(a){i.addNode(a.id,a.value)}),i.parent&&a.forEach(function(a){a.children&&a.children.forEach(function(b){i.parent(b,a.id)})}),b.forEach(function(a){i.addEdge(a.id,a.u,a.v,a.value)}),i},c.encode=function(a){var b=[],c=[];a.eachNode(function(c,d){var e={id:c,value:d};if(a.children){var f=a.children(c);f.length&&(e.children=f)}b.push(e)}),a.eachEdge(function(a,b,d,e){c.push({id:a,u:b,v:d,value:e})});var h;if(a instanceof g)h="cdigraph";else if(a instanceof f)h="cgraph";else if(a instanceof e)h="digraph";else if(a instanceof d)h="graph";else throw new Error("Couldn't determine type of graph: "+a);return{nodes:b,edges:c,type:h}}},{"../CDigraph":30,"../CGraph":31,"../Digraph":32,"../Graph":33}],47:[function(a,b,c){var d=a("cp-data").Set;c.all=function(){return function(){return!0}},c.nodesFromList=function(a){var b=new d(a);return function(a){return b.has(a)}}},{"cp-data":5}],48:[function(a,b,c){var d=a("./Graph"),e=a("./Digraph");d.prototype.toDigraph=d.prototype.asDirected=function(){var a=new e;return this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(null,c,d,e),a.addEdge(null,d,c,e)}),a},e.prototype.toGraph=e.prototype.asUndirected=function(){var a=new d;return this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(b,c,d,e)}),a}},{"./Digraph":32,"./Graph":33}],49:[function(a,b,c){c.values=function(a){var b=Object.keys(a),c=b.length,d=new Array(c),e;for(e=0;e<c;++e)d[e]=a[b[e]];return d}},{}],50:[function(a,b,c){b.exports="0.7.4"},{}]},{},[1]);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="d3viz/css/d3viz.css"/>
<link rel="stylesheet" href="d3viz/css/d3-context-menu.css"/>
<script type="text/javascript" src="d3viz/js/d3viz.js"></script>
<script type="text/javascript" src="d3viz/js/d3.v3.min.js"></script>
<script type="text/javascript" src="d3viz/js/dagre-d3.min.js"></script>
<script type="text/javascript" src="d3viz/js/graphlib-dot.min.js"></script>
<script type="text/javascript" src="d3viz/js/d3-context-menu.js"></script>
</head>
<body>
<div id='menu' class='menuBar'>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
// Backend graph in DOT format
var dotGraph = graphlibDot.read("digraph G { graph [bb=\"0,0,272,476\"]; node [label=\"\N\"]; n1 [apply_op=DimShuffle, height=0.5, label=DimShuffle, node_type=apply, pos=\"219,370\", shape=ellipse, width=1.4763]; n8 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"170,282\", shape=ellipse, style=filled, type=colored, width=1.2888]; n1 -> n8 [label=\"1 dmatrix\", lp=\"225.5,326\", pos=\"e,179.49,299.66 209.32,352.01 202.23,339.56 192.48,322.45 184.47,308.39\"]; n2 [dtype=dvector, fillcolor=YellowGreen, height=0.5, label=dvector, node_type=shared_input, pos=\"219,458\", shape=box, style=filled, width=0.80556]; n2 -> n1 [color=dodgerblue, label=dvector, lp=\"240,414\", pos=\"e,219,388.08 219,439.6 219,427.75 219,411.82 219,398.29\"]; n4 [apply_op=Dot22, height=0.5, label=Dot22, node_type=apply, pos=\"114,370\", shape=ellipse, width=0.92774]; n4 -> n8 [color=red, label=\"0 dmatrix\", lp=\"158.5,326\", pos=\"e,148.95,298.37 117.35,351.95 119.93,341.54 124.24,328.3 131,318 133.92,313.55 137.54,309.31 141.39,305.41\"]; n5 [dtype=dmatrix, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"50,458\", shape=box, style=filled, width=0.75]; n5 -> n4 [label=\"0 dmatrix\", lp=\"88.5,414\", pos=\"e,89.174,382.09 50.492,439.64 51.537,429.12 54.251,415.87 61,406 66.011,398.67 73.111,392.49 80.491,387.46\"]; n6 [dtype=dmatrix, fillcolor=YellowGreen, height=0.5, label=dmatrix, node_type=shared_input, pos=\"128,458\", shape=box, style=filled, width=0.83333]; n6 -> n4 [label=\"1 dmatrix\", lp=\"149.5,414\", pos=\"e,116.78,388.08 125.17,439.6 123.24,427.75 120.64,411.82 118.44,398.29\"]; n10 [apply_op=Dot22, height=0.5, label=Dot22, node_type=apply, pos=\"118,194\", shape=ellipse, width=0.92774]; n8 -> n10 [label=\"0 dmatrix\", lp=\"175.5,238\", pos=\"e,127.82,211.24 159.97,264.42 152.37,251.85 141.8,234.35 133.16,220.07\"]; n13 [apply_op=SoftmaxWithBias, height=0.5, label=SoftmaxWithBias, node_type=apply, pos=\"76,106\", shape=ellipse, width=2.1117]; n10 -> n13 [label=\"0 dmatrix\", lp=\"128.5,150\", pos=\"e,84.361,124.12 109.9,176.42 103.94,164.2 95.704,147.35 88.845,133.3\"]; n11 [dtype=dmatrix, fillcolor=YellowGreen, height=0.5, label=dmatrix, node_type=shared_input, pos=\"75,282\", shape=box, style=filled, width=0.83333]; n11 -> n10 [label=\"1 dmatrix\", lp=\"111.5,238\", pos=\"e,100.16,209.41 75.526,263.66 76.44,253.4 78.677,240.41 84,230 86.398,225.31 89.636,220.84 93.164,216.75\"]; n15 [dtype=dmatrix, fillcolor=dodgerblue, height=0.5, label=dmatrix, node_type=output, pos=\"76,18\", shape=box, style=filled, width=0.83333]; n13 -> n15 [label=dmatrix, lp=\"98,62\", pos=\"e,76,36.084 76,87.597 76,75.746 76,59.817 76,46.292\"]; n14 [dtype=dvector, fillcolor=YellowGreen, height=0.5, label=dvector, node_type=shared_input, pos=\"37,194\", shape=box, style=filled, width=0.80556]; n14 -> n13 [label=\"1 dvector\", lp=\"66.5,150\", pos=\"e,53.961,123.42 34.978,175.72 34.504,165.47 35.203,152.48 40,142 41.795,138.08 44.242,134.36 47.022,130.92\"];}");
// Frontend graph for visualization
var graph = {};
var forceLayout;
var isProfiled = false; // true if profiling information available
var useProfileColors = false;
var fixOnInit = true; // fix nodes on initialization
var maxProfilePer = 0;
var profileColors = ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15"];
var pad = 10;
var isEditNode = false; // true if node is edited
var menuItems = [
{
title: 'Edit',
action: function(elm, d, i) { editNode(elm, d); }
},
{
title: 'Release',
action: function(elm, d, i) { releaseNode(d); }
}
];
// Create main panel
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
// Zoom behaviour
var zoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
.on('zoom', function(d) {
var trans = d3.event.translate;
trans[0] += 300;
trans[1] += 100;
pane.attr('transform', 'translate(' + trans + ') scale(' + d3.event.scale + ')');
});
svg.call(zoom);
zoom.event(svg);
svg.on("dblclick.zoom", null);
// Edges
var edgeDiv = d3.select('body').append('div')
.attr('class', 'edgeTooltip')
.style('opacity', 0.0);
// Div for node details
var nodeInfo = d3.select('body').append('div')
.attr('class', 'nodeInfo')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
// Initialize graph
processDotGraph(dotGraph);
graph = frontEndGraph(dotGraph);
drawGraph();
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="d3viz/css/d3viz.css"/>
<link rel="stylesheet" href="d3viz/css/d3-context-menu.css"/>
<script type="text/javascript" src="d3viz/js/d3viz.js"></script>
<script type="text/javascript" src="d3viz/js/d3.v3.min.js"></script>
<script type="text/javascript" src="d3viz/js/dagre-d3.min.js"></script>
<script type="text/javascript" src="d3viz/js/graphlib-dot.min.js"></script>
<script type="text/javascript" src="d3viz/js/d3-context-menu.js"></script>
</head>
<body>
<div id='menu' class='menuBar'>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
// Backend graph in DOT format
var dotGraph = graphlibDot.read("digraph G { graph [bb=\"0,0,272,476\"]; node [label=\"\N\"]; n1 [apply_op=DimShuffle, height=0.5, label=DimShuffle, node_type=apply, pos=\"219,370\", profile=\"1.28746032715e-05 0.0101599693298\", shape=ellipse, width=1.4763]; n8 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"170,282\", profile=\"0.00068998336792 0.0101599693298\", shape=ellipse, style=filled, type=colored, width=1.2888]; n1 -> n8 [label=\"1 dmatrix\", lp=\"225.5,326\", pos=\"e,179.49,299.66 209.32,352.01 202.23,339.56 192.48,322.45 184.47,308.39\"]; n2 [dtype=dvector, fillcolor=YellowGreen, height=0.5, label=dvector, node_type=shared_input, pos=\"219,458\", shape=box, style=filled, width=0.80556]; n2 -> n1 [color=dodgerblue, label=dvector, lp=\"240,414\", pos=\"e,219,388.08 219,439.6 219,427.75 219,411.82 219,398.29\"]; n4 [apply_op=Dot22, height=0.5, label=Dot22, node_type=apply, pos=\"114,370\", profile=\"0.00877094268799 0.0101599693298\", shape=ellipse, width=0.92774]; n4 -> n8 [color=red, label=\"0 dmatrix\", lp=\"158.5,326\", pos=\"e,148.95,298.37 117.35,351.95 119.93,341.54 124.24,328.3 131,318 133.92,313.55 137.54,309.31 141.39,305.41\"]; n5 [dtype=dmatrix, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"50,458\", shape=box, style=filled, width=0.75]; n5 -> n4 [label=\"0 dmatrix\", lp=\"88.5,414\", pos=\"e,89.174,382.09 50.492,439.64 51.537,429.12 54.251,415.87 61,406 66.011,398.67 73.111,392.49 80.491,387.46\"]; n6 [dtype=dmatrix, fillcolor=YellowGreen, height=0.5, label=dmatrix, node_type=shared_input, pos=\"128,458\", shape=box, style=filled, width=0.83333]; n6 -> n4 [label=\"1 dmatrix\", lp=\"149.5,414\", pos=\"e,116.78,388.08 125.17,439.6 123.24,427.75 120.64,411.82 118.44,398.29\"]; n10 [apply_op=Dot22, height=0.5, label=Dot22, node_type=apply, pos=\"118,194\", profile=\"0.000282049179077 0.0101599693298\", shape=ellipse, width=0.92774]; n8 -> n10 [label=\"0 dmatrix\", lp=\"175.5,238\", pos=\"e,127.82,211.24 159.97,264.42 152.37,251.85 141.8,234.35 133.16,220.07\"]; n13 [apply_op=SoftmaxWithBias, height=0.5, label=SoftmaxWithBias, node_type=apply, pos=\"76,106\", profile=\"0.000186920166016 0.0101599693298\", shape=ellipse, width=2.1117]; n10 -> n13 [label=\"0 dmatrix\", lp=\"128.5,150\", pos=\"e,84.361,124.12 109.9,176.42 103.94,164.2 95.704,147.35 88.845,133.3\"]; n11 [dtype=dmatrix, fillcolor=YellowGreen, height=0.5, label=dmatrix, node_type=shared_input, pos=\"75,282\", shape=box, style=filled, width=0.83333]; n11 -> n10 [label=\"1 dmatrix\", lp=\"111.5,238\", pos=\"e,100.16,209.41 75.526,263.66 76.44,253.4 78.677,240.41 84,230 86.398,225.31 89.636,220.84 93.164,216.75\"]; n15 [dtype=dmatrix, fillcolor=dodgerblue, height=0.5, label=dmatrix, node_type=output, pos=\"76,18\", shape=box, style=filled, width=0.83333]; n13 -> n15 [label=dmatrix, lp=\"98,62\", pos=\"e,76,36.084 76,87.597 76,75.746 76,59.817 76,46.292\"]; n14 [dtype=dvector, fillcolor=YellowGreen, height=0.5, label=dvector, node_type=shared_input, pos=\"37,194\", shape=box, style=filled, width=0.80556]; n14 -> n13 [label=\"1 dvector\", lp=\"66.5,150\", pos=\"e,53.961,123.42 34.978,175.72 34.504,165.47 35.203,152.48 40,142 41.795,138.08 44.242,134.36 47.022,130.92\"];}");
// Frontend graph for visualization
var graph = {};
var forceLayout;
var isProfiled = false; // true if profiling information available
var useProfileColors = false;
var fixOnInit = true; // fix nodes on initialization
var maxProfilePer = 0;
var profileColors = ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15"];
var pad = 10;
var isEditNode = false; // true if node is edited
var menuItems = [
{
title: 'Edit',
action: function(elm, d, i) { editNode(elm, d); }
},
{
title: 'Release',
action: function(elm, d, i) { releaseNode(d); }
}
];
// Create main panel
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
// Zoom behaviour
var zoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
.on('zoom', function(d) {
var trans = d3.event.translate;
trans[0] += 300;
trans[1] += 100;
pane.attr('transform', 'translate(' + trans + ') scale(' + d3.event.scale + ')');
});
svg.call(zoom);
zoom.event(svg);
svg.on("dblclick.zoom", null);
// Edges
var edgeDiv = d3.select('body').append('div')
.attr('class', 'edgeTooltip')
.style('opacity', 0.0);
// Div for node details
var nodeInfo = d3.select('body').append('div')
.attr('class', 'nodeInfo')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
// Initialize graph
processDotGraph(dotGraph);
graph = frontEndGraph(dotGraph);
drawGraph();
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="d3viz/css/d3viz.css"/>
<link rel="stylesheet" href="d3viz/css/d3-context-menu.css"/>
<script type="text/javascript" src="d3viz/js/d3viz.js"></script>
<script type="text/javascript" src="d3viz/js/d3.v3.min.js"></script>
<script type="text/javascript" src="d3viz/js/dagre-d3.min.js"></script>
<script type="text/javascript" src="d3viz/js/graphlib-dot.min.js"></script>
<script type="text/javascript" src="d3viz/js/d3-context-menu.js"></script>
</head>
<body>
<div id='menu' class='menuBar'>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
// Backend graph in DOT format
var dotGraph = graphlibDot.read("digraph G { graph [bb=\"0,0,877.01,316\"]; node [label=\"\N\"]; subgraph cluster_n1 { graph [bb=\"413.01,80,637.01,308\"]; n11 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"510.01,194\", shape=ellipse, style=filled, type=colored, width=1.2888]; n15 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"510.01,106\", shape=box, style=filled, width=0.75]; n11 -> n15 [label=fscalar, lp=\"529.01,150\", pos=\"e,510.01,124.08 510.01,175.6 510.01,163.75 510.01,147.82 510.01,134.29\"]; n12 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=z, node_type=input, pos=\"592.01,282\", shape=box, style=filled, width=0.75]; n12 -> n11 [label=\"0 fscalar\", lp=\"596.01,238\", pos=\"e,534.12,209.56 583.05,263.78 577.09,253.31 568.62,240.07 559.01,230 554.11,224.87 548.36,220 542.56,215.61\"]; n13 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"520.01,282\", shape=box, style=filled, width=0.75]; n13 -> n11 [label=\"1 fscalar\", lp=\"535.01,238\", pos=\"e,509.36,212.08 514.71,263.71 513.23,258.11 511.8,251.84 511.01,246 509.95,238.28 509.51,229.84 509.37,222.09\"]; n14 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"448.01,282\", shape=box, style=filled, width=0.75]; n14 -> n11 [label=\"2 fscalar\", lp=\"482.01,238\", pos=\"e,481.77,208.39 448.14,263.68 448.97,253.17 451.42,239.92 458.01,230 462.06,223.89 467.58,218.57 473.52,214.05\"]; } subgraph cluster_n7 { graph [bb=\"645.01,80,869.01,308\"]; n71 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"742.01,194\", shape=ellipse, style=filled, type=colored, width=1.2888]; n75 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"742.01,106\", shape=box, style=filled, width=0.75]; n71 -> n75 [label=fscalar, lp=\"761.01,150\", pos=\"e,742.01,124.08 742.01,175.6 742.01,163.75 742.01,147.82 742.01,134.29\"]; n72 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=z, node_type=input, pos=\"824.01,282\", shape=box, style=filled, width=0.75]; n72 -> n71 [label=\"0 fscalar\", lp=\"828.01,238\", pos=\"e,766.12,209.56 815.05,263.78 809.09,253.31 800.62,240.07 791.01,230 786.11,224.87 780.36,220 774.56,215.61\"]; n73 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"752.01,282\", shape=box, style=filled, width=0.75]; n73 -> n71 [label=\"1 fscalar\", lp=\"767.01,238\", pos=\"e,741.36,212.08 746.71,263.71 745.23,258.11 743.8,251.84 743.01,246 741.95,238.28 741.51,229.84 741.37,222.09\"]; n74 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"680.01,282\", shape=box, style=filled, width=0.75]; n74 -> n71 [label=\"2 fscalar\", lp=\"714.01,238\", pos=\"e,713.77,208.39 680.14,263.68 680.97,253.17 683.42,239.92 690.01,230 694.06,223.89 699.58,218.57 705.52,214.05\"]; } n1 [apply_op=OpFromGraph, height=0.5, label=OpFromGraph, node_type=apply, pos=\"217.01,194\", shape=ellipse, subg=cluster_n1, subg_map_inputs=\"[[\'n2\', \'n13\'], [\'n3\', \'n14\'], [\'n4\', \'n12\']]\", subg_map_outputs=\"[[\'n15\', \'n6\']]\", width=1.7826]; n6 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"153.01,106\", shape=ellipse, style=filled, type=colored, width=1.2888]; n1 -> n6 [label=\"1 fscalar\", lp=\"215.01,150\", pos=\"e,165.19,123.37 204.36,176.01 194.95,163.36 181.96,145.9 171.4,131.71\"]; n2 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=z, node_type=input, pos=\"55.007,282\", shape=box, style=filled, width=0.75]; n2 -> n1 [label=\"0 fscalar\", lp=\"175.01,238\", pos=\"e,188.78,210.26 82.101,266.93 94.312,260.57 108.9,252.94 122.01,246 141.17,235.85 162.43,224.45 179.98,215\"]; n7 [apply_op=OpFromGraph, height=0.5, label=OpFromGraph, node_type=apply, pos=\"70.007,194\", shape=ellipse, subg=cluster_n7, subg_map_inputs=\"[[\'n4\', \'n73\'], [\'n3\', \'n74\'], [\'n2\', \'n72\']]\", subg_map_outputs=\"[[\'n75\', \'n6\']]\", width=1.7826]; n2 -> n7 [label=\"2 fscalar\", lp=\"27.007,238\", pos=\"e,28.697,207.85 27.721,268.8 10.72,259.37 -6.2971,245.36 3.007,230 7.1667,223.13 13.164,217.55 19.883,213.03\"]; n3 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"304.01,282\", shape=box, style=filled, width=0.75]; n3 -> n1 [label=\"1 fscalar\", lp=\"363.01,238\", pos=\"e,279.27,198.73 325,263.94 334.94,253.78 343.06,240.79 335.01,230 323.95,215.19 306.9,206.39 289.16,201.24\"]; n3 -> n7 [label=\"1 fscalar\", lp=\"304.01,238\", pos=\"e,116.87,206.31 294.23,263.96 286.68,252.44 275.15,237.94 261.01,230 215.13,204.25 195.68,221.9 144.01,212 138.37,210.92 132.5,209.72 \126.66,208.46\"]; n4 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"197.01,282\", shape=box, style=filled, width=0.75]; n4 -> n1 [label=\"2 fscalar\", lp=\"233.01,238\", pos=\"e,213.03,212.08 201.05,263.6 203.84,251.63 207.59,235.5 210.75,221.89\"]; n4 -> n7 [label=\"0 fscalar\", lp=\"98.007,238\", pos=\"e,66.731,212.43 169.97,281.61 141.33,280.65 97.14,274.26 74.007,246 68.707,239.52 66.766,230.91 66.43,222.6\"]; n7 -> n6 [color=red, label=\"0 fscalar\", lp=\"143.01,150\", pos=\"e,137.33,123.24 86.01,176.42 98.606,163.37 116.32,145.01 130.37,130.46\"]; n9 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"153.01,18\", shape=box, style=filled, width=0.75]; n6 -> n9 [label=fscalar, lp=\"172.01,62\", pos=\"e,153.01,36.084 153.01,87.597 153.01,75.746 153.01,59.817 153.01,46.292\"];}");
// Frontend graph for visualization
var graph = {};
var forceLayout;
var isProfiled = false; // true if profiling information available
var useProfileColors = false;
var fixOnInit = true; // fix nodes on initialization
var maxProfilePer = 0;
var profileColors = ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15"];
var pad = 10;
var isEditNode = false; // true if node is edited
var menuItems = [
{
title: 'Edit',
action: function(elm, d, i) { editNode(elm, d); }
},
{
title: 'Release',
action: function(elm, d, i) { releaseNode(d); }
}
];
// Create main panel
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
// Zoom behaviour
var zoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
.on('zoom', function(d) {
var trans = d3.event.translate;
trans[0] += 300;
trans[1] += 100;
pane.attr('transform', 'translate(' + trans + ') scale(' + d3.event.scale + ')');
});
svg.call(zoom);
zoom.event(svg);
svg.on("dblclick.zoom", null);
// Edges
var edgeDiv = d3.select('body').append('div')
.attr('class', 'edgeTooltip')
.style('opacity', 0.0);
// Div for node details
var nodeInfo = d3.select('body').append('div')
.attr('class', 'nodeInfo')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
// Initialize graph
processDotGraph(dotGraph);
graph = frontEndGraph(dotGraph);
drawGraph();
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="d3viz/css/d3viz.css"/>
<link rel="stylesheet" href="d3viz/css/d3-context-menu.css"/>
<script type="text/javascript" src="d3viz/js/d3viz.js"></script>
<script type="text/javascript" src="d3viz/js/d3.v3.min.js"></script>
<script type="text/javascript" src="d3viz/js/dagre-d3.min.js"></script>
<script type="text/javascript" src="d3viz/js/graphlib-dot.min.js"></script>
<script type="text/javascript" src="d3viz/js/d3-context-menu.js"></script>
</head>
<body>
<div id='menu' class='menuBar'>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
// Backend graph in DOT format
var dotGraph = graphlibDot.read("digraph G { graph [bb=\"0,0,636,340\"]; node [label=\"\N\"]; subgraph cluster_n1 { graph [bb=\"251,8,628,332\"]; subgraph cluster_n11 { graph [bb=\"467,96,620,324\"]; n111 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"544,210\", shape=ellipse, style=filled, type=colored, width=1.2888]; n114 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"544,122\", shape=box, style=filled, width=0.75]; n111 -> n114 [label=fscalar, lp=\"563,166\", pos=\"e,544,140.08 544,191.6 544,179.75 544,163.82 544,150.29\"]; n112 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"574,298\", shape=box, style=filled, width=0.75]; n112 -> n111 [label=\"0 fscalar\", lp=\"586,254\", pos=\"e,549.96,228.08 567.93,279.6 563.75,267.63 558.13,251.5 553.38,237.89\"]; n113 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"502,298\", shape=box, style=filled, width=0.75]; n113 -> n111 [label=\"1 fscalar\", lp=\"529,254\", pos=\"e,521.37,226.05 499.83,279.65 499.3,269.38 499.99,256.39 505,246 507.26,241.31 510.46,236.96 514.07,233.04\"]; } n11 [apply_op=OpFromGraph, height=0.5, label=OpFromGraph, node_type=apply, pos=\"395,210\", shape=ellipse, subg=cluster_n11, subg_map_inputs=\"[[\'n12\', \'n112\'], [\'n13\', \'n113\']]\", subg_map_outputs=\"[[\'n114\', \'n15\']]\", width=1.7826]; n15 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"345,122\", shape=ellipse, style=filled, type=colored, width=1.2888]; n11 -> n15 [color=red, label=\"0 fscalar\", lp=\"399,166\", pos=\"e,354.69,139.66 385.12,192.01 377.88,179.56 367.94,162.45 359.76,148.39\"]; n12 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"404,298\", shape=box, style=filled, width=0.75]; n12 -> n11 [label=\"0 fscalar\", lp=\"425,254\", pos=\"e,396.79,228.08 402.18,279.6 400.94,267.75 399.27,251.82 397.86,238.29\"]; n13 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"332,298\", shape=box, style=filled, width=0.75]; n13 -> n11 [label=\"1 fscalar\", lp=\"371,254\", pos=\"e,367.62,226.31 334.21,279.71 336.22,269.21 339.99,255.96 347,246 350.46,241.08 354.83,236.59 359.53,232.59\"]; n17 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"345,34\", shape=box, style=filled, width=0.75]; n15 -> n17 [label=fscalar, lp=\"364,78\", pos=\"e,345,52.084 345,103.6 345,91.746 345,75.817 345,62.292\"]; n16 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=z, node_type=input, pos=\"286,210\", shape=box, style=filled, width=0.75]; n16 -> n15 [label=\"1 fscalar\", lp=\"338,166\", pos=\"e,329.38,139.11 294.18,191.9 299.34,181.72 306.44,168.74 314,158 316.65,154.24 319.64,150.43 322.7,146.78\"]; } n1 [apply_op=OpFromGraph, height=0.5, label=OpFromGraph, node_type=apply, pos=\"136,210\", shape=ellipse, subg=cluster_n1, subg_map_inputs=\"[[\'n2\', \'n12\'], [\'n3\', \'n13\'], [\'n4\', \'n16\']]\", subg_map_outputs=\"[[\'n17\', \'n6\']]\", width=1.7826]; n6 [apply_op=Elemwise, fillcolor=\"#FFAABB\", height=0.5, label=Elemwise, node_type=apply, pos=\"46,122\", shape=ellipse, style=filled, type=colored, width=1.2888]; n1 -> n6 [color=red, label=\"0 fscalar\", lp=\"124,166\", pos=\"e,62.691,138.95 118.65,192.42 104.9,179.28 85.538,160.78 70.258,146.18\"]; n2 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=x, node_type=input, pos=\"123,298\", shape=box, style=filled, width=0.75]; n2 -> n1 [label=\"0 fscalar\", lp=\"149,254\", pos=\"e,129.59,228.22 122.55,279.88 122.56,269.92 123.06,257.17 125,246 125.46,243.33 126.08,240.57 126.78,237.84\"]; n3 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=y, node_type=input, pos=\"195,298\", shape=box, style=filled, width=0.75]; n3 -> n1 [label=\"1 fscalar\", lp=\"207,254\", pos=\"e,155.98,227.13 189.48,279.56 185.74,269.26 180.18,256.27 173,246 170.17,241.96 166.82,238.01 163.3,234.32\"]; n4 [dtype=fscalar, fillcolor=limegreen, height=0.5, label=z, node_type=input, pos=\"29,298\", shape=box, style=filled, width=0.75]; n4 -> n1 [label=\"2 fscalar\", lp=\"96,254\", pos=\"e,103.61,225.73 40.803,279.98 48.767,269.34 59.985,255.82 72,246 78.91,240.35 86.866,235.16 94.768,230.6\"]; n4 -> n6 [label=\"1 fscalar\", lp=\"39,210\", pos=\"e,36.126,139.61 23.104,279.76 16.772,258.84 8.4483,222.57 15,192 18.224,176.96 24.988,161.23 31.378,148.6\"]; n7 [dtype=fscalar, fillcolor=dodgerblue, height=0.5, label=fscalar, node_type=output, pos=\"46,34\", shape=box, style=filled, width=0.75]; n6 -> n7 [label=fscalar, lp=\"65,78\", pos=\"e,46,52.084 46,103.6 46,91.746 46,75.817 46,62.292\"];}");
// Frontend graph for visualization
var graph = {};
var forceLayout;
var isProfiled = false; // true if profiling information available
var useProfileColors = false;
var fixOnInit = true; // fix nodes on initialization
var maxProfilePer = 0;
var profileColors = ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15"];
var pad = 10;
var isEditNode = false; // true if node is edited
var menuItems = [
{
title: 'Edit',
action: function(elm, d, i) { editNode(elm, d); }
},
{
title: 'Release',
action: function(elm, d, i) { releaseNode(d); }
}
];
// Create main panel
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
// Zoom behaviour
var zoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
.on('zoom', function(d) {
var trans = d3.event.translate;
trans[0] += 300;
trans[1] += 100;
pane.attr('transform', 'translate(' + trans + ') scale(' + d3.event.scale + ')');
});
svg.call(zoom);
zoom.event(svg);
svg.on("dblclick.zoom", null);
// Edges
var edgeDiv = d3.select('body').append('div')
.attr('class', 'edgeTooltip')
.style('opacity', 0.0);
// Div for node details
var nodeInfo = d3.select('body').append('div')
.attr('class', 'nodeInfo')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
// Initialize graph
processDotGraph(dotGraph);
graph = frontEndGraph(dotGraph);
drawGraph();
</script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
.. _libdoc_d3viz:
============================================================================
:mod:`d3viz` -- d3viz: Interactive visualization of Theano compute graphs
============================================================================
.. module:: d3viz
:platform: Unix, Windows
:synopsis: Allows to interactively visualize Theano compute graphs
.. moduleauthor:: Christof Angermueller
Guide
=====
Requirements
------------
``d3viz`` requires the `pydot <https://pypi.python.org/pypi/pydot>`__
package, which can be installed with ``pip``:
::
pip install pydot
Overview
--------
``d3viz`` extends Theano’s `printing
module <http://deeplearning.net/software/theano/library/printing.html>`__
to interactively visualize compute graphs. Instead of creating a static
picture, it creates an HTML file, which can be opened with current
web-browsers. ``d3viz`` allows
- to zoom to different regions and to move graphs via drag and drop,
- to position nodes both manually and automatically,
- to retrieve additional information about nodes and edges such as
their data type or definition in the source code,
- to edit node labels,
- to visualizing profiling information, and
- to explore nested graphs such as OpFromGraph nodes.
.. note::
This userguide is also avaible as
:download:`IPython notebook <index.ipynb>`.
As an example, consider the following multilayer perceptron with one
hidden layer and a softmax output layer.
.. code:: python
import theano as th
import theano.tensor as T
import numpy as np
ninputs = 1000
nfeatures = 100
noutputs = 10
nhiddens = 50
rng = np.random.RandomState(0)
x = T.dmatrix('x')
wh = th.shared(rng.normal(0, 1, (nfeatures, nhiddens)), borrow=True)
bh = th.shared(np.zeros(nhiddens), borrow=True)
h = T.nnet.sigmoid(T.dot(x, wh) + bh)
wy = th.shared(rng.normal(0, 1, (nhiddens, noutputs)))
by = th.shared(np.zeros(noutputs), borrow=True)
y = T.nnet.softmax(T.dot(h, wy) + by)
predict = th.function([x], y)
The function ``predict`` outputs the probability of 10 classes. You can
visualize it with :py:func:`theano.printing.pydotprint` as follows:
.. code:: python
from theano.printing import pydotprint
import os
if not os.path.exists('examples'):
os.makedirs('examples')
pydotprint(predict, 'examples/mlp.png')
.. parsed-literal::
The output file is available at examples/mlp.png
.. code:: python
from IPython.display import Image
Image('./examples/mlp.png', width='80%')
.. image:: index_files/index_10_0.png
To visualize it interactively, import :py:func:`theano.d3viz.d3viz.d3viz` from
the the :py:mod:`theano.d3viz.d3viz` module, which can be called as before:
.. code:: python
import theano.d3viz as d3v
d3v.d3viz(predict, 'examples/mlp.html')
`Open visualization! <../../_static/mlp.html>`_
When you open the output file ``mlp.html`` in your web-browser, you will
see an interactive visualization of the compute graph. You can move the
whole graph or single nodes via drag and drop, and zoom via the mouse
wheel. When you move the mouse cursor over a node, a window will pop up
that displays detailed information about the node, such as its data type
or definition in the source code. When you left-click on a node and
select ``Edit``, you can change the predefined node label. If you are
dealing with a complex graph with many nodes, the default node layout
may not be perfect. In this case, you can press the ``Release node``
button in the top-left corner to automatically arrange nodes. To reset
nodes to their default position, press the ``Reset nodes`` button.
Profiling
---------
Theano allows `function
profiling <http://deeplearning.net/software/theano/tutorial/profiling.html>`__
via the ``profile=True`` flag. After at least one function call, the
compute time of each node can be printed in text form with
``debugprint``. However, analyzing complex graphs in this way can be
cumbersome.
``d3viz`` can visualize the same timing information graphically, and
hence help to spot bottlenecks in the compute graph more easily! To
begin with, we will redefine the ``predict`` function, this time by
using ``profile=True`` flag. Afterwards, we capture the runtime on
random data:
.. code:: python
predict_profiled = th.function([x], y, profile=True)
x_val = rng.normal(0, 1, (ninputs, nfeatures))
y_val = predict_profiled(x_val)
.. code:: python
d3v.d3viz(predict_profiled, 'examples/mlp2.html')
`Open visualization! <../../_static/mlp2.html>`_
When you open the HTML file in your browser, you will find an additional
``Toggle profile colors`` button in the menu bar. By clicking on it,
nodes will be colored by their compute time, where red corresponds to a
high compute time. You can read out the exact timing information of a
node by moving the cursor over it.
Different output formats
------------------------
Internally, ``d3viz`` represents a compute graph in the `Graphviz DOT
language <http://www.graphviz.org/>`__, using the
`pydot <https://pypi.python.org/pypi/pydot>`__ package, and defines a
front-end based on the `d3.js <http://d3js.org/>`__ library to visualize
it. However, any other Graphviz front-end can be used, which allows to
export graphs to different formats.
.. code:: python
formatter = d3v.formatting.PyDotFormatter()
pydot_graph = formatter(predict_profiled)
pydot_graph.write_png('examples/mlp2.png');
pydot_graph.write_png('examples/mlp2.pdf');
.. code:: python
Image('./examples/mlp2.png')
.. image:: index_files/index_24_0.png
Here, we used the :py:class:`theano.d3viz.formatting.PyDotFormatter` class to
convert the compute graph into a ``pydot`` graph, and created a
:download:`PNG <examples/mlp2.png>` and :download:`PDF <examples/mlp2.pdf>`
file. You can find all output formats supported by Graphviz `here
<http://www.graphviz.org/doc/info/output.html>`__.
OpFromGraph nodes
-----------------
An
`OpFromGraph <http://deeplearning.net/software/theano/library/compile/opfromgraph.html>`__
node defines a new operation, which can be called with different inputs
at different places in the compute graph. Each ``OpFromGraph`` node
defines a nested graph, which will be visualized accordingly by
``d3viz``.
.. code:: python
x, y, z = T.scalars('xyz')
e = T.nnet.sigmoid((x + y + z)**2)
op = th.OpFromGraph([x, y, z], [e])
e2 = op(x, y, z) + op(z, y, x)
f = th.function([x, y, z], e2)
.. code:: python
d3v.d3viz(f, 'examples/ofg.html')
`Open visualization! <../../_static/ofg.html>`_
In this example, an operation with three inputs is defined, which is
used to build a function that calls this operations twice, each time
with different input arguments.
In the ``d3viz`` visualization, you will find two OpFromGraph nodes,
which correspond to the two OpFromGraph calls. When you double click on
one of them, the nested graph appears with the correct mapping of its
input arguments. You can move it around by drag and drop in the shaded
area, and close it again by double-click.
An OpFromGraph operation can be composed of further OpFromGraph
operations, which will be visualized as nested graphs as you can see in
the following example.
.. code:: python
x, y, z = T.scalars('xyz')
e = x * y
op = th.OpFromGraph([x, y], [e])
e2 = op(x, y) + z
op2 = th.OpFromGraph([x, y, z], [e2])
e3 = op2(x, y, z) + z
f = th.function([x, y, z], [e3])
.. code:: python
d3v.d3viz(f, 'examples/ofg2.html')
`Open visualization! <../../_static/ofg2.html>`_
Feedback
--------
If you have any problems or great ideas on how to improve ``d3viz``,
please let me know!
- Christof Angermueller
- cangermueller@gmail.com
- https://cangermueller.com
References
==========
d3viz module
------------
.. automodule:: theano.d3viz.d3viz
:members:
PyDotFormatter
--------------
.. autoclass:: theano.d3viz.formatting.PyDotFormatter
:members: __call__
:special-members:
:private-members:
......@@ -15,6 +15,7 @@ Types and Ops that you can use to build and compile expression graphs.
gradient
config
printing
d3viz/index
compile/index
sparse/index
sparse/sandbox
......
from theano.d3viz.d3viz import d3viz, d3write
has_requirements = False
try:
import pydot as pd
if pd.find_graphviz():
has_requirements = True
except ImportError:
pass
.d3-context-menu {
position: absolute;
display: none;
background-color: #f2f2f2;
border-radius: 4px;
font-family: Arial, sans-serif;
font-size: 14px;
min-width: 50px;
border: 1px solid #d4d4d4;
z-index:1200;
}
.d3-context-menu ul {
list-style-type: none;
margin: 4px 0px;
padding: 0px;
cursor: default;
}
.d3-context-menu ul li {
padding: 4px 16px;
}
.d3-context-menu ul li:hover {
background-color: #4677f8;
color: #fefefe;
}
svg {
margin-left:auto;
margin-right:auto;
display:block;
position: fixed;
border: 0px solid black;
top: 32px; left:0%; right:0%; bottom:0%;
}
.menuBar {
border-bottom: 1px solid black;
height: 22px;
}
input.menuBar {
}
.nodeRect {
stroke: black;
border: 3px solid black;
}
.nodeEllipse {
stroke: black;
border: 3px solid black;
}
.nodeText {
color: black;
}
.edge {
stroke-width: 3px;
cursor: pointer;
opacity: 0.4;
}
.edgeLabelRect {
stroke: black;
border: 1px solid black;
fill: skyblue;
opacity: 0.9;
}
.edgeLabelText {
fill: black;
text-anchor: start;
}
.arrowHead {
stroke: green;
stroke-width: 1px;
}
.arrowHead_n {
stroke: green;
}
.arrowHead_r {
stroke-width: 3px;
fill: red;
stroke: red;
}
.arrowHead_b {
stroke: dodgerblue;
}
.edgeTooltip {
position: absolute;
text-align: center;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
.nodeInfo {
position: absolute;
text-align: left;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
path.hull {
fill: lightsteelblue;
fill-opacity: 0.3;
}
\ No newline at end of file
"""Dynamic visualization of Theano graphs.
Author: Christof Angermueller <cangermueller@gmail.com>
"""
import os
import shutil
import re
from six import iteritems
from theano.d3viz.formatting import PyDotFormatter
__path__ = os.path.dirname(os.path.realpath(__file__))
def replace_patterns(x, replace):
"""Replace `replace` in string `x`.
Parameters
----------
s: str
String on which function is applied
replace: dict
`key`, `value` pairs where key is a regular expression and `value` a
string by which `key` is replaced
"""
for from_, to in iteritems(replace):
x = x.replace(str(from_), str(to))
return x
def escape_quotes(s):
"""Escape quotes in string.
Parameters
----------
s: str
String on which function is applied
"""
s = re.sub(r'''(['"])''', r'\\\1', s)
return s
def d3viz(fct, outfile, copy_deps=True, *args, **kwargs):
"""Create HTML file with dynamic visualizing of a Theano function graph.
In the HTML file, the whole graph or single nodes can be moved by drag and
drop. Zooming is possible via the mouse wheel. Detailed information about
nodes and edges are displayed via mouse-over events. Node labels can be
edited by selecting Edit from the context menu.
Input nodes are colored in green, output nodes in blue. Apply nodes are
ellipses, and colored depending on the type of operation they perform. Red
ellipses are transfers from/to the GPU (ops with names GpuFromHost,
HostFromGpu).
Edges are black by default. If a node returns a view of an
input, the input edge will be blue. If it returns a destroyed input, the
edge will be red.
Parameters
----------
fct : theano.compile.function_module.Function
A compiled Theano function, variable, apply or a list of variables.
outfile : str
Path to output HTML file.
copy_deps : bool, optional
Copy javascript and CSS dependencies to output directory.
*args : tuple, optional
Arguments passed to PyDotFormatter.
*kwargs : dict, optional
Arguments passed to PyDotFormatter.
"""
# Create DOT graph
formatter = PyDotFormatter(*args, **kwargs)
graph = formatter(fct)
dot_graph = escape_quotes(graph.create_dot()).replace('\n', '')
# Create output directory if not existing
outdir = os.path.dirname(outfile)
if not os.path.exists(outdir):
os.makedirs(outdir)
# Read template HTML file
template_file = os.path.join(__path__, 'html', 'template.html')
f = open(template_file)
template = f.read()
f.close()
# Copy dependencies to output directory
src_deps = __path__
if copy_deps:
dst_deps = 'd3viz'
for d in ['js', 'css']:
dep = os.path.join(outdir, dst_deps, d)
if not os.path.exists(dep):
shutil.copytree(os.path.join(src_deps, d), dep)
else:
dst_deps = src_deps
# Replace patterns in template
replace = {
'%% JS_DIR %%': os.path.join(dst_deps, 'js'),
'%% CSS_DIR %%': os.path.join(dst_deps, 'css'),
'%% DOT_GRAPH %%': os.path.basename(dot_graph),
}
html = replace_patterns(template, replace)
# Write HTML file
with open(outfile, 'w') as f:
f.write(html)
def d3write(fct, path, *args, **kwargs):
"""Convert Theano graph to pydot graph and write to dot file.
Parameters
----------
fct : theano.compile.function_module.Function
A compiled Theano function, variable, apply or a list of variables.
path: str
Path to output file
*args : tuple, optional
Arguments passed to PyDotFormatter.
*kwargs : dict, optional
Arguments passed to PyDotFormatter.
"""
formatter = PyDotFormatter(*args, **kwargs)
graph = formatter(fct)
graph.write_dot(path)
"""Functions for formatting Theano compute graphs.
Author: Christof Angermueller <cangermueller@gmail.com>
"""
import numpy as np
import os
from functools import reduce
from six import iteritems, itervalues
import theano
from theano import gof
from theano.compile.profilemode import ProfileMode
from theano.compile import Function
from theano.compile import builders
pydot_imported = False
try:
import pydot as pd
if pd.find_graphviz():
pydot_imported = True
except ImportError:
pass
class PyDotFormatter(object):
"""Create `pydot` graph object from Theano function.
Parameters
----------
compact : bool
if True, will remove intermediate variables without name.
Attributes
----------
node_colors : dict
Color table of node types.
apply_colors : dict
Color table of apply nodes.
shapes : dict
Shape table of node types.
"""
def __init__(self, compact=True):
"""Construct PyDotFormatter object."""
if not pydot_imported:
raise ImportError('Failed to import pydot. Please install pydot!')
self.compact = compact
self.node_colors = {'input': 'limegreen',
'constant_input': 'SpringGreen',
'shared_input': 'YellowGreen',
'output': 'dodgerblue',
'unused': 'lightgrey'}
self.apply_colors = {'GpuFromHost': 'red',
'HostFromGpu': 'red',
'Scan': 'yellow',
'Shape': 'cyan',
'IfElse': 'magenta',
'Elemwise': '#FFAABB', # dark pink
'Subtensor': '#FFAAFF', # purple
'Alloc': '#FFAA22'} # orange
self.shapes = {'input': 'box',
'output': 'box',
'apply': 'ellipse'}
self.__node_prefix = 'n'
def __add_node(self, node):
"""Add new node to node list and return unique id.
Parameters
----------
node : Theano graph node
Apply node, tensor variable, or shared variable in compute graph.
Returns
-------
str
Unique node id.
"""
assert node not in self.__nodes
_id = '%s%d' % (self.__node_prefix, len(self.__nodes) + 1)
self.__nodes[node] = _id
return _id
def __node_id(self, node):
"""Return unique node id.
Parameters
----------
node : Theano graph node
Apply node, tensor variable, or shared variable in compute graph.
Returns
-------
str
Unique node id.
"""
if node in self.__nodes:
return self.__nodes[node]
else:
return self.__add_node(node)
def __call__(self, fct, graph=None):
"""Create pydot graph from function.
Parameters
----------
fct : theano.compile.function_module.Function
A compiled Theano function, variable, apply or a list of variables.
graph: pydot.Dot
`pydot` graph to which nodes are added. Creates new one if
undefined.
Returns
-------
pydot.Dot
Pydot graph of `fct`
"""
if graph is None:
graph = pd.Dot()
self.__nodes = {}
profile = None
if isinstance(fct, Function):
mode = fct.maker.mode
if (not isinstance(mode, ProfileMode) or
fct not in mode.profile_stats):
mode = None
if mode:
profile = mode.profile_stats[fct]
else:
profile = getattr(fct, "profile", None)
outputs = fct.maker.fgraph.outputs
topo = fct.maker.fgraph.toposort()
elif isinstance(fct, gof.FunctionGraph):
outputs = fct.outputs
topo = fct.toposort()
else:
if isinstance(fct, gof.Variable):
fct = [fct]
elif isinstance(fct, gof.Apply):
fct = fct.outputs
assert isinstance(fct, (list, tuple))
assert all(isinstance(v, gof.Variable) for v in fct)
fct = gof.FunctionGraph(inputs=gof.graph.inputs(fct),
outputs=fct)
outputs = fct.outputs
topo = fct.toposort()
outputs = list(outputs)
# Loop over apply nodes
for node in topo:
nparams = {}
__node_id = self.__node_id(node)
nparams['name'] = __node_id
nparams['label'] = apply_label(node)
nparams['profile'] = apply_profile(node, profile)
nparams['node_type'] = 'apply'
nparams['apply_op'] = nparams['label']
nparams['shape'] = self.shapes['apply']
use_color = None
for opName, color in iteritems(self.apply_colors):
if opName in node.op.__class__.__name__:
use_color = color
if use_color:
nparams['style'] = 'filled'
nparams['fillcolor'] = use_color
nparams['type'] = 'colored'
pd_node = dict_to_pdnode(nparams)
graph.add_node(pd_node)
# Loop over input nodes
for id, var in enumerate(node.inputs):
var_id = self.__node_id(var.owner if var.owner else var)
if var.owner is None:
vparams = {}
vparams['name'] = var_id
vparams['label'] = var_label(var)
vparams['node_type'] = 'input'
if isinstance(var, gof.Constant):
vparams['node_type'] = 'constant_input'
elif isinstance(var, theano.tensor.sharedvar.
TensorSharedVariable):
vparams['node_type'] = 'shared_input'
vparams['dtype'] = type_to_str(var.type)
vparams['tag'] = var_tag(var)
vparams['style'] = 'filled'
vparams['fillcolor'] = self.node_colors[
vparams['node_type']]
vparams['shape'] = self.shapes['input']
pd_var = dict_to_pdnode(vparams)
graph.add_node(pd_var)
edge_params = {}
if hasattr(node.op, 'view_map') and \
id in reduce(list.__add__,
itervalues(node.op.view_map), []):
edge_params['color'] = self.node_colors['output']
elif hasattr(node.op, 'destroy_map') and \
id in reduce(list.__add__,
itervalues(node.op.destroy_map), []):
edge_params['color'] = 'red'
edge_label = vparams['dtype']
if len(node.inputs) > 1:
edge_label = str(id) + ' ' + edge_label
pdedge = pd.Edge(var_id, __node_id, label=edge_label,
**edge_params)
graph.add_edge(pdedge)
# Loop over output nodes
for id, var in enumerate(node.outputs):
var_id = self.__node_id(var)
if var in outputs or len(var.clients) == 0:
vparams = {}
vparams['name'] = var_id
vparams['label'] = var_label(var)
vparams['node_type'] = 'output'
vparams['dtype'] = type_to_str(var.type)
vparams['tag'] = var_tag(var)
vparams['style'] = 'filled'
if len(var.clients) == 0:
vparams['fillcolor'] = self.node_colors['unused']
else:
vparams['fillcolor'] = self.node_colors['output']
vparams['shape'] = self.shapes['output']
pd_var = dict_to_pdnode(vparams)
graph.add_node(pd_var)
graph.add_edge(pd.Edge(__node_id, var_id,
label=vparams['dtype']))
elif var.name or not self.compact:
graph.add_edge(pd.Edge(__node_id, var_id,
label=vparams['dtype']))
# Create sub-graph for OpFromGraph nodes
if isinstance(node.op, builders.OpFromGraph):
subgraph = pd.Cluster(__node_id)
gf = PyDotFormatter()
# Use different node prefix for sub-graphs
gf.__node_prefix = __node_id
gf(node.op.fn, subgraph)
graph.add_subgraph(subgraph)
pd_node.get_attributes()['subg'] = subgraph.get_name()
def format_map(m):
return str([list(x) for x in m])
# Inputs mapping
ext_inputs = [self.__node_id(x) for x in node.inputs]
int_inputs = [gf.__node_id(x)
for x in node.op.fn.maker.fgraph.inputs]
assert len(ext_inputs) == len(int_inputs)
h = format_map(zip(ext_inputs, int_inputs))
pd_node.get_attributes()['subg_map_inputs'] = h
# Outputs mapping
ext_outputs = []
for n in topo:
for i in n.inputs:
h = i.owner if i.owner else i
if h is node:
ext_outputs.append(self.__node_id(n))
int_outputs = node.op.fn.maker.fgraph.outputs
int_outputs = [gf.__node_id(x) for x in int_outputs]
assert len(ext_outputs) == len(int_outputs)
h = format_map(zip(int_outputs, ext_outputs))
pd_node.get_attributes()['subg_map_outputs'] = h
return graph
def var_label(var, precision=3):
"""Return label of variable node."""
if var.name is not None:
return var.name
elif isinstance(var, gof.Constant):
h = np.asarray(var.data)
is_const = False
if h.ndim == 0:
is_const = True
h = np.array([h])
dstr = np.array2string(h, precision=precision)
if '\n' in dstr:
dstr = dstr[:dstr.index('\n')]
if is_const:
dstr = dstr.replace('[', '').replace(']', '')
return dstr
else:
return type_to_str(var.type)
def var_tag(var):
"""Parse tag attribute of variable node."""
tag = var.tag
if hasattr(tag, 'trace') and len(tag.trace) and len(tag.trace[0]) == 4:
path, line, _, src = tag.trace[0]
path = os.path.basename(path)
path = path.replace('<', '')
path = path.replace('>', '')
src = src.encode()
return [path, line, src]
else:
return None
def apply_label(node):
"""Return label of apply node."""
return node.op.__class__.__name__
def apply_profile(node, profile):
"""Return apply profiling informaton."""
if not profile or profile.fct_call_time == 0:
return None
time = profile.apply_time.get(node, 0)
call_time = profile.fct_call_time
return [time, call_time]
def broadcastable_to_str(b):
"""Return string representation of broadcastable."""
named_broadcastable = {(): 'scalar',
(False,): 'vector',
(False, True): 'col',
(True, False): 'row',
(False, False): 'matrix'}
if b in named_broadcastable:
bcast = named_broadcastable[b]
else:
bcast = ''
return bcast
def dtype_to_char(dtype):
"""Return character that represents data type."""
dtype_char = {
'complex64': 'c',
'complex128': 'z',
'float32': 'f',
'float64': 'd',
'int8': 'b',
'int16': 'w',
'int32': 'i',
'int64': 'l'}
if dtype in dtype_char:
return dtype_char[dtype]
else:
return 'X'
def type_to_str(t):
"""Return str of variable type."""
if not hasattr(t, 'broadcastable'):
return str(t)
s = broadcastable_to_str(t.broadcastable)
if s == '':
s = str(t.dtype)
else:
s = dtype_to_char(t.dtype) + s
return s
def dict_to_pdnode(d):
"""Create pydot node from dict."""
e = dict()
for k, v in iteritems(d):
if v is not None:
if isinstance(v, list):
v = '\t'.join([str(x) for x in v])
else:
v = str(v)
v = str(v)
v = v.replace('"', '\'')
e[k] = v
pynode = pd.Node(**e)
return pynode
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="%% CSS_DIR %%/d3viz.css"/>
<link rel="stylesheet" href="%% CSS_DIR %%/d3-context-menu.css"/>
<script type="text/javascript" src="%% JS_DIR %%/d3viz.js"></script>
<script type="text/javascript" src="%% JS_DIR %%/d3.v3.min.js"></script>
<script type="text/javascript" src="%% JS_DIR %%/dagre-d3.min.js"></script>
<script type="text/javascript" src="%% JS_DIR %%/graphlib-dot.min.js"></script>
<script type="text/javascript" src="%% JS_DIR %%/d3-context-menu.js"></script>
</head>
<body>
<div id='menu' class='menuBar'>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
// Backend graph in DOT format
var dotGraph = graphlibDot.read("%% DOT_GRAPH %%");
// Frontend graph for visualization
var graph = {};
var forceLayout;
var isProfiled = false; // true if profiling information available
var useProfileColors = false;
var fixOnInit = true; // fix nodes on initialization
var maxProfilePer = 0;
var profileColors = ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15"];
var pad = 10;
var isEditNode = false; // true if node is edited
var menuItems = [
{
title: 'Edit',
action: function(elm, d, i) { editNode(elm, d); }
},
{
title: 'Release',
action: function(elm, d, i) { releaseNode(d); }
}
];
// Create main panel
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
// Zoom behaviour
var zoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
.on('zoom', function(d) {
var trans = d3.event.translate;
trans[0] += 300;
trans[1] += 100;
pane.attr('transform', 'translate(' + trans + ') scale(' + d3.event.scale + ')');
});
svg.call(zoom);
zoom.event(svg);
svg.on("dblclick.zoom", null);
// Edges
var edgeDiv = d3.select('body').append('div')
.attr('class', 'edgeTooltip')
.style('opacity', 0.0);
// Div for node details
var nodeInfo = d3.select('body').append('div')
.attr('class', 'nodeInfo')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
// Initialize graph
processDotGraph(dotGraph);
graph = frontEndGraph(dotGraph);
drawGraph();
</script>
</body>
</html>
d3.contextMenu = function (menu, openCallback) {
// create the div element that will hold the context menu
d3.selectAll('.d3-context-menu').data([1])
.enter()
.append('div')
.attr('class', 'd3-context-menu');
// close menu
d3.select('body').on('click.d3-context-menu', function() {
d3.select('.d3-context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
return function(data, index) {
var elm = this;
d3.selectAll('.d3-context-menu').html('');
var list = d3.selectAll('.d3-context-menu').append('ul');
list.selectAll('li').data(menu).enter()
.append('li')
.html(function(d) {
return d.title;
})
.on('click', function(d, i) {
d.action(elm, data, index);
d3.select('.d3-context-menu').style('display', 'none');
});
// the openCallback allows an action to fire before the menu is displayed
// an example usage would be closing a tooltip
if (openCallback) openCallback(data, index);
// display context menu
d3.select('.d3-context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
};
};
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Theano javascript library for interactive visualization.
*
* Author: Christof Angermueller <cangermueller@gmail.com>
*/
/*
* Checks if variable is defined.
*/
function exists(x) {
return typeof(x) != 'undefined';
}
/*
* Replace all patterns in string.
*/
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
}
/*
* Computes len equally spaces points between start and end.
*/
function linspace(start, end, len) {
var d = (end - start) / (len - 1);
var rv = [start];
for (i = 1; i < len; ++i) {
rv.push(rv[i - 1] + d);
}
return rv;
}
/*
* Converts string to list
*/
function str2List(s) {
s = s.split('\t');
return s;
}
/*
* Flips y-scale such that (0, 0) points to top-left corner.
*/
function flipAxes(nodes) {
var size = [0, 0];
for (var i in nodes) {
var node = nodes[i];
size[0] = Math.max(size[0], node.pos[0] + node.width);
size[1] = Math.max(size[1], node.pos[1] + node.height);
}
for (var i in nodes) {
var node = nodes[i];
node.pos[1] = size[1] - (node.pos[1] + node.height);
}
}
/*
* Preprocesses raw dotGraph
*/
function processDotGraph(dotGraph) {
// Ignore cluster nodes
dotGraph.rnodes = {};
for (var nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.id = nodeId;
node.isCluster = nodeId.substr(0, 7) == 'cluster';
if (!node.isCluster) {
dotGraph.rnodes[nodeId] = node;
}
}
// Precompute attributes
var i = 0;
for (var nodeId in dotGraph.rnodes) {
var node = dotGraph._nodes[nodeId];
node.pos = node.pos.split(',').map(function(d) {return parseInt(d);});
var size = textSize(node.label, {'class': 'nodeText'});
node.width = size.width + 2 * pad;
node.height = size.height + 2 * pad;
node.cx = node.width / 2;
node.cy = node.height / 2;
node.hasChilds = exists(node.subg);
node.showChilds = false;
if (exists(node.profile)) {
node.profile = parseProfile(node.profile);
isProfiled = true;
}
if (exists(node.tag)) {
node.tag = str2List(node.tag);
}
if (exists(node.subg_map_inputs)) {
node.subg_map_inputs = eval(node.subg_map_inputs)
}
if (exists(node.subg_map_outputs)) {
node.subg_map_outputs = eval(node.subg_map_outputs)
}
}
flipAxes(dotGraph.rnodes);
// Offset and scale positions
var posMin = [Infinity, Infinity];
for (var i in dotGraph.rnodes) {
var node = dotGraph._nodes[i];
posMin[0] = Math.min(posMin[0], node.pos[0]);
posMin[1] = Math.min(posMin[1], node.pos[1]);
}
for (var i in dotGraph.rnodes) {
var node = dotGraph._nodes[i];
var pos = node.pos;
pos[0] -= posMin[0];
pos[1] -= posMin[1];
pos[0] = 1.2 * pos[0];
pos[1] = 1.2 * pos[1];
}
// Preprocess edges
var edges = dotGraph.edges();
for (var i in edges) {
var edge = dotGraph.edge(edges[i]);
var size = textSize(edge.label, {'class': 'edgeLabelText'});
edge.width = size.width + 2 * pad;
edge.height = size.height + 2 * pad;
if (!exists(edge.color)) {
edge.color = 'black';
}
switch (edge.color) {
case 'dodgerblue':
edge.type = 'b';
break;
case 'red':
edge.type = 'r';
break;
default:
edge.type = 'n';
}
}
}
/*
* Extracts profiling information from string.
*/
function parseProfile(s) {
var p = str2List(s);
p = p.map(function(x) { return parseFloat(x); });
return p;
}
/*
* Preprocesses DOT nodes for front-end visualization.
* Assigns all children of parent (root of graph if not specified)
* to the same group and calls function recursively on children.
*
*/
function traverseChilds(dotGraph, nodes, groups, parent) {
var preId = '';
var ref = undefined;
// Create new group with parent as parent
var group = {'id': groups.length, 'nodes': [], 'parent': parent};
if (exists(parent)) {
ref = parent.value.subg;
group.parent = parent;
group.nodes.push(parent);
parent.group = group;
}
groups.push(group);
// Loop over all children
var childs = dotGraph.children(ref);
for (var i in childs) {
var child = dotGraph.node(childs[i]);
if (child.isCluster) {
continue;
}
var node = {
'id': child.id,
'value': child,
'index': nodes.length,
'fixed': fixOnInit,
'group': group,
'isParent': child.showChilds,
'parent': parent
};
nodes.push(node);
if (child.showChilds) {
// Recurse if child is root of subcluster that should be expandend
traverseChilds(dotGraph, nodes, groups, node);
} else {
group.nodes.push(node);
}
}
// Groups appended to groups after group are group children.
group.childs = [];
for (var i = group.id + 1; i < groups.length; ++i) {
group.childs.push(groups[i].id);
}
}
/*
* Computes width and height of group of nodes.
*/
function groupSize(nodes) {
var minPos = [Infinity, Infinity];
var maxPos = [-Infinity, -Infinity];
for (var i in nodes) {
var node = nodes[i];
if (node.isParent) {
continue;
}
minPos[0] = Math.min(minPos[0], node.value.pos[0]);
minPos[1] = Math.min(minPos[1], node.value.pos[1]);
maxPos[0] = Math.max(maxPos[0], node.value.pos[0] + node.value.width);
maxPos[1] = Math.max(maxPos[1], node.value.pos[1] + node.value.height);
}
return [maxPos[0] - minPos[0], maxPos[1] - minPos[1]];
}
/*
* Creates front-end graph for visualizing from DOT graph.
*/
function frontEndGraph(dotGraph, prevGraph) {
var graph = {'nodes': [], 'groups': []};
traverseChilds(dotGraph, graph.nodes, graph.groups);
// Dictionary to access nodes by id
graph.nodesd = {};
for (var i in graph.nodes) {
var node = graph.nodes[i];
graph.nodesd[node.id] = node;
}
// Dictionary to access groups by id
graph.groupsd = {};
for (var i in graph.groups) {
var group = graph.groups[i];
graph.groupsd[group.id] = group;
}
// Parent nodes
graph.nodesp = graph.nodes.filter(function(d) {return d.isParent;});
// Non-parent nodes
graph.nodesn = graph.nodes.filter(function(d) {return !d.isParent;});
// Compute size of groups
for (i in graph.groups) {
var group = graph.groups[i];
group.size = groupSize(group.nodes);
var parent = group.parent;
if (exists(parent)) {
var prevParent = prevGraph.nodesd[group.parent.id];
if (exists(prevParent)) {
// Restore previous group position if given
group.pos = [prevParent.x, prevParent.y];
} else {
// Use position of parent otherwise
group.pos = parent.value.pos.slice(0);
}
group.pos[0] += parent.value.cx;
group.pos[1] += parent.value.cy;
} else {
group.pos = [group.size[0] / 2, group.size[1] / 2];
}
// Offset nodes on group center
var min = [Infinity, Infinity];
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
min[0] = Math.min(min[0], node.value.pos[0]);
min[1] = Math.min(min[0], node.value.pos[1]);
}
}
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
node.x = group.pos[0] - group.size[0] / 2 + node.value.pos[0] - min[0];
node.y = group.pos[1] - group.size[1] / 2 + node.value.pos[1] - min[1];
}
}
}
graph.size = graph.groups[0].size;
// Reuse previous positions
if (exists(prevGraph)) {
for (var i in graph.nodes) {
var node = graph.nodes[i];
var prevNode;
prevNode = prevGraph.nodesd[node.id];
if (exists(prevNode)) {
node.x = prevNode.x;
node.y = prevNode.y;
node.fixed = prevNode.fixed;
} else {
for (var j in prevGraph.groups) {
var group = prevGraph.groups[j];
if (exists(group.parent) && group.parent.id == node.id) {
node.x = group.pos[0] + group.size[0] / 2;
node.y = group.pos[1] + group.size[1] / 2;
}
}
}
}
}
// Edges
graph.edges = [];
for (var i in graph.nodesn) {
for (var j in graph.nodesn) {
var source = graph.nodesn[i];
var target = graph.nodesn[j];
var dotEdge = dotGraph.edge(source.value.id, target.value.id);
if (exists(dotEdge)) {
var edge = {};
edge.source = parseInt(source.index);
edge.target = parseInt(target.index);
edge.value = dotEdge;
graph.edges.push(edge);
}
// Redirect edges to subgraph
function redirectEdges(map, dotEdge) {
for (var k in map) {
var kmap = map[k];
if (kmap[0] == source.id && kmap[1] == target.id) {
var edge = {};
edge.source = parseInt(source.index);
edge.target = parseInt(target.index);
edge.value = dotEdge;
graph.edges.push(edge);
}
}
}
var map = undefined;
if (exists(target.parent)) {
var parent = target.parent;
var dotEdge = dotGraph.edge(source.id, parent.id);
if (exists(dotEdge)) {
map = parent.value.subg_map_inputs;
redirectEdges(map, dotEdge);
}
}
if (exists(source.parent)) {
var parent = source.parent;
var dotEdge = dotGraph.edge(parent.id, target.id);
if (exists(dotEdge)) {
map = parent.value.subg_map_outputs;
redirectEdges(map, dotEdge);
}
}
}
}
return graph;
}
/*
* Computes d3.js convex hull surrounding nodes that
* belong to the same group.
*/
function convexHulls(graph, offset) {
var hulls = [];
offset = offset || 20;
for (var i in graph.groups) {
var group = graph.groups[i];
if (!exists(group.parent)) {
continue;
}
var points = [];
for (var j in group.nodes) {
var node = group.nodes[j];
if (!node.isParent) {
points.push([node.x - node.value.cx - offset, node.y - node.value.cy - offset]);
points.push([node.x - node.value.cx - offset, node.y + node.value.cy + offset]);
points.push([node.x + node.value.cx + offset, node.y - node.value.cy - offset]);
points.push([node.x + node.value.cx + offset, node.y + node.value.cy + offset]);
}
}
for (var k in group.childs) {
var nodes = graph.groupsd[group.childs[k]].nodes;
for (var j in nodes) {
var node = nodes[j];
if (!node.isParent) {
points.push([node.x - node.value.cx - offset, node.y - node.value.cy - offset]);
points.push([node.x - node.value.cx - offset, node.y + node.value.cy + offset]);
points.push([node.x + node.value.cx + offset, node.y - node.value.cy - offset]);
points.push([node.x + node.value.cx + offset, node.y + node.value.cy + offset]);
}
}
}
hulls.push({group: i, path: d3.geom.hull(points)});
}
return hulls;
}
/*
* Draws convex hull.
*/
function drawConvexHull(d) {
var curve = d3.svg.line()
.interpolate("cardinal-closed")
.tension(.85);
return curve(d.path);
}
/*
* Creates skeleton for graph visualization.
* Positions will be updated by updateGraph().
*/
function drawGraph() {
d3.select('body').select('#menu').select('#toggleColors').remove();
if (isProfiled) {
d3.select('body').select('#menu').append('input')
.attr('id', 'toggleColors')
.attr('type', 'button')
.attr('value', 'Toggle profile colors')
.attr('onclick', "toggleNodeColors()");
maxProfilePer = 0;
for (i in graph.nodes) {
var p = graph.nodes[i].value.profile;
if (exists(p)) {
maxProfilePer = Math.max(maxProfilePer, p[0] / p[1]);
}
}
}
var isEdgeOver = false;
var isEdgeLabelOver = false;
// Event handler for dragging groups
var dragHulls = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
forceLayout.stop();
})
.on("drag", function dragged(d) {
// Shift all group members
var group = graph.groups[d.group];
for (var i in group.nodes) {
var node = group.nodes[i];
node.x += d3.event.dx;
node.y += d3.event.dy;
node.px += d3.event.dx;
node.py += d3.event.dy;
}
group.pos[0] += d3.event.dx;
group.pos[1] += d3.event.dy;
// Shift all members of sub groups
for (var k in group.childs) {
var cgroup = graph.groupsd[group.childs[k]];
var nodes = cgroup.nodes;
for (var j in nodes) {
var node = nodes[j];
node.x += d3.event.dx;
node.y += d3.event.dy;
node.px += d3.event.dx;
node.py += d3.event.dy;
cgroup.pos[0] += d3.event.dx;
cgroup.pos[1] += d3.event.dy;
}
}
updateGraph();
})
.on('dragend', function(d) {forceLayout.resume();});
// Draw convex hull surrounding group of nodes
graph.hulls = convexHulls(graph);
hulls = pane.selectAll('#hulls').remove();
hulls = pane.append('g').attr('id', 'hulls')
.selectAll('path')
.data(graph.hulls).enter()
.append('path')
.attr('class', 'hull')
.attr('d', drawConvexHull)
.call(dragHulls);
// Event handler to open/close groups
hulls.on('dblclick', function(d) {
var group = graph.groups[d.group];
group.parent.value.showChilds = !group.parent.value.showChilds;
if (!group.parent.value.showChilds) {
for (i in group.childs) {
var child = graph.groupsd[group.childs[i]];
child.parent.value.showChilds = false;
}
}
graph = frontEndGraph(dotGraph, graph);
drawGraph();
});
// Add edges
edges = pane.selectAll('#edges').remove();
edges = pane.append('g').attr('id', 'edges')
.selectAll('path').data(graph.edges).enter().append('path')
.attr('class', 'edge')
.attr('stroke', function(d) {return d.value.color;})
.attr('marker-mid', function(d) { return 'url(#edgeArrow_' + d.value.type + ')';});
edges.on('mouseover', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 1.0);
edgeDiv.transition()
.duration(200)
.style('opacity', .9);
edgeDiv
.html(d.value.label)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
});
edges.on('mouseout', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 0.4);
edgeDiv.transition()
.duration(200)
.style('opacity', 0);
});
// Add nodes
pane.selectAll('#nodes').remove();
nodes = pane.append('g').attr('id', 'nodes')
.selectAll('g').data(graph.nodesn).enter().append('g');
updateNodes();
updateGraph();
nodes.on('dblclick', function(d) {
if (d.value.hasChilds) {
d.value.showChilds = !d.value.showChilds;
graph = frontEndGraph(dotGraph, graph);
if (!fixOnInit && d.value.showChilds) {
var n = dotGraph.neighbors(d.id);
for (i in n) {
graph.nodesd[n[i]].fixed = false;
}
}
drawGraph();
}
});
nodes.on('mouseover', function(node) {
// Highlight incoming edges
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source == node || d.target == node) {
edge.transition()
.duration(200)
.style('opacity', 1.0);
}
});
// Show node details
if (!isEditNode) {
nodeInfo.transition()
.duration(200)
.style('opacity', .9);
nodeInfo
.html(formatNodeInfos(node))
.style('left', (d3.event.pageX) + 30 + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
}
});
nodes.on('mouseout', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source.index == node.index || d.target.index == node.index) {
edge.transition()
.duration(200)
.style('opacity', 0.4);
}
});
hideNodeInfo();
});
nodes.on('contextmenu', d3.contextMenu(menuItems));
forceLayout = d3.layout.force()
.nodes(graph.nodes)
.links(graph.edges)
.size(graph.size)
.linkDistance(200)
.charge(-1000)
.linkStrength(0.2)
.gravity(0.05)
.friction(0.5)
.on('tick', updateGraph)
.start();
// Drag behavour
var drag = forceLayout.drag()
.on('dragstart', function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
d.fixed = true;
});
nodes.call(drag);
}
/*
* Computes weighted average between two points.
*/
function avgPos(x1, y1, x2, y2, c) {
x = (1 - c) * x1 + c * x2;
y = (1 - c) * y1 + c * y2;
p = x + ',' + y;
return p;
}
/*
* Checks for collisions in d3.js quadtree.
* See http://bl.ocks.org/mbostock/3231298 for more details.
*/
function collide(node) {
var eps = 10;
var nx1 = node.x - node.value.cx - eps;
var nx2 = node.x + node.value.cx + eps;
var ny1 = node.y - node.value.cy - eps;
var ny2 = node.y + node.value.cy + eps;
return function(quad, x1, y1, x2, y2) {
var point = quad.point;
if (point && (point != node) && !point.fixed && ! node.fixed) {
var px1 = point.x - point.value.cx;
var px2 = point.x + point.value.cx;
var py1 = point.y - point.value.cy;
var py2 = point.y + point.value.cy;
if (!(px1 > nx2 || px2 < nx1 || py1 >= ny2 || py2 <= ny1)) {
var eta = 0.1;
if (px1 < nx1) {
// move quad to left
var d = eta * (px2 - nx1);
point.x -= d;
node.x += d;
} else {
var d = eta * (nx2 - px1);
point.x += d;
node.x -= d;
}
if (py1 < ny1) {
// move quad to top
var d = eta * (py2 - ny1);
point.y -= d;
node.y += d;
} else {
var d = eta * (ny2 - py1);
point.y += d;
node.y -= d;
}
}
}
return x1 > nx2 || x2 < nx1 || y1 >= ny2 || y2 <= ny1;
};
}
/*
* Computes euclidean distance between points.
*/
function distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}
/*
* Updates graph visualization.
*/
function updateGraph() {
// Avoid collisions
var q = d3.geom.quadtree(graph.nodes);
for (var i in graph.nodes) {
q.visit(collide(graph.nodes[i]));
}
graph.hulls = convexHulls(graph);
hulls.data(graph.hulls)
.attr('d', drawConvexHull);
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; });
// Update edges
edges.attr('d', function(d) {
var dist = 100;
var l = distance(d.source.x, d.source.y, d.target.x, d.target.y);
var n = Math.max(2, Math.floor(l / dist));
var marker = [];
for (var i = 1; i < n; ++i) {
marker.push(i / n);
}
var markerPos = marker.map(function(c) {
return avgPos(d.source.x, d.source.y, d.target.x, d.target.y, c);});
var markerPos = ' L' + markerPos.join(' L');
return 'M' + d.source.x + ',' + d.source.y + markerPos + ' L' + d.target.x + ',' + d.target.y;
});
}
/*
* Toggles between usual nodes colors and profiling colors
*/
function toggleNodeColors() {
useProfileColors = !useProfileColors;
updateNodes();
updateGraph();
}
/*
* Computes bounding box that fits text of a certain length.
*/
function textSize(text, attr) {
var t = svg.append('text').text(text);
if (typeof(attr) != 'undefined') {
for (a in attr) {
t.attr(a, attr[a]);
}
}
var bbox = t.node().getBBox();
t.remove();
return bbox;
}
/*
* Computes profiling color.
*/
function profileColor(per) {
var s = d3.scale.linear()
.domain(linspace(0, maxProfilePer, profileColors.length))
.range(profileColors)
.interpolate(d3.interpolateRgb);
return s(per);
}
/*
* Retuns node fill color.
*/
function nodeFillColor(d) {
if (useProfileColors) {
var p = d.value.profile;
if (d.value.node_type == 'apply' && exists(p)) {
return profileColor(d.value.profile[0] / d.value.profile[1]);
} else {
return 'white';
}
} else {
return typeof(d.value.fillcolor) == 'undefined' ? 'white' : d.value.fillcolor;
}
}
/*
* Formats profiling timing information.
*/
function formatTime(sec) {
var s;
if (sec < 0.1) {
s = (sec * 1000).toFixed(1) + ' ms';
} else {
s = sec.toFixed(1) + ' s';
}
return s;
}
/*
* Formats node details.
*/
function formatNodeInfos(node) {
var v = node.value;
var s = '<b><center>' + v.label + '</center></b><hr>';
s += '<b>Node:</b> ' + replaceAll(v.node_type, '_', ' ') + ' node';
if (exists(v.dtype)) {
s += '</br>';
s += '<b>Type:</b> <source>' + v.dtype + '</source>';
}
if (exists(v.apply_op)) {
s += '</br>';
s += '<b>Apply:</b> <source>' + v.apply_op + '</source>';
}
if (exists(v.tag)) {
s += '<p>';
s += '<b>Location:</b> <source>' + v.tag[1] + ': ' + v.tag[0] + '</source><br>';
s += '<b>Definition:</b> <source>' + v.tag[2] + '</source><br>';
s += '</p>';
}
var p = v.profile;
if (exists(p)) {
s += '<p>';
s += '<b>Time:</b> ' + formatTime(p[0]);
s += ' / ' + (p[0] / p[1] * 100).toFixed(1) + ' %';
s += '</p>';
}
return s;
}
/*
* Updates node visualization.
*/
function updateNode(d, node) {
var shape;
if (d.value.shape == 'ellipse') {
node.selectAll('ellipse').remove();
shape = node.append('ellipse')
.attr('class', 'nodeEllipse')
.attr('cx', d.value.cx)
.attr('cy', d.value.cy)
.attr('rx', d.value.width * 0.6)
.attr('ry', d.value.height * 0.6);
} else {
node.selectAll('rect').remove();
shape = node.append('rect')
.attr('class', 'nodeRect')
.attr('width', d.value.width)
.attr('height', d.value.height);
}
shape.attr('fill', nodeFillColor(d));
node.selectAll('text').remove();
var text = node.append('text')
.attr('class', 'nodeText')
.attr('x', pad)
.attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;});
if (d.value.hasChilds) {
node.style('cursor', 'pointer');
}
}
/*
* Updates visualization of all nodes.
*/
function updateNodes() {
nodes.each(function(d) {
var node = d3.select(this);
updateNode(d, node);
});
}
/*
* Hides node information field.
*/
function hideNodeInfo() {
nodeInfo.transition()
.duration(200)
.style('opacity', 0);
}
/*
* Adjusts node size.
*/
function setNodeSize(node) {
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
}
/*
* Event handler for editing nodes.
*/
function editNode(elm, d) {
var node = d3.select(elm);
var pos = elm.getBBox();
if (d3.event.defaultPrevented) return;
isEditNode = true;
hideNodeInfo();
var form = node.append('foreignObject')
.attr('x', pos.x)
.attr('y', pos.y)
.attr('width', d.value.width)
.attr('height', 25);
var input = form.append('xhtml:form').append('input')
.attr('style', 'width: ' + d.value.width + 'px')
.attr('value', function() {
this.focus();
return d.value.label;
})
.on('blur', function() {
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
isEditNode = false;
})
.on('keypress', function() {
if (!d3.event) {
d3.event = window.event;
}
var event = d3.event;
if (event.keyCode == 13) {
if (typeof(event.cancelBubble)) {
event.cancelBubble = true;
}
if (event.stopPropagation) {
event.stopPropagation();
}
event.preventDefault();
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
isEditNode = false;
}
});
}
/*
* Release node from fixed positions.
*/
function releaseNode(d) {
d.fixed = false;
forceLayout.start();
}
/*
* Releases positions of all nodes.
*/
function releaseNodes() {
graph['nodes'].forEach (function (d) {
d.fixed = false;
});
forceLayout.start();
}
/*
* Restores original node positions.
*/
function resetNodes() {
graph = frontEndGraph(dotGraph);
drawGraph();
}
(function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var j=typeof require=="function"&&require;if(!h&&j)return j(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}var f=typeof require=="function"&&require;for(var g=0;g<d.length;g++)e(d[g]);return e})({1:[function(a,b,c){var d=self;d.dagreD3=a("./index")},{"./index":2}],2:[function(a,b,c){b.exports={Digraph:a("graphlib").Digraph,Renderer:a("./lib/Renderer"),json:a("graphlib").converter.json,layout:a("dagre").layout,version:a("./lib/version")}},{"./lib/Renderer":3,"./lib/version":4,dagre:11,graphlib:28}],3:[function(a,b,c){function g(){this._layout=d(),this.drawNodes(k),this.drawEdgeLabels(l),this.drawEdgePaths(m),this.positionNodes(n),this.positionEdgeLabels(o),this.positionEdgePaths(p),this.transition(q),this.postLayout(r),this.postRender(s),this.edgeInterpolate("bundle"),this.edgeTension(.95)}function h(a){var b=a.copy();return b.nodes().forEach(function(a){var c=b.node(a);c===undefined&&(c={},b.node(a,c)),"label"in c||(c.label="")}),b.edges().forEach(function(a){var c=b.edge(a);c===undefined&&(c={},b.edge(a,c)),"label"in c||(c.label="")}),b}function i(a,b){var c=a.getBBox();b.width=c.width,b.height=c.height}function j(a,b){var c=b.run(a);return a.eachNode(function(a,b){c.node(a).label=b.label}),a.eachEdge(function(a,b,d,e){c.edge(a).label=e.label}),c}function k(a,b){var c=a.nodes().filter(function(b){return!z(a,b)}),d=b.selectAll("g.node").classed("enter",!1).data(c,function(a){return a});return d.selectAll("*").remove(),d.enter().append("g").style("opacity",0).attr("class","node enter"),d.each(function(b){t(a.node(b),e.select(this),10,10)}),this._transition(d.exit()).style("opacity",0).remove(),d}function l(a,b){var c=b.selectAll("g.edgeLabel").classed("enter",!1).data(a.edges(),function(a){return a});return c.selectAll("*").remove(),c.enter().append("g").style("opacity",0).attr("class","edgeLabel enter"),c.each(function(b){t(a.edge(b),e.select(this),0,0)}),this._transition(c.exit()).style("opacity",0).remove(),c}function n(a,b,c){function d(b){var c=a.node(b);return"translate("+c.x+","+c.y+")"}b.filter(".enter").attr("transform",d),this._transition(b).style("opacity",1).attr("transform",d)}function o(a,b){function c(b){var c=a.edge(b),d=x(c.points);return"translate("+d.x+","+d.y+")"}b.filter(".enter").attr("transform",c),this._transition(b).style("opacity",1).attr("transform",c)}function p(a,b){function f(b){var f=a.edge(b),g=a.node(a.incidentNodes(b)[0]),h=a.node(a.incidentNodes(b)[1]),i=f.points.slice(),j=i.length===0?h:i[0],k=i.length===0?g:i[i.length-1];return i.unshift(y(g,j)),i.push(y(h,k)),e.svg.line().x(function(a){return a.x}).y(function(a){return a.y}).interpolate(c).tension(d)(i)}var c=this._edgeInterpolate,d=this._edgeTension;b.filter(".enter").selectAll("path").attr("d",f),this._transition(b.selectAll("path")).attr("d",f).style("opacity",1)}function q(a){return a}function r(){}function s(a,b){a.isDirected()&&b.select("#arrowhead").empty()&&b.append("svg:defs").append("svg:marker").attr("id","arrowhead").attr("viewBox","0 0 10 10").attr("refX",8).attr("refY",5).attr("markerUnits","strokewidth").attr("markerWidth",8).attr("markerHeight",5).attr("orient","auto").attr("style","fill: #333").append("svg:path").attr("d","M 0 0 L 10 5 L 0 10 z")}function t(a,b,c,d){var e=a.label,f=b.append("rect"),g=b.append("g");e[0]==="<"?(u(e,g),c=d=0):v(e,g,Math.floor(a.labelCols),a.labelCut);var h=b.node().getBBox();g.attr("transform","translate("+ -h.width/2+","+ -h.height/2+")"),f.attr("rx",5).attr("ry",5).attr("x",-(h.width/2+c)).attr("y",-(h.height/2+d)).attr("width",h.width+2*c).attr("height",h.height+2*d)}function u(a,b){var c=b.append("foreignObject").attr("width","100000"),d,e;c.append("xhtml:div").style("float","left").html(function(){return a}).each(function(){d=this.clientWidth,e=this.clientHeight}),c.attr("width",d).attr("height",e)}function v(a,b,c,d){d===undefined&&(d="false"),d=d.toString().toLowerCase()==="true";var e=b.append("text").attr("text-anchor","left");a=a.replace(/\\n/g,"\n");var f=c?w(a,c,d):a;f=f.split("\n");for(var g=0;g<f.length;g++)e.append("tspan").attr("dy","1em").attr("x","1").text(f[g])}function w(a,b,c,d){d=d||"\n",b=b||75,c=c||!1;if(!a)return a;var e=".{1,"+b+"}(\\s|$)"+(c?"|.{"+b+"}|.+$":"|\\S+?(\\s|$)");return a.match(RegExp(e,"g")).join(d)}function x(a){var b=a.length/2;if(a.length%2)return a[Math.floor(b)];var c=a[b-1],d=a[b];return{x:(c.x+d.x)/2,y:(c.y+d.y)/2}}function y(a,b){var c=a.x,d=a.y,e=b.x-c,f=b.y-d,g=a.width/2,h=a.height/2,i,j;return Math.abs(f)*g>Math.abs(e)*h?(f<0&&(h=-h),i=f===0?0:h*e/f,j=h):(e<0&&(g=-g),i=g,j=e===0?0:g*f/e),{x:c+i,y:d+j}}function z(a,b){return"children"in a&&a.children(b).length}function A(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}}var d=a("dagre").layout,e;try{e=a("d3")}catch(f){e=window.d3}b.exports=g,g.prototype.layout=function(a){return arguments.length?(this._layout=a,this):this._layout},g.prototype.drawNodes=function(a){return arguments.length?(this._drawNodes=A(a,this),this):this._drawNodes},g.prototype.drawEdgeLabels=function(a){return arguments.length?(this._drawEdgeLabels=A(a,this),this):this._drawEdgeLabels},g.prototype.drawEdgePaths=function(a){return arguments.length?(this._drawEdgePaths=A(a,this),this):this._drawEdgePaths},g.prototype.positionNodes=function(a){return arguments.length?(this._positionNodes=A(a,this),this):this._positionNodes},g.prototype.positionEdgeLabels=function(a){return arguments.length?(this._positionEdgeLabels=A(a,this),this):this._positionEdgeLabels},g.prototype.positionEdgePaths=function(a){return arguments.length?(this._positionEdgePaths=A(a,this),this):this._positionEdgePaths},g.prototype.transition=function(a){return arguments.length?(this._transition=A(a,this),this):this._transition},g.prototype.postLayout=function(a){return arguments.length?(this._postLayout=A(a,this),this):this._postLayout},g.prototype.postRender=function(a){return arguments.length?(this._postRender=A(a,this),this):this._postRender},g.prototype.edgeInterpolate=function(a){return arguments.length?(this._edgeInterpolate=a,this):this._edgeInterpolate},g.prototype.edgeTension=function(a){return arguments.length?(this._edgeTension=a,this):this._edgeTension},g.prototype.run=function(a,b){a=h(a),b.selectAll("g.edgePaths, g.edgeLabels, g.nodes").data(["edgePaths","edgeLabels","nodes"]).enter().append("g").attr("class",function(a){return a});var c=this._drawNodes(a,b.select("g.nodes")),d=this._drawEdgeLabels(a,b.select("g.edgeLabels"));c.each(function(b){i(this,a.node(b))}),d.each(function(b){i(this,a.edge(b))});var e=j(a,this._layout);this._postLayout(e,b);var f=this._drawEdgePaths(a,b.select("g.edgePaths"));return this._positionNodes(e,c),this._positionEdgeLabels(e,d),this._positionEdgePaths(e,f),this._postRender(e,b),e};var m=function(a,b){var c=b.selectAll("g.edgePath").classed("enter",!1).data(a.edges(),function(a){return a});return c.enter().append("g").attr("class","edgePath enter").append("path").style("opacity",0).attr("marker-end","url(#arrowhead)"),this._transition(c.exit()).style("opacity",0).remove(),c}},{d3:10,dagre:11}],4:[function(a,b,c){b.exports="0.1.5"},{}],5:[function(a,b,c){c.Set=a("./lib/Set"),c.PriorityQueue=a("./lib/PriorityQueue"),c.version=a("./lib/version")},{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(a,b,c){function d(){this._arr=[],this._keyIndices={}}b.exports=d,d.prototype.size=function(){return this._arr.length},d.prototype.keys=function(){return this._arr.map(function(a){return a.key})},d.prototype.has=function(a){return a in this._keyIndices},d.prototype.priority=function(a){var b=this._keyIndices[a];if(b!==undefined)return this._arr[b].priority},d.prototype.min=function(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key},d.prototype.add=function(a,b){var c=this._keyIndices;if(a in c)return!1;var d=this._arr,e=d.length;return c[a]=e,d.push({key:a,priority:b}),this._decrease(e),!0},d.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var a=this._arr.pop();return delete this._keyIndices[a.key],this._heapify(0),a.key},d.prototype.decrease=function(a,b){var c=this._keyIndices[a];if(b>this._arr[c].priority)throw new Error("New priority is greater than current priority. Key: "+a+" Old: "+this._arr[c].priority+" New: "+b);this._arr[c].priority=b,this._decrease(c)},d.prototype._heapify=function(a){var b=this._arr,c=2*a,d=c+1,e=a;c<b.length&&(e=b[c].priority<b[e].priority?c:e,d<b.length&&(e=b[d].priority<b[e].priority?d:e),e!==a&&(this._swap(a,e),this._heapify(e)))},d.prototype._decrease=function(a){var b=this._arr,c=b[a].priority,d;while(a!==0){d=a>>1;if(b[d].priority<c)break;this._swap(a,d),a=d}},d.prototype._swap=function(a,b){var c=this._arr,d=this._keyIndices,e=c[a],f=c[b];c[a]=f,c[b]=e,d[f.key]=a,d[e.key]=b}},{}],7:[function(a,b,c){function e(a){this._size=0,this._keys={};if(a)for(var b=0,c=a.length;b<c;++b)this.add(a[b])}function f(a){var b=Object.keys(a),c=b.length,d=new Array(c),e;for(e=0;e<c;++e)d[e]=a[b[e]];return d}var d=a("./util");b.exports=e,e.intersect=function(a){if(a.length===0)return new e;var b=new e(d.isArray(a[0])?a[0]:a[0].keys());for(var c=1,f=a.length;c<f;++c){var g=b.keys(),h=d.isArray(a[c])?new e(a[c]):a[c];for(var i=0,j=g.length;i<j;++i){var k=g[i];h.has(k)||b.remove(k)}}return b},e.union=function(a){var b=d.reduce(a,function(a,b){return a+(b.size?b.size():b.length)},0),c=new Array(b),f=0;for(var g=0,h=a.length;g<h;++g){var i=a[g],j=d.isArray(i)?i:i.keys();for(var k=0,l=j.length;k<l;++k)c[f++]=j[k]}return new e(c)},e.prototype.size=function(){return this._size},e.prototype.keys=function(){return f(this._keys)},e.prototype.has=function(a){return a in this._keys},e.prototype.add=function(a){return a in this._keys?!1:(this._keys[a]=a,++this._size,!0)},e.prototype.remove=function(a){return a in this._keys?(delete this._keys[a],--this._size,!0):!1}},{"./util":8}],8:[function(a,b,c){Array.isArray?c.isArray=Array.isArray:c.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"},"function"!=typeof Array.prototype.reduce?c.reduce=function(a,b,c){"use strict";if(null===a||"undefined"==typeof a)throw new TypeError("Array.prototype.reduce called on null or undefined");if("function"!=typeof b)throw new TypeError(b+" is not a function");var d,e,f=a.length>>>0,g=!1;1<arguments.length&&(e=c,g=!0);for(d=0;f>d;++d)a.hasOwnProperty(d)&&(g?e=b(e,a[d],d,a):(e=a[d],g=!0));if(!g)throw new TypeError("Reduce of empty array with no initial value");return e}:c.reduce=function(a,b,c){return a.reduce(b,c)}},{}],9:[function(a,b,c){b.exports="1.1.3"},{}],10:[function(a,b,c){a("./d3"),b.exports=d3,function(){delete this.d3}()},{}],11:[function(a,b,c){c.Digraph=a("graphlib").Digraph,c.Graph=a("graphlib").Graph,c.layout=a("./lib/layout"),c.version=a("./lib/version")},{"./lib/layout":12,"./lib/version":27,graphlib:28}],12:[function(a,b,c){var d=a("./util"),e=a("./rank"),f=a("./order"),g=a("graphlib").CGraph,h=a("graphlib").CDigraph;b.exports=function(){function j(a){var c=new h;a.eachNode(function(a,b){b===undefined&&(b={}),c.addNode(a,{width:b.width,height:b.height}),b.hasOwnProperty("rank")&&(c.node(a).prefRank=b.rank)}),a.parent&&a.nodes().forEach(function(b){c.parent(b,a.parent(b))}),a.eachEdge(function(a,b,d,e){e===undefined&&(e={});var f={e:a,minLen:e.minLen||1,width:e.width||0,height:e.height||0,points:[]};c.addEdge(null,b,d,f)});var d=a.graph()||{};return c.graph({rankDir:d.rankDir||b.rankDir,orderRestarts:d.orderRestarts}),c}function k(a){var g=i.rankSep(),h;try{return h=d.time("initLayoutGraph",j)(a),h.order()===0?h:(h.eachEdge(function(a,b,c,d){d.minLen*=2}),i.rankSep(g/2),d.time("rank.run",e.run)(h,b.rankSimplex),d.time("normalize",l)(h),d.time("order",f)(h,b.orderMaxSweeps),d.time("position",c.run)(h),d.time("undoNormalize",m)(h),d.time("fixupEdgePoints",n)(h),d.time("rank.restoreEdges",e.restoreEdges)(h),d.time("createFinalGraph",o)(h,a.isDirected()))}finally{i.rankSep(g)}}function l(a){var b=0;a.eachEdge(function(c,d,e,f){var g=a.node(d).rank,h=a.node(e).rank;if(g+1<h){for(var i=d,j=g+1,k=0;j<h;++j,++k){var l="_D"+ ++b,m={width:f.width,height:f.height,edge:{id:c,source:d,target:e,attrs:f},rank:j,dummy:!0};k===0?m.index=0:j+1===h&&(m.index=1),a.addNode(l,m),a.addEdge(null,i,l,{}),i=l}a.addEdge(null,i,e,{}),a.delEdge(c)}})}function m(a){a.eachNode(function(b,c){if(c.dummy){if("index"in c){var d=c.edge;a.hasEdge(d.id)||a.addEdge(d.id,d.source,d.target,d.attrs);var e=a.edge(d.id).points;e[c.index]={x:c.x,y:c.y,ul:c.ul,ur:c.ur,dl:c.dl,dr:c.dr}}a.delNode(b)}})}function n(a){a.eachEdge(function(a,b,c,d){d.reversed&&d.points.reverse()})}function o(a,b){var c=b?new h:new g;c.graph(a.graph()),a.eachNode(function(a,b){c.addNode(a,b)}),a.eachNode(function(b){c.parent(b,a.parent(b))}),a.eachEdge(function(a,b,d,e){c.addEdge(e.e,b,d,e)});var d=0,e=0;return a.eachNode(function(b,c){a.children(b).length||(d=Math.max(d,c.x+c.width/2),e=Math.max(e,c.y+c.height/2))}),a.eachEdge(function(a,b,c,f){var g=Math.max.apply(Math,f.points.map(function(a){return a.x})),h=Math.max.apply(Math,f.points.map(function(a){return a.y}));d=Math.max(d,g+f.width/2),e=Math.max(e,h+f.height/2)}),c.graph().width=d,c.graph().height=e,c}function p(a){return function(){return arguments.length?(a.apply(null,arguments),i):a()}}var b={debugLevel:0,orderMaxSweeps:f.DEFAULT_MAX_SWEEPS,rankSimplex:!1,rankDir:"TB"},c=a("./position")(),i={};return i.orderIters=d.propertyAccessor(i,b,"orderMaxSweeps"),i.rankSimplex=d.propertyAccessor(i,b,"rankSimplex"),i.nodeSep=p(c.nodeSep),i.edgeSep=p(c.edgeSep),i.universalSep=p(c.universalSep),i.rankSep=p(c.rankSep),i.rankDir=d.propertyAccessor(i,b,"rankDir"),i.debugAlignment=p(c.debugAlignment),i.debugLevel=d.propertyAccessor(i,b,"debugLevel",function(a){d.log.level=a,c.debugLevel(a)}),i.run=d.time("Total layout",k),i._normalize=l,i}},{"./order":13,"./position":18,"./rank":19,"./util":26,graphlib:28}],13:[function(a,b,c){function k(a,b){function o(){a.eachNode(function(a,b){m[a]=b.order})}arguments.length<2&&(b=j);var c=a.graph().orderRestarts||0,h=f(a);h.forEach(function(b){b=b.filterNodes(function(b){return!a.children(b).length})});var i=0,k,l=Number.MAX_VALUE,m={};for(var p=0;p<Number(c)+1&&l!==0;++p){k=Number.MAX_VALUE,g(a,c>0),d.log(2,"Order phase start cross count: "+a.graph().orderInitCC);var q,r,s;for(q=0,r=0;r<4&&q<b&&k>0;++q,++r,++i)n(a,h,q),s=e(a),s<k&&(r=0,k=s,s<l&&(o(),l=s)),d.log(3,"Order phase start "+p+" iter "+q+" cross count: "+s)}Object.keys(m).forEach(function(b){if(!a.children||!a.children(b).length)a.node(b).order=m[b]}),a.graph().orderCC=l,d.log(2,"Order iterations: "+i),d.log(2,"Order phase best cross count: "+a.graph().orderCC)}function l(a,b){var c={};return b.forEach(function(b){c[b]=a.inEdges(b).map(function(b){return a.node(a.source(b)).order})}),c}function m(a,b){var c={};return b.forEach(function(b){c[b]=a.outEdges(b).map(function(b){return a.node(a.target(b)).order})}),c}function n(a,b,c){c%2===0?o(a,b,c):p(a,b,c)}function o(a,b){var c;for(i=1;i<b.length;++i)c=h(b[i],c,l(a,b[i].nodes()))}function p(a,b){var c;for(i=b.length-2;i>=0;--i)h(b[i],c,m(a,b[i].nodes()))}var d=a("./util"),e=a("./order/crossCount"),f=a("./order/initLayerGraphs"),g=a("./order/initOrder"),h=a("./order/sortLayer");b.exports=k;var j=24;k.DEFAULT_MAX_SWEEPS=j},{"./order/crossCount":14,"./order/initLayerGraphs":15,"./order/initOrder":16,"./order/sortLayer":17,"./util":26}],14:[function(a,b,c){function e(a){var b=0,c=d.ordering(a);for(var e=1;e<c.length;++e)b+=f(a,c[e-1],c[e]);return b}function f(a,b,c){var d=[];b.forEach(function(b){var c=[];a.outEdges(b).forEach(function(b){c.push(a.node(a.target(b)).order)}),c.sort(function(a,b){return a-b}),d=d.concat(c)});var e=1;while(e<c.length)e<<=1;var f=2*e-1;e-=1;var g=[];for(var h=0;h<f;++h)g[h]=0;var i=0;return d.forEach(function(a){var b=a+e;++g[b];while(b>0)b%2&&(i+=g[b+1]),b=b-1>>1,++g[b]}),i}var d=a("../util");b.exports=e},{"../util":26}],15:[function(a,b,c){function f(a){function c(d){if(d===null){a.children(d).forEach(function(a){c(a)});return}var f=a.node(d);f.minRank="rank"in f?f.rank:Number.MAX_VALUE,f.maxRank="rank"in f?f.rank:Number.MIN_VALUE;var h=new e;return a.children(d).forEach(function(b){var d=c(b);h=e.union([h,d]),f.minRank=Math.min(f.minRank,a.node(b).minRank),f.maxRank=Math.max(f.maxRank,a.node(b).maxRank)}),"rank"in f&&h.add(f.rank),h.keys().forEach(function(a){a in b||(b[a]=[]),b[a].push(d)}),h}var b=[];c(null);var f=[];return b.forEach(function(b,c){f[c]=a.filterNodes(d(b))}),f}var d=a("graphlib").filter.nodesFromList,e=a("cp-data").Set;b.exports=f},{"cp-data":5,graphlib:28}],16:[function(a,b,c){function f(a,b){var c=[];a.eachNode(function(b,d){var e=c[d.rank];if(a.children&&a.children(b).length>0)return;e||(e=c[d.rank]=[]),e.push(b)}),c.forEach(function(c){b&&e.shuffle(c),c.forEach(function(b,c){a.node(b).order=c})});var f=d(a);a.graph().orderInitCC=f,a.graph().orderCC=Number.MAX_VALUE}var d=a("./crossCount"),e=a("../util");b.exports=f},{"../util":26,"./crossCount":14}],17:[function(a,b,c){function e(a,b,c){var e=[],f={};a.eachNode(function(a,b){e[b.order]=a;var g=c[a];g.length&&(f[a]=d.sum(g)/g.length)});var g=a.nodes().filter(function(a){return f[a]!==undefined});g.sort(function(b,c){return f[b]-f[c]||a.node(b).order-a.node(c).order});for(var h=0,i=0,j=g.length;i<j;++h)f[e[h]]!==undefined&&(a.node(g[i++]).order=h)}var d=a("../util");b.exports=e},{"../util":26}],18:[function(a,b,c){var d=a("./util");b.exports=function(){function c(b){b=b.filterNodes(d.filterNonSubgraphs(b));var c=d.ordering(b),e=f(b,c),i={};["u","d"].forEach(function(d){d==="d"&&c.reverse(),["l","r"].forEach(function(f){f==="r"&&m(c);var j=d+f,k=g(b,c,e,d==="u"?"predecessors":"successors");i[j]=h(b,c,k.pos,k.root,k.align),a.debugLevel>=3&&t(d+f,b,c,i[j]),f==="r"&&l(i[j]),f==="r"&&m(c)}),d==="d"&&c.reverse()}),k(b,c,i),b.eachNode(function(a){var c=[];for(var d in i){var e=i[d][a];r(d,b,a,e),c.push(e)}c.sort(function(a,b){return a-b}),q(b,a,(c[1]+c[2])/2)});var j=0,p=b.graph().rankDir==="BT"||b.graph().rankDir==="RL";c.forEach(function(c){var e=d.max(c.map(function(a){return o(b,a)}));j+=e/2,c.forEach(function(a){s(b,a,p?-j:j)}),j+=e/2+a.rankSep});var u=d.min(b.nodes().map(function(a){return q(b,a)-n(b,a)/2})),v=d.min(b.nodes().map(function(a){return s(b,a)-o(b,a)/2}));b.eachNode(function(a){q(b,a,q(b,a)-u),s(b,a,s(b,a)-v)})}function e(a,b){return a<b?a.toString().length+":"+a+"-"+b:b.toString().length+":"+b+"-"+a}function f(a,b){function k(a){var b=d[a];if(b<h||b>j)c[e(g[i],a)]=!0}var c={},d={},f,g,h,i,j;if(b.length<=2)return c;b[1].forEach(function(a,b){d[a]=b});for(var l=1;l<b.length-1;++l){f=b[l],g=b[l+1],h=0,i=0;for(var m=0;m<g.length;++m){var n=g[m];d[n]=m,j=undefined;if(a.node(n).dummy){var o=a.predecessors(n)[0];o!==undefined&&a.node(o).dummy&&(j=d[o])}j===undefined&&m===g.length-1&&(j=f.length-1);if(j!==undefined){for(;i<=m;++i)a.predecessors(g[i]).forEach(k);h=j}}}return c}function g(a,b,c,d){var f={},g={},h={};return b.forEach(function(a){a.forEach(function(a,b){g[a]=a,h[a]=a,f[a]=b})}),b.forEach(function(b){var i=-1;b.forEach(function(b){var j=a[d](b),k;j.length>0&&(j.sort(function(a,b){return f[a]-f[b]}),k=(j.length-1)/2,j.slice(Math.floor(k),Math.ceil(k)+1).forEach(function(a){h[b]===b&&!c[e(a,b)]&&i<f[a]&&(h[a]=b,h[b]=g[b]=g[a],i=f[a])}))})}),{pos:f,root:g,align:h}}function h(a,b,c,e,f){function l(a,b,c){b in h[a]?h[a][b]=Math.min(h[a][b],c):h[a][b]=c}function m(b){if(!(b in k)){k[b]=0;var d=b;do{if(c[d]>0){var h=e[j[d]];m(h),g[b]===b&&(g[b]=g[h]);var i=p(a,j[d])+p(a,d);g[b]!==g[h]?l(g[h],g[b],k[b]-k[h]-i):k[b]=Math.max(k[b],k[h]+i)}d=f[d]}while(d!==b)}}var g={},h={},i={},j={},k={};return b.forEach(function(a){a.forEach(function(b,c){g[b]=b,h[b]={},c>0&&(j[b]=a[c-1])})}),d.values(e).forEach(function(a){m(a)}),b.forEach(function(a){a.forEach(function(a){k[a]=k[e[a]];if(a===e[a]&&a===g[a]){var b=0;a in h&&Object.keys(h[a]).length>0&&(b=d.min(Object.keys(h[a]).map(function(b){return h[a][b]+(b in i?i[b]:0)}))),i[a]=b}})}),b.forEach(function(a){a.forEach(function(a){k[a]+=i[g[e[a]]]||0})}),k}function i(a,b,c){return d.min(b.map(function(a){var b=a[0];return c[b]}))}function j(a,b,c){return d.max(b.map(function(a){var b=a[a.length-1];return c[b]}))}function k(a,b,c){function h(a){c[l][a]+=g[l]}var d={},e={},f,g={},k=Number.POSITIVE_INFINITY;for(var l in c){var m=c[l];d[l]=i(a,b,m),e[l]=j(a,b,m);var n=e[l]-d[l];n<k&&(k=n,f=l)}["u","d"].forEach(function(a){["l","r"].forEach(function(b){var c=a+b;g[c]=b==="l"?d[f]-d[c]:e[f]-e[c]})});for(l in c)a.eachNode(h)}function l(a){for(var b in a)a[b]=-a[b]}function m(a){a.forEach(function(a){a.reverse()})}function n(a,b){switch(a.graph().rankDir){case"LR":return a.node(b).height;case"RL":return a.node(b).height;default:return a.node(b).width}}function o(a,b){switch(a.graph().rankDir){case"LR":return a.node(b).width;case"RL":return a.node(b).width;default:return a.node(b).height}}function p(b,c){if(a.universalSep!==null)return a.universalSep;var d=n(b,c),e=b.node(c).dummy?a.edgeSep:a.nodeSep;return(d+e)/2}function q(a,b,c){if(a.graph().rankDir==="LR"||a.graph().rankDir==="RL"){if(arguments.length<3)return a.node(b).y;a.node(b).y=c}else{if(arguments.length<3)return a.node(b).x;a.node(b).x=c}}function r(a,b,c,d){if(b.graph().rankDir==="LR"||b.graph().rankDir==="RL"){if(arguments.length<3)return b.node(c)[a];b.node(c)[a]=d}else{if(arguments.length<3)return b.node(c)[a];b.node(c)[a]=d}}function s(a,b,c){if(a.graph().rankDir==="LR"||a.graph().rankDir==="RL"){if(arguments.length<3)return a.node(b).x;a.node(b).x=c}else{if(arguments.length<3)return a.node(b).y;a.node(b).y=c}}function t(a,b,c,d){c.forEach(function(c,e){var f,g;c.forEach(function(c){var h=d[c];if(f){var i=p(b,f)+p(b,c);h-g<i&&console.log("Position phase: sep violation. Align: "+a+". Layer: "+e+". "+"U: "+f+" V: "+c+". Actual sep: "+(h-g)+" Expected sep: "+i)}f=c,g=h})})}var a={nodeSep:50,edgeSep:10,universalSep:null,rankSep:30},b={};return b.nodeSep=d.propertyAccessor(b,a,"nodeSep"),b.edgeSep=d.propertyAccessor(b,a,"edgeSep"),b.universalSep=d.propertyAccessor(b,a,"universalSep"),b.rankSep=d.propertyAccessor(b,a,"rankSep"),b.debugLevel=d.propertyAccessor(b,a,"debugLevel"),b.run=c,b}},{"./util":26}],19:[function(a,b,c){function l(a,b){n(a),d.time("constraints.apply",h.apply)(a),o(a),d.time("acyclic",e)(a);var c=a.filterNodes(d.filterNonSubgraphs(a));f(c),j(c).forEach(function(a){var d=c.filterNodes(k.nodesFromList(a));r(d,b)}),d.time("constraints.relax",h.relax(a)),d.time("reorientEdges",q)(a)}function m(a){e.undo(a)}function n(a){a.eachEdge(function(b,c,d,e){if(c===d){var f=p(a,b,c,d,e,0,!1),g=p(a,b,c,d,e,1,!0),h=p(a,b,c,d,e,2,!1);a.addEdge(null,f,c,{minLen:1,selfLoop:!0}),a.addEdge(null,f,g,{minLen:1,selfLoop:!0}),a.addEdge(null,c,h,{minLen:1,selfLoop:!0}),a.addEdge(null,g,h,{minLen:1,selfLoop:!0}),a.delEdge(b)}})}function o(a){a.eachEdge(function(b,c,d,e){if(c===d){var f=e.originalEdge,g=p(a,f.e,f.u,f.v,f.value,0,!0);a.addEdge(null,c,g,{minLen:1}),a.addEdge(null,g,d,{minLen:1}),a.delEdge(b)}})}function p(a,b,c,d,e,f,g){return a.addNode(null,{width:g?e.width:0,height:g?e.height:0,edge:{id:b,source:c,target:d,attrs:e},dummy:!0,index:f})}function q(a){a.eachEdge(function(b,c,d,e){a.node(c).rank>a.node(d).rank&&(a.delEdge(b),e.reversed=!0,a.addEdge(b,d,c,e))})}function r(a,b){var c=g(a);b&&(d.log(1,"Using network simplex for ranking"),i(a,c)),s(a)}function s(a){var b=d.min(a.nodes().map(function(b){return a.node(b).rank}));a.eachNode(function(a,c){c.rank-=b})}var d=a("./util"),e=a("./rank/acyclic"),f=a("./rank/initRank"),g=a("./rank/feasibleTree"),h=a("./rank/constraints"),i=a("./rank/simplex"),j=a("graphlib").alg.components,k=a("graphlib").filter;c.run=l,c.restoreEdges=m},{"./rank/acyclic":20,"./rank/constraints":21,"./rank/feasibleTree":22,"./rank/initRank":23,"./rank/simplex":25,"./util":26,graphlib:28}],20:[function(a,b,c){function e(a){function f(d){if(d in c)return;c[d]=b[d]=!0,a.outEdges(d).forEach(function(c){var h=a.target(c),i;d===h?console.error('Warning: found self loop "'+c+'" for node "'+d+'"'):h in b?(i=a.edge(c),a.delEdge(c),i.reversed=!0,++e,a.addEdge(c,h,d,i)):f(h)}),delete b[d]}var b={},c={},e=0;return a.eachNode(function(a){f(a)}),d.log(2,"Acyclic Phase: reversed "+e+" edge(s)"),e}function f(a){a.eachEdge(function(b,c,d,e){e.reversed&&(delete e.reversed,a.delEdge(b),a.addEdge(b,d,c,e))})}var d=a("../util");b.exports=e,b.exports.undo=f},{"../util":26}],21:[function(a,b,c){function d(a){return a!=="min"&&a!=="max"&&a.indexOf("same_")!==0?(console.error("Unsupported rank type: "+a),!1):!0}function e(a,b,c,d){a.inEdges(b).forEach(function(b){var e=a.edge(b),f;e.originalEdge?f=e:f={originalEdge:{e:b,u:a.source(b),v:a.target(b),value:e},minLen:a.edge(b).minLen},e.selfLoop&&(d=!1),d?(a.addEdge(null,c,a.source(b),f),f.reversed=!0):a.addEdge(null,a.source(b),c,f)})}function f(a,b,c,d){a.outEdges(b).forEach(function(b){var e=a.edge(b),f;e.originalEdge?f=e:f={originalEdge:{e:b,u:a.source(b),v:a.target(b),value:e},minLen:a.edge(b).minLen},e.selfLoop&&(d=!1),d?(a.addEdge(null,a.target(b),c,f),f.reversed=!0):a.addEdge(null,c,a.target(b),f)})}function g(a,b,c){c!==undefined&&a.children(b).forEach(function(b){b!==c&&!a.outEdges(c,b).length&&!a.node(b).dummy&&a.addEdge(null,c,b,{minLen:0})})}function h(a,b,c){c!==undefined&&a.children(b).forEach(function(b){b!==c&&!a.outEdges(b,c).length&&!a.node(b).dummy&&a.addEdge(null,b,c,{minLen:0})})}c.apply=function(a){function b(c){var i={};a.children(c).forEach(function(g){if(a.children(g).length){b(g);return}var h=a.node(g),j=h.prefRank;if(j!==undefined){if(!d(j))return;j in i?i.prefRank.push(g):i.prefRank=[g];var k=i[j];k===undefined&&(k=i[j]=a.addNode(null,{originalNodes:[]}),a.parent(k,c)),e(a,g,k,j==="min"),f(a,g,k,j==="max"),a.node(k).originalNodes.push({u:g,value:h,parent:c}),a.delNode(g)}}),g(a,c,i.min),h(a,c,i.max)}b(null)},c.relax=function(a){var b=[];a.eachEdge(function(a,c,d,e){var f=e.originalEdge;f&&b.push(f)}),a.eachNode(function(b,c){var d=c.originalNodes;d&&(d.forEach(function(b){b.value.rank=c.rank,a.addNode(b.u,b.value),a.parent(b.u,b.parent)}),a.delNode(b))}),b.forEach(function(b){a.addEdge(b.e,b.u,b.v,b.value)})}},{}],22:[function(a,b,c){function g(a){function g(d){var e=!0;return a.predecessors(d).forEach(function(f){b.has(f)&&!h(a,f,d)&&(b.has(d)&&(c.addNode(d,{}),b.remove(d),c.graph({root:d})),c.addNode(f,{}),c.addEdge(null,f,d,{reversed:!0}),b.remove(f),g(f),e=!1)}),a.successors(d).forEach(function(f){b.has(f)&&!h(a,d,f)&&(b.has(d)&&(c.addNode(d,{}),b.remove(d),c.graph({root:d})),c.addNode(f,{}),c.addEdge(null,d,f,{}),b.remove(f),g(f),e=!1)}),e}function i(){var d=Number.MAX_VALUE;b.keys().forEach(function(c){a.predecessors(c).forEach(function(e){if(!b.has(e)){var f=h(a,e,c);Math.abs(f)<Math.abs(d)&&(d=-f)}}),a.successors(c).forEach(function(e){if(!b.has(e)){var f=h(a,c,e);Math.abs(f)<Math.abs(d)&&(d=f)}})}),c.eachNode(function(b){a.node(b).rank-=d})}var b=new d(a.nodes()),c=new e;if(b.size()===1){var f=a.nodes()[0];return c.addNode(f,{}),c.graph({root:f}),c}while(b.size()){var j=c.order()?c.nodes():b.keys();for(var k=0,l=j.length;k<l&&g(j[k]);++k);b.size()&&i()}return c}function h(a,b,c){var d=a.node(c).rank-a.node(b).rank,e=f.max(a.outEdges(b,c).map(function(b){return a.edge(b).minLen}));return d-e}var d=a("cp-data").Set,e=a("graphlib").Digraph,f=a("../util");b.exports=g},{"../util":26,"cp-data":5,graphlib:28}],23:[function(a,b,c){function f(a){var b=e(a);b.forEach(function(b){var c=a.inEdges(b);if(c.length===0){a.node(b).rank=0;return}var e=c.map(function(b){return a.node(a.source(b)).rank+a.edge(b).minLen});a.node(b).rank=d.max(e)})}var d=a("../util"),e=a("graphlib").alg.topsort;b.exports=f},{"../util":26,graphlib:28}],24:[function(a,b,c){function d(a,b,c,d){return Math.abs(a.node(b).rank-a.node(c).rank)-d}b.exports={slack:d}},{}],25:[function(a,b,c){function f(a,b){g(a,b);for(;;){var c=k(b);if(c===null)break;var d=l(a,b,c);m(a,b,c,d)}}function g(a,b){function c(d){var e=b.successors(d);for(var f in e){var g=e[f];c(g)}d!==b.graph().root&&i(a,b,d)}h(b),b.eachEdge(function(a,b,c,d){d.cutValue=0}),c(b.graph().root)}function h(a){function c(d){var e=a.successors(d),f=b;for(var g in e){var h=e[g];c(h),f=Math.min(f,a.node(h).low)}a.node(d).low=f,a.node(d).lim=b++}var b=0;c(a.graph().root)}function i(a,b,c){var d=b.inEdges(c)[0],e=[],f=b.outEdges(c);for(var g in f)e.push(b.target(f[g]));var h=0,i=0,k=0,l=0,m=0,n=a.outEdges(c),o;for(var p in n){var q=a.target(n[p]);for(o in e)j(b,q,e[o])&&i++;j(b,q,c)||l++}var r=a.inEdges(c);for(var s in r){var t=a.source(r[s]);for(o in e)j(b,t,e[o])&&k++;j(b,t,c)||m++}var u=0;for(o in e){var v=b.edge(f[o]).cutValue;b.edge(f[o]).reversed?u-=v:u+=v}b.edge(d).reversed?h-=u-i+k-l+m:h+=u-i+k-l+m,b.edge(d).cutValue=h}function j(a,b,c){return a.node(c).low<=a.node(b).lim&&a.node(b).lim<=a.node(c).lim}function k(a){var b=a.edges();for(var c in b){var d=b[c],e=a.edge(d);if(e.cutValue<0)return d}return null}function l(a,b,c){var d=b.source(c),f=b.target(c),g=b.node(f).lim<b.node(d).lim?f:d,h=!b.edge(c).reversed,i=Number.POSITIVE_INFINITY,k;h?a.eachEdge(function(d,f,h,l){if(d!==c&&j(b,f,g)&&!j(b,h,g)){var m=e.slack(a,f,h,l.minLen);m<i&&(i=m,k=d)}}):a.eachEdge(function(d,f,h,l){if(d!==c&&!j(b,f,g)&&j(b,h,g)){var m=e.slack(a,f,h,l.minLen);m<i&&(i=m,k=d)}});if(k===undefined){var l=[],m=[];throw a.eachNode(function(a){j(b,a,g)?m.push(a):l.push(a)}),new Error("No edge found from outside of tree to inside")}return k}function m(a,b,c,d){function h(a){var c=b.inEdges(a);for(var d in c){var e=c[d],f=b.source(e),g=b.edge(e);h(f),b.delEdge(e),g.reversed=!g.reversed,b.addEdge(e,a,f,g)}}b.delEdge(c);var e=a.source(d),f=a.target(d);h(f);var i=e,j=b.inEdges(i);while(j.length>0)i=b.source(j[0]),j=b.inEdges(i);b.graph().root=i,b.addEdge(null,e,f,{cutValue:0}),g(a,b),n(a,b)}function n(a,b){function c(d){var e=b.successors(d);e.forEach(function(b){var e=o(a,d,b);a.node(b).rank=a.node(d).rank+e,c(b)})}c(b.graph().root)}function o(a,b,c){var e=a.outEdges(b,c);if(e.length>0)return d.max(e.map(function(b){return a.edge(b).minLen}));var f=a.inEdges(b,c);if(f.length>0)return-d.max(f.map(function(b){return a.edge(b).minLen}))}var d=a("../util"),e=a("./rankUtil");b.exports=f},{"../util":26,"./rankUtil":24}],26:[function(a,b,c){function d(a,b){return function(){var c=(new Date).getTime();try{return b.apply(null,arguments)}finally{e(1,a+" time: "+((new Date).getTime()-c)+"ms")}}}function e(a){e.level>=a&&console.log.apply(console,Array.prototype.slice.call(arguments,1))}c.min=function(a){return Math.min.apply(Math,a)},c.max=function(a){return Math.max.apply(Math,a)},c.all=function(a,b){for(var c=0;c<a.length;++c)if(!b(a[c]))return!1;return!0},c.sum=function(a){return a.reduce(function(a,b){return a+b},0)},c.values=function(a){return Object.keys(a).map(function(b){return a[b]})},c.shuffle=function(a){for(i=a.length-1;i>0;--i){var b=Math.floor(Math.random()*(i+1)),c=a[b];a[b]=a[i],a[i]=c}},c.propertyAccessor=function(a,b,c,d){return function(e){return arguments.length?(b[c]=e,d&&d(e),a):b[c]}},c.ordering=function(a){var b=[];return a.eachNode(function(a,c){var d=b[c.rank]||(b[c.rank]=[]);d[c.order]=a}),b},c.filterNonSubgraphs=function(a){return function(b){return a.children(b).length===0}},d.enabled=!1,c.time=d,e.level=0,c.log=e},{}],27:[function(a,b,c){b.exports="0.4.5"},{}],28:[function(a,b,c){c.Graph=a("./lib/Graph"),c.Digraph=a("./lib/Digraph"),c.CGraph=a("./lib/CGraph"),c.CDigraph=a("./lib/CDigraph"),a("./lib/graph-converters"),c.alg={isAcyclic:a("./lib/alg/isAcyclic"),components:a("./lib/alg/components"),dijkstra:a("./lib/alg/dijkstra"),dijkstraAll:a("./lib/alg/dijkstraAll"),findCycles:a("./lib/alg/findCycles"),floydWarshall:a("./lib/alg/floydWarshall"),postorder:a("./lib/alg/postorder"),preorder:a("./lib/alg/preorder"),prim:a("./lib/alg/prim"),tarjan:a("./lib/alg/tarjan"),topsort:a("./lib/alg/topsort")},c.converter={json:a("./lib/converter/json.js")};var d=a("./lib/filter");c.filter={all:d.all,nodesFromList:d.nodesFromList},c.version=a("./lib/version")},{"./lib/CDigraph":30,"./lib/CGraph":31,"./lib/Digraph":32,"./lib/Graph":33,"./lib/alg/components":34,"./lib/alg/dijkstra":35,"./lib/alg/dijkstraAll":36,"./lib/alg/findCycles":37,"./lib/alg/floydWarshall":38,"./lib/alg/isAcyclic":39,"./lib/alg/postorder":40,"./lib/alg/preorder":41,"./lib/alg/prim":42,"./lib/alg/tarjan":43,"./lib/alg/topsort":44,"./lib/converter/json.js":46,"./lib/filter":47,"./lib/graph-converters":48,"./lib/version":50}],29:[function(a,b,c){function e(){this._value=undefined,this._nodes={},this._edges={},this._nextId=0}function f(a,b,c){(a[b]||(a[b]=new d)).add(
c)}function g(a,b,c){var d=a[b];d.remove(c),d.size()===0&&delete a[b]}var d=a("cp-data").Set;b.exports=e,e.prototype.order=function(){return Object.keys(this._nodes).length},e.prototype.size=function(){return Object.keys(this._edges).length},e.prototype.graph=function(a){if(arguments.length===0)return this._value;this._value=a},e.prototype.hasNode=function(a){return a in this._nodes},e.prototype.node=function(a,b){var c=this._strictGetNode(a);if(arguments.length===1)return c.value;c.value=b},e.prototype.nodes=function(){var a=[];return this.eachNode(function(b){a.push(b)}),a},e.prototype.eachNode=function(a){for(var b in this._nodes){var c=this._nodes[b];a(c.id,c.value)}},e.prototype.hasEdge=function(a){return a in this._edges},e.prototype.edge=function(a,b){var c=this._strictGetEdge(a);if(arguments.length===1)return c.value;c.value=b},e.prototype.edges=function(){var a=[];return this.eachEdge(function(b){a.push(b)}),a},e.prototype.eachEdge=function(a){for(var b in this._edges){var c=this._edges[b];a(c.id,c.u,c.v,c.value)}},e.prototype.incidentNodes=function(a){var b=this._strictGetEdge(a);return[b.u,b.v]},e.prototype.addNode=function(a,b){if(a===undefined||a===null){do a="_"+ ++this._nextId;while(this.hasNode(a))}else if(this.hasNode(a))throw new Error("Graph already has node '"+a+"'");return this._nodes[a]={id:a,value:b},a},e.prototype.delNode=function(a){this._strictGetNode(a),this.incidentEdges(a).forEach(function(a){this.delEdge(a)},this),delete this._nodes[a]},e.prototype._addEdge=function(a,b,c,d,e,g){this._strictGetNode(b),this._strictGetNode(c);if(a===undefined||a===null){do a="_"+ ++this._nextId;while(this.hasEdge(a))}else if(this.hasEdge(a))throw new Error("Graph already has edge '"+a+"'");return this._edges[a]={id:a,u:b,v:c,value:d},f(e[c],b,a),f(g[b],c,a),a},e.prototype._delEdge=function(a,b,c){var d=this._strictGetEdge(a);g(b[d.v],d.u,a),g(c[d.u],d.v,a),delete this._edges[a]},e.prototype.copy=function(){var a=new this.constructor;return a.graph(this.graph()),this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(b,c,d,e)}),a._nextId=this._nextId,a},e.prototype.filterNodes=function(a){var b=new this.constructor;return b.graph(this.graph()),this.eachNode(function(c,d){a(c)&&b.addNode(c,d)}),this.eachEdge(function(a,c,d,e){b.hasNode(c)&&b.hasNode(d)&&b.addEdge(a,c,d,e)}),b},e.prototype._strictGetNode=function(a){var b=this._nodes[a];if(b===undefined)throw new Error("Node '"+a+"' is not in graph");return b},e.prototype._strictGetEdge=function(a){var b=this._edges[a];if(b===undefined)throw new Error("Edge '"+a+"' is not in graph");return b}},{"cp-data":5}],30:[function(a,b,c){var d=a("./Digraph"),e=a("./compoundify"),f=e(d);b.exports=f,f.fromDigraph=function(a){var b=new f,c=a.graph();return c!==undefined&&b.graph(c),a.eachNode(function(a,c){c===undefined?b.addNode(a):b.addNode(a,c)}),a.eachEdge(function(a,c,d,e){e===undefined?b.addEdge(null,c,d):b.addEdge(null,c,d,e)}),b},f.prototype.toString=function(){return"CDigraph "+JSON.stringify(this,null,2)}},{"./Digraph":32,"./compoundify":45}],31:[function(a,b,c){var d=a("./Graph"),e=a("./compoundify"),f=e(d);b.exports=f,f.fromGraph=function(a){var b=new f,c=a.graph();return c!==undefined&&b.graph(c),a.eachNode(function(a,c){c===undefined?b.addNode(a):b.addNode(a,c)}),a.eachEdge(function(a,c,d,e){e===undefined?b.addEdge(null,c,d):b.addEdge(null,c,d,e)}),b},f.prototype.toString=function(){return"CGraph "+JSON.stringify(this,null,2)}},{"./Graph":33,"./compoundify":45}],32:[function(a,b,c){function g(){e.call(this),this._inEdges={},this._outEdges={}}var d=a("./util"),e=a("./BaseGraph"),f=a("cp-data").Set;b.exports=g,g.prototype=new e,g.prototype.constructor=g,g.prototype.isDirected=function(){return!0},g.prototype.successors=function(a){return this._strictGetNode(a),Object.keys(this._outEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.predecessors=function(a){return this._strictGetNode(a),Object.keys(this._inEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.neighbors=function(a){return f.union([this.successors(a),this.predecessors(a)]).keys()},g.prototype.sources=function(){var a=this;return this._filterNodes(function(b){return a.inEdges(b).length===0})},g.prototype.sinks=function(){var a=this;return this._filterNodes(function(b){return a.outEdges(b).length===0})},g.prototype.source=function(a){return this._strictGetEdge(a).u},g.prototype.target=function(a){return this._strictGetEdge(a).v},g.prototype.inEdges=function(a,b){this._strictGetNode(a);var c=f.union(d.values(this._inEdges[a])).keys();return arguments.length>1&&(this._strictGetNode(b),c=c.filter(function(a){return this.source(a)===b},this)),c},g.prototype.outEdges=function(a,b){this._strictGetNode(a);var c=f.union(d.values(this._outEdges[a])).keys();return arguments.length>1&&(this._strictGetNode(b),c=c.filter(function(a){return this.target(a)===b},this)),c},g.prototype.incidentEdges=function(a,b){return arguments.length>1?f.union([this.outEdges(a,b),this.outEdges(b,a)]).keys():f.union([this.inEdges(a),this.outEdges(a)]).keys()},g.prototype.toString=function(){return"Digraph "+JSON.stringify(this,null,2)},g.prototype.addNode=function(a,b){return a=e.prototype.addNode.call(this,a,b),this._inEdges[a]={},this._outEdges[a]={},a},g.prototype.delNode=function(a){e.prototype.delNode.call(this,a),delete this._inEdges[a],delete this._outEdges[a]},g.prototype.addEdge=function(a,b,c,d){return e.prototype._addEdge.call(this,a,b,c,d,this._inEdges,this._outEdges)},g.prototype.delEdge=function(a){e.prototype._delEdge.call(this,a,this._inEdges,this._outEdges)},g.prototype._filterNodes=function(a){var b=[];return this.eachNode(function(c){a(c)&&b.push(c)}),b}},{"./BaseGraph":29,"./util":49,"cp-data":5}],33:[function(a,b,c){function g(){e.call(this),this._incidentEdges={}}var d=a("./util"),e=a("./BaseGraph"),f=a("cp-data").Set;b.exports=g,g.prototype=new e,g.prototype.constructor=g,g.prototype.isDirected=function(){return!1},g.prototype.neighbors=function(a){return this._strictGetNode(a),Object.keys(this._incidentEdges[a]).map(function(a){return this._nodes[a].id},this)},g.prototype.incidentEdges=function(a,b){return this._strictGetNode(a),arguments.length>1?(this._strictGetNode(b),b in this._incidentEdges[a]?this._incidentEdges[a][b].keys():[]):f.union(d.values(this._incidentEdges[a])).keys()},g.prototype.toString=function(){return"Graph "+JSON.stringify(this,null,2)},g.prototype.addNode=function(a,b){return a=e.prototype.addNode.call(this,a,b),this._incidentEdges[a]={},a},g.prototype.delNode=function(a){e.prototype.delNode.call(this,a),delete this._incidentEdges[a]},g.prototype.addEdge=function(a,b,c,d){return e.prototype._addEdge.call(this,a,b,c,d,this._incidentEdges,this._incidentEdges)},g.prototype.delEdge=function(a){e.prototype._delEdge.call(this,a,this._incidentEdges,this._incidentEdges)}},{"./BaseGraph":29,"./util":49,"cp-data":5}],34:[function(a,b,c){function e(a){function e(b,d){c.has(b)||(c.add(b),d.push(b),a.neighbors(b).forEach(function(a){e(a,d)}))}var b=[],c=new d;return a.nodes().forEach(function(a){var c=[];e(a,c),c.length>0&&b.push(c)}),b}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],35:[function(a,b,c){function e(a,b,c,e){function h(b){var d=a.incidentNodes(b),e=d[0]!==i?d[0]:d[1],h=f[e],k=c(b),l=j.distance+k;if(k<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+b+" Weight: "+k);l<h.distance&&(h.distance=l,h.predecessor=i,g.decrease(e,l))}var f={},g=new d;c=c||function(){return 1},e=e||(a.isDirected()?function(b){return a.outEdges(b)}:function(b){return a.incidentEdges(b)}),a.eachNode(function(a){var c=a===b?0:Number.POSITIVE_INFINITY;f[a]={distance:c},g.add(a,c)});var i,j;while(g.size()>0){i=g.removeMin(),j=f[i];if(j.distance===Number.POSITIVE_INFINITY)break;e(i).forEach(h)}return f}var d=a("cp-data").PriorityQueue;b.exports=e},{"cp-data":5}],36:[function(a,b,c){function e(a,b,c){var e={};return a.eachNode(function(f){e[f]=d(a,f,b,c)}),e}var d=a("./dijkstra");b.exports=e},{"./dijkstra":35}],37:[function(a,b,c){function e(a){return d(a).filter(function(a){return a.length>1})}var d=a("./tarjan");b.exports=e},{"./tarjan":43}],38:[function(a,b,c){function d(a,b,c){var d={},e=a.nodes();return b=b||function(){return 1},c=c||(a.isDirected()?function(b){return a.outEdges(b)}:function(b){return a.incidentEdges(b)}),e.forEach(function(f){d[f]={},d[f][f]={distance:0},e.forEach(function(a){f!==a&&(d[f][a]={distance:Number.POSITIVE_INFINITY})}),c(f).forEach(function(c){var e=a.incidentNodes(c),h=e[0]!==f?e[0]:e[1],i=b(c);i<d[f][h].distance&&(d[f][h]={distance:i,predecessor:f})})}),e.forEach(function(a){var b=d[a];e.forEach(function(c){var f=d[c];e.forEach(function(c){var d=f[a],e=b[c],g=f[c],h=d.distance+e.distance;h<g.distance&&(g.distance=h,g.predecessor=e.predecessor)})})}),d}b.exports=d},{}],39:[function(a,b,c){function e(a){try{d(a)}catch(b){if(b instanceof d.CycleException)return!1;throw b}return!0}var d=a("./topsort");b.exports=e},{"./topsort":44}],40:[function(a,b,c){function e(a,b,c){function f(b,d){if(e.has(b))throw new Error("The input graph is not a tree: "+a);e.add(b),a.neighbors(b).forEach(function(a){a!==d&&f(a,b)}),c(b)}var e=new d;if(a.isDirected())throw new Error("This function only works for undirected graphs");f(b)}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],41:[function(a,b,c){function e(a,b,c){function f(b,d){if(e.has(b))throw new Error("The input graph is not a tree: "+a);e.add(b),c(b),a.neighbors(b).forEach(function(a){a!==d&&f(a,b)})}var e=new d;if(a.isDirected())throw new Error("This function only works for undirected graphs");f(b)}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],42:[function(a,b,c){function f(a,b){function i(c){var d=a.incidentNodes(c),e=d[0]!==h?d[0]:d[1],i=g.priority(e);if(i!==undefined){var j=b(c);j<i&&(f[e]=h,g.decrease(e,j))}}var c=new d,f={},g=new e,h;if(a.order()===0)return c;a.eachNode(function(a){g.add(a,Number.POSITIVE_INFINITY),c.addNode(a)}),g.decrease(a.nodes()[0],0);var j=!1;while(g.size()>0){h=g.removeMin();if(h in f)c.addEdge(null,h,f[h]);else{if(j)throw new Error("Input graph is not connected: "+a);j=!0}a.incidentEdges(h).forEach(i)}return c}var d=a("../Graph"),e=a("cp-data").PriorityQueue;b.exports=f},{"../Graph":33,"cp-data":5}],43:[function(a,b,c){function d(a){function f(h){var i=d[h]={onStack:!0,lowlink:b,index:b++};c.push(h),a.successors(h).forEach(function(a){a in d?d[a].onStack&&(i.lowlink=Math.min(i.lowlink,d[a].index)):(f(a),i.lowlink=Math.min(i.lowlink,d[a].lowlink))});if(i.lowlink===i.index){var j=[],k;do k=c.pop(),d[k].onStack=!1,j.push(k);while(h!==k);e.push(j)}}if(!a.isDirected())throw new Error("tarjan can only be applied to a directed graph. Bad input: "+a);var b=0,c=[],d={},e=[];return a.nodes().forEach(function(a){a in d||f(a)}),e}b.exports=d},{}],44:[function(a,b,c){function d(a){function f(g){if(g in c)throw new e;g in b||(c[g]=!0,b[g]=!0,a.predecessors(g).forEach(function(a){f(a)}),delete c[g],d.push(g))}if(!a.isDirected())throw new Error("topsort can only be applied to a directed graph. Bad input: "+a);var b={},c={},d=[],g=a.sinks();if(a.order()!==0&&g.length===0)throw new e;return a.sinks().forEach(function(a){f(a)}),d}function e(){}b.exports=d,d.CycleException=e,e.prototype.toString=function(){return"Graph has at least one cycle"}},{}],45:[function(a,b,c){function e(a){function b(){a.call(this),this._parents={},this._children={},this._children[null]=new d}return b.prototype=new a,b.prototype.constructor=b,b.prototype.parent=function(a,b){this._strictGetNode(a);if(arguments.length<2)return this._parents[a];if(a===b)throw new Error("Cannot make "+a+" a parent of itself");b!==null&&this._strictGetNode(b),this._children[this._parents[a]].remove(a),this._parents[a]=b,this._children[b].add(a)},b.prototype.children=function(a){return a!==null&&this._strictGetNode(a),this._children[a].keys()},b.prototype.addNode=function(b,c){return b=a.prototype.addNode.call(this,b,c),this._parents[b]=null,this._children[b]=new d,this._children[null].add(b),b},b.prototype.delNode=function(b){var c=this.parent(b);return this._children[b].keys().forEach(function(a){this.parent(a,c)},this),this._children[c].remove(b),delete this._parents[b],delete this._children[b],a.prototype.delNode.call(this,b)},b.prototype.copy=function(){var b=a.prototype.copy.call(this);return this.nodes().forEach(function(a){b.parent(a,this.parent(a))},this),b},b.prototype.filterNodes=function(b){function f(a){var b=c.parent(a);return b===null||d.hasNode(b)?(e[a]=b,b):b in e?e[b]:f(b)}var c=this,d=a.prototype.filterNodes.call(this,b),e={};return d.eachNode(function(a){d.parent(a,f(a))}),d},b}var d=a("cp-data").Set;b.exports=e},{"cp-data":5}],46:[function(a,b,c){function h(a){return Object.prototype.toString.call(a).slice(8,-1)}var d=a("../Graph"),e=a("../Digraph"),f=a("../CGraph"),g=a("../CDigraph");c.decode=function(a,b,c){c=c||e;if(h(a)!=="Array")throw new Error("nodes is not an Array");if(h(b)!=="Array")throw new Error("edges is not an Array");if(typeof c=="string")switch(c){case"graph":c=d;break;case"digraph":c=e;break;case"cgraph":c=f;break;case"cdigraph":c=g;break;default:throw new Error("Unrecognized graph type: "+c)}var i=new c;return a.forEach(function(a){i.addNode(a.id,a.value)}),i.parent&&a.forEach(function(a){a.children&&a.children.forEach(function(b){i.parent(b,a.id)})}),b.forEach(function(a){i.addEdge(a.id,a.u,a.v,a.value)}),i},c.encode=function(a){var b=[],c=[];a.eachNode(function(c,d){var e={id:c,value:d};if(a.children){var f=a.children(c);f.length&&(e.children=f)}b.push(e)}),a.eachEdge(function(a,b,d,e){c.push({id:a,u:b,v:d,value:e})});var h;if(a instanceof g)h="cdigraph";else if(a instanceof f)h="cgraph";else if(a instanceof e)h="digraph";else if(a instanceof d)h="graph";else throw new Error("Couldn't determine type of graph: "+a);return{nodes:b,edges:c,type:h}}},{"../CDigraph":30,"../CGraph":31,"../Digraph":32,"../Graph":33}],47:[function(a,b,c){var d=a("cp-data").Set;c.all=function(){return function(){return!0}},c.nodesFromList=function(a){var b=new d(a);return function(a){return b.has(a)}}},{"cp-data":5}],48:[function(a,b,c){var d=a("./Graph"),e=a("./Digraph");d.prototype.toDigraph=d.prototype.asDirected=function(){var a=new e;return this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(null,c,d,e),a.addEdge(null,d,c,e)}),a},e.prototype.toGraph=e.prototype.asUndirected=function(){var a=new d;return this.eachNode(function(b,c){a.addNode(b,c)}),this.eachEdge(function(b,c,d,e){a.addEdge(b,c,d,e)}),a}},{"./Digraph":32,"./Graph":33}],49:[function(a,b,c){c.values=function(a){var b=Object.keys(a),c=b.length,d=new Array(c),e;for(e=0;e<c;++e)d[e]=a[b[e]];return d}},{}],50:[function(a,b,c){b.exports="0.7.4"},{}]},{},[1]);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
import numpy as np
import theano as th
import theano.tensor as T
class Mlp(object):
def __init__(self, nfeatures=100, noutputs=10, nhiddens=50, rng=None):
if rng is None:
rng = 0
if isinstance(rng, int):
rng = np.random.RandomState(rng)
self.rng = rng
self.nfeatures = nfeatures
self.noutputs = noutputs
self.nhiddens = nhiddens
x = T.dmatrix('x')
wh = th.shared(self.rng.normal(0, 1, (nfeatures, nhiddens)),
borrow=True)
bh = th.shared(np.zeros(nhiddens), borrow=True)
h = T.nnet.sigmoid(T.dot(x, wh) + bh)
wy = th.shared(self.rng.normal(0, 1, (nhiddens, noutputs)))
by = th.shared(np.zeros(noutputs), borrow=True)
y = T.nnet.softmax(T.dot(h, wy) + by)
self.inputs = [x]
self.outputs = [y]
class OfgNested(object):
def __init__(self):
x, y, z = T.scalars('xyz')
e = x * y
op = th.OpFromGraph([x, y], [e])
e2 = op(x, y) + z
op2 = th.OpFromGraph([x, y, z], [e2])
e3 = op2(x, y, z) + z
self.inputs = [x, y, z]
self.outputs = [e3]
class Ofg(object):
def __init__(self):
x, y, z = T.scalars('xyz')
e = T.nnet.sigmoid((x + y + z)**2)
op = th.OpFromGraph([x, y, z], [e])
e2 = op(x, y, z) + op(z, y, x)
self.inputs = [x, y, z]
self.outputs = [e2]
from nose.plugins.skip import SkipTest
from theano.d3viz import has_requirements
if not has_requirements:
raise SkipTest('Missing requirements')
import numpy as np
import os.path as pt
import tempfile
import unittest
import filecmp
import theano as th
import theano.d3viz as d3v
from theano.d3viz.tests import models
class TestD3Viz(unittest.TestCase):
def setUp(self):
self.rng = np.random.RandomState(0)
self.data_dir = pt.join('data', 'test_d3viz')
def check(self, f, reference=None, verbose=False):
tmp_dir = tempfile.mkdtemp()
html_file = pt.join(tmp_dir, 'index.html')
if verbose:
print(html_file)
d3v.d3viz(f, html_file)
assert pt.getsize(html_file) > 0
if reference:
assert filecmp.cmp(html_file, reference)
def test_mlp(self):
m = models.Mlp()
f = th.function(m.inputs, m.outputs)
self.check(f)
def test_mlp_profiled(self):
m = models.Mlp()
profile = th.compile.profiling.ProfileStats(False)
f = th.function(m.inputs, m.outputs, profile=profile)
x_val = self.rng.normal(0, 1, (1000, m.nfeatures))
f(x_val)
self.check(f)
def test_ofg(self):
m = models.Ofg()
f = th.function(m.inputs, m.outputs)
self.check(f)
def test_ofg_nested(self):
m = models.OfgNested()
f = th.function(m.inputs, m.outputs)
self.check(f)
from nose.plugins.skip import SkipTest
from theano.d3viz import has_requirements
if not has_requirements:
raise SkipTest('Missing requirements')
import numpy as np
import unittest
import theano as th
from theano.d3viz.formatting import PyDotFormatter
from theano.d3viz.tests import models
class TestPyDotFormatter(unittest.TestCase):
def setUp(self):
self.rng = np.random.RandomState(0)
def node_counts(self, graph):
node_types = [node.get_attributes()['node_type']
for node in graph.get_nodes()]
a, b = np.unique(node_types, return_counts=True)
nc = dict(zip(a, b))
return nc
def test_mlp(self):
m = models.Mlp()
f = th.function(m.inputs, m.outputs)
pdf = PyDotFormatter()
graph = pdf(f)
self.assertEqual(len(graph.get_nodes()), 11)
nc = self.node_counts(graph)
assert nc['apply'] == 5
assert nc['output'] == 1
def test_ofg(self):
m = models.Ofg()
f = th.function(m.inputs, m.outputs)
pdf = PyDotFormatter()
graph = pdf(f)
assert len(graph.get_nodes()) == 10
sub_graphs = graph.get_subgraph_list()
assert len(sub_graphs) == 2
ofg1, ofg2 = sub_graphs
assert len(ofg1.get_nodes()) == 5
assert len(ofg1.get_nodes()) == len(ofg2.get_nodes())
def test_ofg_nested(self):
m = models.OfgNested()
f = th.function(m.inputs, m.outputs)
pdf = PyDotFormatter()
graph = pdf(f)
assert len(graph.get_nodes()) == 7
assert len(graph.get_subgraph_list()) == 1
ofg1 = graph.get_subgraph_list()[0]
assert len(ofg1.get_nodes()) == 6
assert len(ofg1.get_subgraph_list()) == 1
ofg2 = ofg1.get_subgraph_list()[0]
assert len(ofg2.get_nodes()) == 4
......@@ -207,6 +207,9 @@ whitelist_flake8 = [
"gof/unify.py",
"gof/__init__.py",
"gof/sandbox/equilibrium.py",
"d3viz/__init__.py",
"d3viz/tests/test_d3viz.py",
"d3viz/tests/test_formatting.py"
]
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论