This file is indexed.

/usr/share/pyshared/genetic/organism.py is in python-genetic 0.1.1b-11.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# Genetic
# Copyright (C) 2001 Jean-Baptiste LAMY
#
# This program is free software. See README or LICENSE for the license terms.

"""genetic.organism -- Organism and Chromosom class.

FUNC is the function to minimize. The only thing this module can do is minimizing a function, but hey, that can do a LOT of thing !!!
You should provide FUNC by calling setfunc(FUNC); see the demos for example.
FUNC can take as many args as you need, and those args's names must correspond to genes's names. FUNC must return a float value, or None is no result is available.

An Organism is described by his 'genotype' and his 'phenotype'.

A 'genotype' is considered to be a list of pairs of chromosoms : [(chromosom_0_A, chromosom_0_B), (chromosom_1_A, chromosom_1_B), ...]
Some chromosoms can be 'None', for incomplete pairs.
The genotype is inherited from the parents (A child take one chromosom of each pair of each parent).

A 'phenotype' is a 2-values tuple; the first value is the result of FUNC for the organism, the second is the sequence of the args for FUNC.
The phenotype is computed from the genotype, according to the PHENOTYPE function. This function take the genotype, and must return the genotype, or None is the organism cannot live (e.g. he has lost some gene...).
You may want to provide the PHENOTYPE function by calling setphenotypefunc(PHENOTYPE); see its docstring for more info. Default is recessive choice.
"""

#from __future__ import nested_scopes
import random, types, copy, operator

RECESSIVE_PHENOTYPE = 1
DOMINANT_PHENOTYPE = 2
PERCHROMOSOM_DOMINANCY_PHENOTYPE = 3

