
Contracts
*********

Contracts are used to check the pre and post conditions of functions
to make sure that the evolutionary algorithm remains constrained
within the solution space.

All contracts used by all functions are listed here. It is highly
recommended that similar functions that are implemented in the future
implement similar contracts. This will be explained further as each
contract is explaioned.


GA.py
=====

The main GA driver has the following contracts. It is highly
recommended that any GA implemented to maximize the fitness score
should implement these contracts.


The main GA runner
------------------


Preconditions
~~~~~~~~~~~~~

1. "kwargs" should be supplied

2. "kwargs" is a dict mapping argument names (strings) to argument
   values

3. The maximum number of generations allowed is greater than 0


Postconditions
~~~~~~~~~~~~~~

1. "kwargs" should not be changed

2. At least one of the following two conditions must hold
      1. the fitness of the fittest individual (being returned) is at
         least "targetscore"

      2. the current generation count is equal to the maximum number
         of generations allowed

3. the maximum number of generations allowed is greater than 0


Individual.py
=============

The following contracts must be followed for any implementation of the
"Individual" class


"Individual.__hash__(self, other)"
----------------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. an "int" should be returned

2. "self" should not be changed

In addition to these, the current implementation has the following
methods implemented:


"Individual.__eq__(self, other)"
--------------------------------


Preconditions
~~~~~~~~~~~~~

1. "other" should be an instance of "Individual"


Postconditions
~~~~~~~~~~~~~~

1. "other" should not be changed

2. "self" should not be changed


"Individual.__len__(self)"
--------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. "self" should not be changed


"Individual.__setitem__(self, index, obj)"
------------------------------------------


Preconditions
~~~~~~~~~~~~~

1. Exactly one of the following two conditions must be satisfied:
      1. 0 <= "index" <= len(self.chromosomes)

      2. len(self.chromosomes)*-1 >= index >= -1


Postconditions
~~~~~~~~~~~~~~

1. The object at "self.chromosomes[index]" should be "obj"


"Individual.__contains__(self, chromosome)"
-------------------------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. "self" should not be changed

2. "chromosome" should not be changed


"Individual.__repr__(self)"
---------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. "self" should not be changed

"Individual.append(self, chrom)" --=--------------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. The length of "self.chromosomes" should be increased by exactly 1

2. The last chromosome in "self.chromosomes" should be "chrom"


"Individual.count(self, sub, chrom)"
------------------------------------


Preconditions
~~~~~~~~~~~~~

None


Postconditions
~~~~~~~~~~~~~~

1. "self" should not be changed


population.py
=============

The following contracts are applied to the functions in population.py


"genPop(N, chromGenfuncs, chromGenParams)"
------------------------------------------


Preconditions
~~~~~~~~~~~~~

1. N >= 0

2. "chromGenfuncs" is a list

3. Every entry in "chromGenfuncs" is a function

4. "chromGenParamss" is a list

5. The lengths of "chromGenfuncs" and "chromGenParams" are equal


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. Function returns a list

3. The length of the returned list is "N"

4. The returned list contains exactly 1 of each item i.e. no two items
   in the returned list are equal


"genCharsChrom(l, chars)"
-------------------------


Preconditions
~~~~~~~~~~~~~

1. "l" is an integer

2. "chars" is an instance of some class that implements "__getitem__"

3. "chars" is an instance of some class that implements "__len__"

4. "len(chars)" is greater than 0


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. Function returns a list

3. The length of the returned list is "l"

4. Every element in the returned list exists in "chars"


"genTour(numCities)"
--------------------


Preconditions
~~~~~~~~~~~~~

1. "numCities" is an integer


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. Function returns a list

3. The length of the returned list is "numCities"

4. Every element in the returned list exists exactly once in the
   returned list.


score.py
========


"score(p, scorefuncs, scorefuncparams, SCORES)"
-----------------------------------------------


Preconditions
~~~~~~~~~~~~~

1. "p" is an instance of "Individual"

2. "scorefuncs" is a list of functions

3. "scorefuncparams" is a list of tuples

4. The lengths of "scorefuncs" and "scorefuncparams" are equal

5. "SCORES" is a dictionary


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. "p" is in "SCORES"

3. Exactly one of the following two conditions are met:
      1. "p" was in "SCORES" before this function was called and the
         number of entries in "SCORES" has not changed

      2. "p" was not in "SCORES" before this function was called and
         the number of entries in "SCORES" has increased by exactly 1


"scoreOnes(p)"
--------------


Preconditions
~~~~~~~~~~~~~

1. "p" is list

2. All elements in "p" are strings of length exactly 1

3. All elements in "p" are either '0' or '1'


Postconditions
~~~~~~~~~~~~~~

1. "p" is unchaged

2. An integer is returned

3. The value returned is at least 0


scoreTSP(tour, DIST)
--------------------

   post:
      isinstance(__return__, float)

   post[tour, DIST]:
      __old__.tour == tour __old__.DIST == DIST


Preconditions
~~~~~~~~~~~~~

1. "tour" is a list

2. "DIST" is a dictionary

3. All elements in "tour" are integers

4. All keys in "DIST" are integers

5. All values in "DIST" are dictionaries

6. Every key in every value of "DIST" is an integer

7. Every value in every value of "DIST" is a float


Loop Invariant
~~~~~~~~~~~~~~

