CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cma

CMA-ES, Covariance Matrix Adaptation Evolution Strategy for non-linear numerical optimization in Python

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

constraints-boundaries.mddocs/

Constraints and Boundaries

Comprehensive support for box constraints, boundary conditions, and general nonlinear constraints in CMA-ES optimization. This includes boundary handlers, constraint handlers, and constrained optimization interfaces.

Box Constraints and Boundary Handlers

Box constraints define simple lower and upper bounds for each variable. CMA-ES provides several boundary handling strategies.

Boundary Handler Classes

# All boundary handler classes from cma.boundary_handler
from cma.boundary_handler import BoundTransform, BoundPenalty, BoundNone, BoundDomainTransform

class BoundTransform:
    """
    Transform solutions to stay within box constraints using bijective transformation.
    
    This is the recommended boundary handler for most applications. It uses
    smooth, invertible transformations that preserve the CMA-ES distribution
    properties while ensuring feasibility.
    """
    
    def __init__(self, bounds):
        """
        Initialize boundary transformation handler.
        
        Parameters:
        -----------
        bounds : list or None
            Box constraints as [lower_bounds, upper_bounds] where each can be:
            - None: no bounds
            - scalar: same bound for all variables  
            - array-like: individual bounds per variable
            - Can contain None entries for unbounded variables
            
        Examples:
        ---------
        >>> import cma
        >>> 
        >>> # 2D box constraints [-5,5]^2
        >>> bounds = [[-5, -5], [5, 5]]
        >>> handler = cma.BoundTransform(bounds)
        >>> 
        >>> # Mixed bounds: x[0] in [0,10], x[1] unbounded, x[2] in [-1,1] 
        >>> bounds = [[0, None, -1], [10, None, 1]]
        >>> handler = cma.BoundTransform(bounds)
        >>> 
        >>> # Single bound for all variables
        >>> bounds = [[-2], [2]]  # All variables in [-2, 2]
        >>> handler = cma.BoundTransform(bounds)
        """
        pass
    
    def transform(self, x):
        """
        Transform solution from internal to external (feasible) coordinates.
        
        Parameters:
        -----------
        x : array-like
            Solution in internal coordinates (can be unbounded).
            
        Returns:
        --------
        numpy.ndarray
            Solution transformed to satisfy box constraints.
            
        Examples:
        ---------
        >>> bounds = [[-1, -1], [1, 1]]
        >>> handler = cma.BoundTransform(bounds)
        >>> 
        >>> # Large internal values are mapped to boundaries
        >>> x_internal = [100, -100]
        >>> x_feasible = handler.transform(x_internal)
        >>> print(x_feasible)  # Close to [1, -1]
        >>> 
        >>> # Values near zero map to middle of domain
        >>> x_internal = [0, 0]
        >>> x_feasible = handler.transform(x_internal)
        >>> print(x_feasible)  # Close to [0, 0]
        """
        pass

class BoundPenalty:
    """
    Handle bounds using penalty method.
    
    Penalizes solutions that violate constraints by adding penalty
    terms to the objective function value.
    """
    
    def __init__(self, bounds, **kwargs):
        """
        Initialize penalty-based boundary handler.
        
        Parameters:
        -----------
        bounds : list
            Box constraints as [lower_bounds, upper_bounds].
        **kwargs : dict
            Additional options for penalty computation.
        """
        pass
    
    def repair(self, x, copy_if_changed=True):
        """
        Repair solutions to satisfy bounds and compute penalties.
        
        Parameters:
        -----------
        x : array-like
            Solutions to repair.
        copy_if_changed : bool
            Whether to copy array if modifications needed.
            
        Returns:
        --------
        tuple[array, float]
            Repaired solutions and penalty value.
        """
        pass

class BoundNone:
    """
    No boundary handling - allows unbounded optimization.
    
    Dummy boundary handler for unconstrained problems.
    """
    
    def __init__(self, bounds=None):
        """Initialize no-bounds handler."""
        pass

