diff --git a/.gitignore b/.gitignore index 50b083b58f..1332ef84d8 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ target .mypy_cache mutants .mutmut-cache +dagre/ +graphlib/ diff --git a/docs/developer/layout.md b/docs/developer/layout.md index 7496c3f87d..a74d239fe7 100644 --- a/docs/developer/layout.md +++ b/docs/developer/layout.md @@ -10,7 +10,7 @@ Directories are listed in order of how they are processed. Group UOps into kernels. -::: tinygrad.schedule.rangeify.get_rangeify +::: tinygrad.schedule.rangeify.get_kernel_graph options: members: false show_labels: false diff --git a/tinygrad/engine/allocations.py b/tinygrad/engine/allocations.py index 4c73e323e3..8508528e12 100644 --- a/tinygrad/engine/allocations.py +++ b/tinygrad/engine/allocations.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from tinygrad.uop.ops import UOp, UPat, PatternMatcher, Ops, GroupOp, graph_rewrite, identity_element +from tinygrad.uop.ops import UOp, UPat, PatternMatcher, Ops, GroupOp, graph_rewrite, identity_element, profile_matches from tinygrad.dtype import ImageDType from tinygrad.helpers import prod, DEBUG, argsort, VIZ @@ -125,7 +125,8 @@ pm_replace_buf = PatternMatcher([ (UPat(Ops.BIND, src=(UPat(Ops.DEFINE_VAR), UPat(Ops.CONST)), name="b"), replace_input_buffer), ]) -def allocate_global_buffers(big_sink:UOp) -> tuple[UOp, dict[UOp, UOp]]: +@profile_matches +def transform_to_call(big_sink:UOp) -> tuple[UOp, dict[UOp, UOp]]: # uop list is a list in the original_sink graph and we can map to the tags later # here we build buffer map dont_realize = {Ops.CONST, Ops.BUFFER, Ops.BIND, Ops.DEFINE_VAR, Ops.AFTER} @@ -141,5 +142,5 @@ def allocate_global_buffers(big_sink:UOp) -> tuple[UOp, dict[UOp, UOp]]: # here we construct the final buffer_map. this is everything that will go into the tensor map graph_rewrite(big_sink, pm_finalize_call, ctx=ctx, name="finalize call") ret = graph_rewrite(UOp.sink(*ctx.assigns), pm_replace_buf, ctx=ctx, name="replace bufs").call(*ctx.replacements) - if VIZ: graph_rewrite(ret, PatternMatcher([]), name="*** Call") + if VIZ: graph_rewrite(ret, PatternMatcher([]), name="View Call") return ret, ctx.buffer_map diff --git a/tinygrad/engine/schedule.py b/tinygrad/engine/schedule.py index 1f19e99eae..bffed14c1d 100644 --- a/tinygrad/engine/schedule.py +++ b/tinygrad/engine/schedule.py @@ -6,7 +6,7 @@ from tinygrad.uop.spec import type_verify, tensor_spec from tinygrad.device import Buffer, MultiBuffer from tinygrad.helpers import DEBUG, cpu_profile, TracingKey, SPEC, pluralize, SCACHE, BASEDIR from tinygrad.engine.realize import ExecItem -from tinygrad.engine.allocations import allocate_global_buffers +from tinygrad.engine.allocations import transform_to_call # **** schedule linearizer @@ -63,8 +63,7 @@ def create_schedule(sched_sink:UOp) -> tuple[list[ExecItem], UOp]: return pre_schedule, UOp.sink(*buf_uops_list) from tinygrad.engine.memory import memory_planner -from tinygrad.schedule.rangeify import get_rangeify -from tinygrad.schedule.multi import multi_pm +from tinygrad.schedule.rangeify import get_kernel_graph from tinygrad.uop.ops import PatternMatcher, UPat def create_new_buffer(ctx:tuple[dict[UOp, UOp], tuple[UOp, ...]], b:UOp): @@ -82,29 +81,18 @@ schedule_cache: dict[bytes, tuple[list[ExecItem], UOp]] = {} def complete_create_schedule_with_vars(big_sink:UOp) -> tuple[dict[UOp, UOp], list[ExecItem], dict[str, int]]: # big_sink srcs are all the Tensors st = time.perf_counter() - big_sink, buffer_map = allocate_global_buffers(big_sink) + big_sink, buffer_map = transform_to_call(big_sink) - # get var_vals - var_vals: dict[str, int] = {} - for i,b in enumerate(big_sink.src[1:]): - if b.op is Ops.BIND: - nm = b.src[0].expr - val = b.src[1].arg - assert nm not in var_vals or var_vals[nm] == val, f"bind mismatch on {nm}, {var_vals[nm]} != {val}" - var_vals[nm] = val - - big_sink_cache = big_sink.src[0] - sched_cache_key = big_sink_cache.key - if not SCACHE or (sc_ret:=schedule_cache.get(sched_cache_key, None)) is None: + function = big_sink.src[0] + if not SCACHE or (sc_ret:=schedule_cache.get(function.key, None)) is None: if SPEC: type_verify(big_sink, tensor_spec) - big_sink_cache = graph_rewrite(big_sink_cache, multi_pm, name="multi_pm", rewrite_into_calls=True) - pre_schedule, buf_uops_sink = create_schedule(get_rangeify(big_sink_cache)) - if SCACHE: schedule_cache[sched_cache_key] = (pre_schedule, buf_uops_sink) + pre_schedule, buf_uops_sink = create_schedule(get_kernel_graph(function)) + if SCACHE: schedule_cache[function.key] = (pre_schedule, buf_uops_sink) else: # schedule cache hit pre_schedule, buf_uops_sink = sc_ret # it's a call that we late apply - buf_uops_sink = graph_rewrite(buf_uops_sink, pm_post_sched_cache, ctx=({}, big_sink.src[1:]), name="apply buffers") + buf_uops_sink = graph_rewrite(buf_uops_sink, pm_post_sched_cache, ctx=({}, big_sink.src[1:]), name="params to buffers") # add bufs to pre_schedule schedule: list[ExecItem] = [] @@ -133,7 +121,18 @@ def complete_create_schedule_with_vars(big_sink:UOp) -> tuple[dict[UOp, UOp], li else: frm = None print(f"scheduled {len(schedule):5d} kernels in {(time.perf_counter()-st)*1000:8.2f} ms"+\ - f" | {' cache hit' if SCACHE and sc_ret is not None else 'CACHE MISS'} {sched_cache_key.hex()[:8]}"+\ + f" | {' cache hit' if SCACHE and sc_ret is not None else 'CACHE MISS'} {function.key.hex()[:8]}"+\ f" | {len(UOpMetaClass.ucache):7d} uops in cache"+("" if frm is None else f" | {frm.filename}:{frm.lineno}")) + + # vars used in the schedule used_vars = set().union(*[{v.expr for v in si.ast.variables()} for si in schedule]) - return buffer_map, schedule, {k:v for k,v in var_vals.items() if k in used_vars} \ No newline at end of file + # get var_vals + var_vals: dict[str, int] = {} + for i,b in enumerate(big_sink.src[1:]): + if b.op is Ops.BIND: + nm = b.src[0].expr + if nm not in used_vars: continue + val = b.src[1].arg + assert nm not in var_vals or var_vals[nm] == val, f"bind mismatch on {nm}, {var_vals[nm]} != {val}" + var_vals[nm] = val + return buffer_map, schedule, var_vals diff --git a/tinygrad/schedule/rangeify.py b/tinygrad/schedule/rangeify.py index 8275ca6ce3..c739116489 100644 --- a/tinygrad/schedule/rangeify.py +++ b/tinygrad/schedule/rangeify.py @@ -2,13 +2,14 @@ from dataclasses import dataclass, field, replace import itertools from tinygrad.dtype import dtypes, PtrDType, ImageDType, AddrSpace from tinygrad.uop.ops import PatternMatcher, UPat, Ops, UOp, resolve, GroupOp, _substitute, KernelInfo -from tinygrad.uop.ops import graph_rewrite, sint, AxisType, BottomUpGate +from tinygrad.uop.ops import graph_rewrite, sint, AxisType, BottomUpGate, profile_matches from tinygrad.uop.symbolic import symbolic from tinygrad.helpers import prod, all_same, getenv, dedup, all_int, DEBUG, SPLIT_REDUCEOP, DEBUG_RANGEIFY, VIZ, MAX_KERNEL_BUFFERS from tinygrad.helpers import PCONTIG, partition, get_single_element from tinygrad.codegen.simplify import pm_flatten_range, pm_reduce_simplify from tinygrad.codegen.opt import Opt from tinygrad.schedule.indexing import run_rangeify, BufferizeOpts, ALWAYS_CONTIGUOUS, IndexingContext, apply_movement_op +from tinygrad.schedule.multi import multi_pm # creation can recurse a lot import sys @@ -478,9 +479,10 @@ split_kernels = PatternMatcher([ (UPat((Ops.STORE, Ops.END), name="x"), split_store), ]) -def get_rangeify(sink:UOp) -> UOp: - if VIZ: graph_rewrite(sink, PatternMatcher([]), name="View Input Graph") - tsink = graph_rewrite(sink, pm_syntactic_sugar+pm_mops+earliest_rewrites, bottom_up=True, name="earliest rewrites") +@profile_matches +def get_kernel_graph(sink:UOp) -> UOp: + tsink = graph_rewrite(sink, multi_pm, name="multi_pm", rewrite_into_calls=True) + tsink = graph_rewrite(tsink, pm_syntactic_sugar+pm_mops+earliest_rewrites, bottom_up=True, name="earliest rewrites") # convert movement ops to ranges tsink, rctx = run_rangeify(tsink, bool(DEBUG_RANGEIFY)) diff --git a/tinygrad/tensor.py b/tinygrad/tensor.py index e46e59d8be..394bdc61ad 100644 --- a/tinygrad/tensor.py +++ b/tinygrad/tensor.py @@ -259,7 +259,7 @@ class Tensor(OpMixin): # this is where the schedule cache should go becomes_map, schedule, var_vals = complete_create_schedule_with_vars(big_sink) - _apply_map_to_tensors(becomes_map, name="Apply Schedule Map") + _apply_map_to_tensors(becomes_map, name="buffers") return schedule, var_vals def schedule(self, *lst:Tensor) -> list[ExecItem]: diff --git a/tinygrad/uop/ops.py b/tinygrad/uop/ops.py index ff84d51dc1..1aed20480f 100644 --- a/tinygrad/uop/ops.py +++ b/tinygrad/uop/ops.py @@ -859,7 +859,7 @@ class UOp(OpMixin, metaclass=UOpMetaClass): # TODO: this should replace placeholder @staticmethod - def param(slot:int, dtype:DType, shape:tuple[sint, ...]|None=None, device=None, vmin_vmax:tuple[PyConst, PyConst]|None=None, name="None"): + def param(slot:int, dtype:DType, shape:tuple[sint, ...]|None=None, device=None, vmin_vmax:tuple[PyConst, PyConst]|None=None, name=None): src: tuple[UOp, ...] = (UOp(Ops.NOOP) if shape is None else shape_to_shape_arg(shape),) + \ (UOp(Ops.NOOP) if device is None else UOp(Ops.DEVICE, arg=device),) if vmin_vmax is not None: src += (UOp.const(dtype, vmin_vmax[0]), UOp.const(dtype.scalar(), vmin_vmax[1])) diff --git a/tinygrad/viz/assets/dagrejs.github.io/project/dagre/latest/dagre.min.js b/tinygrad/viz/assets/dagrejs.github.io/project/dagre/latest/dagre.min.js index 01b25ea0f8..df5c90c1c3 100644 --- a/tinygrad/viz/assets/dagrejs.github.io/project/dagre/latest/dagre.min.js +++ b/tinygrad/viz/assets/dagrejs.github.io/project/dagre/latest/dagre.min.js @@ -1,801 +1,3 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.dagre=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i{let label=g.edge(e);g.removeEdge(e);label.forwardName=e.name;label.reversed=true;g.setEdge(e.w,e.v,label,uniqueId("rev"))});function weightFn(g){return e=>{return g.edge(e).weight}}}function dfsFAS(g){let fas=[];let stack={};let visited={};function dfs(v){if(Object.hasOwn(visited,v)){return}visited[v]=true;stack[v]=true;g.outEdges(v).forEach(e=>{if(Object.hasOwn(stack,e.w)){fas.push(e)}else{dfs(e.w)}});delete stack[v]}g.nodes().forEach(dfs);return fas}function undo(g){g.edges().forEach(e=>{let label=g.edge(e);if(label.reversed){g.removeEdge(e);let forwardName=label.forwardName;delete label.reversed;delete label.forwardName;g.setEdge(e.w,e.v,label,forwardName)}})}},{"./greedy-fas":7,"./util":27}],3:[function(require,module,exports){let util=require("./util");module.exports=addBorderSegments;function addBorderSegments(g){function dfs(v){let children=g.children(v);let node=g.node(v);if(children.length){children.forEach(dfs)}if(Object.hasOwn(node,"minRank")){node.borderLeft=[];node.borderRight=[];for(let rank=node.minRank,maxRank=node.maxRank+1;rankswapWidthHeightOne(g.node(v)));g.edges().forEach(e=>swapWidthHeightOne(g.edge(e)))}function swapWidthHeightOne(attrs){let w=attrs.width;attrs.width=attrs.height;attrs.height=w}function reverseY(g){g.nodes().forEach(v=>reverseYOne(g.node(v)));g.edges().forEach(e=>{let edge=g.edge(e);edge.points.forEach(reverseYOne);if(Object.hasOwn(edge,"y")){reverseYOne(edge)}})}function reverseYOne(attrs){attrs.y=-attrs.y}function swapXY(g){g.nodes().forEach(v=>swapXYOne(g.node(v)));g.edges().forEach(e=>{let edge=g.edge(e);edge.points.forEach(swapXYOne);if(Object.hasOwn(edge,"x")){swapXYOne(edge)}})}function swapXYOne(attrs){let x=attrs.x;attrs.x=attrs.y;attrs.y=x}},{}],5:[function(require,module,exports){ -/* - * Simple doubly linked list implementation derived from Cormen, et al., - * "Introduction to Algorithms". - */ -class List{constructor(){let sentinel={};sentinel._next=sentinel._prev=sentinel;this._sentinel=sentinel}dequeue(){let sentinel=this._sentinel;let entry=sentinel._prev;if(entry!==sentinel){unlink(entry);return entry}}enqueue(entry){let sentinel=this._sentinel;if(entry._prev&&entry._next){unlink(entry)}entry._next=sentinel._next;sentinel._next._prev=entry;sentinel._next=entry;entry._prev=sentinel}toString(){let strs=[];let sentinel=this._sentinel;let curr=sentinel._prev;while(curr!==sentinel){strs.push(JSON.stringify(curr,filterOutLinks));curr=curr._prev}return"["+strs.join(", ")+"]"}}function unlink(entry){entry._prev._next=entry._next;entry._next._prev=entry._prev;delete entry._next;delete entry._prev}function filterOutLinks(k,v){if(k!=="_next"&&k!=="_prev"){return v}}module.exports=List},{}],6:[function(require,module,exports){let util=require("./util");let Graph=require("@dagrejs/graphlib").Graph;module.exports={debugOrdering:debugOrdering}; -/* istanbul ignore next */function debugOrdering(g){let layerMatrix=util.buildLayerMatrix(g);let h=new Graph({compound:true,multigraph:true}).setGraph({});g.nodes().forEach(v=>{h.setNode(v,{label:v});h.setParent(v,"layer"+g.node(v).rank)});g.edges().forEach(e=>h.setEdge(e.v,e.w,{},e.name));layerMatrix.forEach((layer,i)=>{let layerV="layer"+i;h.setNode(layerV,{rank:"same"});layer.reduce((u,v)=>{h.setEdge(u,v,{style:"invis"});return v})});return h}},{"./util":27,"@dagrejs/graphlib":29}],7:[function(require,module,exports){let Graph=require("@dagrejs/graphlib").Graph;let List=require("./data/list"); -/* - * A greedy heuristic for finding a feedback arc set for a graph. A feedback - * arc set is a set of edges that can be removed to make a graph acyclic. - * The algorithm comes from: P. Eades, X. Lin, and W. F. Smyth, "A fast and - * effective heuristic for the feedback arc set problem." This implementation - * adjusts that from the paper to allow for weighted edges. - */module.exports=greedyFAS;let DEFAULT_WEIGHT_FN=()=>1;function greedyFAS(g,weightFn){if(g.nodeCount()<=1){return[]}let state=buildState(g,weightFn||DEFAULT_WEIGHT_FN);let results=doGreedyFAS(state.graph,state.buckets,state.zeroIdx); -// Expand multi-edges -return results.flatMap(e=>g.outEdges(e.v,e.w))}function doGreedyFAS(g,buckets,zeroIdx){let results=[];let sources=buckets[buckets.length-1];let sinks=buckets[0];let entry;while(g.nodeCount()){while(entry=sinks.dequeue()){removeNode(g,buckets,zeroIdx,entry)}while(entry=sources.dequeue()){removeNode(g,buckets,zeroIdx,entry)}if(g.nodeCount()){for(let i=buckets.length-2;i>0;--i){entry=buckets[i].dequeue();if(entry){results=results.concat(removeNode(g,buckets,zeroIdx,entry,true));break}}}}return results}function removeNode(g,buckets,zeroIdx,entry,collectPredecessors){let results=collectPredecessors?[]:undefined;g.inEdges(entry.v).forEach(edge=>{let weight=g.edge(edge);let uEntry=g.node(edge.v);if(collectPredecessors){results.push({v:edge.v,w:edge.w})}uEntry.out-=weight;assignBucket(buckets,zeroIdx,uEntry)});g.outEdges(entry.v).forEach(edge=>{let weight=g.edge(edge);let w=edge.w;let wEntry=g.node(w);wEntry["in"]-=weight;assignBucket(buckets,zeroIdx,wEntry)});g.removeNode(entry.v);return results}function buildState(g,weightFn){let fasGraph=new Graph;let maxIn=0;let maxOut=0;g.nodes().forEach(v=>{fasGraph.setNode(v,{v:v,in:0,out:0})}); -// Aggregate weights on nodes, but also sum the weights across multi-edges -// into a single edge for the fasGraph. -g.edges().forEach(e=>{let prevWeight=fasGraph.edge(e.v,e.w)||0;let weight=weightFn(e);let edgeWeight=prevWeight+weight;fasGraph.setEdge(e.v,e.w,edgeWeight);maxOut=Math.max(maxOut,fasGraph.node(e.v).out+=weight);maxIn=Math.max(maxIn,fasGraph.node(e.w)["in"]+=weight)});let buckets=range(maxOut+maxIn+3).map(()=>new List);let zeroIdx=maxIn+1;fasGraph.nodes().forEach(v=>{assignBucket(buckets,zeroIdx,fasGraph.node(v))});return{graph:fasGraph,buckets:buckets,zeroIdx:zeroIdx}}function assignBucket(buckets,zeroIdx,entry){if(!entry.out){buckets[0].enqueue(entry)}else if(!entry["in"]){buckets[buckets.length-1].enqueue(entry)}else{buckets[entry.out-entry["in"]+zeroIdx].enqueue(entry)}}function range(limit){const range=[];for(let i=0;i{let layoutGraph=time(" buildLayoutGraph",()=>buildLayoutGraph(g));time(" runLayout",()=>runLayout(layoutGraph,time,opts));time(" updateInputGraph",()=>updateInputGraph(g,layoutGraph))})}function runLayout(g,time,opts){time(" makeSpaceForEdgeLabels",()=>makeSpaceForEdgeLabels(g));time(" removeSelfEdges",()=>removeSelfEdges(g));time(" acyclic",()=>acyclic.run(g));time(" nestingGraph.run",()=>nestingGraph.run(g));time(" rank",()=>rank(util.asNonCompoundGraph(g)));time(" injectEdgeLabelProxies",()=>injectEdgeLabelProxies(g));time(" removeEmptyRanks",()=>removeEmptyRanks(g));time(" nestingGraph.cleanup",()=>nestingGraph.cleanup(g));time(" normalizeRanks",()=>normalizeRanks(g));time(" assignRankMinMax",()=>assignRankMinMax(g));time(" removeEdgeLabelProxies",()=>removeEdgeLabelProxies(g));time(" normalize.run",()=>normalize.run(g));time(" parentDummyChains",()=>parentDummyChains(g));time(" addBorderSegments",()=>addBorderSegments(g));time(" order",()=>order(g,opts));time(" insertSelfEdges",()=>insertSelfEdges(g));time(" adjustCoordinateSystem",()=>coordinateSystem.adjust(g));time(" position",()=>position(g));time(" positionSelfEdges",()=>positionSelfEdges(g));time(" removeBorderNodes",()=>removeBorderNodes(g));time(" normalize.undo",()=>normalize.undo(g));time(" fixupEdgeLabelCoords",()=>fixupEdgeLabelCoords(g));time(" undoCoordinateSystem",()=>coordinateSystem.undo(g));time(" translateGraph",()=>translateGraph(g));time(" assignNodeIntersects",()=>assignNodeIntersects(g));time(" reversePoints",()=>reversePointsForReversedEdges(g));time(" acyclic.undo",()=>acyclic.undo(g))} -/* - * Copies final layout information from the layout graph back to the input - * graph. This process only copies whitelisted attributes from the layout graph - * to the input graph, so it serves as a good place to determine what - * attributes can influence layout. - */function updateInputGraph(inputGraph,layoutGraph){inputGraph.nodes().forEach(v=>{let inputLabel=inputGraph.node(v);let layoutLabel=layoutGraph.node(v);if(inputLabel){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y;inputLabel.rank=layoutLabel.rank;if(layoutGraph.children(v).length){inputLabel.width=layoutLabel.width;inputLabel.height=layoutLabel.height}}});inputGraph.edges().forEach(e=>{let inputLabel=inputGraph.edge(e);let layoutLabel=layoutGraph.edge(e);inputLabel.points=layoutLabel.points;if(Object.hasOwn(layoutLabel,"x")){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y}});inputGraph.graph().width=layoutGraph.graph().width;inputGraph.graph().height=layoutGraph.graph().height}let graphNumAttrs=["nodesep","edgesep","ranksep","marginx","marginy"];let graphDefaults={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"};let graphAttrs=["acyclicer","ranker","rankdir","align"];let nodeNumAttrs=["width","height"];let nodeDefaults={width:0,height:0};let edgeNumAttrs=["minlen","weight","width","height","labeloffset"];let edgeDefaults={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"};let edgeAttrs=["labelpos"]; -/* - * Constructs a new graph from the input graph, which can be used for layout. - * This process copies only whitelisted attributes from the input graph to the - * layout graph. Thus this function serves as a good place to determine what - * attributes can influence layout. - */function buildLayoutGraph(inputGraph){let g=new Graph({multigraph:true,compound:true});let graph=canonicalize(inputGraph.graph());g.setGraph(Object.assign({},graphDefaults,selectNumberAttrs(graph,graphNumAttrs),util.pick(graph,graphAttrs)));inputGraph.nodes().forEach(v=>{let node=canonicalize(inputGraph.node(v));const newNode=selectNumberAttrs(node,nodeNumAttrs);Object.keys(nodeDefaults).forEach(k=>{if(newNode[k]===undefined){newNode[k]=nodeDefaults[k]}});g.setNode(v,newNode);g.setParent(v,inputGraph.parent(v))});inputGraph.edges().forEach(e=>{let edge=canonicalize(inputGraph.edge(e));g.setEdge(e,Object.assign({},edgeDefaults,selectNumberAttrs(edge,edgeNumAttrs),util.pick(edge,edgeAttrs)))});return g} -/* - * This idea comes from the Gansner paper: to account for edge labels in our - * layout we split each rank in half by doubling minlen and halving ranksep. - * Then we can place labels at these mid-points between nodes. - * - * We also add some minimal padding to the width to push the label for the edge - * away from the edge itself a bit. - */function makeSpaceForEdgeLabels(g){let graph=g.graph();graph.ranksep/=2;g.edges().forEach(e=>{let edge=g.edge(e);edge.minlen*=2;if(edge.labelpos.toLowerCase()!=="c"){if(graph.rankdir==="TB"||graph.rankdir==="BT"){edge.width+=edge.labeloffset}else{edge.height+=edge.labeloffset}}})} -/* - * Creates temporary dummy nodes that capture the rank in which each edge's - * label is going to, if it has one of non-zero width and height. We do this - * so that we can safely remove empty ranks while preserving balance for the - * label's position. - */function injectEdgeLabelProxies(g){g.edges().forEach(e=>{let edge=g.edge(e);if(edge.width&&edge.height){let v=g.node(e.v);let w=g.node(e.w);let label={rank:(w.rank-v.rank)/2+v.rank,e:e};util.addDummyNode(g,"edge-proxy",label,"_ep")}})}function assignRankMinMax(g){let maxRank=0;g.nodes().forEach(v=>{let node=g.node(v);if(node.borderTop){node.minRank=g.node(node.borderTop).rank;node.maxRank=g.node(node.borderBottom).rank;maxRank=Math.max(maxRank,node.maxRank)}});g.graph().maxRank=maxRank}function removeEdgeLabelProxies(g){g.nodes().forEach(v=>{let node=g.node(v);if(node.dummy==="edge-proxy"){g.edge(node.e).labelRank=node.rank;g.removeNode(v)}})}function translateGraph(g){let minX=Number.POSITIVE_INFINITY;let maxX=0;let minY=Number.POSITIVE_INFINITY;let maxY=0;let graphLabel=g.graph();let marginX=graphLabel.marginx||0;let marginY=graphLabel.marginy||0;function getExtremes(attrs){let x=attrs.x;let y=attrs.y;let w=attrs.width;let h=attrs.height;minX=Math.min(minX,x-w/2);maxX=Math.max(maxX,x+w/2);minY=Math.min(minY,y-h/2);maxY=Math.max(maxY,y+h/2)}g.nodes().forEach(v=>getExtremes(g.node(v)));g.edges().forEach(e=>{let edge=g.edge(e);if(Object.hasOwn(edge,"x")){getExtremes(edge)}});minX-=marginX;minY-=marginY;g.nodes().forEach(v=>{let node=g.node(v);node.x-=minX;node.y-=minY});g.edges().forEach(e=>{let edge=g.edge(e);edge.points.forEach(p=>{p.x-=minX;p.y-=minY});if(Object.hasOwn(edge,"x")){edge.x-=minX}if(Object.hasOwn(edge,"y")){edge.y-=minY}});graphLabel.width=maxX-minX+marginX;graphLabel.height=maxY-minY+marginY}function assignNodeIntersects(g){g.edges().forEach(e=>{let edge=g.edge(e);let nodeV=g.node(e.v);let nodeW=g.node(e.w);let p1,p2;if(!edge.points){edge.points=[];p1=nodeW;p2=nodeV}else{p1=edge.points[0];p2=edge.points[edge.points.length-1]}edge.points.unshift(util.intersectRect(nodeV,p1));edge.points.push(util.intersectRect(nodeW,p2))})}function fixupEdgeLabelCoords(g){g.edges().forEach(e=>{let edge=g.edge(e);if(Object.hasOwn(edge,"x")){if(edge.labelpos==="l"||edge.labelpos==="r"){edge.width-=edge.labeloffset}switch(edge.labelpos){case"l":edge.x-=edge.width/2+edge.labeloffset;break;case"r":edge.x+=edge.width/2+edge.labeloffset;break}}})}function reversePointsForReversedEdges(g){g.edges().forEach(e=>{let edge=g.edge(e);if(edge.reversed){edge.points.reverse()}})}function removeBorderNodes(g){g.nodes().forEach(v=>{if(g.children(v).length){let node=g.node(v);let t=g.node(node.borderTop);let b=g.node(node.borderBottom);let l=g.node(node.borderLeft[node.borderLeft.length-1]);let r=g.node(node.borderRight[node.borderRight.length-1]);node.width=Math.abs(r.x-l.x);node.height=Math.abs(b.y-t.y);node.x=l.x+node.width/2;node.y=t.y+node.height/2}});g.nodes().forEach(v=>{if(g.node(v).dummy==="border"){g.removeNode(v)}})}function removeSelfEdges(g){g.edges().forEach(e=>{if(e.v===e.w){var node=g.node(e.v);if(!node.selfEdges){node.selfEdges=[]}node.selfEdges.push({e:e,label:g.edge(e)});g.removeEdge(e)}})}function insertSelfEdges(g){var layers=util.buildLayerMatrix(g);layers.forEach(layer=>{var orderShift=0;layer.forEach((v,i)=>{var node=g.node(v);node.order=i+orderShift;(node.selfEdges||[]).forEach(selfEdge=>{util.addDummyNode(g,"selfedge",{width:selfEdge.label.width,height:selfEdge.label.height,rank:node.rank,order:i+ ++orderShift,e:selfEdge.e,label:selfEdge.label},"_se")});delete node.selfEdges})})}function positionSelfEdges(g){g.nodes().forEach(v=>{var node=g.node(v);if(node.dummy==="selfedge"){var selfNode=g.node(node.e.v);var x=selfNode.x+selfNode.width/2;var y=selfNode.y;var dx=node.x-x;var dy=selfNode.height/2;g.setEdge(node.e,node.label);g.removeNode(v);node.label.points=[{x:x+2*dx/3,y:y-dy},{x:x+5*dx/6,y:y-dy},{x:x+dx,y:y},{x:x+5*dx/6,y:y+dy},{x:x+2*dx/3,y:y+dy}];node.label.x=node.x;node.label.y=node.y}})}function selectNumberAttrs(obj,attrs){return util.mapValues(util.pick(obj,attrs),Number)}function canonicalize(attrs){var newAttrs={};if(attrs){Object.entries(attrs).forEach(([k,v])=>{if(typeof k==="string"){k=k.toLowerCase()}newAttrs[k]=v})}return newAttrs}},{"./acyclic":2,"./add-border-segments":3,"./coordinate-system":4,"./nesting-graph":9,"./normalize":10,"./order":15,"./parent-dummy-chains":20,"./position":22,"./rank":24,"./util":27,"@dagrejs/graphlib":29}],9:[function(require,module,exports){let util=require("./util");module.exports={run:run,cleanup:cleanup}; -/* - * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, - * adds appropriate edges to ensure that all cluster nodes are placed between - * these boundaries, and ensures that the graph is connected. - * - * In addition we ensure, through the use of the minlen property, that nodes - * and subgraph border nodes to not end up on the same rank. - * - * Preconditions: - * - * 1. Input graph is a DAG - * 2. Nodes in the input graph has a minlen attribute - * - * Postconditions: - * - * 1. Input graph is connected. - * 2. Dummy nodes are added for the tops and bottoms of subgraphs. - * 3. The minlen attribute for nodes is adjusted to ensure nodes do not - * get placed on the same rank as subgraph border nodes. - * - * The nesting graph idea comes from Sander, "Layout of Compound Directed - * Graphs." - */function run(g){let root=util.addDummyNode(g,"root",{},"_root");let depths=treeDepths(g);let depthsArr=Object.values(depths);let height=util.applyWithChunking(Math.max,depthsArr)-1;// Note: depths is an Object not an array -let nodeSep=2*height+1;g.graph().nestingRoot=root; -// Multiply minlen by nodeSep to align nodes on non-border ranks. -g.edges().forEach(e=>g.edge(e).minlen*=nodeSep); -// Calculate a weight that is sufficient to keep subgraphs vertically compact -let weight=sumWeights(g)+1; -// Create border nodes and link them up -g.children().forEach(child=>dfs(g,root,nodeSep,weight,height,depths,child)); -// Save the multiplier for node layers for later removal of empty border -// layers. -g.graph().nodeRankFactor=nodeSep}function dfs(g,root,nodeSep,weight,height,depths,v){let children=g.children(v);if(!children.length){if(v!==root){g.setEdge(root,v,{weight:0,minlen:nodeSep})}return}let top=util.addBorderNode(g,"_bt");let bottom=util.addBorderNode(g,"_bb");let label=g.node(v);g.setParent(top,v);label.borderTop=top;g.setParent(bottom,v);label.borderBottom=bottom;children.forEach(child=>{dfs(g,root,nodeSep,weight,height,depths,child);let childNode=g.node(child);let childTop=childNode.borderTop?childNode.borderTop:child;let childBottom=childNode.borderBottom?childNode.borderBottom:child;let thisWeight=childNode.borderTop?weight:2*weight;let minlen=childTop!==childBottom?1:height-depths[v]+1;g.setEdge(top,childTop,{weight:thisWeight,minlen:minlen,nestingEdge:true});g.setEdge(childBottom,bottom,{weight:thisWeight,minlen:minlen,nestingEdge:true})});if(!g.parent(v)){g.setEdge(root,top,{weight:0,minlen:height+depths[v]})}}function treeDepths(g){var depths={};function dfs(v,depth){var children=g.children(v);if(children&&children.length){children.forEach(child=>dfs(child,depth+1))}depths[v]=depth}g.children().forEach(v=>dfs(v,1));return depths}function sumWeights(g){return g.edges().reduce((acc,e)=>acc+g.edge(e).weight,0)}function cleanup(g){var graphLabel=g.graph();g.removeNode(graphLabel.nestingRoot);delete graphLabel.nestingRoot;g.edges().forEach(e=>{var edge=g.edge(e);if(edge.nestingEdge){g.removeEdge(e)}})}},{"./util":27}],10:[function(require,module,exports){"use strict";let util=require("./util");module.exports={run:run,undo:undo}; -/* - * Breaks any long edges in the graph into short segments that span 1 layer - * each. This operation is undoable with the denormalize function. - * - * Pre-conditions: - * - * 1. The input graph is a DAG. - * 2. Each node in the graph has a "rank" property. - * - * Post-condition: - * - * 1. All edges in the graph have a length of 1. - * 2. Dummy nodes are added where edges have been split into segments. - * 3. The graph is augmented with a "dummyChains" attribute which contains - * the first dummy in each chain of dummy nodes produced. - */function run(g){g.graph().dummyChains=[];g.edges().forEach(edge=>normalizeEdge(g,edge))}function normalizeEdge(g,e){let v=e.v;let vRank=g.node(v).rank;let w=e.w;let wRank=g.node(w).rank;let name=e.name;let edgeLabel=g.edge(e);let labelRank=edgeLabel.labelRank;if(wRank===vRank+1)return;g.removeEdge(e);let dummy,attrs,i;for(i=0,++vRank;vRank{let node=g.node(v);let origLabel=node.edgeLabel;let w;g.setEdge(node.edgeObj,origLabel);while(node.dummy){w=g.successors(v)[0];g.removeNode(v);origLabel.points.push({x:node.x,y:node.y});if(node.dummy==="edge-label"){origLabel.x=node.x;origLabel.y=node.y;origLabel.width=node.width;origLabel.height=node.height}v=w;node=g.node(v)}})}},{"./util":27}],11:[function(require,module,exports){module.exports=addSubgraphConstraints;function addSubgraphConstraints(g,cg,vs){let prev={},rootPrev;vs.forEach(v=>{let child=g.parent(v),parent,prevChild;while(child){parent=g.parent(child);if(parent){prevChild=prev[parent];prev[parent]=child}else{prevChild=rootPrev;rootPrev=child}if(prevChild&&prevChild!==child){cg.setEdge(prevChild,child);return}child=parent}}); -/* - function dfs(v) { - var children = v ? g.children(v) : g.children(); - if (children.length) { - var min = Number.POSITIVE_INFINITY, - subgraphs = []; - children.forEach(function(child) { - var childMin = dfs(child); - if (g.children(child).length) { - subgraphs.push({ v: child, order: childMin }); - } - min = Math.min(min, childMin); - }); - _.sortBy(subgraphs, "order").reduce(function(prev, curr) { - cg.setEdge(prev.v, curr.v); - return curr; - }); - return min; - } - return g.node(v).order; - } - dfs(undefined); - */}},{}],12:[function(require,module,exports){module.exports=barycenter;function barycenter(g,movable=[]){return movable.map(v=>{let inV=g.inEdges(v);if(!inV.length){return{v:v}}else{let result=inV.reduce((acc,e)=>{let edge=g.edge(e),nodeU=g.node(e.v);return{sum:acc.sum+edge.weight*nodeU.order,weight:acc.weight+edge.weight}},{sum:0,weight:0});return{v:v,barycenter:result.sum/result.weight,weight:result.weight}}})}},{}],13:[function(require,module,exports){let Graph=require("@dagrejs/graphlib").Graph;let util=require("../util");module.exports=buildLayerGraph; -/* - * Constructs a graph that can be used to sort a layer of nodes. The graph will - * contain all base and subgraph nodes from the request layer in their original - * hierarchy and any edges that are incident on these nodes and are of the type - * requested by the "relationship" parameter. - * - * Nodes from the requested rank that do not have parents are assigned a root - * node in the output graph, which is set in the root graph attribute. This - * makes it easy to walk the hierarchy of movable nodes during ordering. - * - * Pre-conditions: - * - * 1. Input graph is a DAG - * 2. Base nodes in the input graph have a rank attribute - * 3. Subgraph nodes in the input graph has minRank and maxRank attributes - * 4. Edges have an assigned weight - * - * Post-conditions: - * - * 1. Output graph has all nodes in the movable rank with preserved - * hierarchy. - * 2. Root nodes in the movable layer are made children of the node - * indicated by the root attribute of the graph. - * 3. Non-movable nodes incident on movable nodes, selected by the - * relationship parameter, are included in the graph (without hierarchy). - * 4. Edges incident on movable nodes, selected by the relationship - * parameter, are added to the output graph. - * 5. The weights for copied edges are aggregated as need, since the output - * graph is not a multi-graph. - */function buildLayerGraph(g,rank,relationship){let root=createRootNode(g),result=new Graph({compound:true}).setGraph({root:root}).setDefaultNodeLabel(v=>g.node(v));g.nodes().forEach(v=>{let node=g.node(v),parent=g.parent(v);if(node.rank===rank||node.minRank<=rank&&rank<=node.maxRank){result.setNode(v);result.setParent(v,parent||root); -// This assumes we have only short edges! -g[relationship](v).forEach(e=>{let u=e.v===v?e.w:e.v,edge=result.edge(u,v),weight=edge!==undefined?edge.weight:0;result.setEdge(u,v,{weight:g.edge(e).weight+weight})});if(Object.hasOwn(node,"minRank")){result.setNode(v,{borderLeft:node.borderLeft[rank],borderRight:node.borderRight[rank]})}}});return result}function createRootNode(g){var v;while(g.hasNode(v=util.uniqueId("_root")));return v}},{"../util":27,"@dagrejs/graphlib":29}],14:[function(require,module,exports){"use strict";let zipObject=require("../util").zipObject;module.exports=crossCount; -/* - * A function that takes a layering (an array of layers, each with an array of - * ordererd nodes) and a graph and returns a weighted crossing count. - * - * Pre-conditions: - * - * 1. Input graph must be simple (not a multigraph), directed, and include - * only simple edges. - * 2. Edges in the input graph must have assigned weights. - * - * Post-conditions: - * - * 1. The graph and layering matrix are left unchanged. - * - * This algorithm is derived from Barth, et al., "Bilayer Cross Counting." - */function crossCount(g,layering){let cc=0;for(let i=1;ii));let southEntries=northLayer.flatMap(v=>{return g.outEdges(v).map(e=>{return{pos:southPos[e.w],weight:g.edge(e).weight}}).sort((a,b)=>a.pos-b.pos)}); -// Build the accumulator tree -let firstIndex=1;while(firstIndex{let index=entry.pos+firstIndex;tree[index]+=entry.weight;let weightSum=0;while(index>0){if(index%2){weightSum+=tree[index+1]}index=index-1>>1;tree[index]+=entry.weight}cc+=entry.weight*weightSum});return cc}},{"../util":27}],15:[function(require,module,exports){"use strict";let initOrder=require("./init-order");let crossCount=require("./cross-count");let sortSubgraph=require("./sort-subgraph");let buildLayerGraph=require("./build-layer-graph");let addSubgraphConstraints=require("./add-subgraph-constraints");let Graph=require("@dagrejs/graphlib").Graph;let util=require("../util");module.exports=order; -/* - * Applies heuristics to minimize edge crossings in the graph and sets the best - * order solution as an order attribute on each node. - * - * Pre-conditions: - * - * 1. Graph must be DAG - * 2. Graph nodes must be objects with a "rank" attribute - * 3. Graph edges must have the "weight" attribute - * - * Post-conditions: - * - * 1. Graph nodes will have an "order" attribute based on the results of the - * algorithm. - */function order(g,opts){if(opts&&typeof opts.customOrder==="function"){opts.customOrder(g,order);return}let maxRank=util.maxRank(g),downLayerGraphs=buildLayerGraphs(g,util.range(1,maxRank+1),"inEdges"),upLayerGraphs=buildLayerGraphs(g,util.range(maxRank-1,-1,-1),"outEdges");let layering=initOrder(g);assignOrder(g,layering);if(opts&&opts.disableOptimalOrderHeuristic){return}let bestCC=Number.POSITIVE_INFINITY,best;for(let i=0,lastBest=0;lastBest<4;++i,++lastBest){sweepLayerGraphs(i%2?downLayerGraphs:upLayerGraphs,i%4>=2);layering=util.buildLayerMatrix(g);let cc=crossCount(g,layering);if(cclg.node(v).order=i);addSubgraphConstraints(lg,cg,sorted.vs)})}function assignOrder(g,layering){Object.values(layering).forEach(layer=>layer.forEach((v,i)=>g.node(v).order=i))}},{"../util":27,"./add-subgraph-constraints":11,"./build-layer-graph":13,"./cross-count":14,"./init-order":16,"./sort-subgraph":18,"@dagrejs/graphlib":29}],16:[function(require,module,exports){"use strict";let util=require("../util");module.exports=initOrder; -/* - * Assigns an initial order value for each node by performing a DFS search - * starting from nodes in the first rank. Nodes are assigned an order in their - * rank as they are first visited. - * - * This approach comes from Gansner, et al., "A Technique for Drawing Directed - * Graphs." - * - * Returns a layering matrix with an array per layer and each layer sorted by - * the order of its nodes. - */function initOrder(g){let visited={};let simpleNodes=g.nodes().filter(v=>!g.children(v).length);let simpleNodesRanks=simpleNodes.map(v=>g.node(v).rank);let maxRank=util.applyWithChunking(Math.max,simpleNodesRanks);let layers=util.range(maxRank+1).map(()=>[]);function dfs(v){if(visited[v])return;visited[v]=true;let node=g.node(v);layers[node.rank].push(v);g.successors(v).forEach(dfs)}let orderedVs=simpleNodes.sort((a,b)=>g.node(a).rank-g.node(b).rank);orderedVs.forEach(dfs);return layers}},{"../util":27}],17:[function(require,module,exports){"use strict";let util=require("../util");module.exports=resolveConflicts; -/* - * Given a list of entries of the form {v, barycenter, weight} and a - * constraint graph this function will resolve any conflicts between the - * constraint graph and the barycenters for the entries. If the barycenters for - * an entry would violate a constraint in the constraint graph then we coalesce - * the nodes in the conflict into a new node that respects the contraint and - * aggregates barycenter and weight information. - * - * This implementation is based on the description in Forster, "A Fast and - * Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it - * differs in some specific details. - * - * Pre-conditions: - * - * 1. Each entry has the form {v, barycenter, weight}, or if the node has - * no barycenter, then {v}. - * - * Returns: - * - * A new list of entries of the form {vs, i, barycenter, weight}. The list - * `vs` may either be a singleton or it may be an aggregation of nodes - * ordered such that they do not violate constraints from the constraint - * graph. The property `i` is the lowest original index of any of the - * elements in `vs`. - */function resolveConflicts(entries,cg){let mappedEntries={};entries.forEach((entry,i)=>{let tmp=mappedEntries[entry.v]={indegree:0,in:[],out:[],vs:[entry.v],i:i};if(entry.barycenter!==undefined){tmp.barycenter=entry.barycenter;tmp.weight=entry.weight}});cg.edges().forEach(e=>{let entryV=mappedEntries[e.v];let entryW=mappedEntries[e.w];if(entryV!==undefined&&entryW!==undefined){entryW.indegree++;entryV.out.push(mappedEntries[e.w])}});let sourceSet=Object.values(mappedEntries).filter(entry=>!entry.indegree);return doResolveConflicts(sourceSet)}function doResolveConflicts(sourceSet){let entries=[];function handleIn(vEntry){return uEntry=>{if(uEntry.merged){return}if(uEntry.barycenter===undefined||vEntry.barycenter===undefined||uEntry.barycenter>=vEntry.barycenter){mergeEntries(vEntry,uEntry)}}}function handleOut(vEntry){return wEntry=>{wEntry["in"].push(vEntry);if(--wEntry.indegree===0){sourceSet.push(wEntry)}}}while(sourceSet.length){let entry=sourceSet.pop();entries.push(entry);entry["in"].reverse().forEach(handleIn(entry));entry.out.forEach(handleOut(entry))}return entries.filter(entry=>!entry.merged).map(entry=>{return util.pick(entry,["vs","i","barycenter","weight"])})}function mergeEntries(target,source){let sum=0;let weight=0;if(target.weight){sum+=target.barycenter*target.weight;weight+=target.weight}if(source.weight){sum+=source.barycenter*source.weight;weight+=source.weight}target.vs=source.vs.concat(target.vs);target.barycenter=sum/weight;target.weight=weight;target.i=Math.min(source.i,target.i);source.merged=true}},{"../util":27}],18:[function(require,module,exports){let barycenter=require("./barycenter");let resolveConflicts=require("./resolve-conflicts");let sort=require("./sort");module.exports=sortSubgraph;function sortSubgraph(g,v,cg,biasRight){let movable=g.children(v);let node=g.node(v);let bl=node?node.borderLeft:undefined;let br=node?node.borderRight:undefined;let subgraphs={};if(bl){movable=movable.filter(w=>w!==bl&&w!==br)}let barycenters=barycenter(g,movable);barycenters.forEach(entry=>{if(g.children(entry.v).length){let subgraphResult=sortSubgraph(g,entry.v,cg,biasRight);subgraphs[entry.v]=subgraphResult;if(Object.hasOwn(subgraphResult,"barycenter")){mergeBarycenters(entry,subgraphResult)}}});let entries=resolveConflicts(barycenters,cg);expandSubgraphs(entries,subgraphs);let result=sort(entries,biasRight);if(bl){result.vs=[bl,result.vs,br].flat(true);if(g.predecessors(bl).length){let blPred=g.node(g.predecessors(bl)[0]),brPred=g.node(g.predecessors(br)[0]);if(!Object.hasOwn(result,"barycenter")){result.barycenter=0;result.weight=0}result.barycenter=(result.barycenter*result.weight+blPred.order+brPred.order)/(result.weight+2);result.weight+=2}}return result}function expandSubgraphs(entries,subgraphs){entries.forEach(entry=>{entry.vs=entry.vs.flatMap(v=>{if(subgraphs[v]){return subgraphs[v].vs}return v})})}function mergeBarycenters(target,other){if(target.barycenter!==undefined){target.barycenter=(target.barycenter*target.weight+other.barycenter*other.weight)/(target.weight+other.weight);target.weight+=other.weight}else{target.barycenter=other.barycenter;target.weight=other.weight}}},{"./barycenter":12,"./resolve-conflicts":17,"./sort":19}],19:[function(require,module,exports){let util=require("../util");module.exports=sort;function sort(entries,biasRight){let parts=util.partition(entries,entry=>{return Object.hasOwn(entry,"barycenter")});let sortable=parts.lhs,unsortable=parts.rhs.sort((a,b)=>b.i-a.i),vs=[],sum=0,weight=0,vsIndex=0;sortable.sort(compareWithBias(!!biasRight));vsIndex=consumeUnsortable(vs,unsortable,vsIndex);sortable.forEach(entry=>{vsIndex+=entry.vs.length;vs.push(entry.vs);sum+=entry.barycenter*entry.weight;weight+=entry.weight;vsIndex=consumeUnsortable(vs,unsortable,vsIndex)});let result={vs:vs.flat(true)};if(weight){result.barycenter=sum/weight;result.weight=weight}return result}function consumeUnsortable(vs,unsortable,index){let last;while(unsortable.length&&(last=unsortable[unsortable.length-1]).i<=index){unsortable.pop();vs.push(last.vs);index++}return index}function compareWithBias(bias){return(entryV,entryW)=>{if(entryV.barycenterentryW.barycenter){return 1}return!bias?entryV.i-entryW.i:entryW.i-entryV.i}}},{"../util":27}],20:[function(require,module,exports){module.exports=parentDummyChains;function parentDummyChains(g){let postorderNums=postorder(g);g.graph().dummyChains.forEach(v=>{let node=g.node(v);let edgeObj=node.edgeObj;let pathData=findPath(g,postorderNums,edgeObj.v,edgeObj.w);let path=pathData.path;let lca=pathData.lca;let pathIdx=0;let pathV=path[pathIdx];let ascending=true;while(v!==edgeObj.w){node=g.node(v);if(ascending){while((pathV=path[pathIdx])!==lca&&g.node(pathV).maxRanklow||lim>postorderNums[parent].lim));lca=parent; -// Traverse from w to LCA -parent=w;while((parent=g.parent(parent))!==lca){wPath.push(parent)}return{path:vPath.concat(wPath.reverse()),lca:lca}}function postorder(g){let result={};let lim=0;function dfs(v){let low=lim;g.children(v).forEach(dfs);result[v]={low:low,lim:lim++}}g.children().forEach(dfs);return result}},{}],21:[function(require,module,exports){"use strict";let Graph=require("@dagrejs/graphlib").Graph;let util=require("../util"); -/* - * This module provides coordinate assignment based on Brandes and Köpf, "Fast - * and Simple Horizontal Coordinate Assignment." - */module.exports={positionX:positionX,findType1Conflicts:findType1Conflicts,findType2Conflicts:findType2Conflicts,addConflict:addConflict,hasConflict:hasConflict,verticalAlignment:verticalAlignment,horizontalCompaction:horizontalCompaction,alignCoordinates:alignCoordinates,findSmallestWidthAlignment:findSmallestWidthAlignment,balance:balance}; -/* - * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" - * property. A type-1 conflict is one where a non-inner segment crosses an - * inner segment. An inner segment is an edge with both incident nodes marked - * with the "dummy" property. - * - * This algorithm scans layer by layer, starting with the second, for type-1 - * conflicts between the current layer and the previous layer. For each layer - * it scans the nodes from left to right until it reaches one that is incident - * on an inner segment. It then scans predecessors to determine if they have - * edges that cross that inner segment. At the end a final scan is done for all - * nodes on the current rank to see if they cross the last visited inner - * segment. - * - * This algorithm (safely) assumes that a dummy node will only be incident on a - * single node in the layers being scanned. - */function findType1Conflicts(g,layering){let conflicts={};function visitLayer(prevLayer,layer){let -// last visited node in the previous layer that is incident on an inner -// segment. -k0=0, -// Tracks the last node in this layer scanned for crossings with a type-1 -// segment. -scanPos=0,prevLayerLength=prevLayer.length,lastNode=layer[layer.length-1];layer.forEach((v,i)=>{let w=findOtherInnerSegmentNode(g,v),k1=w?g.node(w).order:prevLayerLength;if(w||v===lastNode){layer.slice(scanPos,i+1).forEach(scanNode=>{g.predecessors(scanNode).forEach(u=>{let uLabel=g.node(u),uPos=uLabel.order;if((uPos{v=south[i];if(g.node(v).dummy){g.predecessors(v).forEach(u=>{let uNode=g.node(u);if(uNode.dummy&&(uNode.ordernextNorthBorder)){addConflict(conflicts,u,v)}})}})}function visitLayer(north,south){let prevNorthPos=-1,nextNorthPos,southPos=0;south.forEach((v,southLookahead)=>{if(g.node(v).dummy==="border"){let predecessors=g.predecessors(v);if(predecessors.length){nextNorthPos=g.node(predecessors[0]).order;scan(south,southPos,southLookahead,prevNorthPos,nextNorthPos);southPos=southLookahead;prevNorthPos=nextNorthPos}}scan(south,southPos,south.length,nextNorthPos,north.length)});return south}layering.length&&layering.reduce(visitLayer);return conflicts}function findOtherInnerSegmentNode(g,v){if(g.node(v).dummy){return g.predecessors(v).find(u=>g.node(u).dummy)}}function addConflict(conflicts,v,w){if(v>w){let tmp=v;v=w;w=tmp}let conflictsV=conflicts[v];if(!conflictsV){conflicts[v]=conflictsV={}}conflictsV[w]=true}function hasConflict(conflicts,v,w){if(v>w){let tmp=v;v=w;w=tmp}return!!conflicts[v]&&Object.hasOwn(conflicts[v],w)} -/* - * Try to align nodes into vertical "blocks" where possible. This algorithm - * attempts to align a node with one of its median neighbors. If the edge - * connecting a neighbor is a type-1 conflict then we ignore that possibility. - * If a previous node has already formed a block with a node after the node - * we're trying to form a block with, we also ignore that possibility - our - * blocks would be split in that scenario. - */function verticalAlignment(g,layering,conflicts,neighborFn){let root={},align={},pos={}; -// We cache the position here based on the layering because the graph and -// layering may be out of sync. The layering matrix is manipulated to -// generate different extreme alignments. -layering.forEach(layer=>{layer.forEach((v,order)=>{root[v]=v;align[v]=v;pos[v]=order})});layering.forEach(layer=>{let prevIdx=-1;layer.forEach(v=>{let ws=neighborFn(v);if(ws.length){ws=ws.sort((a,b)=>pos[a]-pos[b]);let mp=(ws.length-1)/2;for(let i=Math.floor(mp),il=Math.ceil(mp);i<=il;++i){let w=ws[i];if(align[v]===v&&prevIdx{return Math.max(acc,xs[e.v]+blockG.edge(e))},0)} -// Second pass, assign greatest coordinates -function pass2(elem){let min=blockG.outEdges(elem).reduce((acc,e)=>{return Math.min(acc,xs[e.w]-blockG.edge(e))},Number.POSITIVE_INFINITY);let node=g.node(elem);if(min!==Number.POSITIVE_INFINITY&&node.borderType!==borderType){xs[elem]=Math.max(xs[elem],min)}}iterate(pass1,blockG.predecessors.bind(blockG));iterate(pass2,blockG.successors.bind(blockG)); -// Assign x coordinates to all nodes -Object.keys(align).forEach(v=>xs[v]=xs[root[v]]);return xs}function buildBlockGraph(g,layering,root,reverseSep){let blockGraph=new Graph,graphLabel=g.graph(),sepFn=sep(graphLabel.nodesep,graphLabel.edgesep,reverseSep);layering.forEach(layer=>{let u;layer.forEach(v=>{let vRoot=root[v];blockGraph.setNode(vRoot);if(u){var uRoot=root[u],prevMax=blockGraph.edge(uRoot,vRoot);blockGraph.setEdge(uRoot,vRoot,Math.max(sepFn(g,v,u),prevMax||0))}u=v})});return blockGraph} -/* - * Returns the alignment that has the smallest width of the given alignments. - */function findSmallestWidthAlignment(g,xss){return Object.values(xss).reduce((currentMinAndXs,xs)=>{let max=Number.NEGATIVE_INFINITY;let min=Number.POSITIVE_INFINITY;Object.entries(xs).forEach(([v,x])=>{let halfWidth=width(g,v)/2;max=Math.max(x+halfWidth,max);min=Math.min(x-halfWidth,min)});const newMin=max-min;if(newMin{["l","r"].forEach(horiz=>{let alignment=vert+horiz,xs=xss[alignment];if(xs===alignTo)return;let xsVals=Object.values(xs);let delta=alignToMin-util.applyWithChunking(Math.min,xsVals);if(horiz!=="l"){delta=alignToMax-util.applyWithChunking(Math.max,xsVals)}if(delta){xss[alignment]=util.mapValues(xs,x=>x+delta)}})})}function balance(xss,align){return util.mapValues(xss.ul,(num,v)=>{if(align){return xss[align.toLowerCase()][v]}else{let xs=Object.values(xss).map(xs=>xs[v]).sort((a,b)=>a-b);return(xs[1]+xs[2])/2}})}function positionX(g){let layering=util.buildLayerMatrix(g);let conflicts=Object.assign(findType1Conflicts(g,layering),findType2Conflicts(g,layering));let xss={};let adjustedLayering;["u","d"].forEach(vert=>{adjustedLayering=vert==="u"?layering:Object.values(layering).reverse();["l","r"].forEach(horiz=>{if(horiz==="r"){adjustedLayering=adjustedLayering.map(inner=>{return Object.values(inner).reverse()})}let neighborFn=(vert==="u"?g.predecessors:g.successors).bind(g);let align=verticalAlignment(g,adjustedLayering,conflicts,neighborFn);let xs=horizontalCompaction(g,adjustedLayering,align.root,align.align,horiz==="r");if(horiz==="r"){xs=util.mapValues(xs,x=>-x)}xss[vert+horiz]=xs})});let smallestWidth=findSmallestWidthAlignment(g,xss);alignCoordinates(xss,smallestWidth);return balance(xss,g.graph().align)}function sep(nodeSep,edgeSep,reverseSep){return(g,v,w)=>{let vLabel=g.node(v);let wLabel=g.node(w);let sum=0;let delta;sum+=vLabel.width/2;if(Object.hasOwn(vLabel,"labelpos")){switch(vLabel.labelpos.toLowerCase()){case"l":delta=-vLabel.width/2;break;case"r":delta=vLabel.width/2;break}}if(delta){sum+=reverseSep?delta:-delta}delta=0;sum+=(vLabel.dummy?edgeSep:nodeSep)/2;sum+=(wLabel.dummy?edgeSep:nodeSep)/2;sum+=wLabel.width/2;if(Object.hasOwn(wLabel,"labelpos")){switch(wLabel.labelpos.toLowerCase()){case"l":delta=wLabel.width/2;break;case"r":delta=-wLabel.width/2;break}}if(delta){sum+=reverseSep?delta:-delta}delta=0;return sum}}function width(g,v){return g.node(v).width}},{"../util":27,"@dagrejs/graphlib":29}],22:[function(require,module,exports){"use strict";let util=require("../util");let positionX=require("./bk").positionX;module.exports=position;function position(g){g=util.asNonCompoundGraph(g);positionY(g);Object.entries(positionX(g)).forEach(([v,x])=>g.node(v).x=x)}function positionY(g){let layering=util.buildLayerMatrix(g);let rankSep=g.graph().ranksep;let prevY=0;layering.forEach(layer=>{const maxHeight=layer.reduce((acc,v)=>{const height=g.node(v).height;if(acc>height){return acc}else{return height}},0);layer.forEach(v=>g.node(v).y=prevY+maxHeight/2);prevY+=maxHeight+rankSep})}},{"../util":27,"./bk":21}],23:[function(require,module,exports){"use strict";var Graph=require("@dagrejs/graphlib").Graph;var slack=require("./util").slack;module.exports=feasibleTree; -/* - * Constructs a spanning tree with tight edges and adjusted the input node's - * ranks to achieve this. A tight edge is one that is has a length that matches - * its "minlen" attribute. - * - * The basic structure for this function is derived from Gansner, et al., "A - * Technique for Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a DAG. - * 2. Graph must be connected. - * 3. Graph must have at least one node. - * 5. Graph nodes must have been previously assigned a "rank" property that - * respects the "minlen" property of incident edges. - * 6. Graph edges must have a "minlen" property. - * - * Post-conditions: - * - * - Graph nodes will have their rank adjusted to ensure that all edges are - * tight. - * - * Returns a tree (undirected graph) that is constructed using only "tight" - * edges. - */function feasibleTree(g){var t=new Graph({directed:false}); -// Choose arbitrary node from which to start our tree -var start=g.nodes()[0];var size=g.nodeCount();t.setNode(start,{});var edge,delta;while(tightTree(t,g){var edgeV=e.v,w=v===edgeV?e.w:edgeV;if(!t.hasNode(w)&&!slack(g,e)){t.setNode(w,{});t.setEdge(v,w,{});dfs(w)}})}t.nodes().forEach(dfs);return t.nodeCount()} -/* - * Finds the edge with the smallest slack that is incident on tree and returns - * it. - */function findMinSlackEdge(t,g){const edges=g.edges();return edges.reduce((acc,edge)=>{let edgeSlack=Number.POSITIVE_INFINITY;if(t.hasNode(edge.v)!==t.hasNode(edge.w)){edgeSlack=slack(g,edge)}if(edgeSlackg.node(v).rank+=delta)}},{"./util":26,"@dagrejs/graphlib":29}],24:[function(require,module,exports){"use strict";var rankUtil=require("./util");var longestPath=rankUtil.longestPath;var feasibleTree=require("./feasible-tree");var networkSimplex=require("./network-simplex");module.exports=rank; -/* - * Assigns a rank to each node in the input graph that respects the "minlen" - * constraint specified on edges between nodes. - * - * This basic structure is derived from Gansner, et al., "A Technique for - * Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a connected DAG - * 2. Graph nodes must be objects - * 3. Graph edges must have "weight" and "minlen" attributes - * - * Post-conditions: - * - * 1. Graph nodes will have a "rank" attribute based on the results of the - * algorithm. Ranks can start at any index (including negative), we'll - * fix them up later. - */function rank(g){switch(g.graph().ranker){case"network-simplex":networkSimplexRanker(g);break;case"tight-tree":tightTreeRanker(g);break;case"longest-path":longestPathRanker(g);break;default:networkSimplexRanker(g)}} -// A fast and simple ranker, but results are far from optimal. -var longestPathRanker=longestPath;function tightTreeRanker(g){longestPath(g);feasibleTree(g)}function networkSimplexRanker(g){networkSimplex(g)}},{"./feasible-tree":23,"./network-simplex":25,"./util":26}],25:[function(require,module,exports){"use strict";var feasibleTree=require("./feasible-tree");var slack=require("./util").slack;var initRank=require("./util").longestPath;var preorder=require("@dagrejs/graphlib").alg.preorder;var postorder=require("@dagrejs/graphlib").alg.postorder;var simplify=require("../util").simplify;module.exports=networkSimplex; -// Expose some internals for testing purposes -networkSimplex.initLowLimValues=initLowLimValues;networkSimplex.initCutValues=initCutValues;networkSimplex.calcCutValue=calcCutValue;networkSimplex.leaveEdge=leaveEdge;networkSimplex.enterEdge=enterEdge;networkSimplex.exchangeEdges=exchangeEdges; -/* - * The network simplex algorithm assigns ranks to each node in the input graph - * and iteratively improves the ranking to reduce the length of edges. - * - * Preconditions: - * - * 1. The input graph must be a DAG. - * 2. All nodes in the graph must have an object value. - * 3. All edges in the graph must have "minlen" and "weight" attributes. - * - * Postconditions: - * - * 1. All nodes in the graph will have an assigned "rank" attribute that has - * been optimized by the network simplex algorithm. Ranks start at 0. - * - * - * A rough sketch of the algorithm is as follows: - * - * 1. Assign initial ranks to each node. We use the longest path algorithm, - * which assigns ranks to the lowest position possible. In general this - * leads to very wide bottom ranks and unnecessarily long edges. - * 2. Construct a feasible tight tree. A tight tree is one such that all - * edges in the tree have no slack (difference between length of edge - * and minlen for the edge). This by itself greatly improves the assigned - * rankings by shorting edges. - * 3. Iteratively find edges that have negative cut values. Generally a - * negative cut value indicates that the edge could be removed and a new - * tree edge could be added to produce a more compact graph. - * - * Much of the algorithms here are derived from Gansner, et al., "A Technique - * for Drawing Directed Graphs." The structure of the file roughly follows the - * structure of the overall algorithm. - */function networkSimplex(g){g=simplify(g);initRank(g);var t=feasibleTree(g);initLowLimValues(t);initCutValues(t,g);var e,f;while(e=leaveEdge(t)){f=enterEdge(t,g,e);exchangeEdges(t,g,e,f)}} -/* - * Initializes cut values for all edges in the tree. - */function initCutValues(t,g){var vs=postorder(t,t.nodes());vs=vs.slice(0,vs.length-1);vs.forEach(v=>assignCutValue(t,g,v))}function assignCutValue(t,g,child){var childLab=t.node(child);var parent=childLab.parent;t.edge(child,parent).cutvalue=calcCutValue(t,g,child)} -/* - * Given the tight tree, its graph, and a child in the graph calculate and - * return the cut value for the edge between the child and its parent. - */function calcCutValue(t,g,child){var childLab=t.node(child);var parent=childLab.parent; -// True if the child is on the tail end of the edge in the directed graph -var childIsTail=true; -// The graph's view of the tree edge we're inspecting -var graphEdge=g.edge(child,parent); -// The accumulated cut value for the edge between this node and its parent -var cutValue=0;if(!graphEdge){childIsTail=false;graphEdge=g.edge(parent,child)}cutValue=graphEdge.weight;g.nodeEdges(child).forEach(e=>{var isOutEdge=e.v===child,other=isOutEdge?e.w:e.v;if(other!==parent){var pointsToHead=isOutEdge===childIsTail,otherWeight=g.edge(e).weight;cutValue+=pointsToHead?otherWeight:-otherWeight;if(isTreeEdge(t,child,other)){var otherCutValue=t.edge(child,other).cutvalue;cutValue+=pointsToHead?-otherCutValue:otherCutValue}}});return cutValue}function initLowLimValues(tree,root){if(arguments.length<2){root=tree.nodes()[0]}dfsAssignLowLim(tree,{},1,root)}function dfsAssignLowLim(tree,visited,nextLim,v,parent){var low=nextLim;var label=tree.node(v);visited[v]=true;tree.neighbors(v).forEach(w=>{if(!Object.hasOwn(visited,w)){nextLim=dfsAssignLowLim(tree,visited,nextLim,w,v)}});label.low=low;label.lim=nextLim++;if(parent){label.parent=parent}else{ -// TODO should be able to remove this when we incrementally update low lim -delete label.parent}return nextLim}function leaveEdge(tree){return tree.edges().find(e=>tree.edge(e).cutvalue<0)}function enterEdge(t,g,edge){var v=edge.v;var w=edge.w; -// For the rest of this function we assume that v is the tail and w is the -// head, so if we don't have this edge in the graph we should flip it to -// match the correct orientation. -if(!g.hasEdge(v,w)){v=edge.w;w=edge.v}var vLabel=t.node(v);var wLabel=t.node(w);var tailLabel=vLabel;var flip=false; -// If the root is in the tail of the edge then we need to flip the logic that -// checks for the head and tail nodes in the candidates function below. -if(vLabel.lim>wLabel.lim){tailLabel=wLabel;flip=true}var candidates=g.edges().filter(edge=>{return flip===isDescendant(t,t.node(edge.v),tailLabel)&&flip!==isDescendant(t,t.node(edge.w),tailLabel)});return candidates.reduce((acc,edge)=>{if(slack(g,edge)!g.node(v).parent);var vs=preorder(t,root);vs=vs.slice(1);vs.forEach(v=>{var parent=t.node(v).parent,edge=g.edge(v,parent),flipped=false;if(!edge){edge=g.edge(parent,v);flipped=true}g.node(v).rank=g.node(parent).rank+(flipped?edge.minlen:-edge.minlen)})} -/* - * Returns true if the edge is in the tree. - */function isTreeEdge(tree,u,v){return tree.hasEdge(u,v)} -/* - * Returns true if the specified node is descendant of the root node per the - * assigned low and lim attributes in the tree. - */function isDescendant(tree,vLabel,rootLabel){return rootLabel.low<=vLabel.lim&&vLabel.lim<=rootLabel.lim}},{"../util":27,"./feasible-tree":23,"./util":26,"@dagrejs/graphlib":29}],26:[function(require,module,exports){"use strict";const{applyWithChunking}=require("../util");module.exports={longestPath:longestPath,slack:slack}; -/* - * Initializes ranks for the input graph using the longest path algorithm. This - * algorithm scales well and is fast in practice, it yields rather poor - * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom - * ranks wide and leaving edges longer than necessary. However, due to its - * speed, this algorithm is good for getting an initial ranking that can be fed - * into other algorithms. - * - * This algorithm does not normalize layers because it will be used by other - * algorithms in most cases. If using this algorithm directly, be sure to - * run normalize at the end. - * - * Pre-conditions: - * - * 1. Input graph is a DAG. - * 2. Input graph node labels can be assigned properties. - * - * Post-conditions: - * - * 1. Each node will be assign an (unnormalized) "rank" property. - */function longestPath(g){var visited={};function dfs(v){var label=g.node(v);if(Object.hasOwn(visited,v)){return label.rank}visited[v]=true;let outEdgesMinLens=g.outEdges(v).map(e=>{if(e==null){return Number.POSITIVE_INFINITY}return dfs(e.w)-g.edge(e).minlen});var rank=applyWithChunking(Math.min,outEdgesMinLens);if(rank===Number.POSITIVE_INFINITY){rank=0}return label.rank=rank}g.sources().forEach(dfs)} -/* - * Returns the amount of slack for the given edge. The slack is defined as the - * difference between the length of the edge and its minimum length. - */function slack(g,e){return g.node(e.w).rank-g.node(e.v).rank-g.edge(e).minlen}},{"../util":27}],27:[function(require,module,exports){ -/* eslint "no-console": off */ -"use strict";let Graph=require("@dagrejs/graphlib").Graph;module.exports={addBorderNode:addBorderNode,addDummyNode:addDummyNode,applyWithChunking:applyWithChunking,asNonCompoundGraph:asNonCompoundGraph,buildLayerMatrix:buildLayerMatrix,intersectRect:intersectRect,mapValues:mapValues,maxRank:maxRank,normalizeRanks:normalizeRanks,notime:notime,partition:partition,pick:pick,predecessorWeights:predecessorWeights,range:range,removeEmptyRanks:removeEmptyRanks,simplify:simplify,successorWeights:successorWeights,time:time,uniqueId:uniqueId,zipObject:zipObject}; -/* - * Adds a dummy node to the graph and return v. - */function addDummyNode(g,type,attrs,name){let v;do{v=uniqueId(name)}while(g.hasNode(v));attrs.dummy=type;g.setNode(v,attrs);return v} -/* - * Returns a new graph with only simple edges. Handles aggregation of data - * associated with multi-edges. - */function simplify(g){let simplified=(new Graph).setGraph(g.graph());g.nodes().forEach(v=>simplified.setNode(v,g.node(v)));g.edges().forEach(e=>{let simpleLabel=simplified.edge(e.v,e.w)||{weight:0,minlen:1};let label=g.edge(e);simplified.setEdge(e.v,e.w,{weight:simpleLabel.weight+label.weight,minlen:Math.max(simpleLabel.minlen,label.minlen)})});return simplified}function asNonCompoundGraph(g){let simplified=new Graph({multigraph:g.isMultigraph()}).setGraph(g.graph());g.nodes().forEach(v=>{if(!g.children(v).length){simplified.setNode(v,g.node(v))}});g.edges().forEach(e=>{simplified.setEdge(e,g.edge(e))});return simplified}function successorWeights(g){let weightMap=g.nodes().map(v=>{let sucs={};g.outEdges(v).forEach(e=>{sucs[e.w]=(sucs[e.w]||0)+g.edge(e).weight});return sucs});return zipObject(g.nodes(),weightMap)}function predecessorWeights(g){let weightMap=g.nodes().map(v=>{let preds={};g.inEdges(v).forEach(e=>{preds[e.v]=(preds[e.v]||0)+g.edge(e).weight});return preds});return zipObject(g.nodes(),weightMap)} -/* - * Finds where a line starting at point ({x, y}) would intersect a rectangle - * ({x, y, width, height}) if it were pointing at the rectangle's center. - */function intersectRect(rect,point){let x=rect.x;let y=rect.y; -// Rectangle intersection algorithm from: -// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes -let dx=point.x-x;let dy=point.y-y;let w=rect.width/2;let h=rect.height/2;if(!dx&&!dy){throw new Error("Not possible to find intersection inside of the rectangle")}let sx,sy;if(Math.abs(dy)*w>Math.abs(dx)*h){ -// Intersection is top or bottom of rect. -if(dy<0){h=-h}sx=h*dx/dy;sy=h}else{ -// Intersection is left or right of rect. -if(dx<0){w=-w}sx=w;sy=w*dy/dx}return{x:x+sx,y:y+sy}} -/* - * Given a DAG with each node assigned "rank" and "order" properties, this - * function will produce a matrix with the ids of each node. - */function buildLayerMatrix(g){let layering=range(maxRank(g)+1).map(()=>[]);g.nodes().forEach(v=>{let node=g.node(v);let rank=node.rank;if(rank!==undefined){layering[rank][node.order]=v}});return layering} -/* - * Adjusts the ranks for all nodes in the graph such that all nodes v have - * rank(v) >= 0 and at least one node w has rank(w) = 0. - */function normalizeRanks(g){let nodeRanks=g.nodes().map(v=>{let rank=g.node(v).rank;if(rank===undefined){return Number.MAX_VALUE}return rank});let min=applyWithChunking(Math.min,nodeRanks);g.nodes().forEach(v=>{let node=g.node(v);if(Object.hasOwn(node,"rank")){node.rank-=min}})}function removeEmptyRanks(g){ -// Ranks may not start at 0, so we need to offset them -let nodeRanks=g.nodes().map(v=>g.node(v).rank);let offset=applyWithChunking(Math.min,nodeRanks);let layers=[];g.nodes().forEach(v=>{let rank=g.node(v).rank-offset;if(!layers[rank]){layers[rank]=[]}layers[rank].push(v)});let delta=0;let nodeRankFactor=g.graph().nodeRankFactor;Array.from(layers).forEach((vs,i)=>{if(vs===undefined&&i%nodeRankFactor!==0){--delta}else if(vs!==undefined&&delta){vs.forEach(v=>g.node(v).rank+=delta)}})}function addBorderNode(g,prefix,rank,order){let node={width:0,height:0};if(arguments.length>=4){node.rank=rank;node.order=order}return addDummyNode(g,"border",node,prefix)}function splitToChunks(array,chunkSize=CHUNKING_THRESHOLD){const chunks=[];for(let i=0;iCHUNKING_THRESHOLD){const chunks=splitToChunks(argsArray);return fn.apply(null,chunks.map(chunk=>fn.apply(null,chunk)))}else{return fn.apply(null,argsArray)}}function maxRank(g){const nodes=g.nodes();const nodeRanks=nodes.map(v=>{let rank=g.node(v).rank;if(rank===undefined){return Number.MIN_VALUE}return rank});return applyWithChunking(Math.max,nodeRanks)} -/* - * Partition a collection into two groups: `lhs` and `rhs`. If the supplied - * function returns true for an entry it goes into `lhs`. Otherwise it goes - * into `rhs. - */function partition(collection,fn){let result={lhs:[],rhs:[]};collection.forEach(value=>{if(fn(value)){result.lhs.push(value)}else{result.rhs.push(value)}});return result} -/* - * Returns a new function that wraps `fn` with a timer. The wrapper logs the - * time it takes to execute the function. - */function time(name,fn){let start=Date.now();try{return fn()}finally{console.log(name+" time: "+(Date.now()-start)+"ms")}}function notime(name,fn){return fn()}let idCounter=0;function uniqueId(prefix){var id=++idCounter;return toString(prefix)+id}function range(start,limit,step=1){if(limit==null){limit=start;start=0}let endCon=i=>ilimitval[funcOrProp]}return Object.entries(obj).reduce((acc,[k,v])=>{acc[k]=func(v,k);return acc},{})}function zipObject(props,values){return props.reduce((acc,key,i)=>{acc[key]=values[i];return acc},{})}},{"@dagrejs/graphlib":29}],28:[function(require,module,exports){module.exports="1.1.4"},{}],29:[function(require,module,exports){ -/** - * Copyright (c) 2014, Chris Pettitt - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -var lib=require("./lib");module.exports={Graph:lib.Graph,json:require("./lib/json"),alg:require("./lib/alg"),version:lib.version}},{"./lib":45,"./lib/alg":36,"./lib/json":46}],30:[function(require,module,exports){module.exports=components;function components(g){var visited={};var cmpts=[];var cmpt;function dfs(v){if(Object.hasOwn(visited,v))return;visited[v]=true;cmpt.push(v);g.successors(v).forEach(dfs);g.predecessors(v).forEach(dfs)}g.nodes().forEach(function(v){cmpt=[];dfs(v);if(cmpt.length){cmpts.push(cmpt)}});return cmpts}},{}],31:[function(require,module,exports){module.exports=dfs; -/* - * A helper that preforms a pre- or post-order traversal on the input graph - * and returns the nodes in the order they were visited. If the graph is - * undirected then this algorithm will navigate using neighbors. If the graph - * is directed then this algorithm will navigate using successors. - * - * If the order is not "post", it will be treated as "pre". - */function dfs(g,vs,order){if(!Array.isArray(vs)){vs=[vs]}var navigation=g.isDirected()?v=>g.successors(v):v=>g.neighbors(v);var orderFunc=order==="post"?postOrderDfs:preOrderDfs;var acc=[];var visited={};vs.forEach(v=>{if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}orderFunc(v,navigation,visited,acc)});return acc}function postOrderDfs(v,navigation,visited,acc){var stack=[[v,false]];while(stack.length>0){var curr=stack.pop();if(curr[1]){acc.push(curr[0])}else{if(!Object.hasOwn(visited,curr[0])){visited[curr[0]]=true;stack.push([curr[0],true]);forEachRight(navigation(curr[0]),w=>stack.push([w,false]))}}}}function preOrderDfs(v,navigation,visited,acc){var stack=[v];while(stack.length>0){var curr=stack.pop();if(!Object.hasOwn(visited,curr)){visited[curr]=true;acc.push(curr);forEachRight(navigation(curr),w=>stack.push(w))}}}function forEachRight(array,iteratee){var length=array.length;while(length--){iteratee(array[length],length,array)}return array}},{}],32:[function(require,module,exports){var dijkstra=require("./dijkstra");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return g.nodes().reduce(function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc);return acc},{})}},{"./dijkstra":33}],33:[function(require,module,exports){var PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=()=>1;function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={};var pq=new PriorityQueue;var v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w;var wEntry=results[w];var weight=weightFn(edge);var distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":43}],34:[function(require,module,exports){var tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return tarjan(g).filter(function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"./tarjan":41}],35:[function(require,module,exports){module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=()=>1;function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={};var nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v;var d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance0){v=pq.removeMin();if(Object.hasOwn(parents,v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":43,"../graph":44}],41:[function(require,module,exports){module.exports=tarjan;function tarjan(g){var index=0;var stack=[];var visited={};// node id -> { onStack, lowlink, index } -var results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!Object.hasOwn(visited,w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[];var w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!Object.hasOwn(visited,v)){dfs(v)}});return results}},{}],42:[function(require,module,exports){function topsort(g){var visited={};var stack={};var results=[];function visit(node){if(Object.hasOwn(stack,node)){throw new CycleException}if(!Object.hasOwn(visited,node)){stack[node]=true;visited[node]=true;g.predecessors(node).forEach(visit);delete stack[node];results.push(node)}}g.sinks().forEach(visit);if(Object.keys(visited).length!==g.nodeCount()){throw new CycleException}return results}class CycleException extends Error{constructor(){super(...arguments)}}module.exports=topsort;topsort.CycleException=CycleException},{}],43:[function(require,module,exports){ -/** - * A min-priority queue data structure. This algorithm is derived from Cormen, - * et al., "Introduction to Algorithms". The basic idea of a min-priority - * queue is that you can efficiently (in O(1) time) get the smallest key in - * the queue. Adding and removing elements takes O(log n) time. A key can - * have its priority decreased in O(log n) time. - */ -class PriorityQueue{_arr=[];_keyIndices={}; -/** - * Returns the number of elements in the queue. Takes `O(1)` time. - */size(){return this._arr.length} -/** - * Returns the keys that are in the queue. Takes `O(n)` time. - */keys(){return this._arr.map(function(x){return x.key})} -/** - * Returns `true` if **key** is in the queue and `false` if not. - */has(key){return Object.hasOwn(this._keyIndices,key)} -/** - * Returns the priority for **key**. If **key** is not present in the queue - * then this function returns `undefined`. Takes `O(1)` time. - * - * @param {Object} key - */priority(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}} -/** - * Returns the key for the minimum element in this queue. If the queue is - * empty this function throws an Error. Takes `O(1)` time. - */min(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key} -/** - * Inserts a new key into the priority queue. If the key already exists in - * the queue this function returns `false`; otherwise it will return `true`. - * Takes `O(n)` time. - * - * @param {Object} key the key to add - * @param {Number} priority the initial priority for the key - */add(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!Object.hasOwn(keyIndices,key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false} -/** - * Removes and returns the smallest key in the queue. Takes `O(log n)` time. - */removeMin(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key} -/** - * Decreases the priority for **key** to **priority**. If the new priority is - * greater than the previous priority, this function will throw an Error. - * - * @param {Object} key the key for which to raise priority - * @param {Number} priority the new priority for the key - */decrease(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priority=priority;this._decrease(index)}_heapify(i){var arr=this._arr;var l=2*i;var r=l+1;var largest=i;if(l>1;if(arr[parent].priorityundefined; -// Defaults to be set when creating a new edge -_defaultEdgeLabelFn=()=>undefined; -// v -> label -_nodes={}; -// v -> edgeObj -_in={}; -// u -> v -> Number -_preds={}; -// v -> edgeObj -_out={}; -// v -> w -> Number -_sucs={}; -// e -> edgeObj -_edgeObjs={}; -// e -> label -_edgeLabels={}; -/* Number of nodes in the graph. Should only be changed by the implementation. */_nodeCount=0; -/* Number of edges in the graph. Should only be changed by the implementation. */_edgeCount=0;_parent;_children;constructor(opts){if(opts){this._isDirected=Object.hasOwn(opts,"directed")?opts.directed:true;this._isMultigraph=Object.hasOwn(opts,"multigraph")?opts.multigraph:false;this._isCompound=Object.hasOwn(opts,"compound")?opts.compound:false}if(this._isCompound){ -// v -> parent -this._parent={}; -// v -> children -this._children={};this._children[GRAPH_NODE]={}}} -/* === Graph functions ========= */ -/** - * Whether graph was created with 'directed' flag set to true or not. - */isDirected(){return this._isDirected} -/** - * Whether graph was created with 'multigraph' flag set to true or not. - */isMultigraph(){return this._isMultigraph} -/** - * Whether graph was created with 'compound' flag set to true or not. - */isCompound(){return this._isCompound} -/** - * Sets the label of the graph. - */setGraph(label){this._label=label;return this} -/** - * Gets the graph label. - */graph(){return this._label} -/* === Node functions ========== */ -/** - * Sets the default node label. If newDefault is a function, it will be - * invoked ach time when setting a label for a node. Otherwise, this label - * will be assigned as default label in case if no label was specified while - * setting a node. - * Complexity: O(1). - */setDefaultNodeLabel(newDefault){this._defaultNodeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultNodeLabelFn=()=>newDefault}return this} -/** - * Gets the number of nodes in the graph. - * Complexity: O(1). - */nodeCount(){return this._nodeCount} -/** - * Gets all nodes of the graph. Note, the in case of compound graph subnodes are - * not included in list. - * Complexity: O(1). - */nodes(){return Object.keys(this._nodes)} -/** - * Gets list of nodes without in-edges. - * Complexity: O(|V|). - */sources(){var self=this;return this.nodes().filter(v=>Object.keys(self._in[v]).length===0)} -/** - * Gets list of nodes without out-edges. - * Complexity: O(|V|). - */sinks(){var self=this;return this.nodes().filter(v=>Object.keys(self._out[v]).length===0)} -/** - * Invokes setNode method for each node in names list. - * Complexity: O(|names|). - */setNodes(vs,value){var args=arguments;var self=this;vs.forEach(function(v){if(args.length>1){self.setNode(v,value)}else{self.setNode(v)}});return this} -/** - * Creates or updates the value for the node v in the graph. If label is supplied - * it is set as the value for the node. If label is not supplied and the node was - * created by this call then the default node label will be assigned. - * Complexity: O(1). - */setNode(v,value){if(Object.hasOwn(this._nodes,v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this} -/** - * Gets the label of node with specified name. - * Complexity: O(|V|). - */node(v){return this._nodes[v]} -/** - * Detects whether graph has a node with specified name or not. - */hasNode(v){return Object.hasOwn(this._nodes,v)} -/** - * Remove the node with the name from the graph or do nothing if the node is not in - * the graph. If the node was removed this function also removes any incident - * edges. - * Complexity: O(1). - */removeNode(v){var self=this;if(Object.hasOwn(this._nodes,v)){var removeEdge=e=>self.removeEdge(self._edgeObjs[e]);delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];this.children(v).forEach(function(child){self.setParent(child)});delete this._children[v]}Object.keys(this._in[v]).forEach(removeEdge);delete this._in[v];delete this._preds[v];Object.keys(this._out[v]).forEach(removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this} -/** - * Sets node p as a parent for node v if it is defined, or removes the - * parent for v if p is undefined. Method throws an exception in case of - * invoking it in context of noncompound graph. - * Average-case complexity: O(1). - */setParent(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(parent===undefined){parent=GRAPH_NODE}else{ -// Coerce parent to string -parent+="";for(var ancestor=parent;ancestor!==undefined;ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromParentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this}_removeFromParentsChildList(v){delete this._children[this._parent[v]][v]} -/** - * Gets parent node for node v. - * Complexity: O(1). - */parent(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}} -/** - * Gets list of direct children of node v. - * Complexity: O(1). - */children(v=GRAPH_NODE){if(this._isCompound){var children=this._children[v];if(children){return Object.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}} -/** - * Return all nodes that are predecessors of the specified node or undefined if node v is not in - * the graph. Behavior is undefined for undirected graphs - use neighbors instead. - * Complexity: O(|V|). - */predecessors(v){var predsV=this._preds[v];if(predsV){return Object.keys(predsV)}} -/** - * Return all nodes that are successors of the specified node or undefined if node v is not in - * the graph. Behavior is undefined for undirected graphs - use neighbors instead. - * Complexity: O(|V|). - */successors(v){var sucsV=this._sucs[v];if(sucsV){return Object.keys(sucsV)}} -/** - * Return all nodes that are predecessors or successors of the specified node or undefined if - * node v is not in the graph. - * Complexity: O(|V|). - */neighbors(v){var preds=this.predecessors(v);if(preds){const union=new Set(preds);for(var succ of this.successors(v)){union.add(succ)}return Array.from(union.values())}}isLeaf(v){var neighbors;if(this.isDirected()){neighbors=this.successors(v)}else{neighbors=this.neighbors(v)}return neighbors.length===0} -/** - * Creates new graph with nodes filtered via filter. Edges incident to rejected node - * are also removed. In case of compound graph, if parent is rejected by filter, - * than all its children are rejected too. - * Average-case complexity: O(|E|+|V|). - */filterNodes(filter){var copy=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});copy.setGraph(this.graph());var self=this;Object.entries(this._nodes).forEach(function([v,value]){if(filter(v)){copy.setNode(v,value)}});Object.values(this._edgeObjs).forEach(function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,self.edge(e))}});var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this._isCompound){copy.nodes().forEach(v=>copy.setParent(v,findParent(v)))}return copy} -/* === Edge functions ========== */ -/** - * Sets the default edge label or factory function. This label will be - * assigned as default label in case if no label was specified while setting - * an edge or this function will be invoked each time when setting an edge - * with no label specified and returned value * will be used as a label for edge. - * Complexity: O(1). - */setDefaultEdgeLabel(newDefault){this._defaultEdgeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultEdgeLabelFn=()=>newDefault}return this} -/** - * Gets the number of edges in the graph. - * Complexity: O(1). - */edgeCount(){return this._edgeCount} -/** - * Gets edges of the graph. In case of compound graph subgraphs are not considered. - * Complexity: O(|E|). - */edges(){return Object.values(this._edgeObjs)} -/** - * Establish an edges path over the nodes in nodes list. If some edge is already - * exists, it will update its label, otherwise it will create an edge between pair - * of nodes with label provided or default label if no label provided. - * Complexity: O(|nodes|). - */setPath(vs,value){var self=this;var args=arguments;vs.reduce(function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this} -/** - * Creates or updates the label for the edge (v, w) with the optionally supplied - * name. If label is supplied it is set as the value for the edge. If label is not - * supplied and the edge was created by this call then the default edge label will - * be assigned. The name parameter is only useful with multigraphs. - */setEdge(){var v,w,name,value;var valueSpecified=false;var arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(name!==undefined){name=""+name}var e=edgeArgsToId(this._isDirected,v,w,name);if(Object.hasOwn(this._edgeLabels,e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(name!==undefined&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} -// It didn't exist, so we need to create it. -// First ensure the nodes exist. -this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirected,v,w,name); -// Ensure we add undirected edges in a consistent way. -v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this} -/** - * Gets the label for the specified edge. - * Complexity: O(1). - */edge(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]} -/** - * Gets the label for the specified edge and converts it to an object. - * Complexity: O(1) - */edgeAsObj(){const edge=this.edge(...arguments);if(typeof edge!=="object"){return{label:edge}}return edge} -/** - * Detects whether the graph contains specified edge or not. No subgraphs are considered. - * Complexity: O(1). - */hasEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return Object.hasOwn(this._edgeLabels,e)} -/** - * Removes the specified edge from the graph. No subgraphs are considered. - * Complexity: O(1). - */removeEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);var edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._edgeCount--}return this} -/** - * Return all edges that point to the node v. Optionally filters those edges down to just those - * coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. - * Complexity: O(|E|). - */inEdges(v,u){var inV=this._in[v];if(inV){var edges=Object.values(inV);if(!u){return edges}return edges.filter(edge=>edge.v===u)}} -/** - * Return all edges that are pointed at by node v. Optionally filters those edges down to just - * those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. - * Complexity: O(|E|). - */outEdges(v,w){var outV=this._out[v];if(outV){var edges=Object.values(outV);if(!w){return edges}return edges.filter(edge=>edge.w===w)}} -/** - * Returns all edges to or from node v regardless of direction. Optionally filters those edges - * down to just those between nodes v and w regardless of direction. - * Complexity: O(|E|). - */nodeEdges(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}}}function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(name===undefined?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}module.exports=Graph},{}],45:[function(require,module,exports){ -// Includes only the "core" of graphlib -module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":44,"./version":47}],46:[function(require,module,exports){var Graph=require("./graph");module.exports={write:write,read:read}; -/** - * Creates a JSON representation of the graph that can be serialized to a string with - * JSON.stringify. The graph can later be restored using json.read. - */function write(g){var json={options:{directed:g.isDirected(),multigraph:g.isMultigraph(),compound:g.isCompound()},nodes:writeNodes(g),edges:writeEdges(g)};if(g.graph()!==undefined){json.value=structuredClone(g.graph())}return json}function writeNodes(g){return g.nodes().map(function(v){var nodeValue=g.node(v);var parent=g.parent(v);var node={v:v};if(nodeValue!==undefined){node.value=nodeValue}if(parent!==undefined){node.parent=parent}return node})}function writeEdges(g){return g.edges().map(function(e){var edgeValue=g.edge(e);var edge={v:e.v,w:e.w};if(e.name!==undefined){edge.name=e.name}if(edgeValue!==undefined){edge.value=edgeValue}return edge})} -/** - * Takes JSON as input and returns the graph representation. - * - * @example - * var g2 = graphlib.json.read(JSON.parse(str)); - * g2.nodes(); - * // ['a', 'b'] - * g2.edges() - * // [ { v: 'a', w: 'b' } ] - */function read(json){var g=new Graph(json.options).setGraph(json.value);json.nodes.forEach(function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});json.edges.forEach(function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":44}],47:[function(require,module,exports){module.exports="2.2.4"},{}]},{},[1])(1)}); +var dagre=(()=>{var v=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var y=v((Si,ee)=>{var Gt=Object.defineProperty,Vt=(e,t,r)=>t in e?Gt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,E=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),k=(e,t,r)=>Vt(e,typeof t!="symbol"?t+"":t,r),R=E((e,t)=>{"use strict";var r="\0",n="\0",i="",o=class{constructor(d){k(this,"_isDirected",!0),k(this,"_isMultigraph",!1),k(this,"_isCompound",!1),k(this,"_label"),k(this,"_defaultNodeLabelFn",()=>{}),k(this,"_defaultEdgeLabelFn",()=>{}),k(this,"_nodes",{}),k(this,"_in",{}),k(this,"_preds",{}),k(this,"_out",{}),k(this,"_sucs",{}),k(this,"_edgeObjs",{}),k(this,"_edgeLabels",{}),k(this,"_nodeCount",0),k(this,"_edgeCount",0),k(this,"_parent"),k(this,"_children"),d&&(this._isDirected=Object.hasOwn(d,"directed")?d.directed:!0,this._isMultigraph=Object.hasOwn(d,"multigraph")?d.multigraph:!1,this._isCompound=Object.hasOwn(d,"compound")?d.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children[n]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(d){return this._label=d,this}graph(){return this._label}setDefaultNodeLabel(d){return this._defaultNodeLabelFn=d,typeof d!="function"&&(this._defaultNodeLabelFn=()=>d),this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){var d=this;return this.nodes().filter(h=>Object.keys(d._in[h]).length===0)}sinks(){var d=this;return this.nodes().filter(h=>Object.keys(d._out[h]).length===0)}setNodes(d,h){var f=arguments,m=this;return d.forEach(function(p){f.length>1?m.setNode(p,h):m.setNode(p)}),this}setNode(d,h){return Object.hasOwn(this._nodes,d)?(arguments.length>1&&(this._nodes[d]=h),this):(this._nodes[d]=arguments.length>1?h:this._defaultNodeLabelFn(d),this._isCompound&&(this._parent[d]=n,this._children[d]={},this._children[n][d]=!0),this._in[d]={},this._preds[d]={},this._out[d]={},this._sucs[d]={},++this._nodeCount,this)}node(d){return this._nodes[d]}hasNode(d){return Object.hasOwn(this._nodes,d)}removeNode(d){var h=this;if(Object.hasOwn(this._nodes,d)){var f=m=>h.removeEdge(h._edgeObjs[m]);delete this._nodes[d],this._isCompound&&(this._removeFromParentsChildList(d),delete this._parent[d],this.children(d).forEach(function(m){h.setParent(m)}),delete this._children[d]),Object.keys(this._in[d]).forEach(f),delete this._in[d],delete this._preds[d],Object.keys(this._out[d]).forEach(f),delete this._out[d],delete this._sucs[d],--this._nodeCount}return this}setParent(d,h){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(h===void 0)h=n;else{h+="";for(var f=h;f!==void 0;f=this.parent(f))if(f===d)throw new Error("Setting "+h+" as parent of "+d+" would create a cycle");this.setNode(h)}return this.setNode(d),this._removeFromParentsChildList(d),this._parent[d]=h,this._children[h][d]=!0,this}_removeFromParentsChildList(d){delete this._children[this._parent[d]][d]}parent(d){if(this._isCompound){var h=this._parent[d];if(h!==n)return h}}children(d=n){if(this._isCompound){var h=this._children[d];if(h)return Object.keys(h)}else{if(d===n)return this.nodes();if(this.hasNode(d))return[]}}predecessors(d){var h=this._preds[d];if(h)return Object.keys(h)}successors(d){var h=this._sucs[d];if(h)return Object.keys(h)}neighbors(d){var h=this.predecessors(d);if(h){let m=new Set(h);for(var f of this.successors(d))m.add(f);return Array.from(m.values())}}isLeaf(d){var h;return this.isDirected()?h=this.successors(d):h=this.neighbors(d),h.length===0}filterNodes(d){var h=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});h.setGraph(this.graph());var f=this;Object.entries(this._nodes).forEach(function([w,b]){d(w)&&h.setNode(w,b)}),Object.values(this._edgeObjs).forEach(function(w){h.hasNode(w.v)&&h.hasNode(w.w)&&h.setEdge(w,f.edge(w))});var m={};function p(w){var b=f.parent(w);return b===void 0||h.hasNode(b)?(m[w]=b,b):b in m?m[b]:p(b)}return this._isCompound&&h.nodes().forEach(w=>h.setParent(w,p(w))),h}setDefaultEdgeLabel(d){return this._defaultEdgeLabelFn=d,typeof d!="function"&&(this._defaultEdgeLabelFn=()=>d),this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(d,h){var f=this,m=arguments;return d.reduce(function(p,w){return m.length>1?f.setEdge(p,w,h):f.setEdge(p,w),w}),this}setEdge(){var d,h,f,m,p=!1,w=arguments[0];typeof w=="object"&&w!==null&&"v"in w?(d=w.v,h=w.w,f=w.name,arguments.length===2&&(m=arguments[1],p=!0)):(d=w,h=arguments[1],f=arguments[3],arguments.length>2&&(m=arguments[2],p=!0)),d=""+d,h=""+h,f!==void 0&&(f=""+f);var b=l(this._isDirected,d,h,f);if(Object.hasOwn(this._edgeLabels,b))return p&&(this._edgeLabels[b]=m),this;if(f!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(d),this.setNode(h),this._edgeLabels[b]=p?m:this._defaultEdgeLabelFn(d,h,f);var g=u(this._isDirected,d,h,f);return d=g.v,h=g.w,Object.freeze(g),this._edgeObjs[b]=g,s(this._preds[h],d),s(this._sucs[d],h),this._in[h][b]=g,this._out[d][b]=g,this._edgeCount++,this}edge(d,h,f){var m=arguments.length===1?c(this._isDirected,arguments[0]):l(this._isDirected,d,h,f);return this._edgeLabels[m]}edgeAsObj(){let d=this.edge(...arguments);return typeof d!="object"?{label:d}:d}hasEdge(d,h,f){var m=arguments.length===1?c(this._isDirected,arguments[0]):l(this._isDirected,d,h,f);return Object.hasOwn(this._edgeLabels,m)}removeEdge(d,h,f){var m=arguments.length===1?c(this._isDirected,arguments[0]):l(this._isDirected,d,h,f),p=this._edgeObjs[m];return p&&(d=p.v,h=p.w,delete this._edgeLabels[m],delete this._edgeObjs[m],a(this._preds[h],d),a(this._sucs[d],h),delete this._in[h][m],delete this._out[d][m],this._edgeCount--),this}inEdges(d,h){return this.isDirected()?this.filterEdges(this._in[d],d,h):this.nodeEdges(d,h)}outEdges(d,h){return this.isDirected()?this.filterEdges(this._out[d],d,h):this.nodeEdges(d,h)}nodeEdges(d,h){if(d in this._nodes)return this.filterEdges({...this._in[d],...this._out[d]},d,h)}filterEdges(d,h,f){if(d){var m=Object.values(d);return f?m.filter(function(p){return p.v===h&&p.w===f||p.v===f&&p.w===h}):m}}};function s(d,h){d[h]?d[h]++:d[h]=1}function a(d,h){--d[h]||delete d[h]}function l(d,h,f,m){var p=""+h,w=""+f;if(!d&&p>w){var b=p;p=w,w=b}return p+i+w+i+(m===void 0?r:m)}function u(d,h,f,m){var p=""+h,w=""+f;if(!d&&p>w){var b=p;p=w,w=b}var g={v:p,w};return m&&(g.name=m),g}function c(d,h){return l(d,h.v,h.w,h.name)}t.exports=o}),At=E((e,t)=>{t.exports="3.0.4"}),Yt=E((e,t)=>{t.exports={Graph:R(),version:At()}}),Bt=E((e,t)=>{var r=R();t.exports={write:n,read:s};function n(a){var l={options:{directed:a.isDirected(),multigraph:a.isMultigraph(),compound:a.isCompound()},nodes:i(a),edges:o(a)};return a.graph()!==void 0&&(l.value=structuredClone(a.graph())),l}function i(a){return a.nodes().map(function(l){var u=a.node(l),c=a.parent(l),d={v:l};return u!==void 0&&(d.value=u),c!==void 0&&(d.parent=c),d})}function o(a){return a.edges().map(function(l){var u=a.edge(l),c={v:l.v,w:l.w};return l.name!==void 0&&(c.name=l.name),u!==void 0&&(c.value=u),c})}function s(a){var l=new r(a.options).setGraph(a.value);return a.nodes.forEach(function(u){l.setNode(u.v,u.value),u.parent&&l.setParent(u.v,u.parent)}),a.edges.forEach(function(u){l.setEdge({v:u.v,w:u.w,name:u.name},u.value)}),l}}),H=E((e,t)=>{t.exports=n;var r=()=>1;function n(o,s,a,l){return i(o,String(s),a||r,l||function(u){return o.outEdges(u)})}function i(o,s,a,l){var u={},c=!0,d=0,h=o.nodes(),f=function(b){var g=a(b);u[b.v].distance+g{t.exports=r;function r(n){var i={},o=[],s;function a(l){Object.hasOwn(i,l)||(i[l]=!0,s.push(l),n.successors(l).forEach(a),n.predecessors(l).forEach(a))}return n.nodes().forEach(function(l){s=[],a(l),s.length&&o.push(s)}),o}}),Q=E((e,t)=>{var r=class{constructor(){k(this,"_arr",[]),k(this,"_keyIndices",{})}size(){return this._arr.length}keys(){return this._arr.map(function(n){return n.key})}has(n){return Object.hasOwn(this._keyIndices,n)}priority(n){var i=this._keyIndices[n];if(i!==void 0)return this._arr[i].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(n,i){var o=this._keyIndices;if(n=String(n),!Object.hasOwn(o,n)){var s=this._arr,a=s.length;return o[n]=a,s.push({key:n,priority:i}),this._decrease(a),!0}return!1}removeMin(){this._swap(0,this._arr.length-1);var n=this._arr.pop();return delete this._keyIndices[n.key],this._heapify(0),n.key}decrease(n,i){var o=this._keyIndices[n];if(i>this._arr[o].priority)throw new Error("New priority is greater than current priority. Key: "+n+" Old: "+this._arr[o].priority+" New: "+i);this._arr[o].priority=i,this._decrease(o)}_heapify(n){var i=this._arr,o=2*n,s=o+1,a=n;o>1,!(i[s].priority{var r=Q();t.exports=i;var n=()=>1;function i(s,a,l,u){var c=function(d){return s.outEdges(d)};return o(s,String(a),l||n,u||c)}function o(s,a,l,u){var c={},d=new r,h,f,m=function(p){var w=p.v!==h?p.v:p.w,b=c[w],g=l(p),I=f.distance+g;if(g<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+p+" Weight: "+g);I0&&(h=d.removeMin(),f=c[h],f.distance!==Number.POSITIVE_INFINITY);)u(h).forEach(m);return c}}),zt=E((e,t)=>{var r=T();t.exports=n;function n(i,o,s){return i.nodes().reduce(function(a,l){return a[l]=r(i,l,o,s),a},{})}}),Xt=E((e,t)=>{t.exports=r;function r(i,o,s){if(i[o].predecessor!==void 0)throw new Error("Invalid source vertex");if(i[s].predecessor===void 0&&s!==o)throw new Error("Invalid destination vertex");return{weight:i[s].distance,path:n(i,o,s)}}function n(i,o,s){for(var a=[],l=s;l!==o;)a.push(l),l=i[l].predecessor;return a.push(o),a.reverse()}}),J=E((e,t)=>{t.exports=r;function r(n){var i=0,o=[],s={},a=[];function l(u){var c=s[u]={onStack:!0,lowlink:i,index:i++};if(o.push(u),n.successors(u).forEach(function(f){Object.hasOwn(s,f)?s[f].onStack&&(c.lowlink=Math.min(c.lowlink,s[f].index)):(l(f),c.lowlink=Math.min(c.lowlink,s[f].lowlink))}),c.lowlink===c.index){var d=[],h;do h=o.pop(),s[h].onStack=!1,d.push(h);while(u!==h);a.push(d)}}return n.nodes().forEach(function(u){Object.hasOwn(s,u)||l(u)}),a}}),Ut=E((e,t)=>{var r=J();t.exports=n;function n(i){return r(i).filter(function(o){return o.length>1||o.length===1&&i.hasEdge(o[0],o[0])})}}),Ht=E((e,t)=>{t.exports=n;var r=()=>1;function n(o,s,a){return i(o,s||r,a||function(l){return o.outEdges(l)})}function i(o,s,a){var l={},u=o.nodes();return u.forEach(function(c){l[c]={},l[c][c]={distance:0},u.forEach(function(d){c!==d&&(l[c][d]={distance:Number.POSITIVE_INFINITY})}),a(c).forEach(function(d){var h=d.v===c?d.w:d.v,f=s(d);l[c][h]={distance:f,predecessor:c}})}),u.forEach(function(c){var d=l[c];u.forEach(function(h){var f=l[h];u.forEach(function(m){var p=f[c],w=d[m],b=f[m],g=p.distance+w.distance;g{function r(i){var o={},s={},a=[];function l(u){if(Object.hasOwn(s,u))throw new n;Object.hasOwn(o,u)||(s[u]=!0,o[u]=!0,i.predecessors(u).forEach(l),delete s[u],a.push(u))}if(i.sinks().forEach(l),Object.keys(o).length!==i.nodeCount())throw new n;return a}var n=class extends Error{constructor(){super(...arguments)}};t.exports=r,r.CycleException=n}),Qt=E((e,t)=>{var r=K();t.exports=n;function n(i){try{r(i)}catch(o){if(o instanceof r.CycleException)return!1;throw o}return!0}}),Z=E((e,t)=>{t.exports=r;function r(i,o,s,a,l){Array.isArray(o)||(o=[o]);var u=(i.isDirected()?i.successors:i.neighbors).bind(i),c={};return o.forEach(function(d){if(!i.hasNode(d))throw new Error("Graph does not have node: "+d);l=n(i,d,s==="post",c,u,a,l)}),l}function n(i,o,s,a,l,u,c){if(Object.hasOwn(a,o))return c;var d=l(o),h=[{v:o,nb:d,childIdx:0}];for(a[o]=!0,s||(c=u(c,o));h.length>0;){for(var f=h[h.length-1],m=!1;f.childIdx{var r=Z();t.exports=n;function n(i,o,s){return r(i,o,s,function(a,l){return a.push(l),a},[])}}),Jt=E((e,t)=>{var r=$();t.exports=n;function n(i,o){return r(i,o,"post")}}),Kt=E((e,t)=>{var r=$();t.exports=n;function n(i,o){return r(i,o,"pre")}}),Zt=E((e,t)=>{var r=R(),n=Q();t.exports=i;function i(o,s){var a=new r,l={},u=new n,c;function d(f){var m=f.v===c?f.w:f.v,p=u.priority(m);if(p!==void 0){var w=s(f);w0;){if(c=u.removeMin(),Object.hasOwn(l,c))a.setEdge(c,l[c]);else{if(h)throw new Error("Input graph is not connected: "+o);h=!0}o.nodeEdges(c).forEach(d)}return a}}),$t=E((e,t)=>{var r=T(),n=H();t.exports=i;function i(s,a,l,u){return o(s,a,l,u||function(c){return s.outEdges(c)})}function o(s,a,l,u){if(l===void 0)return r(s,a,l,u);for(var c=!1,d=s.nodes(),h=0;h{t.exports={bellmanFord:H(),components:Wt(),dijkstra:T(),dijkstraAll:zt(),extractPath:Xt(),findCycles:Ut(),floydWarshall:Ht(),isAcyclic:Qt(),postorder:Jt(),preorder:Kt(),prim:Zt(),shortestPaths:$t(),reduce:Z(),tarjan:J(),topsort:K()}}),U=Yt();ee.exports={Graph:U.Graph,json:Bt(),alg:er(),version:U.version}});var ne=v((Gi,re)=>{var S=class{constructor(){let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,r=t._prev;if(r!==t)return te(r),r}enqueue(t){let r=this._sentinel;t._prev&&t._next&&te(t),t._next=r._next,r._next._prev=t,r._next=t,t._prev=r}toString(){let t=[],r=this._sentinel,n=r._prev;for(;n!==r;)t.push(JSON.stringify(n,tr)),n=n._prev;return"["+t.join(", ")+"]"}};function te(e){e._prev._next=e._next,e._next._prev=e._prev,delete e._next,delete e._prev}function tr(e,t){if(e!=="_next"&&e!=="_prev")return t}re.exports=S});var oe=v((Vi,ie)=>{var rr=y().Graph,nr=ne();ie.exports=or;var ir=()=>1;function or(e,t){if(e.nodeCount()<=1)return[];let r=ar(e,t||ir);return sr(r.graph,r.buckets,r.zeroIdx).flatMap(i=>e.outEdges(i.v,i.w))}function sr(e,t,r){let n=[],i=t[t.length-1],o=t[0],s;for(;e.nodeCount();){for(;s=o.dequeue();)P(e,t,r,s);for(;s=i.dequeue();)P(e,t,r,s);if(e.nodeCount()){for(let a=t.length-2;a>0;--a)if(s=t[a].dequeue(),s){n=n.concat(P(e,t,r,s,!0));break}}}return n}function P(e,t,r,n,i){let o=i?[]:void 0;return e.inEdges(n.v).forEach(s=>{let a=e.edge(s),l=e.node(s.v);i&&o.push({v:s.v,w:s.w}),l.out-=a,F(t,r,l)}),e.outEdges(n.v).forEach(s=>{let a=e.edge(s),l=s.w,u=e.node(l);u.in-=a,F(t,r,u)}),e.removeNode(n.v),o}function ar(e,t){let r=new rr,n=0,i=0;e.nodes().forEach(a=>{r.setNode(a,{v:a,in:0,out:0})}),e.edges().forEach(a=>{let l=r.edge(a.v,a.w)||0,u=t(a),c=l+u;r.setEdge(a.v,a.w,c),i=Math.max(i,r.node(a.v).out+=u),n=Math.max(n,r.node(a.w).in+=u)});let o=dr(i+n+3).map(()=>new nr),s=n+1;return r.nodes().forEach(a=>{F(o,s,r.node(a))}),{graph:r,buckets:o,zeroIdx:s}}function F(e,t,r){r.out?r.in?e[r.out-r.in+t].enqueue(r):e[e.length-1].enqueue(r):e[0].enqueue(r)}function dr(e){let t=[];for(let r=0;r{"use strict";var se=y().Graph;he.exports={addBorderNode:br,addDummyNode:ae,applyWithChunking:C,asNonCompoundGraph:ur,buildLayerMatrix:pr,intersectRect:fr,mapValues:yr,maxRank:de,normalizeRanks:mr,notime:Er,partition:vr,pick:kr,predecessorWeights:cr,range:ue,removeEmptyRanks:wr,simplify:lr,successorWeights:hr,time:gr,uniqueId:le,zipObject:D};function ae(e,t,r,n){for(var i=n;e.hasNode(i);)i=le(n);return r.dummy=t,e.setNode(i,r),i}function lr(e){let t=new se().setGraph(e.graph());return e.nodes().forEach(r=>t.setNode(r,e.node(r))),e.edges().forEach(r=>{let n=t.edge(r.v,r.w)||{weight:0,minlen:1},i=e.edge(r);t.setEdge(r.v,r.w,{weight:n.weight+i.weight,minlen:Math.max(n.minlen,i.minlen)})}),t}function ur(e){let t=new se({multigraph:e.isMultigraph()}).setGraph(e.graph());return e.nodes().forEach(r=>{e.children(r).length||t.setNode(r,e.node(r))}),e.edges().forEach(r=>{t.setEdge(r,e.edge(r))}),t}function hr(e){let t=e.nodes().map(r=>{let n={};return e.outEdges(r).forEach(i=>{n[i.w]=(n[i.w]||0)+e.edge(i).weight}),n});return D(e.nodes(),t)}function cr(e){let t=e.nodes().map(r=>{let n={};return e.inEdges(r).forEach(i=>{n[i.v]=(n[i.v]||0)+e.edge(i).weight}),n});return D(e.nodes(),t)}function fr(e,t){let r=e.x,n=e.y,i=t.x-r,o=t.y-n,s=e.width/2,a=e.height/2;if(!i&&!o)throw new Error("Not possible to find intersection inside of the rectangle");let l,u;return Math.abs(o)*s>Math.abs(i)*a?(o<0&&(a=-a),l=a*i/o,u=a):(i<0&&(s=-s),l=s,u=s*o/i),{x:r+l,y:n+u}}function pr(e){let t=ue(de(e)+1).map(()=>[]);return e.nodes().forEach(r=>{let n=e.node(r),i=n.rank;i!==void 0&&(t[i][n.order]=r)}),t}function mr(e){let t=e.nodes().map(n=>{let i=e.node(n).rank;return i===void 0?Number.MAX_VALUE:i}),r=C(Math.min,t);e.nodes().forEach(n=>{let i=e.node(n);Object.hasOwn(i,"rank")&&(i.rank-=r)})}function wr(e){let t=e.nodes().map(s=>e.node(s).rank).filter(s=>s!==void 0),r=C(Math.min,t),n=[];e.nodes().forEach(s=>{let a=e.node(s).rank-r;n[a]||(n[a]=[]),n[a].push(s)});let i=0,o=e.graph().nodeRankFactor;Array.from(n).forEach((s,a)=>{s===void 0&&a%o!==0?--i:s!==void 0&&i&&s.forEach(l=>e.node(l).rank+=i)})}function br(e,t,r,n){let i={width:0,height:0};return arguments.length>=4&&(i.rank=r,i.order=n),ae(e,"border",i,t)}function C(e,t){if(t.length===0)return e();let r=t[0];for(let n=1;n{let i=e.node(n).rank;return i===void 0?Number.MIN_VALUE:i});return C(Math.max,r)}function vr(e,t){let r={lhs:[],rhs:[]};return e.forEach(n=>{t(n)?r.lhs.push(n):r.rhs.push(n)}),r}function gr(e,t){let r=Date.now();try{return t()}finally{console.log(e+" time: "+(Date.now()-r)+"ms")}}function Er(e,t){return t()}var _r=0;function le(e){var t=++_r;return e+(""+t)}function ue(e,t,r=1){t==null&&(t=e,e=0);let n=o=>otn[t]),Object.entries(e).reduce((n,[i,o])=>(n[i]=r(o,i),n),{})}function D(e,t){return e.reduce((r,n,i)=>(r[n]=t[i],r),{})}});var fe=v((Yi,ce)=>{"use strict";var xr=oe(),Or=_().uniqueId;ce.exports={run:Nr,undo:jr};function Nr(e){(e.graph().acyclicer==="greedy"?xr(e,r(e)):Ir(e)).forEach(n=>{let i=e.edge(n);e.removeEdge(n),i.forwardName=n.name,i.reversed=!0,e.setEdge(n.w,n.v,i,Or("rev"))});function r(n){return i=>n.edge(i).weight}}function Ir(e){let t=[],r={},n={};function i(o){Object.hasOwn(n,o)||(n[o]=!0,r[o]=!0,e.outEdges(o).forEach(s=>{Object.hasOwn(r,s.w)?t.push(s):i(s.w)}),delete r[o])}return e.nodes().forEach(i),t}function jr(e){e.edges().forEach(t=>{let r=e.edge(t);if(r.reversed){e.removeEdge(t);let n=r.forwardName;delete r.reversed,delete r.forwardName,e.setEdge(t.w,t.v,r,n)}})}});var me=v((Bi,pe)=>{"use strict";var Cr=_();pe.exports={run:Lr,undo:Mr};function Lr(e){e.graph().dummyChains=[],e.edges().forEach(t=>qr(e,t))}function qr(e,t){let r=t.v,n=e.node(r).rank,i=t.w,o=e.node(i).rank,s=t.name,a=e.edge(t),l=a.labelRank;if(o===n+1)return;e.removeEdge(t);let u,c,d;for(d=0,++n;n{let r=e.node(t),n=r.edgeLabel,i;for(e.setEdge(r.edgeObj,n);r.dummy;)i=e.successors(t)[0],e.removeNode(t),n.points.push({x:r.x,y:r.y}),r.dummy==="edge-label"&&(n.x=r.x,n.y=r.y,n.width=r.width,n.height=r.height),t=i,r=e.node(t)})}});var j=v((Wi,we)=>{"use strict";var{applyWithChunking:Rr}=_();we.exports={longestPath:Tr,slack:Sr};function Tr(e){var t={};function r(n){var i=e.node(n);if(Object.hasOwn(t,n))return i.rank;t[n]=!0;let o=e.outEdges(n).map(a=>a==null?Number.POSITIVE_INFINITY:r(a.w)-e.edge(a).minlen);var s=Rr(Math.min,o);return s===Number.POSITIVE_INFINITY&&(s=0),i.rank=s}e.sources().forEach(r)}function Sr(e,t){return e.node(t.w).rank-e.node(t.v).rank-e.edge(t).minlen}});var G=v((zi,be)=>{"use strict";var Pr=y().Graph,L=j().slack;be.exports=Fr;function Fr(e){var t=new Pr({directed:!1}),r=e.nodes()[0],n=e.nodeCount();t.setNode(r,{});for(var i,o;Dr(t,e){var o=i.v,s=n===o?i.w:o;!e.hasNode(s)&&!L(t,i)&&(e.setNode(s,{}),e.setEdge(n,s,{}),r(s))})}return e.nodes().forEach(r),e.nodeCount()}function Gr(e,t){return t.edges().reduce((n,i)=>{let o=Number.POSITIVE_INFINITY;return e.hasNode(i.v)!==e.hasNode(i.w)&&(o=L(t,i)),ot.node(n).rank+=r)}});var Ne=v((Xi,Oe)=>{"use strict";var Ar=G(),ve=j().slack,Yr=j().longestPath,Br=y().alg.preorder,Wr=y().alg.postorder,zr=_().simplify;Oe.exports=N;N.initLowLimValues=A;N.initCutValues=V;N.calcCutValue=Ee;N.leaveEdge=ke;N.enterEdge=ye;N.exchangeEdges=xe;function N(e){e=zr(e),Yr(e);var t=Ar(e);A(t),V(t,e);for(var r,n;r=ke(t);)n=ye(t,e,r),xe(t,e,r,n)}function V(e,t){var r=Wr(e,e.nodes());r=r.slice(0,r.length-1),r.forEach(n=>Xr(e,t,n))}function Xr(e,t,r){var n=e.node(r),i=n.parent;e.edge(r,i).cutvalue=Ee(e,t,r)}function Ee(e,t,r){var n=e.node(r),i=n.parent,o=!0,s=t.edge(r,i),a=0;return s||(o=!1,s=t.edge(i,r)),a=s.weight,t.nodeEdges(r).forEach(l=>{var u=l.v===r,c=u?l.w:l.v;if(c!==i){var d=u===o,h=t.edge(l).weight;if(a+=d?h:-h,Hr(e,r,c)){var f=e.edge(r,c).cutvalue;a+=d?-f:f}}}),a}function A(e,t){arguments.length<2&&(t=e.nodes()[0]),_e(e,{},1,t)}function _e(e,t,r,n,i){var o=r,s=e.node(n);return t[n]=!0,e.neighbors(n).forEach(a=>{Object.hasOwn(t,a)||(r=_e(e,t,r,a,n))}),s.low=o,s.lim=r++,i?s.parent=i:delete s.parent,r}function ke(e){return e.edges().find(t=>e.edge(t).cutvalue<0)}function ye(e,t,r){var n=r.v,i=r.w;t.hasEdge(n,i)||(n=r.w,i=r.v);var o=e.node(n),s=e.node(i),a=o,l=!1;o.lim>s.lim&&(a=s,l=!0);var u=t.edges().filter(c=>l===ge(e,e.node(c.v),a)&&l!==ge(e,e.node(c.w),a));return u.reduce((c,d)=>ve(t,d)!t.node(i).parent),n=Br(e,r);n=n.slice(1),n.forEach(i=>{var o=e.node(i).parent,s=t.edge(i,o),a=!1;s||(s=t.edge(o,i),a=!0),t.node(i).rank=t.node(o).rank+(a?s.minlen:-s.minlen)})}function Hr(e,t,r){return e.hasEdge(t,r)}function ge(e,t,r){return r.low<=t.lim&&t.lim<=r.lim}});var Le=v((Ui,Ce)=>{"use strict";var Qr=j(),je=Qr.longestPath,Jr=G(),Kr=Ne();Ce.exports=Zr;function Zr(e){var t=e.graph().ranker;if(t instanceof Function)return t(e);switch(e.graph().ranker){case"network-simplex":Ie(e);break;case"tight-tree":en(e);break;case"longest-path":$r(e);break;case"none":break;default:Ie(e)}}var $r=je;function en(e){je(e),Jr(e)}function Ie(e){Kr(e)}});var Me=v((Hi,qe)=>{qe.exports=tn;function tn(e){let t=nn(e);e.graph().dummyChains.forEach(r=>{let n=e.node(r),i=n.edgeObj,o=rn(e,t,i.v,i.w),s=o.path,a=o.lca,l=0,u=s[l],c=!0;for(;r!==i.w;){if(n=e.node(r),c){for(;(u=s[l])!==a&&e.node(u).maxRanks||a>t[l].lim));for(u=l,l=n;(l=e.parent(l))!==u;)o.push(l);return{path:i.concat(o.reverse()),lca:u}}function nn(e){let t={},r=0;function n(i){let o=r;e.children(i).forEach(n),t[i]={low:o,lim:r++}}return e.children().forEach(n),t}});var Se=v((Qi,Te)=>{var q=_();Te.exports={run:on,cleanup:dn};function on(e){let t=q.addDummyNode(e,"root",{},"_root"),r=sn(e),n=Object.values(r),i=q.applyWithChunking(Math.max,n)-1,o=2*i+1;e.graph().nestingRoot=t,e.edges().forEach(a=>e.edge(a).minlen*=o);let s=an(e)+1;e.children().forEach(a=>Re(e,t,o,s,i,r,a)),e.graph().nodeRankFactor=o}function Re(e,t,r,n,i,o,s){let a=e.children(s);if(!a.length){s!==t&&e.setEdge(t,s,{weight:0,minlen:r});return}let l=q.addBorderNode(e,"_bt"),u=q.addBorderNode(e,"_bb"),c=e.node(s);e.setParent(l,s),c.borderTop=l,e.setParent(u,s),c.borderBottom=u,a.forEach(d=>{Re(e,t,r,n,i,o,d);let h=e.node(d),f=h.borderTop?h.borderTop:d,m=h.borderBottom?h.borderBottom:d,p=h.borderTop?n:2*n,w=f!==m?1:i-o[s]+1;e.setEdge(l,f,{weight:p,minlen:w,nestingEdge:!0}),e.setEdge(m,u,{weight:p,minlen:w,nestingEdge:!0})}),e.parent(s)||e.setEdge(t,l,{weight:0,minlen:i+o[s]})}function sn(e){var t={};function r(n,i){var o=e.children(n);o&&o.length&&o.forEach(s=>r(s,i+1)),t[n]=i}return e.children().forEach(n=>r(n,1)),t}function an(e){return e.edges().reduce((t,r)=>t+e.edge(r).weight,0)}function dn(e){var t=e.graph();e.removeNode(t.nestingRoot),delete t.nestingRoot,e.edges().forEach(r=>{var n=e.edge(r);n.nestingEdge&&e.removeEdge(r)})}});var De=v((Ji,Fe)=>{var ln=_();Fe.exports=un;function un(e){function t(r){let n=e.children(r),i=e.node(r);if(n.length&&n.forEach(t),Object.hasOwn(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(let o=i.minRank,s=i.maxRank+1;o{"use strict";Ae.exports={adjust:hn,undo:cn};function hn(e){let t=e.graph().rankdir.toLowerCase();(t==="lr"||t==="rl")&&Ve(e)}function cn(e){let t=e.graph().rankdir.toLowerCase();(t==="bt"||t==="rl")&&fn(e),(t==="lr"||t==="rl")&&(pn(e),Ve(e))}function Ve(e){e.nodes().forEach(t=>Ge(e.node(t))),e.edges().forEach(t=>Ge(e.edge(t)))}function Ge(e){let t=e.width;e.width=e.height,e.height=t}function fn(e){e.nodes().forEach(t=>Y(e.node(t))),e.edges().forEach(t=>{let r=e.edge(t);r.points.forEach(Y),Object.hasOwn(r,"y")&&Y(r)})}function Y(e){e.y=-e.y}function pn(e){e.nodes().forEach(t=>B(e.node(t))),e.edges().forEach(t=>{let r=e.edge(t);r.points.forEach(B),Object.hasOwn(r,"x")&&B(r)})}function B(e){let t=e.x;e.x=e.y,e.y=t}});var ze=v((Zi,We)=>{"use strict";var Be=_();We.exports=mn;function mn(e){let t={},r=e.nodes().filter(l=>!e.children(l).length),n=r.map(l=>e.node(l).rank),i=Be.applyWithChunking(Math.max,n),o=Be.range(i+1).map(()=>[]);function s(l){if(t[l])return;t[l]=!0;let u=e.node(l);o[u.rank].push(l),e.successors(l).forEach(s)}return r.sort((l,u)=>e.node(l).rank-e.node(u).rank).forEach(s),o}});var Ue=v(($i,Xe)=>{"use strict";var wn=_().zipObject;Xe.exports=bn;function bn(e,t){let r=0;for(let n=1;nc)),i=t.flatMap(u=>e.outEdges(u).map(c=>({pos:n[c.w],weight:e.edge(c).weight})).sort((c,d)=>c.pos-d.pos)),o=1;for(;o{let c=u.pos+o;a[c]+=u.weight;let d=0;for(;c>0;)c%2&&(d+=a[c+1]),c=c-1>>1,a[c]+=u.weight;l+=u.weight*d}),l}});var Qe=v((eo,He)=>{He.exports=gn;function gn(e,t=[]){return t.map(r=>{let n=e.inEdges(r);if(n.length){let i=n.reduce((o,s)=>{let a=e.edge(s),l=e.node(s.v);return{sum:o.sum+a.weight*l.order,weight:o.weight+a.weight}},{sum:0,weight:0});return{v:r,barycenter:i.sum/i.weight,weight:i.weight}}else return{v:r}})}});var Ke=v((to,Je)=>{"use strict";var En=_();Je.exports=_n;function _n(e,t){let r={};e.forEach((i,o)=>{let s=r[i.v]={indegree:0,in:[],out:[],vs:[i.v],i:o};i.barycenter!==void 0&&(s.barycenter=i.barycenter,s.weight=i.weight)}),t.edges().forEach(i=>{let o=r[i.v],s=r[i.w];o!==void 0&&s!==void 0&&(s.indegree++,o.out.push(r[i.w]))});let n=Object.values(r).filter(i=>!i.indegree);return kn(n)}function kn(e){let t=[];function r(i){return o=>{o.merged||(o.barycenter===void 0||i.barycenter===void 0||o.barycenter>=i.barycenter)&&yn(i,o)}}function n(i){return o=>{o.in.push(i),--o.indegree===0&&e.push(o)}}for(;e.length;){let i=e.pop();t.push(i),i.in.reverse().forEach(r(i)),i.out.forEach(n(i))}return t.filter(i=>!i.merged).map(i=>En.pick(i,["vs","i","barycenter","weight"]))}function yn(e,t){let r=0,n=0;e.weight&&(r+=e.barycenter*e.weight,n+=e.weight),t.weight&&(r+=t.barycenter*t.weight,n+=t.weight),e.vs=t.vs.concat(e.vs),e.barycenter=r/n,e.weight=n,e.i=Math.min(t.i,e.i),t.merged=!0}});var et=v((ro,$e)=>{var xn=_();$e.exports=On;function On(e,t){let r=xn.partition(e,c=>Object.hasOwn(c,"barycenter")),n=r.lhs,i=r.rhs.sort((c,d)=>d.i-c.i),o=[],s=0,a=0,l=0;n.sort(Nn(!!t)),l=Ze(o,i,l),n.forEach(c=>{l+=c.vs.length,o.push(c.vs),s+=c.barycenter*c.weight,a+=c.weight,l=Ze(o,i,l)});let u={vs:o.flat(!0)};return a&&(u.barycenter=s/a,u.weight=a),u}function Ze(e,t,r){let n;for(;t.length&&(n=t[t.length-1]).i<=r;)t.pop(),e.push(n.vs),r++;return r}function Nn(e){return(t,r)=>t.barycenterr.barycenter?1:e?r.i-t.i:t.i-r.i}});var nt=v((no,rt)=>{var In=Qe(),jn=Ke(),Cn=et();rt.exports=tt;function tt(e,t,r,n){let i=e.children(t),o=e.node(t),s=o?o.borderLeft:void 0,a=o?o.borderRight:void 0,l={};s&&(i=i.filter(h=>h!==s&&h!==a));let u=In(e,i);u.forEach(h=>{if(e.children(h.v).length){let f=tt(e,h.v,r,n);l[h.v]=f,Object.hasOwn(f,"barycenter")&&qn(h,f)}});let c=jn(u,r);Ln(c,l);let d=Cn(c,n);if(s&&(d.vs=[s,d.vs,a].flat(!0),e.predecessors(s).length)){let h=e.node(e.predecessors(s)[0]),f=e.node(e.predecessors(a)[0]);Object.hasOwn(d,"barycenter")||(d.barycenter=0,d.weight=0),d.barycenter=(d.barycenter*d.weight+h.order+f.order)/(d.weight+2),d.weight+=2}return d}function Ln(e,t){e.forEach(r=>{r.vs=r.vs.flatMap(n=>t[n]?t[n].vs:n)})}function qn(e,t){e.barycenter!==void 0?(e.barycenter=(e.barycenter*e.weight+t.barycenter*t.weight)/(e.weight+t.weight),e.weight+=t.weight):(e.barycenter=t.barycenter,e.weight=t.weight)}});var ot=v((io,it)=>{var Mn=y().Graph,Rn=_();it.exports=Tn;function Tn(e,t,r,n){n||(n=e.nodes());let i=Sn(e),o=new Mn({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(s=>e.node(s));return n.forEach(s=>{let a=e.node(s),l=e.parent(s);(a.rank===t||a.minRank<=t&&t<=a.maxRank)&&(o.setNode(s),o.setParent(s,l||i),e[r](s).forEach(u=>{let c=u.v===s?u.w:u.v,d=o.edge(c,s),h=d!==void 0?d.weight:0;o.setEdge(c,s,{weight:e.edge(u).weight+h})}),Object.hasOwn(a,"minRank")&&o.setNode(s,{borderLeft:a.borderLeft[t],borderRight:a.borderRight[t]}))}),o}function Sn(e){for(var t;e.hasNode(t=Rn.uniqueId("_root")););return t}});var at=v((oo,st)=>{st.exports=Pn;function Pn(e,t,r){let n={},i;r.forEach(o=>{let s=e.parent(o),a,l;for(;s;){if(a=e.parent(s),a?(l=n[a],n[a]=s):(l=i,i=s),l&&l!==s){t.setEdge(l,s);return}s=a}})}});var ct=v((so,ht)=>{"use strict";var Fn=ze(),Dn=Ue(),Gn=nt(),Vn=ot(),An=at(),Yn=y().Graph,M=_();ht.exports=ut;function ut(e,t={}){if(typeof t.customOrder=="function"){t.customOrder(e,ut);return}let r=M.maxRank(e),n=dt(e,M.range(1,r+1),"inEdges"),i=dt(e,M.range(r-1,-1,-1),"outEdges"),o=Fn(e);if(lt(e,o),t.disableOptimalOrderHeuristic)return;let s=Number.POSITIVE_INFINITY,a,l=t.constraints||[];for(let u=0,c=0;c<4;++u,++c){Bn(u%2?n:i,u%4>=2,l),o=M.buildLayerMatrix(e);let d=Dn(e,o);d{n.has(o)||n.set(o,[]),n.get(o).push(s)};for(let o of e.nodes()){let s=e.node(o);if(typeof s.rank=="number"&&i(s.rank,o),typeof s.minRank=="number"&&typeof s.maxRank=="number")for(let a=s.minRank;a<=s.maxRank;a++)a!==s.rank&&i(a,o)}return t.map(function(o){return Vn(e,o,r,n.get(o)||[])})}function Bn(e,t,r){let n=new Yn;e.forEach(function(i){r.forEach(a=>n.setEdge(a.left,a.right));let o=i.graph().root,s=Gn(i,o,n,t);s.vs.forEach((a,l)=>i.node(a).order=l),An(i,n,s.vs)})}function lt(e,t){Object.values(t).forEach(r=>r.forEach((n,i)=>e.node(n).order=i))}});var kt=v((ao,_t)=>{"use strict";var Wn=y().Graph,O=_();_t.exports={positionX:Un,findType1Conflicts:ft,findType2Conflicts:pt,addConflict:W,hasConflict:mt,verticalAlignment:wt,horizontalCompaction:bt,alignCoordinates:gt,findSmallestWidthAlignment:vt,balance:Et};function ft(e,t){let r={};function n(i,o){let s=0,a=0,l=i.length,u=o[o.length-1];return o.forEach((c,d)=>{let h=zn(e,c),f=h?e.node(h).order:l;(h||c===u)&&(o.slice(a,d+1).forEach(m=>{e.predecessors(m).forEach(p=>{let w=e.node(p),b=w.order;(b{c=o[d],e.node(c).dummy&&e.predecessors(c).forEach(h=>{let f=e.node(h);f.dummy&&(f.orderu)&&W(r,h,c)})})}function i(o,s){let a=-1,l,u=0;return s.forEach((c,d)=>{if(e.node(c).dummy==="border"){let h=e.predecessors(c);h.length&&(l=e.node(h[0]).order,n(s,u,d,a,l),u=d,a=l)}n(s,u,s.length,l,o.length)}),s}return t.length&&t.reduce(i),r}function zn(e,t){if(e.node(t).dummy)return e.predecessors(t).find(r=>e.node(r).dummy)}function W(e,t,r){if(t>r){let i=t;t=r,r=i}let n=e[t];n||(e[t]=n={}),n[r]=!0}function mt(e,t,r){if(t>r){let n=t;t=r,r=n}return!!e[t]&&Object.hasOwn(e[t],r)}function wt(e,t,r,n){let i={},o={},s={};return t.forEach(a=>{a.forEach((l,u)=>{i[l]=l,o[l]=l,s[l]=u})}),t.forEach(a=>{let l=-1;a.forEach(u=>{let c=n(u);if(c.length){c=c.sort((h,f)=>s[h]-s[f]);let d=(c.length-1)/2;for(let h=Math.floor(d),f=Math.ceil(d);h<=f;++h){let m=c[h];o[u]===u&&lMath.max(h,o[f.v]+s.edge(f)),0)}function c(d){let h=s.outEdges(d).reduce((m,p)=>Math.min(m,o[p.w]-s.edge(p)),Number.POSITIVE_INFINITY),f=e.node(d);h!==Number.POSITIVE_INFINITY&&f.borderType!==a&&(o[d]=Math.max(o[d],h))}return l(u,s.predecessors.bind(s)),l(c,s.successors.bind(s)),Object.keys(n).forEach(d=>o[d]=o[r[d]]),o}function Xn(e,t,r,n){let i=new Wn,o=e.graph(),s=Hn(o.nodesep,o.edgesep,n);return t.forEach(a=>{let l;a.forEach(u=>{let c=r[u];if(i.setNode(c),l){var d=r[l],h=i.edge(d,c);i.setEdge(d,c,Math.max(s(e,u,l),h||0))}l=u})}),i}function vt(e,t){return Object.values(t).reduce((r,n)=>{let i=Number.NEGATIVE_INFINITY,o=Number.POSITIVE_INFINITY;Object.entries(n).forEach(([a,l])=>{let u=Qn(e,a)/2;i=Math.max(l+u,i),o=Math.min(l-u,o)});let s=i-o;return s{["l","r"].forEach(s=>{let a=o+s,l=e[a];if(l===t)return;let u=Object.values(l),c=n-O.applyWithChunking(Math.min,u);s!=="l"&&(c=i-O.applyWithChunking(Math.max,u)),c&&(e[a]=O.mapValues(l,d=>d+c))})})}function Et(e,t){return O.mapValues(e.ul,(r,n)=>{if(t)return e[t.toLowerCase()][n];{let i=Object.values(e).map(o=>o[n]).sort((o,s)=>o-s);return(i[1]+i[2])/2}})}function Un(e){let t=O.buildLayerMatrix(e),r=Object.assign(ft(e,t),pt(e,t)),n={},i;["u","d"].forEach(s=>{i=s==="u"?t:Object.values(t).reverse(),["l","r"].forEach(a=>{a==="r"&&(i=i.map(d=>Object.values(d).reverse()));let l=(s==="u"?e.predecessors:e.successors).bind(e),u=wt(e,i,r,l),c=bt(e,i,u.root,u.align,a==="r");a==="r"&&(c=O.mapValues(c,d=>-d)),n[s+a]=c})});let o=vt(e,n);return gt(n,o),Et(n,e.graph().align)}function Hn(e,t,r){return(n,i,o)=>{let s=n.node(i),a=n.node(o),l=0,u;if(l+=s.width/2,Object.hasOwn(s,"labelpos"))switch(s.labelpos.toLowerCase()){case"l":u=-s.width/2;break;case"r":u=s.width/2;break}if(u&&(l+=r?u:-u),u=0,l+=(s.dummy?t:e)/2,l+=(a.dummy?t:e)/2,l+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":u=a.width/2;break;case"r":u=-a.width/2;break}return u&&(l+=r?u:-u),u=0,l}}function Qn(e,t){return e.node(t).width}});var Ot=v((lo,xt)=>{"use strict";var yt=_(),Jn=kt().positionX;xt.exports=Kn;function Kn(e){e=yt.asNonCompoundGraph(e),Zn(e),Object.entries(Jn(e)).forEach(([t,r])=>e.node(t).x=r)}function Zn(e){let t=yt.buildLayerMatrix(e),r=e.graph().ranksep,n=e.graph().rankalign,i=0;t.forEach(o=>{let s=o.reduce((a,l)=>{let u=e.node(l).height;return a>u?a:u},0);o.forEach(a=>{let l=e.node(a);n==="top"?l.y=i+l.height/2:n==="bottom"?l.y=i+s-l.height/2:l.y=i+s/2}),i+=s+r})}});var Mt=v((uo,qt)=>{"use strict";var Nt=fe(),It=me(),$n=Le(),ei=_().normalizeRanks,ti=Me(),ri=_().removeEmptyRanks,jt=Se(),ni=De(),Ct=Ye(),ii=ct(),oi=Ot(),x=_(),si=y().Graph;qt.exports=ai;function ai(e,t={}){let r=t.debugTiming?x.time:x.notime;return r("layout",()=>{let n=r(" buildLayoutGraph",()=>bi(e));return r(" runLayout",()=>di(n,r,t)),r(" updateInputGraph",()=>li(e,n)),n})}function di(e,t,r){t(" makeSpaceForEdgeLabels",()=>vi(e)),t(" removeSelfEdges",()=>Ii(e)),t(" acyclic",()=>Nt.run(e)),t(" nestingGraph.run",()=>jt.run(e)),t(" rank",()=>$n(x.asNonCompoundGraph(e))),t(" injectEdgeLabelProxies",()=>gi(e)),t(" removeEmptyRanks",()=>ri(e)),t(" nestingGraph.cleanup",()=>jt.cleanup(e)),t(" normalizeRanks",()=>ei(e)),t(" assignRankMinMax",()=>Ei(e)),t(" removeEdgeLabelProxies",()=>_i(e)),t(" normalize.run",()=>It.run(e)),t(" parentDummyChains",()=>ti(e)),t(" addBorderSegments",()=>ni(e)),t(" order",()=>ii(e,r)),t(" insertSelfEdges",()=>ji(e)),t(" adjustCoordinateSystem",()=>Ct.adjust(e)),t(" position",()=>oi(e)),t(" positionSelfEdges",()=>Ci(e)),t(" removeBorderNodes",()=>Ni(e)),t(" normalize.undo",()=>It.undo(e)),t(" fixupEdgeLabelCoords",()=>xi(e)),t(" undoCoordinateSystem",()=>Ct.undo(e)),t(" translateGraph",()=>ki(e)),t(" assignNodeIntersects",()=>yi(e)),t(" reversePoints",()=>Oi(e)),t(" acyclic.undo",()=>Nt.undo(e))}function li(e,t){e.nodes().forEach(r=>{let n=e.node(r),i=t.node(r);n&&(n.x=i.x,n.y=i.y,n.order=i.order,n.rank=i.rank,t.children(r).length&&(n.width=i.width,n.height=i.height))}),e.edges().forEach(r=>{let n=e.edge(r),i=t.edge(r);n.points=i.points,Object.hasOwn(i,"x")&&(n.x=i.x,n.y=i.y)}),e.graph().width=t.graph().width,e.graph().height=t.graph().height}var ui=["nodesep","edgesep","ranksep","marginx","marginy"],hi={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb",rankalign:"center"},ci=["acyclicer","ranker","rankdir","align","rankalign"],fi=["width","height","rank"],Lt={width:0,height:0},pi=["minlen","weight","width","height","labeloffset"],mi={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},wi=["labelpos"];function bi(e){let t=new si({multigraph:!0,compound:!0}),r=X(e.graph());return t.setGraph(Object.assign({},hi,z(r,ui),x.pick(r,ci))),e.nodes().forEach(n=>{let i=X(e.node(n)),o=z(i,fi);Object.keys(Lt).forEach(s=>{o[s]===void 0&&(o[s]=Lt[s])}),t.setNode(n,o),t.setParent(n,e.parent(n))}),e.edges().forEach(n=>{let i=X(e.edge(n));t.setEdge(n,Object.assign({},mi,z(i,pi),x.pick(i,wi)))}),t}function vi(e){let t=e.graph();t.ranksep/=2,e.edges().forEach(r=>{let n=e.edge(r);n.minlen*=2,n.labelpos.toLowerCase()!=="c"&&(t.rankdir==="TB"||t.rankdir==="BT"?n.width+=n.labeloffset:n.height+=n.labeloffset)})}function gi(e){e.edges().forEach(t=>{let r=e.edge(t);if(r.width&&r.height){let n=e.node(t.v),o={rank:(e.node(t.w).rank-n.rank)/2+n.rank,e:t};x.addDummyNode(e,"edge-proxy",o,"_ep")}})}function Ei(e){let t=0;e.nodes().forEach(r=>{let n=e.node(r);n.borderTop&&(n.minRank=e.node(n.borderTop).rank,n.maxRank=e.node(n.borderBottom).rank,t=Math.max(t,n.maxRank))}),e.graph().maxRank=t}function _i(e){e.nodes().forEach(t=>{let r=e.node(t);r.dummy==="edge-proxy"&&(e.edge(r.e).labelRank=r.rank,e.removeNode(t))})}function ki(e){let t=Number.POSITIVE_INFINITY,r=0,n=Number.POSITIVE_INFINITY,i=0,o=e.graph(),s=o.marginx||0,a=o.marginy||0;function l(u){let c=u.x,d=u.y,h=u.width,f=u.height;t=Math.min(t,c-h/2),r=Math.max(r,c+h/2),n=Math.min(n,d-f/2),i=Math.max(i,d+f/2)}e.nodes().forEach(u=>l(e.node(u))),e.edges().forEach(u=>{let c=e.edge(u);Object.hasOwn(c,"x")&&l(c)}),t-=s,n-=a,e.nodes().forEach(u=>{let c=e.node(u);c.x-=t,c.y-=n}),e.edges().forEach(u=>{let c=e.edge(u);c.points.forEach(d=>{d.x-=t,d.y-=n}),Object.hasOwn(c,"x")&&(c.x-=t),Object.hasOwn(c,"y")&&(c.y-=n)}),o.width=r-t+s,o.height=i-n+a}function yi(e){e.edges().forEach(t=>{let r=e.edge(t),n=e.node(t.v),i=e.node(t.w),o,s;r.points?(o=r.points[0],s=r.points[r.points.length-1]):(r.points=[],o=i,s=n),r.points.unshift(x.intersectRect(n,o)),r.points.push(x.intersectRect(i,s))})}function xi(e){e.edges().forEach(t=>{let r=e.edge(t);if(Object.hasOwn(r,"x"))switch((r.labelpos==="l"||r.labelpos==="r")&&(r.width-=r.labeloffset),r.labelpos){case"l":r.x-=r.width/2+r.labeloffset;break;case"r":r.x+=r.width/2+r.labeloffset;break}})}function Oi(e){e.edges().forEach(t=>{let r=e.edge(t);r.reversed&&r.points.reverse()})}function Ni(e){e.nodes().forEach(t=>{if(e.children(t).length){let r=e.node(t),n=e.node(r.borderTop),i=e.node(r.borderBottom),o=e.node(r.borderLeft[r.borderLeft.length-1]),s=e.node(r.borderRight[r.borderRight.length-1]);r.width=Math.abs(s.x-o.x),r.height=Math.abs(i.y-n.y),r.x=o.x+r.width/2,r.y=n.y+r.height/2}}),e.nodes().forEach(t=>{e.node(t).dummy==="border"&&e.removeNode(t)})}function Ii(e){e.edges().forEach(t=>{if(t.v===t.w){var r=e.node(t.v);r.selfEdges||(r.selfEdges=[]),r.selfEdges.push({e:t,label:e.edge(t)}),e.removeEdge(t)}})}function ji(e){var t=x.buildLayerMatrix(e);t.forEach(r=>{var n=0;r.forEach((i,o)=>{var s=e.node(i);s.order=o+n,(s.selfEdges||[]).forEach(a=>{x.addDummyNode(e,"selfedge",{width:a.label.width,height:a.label.height,rank:s.rank,order:o+ ++n,e:a.e,label:a.label},"_se")}),delete s.selfEdges})})}function Ci(e){e.nodes().forEach(t=>{var r=e.node(t);if(r.dummy==="selfedge"){var n=e.node(r.e.v),i=n.x+n.width/2,o=n.y,s=r.x-i,a=n.height/2;e.setEdge(r.e,r.label),e.removeNode(t),r.label.points=[{x:i+2*s/3,y:o-a},{x:i+5*s/6,y:o-a},{x:i+s,y:o},{x:i+5*s/6,y:o+a},{x:i+2*s/3,y:o+a}],r.label.x=r.x,r.label.y=r.y}})}function z(e,t){return x.mapValues(x.pick(e,t),Number)}function X(e){var t={};return e&&Object.entries(e).forEach(([r,n])=>{typeof r=="string"&&(r=r.toLowerCase()),t[r]=n}),t}});var Tt=v((ho,Rt)=>{var Li=_(),qi=y().Graph;Rt.exports={debugOrdering:Mi};function Mi(e){let t=Li.buildLayerMatrix(e),r=new qi({compound:!0,multigraph:!0}).setGraph({});return e.nodes().forEach(n=>{r.setNode(n,{label:n}),r.setParent(n,"layer"+e.node(n).rank)}),e.edges().forEach(n=>r.setEdge(n.v,n.w,{},n.name)),t.forEach((n,i)=>{let o="layer"+i;r.setNode(o,{rank:"same"}),n.reduce((s,a)=>(r.setEdge(s,a,{style:"invis"}),a))}),r}});var Pt=v((co,St)=>{St.exports="2.0.5-pre"});var Ri=v((fo,Ft)=>{Ft.exports={graphlib:y(),layout:Mt(),debug:Tt(),util:{time:_().time,notime:_().notime},version:Pt()}});return Ri();})(); +/*! For license information please see dagre.min.js.LEGAL.txt */ +//# sourceMappingURL=dagre.min.js.map diff --git a/tinygrad/viz/js/index.js b/tinygrad/viz/js/index.js index 734474f692..157b704427 100644 --- a/tinygrad/viz/js/index.js +++ b/tinygrad/viz/js/index.js @@ -131,10 +131,15 @@ function renderDag(layoutSpec, { recenter }) { worker = new Worker(workerUrl); worker.postMessage(layoutSpec); worker.onmessage = (e) => { + if (e.data.error) { + updateProgress(Status.ERR, "Error in graph layout:\n"+e.data.error); + return; + } + const data = e.data.result; displaySelection("#graph"); updateProgress(Status.COMPLETE); - drawGraph(e.data); - addTags(d3.select("#edge-labels").selectAll("g").data(e.data.edges).join("g").attr("transform", (e) => { + drawGraph(data); + addTags(d3.select("#edge-labels").selectAll("g").data(data.edges).join("g").attr("transform", (e) => { // get a point near the end const [p1, p2] = e.value.points.slice(-2); const dx = p2.x-p1.x; @@ -751,6 +756,8 @@ const createToggle = (id, text) => { } const showIndexing = createToggle("show-indexing", "Show indexing (r)"); const showCallSrc = createToggle("show-call-src", "Show CALL src (c)"); +const showSink = createToggle("show-sink", "Show SINK (s)"); +showSink.toggle.checked = false; const showGraph = createToggle("show-graph", "Show graph (g)"); showGraph.toggle.onchange = () => displaySelection(rect("#graph").width > 0 ? "#custom" : "#graph"); @@ -900,13 +907,14 @@ async function main() { // ** center graph const data = ret[currentRewrite]; const render = (opts) => renderDag({ data, opts }, { recenter:currentRewrite === 0 }); - const getOpts = () => ({ showIndexing:showIndexing.toggle.checked, showCallSrc:showCallSrc.toggle.checked }); + const getOpts = () => ({ showIndexing:showIndexing.toggle.checked, showCallSrc:showCallSrc.toggle.checked, showSink:showSink.toggle.checked }); render(getOpts()); showIndexing.toggle.onchange = () => render(getOpts()); showCallSrc.toggle.onchange = () => render(getOpts()); + showSink.toggle.onchange = () => render(getOpts()); // ** right sidebar metadata metadata.innerHTML = ""; - if (ckey.includes("rewrites")) metadata.append(showIndexing.label, showCallSrc.label); + if (ckey.includes("rewrites")) metadata.append(showIndexing.label, showCallSrc.label, showSink.label); if (step.code_line != null) metadata.appendChild(codeBlock(step.code_line, "python", { loc:step.loc, wrap:true })); if (step.trace) { const trace = d3.create("pre").append("code").classed("hljs", true); @@ -1037,6 +1045,8 @@ document.addEventListener("keydown", (event) => { if (event.key === "r") showIndexing.toggle.click(); // c key toggles CALL src if (event.key === "c") showCallSrc.toggle.click(); + // s key toggles SINK + if (event.key === "s") showSink.toggle.click(); // g key toggles graph if (event.key === "g") showGraph.toggle.click(); }); diff --git a/tinygrad/viz/js/worker.js b/tinygrad/viz/js/worker.js index 0241579dad..40230f13f7 100644 --- a/tinygrad/viz/js/worker.js +++ b/tinygrad/viz/js/worker.js @@ -5,11 +5,16 @@ const canvas = new OffscreenCanvas(0, 0); const ctx = canvas.getContext("2d"); onmessage = (e) => { - const { data, opts } = e.data; - const g = new dagre.graphlib.Graph({ compound: true }).setDefaultEdgeLabel(function() { return {}; }); - (data.blocks != null ? layoutCfg : layoutUOp)(g, data, opts); - postMessage(dagre.graphlib.json.write(g)); - self.close(); + try { + const { data, opts } = e.data; + const g = new dagre.graphlib.Graph({ compound: true }).setDefaultEdgeLabel(function() { return {}; }); + (data.blocks != null ? layoutCfg : layoutUOp)(g, data, opts); + postMessage({result: dagre.graphlib.json.write(g)}); + self.close(); + } catch (err) { + postMessage({error: err.stack || err.message || String(err)}); + self.close(); + } } const layoutCfg = (g, { blocks, paths, pc_tokens }) => { @@ -56,6 +61,12 @@ const layoutUOp = (g, { graph, change }, opts) => { if (change?.includes(parseInt(k))) g.setParent(k, "overlay"); } // optionally hide nodes from the layout + if (!opts.showSink) { + for (const n of g.nodes()) { + const node = g.node(n); + if ((node.label === "SINK" || node.label.startsWith("SINK\n")) && (g.successors(n) || []).length === 0) g.removeNode(n); + } + } if (!opts.showIndexing) { for (const n of g.nodes()) { const node = g.node(n);