Files
gh-k-dense-ai-claude-scient…/skills/pymoo/references/constraints_mcdm.md
2025-11-30 08:30:10 +08:00

11 KiB

Pymoo Constraints and Decision Making Reference

Reference for constraint handling and multi-criteria decision making in pymoo.

Constraint Handling

Defining Constraints

Constraints are specified in the Problem definition:

from pymoo.core.problem import ElementwiseProblem
import numpy as np

class ConstrainedProblem(ElementwiseProblem):
    def __init__(self):
        super().__init__(
            n_var=2,
            n_obj=2,
            n_ieq_constr=2,    # Number of inequality constraints
            n_eq_constr=1,      # Number of equality constraints
            xl=np.array([0, 0]),
            xu=np.array([5, 5])
        )

    def _evaluate(self, x, out, *args, **kwargs):
        # Objectives
        f1 = x[0]**2 + x[1]**2
        f2 = (x[0]-1)**2 + (x[1]-1)**2

        out["F"] = [f1, f2]

        # Inequality constraints (formulated as g(x) <= 0)
        g1 = x[0] + x[1] - 5  # x[0] + x[1] >= 5 → -(x[0] + x[1] - 5) <= 0
        g2 = x[0]**2 + x[1]**2 - 25  # x[0]^2 + x[1]^2 <= 25

        out["G"] = [g1, g2]

        # Equality constraints (formulated as h(x) = 0)
        h1 = x[0] - 2*x[1]

        out["H"] = [h1]

Constraint formulation rules:

  • Inequality: g(x) <= 0 (feasible when negative or zero)
  • Equality: h(x) = 0 (feasible when zero)
  • Convert g(x) >= 0 to -g(x) <= 0

Constraint Handling Techniques

1. Feasibility First (Default)

Mechanism: Always prefer feasible over infeasible solutions Comparison:

  1. Both feasible → compare by objective values
  2. One feasible, one infeasible → feasible wins
  3. Both infeasible → compare by constraint violation

Usage:

from pymoo.algorithms.moo.nsga2 import NSGA2

# Feasibility first is default for most algorithms
algorithm = NSGA2(pop_size=100)

Advantages:

  • Works with any sorting-based algorithm
  • Simple and effective
  • No parameter tuning

Disadvantages:

  • May struggle with small feasible regions
  • Can ignore good infeasible solutions

2. Penalty Methods

Mechanism: Add penalty to objective based on constraint violation Formula: F_penalized = F + penalty_factor * violation

Usage:

from pymoo.algorithms.soo.nonconvex.ga import GA
from pymoo.constraints.as_penalty import ConstraintsAsPenalty

# Wrap problem with penalty
problem_with_penalty = ConstraintsAsPenalty(problem, penalty=1e6)

algorithm = GA(pop_size=100)

Parameters:

  • penalty: Penalty coefficient (tune based on problem scale)

Advantages:

  • Converts constrained to unconstrained problem
  • Works with any optimization algorithm

Disadvantages:

  • Penalty parameter sensitive
  • May need problem-specific tuning

3. Constraint as Objective

Mechanism: Treat constraint violation as additional objective Result: Multi-objective problem with M+1 objectives (M original + constraint)

Usage:

from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.constraints.as_obj import ConstraintsAsObjective

# Add constraint violation as objective
problem_with_cv_obj = ConstraintsAsObjective(problem)

algorithm = NSGA2(pop_size=100)

Advantages:

  • No parameter tuning
  • Maintains infeasible solutions that may be useful
  • Works well when feasible region is small

Disadvantages:

  • Increases problem dimensionality
  • More complex Pareto front analysis

4. Epsilon-Constraint Handling

Mechanism: Dynamic feasibility threshold Concept: Gradually tighten constraint tolerance over generations

Advantages:

  • Smooth transition to feasible region
  • Helps with difficult constraint landscapes

Disadvantages:

  • Algorithm-specific implementation
  • Requires parameter tuning

5. Repair Operators

Mechanism: Modify infeasible solutions to satisfy constraints Application: After crossover/mutation, repair offspring

Usage:

from pymoo.core.repair import Repair

class MyRepair(Repair):
    def _do(self, problem, X, **kwargs):
        # Project X onto feasible region
        # Example: clip to bounds
        X = np.clip(X, problem.xl, problem.xu)
        return X

from pymoo.algorithms.soo.nonconvex.ga import GA

algorithm = GA(pop_size=100, repair=MyRepair())

Advantages:

  • Maintains feasibility throughout optimization
  • Can encode domain knowledge

Disadvantages:

  • Requires problem-specific implementation
  • May restrict search

Constraint-Handling Algorithms

Some algorithms have built-in constraint handling:

SRES (Stochastic Ranking Evolution Strategy)

Purpose: Single-objective constrained optimization Mechanism: Stochastic ranking balances objectives and constraints

Usage:

from pymoo.algorithms.soo.nonconvex.sres import SRES

algorithm = SRES()

ISRES (Improved SRES)

Purpose: Enhanced constrained optimization Improvements: Better parameter adaptation

Usage:

from pymoo.algorithms.soo.nonconvex.isres import ISRES

algorithm = ISRES()

Constraint Handling Guidelines

Choose technique based on:

Problem Characteristic Recommended Technique
Large feasible region Feasibility First
Small feasible region Constraint as Objective, Repair
Heavily constrained SRES/ISRES, Epsilon-constraint
Linear constraints Repair (projection)
Nonlinear constraints Feasibility First, Penalty
Known feasible solutions Biased initialization

