package geniusweb.profile.utilityspace; import java.math.BigDecimal; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import geniusweb.issuevalue.Value; import geniusweb.issuevalue.ValueSet; /** * Contains utilities of a ProductOfValue of a {@link SumOfGroupsUtilitySpace}. * So this is similar to a {@link DiscreteValueSetUtilities} but instead of * issues this contains an (ordered) list of issues. This object serializes to * something like * {"partsutils": * {"issues":["issue1","issue2"], * "utilslist":[{"values":["low","low"],"util":0.3},{"values":["high","high"],"util":0.9}]}} The issues field contains a list of N issues in the domain, The * utilslist contains a list of dictionaries, with "values" containing a list of * N issue values, in the same order as the issues list and with "util" * containng the utility value of that set of values. */ @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE) public class PartsUtilities implements ValueSetUtilities { private final List issues; // the real but not-serializable values (because Map can only have String // keys in jackson) private transient final Map utilities = new HashMap<>(); // copy only for serializing private final List utilslist = new LinkedList<>(); /** * * @param issues list of issues * @param utils with keys: list of values and value: utility value for that * list of values. All list-of-values missing from the map are * assumed to have utility 0. This includes partial bids where * list-of-values contain null objects. */ public PartsUtilities(List issues, Map utils) { if (issues == null || utils == null) { throw new IllegalArgumentException( "issues and utils must be not null"); } this.issues = issues; this.utilities.putAll(utils); checkUtilities(); for (ProductOfValue pval : utils.keySet()) { utilslist.add(new PartUtil(pval.getValues(), utils.get(pval))); } } @JsonCreator public PartsUtilities(@JsonProperty("issues") List issues, @JsonProperty("utilslist") List utilslist) { this(issues, list2map(utilslist)); } /** * * @param pval the {@link ProductOfValue} value * @return the utility of the value, in the same order as * {@link #getIssues()}. Returns 0 if there is no utility set for * the given combination of values. Notice, partial bids will * usually have utility 0. * */ @Override public BigDecimal getUtility(Value pval) { BigDecimal val = utilities.get(pval); if (val == null) return BigDecimal.ZERO; return val; } @Override public String isFitting(ValueSet valueset) { return null; // what can we tests here? } /** * @return the issues that are contained here. */ public List getIssues() { return issues; } /** * * @return map with all available values and their utilities. The map and * its contents should all be immutable. */ public Map getUtilities() { return Collections.unmodifiableMap(utilities); } /** * * @param other another {@link PartsUtilities} map. The issues in the other * map must be different from {@link #issues}. * @return new PartsUtils, with the powermap of all combinations of one * element from this and one from the other map, and with the * utilities computed as the sum of thisvalue + othervalue. */ public PartsUtilities add(PartsUtilities other) { for (String issue : issues) { if (other.issues.contains(issue)) { throw new IllegalArgumentException( "Issue " + issue + " exists already"); } } List combinedissues = new LinkedList(issues); combinedissues.addAll(other.issues); Map combinedvalues = new HashMap<>(); for (ProductOfValue productOfValue : this.utilities.keySet()) { for (ProductOfValue otherProductOfValue : other.utilities .keySet()) { BigDecimal combinedutil = getUtility(productOfValue) .add(other.getUtility(otherProductOfValue)); combinedvalues.put(productOfValue.merge(otherProductOfValue), combinedutil); } } return new PartsUtilities(combinedissues, combinedvalues); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((issues == null) ? 0 : issues.hashCode()); result = prime * result + ((utilities == null) ? 0 : utilities.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PartsUtilities other = (PartsUtilities) obj; if (issues == null) { if (other.issues != null) return false; } else if (!issues.equals(other.issues)) return false; if (utilities == null) { if (other.utilities != null) return false; } else if (!utilities.equals(other.utilities)) return false; return true; } @Override public String toString() { return "PartsUtilities[" + issues + "," + utilities + "]"; } private void checkUtilities() { if (utilities.values().stream() .anyMatch(v -> v == null || v.compareTo(BigDecimal.ZERO) < 0 || v.compareTo(BigDecimal.ONE) > 0)) { throw new IllegalArgumentException( "part weights must all be in [0,1]"); } } private static Map list2map( List list) { Map map = new HashMap<>(); for (PartUtil partutil : list) { map.put(new ProductOfValue(partutil.getValues()), partutil.getUtil()); } return map; } /** * * @return the max utility of all values contained here. */ public BigDecimal getMaxUtility() { BigDecimal maxutil = BigDecimal.ZERO; for (ProductOfValue vakye : utilities.keySet()) { if (utilities.get(vakye).compareTo(maxutil) > 0) { maxutil = utilities.get(vakye); } } return maxutil; } } /** * contains list of values, and the utility for this combi of values. The values * make only sense with a corresponding list of issues, but that is managed in * (see {@link PartsUtilities}. *

* immutable. */ @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE) class PartUtil { private final List values; private final BigDecimal util; @JsonCreator public PartUtil(@JsonProperty("values") List values, @JsonProperty("util") BigDecimal util) { this.values = values; this.util = util; } public List getValues() { return values; } public BigDecimal getUtil() { return util; } }