Changeset 53 for profile/src/main
- Timestamp:
- 12/18/24 13:28:59 (5 days ago)
- Location:
- profile/src/main/java/geniusweb/profile
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
profile/src/main/java/geniusweb/profile/DefaultPartialOrdering.java
r52 r53 1 1 package geniusweb.profile; 2 2 3 import java.io.IOException; 3 4 import java.util.ArrayList; 4 5 import java.util.Arrays; 5 import java.util.HashMap;6 6 import java.util.HashSet; 7 7 import java.util.LinkedList; … … 10 10 import java.util.Set; 11 11 12 import com.fasterxml.jackson.annotation.JsonAutoDetect; 13 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; 12 14 import com.fasterxml.jackson.annotation.JsonCreator; 13 import com.fasterxml.jackson.annotation.JsonGetter;14 15 import com.fasterxml.jackson.annotation.JsonProperty; 16 import com.fasterxml.jackson.core.JsonGenerator; 17 import com.fasterxml.jackson.databind.DeserializationContext; 18 import com.fasterxml.jackson.databind.JsonSerializer; 19 import com.fasterxml.jackson.databind.KeyDeserializer; 20 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.SerializerProvider; 22 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 23 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 15 24 16 25 import geniusweb.issuevalue.Bid; 17 26 import geniusweb.issuevalue.Domain; 27 28 /** 29 * Strange that we need to do this. Bid is completely standard. The complication 30 * that this solves is that the keys are places as STRING in the json code 31 * because json allows only strings as key. 32 */ 33 @SuppressWarnings("serial") 34 class BidDeserializer extends KeyDeserializer { 35 private ObjectMapper jackson = new ObjectMapper(); 36 37 @Override 38 public Bid deserializeKey(String key, DeserializationContext ctxt) 39 throws IOException { 40 return jackson.readValue(key, Bid.class); 41 } 42 43 } 44 45 /** 46 * Serializes a Bid to string. Unfortunately by default jackson uses 47 * key.toString() for serializing (rather than 48 * {@link ObjectMapper#writeValueAsString(Object)}). 49 * 50 */ 51 class BidSerializer extends JsonSerializer<Bid> { 52 private ObjectMapper jackson = new ObjectMapper(); 53 54 @Override 55 public void serialize(Bid bid, JsonGenerator gen, 56 SerializerProvider serializers) throws IOException { 57 gen.writeFieldName(jackson.writeValueAsString(bid)); 58 } 59 60 } 18 61 19 62 /** … … 26 69 * limited anyway. 27 70 */ 71 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE) 28 72 public class DefaultPartialOrdering extends DefaultProfile 29 73 implements PartialOrdering { … … 33 77 * other bid. 34 78 */ 35 private final Map<Bid, Set<Bid>> isBetter; 79 @JsonDeserialize(keyUsing = BidDeserializer.class) 80 @JsonSerialize(keyUsing = BidSerializer.class) 81 private final Map<Bid, Set<Bid>> better; 36 82 37 83 /** 38 * 39 * @param name name of the profile 40 * @param domain the {@link Domain} 41 * @param reservationbid the reservation bid, this is the minimum acceptable 42 * bid 43 * @param bids a list of bids that this ordering contains 44 * comparisons for 45 * @param isbetterList a list of tuples [bid1index, bid2index]. It 46 * indicates that bids[bid1index] isbetterthan 47 * bids[bid2index]. 84 * @param name the name for the profile 85 * @param domain the {@link Domain} description 86 * @param reservationbid the reservation {@link Bid} 87 * @param better a map with keys = a better bid and value=a less 88 * good bid. 48 89 */ 49 90 @JsonCreator … … 51 92 @JsonProperty("domain") Domain domain, 52 93 @JsonProperty("reservationBid") Bid reservationbid, 53 @JsonProperty("bids") List<Bid> bids, 54 @JsonProperty("better") List<List<Integer>> isbetterList) { 55 this(name, domain, reservationbid, makeBidMap(bids, isbetterList)); 56 } 57 58 /** 59 * @param name the name for the profile 60 * @param domain the {@link Domain} description 61 * @param reservationbid the reservation {@link Bid} 62 * @param isBetterMap a map with keys = a better bid and value=a less 63 * good bid. 64 */ 65 public DefaultPartialOrdering(String name, Domain domain, 66 Bid reservationbid, Map<Bid, Set<Bid>> isBetterMap) { 94 @JsonProperty("better") Map<Bid, Set<Bid>> better) { 67 95 super(name, domain, reservationbid); 68 this. isBetter = isBetterMap;96 this.better = better; 69 97 } 70 98 71 99 @Override 72 100 public boolean isPreferredOrEqual(Bid bid1, Bid bid2) { 73 if (! isBetter.containsKey(bid1))101 if (!better.containsKey(bid1)) 74 102 return false; 75 return isBetter.get(bid1).contains(bid2);103 return better.get(bid1).contains(bid2); 76 104 } 77 105 … … 81 109 * or as worse than another bid 82 110 */ 83 @JsonGetter84 111 public List<Bid> getBids() { 85 112 // FIXME the iteration order may not be guaranteed! 86 113 Set<Bid> bids = new HashSet<>(); 87 for (Bid bid : isBetter.keySet()) {114 for (Bid bid : better.keySet()) { 88 115 bids.add(bid); 89 bids.addAll( isBetter.get(bid));116 bids.addAll(better.get(bid)); 90 117 } 91 118 return new ArrayList<Bid>(bids); 92 119 } 93 120 94 @JsonGetter95 121 /** 96 122 * … … 103 129 104 130 for (Bid bid : bidslist) { 105 if ( isBetter.containsKey(bid)) {106 for (Bid worsebid : isBetter.get(bid)) {131 if (better.containsKey(bid)) { 132 for (Bid worsebid : better.get(bid)) { 107 133 betterlist.add(Arrays.asList(bidslist.indexOf(bid), 108 134 bidslist.indexOf(worsebid))); … … 116 142 @Override 117 143 public String toString() { 118 return "DefaultPartialOrdering[" + getValuesString() + "," + isBetter144 return "DefaultPartialOrdering[" + getValuesString() + "," + better 119 145 + "]"; 120 }121 122 private static Map<Bid, Set<Bid>> makeBidMap(List<Bid> bids,123 List<List<Integer>> isBetterList) {124 Map<Bid, Set<Bid>> betterMap = new HashMap<>();125 126 for (List<Integer> tuple : isBetterList) {127 if (tuple.size() != 2) {128 throw new IllegalArgumentException("Expected tuple but found "129 + tuple + "in " + isBetterList);130 }131 Bid betterbid = bids.get(tuple.get(0));132 Set<Bid> map = betterMap.get(betterbid);133 if (map == null) {134 map = new HashSet<>();135 betterMap.put(betterbid, map);136 }137 map.add(bids.get(tuple.get(1)));138 }139 140 return betterMap;141 146 } 142 147 … … 145 150 final int prime = 31; 146 151 int result = super.hashCode(); 147 result = prime * result 148 + ((isBetter == null) ? 0 : isBetter.hashCode()); 152 result = prime * result + ((better == null) ? 0 : better.hashCode()); 149 153 return result; 150 154 } … … 159 163 return false; 160 164 DefaultPartialOrdering other = (DefaultPartialOrdering) obj; 161 if ( isBetter == null) {162 if (other. isBetter != null)165 if (better == null) { 166 if (other.better != null) 163 167 return false; 164 } else if (! isBetter.equals(other.isBetter))168 } else if (!better.equals(other.better)) 165 169 return false; 166 170 return true; -
profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java
r52 r53 49 49 * @param domain the {@link Domain} in which this profile is defined. 50 50 * @param name the name of this profile. Must be simple name (a-Z, 0-9) 51 * @param utils a map with key: issue names (String) and value: the values51 * @param issueUtilities a map with key: issue names (String) and value: the values 52 52 * for that issue. There MUST NOT be a null issue. All values 53 53 * MUST NOT be null. 54 * @param weights the weight of each issue in the computation of the54 * @param issueWeights the weight of each issue in the computation of the 55 55 * weighted sum. The issues must be the same as those in the 56 56 * utils map. All weights MUST NOT be null. The weights MUST 57 57 * sum to 1. 58 * @param res Bid the reservation bid. Only bids that are58 * @param reservationBid the reservation bid. Only bids that are 59 59 * {@link #isPreferredOrEqual(Bid, Bid)} should be accepted. 60 60 * Can be null, meaning that there is no reservation bid and … … 66 66 public LinearAdditiveUtilitySpace(@JsonProperty("domain") Domain domain, 67 67 @JsonProperty("name") String name, 68 @JsonProperty("issueUtilities") Map<String, ValueSetUtilities> utils, 69 @JsonProperty("issueWeights") Map<String, BigDecimal> weights, 70 @JsonProperty("reservationBid") Bid resBid) { 71 this.domain = domain; 72 this.name = name; 73 this.reservationBid = resBid; 74 this.issueUtilities.putAll(utils); 75 this.issueWeights.putAll(weights); 68 @JsonProperty("issueUtilities") Map<String, ValueSetUtilities> issueUtilities, 69 @JsonProperty("issueWeights") Map<String, BigDecimal> issueWeights, 70 @JsonProperty("reservationBid") Bid reservationBid) { 76 71 77 72 if (domain == null) { 78 73 throw new NullPointerException("domain=null"); 79 74 } 80 if ( utils == null) {75 if (issueUtilities == null) { 81 76 throw new NullPointerException("utils=null"); 82 77 } 83 if ( weights == null) {78 if (issueWeights == null) { 84 79 throw new NullPointerException("weights=null"); 85 80 } 86 if ( utils.values().contains(null)) {81 if (issueUtilities.values().contains(null)) { 87 82 throw new NullPointerException( 88 83 "One of the ValueSetUtilities in issueUtilitiesis null:" 89 + utils);90 } 91 if ( weights.values().contains(null)) {84 + issueUtilities); 85 } 86 if (issueWeights.values().contains(null)) { 92 87 throw new NullPointerException("One of the weights is null"); 93 88 } 94 if ( utils.keySet().contains(null)) {89 if (issueUtilities.keySet().contains(null)) { 95 90 throw new NullPointerException("One of the issue names is null"); 96 91 } … … 99 94 "Name must be simple (a-Z, 0-9) but got " + name); 100 95 } 101 if (!( utils.keySet().equals(domain.getIssues()))) {96 if (!(issueUtilities.keySet().equals(domain.getIssues()))) { 102 97 throw new IllegalArgumentException( 103 98 "The issues in utilityspace and domain do not match: utilityspace has issues " 104 + utils.keySet() + " but domain contains "99 + issueUtilities.keySet() + " but domain contains " 105 100 + domain.getIssues()); 106 101 } 107 if (!( weights.keySet().equals(domain.getIssues()))) {102 if (!(issueWeights.keySet().equals(domain.getIssues()))) { 108 103 throw new IllegalArgumentException( 109 104 "The issues in weights and domain do not match: weights has " 110 + weights.keySet() + " but domain contains "105 + issueWeights.keySet() + " but domain contains " 111 106 + domain.getIssues()); 112 107 } … … 118 113 } 119 114 120 BigDecimal sum = weights.values().stream().reduce(BigDecimal.ZERO,115 BigDecimal sum = issueWeights.values().stream().reduce(BigDecimal.ZERO, 121 116 BigDecimal::add); 122 117 if (BigDecimal.ONE.compareTo(sum) != 0) { // equals does NOT work for 123 118 // comparing BigDecimals!! 124 119 throw new IllegalArgumentException("The sum of the weights (" 125 + weights.values() + ") must be 1");126 } 127 if (res Bid != null) {128 String message = domain.isFitting(res Bid);120 + issueWeights.values() + ") must be 1"); 121 } 122 if (reservationBid != null) { 123 String message = domain.isFitting(reservationBid); 129 124 if (message != null) 130 125 throw new IllegalArgumentException( 131 126 "reservationbid is not fitting domain: " + message); 132 127 } 128 129 this.domain = domain; 130 this.name = name; 131 this.reservationBid = reservationBid; 132 this.issueUtilities.putAll(issueUtilities); 133 this.issueWeights.putAll(issueWeights); 134 133 135 } 134 136 -
profile/src/main/java/geniusweb/profile/utilityspace/PartsUtilities.java
r52 r53 7 7 import java.util.List; 8 8 import java.util.Map; 9 import java.util.stream.Collectors; 9 10 10 11 import com.fasterxml.jackson.annotation.JsonAutoDetect; … … 13 14 import com.fasterxml.jackson.annotation.JsonProperty; 14 15 16 import geniusweb.issuevalue.Domain; 15 17 import geniusweb.issuevalue.Value; 16 18 import geniusweb.issuevalue.ValueSet; 19 import tudelft.utilities.immutablelist.ImmutableList; 20 import tudelft.utilities.immutablelist.Outer; 17 21 18 22 /** … … 41 45 * 42 46 * @param issues list of issues 43 * @param utils with keys: list of values and value: utility value for that 44 * list of values. All list-of-values missing from the map are 45 * assumed to have utility 0. This includes partial bids where 46 * list-of-values contain null objects. 47 * @param utils with keys: list of values, one for each issue in issues (in 48 * the order of the list of issues) and value: utility value 49 * for that list of values. All list-of-values missing from 50 * the map are assumed to have utility 0. This includes 51 * partial bids where list-of-values contain null objects. 47 52 */ 48 53 public PartsUtilities(List<String> issues, 49 54 Map<ProductOfValue, BigDecimal> utils) { 50 if (issues == null || utils == null ) {55 if (issues == null || utils == null || issues.isEmpty()) { 51 56 throw new IllegalArgumentException( 52 "issues and utils must be not null ");57 "issues and utils must be not null or empty"); 53 58 } 54 59 … … 149 154 } 150 155 156 /** 157 * Check that all needed values have been set to a utility. 158 * 159 * @param domain the domain for which this should be parts. We need this to 160 * check that the values used are actually from the dmoain 161 * @throws IllegalArgumentException if a problem is found 162 */ 163 public void checkComplete(Domain domain) { 164 165 final List<ImmutableList<Value>> valuesofissues = issues.stream() 166 .map(iss -> domain.getValues(iss)).collect(Collectors.toList()); 167 168 // check that all possible value combinations are handled 169 for (ImmutableList<Value> vals : new Outer<Value>(valuesofissues)) { 170 ProductOfValue expected = ProductOfValue.create(vals); 171 if (!utilities.containsKey(expected)) 172 throw new IllegalArgumentException( 173 "Values " + vals + " must be assigned a utility"); 174 } 175 } 176 151 177 @Override 152 178 public boolean equals(Object obj) { … … 176 202 } 177 203 204 /** 205 * Check that the set of utilities is complete and no weird utility values 206 * were used 207 * 208 */ 178 209 private void checkUtilities() { 179 if (utilities.values().stream()180 .anyMatch(v -> v == null || v.compareTo(BigDecimal.ZERO) < 0181 || v.compareTo(BigDecimal.ONE) > 0)) {182 throw new IllegalArgumentException(183 "part weights must all be in [0,1]");210 for (BigDecimal value : utilities.values()) { 211 if (value == null || value.compareTo(BigDecimal.ZERO) < 0 212 || value.compareTo(BigDecimal.ONE) > 0) 213 throw new IllegalArgumentException( 214 "part weights must all be in [0,1] but found " + value); 184 215 } 185 216 } … … 196 227 197 228 /** 198 *199 229 * @return the max utility of all values contained here. 200 230 */ -
profile/src/main/java/geniusweb/profile/utilityspace/ProductOfValue.java
r52 r53 2 2 3 3 import java.util.ArrayList; 4 import java.util.LinkedList; 4 5 import java.util.List; 5 6 … … 10 11 11 12 import geniusweb.issuevalue.Value; 13 import tudelft.utilities.immutablelist.ImmutableList; 12 14 13 15 /** 14 * A {@link Value} that is the product of some existing values16 * A {@link Value} that is a list of a number of values in the space 15 17 */ 16 18 @JsonDeserialize(using = JsonDeserializer.None.class) … … 24 26 public ProductOfValue(List<Value> newvals) { 25 27 values.addAll(newvals); 28 } 29 30 public static ProductOfValue create(ImmutableList<Value> vals) { 31 List<Value> list = new LinkedList<>(); 32 for (Value val : vals) { 33 list.add(val); 34 } 35 return new ProductOfValue(list); 26 36 } 27 37 -
profile/src/main/java/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java
r52 r53 3 3 import java.math.BigDecimal; 4 4 import java.util.Arrays; 5 import java.util.Collections; 5 6 import java.util.HashMap; 6 7 import java.util.HashSet; … … 51 52 * @param partutils a map with key: part names (String) and value: the 52 53 * {@link PartsUtilities} for that part. There MUST NOT be 53 * a null part name. All values MUST NOT be null. The 54 * PartsUtilities must match the 54 * a null part name. The PartsUtilities must be complete: 55 * all possible combinations of all parts must cover all 56 * combinations of issue values and assign a proper utility 57 * in [0,1]. 55 58 * @param resBid the reservation bid. Only bids that are 56 59 * {@link #isPreferredOrEqual(Bid, Bid)} should be 57 60 * accepted. Can be null, meaning that there is no 58 61 * reservation bid and any agreement is better than no 59 * agreement. 62 * agreement. Read the note about partial bids in 63 * {@link #getUtility(Bid)}. 60 64 * @throws NullPointerException if values are incorrectly null. 61 65 * @throws IllegalArgumentException if preconditions not met. … … 69 73 this.partUtilities.putAll(partutils); 70 74 71 String err = checkParts(); 72 if (err != null) { 73 throw new IllegalArgumentException(err); 74 } 75 } 76 77 /** 78 * Copy settings in las. This will have the exact same utilities as the las 79 * but this then gives you the power to change it into a non-linear space by 80 * grouping. 75 checkParts(); 76 } 77 78 /** 79 * Copy settings in las. This will have the exact same utilities as the 80 * {@link LinearAdditive} space but this then gives you the power to change 81 * it into a non-linear space by grouping. 81 82 * 82 83 * @param las the {@link LinearAdditive} to be converted/copied. 83 84 * 84 85 */ 85 public SumOfGroupsUtilitySpace(LinearAdditive las) { 86 this(las.getDomain(), las.getName(), las2parts(las), 87 las.getReservationBid()); 88 } 89 86 public static SumOfGroupsUtilitySpace create(LinearAdditive las) { 87 return new SumOfGroupsUtilitySpace(las.getDomain(), las.getName(), 88 las2parts(las), las.getReservationBid()); 89 } 90 91 /** 92 * 93 * @return all partsutilities 94 */ 95 public Map<String, PartsUtilities> getPartsUtilities() { 96 return Collections.unmodifiableMap(partUtilities); 97 } 98 99 /** 100 * 101 * Note: If bid is partial for a group, the utility of that group can not be 102 * evaluated and is set to 0. 103 * 104 */ 90 105 @Override 91 106 public BigDecimal getUtility(Bid bid) { 92 return partUtilities.keySet().stream() 93 .map(partname -> util(partname, bid)) 94 .reduce(BigDecimal.ZERO, BigDecimal::add); 107 BigDecimal sum = BigDecimal.ZERO; 108 for (String partname : partUtilities.keySet()) { 109 sum = sum.add(util(partname, bid)); 110 } 111 return sum; 95 112 } 96 113 97 114 @Override 98 115 public String toString() { 99 return " PartialAdditive[" + partUtilities + "," + getReservationBid()100 + " ]";116 return "SumOfGroupsUtilitySpace[" + getName() + "," + partUtilities 117 + "," + getReservationBid() + "]"; 101 118 } 102 119 … … 235 252 * @return error string, or null if no error (all parts seem fine) 236 253 */ 237 private String checkParts(){254 private void checkParts() throws IllegalArgumentException { 238 255 239 256 Set<String> collectedIssues = new HashSet<>(); … … 241 258 PartsUtilities part = partUtilities.get(partname); 242 259 if (part == null) { 243 return "partUtilities " + partname + " contains null value"; 260 throw new IllegalArgumentException( 261 "partUtilities " + partname + " contains null value"); 244 262 } 245 263 List<String> issues = part.getIssues(); … … 247 265 intersection.retainAll(issues); 248 266 if (!intersection.isEmpty()) { 249 return "issues " + intersection + " occur multiple times"; 250 } 267 throw new IllegalArgumentException( 268 "issues " + intersection + " occur multiple times"); 269 } 270 part.checkComplete(getDomain()); 251 271 collectedIssues.addAll(issues); 252 272 } 253 273 254 274 if (!collectedIssues.equals(getDomain().getIssues())) { 255 return "parts must cover the domain issues " 256 + getDomain().getIssues() + " but cover " + collectedIssues; 275 throw new IllegalArgumentException( 276 "parts must cover the domain issues " 277 + getDomain().getIssues() + " but cover " 278 + collectedIssues); 257 279 } 258 280 if (getMaxUtility().compareTo(BigDecimal.ONE) > 0) { 259 return "Max utility of the space exceedds 1";260 }261 return null;281 throw new IllegalArgumentException( 282 "Max utility of the space exceedds 1"); 283 } 262 284 263 285 }
Note:
See TracChangeset
for help on using the changeset viewer.