Skip to content

Commit

Permalink
Schema (using Malli) (#9)
Browse files Browse the repository at this point in the history
* schema for input args

* refac

* schema for input args

* schema for GA pheno population

* schema for GA pheno population

* schema for GA pheno population

* schema for GA pheno mutation

* schema for GA phenos

* schema

* schema failure case test

* more schemas

* more schemas

* CLI args schema

* eval schema / refac to ns

* apply mods schema -> somewhat of a perf hit can be felt now

* WIP failing to de-intrument all schema on command

* log points

* ns reorg

* test style, coverage update

* mods style, bias fewer

* more schema
  • Loading branch information
ogeagla authored Feb 16, 2024
1 parent e94f071 commit c3d83a2
Show file tree
Hide file tree
Showing 23 changed files with 371 additions and 74 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ An example of using the GUI:

Coverage looks like this if you run `lein cloverage`:

![test_coverage_2024-02-15_10-13.png](screenshots%2Ftest_coverage_2024-02-15_10-13.png)
![test_coverage_2024-02-16_11-15.png](screenshots%2Ftest_coverage_2024-02-16_11-15.png)

## How It Works

Expand Down
Binary file modified nomis-ns-graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/core.async "1.6.681"]
[metosin/malli "0.14.0"]

[org.clojure/tools.cli "1.0.219"]

Expand Down
Binary file added screenshots/test_coverage_2024-02-16_11-15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/closyr/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
(:require
[clojure.string :as str]
[clojure.tools.cli :as cli]
[closyr.dataset.csv :as input-csv]
[closyr.log :as log]
[closyr.util.csv :as input-csv]
[closyr.util.log :as log]
[closyr.symbolic-regression :as symreg])
(:import
(java.io
Expand Down
4 changes: 2 additions & 2 deletions src/closyr/ga.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
(ns closyr.ga
(:refer-clojure :exclude [rand rand-int rand-nth shuffle])
(:require
[closyr.log :as log]
[closyr.dataset.prng :refer :all]))
[closyr.util.log :as log]
[closyr.util.prng :refer :all]))


(set! *warn-on-reflection* true)
Expand Down
15 changes: 11 additions & 4 deletions src/closyr/ops.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
(:require
[clojure.core.async :as async :refer [go go-loop timeout <!! >!! <! >! chan put! take! alts!! alts! close!]]
[clojure.string :as str]
[closyr.dataset.prng :refer :all]
[closyr.log :as log]
[closyr.util.prng :refer :all]
[closyr.util.log :as log]
[closyr.ops.common :as ops-common]
[closyr.ops.eval :as ops-eval]
[closyr.ops.modify :as ops-modify])
[closyr.ops.modify :as ops-modify]
[closyr.util.spec :as specs])
(:import
(java.text
DecimalFormat)
Expand Down Expand Up @@ -74,7 +75,9 @@
(and (number? n) (Double/isNaN n))))


(defn- compute-residual
(defn compute-residual
"Compute the residual (difference) between 2 number (y-values)"
{:malli/schema [:=> [:cat number? number?] number?]}
[expected actual]
(let [res (if (not-finite? actual)
max-resid
Expand Down Expand Up @@ -281,6 +284,7 @@

(reset! test-timer* (Date.))
(log/info i "-step pop size: " pop-size
" points: " (count input-ys-vec)
" max leafs: " max-leafs
" took secs: " took-s
" phenos/s: " (Math/round ^double (/ (* pop-size *log-steps*) took-s))
Expand All @@ -303,3 +307,6 @@
:best-p95-score (:score best-p95-v)
:best-p90-score (:score best-p90-v)}))))
(reset! sim-stats* {}))


