Refactor instructions

This commit is contained in:
mcgirjau 2020-06-24 16:16:53 -04:00
parent af033c1b0e
commit bb5f74823c
11 changed files with 285 additions and 293 deletions

View File

@ -1,5 +1,6 @@
(ns propeller.core (ns propeller.core
(:gen-class) (:gen-class)
(:require [propeller.push.instructions boolean char code input-output numeric random string])
(:use propeller.gp (:use propeller.gp
propeller.push.instructions propeller.push.instructions
[propeller.problems simple-regression string-classification])) [propeller.problems simple-regression string-classification]))
@ -7,19 +8,18 @@
(defn -main (defn -main
"Runs propel-gp, giving it a map of arguments." "Runs propel-gp, giving it a map of arguments."
[& args] [& args]
(binding [*ns* (the-ns 'propeller.core)] (gp (update-in (merge {:instructions default-instructions
(gp (update-in (merge {:instructions default-instructions :error-function regression-error-function
:error-function regression-error-function :max-generations 500
:max-generations 500 :population-size 500
:population-size 500 :max-initial-plushy-size 50
:max-initial-plushy-size 50 :step-limit 100
:step-limit 100 :parent-selection :lexicase
:parent-selection :lexicase :tournament-size 5
:tournament-size 5 :umad-rate 0.1
:umad-rate 0.1 :variation {:umad 0.5 :crossover 0.5}
:variation {:umad 0.5 :crossover 0.5} :elitism false}
:elitism false} (apply hash-map
(apply hash-map (map read-string args)))
(map read-string args))) [:error-function]
[:error-function] #(if (fn? %) % (eval %)))))
#(if (fn? %) % (eval %))))))

View File

@ -1,16 +1,61 @@
(ns propeller.push.instructions (ns propeller.push.instructions
(:use propeller.push.state) (:require [propeller.push.state :refer [get-args-from-stacks
(:require [tools.character :as char])) push-to-stack]]))
;; ============================================================================= ;; =============================================================================
;; PushGP Instructions ;; PushGP Instructions
;; ;;
;; Instructions are represented as keywords, and stored in an atom.
;;
;; Instructions must all be either functions that take one Push state and ;; Instructions must all be either functions that take one Push state and
;; return another, or constant literals. ;; return another, or constant literals.
;; ;;
;; TMH: ERCs? ;; TMH: ERCs?
;; ============================================================================= ;; =============================================================================
;; Set of original propel instructions
(def default-instructions
(list :in1
:integer_+
:integer_-
:integer_*
:integer_%
:integer_=
:exec_dup
:exec_if
:boolean_and
:boolean_or
:boolean_not
:boolean_=
:string_=
:string_take
:string_drop
:string_reverse
:string_concat
:string_length
:string_includes?
'close
0
1
true
false
""
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"A"
"C"
"G"
"T"))
(def instruction-table (atom (hash-map)))
(defmacro def-instruction
[instruction definition]
`(swap! instruction-table assoc '~instruction ~definition))
;; Number of blocks opened by instructions (default = 0)
(def opens {:exec_dup 1
:exec_if 2})
(defn make-instruction (defn make-instruction
"A utility function for making Push instructions. Takes a state, a function "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 to apply to the args, the stacks to take the args from, and the stack to
@ -23,272 +68,3 @@
(let [result (apply function (:args popped-args)) (let [result (apply function (:args popped-args))
new-state (:state popped-args)] new-state (:state popped-args)]
(push-to-stack new-state return-stack result))))) (push-to-stack new-state return-stack result)))))
;; Original propel instructions
(def default-instructions
(list
'in1
'integer_+
'integer_-
'integer_*
'integer_%
'integer_=
'exec_dup
'exec_if
'boolean_and
'boolean_or
'boolean_not
'boolean_=
'string_=
'string_take
'string_drop
'string_reverse
'string_concat
'string_length
'string_includes?
'close
0
1
true
false
""
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"A"
"C"
"G"
"T"))
;; Number of blocks opened by instructions (default = 0)
(def opens
{'exec_dup 1
'exec_if 2})
;; =============================================================================
;; BOOLEAN
;; =============================================================================
(defn boolean_=
"Pushes TRUE if the top two BOOLEANs are equal, and FALSE otherwise."
[state]
(make-instruction state = [:boolean :boolean] :boolean))
(defn boolean_and
[state]
"Pushes the logical AND of the top two BOOLEANs."
(make-instruction state #(and %1 %2) [:boolean :boolean] :boolean))
(defn boolean_or
[state]
"Pushes the logical OR of the top two BOOLEANs."
(make-instruction state #(or %1 %2) [:boolean :boolean] :boolean))
(defn boolean_not
[state]
"Pushes the logical NOT of the top BOOLEAN."
(make-instruction state not [:boolean] :boolean))
(defn boolean_xor
[state]
"Pushes the logical XOR of the top two BOOLEANs."
(make-instruction state #(or (and %1 (not %2))
(and (not %1) %2))
[:boolean :boolean]
:boolean))
(defn boolean_invert_first_then_and
[state]
"Pushes the logical AND of the top two BOOLEANs, after applying NOT to the
first one."
(make-instruction state #(and %1 (not %2)) [:boolean :boolean] :boolean))
(defn boolean_invert_second_then_and
[state]
"Pushes the logical AND of the top two BOOLEANs, after applying NOT to the
second one."
(make-instruction state #(and (not %1) %2) [:boolean :boolean] :boolean))
(defn boolean_fromfloat
[state]
"Pushes FALSE if the top FLOAT is 0.0, and TRUE otherwise."
(make-instruction state #(not (zero? %)) [:float] :boolean))
(defn boolean_frominteger
[state]
"Pushes FALSE if the top INTEGER is 0, and TRUE otherwise."
(make-instruction state #(not (zero? %)) [:integer] :boolean))
;; =============================================================================
;; CHAR
;; =============================================================================
(defn char_isletter
"Pushes TRUE onto the BOOLEAN stack if the popped character is a letter."
[state]
(make-instruction state char/is-letter [:char] :boolean))
(defn char_isdigit
"Pushes TRUE onto the BOOLEAN stack if the popped character is a digit."
[state]
(make-instruction state char/is-digit [:char] :boolean))
(defn char_iswhitespace
"Pushes TRUE onto the BOOLEAN stack if the popped character is whitespace
(newline, space, or tab)."
[state]
(make-instruction state char/is-whitespace [:char] :boolean))
(defn char_allfromstring
"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."
[state]
(make-instruction state #(map char %) [:string] :char))
(defn char_frominteger
"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
128. For instance, 248 will result in x being pushed."
[state]
(make-instruction state #(char (mod % 128)) [:integer] :char))
(defn char_fromfloat
"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."
[state]
(make-instruction state #(char (mod (long %) 128)) [:float] :char))
;; =============================================================================
;; CODE
;; =============================================================================
;; ...to be added
;; =============================================================================
;; EXEC
;; =============================================================================
(defn exec_dup
[state]
(if (empty-stack? state :exec)
state
(push-to-stack state :exec (first (:exec state)))))
(defn exec_if
[state]
(make-instruction state #(if %1 %3 %2) [:boolean :exec :exec] :exec))
;; =============================================================================
;; ENVIRONMENT
;; =============================================================================
;; ...to be added
;; =============================================================================
;; GENETIC TURING MACHINE
;; =============================================================================
;; ...to be added
;; =============================================================================
;; GENOME
;; =============================================================================
;; ...to be added
;; =============================================================================
;; INPUT-OUTPUT
;; =============================================================================
(defn in1
"Pushes the input labeled :in1 on the inputs map onto the :exec stack."
[state]
(push-to-stack state :exec (:in1 (:input state))))
;; =============================================================================
;; INTEGER AND FLOAT
;; =============================================================================
(defn integer_=
[state]
(make-instruction state = [:integer :integer] :boolean))
(defn integer_+
[state]
(make-instruction state +' [:integer :integer] :integer))
(defn integer_-
[state]
(make-instruction state -' [:integer :integer] :integer))
(defn integer_*
[state]
(make-instruction state *' [:integer :integer] :integer))
(defn integer_%
[state]
(make-instruction state
(fn [int1 int2]
(if (zero? int2) int1 (quot int1 int2)))
[:integer :integer]
:integer))
;; =============================================================================
;; RANDOM
;; =============================================================================
(defn boolean_rand
[state]
"Pushes a random BOOLEAN."
(make-instruction state #(rand-nth [true false]) [] :boolean))
;; =============================================================================
;; STRING
;; =============================================================================
(defn string_=
[state]
(make-instruction state = [:string :string] :boolean))
(defn string_concat
[state]
(make-instruction state #(apply str (concat %1 %2)) [:string :string] :string))
(defn string_drop
[state]
(make-instruction state #(apply str (drop %1 %2)) [:integer :string] :string))
(defn string_includes?
[state]
(make-instruction state clojure.string/includes? [:string :string] :boolean))
(defn string_length
[state]
(make-instruction state count [:string] :integer))
(defn string_reverse
[state]
(make-instruction state #(apply str (reverse %)) [:string] :string))
(defn string_take
[state]
(make-instruction state #(apply str (take %1 %2)) [:integer :string] :string))
;; =============================================================================
;; TAG
;; =============================================================================
;; ...to be added
;; =============================================================================
;; VECTOR
;; =============================================================================
;; ...to be added
;; =============================================================================
;; ZIP
;; =============================================================================
;; ...to be added

View File

@ -0,0 +1,62 @@
(ns propeller.push.instructions.boolean
(:require [propeller.push.instructions :refer [make-instruction
def-instruction]]))
;; Pushes TRUE if the top two BOOLEANs are equal, and FALSE otherwise
(def-instruction
:boolean_=
(fn [state]
(make-instruction state = [:boolean :boolean] :boolean)))
;; Pushes the logical AND of the top two BOOLEANs
(def-instruction
:boolean_and
(fn [state]
(make-instruction state #(and %1 %2) [:boolean :boolean] :boolean)))
;; Pushes the logical OR of the top two BOOLEANs
(def-instruction
:boolean_or
(fn [state]
(make-instruction state #(or %1 %2) [:boolean :boolean] :boolean)))
;; Pushes the logical NOT of the top BOOLEAN
(def-instruction
:boolean_not
(fn [state]
(make-instruction state not [:boolean] :boolean)))
;; Pushes the logical XOR of the top two BOOLEAN
(def-instruction
:boolean_xor
(fn [state]
(make-instruction state #(or (and %1 (not %2))
(and (not %1) %2))
[:boolean :boolean]
:boolean)))
;; Pushes the logical AND of the top two BOOLEANs, after applying NOT to the
;; first one
(def-instruction
:boolean_invert_first_then_and
(fn [state]
(make-instruction state #(and %1 (not %2)) [:boolean :boolean] :boolean)))
;; Pushes the logical AND of the top two BOOLEANs, after applying NOT to the
;; second one
(def-instruction
:boolean_invert_second_then_and
(fn [state]
(make-instruction state #(and (not %1) %2) [:boolean :boolean] :boolean)))
;; Pushes FALSE if the top FLOAT is 0.0, and TRUE otherwise
(def-instruction
:boolean_fromfloat
(fn [state]
(make-instruction state #(not (zero? %)) [:float] :boolean)))
;; Pushes FALSE if the top INTEGER is 0, and TRUE otherwise
(def-instruction
:boolean_frominteger
(fn [state]
(make-instruction state #(not (zero? %)) [:integer] :boolean)))

View File

@ -0,0 +1,48 @@
(ns propeller.push.instructions.char
(:require [propeller.push.instructions :refer [make-instruction
def-instruction]]
[tools.character :as char]))
;; Pushes TRUE onto the BOOLEAN stack if the popped character is a letter
(def-instruction
:char_isletter
(fn [state]
(make-instruction state char/is-letter [:char] :boolean)))
;; Pushes TRUE onto the BOOLEAN stack if the popped character is a digit
(def-instruction
:char_isdigit
(fn [state]
(make-instruction state char/is-digit [:char] :boolean)))
;; Pushes TRUE onto the BOOLEAN stack if the popped character is whitespace
;; (newline, space, or tab)
(def-instruction
:char_iswhitespace
(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
(def-instruction
:char_allfromstring
(fn [state]
(make-instruction state #(map char %) [:string] :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
;; 128. For instance, 248 will result in x being pushed
(def-instruction
:char_frominteger
(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.
(def-instruction
:char_fromfloat
(fn [state]
(make-instruction state #(char (mod (long %) 128)) [:float] :char)))

View File

@ -0,0 +1,16 @@
(ns propeller.push.instructions.code
(:require [propeller.push.state :as state]
[propeller.push.instructions :refer [make-instruction
def-instruction]]))
(def-instruction
:exec_dup
(fn [state]
(if (state/empty-stack? state :exec)
state
(state/push-to-stack state :exec (first (:exec state))))))
(def-instruction
:exec_if
(fn [state]
(make-instruction state #(if %1 %3 %2) [:boolean :exec :exec] :exec)))

View File

@ -0,0 +1,9 @@
(ns propeller.push.instructions.input-output
(:require [propeller.push.state :as state]
[propeller.push.instructions :refer [def-instruction]]))
;; Pushes the input labeled :in1 on the inputs map onto the :exec stack
(def-instruction
:in1
(fn [state]
(state/push-to-stack state :exec (:in1 (:input state)))))

View File

@ -0,0 +1,32 @@
(ns propeller.push.instructions.numeric
(:require [propeller.push.instructions :refer [make-instruction
def-instruction]]))
(def-instruction
:integer_=
(fn [state]
(make-instruction state = [:integer :integer] :boolean)))
(def-instruction
:integer_+
(fn [state]
(make-instruction state +' [:integer :integer] :integer)))
(def-instruction
:integer_-
(fn [state]
(make-instruction state -' [:integer :integer] :integer)))
(def-instruction
:integer_*
(fn [state]
(make-instruction state *' [:integer :integer] :integer)))
(def-instruction
:integer_%
(fn [state]
(make-instruction state
(fn [int1 int2]
(if (zero? int2) int1 (quot int1 int2)))
[:integer :integer]
:integer)))

View File

@ -0,0 +1,9 @@
(ns propeller.push.instructions.random
(:require [propeller.push.instructions :refer [def-instruction
make-instruction]]))
;; Pushes a random BOOLEAN
(def-instruction
:boolean_rand
(fn [state]
(make-instruction state #(rand-nth [true false]) [] :boolean)))

View File

@ -0,0 +1,38 @@
(ns propeller.push.instructions.string
(:require [propeller.push.instructions :refer [def-instruction
make-instruction]]))
(def-instruction
:string_=
(fn [state]
(make-instruction state = [:string :string] :boolean)))
(def-instruction
:string_concat
(fn [state]
(make-instruction state #(apply str (concat %1 %2)) [:string :string] :string)))
(def-instruction
:string_drop
(fn [state]
(make-instruction state #(apply str (drop %1 %2)) [:integer :string] :string)))
(def-instruction
:string_includes?
(fn [state]
(make-instruction state clojure.string/includes? [:string :string] :boolean)))
(def-instruction
:string_length
(fn [state]
(make-instruction state count [:string] :integer)))
(def-instruction
:string_reverse
(fn [state]
(make-instruction state #(apply str (reverse %)) [:string] :string)))
(def-instruction
:string_take
(fn [state]
(make-instruction state #(apply str (take %1 %2)) [:integer :string] :string)))

View File

@ -1,14 +1,15 @@
(ns propeller.push.interpreter (ns propeller.push.interpreter
(:use [propeller.push state instructions])) (:require [propeller.push.instructions :refer [instruction-table]])
(:require [propeller.push.state :refer :all]))
(defn interpret-one-step (defn interpret-one-step
"Takes a Push state and executes the next instruction on the exec stack." "Takes a Push state and executes the next instruction on the exec stack."
[state] [state]
(let [popped-state (pop-stack state :exec) (let [popped-state (pop-stack state :exec)
first-raw (first (:exec state)) first-instruction-raw (first (:exec state))
first-instruction (if (symbol? first-raw) first-instruction (if (keyword? first-instruction-raw)
(var-get (resolve first-raw)) (first-instruction-raw @instruction-table)
first-raw)] first-instruction-raw)]
(cond (cond
(fn? first-instruction) (fn? first-instruction)
(first-instruction popped-state) (first-instruction popped-state)
@ -27,7 +28,7 @@
; ;
:else :else
(throw (Exception. (str "Unrecognized Push instruction in program: " (throw (Exception. (str "Unrecognized Push instruction in program: "
first-instruction)))))) (name first-instruction-raw)))))))
(defn interpret-program (defn interpret-program
"Runs the given problem starting with the stacks in start-state." "Runs the given problem starting with the stacks in start-state."

View File

@ -0,0 +1 @@
(ns propeller.push.utils)