class Characteristic:
  """A characteristic of an organism : e.g. eyes color, ...
The characteritic's phenotype is computed from the genotype.
This class should be inherited/extended to create different, and :
A characteristic id defined by :
 - The "func" function that computes its value. "func" takes genes values
   and return the phenotype. The args of "func" should have the same names
   that genes.
 - The "phenotype" function that choose between the multiple values for a
   genes the one used by "func" : there's typically 2 values for the same gene,
   but only one can be used by "func" !
   Common phenotype functions are provided in this class, use the following
   constant in the constructor : RECESSIVE_PHENOTYPE (the default),
   DOMINANT_PHENOTYPE and PERCHROMOSOME_DOMINANCY_PHENOTYPE.
"""
  def __init__(self, name, func, phenotype = RECESSIVE_PHENOTYPE, funcargs = None):
    """Characteristic(name, func, phenotype = RECESSIVE_PHENOTYPE, funcargs = None) -> Characteristic -- Create a new characteristic.
funcargs is the list of the name of the genes that correspond to "func" argument.
If not provided, funcargs is guessed from func's arguments names.
"""
    self.name = name
    self.func = func
    
    if funcargs is None:
      if not type(func) is types.FunctionType: func = func.im_func
      self.funcargs = func.func_code.co_varnames[: func.func_code.co_argcount]
    else: self.funcargs = funcargs

    if   phenotype is RECESSIVE_PHENOTYPE: self.phenotype = self.recessive_phenotype
    elif phenotype is DOMINANT_PHENOTYPE : self.phenotype = self.dominant_phenotype
    elif phenotype is PERCHROMOSOM_DOMINANCY_PHENOTYPE:
      self.phenotype = self.perchromosom_dominancy_phenotype
    else: self.phenotype = phenotype
    
  def perchromosom_dominancy_phenotype(self, genotype):
    """perchromosom_dominancy_phenotype(genotype) -> phenotype -- Gets the dominant phenotype from the given list, this func use per-chromosom dominancy (__dominancy__ gene).
Notice that this phenotype computation method is cheaper in time that dominant or recessive method."""
    # 1 - Collects all the values for each gene, in a dict : {"gene1" : (dominancy, value), ...}
    attrs = {}
    for pair in genotype:
      for chromosom in pair:
        if chromosom is None: continue
        for gene, value in chromosom.__dict__.items():
          attr = attrs.get(gene, None)
          if (not attr is None) and (attr[0] >= chromosom.__dominancy__):
            # The previous found version of this gene is on a chromosom more dominant that the current one... skip !
            continue
          
          attrs[gene] = (chromosom.__dominancy__, value)
  
    # 2 - Gets all the attrs needed by func in a list : [(dominancy1, value1), ...]
    attrs_for_func = map(attrs.get, self.funcargs)
    
    # 3 - Gets all the args in a list : [value1, value2, ...]
    try: args = [attr[1] for attr in attrs_for_func]
    except TypeError:
      # A needed gene is not present ! (= attr is None)
      return (None, None)
    
    # 4 - Computes the phenotype
    return (self.func(*args), args)
  
  def recessive_phenotype(self, genotype):
    """recessive_phenotype(genotype) -> phenotype -- Gets the better phenotype from the given list. This roughly corresponds to a recessive disease."""
    # Compute all the phenotype, and choose the one with the minimal value -- the best one
    def chooser(phenotypes):
      phenotypes = filter(lambda phenotype: not phenotype[0] is None, phenotypes)
      if phenotypes: return min(phenotypes)
      return (None, None)
      
    return self._compute_all_phenotypes(genotype, chooser)
  
  phenotype = recessive_phenotype
  
  def dominant_phenotype(self, genotype):
    """dominant_phenotype(genotype) -> phenotype -- Gets the worst phenotype from the given list. This roughly corresponds to a dominant disease."""
    # Compute all the phenotype, and choose the one with the maximal value -- the worst one
    def chooser(phenotypes):
      for phenotype in phenotypes:
        if phenotype[0] is None: return phenotype
      return max(phenotypes)
    
    return self._compute_all_phenotypes(genotype, chooser)
  
  def _compute_all_phenotypes(self, genotype, chooser):
    """_compute_all_phenotypes(genotype, chooser) -> phenotype -- An utility func that compute ALL the possible phenotype, and let chooser choose the right one.
genotype is a list of chromosoms pair, and chooser a function that take a list of phenotypes as argument, and should return the right one."""
    # 1 - Collects all the values for each gene, in a dict : {"gene1":[value1, value2, ...], ...}
    attrs = {}
    for pair in genotype:
      for chromosom in pair:
        if chromosom is None: continue
        for gene, value in chromosom.__dict__.items():
          attr = attrs.get(gene, None)
          if attr is None:
            attr = []
            attrs[gene] = attr
          attr.append(value)
    
    # Computes the phenotype from the genotype :
    # 2 - Gets all the args in a list : [[x1, x2], [y1, y2], ...]
    attrs_for_func = map(attrs.get, self.funcargs)
    
    # 3 - Gets all the combinations of these args : [[x1, y1], [x1, y2], ..., [x2, y1], [x2, y2], ...]
    try: allcombinations = combinations(*attrs_for_func)
    except EmptyListError:
      # All the needed genes are not present... this organism cannot live !
      return (None, None)
    
    # 4 - Computes all the possible phenotypes : [(result, [x1, y1]), ...]
    phenotypes = [(self.func(*combination), combination) for combination in allcombinations]
    
    # 5 - Choose the right one.
    return chooser(phenotypes)
    



