From 8aede1ee1f693432ff24baf36e93d59d2bccdcfb Mon Sep 17 00:00:00 2001 From: Berke Tezergil Date: Mon, 6 Nov 2023 20:19:00 +0300 Subject: [PATCH 1/3] Added sequence generation option by giving a generator method --- src/nature/core.cljc | 7 +++++-- src/nature/initialization_operators.cljc | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/nature/core.cljc b/src/nature/core.cljc index 4730906..7ffb988 100644 --- a/src/nature/core.cljc +++ b/src/nature/core.cljc @@ -26,8 +26,11 @@ (fn? fitness-function))]} (let [solutions (max 1 (:solutions options)) carry-over (max 1 (:carry-over options)) - monitors (:monitors options)] - (loop [population (io/build-population population-size allele-set genome-length fitness-function) + monitors (:monitors options) + sequence-generator-function (:generator options)] + (loop [population (if (some? sequence-generator-function) + (io/build-population population-size sequence-generator-function fitness-function) + (io/build-population population-size allele-set genome-length fitness-function)) current-generation 0] (when monitors (monitors/apply-monitors monitors population current-generation)) (if (>= current-generation generations) diff --git a/src/nature/initialization_operators.cljc b/src/nature/initialization_operators.cljc index f83fa1b..13fe437 100644 --- a/src/nature/initialization_operators.cljc +++ b/src/nature/initialization_operators.cljc @@ -32,6 +32,10 @@ :fitness-score (fitness-function genetic-sequence)))) (defn build-population - "Build `population-size` individuals by invoking `build-individual` on random, conforming genetic sequences." - [population-size alleles sequence-length fitness-function] - (repeatedly population-size #(build-individual (generate-sequence alleles sequence-length) fitness-function))) + "Build `population-size` individuals by invoking `build-individual` on random, conforming genetic sequences. + If given a sequence generator function, uses this function to generate valid sequences instead." + ([population-size sequence-generator-function fitness-function] + (repeatedly population-size #(build-individual (sequence-generator-function) fitness-function))) + + ([population-size alleles sequence-length fitness-function] + (repeatedly population-size #(build-individual (generate-sequence alleles sequence-length) fitness-function)))) From ee2ed7ca3fccf74e91fc28bb8d710c46a1fd24d3 Mon Sep 17 00:00:00 2001 From: Berke Tezergil Date: Mon, 22 Jan 2024 12:36:48 +0300 Subject: [PATCH 2/3] Added new project qualifier to push my version to clojars --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 6a29e9a..b1744c4 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject nature "1.0.0" +(defproject org.clojars.btezergil/nature "1.0.0" :description "A simple genetic algorithms library for Clojure(Script)" :url "https://github.com/nnichols/nature" :license {:name "Eclipse Public License" From e79d5f892b16d190a61f6d45c912070f7321be20 Mon Sep 17 00:00:00 2001 From: Berke Tezergil Date: Sun, 28 Jan 2024 22:10:31 +0300 Subject: [PATCH 3/3] Refactored the evolve method, extracted the common part into a function and removed the unused function parameters for sequence generation functon case --- src/nature/core.cljc | 47 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/nature/core.cljc b/src/nature/core.cljc index 7ffb988..9890cf8 100644 --- a/src/nature/core.cljc +++ b/src/nature/core.cljc @@ -3,6 +3,19 @@ [nature.population-operators :as po] [nature.monitors :as monitors])) +(defn evolution-loop + "Evolution loop that is used commonly by sequence generator function and random generation from allele set." + [initial-population generations population-size binary-operators unary-operators options] + (let [solutions (max 1 (:solutions options)) + carry-over (max 1 (:carry-over options)) + monitors (:monitors options)] + (loop [population initial-population + current-generation 0] + (when monitors (monitors/apply-monitors monitors population current-generation)) + (if (>= current-generation generations) + (take solutions (sort-by :fitness-score #(> %1 %2) population)) + (recur (po/advance-generation population population-size binary-operators unary-operators {:carry-over carry-over}) (inc current-generation)))))) + (defn evolve "Create and evolve a population under the specified conditions until a termination criteria is reached `allele-set` is a collection of legal genome values @@ -13,10 +26,10 @@ `binary-operators` is a collection of partial functions accepting and returning 1 or more individuals `unary-operators` is a collection of partial functions accepting and returning exactly 1 individual `options` an optional map of pre-specified keywords to values that further tune the behavior of nature. - Current examples follow: - `:carry-over` an integer representing the top n individuals to be carried over between each generation. Default is 1 - `:solutions` an integer representing the top n individuals to return after evolution completes. Default is 1 - `:monitors` a sequence of functions, assumed to be side-effectful, to be executed against `population` and `current-genration` for run-time stats. Default is nil" + Current examples follow: + `:carry-over` an integer representing the top n individuals to be carried over between each generation. Default is 1 + `:solutions` an integer representing the top n individuals to return after evolution completes. Default is 1 + `:monitors` a sequence of functions, assumed to be side-effectful, to be executed against `population` and `current-genration` for run-time stats. Default is nil" ([allele-set genome-length population-size generations fitness-function binary-operators unary-operators] (evolve allele-set genome-length population-size generations fitness-function binary-operators unary-operators {:solutions 1, :carry-over 1})) @@ -24,15 +37,17 @@ {:pre [(and (every? coll? [allele-set binary-operators unary-operators]) (every? int? [genome-length population-size generations]) (fn? fitness-function))]} - (let [solutions (max 1 (:solutions options)) - carry-over (max 1 (:carry-over options)) - monitors (:monitors options) - sequence-generator-function (:generator options)] - (loop [population (if (some? sequence-generator-function) - (io/build-population population-size sequence-generator-function fitness-function) - (io/build-population population-size allele-set genome-length fitness-function)) - current-generation 0] - (when monitors (monitors/apply-monitors monitors population current-generation)) - (if (>= current-generation generations) - (take solutions (sort-by :fitness-score #(> %1 %2) population)) - (recur (po/advance-generation population population-size binary-operators unary-operators {:carry-over carry-over}) (inc current-generation))))))) + (evolution-loop (io/build-population population-size allele-set genome-length fitness-function) population-size generations binary-operators unary-operators options))) + +(defn evolve-with-sequence-generator + "Same with evolve method, but takes a sequence generator function instead of an allele set and genome length. + This method uses the sequence generator function to generate sequences for the initial population." + ([generator-function population-size generations fitness-function binary-operators unary-operators] + (evolve-with-sequence-generator generator-function population-size generations fitness-function binary-operators unary-operators {:solutions 1, :carry-over 1})) + + ([generator-function population-size generations fitness-function binary-operators unary-operators options] + {:pre [(and (every? coll? [binary-operators unary-operators]) + (every? int? [population-size generations]) + (every? fn? [generator-function fitness-function]))]} + (evolution-loop (io/build-population population-size generator-function fitness-function) population-size generations binary-operators unary-operators options))) +