Constraint

PyOptInterface supports the following types of constraints:

  • Linear Constraint

  • Quadratic Constraint

  • Second-Order Cone Constraint

  • Special Ordered Set (SOS) Constraint

Note

Not all optimizers support all types of constraints. Please refer to the documentation of the optimizer you are using to see which types of constraints are supported.

import pyoptinterface as poi
from pyoptinterface import copt

model = copt.Model()
2025-01-27 09:12:58 [INFO] checks license for COPT v7.2.4 20241206
2025-01-27 09:12:58 [WARN] no license files in current working folder: /home/runner/work/PyOptInterface/PyOptInterface/docs/source
2025-01-27 09:12:58 [WARN] no license files in binary folder: /opt/hostedtoolcache/Python/3.12.8/x64/bin
2025-01-27 09:12:58 [WARN] no license files in HOME folder: /home/runner/copt
2025-01-27 09:12:58 [INFO] empty environment variable: COPT_LICENSE_DIR
2025-01-27 09:12:58 [WARN] no license files in EV 'COPT_LICENSE_DIR': 

No license found. Starting COPT with size limitations for non-commercial use
Please apply for a license from www.shanshu.ai/copt

Constraint Sense

The sense of a constraint can be one of the following:

  • poi.Eq: equal

  • poi.Leq: less than or equal

  • poi.Geq: greater than or equal

They are the abbreviations of poi.ConstraintSense.Equal, poi.ConstraintSense.LessEqual or poi.ConstraintSense.GreaterEqual and can be used in the sense argument of the constraint creation functions.

Linear Constraint

It is defined as:

\[\begin{split} \begin{align} \text{expr} &= a^T x + b &\leq \text{rhs} \\ \text{expr} &= a^T x + b &= \text{rhs} \\ \text{expr} &= a^T x + b &\geq \text{rhs} \end{align} \end{split}\]

It can be added to the model using the add_linear_constraint method of the Model class.

x = model.add_variable(name="x")
y = model.add_variable(name="y")

con = model.add_linear_constraint(2.0*x + 3.0*y, poi.Leq, 1.0)
model.add_linear_constraint(expr, sense, rhs[, name=""])

add a linear constraint to the model

Parameters:
  • expr – the expression of the constraint

  • sense (pyoptinterface.ConstraintSense) – the sense of the constraint

  • rhs (float) – the right-hand side of the constraint

  • name (str) – the name of the constraint, optional

Returns:

the handle of the constraint

Note

PyOptInterface provides pyoptinterface.Eq, pyoptinterface.Leq, and pyoptinterface.Geq as alias of pyoptinterface.ConstraintSense to represent the sense of the constraint with a shorter name.

Quadratic Constraint

Like the linear constraint, it is defined as:

\[\begin{split} \begin{align} \text{expr} &= x^TQx + a^Tx + b &\leq \text{rhs} \\ \text{expr} &= x^TQx + a^Tx + b &= \text{rhs} \\ \text{expr} &= x^TQx + a^Tx + b &\geq \text{rhs} \end{align} \end{split}\]

It can be added to the model using the add_quadratic_constraint method of the Model class.

x = model.add_variable(name="x")
y = model.add_variable(name="y")

expr = x*x + 2.0*x*y + 4.0*y*y
con = model.add_quadratic_constraint(expr, poi.ConstraintSense.LessEqual, 1.0)
model.add_quadratic_constraint(expr, sense, rhs[, name=""])

add a quadratic constraint to the model

Parameters:
  • expr – the expression of the constraint

  • sense (pyoptinterface.ConstraintSense) – the sense of the constraint, which can be GreaterEqual, Equal, or LessEqual

  • rhs (float) – the right-hand side of the constraint

  • name (str) – the name of the constraint, optional

Returns:

the handle of the constraint

Second-Order Cone Constraint

It is defined as:

\[ variables=(t,x) \in \mathbb{R}^{N} : t \ge \lVert x \rVert_2 \]

It can be added to the model using the add_second_order_cone_constraint method of the Model class.

N = 6
vars = [model.add_variable() for i in range(N)]

con = model.add_second_order_cone_constraint(vars)

There is another form of second-order cone constraint called as rotated second-order cone constraint, which is defined as:

