Namespace cleanup and add polymorphic instructions

This commit is contained in:
mcgirjau 2020-06-24 22:30:25 -04:00
parent 73b3963b90
commit f04804b00b
17 changed files with 233 additions and 85 deletions

View File

@ -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."

View File

@ -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."

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
(ns propeller.push.instructions
(ns propeller.push.core
(:require [propeller.push.state :refer [get-args-from-stacks
push-to-stack]]))

View File

@ -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

View File

@ -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)))

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -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)]
; ()))

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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)))

View File

@ -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)