class BoundDomainTransform:
    """
    Domain transformation for more complex boundary handling.
    
    Advanced boundary handler with customizable transformation methods.
    """
    
    def __init__(self, bounds, **kwargs):
        """
        Initialize domain transformation handler.
        
        Parameters:
        -----------
        bounds : list
            Boundary constraints.
        **kwargs : dict
            Transformation options.
        """
        pass
    
    def inverse_transform(self, x):
        """
        Transform from external (feasible) to internal coordinates.
        
        Parameters:
        -----------
        x : array-like
            Feasible solution satisfying box constraints.
            
        Returns:
        --------
        numpy.ndarray
            Solution in internal coordinates.
        """
        pass

class BoundPenalty:
    """
    Penalty-based boundary handler using quadratic penalties.
    
    Adds penalty terms to the objective function for constraint violations.
    Less smooth than BoundTransform but simpler conceptually.
    """
    
    def __init__(self, bounds):
        """
        Initialize penalty-based boundary handler.
        
        Parameters:
        -----------
        bounds : list
            Box constraints as [lower_bounds, upper_bounds].
            
        Examples:
        ---------
        >>> bounds = [[-5, -5], [5, 5]]
        >>> handler = cma.BoundPenalty(bounds)
        """
        pass
    
    def __call__(self, f, x):
        """
        Evaluate objective with penalty for bound violations.
        
        Parameters:
        -----------
        f : float
            Original objective function value.
            
        x : array-like
            Solution to check for bound violations.
            
        Returns:
        --------
        float
            Penalized objective value.
        """
        pass
    
    def repair(self, x):
        """
        Repair solution to satisfy box constraints by projection.
        
        Parameters:
        -----------
        x : array-like
            Potentially infeasible solution.
            
        Returns:
        --------
        numpy.ndarray
            Solution projected onto feasible region.
        """
        pass

class BoundNone:
    """
    No-op boundary handler for unconstrained problems.
    
    Provides same interface as other handlers but performs no transformations.
    """
    
    def __init__(self, bounds=None):
        """Initialize no-op boundary handler."""
        pass
    
    def transform(self, x):
        """Return x unchanged."""
        return x
    
    def inverse_transform(self, x):  
        """Return x unchanged."""
        return x

class BoundDomainTransform:
    """
    Function wrapper that automatically transforms domain using BoundTransform.
    
    Wraps an objective function to handle box constraints transparently.
    The function can be called with any input and constraints are handled
    automatically via coordinate transformation.
    """
    
    def __init__(self, function, boundaries):
        """
        Create domain-transforming function wrapper.
        
        Parameters:
        -----------
        function : callable
            Original objective function to be wrapped.
            
        boundaries : list
            Box constraints as [lower_bounds, upper_bounds].
            
        Examples:
        ---------
        >>> import cma
        >>> 
        >>> # Original unbounded function
        >>> def sphere(x):
        ...     return sum(xi**2 for xi in x)
        >>> 
        >>> # Create bounded version
        >>> bounds = [[-2, -2], [2, 2]]
        >>> bounded_sphere = cma.BoundDomainTransform(sphere, bounds)
        >>> 
        >>> # Can now call with any input - bounds handled automatically
        >>> result = bounded_sphere([100, -100])  # Maps to feasible region
        >>> 
        >>> # Use in optimization
        >>> x, es = cma.fmin2(bounded_sphere, [0, 0], 1.0)
        """
        pass
    
    def __call__(self, x, *args, **kwargs):
        """
        Evaluate wrapped function with automatic bound handling.
        
        Parameters:
        -----------
        x : array-like
            Input solution (will be transformed to feasible region).
            
        *args, **kwargs
            Additional arguments passed to wrapped function.
            
        Returns:
        --------
        float
            Function value evaluated at transformed (feasible) point.
        """
        pass

Box-Constrained Optimization Usage

import cma
import numpy as np

# Pattern 1: Simple box constraints using options
def simple_box_constraints():
    """Basic box constraint optimization."""
    
    def objective(x):
        # Rosenbrock function
        return sum(100*(x[1:] - x[:-1]**2)**2 + (1 - x[:-1])**2)
    
    # Define box constraints
    bounds = [[-2, -2, -2], [2, 2, 2]]  # Each variable in [-2, 2]
    
    # Optimize with bounds in options
    x_best, es = cma.fmin2(
        objective, 
        [0, 0, 0], 
        0.5,
        options={'bounds': bounds}
    )
    
    print(f"Bounded optimization result: {x_best}")
    print(f"All variables in bounds: {all(-2 <= xi <= 2 for xi in x_best)}")
    
    return x_best, es

