Skip to content

Commit

Permalink
Merge branch 'master' into track-selection-counts
Browse files Browse the repository at this point in the history
  • Loading branch information
thelmuth committed Jul 23, 2015
2 parents f3dfe12 + d95ff6d commit 297dd35
Show file tree
Hide file tree
Showing 18 changed files with 1,101 additions and 606 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -18,3 +18,4 @@ lib/
/bin
*.csv
*.json
.lein-failures
4 changes: 4 additions & 0 deletions PushState
@@ -0,0 +1,4 @@
src/clojush/pushgp/report.clj: (map-indexed (fn [location individual]
src/clojush/pushgp/report.clj~: (doseq [[ind p] (map-indexed vector population)]
src/clojush/pushgp/report.clj~: (doseq [[ind p] (map-indexed vector population)]
src/clojush/pushstate.clj:(let [empty-state (map->PushState {})]
4 changes: 0 additions & 4 deletions development.md

This file was deleted.

3 changes: 2 additions & 1 deletion project.clj
@@ -1,4 +1,4 @@
(defproject clojush "2.0.32"
(defproject clojush "2.0.33"
:description "The Push programming language and the PushGP genetic programming
system implemented in Clojure.
See http://hampshire.edu/lspector/push.html"
Expand All @@ -14,6 +14,7 @@
[org.clojure/data.json "0.1.3"]
[clj-random "0.1.7"]]
:dev-dependencies [[lein-ccw "1.2.0"]]
:profiles {:dev {:dependencies [[midje "1.6.3"]]}}
;;;;;;;;;; jvm settings for high performance, using most of the machine's RAM
; :jvm-opts ~(let [mem-to-use
; (long (* (.getTotalPhysicalMemorySize
Expand Down
4 changes: 0 additions & 4 deletions research.md

This file was deleted.

2 changes: 1 addition & 1 deletion src/clojush/instructions/genome.clj
Expand Up @@ -246,5 +246,5 @@
autoconstructive_integer_rand
;; pushes a constant integer, but is replaced with integer_rand during
;; nondetermistic autoconstruction
^{:stack-types [:genome]} (fn [state] (push-item 23 :integer state)))
^{:stack-types [:genome :integer]} (fn [state] (push-item 23 :integer state)))

5 changes: 2 additions & 3 deletions src/clojush/problems/demos/odd.clj
Expand Up @@ -26,8 +26,7 @@
(if (not (= top-bool :no-stack-item))
(if (= top-bool (odd? input)) 0 1)
1000)))))
:atom-generators (concat (registered-nonrandom)
:atom-generators (concat (registered-for-stacks [:integer :boolean :code :exec])
(list (fn [] (lrand-int 100))
'in1
'code_rand))
'in1))
})
60 changes: 60 additions & 0 deletions src/clojush/problems/tozier/the_idea_of_numbers.clj
@@ -0,0 +1,60 @@
;; the_idea_of_numbers.clj
;; Bill Tozier, bill@vagueinnovation.com
;;
;; This implements a common pedagogic demonstration from classes I
;; teach in GP. The objective is a simple symbolic regression problem
;; except there are no numeric ERCs.
;;
;; Input and output are given as integers using the integer stack.

(ns clojush.problems.tozier.the-idea-of-numbers
(:use clojush.pushgp.pushgp
[clojush pushstate interpreter random util]
[clojure.math.numeric-tower]
))


(defn birthday-polynomial
"Returns a polynomial y = YYYY + MM * x * DD * x * x"
[x year month day]
(+ year (* month x) (* day x x))
)


(defn missing-numbers-error-function
"Returns the absolute error."
[number-test-cases]
(fn [program]
(doall
(for [input (range 0 number-test-cases)]
(let [final-state (run-push program
(push-item input :input
(make-push-state)))
result-output (top-item :integer final-state)]
(if (and (number? result-output))
(abs (- result-output (birthday-polynomial input 1964 9 11))) ;; edit this so it's your birthday
1000000000)
)))))

; Atom generators
(def missing-numbers-atom-generators
(cons 'in1
(registered-for-stacks [:integer :code :boolean :exec :char :string :float])))



; Define the argmap
(def argmap
{:error-function (missing-numbers-error-function 20)
:atom-generators missing-numbers-atom-generators
:max-points 500
:max-genome-size-in-initial-program 300
:evalpush-limit 1000
:population-size 1000
:max-generations 300
:parent-selection :lexicase
:final-report-simplifications 1000
:genetic-operator-probabilities {
:alternation 0.5
:uniform-mutation 0.5}
})
139 changes: 139 additions & 0 deletions src/clojush/problems/tozier/winkler01.clj
@@ -0,0 +1,139 @@
;; winkler01.clj
;; Bill Tozier, bill@vagueinnovation.com
;;
;; This is code for running Tozier's variant on Winkler's Zeros-and-Ones puzzle:
;; For any positive (non-zero) integer input, return a strictly positive integer which
;; when multiplied by the input value produces a result which contains only the
;; digits 0 and 1 (in base 10 notation)
;;
;; Input and output are given as integers using the integer stack.

(ns clojush.problems.tozier.winkler01
(:use clojush.pushgp.pushgp
[clojush pushstate interpreter random util]
[clojure.math.numeric-tower]
))

; Create the error function
(defn count-digits [num] (count (re-seq #"\d" (str num))))


(defn proportion-not-01
"Returns the proportion of digits in the argument integer which are not 0 or 1"
[num]
(let [counts (frequencies (re-seq #"\d" (str num)))]
(- 1
(/ (+ (get counts "0" 0)
(get counts "1" 0))
(count-digits num)))))


(defn kill-trailing-zeros
"Returns an integer with all trailing zeros stripped off"
[num]
(read-string (clojure.string/replace (str num) #"(0+)$" ""))
)


;; "obvious" first attempt at an error function
(defn winkler-error-function-01
"Returns the proportion of digits in the product of input * output that are not 0 or 1."
[number-test-cases]
(fn [program]
(doall
(for [input (range 1 number-test-cases)]
(let [final-state (run-push program
(push-item input :input
(make-push-state)))
result-output (top-item :integer final-state)]
(when false (println ;; change to true to print every result (which is awful)
(if (and (number? result-output) (pos? result-output))
(* input result-output)
"N/A")))
(if (and (number? result-output) (pos? result-output))
(proportion-not-01 (* input result-output))
100)
)))))


;; "obvious" second attempt at an error function;
;; accommodation to trivial strategy of multiplying by 10000000...
(defn winkler-error-function-02
"Returns the proportion of digits in the product of input * output that are not 0 or 1, after trimming trailing zeros."
[number-test-cases]
(fn [program]
(doall
(for [input (range 1 number-test-cases)]
(let [final-state (run-push program
(push-item input :input
(make-push-state)))
result-output (top-item :integer final-state)]
(when false (println ;; change to true to print every result (which is awful)
(if (and (number? result-output) (pos? result-output))
(* input result-output)
"N/A")))
(if (and (number? result-output) (pos? result-output))
(proportion-not-01 (kill-trailing-zeros (* input result-output)))
100)
)))))


;; trying to give it some raw materials it might want to use

(defn prime-factors
"Return a vector of the prime factors of the argument integer; cadged from http://rosettacode.org/wiki/Prime_decomposition#Clojure"
([num]
(prime-factors num 2 ()))
([num k acc]
(if (= 1 num)
acc
(if (= 0 (rem num k))
(recur (quot num k) k (cons k acc))
(recur num (inc k) acc)))))


(defn prime-factors-as-sorted-vector
"Return the argument's prime factors as a sorted vector of integers; if the argument is 0, it returns (0); if the argument is negative, it returns the factors of the positive number with -1 added to the list;"
[num]
(cond
(pos? num) (into [] (sort (prime-factors num)))
(neg? num) (into [] (cons -1 (sort (prime-factors (abs num)))))
:else [0]
))


; Define new instructions
(define-registered
integer_factors
^{:stack-types [:integer :vector_integer]}
(fn [state]
(if (not (empty? (:integer state)))
(push-item (prime-factors-as-sorted-vector (stack-ref :integer 0 state))
:vector_integer
(pop-item :integer state))
state)))


; Atom generators
(def winkler-atom-generators
(concat (take 100 (repeat 'in1))
(take 50 (repeat (fn [] (lrand-int 65536)))) ;Integer ERC [0,65536]
(registered-for-stacks [:integer :code :boolean :exec :vector_integer :char :string :float])))



; Define the argmap
(def argmap
{:error-function (winkler-error-function-02 44) ;; change the error function to follow along...
:atom-generators winkler-atom-generators
:max-points 1000
:print-csv-logs true
:csv-columns [:generation :location :parent-uuids :genetic-operators :push-program-size :push-program :total-error :test-case-errors]
:csv-log-filename "log.csv"
:max-genome-size-in-initial-program 500
:evalpush-limit 1000
:population-size 1000
:max-generations 1000
:parent-selection :lexicase
:final-report-simplifications 1000
})
35 changes: 22 additions & 13 deletions src/clojush/pushgp/report.clj
Expand Up @@ -246,7 +246,7 @@
print-errors print-history print-cosmos-data print-timings
problem-specific-report total-error-method
parent-selection print-homology-data max-point-evaluations
print-error-frequencies-by-case normalization
print-error-frequencies-by-case normalization autoconstructive
print-selection-counts
;; The following are for CSV or JSON logs
print-csv-logs print-json-logs csv-log-filename json-log-filename
Expand Down Expand Up @@ -354,21 +354,22 @@
(count population))))
(printf "Average percent parens in population: %.3f\n" (/ (apply + (map #(double (/ (count-parens (:program %)) (count-points (:program %)))) sorted))
(count population)))
(println "--- Population Diversity Statistics ---")
(let [genome-frequency-map (frequencies (map :genome population))]
(println "Min copy number of one Plush genome:" (apply min (vals genome-frequency-map)))
(println "Median copy number of one Plush genome:" (nth (sort (vals genome-frequency-map)) (Math/floor (/ (count genome-frequency-map) 2))))
(println "Max copy number of one Plush genome:" (apply max (vals genome-frequency-map)))
(println "Genome diversity (% unique Plush genomes):\t" (float (/ (count genome-frequency-map) (count population)))))
(let [frequency-map (frequencies (map :program population))]
(println "Number of unique programs in population:" (count frequency-map))
(println "Max copy number of one program:" (apply max (vals frequency-map)))
(println "Min copy number of one program:" (apply min (vals frequency-map)))
(println "Median copy number:" (nth (sort (vals frequency-map)) (Math/floor (/ (count frequency-map) 2)))))
(println "Number of random replacements for reproductively incompetent individuals:"
(count (filter :random-replacement-for-reproductively-incompetent-genome population)))
(when print-selection-counts
(println "Selection counts:" (sort > (concat (vals @selection-counts)
(repeat (- population-size (count @selection-counts)) 0))))
(reset! selection-counts {}))
(println "Min copy number of one Push program:" (apply min (vals frequency-map)))
(println "Median copy number of one Push program:" (nth (sort (vals frequency-map)) (Math/floor (/ (count frequency-map) 2))))
(println "Max copy number of one Push program:" (apply max (vals frequency-map)))
(println "Syntactic diversity (% unique Push programs):\t" (float (/ (count frequency-map) (count population)))))
(println "Total error diversity:\t\t\t\t" (float (/ (count (frequencies (map :total-error population))) (count population))))
(println "Error (vector) diversity:\t\t\t" (float (/ (count (frequencies (map :errors population))) (count population))))
(when @global-print-behavioral-diversity
(swap! population-behaviors #(take-last population-size %)) ; Only use behaviors during evaluation, not those during simplification
(println "Behavioral diversity:" (behavioral-diversity))
;(println "Number of behaviors:" (count @population-behaviors))
(println "Behavioral diversity:\t\t\t\t" (behavioral-diversity))
(reset! population-behaviors ()))
(when print-homology-data
(let [num-samples 1000
Expand All @@ -382,6 +383,14 @@
(println "Median: " median-1)
(println "Third quartile: " third-quart-1)
))
(when print-selection-counts
(println "Selection counts:" (sort > (concat (vals @selection-counts)
(repeat (- population-size (count @selection-counts)) 0))))
(reset! selection-counts {}))
(when autoconstructive
(println "Number of random replacements for reproductively incompetent individuals:"
(count (filter :random-replacement-for-reproductively-incompetent-genome population))))
(println "--- Run Statistics ---")
(println "Number of program evaluations used so far:" @evaluations-count)
(println "Number of point (instruction) evaluations so far:" point-evaluations-before-report)
(reset! point-evaluations-count point-evaluations-before-report)
Expand Down
8 changes: 8 additions & 0 deletions src/clojush/pushstate.clj
Expand Up @@ -138,3 +138,11 @@
(and (:stack-types (meta instr-fn))
(clojure.set/subset? (set (:stack-types (meta instr-fn))) (set types-list))))
@instruction-table)))


(defn push-state-from-stacks
"Takes a map of stack names and entire stack states, and returns a new push-state
with those stacks set."
[& {:as stack-assignments}]
(merge (make-push-state) stack-assignments)
)
21 changes: 21 additions & 0 deletions test/clojush/midje/interpreter/literal_handling.clj
@@ -0,0 +1,21 @@
; To run these tests with autotest use:
;
; lein midje :autotest test
;
; This runs everything in the test sub-directory but
; _doesn't_ run all the stuff in src, which midje tries
; to run by default, which breaks the world.

(ns clojush.midje.interpreter.literal-handling
(:use clojure.test
midje.sweet
clojush.interpreter
clojush.pushstate))

(fact "Evaluating a null instruction returns the same state"
(execute-instruction nil :test-state) => :test-state)

(fact "Evaluating an integer constant as an instruction adds that value to the integer stack"
(let [test-state (make-push-state)
value 8]
(:integer (execute-instruction value (make-push-state))) => (list value)))
46 changes: 46 additions & 0 deletions test/clojush/midje/problems/tozier/the_idea_of_numbers.clj
@@ -0,0 +1,46 @@
; To run these tests with autotest use:
;
; lein midje :autotest test
;
; This runs everything in the test sub-directory but
; _doesn't_ run all the stuff in src, which midje tries
; to run by default, which breaks the world.

(ns clojush.midje.problems.tozier.the-idea-of-numbers
(:use clojure.test
clojush.pushstate
clojush.interpreter
midje.sweet
clojush.problems.tozier.the-idea-of-numbers))

(facts "birthday-polynomial works as expected"
(birthday-polynomial 0 0 0 0) => 0
(birthday-polynomial 1 1 1 1) => 3 ;; 1 + 1*1 + 1*1*1
(birthday-polynomial 2 3 4 5) => 31 ;; 3 + 4*2 + 5*2*2
(birthday-polynomial 0 1988 9 12) => 1988
)

;; checking error function
;;

(fact "missing-numbers-error-function responds with the number of cases indicated by the argument"
(count ((missing-numbers-error-function 5) '())) => 5 ;; (tests on 0,1,2,3,4)
)

(fact "missing-numbers-error-function produces the expected penalties when no answer is returned"
((missing-numbers-error-function 2) '()) => (just 1000000000 1000000000) ;; empty program
)

(fact "missing-numbers-error-function produces the expected scores"
((missing-numbers-error-function 3) '(0)) => (just 1964 1984 2026) ;; 1964+0; 1964+9+11; 1964+18+36
((missing-numbers-error-function 3) '(1000)) => (just 964 984 1026) ;; 1000 closer!
((missing-numbers-error-function 3)
'(1964 9 in1 integer_mult 11 in1 in1 integer_mult integer_mult integer_add integer_add)) => (just 0 0 0) ;; the right answer
)

;; check atom-generators
(fact "atom-generators have no numbers"
missing-numbers-atom-generators => (has not-any? integer?))

(fact "atom-generators does include the input (always good to check)"
missing-numbers-atom-generators => (contains ['in1]))

0 comments on commit 297dd35

Please sign in to comment.