From 6b454451d7d8d415c40dd14dcc8a608cbc21794b Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Fri, 11 Feb 2022 17:27:33 -0600 Subject: [PATCH 1/6] Fix the `_from_string` instruction This replaces the dangerous use of `read-string`with `parseInt` and `parseFloat`, and returns `ignore-instruction` if an exception is thrown when trying to do the parsing. Hopefully this will fix the stack overflow problems with the PSB2 Middle Character and Twitter problems. --- src/propeller/push/instructions/numeric.cljc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/propeller/push/instructions/numeric.cljc b/src/propeller/push/instructions/numeric.cljc index b9cbf1f..455dad4 100755 --- a/src/propeller/push/instructions/numeric.cljc +++ b/src/propeller/push/instructions/numeric.cljc @@ -121,9 +121,13 @@ :name "_from_string"} (fn [stack state] (make-instruction state - #(try ((if (= stack :integer) int float) (read-string %)) - #?(:clj (catch Exception e) - :cljs (catch js/Error. e))) + #(try (if (= stack :integer) + #?(:clj (Integer/parseInt %) + :cljs (js/parseInt %)) + #?(:clj (Float/parseFloat %) + :cljs (js/parseFloat %))) + #?(:clj (catch Exception e :ignore-instruction) + :cljs (catch js/Error e :ignore-instruction))) [:string] stack))) From cb33af755a775770d67eb4645e287a7170458a58 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Fri, 25 Feb 2022 16:29:02 -0600 Subject: [PATCH 2/6] Remove unnecessary use of `read-string` in Middle Character For some reason `read-string` was being called to parse a string to a string in Middle Character, along with a totally unnecessary try/catch block. This cleans that up. It looks like most if not all of the PSB2 problems have this issue and we'll need to go through and update all of those to not use `read-string`. --- src/propeller/problems/PSB2/middle_character.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/propeller/problems/PSB2/middle_character.cljc b/src/propeller/problems/PSB2/middle_character.cljc index f9a4b18..ee2378b 100644 --- a/src/propeller/problems/PSB2/middle_character.cljc +++ b/src/propeller/problems/PSB2/middle_character.cljc @@ -48,9 +48,9 @@ :string)) inputs) parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) + (if (= output :no-stack-item) + 1000.0 + output)) outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) From 404e640cfb19c64f9dfee0aa73eb9a37fbf9f4a8 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Fri, 25 Feb 2022 16:49:45 -0600 Subject: [PATCH 3/6] Further improve Middle Character and Twitter Upon further inspection, Richard and I realized that we didn't need to have `parsed-outputs` at all in problems that just return a string (take the top value from the `:string` stack). So we removed all that from the Middle Character and Twitter problems, and removed the unnecessary calls to `str` in the `levenshtein-distance` calls. Co-authored-by: RichardLussier --- src/propeller/problems/PSB2/middle_character.cljc | 11 +++-------- src/propeller/problems/PSB2/twitter.cljc | 11 +++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/propeller/problems/PSB2/middle_character.cljc b/src/propeller/problems/PSB2/middle_character.cljc index ee2378b..a665d9b 100644 --- a/src/propeller/problems/PSB2/middle_character.cljc +++ b/src/propeller/problems/PSB2/middle_character.cljc @@ -47,19 +47,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (if (= output :no-stack-item) - 1000.0 - output)) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 - (metrics/levenshtein-distance (str correct-output) (str output)))) + (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) diff --git a/src/propeller/problems/PSB2/twitter.cljc b/src/propeller/problems/PSB2/twitter.cljc index 3d70851..95273e2 100644 --- a/src/propeller/problems/PSB2/twitter.cljc +++ b/src/propeller/problems/PSB2/twitter.cljc @@ -50,19 +50,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 - (metrics/levenshtein-distance (str correct-output) (str output)))) + (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) From c6f08d4ca30154cd8166aaa37810cf7257657d16 Mon Sep 17 00:00:00 2001 From: RichardLussier Date: Thu, 3 Mar 2022 14:44:09 -0600 Subject: [PATCH 4/6] Fix other read-string uses in PSB2 problems Since removing 'read-string' and 'str' from middle-character and twitter seemed to help diversify the outputs, we chose to replace all other occurrences of this pattern from the other PSB2 problems. Additionally, this conversion with 'read-string' and 'str' was completely unnecessary since the program was already taking from the top of the string stack. --- src/propeller/problems/PSB2/camel_case.cljc | 9 ++------- src/propeller/problems/PSB2/fizz_buzz.cljc | 11 +++-------- src/propeller/problems/PSB2/spin_words.cljc | 9 ++------- src/propeller/problems/PSB2/square_digits.cljc | 11 +++-------- src/propeller/problems/PSB2/substitution_cipher.cljc | 11 +++-------- 5 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/propeller/problems/PSB2/camel_case.cljc b/src/propeller/problems/PSB2/camel_case.cljc index 99b20be..ba80c6b 100644 --- a/src/propeller/problems/PSB2/camel_case.cljc +++ b/src/propeller/problems/PSB2/camel_case.cljc @@ -78,19 +78,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) diff --git a/src/propeller/problems/PSB2/fizz_buzz.cljc b/src/propeller/problems/PSB2/fizz_buzz.cljc index 4b892e7..ee1f536 100644 --- a/src/propeller/problems/PSB2/fizz_buzz.cljc +++ b/src/propeller/problems/PSB2/fizz_buzz.cljc @@ -46,19 +46,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 - (metrics/levenshtein-distance (str correct-output) (str output)))) + (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) diff --git a/src/propeller/problems/PSB2/spin_words.cljc b/src/propeller/problems/PSB2/spin_words.cljc index 5698f7a..b25386a 100644 --- a/src/propeller/problems/PSB2/spin_words.cljc +++ b/src/propeller/problems/PSB2/spin_words.cljc @@ -74,19 +74,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) diff --git a/src/propeller/problems/PSB2/square_digits.cljc b/src/propeller/problems/PSB2/square_digits.cljc index bc3bc61..76b1a24 100644 --- a/src/propeller/problems/PSB2/square_digits.cljc +++ b/src/propeller/problems/PSB2/square_digits.cljc @@ -46,19 +46,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 - (metrics/levenshtein-distance (str correct-output) (str output)))) + (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) diff --git a/src/propeller/problems/PSB2/substitution_cipher.cljc b/src/propeller/problems/PSB2/substitution_cipher.cljc index 3f83e64..f6b5d49 100644 --- a/src/propeller/problems/PSB2/substitution_cipher.cljc +++ b/src/propeller/problems/PSB2/substitution_cipher.cljc @@ -59,19 +59,14 @@ (:step-limit argmap)) :string)) inputs) - parsed-outputs (map (fn [output] - (try (read-string output) - #?(:clj (catch Exception e 1000.0) - :cljs (catch js/Error. e 1000.0)))) - outputs) errors (map (fn [correct-output output] (if (= output :no-stack-item) 10000 - (metrics/levenshtein-distance (str correct-output) (str output)))) + (metrics/levenshtein-distance correct-output output))) correct-outputs - parsed-outputs)] + outputs)] (assoc individual - :behaviors parsed-outputs + :behaviors outputs :errors errors :total-error #?(:clj (apply +' errors) :cljs (apply + errors))))) From a7b625d9422e197efd369e01fcf1fecb5e9a3b00 Mon Sep 17 00:00:00 2001 From: Ryan Boldi Date: Thu, 3 Mar 2022 22:56:10 -0500 Subject: [PATCH 5/6] removed simplification complexity from gp.cljc and added it to simplification.cljc. also updated tests to reflect this --- src/propeller/gp.cljc | 4 ++-- src/propeller/simplification.cljc | 12 ++++++------ test/propeller/utils_test.cljc | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/propeller/gp.cljc b/src/propeller/gp.cljc index 3c89f00..72ec5af 100644 --- a/src/propeller/gp.cljc +++ b/src/propeller/gp.cljc @@ -60,8 +60,8 @@ (do (prn {:success-generation generation}) (prn {:total-test-error (:total-error (error-function argmap (:testing-data argmap) best-individual))}) - (if (:simplification? argmap) - (let [simplified-plushy (simplification/auto-simplify-plushy argmap (:plushy best-individual) (:simplification-steps argmap) error-function (:training-data argmap) (:simplification-k argmap) (:simplification-verbose? argmap))] + (when (:simplification? argmap) + (let [simplified-plushy (simplification/auto-simplify-plushy (:plushy best-individual) error-function argmap)] (prn {:total-test-error-simplified (:total-error (error-function argmap (:testing-data argmap) (hash-map :plushy simplified-plushy)))})))) ;; (>= generation max-generations) diff --git a/src/propeller/simplification.cljc b/src/propeller/simplification.cljc index fbd47b1..f896e38 100644 --- a/src/propeller/simplification.cljc +++ b/src/propeller/simplification.cljc @@ -13,7 +13,7 @@ "deletes the values at given set of indices" [indices plushy] (let [sorted-indices (sort > indices)] - (keep-indexed #(if (not (some #{%1} sorted-indices)) %2) plushy))) + (keep-indexed #(when (not (some #{%1} sorted-indices)) %2) plushy))) (defn delete-k-random [k plushy] @@ -21,13 +21,13 @@ (defn auto-simplify-plushy "naive auto-simplification" - [argmap plushy steps error-function training-data k verbose?] - (if verbose? (prn {:start-plushy-length (count plushy) :k k})) + [plushy error-function {:keys [simplification-steps training-data simplification-k simplification-verbose?] :as argmap}] + (when simplification-verbose? (prn {:start-plushy-length (count plushy) :k simplification-k})) (let [initial-errors (:errors (error-function argmap training-data {:plushy plushy}))] (loop [step 0 curr-plushy plushy] - (if (< steps step) - (do (if verbose? (prn {:final-plushy-length (count curr-plushy) :final-plushy curr-plushy})) curr-plushy) - (let [new-plushy (delete-k-random (rand-int k) curr-plushy) + (if (< simplification-steps step) + (do (when simplification-verbose? (prn {:final-plushy-length (count curr-plushy) :final-plushy curr-plushy})) curr-plushy) + (let [new-plushy (delete-k-random (rand-int simplification-k) curr-plushy) new-plushy-errors (:errors (error-function argmap training-data {:plushy new-plushy})) new-equal? (= new-plushy-errors initial-errors)] (recur (inc step) diff --git a/test/propeller/utils_test.cljc b/test/propeller/utils_test.cljc index e450a38..f8b4567 100644 --- a/test/propeller/utils_test.cljc +++ b/test/propeller/utils_test.cljc @@ -71,8 +71,8 @@ (t/testing "auto-simplify-plushy" (t/testing "should handle having an empty plushy" - (t/is (= (s/auto-simplify-plushy {} '() 100 (fn [argmap data plushy] 0) {} 3 false) '()))) + (t/is (= (s/auto-simplify-plushy '() (fn [argmap data plushy] 0) {:simplification-steps 100 :simplification-k 4 :simplification-verbose? false}) '()))) (let [plushy '(:exec_dup 1 :integer_add close :in1 :integer_add 0 :in1 :in1 :integer_mult :integer_add)] (t/testing "should decrease size of plushy that always has perfect scores" - (t/is (< (count (s/auto-simplify-plushy {} plushy 5 (fn [argmap data plushy] 0) {} 3 false)) (count plushy))) - (t/is (< (count (s/auto-simplify-plushy {} plushy 1 (fn [argmap data plushy] 0) {} 10 false)) (count plushy)))))) \ No newline at end of file + (t/is (< (count (s/auto-simplify-plushy plushy (fn [argmap data plushy] 0) {:simplification-steps 100 :simplification-k 4 :simplification-verbose? false})) (count plushy))) + (t/is (< (count (s/auto-simplify-plushy plushy (fn [argmap data plushy] 0) {:simplification-steps 100 :simplification-k 10 :simplification-verbose? false})) (count plushy)))))) \ No newline at end of file From 64a81cb4502d91123ba41084b5d8dc4f7ccb567b Mon Sep 17 00:00:00 2001 From: Ryan Boldi Date: Thu, 3 Mar 2022 21:29:42 -0500 Subject: [PATCH 6/6] update documentation to include simplification --- doc/intro.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/intro.md b/doc/intro.md index 2a2a4ec..0e67dcf 100644 --- a/doc/intro.md +++ b/doc/intro.md @@ -1,3 +1,32 @@ -# Introduction to propeller +# Introduction to Propeller TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) + + + + +# Simplification + +To use Propeller's auto-simplification system, simply include the following four command line arguments when running a problem: + +```clojure +:simplification? true +``` +Toggle auto-simplification +```clojure +:simplification-k 4 +``` +This is the upper bound for elements deleted from the plushy every step. Every step, a number in $[1, k]$ of elements is deleted from the plushy representation of the solution. +```clojure +:simplification-steps 1000 +``` +Number of simplification steps to perform +```clojure +:simplification-verbose? true +``` +whether or not to output simplification info into the output of the evolutionary run. +The output with verbose adds the following lines to the output: +```clojure +{:start-plushy-length 42, :k 4} +{:final-plushy-length 13, :final-plushy (:in1 :in1 :integer_quot :in1 :in1 :exec_dup :in1 :integer_mult close :exec_dup :integer_add 1 :integer_add)} +``` \ No newline at end of file