3

Chromosome contains a number of scores generated in different ways. The compareTo method actually tests the agreement of the methods and accordingly returns a result.

return 1: comp = -5..-1

return 0: comp = 0 (can happen in different scenarios one of which is that all scores are equal.

return -1: comp = 1..5

public int compareTo(Chromosome o) {
    if(o == null)
        return(1);
    int comp = 0;
    comp += Double.compare(getScore(1),o.getScore(1));
    comp += Double.compare(getScore(2),o.getScore(2));
    comp += Double.compare(getScore(3),o.getScore(3));
    comp += Double.compare(getScore(5),o.getScore(5));
    comp += Double.compare(getScore(7),o.getScore(7));
    if(comp == 0)
        return(0);
    if(comp > 0)
        return(1);
    else
        return(-1);
}

My question is, how to make this scenario adhere to the rules imposed by the contract for the comparator. Apparently it doesn't and I keep getting: java.lang.IllegalArgumentException: Comparison method violates its general contract!

jallmer
  • 589
  • 3
  • 17
  • Is it possible to call the older version of Collection.sort without requiring the user to set some kind of flag in some java.ini? Also, is it possible to tell the JVM to ignore the contract? – jallmer May 29 '13 at 13:54

3 Answers3

2

If you're implementing the Comparator interface then you need to use this method (given that your class is generic with the Chromosome tpye):

int compare(Chromosome o1, Chromosome o2)

However, it seems the more appropriate interface to implement in your case is Comparable. http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

int compareTo(Chromosome o)

Comparable is typically implemented to give instances of your class a natural ordering. Comparator is typically a separate class to what you're trying to compare, and can be used to give you several different types of orderings.

Regardless of what you're implementing, the class also needs to be typed:

class Chromosome implements Comparable<Chromosome> 

Otherwise the arguments should be Object, not Chromosome.

zetches
  • 148
  • 8
  • Does a Comparator adhere to the same contract? – jallmer May 29 '13 at 11:54
  • I think so, apart from the fact that they're different methods with different signatures. In both cases you should be consistent with equals(), even though it's not a strict requirement according to the JavaDoc. It's also recommended that a Comparator implements Serializable. Also, both should throw a NullPointerException for a null. – zetches May 29 '13 at 13:40
2

To elaborate a little on Sir RotN's answer:

The compareTo method should adhere to two properties:

  • The comparison is symmetric, i.e., if A=B then B=A and if A<B then B>A
  • The comparison is transitive, i.e., if A<B and B<C then A<C, and if A=B and B=C then A=C

The first property is met for your comparison, but the second is not. Consider the following example from voting theory: we have 3 people, who vote for 3 alternatives. The highest ranked alternative wins. It is known that this can lead to an ambiguous situation where no alternative wins.


In your case, the scores are the people, and the chromosomes are the alternatives. Instead of 5 scores, I only use 3, since that is enough to show the problem. I have 3 chromosomes, A, B, and C, with scores as follows:

A: 1, 2, 3
B: 2, 3, 1
C: 3, 1, 2

It's not hard to see that A<B, B<C, and C<A, so your comparison is not transitive.


You could overcome this problem by ordering the chromosomes lexicographically:

public int compareTo(Chromosome o) {
    if(o == null)
        return(1);
    int[] indices = {1, 2, 3, 5, 7};
    for (int i : indices) {
        int c = Double.compare(getScore(i),o.getScore(i));
        if (c != 0)
            return c;
    }
    return 0;
}
Vincent van der Weele
  • 12,927
  • 1
  • 33
  • 61
  • @jallmer That really depends on your requirements. Is the order important or do you just want some arbitrary order? – Vincent van der Weele May 29 '13 at 11:33
  • The perfect order is not crucially important but it should be somewhat ordered. – jallmer May 29 '13 at 11:36
  • @jallmer Then you could for instance first add all scores for each chromosome and then compare the 2 sums, but note that you might have many pairs that are considered equal (that is, the result of compare is 0) – Vincent van der Weele May 29 '13 at 11:45
  • Hmm, the scores are not really additive. I am adding the result of the comparison shouldn't that lead to similar behavior? – jallmer May 29 '13 at 11:54
  • @jallmer unfortunately not, as we can see from the counterexample. You try to compare 2 points in a high-dimensional space. Adding them would basically compare their Manhattan distance to the origin. You could also sum the squares of each score and take the square root, in order to get the Euclidean distance to the origin, but that's probably also not gonna help. I would propose a lexicographic order, then. I'll update my answer with a code example. – Vincent van der Weele May 29 '13 at 12:05
  • Thank you for the example code, I changed the order of the indices according to success rate of the individual scores. When merely summing the score I succeed at 40% when using your provided method it is 37% in case the error wouldn't be thrown I would succeed at 55%. I can test this on some experimental data I have. – jallmer May 29 '13 at 13:51
0

Wat you try to implement seems to be one Chromosome is greater than another if it has more scores that are greater than the respective of the other. Unfortunately this doesn't provide clear precedence. I.e. you can't garantee that each o1 = o2 and o2 = o3 o1 = o3 is true. This might lead to an endless loop in ordering or, using a more advanced algorithm the excpetion you are facing. So you need to find another algorithm the provides a stable sorting.

Approaches are:

  1. Compare the sum of scores
  2. Define a score priority (score2 is only compared if score1 is equal and so on)
Bernd Ebertz
  • 1,317
  • 8
  • 10
  • Thank you, 1) seems to be what I am doing, isn't it? I am adding up the outcomes of the individual comparisons which should be straight forward but doesn't seem to work as expected. – jallmer May 29 '13 at 11:29