Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revised clique related code #365

Merged
merged 2 commits into from Mar 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -30,7 +30,9 @@
* @param <E> the graph edge type
*
* @author Ewgenij Proschak
* @deprecated In favor of {@link org.jgrapht.alg.clique.BronKerboschCliqueFinder}.
*/
@Deprecated
public class BronKerboschCliqueFinder<V, E>
{
private final Graph<V, E> graph;
Expand Down
Expand Up @@ -41,7 +41,10 @@
* @author Thomas Tschager (thomas.tschager@inf.ethz.ch)
* @author Tomas Hruz (tomas.hruz@inf.ethz.ch)
* @author Philipp Hoppen
*
* @deprecated Use {@link org.jgrapht.alg.clique.CliqueMinimalSeparatorDecomposition} instead.
*/
@Deprecated
public class CliqueMinimalSeparatorDecomposition<V, E>
{
/**
Expand Down
@@ -0,0 +1,117 @@
/*
* (C) Copyright 2005-2017, by Ewgenij Proschak and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
package org.jgrapht.alg.clique;

import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.jgrapht.Graph;
import org.jgrapht.alg.interfaces.MaximalCliqueEnumerationAlgorithm;

/**
* Base implementation of the Bron-Kerbosch algorithm.
*
* @param <V> the graph vertex type
* @param <E> the graph edge type
*
* @author Ewgenij Proschak
*/
abstract class BaseBronKerboschCliqueFinder<V, E>
implements MaximalCliqueEnumerationAlgorithm<V, E>
{
/**
* The underlying graph
*/
protected final Graph<V, E> graph;
/**
* Timeout in nanoseconds
*/
protected final long nanos;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good to have a time limit, but currently there's no way of telling whether the algorithm terminated due to a time limit, or whether it simply finished its computations. I would propose to add a method which returns the termination status of the algorithm. This can be simply a boolean function, e.g. boolean timeLimReached()
or it can be a more fancy 'getStatus()' function which returns a TerminationStatus (enum), such as:
TerminationStatus.Optimal, TerminationStatus.TimeLimReached, TerminationStatus.MaxIterationsReached.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a boolean returning method to test whether the timeout has been reached.

/**
* Whether the last computation terminated due to a time limit.
*/
protected boolean timeLimitReached;
/**
* The result
*/
protected List<Set<V>> allMaximalCliques;
/**
* Size of biggest maximal clique found.
*/
protected int maxSize;

/**
* Constructor
*
* @param graph the input graph; must be simple
* @param timeout the maximum time to wait, if zero no timeout
* @param unit the time unit of the timeout argument
*/
public BaseBronKerboschCliqueFinder(Graph<V, E> graph, long timeout, TimeUnit unit)
{
this.graph = Objects.requireNonNull(graph, "Graph cannot be null");
if (timeout == 0L) {
this.nanos = Long.MAX_VALUE;
} else {
this.nanos = unit.toNanos(timeout);
}
if (this.nanos < 1L) {
throw new IllegalArgumentException("Invalid timeout, must be positive");
}
this.timeLimitReached = false;
}

@Override
public Iterator<Set<V>> iterator()
{
lazyRun();
return allMaximalCliques.iterator();
}

/**
* Create an iterator which returns only the maximum cliques of a graph. The iterator computes
* all maximal cliques and then filters them by the size of the maximum found clique.
*
* @return an iterator which returns only the maximum cliques of a graph
*/
public Iterator<Set<V>> maximumIterator()
{
lazyRun();
return allMaximalCliques.stream().filter(c -> c.size() == maxSize).iterator();
}

/**
* Check the computation has stopped due to a time limit or due to computing all maximal
* cliques.
*
* @return true if the computation has stopped due to a time limit, false otherwise
*/
public boolean isTimeLimitReached()
{
return timeLimitReached;
}

/**
* Lazily start the computation.
*/
protected abstract void lazyRun();

}
@@ -0,0 +1,167 @@
/*
* (C) Copyright 2005-2017, by Ewgenij Proschak and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
package org.jgrapht.alg.clique;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.jgrapht.Graph;
import org.jgrapht.GraphTests;

/**
* Bron-Kerbosch maximal clique enumeration algorithm.
*
* <p>
* Implementation of the Bron-Kerbosch clique enumeration algorithm as described in:
* <ul>
* <li>R. Samudrala and J. Moult. A graph-theoretic algorithm for comparative modeling of protein
* structure. Journal of Molecular Biology, 279(1):287--302, 1998.</li>
* </ul>
*
* <p>
* The algorithm first computes all maximal cliques and then returns the result to the user. A
* timeout can be set using the constructor parameters.
*
* @param <V> the graph vertex type
* @param <E> the graph edge type
*
* @see PivotBronKerboschCliqueFinder
* @see DegeneracyBronKerboschCliqueFinder
*
* @author Ewgenij Proschak
*/
public class BronKerboschCliqueFinder<V, E>
extends BaseBronKerboschCliqueFinder<V, E>
{
/**
* Constructs a new clique finder.
*
* @param graph the input graph; must be simple
*/
public BronKerboschCliqueFinder(Graph<V, E> graph)
{
this(graph, 0L, TimeUnit.SECONDS);
}

/**
* Constructs a new clique finder.
*
* @param graph the input graph; must be simple
* @param timeout the maximum time to wait, if zero no timeout
* @param unit the time unit of the timeout argument
*/
public BronKerboschCliqueFinder(Graph<V, E> graph, long timeout, TimeUnit unit)
{
super(graph, timeout, unit);
}

/**
* Lazily execute the enumeration algorithm.
*/
@Override
protected void lazyRun()
{
if (allMaximalCliques == null) {
if (!GraphTests.isSimple(graph)) {
throw new IllegalArgumentException("Graph must be simple");
}
allMaximalCliques = new ArrayList<>();

long nanosTimeLimit;
try {
nanosTimeLimit = Math.addExact(System.nanoTime(), nanos);
} catch (ArithmeticException ignore) {
nanosTimeLimit = Long.MAX_VALUE;
}

findCliques(
new ArrayList<>(), new ArrayList<>(graph.vertexSet()), new ArrayList<>(),
nanosTimeLimit);
}
}

private void findCliques(
List<V> potentialClique, List<V> candidates, List<V> alreadyFound,
final long nanosTimeLimit)
{
/*
* Termination condition: check if any already found node is connected to all candidate
* nodes.
*/
for (V v : alreadyFound) {
if (candidates.stream().allMatch(c -> graph.containsEdge(v, c))) {
return;
}
}

/*
* Check each candidate
*/
for (V candidate : new ArrayList<>(candidates)) {
/*
* Check if timeout
*/
if (nanosTimeLimit - System.nanoTime() < 0) {
timeLimitReached = true;
return;
}

List<V> newCandidates = new ArrayList<>();
List<V> newAlreadyFound = new ArrayList<>();

// move candidate node to potentialClique
potentialClique.add(candidate);
candidates.remove(candidate);

// create newCandidates by removing nodes in candidates not
// connected to candidate node
for (V newCandidate : candidates) {
if (graph.containsEdge(candidate, newCandidate)) {
newCandidates.add(newCandidate);
}
}

// create newAlreadyFound by removing nodes in alreadyFound
// not connected to candidate node
for (V newFound : alreadyFound) {
if (graph.containsEdge(candidate, newFound)) {
newAlreadyFound.add(newFound);
}
}

// if newCandidates and newAlreadyFound are empty
if (newCandidates.isEmpty() && newAlreadyFound.isEmpty()) {
// potential clique is maximal clique
Set<V> maximalClique = new HashSet<>(potentialClique);
allMaximalCliques.add(maximalClique);
maxSize = Math.max(maxSize, maximalClique.size());
} else {
// recursive call
findCliques(potentialClique, newCandidates, newAlreadyFound, nanosTimeLimit);
}

// move candidate node from potentialClique to alreadyFound
alreadyFound.add(candidate);
potentialClique.remove(candidate);
}
}

}