class Organism:
  """The Organism class. An organism has :
 - a genotype = a list of (possibly incomplete) pair of chromosoms,
 - a phenotype, computed from the genotype by the phenotype function of the
   environment. A phenotype is a tupple whose first element is the phenotype
   value and whose second element is the list of arguments (= values of genes)
   that has given this value.

You must override this class, and change the class attribute "characteristics".
This attribute should be set to the list of characteristics yours organisms
have.
"""
  characteristics = []
  
  def __init__(self, genotype):
    """Organism(genotype) -> Organism -- Creates a new Organism with the given genotype.
genotype can be either a list of pairs of chromosoms, or a list of chromosoms
for homozygote organisms (= both chromosoms of each pair are the same)."""
    # 1 - Save data
    if len(genotype) > 0 and isinstance(genotype[0], Chromosom):
      # genotype is not a list of pairs of chromosoms but a list of chromosom
      # => this organism is homozygote
      genotype = map(None, genotype, genotype)
    self.genotype = genotype
    
    # 2 - Remove useless pair of chromosoms from the genotype -- this may not be really fair... ??
    for pair in self.genotype[:]:
      if (pair[0] is None or pair[0].useless()) and (pair[1] is None or pair[1].useless()): self.genotype.remove(pair)
    
    # 3 - Compute the phenotypes
    self._compute_phenotypes()
    
  def _compute_phenotypes(self):
    # Compute the phenotypes for each characteristic
    self.canlive = 1
    for characteristic in self.characteristics:
      value, args = characteristic.phenotype(self.genotype)
      setattr(self, characteristic.name, value)
      setattr(self, characteristic.name + "_args", args) # The args that give this result -- usefull ! It's often what we want to know.
      
      # If a phenotype returns None, there is no available phenotype, so the organism cannot live.
      if value is None: self.canlive = 0
  
  def __repr__(self):
    if self.canlive:
      charac = ["%s%s : %s%s\n" % (characteristic.name, characteristic.funcargs, getattr(self, characteristic.name), getattr(self, characteristic.name + "_args")) for characteristic in self.characteristics]
      repr = reduce(operator.add, charac)
      
#      repr = reduce(operator.add,
#                    map(lambda characteristic: "%s%s : %s%s\n" % (characteristic.name, characteristic.funcargs, getattr(self, characteristic.name), getattr(self, characteristic.name + "_args")),
#                        self.characteristics
#                        )
#                    )
    else:
      repr = "phenotype : DEAD (non-viable)\n"
    i = 0

    for pair in self.genotype:
      if pair[0] is None: repr = repr + "genotype, chromosome %s A : None\n" % i
      else:               repr = repr + "genotype, chromosome %s A :\n%s" % (i, `pair[0]`)
      if pair[1] is None: repr = repr + "genotype, chromosome %s B : None\n" % i
      else:               repr = repr + "genotype, chromosome %s B :\n%s" % (i, `pair[1]`)
      i = i + 1
      
    return repr
  
  def __eq__(self, other): return self.genotype == other.genotype
  
  def givetochild(self):
    gift = []
    for pair in self.genotype:
      chromosom = self.givechromosomtochild(pair)
      if isinstance(chromosom, Chromosom): gift.append(chromosom)
      else: gift.extend(chromosom)
    return gift
  
  def givechromosomtochild(self, pair):
    if pair[0] is None: return pair[1] or []
    if pair[1] is None: return pair[0]
    
    if random.random() < (pair[0].__crossover__ + pair[1].__crossover__) / 2.0:
      # Crossing over !
      chromosom = pair[0].crossover(pair[1])
    else:
      # Choose a random chromosom
      if random.random() < 0.5: chromosom = pair[0]
      else:                     chromosom = pair[1]
      
    # Checks for mutation
    return chromosom.checkmutate()
  


class Mutable:
  """Mixin class for mutable object. This allows to use object as gene's value,
instead of float.
"""
  def checkmutate(self):
    """Mutable.checkmutate() -> new value -- Called to check mutation in this mutable object.
The returned value should be the mutable object itself if it hasn't been
changed.
"""
    pass
  


NotMutableAttributeError = "NotMutableAttributeError"

