Basic Optimization Tutorial

Step-by-step guide to solving optimization problems
Published

February 8, 2026

1 Introduction

This tutorial walks through the complete process of formulating and solving optimization problems with Optyx. By the end, you’ll understand how to:

  • Define decision variables
  • Build objective functions
  • Add constraints
  • Solve and interpret results

2 Problem: Production Planning

A factory produces two products, A and B:

  • Product A yields $40 profit per unit
  • Product B yields $30 profit per unit

Constraints: - Machine time: 2 hours for A, 1 hour for B, 100 hours available - Labor: 1 hour for A, 2 hours for B, 80 hours available - Raw materials limit: 50 units total

Goal: Maximize profit.


3 Step 1: Define Variables

from optyx import Variable

# Decision variables: units to produce
a = Variable("product_a", lb=0)  # Non-negative
b = Variable("product_b", lb=0)  # Non-negative

print(f"Created variables: {a.name}, {b.name}")
Created variables: product_a, product_b
Tip

Always use descriptive variable names. They’ll appear in your solution output.


4 Step 2: Build the Objective

# Profit function
profit = 40*a + 30*b

# Test it works
test_profit = profit.evaluate({"product_a": 10, "product_b": 20})
print(f"Profit for 10A + 20B = ${test_profit}")
Profit for 10A + 20B = $1000

5 Step 3: Add Constraints

# Resource constraints
machine_time = 2*a + b <= 100     # Machine hours
labor = a + 2*b <= 80              # Labor hours
materials = a + b <= 50            # Raw materials

print(f"Constraints created: {machine_time.sense}, {labor.sense}, {materials.sense}")
Constraints created: <=, <=, <=

6 Step 4: Create and Solve the Problem

from optyx import Problem

prob = (
    Problem("production_planning")
    .maximize(profit)
    .subject_to(machine_time)
    .subject_to(labor)
    .subject_to(materials)
)

# Inspect the problem before solving
print(prob.summary())
Optyx Problem: production_planning
  Variables: 2
  Constraints: 3 (0 equality, 3 inequality)
  Objective: maximize
solution = prob.solve()

7 Step 5: Interpret Results

print("=" * 50)
print("PRODUCTION PLANNING SOLUTION")
print("=" * 50)
print(f"Status: {solution.status}")
print(f"Solve time: {solution.solve_time*1000:.1f} ms")
print()
print("Optimal Production:")
print(f"  Product A: {solution['product_a']:.1f} units")
print(f"  Product B: {solution['product_b']:.1f} units")
print()
print(f"Maximum Profit: ${solution.objective_value:.2f}")
==================================================
PRODUCTION PLANNING SOLUTION
==================================================
Status: SolverStatus.OPTIMAL
Solve time: 1.8 ms

Optimal Production:
  Product A: 50.0 units
  Product B: -0.0 units

Maximum Profit: $2000.00

8 Verifying Constraints

Let’s check that all constraints are satisfied:

a_opt = solution['product_a']
b_opt = solution['product_b']

print("Constraint Verification:")
print(f"  Machine time: {2*a_opt + b_opt:.1f} ≤ 100 ✓")
print(f"  Labor hours:  {a_opt + 2*b_opt:.1f} ≤ 80 ✓")
print(f"  Materials:    {a_opt + b_opt:.1f} ≤ 50 ✓")
Constraint Verification:
  Machine time: 100.0 ≤ 100 ✓
  Labor hours:  50.0 ≤ 80 ✓
  Materials:    50.0 ≤ 50 ✓

9 Sensitivity Analysis

What if we had more machine time?

from optyx import Variable, Problem

def solve_with_machine_capacity(capacity):
    a = Variable("product_a", lb=0)
    b = Variable("product_b", lb=0)
    
    sol = (
        Problem()
        .maximize(40*a + 30*b)
        .subject_to(2*a + b <= capacity)
        .subject_to(a + 2*b <= 80)
        .subject_to(a + b <= 50)
        .solve()
    )
    return sol.objective_value

print("Sensitivity to Machine Capacity:")
for cap in [80, 100, 120, 140]:
    profit = solve_with_machine_capacity(cap)
    print(f"  {cap} hours → ${profit:.2f}")
Sensitivity to Machine Capacity:
  80 hours → $1800.00
  100 hours → $2000.00
  120 hours → $2000.00
  140 hours → $2000.00

10 Alternative Formulation: Step-by-Step

You can also build problems incrementally:

from optyx import Variable, Problem

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

# Create empty problem
prob = Problem("incremental")

# Add objective
prob.maximize(40*x + 30*y)

# Add constraints one by one
prob.subject_to(2*x + y <= 100)
prob.subject_to(x + 2*y <= 80)
prob.subject_to(x + y <= 50)

# Solve
solution = prob.solve()
print(f"Same result: ${solution.objective_value:.2f}")
Same result: $2000.00

11 Common Patterns

11.1 Constraint from List

from optyx import Variable, Problem

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

constraints = [
    x + y >= 10,
    x <= 15,
    y <= 15,
    2*x + y <= 25
]

prob = Problem().minimize(x + y)
for c in constraints:
    prob.subject_to(c)

sol = prob.solve()
print(f"Minimum: {sol.objective_value:.2f}")
Minimum: 10.00

11.2 Multiple Variables

from optyx import Variable, Problem

# Create many variables
n = 5
vars = [Variable(f"x_{i}", lb=0, ub=10) for i in range(n)]

# Sum them
total = sum(vars[i] for i in range(n))

# Objective: minimize sum of squares
objective = sum(v**2 for v in vars)

sol = (
    Problem()
    .minimize(objective)
    .subject_to(total >= 10)
    .solve()
)

print("Optimal values:")
for i, v in enumerate(vars):
    print(f"  x_{i} = {sol[v.name]:.2f}")
Optimal values:
  x_0 = 2.00
  x_1 = 2.00
  x_2 = 2.00
  x_3 = 2.00
  x_4 = 2.00
Tip

For larger problems, use VectorVariable for better performance and cleaner code.


12 Next Steps