Goal Programming Example

Overview

This example demonstrates LumiX’s built-in goal programming support, which allows automatic conversion of hard constraints into soft constraints (goals) that can be violated with penalties.

Goal programming enables multi-objective optimization by prioritizing multiple conflicting goals and minimizing deviations from target values.

Problem Description

A company produces products with multiple conflicting objectives:

Goals (soft constraints that can be violated):

  1. Meet minimum production targets (Priority 1 - highest)

  2. Limit overtime hours (Priority 2 - medium)

  3. Achieve target profit (Priority 3 - lowest)

Hard Constraints (must be satisfied):

  • Total hours ≤ capacity + overtime

Challenge: Traditional LP requires choosing one objective. Goal programming allows pursuing multiple goals simultaneously with priorities.

Mathematical Formulation

Traditional LP (Single Objective):

\[\begin{split}\text{Maximize} \quad \text{profit} \\ \text{subject to:} \quad \text{production} \geq \text{target}\end{split}\]

Goal Programming (Multiple Goals):

\[\text{Minimize} \quad \sum_{p=1}^{P} w_p \sum_{g \in G_p} (d_g^+ + d_g^-)\]

Subject to (relaxed constraints):

\[\begin{split}\text{expr}_g + d_g^- - d_g^+ &= \text{rhs}_g \\ d_g^+, d_g^- &\geq 0\end{split}\]

where:

  • \(d_g^+\) = positive deviation (exceeding target)

  • \(d_g^-\) = negative deviation (falling short of target)

  • \(w_p\) = priority weight (e.g., \(10^6, 10^5, 10^4\))

  • \(G_p\) = goals at priority level \(p\)

Goal Types

Minimize Excess (LE Constraints)

For expr rhs goals, minimize \(d^+\) (exceeding is bad):

model.add_constraint(
    LXConstraint("overtime_limit")
    .expression(overtime_expr)
    .le()
    .rhs(40)
    .as_goal(priority=2, weight=1.0)
)

Minimize Shortfall (GE Constraints)

For expr rhs goals, minimize \(d^-\) (falling short is bad):

model.add_constraint(
    LXConstraint("production_target")
    .expression(production_expr)
    .ge()
    .rhs(100)
    .as_goal(priority=1, weight=1.0)
)

Minimize Both Deviations (EQ Constraints)

For expr = rhs goals, minimize both \(d^+\) and \(d^-\):

model.add_constraint(
    LXConstraint("profit_target")
    .expression(profit_expr)
    .eq()
    .rhs(1800)
    .as_goal(priority=3, weight=1.0)
)

Key Features

Automatic Goal Relaxation

Mark constraints as goals:

    Product A       :   100.00 units (Target: 100.00)
    Product B       :    80.00 units (Target: 80.00)
    ...

    Goal Satisfaction:
    Production Goal Product A: [SATISFIED]
      Under-production: 0.00
      Over-production:  0.00

Key Points:

  • .as_goal(priority, weight) marks constraint as soft

  • Priority determines importance (1 = highest)

  • Weight allows fine-tuning within priority level

Automatic Deviation Variables

LumiX automatically creates deviation variables:

# For each goal, LumiX creates:
# - goal_name_pos_dev (d⁺)
# - goal_name_neg_dev (d⁻)

# Original: production >= 100
# Relaxed:  production + d⁻ - d⁺ = 100

Weighted vs Sequential Mode

Weighted Mode (default - single solve):

model.set_goal_mode("weighted")
# Priorities converted to exponential weights:
# P1: 10^6, P2: 10^5, P3: 10^4

Sequential Mode (multiple solves - true preemptive):

model.set_goal_mode("sequential")
# Solve priorities lexicographically:
# 1. Minimize P1 deviations
# 2. Fix P1, minimize P2 deviations
# 3. Fix P2, minimize P3 deviations

Solution Analysis

Check goal satisfaction:

        LXConstraint(f"production_goal_{product.id}")
        .expression(prod_expr)
        .ge()
        .rhs(product.target_production)
        .as_goal(priority=1, weight=1.0)  # Highest priority
    )

# Goal 2: Limit overtime (Priority 2, medium)
# overtime <= MAX_OVERTIME
overtime_expr = LXLinearExpression().add_term(overtime, coeff=1.0)

model.add_constraint(
    LXConstraint("overtime_goal")
    .expression(overtime_expr)
    .le()
    .rhs(MAX_OVERTIME)
    .as_goal(priority=2, weight=1.0)  # Medium priority
)

Running the Example