(specs/instrument-all!)
9 changes: 7 additions & 2 deletions src/closyr/ops/common.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
(:refer-clojure :exclude [rand rand-int rand-nth shuffle])
(:require
[clojure.core.async :as async :refer [go go-loop timeout <!! >!! <! >! chan put! take! alts!! alt!! close!]]
[closyr.dataset.prng :refer :all]
[closyr.log :as log])
[closyr.util.prng :refer :all]
[closyr.util.log :as log]
[closyr.util.spec :as specs])
(:import
(java.util
Date
Expand Down Expand Up @@ -257,6 +258,7 @@

(defn extend-xs
"Add extra xs on either side of the provided range"
{:malli/schema [:=> [:cat [:sequential number?]] map?]}
[input-xs-vec]
(let [x-min (first input-xs-vec)
x-max (last input-xs-vec)
Expand Down Expand Up @@ -284,3 +286,6 @@
:x-head-list x-head-list
:x-tail x-tail
:x-tail-list x-tail-list}))


(specs/instrument-all!)
12 changes: 9 additions & 3 deletions src/closyr/ops/eval.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(ns closyr.ops.eval
(:require
[closyr.log :as log]
[closyr.ops.common :as ops-common])
[closyr.util.log :as log]
[closyr.ops.common :as ops-common]
[closyr.util.spec :as specs])
(:import
(org.matheclipse.core.eval
EvalControlledCallable
Expand Down Expand Up @@ -38,6 +39,7 @@

(defn ^IExpr eval-phenotype-on-expr-args
"Eval an expr at every point in the args"
{:malli/schema [:=> [:cat #'specs/GAPhenotype some?] any?]}
[{^IAST expr :expr ^ISymbol x-sym :sym ^ExprEvaluator util :util p-id :id :as pheno}
^"[Lorg.matheclipse.core.interfaces.IExpr;" expr-args]
(try
Expand Down Expand Up @@ -90,8 +92,9 @@

(defn eval-vec-pheno
"Evaluate a phenotype's expr on input xs/ys vecs"
{:malli/schema [:=> [:cat #'specs/GAPhenotype #'specs/SolverEvalArgs] [:or [:vector number?] nil?]]}
[p
{:keys [input-xs-list input-xs-count input-ys-vec]
{:keys [input-xs-list input-xs-count]
:as run-args}]
(let [^IExpr new-expr (:expr p)
^IExpr eval-p (eval-phenotype-on-expr-args p input-xs-list)]
Expand Down Expand Up @@ -164,3 +167,6 @@

{:xs xs
:ys evaluated-ys}))


(specs/instrument-all!)
14 changes: 10 additions & 4 deletions src/closyr/ops/initialize.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(ns closyr.ops.initialize
(:require
[closyr.log :as log]
[closyr.ops.common :as ops-common])
[closyr.util.log :as log]
[closyr.ops.common :as ops-common]
[closyr.util.spec :as specs])
(:import
(org.matheclipse.core.expression
AST
Expand All @@ -21,6 +22,7 @@

(defn initial-phenotypes
"Initial exprs scaled up in quantity to use in GA evolution"
{:malli/schema [:=> [:cat pos-int?] #'specs/GAPopulationPhenotypes]}
[p-count]
(let [^ISymbol x ops-common/sym-x]
(->>
Expand All @@ -31,6 +33,7 @@

(defn initial-mutations
"All mutations to use in GA evolutions"
{:malli/schema [:=> [:cat] [:vector #'specs/GAMutation]]}
[]
[{:op :modify-fn
:label "Derivative"
Expand Down Expand Up @@ -61,8 +64,8 @@
:modifier-fn (fn ^IExpr [{^IAST expr :expr ^ISymbol x-sym :sym :as pheno}]
(F/Plus expr (F/Divide 1 F/C100)))}

{:op :modify-fn
:label "-1/100"
{:op :modify-fn
:label "-1/100"
:modifier-fn (fn ^IExpr [{^IAST expr :expr ^ISymbol x-sym :sym :as pheno}]
(F/Subtract expr (F/Divide 1 F/C100)))}

Expand Down Expand Up @@ -659,3 +662,6 @@
(if (ops-common/should-modify-branch leaf-count pheno)
(F/Subtract ie (F/num 0.1))
ie))}])


(specs/instrument-all!)
72 changes: 52 additions & 20 deletions src/closyr/ops/modify.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
(:require
[clojure.core.async :as async :refer [go go-loop timeout <!! >!! <! >! chan put! take! alts!! alt!! close!]]
[clojure.string :as str]
[closyr.dataset.prng :refer :all]
[closyr.log :as log]
[closyr.ops.common :as ops-common])
[closyr.ops.common :as ops-common]
[closyr.util.log :as log]
[closyr.util.prng :refer :all]
[closyr.util.spec :as specs])
(:import
(java.util.function
Function)
Expand Down Expand Up @@ -139,6 +140,17 @@

(defn crossover
"Do phenotype crossover on their expr AST"
{:malli/schema
[:=>

;; inputs:
[:cat
pos-int?
#'specs/GAPhenotype
#'specs/GAPhenotype]

;; outputs:
#'specs/GAPhenotype]}
[max-leafs
{^IAST e1 :expr ^ISymbol x-sym :sym ^ExprEvaluator util :util :as p}
{^IAST e2 :expr :as p-discard}]
Expand Down Expand Up @@ -184,6 +196,23 @@

(defn apply-modifications
"Apply a sequence of modifications"
{:malli/schema
[:=>

;; inputs:
[:cat
pos-int?
pos-int?
[:sequential #'specs/GAMutation]
#'specs/GAPhenotype
#'specs/GAPhenotype]

;; outputs:
[:map {:closed true}
[:new-pheno #'specs/GAPhenotype]
[:iters int?]
[:mods [:sequential #'specs/GAMutation]]]]}

[max-leafs mods-count initial-muts p-winner p-discard]
(loop [iters 0
mods-left-to-apply mods-count
Expand Down Expand Up @@ -237,26 +266,29 @@
(concat (repeat 40 1))
(concat (repeat 30 2))
(concat (repeat 20 3))
(concat (repeat 18 4))
(concat (repeat 16 5))
(concat (repeat 15 6))
(concat (repeat 14 7))
(concat (repeat 13 8))
(concat (repeat 12 9))
(concat (repeat 11 10))
(concat (repeat 10 11))
(concat (repeat 9 12))
(concat (repeat 8 13))
(concat (repeat 7 14))
(concat (repeat 6 15))
(concat (repeat 5 16))
(concat (repeat 4 17))
(concat (repeat 3 18))
(concat (repeat 2 19))
(concat (repeat 1 20))
(concat (repeat 15 4))
(concat (repeat 12 5))
(concat (repeat 10 6))
(concat (repeat 9 7))
(concat (repeat 8 8))
(concat (repeat 7 9))
(concat (repeat 6 10))
(concat (repeat 5 11))
(concat (repeat 4 12))
(concat (repeat 3 13))
(concat (repeat 2 14))
(concat (repeat 1 15))
;; (concat (repeat 5 16))
;; (concat (repeat 4 17))
;; (concat (repeat 3 18))
;; (concat (repeat 2 19))
;; (concat (repeat 1 20))
;; (concat (repeat 3 21))
;; (concat (repeat 2 22))
;; (concat (repeat 1 23))
;; (concat (repeat 1 24))
;; (concat (repeat 1 25))
vec))


(specs/instrument-all!)
44 changes: 35 additions & 9 deletions src/closyr/symbolic_regression.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
(:require
[clojure.core.async :as async :refer [go go-loop timeout <!! >!! <! >! chan put! take! alts!! alts! close!]]
[closyr.ga :as ga]
[closyr.log :as log]
[closyr.util.log :as log]
[closyr.ops :as ops]
[closyr.ops.common :as ops-common]
[closyr.ops.initialize :as ops-init]
[closyr.util.spec :as specs]
[closyr.ui.gui :as gui]
[flames.core :as flames]
[malli.core :as m]
[seesaw.core :as ss])
(:import
(java.util
Expand Down Expand Up @@ -466,6 +468,8 @@

(init
[this]
(specs/validate! "SolverRunConfig" specs/SolverRunConfig run-config)
(specs/validate! "SolverRunArgs" specs/SolverRunArgs run-args)
(let [{:keys [iters initial-phenos initial-muts use-gui?]} run-config
start (print-and-save-start-time iters initial-phenos)
init-pop (ga/initialize
Expand All @@ -476,7 +480,8 @@

(log/info "Running with logging every n steps: " (:log-steps run-config))

(assoc this :ga-result init-pop
(assoc this
:ga-result init-pop
:iters-to-go iters
:start-ms start)))

Expand All @@ -488,10 +493,13 @@
iters-to-go (:iters-to-go this)]
(binding [ops/*log-steps* log-steps]
(if (zero? iters-to-go)
(assoc this :status :done :result {:iters-done (- iters iters-to-go)
:final-population population
:next-step :wait})
(assoc this
:status :done
:result {:iters-done (- iters iters-to-go)
:final-population population
:next-step :wait})
(let [{scores :pop-scores :as ga-result} (ga/evolve population)]
(specs/validate! "GAPopulation" specs/GAPopulationPhenotypes (:pop ga-result))
(ops/report-iteration iters-to-go iters ga-result run-args run-config)
(assoc this :ga-result ga-result :iters-to-go (next-iters iters-to-go scores)))))))

Expand Down Expand Up @@ -534,11 +542,10 @@
return-value))


(defn- run-ga-iterations-using-record
(defn run-ga-iterations-using-record
"Run GA evolution iterations on initial population"
[{:keys [iters initial-phenos initial-muts use-gui?] :as run-config}
run-args]

{:malli/schema [:=> [:cat #'specs/SolverRunConfig #'specs/SolverRunArgs] #'specs/SolverRunResults]}
[run-config run-args]
(loop [solver-state (init (map->SolverStateController {:run-config run-config :run-args run-args}))]
(let [[recur? next-solver-state] (run-iteration solver-state)]
(if recur?
Expand Down Expand Up @@ -700,8 +707,22 @@
(System/exit 0)))


(def ^:private CLIArgs
[:map
{:closed true}
[:log-level {:optional true} keyword?]
[:iterations any?]
[:population any?]
[:headless any?]
[:xs {:optional true} any?]
[:ys {:optional true} any?]
[:use-flamechart {:optional true} any?]
[:max-leafs {:optional true} any?]])


(defn run-app-from-cli-args
"Run app from CLI args"
{:malli/schema [:=> [:cat #'CLIArgs] #'specs/SolverRunResults]}
[{:keys [iterations population headless xs ys use-flamechart max-leafs] :as cli-opts}]
(log/info "CLI: run from options: " cli-opts)
(let [run-config {:initial-phenos (ops-init/initial-phenotypes population)
Expand All @@ -722,6 +743,11 @@
result))


;; todo: feel kind of hacky to have to call this in ?every? ns that has defn schema
(specs/instrument-all!)


(comment (println "FN SCHEMAS: " (m/function-schemas)))
(comment (macroexpand-1 `(log/info "Hello")))
(comment (log/info "Hello"))
(comment (run-app-without-gui))
Expand Down
Loading

0 comments on commit c3d83a2

Please sign in to comment.