public DominatingInducedMatching(int n) : base(n) { Sigma = new BitSet(0, MaxSize) { 1 }; Rho = new BitSet(0, MaxSize) { 0 }; Rho = !Rho; }
public IndependentDominatingSet(int n) : base(n) { Sigma = new BitSet(0, MaxSize) { 0 }; Rho = new BitSet(0, MaxSize) { 0 }; Rho = !Rho; }
// Uses depth-first search to check if the graph induced by the subgraph given as a parameter is connected // In other words, we retreive all edges from the original graph and check the subgraph for connectedness public static bool Connected(Graph graph, BitSet subgraph) { // Vertices that are visited Set<int> visited = new Set<int>(); // Stack of vertices yet to visit Stack<int> stack = new Stack<int>(); // Initial vertex int s = subgraph.First(); stack.Push(s); // Continue while there are vertices on the stack while (stack.Count > 0) { int v = stack.Pop(); // If we have not encountered this vertex before, then we check for all neighbors if they are part of the subgraph // If a neighbor is part of the subgraph it means that we have to push it on the stack to explore it at a later stage if (!visited.Contains(v)) { visited.Add(v); foreach (int w in graph.OpenNeighborhood(v)) if (subgraph.Contains(w)) stack.Push(w); } } // If we visited an equal number of vertices as there are vertices in the subgraph then the subgraph is connected return visited.Count == subgraph.Count; }
public DominatingSet(int n) : base(n) { Sigma = new BitSet(0, MaxSize); Sigma = !Sigma; Rho = new BitSet(0, MaxSize) { 0 }; Rho = !Rho; }
// Basic constructor for a dNeighborhood public dNeighborhood(BitSet vector) { Occurrences = new Dictionary<int, int>(); Vector = vector; foreach (int v in Vector) Occurrences[v] = 0; }
// Counts the number of independent sets in a graph, such that: // - The vertices in P are legal choices for our IS, initially set to all vertices in the graph // - None of the vertices in X are used, initially empty // The performDFS boolean is used to check if we should perform a check for connectedness on this level of the recursion private static long Compute(Graph graph, BitSet P, BitSet X, bool performDFS) { // Base case, when P and X are both empty we cannot expand the IS if (P.IsEmpty && X.IsEmpty) return 1; // Base case, if a vertex w in X has no neighbor in P, then it means that this IS will never get maximal // since we could always include w. Thus, the IS will not be valid and we return 0. foreach (int w in X) if ((graph.OpenNeighborhood(w) * P).IsEmpty) return 0; long count = 0; // If a DFS is needed we check if the graph induced by (P + X) is still connected. // If the graph is disconnected, in components c1,...,cn then we can simply count the IS of all these components // after which we simply multiply these numbers. if (performDFS) { if (!DepthFirstSearch.Connected(graph, P + X)) { count = 1; foreach (BitSet component in DepthFirstSearch.ConnectedComponents(graph, P + X)) count *= Compute(graph, component * P, component * X, false); return count; } } // Select a pivot in P to branch on // In this case we pick the vertex with the largest degree int maxDegree = -1; ; int pivot = -1; foreach (int u in P) { int deg = graph.Degree(u); if (deg > maxDegree) { maxDegree = deg; pivot = u; } } // There should always be a pivot after the selection procedure if (pivot == -1) throw new Exception("Pivot has not been selected"); // We branch on the pivot, one branch we include the pivot in the IS. // This might possibly disconnect the subgraph G(P + X), thus we set the performDFS boolean to true. count = Compute(graph, P - graph.ClosedNeighborhood(pivot), X - graph.OpenNeighborhood(pivot), true); // One branch we exclude the pivot of the IS. This will not cause the graph to get possibly disconnected count += Compute(graph, P - pivot, X + pivot, false); return count; }
private static List<int> ComputeSequence(Graph graph, BitSet connectedComponent, CandidateStrategy candidateStrategy, int init, out int value) { int n = graph.Size; List<int> sequence = new List<int>() { init }; BitSet left = new BitSet(0, n) { init }; BitSet right = connectedComponent - init; // Initially we store the empty set and the set with init as the representative, ie N(init) * right Set<BitSet> UN_left = new Set<BitSet>() { new BitSet(0, n), graph.OpenNeighborhood(init) * right }; value = int.MinValue; while (!right.IsEmpty) { Set<BitSet> UN_chosen = new Set<BitSet>(); int chosen = Heuristics.TrivialCases(graph, left, right); if (chosen != -1) { UN_chosen = IncrementUN(graph, left, UN_left, chosen); } // If chosen has not been set it means that no trivial case was found // Depending on the criteria for the next vertex we call a different algorithm else { BitSet candidates = Heuristics.Candidates(graph, left, right, candidateStrategy); int min = int.MaxValue; foreach (int v in candidates) { Set<BitSet> UN_v = IncrementUN(graph, left, UN_left, v); if (UN_v.Count < min) { chosen = v; UN_chosen = UN_v; min = UN_v.Count; } } } // This should never happen if (chosen == -1) throw new Exception("No vertex is chosen for next step in the heuristic"); // Add/remove the next vertex in the appropiate sets sequence.Add(chosen); left.Add(chosen); right.Remove(chosen); UN_left = UN_chosen; value = Math.Max(UN_chosen.Count, value); } return sequence; }
// This constructor returns a new bipartite graph by putting all vertices in 'left' on one side, and 'right' on the other side // There will be an edge between two vertices if there was an edge in the original graph public BipartiteGraph(Graph graph, BitSet _left, BitSet _right) : this(_left, _right, _left.Count + _right.Count) { Dictionary<int, int> mapping = new Dictionary<int, int>(); int i = 0; foreach (int v in left + right) mapping[v] = i++; foreach (int v in left) foreach (int w in graph.OpenNeighborhood(v) * right) Connect(mapping[v], mapping[w]); }
/*************************/ // Construction /*************************/ // Constructor for a new graph of size n public Graph(int n) { vertices = new BitSet(0, n); AdjacencyMatrix = new BitSet[n]; Size = n; for (int i = 0; i < n; i++) { vertices.Add(i); AdjacencyMatrix[i] = new BitSet(0, n); } }
private void FillTable(Graph graph, List<int> sequence) { int n = graph.Size; // Processed vertices BitSet left = new BitSet(0, n); // Unprocessed vertices BitSet right = graph.Vertices; // Lists of representatives that we keep track of on each level of the algorithm LinearRepresentativeList reps = new LinearRepresentativeList(); // Initial insertions, the empty set always has the empty neighborhood set as a representative initially reps.Update(new BitSet(0, n), new BitSet(0, n)); for (int i = 0; i < sequence.Count; i++) { /// We give v the possibility to be a representative instead of being contained in neighborhoods int v = sequence[i]; // Actually move v from one set to the other set left.Add(v); right.Remove(v); // We don't want to disturb any pointers so we create new empty datastructures to save everything for the next iteration LinearRepresentativeList nextReps = new LinearRepresentativeList(); // We are iterating over all representatives saved inside the list of the previous step. For each entry there are only two possibilities to create a new neighborhood foreach (BitSet representative in reps) { // Case 1: The neighborhood possibly contained v (thus v has to be removed), but is still valid BitSet neighborhood = reps.GetNeighborhood(representative) - v; nextReps.Update(representative, neighborhood); // Case 2: The union of the old neighborhood, together with v's neighborhood, creates a new entry. The representative is uniond together with v and saved. BitSet representative_ = representative + v; BitSet neighborhood_ = neighborhood + (graph.OpenNeighborhood(v) * right); nextReps.Update(representative_, neighborhood_); } // Update the values for the next iteration reps = nextReps; // Save the maximum size that we encounter during all iterations; this will be the boolean dimension of the graph. MaxDimension = Math.Max(MaxDimension, reps.Count); // Save the representatives at the current cut in the table Table[left.Copy()] = reps; } }
// Builds a neighborhood for a set X from the ground on up, without relying on what has been saved previously in O(n^2) time public dNeighborhood(BitSet X, BitSet vector, Graph graph) { // O(n) time copy Vector = vector.Copy(); Occurrences = new Dictionary<int, int>(); // Loops in O(|Vector|) time over all vertices in the vector foreach (int v in Vector) { // Bitset operation of O(n) time BitSet nv = graph.OpenNeighborhood(v) * X; Occurrences[v] = Math.Min(nv.Count, dValue); } }
// Implementation of Algorithm 1 of 'Fast dynamic programming for locally checkable vertex subset and vertex partitioning problems' (Bui-Xuan et al.) // Used to compute the representatives and their corresponding dNeighborhoods for a given node of the decomposition tree private void FillTable(Graph graph, BitSet cut) { int n = graph.Size; BitSet _cut = graph.Vertices - cut; // Lists of representatives that we keep track of on each level of the algorithm RepresentativeList representatives = new RepresentativeList(); RepresentativeList lastLevel = new RepresentativeList(); // Initial insertions, the empty set always has the empty neighborhood set as a representative initially dNeighborhood dInitial = new dNeighborhood(_cut); representatives.Update(new BitSet(0, n), dInitial); lastLevel.Update(new BitSet(0, n), dInitial); while (lastLevel.Count > 0) { RepresentativeList nextLevel = new RepresentativeList(); foreach (BitSet r in lastLevel) { foreach (int v in cut) { // avoid that r_ = r, since we already saved all sets of that size if (r.Contains(v)) continue; BitSet r_ = r + v; dNeighborhood dn = representatives.GetNeighborhood(r).CopyAndUpdate(graph, v); if (!representatives.ContainsNeighborhood(dn) && !dn.Equals(representatives.GetNeighborhood(r))) { nextLevel.Update(r_, dn); representatives.Update(r_, dn); } } } // Update the values for the next iteration lastLevel = nextLevel; } // Save the representatives at the current cut in the table Table[cut.Copy()] = representatives; // Save the maximum size that we encounter during all iterations; this will be the boolean dimension of the graph is d = 1. MaxDimension = Math.Max(MaxDimension, representatives.Count); }
// Counts the number of independent sets in a graph, such that: // - All vertices in R are included in the independent set, initially empty // - Some of the vertices in P are included in the independent set, initially all vertices of the graph // - None of the vertices in X are included in the independent set, initially empty private static int Compute(Graph graph, BitSet R, BitSet P, BitSet X) { // Base case, when P and X are both empty we cannot expand the IS if (P.IsEmpty && X.IsEmpty) return 1; int count = 0; BitSet copy = P.Copy(); // Foreach vertex v in P we include it in the IS and compute how many maximal IS will include v by going into recursion. foreach (int v in copy) { count += Compute(graph, R + v, P - graph.ClosedNeighborhood(v), X - graph.OpenNeighborhood(v)); P.Remove(v); X.Add(v); } return count; }
// Returns all connected components in a certain subgraph, where we define a subgraph by the vertices that are contained in it // We apply multiple dfs searches to find all connected parts of the graph public static List<BitSet> ConnectedComponents(Graph graph, BitSet subgraph) { // Each connected component is defined as a bitset, thus the result is a list of these bitsets List<BitSet> result = new List<BitSet>(); // Working stack for the dfs algorithm Stack<int> stack = new Stack<int>(); // Each vertex of the subgraph will either be explored, or it will be the starting point of a new dfs search BitSet todo = subgraph.Copy(); while (!todo.IsEmpty) { int s = todo.First(); stack.Push(s); // Start tracking the new component BitSet component = new BitSet(0, graph.Size); // Default dfs exploring part of the graph while (stack.Count > 0) { int v = stack.Pop(); // If we have not encountered this vertex before (meaning it isn't in this component), then we check for all neighbors if they are part of the subgraph // If a neighbor is part of the subgraph it means that we have to push it on the stack to explore it at a later stage for this component if (!component.Contains(v)) { component.Add(v); // Remove this vertex from the 'todo' list, since it can never be the starting point of a new component todo.Remove(v); foreach (int w in graph.OpenNeighborhood(v)) if (subgraph.Contains(w)) stack.Push(w); } } // The whole connected component has been found, so we can add it to the list of results result.Add(component); } return result; }
// Constructs the actual width values // The idea is the following: If we want to know the optimal width for a certain cut A, thus Width[A], then we can obtain this by // either taking the max(Cuts[A], the minimum over all widths of Width[A - a]), which is our recurrence relation. private static void ConstructSequence(Graph graph, BitSet A) { // min saves the minimum size of all neighborhoods of the cut [A - a], where a can be any vertex in A long min = long.MaxValue; // v will be the optimal choice to leave out in the previous iteration in order to obtain A's full neighborhood int v = -1; foreach (int a in A) { BitSet previous = A - a; // If we have not constructed the previous step yet, then go in recursion and do so if (!Neighborhoods.ContainsKey(previous)) ConstructSequence(graph, previous); // Save the minimum value if (Width[previous] < min) { min = Width[previous]; v = a; } } // Obtain the neighborhood of v BitSet nv = graph.OpenNeighborhood(v) * (graph.Vertices - A); // We save the full set of neighborhood vertices at cut A. It does not matter that v was chosen arbitrarely; we always end up in the same collection of neighboring vertices for the set A Set<BitSet> un = new Set<BitSet>(); foreach (BitSet _base in Neighborhoods[A - v]) { un.Add(_base - v); // previous neighbor without v is a possible new neighborhood un.Add((_base - v) + nv); // previous neighbor without v, unioned with the neighborhood of v is a possible new neighborhood } // Save all entries Neighborhoods[A] = un; // List of all neighbors at cut A Cuts[A] = Neighborhoods[A].Count; // Dimension at this cut Width[A] = Math.Max(min, Cuts[A]); // Actual possible width to get to this cut }
// Parses a decomposition given the filename of the decomposition and the graph files. public static Decomposition ParseDecomposition(string decompositionFilename, string graphFilename) { Graph graph = ParseGraph(graphFilename); StreamReader sr = new StreamReader(decompositionFilename); string line; Tree tree = new Tree(); // Each line is simply an integer, which is the sequence of the linear decomposition while ((line = sr.ReadLine()) != null) { // Skip comments if (line.StartsWith("c ")) continue; string[] s = line.Trim().Split(' '); BitSet node = new BitSet(0, graph.Size); foreach (string vertex in s) node.Add(int.Parse(vertex) - 1); // -1 because internally we work with [0,...,n) tree.Insert(node); } return new Decomposition(graph, tree); }
// Counts the number of independent sets in a graph, such that: // - All vertices in R are included in the independent set, initially empty // - Some of the vertices in P are included in the independent set, initially all vertices of the graph // - None of the vertices in X are included in the independent set, initially empty private static long Compute(Graph graph, BitSet R, BitSet P, BitSet X) { // Base case, when P and X are both empty we cannot expand the IS if (P.IsEmpty && X.IsEmpty) return 1; long count = 0; int pivot = -1; int min = int.MaxValue; // Procedure to find a pivot // The idea is that in any maximal IS, either vertex u or a neighbor of u is included (else we could expand by adding u to the IS) // We find the u with the smallest neighborhood, so that we will keep the number of branches low foreach (int u in (P + X)) { int size = (P * graph.OpenNeighborhood(u)).Count; if (size < min) { min = size; pivot = u; } } // There should always be a pivot after the selection procedure, else P and X should both have been empty if (pivot == -1) throw new Exception("Pivot has not been selected"); // Foreach vertex v in the set containing the legal choices of the the closed neighborhood of the pivot, // we include each choice in the IS and compute how many maximal IS will include v by going into recursion foreach (int v in (P * graph.ClosedNeighborhood(pivot))) { count += Compute(graph, R + v, P - graph.ClosedNeighborhood(v), X - graph.OpenNeighborhood(v)); P.Remove(v); X.Add(v); } return count; }
/*************************/ // Trivial cases /*************************/ public static int TrivialCases(Graph graph, BitSet left, BitSet right) { int chosen = -1; // Check if any vertex in right belongs to one of the trivial cases, if yes then we can add this vertex directly to the sequence foreach (int v in right) { // Trivial case 1. If the neighbors of a vertex v in right are all contained in left, then select v // What this means is that v has an empty neighborhood, thus it will not add anything to the boolean-dimension if ((graph.OpenNeighborhood(v) - left).IsEmpty) { chosen = v; break; } bool stop = false; // 2. If there are two vertices, v in right and u in left, such that N(v) * right == (N(u)\v) * right, // then v is selected as our next vertex // What this means is that all neighbors of v are already 'represented' by u, thus making the choice for v will not add anything to the dimension foreach (int u in left) { BitSet nv = graph.OpenNeighborhood(v) * right; // N(v) * right BitSet nu = (graph.OpenNeighborhood(u) - v) * right; // (N(u)\v) * right if (nv.Equals(nu)) { chosen = v; stop = true; break; } } if (stop) break; } return chosen; }
public int this[BitSet representative, int k] { get { if (!Data[k].ContainsKey(representative)) return 0; return Data[k][representative]; } set { Data[k][representative.Copy()] = value; } }
public dNeighborhood CopyAndUpdateVector(BitSet vector, bool increment) { // initialize an empty dNeighborhood in O(|Vector|) time dNeighborhood nx = new dNeighborhood(vector); BitSet iterateOver = increment ? Vector : vector; foreach (int v in iterateOver) nx.Occurrences[v] = Occurrences[v]; return nx; }
/*************************/ // Candidate strategy /*************************/ public static BitSet Candidates(Graph graph, BitSet left, BitSet right, CandidateStrategy candidateStrategy) { BitSet nl = graph.Neighborhood(left) * right; return candidateStrategy == CandidateStrategy.All ? right.Copy() : (nl + graph.Neighborhood(nl)) * right; }
public static void Compute(LinearDecomposition decomposition) { Graph graph = decomposition.Graph; List<int> sequence = decomposition.Sequence; int n = graph.Size; BitSet right = graph.Vertices; BitSet left = new BitSet(0, n); LookupTable table = new LookupTable(n); LinearRepresentativeTable cuts = new LinearRepresentativeTable(graph, sequence); table[new BitSet(0, n), 0] = 1; table[new BitSet(0, n), 1] = 0; for (int i = 0; i < sequence.Count; i++) { int v = sequence[i]; left.Add(v); right.Remove(v); LinearRepresentativeList LRw = cuts[left]; LinearRepresentativeList LRa = cuts[left - v]; LookupTable newTable = new LookupTable(n); foreach (BitSet ra in LRa) { BitSet nra = graph.Neighborhood(ra) * right; BitSet rw = LRw.GetRepresentative(nra); int maxValue = int.MinValue; int limit = (left - v).Count; for (int k = 0; k <= limit; k++) if (table[ra, k] > 0) maxValue = Math.Max(maxValue, k); for (int j = 0; j <= maxValue; j++) { newTable[rw, j] = newTable[rw, j] + table[ra, j]; } //------------ // ra + {v} is not a valid independent set if (LRa.GetNeighborhood(ra).Contains(v)) continue; //------------ // add {v} to the independent set BitSet nrav = graph.Neighborhood(ra + v) * right; BitSet rwv = LRw.GetRepresentative(nrav); for (int j = 0; j <= maxValue; j++) { newTable[rwv, j + 1] = newTable[rwv, j + 1] + table[ra, j]; } } table = newTable; } return; }
/*************************/ // Basic operations /*************************/ // When adding a new entry we check if the neighborhood is already contained in the set // If true, then we might replace the previous representative that is not connected to this neighborhood if the new one is lexicographically smaller public void Update(BitSet representative, dNeighborhood neighborhood) { Map.Add(representative.Copy(), neighborhood); }
// Inserting a node with its parent saves us the time of finding the parent public void InsertWithParent(BitSet node, BitSet parent) { Parent[node] = parent; if (Size % 2 == 0) { if (RightChild.ContainsKey(parent)) throw new Exception("Inserted parent already has a leftchild"); RightChild[parent] = node; } else { if (LeftChild.ContainsKey(parent)) throw new Exception("Inserted parent already has a leftchild"); LeftChild[parent] = node; } Contained.Add(node); }
// Determines the dValue given set // For a set x, d is defined as follows: // d(N) = 0 // d(x) = 1 + min(max v in x, max v not in x) private static int DetermineDValue(BitSet set) { BitSet _set = !set; if (set.IsEmpty || _set.IsEmpty) return 0; return 1 + Math.Min(set.Last(), _set.Last()); }
/*************************/ // Basic operations /*************************/ // Inserting works correctly because we assume that a node that gets newly inserted will not be in the tree already // By definition of boolean decompositions this is redundant and the node should be removed from the decomposition tree public void Insert(BitSet node) { if (Parent.ContainsKey(node)) throw new Exception("This node already exists in the decomposition tree"); // Check if this is the first node that we add to the collection if (Size == 0) { root = node; Contained.Add(node); return; } // The parent of the node that we are currently insering should be the node X that this node is a subset of, and X is has the highest index so far BitSet parent = new BitSet(0, 0); for (int i = Size - 1; i >= 0; i--) { parent = Contained[i]; if (node.IsSubsetOf(parent)) break; } Parent[node] = parent; // The node will always be a leftchild if the previously inserted node had an even number, and vice versa // This assumes that we always add children of a node directly after each other if (Size % 2 == 0) RightChild[parent] = node; else LeftChild[parent] = node; Contained.Add(node); }
// Alternative way of calculating the boolean width of a decomposition by counting the number of maximal independent sets in bipartite graphs, // constructed for each cut of the decomposition private long CalculateBooleanWidthBiPartite() { BitSet left = new BitSet(0, Graph.Size); BitSet right = Graph.Vertices; long max = int.MinValue; foreach (int v in Sequence) { left.Add(v); right.Remove(v); // Construct the bipartite graph BipartiteGraph bg = new BipartiteGraph(Graph, left, right); // Count the number of maximal independent sets in this bipartite graph max = Math.Max(max, CC_MIS.Compute(bg)); } return max; }
public static int Compute(LinearDecomposition decomposition) { Graph graph = decomposition.Graph; int n = graph.Size; List<int> sequence = decomposition.Sequence; BitSet left = new BitSet(0, graph.Size); BitSet right = graph.Vertices; BitSet VG = graph.Vertices; LinearRepresentativeTable cuts = new LinearRepresentativeTable(graph, sequence); LookupTable table = new LookupTable(); // first initialize the very first leaf node int l = sequence[0]; left.Add(l); right.Remove(l); // Base cases BitSet leaf = new BitSet(0, n) { l }; BitSet nleaf = new BitSet(0, n) { graph.OpenNeighborhood(l).First() }; table[new BitSet(0, n), new BitSet(0, n)] = int.MaxValue; table[leaf, new BitSet(0, n)] = 1; table[leaf, nleaf] = 1; table[new BitSet(0, n), nleaf] = 0; for (int i = 1; i < sequence.Count; i++) { int v = sequence[i]; left.Add(v); right.Remove(v); LinearRepresentativeList LRw = cuts[left]; LinearRepresentativeList LRw_ = cuts[right]; LinearRepresentativeList LRa = cuts[left - v]; LinearRepresentativeList LRa_ = cuts[right + v]; LookupTable newTable = new LookupTable(); foreach (BitSet outside in LRw_) { foreach (BitSet inside in LRa) { BitSet nrw_ = graph.Neighborhood(outside) * (left - v); BitSet ra_ = LRa_.GetRepresentative(nrw_); BitSet nra = graph.Neighborhood(inside) * right; BitSet rw = LRw.GetRepresentative(nra); int savedValue = newTable[rw, outside]; int newValue = table[inside, ra_]; BitSet raw_ = inside + outside; BitSet nraw_ = graph.Neighborhood(raw_); if (!nraw_.Contains(v)) newValue = int.MaxValue; int min = Math.Min(savedValue, newValue); newTable[rw, outside] = min; //-------- nrw_ = graph.Neighborhood(outside + v) * (left - v); ra_ = LRa_.GetRepresentative(nrw_); nra = graph.Neighborhood(inside + v) * right; rw = LRw.GetRepresentative(nra); savedValue = newTable[rw, outside]; newValue = table[inside, ra_]; newValue = newValue == int.MaxValue ? newValue : newValue + 1; min = Math.Min(savedValue, newValue); newTable[rw, outside] = min; } } table = newTable; } return table[new BitSet(0, graph.Size), new BitSet(0, graph.Size)]; }
public int this[BitSet inside, BitSet outside] { get { Tuple<BitSet, BitSet> t = new Tuple<BitSet, BitSet>(inside, outside); if (!Data.ContainsKey(t)) return int.MaxValue; return Data[t]; } set { Tuple<BitSet, BitSet> t = new Tuple<BitSet, BitSet>(inside, outside); Data[t] = value; } }
// Indexer public LinearRepresentativeList this[BitSet cut] { get { return Table[cut]; } }