Prerequisites:

pip install lumix
pip install ortools  # or cplex, gurobi

Run:

cd examples/11_goal_programming
python 01_basic_goal_programming.py

Expected Output:

======================================================================
Basic Goal Programming Example
======================================================================

Model Summary:
  Variables: 2 families + deviation variables
  Constraints: 1 hard + 7 goals
  Mode: weighted

======================================================================
Solution
======================================================================
Status: optimal
Objective: 1234.56 (weighted deviations)

----------------------------------------------------------------------
Production Plan:
----------------------------------------------------------------------
Product A       :   105.00 units  (Target: 100.00)
Product B       :    95.00 units  (Target: 80.00)

Overtime        :    45.00 hours (Target: <=50.00)
Total Profit    : $1,750.00 (Target: $1,800.00)

----------------------------------------------------------------------
Goal Satisfaction:
----------------------------------------------------------------------
Production Goal Product A: ✓ Satisfied
  Under-production: 0.00
  Over-production:  5.00

Production Goal Product B: ✓ Satisfied
  Under-production: 0.00
  Over-production:  15.00

Overtime Goal: ✓ Satisfied
  Under limit:  0.00
  Over limit:   0.00

Profit Goal: ✗ Not Satisfied
  Under target: $50.00
  Over target:  $0.00

Complete Code Walkthrough

Step 1: Define Variables

variables. The workflow includes:
    1. Building a production planning model with hard and soft constraints
    2. Marking constraints as goals with priorities and weights
    3. Automatically preparing goal programming (creates deviation variables)
    4. Solving with weighted goal programming (single optimization)
    5. Analyzing goal satisfaction and deviations

The example uses a simple production planning problem with three goals:
    - Priority 1 (highest): Meet production targets
    - Priority 2 (medium): Limit overtime hours
    - Priority 3 (lowest): Achieve target profit

Returns:
    None. Results are printed to console including:
        - Model summary with variables and constraints
        - Optimal solution with production quantities
        - Goal satisfaction status for each goal

Step 2: Add Hard Constraints

        $ python 01_basic_goal_programming.py

    Or import and run programmatically::

        >>> from examples.goal_programming.basic_goal_programming import main
        >>> main()

    Expected output::

Step 3: Add Goal Constraints

        ====================================================================

        Model Summary:
        ...

        Production Plan:
        Product A       :   100.00 units (Target: 100.00)
        Product B       :    80.00 units (Target: 80.00)
        ...

        Goal Satisfaction:
        Production Goal Product A: [SATISFIED]
          Under-production: 0.00
          Over-production:  0.00
        ...

Notes:
    Goal Programming concepts demonstrated:
        - Hard constraints: Must be satisfied (e.g., capacity limits)
        - Soft constraints (goals): Can be violated with penalty
        - Deviation variables: neg (under) and pos (over) deviation
        - Priorities: Higher priority goals are more important
        - Weights: Relative importance within same priority level

    The .as_goal() method marks a constraint as a goal:
        - .as_goal(priority=1, weight=1.0): Highest priority
        - Higher priorities dominate lower priorities in weighted mode
        - Within same priority, weights determine relative importance

    Weighted mode converts priorities to exponentially different weights:
        - Priority 1: weight * 10^6
        - Priority 2: weight * 10^5
        - Priority 3: weight * 10^4

    This ensures higher priorities are always satisfied first before
    considering lower priority goals.

    The .prepare_goal_programming() method automatically:
        - Creates deviation variables (neg, pos) for each goal

Step 4: Configure Goal Mode

        - Maintains the original model structure
