GEP-in-netlogo
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
Gene expression programming in NetLogo
WHAT IS IT?
https://en.wikipedia.org/wiki/Gene_expression_programming]">Gene expression programming (GEP) is an evolutionary algorithm that creates computer programs or models. These computer programs are complex tree structures that learn and adapt by changing their sizes, shapes, and composition, much like a living organism. And like living organisms, the computer programs of GEP are also encoded in simple linear chromosomes of fixed length. Thus, GEP is a genotype-phenotype system, benefiting from a simple genome to keep and transmit the genetic information and a complex phenotype to explore the environment and adapt to it.
HOW IT WORKS
You can find details in http://www.gene-expression-programming.com/webpapers/GEP.pdf
HOW TO USE IT
0, (optional) modify "gp-target-answer" in code tab, which is by default
to-report gp-target-answer report ticks * 3 end
gp-target-answer is the target function which is the goal for the GEP to find out.
1, click "setup" button (or run "setup" in command-center) 2, click "gp-episode" 3, Observe the GEP process by watching "Fitness Plot", or hit "gp-show-all", "gp-showbest" or "select-one" to see code(s) and code tree(s). If the plot line "best" reached and stablized at 1, then there is some codeturtles successfully invented the target function. If the "best", "avg" is far below 1 at long run, you may change the option in "breed_option" chooser to see if there is some improvement. If still
CREDITS AND REFERENCES
https://github.com/jihegao/NetLogo_GEP
contact: jihe.gao(at)jiejiaotech.com
Comments and Questions
; ------------------------------------------------------------------------------------------ ; ; An example of Genetic Expression Programming in NetLogo ; ; Author: jihe.Gao@jiejiaotech.com ; ; ------------------------------------------------------------------------------------------ extensions [csv table time] breed [ Stocks stock ] breed [ Investors investor ] breed [ genes gene ] breed [ codeturtles codeturtle ] globals [ gp_weighted_gene_list gp_syntaxlist gp_generation avgfitness bestfitness worstfitness gp_best_fitness_this_gen the_watched_one ] genes-own [ c_type input_var_list output_type ; void (command), reporter, boolean odds ] codeturtles-own [ my_code my_tree gp_raw_fitness birthtime result ] to setup clear-all reset-ticks gp-setup end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; START OF GP LIBRARY CODE ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; gpuser-setup-syntax (determines the ingredients for randomlly generated code trees) ;; gpuser-run-codeturtles (determines what happens to the codeturtles during a generation) ;; gpuser-initialize-codeturtle (run when each codeturtle that is created, sets initial variables, etc) ;; gpuser-calculate-raw-fitness (reports some measure analogous to "distance to goal", where 0 is perfection) ;; gpuser-calculate-adjusted-fitness (reports a value between 0 and 1, where 1 is perfection) ;; ;; In addition, your setup should call "gp-setup" and your go should call "gp-episode" ;; ;; Notes ;; ;; Structure of a tree node: ;; ["NODE_VALUE" RTYPE_* TYPE_* CHILD1 CHILD2 ... ] ;; to-report gp-target-answer report ticks * 3 end to gp-setup gp-setup-genes gp-setup-codeturtles end to gp-setup-genes ;set gp_syntaxlist csv:from-file "genes.csv" set gp_syntaxlist init-gp-syntaxlist foreach gp_syntaxlist [ [?]-> if not any? turtles with [label = first ?][ create-genes 1 [ ;["gp_if" "TYPE_COMMAND" "RTYPE_BOOLEAN" "RTYPE_COMMAND" 10] set label first ? set input_var_list sublist ? 2 (length ? - 1) set output_type item 1 ? set odds last ? move-to one-of patches with [pycor = min-pycor] ] ] ] set gp_weighted_gene_list (reduce [[?1 ?2]-> (sentence ?1 ?2)] (map [[?]-> n-values ([odds] of ?) [[label] of ?] ] sort genes)) end to gp-setup-codeturtles create-codeturtles population-size [ gp-initialize-codeturtle [0]] end ;; whenever codeturtles are created (at the beginning of each generation) this procedure is run ;; gen_config: (list [my_code] of winner [my_code] of partner ) to gp-initialize-codeturtle [gene_lists] ifelse gene_lists = [0] [ set my_code (gp-weighted-random-genes initial-chromosome-length) ] [ set my_code (gp-cross-chromosomes gene_lists) ] carefully [ set my_tree gp-compile-tree (sublist my_code (1 + position "Q" my_code) length my_code) ][set my_tree "0"] set birthtime ticks set size 2 set shape "circle" set gp_raw_fitness 100 ; large number (very unfit) set xcor random-xcor ; find the start patch end ;; reports a random entry from a syntaxlist, where the chance of an entry being chosen ;; is weighted by the number that is the last entry of the element to-report gp-weighted-random-genes [ length_gene ] report fput "Q" n-of length_gene gp_weighted_gene_list end to-report gp-cross-chromosomes [DNA_lists] let DNA0 first DNA_lists let DNA1 last DNA_lists ifelse length DNA0 < length DNA1 [ set DNA1 sublist DNA1 0 (1 + length DNA0) ] [ if length DNA0 > length DNA1 [ set DNA1 (sentence DNA1 n-values (length DNA0 - length DNA1) ["0"]) ] ] let dna [] (foreach DNA0 DNA1 [ [d1 d2]-> ifelse (random 100 < crossover-chance) [ set dna lput d2 dna ] [ ifelse (random 100 < mutate-chance) [ set dna lput (one-of gp_weighted_gene_list) dna ] [ set dna lput d1 dna ]] ]) report dna end ;; to-report gp-compile-tree [this_code] ; this_code : [ "gp_+" "3" "8" "32" "0" ] let thegene one-of turtles with [label = first this_code] set this_code but-first this_code let tree reduce [[?1 ?2]-> (word ?1 " " ?2)] [(sentence label input_var_list)] of thegene while [not empty? this_code and member? "R" tree][ set thegene one-of turtles with [label = first this_code] set this_code but-first this_code let index position "R" tree set tree replace-item index tree (insert-node-string thegene) ] report tree end to-report insert-node-string [thegene] report reduce [[?1 ?2]-> (word " " ?1 " " ?2 " ")] [(sentence label map [[?]-> (word "(" ? ")")] input_var_list)] of thegene end ;; This is the main "go" procedure for the gp-library. to gp-episode gp-create-next-generation ; breed_option = "wta" "equal" gp-run-codeturtles gp-update-fitness if move? [ask codeturtles [set ycor min-pycor + gp-fitness * world-height - 1]] gp-plot tick end ;; codeturtles procedure ;; Think of this reporter as "how far am I from my goal?" ;; Thus, a perfect codeturtle has a raw fitness of 0 ;; a bad codeturtle has an arbitrarily large raw fitness to gp-run-codeturtles ask codeturtles [carefully [set result (runresult my_tree)][die]] end to gp-update-fitness ask codeturtles [ set gp_raw_fitness gp-calculate-fitness ] end ; user-defined fitness function ( cost function ) to-report gp-calculate-fitness report abs (gp-target-answer - result) end to-report gp-fitness report 1 / (1 + gp_raw_fitness) end to gp-create-next-generation if breed_option = "wta" ; winner takes all [ let winner min-one-of codeturtles [gp_raw_fitness] repeat population-size - count turtles [ let partner one-of codeturtles gp-breed-new-codeturtle (list gp_generation [my_code] of winner [my_code] of partner ) ] ] if breed_option = "equal" [ repeat population-size - count turtles [ let couple n-of 2 codeturtles with [gp-fitness >= avgfitness] if couple = nobody [set couple n-of 2 codeturtles] gp-breed-new-codeturtle (list [my_code] of first sort couple [my_code] of last sort couple) ] ] if count codeturtles > n-die-out-each-tick [ ask n-of n-die-out-each-tick codeturtles [ die ] ] end ; gen_config: (list [my_code] of turtle1 [my_code] of turtle2 ) to gp-breed-new-codeturtle [gene_lists] create-codeturtles 1 [ gp-initialize-codeturtle gene_lists ] end ;; plot the fitnesses of the current generation, and update the gp-best-fitness-this-gen variable to gp-plot if any? codeturtles [ set bestfitness max [ gp-fitness ] of codeturtles set avgfitness mean [ gp-fitness ] of codeturtles set worstfitness min [ gp-fitness ] of codeturtles set gp_best_fitness_this_gen bestfitness set-current-plot "Fitness Plot" set-current-plot-pen "avg" plot avgfitness set-current-plot-pen "best" plot bestfitness set-current-plot-pen "worst" plot worstfitness ] end ;; displays the code of the best-this-gen codeturtle in the output box to gp-showbest clear-output ask one-of (codeturtles with-min [ gp_raw_fitness ]) [ print (word self " tree: " my_tree " result: " result )] end to gp-show-all ;clear-output foreach sort-by [[?1 ?2]-> [gp_raw_fitness] of ?1 < [gp_raw_fitness] of ?2] codeturtles [ [?]-> ask ? [ show (word self " code: " my_code " tree: " my_tree " result: " result ) ] ] end to select-one if mouse-down? [ set the_watched_one closest-codeturtle mouse-xcor mouse-ycor turtles with [breed != links] ask links [die] ask turtles with [shape = "square"][die] ask codeturtles [set label ""] watch the_watched_one ask the_watched_one [ set label my_tree setxy mouse-xcor mouse-ycor ] display ] end to-report closest-codeturtle [x y agent-set] ; Return closest agent to x, y report min-one-of codeturtles [distancexy x y] end to reset-codeturtles end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; END OF GP LIBRARY CODE ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; These are atom genes of procedures and reporters, that help form the DNA of the codeturtles. to-report init-gp-syntaxlist report [ ["gp_+" "TYPE_OPERATOR" "R" "R" 10] ["gp_-" "TYPE_OPERATOR" "R" "R" 10] ["gp_*" "TYPE_OPERATOR" "R" "R" 5] ["gp_safediv" "TYPE_REPORTER" "R" "R" 5] ["gp_random" "TYPE_REPORTER" "R" 10] ["gp_ticks" "TYPE_REPORTER" 5] ["0" "TYPE_REPORTER" 20] ["1" "TYPE_REPORTER" 20] ["2" "TYPE_REPORTER" 20] ["3" "TYPE_REPORTER" 20] ["4" "TYPE_REPORTER" 20] ["5" "TYPE_REPORTER" 20] ["8" "TYPE_REPORTER" 20] ["16" "TYPE_REPORTER" 20] ["32" "TYPE_REPORTER" 20] ] end to-report gp_ticks report ticks end to-report gp_ifelse-value [bool tree1 tree2] report ifelse-value bool [ runresult tree1 ][ runresult tree2 ] end to-report gp_+ [input1 input2] report input1 + input2 end to-report gp_- [input1 input2] report input1 - input2 end to-report gp_* [input1 input2] report input1 * input2 end to-report gp_safediv [input1 input2] report ifelse-value (input2 != 0) [input1 / input2][999999] end to-report gp_random [input] report random input end to-report gp_>= [input1 input2] report input1 >= input2 end to-report gp_<= [input1 input2] report input1 <= input2 end to-report gp_> [input1 input2] report input1 >= input2 end to-report gp_< [input1 input2] report input1 >= input2 end
There are 4 versions of this model.
This model does not have any ancestors.
This model does not have any descendants.