|
| 1 | +;; This file implements a the symbolic regression problem described: |
| 2 | +;; |
| 3 | +;; Saini, A.K., Spector, L. (2020). Using Modularity Metrics as |
| 4 | +;; Design Features to Guide Evolution in Genetic Programming. |
| 5 | +;; In: Banzhaf, W., Goodman, E., Sheneman, L., Trujillo, L., |
| 6 | +;; Worzel, B. (eds) Genetic Programming Theory and Practice XVII. |
| 7 | +;; Genetic and Evolutionary Computation. Springer, Cham. |
| 8 | +;; https://doi.org/10.1007/978-3-030-39958-0_9 |
| 9 | + |
| 10 | +(ns propeller.problems.regression.saini-modular |
| 11 | + (:require [propeller.genome :as genome] |
| 12 | + [propeller.push.interpreter :as interpreter] |
| 13 | + [propeller.push.state :as state] |
| 14 | + [propeller.tools.math :as math] |
| 15 | + [propeller.gp :as gp] |
| 16 | + #?(:cljs [cljs.reader :refer [read-string]]))) |
| 17 | + |
| 18 | +(defn- target-function |
| 19 | + "Saini modular = (x^3 + 1)^3 + 1" |
| 20 | + [x] |
| 21 | + (+ (* (+ (* x x x) 1) |
| 22 | + (+ (* x x x) 1) |
| 23 | + (+ (* x x x) 1)) |
| 24 | + 1)) |
| 25 | + |
| 26 | +(def train-and-test-data |
| 27 | + (let [train-inputs (range -4.0 4.0 0.1) |
| 28 | + test-inputs (range -4.0 4.0 0.05)] |
| 29 | + {:train (map (fn [x] {:input1 (vector x) :output1 (vector (target-function x))}) train-inputs) |
| 30 | + :test (map (fn [x] {:input1 (vector x) :output1 (vector (target-function x))}) test-inputs)})) |
| 31 | + |
| 32 | +(def instructions |
| 33 | + (list :in1 |
| 34 | + :float_add |
| 35 | + :float_subtract |
| 36 | + :float_mult |
| 37 | + :float_div |
| 38 | + :float_sin |
| 39 | + :float_cos |
| 40 | + :float_tan |
| 41 | + 0.0 |
| 42 | + 1.0)) |
| 43 | + |
| 44 | +(defn error-function |
| 45 | + "Finds the behaviors and errors of an individual. The error is the absolute |
| 46 | + deviation between the target output value and the program's selected behavior, |
| 47 | + or 1000000 if no behavior is produced. The behavior is here defined as the |
| 48 | + final top item on the FLOAT stack." |
| 49 | + ([argmap data individual] |
| 50 | + (let [program (genome/plushy->push (:plushy individual) argmap) |
| 51 | + inputs (map (fn [x] (first (:input1 x))) data) |
| 52 | + correct-outputs (map (fn [x] (first (:output1 x))) data) |
| 53 | + outputs (map (fn [input] |
| 54 | + (state/peek-stack |
| 55 | + (interpreter/interpret-program |
| 56 | + program |
| 57 | + (assoc state/empty-state :input {:in1 input}) |
| 58 | + (:step-limit argmap)) |
| 59 | + :float)) |
| 60 | + inputs) |
| 61 | + errors (map (fn [correct-output output] |
| 62 | + (if (= output :no-stack-item) |
| 63 | + 1000000 |
| 64 | + (math/abs (- correct-output output)))) |
| 65 | + correct-outputs |
| 66 | + outputs)] |
| 67 | + (assoc individual |
| 68 | + :behaviors outputs |
| 69 | + :errors errors |
| 70 | + :total-error #?(:clj (apply +' errors) |
| 71 | + :cljs (apply + errors)))))) |
| 72 | + |
| 73 | +(defn -main |
| 74 | + "Runs the top-level genetic programming function, giving it a map of |
| 75 | + arguments with defaults that can be overridden from the command line |
| 76 | + or through a passed map." |
| 77 | + [& args] |
| 78 | + (gp/gp |
| 79 | + (merge |
| 80 | + {:instructions instructions |
| 81 | + :error-function error-function |
| 82 | + :training-data (:train train-and-test-data) |
| 83 | + :testing-data (:test train-and-test-data) |
| 84 | + :downsample? false |
| 85 | + :solution-error-threshold 0.1 |
| 86 | + :max-generations 300 |
| 87 | + :population-size 1000 |
| 88 | + :max-initial-plushy-size 50 |
| 89 | + :step-limit 100 |
| 90 | + :parent-selection :epsilon-lexicase |
| 91 | + :umad-rate 0.05 |
| 92 | + :variation {:umad 1.0} |
| 93 | + :simplification? true} |
| 94 | + (apply hash-map (map #(if (string? %) (read-string %) %) args))))) |
0 commit comments