From 58cdf9ae9355ba8d0a9dc9c2edc8b91fd8ab75c0 Mon Sep 17 00:00:00 2001 From: eugenefischer <66030419+eugenefischer@users.noreply.github.com> Date: Sun, 9 Apr 2023 20:45:03 -0500 Subject: [PATCH] Lookback AA implementation, doesn't currently work --- ...eightBipartiteLookBackAuctionMatching.java | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 src/main/java/MaximumWeightBipartiteLookBackAuctionMatching.java diff --git a/src/main/java/MaximumWeightBipartiteLookBackAuctionMatching.java b/src/main/java/MaximumWeightBipartiteLookBackAuctionMatching.java new file mode 100644 index 0000000..74743e7 --- /dev/null +++ b/src/main/java/MaximumWeightBipartiteLookBackAuctionMatching.java @@ -0,0 +1,212 @@ +import org.jgrapht.Graph; +import org.jgrapht.GraphTests; +import org.jgrapht.alg.interfaces.MatchingAlgorithm; +import org.jgrapht.alg.util.Pair; + +import java.math.BigDecimal; +import java.util.*; + +/* +Maximum weight matching in bipartite graphs with strictly integer edge weights, found using the +unscaled look-back auction algorithm + */ + +public class MaximumWeightBipartiteLookBackAuctionMatching implements MatchingAlgorithm { + + private final Graph graph; + private final Set partition1; + private final Set partition2; + private final BigDecimal delta; + private final Set matching; + private BigDecimal matchingWeight; + private boolean swappedPartitions = false; + + public MaximumWeightBipartiteLookBackAuctionMatching(Graph graph, Set partition1, Set 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"); + int n = Math.max(partition1.size(), partition2.size()); + this.delta = BigDecimal.valueOf(1 / ((double) n + 1)); + this.matching = new LinkedHashSet<>(); + this.matchingWeight = BigDecimal.ZERO; + } + + + /* + Method coded using MaximumWeightBipartiteMatching.class from JgraphT as a model + */ + @Override + public Matching 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"); + } + + /* + If the two partitions are different sizes, the bidders must be the smaller of the two partitions. + */ + Set items; + Set bidders; + if (partition2.size() >= partition1.size()) { + bidders = partition1; + items = partition2; + } + else { + bidders = partition2; + items = partition1; + swappedPartitions = true; + } + + /* + Create a map to track the owner of each item, which is initially null, + and a map to track the price of each item, which is initially 0. + */ + Map owners = new HashMap<>(); + + /* + Create a map to track the prices of the objects + */ + Map prices = new HashMap<>(); + for(V item: items) { + owners.put(item, null); + prices.put(item, BigDecimal.ZERO); + } + + /* + Create a map to track the most valuable object for a bidder + */ + Map mostValuableItems = new HashMap<>(); + + /* + Create a map to track the second most valuable object for a bidder + */ + Map runnerUpItems = new HashMap<>(); + + /* + Create a map to track the bidder value thresholds + */ + Map valueThresholds = new HashMap<>(); + + + //Initialize queue of all bidders that don't currently own an item + Queue unmatchedBidders = new ArrayDeque<>(); + for(V bidder: bidders) { + unmatchedBidders.offer(bidder); + valueThresholds.put(bidder, BigDecimal.ZERO); + mostValuableItems.put(bidder, null); + runnerUpItems.put(bidder, null); + } + + while (unmatchedBidders.size() > 0) { + V bidder = unmatchedBidders.poll(); +// BigDecimal valueThreshold = valueThresholds.get(bidder); + BigDecimal bestValue = BigDecimal.ZERO; + BigDecimal runnerUpValue = BigDecimal.ZERO; + boolean reinitialize = true; +// if (mostValuableItems.get(bidder) != null && runnerUpItems.get(bidder) != null) { +// reinitialize = false; +// //get the weight of the edge between the bidder and the best valued item +// V bestItem = mostValuableItems.get(bidder); +// BigDecimal bestItemWeight = BigDecimal.valueOf(graph.getEdgeWeight(graph.getEdge(bidder, bestItem))); +// bestValue = bestItemWeight.subtract(prices.get(bestItem)); +// V runnerUpItem = runnerUpItems.get(bidder); +// BigDecimal runnerUpWeight = BigDecimal.valueOf(graph.getEdgeWeight(graph.getEdge(bidder, runnerUpItem))); +// runnerUpValue = runnerUpWeight.subtract(prices.get(runnerUpItem)); +// //if both values are still above the threshold +// if (bestValue.compareTo(valueThreshold) >= 0 && runnerUpValue.compareTo(valueThreshold) >= 0) { +// if (bestValue.compareTo(runnerUpValue) < 0) { //if best value is lower than runner up +// BigDecimal tmp = bestValue; +// bestValue = runnerUpValue; +// runnerUpValue = tmp; +// mostValuableItems.put(bidder, runnerUpItem); +// runnerUpItems.put(bidder, bestItem); +// } +// BigDecimal newValueThreshold = bestValue.min(runnerUpValue); +// valueThresholds.put(bidder, newValueThreshold); +// System.out.println("lookback successful"); +// } +// else { +// reinitialize = true; //lookback failed +// } +// } + if (reinitialize){ + bestValue = BigDecimal.ZERO; + runnerUpValue = BigDecimal.ZERO; + for (E edge: graph.edgesOf(bidder)) { + double weight = graph.getEdgeWeight(edge); + if (weight == 0.0) { + continue; + } + V tmpItem = getItem(bidder, edge); + BigDecimal tmpValue = BigDecimal.valueOf(weight).subtract(prices.get(tmpItem)); + if (tmpValue.compareTo(bestValue) >= 0) { + runnerUpValue = bestValue; + bestValue = tmpValue; + runnerUpItems.put(bidder, mostValuableItems.get(bidder)); + mostValuableItems.put(bidder, tmpItem); + } + else if (tmpValue.compareTo(runnerUpValue) >= 0) { + runnerUpValue = tmpValue; + runnerUpItems.put(bidder, tmpItem); + } + } + valueThresholds.put(bidder, runnerUpValue); + } + //Should now have initialized the maps to make look back possible + //skip this bidder if the best value is still zero + if (BigDecimal.ZERO.equals(bestValue)) { + continue; + } + V mostValuableItem = mostValuableItems.get(bidder); + BigDecimal price = prices.get(mostValuableItem); + BigDecimal bid = price.add(bestValue).subtract(runnerUpValue).add(this.delta); + V formerOwner = owners.get(mostValuableItem); + if (formerOwner != null) { + unmatchedBidders.offer(formerOwner); + } + owners.put(mostValuableItem, bidder); + prices.put(mostValuableItem, bid); + } + + for (V item: owners.keySet()) { + if (owners.get(item) != null) { + matching.add(graph.getEdge(item, owners.get(item))); + } + } + + 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) { + if (swappedPartitions) { + return graph.getEdgeSource(edge); + } + else { + return graph.getEdgeTarget(edge); + } + } + + private V getBidder(V item, E edge) { + if (swappedPartitions) { + return graph.getEdgeTarget(edge); + } + else { + return graph.getEdgeSource(edge); + } + } + + public BigDecimal getMatchingWeight() { + return matchingWeight; + } +} \ No newline at end of file