From 5a8e5b6649cdb220ce7b4ba2291c860f8da91119 Mon Sep 17 00:00:00 2001 From: Conrad Barski Date: Wed, 27 May 2020 16:51:05 -0500 Subject: [PATCH] support spea2 with > 2 goals, add deps.edn for tools.cli support --- deps.edn | 1 + src/darwin/algorithms/spea2.clj | 24 +++++++++++++----------- src/darwin/evolution/pareto.clj | 30 +++++++++++++++++------------- 3 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 deps.edn diff --git a/deps.edn b/deps.edn new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/deps.edn @@ -0,0 +1 @@ +{} diff --git a/src/darwin/algorithms/spea2.clj b/src/darwin/algorithms/spea2.clj index 829309c..592734b 100644 --- a/src/darwin/algorithms/spea2.clj +++ b/src/darwin/algorithms/spea2.clj @@ -56,8 +56,10 @@ (defn coords-from-individual "Get the individuals coordinates in objective space as a vector." - [[k1 k2] i] - [(k1 i) (k2 i)]) + [ks i] + (mapv (fn [k] + (k i)) + ks)) (defn- calculate-density [distance] @@ -72,15 +74,15 @@ "Calculate the 'densities' for each individual in a population. The density is defined as 1 / (distance to kth-nearest neighbour + 2). k is taken as the sqrt of the population size. Assocs the densities into the individuals." - [[k1 k2] population] + [ks population] (let [k (Math/sqrt (count population)) ;; we extract the coordinates of each individual in objective space and build a kd-tree from them ;; so we can efficiently find the nearest neighbours. - coords (map (partial coords-from-individual [k1 k2]) population) + coords (map (partial coords-from-individual ks) population) tree (kdtree/build-tree coords)] (map #(assoc % :spea2-density - (calculate-density - (kth-nearest-distance tree k (coords-from-individual [k1 k2] %)))) + (calculate-density + (kth-nearest-distance tree k (coords-from-individual ks %)))) population))) (defn calculate-fitnesses @@ -108,7 +110,7 @@ (let [tree (:tree tree-and-archive) oversized-archive (:archive tree-and-archive) measured-archive (map #(assoc % :spea2-distances - (k-nearest-distances tree comparison-depth (coords-from-individual goals %))) + (k-nearest-distances tree comparison-depth (coords-from-individual goals %))) oversized-archive) ;; this next step relies on the sort being done lexicographically on the distance arrays. ;; That `sort-by` does this isn't mentioned in the docstring, but is explicitly stated @@ -134,8 +136,8 @@ (let [coords (map (partial coords-from-individual goals) oversized-archive) tree (kdtree/build-tree coords) thinned-tree-and-archive (nth (iterate - (partial remove-one-item goals comparison-depth) - {:tree tree :archive oversized-archive}) + (partial remove-one-item goals comparison-depth) + {:tree tree :archive oversized-archive}) (- (count oversized-archive) target-size))] (:archive thinned-tree-and-archive))) @@ -202,8 +204,8 @@ :or {comparison-depth 5 deduplicate false}} config] {:elite-selector (fn [rabble elite] - (make-new-archive goals deduplicate comparison-depth archive-size rabble elite)) + (make-new-archive goals deduplicate comparison-depth archive-size rabble elite)) :mating-pool-selector (fn [_ elite] elite) :reproduction-config {:selector (partial selection/tournament-selector 2 :spea2-fitness) :unary-ops unary-ops - :binary-ops binary-ops}})) \ No newline at end of file + :binary-ops binary-ops}})) diff --git a/src/darwin/evolution/pareto.clj b/src/darwin/evolution/pareto.clj index cf047c8..f9eb355 100644 --- a/src/darwin/evolution/pareto.clj +++ b/src/darwin/evolution/pareto.clj @@ -15,32 +15,36 @@ (defn dominates "Does i1 Pareto-dominate i2, as judged by the values associated with the given keys, k1 and k2. Lower scores are considered better." - [[k1 k2] i1 i2] - (or (and (<= (k1 i1) (k1 i2)) (< (k2 i1) (k2 i2))) - (and (<= (k2 i1) (k2 i2)) (< (k1 i1) (k1 i2))))) + [ks i1 i2] + (and (every? (fn [k] + (<= (k i1) (k i2))) + ks) + (some (fn [k] + (< (k i1) (k i2))) + ks))) (defn dominated-set "Returns the individuals that i dominates wrt k1 and k2. Note that it doesn't return a set, rather a list (which I guess is a multiset) but `dominated-multiset` is too much of a mouthful." - [[k1 k2] individuals i] - (filter #(dominates [k1 k2] i %) individuals)) + [ks individuals i] + (filter #(dominates ks i %) individuals)) (defn dominator-set "Returns the individuals that dominate i wrt k1 and k2. As above, it doesn't return a set." - [[k1 k2] individuals i] - (filter #(dominates [k1 k2] % i) individuals)) + [ks individuals i] + (filter #(dominates ks % i) individuals)) (defn dominated-count "Count how many individuals i dominates wrt to k1 and k2." - [[k1 k2] individuals i] - (count (dominated-set [k1 k2] individuals i))) + [ks individuals i] + (count (dominated-set ks individuals i))) (defn- individual-dominated? "Is an individual i dominated by any of the given individuals wrt the keys k1 and k2?" - [[k1 k2] individuals i] - (reduce #(or %1 %2) (map #(dominates [k1 k2] % i) individuals))) + [ks individuals i] + (reduce #(or %1 %2) (map #(dominates ks % i) individuals))) (defn non-dominated-individuals "Returns the individuals that are non-dominated with respect to k1 and k2." - [[k1 k2] individuals] - (filter #(not (individual-dominated? [k1 k2] individuals %)) individuals)) \ No newline at end of file + [ks individuals] + (filter #(not (individual-dominated? ks individuals %)) individuals))