From fbbb5a8792ea1b5013b72cd575913d4c9f7fc0f0 Mon Sep 17 00:00:00 2001 From: eugenefischer <66030419+eugenefischer@users.noreply.github.com> Date: Sat, 22 Oct 2022 14:59:43 -0500 Subject: [PATCH] Update comments --- ...IntegerWeightBipartiteAuctionMatching.java | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/main/java/MaximumIntegerWeightBipartiteAuctionMatching.java b/src/main/java/MaximumIntegerWeightBipartiteAuctionMatching.java index aee42e1..901caf9 100644 --- a/src/main/java/MaximumIntegerWeightBipartiteAuctionMatching.java +++ b/src/main/java/MaximumIntegerWeightBipartiteAuctionMatching.java @@ -5,12 +5,27 @@ import org.jgrapht.alg.interfaces.MatchingAlgorithm; import java.math.BigDecimal; import java.util.*; -/* -Maximum weight matching in bipartite graphs using a forward auction algorithm. This implementation uses the Gauss-Seidel -version of the forward auction algorithm, in which bids are submitted one at a time. For any weighted bipartite graph -with n vertices in the smaller partition, this algorithm will produce a matching that is within n*epsilon of being -optimal. For a weighted bipartite graph with strictly integer weights, this matching will always be optimal, and thus -is a maximum weight matching. +/** + * Maximum weight matching in bipartite graphs with strictly integer edge weights, using a forward auction algorithm. + * This implementation uses the Gauss-Seidel version of the forward auction algorithm, in which bids are submitted + * one at a time. For any weighted bipartite graph with n vertices in the smaller partition, this algorithm will produce + * a matching that is within n*epsilon of being optimal. Using an epsilon = 1/(n+1) ensures that this matching differs + * from an optimal matching by <1. Thus, for a bipartite graph with strictly integer weights, this algorithm returns + * a maximum weight matching. + * + * See: + * "Towards auction algorithms for large dense assignment problems" + * Libor Buš and Pavel Tvrdík, Comput Optim Appl (2009) 43:411-436 + * https://link.springer.com/article/10.1007/s10589-007-9146-5 + * + * See also: + * Many books and papers by Dimitri Bertsekas, including chapter 4 of Linear Network Optimization: + * https://web.mit.edu/dimitrib/www/LNets_Full_Book.pdf + * + * @param the graph vertex type + * @param the graph edge type + * + * @author Eugene Fischer */ public class MaximumIntegerWeightBipartiteAuctionMatching implements MatchingAlgorithm { @@ -28,7 +43,7 @@ public class MaximumIntegerWeightBipartiteAuctionMatching implements Match this.partition1 = Objects.requireNonNull(partition1, "Partition 1 cannot be null"); this.partition2 = Objects.requireNonNull(partition2, "Partition 2 cannot be null"); int n = Math.max(partition1.size(), partition2.size()); - this.epsilon = BigDecimal.valueOf(1 / ((double) n + 1)); + this.epsilon = BigDecimal.valueOf(1 / ((double) n + 1)); //The minimum price increase of a bid this.matching = new LinkedHashSet<>(); this.matchingWeight = BigDecimal.ZERO; } @@ -76,12 +91,13 @@ public class MaximumIntegerWeightBipartiteAuctionMatching implements Match prices.put(item, BigDecimal.ZERO); } - //Initialize queue of all bidders that don't currently own an item + //Create a queue of bidders that don't currently own an item, which is initially all of them Queue unmatchedBidders = new ArrayDeque<>(); for(V bidder: bidders) { unmatchedBidders.offer(bidder); } + //Run the auction while there are remaining unmatched bidders while (unmatchedBidders.size() > 0) { V bidder = unmatchedBidders.poll(); V item = null; @@ -89,15 +105,15 @@ public class MaximumIntegerWeightBipartiteAuctionMatching implements Match BigDecimal runnerUpValue = BigDecimal.valueOf(-1.0); /* Find the items that offer the best and second-best value for the bidder, - then submit a bid equal to the price of the best item plus the marginal value over - the second-best item plus delta. + then submit a bid equal to the price of the best-valued item plus the marginal value over + the second-best-valued item plus epsilon. */ for (E edge: graph.edgesOf(bidder)) { double weight = graph.getEdgeWeight(edge); if(weight == 0.0) { continue; } - V tmp = getItem(bidder, edge); + V tmp = getItem(edge); BigDecimal value = BigDecimal.valueOf(weight).subtract(prices.get(tmp)); if (value.compareTo(bestValue) >= 0) { runnerUpValue = bestValue; @@ -119,21 +135,21 @@ public class MaximumIntegerWeightBipartiteAuctionMatching implements Match prices.put(item, bid); } } + //Add all edges between items and their owners to the matching for (V item: owners.keySet()) { if (owners.get(item) != null) { matching.add(graph.getEdge(item, owners.get(item))); } } - + //Sum the edges of the matching to obtain the matching weight for(E edge: matching) { this.matchingWeight = this.matchingWeight.add(BigDecimal.valueOf(graph.getEdgeWeight(edge))); } - return new MatchingImpl<>(graph, matching, matchingWeight.doubleValue()); } - private V getItem(V bidder, E edge) { + private V getItem(E edge) { if (swappedPartitions) { return graph.getEdgeSource(edge); } @@ -142,14 +158,15 @@ public class MaximumIntegerWeightBipartiteAuctionMatching implements Match } } - private V getBidder(V item, E edge) { - if (swappedPartitions) { - return graph.getEdgeTarget(edge); - } - else { - return graph.getEdgeSource(edge); - } - } +// //method for implementing a forward-reverse auction algorithm, not used here +// private V getBidder(E edge) { +// if (swappedPartitions) { +// return graph.getEdgeTarget(edge); +// } +// else { +// return graph.getEdgeSource(edge); +// } +// } public BigDecimal getMatchingWeight() { return matchingWeight;