MilesCranmer commited on
Commit
6f3a331
1 Parent(s): c3d54db

Reduce precision

Browse files
Files changed (2) hide show
  1. eureqa.jl +66 -62
  2. paralleleureqa.jl +1 -1
eureqa.jl CHANGED
@@ -1,6 +1,6 @@
1
  # Define allowed operators
2
- plus(x::Float64, y::Float64) = x+y
3
- mult(x::Float64, y::Float64) = x*y;
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 = 1e-3
28
  # How much to scale temperature by (T between 0 and 1)
29
- const alpha = 100.0
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::Int #0 for constant/variable, 1 for cos/sin, 2 for +/* etc.
42
- val::Union{Float64, Int} #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::Float64) = new(0, val, true, id, nothing, nothing)
49
- Node(val::Int) = new(0, val, false, id, nothing, nothing)
50
- Node(op, l::Node) = new(1, 0.0, false, op, l, nothing)
51
- Node(op, l::Union{Float64, Int}) = new(1, 0.0, false, op, Node(l), nothing)
52
- Node(op, l::Node, r::Node) = new(2, 0.0, false, op, l, r)
53
 
54
  #Allow to pass the leaf value without additional node call:
55
- Node(op, l::Union{Float64, Int}, r::Node) = new(2, 0.0, false, op, Node(l), r)
56
- Node(op, l::Node, r::Union{Float64, Int}) = new(2, 0.0, false, op, l, Node(r))
57
- Node(op, l::Union{Float64, Int}, r::Union{Float64, Int}) = new(2, 0.0, false, op, Node(l), Node(r))
58
  end
59
 
60
  # Evaluate a symbolic equation:
61
- function evalTree(tree::Node, x::Array{Float64, 1}=Float64[])::Float64
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)::Int
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)::Int
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)::Int
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)::Int
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)::Int
178
  if tree.degree == 0
179
- return convert(Int, tree.constant)
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::Float64,
190
- probNegate::Float64=0.01)::Node
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.1
202
- maxChange = T + 1.0 + bottom
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{Float64, 2})::Array{Float64, 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{Float64}, y::Array{Float64})::Float64
232
  return sum(((cx,)->cx^2).(x - y))
233
  end
234
 
235
  # Mean of square error between two arrays
236
- function MSE(x::Array{Float64}, y::Array{Float64})::Float64
237
  return SSE(x, y)/size(x)[1]
238
  end
239
 
240
  # Score an equation
241
  function scoreFunc(
242
  tree::Node,
243
- X::Array{Float64, 2},
244
- y::Array{Float64, 1},
245
- parsimony::Float64=0.1)::Float64
246
- return MSE(evalTreeArray(tree, X), y) + countNodes(tree)*parsimony
 
 
 
 
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::Float64,
314
- X::Array{Float64, 2}, y::Array{Float64, 1},
315
- alpha::Float64=1.0,
316
- mult::Float64=0.1
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::Int)::Node
361
- tree = Node(1.0)
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::Float64
373
- birth::Float64
374
 
375
- PopMember(t) = new(t, scoreFunc(t, X, y, parsimony), time()-1.6e9)
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::Int
383
 
384
  Population(pop::Array{PopMember, 1}) = new(pop, size(pop)[1])
385
- Population(npop::Int64) = new([PopMember(genRandomTree(3)) for i=1:npop], npop)
386
- Population(npop::Int64, nlength::Int64) = new([PopMember(genRandomTree(nlength)) for i=1:npop], 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::Float64)::PopMember
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.6e9
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::Float64)::Population
422
- for i=1:Int(pop.n/ns)
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::Int,
436
  annealing::Bool=false;
437
- verbose::Int=0
438
  )::Population
439
  pop = deepcopy(pop)
440
 
441
- allT = LinRange(1.0, 0.0, ncycles)
442
  for iT in 1:size(allT)[1]
443
  if annealing
444
  pop = regEvolCycle(pop, allT[iT])
445
  else
446
- pop = regEvolCycle(pop, 1.0)
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=1000)
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.