
com.hazelcast.internal.util.graph.BronKerboschCliqueFinder Maven / Gradle / Ivy
The newest version!
/*
* Original work Copyright (c) 2005-2021, by Ewgenij Proschak and Contributors.
* Modified work Copyright (c) 2020-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.internal.util.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
public class BronKerboschCliqueFinder {
private final Graph graph;
private final long nanos;
private boolean timeLimitReached;
private List> maximumCliques;
/**
* 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 BronKerboschCliqueFinder(Graph graph, long timeout, TimeUnit unit) {
this.graph = requireNonNull(graph, "Graph cannot be null");
if (timeout < 1L) {
throw new IllegalArgumentException("Invalid timeout, must be positive!");
}
this.nanos = unit.toNanos(timeout);
}
/**
* Constructs a new clique finder.
*
* @param graph the input graph; must be "simple".
*/
public BronKerboschCliqueFinder(Graph graph) {
this(graph, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
/**
* Computes and returns the maximum cliques of the given graph.
*
* @return the maximum cliques of the given graph.
*/
public Collection> computeMaxCliques() {
lazyRun();
return maximumCliques;
}
/**
* 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 execute the enumeration algorithm.
*/
private void lazyRun() {
if (maximumCliques != null) {
return;
}
maximumCliques = new ArrayList<>();
long nanosTimeLimit;
try {
nanosTimeLimit = Math.addExact(System.nanoTime(), nanos);
} catch (ArithmeticException e) {
nanosTimeLimit = Long.MAX_VALUE;
}
findCliques(new HashSet<>(), new HashSet<>(graph.vertexSet()), new HashSet<>(), nanosTimeLimit);
}
private void findCliques(Collection potentialClique, Collection candidates, Collection alreadyFound,
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;
}
}
Iterator it = candidates.iterator();
while (it.hasNext()) {
if (nanosTimeLimit - System.nanoTime() < 0) {
timeLimitReached = true;
return;
}
V candidate = it.next();
it.remove();
// move candidate node to potentialClique
potentialClique.add(candidate);
// create newCandidates by removing nodes in candidates
// not connected to candidate node
Collection newCandidates = populate(candidates, candidate);
// create newAlreadyFound by removing nodes in alreadyFound
// not connected to candidate node
Collection newAlreadyFound = populate(alreadyFound, candidate);
// if newCandidates and newAlreadyFound are empty
if (newCandidates.isEmpty() && newAlreadyFound.isEmpty()) {
// potential clique is maximum clique
addMaxClique(potentialClique);
} else {
// recursive call
findCliques(potentialClique, newCandidates, newAlreadyFound, nanosTimeLimit);
}
// move candidate node from potentialClique to alreadyFound
alreadyFound.add(candidate);
potentialClique.remove(candidate);
}
}
private Collection populate(Collection candidates, V candidate) {
Collection newCandidates = new HashSet<>();
for (V newCandidate : candidates) {
if (graph.containsEdge(candidate, newCandidate)) {
newCandidates.add(newCandidate);
}
}
return newCandidates;
}
private void addMaxClique(Collection potentialClique) {
if (maximumCliques.isEmpty() || potentialClique.size() == maximumCliques.get(0).size()) {
maximumCliques.add(new HashSet<>(potentialClique));
} else if (potentialClique.size() > maximumCliques.get(0).size()) {
maximumCliques.clear();
maximumCliques.add(new HashSet<>(potentialClique));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy