Пример #1
0
        // Removes a separator from the graph, and recursively computes a tree decomposition. The separator should be safe.
        // If check3isolated is true, then this also checks the property that given a separator of size 3, it should not split off any isolated vertices (or else it is not safe).
        // If any connected component has more than maximumSize vertices, do not proceed (balanced separator check)
        public Graph RemoveSeparator(List <Vertex> separator, bool check3isolated, int maximumSize)
        {
            // Compute the graph after removing separator
            List <Vertex> tempVertices = vertices.Keys.ToList();

            foreach (Vertex v in separator)
            {
                tempVertices.Remove(v);
            }

            // Get the connected components
            Graph componentsGraph            = new Graph(tempVertices);
            List <List <Vertex> > components = componentsGraph.getComponents();

            // If applicable, check that it is safe
            if (check3isolated && components.Any((c) => c.Count == 1))
            {
                return(null);
            }

            // Check balance
            if (components.Any((c) => c.Count > maximumSize))
            {
                return(null);
            }

            // Prepare the new tree decomposition: the root bag contains the separator
            Graph result = new Graph();

            Program.BagsList.Add(new List <Vertex>(separator));
            Vertex root = result.AddVertex(Program.BagsList.Count - 1);

            // Decompose each component
            foreach (List <Vertex> component in components)
            {
                // Create a new graph with the component in it
                Graph componentGraph = new Graph(component);

                // Add the separator
                foreach (Vertex v in separator)
                {
                    componentGraph.AddVertex(v);
                }

                foreach (Vertex u in separator)
                {
                    // Make the separator a clique
                    foreach (Vertex v in separator)
                    {
                        if (u.Label <= v.Label) // Not strictly necessary, but might save some time
                        {
                            continue;
                        }
                        componentGraph.AddEdge(u, v);
                    }
                    // And add edges from the separator to the rest
                    foreach (Vertex v in u.Adj)
                    {
                        componentGraph.AddEdge(u, v);
                    }
                }

                // Compute the TD of the component
                Graph TDComponent = componentGraph.Decompose();
                result.AddGraph(TDComponent);

                // Find an appropriate bag in the decomposition to connect our root bag to
                foreach (Vertex v in TDComponent.vertices.Keys)
                {
                    if (separator.All((u) => { return(Program.BagsList[v.Label].Contains(u)); }))
                    {
                        result.AddEdge(root, v);
                        break;
                    }
                }
            }

            return(result);
        }
Пример #2
0
        // Recursively computes a tree decomposition
        // Tries several reduction rules (connected components, size 1,2,3 separators, almost clique separators)
        public Graph Decompose()
        {
            Graph result = new Graph();

            // Most simple case: the graph is disconnected
            getComponents();
            if (components.Count > 1)
            {
                Vertex prevVertex = null;
                foreach (List <Vertex> component in components)
                {
                    Graph componentGraph = new Graph(component);
                    Graph TDComponent    = componentGraph.Decompose();

                    // Combine TDs into path (any way that makes the components connected is OK
                    result.AddGraph(TDComponent);
                    Vertex TDVertex = TDComponent.vertices.First().Value;
                    if (prevVertex != null)
                    {
                        result.AddEdge(TDVertex, prevVertex);
                    }
                    prevVertex = TDVertex;
                }

                return(result);
            }

            // Biconnected graph: An articulation point is always safe (single vertex separator)
            Vertex ap = ArticulationPoint();

            if (ap != null)
            {
                return(RemoveSeparator(new List <Vertex>(new Vertex[] { ap })));
            }

            // 3-connected graph: try to find an articulation pair by removing a vertex, then testing if some other vertex is an articulation point
            // These are always safe
            // Note that thia can be done more efficiently, but this is easier and fast enough for reasonable graphs
            foreach (Vertex v1 in vertices.Keys)
            {
                List <Vertex> tempVertices = vertices.Keys.ToList();
                tempVertices.Remove(v1);
                Vertex v2 = new Graph(tempVertices).ArticulationPoint();
                if (v2 != null) // Found an articulation pair (v1, v2)
                {
                    return(RemoveSeparator(new List <Vertex>(new Vertex[] { v1, v2 })));
                }
            }

            // 4-connected graph: same idea, remove pair of vertices, then find an articulation point
            // Again quite inefficient (cubic) but we've already done a lot of reduction so the graph is hopefully small at this point
            foreach (Vertex v1 in vertices.Keys)
            {
                foreach (Vertex v2 in vertices.Keys)
                {
                    if (v1.Label >= v2.Label)
                    {
                        continue;
                    }

                    List <Vertex> tempVertices = vertices.Keys.ToList();
                    tempVertices.Remove(v1);
                    tempVertices.Remove(v2);
                    Vertex v3 = new Graph(tempVertices).ArticulationPoint();
                    if (v3 != null) // Found a separator (v1, v2, v3)
                    {
                        // RemoveSeparator checks that this is a safe separator (not all 3-vertex separators are safe) and returns null if not
                        Graph temp = RemoveSeparator(new List <Vertex>(new Vertex[] { v1, v2, v3 }), true);
                        if (temp != null)
                        {
                            return(temp);
                        }
                    }
                }
            }

            // Our final reduction rule: a minimal separator that is a clique or almost clique (a clique plus a vertex) is safe
            List <List <Vertex> > cliques = new List <List <Vertex> >();

            foreach (Vertex v in vertices.Keys) // Initialization: all vertices cliques of size 1
            {
                cliques.Add(new List <Vertex>(new Vertex[] { v }));
            }

            // Enumerate all cliques; we don't need to consider cliques that are as large as the entire graph
            for (int i = 2; i < vertices.Count - 1; i++)
            {
                List <List <Vertex> > newCliques = new List <List <Vertex> >();
                foreach (List <Vertex> clique in cliques) // Enumerate over cliques of size i-1
                {
                    foreach (Vertex v in vertices.Keys)
                    {
                        if (v.Label <= clique[clique.Count - 1].Label)
                        {
                            continue;                                                       // Optimization: only consider cliques where the vertices are in increasing order
                        }
                        if (clique.All((u) => { return(edges.Contains(new Edge(u, v))); })) // Can we add the vertex (is it connected to everything else)?
                        {
                            // Found a new clique
                            List <Vertex> newClique = new List <Vertex>(clique);
                            newClique.Add(v);
                            newCliques.Add(newClique);

                            // Copy the graph, remove a clique
                            List <Vertex> tempVertices = vertices.Keys.ToList();
                            foreach (Vertex u in newClique)
                            {
                                tempVertices.Remove(u);
                            }

                            // If the graph minus the clique has an AP, the clique plus the AP is an (almost) clique minimal separator
                            Vertex v1 = new Graph(tempVertices).ArticulationPoint();
                            if (v1 != null) // Found an (almost) clique separator
                            {
                                newClique.Add(v1);
                                return(RemoveSeparator(newClique));
                            }
                        }
                    }
                }
                cliques = newCliques;

                // Limit the number of cases considered, as we want to limit the time and don't necessarily need to consider all of them
                cliques.Shuffle();
                cliques = cliques.Take(10000).ToList();
            }

            // No more reduction rules apply: we now try to find a tree decomposition using a 2^n DP algorithm
            return(ComputeTD());
        }