Creating a generic mathematical formula using a genetic algorithm
I would suggest to use a sequence of operations of the form
op1(var) op2 op1(var) op2 op1(var) ...
where op1 refers to a unary operation and op2 refers to a binary operation. Making unary operations mandatory gets rid of a distinction of cases. If you don't want a unary operation in front of a variable, just use the identity function.

I wrote two implementations for unary composition of a list/tuple of unary operations. One in Haskell, one in Python:

square:: Num a =&gt; a -&gt; a
square x = x * x

compose_multiple:: [a -&gt; a] -&gt; (a -&gt; a)
compose_multiple []     = (\x -&gt; x)                    -- for the empty case, use the identity
compose_multiple (f:fs) = f . compose_multiple fs  -- recursive case


This gives us 256 for

compose_multiple [square, square, square] 2


In Python:

def compose(op1, op2):
    def result(x):
        return op1(op2(x))
    return result

def compose_multiple(operations: tuple):
    if len(operations) == 1:
            return operations[0]
    return compose(compose_multiple(operations[:-1]), operations[-1])


This gives us 256 for

def square(x):
  return x * x
compose_multiple((square, square, square))(2)


An implementation for the composition of binary operations should work on the lines of the unary case.

The complexity of the final code can be reduced by using infix notation (which is easy to do in Haskell using parenthesis () in the function definition).
I have no experience using infix notation in Python. Maybe https://pypi.org/project/infix/ might help.