Python – Generates a combination of lambda function combinations

Generates a combination of lambda function combinations… here is a solution to the problem.

Generates a combination of lambda function combinations

To make my Python3 code more elegant, I was faced with a challenging problem.

Suppose I have a numeric function with a variable number of different inputs, for example as follows:

def fun1(a,b):
    return a+b

def fun2(c,d,e):
    return c*d + e

def fun3(x):
    return x*x

These functions need to be clustered together in a function that needs to be used as an optimization function for the numeric solver.

But I need to create different combinations of various operations with these functions, such as multiplying the outputs of the first two functions and adding them to the third.

The manual solution is to create a specific lambda function:

fun = lambda x : fun1(x[0],x[1])*fun2(x[2],x[3],x[4]) + fun3(x[4])

But I have a lot of functions and I need to generate all possible combinations of them.
I want to be able to combine these functions systematically and always understand the mapping from the parameters of the high-level function fun to the low-level parameters of each function.
In this case, I manually specify that x[0] corresponds to parameter a of fun1, x[1] corresponds to parameter b of fun1 etc.

Any ideas?

Solution

It sounds like you’re trying to do what’s called symbolic regression This problem is usually solved by some variation of genetic algorithms that encode functional relationships in genes and then optimize them based on fitness functions that include prediction errors and penalize more complex relationships.

Here are two libraries that can solve this problem for you:

The following classes provide a basic way to combine functions and keep track of the number of arguments required by each function, which seems to be the main problem you encounter:

class Wrapper:
    def __init__(self, f):
        self.f = f
        self.n = f.__code__.co_argcount

def __call__(self, x):
        return self.f(*x)

def __add__(self, other):
        return Add(self, other)

def __mul__(self, other):
        return Mul(self, other)

class Operator:
    def __init__(self, left, right):
        self.left = left
        self.right = right
        self.n = left.n + right.n

class Mul(Operator):
    def __call__(self, x):
        return self.left(x[:self.left.n]) * self.right(x[self.left.n:])

class Add(Operator):
    def __call__(self, x):
        return self.left(x[:self.left.n]) + self.right(x[self.left.n:])

To use them, you first create a wrapper for each function:

w1 = Wrapper(fun1)
w2 = Wrapper(fun2)
w3 = Wrapper(fun3)

You can then add and multiply wrappers to get a new function-like object:

(w1 + w2*w3)([1, 2, 3, 4, 5, 6])

Related Problems and Solutions