initial commit of stub of integer weight scaling algorithm

This commit is contained in:
eugenefischer
2025-03-27 13:42:27 -05:00
parent 5f5d77b0a4
commit 3d302cf8ad
5 changed files with 138 additions and 2 deletions

View File

@@ -1,4 +1,5 @@
public enum AlgorithmType { public enum AlgorithmType {
HUNGARIAN, //Hungarian algorithm HUNGARIAN, //Hungarian algorithm
AUCTION, //Forward auction algorithm AUCTION, //Forward auction algorithm
INTEGER_WEIGHT_SCALING, //integer weight scaling algorithm of Duan and Su
} }

View File

@@ -165,6 +165,8 @@ public class BiGpairSEQ {
public static void setHungarianAlgorithm() { matchingAlgoritmType = AlgorithmType.HUNGARIAN; } public static void setHungarianAlgorithm() { matchingAlgoritmType = AlgorithmType.HUNGARIAN; }
public static void setIntegerWeightScalingAlgorithm() { matchingAlgoritmType = AlgorithmType.INTEGER_WEIGHT_SCALING; }
public static void setAuctionAlgorithm() { matchingAlgoritmType = AlgorithmType.AUCTION; } public static void setAuctionAlgorithm() { matchingAlgoritmType = AlgorithmType.AUCTION; }
public static void setPairingHeap() { public static void setPairingHeap() {

View File

@@ -582,7 +582,7 @@ public class InteractiveInterface {
boolean backToOptions = false; boolean backToOptions = false;
while(!backToOptions) { while(!backToOptions) {
System.out.println("\n---------ALGORITHM OPTIONS----------"); System.out.println("\n---------ALGORITHM OPTIONS----------");
System.out.println("1) Use scaling algorithm by Duan and Su."); System.out.println("1) Use integer weight scaling algorithm by Duan and Su.");
System.out.println("2) Use Hungarian algorithm with Fibonacci heap priority queue"); System.out.println("2) Use Hungarian algorithm with Fibonacci heap priority queue");
System.out.println("3) Use Hungarian algorithm with pairing heap priority queue"); System.out.println("3) Use Hungarian algorithm with pairing heap priority queue");
System.out.println("4) Use auction algorithm"); System.out.println("4) Use auction algorithm");
@@ -590,13 +590,19 @@ public class InteractiveInterface {
try { try {
input = sc.nextInt(); input = sc.nextInt();
switch (input) { switch (input) {
case 1 -> System.out.println("This option is not yet implemented. Choose another."); case 1 -> {
BiGpairSEQ.setIntegerWeightScalingAlgorithm();
System.out.println("MWM algorithm set to integer weight scaling algorithm of Duan and Su");
backToOptions = true;
}
case 2 -> { case 2 -> {
BiGpairSEQ.setHungarianAlgorithm();
BiGpairSEQ.setFibonacciHeap(); BiGpairSEQ.setFibonacciHeap();
System.out.println("MWM algorithm set to Hungarian with Fibonacci heap"); System.out.println("MWM algorithm set to Hungarian with Fibonacci heap");
backToOptions = true; backToOptions = true;
} }
case 3 -> { case 3 -> {
BiGpairSEQ.setHungarianAlgorithm();
BiGpairSEQ.setPairingHeap(); BiGpairSEQ.setPairingHeap();
System.out.println("MWM algorithm set to Hungarian with pairing heap"); System.out.println("MWM algorithm set to Hungarian with pairing heap");
backToOptions = true; backToOptions = true;

View File

@@ -36,6 +36,7 @@ public class MaximumIntegerWeightBipartiteAuctionMatching<V, E> implements Match
private final BigDecimal epsilon; private final BigDecimal epsilon;
private final Set<E> matching; private final Set<E> matching;
private BigDecimal matchingWeight; private BigDecimal matchingWeight;
private boolean swappedPartitions = false; private boolean swappedPartitions = false;
public MaximumIntegerWeightBipartiteAuctionMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2) { public MaximumIntegerWeightBipartiteAuctionMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2) {

View File

@@ -0,0 +1,126 @@
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;
}
}