Merge branch 'master' into boolean-multipliers

This commit is contained in:
Lee Spector 2023-08-30 14:47:58 -04:00
commit 1733ffd42e
3 changed files with 55 additions and 39 deletions

View File

@ -83,6 +83,12 @@ or
lein run -m propeller.problems.simple-regression :variation "{:umad 1.0}" lein run -m propeller.problems.simple-regression :variation "{:umad 1.0}"
``` ```
By default, Propeller will conduct many processes concurrently on multiple
cores using threads. If you want to disable this behavior (for example, during
debugging) then provide the argument `:single-thread-mode` with the value `true`.
Threads are not available in Javascript, so no processes are run concurrnetly
when Propeller is run in Clojurescript.
## CLJS Usage ## CLJS Usage

View File

@ -13,7 +13,8 @@
[propeller.push.instructions.polymorphic] [propeller.push.instructions.polymorphic]
[propeller.push.instructions.string] [propeller.push.instructions.string]
[propeller.push.instructions.vector] [propeller.push.instructions.vector]
[propeller.selection :as selection])) [propeller.selection :as selection]
[propeller.utils :as utils]))
(defn report (defn report
"Reports information for each generation." "Reports information for each generation."
@ -32,44 +33,25 @@
(println))) (println)))
(defn gp (defn gp
"Main GP loop. "Main GP loop."
On each iteration, it creates a population of random plushies using a mapper
function and genome/make-random-plushy function,
then it sorts the population by the total error using the error-function
and sort-by function. It then takes the best individual from the sorted population,
and if the parent selection is set to epsilon-lexicase, it adds the epsilons to the argmap.
The function then checks if the custom-report argument is set,
if so it calls that function passing the evaluated population,
current generation and argmap. If not, it calls the report function
passing the evaluated population, current generation and argmap.
Then, it checks if the total error of the best individual is less than or equal
to the solution-error-threshold or if the current generation is greater than or
equal to the max-generations specified. If either is true, the function
exits with the best individual or nil. If not, it creates new individuals
for the next generation using the variation/new-individual function and the
repeatedly function, and then continues to the next iteration of the loop. "
[{:keys [population-size max-generations error-function instructions [{:keys [population-size max-generations error-function instructions
max-initial-plushy-size solution-error-threshold mapper] max-initial-plushy-size solution-error-threshold]
:or {solution-error-threshold 0.0 :or {solution-error-threshold 0.0}
;; The `mapper` will perform a `map`-like operation to apply a function to every individual
;; in the population. The default is `map` but other options include `mapv`, or `pmap`.
mapper #?(:clj pmap :cljs map)}
:as argmap}] :as argmap}]
;; ;;
(prn {:starting-args (update (update argmap :error-function str) :instructions str)}) (prn {:starting-args (update (update argmap :error-function str) :instructions str)})
(println) (println)
;; ;;
(loop [generation 0 (loop [generation 0
population (mapper population (utils/pmapallv
(fn [_] {:plushy (genome/make-random-plushy instructions max-initial-plushy-size)}) (fn [_] {:plushy (genome/make-random-plushy instructions max-initial-plushy-size)})
(range population-size))] ;creates population of random plushys (range population-size)
argmap)] ;creates population of random plushys
(let [evaluated-pop (sort-by :total-error (let [evaluated-pop (sort-by :total-error
(mapper (utils/pmapallv
(partial error-function argmap (:training-data argmap)) (partial error-function argmap (:training-data argmap))
population)) ;population sorted by :total-error population ;population sorted by :total-error
argmap))
best-individual (first evaluated-pop) best-individual (first evaluated-pop)
argmap (if (= (:parent-selection argmap) :epsilon-lexicase) argmap (if (= (:parent-selection argmap) :epsilon-lexicase)
(assoc argmap :epsilons (selection/epsilon-list evaluated-pop)) (assoc argmap :epsilons (selection/epsilon-list evaluated-pop))
@ -84,17 +66,25 @@ repeatedly function, and then continues to the next iteration of the loop. "
(prn {:total-test-error (prn {:total-test-error
(:total-error (error-function argmap (:testing-data argmap) best-individual))}) (:total-error (error-function argmap (:testing-data argmap) best-individual))})
(when (:simplification? argmap) (when (:simplification? argmap)
(let [simplified-plushy (simplification/auto-simplify-plushy (:plushy best-individual) error-function argmap)] (let [simplified-plushy (simplification/auto-simplify-plushy
(prn {:total-test-error-simplified (:total-error (error-function argmap (:testing-data argmap) (hash-map :plushy simplified-plushy)))})))) (:plushy best-individual)
error-function argmap)]
(prn {:total-test-error-simplified
(:total-error (error-function argmap
(:testing-data argmap)
(hash-map :plushy simplified-plushy)))})))
#?(:clj (shutdown-agents)))
;; ;;
(>= generation max-generations) (>= generation max-generations)
nil #?(:clj (shutdown-agents))
;; ;;
:else (recur (inc generation) :else (recur (inc generation)
(if (:elitism argmap) (if (:elitism argmap)
(conj (repeatedly (dec population-size) (conj (utils/pmapallv (fn [_] (variation/new-individual evaluated-pop argmap))
#(variation/new-individual evaluated-pop argmap)) (range (dec population-size))
argmap)
(first evaluated-pop)) ;elitism maintains the most-fit individual (first evaluated-pop)) ;elitism maintains the most-fit individual
(repeatedly population-size (utils/pmapallv (fn [_] (variation/new-individual evaluated-pop argmap))
#(variation/new-individual evaluated-pop argmap)))))))) (range population-size)
argmap)))))))

View File

@ -1,6 +1,7 @@
(ns propeller.utils (ns propeller.utils
"Useful functions." "Useful functions."
(:require [clojure.zip :as zip])) (:require [clojure.zip :as zip]
[clojure.repl :as repl]))
(defn first-non-nil (defn first-non-nil
"Returns the first non-nil values from the collection, or returns `nil` if "Returns the first non-nil values from the collection, or returns `nil` if
@ -85,3 +86,22 @@
zip/path zip/path
count count
(max height)))))) (max height))))))
(defn pmapallv
"A utility for concurrent execution of a function on items in a collection.
In single-thread-mode this acts like mapv. Otherwise it acts like pmap but:
1) coll should be finite, 2) the returned sequence will not be lazy, and will
in fact be a vector, 3) calls to f may occur in any order, to maximize
multicore processor utilization, and 4) takes only one coll so far."
[f coll args]
#?(:clj (vec (if (:single-thread-mode args)
(doall (map f coll))
(let [agents (map #(agent % :error-handler
(fn [agnt except]
(repl/pst except 1000)
(System/exit 0)))
coll)]
(dorun (map #(send % f) agents))
(apply await agents)
(doall (map deref agents)))))
:cljs (mapv f coll)))