# Pattern 2: Mixed bounds (some variables unbounded)  
def mixed_bounds_optimization():
    """Optimization with mixed bounded/unbounded variables."""
    
    def objective(x):
        return sum((x - np.array([1, 2, 3, 4]))**2)
    
    # Mixed bounds: x[0] >= 0, x[1] unbounded, x[2] in [-1,1], x[3] <= 5
    lower = [0, None, -1, None]
    upper = [None, None, 1, 5]
    bounds = [lower, upper]
    
    x_best, es = cma.fmin2(
        objective,
        [0.5, 2, 0, 4],
        0.3,
        options={'bounds': bounds}
    )
    
    print(f"Mixed bounds result: {x_best}")
    return x_best, es

# Pattern 3: Using BoundDomainTransform wrapper
def domain_transform_wrapper():
    """Using domain transformation wrapper."""
    
    # Original function (designed for unbounded domain)
    def unconstrained_function(x):
        return sum(xi**2 for xi in x)
    
    # Create bounded version
    bounds = [[-5, -5], [5, 5]]
    bounded_function = cma.BoundDomainTransform(unconstrained_function, bounds)
    
    # Optimize bounded version (no bounds option needed)
    x_best, es = cma.fmin2(bounded_function, [1, 1], 0.5)
    
    print(f"Domain transform result: {x_best}")
    return x_best, es

# Pattern 4: Manual boundary handler usage
def manual_boundary_handling():
    """Manual use of boundary handlers."""
    
    def objective(x):
        return sum(x**2)
    
    bounds = [[-3, -3, -3], [3, 3, 3]]
    
    # Create and configure boundary handler
    bound_handler = cma.BoundTransform(bounds)
    
    # Manual ask-and-tell with transformation
    es = cma.CMAEvolutionStrategy([0, 0, 0], 0.5)
    
    while not es.stop():
        # Get solutions in internal coordinates
        solutions = es.ask()
        
        # Transform to feasible coordinates and evaluate
        feasible_solutions = [bound_handler.transform(x) for x in solutions]
        fitness_values = [objective(x) for x in feasible_solutions]
        
        # Update with internal coordinates
        es.tell(solutions, fitness_values)
        
        if es.countiter % 50 == 0:
            es.disp()
    
    # Transform final result to feasible coordinates
    final_solution = bound_handler.transform(es.result.xbest)
    print(f"Manual handling result: {final_solution}")
    
    return final_solution, es

# Pattern 5: Comparing boundary handlers
def compare_boundary_handlers():
    """Compare different boundary handling approaches."""
    
    def objective(x):
        # Function with optimum at boundary
        return sum((x - 2)**2)  # Optimum at [2, 2] 
    
    bounds = [[-1, -1], [1, 1]]  # Optimum outside feasible region
    
    results = {}
    
    # BoundTransform (default)
    x1, es1 = cma.fmin2(
        objective, [0, 0], 0.3,
        options={'bounds': bounds, 'BoundaryHandler': 'BoundTransform'}
    )
    results['BoundTransform'] = (x1, es1.result.fbest)
    
    # BoundPenalty 
    x2, es2 = cma.fmin2(
        objective, [0, 0], 0.3,
        options={'bounds': bounds, 'BoundaryHandler': 'BoundPenalty'}  
    )
    results['BoundPenalty'] = (x2, es2.result.fbest)
    
    # Compare results
    for method, (x, f) in results.items():
        print(f"{method}: x = {x}, f = {f:.6f}")
    
    return results

# Run examples
simple_box_constraints()
mixed_bounds_optimization() 
domain_transform_wrapper()
manual_boundary_handling()
compare_boundary_handlers()

General Nonlinear Constraints

Support for general inequality and equality constraints through augmented Lagrangian methods.

Constrained Optimization Functions

