Moved macros and helpers to push/instructions.cljc

This commit is contained in:
Tom Helmuth 2021-11-02 12:37:06 -04:00
parent 0d58508bae
commit d8c97e29e8
33 changed files with 163 additions and 186 deletions

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,9 +3,8 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[clojure.pprint :as pprint]
[propeller.tools.math :as math]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]

View File

@ -3,7 +3,7 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]

View File

@ -3,9 +3,8 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -3,9 +3,8 @@
[propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.utils :as utils]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.tools.metrics :as metrics]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -2,9 +2,8 @@
(:require [propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.utils :as utils]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -2,9 +2,8 @@
(:require [propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [get-stack-instructions]]
[propeller.push.instructions :refer [get-stack-instructions]]
[propeller.utils :as utils]
[propeller.push.state :as state]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))

View File

@ -1,4 +1,9 @@
(ns propeller.push.instructions)
(ns propeller.push.instructions
(:require [clojure.set]
[propeller.push.state :as state]
[propeller.utils :as u]
#?(:cljs [goog.string :as gstring])
#?(:cljs [goog.string.format])))
;; PushGP instructions are represented as keywords, and stored in an atom. They
;; can be either constant literals or functions that take and return a Push state
@ -13,3 +18,117 @@
:exec_k 2
:exec_s 3
:exec_y 1})
#?(:clj
(def cls->type
{Boolean :boolean
Short :integer
Integer :integer
Long :integer
BigInteger :integer
Double :float
BigDecimal :float
Float :float
Character :char
String :string}))
#?(:cljs
(def pred->type
[[boolean? :boolean]
[int? :integer]
[float? :float]
[string? :string]
[char? :char]]))
(defn get-literal-type
"If a piece of data is a literal, return its corresponding stack name
e.g. `:integer`. Otherwise, return `nil`."
[data]
(or (when (vector? data)
(if (empty? data)
:generic-vector
(keyword (str "vector_" (name (get-literal-type (u/first-non-nil data)))))))
#?(:clj (cls->type (type data))
:cljs (loop [remaining pred->type]
(let [[pred d-type] (first remaining)]
(cond
(empty? remaining) nil
(pred data) d-type
:else (recur (rest remaining))))))))
(defn get-vector-literal-type
"Returns the literal stack corresponding to some vector stack."
[vector-stack]
(get state/vec-stacks vector-stack))
(defn def-instruction
"Defines a Push instruction as a keyword-function pair, and adds it to the
instruction table"
[instruction function]
(swap! instruction-table assoc instruction function))
(defn make-metadata
"Given a generic function, e.g. _dup, and a stack type to instantiate it for,
e.g. :char, returns the appropriate stack metadata for that function instance"
[function stack]
(->> (:stacks (meta function))
(replace {:elem (get-vector-literal-type stack)})
(cons stack)
set
(assoc-in (meta function) [:stacks])
(#(dissoc % :name))))
(defn generate-instructions
"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. For some vector
instructions, the placeholder :elem will be replaced with the stack of the
corresponding element type (e.g. for :vector_integer, with :integer)"
[stacks functions]
(doseq [stack stacks
func functions]
(let [instruction-name (keyword (str (name stack) (:name (meta func))))
metadata (make-metadata func stack)
new-func (with-meta (partial func stack) metadata)]
(def-instruction instruction-name new-func))))
(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.
If the function returns :ignore-instruction, then we will return the
initial state unchanged. This allows instructions to fail gracefully
without consuming stack values."
[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)]
(if (= result :ignore-instruction)
state
(state/push-to-stack new-state return-stack result))))))
(defn get-stack-instructions
"Given a set of stacks, returns all instructions that operate on those stacks
only. Won't include random instructions unless :random is in the set as well"
[stacks]
(doseq [[instruction-name function] @instruction-table]
(assert
(:stacks (meta function))
#?(:clj (format
"ERROR: Instruction %s does not have :stacks defined in metadata."
(name instruction-name))
:cljs (gstring/format
"ERROR: Instruction %s does not have :stacks defined in metadata."
(name instruction-name)))))
(for [[instruction-name function] @instruction-table
:when (clojure.set/subset? (:stacks (meta function)) stacks)]
instruction-name))

View File

