Changeset 53 for profile/src/main/java


Ignore:
Timestamp:
12/18/24 13:28:59 (5 days ago)
Author:
ruud
Message:

All TimeDependent parties now support the nonlinear SumOfGroupsUtilitySpace. Example Nonlinear space in the computer domain

Location:
profile/src/main/java/geniusweb/profile
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • profile/src/main/java/geniusweb/profile/DefaultPartialOrdering.java

    r52 r53  
    11package geniusweb.profile;
    22
     3import java.io.IOException;
    34import java.util.ArrayList;
    45import java.util.Arrays;
    5 import java.util.HashMap;
    66import java.util.HashSet;
    77import java.util.LinkedList;
     
    1010import java.util.Set;
    1111
     12import com.fasterxml.jackson.annotation.JsonAutoDetect;
     13import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
    1214import com.fasterxml.jackson.annotation.JsonCreator;
    13 import com.fasterxml.jackson.annotation.JsonGetter;
    1415import com.fasterxml.jackson.annotation.JsonProperty;
     16import com.fasterxml.jackson.core.JsonGenerator;
     17import com.fasterxml.jackson.databind.DeserializationContext;
     18import com.fasterxml.jackson.databind.JsonSerializer;
     19import com.fasterxml.jackson.databind.KeyDeserializer;
     20import com.fasterxml.jackson.databind.ObjectMapper;
     21import com.fasterxml.jackson.databind.SerializerProvider;
     22import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
     23import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    1524
    1625import geniusweb.issuevalue.Bid;
    1726import 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")
     34class 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 */
     51class 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}
    1861
    1962/**
     
    2669 * limited anyway.
    2770 */
     71@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
    2872public class DefaultPartialOrdering extends DefaultProfile
    2973                implements PartialOrdering {
     
    3377         * other bid.
    3478         */
    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;
    3682
    3783        /**
    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.
    4889         */
    4990        @JsonCreator
     
    5192                        @JsonProperty("domain") Domain domain,
    5293                        @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) {
    6795                super(name, domain, reservationbid);
    68                 this.isBetter = isBetterMap;
     96                this.better = better;
    6997        }
    7098
    7199        @Override
    72100        public boolean isPreferredOrEqual(Bid bid1, Bid bid2) {
    73                 if (!isBetter.containsKey(bid1))
     101                if (!better.containsKey(bid1))
    74102                        return false;
    75                 return isBetter.get(bid1).contains(bid2);
     103                return better.get(bid1).contains(bid2);
    76104        }
    77105
     
    81109         *         or as worse than another bid
    82110         */
    83         @JsonGetter
    84111        public List<Bid> getBids() {
    85112                // FIXME the iteration order may not be guaranteed!
    86113                Set<Bid> bids = new HashSet<>();
    87                 for (Bid bid : isBetter.keySet()) {
     114                for (Bid bid : better.keySet()) {
    88115                        bids.add(bid);
    89                         bids.addAll(isBetter.get(bid));
     116                        bids.addAll(better.get(bid));
    90117                }
    91118                return new ArrayList<Bid>(bids);
    92119        }
    93120
    94         @JsonGetter
    95121        /**
    96122         *
     
    103129
    104130                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)) {
    107133                                        betterlist.add(Arrays.asList(bidslist.indexOf(bid),
    108134                                                        bidslist.indexOf(worsebid)));
     
    116142        @Override
    117143        public String toString() {
    118                 return "DefaultPartialOrdering[" + getValuesString() + "," + isBetter
     144                return "DefaultPartialOrdering[" + getValuesString() + "," + better
    119145                                + "]";
    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;
    141146        }
    142147
     
    145150                final int prime = 31;
    146151                int result = super.hashCode();
    147                 result = prime * result
    148                                 + ((isBetter == null) ? 0 : isBetter.hashCode());
     152                result = prime * result + ((better == null) ? 0 : better.hashCode());
    149153                return result;
    150154        }
     
    159163                        return false;
    160164                DefaultPartialOrdering other = (DefaultPartialOrdering) obj;
    161                 if (isBetter == null) {
    162                         if (other.isBetter != null)
     165                if (better == null) {
     166                        if (other.better != null)
    163167                                return false;
    164                 } else if (!isBetter.equals(other.isBetter))
     168                } else if (!better.equals(other.better))
    165169                        return false;
    166170                return true;
  • profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java

    r52 r53  
    4949         * @param domain  the {@link Domain} in which this profile is defined.
    5050         * @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 values
     51         * @param issueUtilities   a map with key: issue names (String) and value: the values
    5252         *                for that issue. There MUST NOT be a null issue. All values
    5353         *                MUST NOT be null.
    54          * @param weights the weight of each issue in the computation of the
     54         * @param issueWeights the weight of each issue in the computation of the
    5555         *                weighted sum. The issues must be the same as those in the
    5656         *                utils map. All weights MUST NOT be null. The weights MUST
    5757         *                sum to 1.
    58          * @param resBid  the reservation bid. Only bids that are
     58         * @param reservationBid  the reservation bid. Only bids that are
    5959         *                {@link #isPreferredOrEqual(Bid, Bid)} should be accepted.
    6060         *                Can be null, meaning that there is no reservation bid and
     
    6666        public LinearAdditiveUtilitySpace(@JsonProperty("domain") Domain domain,
    6767                        @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) {
    7671
    7772                if (domain == null) {
    7873                        throw new NullPointerException("domain=null");
    7974                }
    80                 if (utils == null) {
     75                if (issueUtilities == null) {
    8176                        throw new NullPointerException("utils=null");
    8277                }
    83                 if (weights == null) {
     78                if (issueWeights == null) {
    8479                        throw new NullPointerException("weights=null");
    8580                }
    86                 if (utils.values().contains(null)) {
     81                if (issueUtilities.values().contains(null)) {
    8782                        throw new NullPointerException(
    8883                                        "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)) {
    9287                        throw new NullPointerException("One of the weights is null");
    9388                }
    94                 if (utils.keySet().contains(null)) {
     89                if (issueUtilities.keySet().contains(null)) {
    9590                        throw new NullPointerException("One of the issue names is null");
    9691                }
     
    9994                                        "Name must be simple (a-Z, 0-9) but got " + name);
    10095                }
    101                 if (!(utils.keySet().equals(domain.getIssues()))) {
     96                if (!(issueUtilities.keySet().equals(domain.getIssues()))) {
    10297                        throw new IllegalArgumentException(
    10398                                        "The issues in  utilityspace and domain do not match: utilityspace has issues "
    104                                                         + utils.keySet() + " but domain contains "
     99                                                        + issueUtilities.keySet() + " but domain contains "
    105100                                                        + domain.getIssues());
    106101                }
    107                 if (!(weights.keySet().equals(domain.getIssues()))) {
     102                if (!(issueWeights.keySet().equals(domain.getIssues()))) {
    108103                        throw new IllegalArgumentException(
    109104                                        "The issues in weights and domain do not match: weights has "
    110                                                         + weights.keySet() + " but domain contains "
     105                                                        + issueWeights.keySet() + " but domain contains "
    111106                                                        + domain.getIssues());
    112107                }
     
    118113                }
    119114
    120                 BigDecimal sum = weights.values().stream().reduce(BigDecimal.ZERO,
     115                BigDecimal sum = issueWeights.values().stream().reduce(BigDecimal.ZERO,
    121116                                BigDecimal::add);
    122117                if (BigDecimal.ONE.compareTo(sum) != 0) { // equals does NOT work for
    123118                                                                                                        // comparing BigDecimals!!
    124119                        throw new IllegalArgumentException("The sum of the weights ("
    125                                         + weights.values() + ") must be 1");
    126                 }
    127                 if (resBid != null) {
    128                         String message = domain.isFitting(resBid);
     120                                        + issueWeights.values() + ") must be 1");
     121                }
     122                if (reservationBid != null) {
     123                        String message = domain.isFitting(reservationBid);
    129124                        if (message != null)
    130125                                throw new IllegalArgumentException(
    131126                                                "reservationbid is not fitting domain: " + message);
    132127                }
     128
     129                this.domain = domain;
     130                this.name = name;
     131                this.reservationBid = reservationBid;
     132                this.issueUtilities.putAll(issueUtilities);
     133                this.issueWeights.putAll(issueWeights);
     134
    133135        }
    134136
  • profile/src/main/java/geniusweb/profile/utilityspace/PartsUtilities.java

    r52 r53  
    77import java.util.List;
    88import java.util.Map;
     9import java.util.stream.Collectors;
    910
    1011import com.fasterxml.jackson.annotation.JsonAutoDetect;
     
    1314import com.fasterxml.jackson.annotation.JsonProperty;
    1415
     16import geniusweb.issuevalue.Domain;
    1517import geniusweb.issuevalue.Value;
    1618import geniusweb.issuevalue.ValueSet;
     19import tudelft.utilities.immutablelist.ImmutableList;
     20import tudelft.utilities.immutablelist.Outer;
    1721
    1822/**
     
    4145         *
    4246         * @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.
    4752         */
    4853        public PartsUtilities(List<String> issues,
    4954                        Map<ProductOfValue, BigDecimal> utils) {
    50                 if (issues == null || utils == null) {
     55                if (issues == null || utils == null || issues.isEmpty()) {
    5156                        throw new IllegalArgumentException(
    52                                         "issues and utils must be not null");
     57                                        "issues and utils must be not null or empty");
    5358                }
    5459
     
    149154        }
    150155
     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
    151177        @Override
    152178        public boolean equals(Object obj) {
     
    176202        }
    177203
     204        /**
     205         * Check that the set of utilities is complete and no weird utility values
     206         * were used
     207         *
     208         */
    178209        private void checkUtilities() {
    179                 if (utilities.values().stream()
    180                                 .anyMatch(v -> v == null || v.compareTo(BigDecimal.ZERO) < 0
    181                                                 || 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);
    184215                }
    185216        }
     
    196227
    197228        /**
    198          *
    199229         * @return the max utility of all values contained here.
    200230         */
  • profile/src/main/java/geniusweb/profile/utilityspace/ProductOfValue.java

    r52 r53  
    22
    33import java.util.ArrayList;
     4import java.util.LinkedList;
    45import java.util.List;
    56
     
    1011
    1112import geniusweb.issuevalue.Value;
     13import tudelft.utilities.immutablelist.ImmutableList;
    1214
    1315/**
    14  * A {@link Value} that is the product of some existing values
     16 * A {@link Value} that is a list of a number of values in the space
    1517 */
    1618@JsonDeserialize(using = JsonDeserializer.None.class)
     
    2426        public ProductOfValue(List<Value> newvals) {
    2527                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);
    2636        }
    2737
  • profile/src/main/java/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java

    r52 r53  
    33import java.math.BigDecimal;
    44import java.util.Arrays;
     5import java.util.Collections;
    56import java.util.HashMap;
    67import java.util.HashSet;
     
    5152         * @param partutils a map with key: part names (String) and value: the
    5253         *                  {@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].
    5558         * @param resBid    the reservation bid. Only bids that are
    5659         *                  {@link #isPreferredOrEqual(Bid, Bid)} should be
    5760         *                  accepted. Can be null, meaning that there is no
    5861         *                  reservation bid and any agreement is better than no
    59          *                  agreement.
     62         *                  agreement. Read the note about partial bids in
     63         *                  {@link #getUtility(Bid)}.
    6064         * @throws NullPointerException     if values are incorrectly null.
    6165         * @throws IllegalArgumentException if preconditions not met.
     
    6973                this.partUtilities.putAll(partutils);
    7074
    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.
    8182         *
    8283         * @param las the {@link LinearAdditive} to be converted/copied.
    8384         *
    8485         */
    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         */
    90105        @Override
    91106        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;
    95112        }
    96113
    97114        @Override
    98115        public String toString() {
    99                 return "PartialAdditive[" + partUtilities + "," + getReservationBid()
    100                                 + "]";
     116                return "SumOfGroupsUtilitySpace[" + getName() + "," + partUtilities
     117                                + "," + getReservationBid() + "]";
    101118        }
    102119
     
    235252         * @return error string, or null if no error (all parts seem fine)
    236253         */
    237         private String checkParts() {
     254        private void checkParts() throws IllegalArgumentException {
    238255
    239256                Set<String> collectedIssues = new HashSet<>();
     
    241258                        PartsUtilities part = partUtilities.get(partname);
    242259                        if (part == null) {
    243                                 return "partUtilities " + partname + " contains null value";
     260                                throw new IllegalArgumentException(
     261                                                "partUtilities " + partname + " contains null value");
    244262                        }
    245263                        List<String> issues = part.getIssues();
     
    247265                        intersection.retainAll(issues);
    248266                        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());
    251271                        collectedIssues.addAll(issues);
    252272                }
    253273
    254274                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);
    257279                }
    258280                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                }
    262284
    263285        }
Note: See TracChangeset for help on using the changeset viewer.