def fmin_con2(
    objective_function, 
    x0, 
    sigma0,
    constraints=lambda x: [],
    find_feasible_first=False,
    find_feasible_final=False,
    kwargs_confit=None,
    **kwargs_fmin
):
    """
    Optimize objective function with general inequality constraints.
    
    This is the main interface for constrained optimization with CMA-ES
    using the augmented Lagrangian method.
    
    Parameters:
    -----------
    objective_function : callable
        Function to minimize, called as objective_function(x).
        
    x0 : array-like
        Initial solution estimate.
        
    sigma0 : float
        Initial step size.
        
    constraints : callable, optional
        Constraint function returning list of constraint values.
        Feasibility means all values <= 0. For equality constraint h(x) = 0,
        use two inequalities: [h(x) - eps, -h(x) - eps] with eps >= 0.
        
    find_feasible_first : bool, optional
        Whether to find feasible solution before optimization (default False).
        
    find_feasible_final : bool, optional  
        Whether to find feasible solution after optimization (default False).
        
    kwargs_confit : dict, optional
        Keyword arguments for ConstrainedFitnessAL instance.
        
    **kwargs_fmin : dict
        Additional arguments passed to fmin2.
        
    Returns:
    --------
    tuple[numpy.ndarray, CMAEvolutionStrategy]
        (xbest, es) where xbest is best feasible solution found and es
        contains optimization details and constraint handler as
        es.objective_function attribute.
        
    Examples:
    ---------
    >>> import cma
    >>> import numpy as np
    >>> 
    >>> # Minimize sphere subject to constraint x[0]^2 + x[1]^2 <= 1
    >>> def objective(x):
    ...     return sum(x**2)
    >>> 
    >>> def constraints(x):
    ...     return [x[0]**2 + x[1]**2 - 1]  # <= 0 for feasibility
    >>> 
    >>> x_best, es = cma.fmin_con2(
    ...     objective, 
    ...     [0.5, 0.5], 
    ...     0.3,
    ...     constraints=constraints,
    ...     options={'maxfevals': 2000, 'verb_disp': 100}
    ... )
    >>> 
    >>> print(f"Best solution: {x_best}")
    >>> print(f"Constraint value: {constraints(x_best)[0]:.6f}")  # Should be <= 0
    >>> 
    >>> # Multiple constraints
    >>> def multiple_constraints(x):
    ...     return [
    ...         x[0]**2 + x[1]**2 - 1,      # Circle constraint
    ...         -x[0] - 0.5,                 # x[0] >= -0.5  
    ...         -x[1] - 0.5                  # x[1] >= -0.5
    ...     ]
    >>> 
    >>> x_best, es = cma.fmin_con2(
    ...     objective,
    ...     [0, 0], 
    ...     0.3,
    ...     constraints=multiple_constraints
    ... )
    >>> 
    >>> # Equality constraint h(x) = 0 as two inequalities  
    >>> def equality_as_inequalities(x):
    ...     h_val = x[0] + x[1] - 1  # Want x[0] + x[1] = 1
    ...     eps = 1e-6
    ...     return [h_val - eps, -h_val - eps]  # h-eps <= 0 and -h-eps <= 0
    >>> 
    >>> x_best, es = cma.fmin_con2(
    ...     objective,
    ...     [0.5, 0.5],
    ...     0.2, 
    ...     constraints=equality_as_inequalities
    ... )
    """
    pass

def fmin_con(
    objective_function,
    x0,
    sigma0, 
    g=lambda x: [],
    h=lambda x: [],
    **kwargs
):
    """
    DEPRECATED: Use fmin_con2 instead.
    
    Legacy interface for constrained optimization. Provides separate
    inequality (g) and equality (h) constraint functions.
    
    Parameters:
    -----------
    objective_function : callable
        Function to minimize.
        
    x0 : array-like
        Initial solution.
        
    sigma0 : float
        Initial step size.
        
    g : callable, optional
        Inequality constraints, g(x) <= 0 for feasibility.
        
    h : callable, optional
        Equality constraints, h(x) = 0 for feasibility.
        
    **kwargs : dict
        Additional arguments for fmin2.
        
    Returns:
    --------
    tuple[numpy.ndarray, CMAEvolutionStrategy]
        (xbest, es) with constraint information in es.best_feasible.
    """
    pass

Constraint Handler Classes

