MilesCranmer commited on
Commit
a1e142a
1 Parent(s): 395823a

Parametrize probability weights

Browse files
Files changed (4) hide show
  1. README.md +3 -15
  2. eureqa.jl +11 -11
  3. eureqa.py +31 -9
  4. operators.jl +1 -0
README.md CHANGED
@@ -86,21 +86,9 @@ Then just specify the operator names in your call, as above.
86
  You can also change the dataset learned on by passing in `X` and `y` as
87
  numpy arrays to `eureqa(...)`.
88
 
89
- One can also adjust the relative probabilities of each operation here,
90
- inside `eureqa.jl`:
91
- ```julia
92
- weights = [8, 1, 1, 1, 0.1, 0.5, 2]
93
- ```
94
- for:
95
-
96
- 1. Perturb constant
97
- 2. Mutate operator
98
- 3. Append a node
99
- 4. Delete a subtree
100
- 5. Simplify equation
101
- 6. Randomize completely
102
- 7. Do nothing
103
-
104
 
105
  # TODO
106
 
 
86
  You can also change the dataset learned on by passing in `X` and `y` as
87
  numpy arrays to `eureqa(...)`.
88
 
89
+ One can also adjust the relative probabilities of each mutation operation
90
+ with the `weight...` parameters to `eureqa(...).
91
+ inside `eureqa.jl`.
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  # TODO
94
 
eureqa.jl CHANGED
@@ -15,7 +15,7 @@ function debug(verbosity, string...)
15
  verbosity > 0 ? println(string...) : nothing
16
  end
17
 
18
- function giveBirth()::Int32
19
  return round(Int32, 1e3*(time()-1.6e9))
20
  end
21
 
@@ -326,17 +326,17 @@ end
326
  # exp(-delta/T) defines probability of accepting a change
327
  function iterate(
328
  tree::Node, T::Float32,
329
- alpha::Float32=1.0f0;
330
  annealing::Bool=true
331
  )::Node
332
  prev = tree
333
  tree = deepcopy(tree)
334
 
335
  mutationChoice = rand()
336
- weight_for_constant = min(8, countConstants(tree))
337
- weights = [weight_for_constant, 1, 1, 1, 0.1, 0.5, 2] .* 1.0
338
- weights /= sum(weights)
339
- cweights = cumsum(weights)
 
340
  n = countNodes(tree)
341
 
342
  if mutationChoice < cweights[1]
@@ -386,8 +386,8 @@ mutable struct PopMember
386
  score::Float32
387
  birth::Int32
388
 
389
- PopMember(t::Node) = new(t, scoreFunc(t), giveBirth())
390
- PopMember(t::Node, score::Float32) = new(t, score, giveBirth())
391
 
392
  end
393
 
@@ -429,10 +429,10 @@ function iterateSample(
429
  allstar = bestOfSample(pop)
430
  new = iterate(
431
  allstar.tree, T,
432
- alpha, annealing=annealing)
433
  allstar.tree = new
434
  allstar.score = scoreFunc(new)
435
- allstar.birth = giveBirth()
436
  return allstar
437
  end
438
 
@@ -530,7 +530,7 @@ function optimizeConstants(member::PopMember)::PopMember
530
  if Optim.converged(result)
531
  setConstants(member.tree, result.minimizer)
532
  member.score = convert(Float32, result.minimum)
533
- member.birth = giveBirth()
534
  else
535
  setConstants(member.tree, x0)
536
  end
 
15
  verbosity > 0 ? println(string...) : nothing
16
  end
17
 
18
+ function getTime()::Int32
19
  return round(Int32, 1e3*(time()-1.6e9))
20
  end
21
 
 
326
  # exp(-delta/T) defines probability of accepting a change
327
  function iterate(
328
  tree::Node, T::Float32,
 
329
  annealing::Bool=true
330
  )::Node
331
  prev = tree
332
  tree = deepcopy(tree)
333
 
334
  mutationChoice = rand()
335
+ weightAdjustmentMutateConstant = min(8, countConstants(tree))/8.0
336
+ cur_weights = deepcopy(mutationWeights) .* 1.0
337
+ cur_weights[1] *= weightAdjustmentMutateConstant
338
+ cur_weights /= sum(cur_weights)
339
+ cweights = cumsum(cur_weights)
340
  n = countNodes(tree)
341
 
342
  if mutationChoice < cweights[1]
 
386
  score::Float32
387
  birth::Int32
388
 
389
+ PopMember(t::Node) = new(t, scoreFunc(t), getTime())
390
+ PopMember(t::Node, score::Float32) = new(t, score, getTime())
391
 
392
  end
393
 
 
429
  allstar = bestOfSample(pop)
430
  new = iterate(
431
  allstar.tree, T,
432
+ annealing=annealing)
433
  allstar.tree = new
434
  allstar.score = scoreFunc(new)
435
+ allstar.birth = getTime()
436
  return allstar
437
  end
438
 
 
530
  if Optim.converged(result)
531
  setConstants(member.tree, result.minimizer)
532
  member.score = convert(Float32, result.minimum)
533
+ member.birth = getTime()
534
  else
535
  setConstants(member.tree, x0)
536
  end
eureqa.py CHANGED
@@ -7,15 +7,22 @@ import pandas as pd
7
 
8
 
9
  def eureqa(X=None, y=None, threads=4, parsimony=1e-3, alpha=10,
10
- maxsize=20, migration=True,
11
- hofMigration=True, fractionReplacedHof=0.1,
12
- shouldOptimizeConstants=True,
13
- binary_operators=["plus", "mult"],
14
- unary_operators=["cos", "exp", "sin"],
15
- niterations=20, npop=100, annealing=True,
16
- ncyclesperiteration=5000, fractionReplaced=0.1,
17
- topn=10, equation_file='hall_of_fame.csv',
18
- test='simple1'
 
 
 
 
 
 
 
19
  ):
20
  """ Runs symbolic regression in Julia, to fit y given X.
21
  Either provide a 2D numpy array for X, 1D array for y, or declare a test to run.
@@ -74,6 +81,12 @@ def eureqa(X=None, y=None, threads=4, parsimony=1e-3, alpha=10,
74
  eval_str = "np.sign(X[:, 2])*np.abs(X[:, 2])**2.5 + 5*np.cos(X[:, 3]) - 5"
75
  elif test == 'simple2':
76
  eval_str = "np.sign(X[:, 2])*np.abs(X[:, 2])**3.5 + 1/np.abs(X[:, 0])"
 
 
 
 
 
 
77
 
78
  X = np.random.randn(100, 5)*3
79
  y = eval(eval_str)
@@ -93,6 +106,15 @@ def eureqa(X=None, y=None, threads=4, parsimony=1e-3, alpha=10,
93
  const shouldOptimizeConstants = {'true' if shouldOptimizeConstants else 'false'}
94
  const hofFile = "{equation_file}"
95
  const nthreads = {threads:d}
 
 
 
 
 
 
 
 
 
96
  """
97
 
98
  assert len(X.shape) == 2
 
7
 
8
 
9
  def eureqa(X=None, y=None, threads=4, parsimony=1e-3, alpha=10,
10
+ maxsize=20, migration=True,
11
+ hofMigration=True, fractionReplacedHof=0.1,
12
+ shouldOptimizeConstants=True,
13
+ binary_operators=["plus", "mult"],
14
+ unary_operators=["cos", "exp", "sin"],
15
+ niterations=20, npop=100, annealing=True,
16
+ ncyclesperiteration=5000, fractionReplaced=0.1,
17
+ topn=10, equation_file='hall_of_fame.csv',
18
+ test='simple1',
19
+ weightMutateConstant=4.0,
20
+ weightMutateOperator=0.5,
21
+ weightAddNode=0.5,
22
+ weightDeleteNode=0.5,
23
+ weightSimplify=0.05,
24
+ weightRandomize=0.25,
25
+ weightDoNothing=1.0,
26
  ):
27
  """ Runs symbolic regression in Julia, to fit y given X.
28
  Either provide a 2D numpy array for X, 1D array for y, or declare a test to run.
 
81
  eval_str = "np.sign(X[:, 2])*np.abs(X[:, 2])**2.5 + 5*np.cos(X[:, 3]) - 5"
82
  elif test == 'simple2':
83
  eval_str = "np.sign(X[:, 2])*np.abs(X[:, 2])**3.5 + 1/np.abs(X[:, 0])"
84
+ elif test == 'simple3':
85
+ eval_str = "np.exp(X[:, 0]/2) + 12.0 + np.log(np.abs(X[:, 0])*10 + 1)"
86
+ elif test == 'simple4':
87
+ eval_str = "1.0 + 3*X[:, 0]**2 - 0.5*X[:, 0]**3 + 0.1*X[:, 0]**4"
88
+ elif test == 'simple5':
89
+ eval_str = "(np.exp(X[:, 3]) + 3)/(X[:, 1] + np.cos(X[:, 0]))"
90
 
91
  X = np.random.randn(100, 5)*3
92
  y = eval(eval_str)
 
106
  const shouldOptimizeConstants = {'true' if shouldOptimizeConstants else 'false'}
107
  const hofFile = "{equation_file}"
108
  const nthreads = {threads:d}
109
+ const mutationWeights = [
110
+ {weightMutateConstant:f},
111
+ {weightMutateOperator:f},
112
+ {weightAddNode:f},
113
+ {weightDeleteNode:f},
114
+ {weightSimplify:f},
115
+ {weightRandomize:f},
116
+ {weightDoNothing:f}
117
+ ]
118
  """
119
 
120
  assert len(X.shape) == 2
operators.jl CHANGED
@@ -3,3 +3,4 @@ plus(x::Float32, y::Float32)::Float32 = x+y
3
  mult(x::Float32, y::Float32)::Float32 = x*y;
4
  pow(x::Float32, y::Float32)::Float32 = sign(x)*abs(x)^y;
5
  div(x::Float32, y::Float32)::Float32 = x/y;
 
 
3
  mult(x::Float32, y::Float32)::Float32 = x*y;
4
  pow(x::Float32, y::Float32)::Float32 = sign(x)*abs(x)^y;
5
  div(x::Float32, y::Float32)::Float32 = x/y;
6
+ log(x::Float32)::Float32 = log(abs(x) + 1f-9);