-
Notifications
You must be signed in to change notification settings - Fork 92
/
digits.clj
154 lines (144 loc) · 6.88 KB
/
digits.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
;; digits.clj
;; Tom Helmuth, thelmuth@cs.umass.edu
;;
;; Problem Source: Program Repair Benchmark Paper (add citation later)
;;
;; Given an integer |x| < 10,000,000,000, print that integer's digits each on
;; their own line starting with the least significant digit. A negative integer
;; should have the negative sign printed before the most significant digit.
;;
;; input stack has input integer
(ns clojush.problems.software.digits
(:use clojush.pushgp.pushgp
[clojush pushstate interpreter random util globals]
clojush.instructions.tag
[clojure.math numeric-tower]
))
; Atom generators
(def digits-atom-generators
(concat (list
\newline
;;; end constants
(fn [] (- (lrand-int 21) 10))
;;; end ERCs
(tag-instruction-erc [:integer :boolean :string :char :exec] 1000)
(tagged-instruction-erc 1000)
;;; end tag ERCs
'in1
;;; end input instructions
)
(registered-for-stacks [:integer :boolean :string :char :exec :print])))
(defn my-rand-long
"replaces rand-int when need longs"
[end]
(long (* (lrand) end)))
;; A list of data domains for the problem. Each domain is a vector containing
;; a "set" of inputs and two integers representing how many cases from the set
;; should be used as training and testing cases respectively. Each "set" of
;; inputs is either a list or a function that, when called, will create a
;; random element of the set.
(def digits-data-domains
[[(list -9495969798 -20008000 -777777 -9876 -482 -97 -20 0 19 620 24068 512000 8313227 30000000 9998887776) 15 0] ;; Edge cases by hand
[(fn []
(let [digs (inc (lrand-int 10))
start (expt 10 (dec digs))
end (expt 10 digs)]
((if (< (lrand) 0.5) - +)
(+ (my-rand-long (- end start)) start)))) 85 1000] ;; Random cases such that each number of digits between 1 and 10 will be represented evenly, as will negatives and positives
])
;;Can make Digits test data like this:
;(test-and-train-data-from-domains digits-data-domains)
; Helper function for error function
(defn digits-test-cases
"Takes a sequence of inputs and gives IO test cases of the form
[input output]."
[inputs]
(map (fn [in] (vector in
(apply str ((if (< in 0) #(concat (butlast %) [\- (last %)]) identity)
(interpose \newline (reverse (str (abs in))))))))
inputs))
; Define error function. For now, each run uses different random inputs
(defn digits-error-function
"Returns the error function for the Digits problem. Takes as
input Digits data domains."
[data-domains]
(let [[train-cases test-cases] (map sort (map digits-test-cases
(test-and-train-data-from-domains data-domains)))]
(when true ;; Change to false to not print test cases
(doseq [[i case] (map vector (range) train-cases)]
(println (format "Train Case: %3d | Input/Output: %s" i (str case))))
(doseq [[i case] (map vector (range) test-cases)]
(println (format "Test Case: %3d | Input/Output: %s" i (str case)))))
(fn the-actual-digits-error-function
([program]
(the-actual-digits-error-function program :train))
([program data-cases] ;; data-cases should be :train or :test
(the-actual-digits-error-function program data-cases false))
([program data-cases print-outputs]
(let [behavior (atom '())
errors (doall
(for [[input1 correct-output] (case data-cases
:train train-cases
:test test-cases
[])]
(let [final-state (run-push program
(->> (make-push-state)
(push-item input1 :input)
(push-item "" :output)))
result (stack-ref :output 0 final-state)]
(when print-outputs
(println (format "| Correct output: %s\n| Program output: %s\n" (pr-str correct-output) (pr-str result))))
; Record the behavior
(when @global-print-behavioral-diversity
(swap! behavior conj result))
; Error is Levenshtein distance of printed strings
(levenshtein-distance correct-output result))))]
(when @global-print-behavioral-diversity
(swap! population-behaviors conj @behavior))
errors)))))
(defn digits-report
"Custom generational report."
[best population generation error-function report-simplifications]
(let [best-program (not-lazy (:program best))
best-test-errors (error-function best-program :test)
best-total-test-error (apply +' best-test-errors)]
(println ";;******************************")
(printf ";; -*- Digits problem report - generation %s\n" generation)(flush)
(println "Test total error for best:" best-total-test-error)
(println (format "Test mean error for best: %.5f" (double (/ best-total-test-error (count best-test-errors)))))
(when (zero? (:total-error best))
(doseq [[i error] (map vector
(range)
best-test-errors)]
(println (format "Test Case %3d | Error: %s" i (str error)))))
(println ";;------------------------------")
(println "Outputs of best individual on training cases:")
(error-function best-program :train true)
(println ";;******************************")
)) ;; To do validation, could have this function return an altered best individual
;; with total-error > 0 if it had error of zero on train but not on validation
;; set. Would need a third category of data cases, or a defined split of training cases.
; Define the argmap
(def argmap
{:error-function (digits-error-function digits-data-domains)
:atom-generators digits-atom-generators
:max-points 600
:max-genome-size-in-initial-program 150
:evalpush-limit 600
:population-size 1000
:max-generations 300
:parent-selection :lexicase
:genetic-operator-probabilities {:alternation 0.2
:uniform-mutation 0.2
:uniform-close-mutation 0.1
[:alternation :uniform-mutation] 0.5
}
:alternation-rate 0.01
:alignment-deviation 10
:uniform-mutation-rate 0.01
:problem-specific-report digits-report
:print-behavioral-diversity true
:report-simplifications 0
:final-report-simplifications 5000
:max-error 5000
})