gurobipy.MNLExpr#

class MNLExpr#

Gurobi matrix-friendly nonlinear expression object. An MNLExpr stores a dense array of NLExpr objects, and makes it possible to formulate a set of nonlinear constraints in Python using a single mathematical expression.

You will generally build an MNLExpr by starting from MVar objects and applying Python operators, nonlinear functions, or a combination of both. An MNLExpr will only be created for matrix expressions that cannot be captured by an MLinExpr or MQuadExpr. Specifically, formulating a matrix expression where any element of the matrix is not a polynomial of degree at most 2 will result in an MNLExpr.

An MNLExpr object has a shape representing its dimensions, a size that counts the total number of elements, and an ndim that gives the number of dimensions. These properties lean on their counterparts in NumPy’s ndarray class.

When working with MLinExpr objects, you need to make sure that the operands’ shapes are compatible. For matrix multiplication, we follow the rules of Python’s matrix multiplication operator: both operands need to have at least one dimension, and their inner dimensions must agree. For more information we refer you to Python’s documentation. Other binary operations such as addition and multiplication are straightforward to understand if both operands have the same shape: the operation is applied point wise on the matching indices. For operands that have different shapes, the arithmetic follows NumPy’s broadcasting rules. We refer you to the NumPy documentation for more information.

The following example lists the object types and shapes of various expressions constructed from MVar objects:

from gurobipy import nlfunc

...

x = model.addMVar((3,), name="x")    # MVar, shape (3,)
y = model.addMVar((3, 3), name="y")  # MVar, shape (3,3)
z = model.addMVar((3, 3), name="z")  # MVar, shape (3,3)

expr1 = 2.0 * x             # MLinExpr, shape (3,)
expr2 = 2.0 * x * y         # MQuadExpr, shape (3,3)
expr3 = 2.0 * x * y * z     # MNLExpr, shape (3,3)
expr4 = x / y               # MNLExpr, shape (3,3)
expr5 = nlfunc.sin(x)       # MNLExpr, shape (3,)

Nonlinear matrix expressions are used to build nonlinear matrix general constraints. They are typically temporary objects which are passed immediately to either Model.addConstr or Model.addGenConstrNL to add a group of constraints to the model. Such constraints are always equality constraints with a resultant variable on the left side of the expression. In Python code these constraints can be added using either method. Using the MVar objects from the previous example:

# Add the constraint z_ij = sqrt(x_j + y_ij) for each (i, j)
model.addConstr(z == nlfunc.sqrt(x + y))

# Add the constraint z_ij = x_j / y_ij for each (i, j)
model.addGenConstrNL(z, x / y)

Nonlinear Inequality Constraints

It is not possible to use <= or >= operators to specify nonlinear inequality constraints using NLExpr objects. However, you can formulate an equivalent constraint by creating a bounded resultant variable. For example, the following code constrains that \(log(x_i) \le 1\) for \(i \in {0, 1, 2, 3}\):

x = model.addMVar((4,))
res = model.addMVar(x.shape, lb=-GRB.INFINITY, ub=1.0)
model.addGenConstrNL(res, nlfunc.log(x))

More control over expression creation

If you want more control over the expression trees generated, you can explicitly opt in to the nonlinear world using the nl property of MVar objects. This is considered advanced usage. In the vast majority of cases, you do not need to consider how a nonlinear expression is represented internally.

MVar.nl returns an MNLExpr object holding a matrix of expression trees, each of which represent a single variable. Any arithmetic operations involving that object will also result in an MNLExpr:

x = model.addMVar((2, 2))  # MVar, shape (2,2)
expr1 = x + 1.0            # MLinExpr, shape (2,2)
expr2 = x.nl               # MNLExpr, shape (2,2)
expr3 = x.nl + 1.0         # MNLExpr, shape (2,2)

The implications are subtle, but can have an impact on how a constraint is handled by the solver. For example, the following two constraints represent the same set of expressions \((x_i + y_i)^2\) but have a different internal representation in the solver:

x = model.addMVar((5,))
y = model.addMVar((5,))
z = model.addMVar((5,))
c1 = model.addGenConstrNL(z, (x - y) ** 2)
c2 = model.addGenConstrNL(z, (x.nl - y.nl) ** 2)

Specifically, the first case captures quadratic expressions in expanded form \(x_i^2 - 2 x_i y_i + y_i^2\). The second case captures a nonlinear expression with \(x_i - y_i\) as a node in the expression tree which is explicitly squared.

Domain restrictions of operators

Some subtleties arise from certain arithmetic operators not being applicable to the whole domain that an operand may have (e.g., division by zero). Refer to the additional information for the divide operator and the power operator for all the details.

property shape#

The shape of this expression.

Returns:

A tuple of int

property size#

The total number of elements in this expression.

Returns:

An int

property ndim#

The number of dimensions in this expression.

Returns:

An int

sum(axis=None)#

Sum the elements of this MNLExpr; returns an MNLExpr object.

Parameters:

axis – An int, or None. Sum along the specified axis. If set to None, summation takes place along all axes of this MNLExpr.

Returns:

An MNLExpr representing the sum.

item()#

For an MNLExpr that contains a single element, returns a copy of that element as an NLExpr object. Calling this method on an MNLExpr with more than one element will raise a ValueError.

Returns:

An NLExpr object