1 | package geniusweb.bidspace.pareto;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.Collections;
|
---|
5 | import java.util.HashSet;
|
---|
6 | import java.util.List;
|
---|
7 | import java.util.Set;
|
---|
8 | import java.util.stream.Collectors;
|
---|
9 |
|
---|
10 | import org.eclipse.jdt.annotation.NonNull;
|
---|
11 |
|
---|
12 | import geniusweb.bidspace.AllBidsList;
|
---|
13 | import geniusweb.issuevalue.Bid;
|
---|
14 | import geniusweb.issuevalue.Domain;
|
---|
15 | import geniusweb.profile.PartialOrdering;
|
---|
16 | import geniusweb.profile.Profile;
|
---|
17 |
|
---|
18 | /**
|
---|
19 | * A set of all pareto points associated with a set of profiles. Generally
|
---|
20 | * applicable but slow. It is recommended to use {@link ParetoLinearAdditive} if
|
---|
21 | * applicable.
|
---|
22 | */
|
---|
23 | public class GenericPareto implements ParetoFrontier {
|
---|
24 |
|
---|
25 | private final @NonNull List<@NonNull PartialOrdering> profiles = new ArrayList<>();
|
---|
26 | private transient @NonNull Set<@NonNull Bid> paretobids = new HashSet<>(); // cache
|
---|
27 |
|
---|
28 | /**
|
---|
29 | * Constructs pareto from a general {@link PartialOrdering}. This is a
|
---|
30 | * brute-force algorithm that will inspect (but not store) ALL bids in the
|
---|
31 | * space, and therefore may be inapproproate depending on the size of your
|
---|
32 | * domain. Complexity O( |bidspace|^2 |profiles| )
|
---|
33 | *
|
---|
34 | * @param profiles a set of at least 2 {@link PartialOrdering}s. All must be
|
---|
35 | * defined on the same domain.
|
---|
36 | */
|
---|
37 | public GenericPareto(
|
---|
38 | @NonNull List<@NonNull ? extends PartialOrdering> profiles) {
|
---|
39 | if (profiles.size() < 2) {
|
---|
40 | throw new IllegalArgumentException(
|
---|
41 | "at least 2 profiles are needed");
|
---|
42 | }
|
---|
43 | if (profiles.contains(null)) {
|
---|
44 | throw new IllegalArgumentException("Profiles must not be null");
|
---|
45 | }
|
---|
46 | final @NonNull Domain domain = profiles.get(0).getDomain();
|
---|
47 | for (final @NonNull PartialOrdering profile : profiles) {
|
---|
48 | if (!domain.equals(profile.getDomain())) {
|
---|
49 | throw new IllegalArgumentException(
|
---|
50 | "All profiles must be on same domain ("
|
---|
51 | + domain.getName() + ") but found " + profile);
|
---|
52 | }
|
---|
53 | }
|
---|
54 | this.profiles.addAll(profiles);
|
---|
55 | }
|
---|
56 |
|
---|
57 | @Override
|
---|
58 | public @NonNull List<@NonNull Profile> getProfiles() {
|
---|
59 | return Collections.unmodifiableList(profiles);
|
---|
60 | }
|
---|
61 |
|
---|
62 | @Override
|
---|
63 | public synchronized @NonNull Set<@NonNull Bid> getPoints() {
|
---|
64 | if (paretobids.isEmpty())
|
---|
65 | computePareto();
|
---|
66 | return Collections.unmodifiableSet(paretobids);
|
---|
67 | }
|
---|
68 |
|
---|
69 | @Override
|
---|
70 | public @NonNull String toString() {
|
---|
71 | return "Pareto " + getPoints();
|
---|
72 | }
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * assigns {@link #paretobids}.
|
---|
76 | */
|
---|
77 | private void computePareto() {
|
---|
78 | for (@NonNull
|
---|
79 | Bid newbid : new AllBidsList(profiles.get(0).getDomain())) {
|
---|
80 | /*
|
---|
81 | * invariant: paretobids contains bids not dominated by other bids
|
---|
82 | * in paretobids. That means we need (1) check if new bid is
|
---|
83 | * dominated (2) if existing bids are dominated if we add a new bid
|
---|
84 | */
|
---|
85 | //#PY newBidIsDominated=any( self.__isDominatedBy(newbid, paretobid) for paretobid in paretobids )
|
---|
86 | boolean newBidIsDominated = paretobids.stream()
|
---|
87 | .anyMatch(paretobid -> isDominatedBy(newbid, paretobid));
|
---|
88 |
|
---|
89 | // if new bid is not dominated, we add it and we re-check existing
|
---|
90 | // if they are now dominated
|
---|
91 | if (!newBidIsDominated) {
|
---|
92 | //#PY paretobids = { paretobid for paretobid in paretobids if not isDominatedBy(paretobid, newbid) }
|
---|
93 | paretobids = paretobids.stream()
|
---|
94 | .filter(paretobid -> !isDominatedBy(paretobid, newbid))
|
---|
95 | .collect(Collectors.toSet());
|
---|
96 | paretobids.add(newbid);
|
---|
97 | }
|
---|
98 | }
|
---|
99 | }
|
---|
100 |
|
---|
101 | /**
|
---|
102 | *
|
---|
103 | * @param bid1 the bid to check
|
---|
104 | * @param dominantbid the bid that is supposed to dominate bid1
|
---|
105 | * @return true iff bid1 is dominated by dominant bid. "Dominated by" means
|
---|
106 | * that the bid is preferred or equal in all profiles.
|
---|
107 | */
|
---|
108 | private boolean isDominatedBy(@NonNull Bid bid1, @NonNull Bid dominantbid) {
|
---|
109 | //#PY return all( [ profile.isPreferredOrEqual(dominantbid, bid1) for profile in self.__profiles ] )
|
---|
110 | return profiles.stream().allMatch(
|
---|
111 | profile -> profile.isPreferredOrEqual(dominantbid, bid1));
|
---|
112 | }
|
---|
113 | }
|
---|