@ -1,7 +1,6 @@
(ns propeller.push.instructions.bool
#?(:cljs (:require-macros [propeller.push.utils.macros :refer [def-instruction]]))
(:require [propeller.push.utils.helpers :refer [make-instruction]]
#?(:clj [propeller.push.utils.macros :refer [def-instruction]])))
(:require [propeller.push.instructions :refer [def-instruction
make-instruction]]))
;; =============================================================================
;; BOOLEAN Instructions

View File

@ -1,9 +1,8 @@
(ns propeller.push.instructions.character
#?(:cljs (:require-macros [propeller.push.utils.macros :refer [def-instruction]]))
(:require [propeller.push.state :as state]
[propeller.push.utils.helpers :refer [make-instruction]]
[propeller.tools.character :as char]
#?(:clj [propeller.push.utils.macros :refer [def-instruction]])))
[propeller.push.instructions :refer [def-instruction
make-instruction]]))
;; =============================================================================
;; CHAR Instructions

View File

@ -1,9 +1,8 @@
(ns propeller.push.instructions.code
#?(:cljs (:require-macros [propeller.push.utils.macros :refer [def-instruction]]))
(:require [propeller.utils :as utils]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [make-instruction]]
#?(:clj [propeller.push.utils.macros :refer [def-instruction]])))
[propeller.push.instructions :refer [def-instruction
make-instruction]]))
;; =============================================================================
;; CODE Instructions

View File

@ -1,10 +1,6 @@
(ns propeller.push.instructions.input-output
#?(:cljs (:require-macros
[propeller.push.utils.macros :refer [def-instruction
generate-instructions]]))
(:require [propeller.push.state :as state]
[propeller.push.utils.helpers :refer [make-instruction]]
[propeller.push.utils.macros :refer [def-instruction
[propeller.push.instructions :refer [def-instruction
generate-instructions]]))
;; =============================================================================

View File

@ -1,8 +1,8 @@
(ns propeller.push.instructions.numeric
(:require [propeller.push.utils.helpers :refer [make-instruction]]
[propeller.tools.math :as math]
[propeller.push.utils.macros :refer [def-instruction
generate-instructions]]))
(:require [propeller.tools.math :as math]
[propeller.push.instructions :refer [def-instruction
generate-instructions
make-instruction]]))
;; =============================================================================
;; FLOAT and INTEGER Instructions (polymorphic)

View File

@ -1,13 +1,10 @@
(ns propeller.push.instructions.polymorphic
#?(:cljs (:require-macros
[propeller.push.utils.macros :refer [def-instruction
generate-instructions]]))
(:require [propeller.utils :as utils]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [make-instruction]]
[propeller.push.limits :as limit]
#?(:clj [propeller.push.utils.macros :refer [def-instruction
generate-instructions]])))
[propeller.push.instructions :refer [def-instruction
generate-instructions
make-instruction]]))
;; =============================================================================
;; Polymorphic Instructions

View File

@ -1,10 +1,8 @@
(ns propeller.push.instructions.string
#?(:cljs (:require-macros
[propeller.push.utils.macros :refer [def-instruction]]))
(:require [clojure.string :as string]
[propeller.push.utils.helpers :refer [make-instruction]]
[propeller.push.state :as state]
#?(:clj [propeller.push.utils.macros :refer [def-instruction]])))
[propeller.push.instructions :refer [def-instruction
make-instruction]]))
;; =============================================================================
;; STRING Instructions

View File

@ -1,11 +1,10 @@
(ns propeller.push.instructions.vector
#?(:cljs (:require-macros [propeller.push.utils.macros :refer [generate-instructions]]))
(:require [clojure.string]
[propeller.utils :as utils]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [get-vector-literal-type
make-instruction]]
#?(:clj [propeller.push.utils.macros :refer [generate-instructions]])))
[propeller.push.instructions :refer [generate-instructions
make-instruction
get-vector-literal-type]]))
;; =============================================================================
;; VECTOR Instructions

View File

