From 89f0f3568ab960d48ec15ba230ddf0be242b6d96 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 10 Nov 2020 16:14:23 -0600 Subject: [PATCH 01/32] Add `stack-size` Adds a function that returns the size of the specified stack in a given state. --- src/propeller/push/state.cljc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/propeller/push/state.cljc b/src/propeller/push/state.cljc index 5026dec..f6c0490 100755 --- a/src/propeller/push/state.cljc +++ b/src/propeller/push/state.cljc @@ -35,6 +35,11 @@ [state stack] (empty? (get state stack))) +;; Returns the stack size +(defn stack-size + [state stack] + (count (get state stack))) + ;; Returns the top item on the stack (defn peek-stack [state stack] From 1a882836dea29c123640ddfcd4f92092cedcfdb5 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 10 Nov 2020 16:17:50 -0600 Subject: [PATCH 02/32] Limit number of duplications onto a stack Both `_dup_times` and `_dup_items` are risky and can place very large numbers of items on a stack if the value on the `:integer` is large. This limits both `_dup_times` and `_dup_items` so they never make a stack have more than `max-stack-items` entries. This change was motivated by `OutOfMemory` errors we were receiving when doing runs. This is somewhat modeled after similar limits in Clojush, but we chose to not introduce an `atom` and to instead just define a constant in this namespace. That may not be as flexible as people would like, and we can move to the `atom` solution if necessary. --- src/propeller/push/instructions/polymorphic.cljc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/propeller/push/instructions/polymorphic.cljc b/src/propeller/push/instructions/polymorphic.cljc index 696ef39..8849ab7 100755 --- a/src/propeller/push/instructions/polymorphic.cljc +++ b/src/propeller/push/instructions/polymorphic.cljc @@ -25,11 +25,17 @@ state (state/push-to-stack state stack top-item))))) +;; Limits the number of items that can be duplicated onto a stack at once. +;; We might want to extend to limit all the different that things may be +;; placed on a stack. +(def max-stack-items 100) + ;; Duplicates n copies of the top item (i.e leaves n copies there). Does not pop ;; its argument (since that would negate the effect of the duplication). The ;; number n is determined by the top INTEGER. For n = 0, equivalent to POP. ;; For n = 1, equivalent to NOOP. For n = 2, equivalent to DUP. Negative values -;; of n are treated as 0 +;; of n are treated as 0. The final number of items on the stack is limited to +;; max-stack-items. (def _dup_times ^{:stacks #{:integer}} (fn [stack state] @@ -38,7 +44,8 @@ (and (not= stack :integer) (not (state/empty-stack? state :integer)) (not (state/empty-stack? state stack)))) - (let [n (state/peek-stack state :integer) + (let [n (min (state/peek-stack state :integer) + (inc (- max-stack-items (state/stack-size state stack)))) popped-state (state/pop-stack state :integer) top-item (state/peek-stack popped-state stack) top-item-dup (take (- n 1) (repeat top-item))] @@ -50,12 +57,14 @@ ;; Duplicates the top n items on the stack, one time each. The number n is ;; determined by the top INTEGER. If n <= 0, no items will be duplicated. If ;; fewer than n items are on the stack, the entire stack will be duplicated. +;; The final number of items on the stack is limited to max-stack-items. (def _dup_items ^{:stacks #{:integer}} (fn [stack state] (if (state/empty-stack? state :integer) state - (let [n (state/peek-stack state :integer) + (let [n (min (state/peek-stack state :integer) + (- max-stack-items (state/stack-size state stack))) popped-state (state/pop-stack state :integer) top-items (take n (get popped-state stack))] (state/push-to-stack-many popped-state stack top-items))))) From 451633941236b14d666979418952afe34946634b Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 16:14:01 -0500 Subject: [PATCH 03/32] Pass argmap to plushy->push --- .gitignore | 3 +++ propeller.iml | 19 +++++++++++++++++-- src/propeller/genome.cljc | 2 +- src/propeller/problems/simple_regression.cljc | 2 +- .../problems/software/number_io.cljc | 2 +- src/propeller/problems/software/smallest.cljc | 2 +- .../problems/string_classification.cljc | 2 +- 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index d18f225..c8f7bb6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ pom.xml.asc /.nrepl-port .hgignore .hg/ +*.iml +.idea/ +out diff --git a/propeller.iml b/propeller.iml index 5c05a54..073e101 100644 --- a/propeller.iml +++ b/propeller.iml @@ -5,18 +5,33 @@ - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/propeller/genome.cljc b/src/propeller/genome.cljc index 6c460e2..5c12462 100755 --- a/src/propeller/genome.cljc +++ b/src/propeller/genome.cljc @@ -11,7 +11,7 @@ (defn plushy->push "Returns the Push program expressed by the given plushy representation." - [plushy] + [plushy argmap] (let [opener? #(and (vector? %) (= (first %) 'open))] ;; [open ] marks opens (loop [push () ;; iteratively build the Push program from the plushy plushy (mapcat #(if-let [n (get push/opens %)] [% ['open n]] [%]) plushy)] diff --git a/src/propeller/problems/simple_regression.cljc b/src/propeller/problems/simple_regression.cljc index 9606826..1ff2967 100755 --- a/src/propeller/problems/simple_regression.cljc +++ b/src/propeller/problems/simple_regression.cljc @@ -38,7 +38,7 @@ or 1000000 if no behavior is produced. The behavior is here defined as the final top item on the INTEGER stack." [argmap individual] - (let [program (genome/plushy->push (:plushy individual)) + (let [program (genome/plushy->push (:plushy individual) argmap) inputs (range -10 11) correct-outputs (map target-function inputs) outputs (map (fn [input] diff --git a/src/propeller/problems/software/number_io.cljc b/src/propeller/problems/software/number_io.cljc index cf8819b..afc449d 100755 --- a/src/propeller/problems/software/number_io.cljc +++ b/src/propeller/problems/software/number_io.cljc @@ -63,7 +63,7 @@ ([argmap individual] (error-function argmap individual :train)) ([argmap individual subset] - (let [program (genome/plushy->push (:plushy individual)) + (let [program (genome/plushy->push (:plushy individual) argmap) data (get train-and-test-data subset) inputs (:inputs data) correct-outputs (:outputs data) diff --git a/src/propeller/problems/software/smallest.cljc b/src/propeller/problems/software/smallest.cljc index 915043e..c48876b 100755 --- a/src/propeller/problems/software/smallest.cljc +++ b/src/propeller/problems/software/smallest.cljc @@ -65,7 +65,7 @@ ([argmap individual] (error-function argmap individual :train)) ([argmap individual subset] - (let [program (genome/plushy->push (:plushy individual)) + (let [program (genome/plushy->push (:plushy individual) argmap) data (get train-and-test-data subset) inputs (:inputs data) correct-outputs (:outputs data) diff --git a/src/propeller/problems/string_classification.cljc b/src/propeller/problems/string_classification.cljc index 4d29056..42677c0 100755 --- a/src/propeller/problems/string_classification.cljc +++ b/src/propeller/problems/string_classification.cljc @@ -46,7 +46,7 @@ behavior is produced. The behavior is here defined as the final top item on the BOOLEAN stack." [argmap individual] - (let [program (genome/plushy->push (:plushy individual)) + (let [program (genome/plushy->push (:plushy individual) argmap) inputs ["GCG" "GACAG" "AGAAG" "CCCA" "GATTACA" "TAGG" "GACT"] correct-outputs [false false false false true true true] outputs (map (fn [input] From 12002d17a6052b91a803eba5c31d40f04af9ef37 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 16:19:24 -0500 Subject: [PATCH 04/32] Make plushy->push obey :diploid setting --- src/propeller/genome.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/propeller/genome.cljc b/src/propeller/genome.cljc index 5c12462..8d2bbed 100755 --- a/src/propeller/genome.cljc +++ b/src/propeller/genome.cljc @@ -12,7 +12,8 @@ (defn plushy->push "Returns the Push program expressed by the given plushy representation." [plushy argmap] - (let [opener? #(and (vector? %) (= (first %) 'open))] ;; [open ] marks opens + (let [plushy (if (:diploid argmap) (map first (partition 2 plushy)) plushy) + opener? #(and (vector? %) (= (first %) 'open))] ;; [open ] marks opens (loop [push () ;; iteratively build the Push program from the plushy plushy (mapcat #(if-let [n (get push/opens %)] [% ['open n]] [%]) plushy)] (if (empty? plushy) ;; maybe we're done? From bd5f4aba402a5ed98d58ef1cc5fc81f9f090db96 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 16:25:37 -0500 Subject: [PATCH 05/32] Remove extraneous 2 --- src/propeller/variation.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index d04d1f3..5df807a 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -44,7 +44,7 @@ (crossover (:plushy (selection/select-parent pop argmap)) (:plushy (selection/select-parent pop argmap))) (< prob (+ (:crossover (:variation argmap)) - (:umad (:variation argmap)) 2)) + (:umad (:variation argmap)))) (uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap)) (:instructions argmap) (:umad-rate argmap)) From 0a5e17195e9bab0a4fdc89d125462ea29e9244c4 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 16:30:25 -0500 Subject: [PATCH 06/32] Scale UMAD deletion rate from addion rate for size neutrality --- src/propeller/variation.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 5df807a..03e2837 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -48,5 +48,5 @@ (uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap)) (:instructions argmap) (:umad-rate argmap)) - (:umad-rate argmap)) + (/ 1 (+ (/ 1 (:umad-rate argmap)) 1))) :else (:plushy (selection/select-parent pop argmap))))}) From aadc28d3718f584c91ff1ac714465a86936efa70 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 16:35:46 -0500 Subject: [PATCH 07/32] Make plushy->push use an empty argmap if none was passed. --- src/propeller/genome.cljc | 47 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/propeller/genome.cljc b/src/propeller/genome.cljc index 8d2bbed..d032081 100755 --- a/src/propeller/genome.cljc +++ b/src/propeller/genome.cljc @@ -11,26 +11,27 @@ (defn plushy->push "Returns the Push program expressed by the given plushy representation." - [plushy argmap] - (let [plushy (if (:diploid argmap) (map first (partition 2 plushy)) plushy) - opener? #(and (vector? %) (= (first %) 'open))] ;; [open ] marks opens - (loop [push () ;; iteratively build the Push program from the plushy - plushy (mapcat #(if-let [n (get push/opens %)] [% ['open n]] [%]) plushy)] - (if (empty? plushy) ;; maybe we're done? - (if (some opener? push) ;; done with plushy, but unclosed open - (recur push '(close)) ;; recur with one more close - push) ;; otherwise, really done, return push - (let [i (first plushy)] - (if (= i 'close) - (if (some opener? push) ;; process a close when there's an open - (recur (let [post-open (reverse (take-while (comp not opener?) - (reverse push))) - open-index (- (count push) (count post-open) 1) - num-open (second (nth push open-index)) - pre-open (take open-index push)] - (if (= 1 num-open) - (concat pre-open [post-open]) - (concat pre-open [post-open ['open (dec num-open)]]))) - (rest plushy)) - (recur push (rest plushy))) ;; unmatched close, ignore - (recur (concat push [i]) (rest plushy)))))))) ;; anything else + ([plushy] (plushy->push plushy {})) + ([plushy argmap] + (let [plushy (if (:diploid argmap) (map first (partition 2 plushy)) plushy) + opener? #(and (vector? %) (= (first %) 'open))] ;; [open ] marks opens + (loop [push () ;; iteratively build the Push program from the plushy + plushy (mapcat #(if-let [n (get push/opens %)] [% ['open n]] [%]) plushy)] + (if (empty? plushy) ;; maybe we're done? + (if (some opener? push) ;; done with plushy, but unclosed open + (recur push '(close)) ;; recur with one more close + push) ;; otherwise, really done, return push + (let [i (first plushy)] + (if (= i 'close) + (if (some opener? push) ;; process a close when there's an open + (recur (let [post-open (reverse (take-while (comp not opener?) + (reverse push))) + open-index (- (count push) (count post-open) 1) + num-open (second (nth push open-index)) + pre-open (take open-index push)] + (if (= 1 num-open) + (concat pre-open [post-open]) + (concat pre-open [post-open ['open (dec num-open)]]))) + (rest plushy)) + (recur push (rest plushy))) ;; unmatched close, ignore + (recur (concat push [i]) (rest plushy))))))))) ;; anything else From 55644a9b6d08396a907d45c41154ebb26f580ffe Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:06:39 -0500 Subject: [PATCH 08/32] Make crossover, uniform-addition, and uniform-deletion take argmap arguments --- .gitignore | 1 + src/propeller/variation.cljc | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c8f7bb6..025bb9c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ pom.xml.asc *.iml .idea/ out +notes diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 03e2837..c3d4213 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -4,7 +4,7 @@ (defn crossover "Crosses over two individuals using uniform crossover. Pads shorter one." - [plushy-a plushy-b] + [plushy-a plushy-b argmap] (let [shorter (min-key count plushy-a plushy-b) longer (if (= shorter plushy-a) plushy-b @@ -19,7 +19,7 @@ (defn uniform-addition "Returns plushy with new instructions possibly added before or after each existing instruction." - [plushy instructions umad-rate] + [plushy instructions umad-rate argmap] (apply concat (map #(if (< (rand) umad-rate) (shuffle [% (utils/random-instruction instructions)]) @@ -28,7 +28,7 @@ (defn uniform-deletion "Randomly deletes instructions from plushy at some rate." - [plushy umad-rate] + [plushy umad-rate argmap] (remove (fn [_] (< (rand) (/ 1 (+ 1 (/ 1 umad-rate))))) plushy)) @@ -42,11 +42,14 @@ (cond (< prob (:crossover (:variation argmap))) (crossover (:plushy (selection/select-parent pop argmap)) - (:plushy (selection/select-parent pop argmap))) + (:plushy (selection/select-parent pop argmap)) + argmap) (< prob (+ (:crossover (:variation argmap)) (:umad (:variation argmap)))) (uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap)) (:instructions argmap) - (:umad-rate argmap)) - (/ 1 (+ (/ 1 (:umad-rate argmap)) 1))) + (:umad-rate argmap) + argmap) + (/ 1 (+ (/ 1 (:umad-rate argmap)) 1)) + argmap) :else (:plushy (selection/select-parent pop argmap))))}) From 67fcdb13b56d1a5942ee1d49bf07ade3cd9d9991 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:18:30 -0500 Subject: [PATCH 09/32] Undo deletion rate adjustment (done elsewhere), remove argmap arguments to genetic operators, define diploid genetic operators --- src/propeller/variation.cljc | 48 +++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index c3d4213..4526e56 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -4,7 +4,7 @@ (defn crossover "Crosses over two individuals using uniform crossover. Pads shorter one." - [plushy-a plushy-b argmap] + [plushy-a plushy-b] (let [shorter (min-key count plushy-a plushy-b) longer (if (= shorter plushy-a) plushy-b @@ -16,23 +16,56 @@ shorter-padded longer)))) +(defn diploid-crossover + "Crosses over two individuals using uniform crossover. Pads shorter one." + [plushy-a plushy-b argmap] + (let [plushy-a (partition 2 plushy-a) + plushy-b (partition 2 plushy-b) + shorter (min-key count plushy-a plushy-b) + longer (if (= shorter plushy-a) + plushy-b + plushy-a) + length-diff (- (count longer) (count shorter)) + shorter-padded (concat shorter (repeat length-diff :crossover-padding))] + (flatten (remove #(= % :crossover-padding) + (map #(if (< (rand) 0.5) %1 %2) + shorter-padded + longer))))) + (defn uniform-addition "Returns plushy with new instructions possibly added before or after each existing instruction." - [plushy instructions umad-rate argmap] + [plushy instructions umad-rate] (apply concat (map #(if (< (rand) umad-rate) (shuffle [% (utils/random-instruction instructions)]) [%]) plushy))) +(defn diploid-uniform-addition + "Returns plushy with new instructions possibly added before or after each + existing instruction." + [plushy instructions umad-rate] + (flatten + (map #(if (< (rand) umad-rate) + (shuffle [% (repeatedly 2 #(utils/random-instruction instructions))]) + [%]) + (partition 2 plushy)))) + (defn uniform-deletion "Randomly deletes instructions from plushy at some rate." - [plushy umad-rate argmap] + [plushy umad-rate] (remove (fn [_] (< (rand) (/ 1 (+ 1 (/ 1 umad-rate))))) plushy)) +(defn diploid-uniform-deletion + "Randomly deletes instructions from plushy at some rate." + [plushy umad-rate] + (flatten (remove (fn [_] (< (rand) + (/ 1 (+ 1 (/ 1 umad-rate))))) + (partition 2 plushy)))) + (defn new-individual "Returns a new individual produced by selection and variation of individuals in the population." @@ -42,14 +75,11 @@ (cond (< prob (:crossover (:variation argmap))) (crossover (:plushy (selection/select-parent pop argmap)) - (:plushy (selection/select-parent pop argmap)) - argmap) + (:plushy (selection/select-parent pop argmap))) (< prob (+ (:crossover (:variation argmap)) (:umad (:variation argmap)))) (uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap)) (:instructions argmap) - (:umad-rate argmap) - argmap) - (/ 1 (+ (/ 1 (:umad-rate argmap)) 1)) - argmap) + (:umad-rate argmap)) + (:umad-rate argmap)) :else (:plushy (selection/select-parent pop argmap))))}) From 4df6ea7fdd2b2a365b44fb9c48328cb5c750fb19 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:27:07 -0500 Subject: [PATCH 10/32] Use diploid operators when called for; fix bug in diploid-uniform-addition --- src/propeller/variation.cljc | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 4526e56..cdf0d9a 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -47,9 +47,10 @@ existing instruction." [plushy instructions umad-rate] (flatten - (map #(if (< (rand) umad-rate) - (shuffle [% (repeatedly 2 #(utils/random-instruction instructions))]) - [%]) + (map (fn [pair] + (if (< (rand) umad-rate) + (shuffle [pair (repeatedly 2 #(utils/random-instruction instructions))]) + [pair])) (partition 2 plushy)))) (defn uniform-deletion @@ -63,7 +64,7 @@ "Randomly deletes instructions from plushy at some rate." [plushy umad-rate] (flatten (remove (fn [_] (< (rand) - (/ 1 (+ 1 (/ 1 umad-rate))))) + (/ 1 (+ 1 (/ 1 umad-rate))))) (partition 2 plushy)))) (defn new-individual @@ -71,15 +72,18 @@ individuals in the population." [pop argmap] {:plushy - (let [prob (rand)] + (let [prob (rand) + [xover add del] (if (:diploid argmap) + [diploid-crossover diploid-uniform-addition diploid-uniform-deletion] + [crossover uniform-addition uniform-deletion])] (cond (< prob (:crossover (:variation argmap))) - (crossover (:plushy (selection/select-parent pop argmap)) - (:plushy (selection/select-parent pop argmap))) + (xover (:plushy (selection/select-parent pop argmap)) + (:plushy (selection/select-parent pop argmap))) (< prob (+ (:crossover (:variation argmap)) (:umad (:variation argmap)))) - (uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap)) - (:instructions argmap) - (:umad-rate argmap)) - (:umad-rate argmap)) + (del (add (:plushy (selection/select-parent pop argmap)) + (:instructions argmap) + (:umad-rate argmap)) + (:umad-rate argmap)) :else (:plushy (selection/select-parent pop argmap))))}) From 224d33c19d7f20822697631cb26b96160f551e6f Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:32:49 -0500 Subject: [PATCH 11/32] Add diploid-flip genetic operator --- src/propeller/variation.cljc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index cdf0d9a..de99084 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -67,6 +67,14 @@ (/ 1 (+ 1 (/ 1 umad-rate))))) (partition 2 plushy)))) +(defn diploid-flip + "Randomly flips pairs in a diploid plushy at some rate." + [plushy flip-rate] + (flatten (map #(if (< (rand) flip-rate) + (reverse %) + %) + (partition 2 plushy)))) + (defn new-individual "Returns a new individual produced by selection and variation of individuals in the population." From b6a9a410c141f7da9545ff6abd7650fcb66ee426 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:40:57 -0500 Subject: [PATCH 12/32] Call diploid-flip when appropriate; make new-individual robust to missing rates --- src/propeller/variation.cljc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index de99084..ffe0e9e 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -83,15 +83,22 @@ (let [prob (rand) [xover add del] (if (:diploid argmap) [diploid-crossover diploid-uniform-addition diploid-uniform-deletion] - [crossover uniform-addition uniform-deletion])] + [crossover uniform-addition uniform-deletion]) + xover-rate (or (:crossover (:variation argmap)) 0) + umad-rate (or (:umad (:variation argmap)) 0) + flip-rate (or (:flip (:variation argmap)) 0)] (cond - (< prob (:crossover (:variation argmap))) + (< prob xover-rate) (xover (:plushy (selection/select-parent pop argmap)) (:plushy (selection/select-parent pop argmap))) - (< prob (+ (:crossover (:variation argmap)) - (:umad (:variation argmap)))) + ; + (< prob (+ xover-rate umad-rate)) (del (add (:plushy (selection/select-parent pop argmap)) (:instructions argmap) - (:umad-rate argmap)) - (:umad-rate argmap)) + umad-rate) + umad-rate) + ; + (< prob (+ xover-rate umad-rate flip-rate)) + (diploid-flip (:plushy (selection/select-parent pop argmap)) flip-rate) + ; :else (:plushy (selection/select-parent pop argmap))))}) From 97de757f57e8c0f785b87c9665bccc948dffde7c Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 21:57:29 -0500 Subject: [PATCH 13/32] Begin bringing session.clj up to date --- src/propeller/session.cljc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 3e626db..9317c4d 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -10,9 +10,9 @@ [propeller.push.state :as state])) #_(interpreter/interpret-program - '(1 2 integer_add) state/empty-state 1000) + '(1 2 :integer_add) state/empty-state 1000) -#_(interpreter/interpret-program +(interpreter/interpret-program '(3 5 :integer_eq :exec_if (1 "yes") (2 "no")) state/empty-state 1000) @@ -60,3 +60,15 @@ :max-initial-plushy-size 50 :step-limit 100 :parent-selection :lexicase}) + +(gp/gp {:instructions propeller.problems.software.number-io/instructions + :error-function propeller.problems.software.number-io/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 0.5 :crossover 0.5} + :elitism false}) From 0482ccccf53333add55810383aab546d1fddbc75 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 22:05:42 -0500 Subject: [PATCH 14/32] Correct order of clauses for exec_if --- src/propeller/push/instructions/code.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/push/instructions/code.cljc b/src/propeller/push/instructions/code.cljc index 405fdc9..84c34ef 100755 --- a/src/propeller/push/instructions/code.cljc +++ b/src/propeller/push/instructions/code.cljc @@ -124,7 +124,7 @@ :exec_if ^{:stacks #{:boolean :exec}} (fn [state] - (make-instruction state #(if %1 %3 %2) [:boolean :exec :exec] :exec))) + (make-instruction state #(if %1 %2 %3) [:boolean :exec :exec] :exec))) ;; If the top BOOLEAN is TRUE, leaves the first item on the EXEC stack to be ;; executed. Otherwise, it removes it. Acts as a NOOP unless there is at least From 28da548e4c1db1f42d5b0e551897f1ac18cb1685 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 23:28:02 -0500 Subject: [PATCH 15/32] Undo wrong fix for exec_if; fix peek-stack for false on boolean stack --- src/propeller/push/instructions/code.cljc | 2 +- src/propeller/push/state.cljc | 13 ++++++++++--- src/propeller/push/utils/helpers.cljc | 1 + src/propeller/session.cljc | 20 ++++++++++---------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/propeller/push/instructions/code.cljc b/src/propeller/push/instructions/code.cljc index 84c34ef..405fdc9 100755 --- a/src/propeller/push/instructions/code.cljc +++ b/src/propeller/push/instructions/code.cljc @@ -124,7 +124,7 @@ :exec_if ^{:stacks #{:boolean :exec}} (fn [state] - (make-instruction state #(if %1 %2 %3) [:boolean :exec :exec] :exec))) + (make-instruction state #(if %1 %3 %2) [:boolean :exec :exec] :exec))) ;; If the top BOOLEAN is TRUE, leaves the first item on the EXEC stack to be ;; executed. Otherwise, it removes it. Acts as a NOOP unless there is at least diff --git a/src/propeller/push/state.cljc b/src/propeller/push/state.cljc index 5026dec..3b21193 100755 --- a/src/propeller/push/state.cljc +++ b/src/propeller/push/state.cljc @@ -35,12 +35,19 @@ [state stack] (empty? (get state stack))) +;;; Returns the top item on the stack +;(defn peek-stack +; [state stack] +; (if-let [top-item (first (get state stack))] +; top-item +; :no-stack-item)) + ;; Returns the top item on the stack (defn peek-stack [state stack] - (if-let [top-item (first (get state stack))] - top-item - :no-stack-item)) + (if (empty? (get state stack)) + :no-stack-item + (first (get state stack)))) ;; Returns the top n items on the stack, as a chunk. If there are less than n ;; items on the stack, returns the entire stack diff --git a/src/propeller/push/utils/helpers.cljc b/src/propeller/push/utils/helpers.cljc index 46eb572..f9ab440 100755 --- a/src/propeller/push/utils/helpers.cljc +++ b/src/propeller/push/utils/helpers.cljc @@ -32,6 +32,7 @@ (defn make-instruction [state function arg-stacks return-stack] (let [popped-args (get-args-from-stacks state arg-stacks)] + (println popped-args) (if (= popped-args :not-enough-args) state (let [result (apply function (:args popped-args)) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 9317c4d..8af6922 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -13,21 +13,21 @@ '(1 2 :integer_add) state/empty-state 1000) (interpreter/interpret-program - '(3 5 :integer_eq :exec_if (1 "yes") (2 "no")) + '(3 3 :integer_eq :exec_if (1 "yes") (2 "no")) state/empty-state 1000) -#_(interpreter/interpret-program - '(in1 :string_reverse 1 :string_take "?" :string_eq :exec_if - (in1 " I am asking." :string_concat) - (in1 " I am saying." :string_concat)) +(interpreter/interpret-program + '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if + (:in1 " I am asking." :string_concat) + (:in1 " I am saying." :string_concat)) (assoc state/empty-state :input {:in1 "Can you hear me?"}) 1000) -#_(interpreter/interpret-program - '(in1 :string_reverse 1 :string_take "?" :string_eq :exec_if - (in1 " I am asking." :string_concat) - (in1 " I am saying." :string_concat)) +(interpreter/interpret-program + '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if + (:in1 " I am asking." :string_concat) + (:in1 " I am saying." :string_concat)) (assoc state/empty-state :input {:in1 "I can hear you."}) 1000) @@ -61,7 +61,7 @@ :step-limit 100 :parent-selection :lexicase}) -(gp/gp {:instructions propeller.problems.software.number-io/instructions +#_(gp/gp {:instructions propeller.problems.software.number-io/instructions :error-function propeller.problems.software.number-io/error-function :max-generations 500 :population-size 500 From f8bdacc388af7ee9bb709bc6950be24f56cb6d3f Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 22 Nov 2020 23:35:07 -0500 Subject: [PATCH 16/32] Remove debugging printing; update session.clj --- src/propeller/push/utils/helpers.cljc | 1 - src/propeller/session.cljc | 40 +++++---------------------- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/propeller/push/utils/helpers.cljc b/src/propeller/push/utils/helpers.cljc index f9ab440..46eb572 100755 --- a/src/propeller/push/utils/helpers.cljc +++ b/src/propeller/push/utils/helpers.cljc @@ -32,7 +32,6 @@ (defn make-instruction [state function arg-stacks return-stack] (let [popped-args (get-args-from-stacks state arg-stacks)] - (println popped-args) (if (= popped-args :not-enough-args) state (let [result (apply function (:args popped-args)) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 8af6922..e638c35 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -7,24 +7,25 @@ [propeller.problems.string-classification :as string-classif] [propeller.push.core :as push] [propeller.push.interpreter :as interpreter] - [propeller.push.state :as state])) + [propeller.push.state :as state] + [propeller.push.utils.helpers :refer [get-stack-instructions]])) #_(interpreter/interpret-program '(1 2 :integer_add) state/empty-state 1000) -(interpreter/interpret-program +#_(interpreter/interpret-program '(3 3 :integer_eq :exec_if (1 "yes") (2 "no")) state/empty-state 1000) -(interpreter/interpret-program +#_(interpreter/interpret-program '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if (:in1 " I am asking." :string_concat) (:in1 " I am saying." :string_concat)) (assoc state/empty-state :input {:in1 "Can you hear me?"}) 1000) -(interpreter/interpret-program +#_(interpreter/interpret-program '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if (:in1 " I am asking." :string_concat) (:in1 " I am saying." :string_concat)) @@ -32,36 +33,9 @@ 1000) #_(genome/plushy->push - (genome/make-random-plushy push/default-instructions 20)) + (genome/make-random-plushy (get-stack-instructions #{:float :integer :exec :boolean}) 20)) -#_(interpreter/interpret-program - (genome/plushy->push - (genome/make-random-plushy push/default-instructions 20)) - (assoc state/empty-state :input {:in1 "I can hear you."}) - 1000) - -;; ============================================================================= -;; Target function: f(x) = x^3 + x + 3 -;; ============================================================================= - -#_(gp/gp {:instructions push/default-instructions - :error-function regression/error-function - :max-generations 50 - :population-size 200 - :max-initial-plushy-size 50 - :step-limit 100 - :parent-selection :tournament - :tournament-size 5}) - -#_(gp/gp {:instructions push/default-instructions - :error-function string-classif/error-function - :max-generations 50 - :population-size 200 - :max-initial-plushy-size 50 - :step-limit 100 - :parent-selection :lexicase}) - -#_(gp/gp {:instructions propeller.problems.software.number-io/instructions +(gp/gp {:instructions propeller.problems.software.number-io/instructions :error-function propeller.problems.software.number-io/error-function :max-generations 500 :population-size 500 From 509c22b79c68b423ac7cb17ae9ad12ebb80d8e3e Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Mon, 23 Nov 2020 07:49:40 -0500 Subject: [PATCH 17/32] Fix naming clash in new-individual --- src/propeller/session.cljc | 29 ++++++++++++++++++++++++++++- src/propeller/variation.cljc | 18 +++++++++--------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index e638c35..e0469cb 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -35,7 +35,7 @@ #_(genome/plushy->push (genome/make-random-plushy (get-stack-instructions #{:float :integer :exec :boolean}) 20)) -(gp/gp {:instructions propeller.problems.software.number-io/instructions +#_(gp/gp {:instructions propeller.problems.software.number-io/instructions :error-function propeller.problems.software.number-io/error-function :max-generations 500 :population-size 500 @@ -46,3 +46,30 @@ :umad-rate 0.1 :variation {:umad 0.5 :crossover 0.5} :elitism false}) + +#_(gp/gp {:instructions propeller.problems.simple-regression/instructions + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :tournament + :tournament-size 5 + :umad-rate 0.01 + :variation {:umad 1.0 + :crossover 0.0} + :elitism false}) + +(gp/gp {:instructions propeller.problems.simple-regression/instructions + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :tournament + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 1.0 + :crossover 0.0} + :elitism false}) + diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index ffe0e9e..8b98f18 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -84,21 +84,21 @@ [xover add del] (if (:diploid argmap) [diploid-crossover diploid-uniform-addition diploid-uniform-deletion] [crossover uniform-addition uniform-deletion]) - xover-rate (or (:crossover (:variation argmap)) 0) - umad-rate (or (:umad (:variation argmap)) 0) - flip-rate (or (:flip (:variation argmap)) 0)] + xover-prob (or (:crossover (:variation argmap)) 0) + umad-prob (or (:umad (:variation argmap)) 0) + flip-prob (or (:flip (:variation argmap)) 0)] (cond - (< prob xover-rate) + (< prob xover-prob) (xover (:plushy (selection/select-parent pop argmap)) (:plushy (selection/select-parent pop argmap))) ; - (< prob (+ xover-rate umad-rate)) + (< prob (+ xover-prob umad-prob)) (del (add (:plushy (selection/select-parent pop argmap)) (:instructions argmap) - umad-rate) - umad-rate) + (:umad-rate argmap)) + (:umad-rate argmap)) ; - (< prob (+ xover-rate umad-rate flip-rate)) - (diploid-flip (:plushy (selection/select-parent pop argmap)) flip-rate) + (< prob (+ xover-prob umad-prob flip-prob)) + (diploid-flip (:plushy (selection/select-parent pop argmap)) flip-prob) ; :else (:plushy (selection/select-parent pop argmap))))}) From 3ba030ceece90d2de3efc4472e195de8fceff3a8 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Mon, 23 Nov 2020 09:51:19 -0500 Subject: [PATCH 18/32] Add train/test data support to simple-regression problem --- src/propeller/problems/simple_regression.cljc | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/propeller/problems/simple_regression.cljc b/src/propeller/problems/simple_regression.cljc index 1ff2967..a8c4993 100755 --- a/src/propeller/problems/simple_regression.cljc +++ b/src/propeller/problems/simple_regression.cljc @@ -32,31 +32,43 @@ 0 1)) +(defn train-and-test-data + [target-function] + (let [train-inputs (range -10 11) + test-inputs (concat (range -20 -10) (range 11 21))] + {:train {:inputs train-inputs + :outputs (map target-function train-inputs)} + :test {:inputs test-inputs + :outputs (map target-function test-inputs)}})) + (defn error-function "Finds the behaviors and errors of an individual. The error is the absolute deviation between the target output value and the program's selected behavior, or 1000000 if no behavior is produced. The behavior is here defined as the final top item on the INTEGER stack." - [argmap individual] - (let [program (genome/plushy->push (:plushy individual) argmap) - inputs (range -10 11) - correct-outputs (map target-function inputs) - outputs (map (fn [input] - (state/peek-stack - (interpreter/interpret-program - program - (assoc state/empty-state :input {:in1 input}) - (:step-limit argmap)) - :integer)) - inputs) - errors (map (fn [correct-output output] - (if (= output :no-stack-item) - 1000000 - (math/abs (- correct-output output)))) - correct-outputs - outputs)] - (assoc individual - :behaviors outputs - :errors errors - :total-error #?(:clj (apply +' errors) - :cljs (apply + errors))))) + ([argmap individual] + (error-function argmap individual :train)) + ([argmap individual subset] + (let [program (genome/plushy->push (:plushy individual) argmap) + data (get (train-and-test-data target-function) subset) + inputs (:inputs data) + correct-outputs (:outputs data) + outputs (map (fn [input] + (state/peek-stack + (interpreter/interpret-program + program + (assoc state/empty-state :input {:in1 input}) + (:step-limit argmap)) + :integer)) + inputs) + errors (map (fn [correct-output output] + (if (= output :no-stack-item) + 1000000 + (math/abs (- correct-output output)))) + correct-outputs + outputs)] + (assoc individual + :behaviors outputs + :errors errors + :total-error #?(:clj (apply +' errors) + :cljs (apply + errors)))))) From 35ac379f06d0260fa0af08cc4e55c58af73052d0 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Tue, 24 Nov 2020 16:09:44 -0500 Subject: [PATCH 19/32] Add more examples to session.clj --- src/propeller/session.cljc | 110 ++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 25 deletions(-) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index e0469cb..38057a6 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -20,15 +20,15 @@ #_(interpreter/interpret-program '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if - (:in1 " I am asking." :string_concat) - (:in1 " I am saying." :string_concat)) + (:in1 " I am asking." :string_concat) + (:in1 " I am saying." :string_concat)) (assoc state/empty-state :input {:in1 "Can you hear me?"}) 1000) #_(interpreter/interpret-program '(:in1 :string_reverse 1 :string_take "?" :string_eq :exec_if - (:in1 " I am asking." :string_concat) - (:in1 " I am saying." :string_concat)) + (:in1 " I am asking." :string_concat) + (:in1 " I am saying." :string_concat)) (assoc state/empty-state :input {:in1 "I can hear you."}) 1000) @@ -36,16 +36,56 @@ (genome/make-random-plushy (get-stack-instructions #{:float :integer :exec :boolean}) 20)) #_(gp/gp {:instructions propeller.problems.software.number-io/instructions - :error-function propeller.problems.software.number-io/error-function - :max-generations 500 - :population-size 500 - :max-initial-plushy-size 100 - :step-limit 200 - :parent-selection :lexicase - :tournament-size 5 - :umad-rate 0.1 - :variation {:umad 0.5 :crossover 0.5} - :elitism false}) + :error-function propeller.problems.software.number-io/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 0.5 :crossover 0.5} + :elitism false}) + +#_(gp/gp {:instructions propeller.problems.simple-regression/instructions + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :tournament + :tournament-size 5 + :umad-rate 0.01 + :variation {:umad 1.0 + :crossover 0.0} + :elitism false}) + +#_(gp/gp {:instructions propeller.problems.simple-regression/instructions + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :tournament + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 1.0 + :crossover 0.0} + :elitism false}) + + +#_(gp/gp {:instructions propeller.problems.simple-regression/instructions + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 1.0 + :crossover 0.0} + :elitism false}) #_(gp/gp {:instructions propeller.problems.simple-regression/instructions :error-function propeller.problems.simple-regression/error-function @@ -53,23 +93,43 @@ :population-size 500 :max-initial-plushy-size 100 :step-limit 200 - :parent-selection :tournament + :parent-selection :lexicase :tournament-size 5 - :umad-rate 0.01 - :variation {:umad 1.0 - :crossover 0.0} - :elitism false}) + :umad-rate 0.1 + :variation {:umad 0.8 + :flip 0.2} + :elitism false + :diploid true}) -(gp/gp {:instructions propeller.problems.simple-regression/instructions - :error-function propeller.problems.simple-regression/error-function + +#_(gp/gp {:instructions propeller.problems.software.smallest/instructions + :error-function propeller.problems.software.smallest/error-function :max-generations 500 :population-size 500 :max-initial-plushy-size 100 :step-limit 200 - :parent-selection :tournament + :parent-selection :lexicase :tournament-size 5 :umad-rate 0.1 - :variation {:umad 1.0 - :crossover 0.0} - :elitism false}) + :variation {;:umad 0.8 + ;:flip 0.2 + :umad 1 + } + :elitism false + :diploid false}) +(gp/gp {:instructions propeller.problems.software.smallest/instructions + :error-function propeller.problems.software.smallest/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 200 ;100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :variation {:umad 0.8 + :flip 0.2 + ;:umad 1 + } + :elitism false + :diploid true}) \ No newline at end of file From da848522453a67d0fb0cced028cb312864461a0d Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 24 Nov 2020 16:00:03 -0600 Subject: [PATCH 20/32] Move `max-stack-items` to globals This creates a `push/utils/globals/cljc` similar to that created by @mcgirjau and moved the `max-stack-items` def to that namespace, updating `polymorphic` as necessary. --- src/propeller/push/instructions/polymorphic.cljc | 14 +++++--------- src/propeller/push/utils/globals.cljc | 11 +++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 src/propeller/push/utils/globals.cljc diff --git a/src/propeller/push/instructions/polymorphic.cljc b/src/propeller/push/instructions/polymorphic.cljc index 8849ab7..485a64a 100755 --- a/src/propeller/push/instructions/polymorphic.cljc +++ b/src/propeller/push/instructions/polymorphic.cljc @@ -5,6 +5,7 @@ (:require [propeller.utils :as utils] [propeller.push.state :as state] [propeller.push.utils.helpers :refer [make-instruction]] + [propeller.push.utils.globals :as globals] #?(:clj [propeller.push.utils.macros :refer [def-instruction generate-instructions]]))) @@ -25,17 +26,12 @@ state (state/push-to-stack state stack top-item))))) -;; Limits the number of items that can be duplicated onto a stack at once. -;; We might want to extend to limit all the different that things may be -;; placed on a stack. -(def max-stack-items 100) - ;; Duplicates n copies of the top item (i.e leaves n copies there). Does not pop ;; its argument (since that would negate the effect of the duplication). The ;; number n is determined by the top INTEGER. For n = 0, equivalent to POP. ;; For n = 1, equivalent to NOOP. For n = 2, equivalent to DUP. Negative values ;; of n are treated as 0. The final number of items on the stack is limited to -;; max-stack-items. +;; globals/max-stack-items. (def _dup_times ^{:stacks #{:integer}} (fn [stack state] @@ -45,7 +41,7 @@ (not (state/empty-stack? state :integer)) (not (state/empty-stack? state stack)))) (let [n (min (state/peek-stack state :integer) - (inc (- max-stack-items (state/stack-size state stack)))) + (inc (- globals/max-stack-items (state/stack-size state stack)))) popped-state (state/pop-stack state :integer) top-item (state/peek-stack popped-state stack) top-item-dup (take (- n 1) (repeat top-item))] @@ -57,14 +53,14 @@ ;; Duplicates the top n items on the stack, one time each. The number n is ;; determined by the top INTEGER. If n <= 0, no items will be duplicated. If ;; fewer than n items are on the stack, the entire stack will be duplicated. -;; The final number of items on the stack is limited to max-stack-items. +;; The final number of items on the stack is limited to globals/max-stack-items. (def _dup_items ^{:stacks #{:integer}} (fn [stack state] (if (state/empty-stack? state :integer) state (let [n (min (state/peek-stack state :integer) - (- max-stack-items (state/stack-size state stack))) + (- globals/max-stack-items (state/stack-size state stack))) popped-state (state/pop-stack state :integer) top-items (take n (get popped-state stack))] (state/push-to-stack-many popped-state stack top-items))))) diff --git a/src/propeller/push/utils/globals.cljc b/src/propeller/push/utils/globals.cljc new file mode 100644 index 0000000..26c37f1 --- /dev/null +++ b/src/propeller/push/utils/globals.cljc @@ -0,0 +1,11 @@ +(ns propeller.push.utils.globals) + +;; ============================================================================= +;; Values used by the Push instructions to keep the stack sizes within +;; reasonable limits. +;; ============================================================================= + +;; Limits the number of items that can be duplicated onto a stack at once. +;; We might want to extend this to limit all the different that things may be +;; placed on a stack. +(def max-stack-items 100) \ No newline at end of file From e05290d87c0f49e4636096ae394b6baf35fb8e54 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 24 Nov 2020 16:42:22 -0600 Subject: [PATCH 21/32] Support command-line problem specification This change uses the first command-line argument as the problem, which is assumed to be a namespace under `propeller.problems`. This will load the `instructions` and `error-function` from that namespace. If no problem is specified, then a (hopefully) helpful error is generated and the programs exits. This allows us to call Propeller with calls such as: ``` lein run software.smallest ``` --- src/propeller/core.cljc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/propeller/core.cljc b/src/propeller/core.cljc index d88ecc1..720184f 100755 --- a/src/propeller/core.cljc +++ b/src/propeller/core.cljc @@ -3,18 +3,26 @@ (:require [propeller.gp :as gp] [propeller.problems.simple-regression :as regression] [propeller.problems.string-classification :as string-classif] - [propeller.problems.software.number-io :as number-io] - [propeller.problems.software.smallest :as smallest] #?(:cljs [cljs.reader :refer [read-string]]))) +(defn eval-problem-var + [problem-name var-name] + (eval (symbol (str "propeller.problems." problem-name "/" var-name)))) + (defn -main "Runs propel-gp, giving it a map of arguments." [& args] + (when (empty? args) + (println "You must specify a problem to run.") + (println "Try, for example:") + (println " lein run software.smallest") + (System/exit 1)) + (require (symbol (str "propeller.problems." (first args)))) (gp/gp (update-in (merge - {:instructions number-io/instructions - :error-function number-io/error-function + {:instructions (eval-problem-var (first args) "instructions") + :error-function (eval-problem-var (first args) "error-function") :max-generations 500 :population-size 500 :max-initial-plushy-size 100 @@ -25,6 +33,6 @@ :variation {:umad 0.5 :crossover 0.5} :elitism false} (apply hash-map - (map read-string args))) + (map read-string (rest args)))) [:error-function] identity))) From 81f2c3197fd0c79327586ff090bbfb8bcd76111a Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 29 Nov 2020 21:32:42 -0500 Subject: [PATCH 22/32] Distinguish flip rate from flip probability --- src/propeller/session.cljc | 63 +++++++++++++++++++----------------- src/propeller/variation.cljc | 3 +- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 38057a6..9c54e98 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -88,47 +88,50 @@ :elitism false}) #_(gp/gp {:instructions propeller.problems.simple-regression/instructions - :error-function propeller.problems.simple-regression/error-function - :max-generations 500 - :population-size 500 - :max-initial-plushy-size 100 - :step-limit 200 - :parent-selection :lexicase - :tournament-size 5 - :umad-rate 0.1 - :variation {:umad 0.8 - :flip 0.2} - :elitism false - :diploid true}) + :error-function propeller.problems.simple-regression/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :diploid-flip-rate 0.1 + :variation {:umad 0.8 + :diploid-flip 0.2} + :elitism false + :diploid true}) +#_(gp/gp {:instructions propeller.problems.software.smallest/instructions + :error-function propeller.problems.software.smallest/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :diploid-flip-rate 0.1 + :variation {;:umad 0.8 + ;:diploid-flip 0.2 + :umad 1 + } + :elitism false + :diploid false}) + #_(gp/gp {:instructions propeller.problems.software.smallest/instructions :error-function propeller.problems.software.smallest/error-function :max-generations 500 :population-size 500 - :max-initial-plushy-size 100 - :step-limit 200 - :parent-selection :lexicase - :tournament-size 5 - :umad-rate 0.1 - :variation {;:umad 0.8 - ;:flip 0.2 - :umad 1 - } - :elitism false - :diploid false}) - -(gp/gp {:instructions propeller.problems.software.smallest/instructions - :error-function propeller.problems.software.smallest/error-function - :max-generations 500 - :population-size 500 - :max-initial-plushy-size 200 ;100 + :max-initial-plushy-size 200 ;100 :step-limit 200 :parent-selection :lexicase :tournament-size 5 :umad-rate 0.1 + :diploid-flip-rate 0.1 :variation {:umad 0.8 - :flip 0.2 + :diploid-flip 0.2 ;:umad 1 } :elitism false diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 8b98f18..3088c62 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -99,6 +99,7 @@ (:umad-rate argmap)) ; (< prob (+ xover-prob umad-prob flip-prob)) - (diploid-flip (:plushy (selection/select-parent pop argmap)) flip-prob) + (diploid-flip (:plushy (selection/select-parent pop argmap)) + (:diploid-flip-rate argmap)) ; :else (:plushy (selection/select-parent pop argmap))))}) From 0c6ebde41bf43fdf00e9d085b8cb89d17eab9d13 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 29 Nov 2020 21:43:00 -0500 Subject: [PATCH 23/32] Fix :flip that should be :diploid-flip --- src/propeller/push/state.cljc | 7 ------- src/propeller/variation.cljc | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/propeller/push/state.cljc b/src/propeller/push/state.cljc index 3b21193..f06bc89 100755 --- a/src/propeller/push/state.cljc +++ b/src/propeller/push/state.cljc @@ -35,13 +35,6 @@ [state stack] (empty? (get state stack))) -;;; Returns the top item on the stack -;(defn peek-stack -; [state stack] -; (if-let [top-item (first (get state stack))] -; top-item -; :no-stack-item)) - ;; Returns the top item on the stack (defn peek-stack [state stack] diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 3088c62..7c2acd3 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -86,7 +86,7 @@ [crossover uniform-addition uniform-deletion]) xover-prob (or (:crossover (:variation argmap)) 0) umad-prob (or (:umad (:variation argmap)) 0) - flip-prob (or (:flip (:variation argmap)) 0)] + flip-prob (or (:diploid-flip (:variation argmap)) 0)] (cond (< prob xover-prob) (xover (:plushy (selection/select-parent pop argmap)) From 4783d6695ad731e829e6b8a5d5d64c7d24f11254 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 29 Nov 2020 22:09:36 -0500 Subject: [PATCH 24/32] Fix string-classification problem by adding train-and-test-data and fixing wrong instruction name --- .../problems/string_classification.cljc | 64 +++++++++++-------- src/propeller/session.cljc | 18 +++++- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/propeller/problems/string_classification.cljc b/src/propeller/problems/string_classification.cljc index 42677c0..d6d0c0a 100755 --- a/src/propeller/problems/string_classification.cljc +++ b/src/propeller/problems/string_classification.cljc @@ -27,7 +27,7 @@ :string_reverse :string_concat :string_length - :string_includes? + :string_contains 'close 0 1 @@ -40,33 +40,45 @@ "G" "T")) +(defn train-and-test-data + [] + (let [train-inputs ["GCG" "GACAG" "AGAAG" "CCCA" "GATTACA" "TAGG" "GACT"] + test-inputs ["GCGT" "GACTTAG" "AGTAAG" "TCCTCA" "GAACA" "AGG" "GAC"]] + {:train {:inputs train-inputs + :outputs [false false false false true true true]} + :test {:inputs test-inputs + :outputs [true true true true false false false]}})) + (defn error-function "Finds the behaviors and errors of an individual: Error is 0 if the value and the program's selected behavior match, or 1 if they differ, or 1000000 if no behavior is produced. The behavior is here defined as the final top item on the BOOLEAN stack." - [argmap individual] - (let [program (genome/plushy->push (:plushy individual) argmap) - inputs ["GCG" "GACAG" "AGAAG" "CCCA" "GATTACA" "TAGG" "GACT"] - correct-outputs [false false false false true true true] - outputs (map (fn [input] - (state/peek-stack - (interpreter/interpret-program - program - (assoc state/empty-state :input {:in1 input}) - (:step-limit argmap)) - :boolean)) - inputs) - errors (map (fn [correct-output output] - (if (= output :no-stack-item) - 1000000 - (if (= correct-output output) - 0 - 1))) - correct-outputs - outputs)] - (assoc individual - :behaviors outputs - :errors errors - :total-error #?(:clj (apply +' errors) - :cljs (apply + errors))))) \ No newline at end of file + ([argmap individual] + (error-function argmap individual :train)) + ([argmap individual subset] + (let [program (genome/plushy->push (:plushy individual) argmap) + data (get (train-and-test-data) subset) + inputs (:inputs data) + correct-outputs (:outputs data) + outputs (map (fn [input] + (state/peek-stack + (interpreter/interpret-program + program + (assoc state/empty-state :input {:in1 input}) + (:step-limit argmap)) + :boolean)) + inputs) + errors (map (fn [correct-output output] + (if (= output :no-stack-item) + 1000000 + (if (= correct-output output) + 0 + 1))) + correct-outputs + outputs)] + (assoc individual + :behaviors outputs + :errors errors + :total-error #?(:clj (apply +' errors) + :cljs (apply + errors)))))) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 9c54e98..20d62a6 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -135,4 +135,20 @@ ;:umad 1 } :elitism false - :diploid true}) \ No newline at end of file + :diploid true}) + +#_(gp/gp {:instructions propeller.problems.string-classification/instructions + :error-function propeller.problems.string-classification/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :diploid-flip-rate 0.1 + :variation {:umad 0.8 + :diploid-flip 0.2 + } + :elitism false + :diploid true}) From d309869919c0a2dbf33ce14adf2da0e85e049b8d Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 29 Nov 2020 22:16:29 -0500 Subject: [PATCH 25/32] Comment out shutdown-agents since agents not currently used and this messes up REPL sessions --- src/propeller/gp.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/propeller/gp.cljc b/src/propeller/gp.cljc index f116eaa..1f7e927 100755 --- a/src/propeller/gp.cljc +++ b/src/propeller/gp.cljc @@ -57,7 +57,8 @@ (if (zero? (:total-error (error-function argmap best-individual :test))) (println "Test cases passed.") (println "Test cases failed.")) - (#?(:clj shutdown-agents))) + ;(#?(:clj shutdown-agents)) + ) ;; (>= generation max-generations) nil From 14e6cfcd087f4a5ef1615cf4e5e70beac88520ac Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Sun, 29 Nov 2020 22:29:31 -0500 Subject: [PATCH 26/32] Reformat --- src/propeller/session.cljc | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/propeller/session.cljc b/src/propeller/session.cljc index 20d62a6..7154b7e 100755 --- a/src/propeller/session.cljc +++ b/src/propeller/session.cljc @@ -97,7 +97,7 @@ :tournament-size 5 :umad-rate 0.1 :diploid-flip-rate 0.1 - :variation {:umad 0.8 + :variation {:umad 0.8 :diploid-flip 0.2} :elitism false :diploid true}) @@ -121,21 +121,21 @@ :diploid false}) #_(gp/gp {:instructions propeller.problems.software.smallest/instructions - :error-function propeller.problems.software.smallest/error-function - :max-generations 500 - :population-size 500 - :max-initial-plushy-size 200 ;100 - :step-limit 200 - :parent-selection :lexicase - :tournament-size 5 - :umad-rate 0.1 - :diploid-flip-rate 0.1 - :variation {:umad 0.8 - :diploid-flip 0.2 - ;:umad 1 - } - :elitism false - :diploid true}) + :error-function propeller.problems.software.smallest/error-function + :max-generations 500 + :population-size 500 + :max-initial-plushy-size 200 ;100 + :step-limit 200 + :parent-selection :lexicase + :tournament-size 5 + :umad-rate 0.1 + :diploid-flip-rate 0.1 + :variation {:umad 0.8 + :diploid-flip 0.2 + ;:umad 1 + } + :elitism false + :diploid true}) #_(gp/gp {:instructions propeller.problems.string-classification/instructions :error-function propeller.problems.string-classification/error-function @@ -147,7 +147,7 @@ :tournament-size 5 :umad-rate 0.1 :diploid-flip-rate 0.1 - :variation {:umad 0.8 + :variation {:umad 0.8 :diploid-flip 0.2 } :elitism false From aed8eb8a76cfffc1647384756245b6f75bdca9ff Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Tue, 8 Dec 2020 12:20:26 -0500 Subject: [PATCH 27/32] Fix handling of boolean input values --- src/propeller/push/instructions/input_output.cljc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/propeller/push/instructions/input_output.cljc b/src/propeller/push/instructions/input_output.cljc index 55db2ee..2a622e1 100755 --- a/src/propeller/push/instructions/input_output.cljc +++ b/src/propeller/push/instructions/input_output.cljc @@ -12,16 +12,16 @@ ;; ============================================================================= ;; Allows Push to handle input instructions of the form :inN, e.g. :in2, taking -;; elements thus labeled from the :input stack and pushing them onto the :exec -;; stack. We can tell whether a particular inN instruction is valid if N-1 -;; values are on the input stack. +;; elements thus labeled from the :input map and pushing them onto the :exec +;; stack. (defn handle-input-instruction [state instruction] - (if-let [input (instruction (:input state))] - (state/push-to-stack state :exec input) - (throw #?(:clj (Exception. (str "Undefined input instruction " instruction)) + (if (contains? (:input state) instruction) + (let [input (instruction (:input state))] + (state/push-to-stack state :exec input)) + (throw #?(:clj (Exception. (str "Undefined instruction " instruction)) :cljs (js/Error - (str "Undefined input instruction " instruction)))))) + (str "Undefined instruction " instruction)))))) ;; ============================================================================= ;; OUTPUT Instructions From 09d5455da39f1ddc60c9dafc70f8c436616e4cb8 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Tue, 8 Dec 2020 12:21:40 -0500 Subject: [PATCH 28/32] Remove extraneous argmap argument --- src/propeller/variation.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 7c2acd3..1c01464 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -18,7 +18,7 @@ (defn diploid-crossover "Crosses over two individuals using uniform crossover. Pads shorter one." - [plushy-a plushy-b argmap] + [plushy-a plushy-b] (let [plushy-a (partition 2 plushy-a) plushy-b (partition 2 plushy-b) shorter (min-key count plushy-a plushy-b) From 0a5a65518126cea9a097fb234f8ad1480b455d4b Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Tue, 8 Dec 2020 22:59:44 -0500 Subject: [PATCH 29/32] Make genetic operator dispatch more modular and extensible --- src/propeller/variation.cljc | 75 ++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 1c01464..1f29067 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -80,26 +80,61 @@ individuals in the population." [pop argmap] {:plushy - (let [prob (rand) - [xover add del] (if (:diploid argmap) - [diploid-crossover diploid-uniform-addition diploid-uniform-deletion] - [crossover uniform-addition uniform-deletion]) - xover-prob (or (:crossover (:variation argmap)) 0) - umad-prob (or (:umad (:variation argmap)) 0) - flip-prob (or (:diploid-flip (:variation argmap)) 0)] - (cond - (< prob xover-prob) - (xover (:plushy (selection/select-parent pop argmap)) - (:plushy (selection/select-parent pop argmap))) + (let [r (rand) + op (loop [accum 0.0 + ops-probs (vec (:variation argmap))] + (if (empty? ops-probs) + :reproduction + (let [[op1 prob1] (first ops-probs)] + (if (>= (+ accum prob1) r) + op1 + (recur (+ accum prob1) + (rest ops-probs))))))] + (case op + :crossover + (crossover + (:plushy (selection/select-parent pop argmap)) + (:plushy (selection/select-parent pop argmap))) ; - (< prob (+ xover-prob umad-prob)) - (del (add (:plushy (selection/select-parent pop argmap)) - (:instructions argmap) - (:umad-rate argmap)) - (:umad-rate argmap)) + :umad + (-> (:plushy (selection/select-parent pop argmap)) + (uniform-addition (:instructions argmap) (:umad-rate argmap)) + (uniform-deletion (:umad-rate argmap))) ; - (< prob (+ xover-prob umad-prob flip-prob)) - (diploid-flip (:plushy (selection/select-parent pop argmap)) - (:diploid-flip-rate argmap)) + :uniform-addition + (-> (:plushy (selection/select-parent pop argmap)) + (uniform-addition (:instructions argmap) (:umad-rate argmap))) ; - :else (:plushy (selection/select-parent pop argmap))))}) + :uniform-deletion + (-> (:plushy (selection/select-parent pop argmap)) + (uniform-deletion (:umad-rate argmap))) + ; + :diploid-crossover + (diploid-crossover + (:plushy (selection/select-parent pop argmap)) + (:plushy (selection/select-parent pop argmap))) + ; + :diploid-umad + (-> (:plushy (selection/select-parent pop argmap)) + (diploid-uniform-addition (:instructions argmap) (:umad-rate argmap)) + (diploid-uniform-deletion (:umad-rate argmap))) + ; + :diploid-uniform-addition + (-> (:plushy (selection/select-parent pop argmap)) + (diploid-uniform-addition (:instructions argmap) (:umad-rate argmap))) + ; + :diploid-uniform-deletion + (-> (:plushy (selection/select-parent pop argmap)) + (diploid-uniform-deletion (:umad-rate argmap))) + ; + :diploid-flip + (-> (:plushy (selection/select-parent pop argmap)) + (diploid-flip (:diploid-flip-rate argmap))) + ; + :reproduction + (:plushy (selection/select-parent pop argmap)) + ; + :else + (throw #?(:clj (Exception. (str "No match in new-individual for " op)) + :cljs (js/Error + (str "No match in new-individual for " op))))))}) From 40ac1c73a4ecded1a69fa9acf3c147c191487e47 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Wed, 9 Dec 2020 10:44:57 -0500 Subject: [PATCH 30/32] Add uniform-replacement --- src/propeller/variation.cljc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/propeller/variation.cljc b/src/propeller/variation.cljc index 1f29067..d35c452 100755 --- a/src/propeller/variation.cljc +++ b/src/propeller/variation.cljc @@ -42,6 +42,15 @@ [%]) plushy))) +(defn uniform-replacement + "Returns plushy with new instructions possibly replacing existing + instructions." + [plushy instructions replacement-rate] + (map #(if (< (rand) replacement-rate) + (utils/random-instruction instructions) + %) + plushy)) + (defn diploid-uniform-addition "Returns plushy with new instructions possibly added before or after each existing instruction." @@ -105,6 +114,10 @@ (-> (:plushy (selection/select-parent pop argmap)) (uniform-addition (:instructions argmap) (:umad-rate argmap))) ; + :uniform-replacement + (-> (:plushy (selection/select-parent pop argmap)) + (uniform-replacement (:instructions argmap) (:replacement-rate argmap))) + ; :uniform-deletion (-> (:plushy (selection/select-parent pop argmap)) (uniform-deletion (:umad-rate argmap))) From b68cf4ad7587d2fc70afd7506d1be3c90535e48f Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Wed, 9 Dec 2020 10:46:02 -0500 Subject: [PATCH 31/32] Pass argmap to plushy->post in report --- src/propeller/gp.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/gp.cljc b/src/propeller/gp.cljc index 1f7e927..ed68be9 100755 --- a/src/propeller/gp.cljc +++ b/src/propeller/gp.cljc @@ -19,7 +19,7 @@ (println " Report for Generation" generation) (println "-------------------------------------------------------") (print "Best plushy: ") (prn (:plushy best)) - (print "Best program: ") (prn (genome/plushy->push (:plushy best))) + (print "Best program: ") (prn (genome/plushy->push (:plushy best) argmap)) (println "Best total error:" (:total-error best)) (println "Best errors:" (:errors best)) (println "Best behaviors:" (:behaviors best)) From 8cd1a79da31ccdafe294383c3b85f2795f02a4a4 Mon Sep 17 00:00:00 2001 From: Lee Spector Date: Wed, 9 Dec 2020 10:50:49 -0500 Subject: [PATCH 32/32] Pass argmap to report --- src/propeller/gp.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/propeller/gp.cljc b/src/propeller/gp.cljc index ed68be9..2a46aeb 100755 --- a/src/propeller/gp.cljc +++ b/src/propeller/gp.cljc @@ -13,7 +13,7 @@ (defn report "Reports information each generation." - [pop generation] + [pop generation argmap] (let [best (first pop)] (println "-------------------------------------------------------") (println " Report for Generation" generation) @@ -48,7 +48,7 @@ :cljs map) (partial error-function argmap) population)) best-individual (first evaluated-pop)] - (report evaluated-pop generation) + (report evaluated-pop generation argmap) (cond ;; Success on training cases is verified on testing cases (zero? (:total-error best-individual))