hyperselection implementation and tests

really hacky implementation that only works with UMAD for now.
This commit is contained in:
Ryan Boldi 2022-06-05 10:09:05 -04:00
parent 17f2676a47
commit b591403fe8
4 changed files with 89 additions and 24 deletions

View File

@ -5,6 +5,7 @@
[propeller.simplification :as simplification]
[propeller.variation :as variation]
[propeller.downsample :as downsample]
[propeller.hyperselection :as hyperselection]
[propeller.push.instructions.bool]
[propeller.push.instructions.character]
[propeller.push.instructions.code]
@ -93,12 +94,13 @@
nil
;;
:else (recur (inc generation)
(let [reindexed-pop (hyperselection/reindex-pop ds-evaluated-pop)]
(if (:elitism argmap)
(conj (repeatedly (dec population-size)
#(variation/new-individual ds-evaluated-pop argmap))
(first ds-evaluated-pop))
(repeatedly population-size
#(variation/new-individual ds-evaluated-pop argmap)))
(hyperselection/log-hyperselection-and-ret (conj (repeatedly (dec population-size)
#(variation/new-individual reindexed-pop argmap))
(first reindexed-pop)))
(hyperselection/log-hyperselection-and-ret (repeatedly population-size ;need to count occurance of each parent, and reset IDs
#(variation/new-individual reindexed-pop argmap)))))
(if (= (:parent-selection argmap) :ds-lexicase)
(if (zero? (mod generation ds-parent-gens))
(downsample/update-case-distances rep-evaluated-pop indexed-training-data indexed-training-data) ; update distances every ds-parent-gens generations

View File

@ -0,0 +1,35 @@
(ns propeller.hyperselection)
(defn sum-list-map-indices
"sums a list of maps that have the :index property's index multiplicity"
[list-of-maps]
(->> list-of-maps
(map #(:index %))
frequencies))
(defn ordered-freqs
"takes a map from indices to frequencies, and returns a sorted list of the frequences is descencing order"
[freqs]
(->> freqs
vals
(sort >)))
(defn normalize-list-by-popsize [popsize lst]
(map #(double (/ % popsize)) lst))
(defn hyperselection-track
"outputs a normalized list of the hyperselection proportion for each parent"
[new-pop]
(->> new-pop
sum-list-map-indices
ordered-freqs
(normalize-list-by-popsize (count new-pop))))
(defn log-hyperselection-and-ret [new-pop]
(prn {:hyperselection (hyperselection-track new-pop)})
new-pop)
(defn reindex-pop
"assigns each member of the population a unique index before selection to track hyperselection"
[pop]
(map (fn [indiv index] (assoc indiv :index index)) pop (range (count pop))))

View File

@ -130,6 +130,8 @@
"Returns a new individual produced by selection and variation of
individuals in the population."
[pop argmap]
(let [umad-parent (selection/select-parent pop argmap)
parent-ind (:index umad-parent)] ;this is a hack to log hyperselection, only works for umad
{:plushy
(let [r (rand)
op (loop [accum 0.0
@ -153,7 +155,7 @@
(:plushy (selection/select-parent pop argmap)))
;
:umad
(-> (:plushy (selection/select-parent pop argmap))
(-> (:plushy umad-parent)
(uniform-addition (:instructions argmap) (:umad-rate argmap))
(uniform-deletion (:umad-rate argmap)))
;
@ -216,4 +218,5 @@
:else
(throw #?(:clj (Exception. (str "No match in new-individual for " op))
:cljs (js/Error
(str "No match in new-individual for " op))))))})
(str "No match in new-individual for " op))))))
:index parent-ind}))

View File

@ -2,7 +2,8 @@
(:require [clojure.test :as t]
[propeller.utils :as u]
[propeller.simplification :as s]
[propeller.downsample :as ds]))
[propeller.downsample :as ds]
[propeller.hyperselection :as hs]))
(t/deftest first-non-nil-test
(t/is (= 1 (u/first-non-nil '(1 2 3))))
@ -159,10 +160,34 @@
(t/deftest case-maxmin-test
(t/testing "case-maxmin selects correct downsample"
(t/is (let [selected (ds/select-downsample-maxmin '({:input1 [0] :output1 [10] :index 0 :distances [0 5 0 0 0]}
(t/is (let [selected (ds/select-downsample-maxmin
'({:input1 [0] :output1 [10] :index 0 :distances [0 5 0 0 0]}
{:input1 [1] :output1 [11] :index 1 :distances [0 5 0 0 0]}
{:input1 [2] :output1 [12] :index 2 :distances [5 5 5 5 5]}
{:input1 [3] :output1 [13] :index 3 :distances [0 5 0 0 0]}
{:input1 [4] :output1 [14] :index 4 :distances [0 5 0 0 0]})
{:downsample-rate 0.4 :case-t-size 5})]
(or (= (:index (first selected)) 1) (= (:index (second selected)) 1))))))
(t/deftest hyperselection-test
(let [parents1 '({:blah 3 :index 1} {:blah 3 :index 1}
{:blah 3 :index 1} {:blah 3 :index 2})
parents2 '({:plushy 2 :index 0} {:blah 3 :index 2}
{:blah 3 :index 3} {:index 4})
emptyparents '({:blah 1} {:blah 1} {:blah 1})]
(t/testing "sum-list-map-indices function works correctly"
(t/is (= {1 3, 2 1} (hs/sum-list-map-indices parents1)))
(t/is (= {0 1, 2 1, 3 1, 4 1} (hs/sum-list-map-indices parents2))))
(t/testing "ordered-freqs function works correctly"
(t/is (= '(3 1) (hs/ordered-freqs (hs/sum-list-map-indices parents1))))
(t/is (= '(1 1 1 1) (hs/ordered-freqs (hs/sum-list-map-indices parents2)))))
(t/testing "hyperselection-track works correctly"
(t/is (= '(0.75 0.25) (hs/hyperselection-track parents1)))
(t/is (= '(0.25 0.25 0.25 0.25) (hs/hyperselection-track parents2))))
(t/testing "reindex-pop works correctly"
(t/is (= '({:blah 3 :index 0} {:blah 3 :index 1}
{:blah 3 :index 2} {:blah 3 :index 3}) (hs/reindex-pop parents1)))
(t/is (= '({:plushy 2 :index 0} {:blah 3 :index 1}
{:blah 3 :index 2} {:index 3}) (hs/reindex-pop parents2)))
(t/is (= '({:blah 1 :index 0} {:blah 1 :index 1} {:blah 1 :index 2}) (hs/reindex-pop emptyparents))))))