class Chromosom(Mutable):
  DEFAULT_GENES = {
    "__dominancy__" :  1.0 ,
    "__mutation__"  :  0.5 ,
    "__mutampl__"   : 50.0 ,
    "__mutsign__"   :  0.0 ,
    "__crossover__" :  0.2 ,
    "__break__"     :  0.01,
    "__deletion__"  :  0.01,
    "__loss__"      :  0.01,
    }
  
  def __init__(self, **data):
    self.__dict__.update(self.DEFAULT_GENES)
    self.__dict__.update(data)
    
  def useless(self):
    # A useless chromosom is a chromosom that has ONLY "magic" genes (=__(something)__)
    for gene in self.__dict__.keys():
      if not gene.startswith("__"): return 0
    return 1
  
  def crossover(self, other):
    dict = {}
    genes1 = self .__dict__.items()
    genes2 = other.__dict__.items()
    crossat = int(random.random() * len(genes1))
    
    i = 0
    while i < crossat:
      dict[genes1[i][0]] = genes1[i][1]
      i = i + 1
    while i < len(genes2):
      dict[genes2[i][0]] = genes2[i][1]
      i = i + 1
    
    return Chromosom(**dict)
  
  def checkmutate(self):
    # Checks if the chromosom is lost
    if random.random() < self.__loss__: return []
    
    mutated = None

    newdict = self.checkmutate_object(self.__dict__)
    if not newdict is self.__dict__:
      mutated = self.__class__(**newdict)
      
    # Checks if a gene is deleted on the chromosom
    if random.random() < self.__deletion__:
      if mutated is None: mutated = self.__class__(**self.__dict__)
      
      deletablegenes = filter(lambda gene: not gene.startswith("__"), mutated.__dict__.keys())
      if len(deletablegenes) > 0:
        delattr(mutated, random.choice(deletablegenes))
    
    mutated = mutated or self
    
    # Checks if the chromosom break in 2 parts
    if random.random() < self.__break__:
      genes = mutated.__dict__.items()
      breakat = int(random.random() * (len(genes) + 1))
      
      genes1, genes2 = {}, {}
      for i in range(breakat): genes1[genes[i][0]] = genes[i][1]
      for i in range(breakat, len(genes)): genes2[genes[i][0]] = genes[i][1]
      return self.__class__(**genes1), self.__class__(**genes2)
    
    return mutated
  
  def checkmutate_object(self, object, name = ""):
    objtype = type(object)
    if objtype is types.FloatType:
      if random.random() > self.__mutation__: return object

      if name.startswith("__"):
        # The gene is a "magic" gene. For magic gene, the amplitude of mutation is the gene's value itself.
        return object * random.random() * 2.0
      else:
        # Else, use the default mutation sign and amplitude (= the value of the "__mutsign__" and "__mutampl__" gene)
        mutsign = self.__mutsign__
        
        if mutsign == 0.0 or (mutsign > 0.5 and mutsign < 2.0):
          # No sign for the mutation
          return object + (random.random() - 0.5) * 2.0 * self.__mutampl__
        else:
          # The mutation has a sign : if sign < 0.5, mutation can only decrease the gene's value. Else increase it.
          if mutsign < 0.5: return object - random.random() * self.__mutampl__
          else:             return object + random.random() * self.__mutampl__
      
    elif isinstance(object, Mutable): return object.checkmutate()
        
    elif objtype is types.DictType:
      mutated = None
      for gene, value in object.items():
        newvalue = self.checkmutate_object(value, gene)
        if not newvalue is value:
          if mutated is None: mutated = object.copy()
          mutated[gene] = newvalue
      return mutated or object
    
    elif objtype is types.ListType:
      #mutated = object[:]
      #for i in range(len(mutated)): mutated[i] = self.checkmutate_object(mutated[i])
      #return mutated
      return map(self.checkmutate_object, object)
    
    else:
      # Non mutable => do not mutate !
      return object
      
  def __repr__(self):
    repr = ""
    genes = self.__dict__.items()
    genes.sort()
    for gene, value in genes:
      if value != 0.0 or not gene.startswith("__"):
        repr = repr + "    %s\t : %s\n" % (gene, value)
      
    return repr.expandtabs(32)
  

def multiply(organismA, organismB):
  return organismA.__class__(map(None, organismA.givetochild(), organismB.givetochild()))


# Tools functions :

EmptyListError = "EmptyListError"

def combinations(*lists):
  """combinations([x1, x2,...], [y1, y2, ...], ...) -> [[x1, y1], [x1, y2], ..., [x2, y1], [x2, y2], ...] -- Get all the possible combinations, from the given lists of args."""
  if not lists[0]: raise EmptyListError
  if len(lists) == 1: return zip(lists[0])
  
  r = []
  subcombinations = combinations(*lists[1:])
  for first in lists[0]:
    r.extend([[first] + list(combination) for combination in subcombinations])
    
  return r

def mean(*floats):
  return reduce(operator.add, floats) / len(floats)