# Go through one simulated annealing mutation cycle # exp(-delta/T) defines probability of accepting a change function iterate(member::PopMember, T::Float32, curmaxsize::Integer, frequencyComplexity::Array{Float32, 1})::PopMember prev = member.tree tree = prev #TODO - reconsider this if batching beforeLoss = scoreFuncBatch(prev) else beforeLoss = member.score end mutationChoice = rand() #More constants => more likely to do constant mutation weightAdjustmentMutateConstant = min(8, countConstants(prev))/8.0 cur_weights = copy(mutationWeights) .* 1.0 cur_weights[1] *= weightAdjustmentMutateConstant n = countNodes(prev) depth = countDepth(prev) # If equation too big, don't add new operators if n >= curmaxsize || depth >= maxdepth cur_weights[3] = 0.0 cur_weights[4] = 0.0 end cur_weights /= sum(cur_weights) cweights = cumsum(cur_weights) successful_mutation = false #TODO: Currently we dont take this \/ into account is_success_always_possible = true attempts = 0 max_attempts = 10 ############################################# # Mutations ############################################# while (!successful_mutation) && attempts < max_attempts tree = copyNode(prev) successful_mutation = true if mutationChoice < cweights[1] tree = mutateConstant(tree, T) is_success_always_possible = true # Mutating a constant shouldn't invalidate an already-valid function elseif mutationChoice < cweights[2] tree = mutateOperator(tree) is_success_always_possible = true # Can always mutate to the same operator elseif mutationChoice < cweights[3] if rand() < 0.5 tree = appendRandomOp(tree) else tree = prependRandomOp(tree) end is_success_always_possible = false # Can potentially have a situation without success elseif mutationChoice < cweights[4] tree = insertRandomOp(tree) is_success_always_possible = false elseif mutationChoice < cweights[5] tree = deleteRandomOp(tree) is_success_always_possible = true elseif mutationChoice < cweights[6] tree = simplifyTree(tree) # Sometimes we simplify tree tree = combineOperators(tree) # See if repeated constants at outer levels return PopMember(tree, beforeLoss) is_success_always_possible = true # Simplification shouldn't hurt complexity; unless some non-symmetric constraint # to commutative operator... elseif mutationChoice < cweights[7] tree = genRandomTree(5) # Sometimes we generate a new tree completely tree is_success_always_possible = true else # no mutation applied return PopMember(tree, beforeLoss) end # Check for illegal equations for i=1:nbin if successful_mutation && flagBinOperatorComplexity(tree, i) successful_mutation = false end end for i=1:nuna if successful_mutation && flagUnaOperatorComplexity(tree, i) successful_mutation = false end end attempts += 1 end ############################################# if !successful_mutation return PopMember(copyNode(prev), beforeLoss) end if batching afterLoss = scoreFuncBatch(tree) else afterLoss = scoreFunc(tree) end if annealing delta = afterLoss - beforeLoss probChange = exp(-delta/(T*alpha)) if useFrequency oldSize = countNodes(prev) newSize = countNodes(tree) probChange *= frequencyComplexity[oldSize] / frequencyComplexity[newSize] end return_unaltered = (isnan(afterLoss) || probChange < rand()) if return_unaltered return PopMember(copyNode(prev), beforeLoss) end end return PopMember(tree, afterLoss) end