MilesCranmer commited on
Commit
c3d240e
1 Parent(s): ec48038

Add benchmark file

Browse files
Files changed (5) hide show
  1. README.md +26 -8
  2. benchmark.jl +14 -0
  3. benchmark.sh +1 -0
  4. eureqa.jl +12 -9
  5. paralleleureqa.jl +11 -11
README.md CHANGED
@@ -1,14 +1,36 @@
1
  # Running:
2
 
3
- For now, just modify the script in `paralleleureqa.jl`
4
- to your liking and run:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- `julia --threads auto -O3 paralleleureqa.jl`
 
7
 
8
  ## Modification
9
 
10
  You can change the binary and unary operators in `eureqa.jl` here:
11
- ```
12
  const binops = [plus, mult]
13
  const unaops = [sin, cos, exp];
14
  ```
@@ -28,10 +50,6 @@ by either loading in a dataset, or modifying the definition of `y`.
28
 
29
  ### Hyperparameters
30
 
31
- Turn on annealing by setting the following in `paralleleureqa.jl`:
32
-
33
- `const annealing = true`
34
-
35
  Annealing allows each evolutionary cycle to turn down the exploration
36
  rate over time: at the end (temperature 0), it will only select solutions
37
  better than existing solutions.
 
1
  # Running:
2
 
3
+ You can run the performance benchmark with `./benchmark.sh`.
4
+
5
+ Modify the search code in `paralleleureqa.jl` and `eureqa.jl` to your liking
6
+ (see below for options). Then, in a new Julia file called
7
+ `myfile.jl`, you can write:
8
+
9
+ ```julia
10
+ include("paralleleureqa.jl")
11
+ fullRun(10,
12
+ npop=100,
13
+ annealing=true,
14
+ ncyclesperiteration=1000,
15
+ fractionReplaced=0.1f0,
16
+ verbosity=100)
17
+ ```
18
+ The first arg is the number of migration periods to run,
19
+ with `ncyclesperiteration` determining how many generations
20
+ per migration period. `npop` is the number of population members.
21
+ `annealing` determines whether to stay in exploration mode,
22
+ or tune it down with each cycle. `fractionReplaced` is
23
+ how much of the population is replaced by migrated equations each
24
+ step.
25
+
26
 
27
+ Run it with threading turned on using:
28
+ `julia --threads auto -O3 myfile.jl`
29
 
30
  ## Modification
31
 
32
  You can change the binary and unary operators in `eureqa.jl` here:
33
+ ```julia
34
  const binops = [plus, mult]
35
  const unaops = [sin, cos, exp];
36
  ```
 
50
 
51
  ### Hyperparameters
52
 
 
 
 
 
53
  Annealing allows each evolutionary cycle to turn down the exploration
54
  rate over time: at the end (temperature 0), it will only select solutions
55
  better than existing solutions.
benchmark.jl ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ include("paralleleureqa.jl")
2
+ fullRun(1,
3
+ npop=100,
4
+ annealing=true,
5
+ ncyclesperiteration=1000,
6
+ fractionReplaced=0.1f0,
7
+ verbosity=0)
8
+ @time fullRun(3,
9
+ npop=100,
10
+ annealing=true,
11
+ ncyclesperiteration=1000,
12
+ fractionReplaced=0.1f0,
13
+ verbosity=0
14
+ )
benchmark.sh ADDED
@@ -0,0 +1 @@
 
 
1
+ julia --threads 8 -O3 benchmark.jl
eureqa.jl CHANGED
@@ -35,6 +35,10 @@ const nbin = size(binops)[1]
35
  const nops = nuna + nbin
36
  const nvar = size(X)[2];
37
 
 
 
 
 
38
  # Define a serialization format for the symbolic equations:
39
  mutable struct Node
40
  #Holds operators, variables, constants in a tree