1. "answer" (the value to be returned is at most 0 and monotonously
   decreases


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. The function returns a float


getRouletteWheel(pop, SCORES)
-----------------------------


Preconditions
~~~~~~~~~~~~~

1. "pop" is a list of instances of "Individual"

2. "SCORES" is a dictionary

3. Every element in "pop" is a key in "SCORES"


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. A list of 3-tuples of type (Individual, float, float) is returned

3. The length of the returned list is equal to the length of "pop"

4. The first element of every tuple in the returned list exists in
   "pop"

5. The second float is smaller than the third float in every tuple in
   the returned list


rouletteWheelSelect(wheel, s=None)
----------------------------------


Preconditions
~~~~~~~~~~~~~

1. "wheel" is a list of 3-tuples which satisfy all the following
   conditions
      1. The first element is an instance of "Individual"

      2. The last two elements are floats

      3. The first float is smaller than the second

2. Exactly one of the following two conditions are met:
      1. "s" is a float

      2. "s" is None


Postconditions:
~~~~~~~~~~~~~~~

1. The inputs are unchanged

2. An instance of "Individual" is returned


tournamentSelect(pop, T, w, n, scorefunc, scoreparams)
------------------------------------------------------


Preconditions
~~~~~~~~~~~~~

1. "pop" is a list of instances of "Individual"

2. "T" is an integer

3. "w" is an integer

4. "n" is an integer

5. "w" is at most "n"

6. "n%w" is exactly 0

7. "n" is at most "T"

8. "scoreparams" is a tuple


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. A list of "n" instances of "Individual" is returned


crossover.py
============

The following contracts are implemented for the crossover functions.


crossOnes(p1, p2, chrom)
------------------------


Preconditions
~~~~~~~~~~~~~

1. "p1" and "p2" are instances of "Individual"

2. "chrom" is an integer


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchaged

2. A tuple of two instances of "Individual" is returned

3. Each individual in the return tuple satisfies the following
   conditions:
      1. The "chrom" th chromosome is a list of one-character strings

      2. The value of the ith gene of the "chrom" th chromosome is
         exactly the value of the ith gene of the "chrom" th
         chromosome of either "p1" or "p2"


injectionco(p1, p2, chrom)
--------------------------


Preconditions
~~~~~~~~~~~~~

1. "p1" and "p2" are instances of "Individual"

2. "chrom" is an integer

3. The length of *p1* is exactly equal to the length of "p2"

4. For every element in "p1", there exists an element in "p2" of the
   same type

5. For every element in "p2", there exists an element in "p1" of the
   same type


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchaged

2. A new object is returned

3. The length of the returned object is eactly equal to the lengths of
   "p1" or "p2"

4. The function returns a permutation i.e. all elements in the
   returned object occur exactly once


muatation.py
============


mutateSingleAllele(p, chrom, chars)
-----------------------------------


Preconditions
~~~~~~~~~~~~~

1. "p" is an instance of "Individual"

2. "chrom" is an integer

3. The value of each gene in the "chrom" th chromosome of "p" exists
   in "chars"

4. Exactly one of the following two conditions must be satisfied:
      1. 0 <= "index" <= len(self.chromosomes)

      2. len(self.chromosomes)*-1 >= index >= -1


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchanged

2. A new instance of "Individual" is returned

3. The "chrom" th chromosome of the returned individual is not equal
   to the "chrom" th chromosome of "p"

4. All other chromosomes of the returned individual are exactly the
   same as the corresponding chromosome of "p"


swapmut(p, chrom)
-----------------


Preconditions
~~~~~~~~~~~~~

1. "p" is an instance of "Individual"

2. "chrom" is an integer

3. Exactly one of the following two conditions are satisfied:
      1. 0 <= "chrom" <= "len(p.chromosomes)"

      2. "len(self.chromosomes)*-1" >= "index" >= -1


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchaged

2. An instance of "Individual" is returned

3. All values in the "chrom" th chromosome of "p" are present in the
   "chrom" th chromosome of the output individual

4. The "chrom" th chromosomes of the output individual and "p" are not
   equal

5. There are exactly two genes in the "chrom" th chromome of "p" and
   the returned individual, whose values differ


revmut(p, chrom)
----------------


Preconditions
~~~~~~~~~~~~~

1. "p" is an instance of "Individual"

2. "chrom" is an integer

3. Exactly one of the following two conditions are satisfied:
      1. 0 <= "chrom" <= "len(p.chromosomes)"

      2. "len(self.chromosomes)*-1" >= "index" >= -1


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchaged

2. An instance of "Individual" is returned

3. All values in the "chrom" th chromosome of "p" are present in the
   "`chrom" th chromosome of the output individual

4. The "chrom" th chromosomes of the output individual and "p" are not
   equal


shufflemut(p, chrom)
--------------------

   post[p, chrom]:
      __old__.p == p __old__.chrom == chrom isinstance(__return__,
      Individual) __return__.chromosomes[chrom] !=
      p.chromosomes[chrom] forall(p.chromosomes[chrom], lambda e: e in
      __return__.chromosomes[chrom])
      forall(__return__.chromosomes[chrom], lambda e: e in
      p.chromosomes[chrom])


Preconditions
~~~~~~~~~~~~~

1. "p" is an instance of "Individual"

2. "chrom" is an integer

3. Exactly one of the following two conditions are satisfied:
      1. 0 <= "chrom" <= "len(p.chromosomes)"

      2. "len(self.chromosomes)*-1" >= "index" >= -1


Postconditions
~~~~~~~~~~~~~~

1. The inputs are unchaged

2. An instance of "Individual" is returned

3. All values in the "chrom" th chromosome of "p" are present in the
   "`chrom" th chromosome of the output individual

4. The "chrom" th chromosomes of the output individual and "p" are not
   equal

5. The length of the "chrom" th chromosome of the returned individual
   is exactly equal to the length of the "chrom" th chromosome of "p"
