Files
BiGpairSEQ/src/main/java/MaximumIntegerWeightBipartiteMatching.java

127 lines
6.2 KiB
Java

import org.jgrapht.Graph;
import org.jgrapht.GraphTests;
import org.jgrapht.alg.interfaces.MatchingAlgorithm;
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
import java.math.BigInteger;
import java.util.*;
/**
* Maximum weight matching in bipartite graphs with strictly integer edge weights, using a scaling algorithm
* via Duan and Su with O(m * sqrt(n) * log(N)) running time, where m is the number of edges, n is the number
* of vertices in the larger partition of the graph, and N is the maximum integer edge weight.
*
* See:
* "A Scaling Algorithm for Maximum Weight Matching in Bipartite Graphs"
* Ran Duan and Hsin-Hao Su, Proceedings of the Twenty-Third Annual ACM-SIAM Symposium on Discrete Algorithms, p. 1413-1424. (2012)
* https://web.eecs.umich.edu/~pettie/matching/Duan-Su-scaling-bipartite-matching.pdf
*
* @param <V> the graph vertex type
* @param <E> the graph edge type
*
* @author Eugene Fischer
*/
public class MaximumIntegerWeightBipartiteMatching<V, E> implements MatchingAlgorithm<V, E> {
private final Graph<V, E> graph;
private final Set<V> partition1;
private final Set<V> partition2;
private final Set<E> matching;
private final BigInteger maxPartitionSize;
private final BigInteger maxEdgeWeight;
private BigInteger matchingWeight;
private BigInteger delta;
private final Integer numberOfScales;
public final double LOG_2 = Math.log(2.0); //constant for logBigInteger function
private final int MAX_DIGITS_2 = 977; //constant for logBigInteger function
public MaximumIntegerWeightBipartiteMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2) {
this.graph = GraphTests.requireUndirected(graph);
this.partition1 = Objects.requireNonNull(partition1, "Partition 1 cannot be null");
this.partition2 = Objects.requireNonNull(partition2, "Partition 2 cannot be null");
Integer n = Math.max(partition1.size(), partition2.size());
this.maxPartitionSize = new BigInteger(String.valueOf(Math.max(partition1.size(), partition2.size())));
Integer maxEdgeWeight = 0;
for (E edge: graph.edgeSet()) {
Integer weight = Integer.valueOf(String.valueOf(graph.getEdgeWeight(edge)));
maxEdgeWeight = Math.max(maxEdgeWeight, weight);
}
this.maxEdgeWeight = new BigInteger(maxEdgeWeight.toString());
this.matching = new LinkedHashSet<>();
this.matchingWeight = BigInteger.ZERO;
//This takes the integer square root of the maxPartitionSize instead of the square root, not sure if this will introduce an error
Double exponent = maxEdgeWeight / Math.sqrt(Double.parseDouble(n.toString()));
exponent = Math.log10(exponent);
exponent = Math.floor(exponent);
this.delta = BigInteger.TWO.pow(Integer.parseInt(exponent.toString()));
Double numberOfScales = Math.ceil(Math.log10(Double.parseDouble(maxEdgeWeight.toString())));
this.numberOfScales = Integer.parseInt(numberOfScales.toString());
}
public MaximumIntegerWeightBipartiteMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2, Integer maxEdgeWeight) {
this.graph = GraphTests.requireUndirected(graph);
this.partition1 = Objects.requireNonNull(partition1, "Partition 1 cannot be null");
this.partition2 = Objects.requireNonNull(partition2, "Partition 2 cannot be null");
Integer n = Math.max(partition1.size(), partition2.size());
this.maxPartitionSize = new BigInteger(String.valueOf(Math.max(partition1.size(), partition2.size())));
this.maxEdgeWeight = new BigInteger(maxEdgeWeight.toString());
this.matching = new LinkedHashSet<>();
this.matchingWeight = BigInteger.ZERO;
Double exponent = maxEdgeWeight / Math.sqrt(Double.parseDouble(n.toString()));
exponent = Math.log10(exponent);
exponent = Math.floor(exponent);
this.delta = BigInteger.TWO.pow(Integer.parseInt(exponent.toString()));
Double numberOfScales = Math.ceil(Math.log10(Double.parseDouble(maxEdgeWeight.toString())));
this.numberOfScales = Integer.parseInt(numberOfScales.toString());
}
public MaximumIntegerWeightBipartiteMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2, BigInteger maxEdgeWeight) {
this.graph = GraphTests.requireUndirected(graph);
this.partition1 = Objects.requireNonNull(partition1, "Partition 1 cannot be null");
this.partition2 = Objects.requireNonNull(partition2, "Partition 2 cannot be null");
Integer n = Math.max(partition1.size(), partition2.size());
this.maxPartitionSize = new BigInteger(String.valueOf(Math.max(partition1.size(), partition2.size())));
this.maxEdgeWeight = maxEdgeWeight;
this.matching = new LinkedHashSet<>();
this.matchingWeight = BigInteger.ZERO;
Double exponent = maxEdgeWeight.doubleValue() / Math.sqrt(Double.parseDouble(n.toString()));
exponent = Math.log10(exponent);
exponent = Math.floor(exponent);
this.delta = BigInteger.TWO.pow(Integer.parseInt(exponent.toString()));
Double numberOfScales = Math.ceil(Math.log10(Double.parseDouble(maxEdgeWeight.toString())));
this.numberOfScales = Integer.parseInt(numberOfScales.toString());
}
@Override
public Matching<V, E> getMatching() {
/*
* Test input instance
*/
if (!GraphTests.isSimple(graph)) {
throw new IllegalArgumentException("Only simple graphs supported");
}
if (!GraphTests.isBipartitePartition(graph, partition1, partition2)) {
throw new IllegalArgumentException("Graph partition is not bipartite");
}
}
//function to return the natural logarithm of an arbitrary BigInteger
//by leonbloy via https://stackoverflow.com/questions/6827516/logarithm-for-biginteger
private double logBigInteger(BigInteger val) {
if (val.signum() < 1)
return val.signum() < 0 ? Double.NaN : Double.NEGATIVE_INFINITY;
int blex = val.bitLength() - MAX_DIGITS_2; // any value in 60..1023 works here
if (blex > 0)
val = val.shiftRight(blex);
double res = Math.log(val.doubleValue());
return blex > 0 ? res + blex * LOG_2 : res;
}
}