\[ variables=(t_{1},t_{2},x) \in \mathbb{R}^{N} : 2 t_1 t_2 \ge \lVert x \rVert_2^2 \]
model.add_second_order_cone_constraint(variables[, name="", rotated=False])

add a second order cone constraint to the model

Parameters:
  • variables – the variables of the constraint, can be a list of variables

  • name (str) – the name of the constraint, optional

  • rotated (bool) – whether the constraint is a rotated second-order cone constraint, optional

Returns:

the handle of the constraint

Exponential Cone Constraint

It is defined as:

\[ variables=(t,s,r) \in \mathbb{R}^{3} : t \ge s \exp(\frac{r}{s}), s \ge 0 \]

The dual form is:

\[ variables=(t,s,r) \in \mathbb{R}^{3} : t \ge -r \exp(\frac{s}{r} - 1), r \le 0 \]

Currently, only COPT(after 7.1.4), Mosek support exponential cone constraint. It can be added to the model using the add_exp_cone_constraint method of the Model class.

model.add_exp_cone_constraint(variables[, name="", dual=False])

add a second order cone constraint to the model

Parameters:
  • variables – the variables of the constraint, can be a list of variables

  • name (str) – the name of the constraint, optional

  • dual (bool) – whether the constraint is dual form of exponential cone, optional

Returns:

the handle of the constraint

Special Ordered Set (SOS) Constraint

SOS constraints are used to model special structures in the optimization problem. It contains two types: SOS1 and SOS2, the details can be found in Wikipedia.

It can be added to the model using the add_sos_constraint method of the Model class.

N = 6
vars = [model.add_variable(domain=poi.VariableDomain.Binary) for i in range(N)]

con = model.add_sos_constraint(vars, poi.SOSType.SOS1)
model.add_sos_constraint(variables, sos_type[, weights])

add a special ordered set constraint to the model

Parameters:
  • variables – the variables of the constraint, can be a list of variables

  • sos_type (pyoptinterface.SOSType) – the type of the SOS constraint, which can be SOS1 or SOS2

  • weights (list[float]) – the weights of the variables, optional, will be set to 1 if not provided

Returns:

the handle of the constraint

Constraint Attributes

After a constraint is created, we can query or modify its attributes. The following table lists the standard constraint attributes:

Standard constraint attributes

Attribute name

Type

Name

str

Primal

float

Dual

float

IIS

bool

The most common attribute we will use is the Dual attribute, which represents the dual multiplier of the constraint after optimization.

# get the dual multiplier of the constraint after optimization
dual = model.get_constraint_attribute(con, poi.ConstraintAttribute.Dual)

Delete constraint

We can delete a constraint by calling the delete_constraint method of the model:

model.delete_constraint(con)

After a constraint is deleted, it cannot be used in the model anymore, otherwise an exception will be raised.

We can query whether a constraint is active by calling the is_constraint_active method of the model:

is_active = model.is_constraint_active(con)

Modify constraint

For linear and quadratic constraints, we can modify the right-hand side of a constraint by calling the set_normalized_rhs method of the model.

For linear constraints, we can modify the coefficients of the linear part of the constraint by calling the set_normalized_coefficient method of the model.

con = model.add_linear_constraint(x + y, poi.Leq, 1.0)

# modify the right-hand side of the constraint
model.set_normalized_rhs(con, 2.0)

# modify the coefficient of the linear part of the constraint
model.set_normalized_coefficient(con, x, 2.0)

Create constraint with comparison operator

In other modeling languages, we can create a constraint with a comparison operator, like:

model.addConsr(x + y <= 1)

This is quite convenient, so PyOptInterface now supports to create constraint with comparison operators <=, ==, >= as a shortcut to create a linear or quadratic constraint.

model.add_linear_constraint(x + y <= 1)
model.add_linear_constraint(x <= y)
model.add_quadratic_constraint(x*x + y*y <= 1)
<pyoptinterface._src.core_ext.ConstraintIndex at 0x7fe3dc6f44b0>

Note

Creating constraint with comparison operator may cause performance issue especially the left-hand side and right-hand side of the constraint are complex expressions. PyOptInterface needs to create a new expression by subtracting the right-hand side from the left-hand side, which may be time-consuming.

If that becomes the bottleneck of performance, it is recommended to construct the left-hand side expression with ExprBuilder and call add_linear_constraint or add_quadratic_constraint method to create constraints explicitly.