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

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

@ -9,7 +9,7 @@
(println " Report for Generation" generation)
(println "-------------------------------------------------------")
(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 errors:" (:errors best))
(println "Best behaviors:" (:behaviors best))

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

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

@ -55,16 +55,3 @@
;; Number of blocks opened by instructions (default = 0)
(def opens {:exec_dup 1
: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)))))

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

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

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

@ -1,32 +1,138 @@
(ns propeller.push.instructions.numeric
(:require [propeller.push.instructions :refer [make-instruction
def-instruction]]))
(:require [propeller.push.instructions :refer [def-instruction]]
[propeller.push.utils :refer [generate-functions
make-instruction]]
[tools.math :as math]))
(def-instruction
:integer_=
(fn [state]
(make-instruction state = [:integer :integer] :boolean)))
;; =============================================================================
;; FLOAT and INTEGER (polymorphic)
;; =============================================================================
(def-instruction
:integer_+
(fn [state]
(make-instruction state +' [:integer :integer] :integer)))
;; 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))
(def-instruction
:integer_-
(fn [state]
(make-instruction state -' [:integer :integer] :integer)))
;; 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))
(def-instruction
:integer_*
(fn [state]
(make-instruction state *' [:integer :integer] :integer)))
;; 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 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
:integer_%
:float_cos
(fn [state]
(make-instruction state
(fn [int1 int2]
(if (zero? int2) int1 (quot int1 int2)))
[:integer :integer]
:integer)))
(make-instruction state math/cos [:float] :float)))
;; Pushes the sine of the top FLOAT
(def-instruction
: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)))

@ -1,9 +1,17 @@
(ns propeller.push.instructions.random
(:require [propeller.push.instructions :refer [def-instruction
make-instruction]]))
(: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)))
;;; 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)]
; ()))

@ -1,6 +1,6 @@
(ns propeller.push.instructions.string
(:require [propeller.push.instructions :refer [def-instruction
make-instruction]]))
(:require [propeller.push.instructions :refer [def-instruction]]
[propeller.push.utils :refer [make-instruction]]))
(def-instruction
:string_=

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

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

@ -21,9 +21,9 @@
(assoc empty-push-state :input {:in1 "I can hear you."})
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."})
1000)

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