@@ -241,7 +245,7 @@ end
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
@@ -341,8 +345,8 @@ function iterate(
341
  end
342
 
343
  try
344
- beforeLoss = scoreFunc(prev, X, y, mult)
345
- afterLoss = scoreFunc(tree, X, y, mult)
346
  delta = afterLoss - beforeLoss
347
  probChange = exp(-delta/(T*alpha))
348
 
@@ -378,7 +382,7 @@ mutable struct PopMember
378
  score::Float32
379
  birth::Int32
380
 
381
- PopMember(t) = new(t, scoreFunc(t, X, y, parsimony), round(Int32, 1e3*(time()-1.6e9))
382
  )
383
  end
384
 
@@ -418,7 +422,7 @@ function iterateSample(pop::Population, T::Float32)::PopMember
418
  allstar = bestOfSample(pop)
419
  new = iterate(allstar.tree, T, X, y, alpha, parsimony)
420
  allstar.tree = new
421
- allstar.score = scoreFunc(new, X, y, parsimony)
422
  allstar.birth = round(Int32, 1e3*(time()-1.6e9))
423
  return allstar
424
  end
@@ -441,7 +445,7 @@ function run(
441
  pop::Population,
442
  ncycles::Integer,
443
  annealing::Bool=false;
444
- verbose::Integer=0
445
  )::Population
446
  pop = deepcopy(pop)
447
 
@@ -452,12 +456,11 @@ function run(
452
  else
453
  pop = regEvolCycle(pop, 1.0f0)
454
  end
455
- if verbose > 0 && (iT % verbose == 0)
456
- # Get best 10 models from each evolution. Copy because we re-assign later.
457
  bestPops = bestSubPop(pop)
458
  bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
459
  bestCurScore = bestPops.members[bestCurScoreIdx].score
460
- println(bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
461
  end
462
  end
463
  return pop
 
35
  const nops = nuna + nbin
36
  const nvar = size(X)[2];
37
 
38
+ function debug(verbosity, string...)
39
+ verbosity > 0 ? println(string...) : nothing
40
+ end
41
+
42
  # Define a serialization format for the symbolic equations:
43
  mutable struct Node
44
  #Holds operators, variables, constants in a tree
 
245
  function scoreFunc(
246
  tree::Node,
247
  X::Array{Float32, 2},
248
+ y::Array{Float32, 1};
249
  parsimony::Float32=0.1f0)::Float32
250
  try
251
  return MSE(evalTreeArray(tree, X), y) + countNodes(tree)*parsimony
 
345
  end
346
 
347
  try
348
+ beforeLoss = scoreFunc(prev, X, y, parsimony=mult)
349
+ afterLoss = scoreFunc(tree, X, y, parsimony=mult)
350
  delta = afterLoss - beforeLoss
351
  probChange = exp(-delta/(T*alpha))
352
 
 
382
  score::Float32
383
  birth::Int32
384
 
385
+ PopMember(t) = new(t, scoreFunc(t, X, y, parsimony=parsimony), round(Int32, 1e3*(time()-1.6e9))
386
  )
387
  end
388
 
 
422
  allstar = bestOfSample(pop)
423
  new = iterate(allstar.tree, T, X, y, alpha, parsimony)
424
  allstar.tree = new
425
+ allstar.score = scoreFunc(new, X, y, parsimony=parsimony)
426
  allstar.birth = round(Int32, 1e3*(time()-1.6e9))
427
  return allstar
428
  end
 
445
  pop::Population,
446
  ncycles::Integer,
447
  annealing::Bool=false;
448
+ verbosity::Integer=0
449
  )::Population
450
  pop = deepcopy(pop)
451
 
 
456
  else
457
  pop = regEvolCycle(pop, 1.0f0)
458
  end
459
+ if verbosity > 0 && (iT % verbosity == 0)
 
460
  bestPops = bestSubPop(pop)
461
  bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
462
  bestCurScore = bestPops.members[bestCurScoreIdx].score
463
+ debug(verbosity, bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
464
  end
465
  end
466
  return pop
paralleleureqa.jl CHANGED
@@ -1,30 +1,31 @@
1
  include("eureqa.jl")
2
 
3
- println("Lets try to learn (x2^2 + cos(x3)) using regularized evolution from scratch")
4
  const nthreads = Threads.nthreads()
5
- println("Running with $nthreads threads")
6
- const npop = 300
7
- const annealing = true
8
- const ncyclesperiteration = 30000
9
- const fractionReplaced = 0.1
10
 
11
- function fullRun(niterations::Integer)
 
 
 
 
 
 
 
 
12
  # Generate random initial populations
13
  allPops = [Population(npop, 3) for j=1:nthreads]
14
  # Repeat this many evolutions; we collect and migrate the best
15
  # each time.
16
  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=500)
21
  end
22
 
23
  # Get best 10 models from each evolution. Copy because we re-assign later.
24
  bestPops = deepcopy(Population([member for pop in allPops for member in bestSubPop(pop).members]))
25
  bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
26
  bestCurScore = bestPops.members[bestCurScoreIdx].score
27
- println(bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
28
 
29
  # Migration
30
  for j=1:nthreads
@@ -36,4 +37,3 @@ function fullRun(niterations::Integer)
36
  end
37
  end
38
 
39
- fullRun(10)
 
1
  include("eureqa.jl")
2
 
 
3
  const nthreads = Threads.nthreads()
 
 
 
 
 
4
 
5
+ function fullRun(niterations::Integer;
6
+ npop::Integer=300,
7
+ annealing::Bool=true,
8
+ ncyclesperiteration::Integer=3000,
9
+ fractionReplaced::Float32=0.1f0,
10
+ verbosity::Integer=0,
11
+ )
12
+ debug(verbosity, "Lets try to learn (x2^2 + cos(x3)) using regularized evolution from scratch")
13
+ debug(verbosity, "Running with $nthreads threads")
14
  # Generate random initial populations
15
  allPops = [Population(npop, 3) for j=1:nthreads]
16
  # Repeat this many evolutions; we collect and migrate the best
17
  # each time.
18
  for k=1:niterations
 
19
  # Spawn threads to run indepdent evolutions, then gather them
20
  @inbounds Threads.@threads for i=1:nthreads
21
+ allPops[i] = run(allPops[i], ncyclesperiteration, annealing, verbosity=verbosity)
22
  end
23
 
24
  # Get best 10 models from each evolution. Copy because we re-assign later.
25
  bestPops = deepcopy(Population([member for pop in allPops for member in bestSubPop(pop).members]))
26
  bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
27
  bestCurScore = bestPops.members[bestCurScoreIdx].score
28
+ debug(verbosity, bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
29
 
30
  # Migration
31
  for j=1:nthreads
 
37
  end
38
  end
39