using System; using System.Collections.Generic; namespace Microsoft.Msagl.Layout.Layered { /// /// Following "A technique for Drawing Directed Graphs" of Gansner, Koutsofios, North and Vo /// Works on the layered graph. /// For explanations of the algorithm here see https://www.researchgate.net/profile/Lev_Nachmanson/publication/30509007_Drawing_graphs_with_GLEE/links/54b6b2930cf2e68eb27edf71/Drawing-graphs-with-GLEE.pdf /// /// internal partial class Ordering { /// /// for each vertex v let P[v] be the array of predeccessors of v /// int[][] predecessors; /// /// The array contains a dictionary per vertex /// The value POrder[v][u] gives the offset of u in the array P[v] /// Dictionary[] pOrder; /// /// for each vertex v let S[v] be the array of successors of v /// int[][] successors; /// /// The array contains a dictionary per vertex /// The value SOrder[v][u] gives the offset of u in the array S[v] /// Dictionary[] sOrder; Dictionary[] inCrossingCount; /// /// Gets or sets the number of of passes over all layers to rung adjacent exchanges, where every pass goes ' /// all way up to the top layer and down to the lowest layer /// const int MaxNumberOfAdjacentExchanges = 50; Dictionary[] outCrossingCount; bool HeadOfTheCoin() { return random.Next(2) == 0; } void AdjacentExchangeWithBalancingVirtOrigNodes() { InitArrays(); int count = 0; bool progress = true; while (progress && count++ < MaxNumberOfAdjacentExchanges) { progress = false; for (int i = 0; i < layers.Length; i++) progress = AdjExchangeLayerWithBalance(i) || progress; for (int i = layers.Length - 2; i >= 0; i--) progress = AdjExchangeLayerWithBalance(i) || progress; } } void AdjacentExchange() { InitArrays(); int count = 0; bool progress = true; while (progress && count++ < MaxNumberOfAdjacentExchanges) { progress = false; for (int i = 0; i < layers.Length; i++) progress = AdjExchangeLayer(i) || progress; for (int i = layers.Length - 2; i >= 0; i--) progress = AdjExchangeLayer(i) || progress; } } void AllocArrays() { int n = properLayeredGraph.NodeCount; predecessors = new int[n][]; successors = new int[n][]; pOrder = new Dictionary[n]; sOrder = new Dictionary[n]; if (hasCrossWeights) { outCrossingCount = new Dictionary[n]; inCrossingCount = new Dictionary[n]; } for (int i = 0; i < n; i++) { int count = properLayeredGraph.InEdgesCount(i); predecessors[i] = new int[count]; if (hasCrossWeights) { Dictionary inCounts = inCrossingCount[i] = new Dictionary(count); foreach (LayerEdge le in properLayeredGraph.InEdges(i)) inCounts[le.Source] = le.CrossingWeight; } pOrder[i] = new Dictionary(count); count = properLayeredGraph.OutEdgesCount(i); successors[i] = new int[count]; sOrder[i] = new Dictionary(count); if (hasCrossWeights) { Dictionary outCounts = outCrossingCount[i] = new Dictionary(count); foreach (LayerEdge le in properLayeredGraph.OutEdges(i)) outCounts[le.Target] = le.CrossingWeight; } } } /// /// Is called just after median layer swap is done /// void InitArrays() { if (successors == null) AllocArrays(); for (int i = 0; i < properLayeredGraph.NodeCount; i++) { pOrder[i].Clear(); sOrder[i].Clear(); } foreach (int[] t in layers) InitPsArraysForLayer(t); } /// /// calculates the number of intersections between edges adjacent to u and v /// /// a vertex /// a vertex /// the result when u is to the left of v /// the result when v is to the left of u void CalcPair(int u, int v, out int cuv, out int cvu) { int[] su = successors[u], sv = successors[v], pu = predecessors[u], pv = predecessors[v]; if (!hasCrossWeights) { cuv = CountOnArrays(su, sv) + CountOnArrays(pu, pv); cvu = CountOnArrays(sv, su) + CountOnArrays(pv, pu); } else { Dictionary uOutCrossCounts = outCrossingCount[u]; Dictionary vOutCrossCounts = outCrossingCount[v]; Dictionary uInCrossCounts = inCrossingCount[u]; Dictionary vInCrossCounts = inCrossingCount[v]; cuv = CountOnArrays(su, sv, uOutCrossCounts, vOutCrossCounts) + CountOnArrays(pu, pv, uInCrossCounts, vInCrossCounts); cvu = CountOnArrays(sv, su, vOutCrossCounts, uOutCrossCounts) + CountOnArrays(pv, pu, vInCrossCounts, uInCrossCounts); } } /// /// Sweep layer from left to right and fill S,P arrays as we go. /// The arrays P and S will be sorted according to X. Note that we will not keep them sorted /// as we doing adjacent swaps. Initial sorting only needed to calculate initial clr,crl values. /// /// void InitPsArraysForLayer(int[] layer) { this.ProgressStep(); foreach (int l in layer) { foreach (int p in properLayeredGraph.Pred(l)) { Dictionary so = sOrder[p]; int sHasNow = so.Count; successors[p][sHasNow] = l; //l takes the first available slot in S[p] so[l] = sHasNow; } foreach (int s in properLayeredGraph.Succ(l)) { Dictionary po = pOrder[s]; int pHasNow = po.Count; predecessors[s][pHasNow] = l; //l take the first available slot in P[s] po[l] = pHasNow; } } } int CountOnArrays(int[] unbs, int[] vnbs) { int ret = 0; int vl = vnbs.Length - 1; int j = -1; //the right most position of vnbs to the left from the current u neighbor int vnbsSeenAlready = 0; foreach (int uNeighbor in unbs) { int xu = X[uNeighbor]; for (; j < vl && X[vnbs[j + 1]] < xu; j++) vnbsSeenAlready++; ret += vnbsSeenAlready; } return ret; } /// /// every inversion between unbs and vnbs gives an intersecton /// /// neighbors of u but only from one layer /// neighbors of v from the same layers /// number of intersections when u is to the left of v /// /// int CountOnArrays(int[] unbs, int[] vnbs, Dictionary uCrossingCounts, Dictionary vCrossingCount) { int ret = 0; int vl = vnbs.Length - 1; int j = -1; //the right most position of vnbs to the left from the current u neighbor int vCrossingNumberSeenAlready = 0; foreach (int uNeib in unbs) { int xu = X[uNeib]; int vnb; for (; j < vl && X[vnb = vnbs[j + 1]] < xu; j++) vCrossingNumberSeenAlready += vCrossingCount[vnb]; ret += vCrossingNumberSeenAlready*uCrossingCounts[uNeib]; } return ret; } bool AdjExchangeLayer(int i) { this.ProgressStep(); int[] layer = layers[i]; bool gain = ExchangeWithGainWithNoDisturbance(layer); if (gain) return true; DisturbLayer(layer); return ExchangeWithGainWithNoDisturbance(layer); } bool AdjExchangeLayerWithBalance(int i) { this.ProgressStep(); int[] layer = layers[i]; bool gain = ExchangeWithGainWithNoDisturbanceWithBalance(layer); if (gain) return true; DisturbLayerWithBalance(layer); return ExchangeWithGainWithNoDisturbanceWithBalance(layer); } //in this routine u and v are adjacent, and u is to the left of v before the swap void Swap(int u, int v) { int left = X[u]; int right = X[v]; int ln = layering[u]; //layer number int[] layer = layers[ln]; layer[left] = v; layer[right] = u; X[u] = right; X[v] = left; //update sorted arrays POrders and SOrders //an array should be updated only in case it contains both u and v. // More than that, v has to follow u in an the array. UpdateSsContainingUv(u, v); UpdatePsContainingUv(u, v); } void UpdatePsContainingUv(int u, int v) { if (successors[u].Length <= successors[v].Length) foreach (int a in successors[u]) { Dictionary porder = pOrder[a]; //of course porder contains u, let us see if it contains v if (porder.ContainsKey(v)) { int vOffset = porder[v]; //swap u and v in the array P[coeff] int[] p = predecessors[a]; p[vOffset - 1] = v; p[vOffset] = u; //update sorder itself porder[v] = vOffset - 1; porder[u] = vOffset; } } else foreach (int a in successors[v]) { Dictionary porder = pOrder[a]; //of course porder contains u, let us see if it contains v if (porder.ContainsKey(u)) { int vOffset = porder[v]; //swap u and v in the array P[coeff] int[] p = predecessors[a]; p[vOffset - 1] = v; p[vOffset] = u; //update sorder itself porder[v] = vOffset - 1; porder[u] = vOffset; } } } void UpdateSsContainingUv(int u, int v) { if (predecessors[u].Length <= predecessors[v].Length) foreach (int a in predecessors[u]) { Dictionary sorder = sOrder[a]; //of course sorder contains u, let us see if it contains v if (sorder.ContainsKey(v)) { int vOffset = sorder[v]; //swap u and v in the array S[coeff] int[] s = successors[a]; s[vOffset - 1] = v; s[vOffset] = u; //update sorder itself sorder[v] = vOffset - 1; sorder[u] = vOffset; } } else foreach (int a in predecessors[v]) { Dictionary sorder = sOrder[a]; //of course sorder contains u, let us see if it contains v if (sorder.ContainsKey(u)) { int vOffset = sorder[v]; //swap u and v in the array S[coeff] int[] s = successors[a]; s[vOffset - 1] = v; s[vOffset] = u; //update sorder itself sorder[v] = vOffset - 1; sorder[u] = vOffset; } } } void DisturbLayer(int[] layer) { for (int i = 0; i < layer.Length - 1; i++) AdjacentSwapToTheRight(layer, i); } void DisturbLayerWithBalance(int[] layer) { for (int i = 0; i < layer.Length - 1; i++) AdjacentSwapToTheRightWithBalance(layer, i); } bool ExchangeWithGainWithNoDisturbance(int[] layer) { bool wasGain = false; bool gain; do { gain = ExchangeWithGain(layer); wasGain = wasGain || gain; } while (gain); return wasGain; } bool ExchangeWithGainWithNoDisturbanceWithBalance(int[] layer) { bool wasGain = false; bool gain; do { gain = ExchangeWithGainWithBalance(layer); wasGain = wasGain || gain; } while (gain); return wasGain; } bool ExchangeWithGain(int[] layer) { //find a first pair giving some gain for (int i = 0; i < layer.Length - 1; i++) if (SwapWithGain(layer[i], layer[i + 1])) { SwapToTheLeft(layer, i); SwapToTheRight(layer, i + 1); return true; } return false; } bool ExchangeWithGainWithBalance(int[] layer) { //find a first pair giving some gain for (int i = 0; i < layer.Length - 1; i++) if (SwapWithGainWithBalance(layer[i], layer[i + 1])) { SwapToTheLeftWithBalance(layer, i); SwapToTheRightWithBalance(layer, i + 1); return true; } return false; } void SwapToTheLeft(int[] layer, int i) { for (int j = i - 1; j >= 0; j--) AdjacentSwapToTheRight(layer, j); } void SwapToTheRight(int[] layer, int i) { for (int j = i; j < layer.Length - 1; j++) AdjacentSwapToTheRight(layer, j); } void SwapToTheLeftWithBalance(int[] layer, int i) { for (int j = i - 1; j >= 0; j--) AdjacentSwapToTheRightWithBalance(layer, j); } void SwapToTheRightWithBalance(int[] layer, int i) { for (int j = i; j < layer.Length - 1; j++) AdjacentSwapToTheRightWithBalance(layer, j); } /// /// swaps i-th element with i+1 /// /// the layer to work on /// the position to start void AdjacentSwapToTheRight(int[] layer, int i) { int u = layer[i], v = layer[i + 1]; int gain = SwapGain(u, v); if (gain > 0 || (gain == 0 && HeadOfTheCoin())) Swap(u, v); } void AdjacentSwapToTheRightWithBalance(int[] layer, int i) { int u = layer[i], v = layer[i + 1]; int gain = SwapGainWithBalance(u, v); if (gain > 0 || (gain == 0 && HeadOfTheCoin())) Swap(u, v); } int SwapGain(int u, int v) { int cuv; int cvu; CalcPair(u, v, out cuv, out cvu); return cuv - cvu; } int SwapGainWithBalance(int u, int v) { int cuv; int cvu; CalcPair(u, v, out cuv, out cvu); int gain = cuv - cvu; if (gain != 0 && UvAreOfSameKind(u, v)) return gain; //maybe we gain something in the group sizes return SwapGroupGain(u, v); } bool UvAreOfSameKind(int u, int v) { return u < startOfVirtNodes && v < startOfVirtNodes || u >= startOfVirtNodes && v >= startOfVirtNodes; } int SwapGroupGain(int u, int v) { int layerIndex = layerArrays.Y[u]; int[] layer = layers[layerIndex]; if (NeighborsForbidTheSwap(u, v)) return -1; int uPosition = X[u]; bool uIsSeparator; if (IsOriginal(u)) uIsSeparator = optimalOriginalGroupSize[layerIndex] == 1; else uIsSeparator = optimalVirtualGroupSize[layerIndex] == 1; int delta = CalcDeltaBetweenGroupsToTheLeftAndToTheRightOfTheSeparator(layer, uIsSeparator ? uPosition : uPosition + 1, uIsSeparator ? u : v); if (uIsSeparator) { if (delta < -1) return 1; if (delta == -1) return 0; return -1; } if (delta > 1) return 1; if (delta == 1) return 0; return -1; } bool NeighborsForbidTheSwap(int u, int v) { return UpperNeighborsForbidTheSwap(u, v) || LowerNeighborsForbidTheSwap(u, v); } bool LowerNeighborsForbidTheSwap(int u, int v) { int uCount, vCount; if (((uCount = properLayeredGraph.OutEdgesCount(u)) == 0) || ((vCount = properLayeredGraph.OutEdgesCount(v)) == 0)) return false; return X[successors[u][uCount >> 1]] < X[successors[v][vCount >> 1]]; } bool UpperNeighborsForbidTheSwap(int u, int v) { int uCount = properLayeredGraph.InEdgesCount(u); int vCount = properLayeredGraph.InEdgesCount(v); if (uCount == 0 || vCount == 0) return false; return X[predecessors[u][uCount >> 1]] < X[predecessors[v][vCount >> 1]]; } int CalcDeltaBetweenGroupsToTheLeftAndToTheRightOfTheSeparator(int[] layer, int separatorPosition, int separator) { Func kind = GetKindDelegate(separator); int leftGroupSize = 0; for (int i = separatorPosition - 1; i >= 0 && !kind(layer[i]); i--) leftGroupSize++; int rightGroupSize = 0; for (int i = separatorPosition + 1; i < layer.Length && !kind(layer[i]); i++) rightGroupSize++; return leftGroupSize - rightGroupSize; } bool IsOriginal(int v) { return v < startOfVirtNodes; } bool IsVirtual(int v) { return v >= startOfVirtNodes; } Func GetKindDelegate(int v) { Func kind = IsVirtual(v) ? IsVirtual : new Func(IsOriginal); return kind; } ///// ///// swaps two vertices only if reduces the number of intersections ///// ///// the layer to work on ///// left vertex ///// right vertex ///// bool SwapWithGain(int u, int v) { int gain = SwapGain(u, v); if (gain > 0) { Swap(u, v); return true; } return false; } bool SwapWithGainWithBalance(int u, int v) { int gain = SwapGainWithBalance(u, v); if (gain > 0) { Swap(u, v); return true; } return false; } } }