PySR / julia /MutationFunctions.jl
MilesCranmer's picture
Make filenaming more consistent
48d465b
raw
history blame
5.46 kB
# Randomly convert an operator into another one (binary->binary;
# unary->unary)
function mutateOperator(tree::Node)::Node
if countOperators(tree) == 0
return tree
end
node = randomNode(tree)
while node.degree == 0
node = randomNode(tree)
end
if node.degree == 1
node.op = rand(1:length(unaops))
else
node.op = rand(1:length(binops))
end
return tree
end
# Randomly perturb a constant
function mutateConstant(
tree::Node, T::Float32,
probNegate::Float32=0.01f0)::Node
# T is between 0 and 1.
if countConstants(tree) == 0
return tree
end
node = randomNode(tree)
while node.degree != 0 || node.constant == false
node = randomNode(tree)
end
bottom = 0.1f0
maxChange = perturbationFactor * T + 1.0f0 + bottom
factor = maxChange^Float32(rand())
makeConstBigger = rand() > 0.5
if makeConstBigger
node.val *= factor
else
node.val /= factor
end
if rand() > probNegate
node.val *= -1
end
return tree
end
# Add a random unary/binary operation to the end of a tree
function appendRandomOp(tree::Node)::Node
node = randomNode(tree)
while node.degree != 0
node = randomNode(tree)
end
choice = rand()
makeNewBinOp = choice < nbin/nops
if rand() > 0.5
left = Float32(randn())
else
left = rand(1:nvar)
end
if rand() > 0.5
right = Float32(randn())
else
right = rand(1:nvar)
end
if makeNewBinOp
newnode = Node(
rand(1:length(binops)),
left,
right
)
else
newnode = Node(
rand(1:length(unaops)),
left
)
end
node.l = newnode.l
node.r = newnode.r
node.op = newnode.op
node.degree = newnode.degree
node.val = newnode.val
node.constant = newnode.constant
return tree
end
# Insert random node
function insertRandomOp(tree::Node)::Node
node = randomNode(tree)
choice = rand()
makeNewBinOp = choice < nbin/nops
left = copyNode(node)
if makeNewBinOp
right = randomConstantNode()
newnode = Node(
rand(1:length(binops)),
left,
right
)
else
newnode = Node(
rand(1:length(unaops)),
left
)
end
node.l = newnode.l
node.r = newnode.r
node.op = newnode.op
node.degree = newnode.degree
node.val = newnode.val
node.constant = newnode.constant
return tree
end
# Add random node to the top of a tree
function prependRandomOp(tree::Node)::Node
node = tree
choice = rand()
makeNewBinOp = choice < nbin/nops
left = copyNode(tree)
if makeNewBinOp
right = randomConstantNode()
newnode = Node(
rand(1:length(binops)),
left,
right
)
else
newnode = Node(
rand(1:length(unaops)),
left
)
end
node.l = newnode.l
node.r = newnode.r
node.op = newnode.op
node.degree = newnode.degree
node.val = newnode.val
node.constant = newnode.constant
return node
end
function randomConstantNode()::Node
if rand() > 0.5
val = Float32(randn())
else
val = rand(1:nvar)
end
newnode = Node(val)
return newnode
end
# Return a random node from the tree with parent
function randomNodeAndParent(tree::Node, parent::Union{Node, Nothing})::Tuple{Node, Union{Node, Nothing}}
if tree.degree == 0
return tree, parent
end
a = countNodes(tree)
b = 0
c = 0
if tree.degree >= 1
b = countNodes(tree.l)
end
if tree.degree == 2
c = countNodes(tree.r)
end
i = rand(1:1+b+c)
if i <= b
return randomNodeAndParent(tree.l, tree)
elseif i == b + 1
return tree, parent
end
return randomNodeAndParent(tree.r, tree)
end
# Select a random node, and replace it an the subtree
# with a variable or constant
function deleteRandomOp(tree::Node)::Node
node, parent = randomNodeAndParent(tree, nothing)
isroot = (parent === nothing)
if node.degree == 0
# Replace with new constant
newnode = randomConstantNode()
node.l = newnode.l
node.r = newnode.r
node.op = newnode.op
node.degree = newnode.degree
node.val = newnode.val
node.constant = newnode.constant
elseif node.degree == 1
# Join one of the children with the parent
if isroot
return node.l
elseif parent.l == node
parent.l = node.l
else
parent.r = node.l
end
else
# Join one of the children with the parent
if rand() < 0.5
if isroot
return node.l
elseif parent.l == node
parent.l = node.l
else
parent.r = node.l
end
else
if isroot
return node.r
elseif parent.l == node
parent.l = node.r
else
parent.r = node.r
end
end
end
return tree
end
# Create a random equation by appending random operators
function genRandomTree(length::Integer)::Node
tree = Node(1.0f0)
for i=1:length
tree = appendRandomOp(tree)
end
return tree
end