void OrientedGraph::levelPartition(Partition& pi) const /* Assuming the graph has no oriented cycles, this function writes in pi the partition of the vertices according to their level, where sinks have level 0, then sinks in the remaining poset have level one, etc. NOTE : the implementation is simple-minded : we traverse the graph as many times as there are levels. */ { static BitMap b(0); static BitMap b1(0); b.setSize(size()); b.reset(); b1.setSize(size()); b1.reset(); pi.setSize(size()); Ulong count = 0; Ulong current_level = 0; while (count < size()) { for (SetElt x = 0; x < size(); ++x) { if (b.getBit(x)) continue; const EdgeList e = d_edge[x]; for (Ulong j = 0; j < e.size(); ++j) { if (!b.getBit(e[j])) /* next x */ goto nextx; } /* i we get here, x is the next element in the permutation */ pi[x] = current_level; b1.setBit(x); ++count; nextx: continue; } b.assign(b1); current_level++; } pi.setClassCount(current_level); return; }
void OrientedGraph::permute(const Permutation& a) /* This function permutes the graph according to the permutation a, according to the usual rule : the edges of a(x) should be the image under a of the edge set of x. As usual, permuting values is easy : it is enough to apply a to the elements in the various edgelists. Permuting ranges is trickier, because it involves a^-1. It is assumed of course that a holds a permutation of size size(). */ { static BitMap b(0); static EdgeList e_buf(0); /* permute values */ for (SetElt x = 0; x < size(); ++x) { EdgeList& e = d_edge[x]; for (Ulong j = 0; j < e.size(); ++j) { e[j] = a[e[j]]; } } /* permute ranges */ b.setSize(size()); b.reset(); for (SetElt x = 0; x < size(); ++x) { if (b.getBit(x)) continue; if (a[x] == x) { /* fixed point */ b.setBit(x); continue; } for (SetElt y = a[x]; y != x; y = a[y]) { /* back up values for y */ e_buf.shallowCopy(d_edge[y]); /* put values for x in y */ d_edge[y].shallowCopy(d_edge[x]); /* store backup values in x */ d_edge[x].shallowCopy(e_buf); /* set bit */ b.setBit(y); } b.setBit(x); } }
void OrientedGraph::cells(Partition& pi, OrientedGraph* P) const /* Define a preorder relation on the vertices by setting x <= y iff there is an oriented path from x to y. This function puts in pi the partition function corresponding to the equivalence classes of this preorder. We use the Tarjan algorithm, explained in one of the Knuth books, but which I learned from Bill Casselman. The vertices for which the partition function is already defined will be said to be dealt with. These are marked off in an auxiliary bitmap. The algorithm goes as follows. Start with the first vertex that has not been dealt with, say x0. We will have to deal (potentially) with the set of all vertices visible from x0 (i.e >= x0). There will be a stack of vertices, corresponding to a path originating from x0, such that at each point in time, each vertex y >= x0 which is not dealt with will be equivalent to an element of the stack; the least such (in the stack ordering) will be called y_min, so that for instance x0_min = 0. We record these values in an additional table, initialized to some value undefined. Now let x be at the top of the stack. Look at the edges originating from x. Ignore the ones that go to vertices which are dealt with. If there are no edges left, x is minimal and is a class by itself; we can take it off, mark it as dealt with, and continue. Otherwise, run through the edges one by one. Let the edge be x->y. If y_min is defined, this means that y has already been examined, and is not dealt with. But each such element is equivalent to an element in the active stack, so y_min should be one of the elements in the active stack, hence x is visible from y_min: in other words, x and y are equivalent, and we set x_min = y_min if y_min < x_min. Otherwise, y is seen for the first time; then we just put it on the stack. When we are done with the edges of x, we have now the value of x_min which is the inf over the edges originating from x of the y_min. If this value is equal to the stack-position of x, we see that x is minimal in its class, and we get a new class by taking all the successors of x not already dealt with. We then move to the parent of x and continue the process there. */ { static Permutation a(0); static BitMap b(0); static List<Vertex> v(1); static List<const EdgeList*> elist(1); static List<Ulong> ecount(1); static List<Ulong> min(0); pi.setSize(size()); pi.setClassCount(0); b.setSize(size()); b.reset(); min.setSize(size()); min.setZero(); for (Vertex x = 0; x < size(); ++x) min[x] = size(); for (Vertex x = 0; x < size(); ++x) { if (b.getBit(x)) /* x is dealt with */ continue; v[0] = x; v.setSize(1); elist[0] = &edge(x); elist.setSize(1); ecount[0] = 0; ecount.setSize(1); min[x] = 0; Ulong t = 1; while(t) { Vertex y = v[t-1]; Vertex z; const EdgeList& e = *elist[t-1]; for (; ecount[t-1] < e.size(); ++ecount[t-1]) { z = e[ecount[t-1]]; if (b.getBit(z)) continue; if (min[z] == size()) /* z is new */ goto add_path; if (min[y] > min[z]) min[y] = min[z]; } /* at this point we have exhausted the edges of y */ if (min[y] == t-1) { /* take off class */ getClass(*this,y,b,pi,P); } else if (min[y] < min[v[t-2]]) /* if t=1, previous case holds */ min[v[t-2]] = min[y]; t--; continue; add_path: v.setSize(t+1); elist.setSize(t+1); ecount.setSize(t+1); v[t] = z; elist[t] = &edge(z); ecount[t] = 0; min[z] = t; t++; } } return; }