class ConstrainedFitnessAL:
    """
    Augmented Lagrangian fitness function for constrained optimization.
    
    Transforms constrained optimization problem into sequence of unconstrained
    problems using augmented Lagrangian method. Can be used directly for
    fine-grained control over constraint handling.
    """
    
    def __init__(
        self, 
        objective_function, 
        constraints_function,
        find_feasible_first=False,
        logging=True
    ):
        """
        Initialize augmented Lagrangian constraint handler.
        
        Parameters:
        -----------
        objective_function : callable
            Original objective function to minimize.
            
        constraints_function : callable
            Function returning list of constraint values (g(x) <= 0).
            
        find_feasible_first : bool, optional
            Whether to find feasible point before optimization.
            
        logging : bool, optional
            Whether to enable constraint violation logging.
            
        Examples:
        ---------
        >>> import cma
        >>> 
        >>> def objective(x):
        ...     return sum(x**2)
        >>> 
        >>> def constraints(x):
        ...     return [x[0] + x[1] - 1]  # x[0] + x[1] <= 1
        >>> 
        >>> # Create augmented Lagrangian fitness
        >>> al_fitness = cma.ConstrainedFitnessAL(objective, constraints)
        >>> 
        >>> # Use with CMA-ES
        >>> es = cma.CMAEvolutionStrategy([0, 0], 0.3)
        >>> 
        >>> while not es.stop():
        ...     solutions = es.ask()
        ...     fitness_values = [al_fitness(x) for x in solutions]
        ...     es.tell(solutions, fitness_values)
        ...     al_fitness.update(es)  # Update Lagrange multipliers
        >>> 
        >>> # Best feasible solution
        >>> x_best = al_fitness.best_feasible.x
        >>> print(f"Best feasible: {x_best}")
        """
        pass
    
    def __call__(self, x):
        """
        Evaluate augmented Lagrangian function.
        
        Parameters:
        -----------
        x : array-like
            Solution to evaluate.
            
        Returns:
        --------
        float
            Augmented Lagrangian value (objective + constraint penalties).
        """
        pass
    
    def update(self, es):
        """
        Update Lagrange multipliers and penalty parameters.
        
        Should be called after each CMA-ES iteration (tell operation).
        
        Parameters:
        -----------
        es : CMAEvolutionStrategy
            Evolution strategy instance to get population information.
        """
        pass
    
    @property
    def best_feasible(self):
        """
        Best feasible solution found so far.
        
        Returns:
        --------
        object
            Object with attributes x (solution), f (objective value),
            g (constraint values), and info dictionary.
        """
        pass

class AugmentedLagrangian:
    """
    Lower-level augmented Lagrangian implementation.
    
    Provides direct access to augmented Lagrangian computations for
    advanced users who need fine control over the constraint handling process.
    """
    
    def __init__(self, dimension):
        """
        Initialize augmented Lagrangian handler.
        
        Parameters:
        -----------
        dimension : int
            Problem dimension.
        """
        pass
    
    def __call__(self, x, f_x, g_x):
        """
        Compute augmented Lagrangian value.
        
        Parameters:
        -----------
        x : array-like
            Solution vector.
            
        f_x : float
            Objective function value at x.
            
        g_x : array-like
            Constraint values at x.
            
        Returns:
        --------
        float
            Augmented Lagrangian value.
        """
        pass
    
    def update(self, X, F, G):
        """
        Update Lagrange multipliers and penalty parameters.
        
        Parameters:
        -----------
        X : list[array]
            Population of solutions.
            
        F : list[float]
            Objective function values.
            
        G : list[list]
            Constraint values for each solution.
        """
        pass

Constrained Optimization Usage Patterns

import cma
import numpy as np

# Pattern 1: Simple inequality constraint
def simple_inequality_constraint():
    """Optimization with single inequality constraint."""
    
    def objective(x):
        # Minimize distance to point [2, 2] 
        return sum((x - np.array([2, 2]))**2)
    
    def constraints(x):
        # Must stay within unit circle: x[0]^2 + x[1]^2 <= 1
        return [x[0]**2 + x[1]**2 - 1]
    
    x_best, es = cma.fmin_con2(
        objective,
        [0.5, 0.5],  # Start inside feasible region
        0.2,
        constraints=constraints,
        options={'maxfevals': 3000, 'verb_disp': 100}
    )
    
    print(f"Simple constraint result: {x_best}")
    print(f"Constraint violation: {max(0, constraints(x_best)[0]):.6f}")
    
    return x_best, es

