Skip to content

Commit

Permalink
Merge pull request #41 from spectrumauctions/develop
Browse files Browse the repository at this point in the history
Release v0.6.3: sampling reserve price improvements, GSVM/LSVM duplicate fix, supplementary round time limits / tolerances
  • Loading branch information
islerfab authored Mar 6, 2019
2 parents 440648e + e296f0b commit 10bc721
Show file tree
Hide file tree
Showing 21 changed files with 888 additions and 694 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.spectrumauctions</groupId>
<artifactId>sats</artifactId>
<version>0.6.2</version>
<version>0.6.3</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down Expand Up @@ -146,7 +146,7 @@
<connection>scm:git:git://github.com/spectrumauctions/sats.git</connection>
<developerConnection>scm:git:git@github.com:spectrumauctions/sats.git</developerConnection>
<url>https://github.com/spectrumauctions/sats/tree/master/</url>
<tag>v0.6.2</tag>
<tag>v0.6.3</tag>
</scm>

<dependencies>
Expand Down Expand Up @@ -238,7 +238,7 @@
<dependency>
<groupId>edu.harvard.eecs</groupId>
<artifactId>jopt</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion sats.iml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
<orderEntry type="library" name="Maven: org.nutz:nutz:1.r.56.r3" level="project" />
<orderEntry type="library" name="Maven: org.mockito:mockito-all:1.9.5" level="project" />
<orderEntry type="library" name="Maven: edu.harvard.eecs:jopt:1.3.2" level="project" />
<orderEntry type="library" name="Maven: edu.harvard.eecs:jopt:1.3.3" level="project" />
<orderEntry type="library" name="Maven: com.datumbox:lpsolve:5.5.2.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: cplex:cplex:12.8" level="project" />
</component>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ public abstract class CCAMechanism<T extends Good> implements AuctionMechanism<T
protected static final BigDecimal DEFAULT_STARTING_PRICE = BigDecimal.ZERO;
private static final double DEFAULT_EPSILON = 0.1;
private static final int DEFAULT_MAX_ROUNDS = 1000;
private static final int DEFAULT_TIME_LIMIT = 600;
private static final int DEFAULT_REL_RESULT_POOL_TOLERANCE = 0;
private static final int DEFAULT_ABS_RESULT_POOL_TOLERANCE = 0;
private static final int DEFAULT_CLOCKPHASE_NUMBER_OF_BUNDLES = 1;

protected List<Bidder<T>> bidders;
protected int totalRounds = 1;
protected BigDecimal fallbackStartingPrice = DEFAULT_STARTING_PRICE;
protected double epsilon = DEFAULT_EPSILON;
protected int maxRounds = DEFAULT_MAX_ROUNDS;
protected double timeLimit = DEFAULT_TIME_LIMIT;

protected double relativeResultPoolTolerance = DEFAULT_REL_RESULT_POOL_TOLERANCE;
protected double absoluteResultPoolTolerance = DEFAULT_ABS_RESULT_POOL_TOLERANCE;
protected PaymentRuleEnum paymentRule = PaymentRuleEnum.VCG;

// The number of bundles returned in a demand query in the clock phase
Expand Down Expand Up @@ -98,6 +105,49 @@ public void setMaxRounds(int maxRounds) {
this.maxRounds = maxRounds;
}

public double getTimeLimit() {
return timeLimit;
}

/**
* This time limit (in seconds) is applied to all the demand queries
*/
public void setTimeLimit(double timeLimit) {
this.timeLimit = timeLimit;
}

public double getAbsoluteResultPoolTolerance() {
return absoluteResultPoolTolerance;
}


public double getRelativeResultPoolTolerance() {
return relativeResultPoolTolerance;
}

/**
* In some cases in CCA (e.g., the profit maximizing supplementary round), we're interested in a collection of
* most optimal results.
* Since that can take a long time and setting a time limit is sometimes not the best option to solve this,
* the user can specify a result pool tolerance.
*
* This tolerance defines "how far the worst solution is allowed to be from the best solution to stop looking
* for other solutions and return the current collection of solutions".
*
*/
public void setAbsoluteResultPoolTolerance(double absoluteResultPoolTolerance) {
this.absoluteResultPoolTolerance = absoluteResultPoolTolerance;
}

