# Should I replace sin(theta) by piecewise linear and solve the linear problem using gurobipy or use ipopt to solve the nonlinear probelm?

I have a large nonlinear and nonconvex problem. It also has sin (theta) and cos(theta).

While gurobipy accepts sin or cos using the general constraint. gurobipy uses piecewise linear approximation, which means gurobipy introduces binary variables.

If theta dimension is 10,000; gurobipy introduces at least 10,000 binary variables to formulate the sin(theta) or cos(theta). Does that mean it would be better to solve the problem with ipopt, if the computation speed is important? As we want to avoid adding a huge number of binary variables.

ipopt can solve the nonlinear problem without introducing binary variables.

I guess ipopt will be faster, but gurobipy will converge to a better solution.

Any thought?

• What is your domain? Do you have a non-convex feasible region? Is the trigonometry just in the objective function? Dec 26, 2022 at 13:32
• The trigonometry is the constraint. The objective function is linear. Dec 26, 2022 at 13:44

Gurobi can currently solve mixed-integer (non)convex problems with (bi)linear terms, i.e., the only nonlinearities allowed are squares and products of 2 variables. If your model is nonconvex, you have to set the NonConvex parameter to 2.

If your problem involves any other nonlinear functions, you can use Gurobi's general functions feature. There, Gurobi approximates nonlinear functions via piecewise-linear constraints.

You don't have to change the "solver", you just have to possibly set the NonConvex parameter. Unless you mean whether you can change to a different MINLP solver. Then yes, you could for example change to the open-source MINLP solver SCIP.

Also, the following example can give some good starting points to you:

import gurobipy as gp
from gurobipy import GRB
import math

def f(u):
return math.atan(u)

m = gp.Model()

lbz = -2
ubz =  2
z = m.addVar(lb=lbz, ub=ubz, vtype=GRB.CONTINUOUS, name="z")
y = m.addVar(lb=-math.pi/2, ub=math.pi/2, vtype=GRB.CONTINUOUS, name="y")

# Compute piecewise-linear arctan function for z
npts = 101
ptu = []
ptf = []
for i in range(npts):
ptu.append(lbz + (ubz - lbz) * i / (npts - 1))
ptf.append(f(ptu[i]))

# Add constraint y = arctan(z) as piecewise-linear approximation