/// <summary> /// adds a ptdur to the sieve /// </summary> /// <param name="ptdur">the ptdur to add</param> /// <returns>the leaf that the ptdur is stored in</returns> private Leaf Add(PTD ptdur) { int margin = k + 1 - (int)ptdur.Bag.Count(); int sieveIndex = margin == 0 ? 0 : ((int)Math.Ceiling(Math.Log(margin, 2)) + 1); return(blockSieves[sieveIndex].Add(ptdur)); }
public static bool IsPossiblyUsable(List <PTD> children, ImmutableGraph graph) { for (int i = 0; i < children.Count; i++) { PTD child1 = children[i]; for (int j = i + 1; j < children.Count; j++) { PTD child2 = children[j]; BitSet childrenInletsIntersection = new BitSet(child1.inlet); childrenInletsIntersection.IntersectWith(child2.inlet); if (!childrenInletsIntersection.IsEmpty()) { return(false); } BitSet verticesIntersection = new BitSet(child1.vertices); verticesIntersection.IntersectWith(child2.vertices); if (!child1.outlet.IsSupersetOf(verticesIntersection) || !child2.outlet.IsSupersetOf(verticesIntersection)) { return(false); } } } return(true); }
/// <summary> /// Replaces a ptdur with another equivalent one which has a smaller root bag. The replacement is not done immediately, /// but needs to be initiated by the FlushAdd method. /// </summary> /// <param name="oldPtdur">the ptdur to replace</param> /// <param name="newPtdur">the new ptdur</param> public void Replace(PTD oldPtdur, PTD newPtdur) { if (ptdurToLeafMapping.TryGetValue(oldPtdur, out Leaf leaf)) { int oldMargin = k + 1 - (int)oldPtdur.Bag.Count(); int newMargin = k + 1 - (int)newPtdur.Bag.Count(); int oldSieveIndex = oldMargin == 0 ? 0 : ((int)Math.Ceiling(Math.Log(oldMargin, 2)) + 1); int newSieveIndex = newMargin == 0 ? 0 : ((int)Math.Ceiling(Math.Log(newMargin, 2)) + 1); if (oldSieveIndex == newSieveIndex) { leaf.ptdur = newPtdur; ptdurToLeafMapping.Remove(oldPtdur); ptdurToLeafMapping.Add(newPtdur, leaf); } else { leaf.Remove(); ptdurToLeafMapping.Remove(oldPtdur); leaf = Add(newPtdur); ptdurToLeafMapping.Add(newPtdur, leaf); } } else { int index = ptdurToDeferredAdditionIndexMapping[oldPtdur]; ptdurToDeferredAdditionIndexMapping.Add(newPtdur, index); toAdd[index] = newPtdur; } }
//TODO: perhaps maintain the sets of components associated with the outlet #region constructors /// <summary> /// copy constructor /// </summary> /// <param name="ptd">the ptd to copy</param> public PTD(PTD ptd) { Bag = new BitSet(ptd.Bag); vertices = new BitSet(ptd.vertices); inlet = new BitSet(ptd.inlet); outlet = new BitSet(ptd.outlet); children = new List <PTD>(ptd.children); }
/// <summary> /// makes a deep copy of a given ptd /// </summary> /// <param name="original">the ptd to copy</param> /// <returns>a deep copy of that ptd</returns> private static PTD DeepCopy(PTD original) { PTD result = new PTD(original.Bag); for (int i = 0; i < original.children.Count; i++) { result.children.Add(DeepCopy(original.children[i])); } return(result); }
internal void AssertConsistency(int vertexCount) { // create a list of all bags List <BitSet> bagsList = new List <BitSet>(); List <int> parentBags = new List <int>(); Stack <PTD> childrenStack = new Stack <PTD>(); Stack <int> parentStack = new Stack <int>(); childrenStack.Push(this); parentStack.Push(-1); while (childrenStack.Count > 0) { PTD current = childrenStack.Pop(); int parent = parentStack.Pop(); bagsList.Add(current.Bag); parentBags.Add(parent); foreach (PTD child in current.children) { childrenStack.Push(child); parentStack.Push(bagsList.Count - 1); } } // check consistency for (int i = 0; i < vertexCount; i++) { /* * key insight: all bags containing i form a subtree. * Therefore, in order for the tree decomposition to be consistent, there must be only one root for all subtrees containing i */ HashSet <int> ancestors = new HashSet <int>(); for (int j = 0; j < bagsList.Count; j++) { if (bagsList[j][i] == true) { int currentAncestor = j; int parentBag = parentBags[j]; while (parentBag != -1 && bagsList[parentBag][i]) { currentAncestor = parentBag; parentBag = parentBags[currentAncestor]; ; } ancestors.Add(currentAncestor); if (ancestors.Count == 2) { Print(); Trace.Fail(String.Format("The printed ptd is not consistent. There are at least two subtrees containing vertex {0}.", i.ToString())); } } } } }
/// <summary> /// queries this sieve to find all candidate ptdurs that a ptd might be able to be attached to /// </summary> /// <param name="ptd">the ptd to find candidate parents for</param> /// <returns>an enumerable of candidate ptdurs that the ptd might be able to be attached to</returns> public IEnumerable <PTD> EligiblePTDURs(PTD ptd, PTD ptdurVersionOfPTD) { yield return(ptdurVersionOfPTD); for (int i = 0; i < blockSieves.Length; i++) { foreach (PTD ptdur in blockSieves[i].GetCandidatePTDURs(ptd)) { yield return(ptdur); } } }
public static PTD CreatePTDURFromPTD(PTD Tau) { BitSet bag = new BitSet(Tau.outlet); BitSet outlet = new BitSet(Tau.outlet); BitSet inlet = new BitSet(Tau.inlet); BitSet vertices = new BitSet(Tau.vertices); List <PTD> children = new List <PTD>(); children.Add(Tau); return(new PTD(bag, vertices, outlet, inlet, children)); }
public static void Reroot(ref PTD ptd, BitSet rootSet) { if (ptd.Bag.IsSupersetOf(rootSet)) { return; } Dictionary <PTD, PTD> parents = new Dictionary <PTD, PTD>(); parents[ptd] = null; // find root node (its bag is a superset of the root set) PTD rootNode = null; Stack <PTD> nodeStack = new Stack <PTD>(); nodeStack.Push(ptd); PTD currentNode; while (nodeStack.Count > 0) { currentNode = nodeStack.Pop(); for (int i = 0; i < currentNode.children.Count; i++) { PTD child = currentNode.children[i]; parents[child] = currentNode; if (child.Bag.IsSupersetOf(rootSet)) { rootNode = child; break; } nodeStack.Push(child); } } Debug.Assert(rootNode != null); // reroot (swap parent-child relationship between all nodes that lie between the former root and the future root) currentNode = rootNode; PTD formerParentNode; while ((formerParentNode = parents[currentNode]) != null) { currentNode.children.Add(formerParentNode); formerParentNode.children.Remove(currentNode); currentNode = formerParentNode; } ptd = rootNode; }
/// <summary> /// adds a ptdur to this sieve /// </summary> /// <param name="ptdur">the ptdur to add</param> /// <param name="forceAddition">forces the addition of the ptdur without testing if a ptdur already exists that is 'better' in terms of optional components</param> /// <returns>the leaf that this ptdur is stored in</returns> public Leaf Add(PTD ptdur, bool forceAddition = false) { SieveNode currentNode = startNode; // traverse the tree do { InnerNode currentInnerNode = currentNode as InnerNode; // if it exists, then find the child with the matching interval BitSet and mark it as the current node bool currentNodeHasMatchingChild = false; for (int i = 0; i < currentInnerNode.children.Count; i++) { (BitSet childSet, SieveNode sieveNode) = currentInnerNode.children[i]; if (ptdur.vertices.EqualsOnInterval(childSet, currentInnerNode.intervalFrom, currentInnerNode.intervalTo)) { currentNode = sieveNode; currentNodeHasMatchingChild = true; break; } } // if such a child does not exist, make one if (!currentNodeHasMatchingChild) { // add new inner node that covers the rest of the interval if the current node doesn't cover it fully if (currentInnerNode.intervalTo < vertexCount) { InnerNode newNode = new InnerNode(currentInnerNode.intervalTo, vertexCount, currentInnerNode); BitSet intervalBitSet = ptdur.vertices; currentInnerNode.AddChild(intervalBitSet, newNode); currentInnerNode = newNode; } Debug.Assert(currentInnerNode.intervalTo == vertexCount); // add leaf Leaf newLeaf = new Leaf(ptdur, currentInnerNode); currentInnerNode.AddChild(ptdur.vertices, newLeaf); currentNode = newLeaf; return(newLeaf); } }while (!currentNode.isLeaf); // test if a ptdur exists in this leaf that is 'better' than the one to add or vice versa Leaf currentNodeAsLeaf = currentNode as Leaf; currentNodeAsLeaf.ptdur = ptdur; return(currentNodeAsLeaf); }
public static PTD ExtendToPMC_Rule2(PTD Tau_wiggle, BitSet vNeighbors, ImmutableGraph graph) { BitSet bag = new BitSet(vNeighbors); List <PTD> children = new List <PTD>(Tau_wiggle.children); BitSet vertices = new BitSet(Tau_wiggle.vertices); vertices.UnionWith(vNeighbors); BitSet outlet = graph.Outlet(bag, vertices); BitSet inlet = new BitSet(vertices); inlet.ExceptWith(outlet); return(new PTD(bag, vertices, outlet, inlet, children)); }
public static PTD ExtendToPMC_Rule3(PTD Tau_wiggle, BitSet newRoot, ImmutableGraph graph) { Debug.Assert(newRoot.IsSupersetOf(Tau_wiggle.Bag)); BitSet bag = newRoot; BitSet vertices = new BitSet(Tau_wiggle.vertices); vertices.UnionWith(newRoot); List <PTD> children = new List <PTD>(Tau_wiggle.children); BitSet outlet = graph.Outlet(bag, vertices); BitSet inlet = new BitSet(vertices); inlet.ExceptWith(outlet); return(new PTD(bag, vertices, outlet, inlet, children)); }
/// <summary> /// queries this sieve to find all candidate ptdurs that a ptd might be able to be attached to /// </summary> /// <param name="ptd">the ptd to find candidate parents for</param> /// <returns>an enumerable of candidate ptdurs that the ptd might be able to be attached to</returns> public IEnumerable <PTD> GetCandidatePTDURs(PTD ptd) { Stack <(InnerNode, int)> nodeStack = new Stack <(InnerNode, int)>(); nodeStack.Push((startNode, 0)); // perform depth-first search while (nodeStack.Count > 0) { // get the current node and the current i-value for that node (InnerNode currentNode, int currentI) = nodeStack.Pop(); // process all children of current node for (int j = 0; j < currentNode.children.Count; j++) { (BitSet intervalBitSet, SieveNode child) = currentNode.children[j]; // test if child is a candidate vertex-wise if (ptd.inlet.IsDisjointOnInterval(intervalBitSet, currentNode.intervalFrom, currentNode.intervalTo)) { // test if margin is large enough. If not, prune this child by doing nothing int nextI = currentI + (int)ptd.outlet.CountOnIntervalExcept(intervalBitSet, currentNode.intervalFrom, currentNode.intervalTo); if (nextI <= margin) { // return ptdur if the child is a leaf if (child.isLeaf) { Leaf leaf = child as Leaf; Debug.Assert(child != null); yield return(leaf.ptdur); } // put the child on the stack if it is not a leaf else { InnerNode nextNode = child as InnerNode; Debug.Assert(nextNode != null); nodeStack.Push((nextNode, nextI)); } } } } } }
/// <summary> /// reindexes the vertices of this ptd that is a ptd of a reduced graph, so that they correctly represent the same vertices in the non-reduced graph. /// </summary> /// <param name="reindexationMapping">the mapping from the vertex indices in the current ptd to their original vertex indices within the original graph.</param> public void Reindex(ReindexationMapping reindexationMapping) { // initialize a stack of nodes Stack <PTD> nodeStack = new Stack <PTD>(); nodeStack.Push(this); // re-index all bags with the vertices they had before reduction while (nodeStack.Count > 0) { PTD currentNode = nodeStack.Pop(); BitSet reducedBag = currentNode.Bag; BitSet reconstructedBag = reindexationMapping.Reindex(reducedBag); currentNode.SetBag(reconstructedBag); // push children onto stack for (int i = 0; i < currentNode.children.Count; i++) { nodeStack.Push(currentNode.children[i]); } } }
/// <summary> /// computes the outlet of the union of a ptd with a component /// </summary> /// <param name="ptd">the ptd</param> /// <param name="component">the component</param> /// <returns>the vertices in the outlet of ptd that are adjacent to vertices that are neither contained in the ptd nor in the component</returns> public BitSet UnionOutlet(PTD ptd, BitSet component) { BitSet unionOutlet = new BitSet(vertexCount); BitSet unionVertices = new BitSet(ptd.vertices); unionVertices.UnionWith(component); List <int> outletVertices = ptd.outlet.Elements(); for (int i = 0; i < outletVertices.Count; i++) { int u = outletVertices[i]; for (int j = 0; j < adjacencyList[u].Length; j++) { int v = adjacencyList[u][j]; if (!unionVertices[v]) { unionOutlet[u] = true; break; } } } return(unionOutlet); }
public static bool AddPTDToPTDUR_CheckBagSize_CheckPossiblyUsable_CheckCliquish(PTD Tau_prime, PTD Tau, ImmutableGraph graph, int k, out PTD result, Graph mutableGraph) { // return early if bag would get too big uint futureBagSize = BitSet.CountUnion(Tau_prime.Bag, Tau.outlet); if (futureBagSize > k + 1) { result = null; return(false); } BitSet bag = new BitSet(Tau_prime.Bag); bag.UnionWith(Tau.outlet); return(AddPTDToPTDUR_CheckPossiblyUsable_CheckCliquish_Helper(Tau_prime, Tau, graph, out result, bag, futureBagSize, k, mutableGraph)); }
/// <summary> /// Marks a ptdur for later addition to the sieve. The eventual addition needs to be initiated by calling the FlushAdd method. /// The reason for adding the ptdur later is that, during a query of a block sieve, a potential splitting of a node in that sieve /// will confuse the query method. Also in our case the later addition does not cause problems because the ptdur is always a child /// of the currently queried ptd and, thus, combining them will always lead to a ptdur that is not possibly usable. /// </summary> /// <param name="ptdur">the ptdur to add</param> public void DeferredAdd(PTD ptdur) { ptdurToDeferredAdditionIndexMapping.Add(ptdur, toAdd.Count); toAdd.Add(ptdur); }
/// <summary> /// a method extracted from the method above due to performance reasons. Read it as if the body of the method above would just continue here. /// </summary> /// <param name="Tau_prime">the ptdur</param> /// <param name="Tau">the ptd</param> /// <param name="graph">the underlying graph</param> /// <param name="result">the resulting ptd, or null if the return value is false</param> /// <param name="bag">the bag of the ptd to be</param> /// <returns>true, iff the resulting ptd is possibly usable</returns> private static bool AddPTDToPTDUR_CheckPossiblyUsable_CheckCliquish_Helper(PTD Tau_prime, PTD Tau, ImmutableGraph graph, out PTD result, BitSet bag, uint futureBagSize, int k, Graph mutableGraph) { List <PTD> children = new List <PTD>(Tau_prime.children); children.Add(Tau); // exit early if not possibly usable if (!IsPossiblyUsable(children, graph)) { result = null; return(false); } // if no vertices can be added to the bag due to size and the bag is not a pmc, we can reject this ptd immediately because it is not useful if (futureBagSize == k + 1 && !graph.IsPotMaxClique(bag)) { result = null; return(false); } if (!graph.IsCliquish(bag)) { result = null; return(false); } // if only one vertex can be added, determine all the candidates that would make this bag a pmc when added. If there are none, return. if (testIfAddingOneVertexToBagFormsPMC && futureBagSize == k) { // if bag is pmc already, we need this ptdur. (In that case no candidate exists which could be added.) if (!graph.IsPotMaxClique(bag)) { bool useless = true; foreach ((BitSet component, BitSet neighbor) in graph.ComponentsAndNeighbors(bag)) { // candidates are only found in full components if (neighbor.Equals(bag) && !component.Intersects(Tau_prime.vertices) && !component.Intersects(Tau.vertices)) { if (neighborsFirst) { onlyNeighborStopwatch.Start(); // test if a vertex in the bag has exactly one neighbor in this component, and the bag plus that vertex is still cliquish foreach (int v in bag.Elements()) { BitSet neighbors = new BitSet(graph.openNeighborhood[v]); neighbors.IntersectWith(component); if (neighbors.Count() == 1) { int candidate = neighbors.First(); bag[candidate] = true; if (graph.IsCliquish(bag)) // in this case it is guaranteed that the bag is also a pmc { Debug.Assert(graph.IsPotMaxClique(bag)); bag[candidate] = false; useless = false; break; } bag[candidate] = false; } } onlyNeighborStopwatch.Stop(); if (!useless) { break; } } articulationPointCandidatesStopwatch.Start(); // find articulation points within this component foreach (int articulationPoint in SafeSeparator.ArticulationPoints(mutableGraph, component)) { bag[articulationPoint] = true; if (graph.IsPotMaxClique(bag)) { bag[articulationPoint] = false; useless = false; break; } bag[articulationPoint] = false; } articulationPointCandidatesStopwatch.Stop(); if (!useless) { break; } if (!neighborsFirst) { onlyNeighborStopwatch.Start(); // test if a vertex in the bag has exactly one neighbor in this component, and the bag plus that vertex is still cliquish foreach (int v in bag.Elements()) { BitSet neighbors = new BitSet(graph.openNeighborhood[v]); neighbors.IntersectWith(component); if (neighbors.Count() == 1) { int candidate = neighbors.First(); bag[candidate] = true; if (graph.IsCliquish(bag)) // in this case it is guaranteed that the bag is also a pmc { Debug.Assert(graph.IsPotMaxClique(bag)); bag[candidate] = false; useless = false; break; } bag[candidate] = false; } } onlyNeighborStopwatch.Stop(); if (!useless) { break; } } // TODO: possibly exclude candidates that are in this ptdur's inlet? Is that correct? } } if (useless) { result = null; return(false); } } } // usability is established, so we build the ptd BitSet vertices = new BitSet(Tau_prime.vertices); vertices.UnionWith(Tau.vertices); BitSet outlet = graph.Outlet(bag, vertices); BitSet inlet = new BitSet(vertices); inlet.ExceptWith(outlet); result = new PTD(new BitSet(bag), vertices, outlet, inlet, children); return(true); }
/// <summary> /// tests whether this and the other PTD are PTDs that contain the same vertices within them /// </summary> /// <param name="other">the other PTD</param> /// <returns>true iff the PTDs contain the same vertices</returns> public bool Equivalent(PTD other) { return(inlet.Equals(other.inlet)); }
/// <summary> /// asserts that this ptd is actually a tree decomposition for the given graph /// </summary> /// <param name="graph">the graph that this ptd is supposed to be a tree decomposition of</param> public void AssertValidTreeDecomposition(ImmutableGraph graph) { // create a list of all bags List <BitSet> bagsList = new List <BitSet>(); List <int> parentBags = new List <int>(); Stack <PTD> childrenStack = new Stack <PTD>(); Stack <int> parentStack = new Stack <int>(); childrenStack.Push(this); parentStack.Push(-1); while (childrenStack.Count > 0) { PTD current = childrenStack.Pop(); int parent = parentStack.Pop(); bagsList.Add(current.Bag); parentBags.Add(parent); foreach (PTD child in current.children) { childrenStack.Push(child); parentStack.Push(bagsList.Count - 1); } } // check vertex cover for (int i = 0; i < graph.vertexCount; i++) { bool isCovered = false; foreach (BitSet bag in bagsList) { if (bag[i]) { isCovered = true; break; } } if (!isCovered) { Print(); Trace.Fail(String.Format("The printed ptd for graph {0} does not cover all of the graph's vertices. Vertex {1} is not covered.", graph.graphID, i)); } } // check edge cover for (int u = 0; u < graph.vertexCount; u++) { foreach (int v in graph.adjacencyList[u]) { bool isCovered = false; foreach (BitSet bag in bagsList) { if (bag[u] && bag[v]) { isCovered = true; break; } } if (!isCovered) { Print(); Trace.Fail(String.Format("The printed ptd for graph {0} does not cover all of the graph's edges. Edge ({1},{2}) is not covered.", graph.graphID, u, v)); } } } // check consistency for (int i = 0; i < graph.vertexCount; i++) { //if (possiblyUsableIgnore[i]) //{ // continue; //} /* * key insight: all bags containing i form a subtree. * Therefore, in order for the tree decomposition to be consistent, there must be only one root for all subtrees containing i */ HashSet <int> ancestors = new HashSet <int>(); for (int j = 0; j < bagsList.Count; j++) { if (bagsList[j][i] == true) { int currentAncestor = j; int parentBag = parentBags[j]; while (parentBag != -1 && bagsList[parentBag][i]) { currentAncestor = parentBag; parentBag = parentBags[currentAncestor]; ; } ancestors.Add(currentAncestor); if (ancestors.Count == 2) { Print(); Trace.Fail(String.Format("The printed ptd for graph {0} is not consistent. There are at least two subtrees containing vertex {1}.", graph.graphID, i)); } } } } }
/// <summary> /// constructs a leaf initialized with a given ptdur and a reference to the leaf's parent within the sieve /// </summary> /// <param name="ptdur">the initial ptdur at this leaf</param> /// <param name="parent">the leaf's parent</param> public Leaf(PTD ptdur, InnerNode parent) { isLeaf = true; this.ptdur = ptdur; this.parent = parent; }
/// <summary> /// imports a PTD from a .td file /// </summary> /// <param name="filepath">the path to that file</param> public static PTD ImportPTD(string filepath) { PTD[] nodesList = null; bool[] isChild = null; try { using (StreamReader sr = new StreamReader(filepath)) { int vertexCount = -1; while (!sr.EndOfStream) { String line = sr.ReadLine(); if (line.StartsWith("c")) { continue; } else { String[] tokens = line.Split(' '); if (tokens[0] == "s") { int nodesCount = Convert.ToInt32(tokens[2]); nodesList = new PTD[nodesCount]; isChild = new bool[nodesCount]; vertexCount = Convert.ToInt32(tokens[4]); // everything else not really relevant for our purposes here continue; } else if (tokens[0] == "b") { int nodePosition = Convert.ToInt32(tokens[1]) - 1; BitSet bag = new BitSet(vertexCount); for (int i = 2; i < tokens.Length; i++) { bag[Convert.ToInt32(tokens[i]) - 1] = true; } nodesList[nodePosition] = new PTD(bag); } else { int from = Convert.ToInt32(tokens[0]) - 1; int to = Convert.ToInt32(tokens[1]) - 1; nodesList[from].children.Add(nodesList[to]); isChild[to] = true; } } } } } catch (IOException e) { Console.WriteLine("The file could not be read:"); Console.WriteLine(e.Message); } // find root for (int i = 0; i < nodesList.Length; i++) { if (!isChild[i]) { return(nodesList[i]); } } throw new Exception("The imported tree decomposition is not a tree"); }