Skip to content

Commit

Permalink
Document Plush
Browse files Browse the repository at this point in the history
  • Loading branch information
saulshanabrook committed Dec 25, 2015
1 parent e6b281c commit deb4eb9
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 18 deletions.
23 changes: 23 additions & 0 deletions docs/PLUSH.md
@@ -0,0 +1,23 @@
# Plush Genomes / Epigentics

> In a change from previous versions of PushGP, the most recent version of Clojush
does not evolve Push programs directly, but instead uses a separate linear genome representation
that we translate into Push programs prior to execution. The new Plush
genomes are linear sequences of instructions that may have one or more epigenetic
markers attached to each instruction. The epigenetic markers affect the translation of
the Plush genomes into Push programs. For example, the silent marker is a boolean
that tells whether a particular instruction will appear in the translated program.

[- Tom Helmuth](https://web.cs.umass.edu/publication/docs/2015/UM-CS-PhD-2015-005.pdf)

A Plush genome looks like this:

```clojure
({:close 1, :silent false, :instruction code_frominteger} {:close 0, :silent false, :instruction genome_yank} {:close 0, :silent false, :instruction float_eq} {:close 0, :silent false, :instruction code_size} {:close 0, :silent false, :instruction float_max})
```

Look at the [`translate.clj` tests](./test/clojush/midje/translate.clj) for
some examples of how they are translated. Also check out the docs
for the
[`clojush.translate/var-translate-plush-genome-to-push-program`](https://lspector.github.io/Clojush/clojush.translate.html#var-translate-plush-genome-to-push-program)
function to understand how they are represented in the codebase.
54 changes: 36 additions & 18 deletions src/clojush/translate.clj
@@ -1,4 +1,5 @@
(ns clojush.translate
"Functions converting Plush genomes into Push programs."
(:use [clojush util]
clojush.instructions.common))

Expand Down Expand Up @@ -41,24 +42,37 @@
number-close-parens
found-first-close))))


(defn translate-plush-genome-to-push-program
"Takes as input an individual (or map) containing a Plush genome (:genome)
and translates it to the correct Push program with
balanced parens. The linear Plush genome is made up of a list of instruction
maps, each including an :instruction key as well as other epigenetic marker
keys. As the linear Plush genome is traversed, each instruction that requires
parens will push :close and/or :close-open onto the paren-stack, and will
also put an open paren after it in the program. For example, an instruction
that requires 3 paren groupings will push :close, then :close-open, then :close-open.
When a positive number is encountered in the :close key of the
instruction map, it is set to num-parens-here during the next recur. This
indicates the number of parens to put here, if need is indicated on the
paren-stack. If the top item of the paren-stack is :close, a close paren
will be inserted. If the top item is :close-open, a close paren followed by
an open paren will be inserted.
If the end of the program is reached but parens are still needed (as indicated by
the paren-stack), parens are added until the paren-stack is empty.
Instruction maps that have :silence set to true will be ignored entirely."
"Converts a Plush genome into a Push program with balanced parenthesis.
Refer to the `./docs/PLUSH.md` file for background on Plush genomes.
Takes as input an [[clojush/indvidual/->individual]] (or map). It only looks
for the `:genome` and the `:program`. If the `:program` is not `nil` it will
just return that. Otherwise it will convert the `:genome` into a `:program`.
The Plush genome (`:genome`) is a list of maps. Each map has at least an
`:instruction` key, and optionally a `:silent` and `:parens` key.
If `:silent` is true, then this instruciton will be ignored and not output
at all. It defaults to false.
The linear Plush genome is made up of a list of instruction
maps, each including an :instruction key as well as other epigenetic marker
keys. As the linear Plush genome is traversed, each instruction that requires
parens will push :close and/or :close-open onto the paren-stack, and will
also put an open paren after it in the program. For example, an instruction
that requires 3 paren groupings will push :close, then :close-open, then :close-open.
When a positive number is encountered in the :close key of the
instruction map, it is set to num-parens-here during the next recur. This
indicates the number of parens to put here, if need is indicated on the
paren-stack. If the top item of the paren-stack is :close, a close paren
will be inserted. If the top item is :close-open, a close paren followed by
an open paren will be inserted.
If the end of the program is reached but parens are still needed (as indicated by
the paren-stack), parens are added until the paren-stack is empty.
Instruction maps that have :silence set to true will be ignored entirely."
[{:keys [genome program]}
{:keys [max-points] :as argmap}]
(if program
Expand Down Expand Up @@ -121,7 +135,11 @@
argmap))))))

(defn population-translate-plush-to-push
"Converts the population of Plush genomes into Push programs."
"Converts the population of Plush genomes into Push programs.
Associates the `:program` key of each `pop-agent` to
[[translate-plush-genome-to-push-program]] called with that `pop-agent`
"
[pop-agents {:keys [use-single-thread] :as argmap}]
(dorun (map #((if use-single-thread swap! send)
%
Expand Down
55 changes: 55 additions & 0 deletions test/clojush/midje/translate.clj
@@ -0,0 +1,55 @@
(ns clojush.midje.translate
"Tests for [[clojush.translate/translate-plush-genome-to-push-program]] in document it's behavior
That function takes a [[clojush/indvidual/->individual]]"
(:require [midje.sweet :refer :all]
[clojush.translate :refer [translate-plush-genome-to-push-program]]
[clojush.pushgp.pushgp :refer [reset-globals, push-argmap]]
[clojush.problems.demos.odd-with-uniform-silence-mutation :refer [argmap]]))

(with-state-changes [(before :contents (reset-globals argmap))]
; choose two instructions to use in our tests. It doesn't
; matter what they are, as long as one operates on the exec stack
; and the other doesn't
(let [exec 'exec_dup
instr 'code_frominteger]
(tabular
(fact "translate-plush-genome-to-push-program"
(translate-plush-genome-to-push-program {:genome ?plush} @push-argmap) => ?push)
?plush ?push

[] '()
[{:instruction instr, :silent true}] '()
[{:instruction exec, :silent true}] '()


[{:instruction exec},
{:instruction instr}] (list exec (list instr))

[{:instruction exec, :close 1},
{:instruction instr}] (list exec, '(), instr)

[{:instruction exec},
{:instruction instr, :close 1}
{:instruction instr}] (list exec, (list instr), instr)

[{:instruction exec}] (list exec '())


[{:instruction instr},
{:instruction instr},
{:instruction exec}] (list instr, instr, exec '())


[{:instruction exec},
{:instruction exec},
{:instruction instr}] (list exec, (list exec (list instr)))

[{:instruction exec},
{:instruction exec :close 2},
{:instruction instr}] (list exec, (list exec, '() ), instr)

[{:instruction instr :close 1}] instr


)))

0 comments on commit deb4eb9

Please sign in to comment.