removed clj files
This commit is contained in:
parent
74515bd11b
commit
9eae13a89d
@ -1,27 +0,0 @@
|
|||||||
(ns propeller.core
|
|
||||||
(:gen-class)
|
|
||||||
(:require [propeller.gp :as gp]
|
|
||||||
[propeller.problems.simple-regression :as regression]
|
|
||||||
[propeller.problems.string-classification :as string-classif]))
|
|
||||||
|
|
||||||
(defn -main
|
|
||||||
"Runs propel-gp, giving it a map of arguments."
|
|
||||||
[& args]
|
|
||||||
(gp/gp
|
|
||||||
(update-in
|
|
||||||
(merge
|
|
||||||
{:instructions regression/instructions
|
|
||||||
:error-function regression/error-function
|
|
||||||
:max-generations 500
|
|
||||||
:population-size 500
|
|
||||||
:max-initial-plushy-size 50
|
|
||||||
:step-limit 100
|
|
||||||
:parent-selection :lexicase
|
|
||||||
:tournament-size 5
|
|
||||||
:umad-rate 0.1
|
|
||||||
:variation {:umad 0.5 :crossover 0.5}
|
|
||||||
:elitism false}
|
|
||||||
(apply hash-map
|
|
||||||
(map read-string args)))
|
|
||||||
[:error-function]
|
|
||||||
identity)))
|
|
@ -1,35 +0,0 @@
|
|||||||
(ns propeller.genome
|
|
||||||
(:require [propeller.push.core :as push]
|
|
||||||
[propeller.utils :as utils]))
|
|
||||||
|
|
||||||
(defn make-random-plushy
|
|
||||||
"Creates and returns a new plushy."
|
|
||||||
[instructions max-initial-plushy-size]
|
|
||||||
(repeatedly
|
|
||||||
(rand-int max-initial-plushy-size)
|
|
||||||
#(utils/random-instruction instructions)))
|
|
||||||
|
|
||||||
(defn plushy->push
|
|
||||||
"Returns the Push program expressed by the given plushy representation."
|
|
||||||
[plushy]
|
|
||||||
(let [opener? #(and (vector? %) (= (first %) 'open))] ;; [open <n>] marks opens
|
|
||||||
(loop [push () ;; iteratively build the Push program from the plushy
|
|
||||||
plushy (mapcat #(if-let [n (get push/opens %)] [% ['open n]] [%]) plushy)]
|
|
||||||
(if (empty? plushy) ;; maybe we're done?
|
|
||||||
(if (some opener? push) ;; done with plushy, but unclosed open
|
|
||||||
(recur push '(close)) ;; recur with one more close
|
|
||||||
push) ;; otherwise, really done, return push
|
|
||||||
(let [i (first plushy)]
|
|
||||||
(if (= i 'close)
|
|
||||||
(if (some opener? push) ;; process a close when there's an open
|
|
||||||
(recur (let [post-open (reverse (take-while (comp not opener?)
|
|
||||||
(reverse push)))
|
|
||||||
open-index (- (count push) (count post-open) 1)
|
|
||||||
num-open (second (nth push open-index))
|
|
||||||
pre-open (take open-index push)]
|
|
||||||
(if (= 1 num-open)
|
|
||||||
(concat pre-open [post-open])
|
|
||||||
(concat pre-open [post-open ['open (dec num-open)]])))
|
|
||||||
(rest plushy))
|
|
||||||
(recur push (rest plushy))) ;; unmatched close, ignore
|
|
||||||
(recur (concat push [i]) (rest plushy)))))))) ;; anything else
|
|
@ -1,60 +0,0 @@
|
|||||||
(ns propeller.gp
|
|
||||||
(:require [propeller.genome :as genome]
|
|
||||||
[propeller.variation :as variation]
|
|
||||||
[propeller.push.core :as push]
|
|
||||||
[propeller.push.instructions.boolean]
|
|
||||||
[propeller.push.instructions.char]
|
|
||||||
[propeller.push.instructions.code]
|
|
||||||
[propeller.push.instructions.input-output]
|
|
||||||
[propeller.push.instructions.numeric]
|
|
||||||
[propeller.push.instructions.random]
|
|
||||||
[propeller.push.instructions.polymorphic]
|
|
||||||
[propeller.push.instructions.string]
|
|
||||||
[propeller.push.instructions.vector]))
|
|
||||||
|
|
||||||
(defn report
|
|
||||||
"Reports information each generation."
|
|
||||||
[pop generation]
|
|
||||||
(let [best (first pop)]
|
|
||||||
(println "-------------------------------------------------------")
|
|
||||||
(println " Report for Generation" generation)
|
|
||||||
(println "-------------------------------------------------------")
|
|
||||||
(print "Best plushy: ") (prn (:plushy best))
|
|
||||||
(print "Best program: ") (prn (genome/plushy->push (:plushy best)))
|
|
||||||
(println "Best total error:" (:total-error best))
|
|
||||||
(println "Best errors:" (:errors best))
|
|
||||||
(println "Best behaviors:" (:behaviors best))
|
|
||||||
(println "Genotypic diversity:"
|
|
||||||
(float (/ (count (distinct (map :plushy pop))) (count pop))))
|
|
||||||
(println "Average genome length:"
|
|
||||||
(float (/ (reduce + (map count (map :plushy pop))) (count pop))))
|
|
||||||
(println)))
|
|
||||||
|
|
||||||
(defn gp
|
|
||||||
"Main GP loop."
|
|
||||||
[{:keys [population-size max-generations error-function instructions
|
|
||||||
max-initial-plushy-size]
|
|
||||||
:as argmap}]
|
|
||||||
;;
|
|
||||||
(println "Starting GP with args: " argmap)
|
|
||||||
;;
|
|
||||||
(loop [generation 0
|
|
||||||
population (repeatedly
|
|
||||||
population-size
|
|
||||||
#(hash-map :plushy (genome/make-random-plushy
|
|
||||||
instructions
|
|
||||||
max-initial-plushy-size)))]
|
|
||||||
(let [evaluated-pop (sort-by :total-error
|
|
||||||
(map (partial error-function argmap)
|
|
||||||
population))]
|
|
||||||
(report evaluated-pop generation)
|
|
||||||
(cond
|
|
||||||
(zero? (:total-error (first evaluated-pop))) (println "SUCCESS")
|
|
||||||
(>= generation max-generations) nil
|
|
||||||
:else (recur (inc generation)
|
|
||||||
(if (:elitism argmap)
|
|
||||||
(conj (repeatedly (dec population-size)
|
|
||||||
#(variation/new-individual evaluated-pop argmap))
|
|
||||||
(first evaluated-pop))
|
|
||||||
(repeatedly population-size
|
|
||||||
#(variation/new-individual evaluated-pop argmap))))))))
|
|
@ -1,61 +0,0 @@
|
|||||||
(ns propeller.problems.simple-regression
|
|
||||||
(:require [propeller.genome :as genome]
|
|
||||||
[propeller.push.interpreter :as interpreter]
|
|
||||||
[propeller.push.state :as state]
|
|
||||||
[propeller.tools.math :as math]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; Problem: f(x) = 7x^2 - 20x + 13
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(defn- target-function-hard
|
|
||||||
"Target function: f(x) = 7x^2 - 20x + 13"
|
|
||||||
[x]
|
|
||||||
(+ (* 7 x x) (* -20 x) 13))
|
|
||||||
|
|
||||||
(defn- target-function
|
|
||||||
"Target function: f(x) = x^3 + x + 3"
|
|
||||||
[x]
|
|
||||||
(+ (* x x x) x 3))
|
|
||||||
|
|
||||||
;; Set of original propel instructions
|
|
||||||
(def instructions
|
|
||||||
(list :in1
|
|
||||||
:integer_add
|
|
||||||
:integer_subtract
|
|
||||||
:integer_mult
|
|
||||||
:integer_quot
|
|
||||||
:integer_eq
|
|
||||||
:exec_dup
|
|
||||||
:exec_if
|
|
||||||
'close
|
|
||||||
0
|
|
||||||
1))
|
|
||||||
|
|
||||||
(defn error-function
|
|
||||||
"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 (genome/plushy->push (:plushy individual))
|
|
||||||
inputs (range -10 11)
|
|
||||||
correct-outputs (map target-function inputs)
|
|
||||||
outputs (map (fn [input]
|
|
||||||
(state/peek-stack
|
|
||||||
(interpreter/interpret-program
|
|
||||||
program
|
|
||||||
(assoc state/empty-state :input {:in1 input})
|
|
||||||
(:step-limit argmap))
|
|
||||||
:integer))
|
|
||||||
inputs)
|
|
||||||
errors (map (fn [correct-output output]
|
|
||||||
(if (= output :no-stack-item)
|
|
||||||
1000000
|
|
||||||
(math/abs (- correct-output output))))
|
|
||||||
correct-outputs
|
|
||||||
outputs)]
|
|
||||||
(assoc individual
|
|
||||||
:behaviors outputs
|
|
||||||
:errors errors
|
|
||||||
:total-error (apply +' errors))))
|
|
@ -1,3 +0,0 @@
|
|||||||
(ns propeller.problems.software.number-io)
|
|
||||||
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
|||||||
(ns propeller.problems.string-classification
|
|
||||||
(:require [propeller.genome :as genome]
|
|
||||||
[propeller.push.interpreter :as interpreter]
|
|
||||||
[propeller.push.state :as state]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; String classification
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Set of original propel instructions
|
|
||||||
(def instructions
|
|
||||||
(list :in1
|
|
||||||
:integer_add
|
|
||||||
:integer_subtract
|
|
||||||
:integer_mult
|
|
||||||
:integer_quot
|
|
||||||
:integer_eq
|
|
||||||
:exec_dup
|
|
||||||
:exec_if
|
|
||||||
:boolean_and
|
|
||||||
:boolean_or
|
|
||||||
:boolean_not
|
|
||||||
:boolean_eq
|
|
||||||
:string_eq
|
|
||||||
:string_take
|
|
||||||
:string_drop
|
|
||||||
:string_reverse
|
|
||||||
:string_concat
|
|
||||||
:string_length
|
|
||||||
:string_includes?
|
|
||||||
'close
|
|
||||||
0
|
|
||||||
1
|
|
||||||
true
|
|
||||||
false
|
|
||||||
""
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"A"
|
|
||||||
"C"
|
|
||||||
"G"
|
|
||||||
"T"))
|
|
||||||
|
|
||||||
(defn error-function
|
|
||||||
"Finds the behaviors and errors of an individual: Error is 0 if the value and
|
|
||||||
the program's selected behavior match, or 1 if they differ, or 1000000 if no
|
|
||||||
behavior is produced. The behavior is here defined as the final top item on
|
|
||||||
the BOOLEAN stack."
|
|
||||||
[argmap individual]
|
|
||||||
(let [program (genome/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]
|
|
||||||
(state/peek-stack
|
|
||||||
(interpreter/interpret-program
|
|
||||||
program
|
|
||||||
(assoc state/empty-state :input {:in1 input})
|
|
||||||
(:step-limit argmap))
|
|
||||||
:boolean))
|
|
||||||
inputs)
|
|
||||||
errors (map (fn [correct-output output]
|
|
||||||
(if (= output :no-stack-item)
|
|
||||||
1000000
|
|
||||||
(if (= correct-output output)
|
|
||||||
0
|
|
||||||
1)))
|
|
||||||
correct-outputs
|
|
||||||
outputs)]
|
|
||||||
(assoc individual
|
|
||||||
:behaviors outputs
|
|
||||||
:errors errors
|
|
||||||
:total-error (apply +' errors))))
|
|
@ -1,18 +0,0 @@
|
|||||||
(ns propeller.push.core)
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; PushGP Instructions
|
|
||||||
;;
|
|
||||||
;; Instructions are represented as keywords, and stored in an atom.
|
|
||||||
;;
|
|
||||||
;; Instructions must all be either functions that take one Push state and
|
|
||||||
;; return another, or constant literals.
|
|
||||||
;;
|
|
||||||
;; TMH: ERCs?
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(def instruction-table (atom (hash-map)))
|
|
||||||
|
|
||||||
;; Number of blocks opened by instructions (default = 0)
|
|
||||||
(def opens {:exec_dup 1
|
|
||||||
:exec_if 2})
|
|
@ -1,68 +0,0 @@
|
|||||||
(ns propeller.push.instructions.boolean
|
|
||||||
(:require [propeller.push.utils :refer [def-instruction
|
|
||||||
make-instruction]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; BOOLEAN Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes the logical AND of the top two BOOLEANs
|
|
||||||
(def-instruction
|
|
||||||
:boolean_and
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(and %1 %2) [:boolean :boolean] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes the logical OR of the top two BOOLEANs
|
|
||||||
(def-instruction
|
|
||||||
:boolean_or
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(or %1 %2) [:boolean :boolean] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes the logical NOT of the top BOOLEAN
|
|
||||||
(def-instruction
|
|
||||||
:boolean_not
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state not [:boolean] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes the logical XOR of the top two BOOLEAN
|
|
||||||
(def-instruction
|
|
||||||
:boolean_xor
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(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
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(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
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(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
|
|
||||||
^{:stacks #{:boolean :float}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(not (zero? %)) [:float] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes FALSE if the top INTEGER is 0, and TRUE otherwise
|
|
||||||
(def-instruction
|
|
||||||
:boolean_frominteger
|
|
||||||
^{:stacks #{:boolean :integer}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(not (zero? %)) [:integer] :boolean)))
|
|
@ -1,63 +0,0 @@
|
|||||||
(ns propeller.push.instructions.char
|
|
||||||
(:require [propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [def-instruction
|
|
||||||
make-instruction]]
|
|
||||||
[propeller.tools.character :as char]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; CHAR Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the popped character is a letter
|
|
||||||
(def-instruction
|
|
||||||
:char_isletter
|
|
||||||
^{:stacks #{:boolean :char}}
|
|
||||||
(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
|
|
||||||
^{:stacks #{:boolean :char}}
|
|
||||||
(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
|
|
||||||
^{:stacks #{:boolean :char}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state char/is-whitespace [:char] :boolean)))
|
|
||||||
|
|
||||||
;; 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
|
|
||||||
^{:stacks #{:char :float}}
|
|
||||||
(fn [state]
|
|
||||||
(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
|
|
||||||
;; 128. For instance, 248 will result in x being pushed
|
|
||||||
(def-instruction
|
|
||||||
:char_frominteger
|
|
||||||
^{:stacks #{:char :integer}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(char (mod % 128)) [:integer] :char)))
|
|
||||||
|
|
||||||
;; 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 \h \e \l \l \o
|
|
||||||
(def-instruction
|
|
||||||
:char_allfromstring
|
|
||||||
^{:stacks #{:char :string}}
|
|
||||||
(fn [state]
|
|
||||||
(if (state/empty-stack? state :string)
|
|
||||||
state
|
|
||||||
(let [top-string (state/peek-stack state :string)
|
|
||||||
popped-state (state/pop-stack state :string)]
|
|
||||||
(state/push-to-stack-multiple popped-state :char (map char top-string))))))
|
|
@ -1,108 +0,0 @@
|
|||||||
(ns propeller.push.instructions.code
|
|
||||||
(:require [propeller.utils :as utils]
|
|
||||||
[propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [def-instruction
|
|
||||||
generate-instructions
|
|
||||||
make-instruction]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; Polymorphic Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(def _noop
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state] state))
|
|
||||||
|
|
||||||
(def _do*range
|
|
||||||
^{:stacks #{:exec :integer}}
|
|
||||||
(fn [stack state] state))
|
|
||||||
|
|
||||||
(def _noop
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state] state))
|
|
||||||
|
|
||||||
(def _noop
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state] state))
|
|
||||||
|
|
||||||
(generate-instructions
|
|
||||||
[:exec :code]
|
|
||||||
[_noop])
|
|
||||||
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; CODE Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Concatenates the top two instructions on the :code stack and pushes the
|
|
||||||
;; result back onto the stack
|
|
||||||
(def-instruction
|
|
||||||
:code_append
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state
|
|
||||||
#(utils/not-lazy
|
|
||||||
(concat (utils/ensure-list %2)
|
|
||||||
(utils/ensure-list %1)))
|
|
||||||
[:code :code]
|
|
||||||
:code)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_atom
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_car
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_cdr
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_cons
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_do
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_do*
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:code_append
|
|
||||||
^{:stacks #{:code}}
|
|
||||||
(fn [state]
|
|
||||||
()))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; EXEC Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:exec_dup
|
|
||||||
^{:stacks #{:exec}}
|
|
||||||
(fn [state]
|
|
||||||
(if (state/empty-stack? state :exec)
|
|
||||||
state
|
|
||||||
(state/push-to-stack state :exec (first (:exec state))))))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:exec_if
|
|
||||||
^{:stacks #{:boolean :exec}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(if %1 %3 %2) [:boolean :exec :exec] :exec)))
|
|
@ -1,51 +0,0 @@
|
|||||||
(ns propeller.push.instructions.input-output
|
|
||||||
(:require [propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [def-instruction
|
|
||||||
generate-instructions]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; INPUT Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Allows Push to handle input instructions of the form :inN, e.g. :in2, taking
|
|
||||||
;; elements thus labeled from the :input stack and pushing them onto the :exec
|
|
||||||
;; stack. We can tell whether a particular inN instruction is valid if N-1
|
|
||||||
;; values are on the input stack.
|
|
||||||
(defn handle-input-instruction
|
|
||||||
[state instruction]
|
|
||||||
(if-let [input (instruction (:input state))]
|
|
||||||
(state/push-to-stack state :exec input)
|
|
||||||
(throw (Exception. (str "Undefined input instruction " instruction)))))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; OUTPUT Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:print_newline
|
|
||||||
^{:stacks [:print]}
|
|
||||||
(fn [state]
|
|
||||||
(let [current-output (state/peek-stack state :output)
|
|
||||||
popped-state (state/pop-stack state :output)]
|
|
||||||
(state/push-to-stack popped-state :output (str current-output \newline)))))
|
|
||||||
|
|
||||||
(def _print
|
|
||||||
^{:stacks [:print]}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (state/empty-stack? state stack)
|
|
||||||
state
|
|
||||||
(let [top-item (state/peek-stack state stack)
|
|
||||||
top-item-str (if (or (string? top-item)
|
|
||||||
(char? top-item))
|
|
||||||
top-item
|
|
||||||
(pr-str top-item))
|
|
||||||
current-output (state/peek-stack state :output)
|
|
||||||
popped-state (state/pop-stack (state/pop-stack state stack) :output)]
|
|
||||||
(state/push-to-stack popped-state
|
|
||||||
:output
|
|
||||||
(str current-output top-item-str))))))
|
|
||||||
|
|
||||||
(generate-instructions
|
|
||||||
[:boolean :char :code :exec :float :integer :string
|
|
||||||
:vector_boolean :vector_float :vector_integer :vector_string]
|
|
||||||
[_print])
|
|
@ -1,171 +0,0 @@
|
|||||||
(ns propeller.push.instructions.numeric
|
|
||||||
(:require [propeller.push.utils :refer [def-instruction
|
|
||||||
generate-instructions
|
|
||||||
make-instruction]]
|
|
||||||
[propeller.tools.math :as math]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; FLOAT and INTEGER Instructions (polymorphic)
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than the top
|
|
||||||
;; item, and FALSE otherwise
|
|
||||||
(def _gt
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [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
|
|
||||||
(def _gte
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [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
|
|
||||||
(def _lt
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [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
|
|
||||||
(def _lte
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state <= [stack stack] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes the sum of the top two items onto the same stack
|
|
||||||
(def _add
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [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
|
|
||||||
(def _subtract
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state -' [stack stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the product of the top two items onto the same stack
|
|
||||||
(def _mult
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [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, pushes 1
|
|
||||||
(def _quot
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(if (zero? %2) 1 (quot %1 %2)) [stack stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the second item modulo the top item onto the same stack. If the top
|
|
||||||
;; item is zero, pushes 1. The modulus is computed as the remainder of the
|
|
||||||
;; quotient, where the quotient has first been truncated towards negative
|
|
||||||
;; infinity.
|
|
||||||
(def _mod
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(if (zero? %2) 1 (mod %1 %2)) [stack stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the maximum of the top two items
|
|
||||||
(def _max
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state max [stack stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the minimum of the top two items
|
|
||||||
(def _min
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [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
|
|
||||||
(def _fromboolean
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state
|
|
||||||
#((if (= stack :integer) int float) (if % 1 0))
|
|
||||||
[:boolean]
|
|
||||||
stack)))
|
|
||||||
|
|
||||||
;; Pushes the ASCII value of the top CHAR
|
|
||||||
(def _fromchar
|
|
||||||
^{:stacks #{:char}}
|
|
||||||
(fn [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
|
|
||||||
(def _fromstring
|
|
||||||
^{:stacks #{:string}}
|
|
||||||
(fn [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
|
|
||||||
(def _inc
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state inc [stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the decrement (i.e. -1) of the top item of the stack
|
|
||||||
(def _dec
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state dec [stack] stack)))
|
|
||||||
|
|
||||||
;; 2 types x 16 functions = 32 instructions
|
|
||||||
(generate-instructions
|
|
||||||
[:float :integer]
|
|
||||||
[_gt _gte _lt _lte _add _subtract _mult _quot _mod _max _min _inc _dec
|
|
||||||
_fromboolean _fromchar _fromstring])
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; FLOAT Instructions only
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes the cosine of the top FLOAT
|
|
||||||
(def-instruction
|
|
||||||
:float_cos
|
|
||||||
^{:stacks #{:float}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state math/cos [:float] :float)))
|
|
||||||
|
|
||||||
;; Pushes the sine of the top FLOAT
|
|
||||||
(def-instruction
|
|
||||||
:float_sin
|
|
||||||
^{:stacks #{:float}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state math/sin [:float] :float)))
|
|
||||||
|
|
||||||
;; Pushes the tangent of the top FLOAT
|
|
||||||
(def-instruction
|
|
||||||
:float_tan
|
|
||||||
^{:stacks #{:float}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state math/tan [:float] :float)))
|
|
||||||
|
|
||||||
;; Pushes the floating point version of the top INTEGER
|
|
||||||
(def-instruction
|
|
||||||
:float_frominteger
|
|
||||||
^{:stacks #{:float :integer}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state float [:integer] :float)))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; INTEGER Instructions only
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes the result of truncating the top FLOAT towards negative infinity
|
|
||||||
(def-instruction
|
|
||||||
:integer_fromfloat
|
|
||||||
^{:stacks #{:float :integer}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state int [:float] :integer)))
|
|
@ -1,181 +0,0 @@
|
|||||||
(ns propeller.push.instructions.polymorphic
|
|
||||||
(:require [propeller.utils :as utils]
|
|
||||||
[propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [generate-instructions
|
|
||||||
make-instruction]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; Polymorphic Instructions
|
|
||||||
;;
|
|
||||||
;; (for all stacks, with the exception of non-data ones like auxiliary, input,
|
|
||||||
;; and output)
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Duplicates the top item of the stack. Does not pop its argument (since that
|
|
||||||
;; would negate the effect of the duplication)
|
|
||||||
(def _dup
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [top-item (state/peek-stack state stack)]
|
|
||||||
(if (state/empty-stack? state stack)
|
|
||||||
state
|
|
||||||
(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
|
|
||||||
(def _duptimes
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (or (and (= stack :integer)
|
|
||||||
(<= 2 (count (:integer state))))
|
|
||||||
(and (not= stack :integer)
|
|
||||||
(not (state/empty-stack? state :integer))
|
|
||||||
(not (state/empty-stack? state stack))))
|
|
||||||
(let [n (state/peek-stack state :integer)
|
|
||||||
popped-state (state/pop-stack state :integer)
|
|
||||||
top-item (state/peek-stack popped-state stack)
|
|
||||||
top-item-dup (take (- n 1) (repeat top-item))]
|
|
||||||
(cond
|
|
||||||
(< 0 n) (state/push-to-stack-multiple popped-state stack top-item-dup)
|
|
||||||
:else (state/pop-stack popped-state stack)))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; Duplicates the top n items on the stack, one time each. The number n is
|
|
||||||
;; determined by the top INTEGER. If n <= 0, no items will be duplicated. If
|
|
||||||
;; fewer than n items are on the stack, the entire stack will be duplicated.
|
|
||||||
(def _dupitems
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (state/empty-stack? state :integer)
|
|
||||||
state
|
|
||||||
(let [n (state/peek-stack state :integer)
|
|
||||||
popped-state (state/pop-stack state :integer)
|
|
||||||
top-items (take n (get popped-state stack))]
|
|
||||||
(state/push-to-stack-multiple popped-state stack top-items)))))
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the stack is empty. Otherwise FALSE
|
|
||||||
(def _empty
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(state/push-to-stack state :boolean (state/empty-stack? state stack))))
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the top two items are equal.
|
|
||||||
;; Otherwise FALSE
|
|
||||||
(def _eq
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state = [stack stack] :boolean)))
|
|
||||||
|
|
||||||
;; Empties the given stack
|
|
||||||
(def _flush
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(assoc state stack '())))
|
|
||||||
|
|
||||||
;; Pops the given stack
|
|
||||||
(def _pop
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(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)
|
|
||||||
(def _rot
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (<= 3 (count (get state stack)))
|
|
||||||
(let [top-three (state/peek-stack-multiple state stack 3)
|
|
||||||
popped-state (state/pop-stack-multiple state stack 3)
|
|
||||||
top-three-rot (take 3 (conj top-three (last top-three)))]
|
|
||||||
(state/push-to-stack-multiple popped-state stack top-three-rot))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; Inserts the top item deeper into the stack, using the top INTEGER to
|
|
||||||
;; determine how deep
|
|
||||||
(def _shove
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (or (and (= stack :integer)
|
|
||||||
(<= 2 (count (:integer state))))
|
|
||||||
(and (not= stack :integer)
|
|
||||||
(not (state/empty-stack? state :integer))
|
|
||||||
(not (state/empty-stack? state stack))))
|
|
||||||
(let [index-raw (state/peek-stack state :integer)
|
|
||||||
popped-state (state/pop-stack state :integer)
|
|
||||||
top-item (state/peek-stack popped-state stack)
|
|
||||||
popped-state (state/pop-stack popped-state stack)
|
|
||||||
index (max 0 (min index-raw (count (get popped-state stack))))]
|
|
||||||
(update popped-state stack #(utils/not-lazy (concat (take index %)
|
|
||||||
(list top-item)
|
|
||||||
(drop index %)))))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; Pushes the given stack's depth onto the INTEGER stack
|
|
||||||
(def _stackdepth
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [stack-depth (count (get state stack))]
|
|
||||||
(state/push-to-stack state :integer stack-depth))))
|
|
||||||
|
|
||||||
;; Swaps the top two items on the stack
|
|
||||||
(def _swap
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (<= 2 (count (get state stack)))
|
|
||||||
(let [top-two (state/peek-stack-multiple state stack 2)
|
|
||||||
popped-state (state/pop-stack-multiple state stack 2)]
|
|
||||||
(state/push-to-stack-multiple popped-state stack (reverse top-two)))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; Pushes an indexed item from deep in the stack, removing it. The top INTEGER
|
|
||||||
;; is used to determine how deep to yank from
|
|
||||||
(def _yank
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (or (and (= stack :integer)
|
|
||||||
(<= 2 (count (:integer state))))
|
|
||||||
(and (not= stack :integer)
|
|
||||||
(not (state/empty-stack? state :integer))
|
|
||||||
(not (state/empty-stack? state stack))))
|
|
||||||
(let [index-raw (state/peek-stack state :integer)
|
|
||||||
popped-state (state/pop-stack state :integer)
|
|
||||||
index (max 0 (min index-raw (count (get popped-state stack))))
|
|
||||||
indexed-item (nth (get popped-state stack) index)]
|
|
||||||
(update popped-state stack #(utils/not-lazy
|
|
||||||
(concat (list indexed-item)
|
|
||||||
(take index %)
|
|
||||||
(rest (drop index %))))))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; Pushes a copy of an indexed item from deep in the stack, without removing it.
|
|
||||||
;; The top INTEGER is used to determine how deep to yankdup from
|
|
||||||
(def _yankdup
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (or (and (= stack :integer)
|
|
||||||
(<= 2 (count (:integer state))))
|
|
||||||
(and (not= stack :integer)
|
|
||||||
(not (state/empty-stack? state :integer))
|
|
||||||
(not (state/empty-stack? state stack))))
|
|
||||||
(let [index-raw (state/peek-stack state :integer)
|
|
||||||
popped-state (state/pop-stack state :integer)
|
|
||||||
index (max 0 (min index-raw (count (get popped-state stack))))
|
|
||||||
indexed-item (nth (get popped-state stack) index)]
|
|
||||||
(state/push-to-stack popped-state stack indexed-item))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
;; 9 types x 1 functions = 9 instructions
|
|
||||||
(generate-instructions
|
|
||||||
[:boolean :char :float :integer :string
|
|
||||||
:vector_boolean :vector_float :vector_integer :vector_string]
|
|
||||||
[_eq])
|
|
||||||
|
|
||||||
;; 11 types x 12 functions = 132 instructions
|
|
||||||
(generate-instructions
|
|
||||||
[:boolean :char :code :exec :float :integer :string
|
|
||||||
:vector_boolean :vector_float :vector_integer :vector_string]
|
|
||||||
[_dup _duptimes _dupitems _empty _flush _pop _rot _shove _stackdepth
|
|
||||||
_swap _yank _yankdup])
|
|
@ -1,4 +0,0 @@
|
|||||||
(ns propeller.push.instructions.random
|
|
||||||
(:require [propeller.push.utils :refer [def-instruction]]))
|
|
||||||
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
|||||||
(ns propeller.push.instructions.string
|
|
||||||
(:require [propeller.push.utils :refer [def-instruction
|
|
||||||
make-instruction]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; STRING Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_=
|
|
||||||
^{:stacks #{:boolean :string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state = [:string :string] :boolean)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_concat
|
|
||||||
^{:stacks #{:string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(apply str (concat %1 %2)) [:string :string] :string)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_drop
|
|
||||||
^{:stacks #{:integer :string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(apply str (drop %1 %2)) [:integer :string] :string)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_includes?
|
|
||||||
^{:stacks #{:boolean :string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state clojure.string/includes? [:string :string] :boolean)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_length
|
|
||||||
^{:stacks #{:integer :string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state count [:string] :integer)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_reverse
|
|
||||||
^{:stacks #{:string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(apply str (reverse %)) [:string] :string)))
|
|
||||||
|
|
||||||
(def-instruction
|
|
||||||
:string_take
|
|
||||||
^{:stacks #{:integer :string}}
|
|
||||||
(fn [state]
|
|
||||||
(make-instruction state #(apply str (take %1 %2)) [:integer :string] :string)))
|
|
@ -1,204 +0,0 @@
|
|||||||
(ns propeller.push.instructions.vector
|
|
||||||
(:require [clojure.string]
|
|
||||||
[propeller.utils :as utils]
|
|
||||||
[propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [def-instruction
|
|
||||||
generate-instructions
|
|
||||||
make-instruction]]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; VECTOR Instructions
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
;; Pushes the butlast of the top item
|
|
||||||
(def _butlast
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(vec (butlast %)) [stack] stack)))
|
|
||||||
|
|
||||||
;; Concats and pushes the top two vectors of the stack
|
|
||||||
(def _concat
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(vec (concat %2 %1)) [stack stack] stack)))
|
|
||||||
|
|
||||||
;; Conj's the top item of the appropriately-typed literal stack onto the vector
|
|
||||||
;; stack (e.g. pop the top INTEGER and conj it onto the top VECTOR_INTEGER)
|
|
||||||
(def _conj
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state #(conj %2 %1) [lit-stack stack] stack))))
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the top element of the vector stack
|
|
||||||
;; contains the top element of the appropriately-typed literal stack. Otherwise,
|
|
||||||
;; pushes FALSE
|
|
||||||
(def _contains
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state #(contains? (set %2) %1) [lit-stack stack] :boolean))))
|
|
||||||
|
|
||||||
;; Pushes TRUE onto the BOOLEAN stack if the top element is an empty vector.
|
|
||||||
;; Otherwise, pushes FALSE
|
|
||||||
(def _emptyvector
|
|
||||||
^{:stacks #{:boolean}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state empty? [stack] :boolean)))
|
|
||||||
|
|
||||||
;; Pushes the first item of the top element of the vector stack onto the
|
|
||||||
;; approrpiately-typed literal stack
|
|
||||||
(def _first
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state first [stack] lit-stack))))
|
|
||||||
|
|
||||||
;; Pushes onto the INTEGER stack the index of the top element of the
|
|
||||||
;; appropriately-typed literal stack within the top element of the vector stack
|
|
||||||
(def _indexof
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state #(utils/indexof %1 %2) [lit-stack stack] :integer))))
|
|
||||||
|
|
||||||
;; Pushes the last item of the top element of the vector stack onto the
|
|
||||||
;; approrpiately-typed literal stack
|
|
||||||
(def _last
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state last [stack] lit-stack))))
|
|
||||||
|
|
||||||
;; Pushes the length of the top item onto the INTEGER stack
|
|
||||||
(def _length
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state count [stack] :integer)))
|
|
||||||
|
|
||||||
;; Pushes the Nth item of the top element of the vector stack onto the
|
|
||||||
;; approrpiately-typed literal stack, where N is taken from the INTEGER stack.
|
|
||||||
;; To insure the index is within bounds, N is taken mod the vector length
|
|
||||||
(def _nth
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
#(get %2 (mod %1 (count %2)))
|
|
||||||
[:integer stack]
|
|
||||||
lit-stack))))
|
|
||||||
|
|
||||||
;; Pushes onto the INTEGER stack the number of occurrences of the top element of
|
|
||||||
;; the appropriately-typed literal stack within the top element of the vector
|
|
||||||
;; stack
|
|
||||||
(def _occurrencesof
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [lit vect] (count (filter #(= lit %) vect)))
|
|
||||||
[lit-stack stack]
|
|
||||||
:integer))))
|
|
||||||
|
|
||||||
;; Pushes every item of the top element onto the appropriately-typed stack
|
|
||||||
(def _pushall
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(if (state/empty-stack? state stack)
|
|
||||||
state
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)
|
|
||||||
top-vector (state/peek-stack state stack)
|
|
||||||
popped-state (state/pop-stack state stack)]
|
|
||||||
(state/push-to-stack-multiple popped-state lit-stack top-vector)))))
|
|
||||||
|
|
||||||
;; Removes all occurrences of the top element of the appropriately-typed literal
|
|
||||||
;; stack from the first element of the vector stack
|
|
||||||
(def _remove
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [lit vect] (vec (filter #(not= lit %) vect)))
|
|
||||||
[lit-stack stack]
|
|
||||||
stack))))
|
|
||||||
|
|
||||||
;; Replaces all occurrences of the second element of the appropriately-typed
|
|
||||||
;; literal stack with the top element of the appropriately-typed literal stack
|
|
||||||
;; within the top item of the vector stack
|
|
||||||
(def _replace
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [lit1 lit2 vect]
|
|
||||||
(replace {lit1 lit2} vect))
|
|
||||||
[lit-stack lit-stack stack]
|
|
||||||
stack))))
|
|
||||||
|
|
||||||
;; Replaces the first occurrence of the second element of the appropriately-typed
|
|
||||||
;; literal stack with the top element of the appropriately-typed literal stack
|
|
||||||
;; within the top item of the vector stack
|
|
||||||
(def _replacefirst
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [lit1 lit2 vect]
|
|
||||||
(assoc vect (utils/indexof lit1 vect) lit2))
|
|
||||||
[lit-stack lit-stack stack]
|
|
||||||
stack))))
|
|
||||||
|
|
||||||
;; Pushes the rest of the top item
|
|
||||||
(def _rest
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(vec (rest %)) [stack] stack)))
|
|
||||||
|
|
||||||
;; Pushes the reverse of the top item
|
|
||||||
(def _reverse
|
|
||||||
^{:stacks #{}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(vec (reverse %)) [stack] stack)))
|
|
||||||
|
|
||||||
;; Replaces in the top vector the item at index N (taken from the INTEGER stack)
|
|
||||||
;; with the top item from the appropriately-typed literal stack. To insure the
|
|
||||||
;; index is within bounds, N is taken mod the vector length
|
|
||||||
(def _set
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(let [lit-stack (utils/get-vector-literal-type stack)]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [lit n vect]
|
|
||||||
(assoc vect (mod n (count vect)) lit))
|
|
||||||
[:integer lit-stack stack]
|
|
||||||
stack))))
|
|
||||||
|
|
||||||
;; Pushes a subvector of the top item, with start and end indices determined by
|
|
||||||
;; the second and top items of the INTEGER stack respectively
|
|
||||||
(def _subvec
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state
|
|
||||||
(fn [stop-raw start-raw vect]
|
|
||||||
(let [start (min (count vect) (max 0 start-raw))
|
|
||||||
stop (min (count vect) (max start-raw stop-raw))]
|
|
||||||
(subvec vect start stop)))
|
|
||||||
[:integer :integer stack]
|
|
||||||
stack)))
|
|
||||||
|
|
||||||
;; Pushes the first N items of the top element, where N is taken from the top of
|
|
||||||
;; the INTEGER stack
|
|
||||||
(def _take
|
|
||||||
^{:stacks #{:integer}}
|
|
||||||
(fn [stack state]
|
|
||||||
(make-instruction state #(vec (take %1 %2)) [:integer stack] stack)))
|
|
||||||
|
|
||||||
;; 4 types x 20 functions = 80 instructions
|
|
||||||
(generate-instructions
|
|
||||||
[:vector_boolean :vector_float :vector_integer :vector_string]
|
|
||||||
[_butlast _concat _conj _contains _emptyvector _first _indexof _last
|
|
||||||
_length _nth _occurrencesof _pushall _remove _replace _replacefirst
|
|
||||||
_rest _reverse _set _subvec _take])
|
|
||||||
|
|
||||||
;; Manually add extra metadata for _conj
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
|||||||
(ns propeller.push.interpreter
|
|
||||||
(:require [propeller.push.core :as push]
|
|
||||||
[propeller.push.state :as state]
|
|
||||||
[propeller.push.utils :refer [get-literal-type]]
|
|
||||||
[propeller.push.instructions.input-output :as io]))
|
|
||||||
|
|
||||||
(defn interpret-one-step
|
|
||||||
"Takes a Push state and executes the next instruction on the exec stack."
|
|
||||||
[state]
|
|
||||||
(let [popped-state (state/pop-stack state :exec)
|
|
||||||
instruction (first (:exec state))
|
|
||||||
literal-type (get-literal-type instruction)] ; nil for non-literals
|
|
||||||
(cond
|
|
||||||
;;
|
|
||||||
;; Recognize functional instruction or input instruction
|
|
||||||
(keyword? instruction)
|
|
||||||
(if-let [function (instruction @push/instruction-table)]
|
|
||||||
(function popped-state)
|
|
||||||
(io/handle-input-instruction popped-state instruction))
|
|
||||||
;;
|
|
||||||
;; Recognize constant literal instruction
|
|
||||||
literal-type
|
|
||||||
(if (= :generic-vector literal-type)
|
|
||||||
;; Empty vector gets pushed on all vector stacks
|
|
||||||
(reduce #(update-in % [%2] conj []) popped-state
|
|
||||||
[:vector_boolean :vector_float :vector_integer :vector_string])
|
|
||||||
(state/push-to-stack popped-state literal-type instruction))
|
|
||||||
;;
|
|
||||||
;; Recognize parenthesized group of instructions
|
|
||||||
(seq? instruction)
|
|
||||||
(update popped-state :exec #(concat %2 %1) instruction)
|
|
||||||
;;
|
|
||||||
:else
|
|
||||||
(throw (Exception. (str "Unrecognized Push instruction in program: "
|
|
||||||
(name instruction)))))))
|
|
||||||
|
|
||||||
(defn interpret-program
|
|
||||||
"Runs the given problem starting with the stacks in start-state."
|
|
||||||
[program start-state step-limit]
|
|
||||||
(loop [state (assoc start-state :exec program :step 1)]
|
|
||||||
(if (or (empty? (:exec state))
|
|
||||||
(> (:step state) step-limit))
|
|
||||||
state
|
|
||||||
(recur (update (interpret-one-step state) :step inc)))))
|
|
@ -1,84 +0,0 @@
|
|||||||
(ns propeller.push.state)
|
|
||||||
|
|
||||||
;; Empty push state - all available stacks are empty
|
|
||||||
(defonce empty-state {:auxiliary '()
|
|
||||||
:boolean '()
|
|
||||||
:char '()
|
|
||||||
:code '()
|
|
||||||
:exec '()
|
|
||||||
:float '()
|
|
||||||
:input {}
|
|
||||||
:integer '()
|
|
||||||
:output '()
|
|
||||||
:string '()
|
|
||||||
:vector_boolean '()
|
|
||||||
:vector_float '()
|
|
||||||
:vector_integer '()
|
|
||||||
:vector_string '()})
|
|
||||||
|
|
||||||
(def example-push-state
|
|
||||||
{:exec '()
|
|
||||||
:integer '(1 2 3 4 5 6 7)
|
|
||||||
:string '("abc")
|
|
||||||
:input {:in1 4}})
|
|
||||||
|
|
||||||
(defn empty-stack?
|
|
||||||
"Returns true if the stack is empty."
|
|
||||||
[state stack]
|
|
||||||
(empty? (get state stack)))
|
|
||||||
|
|
||||||
(defn peek-stack
|
|
||||||
"Returns the top item on a stack."
|
|
||||||
[state stack]
|
|
||||||
(let [working-stack (get state stack)]
|
|
||||||
(if (empty? working-stack)
|
|
||||||
:no-stack-item
|
|
||||||
(first working-stack))))
|
|
||||||
|
|
||||||
(defn peek-stack-multiple
|
|
||||||
"Returns the top n items on a stack. If there are less than n items on the
|
|
||||||
stack, returns the entire stack."
|
|
||||||
[state stack n]
|
|
||||||
(take n (get state stack)))
|
|
||||||
|
|
||||||
(defn pop-stack
|
|
||||||
"Removes the top item of stack."
|
|
||||||
[state stack]
|
|
||||||
(update state stack rest))
|
|
||||||
|
|
||||||
(defn pop-stack-multiple
|
|
||||||
"Removes the top n items of a stack. If there are less than n items on the
|
|
||||||
stack, pops the entire stack."
|
|
||||||
[state stack n]
|
|
||||||
(update state stack #(drop n %)))
|
|
||||||
|
|
||||||
(defn push-to-stack
|
|
||||||
"Pushes an item onto a stack."
|
|
||||||
[state stack item]
|
|
||||||
(update state stack conj item))
|
|
||||||
|
|
||||||
(defn push-to-stack-multiple
|
|
||||||
"Pushes a list of items onto a stack, leaving them in the order they are in."
|
|
||||||
[state stack items]
|
|
||||||
(let [items-list (if (coll? items) items (list items))
|
|
||||||
items-list-no-nil (filter #(not (nil? %)) items-list)]
|
|
||||||
(update state stack into (reverse items-list-no-nil))))
|
|
||||||
|
|
||||||
(defn get-args-from-stacks
|
|
||||||
"Takes a state and a collection of stacks to take args from. If there are
|
|
||||||
enough args on each of the desired stacks, returns a map with keys
|
|
||||||
{:state :args}, where :state is the new state and :args is a list of args
|
|
||||||
popped from the stacks. If there aren't enough args on the stacks, returns
|
|
||||||
:not-enough-args without popping anything."
|
|
||||||
[state stacks]
|
|
||||||
(loop [state state
|
|
||||||
stacks (reverse stacks)
|
|
||||||
args '()]
|
|
||||||
(if (empty? stacks)
|
|
||||||
{:state state :args args}
|
|
||||||
(let [current-stack (first stacks)]
|
|
||||||
(if (empty-stack? state current-stack)
|
|
||||||
:not-enough-args
|
|
||||||
(recur (pop-stack state current-stack)
|
|
||||||
(rest stacks)
|
|
||||||
(conj args (peek-stack state current-stack))))))))
|
|
@ -1,79 +0,0 @@
|
|||||||
(ns propeller.push.utils
|
|
||||||
(:require [clojure.set]
|
|
||||||
[propeller.push.core :as push]
|
|
||||||
[propeller.push.state :as state]))
|
|
||||||
|
|
||||||
(defmacro def-instruction
|
|
||||||
[instruction definition]
|
|
||||||
`(swap! push/instruction-table assoc '~instruction ~definition))
|
|
||||||
|
|
||||||
;; 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 (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)]
|
|
||||||
(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. [_add, _mult, _eq], automates the generation of all
|
|
||||||
;; possible combination instructions, which here would be :float_add, :float_mult,
|
|
||||||
;; :float_eq, :integer_add, :integer_mult, and :integer_eq, also transferring
|
|
||||||
;; and updating the generic function's stack-type metadata
|
|
||||||
(defmacro generate-instructions [stacks functions]
|
|
||||||
`(do ~@(for [stack stacks
|
|
||||||
func functions
|
|
||||||
:let [instruction-name (keyword (str (name stack) func))
|
|
||||||
metadata `(update-in (meta ~func) [:stacks] #(conj % ~stack))
|
|
||||||
new-func `(with-meta (partial ~func ~stack) ~metadata)]]
|
|
||||||
`(def-instruction ~instruction-name ~new-func))))
|
|
||||||
|
|
||||||
;; Given a set of stacks, returns all instructions that operate on those stacks
|
|
||||||
;; only. This won't include random or parenthesis-altering instructions unless
|
|
||||||
;; :random or :parentheses respectively are in the stacks set
|
|
||||||
(defn get-stack-instructions
|
|
||||||
[stacks]
|
|
||||||
(doseq [[instruction-name function] @push/instruction-table]
|
|
||||||
(assert
|
|
||||||
(:stacks (meta function))
|
|
||||||
(format "ERROR: Instruction %s does not have :stacks defined in metadata."
|
|
||||||
(name instruction-name))))
|
|
||||||
(for [[instruction-name function] @push/instruction-table
|
|
||||||
:when (clojure.set/subset? (:stacks (meta function)) stacks)]
|
|
||||||
instruction-name))
|
|
||||||
|
|
||||||
;; If a piece of data is a literal, return its corresponding stack name, e.g.
|
|
||||||
;; :integer. Otherwise, return nil"
|
|
||||||
(defn get-literal-type
|
|
||||||
[data]
|
|
||||||
(let [literals {:boolean (fn [thing] (or (true? thing) (false? thing)))
|
|
||||||
:char char?
|
|
||||||
:float float?
|
|
||||||
:integer integer?
|
|
||||||
:string string?
|
|
||||||
:vector_boolean (fn [thing] (and (vector? thing)
|
|
||||||
(or (true? (first thing))
|
|
||||||
(false? (first thing)))))
|
|
||||||
:vector_float (fn [thing] (and (vector? thing)
|
|
||||||
(float? (first thing))))
|
|
||||||
:vector_integer (fn [thing] (and (vector? thing)
|
|
||||||
(integer? (first thing))))
|
|
||||||
:vector_string (fn [thing] (and (vector? thing)
|
|
||||||
(string? (first thing))))
|
|
||||||
:generic-vector (fn [thing] (= [] thing))}]
|
|
||||||
(first (for [[stack function] literals
|
|
||||||
:when (function data)]
|
|
||||||
stack))))
|
|
||||||
|
|
||||||
;; Pretty-prints a Push state, for logging or debugging purposes
|
|
||||||
(defn print-state
|
|
||||||
[state]
|
|
||||||
(doseq [stack (keys state/empty-state)]
|
|
||||||
(printf "%-15s = " stack)
|
|
||||||
(prn (if (get state stack) (get state stack) '()))
|
|
||||||
(flush)))
|
|
@ -1,29 +0,0 @@
|
|||||||
(ns propeller.selection)
|
|
||||||
|
|
||||||
(defn tournament-selection
|
|
||||||
"Selects an individual from the population using a tournament."
|
|
||||||
[pop argmap]
|
|
||||||
(let [tournament-size (:tournament-size argmap)
|
|
||||||
tournament-set (take tournament-size (shuffle pop))]
|
|
||||||
(apply min-key :total-error tournament-set)))
|
|
||||||
|
|
||||||
(defn lexicase-selection
|
|
||||||
"Selects an individual from the population using lexicase selection."
|
|
||||||
[pop argmap]
|
|
||||||
(loop [survivors pop
|
|
||||||
cases (shuffle (range (count (:errors (first pop)))))]
|
|
||||||
(if (or (empty? cases)
|
|
||||||
(empty? (rest survivors)))
|
|
||||||
(rand-nth survivors)
|
|
||||||
(let [min-err-for-case (apply min (map #(nth % (first cases))
|
|
||||||
(map :errors survivors)))]
|
|
||||||
(recur (filter #(= (nth (:errors %) (first cases)) min-err-for-case)
|
|
||||||
survivors)
|
|
||||||
(rest cases))))))
|
|
||||||
|
|
||||||
(defn select-parent
|
|
||||||
"Selects a parent from the population using the specified method."
|
|
||||||
[pop argmap]
|
|
||||||
(case (:parent-selection argmap)
|
|
||||||
:tournament (tournament-selection pop argmap)
|
|
||||||
:lexicase (lexicase-selection pop argmap)))
|
|
@ -1,62 +0,0 @@
|
|||||||
(ns propeller.session
|
|
||||||
(:require [propeller.genome :as genome]
|
|
||||||
[propeller.gp :as gp]
|
|
||||||
[propeller.selection :as selection]
|
|
||||||
[propeller.variation :as variation]
|
|
||||||
[propeller.problems.simple-regression :as regression]
|
|
||||||
[propeller.problems.string-classification :as string-classif]
|
|
||||||
[propeller.push.core :as push]
|
|
||||||
[propeller.push.interpreter :as interpreter]
|
|
||||||
[propeller.push.state :as state]))
|
|
||||||
|
|
||||||
#_(interpreter/interpret-program
|
|
||||||
'(1 2 integer_add) state/empty-state 1000)
|
|
||||||
|
|
||||||
#_(interpreter/interpret-program
|
|
||||||
'(3 5 :integer_eq :exec_if (1 "yes") (2 "no"))
|
|
||||||
state/empty-state
|
|
||||||
1000)
|
|
||||||
|
|
||||||
#_(interpreter/interpret-program
|
|
||||||
'(in1 :string_reverse 1 :string_take "?" :string_eq :exec_if
|
|
||||||
(in1 " I am asking." :string_concat)
|
|
||||||
(in1 " I am saying." :string_concat))
|
|
||||||
(assoc state/empty-state :input {:in1 "Can you hear me?"})
|
|
||||||
1000)
|
|
||||||
|
|
||||||
#_(interpreter/interpret-program
|
|
||||||
'(in1 :string_reverse 1 :string_take "?" :string_eq :exec_if
|
|
||||||
(in1 " I am asking." :string_concat)
|
|
||||||
(in1 " I am saying." :string_concat))
|
|
||||||
(assoc state/empty-state :input {:in1 "I can hear you."})
|
|
||||||
1000)
|
|
||||||
|
|
||||||
#_(genome/plushy->push
|
|
||||||
(genome/make-random-plushy push/default-instructions 20))
|
|
||||||
|
|
||||||
#_(interpreter/interpret-program
|
|
||||||
(genome/plushy->push
|
|
||||||
(genome/make-random-plushy push/default-instructions 20))
|
|
||||||
(assoc state/empty-state :input {:in1 "I can hear you."})
|
|
||||||
1000)
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; Target function: f(x) = x^3 + x + 3
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
#_(gp/gp {:instructions push/default-instructions
|
|
||||||
:error-function regression/error-function
|
|
||||||
:max-generations 50
|
|
||||||
:population-size 200
|
|
||||||
:max-initial-plushy-size 50
|
|
||||||
:step-limit 100
|
|
||||||
:parent-selection :tournament
|
|
||||||
:tournament-size 5})
|
|
||||||
|
|
||||||
#_(gp/gp {:instructions push/default-instructions
|
|
||||||
:error-function string-classif/error-function
|
|
||||||
:max-generations 50
|
|
||||||
:population-size 200
|
|
||||||
:max-initial-plushy-size 50
|
|
||||||
:step-limit 100
|
|
||||||
:parent-selection :lexicase})
|
|
@ -1,33 +0,0 @@
|
|||||||
(ns propeller.tools.calculus)
|
|
||||||
|
|
||||||
(defonce ^:const dx 0.0001)
|
|
||||||
|
|
||||||
(defn deriv
|
|
||||||
"Returns the derivative of f evaluated at c. If called with only one argument,
|
|
||||||
it returns the derivative function."
|
|
||||||
([f c]
|
|
||||||
((deriv f) c))
|
|
||||||
([f]
|
|
||||||
(fn [x]
|
|
||||||
(/ (- (f (+ x dx)) (f x)) dx))))
|
|
||||||
|
|
||||||
(defn integrate
|
|
||||||
"Returns the definite integral of f over [a, b] using Simpson's method.
|
|
||||||
If called with only one argument (the function), returns the indefinite
|
|
||||||
integral, which takes as input a value x and (optionally) a constant c."
|
|
||||||
([f]
|
|
||||||
(fn this
|
|
||||||
([x] (this x 0))
|
|
||||||
([x c] (+ (integrate f 0 x) c))))
|
|
||||||
([f a b]
|
|
||||||
(let [n (/ 1 dx)
|
|
||||||
h (/ (- b a) n)]
|
|
||||||
(loop [i 1
|
|
||||||
sum1 (f (+ a (/ h 2)))
|
|
||||||
sum2 0]
|
|
||||||
(if (< i n)
|
|
||||||
(recur (inc i)
|
|
||||||
(+ sum1 (f (+ a (* h i) (/ h 2))))
|
|
||||||
(+ sum2 (f (+ a (* h i)))))
|
|
||||||
(* (/ h 6) (+ (f a) (f b) (* 4 sum1) (* 2 sum2))))))))
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
(ns propeller.tools.character)
|
|
||||||
|
|
||||||
(defn is-letter
|
|
||||||
"Returns true if the given character is a letter, A-Z or a-z."
|
|
||||||
[c]
|
|
||||||
(<= (int \A) (int c) (int \z)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn is-digit
|
|
||||||
"Returns true if the given character is a digit, 0-9."
|
|
||||||
[c]
|
|
||||||
(<= (int \0) (int c) (int \9)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn is-whitespace
|
|
||||||
"Returns true if the given character is whitespace (newline, space, tab)."
|
|
||||||
[c]
|
|
||||||
(contains? #{(int \newline) (int \tab) (int \space)} (int c)))
|
|
@ -1,59 +0,0 @@
|
|||||||
(ns propeller.tools.distributions
|
|
||||||
(:require [propeller.tools.calculus :as calculus]
|
|
||||||
[propeller.tools.math :as math]))
|
|
||||||
|
|
||||||
;; =============================================================================
|
|
||||||
;; NORMAL
|
|
||||||
;; =============================================================================
|
|
||||||
|
|
||||||
(defn- box-muller
|
|
||||||
"Given two uniformly distributed random variables (from 0 to 1), returns a
|
|
||||||
Standard Normal variable computed using the Box-Muller Transform."
|
|
||||||
[u1 u2]
|
|
||||||
(* (math/sqrt (* -2 (math/log u1)))
|
|
||||||
(math/cos (* 2 math/PI u2))))
|
|
||||||
|
|
||||||
(defn- normal-pdf
|
|
||||||
"Given a mean and standard deviation, returns the corresponding Normal
|
|
||||||
Probability Distribution Function."
|
|
||||||
[mu sigma]
|
|
||||||
(fn [x]
|
|
||||||
(* (/ 1 (* sigma (math/sqrt (* 2 math/PI))))
|
|
||||||
(math/exp (- (/ (math/pow (/ (- x mu) sigma) 2) 2))))))
|
|
||||||
|
|
||||||
(defn rand-norm
|
|
||||||
"Generates n Normally-distributed random variables with given mean and
|
|
||||||
standard deviation. If no parameters are provided, defaults to a
|
|
||||||
single random observation from a Standard Normal distribution.
|
|
||||||
Accepts an argument map with optional keys :n, :mu, and :sigma."
|
|
||||||
[{:keys [n mu sigma]
|
|
||||||
:or {n 1, mu 0, sigma 1}}]
|
|
||||||
(repeatedly n #(box-muller (rand) (rand))))
|
|
||||||
|
|
||||||
(defn pdf-norm
|
|
||||||
"Returns the value of the Normal Probability Distribution Function at a
|
|
||||||
particular value x. If no distributional parameters are provided, defaults to
|
|
||||||
the Standard Normal PDF.
|
|
||||||
Accepts an argument map with keys :x, and optionally :mu and :sigma."
|
|
||||||
[{:keys [x mu sigma]
|
|
||||||
:or {mu 0, sigma 1}}]
|
|
||||||
((normal-pdf mu sigma) x))
|
|
||||||
|
|
||||||
(defn cdf-norm
|
|
||||||
"Parameters: {:keys [x mu sigma]}
|
|
||||||
Returns the value of the Normal Cumulative Distribution Function at a
|
|
||||||
particular value x. If no distributional parameters are provided, defaults to
|
|
||||||
the Standard Normal CDF.
|
|
||||||
Accepts an argument map with keys :x, and optionally :mu and :sigma."
|
|
||||||
[{:keys [x mu sigma]
|
|
||||||
:or {mu 0, sigma 1}}]
|
|
||||||
(calculus/integrate (normal-pdf mu sigma) (- mu (* 6 sigma)) x))
|
|
||||||
|
|
||||||
(defn quant-norm
|
|
||||||
"For a given probability p, returns the corresponding value of the quantile
|
|
||||||
function (i.e. the inverse Cumulative Distribution Function). If no
|
|
||||||
distributional parameters are provided, defaults to Standard Normal quantiles.
|
|
||||||
Accepts an argument map with keys :p, and optionally :mu and :sigma."
|
|
||||||
[{:keys [p mu sigma]
|
|
||||||
:or {mu 0, sigma 1}}]
|
|
||||||
()) ; unfinished...
|
|
@ -1,104 +0,0 @@
|
|||||||
(ns propeller.tools.math)
|
|
||||||
|
|
||||||
(defonce PI #?(:clj Math/PI
|
|
||||||
:cljs js/Math.PI))
|
|
||||||
|
|
||||||
(defonce E #?(:clj Math/E
|
|
||||||
:cljs js/Math.PI))
|
|
||||||
|
|
||||||
(defn abs
|
|
||||||
"Returns the absolute value of a number."
|
|
||||||
[x]
|
|
||||||
(if (neg? x) (- x) x))
|
|
||||||
|
|
||||||
(defn approx= [x y epsilon]
|
|
||||||
"Returns true if the absolute difference between x and y is less than or
|
|
||||||
equal to some specified error level, epsilon."
|
|
||||||
(<= (abs (- y x)) epsilon))
|
|
||||||
|
|
||||||
(defn ceil
|
|
||||||
"Returns the smallest integer greater than or equal to x."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/ceil x)
|
|
||||||
:cljs (js/Math.ceil x)))
|
|
||||||
|
|
||||||
(defn cos
|
|
||||||
"Returns the cosine of an angle (specified in radians)."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/cos x)
|
|
||||||
:cljs (js/Math.cos x)))
|
|
||||||
|
|
||||||
(defn div
|
|
||||||
"Returns the result of floating point division between x and y."
|
|
||||||
[x y]
|
|
||||||
(double (/ x y)))
|
|
||||||
|
|
||||||
(defn exp
|
|
||||||
"Returns Euler's number (approx. 2.71) raised to the given power."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/exp x)
|
|
||||||
:cljs (js/Math.exp x)))
|
|
||||||
|
|
||||||
(defn floor
|
|
||||||
"Returns the largest integer less than or equal to x."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/floor x)
|
|
||||||
:cljs (js/Math.floor x)))
|
|
||||||
|
|
||||||
(defn log
|
|
||||||
"Returns the logarithm of x with the given base. If called with only one
|
|
||||||
argument, returns the natural logarithm (base e) of the given value."
|
|
||||||
([x base]
|
|
||||||
(/ (log x) (log base)))
|
|
||||||
([x]
|
|
||||||
#?(:clj (Math/log x)
|
|
||||||
:cljs (js/Math.log x))))
|
|
||||||
|
|
||||||
(defn pow
|
|
||||||
"Returns the value obtained by raising the first argument to the power of
|
|
||||||
the second argument."
|
|
||||||
[x n]
|
|
||||||
#?(:clj (Math/pow x n)
|
|
||||||
:cljs (js/Math.pow x n)))
|
|
||||||
|
|
||||||
(defn root
|
|
||||||
"Returns the root of x with base n."
|
|
||||||
[x n]
|
|
||||||
(pow x (/ 1 n)))
|
|
||||||
|
|
||||||
(defn round
|
|
||||||
"Returns the value of x rounded to the nearest integer."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/round x)
|
|
||||||
:cljs (js/Math.round x)))
|
|
||||||
|
|
||||||
(defn sign
|
|
||||||
"Returns the 1 if the argument is positive, -1 if the argument is negative,
|
|
||||||
and 0 if the argument is zero."
|
|
||||||
[x]
|
|
||||||
(cond (< x 0) -1
|
|
||||||
(> x 0) 1
|
|
||||||
:else 0))
|
|
||||||
|
|
||||||
(defn sin
|
|
||||||
"Returns the sine of an angle (specified in radians)."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/sin x)
|
|
||||||
:cljs (js/Math.sin x)))
|
|
||||||
|
|
||||||
(defn sqrt
|
|
||||||
"Returns the square root of the given value."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/sqrt x)
|
|
||||||
:cljs (js/Math.sqrt x)))
|
|
||||||
|
|
||||||
(defn square
|
|
||||||
"Returns the square of the given value."
|
|
||||||
[x]
|
|
||||||
(* x x))
|
|
||||||
|
|
||||||
(defn tan
|
|
||||||
"Returns the tangent of an angle (specified in radians)."
|
|
||||||
[x]
|
|
||||||
#?(:clj (Math/tan x)
|
|
||||||
:cljs (js/Math.tan x)))
|
|
@ -1,58 +0,0 @@
|
|||||||
(ns propeller.tools.metrics
|
|
||||||
(:require [propeller.tools.math :as math]))
|
|
||||||
|
|
||||||
(defn mean
|
|
||||||
"Returns the mean of a collection."
|
|
||||||
[coll]
|
|
||||||
(if (empty? coll) 0 (math/div (apply + coll) (count coll))))
|
|
||||||
|
|
||||||
(defn median
|
|
||||||
"Returns the median of a collection."
|
|
||||||
[coll]
|
|
||||||
(let [sorted-coll (sort coll)
|
|
||||||
count (count sorted-coll)
|
|
||||||
midpoint (quot count 2)]
|
|
||||||
(if (odd? count)
|
|
||||||
(nth sorted-coll midpoint)
|
|
||||||
(let [below (nth sorted-coll (dec midpoint))
|
|
||||||
above (nth sorted-coll midpoint)]
|
|
||||||
(mean [below above])))))
|
|
||||||
|
|
||||||
(defn hamming-distance
|
|
||||||
"Calculates the Hamming distance between two sequences, including strings."
|
|
||||||
[seq1 seq2]
|
|
||||||
(apply + (map #(if (= %1 %2) 0 1) seq1 seq2)))
|
|
||||||
|
|
||||||
(defn levenshtein-distance
|
|
||||||
"Levenshtein Distance - http://en.wikipedia.org/wiki/Levenshtein_distance
|
|
||||||
In Information Theory and Computer Science, the Levenshtein distance is a
|
|
||||||
metric for measuring the amount of difference between two sequences. This
|
|
||||||
is a functional implementation of the Levenshtein edit distance with as
|
|
||||||
little mutability as possible. Still maintains the O(nm) guarantee."
|
|
||||||
[a b & {p :predicate :or {p =}}]
|
|
||||||
(cond
|
|
||||||
(empty? a) (count b)
|
|
||||||
(empty? b) (count a)
|
|
||||||
:else (peek
|
|
||||||
(reduce
|
|
||||||
;; we use a simple reduction to convert the previous row into the
|
|
||||||
;; next-row using the compute-next-row which takes a current
|
|
||||||
;; element, the previous-row computed so far, and the predicate
|
|
||||||
;; to compare for equality
|
|
||||||
(fn [prev-row current-element]
|
|
||||||
(compute-next-row prev-row current-element b p))
|
|
||||||
;; we need to initialize the prev-row with the edit distance
|
|
||||||
;; between the various prefixes of b and the empty string
|
|
||||||
(range (inc (count b)))
|
|
||||||
a))))
|
|
||||||
|
|
||||||
(defn sequence-similarity
|
|
||||||
"Returns a number between 0 and 1, indicating how similar the sequences are
|
|
||||||
as a normalized, inverted Levenshtein distance, with 1 indicating identity
|
|
||||||
and 0 indicating no similarity."
|
|
||||||
[seq1 seq2]
|
|
||||||
(if (and (empty? seq1) (empty? seq2))
|
|
||||||
1
|
|
||||||
(let [distance (levenshtein-distance seq1 seq2)
|
|
||||||
max-distance (max (count seq1) (count seq2))]
|
|
||||||
(math/div (- max-distance distance) max-distance))))
|
|
@ -1,36 +0,0 @@
|
|||||||
(ns propeller.utils)
|
|
||||||
|
|
||||||
(defn get-vector-literal-type
|
|
||||||
"Returns the literal stack corresponding to some vector stack."
|
|
||||||
[vector-stack]
|
|
||||||
(keyword (clojure.string/replace (str vector-stack) ":vector_" "")))
|
|
||||||
|
|
||||||
(defn indexof
|
|
||||||
"Returns the first index of an element in a collection. If the element is not
|
|
||||||
present in the collection, returns -1."
|
|
||||||
[element coll]
|
|
||||||
(or (first (keep-indexed #(if (= element %2) %1) coll)) -1))
|
|
||||||
|
|
||||||
(defn not-lazy
|
|
||||||
"Returns lst if it is not a seq, or a non-lazy version of lst if it is."
|
|
||||||
[lst]
|
|
||||||
(if (seq? lst)
|
|
||||||
(apply list lst)
|
|
||||||
lst))
|
|
||||||
|
|
||||||
(defn ensure-list
|
|
||||||
"Returns a non-lazy list if passed a seq argument. Othwrwise, returns a list
|
|
||||||
containing the argument."
|
|
||||||
[thing]
|
|
||||||
(if (seq? thing)
|
|
||||||
(not-lazy thing)
|
|
||||||
(list thing)))
|
|
||||||
|
|
||||||
(defn random-instruction
|
|
||||||
"Returns a random instruction from a supplied pool of instructions, evaluating
|
|
||||||
ERC-producing functions to a constant literal."
|
|
||||||
[instructions]
|
|
||||||
(let [instruction (rand-nth instructions)]
|
|
||||||
(if (fn? instruction)
|
|
||||||
(instruction)
|
|
||||||
instruction)))
|
|
@ -1,52 +0,0 @@
|
|||||||
(ns propeller.variation
|
|
||||||
(:require [propeller.selection :as selection]
|
|
||||||
[propeller.utils :as utils]))
|
|
||||||
|
|
||||||
(defn crossover
|
|
||||||
"Crosses over two individuals using uniform crossover. Pads shorter one."
|
|
||||||
[plushy-a plushy-b]
|
|
||||||
(let [shorter (min-key count plushy-a plushy-b)
|
|
||||||
longer (if (= shorter plushy-a)
|
|
||||||
plushy-b
|
|
||||||
plushy-a)
|
|
||||||
length-diff (- (count longer) (count shorter))
|
|
||||||
shorter-padded (concat shorter (repeat length-diff :crossover-padding))]
|
|
||||||
(remove #(= % :crossover-padding)
|
|
||||||
(map #(if (< (rand) 0.5) %1 %2)
|
|
||||||
shorter-padded
|
|
||||||
longer))))
|
|
||||||
|
|
||||||
(defn uniform-addition
|
|
||||||
"Returns plushy with new instructions possibly added before or after each
|
|
||||||
existing instruction."
|
|
||||||
[plushy instructions umad-rate]
|
|
||||||
(apply concat
|
|
||||||
(map #(if (< (rand) umad-rate)
|
|
||||||
(shuffle [% (utils/random-instruction instructions)])
|
|
||||||
[%])
|
|
||||||
plushy)))
|
|
||||||
|
|
||||||
(defn uniform-deletion
|
|
||||||
"Randomly deletes instructions from plushy at some rate."
|
|
||||||
[plushy umad-rate]
|
|
||||||
(remove (fn [_] (< (rand)
|
|
||||||
(/ 1 (+ 1 (/ 1 umad-rate)))))
|
|
||||||
plushy))
|
|
||||||
|
|
||||||
(defn new-individual
|
|
||||||
"Returns a new individual produced by selection and variation of
|
|
||||||
individuals in the population."
|
|
||||||
[pop argmap]
|
|
||||||
{:plushy
|
|
||||||
(let [prob (rand)]
|
|
||||||
(cond
|
|
||||||
(< prob (:crossover (:variation argmap)))
|
|
||||||
(crossover (:plushy (selection/select-parent pop argmap))
|
|
||||||
(:plushy (selection/select-parent pop argmap)))
|
|
||||||
(< prob (+ (:crossover (:variation argmap))
|
|
||||||
(:umad (:variation argmap)) 2))
|
|
||||||
(uniform-deletion (uniform-addition (:plushy (selection/select-parent pop argmap))
|
|
||||||
(:instructions argmap)
|
|
||||||
(:umad-rate argmap))
|
|
||||||
(:umad-rate argmap))
|
|
||||||
:else (:plushy (selection/select-parent pop argmap))))})
|
|
Loading…
x
Reference in New Issue
Block a user