001 (ns nature.population-operators
002 "Functions that span or operate against entire populations"
003 (:require [nature.population-presets :as pp]
004 [bigml.sampling.simple :as bss]))
006 (defn keep-elite
007 "Find the top `number-to-keep` individuals in `population`, and increment their ages by 1"
008 [population number-to-keep]
009 (map #(update % :age inc) (take number-to-keep (sort-by :fitness-score #(> %1 %2) population))))
011 (defn weighted-selection-of-population
012 "Pick `total-retreived` individuals from `population` with a relative probability of
013 the individual's fitness score divided by the population's aggregate fitness score"
014 ([population total-retrieved]
015 (take total-retrieved (bss/sample population :weigh #(:fitness-score %) :replace true)))
016 ([population total-retrieved replace?]
017 (take total-retrieved (bss/sample population :weigh #(:fitness-score %) :replace replace?))))
019 (defn advance-generation
020 "Apply the functions in `binary-operator-set` until a sufficiently large population is built.
021 Then apply functions in `unary-operator-set` to the result.
022 Optionally, include a map of `settings` to guide overall behavior.
023 If the `:carry-over` setting is added, the elite member `n` of the prior generation will be advanced to the next generation."
024 ([population population-size binary-operator-set unary-operator-set]
025 (let [binary-pop (repeatedly population-size #(apply (rand-nth binary-operator-set) [(weighted-selection-of-population population 2)]))]
026 (map #(apply (rand-nth unary-operator-set) [%]) binary-pop)))
027 ([population population-size binary-operator-set unary-operator-set settings]
028 ;; Currently, :carry-over is the only optional behavior
029 (if (> (:carry-over settings) 0)
030 (concat (advance-generation population (- population-size (:carry-over settings)) binary-operator-set unary-operator-set) (keep-elite population (:carry-over settings)))
031 (advance-generation population population-size binary-operator-set unary-operator-set))))