# Pattern 2: Multiple inequality constraints
def multiple_inequality_constraints():
    """Optimization with multiple inequality constraints."""
    
    def objective(x):
        # Quadratic function with minimum at [1, 1, 1]
        return sum((x - 1)**2)
    
    def constraints(x):
        return [
            x[0] + x[1] + x[2] - 2,     # x[0] + x[1] + x[2] <= 2
            -x[0],                       # x[0] >= 0
            -x[1],                       # x[1] >= 0  
            -x[2],                       # x[2] >= 0
            x[0]**2 + x[1]**2 - 1       # x[0]^2 + x[1]^2 <= 1
        ]
    
    x_best, es = cma.fmin_con2(
        objective,
        [0.3, 0.3, 0.3],
        0.2,
        constraints=constraints,
        options={'maxfevals': 5000}
    )
    
    print(f"Multiple constraints result: {x_best}")
    constraint_vals = constraints(x_best)
    print(f"All constraints satisfied: {all(g <= 1e-6 for g in constraint_vals)}")
    
    return x_best, es

# Pattern 3: Equality constraints via inequalities
def equality_constraint_example():
    """Handle equality constraints as pairs of inequalities."""
    
    def objective(x):
        return x[0]**2 + x[1]**2
    
    def constraints_with_equality(x):
        # Equality: x[0] + x[1] = 1 (as two inequalities)
        h1 = x[0] + x[1] - 1
        tolerance = 1e-6
        
        return [
            h1 - tolerance,    # x[0] + x[1] <= 1 + tol
            -h1 - tolerance,   # x[0] + x[1] >= 1 - tol  
            -x[0] + 0.1,       # x[0] >= -0.1 (inequality)
            -x[1] + 0.1        # x[1] >= -0.1 (inequality)
        ]
    
    x_best, es = cma.fmin_con2(
        objective,
        [0.5, 0.5],
        0.2,
        constraints=constraints_with_equality,
        options={'maxfevals': 2000}
    )
    
    print(f"Equality constraint result: {x_best}")
    print(f"Equality satisfied: {abs(x_best[0] + x_best[1] - 1):.6f}")
    
    return x_best, es

# Pattern 4: Direct use of ConstrainedFitnessAL
def direct_constraint_handler_use():
    """Direct use of ConstrainedFitnessAL for advanced control."""
    
    def objective(x):
        return sum(x**4)  # Quartic function
    
    def constraints(x):
        return [
            x[0]**2 + x[1]**2 - 4,  # Stay in circle of radius 2
            x[0] * x[1] - 0.5        # Product constraint
        ]
    
    # Create constraint handler directly
    constraint_handler = cma.ConstrainedFitnessAL(
        objective, 
        constraints,
        find_feasible_first=True  # Find feasible start
    )
    
    # Manual optimization loop
    es = cma.CMAEvolutionStrategy([1, 1], 0.5)
    
    iteration = 0
    while not es.stop() and iteration < 200:
        solutions = es.ask()
        
        # Evaluate augmented Lagrangian fitness
        fitness_values = [constraint_handler(x) for x in solutions]
        
        es.tell(solutions, fitness_values)
        
        # Update constraint handler
        constraint_handler.update(es)
        
        if iteration % 50 == 0:
            best_feasible = constraint_handler.best_feasible
            if best_feasible.x is not None:
                print(f"Iter {iteration}: best feasible f = {best_feasible.f:.6f}")
            else:
                print(f"Iter {iteration}: no feasible solution yet")
        
        iteration += 1
    
    # Extract best feasible solution
    best_feasible = constraint_handler.best_feasible
    if best_feasible.x is not None:
        print(f"Final feasible solution: {best_feasible.x}")
        print(f"Final objective value: {best_feasible.f}")
        print(f"Final constraint values: {best_feasible.g}")
    else:
        print("No feasible solution found")
    
    return best_feasible, es