Multi-Criteria Decision Making (MCDM)

After obtaining a Pareto front, MCDM helps select preferred solution(s).

Decision Making Context

Pareto front characteristics:

  • Multiple non-dominated solutions
  • Each represents different trade-off
  • No objectively "best" solution
  • Requires decision maker preferences

MCDM Methods in Pymoo

1. Pseudo-Weights

Concept: Weight each objective, select solution minimizing weighted sum Formula: score = w1*f1 + w2*f2 + ... + wM*fM

Usage:

from pymoo.mcdm.pseudo_weights import PseudoWeights

# Define weights (must sum to 1)
weights = np.array([0.3, 0.7])  # 30% weight on f1, 70% on f2

dm = PseudoWeights(weights)
best_idx = dm.do(result.F)
best_solution = result.X[best_idx]

When to use:

  • Clear preference articulation available
  • Objectives commensurable
  • Linear trade-offs acceptable

Limitations:

  • Requires weight specification
  • Linear assumption may not capture preferences
  • Sensitive to objective scaling

2. Compromise Programming

Concept: Select solution closest to ideal point Metric: Distance to ideal (e.g., Euclidean, Tchebycheff)

Usage:

from pymoo.mcdm.compromise_programming import CompromiseProgramming

dm = CompromiseProgramming()
best_idx = dm.do(result.F, ideal=ideal_point, nadir=nadir_point)

When to use:

  • Ideal objective values known or estimable
  • Balanced consideration of all objectives
  • No clear weight preferences

3. Interactive Decision Making

Concept: Iterative preference refinement Process:

  1. Show representative solutions to decision maker
  2. Gather feedback on preferences
  3. Focus search on preferred regions
  4. Repeat until satisfactory solution found

Approaches:

  • Reference point methods
  • Trade-off analysis
  • Progressive preference articulation

Decision Making Workflow

Step 1: Normalize objectives

# Normalize to [0, 1] for fair comparison
F_norm = (result.F - result.F.min(axis=0)) / (result.F.max(axis=0) - result.F.min(axis=0))

Step 2: Analyze trade-offs

from pymoo.visualization.scatter import Scatter

plot = Scatter()
plot.add(result.F)
plot.show()

# Identify knee points, extreme solutions

Step 3: Apply MCDM method

from pymoo.mcdm.pseudo_weights import PseudoWeights

weights = np.array([0.4, 0.6])  # Based on preferences
dm = PseudoWeights(weights)
selected = dm.do(F_norm)

Step 4: Validate selection

# Visualize selected solution
from pymoo.visualization.petal import Petal

plot = Petal()
plot.add(result.F[selected], label="Selected")
# Add other candidates for comparison
plot.show()

Advanced MCDM Techniques

Knee Point Detection

Concept: Solutions where small improvement in one objective causes large degradation in others

Usage:

from pymoo.mcdm.knee import KneePoint

km = KneePoint()
knee_idx = km.do(result.F)
knee_solutions = result.X[knee_idx]

When to use:

  • No clear preferences
  • Balanced trade-offs desired
  • Convex Pareto fronts

Hypervolume Contribution

Concept: Select solutions contributing most to hypervolume Use case: Maintain diverse subset of solutions

Usage:

from pymoo.indicators.hv import HV

hv = HV(ref_point=reference_point)
hv_contributions = hv.calc_contributions(result.F)

# Select top contributors
top_k = 5
top_indices = np.argsort(hv_contributions)[-top_k:]
selected_solutions = result.X[top_indices]

Decision Making Guidelines

When decision maker has:

Preference Information Recommended Method
Clear objective weights Pseudo-Weights
Ideal target values Compromise Programming
No prior preferences Knee Point, Visual inspection
Conflicting criteria Interactive methods
Need diverse subset Hypervolume contribution

Best practices:

  1. Normalize objectives before MCDM
  2. Visualize Pareto front to understand trade-offs
  3. Consider multiple methods for robust selection
  4. Validate results with domain experts
  5. Document assumptions and preference sources
  6. Perform sensitivity analysis on weights/parameters

Integration Example

Complete workflow with constraint handling and decision making:

from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.optimize import minimize
from pymoo.mcdm.pseudo_weights import PseudoWeights
import numpy as np

# Define constrained problem
problem = MyConstrainedProblem()

# Setup algorithm with feasibility-first constraint handling
algorithm = NSGA2(
    pop_size=100,
    eliminate_duplicates=True
)

# Optimize
result = minimize(
    problem,
    algorithm,
    ('n_gen', 200),
    seed=1,
    verbose=True
)

# Filter feasible solutions only
feasible_mask = result.CV[:, 0] == 0  # Constraint violation = 0
F_feasible = result.F[feasible_mask]
X_feasible = result.X[feasible_mask]

# Normalize objectives
F_norm = (F_feasible - F_feasible.min(axis=0)) / (F_feasible.max(axis=0) - F_feasible.min(axis=0))

# Apply MCDM
weights = np.array([0.5, 0.5])
dm = PseudoWeights(weights)
best_idx = dm.do(F_norm)

# Get final solution
best_solution = X_feasible[best_idx]
best_objectives = F_feasible[best_idx]

print(f"Selected solution: {best_solution}")
print(f"Objective values: {best_objectives}")