Merge branch 'master'

This commit is contained in:
Ryan Boldi 2023-10-14 21:39:44 -04:00
commit fbf77cdb90
108 changed files with 1137 additions and 255 deletions

View File

@ -10,23 +10,29 @@ If you are working in a Clojure IDE with an integrated REPL, the first
thing you may want to do is to open `src/propeller/session.cljc` and
evaluate the namespace declaration and the commented-out expressions
therein. These demonstrate core components of Propeller including
complete genetic programming runs.
complete genetic programming runs. When conducting complete genetic
programming runs this way (using `gp/gp`), depending on your IDE you
may need to explicitly open and load the problem file before evaluating
the calls to `require` and `gp/gp`.
To run Propeller from the command line, on a genetic programming problem
that is defined within this project, you will probably want to use either
the Clojure [CLI tools](https://clojure.org/guides/deps_and_cli) or
[leiningen](https://leiningen.org).
[leiningen](https://leiningen.org). In the examples below, the leiningen
and CLI commands are identical except that the former begin with
`lein run -m`, while the latter begin with `clj -M -m`.
The instructions below are written for leiningen. If you are using
the CLI tools instead, then replace `lein run -m` in each command
with `clj -M -m`.
If you are using leiningen, then you can start a run with the command
To start a run use `clj -M -m <namespace>` or
`lein run -m <namespace>`, replacing `<namespace>`
with the actual namespace that you will find at the top of the problem file.
For example, you can run the simple-regression genetic programming problem with:
```
clj -M -m propeller.problems.simple-regression
```
or
```
lein run -m propeller.problems.simple-regression
```
@ -35,6 +41,11 @@ Additional command-line arguments may
be provided to override the default key/value pairs specified in the
problem file, for example:
```
clj -M -m propeller.problems.simple-regression :population-size 100
```
or
```
lein run -m propeller.problems.simple-regression :population-size 100
@ -44,6 +55,13 @@ On Unix operating systems, including MacOS, you can use something
like the following to send output both to the terminal
and to a text file (called `outfile` in this example):
```
clj -M -m propeller.problems.simple-regression | tee outfile
```
or
```
lein run -m propeller.problems.simple-regression | tee outfile
```
@ -55,10 +73,24 @@ quotes, like in this example that provides a non-default
value for the `:variation` argument, which is a clojure map
containing curly brackets that may confuse your shell:
```
clj -M -m propeller.problems.simple-regression :variation "{:umad 1.0}"
```
or
```
lein run -m propeller.problems.simple-regression :variation "{:umad 1.0}"
```
For many genetic operator hyperparameters, collections may be provided in place of single values. When this is done, a random element of the collection will be chosen (with each being equally likely) each time the operator is used. When specied at the command line, these collections will also have to be quoted, for example with `:umad-rate "[0.01 0.05 0.1]"` to mean that UMAD rates of 0.01, 0.05, and 0.1 can be used.
By default, Propeller will conduct many processes concurrently on multiple
cores using threads. If you want to disable this behavior (for example, during
debugging) then provide the argument `:single-thread-mode` with the value `true`.
Threads are not available in Javascript, so no processes are run concurrnetly
when Propeller is run in Clojurescript.
## CLJS Usage

View File

@ -3,7 +3,7 @@
{org.clojure/clojure #:mvn{:version "1.10.0"},
org.clojure/clojurescript #:mvn{:version "1.9.946"},
org.clojure/test.check #:mvn{:version "1.1.0"},
net.clojars.schneau/psb2 #:mvn{:version "1.1.0"}},
net.clojars.schneau/psb2 #:mvn{:version "1.1.1"}},
:mvn/repos {}
:codox {:extra-deps {codox/codox {:mvn/version "0.10.8"}}
:exec-fn codox.main/generate-docs

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(defproject net.clojars.lspector/propeller "0.3.0"
(defproject net.clojars.lspector/propeller "0.3.2"
:description "Yet another Push-based genetic programming system in Clojure."
:url "https://github.com/lspector/propeller"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"

1
scripts/GenerateDocs.sh Normal file → Executable file
View File

@ -1,5 +1,6 @@
#!/bin/sh
pip install mdutils
lein codox
python3 FunctionsToMD.py
python3 HTMLFix.py

View File

@ -39,9 +39,9 @@ line with the command `lein run -m <namespace>`, replacing `<namespace>`
with the actual namespace that you will find at the top of the problem file.
If you have installed [Clojure](https://clojure.org/guides/install_clojure#java), you can run Propeller on a genetic programming
problem with the command `clj --main <namespace>`, replacing `<namespace>` with
problem with the command `clj -M -m <namespace>`, replacing `<namespace>` with
the actual namespace that you will find at the top of the problem file.
The examples below use leiningen, but you can replace `lein run -m` with `clj --main` to run the same problem.
The examples below use leiningen, but you can replace `lein run -m` with `clj -M -m` to run the same problem.
A specific example is provided later below.

View File

@ -116,7 +116,6 @@ instructions from `push/instructions`, input instructions, close, and constants
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))
```

View File

@ -7,6 +7,7 @@ Table of contents
* [Additional Instructions](#additional-instructions)
* [input_output.cljc](#input_outputcljc)
* [.DS_Store](#ds_store)
* [numeric.cljc](#numericcljc)
* [string.cljc](#stringcljc)
* [character.cljc](#charactercljc)
@ -19,6 +20,8 @@ Table of contents
## :print_newline
Prints new line
# .DS_Store
# numeric.cljc
## :float_cos

View File

@ -1,6 +1,9 @@
# Generating Documentation for Propeller
To generate documentation with [codox](https://github.com/weavejester/codox), run `scripts/GenerateDocs.sh`in the command line.
In order to generate documentation, you'll need Python and `pip` installed.
To generate documentation with [codox](https://github.com/weavejester/codox), run `scripts/GenerateDocs.sh` in the command line from the `scripts` directory.
To make the script executable, you may need to first run `chmod +x GenerateDocs.sh`.
This will run "lein codox" on the command line to generate first batch of HTMl files.
Then, it runs FunctionsToMD to take Push instructions generated by `def-instruction` and spit it out to a Markdown file.
Then, it runs HTMLFix to fix the ordered lists in `Adding_Genetic_Operators.md`, `Adding_Problem.md`, and

View File

@ -22,6 +22,9 @@ They hold the genetic material for an `individual`. In the initial population, w
([plushy] (plushy->push plushy {}))
([plushy argmap]
(let [plushy (if (:diploid argmap) (map first (partition 2 plushy)) plushy)
plushy (if (> (or (:ah-umad (:variation argmap)) 0) 0) ;; strip :vary and :protect if using :ah-umad
(filter (complement #{:vary :protect}) plushy)
plushy)
opener? #(and (vector? %) (= (first %) 'open))] ;; [open <n>] marks opens
(loop [push () ;; iteratively build the Push program from the plushy
plushy (mapcat #(let [n (get instructions/opens %)]

View File

@ -15,46 +15,41 @@
[propeller.push.instructions.polymorphic]
[propeller.push.instructions.string]
[propeller.push.instructions.vector]
[propeller.selection :as selection]))
[propeller.selection :as selection]
[propeller.utils :as utils]))
(defn report
"Reports information each generation."
[evaluations pop generation argmap training-data]
(let [best (first pop)]
(clojure.pprint/pprint {:generation generation
:best-plushy (:plushy best)
:best-program (genome/plushy->push (:plushy best) argmap)
:best-total-error (:total-error best)
:evaluations evaluations
:ds-indices (map #(:index %) training-data)
:best-errors (:errors best)
:best-behaviors (:behaviors best)
:genotypic-diversity (float (/ (count (distinct (map :plushy pop))) (count pop)))
:behavioral-diversity (float (/ (count (distinct (map :behaviors pop))) (count pop)))
:average-genome-length (float (/ (reduce + (map count (map :plushy pop))) (count pop)))
:average-total-error (float (/ (reduce + (map :total-error pop)) (count pop)))})
(clojure.pprint/pprint
(merge {:generation generation
:best-plushy (:plushy best)
:best-program (genome/plushy->push (:plushy best) argmap)
:best-total-error (:total-error best)
:evaluations evaluations
:ds-indices (map #(:index %) training-data)
:best-errors (:errors best)
:best-behaviors (:behaviors best)
:genotypic-diversity (float (/ (count (distinct (map :plushy pop))) (count pop)))
:behavioral-diversity (float (/ (count (distinct (map :behaviors pop))) (count pop)))
:average-genome-length (float (/ (reduce + (map count (map :plushy pop))) (count pop)))
:average-total-error (float (/ (reduce + (map :total-error pop)) (count pop)))}
(if (> (or (:ah-umad (:variation argmap)) 0) 0) ;; using autoconstructive hypervariability
{:average-hypervariability
(let [variabilities (map (fn [i]
(let [p (:plushy i)]
(if (empty? p)
0
(/ (reduce + (variation/ah-rates p 0 1))
(count p)))))
pop)]
(float (/ (reduce + variabilities) (count variabilities))))}
{})))
(println)))
(defn gp
"Main GP loop.
On each iteration, it creates a population of random plushies using a mapper
function and genome/make-random-plushy function,
then it sorts the population by the total error using the error-function
and sort-by function. It then takes the best individual from the sorted population,
and if the parent selection is set to epsilon-lexicase, it adds the epsilons to the argmap.
The function then checks if the custom-report argument is set,
if so it calls that function passing the evaluated population,
current generation and argmap. If not, it calls the report function
passing the evaluated population, current generation and argmap.
Then, it checks if the total error of the best individual is less than or equal
to the solution-error-threshold or if the current generation is greater than or
equal to the max-generations specified. If either is true, the function
exits with the best individual or nil. If not, it creates new individuals
for the next generation using the variation/new-individual function and the
repeatedly function, and then continues to the next iteration of the loop. "
"Main GP loop."
[{:keys [population-size max-generations error-function instructions
max-initial-plushy-size solution-error-threshold mapper ds-parent-rate ds-parent-gens dont-end ids-type downsample?]
:or {solution-error-threshold 0.0
@ -73,9 +68,11 @@ repeatedly function, and then continues to the next iteration of the loop. "
;;
(loop [generation 0
evaluations 0
population (mapper
(fn [_] {:plushy (genome/make-random-plushy instructions max-initial-plushy-size)})
(range population-size))
population (utils/pmapallv
(fn [_] {:plushy (let [plushy (genome/make-random-plushy instructions max-initial-plushy-size)]
(if (:diploid argmap)
(interleave plushy plushy)
plushy))}) (range population-size) argmap)
indexed-training-data (downsample/assign-indices-to-data (downsample/initialize-case-distances argmap))]
(let [training-data (if downsample?
(case (:ds-function argmap)
@ -89,21 +86,24 @@ repeatedly function, and then continues to the next iteration of the loop. "
(zero? (mod generation ds-parent-gens))) ;every ds-parent-gens generations
(take (* ds-parent-rate (count population)) (shuffle population))
'()) ;else just empty list
; parent representatives for down-sampling
rep-evaluated-pop (if downsample?
(sort-by :total-error
(mapper
(utils/pmapallv
(partial error-function argmap indexed-training-data)
parent-reps))
parent-reps
argmap))
'())
evaluated-pop (sort-by :total-error
(mapper
(utils/pmapallv
(partial error-function argmap training-data)
population))
population
argmap))
best-individual (first evaluated-pop)
best-individual-passes-ds (and downsample? (<= (:total-error best-individual) solution-error-threshold))
argmap (if (= (:parent-selection argmap) :epsilon-lexicase)
(assoc argmap :epsilons (selection/epsilon-list evaluated-pop))
argmap)] ;adds :epsilons if using epsilon-lexicase
argmap)] ; epsilons
(if (:custom-report argmap)
((:custom-report argmap) evaluations evaluated-pop generation argmap)
(report evaluations evaluated-pop generation argmap training-data))

View File

@ -77,5 +77,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -96,4 +96,4 @@
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(#?(:clj shutdown-agents)))

View File

@ -146,5 +146,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -78,5 +78,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -85,5 +85,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -96,5 +96,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -85,5 +85,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -119,5 +119,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -93,5 +93,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -100,5 +100,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -101,5 +101,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -102,5 +102,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -87,5 +87,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -83,5 +83,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -84,5 +84,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -96,5 +96,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -98,5 +98,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -88,5 +88,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -112,5 +112,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -85,5 +85,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -95,5 +95,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -87,5 +87,4 @@ Source: https://arxiv.org/pdf/2106.06086.pdf"
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -0,0 +1,341 @@
(ns propeller.problems.boolean.mul3
(:require [propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.gp :as gp]
[propeller.push.instructions :refer [def-instruction
make-instruction]]
#?(:cljs [cljs.reader :refer [read-string]])))
(defn target-function
"Returns a vector of 8 bits (booleans) for the product of the numbers
a and b, which should be provided as 3 booleans each."
[a2 a1 a0 b2 b1 b0]
(let [a (+ (if a2 4 0)
(if a1 2 0)
(if a0 1 0))
b (+ (if b2 4 0)
(if b1 2 0)
(if b0 1 0))
product (* a b)]
(loop [bit-index 5
product-bits {}
remainder product]
(if (< bit-index 0)
product-bits
(let [pow2 (bit-shift-left 1 bit-index)
this-bit (>= remainder pow2)]
(recur (dec bit-index)
(assoc product-bits (keyword (str "c" bit-index)) this-bit)
(- remainder (* (if this-bit 1 0) pow2))))))))
#_(target-function false true false false false true)
#_(target-function true true true true true)
(def train-and-test-data
(let [bools [false true]]
{:train (for [a2 bools
a1 bools
a0 bools
b2 bools
b1 bools
b0 bools]
{:inputs {:a2 a2 :a1 a1 :a0 a0 :b2 b2 :b1 b1 :b0 b0}
:outputs (target-function a2 a1 a0 b2 b1 b0)})
:test []}))
(def-instruction
:set-c5
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c5]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c4
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c4]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c3
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c3]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c2
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c2]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c1
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c1]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c0
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c0]
(state/peek-stack state :boolean)))))
(def-instruction
:c5
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c5 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:c4
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c4 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:c3
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c3 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:c2
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c2 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:c1
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c1 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:c0
^{:stacks [:boolean :output]}
(fn [state]
(let [val (:c0 (:output state))]
(if (boolean? val)
(state/push-to-stack state :boolean val)
state))))
(def-instruction
:boolean_bufa
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] b1)
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nota
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not b1))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nand
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not (and b1 b2)))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nor
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not (or b1 b2)))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_xnor
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (= b1 b2))
[:boolean :boolean]
:boolean)))
(def instructions
(list :a2
:a1
:a0
:b2
:b1
:b0
:set-c5 ;; defined here
:set-c4 ;; defined here
:set-c3 ;; defined here
:set-c2 ;; defined here
:set-c1 ;; defined here
:set-c0 ;; defined here
:c5 ;; defined here
:c4 ;; defined here
:c3 ;; defined here
:c2 ;; defined here
:c1 ;; defined here
:c0 ;; defined here
;; BOOLEAN TAGGING?
;; Recommended by Kalkreuth et al: BUFa, NOTa, AND, OR, XOR, NAND, NOR, XNOR
;:boolean_bufa ;; defined here
;:boolean_nota ;; defined here
:boolean_and
:boolean_or
:boolean_xor
:boolean_nand ;; defined here
:boolean_nor ;; defined here
:boolean_xnor ;; defined here
:boolean_not ;; added to compensate for commenting out :boolean_nota
;:boolean_pop
:boolean_dup
:boolean_swap
:boolean_rot
;:exec_pop
;:exec_dup
;:exec_swap
;:exec_rot
;'close
;true
;false
))
(defn error-function
[argmap data individual]
(let [program (genome/plushy->push (:plushy individual) argmap)
input-maps (mapv :inputs data)
correct-output-maps (mapv :outputs data)
output-maps (mapv (fn [input-map]
(:output
(interpreter/interpret-program
program
(assoc state/empty-state
:input input-map
:output {:c5 :unset
:c4 :unset
:c3 :unset
:c2 :unset
:c1 :unset
:c0 :unset})
(:step-limit argmap))))
input-maps)
errors (flatten (map (fn [correct-output-map output-map]
(mapv (fn [k]
; no answer same as wrong answer
(if (= (k correct-output-map)
(k output-map))
0
1))
[:c5 :c4 :c3 :c2 :c1 :c0]))
correct-output-maps
output-maps))]
(assoc individual
:behaviors output-maps
:errors errors
:total-error #?(:clj (apply +' errors)
:cljs (apply + errors)))))
(defn -main
"Runs the top-level genetic programming function, giving it a map of
arguments with defaults that can be overridden from the command line
or through a passed map."
[& args]
(gp/gp
(merge
{:instructions (concat instructions [:vary :protect]) ;; ah-umad
:error-function error-function
:training-data (:train train-and-test-data)
:testing-data (:test train-and-test-data)
:max-generations 1000
:population-size 100
:max-initial-plushy-size 100
:step-limit 1000
:parent-selection :lexicase
;:parent-selection :tournament
;:parent-selection :motley-batch-lexicase
;:max-batch-size [1 2 4 8 16 32 64 128 256]
;:tournament-size 5
;:umad-rate 0.09
:ah-umad-protect-rate 0.001 ;; ah-umad
:ah-umad-vary-rate 0.1 ;; ah-umad
:ah-umad-tournament-size 2 ;; ah-umad
;:umad-rate [1/2
; 1/4 1/4
; 1/8 1/8 1/8
; 1/16 1/16 1/16 1/16
; 1/32 1/32 1/32 1/32 1/32
; 1/64 1/64 1/64 1/64 1/64 1/64
; 1/128 1/128 1/128 1/128 1/128 1/128 1/128
; 1/256 1/256 1/256 1/256 1/256 1/256 1/256 1/256]
;:alternation-rate [1 1/2 1/4 1/8 1/16 1/32 1/64 1/128 1/256]
;:alignment-deviation [0 1 2 4 8 16 32 64 128]
:variation {:ah-umad 1 ;; ah-umad
:umad 0
:alternation 0
:reproduction 0
:tail-aligned-crossover 0}
;:diploid true
;:variation {:diploid-vumad 0.8
; :diploid-uniform-silent-replacement 0.1
; :diploid-flip 0.1}
;:replacement-rate 0.01
;:diploid-flip-rate 0.01
:elitism false
:single-thread-mode false}
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -0,0 +1,278 @@
(ns propeller.problems.boolean.mul4
(:require [propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.gp :as gp]
[propeller.push.instructions :refer [def-instruction
make-instruction]]
#?(:cljs [cljs.reader :refer [read-string]])))
(defn target-function
"Returns a vector of 8 bits (booleans) for the product of the numbers
a and b, which should be provided as 4 booleans each."
[a3 a2 a1 a0 b3 b2 b1 b0]
(let [a (+ (if a3 8 0)
(if a2 4 0)
(if a1 2 0)
(if a0 1 0))
b (+ (if b3 8 0)
(if b2 4 0)
(if b1 2 0)
(if b0 1 0))
product (* a b)]
(loop [bit-index 7
product-bits {}
remainder product]
(if (< bit-index 0)
product-bits
(let [pow2 (bit-shift-left 1 bit-index)
this-bit (>= remainder pow2)]
(recur (dec bit-index)
(assoc product-bits (keyword (str "c" bit-index)) this-bit)
(- remainder (* (if this-bit 1 0) pow2))))))))
#_(target-function false true false false false true false false)
#_(target-function true true true true true true true true)
(def train-and-test-data
(let [bools [false true]]
{:train (for [a3 bools
a2 bools
a1 bools
a0 bools
b3 bools
b2 bools
b1 bools
b0 bools]
{:inputs {:a3 a3 :a2 a2 :a1 a1 :a0 a0 :b3 b3 :b2 b2 :b1 b1 :b0 b0}
:outputs (target-function a3 a2 a1 a0 b3 b2 b1 b0)})
:test []}))
(def-instruction
:set-c7
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c7]
(state/peek-stack state :boolean)))))
#_(interpreter/interpret-program
'(:set-c7) (assoc state/empty-state :output {:c7 :unset}) 1000)
#_(interpreter/interpret-program
'(true :set-c7) (assoc state/empty-state :output {:c7 :unset}) 1000)
(def-instruction
:set-c6
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c6]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c5
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c5]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c4
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c4]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c3
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c3]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c2
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c2]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c1
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c1]
(state/peek-stack state :boolean)))))
(def-instruction
:set-c0
^{:stacks [:boolean :output]}
(fn [state]
(if (state/empty-stack? state :boolean)
state
(assoc-in (state/pop-stack state :boolean)
[:output :c0]
(state/peek-stack state :boolean)))))
(def-instruction
:boolean_bufa
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] b1)
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nota
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not b1))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nand
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not (and b1 b2)))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_nor
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (not (or b1 b2)))
[:boolean :boolean]
:boolean)))
(def-instruction
:boolean_xnor
^{:stacks #{:boolean}}
(fn [state]
(make-instruction state
(fn [b1 b2] (= b1 b2))
[:boolean :boolean]
:boolean)))
(def instructions
(list :a3
:a2
:a1
:a0
:b3
:b2
:b1
:b0
:set-c7 ;; defined here
:set-c6 ;; defined here
:set-c5 ;; defined here
:set-c4 ;; defined here
:set-c3 ;; defined here
:set-c2 ;; defined here
:set-c1 ;; defined here
:set-c0 ;; defined here
;; Recommended by Kalkreuth et al: BUFa, NOTa, AND, OR, XOR, NAND, NOR, XNOR
:boolean_bufa ;; defined here
:boolean_nota ;; defined here
:boolean_and
:boolean_or
:boolean_xor
:boolean_nand ;; defined here
:boolean_nor ;; defined here
:boolean_xnor ;; defined here
:boolean_dup
:boolean_swap
:boolean_rot
:boolean_pop
:integer_dup ;; will be a noop
:exec_pop
true
false))
(defn error-function
[argmap data individual]
(let [program (genome/plushy->push (:plushy individual) argmap)
input-maps (mapv :inputs data)
correct-output-maps (mapv :outputs data)
output-maps (mapv (fn [input-map]
(:output
(interpreter/interpret-program
program
(assoc state/empty-state
:input input-map
:output {:c7 :unset
:c6 :unset
:c5 :unset
:c4 :unset
:c3 :unset
:c2 :unset
:c1 :unset
:c0 :unset})
(:step-limit argmap))))
input-maps)
errors (flatten (map (fn [correct-output-map output-map]
(mapv (fn [k]
; no answer same as wrong answer
(if (= (k correct-output-map)
(k output-map))
0
1))
[:c7 :c6 :c5 :c4 :c3 :c2 :c1 :c0]))
correct-output-maps
output-maps))]
(assoc individual
:behaviors output-maps
:errors errors
:total-error #?(:clj (apply +' errors)
:cljs (apply + errors)))))
(defn -main
"Runs the top-level genetic programming function, giving it a map of
arguments with defaults that can be overridden from the command line
or through a passed map."
[& args]
(gp/gp
(merge
{:instructions instructions
:error-function error-function
:training-data (:train train-and-test-data)
:testing-data (:test train-and-test-data)
:max-generations 100
:population-size 100
:max-initial-plushy-size 10
:step-limit 1000
:parent-selection :lexicase
:tournament-size 5
:umad-rate 0.01
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -83,5 +83,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -80,5 +80,4 @@
:solution-error-threshold 0.5
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -82,5 +82,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -101,5 +101,4 @@
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false}
(apply hash-map (map #(if (string? %) (read-string %) %) args))))
(#?(:clj shutdown-agents)))
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -1,15 +0,0 @@
(ns ^:no-doc propeller.problems.software.fizz-buzz
(:require [psb2.core :as psb2]))
;; @todo This namespace is never used an it isn't a complete problem. Furthermore fizz-buzz exists in the PSB2 folder.
;; Consider removing this file.
;; NOTE: Need to change directory below to location of the PSB2 files
(def train-and-test (psb2/fetch-examples "PSB2/directory/path/goes/here/" "fizz-buzz" 200 2000))
(comment
train-and-test
problems
)

Some files were not shown because too many files have changed in this diff Show More