#52 IntegerGene.setToRandomValue() does not support full range.

next JGAP version
closed-accepted
Klaus
5
2009-09-28
2009-09-14
David Kemp
No

NOTE: This a different bug from 1357474.
I would expect to be able to create an IntegerGene with lower and upper bounds of Integer.MIN_VALUE and Integer.MAX_VALUE respectively, but setToRandomValue() will not behave properly for any confuration where the difference between the upper and lower bounds is more than Integer.MAX_VALUE.

Here is a test that exposes the problem:

public void testIntegerGeneSupportsFullIntegerRange() throws Exception {
Gene gene = new IntegerGene(conf, Integer.MIN_VALUE, Integer.MAX_VALUE);
gene.setAllele(new Integer(5));
gene.setToRandomValue(new RandomGeneratorForTesting(0.2d));
int expectedValue = (int) (Integer.MIN_VALUE +
Math.round((0.2d *
((long) Integer.MAX_VALUE - (long) Integer.MIN_VALUE))));
assertEquals(new Integer(expectedValue), gene.getAllele());
}

A solution is to caste the upper and lower bounds to longs before subtracting them:

public void setToRandomValue(final RandomGenerator a_numberGenerator) {
double randomValue = ((long) m_upperBounds - (long) m_lowerBounds) *
a_numberGenerator.nextDouble() +
m_lowerBounds;
setAllele(new Integer( (int) Math.round(randomValue)));
}

Discussion

  • David Kemp

    David Kemp - 2009-09-15

    I have had a closer look and can see other places where IntegerGene has the expression (m_upperBounds - m_lowerBounds) and do break without first casting them to longs.

    mapValueToWithinBounds() will break when (m_upperBounds - m_lowerBounds) is greater than Integer.MAX_VALUE. e.g.

    public void testMapValueToWithinBoundsSupportsFullIntegerRange() throws Exception {

    conf.setRandomGenerator(new RandomGeneratorForTesting(0.2d));
    int lower = Integer.MIN_VALUE + 1;
    int upper = Integer.MAX_VALUE;
    IntegerGene gene = new IntegerGene(conf, lower, upper);
    gene.setAllele(Integer.MIN_VALUE);
    int expectedValue = (int) (lower +
    Math.round((0.2d *
    ((long) upper - (long) lower))));
    assertEquals(new Integer(expectedValue), gene.getAllele());

    }

    To fix it, you could replace in mapValueToWithinBounds()

    if (m_upperBounds - m_lowerBounds == 0) {
    setAllele(new Integer(m_lowerBounds));
    }
    else {
    setAllele(new Integer(rn.nextInt(m_upperBounds - m_lowerBounds) +
    m_lowerBounds));
    }

    with:

    if (m_upperBounds == m_lowerBounds) {
    setAllele(new Integer(m_lowerBounds));
    }
    else {
    setToRandomValue(rn);
    }
    (This will break existing tests as they will need to be modified to expect calls to get the nextDouble() instead of nextInt())

    Similarly, applyMutation() breaks this test:

    public void testApplyMutationSupportsFullIntegerRange()
    throws Exception {
    IntegerGene gene = new IntegerGene(conf, Integer.MIN_VALUE, Integer.MAX_VALUE);
    gene.setAllele(null);
    gene.applyMutation(0, 0.4d);
    double range = ((long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE) * 0.4d;
    int expectedValue = (int) (range + Integer.MIN_VALUE);
    assertEquals(expectedValue, gene.intValue());
    }

    This can be fixed by replacing in applyMutation()

    double range = (m_upperBounds - m_lowerBounds) * a_percentage;

    with:

    double range = ((long) m_upperBounds - (long) m_lowerBounds) * a_percentage;

     
  • Klaus

    Klaus - 2009-09-28

    David, thanx a lot for your comprehensive report and your suggestions, which I followed. The corrections are checked in to CVS now. Also two unit tests were added as you wrote.

    Klaus
    NB: Sorry for the delayed answer

     
  • Klaus

    Klaus - 2009-09-28
    • milestone: --> next JGAP version
    • assigned_to: nobody --> klausikm
    • status: open --> closed-accepted
     

Log in to post a comment.