"""
print("=" * 70)
print("Basic Goal Programming Example")

Step 5: Solve and Analyze Goals

    .continuous()
    .bounds(lower=0)
    .indexed_by(lambda p: p.id)
    .from_data(PRODUCTS)
)

# Scalar variable needs a data source - use single element
overtime = (
    LXVariable[int, float]("overtime")

Learning Objectives

After completing this example, you should understand:

  1. Multi-Objective Optimization: Pursuing multiple goals simultaneously

  2. Soft Constraints: Converting hard constraints to goals

  3. Deviation Variables: How d⁺ and d⁻ represent violations

  4. Priority Levels: Hierarchical goal importance

  5. Weighted Formulation: Single-solve approximation of priorities

  6. Goal Satisfaction: Analyzing which goals were achieved

Common Patterns

Pattern 1: Basic Goal Constraint

model.add_constraint(
    LXConstraint("goal_name")
    .expression(expr)
    .ge()  # or .le(), .eq()
    .rhs(target_value)
    .as_goal(priority=1, weight=1.0)
)

Pattern 2: Multiple Priorities

# Priority 1 (highest - must achieve if possible)
model.add_constraint(...).as_goal(priority=1, weight=1.0)

# Priority 2 (second - achieve after P1)
model.add_constraint(...).as_goal(priority=2, weight=1.0)

# Priority 3 (lowest - nice to have)
model.add_constraint(...).as_goal(priority=3, weight=1.0)

Pattern 3: Custom Objective (Priority 0)

# Use priority 0 to incorporate custom objective
model.add_constraint(
    LXConstraint("maximize_profit")
    .expression(profit_expr)
    .ge()
    .rhs(0)
    .as_goal(priority=0, weight=1.0)
)

Pattern 4: Indexed Goals

# Goals for each entity
for entity in ENTITIES:
    expr = LXLinearExpression().add_term(...)

    model.add_constraint(
        LXConstraint(f"goal_{entity.id}")
        .expression(expr)
        .ge()
        .rhs(entity.target)
        .as_goal(priority=1, weight=entity.importance)
    )

Priority Weighting

Weighted Mode Calculation

# Automatic weight calculation
priority_weight = 10 ** (6 - priority)
combined_weight = priority_weight * goal_weight

# Examples:
# P1, w=1.0: 10^6 × 1.0 = 1,000,000
# P2, w=0.5: 10^5 × 0.5 =    50,000
# P3, w=2.0: 10^4 × 2.0 =    20,000

This ensures higher priorities dominate in the objective function.

Custom Weights Within Priority

# Different weights within same priority
model.add_constraint(...).as_goal(priority=1, weight=2.0)  # More important
model.add_constraint(...).as_goal(priority=1, weight=1.0)  # Less important
model.add_constraint(...).as_goal(priority=1, weight=0.5)  # Least important

Goal Satisfaction Analysis

Check Individual Goals

# Check if goal satisfied
satisfied = solution.is_goal_satisfied("goal_name", tolerance=1e-6)

# Get deviation values
deviations = solution.get_goal_deviations("goal_name")
pos_dev = deviations["pos"]  # Over-achievement
neg_dev = deviations["neg"]  # Under-achievement

Indexed Goals

for entity in ENTITIES:
    goal_name = f"goal_{entity.id}"
    deviations = solution.get_goal_deviations(goal_name)

    # Deviations are dictionaries for indexed goals
    neg_dev_dict = deviations["neg"]
    pos_dev_dict = deviations["pos"]

    print(f"{entity.name}:")
    print(f"  Shortfall: {neg_dev_dict.get(entity.id, 0):.2f}")
    print(f"  Excess: {pos_dev_dict.get(entity.id, 0):.2f}")

Use Cases

Business Applications

  1. Production Planning: Balance profit, costs, quality goals

  2. Resource Allocation: Satisfy competing departmental needs

  3. Project Portfolio: Optimize across budget, risk, strategic fit

  4. Staff Scheduling: Balance coverage, costs, preferences

  5. Supply Chain: Optimize service level, cost, inventory goals

When to Use Goal Programming

Use goal programming when:

  • Multiple conflicting objectives exist

  • Trade-offs between goals are acceptable

  • Priorities can be assigned to objectives

  • Some constraints can be relaxed with penalty

Don’t use when:

  • Single objective is clear

  • All constraints are hard (must be satisfied)

  • Equal importance for all objectives

Extending the Example

Try These Modifications

  1. More Priorities: Add 4th and 5th priority levels

  2. Sequential Mode: Enable true preemptive GP (when available)

  3. Dynamic Weights: Adjust weights based on context

  4. Conditional Goals: Goals that activate under conditions

  5. Interactive: Allow user to set priorities dynamically

Next Steps

After mastering this example:

  1. Example 09 (Sensitivity Analysis): Understanding constraint values

  2. Example 08 (Scenario Analysis): Testing goal achievement under scenarios

  3. Advanced GP: Multi-priority weighted formulations

See Also

Related Examples:

API Reference:

References:

  • Charnes & Cooper (1961). “Management Models and Industrial Applications of Linear Programming”

  • Ignizio (1976). “Goal Programming and Extensions”

  • Romero (1991). “Handbook of Critical Issues in Goal Programming”

Files in This Example

  • 01_basic_goal_programming.py - Basic goal programming demonstration

  • 02_multi_priority_weighted.py - Advanced multi-priority example

  • README.md - Detailed documentation and usage guide