/**
* Same as {@link #setAbsoluteResultPoolTolerance(double)}, but in a relative way (e.g., 0.1 for 10% tolerance).
* This is usually more helpful than the absolute tolerance, but if the best solution has an objective value of 0,
* the relative tolerance won't work.
*/
public void setRelativeResultPoolTolerance(double relativeResultPoolTolerance) {
this.relativeResultPoolTolerance = relativeResultPoolTolerance;
}

public void setClockPhaseNumberOfBundles(int clockPhaseNumberOfBundles) {
this.clockPhaseNumberOfBundles = clockPhaseNumberOfBundles;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.spectrumauctions.sats.mechanism.cca;

import com.google.common.base.Preconditions;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spectrumauctions.sats.core.bidlang.generic.GenericBid;
Expand All @@ -20,11 +20,11 @@
import org.spectrumauctions.sats.mechanism.domain.mechanisms.AuctionMechanism;
import org.spectrumauctions.sats.mechanism.vcg.VCGMechanism;
import org.spectrumauctions.sats.opt.domain.Allocation;
import org.spectrumauctions.sats.opt.domain.GenericDemandQueryMIP;
import org.spectrumauctions.sats.opt.domain.GenericDemandQueryMIPBuilder;
import org.spectrumauctions.sats.opt.domain.GenericDemandQueryResult;
import org.spectrumauctions.sats.opt.xorq.XORQWinnerDetermination;

import javax.management.RuntimeErrorException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -76,43 +76,63 @@ public MechanismResult<T> getMechanismResult() {
@Override
public void calculateSampledStartingPrices(int bidsPerBidder, int numberOfWorldSamples, double fraction, long seed) {
GenericWorld<T> world = (GenericWorld<T>) bidders.stream().findAny().map(Bidder::getWorld).orElseThrow(NoSuchFieldError::new);

Map<G, SimpleRegression> regressions = new HashMap<>();
for (GenericDefinition<T> genericDefinition : world.getAllGenericDefinitions()) {
SimpleRegression regression = new SimpleRegression();
regression.addData(0.0, 0.0);
regressions.put((G) genericDefinition, regression);
}

RNGSupplier rngSupplier = new JavaUtilRNGSupplier(seed);
for (int i = 0; i < numberOfWorldSamples; i++) {
List<Bidder<T>> alternateBidders = bidders.stream().map(b -> b.drawSimilarBidder(rngSupplier)).collect(Collectors.toList());
for (Bidder<T> bidder : alternateBidders) {
XORQRandomOrderSimple<G, T> valueFunction;
try {
// We need a fixed order -> List
List<GenericDefinition<T>> genericDefinitions = new ArrayList<>(world.getAllGenericDefinitions());
try {
ArrayList<Double> yVector = new ArrayList<>();
ArrayList<ArrayList<Double>> xVectors = new ArrayList<>();

RNGSupplier rngSupplier = new JavaUtilRNGSupplier(seed);
for (int i = 0; i < numberOfWorldSamples; i++) {
List<Bidder<T>> alternateBidders = bidders.stream().map(b -> b.drawSimilarBidder(rngSupplier)).collect(Collectors.toList());
for (Bidder<T> bidder : alternateBidders) {
XORQRandomOrderSimple<G, T> valueFunction;
valueFunction = (XORQRandomOrderSimple) bidder.getValueFunction(XORQRandomOrderSimple.class, rngSupplier);
valueFunction.setIterations(bidsPerBidder);

Iterator<? extends GenericValue<G, T>> bidIterator = valueFunction.iterator();
while (bidIterator.hasNext()) {
GenericValue<G, T> bid = bidIterator.next();
for (Map.Entry<G, Integer> entry : bid.getQuantities().entrySet()) {
double y = bid.getValue().doubleValue() * entry.getValue() / bid.getTotalQuantity();
regressions.get(entry.getKey()).addData(entry.getValue().doubleValue(), y);
yVector.add(bid.getValue().doubleValue());
ArrayList<Double> xVector = new ArrayList<>();
for (GenericDefinition<T> definition : genericDefinitions) {
xVector.add((double) bid.getQuantities().getOrDefault(definition, 0));
}
xVectors.add(xVector);
}
} catch (UnsupportedBiddingLanguageException e) {
throw new RuntimeException(e);
}
}
}

for (Map.Entry<G, SimpleRegression> entry : regressions.entrySet()) {
double y = entry.getValue().predict(1);
double price = y * fraction;
logger.info("{}:\nFound y of {}, setting starting price to {}.",
entry.getKey(), y, price);
setStartingPrice(entry.getKey(), BigDecimal.valueOf(price));

OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
regression.setNoIntercept(true);
double[] yArray = new double[yVector.size()];
for (int i = 0; i < yVector.size(); i++) {
yArray[i] = yVector.get(i);
}
double[][] xArrays = new double[xVectors.size()][xVectors.get(0).size()];
for (int i = 0; i < xVectors.size(); i++) {
for (int j = 0; j < xVectors.get(i).size(); j++) {
xArrays[i][j] = xVectors.get(i).get(j);
}
}

regression.newSampleData(yArray, xArrays);
double[] betas = regression.estimateRegressionParameters();

for (int i = 0; i < genericDefinitions.size(); i++) {
double prediction = Math.max(betas[i], 0.0);
double price = prediction * fraction;
logger.info("{}:\nFound prediction of {}, setting starting price to {}.",
genericDefinitions.get(i), prediction, price);
setStartingPrice((G) genericDefinitions.get(i), BigDecimal.valueOf(price));
}

} catch (UnsupportedBiddingLanguageException e) {
// Catching this error here, because it's very unlikely to happen and we don't want to bother
// the user with handling this error. We just log it and don't set the starting prices.
logger.error("Tried to calculate sampled starting prices, but {} doesn't support the " +
"SizeBasedUniqueRandomXOR bidding language. Not setting any starting prices.", world);
}
}

Expand Down Expand Up @@ -165,7 +185,9 @@ private Collection<GenericBid<G, T>> runClockPhase() {
while (!done) {
demand = new HashMap<>();
for (Bidder<T> bidder : bidders) {
List<? extends GenericDemandQueryResult<G, T>> genericDemandQueryResults = genericDemandQueryMIPBuilder.getDemandQueryMipFor(bidder, prices, epsilon).getResultPool(clockPhaseNumberOfBundles);
GenericDemandQueryMIP<G, T> demandQueryMIP = genericDemandQueryMIPBuilder.getDemandQueryMipFor(bidder, prices, epsilon);
demandQueryMIP.setTimeLimit(getTimeLimit());
List<? extends GenericDemandQueryResult<G, T>> genericDemandQueryResults = demandQueryMIP.getResultPool(clockPhaseNumberOfBundles);
// Fill the generic map
GenericValue<G, T> firstResult = genericDemandQueryResults.get(0).getResultingBundle();
if (firstResult.getTotalQuantity() > 0) {
Expand Down Expand Up @@ -229,7 +251,7 @@ private Collection<GenericBid<G, T>> runSupplementaryRound() {
for (Bidder<T> bidder : bidders) {
List<GenericValue<G, T>> newValues = new ArrayList<>();
for (GenericSupplementaryRound<G, T> supplementaryRound : supplementaryRounds) {
newValues.addAll(supplementaryRound.getSupplementaryBids(this, bidder));
newValues.addAll(supplementaryRound.getSupplementaryBids(this, bidder));
}

GenericBid<G, T> bidderBid = bidsAfterClockPhase.stream().filter(bid -> bidder.equals(bid.getBidder())).findFirst().orElseThrow(NoSuchElementException::new);
Expand Down
Loading

0 comments on commit 10bc721

Please sign in to comment.