← Back to Tutorials
Intermediate• 25 minutes

Problem Setup: Objectives & Constraints

Define complex optimization problems with multiple objectives, bounds, and constraints.

What You'll Learn

  • Minimization vs maximization problems
  • Defining bounds for continuous and integer variables
  • Inequality and equality constraints
  • Multi-objective optimization approaches
  • Penalty methods for soft constraints

Minimization vs Maximization

By default, Sematryx minimizes the objective function. For maximization problems, set minimize=False:

Minimization vs maximization
from sematryx import optimize

# MINIMIZATION (default)
# Find the lowest value of the objective function
result = optimize(
    objective_function=cost_function,
    bounds=bounds,
    objective="minimize"  # default
)

# MAXIMIZATION
# Find the highest value of the objective function
result = optimize(
    objective_function=profit_function,
    bounds=bounds,
    objective="maximize"  # maximize instead
)

Defining Bounds

Bounds define the search space for each variable. You can also specify which dimensions should be integers for mixed-integer optimization:

Bounds and integer dimensions
from sematryx import optimize

# Define bounds for each dimension
# Each element is [lower_bound, upper_bound]
bounds = [
    [0, 100],      # x0: between 0 and 100
    [-50, 50],     # x1: between -50 and 50
    [0.001, 10],   # x2: between 0.001 and 10
    [1, 1000],     # x3: between 1 and 1000
]

# Or use variables format for more control
variables = [
    {"name": "x0", "bounds": (0, 100), "type": "integer"},
    {"name": "x1", "bounds": (-50, 50)},
    {"name": "x2", "bounds": (0.001, 10)},
    {"name": "x3", "bounds": (1, 1000), "type": "integer"}
]

result = optimize(
    objective_function=my_function,
    bounds=bounds  # or use variables=variables
)

Bounds Tips

  • Tight bounds: Narrower bounds help the optimizer converge faster
  • Scaling: Keep variables on similar scales (e.g., 0-1 or 0-100)
  • Integer variables: Use integer_dimensions for discrete choices

Inequality Constraints

Inequality constraints define regions where solutions must satisfy g(x) ≥ 0:

Inequality constraints
from sematryx import optimize

def production_cost(x):
    """Objective: Minimize production cost"""
    labor, materials, energy = x
    return 50*labor + 30*materials + 20*energy

# Constraints as string expressions
constraints = [
    {
        "expression": "10*x0 + 5*x1 + 2*x2 >= 1000",  # Must produce at least 1000 units
        "type": "inequality"
    },
    {
        "expression": "50*x0 + 30*x1 + 20*x2 <= 50000",  # Budget cannot exceed $50,000
        "type": "inequality"
    }
]

result = optimize(
    objective_function=production_cost,
    bounds=[[0, 100], [0, 200], [0, 500]],
    constraints=constraints
)

Constraint Convention

Inequality constraints should return a value where positive = satisfied. For example, "budget ≤ 50,000" becomes 50000 - budget (positive when under budget).

Equality Constraints

Equality constraints require h(x) = 0. Common in portfolio optimization where weights must sum to 1:

Equality constraints
from sematryx import optimize
import numpy as np

def portfolio_variance(weights):
    """Minimize portfolio risk"""
    cov_matrix = np.array([
        [0.04, 0.01, 0.02],
        [0.01, 0.03, 0.01],
        [0.02, 0.01, 0.05]
    ])
    return np.dot(weights, np.dot(cov_matrix, weights))

# Equality constraint: weights must sum to 1
constraints = [
    {
        "expression": "x0 + x1 + x2 == 1",
        "type": "equality"
    }
]

result = optimize(
    objective_function=portfolio_variance,
    bounds=[[0, 1], [0, 1], [0, 1]],  # Each weight 0-100%
    constraints=constraints
)

Multi-Objective Optimization

When you have competing objectives (e.g., minimize cost AND maximize quality), you can use scalarization or Pareto optimization:

Multi-objective optimization
from sematryx import optimize

def multi_objective(x):
    """
    Multi-objective: Balance cost vs quality
    Returns weighted combination of objectives
    """
    cost = calculate_cost(x)
    quality = calculate_quality(x)
    
    # Scalarization: combine objectives with weights
    # Lower cost is better, higher quality is better
    # So we minimize cost and minimize negative quality
    return 0.6 * cost - 0.4 * quality

# Use scalarization for multi-objective optimization
result = optimize(
    objective_function=multi_objective,
    bounds=bounds
)

Soft Constraints with Penalties

Sometimes hard constraints are too restrictive. Penalty functions let you express preferences without strict requirements:

Penalty methods for soft constraints
from sematryx import optimize

def objective_with_penalties(x):
    """
    Soft constraints via penalty functions
    Useful when hard constraints are too restrictive
    """
    # Base objective
    base_cost = sum(xi**2 for xi in x)
    
    # Soft constraint: prefer solutions where x[0] > x[1]
    penalty = 0
    if x[0] <= x[1]:
        penalty = 1000 * (x[1] - x[0] + 0.1)  # Quadratic penalty
    
    # Soft constraint: prefer solutions near integer values
    integer_penalty = sum(min(xi % 1, 1 - xi % 1)**2 for xi in x)
    
    return base_cost + penalty + 100 * integer_penalty

result = optimize(
    objective_function=objective_with_penalties,
    bounds=[[-10, 10], [-10, 10], [-10, 10]]
)

print(f"Solution: {result.solution}")
print(f"Value: {result.objective_value}")

Best Practices

Problem Setup Tips

  • Start simple: Begin with minimal constraints, add complexity gradually
  • Scale variables: Normalize inputs to similar ranges for better convergence
  • Test constraints: Verify constraints are satisfiable before running optimization
  • Use explanations: Enable explanation_level to understand why constraints fail

🎉 Next Steps

You now know how to set up complex optimization problems. Next, learn how to configure Sematryx Intelligence for enhanced optimization.