# I want to solve an optimization problem with nonlinear piecewise objective function (I tried Pyomo with "ipopt" solver but I had an error)

I want to solve an optimization problem where the objective function is the summation of nonlinear, piecewise functions in the decision variables q_i's such that when a decision variable q_i < 1, the function associated with it is quadratic; otherwise it is a log function. In Pyomo with "ipopt" solver, I tried to define the objective function (as suggested by the answer here) using the "Expr_If" expression. However, when I run the code (see below), the solver indicates that an optimal solution is reached (Message: Ipopt 3.14.9\x3a Optimal Solution Found, Termination condition: optimal). I can print the value of optimal decision variables by running: for x in model.q: print(model.q[x].value) but I can not print the optimal value of the objective function when I run: model.payoff() such that I get this error "math domain error". This error might suggest that a log function is being evaluated at a negative value, but based on the objective function that I defined this should not happen. Also, I can calculate the value of the objective function by evaluating it (after obtaining the optimal solution) directly using the values of optimal solution q_i's by running the code:

payoff_ = 0
for i in model.P:
if model.q[i].value>=1:
payoff_ +=  model.Beta[i] * log(model.q[i])
else:
payoff_ += (-0.5)*model.Alpha[i] * (model.q[i]-1)**2
print(payoff_())


Do you know why I am getting the "math domain error" when I run model.payoff() ?

Also, is there any other solver I can use to solve this problem? (not necessarily with python)

My code:

model = ConcreteModel()

#Define the set
model.P = Set(initialize=['P1','P2','P3','P4'])

#Parameters
model.Beta = Param(model.P, initialize = {'P1':1,'P2':1.2,'P3':1.4,'P4':1.6})
model.Alpha = Param(model.P, initialize = {'P1':0.1,'P2':0.2,'P3':0.3,'P4':0.4})

#Variables
model.q = Var(model.P)

#Objective
def Payoff(model):
return sum(Expr_if(IF=model.q[i]>=1, THEN=model.Beta[i] * log(model.q[i]),
ELSE=(-0.5)*model.Alpha[i] * (model.q[i]-1)**2) for i in model.P)
model.payoff = Objective(expr = Payoff, sense = maximize)

#Constraints
def limit(model, i):
return -1.1<= model.q[i]
model.limit = Constraint(model.P, rule = limit)

def balance(model):
return summation(model.q) == 0
model.balance = Constraint(rule = balance)

solver = SolverFactory('ipopt')
solver.solve(model)
#model.pprint()

#model.payoff()             <--- printing value of objective function gives an error
#value(model.payoff)        <--- printing value of objective function gives an error



IPOPT only supports continuous variables, not binary or integer.

Look for an MINLP solver which suits your needs and budget.

• @3bod But are binary or integer variables introduced by Pyomo in order to handle the piecewise function? That is my point. BTW, it is a terrible design for Pyomo to call a solver which does not handle binary or integer variables to solve a model which does require them, Pyomo is not the only such guilty party. Feb 23, 2023 at 16:27
• Thank you mark! But my variables are continuous, not binary or integer.
– 3bod
Feb 23, 2023 at 16:27
• Thank you Mark! I see your point now. I was not aware that binary or integer variables will be introduced by Pyomo in this case. Does that mean the "Expr_If" that I am using to define the objective function is not working properly? But in the end, it reaches an optimal solution and I can print the optimal values of the decision variables!! The issue is only when I try to print the optimal value of the objective function.
– 3bod
Feb 23, 2023 at 16:33
• I will defer to a Pyomo expert, which I am not, to answer the question in your immediately preceding comment. Perhaps the solution is not really the optimal solution despite being claimed to be. I believe AMPL also will call MINOS to solve models with binary or integer variables, in which case MINOS ignores the binary or integer declarations and treats all variables as continuous, which can result in a non-optimal solution (maybe not even feasible) being produced, despite optimality claims provided. Feb 23, 2023 at 16:38
• Also, I don't know much about other solvers that should suit my needs using Pyomo. I'd appreciate it if you recommend other solvers with Pyomo, or any other language not necessarily Pyomo.
– 3bod
Feb 23, 2023 at 16:38

The error math domain error implies you are evaluating a function outside their allowed domain: if you adjust your limit-constraint from $$-1.1$$ to a value $$> 0$$, it should not give the error anymore.

Besides that and like Mark already mentioned is that Ipopt only supports continuous variables. A MINLP solver you can use is "mindtpy". You can also define the underlying mip solver and nlp solver like

solver.solve(model, mip_solver="glpk", nlp_solver="ipopt")

• Thank you Steven! Based on the "Expr_If" that I am using to define the objective function, the log(q_i) function should not be evaluated when q_i <1 (and hence when q_i is any negative value) be rather the quadratic (q_i)^2 that should be evaluated.
– 3bod
Feb 23, 2023 at 16:33