Constraints API

Defining inequality and equality constraints
Published

February 8, 2026

1 Constraint

A constraint represents a condition the solution must satisfy.

Constraints are created using comparison operators on expressions:

from optyx import Variable

x = Variable("x")
y = Variable("y")

# Inequality constraints
c1 = x + y >= 1      # Greater than or equal
c2 = x - y <= 5      # Less than or equal

# Equality constraint
c3 = (x + y).eq(10)

2 Creating Constraints

2.1 Inequality Constraints

Use >= and <= operators on expressions:

from optyx import Variable

x = Variable("x")
y = Variable("y")

# Left-hand side >= right-hand side
c1 = x + y >= 1
c2 = 2*x >= y

# Left-hand side <= right-hand side
c3 = x**2 + y**2 <= 100
c4 = x <= 10

2.2 Equality Constraints

Use the .eq() method:

from optyx import Variable

x = Variable("x")
y = Variable("y")

# x + y = 10
c1 = (x + y).eq(10)

# x = y (equivalent to x - y = 0)
c2 = (x - y).eq(0)

# More complex equality
c3 = (x**2 + y**2).eq(1)  # Unit circle
Note

We use .eq() instead of == because Python requires __eq__ to return a boolean for hash operations. This is standard practice in symbolic math libraries.


3 Constraint Properties

Property Type Description
.expr Expression The constraint expression (normalized to rhs = 0)
.sense str One of ">=", "<=", or "=="

3.1 Normalization

Constraints are automatically normalized so the right-hand side is zero:

from optyx import Variable

x = Variable("x")

# x + 5 >= 10 becomes (x + 5 - 10) >= 0, i.e., (x - 5) >= 0
c = x + 5 >= 10
print(f"Sense: {c.sense}")
Sense: >=

4 Using Constraints in Problems

Add constraints to problems with .subject_to():

from optyx import Variable, Problem

x = Variable("x", lb=0)
y = Variable("y", lb=0)

solution = (
    Problem()
    .minimize(x**2 + y**2)
    .subject_to(x + y >= 1)        # Inequality
    .subject_to(x <= 5)            # Upper bound as constraint
    .subject_to((x - y).eq(0))  # Equality
    .solve()
)

print(f"x* = {solution['x']:.4f}")
print(f"y* = {solution['y']:.4f}")
x* = 0.5000
y* = 0.5000

5 Constraint Patterns

5.1 Box Constraints

For simple bounds, prefer using Variable(lb=, ub=):

from optyx import Variable

# Preferred: bounds on variable
x = Variable("x", lb=0, ub=10)

# Alternative: as explicit constraints (less efficient)
y = Variable("y")
c1 = y >= 0
c2 = y <= 10

5.2 Linear Constraints

from optyx import Variable

x = Variable("x")
y = Variable("y")
z = Variable("z")

# Budget constraint
budget = 10*x + 20*y + 15*z <= 1000

# Resource allocation
allocation = x + y + z >= 100

5.3 Nonlinear Constraints

from optyx import Variable, sqrt

x = Variable("x")
y = Variable("y")

# Circle constraint
circle = x**2 + y**2 <= 25

# Norm constraint
norm = sqrt(x**2 + y**2) <= 5

# Ratio constraint
ratio = x / (y + 1) >= 0.5

5.4 Multiple Constraints

from optyx import Variable, Problem

x = Variable("x", lb=0)
y = Variable("y", lb=0)

prob = Problem().minimize(x + y)

# Add multiple constraints
constraints = [
    x + y >= 10,
    x - y <= 5,
    2*x + y <= 20,
    (x - 5).eq(y - 5)
]

for c in constraints:
    prob.subject_to(c)

solution = prob.solve()
print(f"x* = {solution['x']:.2f}, y* = {solution['y']:.2f}")
x* = 5.00, y* = 5.00

6 Solver Handling

Optyx converts constraints to the format expected by SciPy:

Optyx SciPy Equivalent
f(x) >= 0 {'type': 'ineq', 'fun': f}
f(x) <= 0 {'type': 'ineq', 'fun': -f}
f(x) == 0 {'type': 'eq', 'fun': f}

Gradients are computed automatically and passed to the solver for efficient optimization.