Spaces:
Sleeping
Sleeping
MilesCranmer
commited on
Commit
•
319103f
1
Parent(s):
cf89640
Add option to control max depth instead of size
Browse files- README.md +14 -4
- benchmarks/benchmark.sh +3 -1
- julia/sr.jl +15 -3
- pysr/sr.py +4 -0
README.md
CHANGED
@@ -320,6 +320,7 @@ pd.DataFrame, Results dataframe, giving complexity, MSE, and equations
|
|
320 |
- [ ] Create flexible way of providing "simplification recipes." I.e., plus(plus(T, C), C) => plus(T, +(C, C)). The user could pass these.
|
321 |
- [ ] Consider allowing multi-threading turned off, for faster testing (cache issue on travis). Or could simply fix the caching issue there.
|
322 |
- [ ] Consider returning only the equation of interest; rather than all equations.
|
|
|
323 |
|
324 |
## Algorithmic performance ideas:
|
325 |
|
@@ -332,15 +333,18 @@ pd.DataFrame, Results dataframe, giving complexity, MSE, and equations
|
|
332 |
- [ ] Calculate feature importances based on features we've already seen, then weight those features up in all random generations.
|
333 |
- [ ] Calculate feature importances of future mutations, by looking at correlation between residual of model, and the features.
|
334 |
- Store feature importances of future, and periodically update it.
|
|
|
335 |
|
336 |
|
337 |
## Code performance ideas:
|
338 |
|
|
|
|
|
339 |
- [ ] Add true multi-node processing, with MPI, or just file sharing. Multiple populations per core.
|
340 |
- Ongoing in cluster branch
|
341 |
-
- [ ] Try @spawn over each sub-population. Do random sort, compute mutation for each, then replace 10% oldest.
|
342 |
- [ ] Performance: try inling things?
|
343 |
-
- [ ] Try
|
|
|
344 |
```julia
|
345 |
mutable struct Tree
|
346 |
degree::Array{Integer, 1}
|
@@ -350,8 +354,14 @@ mutable struct Tree
|
|
350 |
Tree(s::Integer) = new(zeros(Integer, s), zeros(Float32, s), zeros(Bool, s), zeros(Integer, s))
|
351 |
end
|
352 |
```
|
353 |
-
|
354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
|
356 |
- [ ] Can we cache calculations, or does the compiler do that? E.g., I should only have to run exp(x0) once; after that it should be read from memory.
|
357 |
- Done on caching branch. Currently am finding that this is quiet slow (presumably because memory allocation is the main issue).
|
|
|
320 |
- [ ] Create flexible way of providing "simplification recipes." I.e., plus(plus(T, C), C) => plus(T, +(C, C)). The user could pass these.
|
321 |
- [ ] Consider allowing multi-threading turned off, for faster testing (cache issue on travis). Or could simply fix the caching issue there.
|
322 |
- [ ] Consider returning only the equation of interest; rather than all equations.
|
323 |
+
- [x] Control max depth, rather than max number of nodes?
|
324 |
|
325 |
## Algorithmic performance ideas:
|
326 |
|
|
|
333 |
- [ ] Calculate feature importances based on features we've already seen, then weight those features up in all random generations.
|
334 |
- [ ] Calculate feature importances of future mutations, by looking at correlation between residual of model, and the features.
|
335 |
- Store feature importances of future, and periodically update it.
|
336 |
+
- [ ] Punish depth rather than size, as depth really hurts during optimization.
|
337 |
|
338 |
|
339 |
## Code performance ideas:
|
340 |
|
341 |
+
- [ ] **Try @spawn over each sub-population. Do random sort, compute mutation for each, then replace 10% oldest.**
|
342 |
+
- [ ] **Try defining a binary tree as an array, rather than a linked list. See https://stackoverflow.com/a/6384714/2689923**
|
343 |
- [ ] Add true multi-node processing, with MPI, or just file sharing. Multiple populations per core.
|
344 |
- Ongoing in cluster branch
|
|
|
345 |
- [ ] Performance: try inling things?
|
346 |
+
- [ ] Try storing things like number nodes in a tree; then can iterate instead of counting
|
347 |
+
|
348 |
```julia
|
349 |
mutable struct Tree
|
350 |
degree::Array{Integer, 1}
|
|
|
354 |
Tree(s::Integer) = new(zeros(Integer, s), zeros(Float32, s), zeros(Bool, s), zeros(Integer, s))
|
355 |
end
|
356 |
```
|
357 |
+
|
358 |
+
- Then, we could even work with trees on the GPU, since they are all pre-allocated arrays.
|
359 |
+
- A population could be a Tree, but with degree 2 on all the degrees. So a slice of population arrays forms a tree.
|
360 |
+
- How many operations can we do via matrix ops? Mutate node=>easy.
|
361 |
+
- Can probably batch and do many operations at once across a population.
|
362 |
+
- Or, across all populations! Mutate operator: index 2D array and set it to random vector? But the indexing might hurt.
|
363 |
+
- The big advantage: can evaluate all new mutated trees at once; as massive matrix operation.
|
364 |
+
- Can control depth, rather than maxsize. Then just pretend all trees are full and same depth. Then we really don't need to care about depth.
|
365 |
|
366 |
- [ ] Can we cache calculations, or does the compiler do that? E.g., I should only have to run exp(x0) once; after that it should be read from memory.
|
367 |
- Done on caching branch. Currently am finding that this is quiet slow (presumably because memory allocation is the main issue).
|
benchmarks/benchmark.sh
CHANGED
@@ -6,7 +6,9 @@ import numpy as np
|
|
6 |
from pysr import pysr
|
7 |
X=np.random.randn(100, 2)*5
|
8 |
y=2*np.sin((X[:, 0]+X[:, 1]))*np.exp(X[:, 1]/3)
|
9 |
-
if version[1] >= 3 and version[2] >=
|
|
|
|
|
10 |
eq = pysr(X, y, binary_operators=["plus", "mult", "div", "pow"], unary_operators=["sin"], niterations=20, procs=4, parsimony=1e-10, npop=1000, ncyclesperiteration=1000)
|
11 |
else:
|
12 |
eq = pysr(X, y, binary_operators=["plus", "mult", "div", "pow"], unary_operators=["sin"], niterations=20, threads=4, parsimony=1e-10, npop=1000, ncyclesperiteration=1000)
|
|
|
6 |
from pysr import pysr
|
7 |
X=np.random.randn(100, 2)*5
|
8 |
y=2*np.sin((X[:, 0]+X[:, 1]))*np.exp(X[:, 1]/3)
|
9 |
+
if version[1] >= 3 and version[2] >= 16:
|
10 |
+
eq = pysr(X, y, binary_operators=["plus", "mult", "div", "pow"], unary_operators=["sin"], niterations=20, procs=4, parsimony=1e-10, npop=1000, ncyclesperiteration=1000, maxdepth=6)
|
11 |
+
elif version[1] >= 3 and version[2] >= 2:
|
12 |
eq = pysr(X, y, binary_operators=["plus", "mult", "div", "pow"], unary_operators=["sin"], niterations=20, procs=4, parsimony=1e-10, npop=1000, ncyclesperiteration=1000)
|
13 |
else:
|
14 |
eq = pysr(X, y, binary_operators=["plus", "mult", "div", "pow"], unary_operators=["sin"], niterations=20, threads=4, parsimony=1e-10, npop=1000, ncyclesperiteration=1000)
|
julia/sr.jl
CHANGED
@@ -103,6 +103,17 @@ function countNodes(tree::Node)::Integer
|
|
103 |
end
|
104 |
end
|
105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
# Convert an equation to a string
|
107 |
function stringTree(tree::Node)::String
|
108 |
if tree.degree == 0
|
@@ -535,14 +546,15 @@ function iterate(member::PopMember, T::Float32)::PopMember
|
|
535 |
cur_weights /= sum(cur_weights)
|
536 |
cweights = cumsum(cur_weights)
|
537 |
n = countNodes(tree)
|
|
|
538 |
|
539 |
if mutationChoice < cweights[1]
|
540 |
tree = mutateConstant(tree, T)
|
541 |
elseif mutationChoice < cweights[2]
|
542 |
tree = mutateOperator(tree)
|
543 |
-
elseif mutationChoice < cweights[3] && n < maxsize
|
544 |
tree = appendRandomOp(tree)
|
545 |
-
elseif mutationChoice < cweights[4] && n < maxsize
|
546 |
tree = insertRandomOp(tree)
|
547 |
elseif mutationChoice < cweights[5]
|
548 |
tree = deleteRandomOp(tree)
|
@@ -551,7 +563,7 @@ function iterate(member::PopMember, T::Float32)::PopMember
|
|
551 |
tree = combineOperators(tree) # See if repeated constants at outer levels
|
552 |
return PopMember(tree, beforeLoss)
|
553 |
elseif mutationChoice < cweights[7]
|
554 |
-
tree = genRandomTree(5) # Sometimes we
|
555 |
else
|
556 |
return PopMember(tree, beforeLoss)
|
557 |
end
|
|
|
103 |
end
|
104 |
end
|
105 |
|
106 |
+
# Count the max depth of a tree
|
107 |
+
function countDepth(tree::Node)::Integer
|
108 |
+
if tree.degree == 0
|
109 |
+
return 1
|
110 |
+
elseif tree.degree == 1
|
111 |
+
return 1 + countDepth(tree.l)
|
112 |
+
else
|
113 |
+
return 1 + max(countDepth(tree.l), countDepth(tree.r))
|
114 |
+
end
|
115 |
+
end
|
116 |
+
|
117 |
# Convert an equation to a string
|
118 |
function stringTree(tree::Node)::String
|
119 |
if tree.degree == 0
|
|
|
546 |
cur_weights /= sum(cur_weights)
|
547 |
cweights = cumsum(cur_weights)
|
548 |
n = countNodes(tree)
|
549 |
+
depth = countDepth(tree)
|
550 |
|
551 |
if mutationChoice < cweights[1]
|
552 |
tree = mutateConstant(tree, T)
|
553 |
elseif mutationChoice < cweights[2]
|
554 |
tree = mutateOperator(tree)
|
555 |
+
elseif mutationChoice < cweights[3] && n < maxsize && depth < maxdepth
|
556 |
tree = appendRandomOp(tree)
|
557 |
+
elseif mutationChoice < cweights[4] && n < maxsize && depth < maxdepth
|
558 |
tree = insertRandomOp(tree)
|
559 |
elseif mutationChoice < cweights[5]
|
560 |
tree = deleteRandomOp(tree)
|
|
|
563 |
tree = combineOperators(tree) # See if repeated constants at outer levels
|
564 |
return PopMember(tree, beforeLoss)
|
565 |
elseif mutationChoice < cweights[7]
|
566 |
+
tree = genRandomTree(5) # Sometimes we generate a new tree completely tree
|
567 |
else
|
568 |
return PopMember(tree, beforeLoss)
|
569 |
end
|
pysr/sr.py
CHANGED
@@ -73,6 +73,7 @@ def pysr(X=None, y=None, weights=None,
|
|
73 |
test='simple1',
|
74 |
verbosity=1e9,
|
75 |
maxsize=20,
|
|
|
76 |
threads=None, #deprecated
|
77 |
julia_optimization=3,
|
78 |
):
|
@@ -135,6 +136,8 @@ def pysr(X=None, y=None, weights=None,
|
|
135 |
"""
|
136 |
if threads is not None:
|
137 |
raise ValueError("The threads kwarg is deprecated. Use procs.")
|
|
|
|
|
138 |
|
139 |
# Check for potential errors before they happen
|
140 |
assert len(unary_operators) + len(binary_operators) > 0
|
@@ -200,6 +203,7 @@ const ns=10;
|
|
200 |
const parsimony = {parsimony:f}f0
|
201 |
const alpha = {alpha:f}f0
|
202 |
const maxsize = {maxsize:d}
|
|
|
203 |
const migration = {'true' if migration else 'false'}
|
204 |
const hofMigration = {'true' if hofMigration else 'false'}
|
205 |
const fractionReplacedHof = {fractionReplacedHof}f0
|
|
|
73 |
test='simple1',
|
74 |
verbosity=1e9,
|
75 |
maxsize=20,
|
76 |
+
maxdepth=None,
|
77 |
threads=None, #deprecated
|
78 |
julia_optimization=3,
|
79 |
):
|
|
|
136 |
"""
|
137 |
if threads is not None:
|
138 |
raise ValueError("The threads kwarg is deprecated. Use procs.")
|
139 |
+
if maxdepth is None:
|
140 |
+
maxdepth = maxsize
|
141 |
|
142 |
# Check for potential errors before they happen
|
143 |
assert len(unary_operators) + len(binary_operators) > 0
|
|
|
203 |
const parsimony = {parsimony:f}f0
|
204 |
const alpha = {alpha:f}f0
|
205 |
const maxsize = {maxsize:d}
|
206 |
+
const maxdepth = {maxdepth:d}
|
207 |
const migration = {'true' if migration else 'false'}
|
208 |
const hofMigration = {'true' if hofMigration else 'false'}
|
209 |
const fractionReplacedHof = {fractionReplacedHof}f0
|