Search code examples
pythonif-statementoptimizationlambdagekko

How to use If...else in lambda with gekko


The scenario is the following: I want to minimalize the cost of the running motors. I have X number of motors, each have a lower and an upper bound. It can not go over the upper bound and it can't go lower than the lower bound, unless it goes with 0.

i have to create a way to minimalize these motors costs when they have to reach an Y amount of power. Not every motor has to be active, but if it is active it has to reach its lower bound.

I am trying to minimize the cost of the function and I have a code snippet.

prices = {
'Motor2': lambda y: (1/(np.poly1d(np.loadtxt("path_to_txt"))(y*1000)/100)) * 7048),
'Motor3': lambda z: (1/(np.poly1d(np.loadtxt("path_to_txt"))(z*1000)/100)) * 1674),
'Motor4': lambda a: (1/(np.poly1d(np.loadtxt("path_to_txt"))(a*1000)/100)) * 1637),
'Motor5': lambda b: (1/(np.poly1d(np.loadtxt("path_to_txt"))(b*1000)/100)) * 6354),
'Motor6': lambda c: (1/(np.poly1d(np.loadtxt("path_to_txt"))(c*1000)/100)) * 2634),
'Motor7': lambda d: (1/(np.poly1d(np.loadtxt("path_to_txt"))(d*1000)/100)) * 1654),
'Motor8': lambda e: (1/(np.poly1d(np.loadtxt("path_to_txt"))(e*1000)/100)) * 1354),
'Motor9': lambda x: (1/(np.poly1d(np.loadtxt("path_to_txt"))(x*1000)/100)) * 7048),
'Motor10': lambda f:(1/(np.poly1d(np.loadtxt("path_to_txt"))(f*1000)/100)) * 1629)

}

These are the Prices of given motor. And i would like to give a it a parameter that if the motor speed (y, z, a,b, etc) is 0 than I would like the cost to be zero.

I have tried two differnt approach: The first one eas like this:

'Motor2': lambda y: (1/(np.poly1d(np.loadtxt("path_to_txt"))(y*1000)/100)) * 7048) if y > 0 else 0

This returns the following error when it reaches the Gekko minimize method:

TypeError: object of type 'int' has no len()

The other one was the one with the model.if3/if2 Here that part looked like this:

'Motor2': lambda y: model.if3(y-2.1,(1/(np.poly1d(np.loadtxt("Path_to_txt"))(y*1000)/100))* 70848,0),

i tried to change the order aswell inside the if3, if2 but it either gives that their is no optimal solution or it gives wrong solution.

that 2.1 is the lower bound where the motor has to go if it is active.

The txt-t contains a a^3 + b^2 + c^1 + Constant

So my questions is, how can I use an if...else statement or any other method to solve this problem?

Thank you in advance

Edit1: Here is the full error trace:

Traceback (most recent call last):
File "path_file\file.py", line 56, in <module>
model.Minimize(sum(prices[motor](xx[motor]) for motor in 
power_ranges))
File "path_file\file.py", line 56, in <genexpr>
model.Minimize(sum(prices[motor](xx[motor]) for motor in 
power_ranges))
File "path_file\file.py", line 35, in <lambda>
'Motor2': lambda y: (1/(np.poly1d(np.loadtxt("path_to_txt_motor2") 
(y*1000)/100))* 70848 if y > 0 else 0,
File "path_to_anaconda\gekko\gk_operators.py", line 25, in __len__
return len(self.value)
File "path_to_anaconda\gekko\gk_operators.py", line 144, in __len__
return len(self.value)
TypeError: object of type 'int' has no len()

Solution

  • The second method that you proposed is correct. I recommend m.if3() with a switch point (e.g. 0.1) that is not close to the lower bound (e.g. 2.1). Referencing the complete script from your prior question: How to implement OR constraint in GEKKO instead of adding a small value to avoid divide-by-zero in the lambda function, use the m.if3() function instead:

    prices = {
        'Motor1': lambda x: model.if3(x-0.1,0,(x ** 2)/x*5000),
        'Motor2': lambda y: (y ** 1 / 0.45) * 5500,
        'Motor3': lambda z: (z * 0.45) * 5100,
        'Motor4': lambda a: (a / 0.45) * 5200,
    }
    

    Here is the complete script that solves with the m.if3() function:

    from gekko import GEKKO
    
    power_ranges = {
        'Motor1': (0.6, 1.1),
        'Motor2': (2.1, 6),
        'Motor3': (1, 1.94),
        'Motor4': (1, 1.94),
    }
    
    model = GEKKO()
    
    prices = {
        'Motor1': lambda x: model.if3(x-0.1,0,(x ** 2)/x*5000),
        'Motor2': lambda y: (y ** 1 / 0.45) * 5500,
        'Motor3': lambda z: (z * 0.45) * 5100,
        'Motor4': lambda a: (a / 0.45) * 5200,
    }
    
    # Define decision variables
    x = {}; y = {}; z = {}
    for motor in power_ranges:
        x[motor] = model.Var(lb=power_ranges[motor][0],
                             ub=power_ranges[motor][1])
        y[motor] = model.Var(lb=0, ub=1, integer=True)
        z[motor] = model.Intermediate(x[motor]*y[motor])    
    
    # Define objective function
    model.Minimize(sum(prices[motor](z[motor]) \
                    for motor in power_ranges))
    
    # Define the "or" constraint
    for motor in power_ranges:
        model.Equation(z[motor] >= power_ranges[motor][0] * y[motor])
        model.Equation(z[motor] <= power_ranges[motor][1] * y[motor])
    
    # Define power constraint
    model.Equation(sum(z[motor] for motor in power_ranges) == 4.7)
    
    # Solve the optimization problem
    model.options.SOLVER = 1
    model.solve()
    
    # Print the solution
    if model.options.APPSTATUS == 1:
        print("Optimal solution found:")
        for motor in power_ranges:
            print(f"{motor}: {round(float(z[motor].value[0]), 2)}")
        print(f"Total cost: {round(float(model.options.OBJFCNVAL), 2)}")
    else:
        print("No optimal solution found.")