# Pattern 5: Feasibility search
def feasibility_search_example():
    """Find feasible solutions for difficult constraint sets."""
    
    def objective(x):
        # Minimize sum of squares (not the main goal)
        return sum(x**2)
    
    def difficult_constraints(x):
        return [
            x[0]**2 + x[1]**2 - 0.25,   # Inside small circle
            -(x[0] - 1)**2 - (x[1] - 1)**2 + 0.5,  # Outside another circle
            x[0] + x[1] - 1.2,           # Above line
            -x[0] - x[1] + 0.8           # Below line  
        ]
    
    # First find any feasible solution
    x_feasible, es = cma.fmin_con2(
        lambda x: 0,  # Dummy objective - just find feasible point
        [0, 0],
        0.5,
        constraints=difficult_constraints,
        find_feasible_first=True,
        options={'maxfevals': 5000, 'verb_disp': 200}
    )
    
    constraint_vals = difficult_constraints(x_feasible)
    is_feasible = all(g <= 1e-6 for g in constraint_vals)
    
    print(f"Found feasible solution: {is_feasible}")
    if is_feasible:
        print(f"Feasible point: {x_feasible}")
        
        # Now optimize from feasible starting point
        x_optimal, es2 = cma.fmin_con2(
            objective,  # Real objective
            x_feasible,  # Start from feasible point
            0.1,         # Smaller step size
            constraints=difficult_constraints,
            options={'maxfevals': 3000}
        )
        
        print(f"Optimized feasible solution: {x_optimal}")
        print(f"Final objective: {objective(x_optimal):.6f}")
    
    return x_feasible, es

# Run constraint examples
simple_inequality_constraint()
multiple_inequality_constraints()
equality_constraint_example() 
direct_constraint_handler_use()
feasibility_search_example()

Integration with Box Constraints

Combining general constraints with box constraints for comprehensive constraint handling.

import cma
import numpy as np

def combined_constraints_example():
    """Example combining box constraints with general constraints."""
    
    def objective(x):
        # Rosenbrock function
        return sum(100*(x[1:] - x[:-1]**2)**2 + (1 - x[:-1])**2)
    
    def general_constraints(x):
        # Nonlinear constraint
        return [x[0]**2 + x[1]**2 - 2]  # Stay in circle
    
    # Box constraints: each variable in [-3, 3]
    box_bounds = [[-3, -3], [3, 3]]
    
    x_best, es = cma.fmin_con2(
        objective,
        [0.5, 0.5], 
        0.3,
        constraints=general_constraints,
        options={
            'bounds': box_bounds,        # Box constraints via options
            'maxfevals': 5000,
            'verb_disp': 100
        }
    )
    
    print(f"Combined constraints result: {x_best}")
    print(f"In box bounds: {all(-3 <= xi <= 3 for xi in x_best)}")
    print(f"Satisfies circle: {general_constraints(x_best)[0] <= 0}")
    
    return x_best, es

def integer_constrained_optimization():
    """Example with integer variables and constraints."""
    
    def objective(x):
        # Integer variables x[0], x[1] should be close to [3, 5]
        return (x[0] - 3)**2 + (x[1] - 5)**2 + sum(x[2:]**2)
    
    def constraints(x):
        # Sum of integer variables must be <= 7
        return [x[0] + x[1] - 7]
    
    x_best, es = cma.fmin_con2(
        objective,
        [2, 4, 0, 0],
        0.5, 
        constraints=constraints,
        options={
            'integer_variables': [0, 1],  # x[0], x[1] are integers
            'bounds': [[0, 0, -5, -5], [10, 10, 5, 5]],  # Box bounds
            'maxfevals': 3000
        }
    )
    
    print(f"Integer constrained result: {x_best}")
    print(f"Integer values: {[int(round(x_best[i])) for i in [0, 1]]}")
    print(f"Sum constraint: {x_best[0] + x_best[1]} <= 7")
    
    return x_best, es

combined_constraints_example()
integer_constrained_optimization()

Advanced Constraint Handling

Techniques for difficult constraint scenarios and performance optimization.

import cma
import numpy as np

