diff --git a/src/propeller/core.clj b/src/propeller/core.clj index 342c8a7..c25aa43 100644 --- a/src/propeller/core.clj +++ b/src/propeller/core.clj @@ -1,9 +1,9 @@ (ns propeller.core (:gen-class) - (:require [propeller.push.instructions boolean char code input-output numeric random string]) - (:use propeller.gp - propeller.push.instructions - [propeller.problems simple-regression string-classification])) + (:require [propeller.gp :refer :all] + [propeller.push.core :refer :all] + (propeller.problems [simple-regression :refer :all] + [string-classification :refer :all]))) (defn -main "Runs propel-gp, giving it a map of arguments." diff --git a/src/propeller/genome.clj b/src/propeller/genome.clj index ae778e1..2d8c971 100644 --- a/src/propeller/genome.clj +++ b/src/propeller/genome.clj @@ -1,5 +1,5 @@ (ns propeller.genome - (:use propeller.push.instructions)) + (:require [propeller.push.core :refer :all])) (defn plushy->push "Returns the Push program expressed by the given plushy representation." diff --git a/src/propeller/gp.clj b/src/propeller/gp.clj index 978b3ff..02673fc 100644 --- a/src/propeller/gp.clj +++ b/src/propeller/gp.clj @@ -1,5 +1,7 @@ (ns propeller.gp - (:use [propeller genome variation])) + (:require [propeller.push.core :refer [instruction-table]] + (propeller [genome :refer :all] + [variation :refer :all]))) (defn report "Reports information each generation." @@ -24,7 +26,15 @@ [{:keys [population-size max-generations error-function instructions max-initial-plushy-size] :as argmap}] - (println "Starting GP with args:" argmap) + ;; + (println "Starting GP with args: " argmap) + ;; + (do (print "Registering instructions... ") + (require '[propeller.push.instructions boolean char code input-output + numeric polymorphic string]) + (println "Done. Registered instructions:") + (println (sort (keys @instruction-table)))) + ;; (loop [generation 0 population (repeatedly population-size diff --git a/src/propeller/problems/string_classification.clj b/src/propeller/problems/string_classification.clj index 6929ec0..3434cfa 100644 --- a/src/propeller/problems/string_classification.clj +++ b/src/propeller/problems/string_classification.clj @@ -1,6 +1,7 @@ (ns propeller.problems.string-classification - (:use propeller.genome - [propeller.push state interpreter])) + (:require [propeller.genome :refer :all] + (propeller.push [state :refer :all] + [interpreter :refer :all]))) ;; ============================================================================= ;; String classification diff --git a/src/propeller/push/instructions.clj b/src/propeller/push/core.clj similarity index 97% rename from src/propeller/push/instructions.clj rename to src/propeller/push/core.clj index 942d4e8..aad92c2 100644 --- a/src/propeller/push/instructions.clj +++ b/src/propeller/push/core.clj @@ -1,4 +1,4 @@ -(ns propeller.push.instructions +(ns propeller.push.core (:require [propeller.push.state :refer [get-args-from-stacks push-to-stack]])) diff --git a/src/propeller/push/instructions/boolean.clj b/src/propeller/push/instructions/boolean.clj index 46eb44d..cec31ad 100644 --- a/src/propeller/push/instructions/boolean.clj +++ b/src/propeller/push/instructions/boolean.clj @@ -1,12 +1,10 @@ (ns propeller.push.instructions.boolean - (:require [propeller.push.instructions :refer [def-instruction]] + (:require [propeller.push.core :refer [def-instruction]] [propeller.push.utils :refer [make-instruction]])) -;; Pushes TRUE if the top two BOOLEANs are equal, and FALSE otherwise -(def-instruction - :boolean_= - (fn [state] - (make-instruction state = [:boolean :boolean] :boolean))) +;; ============================================================================= +;; BOOLEAN Instructions +;; ============================================================================= ;; Pushes the logical AND of the top two BOOLEANs (def-instruction diff --git a/src/propeller/push/instructions/char.clj b/src/propeller/push/instructions/char.clj index ddfaa35..ece1d80 100644 --- a/src/propeller/push/instructions/char.clj +++ b/src/propeller/push/instructions/char.clj @@ -1,8 +1,12 @@ (ns propeller.push.instructions.char - (:require [propeller.push.instructions :refer [def-instruction]] + (:require [propeller.push.core :refer [def-instruction]] [propeller.push.utils :refer [make-instruction]] [tools.character :as char])) +;; ============================================================================= +;; CHAR Instructions +;; ============================================================================= + ;; Pushes TRUE onto the BOOLEAN stack if the popped character is a letter (def-instruction :char_isletter @@ -22,13 +26,14 @@ (fn [state] (make-instruction state char/is-whitespace [:char] :boolean))) -;; Pops the STRING stack and pushes the top element's constituent characters -;; onto the CHAR stack, in order. For instance, "hello" will result in the -;; top of the CHAR stack being o l l e h +;; Pops the FLOAT stack, converts the top item to a whole number, and pushes +;; its corresponding ASCII value onto the CHAR stack. Whole numbers larger than +;; 128 will be reduced modulo 128. For instance, 248.45 will result in x being +;; pushed. (def-instruction - :char_allfromstring + :char_fromfloat (fn [state] - (make-instruction state #(map char %) [:string] :char))) + (make-instruction state #(char (mod (long %) 128)) [:float] :char))) ;; Pops the INTEGER stack and pushes the top element's corresponding ASCII ;; value onto the CHAR stack. Integers larger than 128 will be reduced modulo @@ -38,11 +43,10 @@ (fn [state] (make-instruction state #(char (mod % 128)) [:integer] :char))) -;; Pops the FLOAT stack, converts the top item to a whole number, and pushes -;; its corresponding ASCII value onto the CHAR stack. Whole numbers larger than -;; 128 will be reduced modulo 128. For instance, 248.45 will result in x being -;; pushed. +;; Pops the STRING stack and pushes the top element's constituent characters +;; onto the CHAR stack, in order. For instance, "hello" will result in the +;; top of the CHAR stack being o l l e h (def-instruction - :char_fromfloat + :char_allfromstring (fn [state] - (make-instruction state #(char (mod (long %) 128)) [:float] :char))) + (make-instruction state #(map char %) [:string] :char))) diff --git a/src/propeller/push/instructions/code.clj b/src/propeller/push/instructions/code.clj index 1c6aeb8..86b4f32 100644 --- a/src/propeller/push/instructions/code.clj +++ b/src/propeller/push/instructions/code.clj @@ -1,8 +1,12 @@ (ns propeller.push.instructions.code - (:require [propeller.push.instructions :refer [def-instruction]] + (:require [propeller.push.core :refer [def-instruction]] [propeller.push.state :as state] [propeller.push.utils :refer [make-instruction]])) +;; ============================================================================= +;; CODE and EXEC Instructions +;; ============================================================================= + (def-instruction :exec_dup (fn [state] diff --git a/src/propeller/push/instructions/input_output.clj b/src/propeller/push/instructions/input_output.clj index 2271a0d..5544f07 100644 --- a/src/propeller/push/instructions/input_output.clj +++ b/src/propeller/push/instructions/input_output.clj @@ -1,6 +1,10 @@ (ns propeller.push.instructions.input-output (:require [propeller.push.state :as state] - [propeller.push.instructions :refer [def-instruction]])) + [propeller.push.core :refer [def-instruction]])) + +;; ============================================================================= +;; INPUT and OUTPUT Instructions +;; ============================================================================= ;; Pushes the input labeled :in1 on the inputs map onto the :exec stack (def-instruction diff --git a/src/propeller/push/instructions/numeric.clj b/src/propeller/push/instructions/numeric.clj index d534569..cd7db7c 100644 --- a/src/propeller/push/instructions/numeric.clj +++ b/src/propeller/push/instructions/numeric.clj @@ -1,31 +1,37 @@ (ns propeller.push.instructions.numeric - (:require [propeller.push.instructions :refer [def-instruction]] + (:require [propeller.push.core :refer [def-instruction]] [propeller.push.utils :refer [generate-functions make-instruction]] [tools.math :as math])) ;; ============================================================================= -;; FLOAT and INTEGER (polymorphic) +;; FLOAT and INTEGER Instructions (polymorphic) ;; ============================================================================= -;; Pushes TRUE onto the BOOLEAN stack if the top two items are equal, and -;; FALSE otherwise -(defn- _= - [stack state] - (make-instruction state = [stack stack] :boolean)) - ;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than the top ;; item, and FALSE otherwise (defn- _> [stack state] (make-instruction state > [stack stack] :boolean)) +;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than or +;; equal to the top item, and FALSE otherwise +(defn- _>= + [stack state] + (make-instruction state >= [stack stack] :boolean)) + ;; Pushes TRUE onto the BOOLEAN stack if the second item is less than the top ;; item, and FALSE otherwise (defn- _< [stack state] (make-instruction state < [stack stack] :boolean)) +;; Pushes TRUE onto the BOOLEAN stack if the second item is less than or equal +;; to the top item, and FALSE otherwise +(defn- _<= + [stack state] + (make-instruction state <= [stack stack] :boolean)) + ;; Pushes the sum of the top two items onto the same stack (defn- _+ [stack state] @@ -48,7 +54,7 @@ [stack state] (make-instruction state #(if (zero? %2) - (list %1 %2) ; push both items back + (list %1 %2) ; push both items back (NOOP) (quot %1 %2)) [stack stack] stack)) @@ -61,7 +67,7 @@ [stack state] (make-instruction state #(if (zero? %2) - (list %1 %2) ; push both items back + (list %1 %2) ; push both items back (NOOP) (mod %1 %2)) [stack stack] stack)) @@ -80,21 +86,43 @@ (defn- _fromboolean [stack state] (make-instruction state - #((if (= stack :float) float int) (if % 1 0)) + #((if (= stack :integer) int float) (if % 1 0)) [:boolean] stack)) -;; Automate type-specific function generation. All resulting functions take a -;; Push state as their only argument. For FLOAT and INTEGER, create one of each -;; of the 11 following type-specific functions: =, >, <, +, -, *, QUOT, %, MAX, -;; MIN, and FROMBOOLEAN. (22 functions total, with syntax e.g. integer_=, -;; float_min etc.) +;; Pushes the ASCII value of the top CHAR +(defn- _fromchar + [stack state] + (make-instruction state (if (= stack :integer) int float) [:char] stack)) + +;; Pushes the value of the top STRING, if it can be parsed as a number. +;; Otherwise, acts as a NOOP +(defn- _fromstring + [stack state] + (make-instruction state + #(try ((if (= stack :integer) int float) (read-string %)) + (catch Exception e)) + [:string] + stack)) + +;; Pushes the increment (i.e. +1) of the top item of the stack +(defn- _inc + [stack state] + (make-instruction state inc [stack] stack)) + +;; Pushes the decrement (i.e. -1) of the top item of the stack +(defn- _dec + [stack state] + (make-instruction state dec [stack] stack)) + +;; 2 types x 16 functions = 32 instructions (generate-functions [:float :integer] - [_=, _>, _<, _+, _-, _*, _quot, _%, _max, _min, _fromboolean]) + [_> _>= _< _<= _+ _- _* _quot _% _max _min _inc _dec + _fromboolean _fromchar _fromstring]) ;; ============================================================================= -;; FLOAT only +;; FLOAT Instructions only ;; ============================================================================= ;; Pushes the cosine of the top FLOAT @@ -115,20 +143,14 @@ (fn [state] (make-instruction state math/tan [:float] :float))) -;; Pushes the tangent of the top FLOAT -(def-instruction - :float_tan - (fn [state] - (make-instruction state math/tan [:float] :float))) - -;; Pushes a floating point version of the top INTEGER +;; Pushes the floating point version of the top INTEGER (def-instruction :float_frominteger (fn [state] - (make-instruction state math/tan [:float] :float))) + (make-instruction state float [:integer] :float))) ;; ============================================================================= -;; INTEGER only +;; INTEGER Instructions only ;; ============================================================================= ;; Pushes the result of truncating the top FLOAT towards negative infinity diff --git a/src/propeller/push/instructions/polymorphic.clj b/src/propeller/push/instructions/polymorphic.clj new file mode 100644 index 0000000..999a315 --- /dev/null +++ b/src/propeller/push/instructions/polymorphic.clj @@ -0,0 +1,110 @@ +(ns propeller.push.instructions.polymorphic + (:require [propeller.push.core :refer [def-instruction]] + [propeller.push.state :refer [empty-stack? + peek-stack + pop-stack + push-to-stack]] + [propeller.push.utils :refer [generate-functions + make-instruction]])) + +;; ============================================================================= +;; Polymorphic Instructions +;; +;; (for all types, with the exception of non-data stacks like auxiliary, tag, +;; input, and output) +;; ============================================================================= + +;; Pushes TRUE onto the BOOLEAN stack if the top two items are equal. +;; Otherwise FALSE +(defn- _= + [stack state] + (make-instruction state = [stack stack] :boolean)) + +;; Duplicates the top item of the stack. Does not pop its argument (since that +;; would negate the effect of the duplication) +(defn- _dup + [stack state] + (let [top-item (peek-stack state stack)] + (if (empty-stack? state stack) + state + (push-to-stack state stack top-item)))) + +;; 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. +(defn- _duptimes + [stack state] + (if (or (and (= stack :integer) + (>= (count (:integer state)) 2)) + (and (not= stack :integer) + (not (empty-stack? state :integer)) + (not (empty-stack? state stack)))) + (let [n (peek-stack state :integer) + item-to-duplicate (peek-stack state stack)] + nil) + state)) + + +(defn- _dupitems + [stack state] + ()) + +;; Pushes TRUE onto the BOOLEAN stack if the stack is empty. Otherwise FALSE +(defn- _empty + [stack state] + (push-to-stack state :boolean (empty-stack? state stack))) + +;; Empties the given stack +(defn- _flush + [stack state] + ()) + +;; Pops the given stack +(defn- _pop + [stack state] + (pop-stack state stack)) + +;; Rotates the top three items on the stack (i.e. pulls the third item out and +;; pushes it on top). Equivalent to (yank state stack-type 2) +(defn- _rot + [stack state] + ()) + +;; Inserts the top item deeper into the stack, using the top INTEGER to +;; determine how deep +(defn- _shove + [stack state] + ()) + +;; Pushes the given stack's depth onto the INTEGER stack +(defn- _stackdepth + [stack state] + ()) + +;; Swaps the top two items on the stack +(defn- _swap + [stack state] + ()) + +;; Removes an indexed item from deep in the stack. The top INTEGER is used to +;; determine how deep to yank from +(defn- _yank + [stack state] + ()) + +;; Pushes a copy of an indexed item deep in the stack, without removing it. +;; The top INTEGER is used to determine how deep to yankdup from +(defn- _yankdup + [stack state] + ()) + +;; 5 types x 1 function = 5 instructions +(generate-functions [:boolean :char :float :integer :string] [_=]) + +;; 8 types x 12 function = 96 instructions +(generate-functions + [:boolean :char :code :exec :float :integer :string :zip] + [_dup _duptimes _dupitems _empty _flush _pop _rot _shove _stackdepth + _swap _yank _yankdup]) diff --git a/src/propeller/push/instructions/random.clj b/src/propeller/push/instructions/random.clj deleted file mode 100644 index b30a21a..0000000 --- a/src/propeller/push/instructions/random.clj +++ /dev/null @@ -1,17 +0,0 @@ -(ns propeller.push.instructions.random - (:require [propeller.push.instructions :refer [def-instruction]])) - -;;; Pushes a random BOOLEAN -;(def-instruction -; :boolean_rand -; (fn [state] -; (make-instruction state #(rand-nth [true false]) [] :boolean))) -; -;(defn- _rand -; "For an INTEGER stack type, pushes a newly generated random INTEGER that is -; greater than or equal to MIN-RANDOM-INTEGER and less than or equal to -; MAX-RANDOM-INTEGER. Analogous for a FLOAT stack type, with its corresponding -; MAX-RANDOM-FLOAT and MIN-RANDOM-FLOAT." -; [state stack-type] -; (let [data-type (keyword stack-type)] -; ())) diff --git a/src/propeller/push/instructions/string.clj b/src/propeller/push/instructions/string.clj index 1b5fba5..789a2f6 100644 --- a/src/propeller/push/instructions/string.clj +++ b/src/propeller/push/instructions/string.clj @@ -1,7 +1,11 @@ (ns propeller.push.instructions.string - (:require [propeller.push.instructions :refer [def-instruction]] + (:require [propeller.push.core :refer [def-instruction]] [propeller.push.utils :refer [make-instruction]])) +;; ============================================================================= +;; STRING Instructions +;; ============================================================================= + (def-instruction :string_= (fn [state] diff --git a/src/propeller/push/interpreter.clj b/src/propeller/push/interpreter.clj index 7a6e616..82e223a 100644 --- a/src/propeller/push/interpreter.clj +++ b/src/propeller/push/interpreter.clj @@ -1,5 +1,5 @@ (ns propeller.push.interpreter - (:require [propeller.push.instructions :refer [instruction-table]]) + (:require [propeller.push.core :refer [instruction-table]]) (:require [propeller.push.state :refer :all])) (defn interpret-one-step diff --git a/src/propeller/push/state.clj b/src/propeller/push/state.clj index 49bf304..03fb788 100644 --- a/src/propeller/push/state.clj +++ b/src/propeller/push/state.clj @@ -58,7 +58,9 @@ (defn push-to-stack "Pushes item(s) onto stack." [state stack items] - (update state stack (if (seq? items) into conj) items)) + (let [items-list (if (coll? items) items (list items)) + items-list-no-nil (filter #(not (nil? %)) items-list)] ; do not push nil items + (update state stack into items-list-no-nil))) (defn get-args-from-stacks "Takes a state and a collection of stacks to take args from. If there are diff --git a/src/propeller/push/utils.clj b/src/propeller/push/utils.clj index 3ba9a60..793109c 100644 --- a/src/propeller/push/utils.clj +++ b/src/propeller/push/utils.clj @@ -1,6 +1,6 @@ (ns propeller.push.utils - (:require [propeller.push.instructions :refer [def-instruction]] - [propeller.push.state :as push-state])) + (:require [propeller.push.core :refer [def-instruction]] + [propeller.push.state :refer :all])) ;; A utility function for making Push instructions. Takes a state, a function ;; to apply to the args, the stacks to take the args from, and the stack to @@ -8,12 +8,12 @@ ;; given stacks), and pushes the result onto the return-stack (defn make-instruction [state function arg-stacks return-stack] - (let [popped-args (push-state/get-args-from-stacks state arg-stacks)] + (let [popped-args (get-args-from-stacks state arg-stacks)] (if (= popped-args :not-enough-args) state (let [result (apply function (:args popped-args)) new-state (:state popped-args)] - (push-state/push-to-stack new-state return-stack result))))) + (push-to-stack new-state return-stack result))))) ;; Given a sequence of stacks, e.g. [:float :integer], and a sequence of suffix ;; function strings, e.g. [_+, _*, _=], automates the generation of all possible @@ -28,7 +28,7 @@ ;; Pretty-prints a Push state, for logging or debugging purposes (defn print-state [state] - (doseq [stack push-state/stacks] + (doseq [stack stacks] (printf "%-15s = " stack) (prn (if (get state stack) (get state stack) '())) (flush))) diff --git a/src/propeller/session.clj b/src/propeller/session.clj index e827cc8..7fca02f 100644 --- a/src/propeller/session.clj +++ b/src/propeller/session.clj @@ -1,7 +1,13 @@ (ns propeller.session - (:use [propeller core gp variation selection genome] - [propeller.push interpreter instructions state] - [propeller.problems simple-regression string-classification])) + (:require (propeller [gp :refer :all] + [variation :refer :all] + [selection :refer :all] + [genome :refer :all]) + (propeller.push [interpreter :refer :all] + [core :refer :all] + [state :refer :all]) + (propeller.problems [simple-regression :refer :all] + [string-classification :refer :all]))) #_(interpret-program '(1 2 integer_+) empty-push-state 1000)