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) >= 0to-g(x) <= 0
Constraint Handling Techniques
1. Feasibility First (Default)
Mechanism: Always prefer feasible over infeasible solutions Comparison:
- Both feasible → compare by objective values
- One feasible, one infeasible → feasible wins
- 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:
- Show representative solutions to decision maker
- Gather feedback on preferences
- Focus search on preferred regions
- 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:
- Normalize objectives before MCDM
- Visualize Pareto front to understand trade-offs
- Consider multiple methods for robust selection
- Validate results with domain experts
- Document assumptions and preference sources
- 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}")