Spaces:
Running
Running
MilesCranmer
commited on
Commit
•
6f3a331
1
Parent(s):
c3d54db
Reduce precision
Browse files- eureqa.jl +66 -62
- paralleleureqa.jl +1 -1
eureqa.jl
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
# Define allowed operators
|
2 |
-
plus(x::
|
3 |
-
mult(x::
|
4 |
|
5 |
##########################
|
6 |
# # Allowed operators
|
@@ -17,16 +17,16 @@ const nvar = 5;
|
|
17 |
#
|
18 |
##########################
|
19 |
# # Dataset to learn
|
20 |
-
const X = randn(100, nvar)*2
|
21 |
-
const y = ((cx,)->cx^2).(X[:, 2]) + cos.(X[:, 3])
|
22 |
##########################
|
23 |
|
24 |
##################
|
25 |
# Hyperparameters
|
26 |
# How much to punish complexity
|
27 |
-
const parsimony =
|
28 |
# How much to scale temperature by (T between 0 and 1)
|
29 |
-
const alpha =
|
30 |
const maxsize = 20
|
31 |
##################
|
32 |
|
@@ -38,27 +38,27 @@ const nops = nuna + nbin
|
|
38 |
# Define a serialization format for the symbolic equations:
|
39 |
mutable struct Node
|
40 |
#Holds operators, variables, constants in a tree
|
41 |
-
degree::
|
42 |
-
val::Union{
|
43 |
constant::Bool #false if variable
|
44 |
op::Function #enumerates operator (for degree=1,2)
|
45 |
l::Union{Node, Nothing}
|
46 |
r::Union{Node, Nothing}
|
47 |
|
48 |
-
Node(val::
|
49 |
-
Node(val::
|
50 |
-
Node(op, l::Node) = new(1, 0.
|
51 |
-
Node(op, l::Union{
|
52 |
-
Node(op, l::Node, r::Node) = new(2, 0.
|
53 |
|
54 |
#Allow to pass the leaf value without additional node call:
|
55 |
-
Node(op, l::Union{
|
56 |
-
Node(op, l::Node, r::Union{
|
57 |
-
Node(op, l::Union{
|
58 |
end
|
59 |
|
60 |
# Evaluate a symbolic equation:
|
61 |
-
function evalTree(tree::Node, x::Array{
|
62 |
if tree.degree == 0
|
63 |
if tree.constant
|
64 |
return tree.val
|
@@ -73,7 +73,7 @@ function evalTree(tree::Node, x::Array{Float64, 1}=Float64[])::Float64
|
|
73 |
end
|
74 |
|
75 |
# Count the operators, constants, variables in an equation
|
76 |
-
function countNodes(tree::Node)::
|
77 |
if tree.degree == 0
|
78 |
return 1
|
79 |
elseif tree.degree == 1
|
@@ -129,7 +129,7 @@ function randomNode(tree::Node)::Node
|
|
129 |
end
|
130 |
|
131 |
# Count the number of unary operators in the equation
|
132 |
-
function countUnaryOperators(tree::Node)::
|
133 |
if tree.degree == 0
|
134 |
return 0
|
135 |
elseif tree.degree == 1
|
@@ -140,7 +140,7 @@ function countUnaryOperators(tree::Node)::Int
|
|
140 |
end
|
141 |
|
142 |
# Count the number of binary operators in the equation
|
143 |
-
function countBinaryOperators(tree::Node)::
|
144 |
if tree.degree == 0
|
145 |
return 0
|
146 |
elseif tree.degree == 1
|
@@ -151,7 +151,7 @@ function countBinaryOperators(tree::Node)::Int
|
|
151 |
end
|
152 |
|
153 |
# Count the number of operators in the equation
|
154 |
-
function countOperators(tree::Node)::
|
155 |
return countUnaryOperators(tree) + countBinaryOperators(tree)
|
156 |
end
|
157 |
|
@@ -174,9 +174,9 @@ function mutateOperator(tree::Node)::Node
|
|
174 |
end
|
175 |
|
176 |
# Count the number of constants in an equation
|
177 |
-
function countConstants(tree::Node)::
|
178 |
if tree.degree == 0
|
179 |
-
return convert(
|
180 |
elseif tree.degree == 1
|
181 |
return 0 + countConstants(tree.l)
|
182 |
else
|
@@ -186,8 +186,8 @@ end
|
|
186 |
|
187 |
# Randomly perturb a constant
|
188 |
function mutateConstant(
|
189 |
-
tree::Node, T::
|
190 |
-
probNegate::
|
191 |
# T is between 0 and 1.
|
192 |
|
193 |
if countConstants(tree) == 0
|
@@ -198,9 +198,9 @@ function mutateConstant(
|
|
198 |
node = randomNode(tree)
|
199 |
end
|
200 |
|
201 |
-
bottom = 0.
|
202 |
-
maxChange = T + 1.
|
203 |
-
factor = maxChange^rand()
|
204 |
makeConstBigger = rand() > 0.5
|
205 |
|
206 |
if makeConstBigger
|
@@ -219,31 +219,35 @@ end
|
|
219 |
# Evaluate an equation over an array of datapoints
|
220 |
function evalTreeArray(
|
221 |
tree::Node,
|
222 |
-
x::Array{
|
223 |
return mapslices(
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
end
|
229 |
|
230 |
# Sum of square error between two arrays
|
231 |
-
function SSE(x::Array{
|
232 |
return sum(((cx,)->cx^2).(x - y))
|
233 |
end
|
234 |
|
235 |
# Mean of square error between two arrays
|
236 |
-
function MSE(x::Array{
|
237 |
return SSE(x, y)/size(x)[1]
|
238 |
end
|
239 |
|
240 |
# Score an equation
|
241 |
function scoreFunc(
|
242 |
tree::Node,
|
243 |
-
X::Array{
|
244 |
-
y::Array{
|
245 |
-
parsimony::
|
246 |
-
|
|
|
|
|
|
|
|
|
247 |
end
|
248 |
|
249 |
# Add a random unary/binary operation to the end of a tree
|
@@ -256,12 +260,12 @@ function appendRandomOp(tree::Node)::Node
|
|
256 |
choice = rand()
|
257 |
makeNewBinOp = choice < nbin/nops
|
258 |
if rand() > 0.5
|
259 |
-
left = randn()
|
260 |
else
|
261 |
left = rand(1:nvar)
|
262 |
end
|
263 |
if rand() > 0.5
|
264 |
-
right = randn()
|
265 |
else
|
266 |
right = rand(1:nvar)
|
267 |
end
|
@@ -293,7 +297,7 @@ function deleteRandomOp(tree::Node)::Node
|
|
293 |
node = randomNode(tree)
|
294 |
# Can "delete" variable or constant too
|
295 |
if rand() > 0.5
|
296 |
-
val = randn()
|
297 |
else
|
298 |
val = rand(1:nvar)
|
299 |
end
|
@@ -310,10 +314,10 @@ end
|
|
310 |
# Go through one simulated annealing mutation cycle
|
311 |
# exp(-delta/T) defines probability of accepting a change
|
312 |
function iterate(
|
313 |
-
tree::Node, T::
|
314 |
-
X::Array{
|
315 |
-
alpha::
|
316 |
-
mult::
|
317 |
)::Node
|
318 |
prev = deepcopy(tree)
|
319 |
|
@@ -357,8 +361,8 @@ function iterate(
|
|
357 |
end
|
358 |
|
359 |
# Create a random equation by appending random operators
|
360 |
-
function genRandomTree(length::
|
361 |
-
tree = Node(1.
|
362 |
for i=1:length
|
363 |
tree = appendRandomOp(tree)
|
364 |
end
|
@@ -369,21 +373,21 @@ end
|
|
369 |
# Define a member of population by equation, score, and age
|
370 |
mutable struct PopMember
|
371 |
tree::Node
|
372 |
-
score::
|
373 |
-
birth::
|
374 |
|
375 |
-
PopMember(t) = new(t, scoreFunc(t, X, y, parsimony), time()-1.
|
376 |
end
|
377 |
|
378 |
# A list of members of the population, with easy constructors,
|
379 |
# which allow for random generation of new populations
|
380 |
mutable struct Population
|
381 |
members::Array{PopMember, 1}
|
382 |
-
n::
|
383 |
|
384 |
Population(pop::Array{PopMember, 1}) = new(pop, size(pop)[1])
|
385 |
-
Population(npop::
|
386 |
-
Population(npop::
|
387 |
|
388 |
end
|
389 |
|
@@ -407,19 +411,19 @@ function bestSubPop(pop::Population)::Population
|
|
407 |
end
|
408 |
|
409 |
# Mutate the best sampled member of the population
|
410 |
-
function iterateSample(pop::Population, T::
|
411 |
allstar = bestOfSample(pop)
|
412 |
new = iterate(allstar.tree, T, X, y, alpha, parsimony)
|
413 |
allstar.tree = new
|
414 |
allstar.score = scoreFunc(new, X, y, parsimony)
|
415 |
-
allstar.birth = time() - 1.
|
416 |
return allstar
|
417 |
end
|
418 |
|
419 |
# Pass through the population several times, replacing the oldest
|
420 |
# with the fittest of a small subsample
|
421 |
-
function regEvolCycle(pop::Population, T::
|
422 |
-
for i=1:
|
423 |
baby = iterateSample(pop, T)
|
424 |
#printTree(baby.tree)
|
425 |
oldest = argmin([pop.members[member].birth for member=1:pop.n])
|
@@ -432,18 +436,18 @@ end
|
|
432 |
# printing the fittest equation every 10% through
|
433 |
function run(
|
434 |
pop::Population,
|
435 |
-
ncycles::
|
436 |
annealing::Bool=false;
|
437 |
-
verbose::
|
438 |
)::Population
|
439 |
pop = deepcopy(pop)
|
440 |
|
441 |
-
allT = LinRange(1.
|
442 |
for iT in 1:size(allT)[1]
|
443 |
if annealing
|
444 |
pop = regEvolCycle(pop, allT[iT])
|
445 |
else
|
446 |
-
pop = regEvolCycle(pop, 1.
|
447 |
end
|
448 |
if verbose > 0 && (iT % verbose == 0)
|
449 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|
|
|
1 |
# Define allowed operators
|
2 |
+
plus(x::Float32, y::Float32) = x+y
|
3 |
+
mult(x::Float32, y::Float32) = x*y;
|
4 |
|
5 |
##########################
|
6 |
# # Allowed operators
|
|
|
17 |
#
|
18 |
##########################
|
19 |
# # Dataset to learn
|
20 |
+
const X = convert(Array{Float32, 2}, randn(100, nvar)*2)
|
21 |
+
const y = convert(Array{Float32, 1}, ((cx,)->cx^2).(X[:, 2]) + cos.(X[:, 3]))
|
22 |
##########################
|
23 |
|
24 |
##################
|
25 |
# Hyperparameters
|
26 |
# How much to punish complexity
|
27 |
+
const parsimony = 1f-3
|
28 |
# How much to scale temperature by (T between 0 and 1)
|
29 |
+
const alpha = 10.0f0
|
30 |
const maxsize = 20
|
31 |
##################
|
32 |
|
|
|
38 |
# Define a serialization format for the symbolic equations:
|
39 |
mutable struct Node
|
40 |
#Holds operators, variables, constants in a tree
|
41 |
+
degree::Integer #0 for constant/variable, 1 for cos/sin, 2 for +/* etc.
|
42 |
+
val::Union{Float32, Integer} #Either const value, or enumerates variable
|
43 |
constant::Bool #false if variable
|
44 |
op::Function #enumerates operator (for degree=1,2)
|
45 |
l::Union{Node, Nothing}
|
46 |
r::Union{Node, Nothing}
|
47 |
|
48 |
+
Node(val::Float32) = new(0, val, true, id, nothing, nothing)
|
49 |
+
Node(val::Integer) = new(0, val, false, id, nothing, nothing)
|
50 |
+
Node(op, l::Node) = new(1, 0.0f0, false, op, l, nothing)
|
51 |
+
Node(op, l::Union{Float32, Integer}) = new(1, 0.0f0, false, op, Node(l), nothing)
|
52 |
+
Node(op, l::Node, r::Node) = new(2, 0.0f0, false, op, l, r)
|
53 |
|
54 |
#Allow to pass the leaf value without additional node call:
|
55 |
+
Node(op, l::Union{Float32, Integer}, r::Node) = new(2, 0.0f0, false, op, Node(l), r)
|
56 |
+
Node(op, l::Node, r::Union{Float32, Integer}) = new(2, 0.0f0, false, op, l, Node(r))
|
57 |
+
Node(op, l::Union{Float32, Integer}, r::Union{Float32, Integer}) = new(2, 0.0f0, false, op, Node(l), Node(r))
|
58 |
end
|
59 |
|
60 |
# Evaluate a symbolic equation:
|
61 |
+
function evalTree(tree::Node, x::Array{Float32, 1}=Float32[])::Float32
|
62 |
if tree.degree == 0
|
63 |
if tree.constant
|
64 |
return tree.val
|
|
|
73 |
end
|
74 |
|
75 |
# Count the operators, constants, variables in an equation
|
76 |
+
function countNodes(tree::Node)::Integer
|
77 |
if tree.degree == 0
|
78 |
return 1
|
79 |
elseif tree.degree == 1
|
|
|
129 |
end
|
130 |
|
131 |
# Count the number of unary operators in the equation
|
132 |
+
function countUnaryOperators(tree::Node)::Integer
|
133 |
if tree.degree == 0
|
134 |
return 0
|
135 |
elseif tree.degree == 1
|
|
|
140 |
end
|
141 |
|
142 |
# Count the number of binary operators in the equation
|
143 |
+
function countBinaryOperators(tree::Node)::Integer
|
144 |
if tree.degree == 0
|
145 |
return 0
|
146 |
elseif tree.degree == 1
|
|
|
151 |
end
|
152 |
|
153 |
# Count the number of operators in the equation
|
154 |
+
function countOperators(tree::Node)::Integer
|
155 |
return countUnaryOperators(tree) + countBinaryOperators(tree)
|
156 |
end
|
157 |
|
|
|
174 |
end
|
175 |
|
176 |
# Count the number of constants in an equation
|
177 |
+
function countConstants(tree::Node)::Integer
|
178 |
if tree.degree == 0
|
179 |
+
return convert(Integer, tree.constant)
|
180 |
elseif tree.degree == 1
|
181 |
return 0 + countConstants(tree.l)
|
182 |
else
|
|
|
186 |
|
187 |
# Randomly perturb a constant
|
188 |
function mutateConstant(
|
189 |
+
tree::Node, T::Float32,
|
190 |
+
probNegate::Float32=0.01f0)::Node
|
191 |
# T is between 0 and 1.
|
192 |
|
193 |
if countConstants(tree) == 0
|
|
|
198 |
node = randomNode(tree)
|
199 |
end
|
200 |
|
201 |
+
bottom = 0.1f0
|
202 |
+
maxChange = T + 1.0f0 + bottom
|
203 |
+
factor = maxChange^Float32(rand())
|
204 |
makeConstBigger = rand() > 0.5
|
205 |
|
206 |
if makeConstBigger
|
|
|
219 |
# Evaluate an equation over an array of datapoints
|
220 |
function evalTreeArray(
|
221 |
tree::Node,
|
222 |
+
x::Array{Float32, 2})::Array{Float32, 1}
|
223 |
return mapslices(
|
224 |
+
(cx,) -> evalTree(tree, cx),
|
225 |
+
x,
|
226 |
+
dims=[2]
|
227 |
+
)[:, 1]
|
228 |
end
|
229 |
|
230 |
# Sum of square error between two arrays
|
231 |
+
function SSE(x::Array{Float32}, y::Array{Float32})::Float32
|
232 |
return sum(((cx,)->cx^2).(x - y))
|
233 |
end
|
234 |
|
235 |
# Mean of square error between two arrays
|
236 |
+
function MSE(x::Array{Float32}, y::Array{Float32})::Float32
|
237 |
return SSE(x, y)/size(x)[1]
|
238 |
end
|
239 |
|
240 |
# Score an equation
|
241 |
function scoreFunc(
|
242 |
tree::Node,
|
243 |
+
X::Array{Float32, 2},
|
244 |
+
y::Array{Float32, 1},
|
245 |
+
parsimony::Float32=0.1f0)::Float32
|
246 |
+
try
|
247 |
+
return MSE(evalTreeArray(tree, X), y) + countNodes(tree)*parsimony
|
248 |
+
catch error
|
249 |
+
return 1f9
|
250 |
+
end
|
251 |
end
|
252 |
|
253 |
# Add a random unary/binary operation to the end of a tree
|
|
|
260 |
choice = rand()
|
261 |
makeNewBinOp = choice < nbin/nops
|
262 |
if rand() > 0.5
|
263 |
+
left = Float32(randn())
|
264 |
else
|
265 |
left = rand(1:nvar)
|
266 |
end
|
267 |
if rand() > 0.5
|
268 |
+
right = Float32(randn())
|
269 |
else
|
270 |
right = rand(1:nvar)
|
271 |
end
|
|
|
297 |
node = randomNode(tree)
|
298 |
# Can "delete" variable or constant too
|
299 |
if rand() > 0.5
|
300 |
+
val = Float32(randn())
|
301 |
else
|
302 |
val = rand(1:nvar)
|
303 |
end
|
|
|
314 |
# Go through one simulated annealing mutation cycle
|
315 |
# exp(-delta/T) defines probability of accepting a change
|
316 |
function iterate(
|
317 |
+
tree::Node, T::Float32,
|
318 |
+
X::Array{Float32, 2}, y::Array{Float32, 1},
|
319 |
+
alpha::Float32=1.0f0,
|
320 |
+
mult::Float32=0.1f0
|
321 |
)::Node
|
322 |
prev = deepcopy(tree)
|
323 |
|
|
|
361 |
end
|
362 |
|
363 |
# Create a random equation by appending random operators
|
364 |
+
function genRandomTree(length::Integer)::Node
|
365 |
+
tree = Node(1.0f0)
|
366 |
for i=1:length
|
367 |
tree = appendRandomOp(tree)
|
368 |
end
|
|
|
373 |
# Define a member of population by equation, score, and age
|
374 |
mutable struct PopMember
|
375 |
tree::Node
|
376 |
+
score::Float32
|
377 |
+
birth::Float32
|
378 |
|
379 |
+
PopMember(t) = new(t, scoreFunc(t, X, y, parsimony), Float32(time())-1.6f9)
|
380 |
end
|
381 |
|
382 |
# A list of members of the population, with easy constructors,
|
383 |
# which allow for random generation of new populations
|
384 |
mutable struct Population
|
385 |
members::Array{PopMember, 1}
|
386 |
+
n::Integer
|
387 |
|
388 |
Population(pop::Array{PopMember, 1}) = new(pop, size(pop)[1])
|
389 |
+
Population(npop::Integer) = new([PopMember(genRandomTree(3)) for i=1:npop], npop)
|
390 |
+
Population(npop::Integer, nlength::Integer) = new([PopMember(genRandomTree(nlength)) for i=1:npop], npop)
|
391 |
|
392 |
end
|
393 |
|
|
|
411 |
end
|
412 |
|
413 |
# Mutate the best sampled member of the population
|
414 |
+
function iterateSample(pop::Population, T::Float32)::PopMember
|
415 |
allstar = bestOfSample(pop)
|
416 |
new = iterate(allstar.tree, T, X, y, alpha, parsimony)
|
417 |
allstar.tree = new
|
418 |
allstar.score = scoreFunc(new, X, y, parsimony)
|
419 |
+
allstar.birth = Float32(time()) - 1.6f9
|
420 |
return allstar
|
421 |
end
|
422 |
|
423 |
# Pass through the population several times, replacing the oldest
|
424 |
# with the fittest of a small subsample
|
425 |
+
function regEvolCycle(pop::Population, T::Float32)::Population
|
426 |
+
for i=1:Integer(pop.n/ns)
|
427 |
baby = iterateSample(pop, T)
|
428 |
#printTree(baby.tree)
|
429 |
oldest = argmin([pop.members[member].birth for member=1:pop.n])
|
|
|
436 |
# printing the fittest equation every 10% through
|
437 |
function run(
|
438 |
pop::Population,
|
439 |
+
ncycles::Integer,
|
440 |
annealing::Bool=false;
|
441 |
+
verbose::Integer=0
|
442 |
)::Population
|
443 |
pop = deepcopy(pop)
|
444 |
|
445 |
+
allT = LinRange(1.0f0, 0.0f0, ncycles)
|
446 |
for iT in 1:size(allT)[1]
|
447 |
if annealing
|
448 |
pop = regEvolCycle(pop, allT[iT])
|
449 |
else
|
450 |
+
pop = regEvolCycle(pop, 1.0f0)
|
451 |
end
|
452 |
if verbose > 0 && (iT % verbose == 0)
|
453 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|
paralleleureqa.jl
CHANGED
@@ -17,7 +17,7 @@ for k=1:niterations
|
|
17 |
|
18 |
# Spawn threads to run indepdent evolutions, then gather them
|
19 |
@inbounds Threads.@threads for i=1:nthreads
|
20 |
-
allPops[i] = run(allPops[i], ncyclesperiteration, annealing, verbose=
|
21 |
end
|
22 |
|
23 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|
|
|
17 |
|
18 |
# Spawn threads to run indepdent evolutions, then gather them
|
19 |
@inbounds Threads.@threads for i=1:nthreads
|
20 |
+
allPops[i] = run(allPops[i], ncyclesperiteration, annealing, verbose=500)
|
21 |
end
|
22 |
|
23 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|