def constraint_violation_monitoring():
    """Monitor and analyze constraint violations during optimization."""
    
    class ConstraintMonitor:
        def __init__(self):
            self.violations = []
            self.iterations = []
        
        def __call__(self, es):
            # Custom callback to monitor constraint violations
            if hasattr(es, 'objective_function'):
                cf = es.objective_function
                if hasattr(cf, 'best_feasible') and cf.best_feasible.x is not None:
                    max_violation = max(0, max(cf.best_feasible.g))
                else:
                    max_violation = float('inf')
                
                self.violations.append(max_violation)
                self.iterations.append(es.countiter)
    
    def objective(x):
        return sum((x - np.array([1, 2]))**2)
    
    def constraints(x):
        return [
            x[0]**2 + x[1]**2 - 4,    # Circle constraint
            -x[0] - x[1] + 2.5         # Linear constraint
        ]
    
    monitor = ConstraintMonitor()
    
    x_best, es = cma.fmin_con2(
        objective,
        [0, 0],
        0.5,
        constraints=constraints,
        callback=monitor,  # Add monitoring callback
        options={'maxfevals': 2000, 'verb_disp': 100}
    )
    
    print(f"Monitored optimization result: {x_best}")
    print(f"Final max violation: {monitor.violations[-1]:.6f}")
    print(f"Violation decreased monotonically: {all(monitor.violations[i] >= monitor.violations[i+1] for i in range(len(monitor.violations)-1))}")
    
    return x_best, monitor

def adaptive_constraint_parameters():
    """Example with adaptive constraint handling parameters."""
    
    def objective(x):
        return sum(x**2)
    
    def constraints(x):
        return [x[0] + x[1] - 1, x[0]**2 + x[1]**2 - 2]
    
    # Custom constraint handler with adaptive parameters
    constraint_handler = cma.ConstrainedFitnessAL(
        objective,
        constraints,
        logging=True
    )
    
    # Manual optimization with parameter adaptation
    es = cma.CMAEvolutionStrategy([0.5, 0.5], 0.3)
    
    while not es.stop():
        solutions = es.ask()
        fitness_values = [constraint_handler(x) for x in solutions]
        es.tell(solutions, fitness_values)
        constraint_handler.update(es)
        
        # Adaptive parameter adjustment
        if es.countiter % 50 == 0:
            # Access internal augmented Lagrangian
            al = constraint_handler.al
            
            # Example: adapt penalty parameters based on progress
            if hasattr(al, 'gamma') and es.countiter > 100:
                # Increase penalty if not making progress
                best = constraint_handler.best_feasible
                if best.x is not None and max(best.g) > 1e-3:
                    al.gamma *= 1.1  # Increase penalty parameter
    
    best_solution = constraint_handler.best_feasible
    print(f"Adaptive parameters result: {best_solution.x}")
    
    return best_solution, es

def constraint_approximation_example():
    """Handle expensive constraints using approximation."""
    
    def objective(x):
        return sum((x - 1)**2)
    
    # Simulate expensive constraint evaluation
    constraint_call_count = [0]
    
    def expensive_constraints(x):
        constraint_call_count[0] += 1
        # Simulate computational cost
        import time
        time.sleep(0.001)  # Simulate 1ms computation
        
        return [
            x[0]**2 + x[1]**2 - 1,  # Circle constraint
            x[0] * x[1] + 0.5        # Product constraint  
        ]
    
    # Cache constraint evaluations
    constraint_cache = {}
    
    def cached_constraints(x):
        x_key = tuple(np.round(x, 6))  # Round for cache key
        if x_key in constraint_cache:
            return constraint_cache[x_key]
        
        result = expensive_constraints(x)
        constraint_cache[x_key] = result
        return result
    
    x_best, es = cma.fmin_con2(
        objective,
        [0.5, 0.5],
        0.2,
        constraints=cached_constraints,
        options={'maxfevals': 1000, 'verb_disp': 50}
    )
    
    print(f"Cached constraints result: {x_best}")
    print(f"Constraint evaluations: {constraint_call_count[0]}")
    print(f"Cache entries: {len(constraint_cache)}")
    
    return x_best, es

# Run advanced constraint examples
constraint_violation_monitoring()
adaptive_constraint_parameters()
constraint_approximation_example()

Install with Tessl CLI

npx tessl i tessl/pypi-cma

docs

advanced-optimization.md

configuration.md

constraints-boundaries.md

core-optimization.md

fitness-functions.md

index.md

logging-analysis.md

samplers-adaptation.md

tile.json