@ -1,15 +1,14 @@
(ns propeller.push.interpreter
(:require [propeller.push.instructions :as instructions]
[propeller.push.state :as state]
[propeller.push.instructions.input-output :as io]
[propeller.push.utils.helpers :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
literal-type (instructions/get-literal-type instruction)] ; nil for non-literals
(cond
;;
;; Recognize functional instruction or input instruction

View File

@ -1,86 +0,0 @@
(ns propeller.push.utils.helpers
(:require [clojure.set]
[propeller.push.instructions :as instructions]
[propeller.push.state :as state]
[propeller.utils :as u]
#?(:cljs [goog.string :as gstring])
#?(:cljs [goog.string.format])))
;; 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.
;;
;; If the function returns :ignore-instruction, then we will return the
;; initial state unchanged. This allows instructions to fail gracefully
;; without consuming stack values.
(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)]
(if (= result :ignore-instruction)
state
(state/push-to-stack new-state return-stack result))))))
;; Given a set of stacks, returns all instructions that operate on those stacks
;; only. Won't include random instructions unless :random is in the set as well
(defn get-stack-instructions
[stacks]
(doseq [[instruction-name function] @instructions/instruction-table]
(assert
(:stacks (meta function))
#?(:clj (format
"ERROR: Instruction %s does not have :stacks defined in metadata."
(name instruction-name))
:cljs (gstring/format
"ERROR: Instruction %s does not have :stacks defined in metadata."
(name instruction-name)))))
(for [[instruction-name function] @instructions/instruction-table
:when (clojure.set/subset? (:stacks (meta function)) stacks)]
instruction-name))
#?(:clj
(def cls->type
{Boolean :boolean
Short :integer
Integer :integer
Long :integer
BigInteger :integer
Double :float
BigDecimal :float
Float :float
Character :char
String :string}))
#?(:cljs
(def pred->type
[[boolean? :boolean]
[int? :integer]
[float? :float]
[string? :string]
[char? :char]]))
(defn get-literal-type
"If a piece of data is a literal, return its corresponding stack name
e.g. `:integer`. Otherwise, return `nil`."
[data]
(or (when (vector? data)
(if (empty? data)
:generic-vector
(keyword (str "vector_" (name (get-literal-type (u/first-non-nil data)))))))
#?(:clj (cls->type (type data))
:cljs (loop [remaining pred->type]
(let [[pred d-type] (first remaining)]
(cond
(empty? remaining) nil
(pred data) d-type
:else (recur (rest remaining))))))))
(defn get-vector-literal-type
"Returns the literal stack corresponding to some vector stack."
[vector-stack]
(get state/vec-stacks vector-stack))

View File

@ -1,36 +0,0 @@
(ns propeller.push.utils.macros
(:require [propeller.push.instructions :as instructions]
[propeller.push.utils.helpers :refer [get-vector-literal-type]]))
(defn def-instruction
"Defines a Push instruction as a keyword-function pair, and adds it to the
instruction table"
[instruction function]
(swap! instructions/instruction-table assoc instruction function))
(defn make-metadata
"Given a generic function, e.g. _dup, and a stack type to instantiate it for,
e.g. :char, returns the appropriate stack metadata for that function instance"
[function stack]
(->> (:stacks (meta function))
(replace {:elem (get-vector-literal-type stack)})
(cons stack)
set
(assoc-in (meta function) [:stacks])
(#(dissoc % :name))))
(defn generate-instructions
"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. For some vector
instructions, the placeholder :elem will be replaced with the stack of the
corresponding element type (e.g. for :vector_integer, with :integer)"
[stacks functions]
(doseq [stack stacks
func functions]
(let [instruction-name (keyword (str (name stack) (:name (meta func))))
metadata (make-metadata func stack)
new-func (with-meta (partial func stack) metadata)]
(def-instruction instruction-name new-func))))

View File

@ -7,8 +7,7 @@
[propeller.problems.string-classification :as string-classif]
[propeller.push.instructions :as instructions]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.push.utils.helpers :refer [get-stack-instructions]]))
[propeller.push.state :as state]))
#_(interpreter/interpret-program
'(1 2 :integer_add) state/empty-state 1000)
@ -36,7 +35,7 @@
; 1000)
;
;#_(genome/plushy->push
; (genome/make-random-plushy (get-stack-instructions #{:float :integer :exec :boolean}) 20))
; (genome/make-random-plushy (instructions/get-stack-instructions #{:float :integer :exec :boolean}) 20))
;
;#_(gp/gp {:instructions propeller.problems.software.number-io/instructions
; :error-function propeller.problems.software.number-io/error-function