package geniusweb.boa.biddingstrategy;
import java.math.BigDecimal;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import geniusweb.actions.Accept;
import geniusweb.actions.Action;
import geniusweb.actions.EndNegotiation;
import geniusweb.actions.Offer;
import geniusweb.actions.PartyId;
import geniusweb.boa.BoaState;
import geniusweb.inform.Settings;
import geniusweb.issuevalue.Bid;
import geniusweb.profile.Profile;
import geniusweb.profile.utilityspace.LinearAdditive;
import tudelft.utilities.immutablelist.ImmutableList;
/**
* This places offers in a time-dependent-only way, monotonically decreasing the
* utility of the offers according to the given parameters. Implements a
* TimeDependentAgent Strategy adapted from S. Shaheen Fatima Michael Wooldridge
* Nicholas R. Jennings Optimal Negotiation Strategies for Agents with
* Incomplete Information http://eprints.ecs.soton.ac.uk/6151/1/atal01.pdf
*
* Required: the profile is {@link LinearAdditive}. Does not currently handle
* profile changes.
*
* As basis, the original Genius'
* negotiator.boaframework.offeringstrategy.other.GeniusTimeDependent_Offering
* was used. However that seems to ignore the reservation value. This was fixed
* here.
*
* The default strategy was extended to enable the usage of opponent models.
*
* The time-dependent utility target function is f(t) = k + (1.0 - k) *
* Math.pow((t - startTime) / (1.0 - startTime), 1.0 / e). The utility target is
* then set at min+(max-min)*f(t) where the default values for min = either the
* utility of the reservation bid or the absolute minimum utility of the
* profile, and default for max the maximum attainable utility.
*
*
* Parameters:
*
* - e: see {@link #getE(BoaState)}
*
- k: see {@link #getK(BoaState)}
*
- min: see {@link #getMin(BoaState)}.
*
- max: see {@link #getMax(BoaState)}
*
*/
public class TimeDependentBiddingStrategy implements BiddingStrategy {
// bidSpace=null means we're not yet initialized.
private ExtendedUtilSpace bidSpace = null;
private Double e, k, min, max; // min, max attainable utility
private PartyId me;
@Override
public Action getAction(BoaState state) {
if (bidSpace == null) {
init(state);
}
double utilityGoal = p(
state.getProgress().get(System.currentTimeMillis()));
// if there is no opponent model available
ImmutableList bidOptions = bidSpace
.getBids((BigDecimal.valueOf(utilityGoal)));
if (bidOptions.size().intValue() == 0) {
// should not happen unless profile is broken, emergency exit
state.getReporter().log(Level.WARNING,
"No viable bids found around current utility target");
Offer lastOffer = state.getLastReceivedOffer();
if (lastOffer == null)
return new EndNegotiation(me);
return new Accept(me, lastOffer.getBid());
}
Bid pickedBid = bidOptions.get(ThreadLocalRandom.current()
.nextInt(bidOptions.size().intValue()));
return new Offer(me, pickedBid);
}
/**
* Overrideable for hard configuring this component.
*
* @param state the {@link BoaState}
* @return the parameter e for the time depemdency function
* {@link #f(double)}. The parameter is 1 by default, or the value
* for the "e" parameter in the {@link Settings} if available.
*/
protected Double getE(BoaState state) {
return state.getSettings().getParameters().getDouble("e", 1d, 0d, 1d);
}
/**
* Overrideable for hard configuring this component.
*
* @param state the {@link BoaState}
* @return the parameter k for the time depemdency function
* {@link #f(double)}. The parameter is 0 by default, or the value
* for the "k" parameter in the {@link Settings} if available.
*/
protected Double getK(BoaState state) {
return state.getSettings().getParameters().getDouble("k", 0d, 0d, 1d);
}
/**
* Assumes {@link #bidSpace} has been initialized.
*
* Overrideable for hard configuring this component.
*
* @param state the {@link BoaState}
* @return the min value for {@link #p(double)}. We use the "min" parameter
* in the {@link Settings} if available. If not available, the
* parameter is computed as the utility of the reservation bid. If
* there is no reservation bid, we use the minimum utility of the
* available profile.
*/
protected Double getMin(BoaState state) {
Double val = state.getSettings().getParameters().getDouble("min", null,
0d, 1d);
if (val != null)
return val;
// val=null, try the reservation bid
LinearAdditive profile = (LinearAdditive) state.getProfile();
if (profile.getReservationBid() != null) {
return profile.getUtility(profile.getReservationBid())
.doubleValue();
}
return bidSpace.getMin().doubleValue();
}
/**
* Assumes {@link #bidSpace} has been initialized.
*
* Overrideable for hard configuring this component.
*
* @param state the {@link BoaState}
* @return the max value for {@link #p(double)}. We use the "max" parameter
* in the {@link Settings} if available. If not available, we use
* the maximum utility of the available profile.
*/
protected Double getMax(BoaState state) {
Double val = state.getSettings().getParameters().getDouble("max", null,
0d, 1d);
if (val != null)
return val;
return bidSpace.getMax().doubleValue();
}
// /**
// * @return the most recent bid that was offered, or null if no offer has
// * been done yet.
// */
// private Bid getLastBid(List history) {
// for (int n = history.size() - 1; n >= 0; n--) {
// Action action = history.get(n);
// if (action instanceof Offer) {
// return ((Offer) action).getBid();
// }
// }
// return null;
// }
/**
* initializes bidSpace
*
* @param state
*/
private void init(BoaState state) {
this.me = state.getSettings().getID();
Profile prof = state.getProfile();
if (!(prof instanceof LinearAdditive))
throw new IllegalArgumentException(
"Requires a LinearAdditive space but got " + prof);
LinearAdditive profile = (LinearAdditive) prof;
this.bidSpace = getBidSpace(profile);
this.e = getE(state);
this.k = getK(state);
this.min = getMin(state);
this.max = getMax(state);
state.getReporter().log(Level.INFO,
"BOA biddingstrategy min util = " + this.min);
}
/**
* Makes sure the target utility with in the acceptable range according to
* the domain
*
* @param t the normalized current time in the negotiation, where t=9 at
* start of negotiation and t=1 at end of negotiation.
* @return double
*/
private double p(double t) {
return this.min + (this.max - this.min) * (1.0 - f(t));
}
/**
* From the paper:
*
* A wide range of time dependent functions can be defined by varying the
* way in which f(t) is computed. However, functions must ensure that 0 <=
* f(t) <= 1, f(0) = k, and f(1) = 1.
*
* That is, the offer will always be between the value range, at the
* beginning it will give the initial constant and when the deadline is
* reached, it will offer the reservation value.
*
* @param t the normalized current time in the negotiation, where t=9 at
* start of negotiation and t=1 at end of negotiation.
* @return
*/
private double f(double t) {
double ft = k + (1.0 - k) * Math.pow(t, 1.0 / e);
return ft;
}
/**
* Factory method to get the {@link ExtendedUtilSpace} helper class.
* Overridable eg for tests
*
* @param profile a {@link LinearAdditive} profile
*
* @return {@link ExtendedUtilSpace}
*/
protected ExtendedUtilSpace getBidSpace(LinearAdditive profile) {
return new ExtendedUtilSpace(profile);
}
}