org.jgrapht.alg.cycle.BergeGraphInspector Maven / Gradle / Ivy
/*
* (C) Copyright 2016-2023, by Philipp S. Kaesgen and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.alg.cycle;
import org.jgrapht.*;
import org.jgrapht.alg.connectivity.*;
import org.jgrapht.alg.shortestpath.*;
import org.jgrapht.generate.*;
import org.jgrapht.graph.*;
import java.util.*;
import java.util.stream.*;
/**
*
* Tests whether a graph is perfect. A
* perfect graph, also known as a Berge graph, is a graph $G$ such that for every induced subgraph
* of $G$, the clique number $\chi(G)$ equals the chromatic number $\omega(G)$, i.e.,
* $\omega(G)=\chi(G)$. Another characterization of perfect graphs is given by the Strong Perfect
* Graph Theorem [M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. The strong perfect graph
* theorem Annals of Mathematics, vol 164(1): pp. 51–230, 2006]: A graph $G$ is perfect if neither
* $G$ nor its complement $\overline{G}$ have an odd hole. A hole in $G$ is an induced subgraph of
* $G$ that is a cycle of length at least four, and it is odd or even if it has odd (or even,
* respectively) length.
*
* Some special classes of graphs are are
* known to be perfect, e.g. Bipartite graphs and Chordal graphs. Testing whether a graph is resp.
* Bipartite or Chordal can be done efficiently using {@link GraphTests#isBipartite} or
* {@link org.jgrapht.alg.cycle.ChordalityInspector}.
*
* The implementation of this class is based on the paper: M. Chudnovsky, G. Cornuejols, X. Liu, P.
* Seymour, and K. Vuskovic. Recognizing Berge Graphs. Combinatorica 25(2): 143--186, 2003.
*
* Special Thanks to Maria Chudnovsky for her kind help.
*
*
* The runtime complexity of this implementation is $O(|V|^9|)$. This implementation is far more
* efficient than simplistically testing whether graph $G$ or its complement $\overline{G}$ have an
* odd cycle, because testing whether one graph can be found as an induced subgraph of another is
* known to be
* NP-hard.
*
* @author Philipp S. Kaesgen ([email protected])
*
* @param the graph vertex type
* @param the graph edge type
*/
public class BergeGraphInspector
{
private GraphPath certificate = null;
private boolean certify = false;
/**
* Lists the vertices which are covered by two paths
*
* @param p1 A Path in g
* @param p2 A Path in g
* @return Set of vertices covered by both p1 and p2
*/
private List intersectGraphPaths(GraphPath p1, GraphPath p2)
{
List res = new LinkedList<>();
res.addAll(p1.getVertexList());
res.retainAll(p2.getVertexList());
return res;
}
/**
* Assembles a GraphPath of the Paths S and T. Required for the Pyramid Checker
*
* @param g A Graph
* @param pathS A Path in g
* @param pathT A Path in g
* @param m A vertex
* @param b1 A base vertex
* @param b2 A base vertex
* @param b3 A base vertex
* @param s1 A vertex
* @param s2 A vertex
* @param s3 A vertex
* @return The conjunct path of S and T
*/
private GraphPath p(
Graph g, GraphPath pathS, GraphPath pathT, V m, V b1, V b2, V b3, V s1,
V s2, V s3)
{
if (s1 == b1) {
if (b1 == m) {
List edgeList = new LinkedList<>();
return new GraphWalk<>(g, s1, b1, edgeList, 0);
} else
return null;
} else {
if (b1 == m)
return null;
if (g.containsEdge(m, b2) || g.containsEdge(m, b3) || g.containsEdge(m, s2)
|| g.containsEdge(m, s3) || pathS == null || pathT == null)
return null;
if (pathS.getVertexList().stream().anyMatch(
t -> g.containsEdge(t, b2) || g.containsEdge(t, b3) || g.containsEdge(t, s2)
|| g.containsEdge(t, s3))
|| pathT.getVertexList().stream().anyMatch(
t -> t != b1 && (g.containsEdge(t, b2) || g.containsEdge(t, b3)
|| g.containsEdge(t, s2) || g.containsEdge(t, s3))))
return null;
List intersection = intersectGraphPaths(pathS, pathT);
if (intersection.size() != 1 || !intersection.contains(m))
return null;
if (pathS.getVertexList().stream().anyMatch(
s -> s != m && pathT
.getVertexList().stream().anyMatch(t -> t != m && g.containsEdge(s, t))))
return null;
List edgeList = new LinkedList<>();
edgeList.addAll(pathT.getEdgeList());
edgeList.addAll(pathS.getEdgeList());
double weight = edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
return new GraphWalk<>(g, b1, s1, edgeList, weight);
}
}
private void bfOddHoleCertificate(Graph g)
{
for (V start : g.vertexSet()) {
if (g.degreeOf(start) < 2)
continue;
Set set = new HashSet<>();
set.addAll(g.vertexSet());
for (V neighborOfStart : g.vertexSet()) {
if (neighborOfStart == start || !g.containsEdge(start, neighborOfStart)
|| g.degreeOf(neighborOfStart) != 2)
continue;
set.remove(neighborOfStart);
Graph subg = new AsSubgraph<>(g, set);
for (V neighborsNeighbor : g.vertexSet()) {
if (neighborsNeighbor == start || neighborsNeighbor == neighborOfStart
|| !g.containsEdge(neighborsNeighbor, neighborOfStart)
|| g.containsEdge(neighborsNeighbor, start)
|| g.degreeOf(neighborsNeighbor) < 2)
continue;
GraphPath path =
new DijkstraShortestPath<>(subg).getPath(start, neighborsNeighbor);
if (path == null || path.getLength() < 3 || path.getLength() % 2 == 0)
continue;
List edgeList = new LinkedList<>();
edgeList.addAll(path.getEdgeList());
edgeList.add(g.getEdge(neighborsNeighbor, neighborOfStart));
edgeList.add(g.getEdge(neighborOfStart, start));
double weight = edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, start, start, edgeList, weight);
break;
}
if (certificate != null)
break;
}
if (certificate != null)
break;
}
}
/**
* Checks whether a graph contains a pyramid. Running time: O(|V(g)|^9)
*
* @param g Graph
* @return Either it finds a pyramid (and hence an odd hole) in g, or it determines that g
* contains no pyramid
*/
boolean containsPyramid(Graph g)
{
/*
* A pyramid looks like this:
*
* b2-(T2)-m2-(S2)-s2 / | \ b1---(T1)-m1-(S1)-s1--a \ | / b3-(T3)-m3-(S3)-s3
*
* Note that b1, b2, and b3 are connected and all names in parentheses are paths
*
*/
Set> visitedTriangles = new HashSet<>();
for (V b1 : g.vertexSet()) {
for (V b2 : g.vertexSet()) {
if (b1 == b2 || !g.containsEdge(b1, b2))
continue;
for (V b3 : g.vertexSet()) {
if (b3 == b1 || b3 == b2 || !g.containsEdge(b2, b3) || !g.containsEdge(b1, b3))
continue;
// Triangle detected for the pyramid base
Set triangles = new HashSet<>();
triangles.add(b1);
triangles.add(b2);
triangles.add(b3);
if (visitedTriangles.contains(triangles)) {
continue;
}
visitedTriangles.add(triangles);
for (V aCandidate : g.vertexSet()) {
if (aCandidate == b1 || aCandidate == b2 || aCandidate == b3 ||
// a is adjacent to at most one of b1,b2,b3
g.containsEdge(aCandidate, b1) && g.containsEdge(aCandidate, b2)
|| g.containsEdge(aCandidate, b2) && g.containsEdge(aCandidate, b3)
|| g.containsEdge(aCandidate, b1) && g.containsEdge(aCandidate, b3))
{
continue;
}
// aCandidate could now be the top of the pyramid
for (V s1 : g.vertexSet()) {
if (s1 == aCandidate || !g.containsEdge(s1, aCandidate) || s1 == b2
|| s1 == b3
|| s1 != b1 && (g.containsEdge(s1, b2) || g.containsEdge(s1, b3)))
{
continue;
}
for (V s2 : g.vertexSet()) {
if (s2 == aCandidate || !g.containsEdge(s2, aCandidate)
|| g.containsEdge(s1, s2) || s1 == s2 || s2 == b1 || s2 == b3
|| s2 != b2
&& (g.containsEdge(s2, b1) || g.containsEdge(s2, b3)))
{
continue;
}
for (V s3 : g.vertexSet()) {
if (s3 == aCandidate || !g.containsEdge(s3, aCandidate)
|| g.containsEdge(s3, s2) || s1 == s3 || s3 == s2
|| g.containsEdge(s1, s3) || s3 == b1 || s3 == b2
|| s3 != b3
&& (g.containsEdge(s3, b1) || g.containsEdge(s3, b2)))
{
continue;
}
// s1, s2, s3 could now be the closest vertices to the top
// vertex of the pyramid
Set setM = new HashSet<>();
setM.addAll(g.vertexSet());
setM.remove(b1);
setM.remove(b2);
setM.remove(b3);
setM.remove(s1);
setM.remove(s2);
setM.remove(s3);
Map> mapS1 = new HashMap<>(),
mapS2 = new HashMap<>(), mapS3 = new HashMap<>(),
mapT1 = new HashMap<>(), mapT2 = new HashMap<>(),
mapT3 = new HashMap<>();
// find paths which could be the edges of the pyramid
for (V m1 : setM) {
Set validInterior = new HashSet<>();
validInterior.addAll(setM);
validInterior.removeIf(
i -> g.containsEdge(i, b2) || g.containsEdge(i, s2)
|| g.containsEdge(i, b3) || g.containsEdge(i, s3));
validInterior.add(m1);
validInterior.add(s1);
Graph subg = new AsSubgraph<>(g, validInterior);
mapS1.put(
m1, new DijkstraShortestPath<>(subg).getPath(m1, s1));
validInterior.remove(s1);
validInterior.add(b1);
subg = new AsSubgraph<>(g, validInterior);
mapT1.put(
m1, new DijkstraShortestPath<>(subg).getPath(b1, m1));
}
for (V m2 : setM) {
Set validInterior = new HashSet<>();
validInterior.addAll(setM);
validInterior.removeIf(
i -> g.containsEdge(i, b1) || g.containsEdge(i, s1)
|| g.containsEdge(i, b3) || g.containsEdge(i, s3));
validInterior.add(m2);
validInterior.add(s2);
Graph subg = new AsSubgraph<>(g, validInterior);
mapS2.put(
m2, new DijkstraShortestPath<>(subg).getPath(m2, s2));
validInterior.remove(s2);
validInterior.add(b2);
subg = new AsSubgraph<>(g, validInterior);
mapT2.put(
m2, new DijkstraShortestPath<>(subg).getPath(b2, m2));
}
for (V m3 : setM) {
Set validInterior = new HashSet<>();
validInterior.addAll(setM);
validInterior.removeIf(
i -> g.containsEdge(i, b1) || g.containsEdge(i, s1)
|| g.containsEdge(i, b2) || g.containsEdge(i, s2));
validInterior.add(m3);
validInterior.add(s3);
Graph subg = new AsSubgraph<>(g, validInterior);
mapS3.put(
m3, new DijkstraShortestPath<>(subg).getPath(m3, s3));
validInterior.remove(s3);
validInterior.add(b3);
subg = new AsSubgraph<>(g, validInterior, null);
mapT3.put(
m3, new DijkstraShortestPath<>(subg).getPath(b3, m3));
}
// Check if all edges of a pyramid are valid
Set setM1 = new HashSet<>();
setM1.addAll(setM);
setM1.add(b1);
for (V m1 : setM1) {
GraphPath pathP1 = p(
g, mapS1.get(m1), mapT1.get(m1), m1, b1, b2, b3, s1, s2,
s3);
if (pathP1 == null)
continue;
Set setM2 = new HashSet<>();
setM2.addAll(setM);
setM2.add(b2);
for (V m2 : setM) {
GraphPath pathP2 = p(
g, mapS2.get(m2), mapT2.get(m2), m2, b2, b1, b3, s2,
s1, s3);
if (pathP2 == null)
continue;
Set setM3 = new HashSet<>();
setM3.addAll(setM);
setM3.add(b3);
for (V m3 : setM3) {
GraphPath pathP3 = p(
g, mapS3.get(m3), mapT3.get(m3), m3, b3, b1, b2,
s3, s1, s2);
if (pathP3 == null)
continue;
if (certify) {
if ((pathP1.getLength() + pathP2.getLength())
% 2 == 0)
{
Set set = new HashSet<>();
set.addAll(pathP1.getVertexList());
set.addAll(pathP2.getVertexList());
set.add(aCandidate);
bfOddHoleCertificate(
new AsSubgraph<>(g, set));
} else if ((pathP1.getLength()
+ pathP3.getLength()) % 2 == 0)
{
Set set = new HashSet<>();
set.addAll(pathP1.getVertexList());
set.addAll(pathP3.getVertexList());
set.add(aCandidate);
bfOddHoleCertificate(
new AsSubgraph<>(g, set));
} else {
Set set = new HashSet<>();
set.addAll(pathP3.getVertexList());
set.addAll(pathP2.getVertexList());
set.add(aCandidate);
bfOddHoleCertificate(
new AsSubgraph<>(g, set));
}
}
return true;
}
}
}
}
}
}
}
}
}
}
return false;
}
/**
* Finds all Components of a set F contained in V(g)
*
* @param g A graph
* @param f A vertex subset of g
* @return Components of F in g
*/
private List> findAllComponents(Graph g, Set f)
{
return new ConnectivityInspector<>(new AsSubgraph<>(g, f)).connectedSets();
}
/**
* Checks whether a graph contains a Jewel. Running time: O(|V(g)|^6)
*
* @param g Graph
* @return Decides whether there is a jewel in g
*/
boolean containsJewel(Graph g)
{
for (V v2 : g.vertexSet()) {
for (V v3 : g.vertexSet()) {
if (v2 == v3 || !g.containsEdge(v2, v3))
continue;
for (V v5 : g.vertexSet()) {
if (v2 == v5 || v3 == v5)
continue;
Set setF = new HashSet<>();
for (V f : g.vertexSet()) {
if (f == v2 || f == v3 || f == v5 || g.containsEdge(f, v2)
|| g.containsEdge(f, v3) || g.containsEdge(f, v5))
continue;
setF.add(f);
}
List> componentsOfF = findAllComponents(g, setF);
Set setX1 = new HashSet<>();
for (V x1 : g.vertexSet()) {
if (x1 == v2 || x1 == v3 || x1 == v5 || !g.containsEdge(x1, v2)
|| !g.containsEdge(x1, v5) || g.containsEdge(x1, v3))
continue;
setX1.add(x1);
}
Set setX2 = new HashSet<>();
for (V x2 : g.vertexSet()) {
if (x2 == v2 || x2 == v3 || x2 == v5 || g.containsEdge(x2, v2)
|| !g.containsEdge(x2, v5) || !g.containsEdge(x2, v3))
continue;
setX2.add(x2);
}
for (V v1 : setX1) {
if (g.containsEdge(v1, v3))
continue;
for (V v4 : setX2) {
if (v1 == v4 || g.containsEdge(v1, v4) || g.containsEdge(v2, v4))
continue;
for (Set fPrime : componentsOfF) {
if (hasANeighbour(g, fPrime, v1) && hasANeighbour(g, fPrime, v4)) {
if (certify) {
Set validSet = new HashSet<>();
validSet.addAll(fPrime);
validSet.add(v1);
validSet.add(v4);
GraphPath p = new DijkstraShortestPath<>(
new AsSubgraph<>(g, validSet)).getPath(v1, v4);
List edgeList = new LinkedList<>();
edgeList.addAll(p.getEdgeList());
if (p.getLength() % 2 == 1) {
edgeList.add(g.getEdge(v4, v5));
edgeList.add(g.getEdge(v5, v1));
} else {
edgeList.add(g.getEdge(v4, v3));
edgeList.add(g.getEdge(v3, v2));
edgeList.add(g.getEdge(v2, v1));
}
double weight =
edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, v1, v1, edgeList, weight);
}
return true;
}
}
}
}
}
}
}
return false;
}
/**
* Checks whether a graph contains a clean shortest odd hole. Running time: O(|V(g)|^4)
*
* @param g Graph containing no pyramid or jewel
* @return Decides whether g contains a clean shortest odd hole
*/
boolean containsCleanShortestOddHole(Graph g)
{
/*
* Find 3 Paths which are an uneven odd hole when conjunct
*/
for (V u : g.vertexSet()) {
for (V v : g.vertexSet()) {
if (u == v || g.containsEdge(u, v))
continue;
GraphPath puv = new DijkstraShortestPath<>(g).getPath(u, v);
if (puv == null)
continue;
for (V w : g.vertexSet()) {
if (w == u || w == v || g.containsEdge(w, u) || g.containsEdge(w, v))
continue;
GraphPath pvw = new DijkstraShortestPath<>(g).getPath(v, w);
if (pvw == null)
continue;
GraphPath pwu = new DijkstraShortestPath<>(g).getPath(w, u);
if (pwu == null)
continue;
Set set = new HashSet<>();
set.addAll(puv.getVertexList());
set.addAll(pvw.getVertexList());
set.addAll(pwu.getVertexList());
Graph subg = new AsSubgraph<>(g, set);
// Look for holes with more than 6 edges and uneven length
if (set.size() < 7 || subg.vertexSet().size() != set.size()
|| subg.edgeSet().size() != subg.vertexSet().size()
|| subg.vertexSet().size() % 2 == 0
|| subg.vertexSet().stream().anyMatch(t -> subg.degreeOf(t) != 2))
continue;
if (certify) {
List edgeList = new LinkedList<>();
edgeList.addAll(puv.getEdgeList());
edgeList.addAll(pvw.getEdgeList());
edgeList.addAll(pwu.getEdgeList());
double weight = edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, u, u, edgeList, weight);
}
return true;
}
}
}
return false;
}
/**
* Returns a path in g from start to end avoiding the vertices in X
*
* @param g A Graph
* @param start start vertex
* @param end end vertex
* @param x set of vertices which should not be in the graph
* @return A Path in G\X
*/
private GraphPath getPathAvoidingX(Graph g, V start, V end, Set x)
{
Set vertexSet = new HashSet<>();
vertexSet.addAll(g.vertexSet());
vertexSet.removeAll(x);
vertexSet.add(start);
vertexSet.add(end);
Graph subg = new AsSubgraph<>(g, vertexSet, null);
return new DijkstraShortestPath<>(subg).getPath(start, end);
}
/**
* Checks whether the vertex set of a graph without a vertex set X contains a shortest odd hole.
* Running time: O(|V(g)|^4)
*
* @param g Graph containing neither pyramid nor jewel
* @param x Subset of V(g) and a possible Cleaner for an odd hole
* @return Determines whether g has an odd hole such that X is a near-cleaner for it
*/
private boolean containsShortestOddHole(Graph g, Set x)
{
for (V y1 : g.vertexSet()) {
if (x.contains(y1))
continue;
for (V x1 : g.vertexSet()) {
if (x1 == y1)
continue;
GraphPath rx1y1 = getPathAvoidingX(g, x1, y1, x);
for (V x3 : g.vertexSet()) {
if (x3 == x1 || x3 == y1 || !g.containsEdge(x1, x3))
continue;
for (V x2 : g.vertexSet()) {
if (x2 == x3 || x2 == x1 || x2 == y1 || g.containsEdge(x2, x1)
|| !g.containsEdge(x3, x2))
continue;
GraphPath rx2y1 = getPathAvoidingX(g, x2, y1, x);
double n;
if (rx1y1 == null || rx2y1 == null)
continue;
V y2 = null;
for (V y2Candidate : rx2y1.getVertexList()) {
if (g.containsEdge(y1, y2Candidate) && y2Candidate != x1
&& y2Candidate != x2 && y2Candidate != x3 && y2Candidate != y1)
{
y2 = y2Candidate;
break;
}
}
if (y2 == null)
continue;
GraphPath rx3y1 = getPathAvoidingX(g, x3, y1, x);
GraphPath rx3y2 = getPathAvoidingX(g, x3, y2, x);
GraphPath rx1y2 = getPathAvoidingX(g, x1, y2, x);
if (rx3y1 != null && rx3y2 != null && rx1y2 != null
&& rx2y1.getLength() == (n = rx1y1.getLength() + 1)
&& n == rx1y2.getLength() && rx3y1.getLength() >= n
&& rx3y2.getLength() >= n)
{
if (certify) {
List edgeList = new LinkedList<>();
edgeList.addAll(rx1y1.getEdgeList());
for (int i = rx2y1.getLength() - 1; i >= 0; i--)
edgeList.add(rx2y1.getEdgeList().get(i));
edgeList.add(g.getEdge(x2, x3));
edgeList.add(g.getEdge(x3, x1));
double weight =
edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, x1, x1, edgeList, weight);
}
return true;
}
}
}
}
}
return false;
}
/**
* Checks whether a clean shortest odd hole is in g or whether X is a cleaner for an amenable
* shortest odd hole
*
* @param g A graph, containing no pyramid or jewel
* @param x A subset X of V(g) and a possible Cleaner for an odd hole
* @return Returns whether g has an odd hole or there is no shortest odd hole in C such that X
* is a near-cleaner for C.
*/
private boolean routine1(Graph g, Set x)
{
return containsCleanShortestOddHole(g) || containsShortestOddHole(g, x);
}
/**
* Checks whether a graph has a configuration of type T1. A configuration of type T1 in g is a
* hole of length 5
*
* @param g A Graph
* @return whether g contains a configuration of Type T1 (5-cycle)
*/
private boolean hasConfigurationType1(Graph g)
{
for (V v1 : g.vertexSet()) {
Set temp = new ConnectivityInspector<>(g).connectedSetOf(v1);
for (V v2 : temp) {
if (v1 == v2 || !g.containsEdge(v1, v2))
continue;
for (V v3 : temp) {
if (v3 == v1 || v3 == v2 || !g.containsEdge(v2, v3) || g.containsEdge(v1, v3))
continue;
for (V v4 : temp) {
if (v4 == v1 || v4 == v2 || v4 == v3 || g.containsEdge(v1, v4)
|| g.containsEdge(v2, v4) || !g.containsEdge(v3, v4))
continue;
for (V v5 : temp) {
if (v5 == v1 || v5 == v2 || v5 == v3 || v5 == v4
|| g.containsEdge(v2, v5) || g.containsEdge(v3, v5)
|| !g.containsEdge(v1, v5) || !g.containsEdge(v4, v5))
continue;
if (certify) {
List edgeList = new LinkedList<>();
edgeList.add(g.getEdge(v1, v2));
edgeList.add(g.getEdge(v2, v3));
edgeList.add(g.getEdge(v3, v4));
edgeList.add(g.getEdge(v4, v5));
edgeList.add(g.getEdge(v5, v1));
double weight =
edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, v1, v1, edgeList, weight);
}
return true;
}
}
}
}
}
return false;
}
/**
* A vertex y is X-complete if y contained in V(g)\X is adjacent to every vertex in X.
*
* @param g A Graph
* @param y Vertex whose X-completeness is to assess
* @param x Set of vertices
* @return whether y is X-complete
*/
boolean isYXComplete(Graph g, V y, Set x)
{
return x.stream().allMatch(t -> g.containsEdge(t, y));
}
/**
* Returns all anticomponents of a graph and a vertex set.
*
* @param g A Graph
* @param y A set of vertices
* @return List of anticomponents of Y in g
*/
private List> findAllAnticomponentsOfY(Graph g, Set y)
{
Graph target;
if (g.getType().isSimple())
target = new SimpleGraph<>(
g.getVertexSupplier(), g.getEdgeSupplier(), g.getType().isWeighted());
else
target = new Multigraph<>(
g.getVertexSupplier(), g.getEdgeSupplier(), g.getType().isWeighted());
new ComplementGraphGenerator<>(g).generateGraph(target);
return findAllComponents(target, y);
}
/**
*
* Checks whether a graph is of configuration type T2. A configuration of type T2 in g is a
* sequence v1,v2,v3,v4,P,X such that:
*
*
* - v1-v2-v3-v4 is a path of g
* - X is an anticomponent of the set of all {v1,v2,v4}-complete vertices
* - P is a path in G\(X+{v2,v3}) between v1,v4, and no vertex in P*, i.e. P's interior, is
* X-complete or adjacent to v2 or adjacent to v3
*
* An example is the complement graph of a cycle-7-graph
*
* @param g A Graph
* @return whether g contains a configuration of Type T2
*/
boolean hasConfigurationType2(Graph g)
{
for (V v1 : g.vertexSet()) {
for (V v2 : g.vertexSet()) {
if (v1 == v2 || !g.containsEdge(v1, v2))
continue;
for (V v3 : g.vertexSet()) {
if (v3 == v2 || v1 == v3 || g.containsEdge(v1, v3) || !g.containsEdge(v2, v3))
continue;
for (V v4 : g.vertexSet()) {
if (v4 == v1 || v4 == v2 || v4 == v3 || g.containsEdge(v4, v2)
|| g.containsEdge(v4, v1) || !g.containsEdge(v3, v4))
continue;
Set temp = new HashSet<>();
temp.add(v1);
temp.add(v2);
temp.add(v4);
Set setY = new HashSet<>();
for (V y : g.vertexSet()) {
if (isYXComplete(g, y, temp)) {
setY.add(y);
}
}
List> anticomponentsOfY = findAllAnticomponentsOfY(g, setY);
for (Set setX : anticomponentsOfY) {
Set v2v3 = new HashSet<>();
v2v3.addAll(g.vertexSet());
v2v3.remove(v2);
v2v3.remove(v3);
v2v3.removeAll(setX);
if (!v2v3.contains(v1) || !v2v3.contains(v4))
continue;
GraphPath path =
new DijkstraShortestPath<>(new AsSubgraph<>(g, v2v3))
.getPath(v1, v4);
if (path == null)
continue;
List listP = path.getVertexList();
if (!listP.contains(v1) || !listP.contains(v4))
continue;
boolean cont = true;
for (V p : listP) {
if (p != v1 && p != v4 && (g.containsEdge(p, v2)
|| g.containsEdge(p, v3) || isYXComplete(g, p, setX)))
{
cont = false;
break;
}
}
if (cont) {
if (certify) {
List edgeList = new LinkedList<>();
if (path.getLength() % 2 == 0) {
edgeList.add(g.getEdge(v1, v2));
edgeList.add(g.getEdge(v2, v3));
edgeList.add(g.getEdge(v3, v4));
edgeList.addAll(path.getEdgeList());
} else {
edgeList.addAll(path.getEdgeList());
V x = setX.iterator().next();
edgeList.add(g.getEdge(v4, x));
edgeList.add(g.getEdge(x, v1));
}
double weight =
edgeList.stream().mapToDouble(g::getEdgeWeight).sum();
certificate = new GraphWalk<>(g, v1, v1, edgeList, weight);
}
return true;
}
}
}
}
}
}
return false;
}
/**
* Reports whether v has at least one neighbour in set
*
* @param g A Graph
* @param set A set of vertices
* @param v A vertex
* @return whether v has at least one neighbour in set
*/
private boolean hasANeighbour(Graph g, Set set, V v)
{
return set.stream().anyMatch(s -> g.containsEdge(s, v));
}
/**
* For each anticomponent X, find the maximal connected subset F' containing v5 with the
* properties that v1,v2 have no neighbours in F' and no vertex of F'\v5 is X-complete
*
* @param g A Graph
* @param setX A set of vertices
* @param v1 A vertex
* @param v2 A vertex
* @param v5 A Vertex
* @return The maximal connected vertex subset containing v5, no neighbours of v1 and v2, and no
* X-complete vertex except v5
*/
private Set findMaximalConnectedSubset(Graph g, Set setX, V v1, V v2, V v5)
{
Set fPrime = new ConnectivityInspector<>(g).connectedSetOf(v5);
fPrime.removeIf(
t -> t != v5 && isYXComplete(g, t, setX) || v1 == t || v2 == t || g.containsEdge(v1, t)
|| g.containsEdge(v2, t));
return fPrime;
}
/**
* Reports whether a vertex has at least one nonneighbour in X
*
* @param g A Graph
* @param v A Vertex
* @param setX A set of vertices
* @return whether v has a nonneighbour in X
*/
private boolean hasANonneighbourInX(Graph g, V v, Set setX)
{
return setX.stream().anyMatch(x -> !g.containsEdge(v, x));
}
/**
*
* Checks whether a graph is of configuration type T3. A configuration of type T3 in g is a
* sequence v1,...,v6,P,X such that
*
*
* - v1,...,v6 are distinct vertices of g
* - v1v2,v3v4,v2v3,v3v5,v4v6 are edges, and v1v3,v2v4,v1v5,v2v5,v1v6,v2v6,v4v5 are
* non-edges
* - X is an anticomponent of the set of all {v1,v2,v5}-complete vertices, and v3,v4 are not
* X-complete
* - P is a path of g\(X+{v1,v2,v3,v4}) between v5,v6, and no vertex in P* is X-complete or
* adjacent to v1 or adjacent to v2
* - if v5v6 is an edge then v6 is not X-complete
*
*
* @param g A Graph
* @return whether g contains a configuration of Type T3
*/
boolean hasConfigurationType3(Graph g)
{
for (V v1 : g.vertexSet()) {
for (V v2 : g.vertexSet()) {
if (v1 == v2 || !g.containsEdge(v1, v2))
continue;
for (V v5 : g.vertexSet()) {
if (v1 == v5 || v2 == v5 || g.containsEdge(v1, v5) || g.containsEdge(v2, v5))
continue;
Set triple = new HashSet<>();
triple.add(v1);
triple.add(v2);
triple.add(v5);
Set setY = new HashSet<>();
for (V y : g.vertexSet()) {
if (isYXComplete(g, y, triple)) {
setY.add(y);
}
}
List> anticomponents = findAllAnticomponentsOfY(g, setY);
for (Set setX : anticomponents) {
Set fPrime = findMaximalConnectedSubset(g, setX, v1, v2, v5);
Set setF = new HashSet<>();
setF.addAll(fPrime);
for (V x : setX) {
if (!g.containsEdge(x, v1) && !g.containsEdge(x, v2)
&& !g.containsEdge(x, v5) && hasANeighbour(g, fPrime, x))
setF.add(x);
}
for (V v4 : g.vertexSet()) {
if (v4 == v1 || v4 == v2 || v4 == v5 || g.containsEdge(v2, v4)
|| g.containsEdge(v5, v4) || !g.containsEdge(v1, v4)
|| !hasANeighbour(g, setF, v4) || !hasANonneighbourInX(g, v4, setX)
|| isYXComplete(g, v4, setX))
continue;
for (V v3 : g.vertexSet()) {
if (v3 == v1 || v3 == v2 || v3 == v4 || v3 == v5
|| !g.containsEdge(v2, v3) || !g.containsEdge(v3, v4)
|| !g.containsEdge(v5, v3) || g.containsEdge(v1, v3)
|| !hasANonneighbourInX(g, v3, setX)
|| isYXComplete(g, v3, setX))
continue;
for (V v6 : setF) {
if (v6 == v1 || v6 == v2 || v6 == v3 || v6 == v4 || v6 == v5
|| !g.containsEdge(v4, v6) || g.containsEdge(v1, v6)
|| g.containsEdge(v2, v6)
|| g.containsEdge(v5, v6) && !isYXComplete(g, v6, setX))
continue;
Set verticesForPv5v6 = new HashSet<>();
verticesForPv5v6.addAll(fPrime);
verticesForPv5v6.add(v5);
verticesForPv5v6.add(v6);
verticesForPv5v6.remove(v1);
verticesForPv5v6.remove(v2);
verticesForPv5v6.remove(v3);
verticesForPv5v6.remove(v4);
if (new ConnectivityInspector<>(
new AsSubgraph<>(g, verticesForPv5v6)).pathExists(v6, v5))
{
if (certify) {
List edgeList = new LinkedList<>();
edgeList.add(g.getEdge(v1, v4));
edgeList.add(g.getEdge(v4, v6));
GraphPath path =
new DijkstraShortestPath<>(g).getPath(v6, v5);
edgeList.addAll(path.getEdgeList());
if (path.getLength() % 2 == 1) {
V x = setX.iterator().next();
edgeList.add(g.getEdge(v5, x));
edgeList.add(g.getEdge(x, v1));
} else {
edgeList.add(g.getEdge(v5, v3));
edgeList.add(g.getEdge(v3, v4));
edgeList.add(g.getEdge(v4, v1));
}
double weight = edgeList
.stream().mapToDouble(g::getEdgeWeight).sum();
certificate =
new GraphWalk<>(g, v1, v1, edgeList, weight);
}
return true;
}
}
}
}
}
}
}
}
return false;
}
/**
* If true, the graph is not Berge. Checks whether g contains a Pyramid, Jewel, configuration
* type 1, 2 or 3.
*
* @param g A Graph
* @return whether g contains a pyramid, a jewel, a T1, a T2, or a T3
*/
private boolean routine2(Graph g)
{
return containsPyramid(g) || containsJewel(g) || hasConfigurationType1(g)
|| hasConfigurationType2(g) || hasConfigurationType3(g);
}
/**
* N(a,b) is the set of all {a,b}-complete vertices
*
* @param g A Graph
* @param a A Vertex
* @param b A Vertex
* @return The set of all {a,b}-complete vertices
*/
private Set n(Graph g, V a, V b)
{
return g
.vertexSet().stream().filter(t -> g.containsEdge(t, a) && g.containsEdge(t, b))
.collect(Collectors.toSet());
}
/**
* r(a,b,c) is the cardinality of the largest anticomponent of N(a,b) that contains a
* nonneighbour of c (or 0, if c is N(a,b)-complete)
*
* @param g a Graph
* @param nAB The set of all {a,b}-complete vertices
* @param c A vertex
* @return The cardinality of the largest anticomponent of N(a,b) that contains a nonneighbour
* of c (or 0, if c is N(a,b)-complete)
*/
private int r(Graph g, Set nAB, V c)
{
if (isYXComplete(g, c, nAB))
return 0;
List> anticomponents = findAllAnticomponentsOfY(g, nAB);
return anticomponents.stream().mapToInt(Set::size).max().getAsInt();
}
/**
* Y(a,b,c) is the union of all anticomponents of N(a,b) that have cardinality strictly greater
* than r(a,b,c)
*
* @param g A graph
* @param nAB The set of all {a,b}-complete vertices
* @param c A vertex
* @return A Set of vertices with cardinality greater r(a,b,c)
*/
private Set y(Graph g, Set nAB, V c)
{
int cutoff = r(g, nAB, c);
List> anticomponents = findAllAnticomponentsOfY(g, nAB);
Set res = new HashSet<>();
for (Set anticomponent : anticomponents) {
if (anticomponent.size() > cutoff) {
res.addAll(anticomponent);
}
}
return res;
}
/**
* W(a,b,c) is the anticomponent of N(a,b)+{c} that contains c
*
* @param g A graph
* @param nAB The set of all {a,b}-complete vertices
* @param c A vertex
* @return The anticomponent of N(a,b)+{c} containing c
*/
private Set w(Graph g, Set nAB, V c)
{
Set temp = new HashSet<>();
temp.addAll(nAB);
temp.add(c);
List> anticomponents = findAllAnticomponentsOfY(g, temp);
for (Set anticomponent : anticomponents)
if (anticomponent.contains(c))
return anticomponent;
return null;
}
/**
* Z(a,b,c) is the set of all (Y(a,b,c)+W(a,b,c))-complete vertices
*
* @param g A graph
* @param nAB The set of all {a,b}-complete vertices
* @param c A vertex
* @return A set of vertices
*/
private Set z(Graph g, Set nAB, V c)
{
Set temp = new HashSet<>();
temp.addAll(y(g, nAB, c));
temp.addAll(w(g, nAB, c));
Set res = new HashSet<>();
for (V it : g.vertexSet()) {
if (isYXComplete(g, it, temp))
res.add(it);
}
return res;
}
/**
* X(a,b,c)=Y(a,b,c)+Z(a,b,c)
*
* @param g A graph
* @param nAB The set of all {a,b}-complete vertices
* @param c A vertex
* @return The union of Y(a,b,c) and Z(a,b,c)
*/
private Set x(Graph g, Set nAB, V c)
{
Set res = new HashSet<>();
res.addAll(y(g, nAB, c));
res.addAll(z(g, nAB, c));
return res;
}
/**
* A triple (a,b,c) of vertices is relevant if a,b are distinct and nonadjacent, and c is not
* contained in N(a,b) (possibly c is contained in {a,b}).
*
* @param g A graph
* @param a A vertex
* @param b A vertex
* @param c A vertex
* @return Assessement whether a,b,c is a relevant triple
*/
private boolean isTripleRelevant(Graph g, V a, V b, V c)
{
return a != b && !g.containsEdge(a, b) && !n(g, a, b).contains(c);
}
/**
* Returns a set of vertex sets that may be near-cleaners for an amenable hole in g.
*
* @param g A graph
* @return possible near-cleaners
*/
Set> routine3(Graph g)
{
Set> nUVList = new HashSet<>();
for (V u : g.vertexSet()) {
for (V v : g.vertexSet()) {
if (u == v || !g.containsEdge(u, v))
continue;
nUVList.add(n(g, u, v));
}
}
Set> tripleList = new HashSet<>();
for (V a : g.vertexSet()) {
for (V b : g.vertexSet()) {
if (a == b || g.containsEdge(a, b))
continue;
Set nAB = n(g, a, b);
for (V c : g.vertexSet()) {
if (isTripleRelevant(g, a, b, c)) {
tripleList.add(x(g, nAB, c));
}
}
}
}
Set> res = new HashSet<>();
for (Set nUV : nUVList) {
for (Set triple : tripleList) {
Set temp = new HashSet<>();
temp.addAll(nUV);
temp.addAll(triple);
res.add(temp);
}
}
return res;
}
/**
* Performs the Berge Recognition Algorithm.
*
* First this algorithm is used to test whether $G$ or its complement contain a jewel, a pyramid
* or a configuration of type 1, 2 or 3. If so, it is output that $G$ is not Berge. If not, then
* every shortest odd hole in $G$ is amenable. This asserted, the near-cleaner subsets of $V(G)$
* are determined. For each of them in turn it is checked, if this subset is a near-cleaner and,
* thus, if there is an odd hole. If an odd hole is found, this checker will output that $G$ is
* not Berge. If no odd hole is found, all near-cleaners for the complement graph are determined
* and it will be proceeded as before. If again no odd hole is detected, $G$ is Berge.
*
*
* A certificate can be obtained through the {@link BergeGraphInspector#getCertificate} method,
* if computeCertificate
is true
.
*
* Running this method takes $O(|V|^9)$, and computing the certificate takes $O(|V|^5)$.
*
* @param g A graph
* @param computeCertificate toggles certificate computation
* @return whether g is Berge and, thus, perfect
*/
public boolean isBerge(Graph g, boolean computeCertificate)
{
GraphTests.requireDirectedOrUndirected(g);
Graph complementGraph;
if (g.getType().isSimple())
complementGraph = new SimpleGraph<>(
g.getVertexSupplier(), g.getEdgeSupplier(), g.getType().isWeighted());
else
complementGraph = new Multigraph<>(
g.getVertexSupplier(), g.getEdgeSupplier(), g.getType().isWeighted());
new ComplementGraphGenerator<>(g).generateGraph(complementGraph);
certify = computeCertificate;
if (routine2(g) || routine2(complementGraph)) {
certify = false;
return false;
}
for (Set it : routine3(g)) {
if (routine1(g, it)) {
certify = false;
return false;
}
}
for (Set it : routine3(complementGraph)) {
if (routine1(complementGraph, it)) {
certify = false;
return false;
}
}
certify = false;
return true;
}
/**
* Performs the Berge Recognition Algorithm.
*
* First this algorithm is used to test whether $G$ or its complement contain a jewel, a pyramid
* or a configuration of type 1, 2 or 3. If so, it is output that $G$ is not Berge. If not, then
* every shortest odd hole in $G$ is amenable. This asserted, the near-cleaner subsets of $V(G)$
* are determined. For each of them in turn it is checked, if this subset is a near-cleaner and,
* thus, if there is an odd hole. If an odd hole is found, this checker will output that $G$ is
* not Berge. If no odd hole is found, all near-cleaners for the complement graph are determined
* and it will be proceeded as before. If again no odd hole is detected, $G$ is Berge.
*
*
* This method by default does not compute a certificate. For obtaining a certificate, call
* {@link BergeGraphInspector#isBerge} with computeCertificate=true
.
*
* Running this method takes $O(|V|^9)$.
*
* @param g A graph
* @return whether g is Berge and, thus, perfect
*/
public boolean isBerge(Graph g)
{
return this.isBerge(g, false);
}
/**
* Returns the certificate in the form of a hole or anti-hole in the inspected graph, when the
* {@link BergeGraphInspector#isBerge} method is previously called with
* computeCertificate=true
. Returns null if the inspected graph is perfect.
*
* @return a hole or
* anti-hole in the
* inspected graph, null if the graph is perfect
*/
public GraphPath getCertificate()
{
return certificate;
}
}