1. -- parent script "GP"
  2.  
  3. -- max allowed program tree depth
  4. property MAX_DEPTH
  5.  
  6. -- fixed overall population size
  7. property POPULATION
  8.  
  9. -- number of best fitting solutions that survive per generation
  10. property SURVIVORS
  11.  
  12. -- propability that a node mutates in a generation cycle
  13. property PROP_NODE_CHANGE
  14.  
  15. -- propability that in random programs a node is a function
  16. property PROP_FUNC
  17.  
  18. -- propability that in random programs a node - if it's not a function - is a param node
  19. property PROP_PARAM
  20.  
  21. -- lower and upper limit for constant node values
  22. property MIN_CONST
  23. property MAX_CONST
  24.  
  25. property _arity
  26. property _funcs
  27. property _dataSet
  28.  
  29. ----------------------------------------
  30. -- @constructor
  31. -- @param {integer} arity - number of input arguments of the generated program
  32. -- @param {propList} [params]
  33. ----------------------------------------
  34. on new (me, arity, params)
  35.  
  36.   the randomSeed = _system.milliseconds
  37.  
  38.   -- params (arbitrary defaults)
  39.   me.MAX_DEPTH = 5
  40.   me.POPULATION = 100
  41.   me.SURVIVORS = 10
  42.   me.PROP_NODE_CHANGE = 0.2
  43.   me.PROP_FUNC = 0.5
  44.   me.PROP_PARAM = 0.6 -- 1.0 = no constants at all
  45.   me.MIN_CONST = 1
  46.   me.MAX_CONST = 10
  47.  
  48.   if ilk(params)=#propList then
  49.     repeat with i = 1 to params.count
  50.       me.setProp(params.getPropAt(i), params[i])
  51.     end repeat
  52.   end if
  53.  
  54.   me._funcs = [:]
  55.   me._arity = arity
  56.  
  57.   return me
  58. end
  59.  
  60. ----------------------------------------
  61. -- Changes a config parameter.
  62. -- @param {symbol} prop
  63. -- @param {any} val
  64. ----------------------------------------
  65. on setParam (me, prop, val)
  66.   me.setProp(prop, val)
  67. end
  68.  
  69. ----------------------------------------
  70. -- Adds a function to the list of used elementary functions ("genes").
  71. -- @param {string} name
  72. -- @param {integer} arity - number of arguments of the specified function
  73. -- @param {string} code
  74. ----------------------------------------
  75. on addFunction (me, name, arity, code)
  76.   me._funcs[name] = [#name:name, #arity:arity, #code:code]
  77. end
  78.  
  79. ----------------------------------------
  80. -- Sets the dataset that is used for validating the results.
  81. -- Each element of list <dataSet> must be a list in the form [[list of <me._arity> args], <result value>].
  82. -- @param {list} dataSet
  83. ----------------------------------------
  84. on setDataSet (me, dataSet)
  85.   me._dataSet = dataSet
  86. end
  87.  
  88. ----------------------------------------
  89. -- Starts the "loop of live". If no 'maxGenerations' argument is specified,
  90. -- the loop will run until it finds a perfect solution (or forever, if there is none).
  91. -- @param {integer} [maxGenerations=0]
  92. -- @return {instance} prog - Either a perfect program or the best found within 'maxGenerations'
  93. ----------------------------------------
  94. on run (me, maxGenerations)
  95.   if voidP(maxGenerations) then maxGenerations = 0
  96.   genCnt = 0
  97.  
  98.   bestDiff = the maxinteger
  99.   bestProg = VOID
  100.  
  101.   -- create the first random generation
  102.   generation = []
  103.   repeat with i = 1 to me.POPULATION
  104.     prog = me._makeRandomProg()
  105.     generation[i] = prog
  106.   end repeat
  107.  
  108.   -- loop of live :-)
  109.   repeat while TRUE
  110.  
  111.     put "--------------------"
  112.     put "NEW GENERATION"
  113.     put "--------------------"
  114.  
  115.     res = [:]
  116.     res.sort()
  117.  
  118.     repeat with i = 1 to me.POPULATION
  119.  
  120.       -- evaluate
  121.       prog = generation[i]
  122.       diff = me._evaluate(prog)
  123.       if diff<bestDiff then
  124.         bestDiff = diff
  125.         put diff
  126.         bestProg = prog
  127.         if diff=0 then
  128.           put "--------------------"
  129.           put "SOLUTION FOUND"
  130.           put "--------------------"
  131.           bestProg.print()
  132.           return bestProg
  133.         end if
  134.       end if
  135.       res.addProp(diff, prog)
  136.  
  137.     end repeat
  138.  
  139.     -- show the currently best
  140.     bestProg.print()
  141.  
  142.     genCnt = genCnt+1
  143.     if maxGenerations and genCnt>=maxGenerations then
  144.       return bestProg
  145.     end if
  146.  
  147.     -- survival of the fittest and "asexual reproduction" - no breeding yet
  148.     repeat with i = 1 to me.POPULATION
  149.       generation[i] = me._mutate(res[random(me.SURVIVORS)].clone())
  150.     end repeat
  151.  
  152.   end repeat
  153.  
  154. end
  155.  
  156. ----------------------------------------
  157. -- Returns a random program (tree/node) with max. <depth> tree levels
  158. ----------------------------------------
  159. on _makeRandomProg (me, depth)
  160.   if voidP(depth) then depth = me.MAX_DEPTH
  161.  
  162.   if me._randf()<=me.PROP_FUNC and depth>0 then
  163.     -- get a random function node
  164.     f = me._funcs[random(me._funcs.count)]
  165.     c = []
  166.     repeat with i = 1 to f.arity
  167.       c[i] = me._makeRandomProg(depth-1)
  168.     end repeat
  169.     node = script("Node").new(f.name, f.arity, f.code, c)
  170.  
  171.   else if me._randf()<=me.PROP_PARAM then
  172.     -- get a random param node
  173.     n = random(me._arity)
  174.     name = "param "&n
  175.     arity = 0
  176.     code = "res=arg["&n&"]"
  177.     node = script("Node").new(name, arity, code)
  178.  
  179.   else
  180.     -- get a random const node (with const in between me.MIN_CONST and me.MAX_CONST)
  181.     n = random(me.MAX_CONST-me.MIN_CONST+1) + me.MIN_CONST -1
  182.     name = "const "&n
  183.     arity = 0
  184.     code = "res="&n
  185.     node = script("Node").new(name, arity, code)
  186.  
  187.   end if
  188.   return node
  189. end
  190.  
  191. ----------------------------------------
  192. -- Mutates a program (tree/node) based on propability PROP_NODE_CHANGE
  193. ----------------------------------------
  194. on _mutate (me, prog)
  195.   if me._randf()<=me.PROP_NODE_CHANGE then
  196.     return me._makeRandomProg()
  197.   else
  198.     prog = prog.clone()
  199.     children = prog.getChildren()
  200.     repeat with i = 1 to children.count
  201.       children[i] = me._mutate(children[i])
  202.     end repeat
  203.     return prog
  204.   end if
  205. end
  206.  
  207. ----------------------------------------
  208. -- Evaluates the fitness of a program.
  209. -- Returns accumulated absolute differences to results in <dataSet>.
  210. -- The smaller the return value, the better the program's fitness (0=perfect solution).
  211. ----------------------------------------
  212. on _evaluate (me, prog)
  213.   diff = 0.0
  214.   repeat with row in me._dataSet
  215.     v = call(prog, _movie, row[1])
  216.     diff = diff + abs(row[2] - v)
  217.   end repeat
  218.   return diff
  219. end
  220.  
  221. ----------------------------------------
  222. -- Returns a random float value in between 0 and 1
  223. ----------------------------------------
  224. on _randf (me, n)
  225.   n = random(the maxinteger)-1
  226.   r = n / float(the maxinteger-1)
  227.   return r
  228. end
  229.  
[raw code]