Port numeric instructions

This commit is contained in:
mcgirjau 2020-06-24 19:57:20 -04:00
parent bb5f74823c
commit 73b3963b90
15 changed files with 227 additions and 99 deletions

View File

@ -1,7 +1,7 @@
(ns propeller.genome (ns propeller.genome
(:use propeller.push.instructions)) (:use propeller.push.instructions))
(defn push-from-plushy (defn plushy->push
"Returns the Push program expressed by the given plushy representation." "Returns the Push program expressed by the given plushy representation."
[plushy] [plushy]
(let [opener? #(and (vector? %) (= (first %) 'open))] ;; [open <n>] marks opens (let [opener? #(and (vector? %) (= (first %) 'open))] ;; [open <n>] marks opens

View File

@ -9,7 +9,7 @@
(println " Report for Generation" generation) (println " Report for Generation" generation)
(println "-------------------------------------------------------") (println "-------------------------------------------------------")
(print "Best plushy: ") (prn (:plushy best)) (print "Best plushy: ") (prn (:plushy best))
(print "Best program: ") (prn (push-from-plushy (:plushy best))) (print "Best program: ") (prn (plushy->push (:plushy best)))
(println "Best total error:" (:total-error best)) (println "Best total error:" (:total-error best))
(println "Best errors:" (:errors best)) (println "Best errors:" (:errors best))
(println "Best behaviors:" (:behaviors best)) (println "Best behaviors:" (:behaviors best))

View File

@ -1,29 +1,31 @@
(ns propeller.problems.simple-regression (ns propeller.problems.simple-regression
(:use propeller.genome (:require [propeller.genome :refer [plushy->push]]
[propeller.push state interpreter]) [propeller.push.interpreter :refer [interpret-program]]
(:require [tools.math :as math])) [propeller.push.state :refer [empty-state
peek-stack]]
[tools.math :as math]))
;; ============================================================================= ;; =============================================================================
;; Problem: f(x) = 7x^2 - 20x + 13 ;; Problem: f(x) = 7x^2 - 20x + 13
;; ============================================================================= ;; =============================================================================
(defn target-function-hard (defn- target-function-hard
"Target function: f(x) = 7x^2 - 20x + 13" "Target function: f(x) = 7x^2 - 20x + 13"
[x] [x]
(+ (* 7 x x) (* -20 x) 13)) (+ (* 7 x x) (* -20 x) 13))
(defn target-function (defn- target-function
"Target function: f(x) = x^3 + x + 3" "Target function: f(x) = x^3 + x + 3"
[x] [x]
(+ (* x x x) x 3)) (+ (* x x x) x 3))
(defn regression-error-function (defn regression-error-function
"Finds the behaviors and errors of an individual: Error is the absolute "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, 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 or 1000000 if no behavior is produced. The behavior is here defined as the
final top item on the INTEGER stack." final top item on the INTEGER stack."
[argmap individual] [argmap individual]
(let [program (push-from-plushy (:plushy individual)) (let [program (plushy->push (:plushy individual))
inputs (range -10 11) inputs (range -10 11)
correct-outputs (map target-function inputs) correct-outputs (map target-function inputs)
outputs (map (fn [input] outputs (map (fn [input]

View File

@ -12,7 +12,7 @@
behavior is produced. The behavior is here defined as the final top item on behavior is produced. The behavior is here defined as the final top item on
the BOOLEAN stack." the BOOLEAN stack."
[argmap individual] [argmap individual]
(let [program (push-from-plushy (:plushy individual)) (let [program (plushy->push (:plushy individual))
inputs ["GCG" "GACAG" "AGAAG" "CCCA" "GATTACA" "TAGG" "GACT"] inputs ["GCG" "GACAG" "AGAAG" "CCCA" "GATTACA" "TAGG" "GACT"]
correct-outputs [false false false false true true true] correct-outputs [false false false false true true true]
outputs (map (fn [input] outputs (map (fn [input]

View File

@ -55,16 +55,3 @@
;; Number of blocks opened by instructions (default = 0) ;; Number of blocks opened by instructions (default = 0)
(def opens {:exec_dup 1 (def opens {:exec_dup 1
:exec_if 2}) :exec_if 2})
(defn make-instruction
"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
return the result to. Applies the function to the args (popped from the
given stacks), and pushes the result onto the return-stack."
[state function arg-stacks return-stack]
(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-to-stack new-state return-stack result)))))

View File

@ -1,6 +1,6 @@
(ns propeller.push.instructions.boolean (ns propeller.push.instructions.boolean
(:require [propeller.push.instructions :refer [make-instruction (:require [propeller.push.instructions :refer [def-instruction]]
def-instruction]])) [propeller.push.utils :refer [make-instruction]]))
;; Pushes TRUE if the top two BOOLEANs are equal, and FALSE otherwise ;; Pushes TRUE if the top two BOOLEANs are equal, and FALSE otherwise
(def-instruction (def-instruction

View File

@ -1,6 +1,6 @@
(ns propeller.push.instructions.char (ns propeller.push.instructions.char
(:require [propeller.push.instructions :refer [make-instruction (:require [propeller.push.instructions :refer [def-instruction]]
def-instruction]] [propeller.push.utils :refer [make-instruction]]
[tools.character :as char])) [tools.character :as char]))
;; Pushes TRUE onto the BOOLEAN stack if the popped character is a letter ;; Pushes TRUE onto the BOOLEAN stack if the popped character is a letter

View File

@ -1,7 +1,7 @@
(ns propeller.push.instructions.code (ns propeller.push.instructions.code
(:require [propeller.push.state :as state] (:require [propeller.push.instructions :refer [def-instruction]]
[propeller.push.instructions :refer [make-instruction [propeller.push.state :as state]
def-instruction]])) [propeller.push.utils :refer [make-instruction]]))
(def-instruction (def-instruction
:exec_dup :exec_dup

View File

@ -1,32 +1,138 @@
(ns propeller.push.instructions.numeric (ns propeller.push.instructions.numeric
(:require [propeller.push.instructions :refer [make-instruction (:require [propeller.push.instructions :refer [def-instruction]]
def-instruction]])) [propeller.push.utils :refer [generate-functions
make-instruction]]
[tools.math :as math]))
(def-instruction ;; =============================================================================
:integer_= ;; FLOAT and INTEGER (polymorphic)
(fn [state] ;; =============================================================================
(make-instruction state = [:integer :integer] :boolean)))
(def-instruction ;; Pushes TRUE onto the BOOLEAN stack if the top two items are equal, and
:integer_+ ;; FALSE otherwise
(fn [state] (defn- _=
(make-instruction state +' [:integer :integer] :integer))) [stack state]
(make-instruction state = [stack stack] :boolean))
(def-instruction ;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than the top
:integer_- ;; item, and FALSE otherwise
(fn [state] (defn- _>
(make-instruction state -' [:integer :integer] :integer))) [stack state]
(make-instruction state > [stack stack] :boolean))
(def-instruction ;; Pushes TRUE onto the BOOLEAN stack if the second item is less than the top
:integer_* ;; item, and FALSE otherwise
(fn [state] (defn- _<
(make-instruction state *' [:integer :integer] :integer))) [stack state]
(make-instruction state < [stack stack] :boolean))
;; Pushes the sum of the top two items onto the same stack
(defn- _+
[stack state]
(make-instruction state +' [stack stack] stack))
;; Pushes the difference of the top two items (i.e. the second item minus the
;; top item) onto the same stack
(defn- _-
[stack state]
(make-instruction state -' [stack stack] stack))
;; Pushes the product of the top two items onto the same stack
(defn- _*
[stack state]
(make-instruction state *' [stack stack] stack))
;; Pushes the quotient of the top two items (i.e. the second item divided by the
;; top item) onto the same stack. If the top item is zero, acts as a NOOP
(defn- _quot
[stack state]
(make-instruction state
#(if (zero? %2)
(list %1 %2) ; push both items back
(quot %1 %2))
[stack stack]
stack))
;; Pushes the second item modulo the top item onto the same stack. If the top
;; item is zero, acts as a NOOP. The modulus is computed as the remainder of the
;; quotient, where the quotient has first been truncated towards negative
;; infinity.
(defn- _%
[stack state]
(make-instruction state
#(if (zero? %2)
(list %1 %2) ; push both items back
(mod %1 %2))
[stack stack]
stack))
;; Pushes the maximum of the top two items
(defn- _max
[stack state]
(make-instruction state max [stack stack] stack))
;; Pushes the minimum of the top two items
(defn- _min
[stack state]
(make-instruction state min [stack stack] stack))
;; Pushes 1 / 1.0 if the top BOOLEAN is TRUE, or 0 / 0.0 if FALSE
(defn- _fromboolean
[stack state]
(make-instruction state
#((if (= stack :float) float int) (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.)
(generate-functions
[:float :integer]
[_=, _>, _<, _+, _-, _*, _quot, _%, _max, _min, _fromboolean])
;; =============================================================================
;; FLOAT only
;; =============================================================================
;; Pushes the cosine of the top FLOAT
(def-instruction (def-instruction
:integer_% :float_cos
(fn [state] (fn [state]
(make-instruction state (make-instruction state math/cos [:float] :float)))
(fn [int1 int2]
(if (zero? int2) int1 (quot int1 int2))) ;; Pushes the sine of the top FLOAT
[:integer :integer] (def-instruction
:integer))) :float_sin
(fn [state]
(make-instruction state math/sin [:float] :float)))
;; Pushes the tangent of the top FLOAT
(def-instruction
:float_tan
(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
(def-instruction
:float_frominteger
(fn [state]
(make-instruction state math/tan [:float] :float)))
;; =============================================================================
;; INTEGER only
;; =============================================================================
;; Pushes the result of truncating the top FLOAT towards negative infinity
(def-instruction
:integer_fromfloat
(fn [state]
(make-instruction state int [:float] :integer)))

View File

@ -1,9 +1,17 @@
(ns propeller.push.instructions.random (ns propeller.push.instructions.random
(:require [propeller.push.instructions :refer [def-instruction (:require [propeller.push.instructions :refer [def-instruction]]))
make-instruction]]))
;; Pushes a random BOOLEAN ;;; Pushes a random BOOLEAN
(def-instruction ;(def-instruction
:boolean_rand ; :boolean_rand
(fn [state] ; (fn [state]
(make-instruction state #(rand-nth [true false]) [] :boolean))) ; (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,6 +1,6 @@
(ns propeller.push.instructions.string (ns propeller.push.instructions.string
(:require [propeller.push.instructions :refer [def-instruction (:require [propeller.push.instructions :refer [def-instruction]]
make-instruction]])) [propeller.push.utils :refer [make-instruction]]))
(def-instruction (def-instruction
:string_= :string_=

View File

@ -1,30 +1,30 @@
(ns propeller.push.state) (ns propeller.push.state)
;; Set of all stacks used by the Push interpreter ;; Set of all stacks used by the Push interpreter
(defonce ^:private stack-types #{:auxiliary (defonce stacks #{:auxiliary
:boolean :boolean
:char :char
:code :code
:environment :environment
:exec :exec
:float :float
:genome :genome
:gtm :gtm
:input :input
:integer :integer
:output :output
:return :return
:string :string
:tag :tag
:vector_boolean :vector_boolean
:vector_float :vector_float
:vector_integer :vector_integer
:vector_string :vector_string
:zip}) :zip})
;; Record-based states for performance ;; Record-based states for performance
(defmacro define-push-state [] (defmacro define-push-state []
`(defrecord ~'State [~@(map #(symbol (name %)) stack-types)])) `(defrecord ~'State [~@(map #(symbol (name %)) stacks)]))
(define-push-state) (define-push-state)
@ -32,18 +32,10 @@
(defonce empty-state (map->State {})) (defonce empty-state (map->State {}))
(def example-push-state (def example-push-state
{:exec '() {:exec '()
:integer '(1 2 3 4 5 6 7) :integer '(1 2 3 4 5 6 7)
:string '("abc") :string '("abc")
:input {:in1 4}}) :input {:in1 4}})
(defn print-state
"Pretty-prints a Push state, for logging or debugging purposes."
[state]
(doseq [stack stack-types]
(printf "%-15s = " stack)
(prn (if (get state stack) (get state stack) '()))
(flush)))
(defn empty-stack? (defn empty-stack?
"Returns true if the stack is empty." "Returns true if the stack is empty."

View File

@ -1 +1,34 @@
(ns propeller.push.utils) (ns propeller.push.utils
(:require [propeller.push.instructions :refer [def-instruction]]
[propeller.push.state :as push-state]))
;; 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
;; return the result to. Applies the function to the args (popped from the
;; 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)]
(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)))))
;; Given a sequence of stacks, e.g. [:float :integer], and a sequence of suffix
;; function strings, e.g. [_+, _*, _=], automates the generation of all possible
;; combination instructions, which here would be :float_+, :float_*, :float_=,
;; :integer_+, :integer_*, and :integer_=
(defmacro generate-functions [stacks functions]
`(do ~@(for [stack stacks
function functions
:let [instruction-name (keyword (str (name stack) function))]]
`(def-instruction ~instruction-name (partial ~function ~stack)))))
;; Pretty-prints a Push state, for logging or debugging purposes
(defn print-state
[state]
(doseq [stack push-state/stacks]
(printf "%-15s = " stack)
(prn (if (get state stack) (get state stack) '()))
(flush)))

View File

@ -21,9 +21,9 @@
(assoc empty-push-state :input {:in1 "I can hear you."}) (assoc empty-push-state :input {:in1 "I can hear you."})
1000) 1000)
#_(push-from-plushy (make-random-plushy default-instructions 20)) #_(plushy->push (make-random-plushy default-instructions 20))
#_(interpret-program (push-from-plushy (make-random-plushy default-instructions 20)) #_(interpret-program (plushy->push (make-random-plushy default-instructions 20))
(assoc empty-push-state :input {:in1 "I can hear you."}) (assoc empty-push-state :input {:in1 "I can hear you."})
1000) 1000)

View File

@ -1,5 +1,5 @@
(ns propeller.variation (ns propeller.variation
(:use [propeller selection])) (:require [propeller.selection :refer :all]))
(defn crossover (defn crossover
"Crosses over two individuals using uniform crossover. Pads shorter one." "Crosses over two individuals using uniform crossover. Pads shorter one."