or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-optimization.mdconfiguration.mdconstraints-boundaries.mdcore-optimization.mdfitness-functions.mdindex.mdlogging-analysis.mdsamplers-adaptation.md

constraints-boundaries.mddocs/

0

# Constraints and Boundaries

1

2

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.

3

4

## Box Constraints and Boundary Handlers

5

6

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

7

8

### Boundary Handler Classes

9

10

```python { .api }

11

# All boundary handler classes from cma.boundary_handler

12

from cma.boundary_handler import BoundTransform, BoundPenalty, BoundNone, BoundDomainTransform

13

14

class BoundTransform:

15

"""

16

Transform solutions to stay within box constraints using bijective transformation.

17

18

This is the recommended boundary handler for most applications. It uses

19

smooth, invertible transformations that preserve the CMA-ES distribution

20

properties while ensuring feasibility.

21

"""

22

23

def __init__(self, bounds):

24

"""

25

Initialize boundary transformation handler.

26

27

Parameters:

28

-----------

29

bounds : list or None

30

Box constraints as [lower_bounds, upper_bounds] where each can be:

31

- None: no bounds

32

- scalar: same bound for all variables

33

- array-like: individual bounds per variable

34

- Can contain None entries for unbounded variables

35

36

Examples:

37

---------

38

>>> import cma

39

>>>

40

>>> # 2D box constraints [-5,5]^2

41

>>> bounds = [[-5, -5], [5, 5]]

42

>>> handler = cma.BoundTransform(bounds)

43

>>>

44

>>> # Mixed bounds: x[0] in [0,10], x[1] unbounded, x[2] in [-1,1]

45

>>> bounds = [[0, None, -1], [10, None, 1]]

46

>>> handler = cma.BoundTransform(bounds)

47

>>>

48

>>> # Single bound for all variables

49

>>> bounds = [[-2], [2]] # All variables in [-2, 2]

50

>>> handler = cma.BoundTransform(bounds)

51

"""

52

pass

53

54

def transform(self, x):

55

"""

56

Transform solution from internal to external (feasible) coordinates.

57

58

Parameters:

59

-----------

60

x : array-like

61

Solution in internal coordinates (can be unbounded).

62

63

Returns:

64

--------

65

numpy.ndarray

66

Solution transformed to satisfy box constraints.

67

68

Examples:

69

---------

70

>>> bounds = [[-1, -1], [1, 1]]

71

>>> handler = cma.BoundTransform(bounds)

72

>>>

73

>>> # Large internal values are mapped to boundaries

74

>>> x_internal = [100, -100]

75

>>> x_feasible = handler.transform(x_internal)

76

>>> print(x_feasible) # Close to [1, -1]

77

>>>

78

>>> # Values near zero map to middle of domain

79

>>> x_internal = [0, 0]

80

>>> x_feasible = handler.transform(x_internal)

81

>>> print(x_feasible) # Close to [0, 0]

82

"""

83

pass

84

85

class BoundPenalty:

86

"""

87

Handle bounds using penalty method.

88

89

Penalizes solutions that violate constraints by adding penalty

90

terms to the objective function value.

91

"""

92

93

def __init__(self, bounds, **kwargs):

94

"""

95

Initialize penalty-based boundary handler.

96

97

Parameters:

98

-----------

99

bounds : list

100

Box constraints as [lower_bounds, upper_bounds].

101

**kwargs : dict

102

Additional options for penalty computation.

103

"""

104

pass

105

106

def repair(self, x, copy_if_changed=True):

107

"""

108

Repair solutions to satisfy bounds and compute penalties.

109

110

Parameters:

111

-----------

112

x : array-like

113

Solutions to repair.

114

copy_if_changed : bool

115

Whether to copy array if modifications needed.

116

117

Returns:

118

--------

119

tuple[array, float]

120

Repaired solutions and penalty value.

121

"""

122

pass

123

124

class BoundNone:

125

"""

126

No boundary handling - allows unbounded optimization.

127

128

Dummy boundary handler for unconstrained problems.

129

"""

130

131

def __init__(self, bounds=None):

132

"""Initialize no-bounds handler."""

133

pass

134

135

class BoundDomainTransform:

136

"""

137

Domain transformation for more complex boundary handling.

138

139

Advanced boundary handler with customizable transformation methods.

140

"""

141

142

def __init__(self, bounds, **kwargs):

143

"""

144

Initialize domain transformation handler.

145

146

Parameters:

147

-----------

148

bounds : list

149

Boundary constraints.

150

**kwargs : dict

151

Transformation options.

152

"""

153

pass

154

155

def inverse_transform(self, x):

156

"""

157

Transform from external (feasible) to internal coordinates.

158

159

Parameters:

160

-----------

161

x : array-like

162

Feasible solution satisfying box constraints.

163

164

Returns:

165

--------

166

numpy.ndarray

167

Solution in internal coordinates.

168

"""

169

pass

170

171

class BoundPenalty:

172

"""

173

Penalty-based boundary handler using quadratic penalties.

174

175

Adds penalty terms to the objective function for constraint violations.

176

Less smooth than BoundTransform but simpler conceptually.

177

"""

178

179

def __init__(self, bounds):

180

"""

181

Initialize penalty-based boundary handler.

182

183

Parameters:

184

-----------

185

bounds : list

186

Box constraints as [lower_bounds, upper_bounds].

187

188

Examples:

189

---------

190

>>> bounds = [[-5, -5], [5, 5]]

191

>>> handler = cma.BoundPenalty(bounds)

192

"""

193

pass

194

195

def __call__(self, f, x):

196

"""

197

Evaluate objective with penalty for bound violations.

198

199

Parameters:

200

-----------

201

f : float

202

Original objective function value.

203

204

x : array-like

205

Solution to check for bound violations.

206

207

Returns:

208

--------

209

float

210

Penalized objective value.

211

"""

212

pass

213

214

def repair(self, x):

215

"""

216

Repair solution to satisfy box constraints by projection.

217

218

Parameters:

219

-----------

220

x : array-like

221

Potentially infeasible solution.

222

223

Returns:

224

--------

225

numpy.ndarray

226

Solution projected onto feasible region.

227

"""

228

pass

229

230

class BoundNone:

231

"""

232

No-op boundary handler for unconstrained problems.

233

234

Provides same interface as other handlers but performs no transformations.

235

"""

236

237

def __init__(self, bounds=None):

238

"""Initialize no-op boundary handler."""

239

pass

240

241

def transform(self, x):

242

"""Return x unchanged."""

243

return x

244

245

def inverse_transform(self, x):

246

"""Return x unchanged."""

247

return x

248

249

class BoundDomainTransform:

250

"""

251

Function wrapper that automatically transforms domain using BoundTransform.

252

253

Wraps an objective function to handle box constraints transparently.

254

The function can be called with any input and constraints are handled

255

automatically via coordinate transformation.

256

"""

257

258

def __init__(self, function, boundaries):

259

"""

260

Create domain-transforming function wrapper.

261

262

Parameters:

263

-----------

264

function : callable

265

Original objective function to be wrapped.

266

267

boundaries : list

268

Box constraints as [lower_bounds, upper_bounds].

269

270

Examples:

271

---------

272

>>> import cma

273

>>>

274

>>> # Original unbounded function

275

>>> def sphere(x):

276

... return sum(xi**2 for xi in x)

277

>>>

278

>>> # Create bounded version

279

>>> bounds = [[-2, -2], [2, 2]]

280

>>> bounded_sphere = cma.BoundDomainTransform(sphere, bounds)

281

>>>

282

>>> # Can now call with any input - bounds handled automatically

283

>>> result = bounded_sphere([100, -100]) # Maps to feasible region

284

>>>

285

>>> # Use in optimization

286

>>> x, es = cma.fmin2(bounded_sphere, [0, 0], 1.0)

287

"""

288

pass

289

290

def __call__(self, x, *args, **kwargs):

291

"""

292

Evaluate wrapped function with automatic bound handling.

293

294

Parameters:

295

-----------

296

x : array-like

297

Input solution (will be transformed to feasible region).

298

299

*args, **kwargs

300

Additional arguments passed to wrapped function.

301

302

Returns:

303

--------

304

float

305

Function value evaluated at transformed (feasible) point.

306

"""

307

pass

308

```

309

310

### Box-Constrained Optimization Usage

311

312

```python { .api }

313

import cma

314

import numpy as np

315

316

# Pattern 1: Simple box constraints using options

317

def simple_box_constraints():

318

"""Basic box constraint optimization."""

319

320

def objective(x):

321

# Rosenbrock function

322

return sum(100*(x[1:] - x[:-1]**2)**2 + (1 - x[:-1])**2)

323

324

# Define box constraints

325

bounds = [[-2, -2, -2], [2, 2, 2]] # Each variable in [-2, 2]

326

327

# Optimize with bounds in options

328

x_best, es = cma.fmin2(

329

objective,

330

[0, 0, 0],

331

0.5,

332

options={'bounds': bounds}

333

)

334

335

print(f"Bounded optimization result: {x_best}")

336

print(f"All variables in bounds: {all(-2 <= xi <= 2 for xi in x_best)}")

337

338

return x_best, es

339

340

# Pattern 2: Mixed bounds (some variables unbounded)

341

def mixed_bounds_optimization():

342

"""Optimization with mixed bounded/unbounded variables."""

343

344

def objective(x):

345

return sum((x - np.array([1, 2, 3, 4]))**2)

346

347

# Mixed bounds: x[0] >= 0, x[1] unbounded, x[2] in [-1,1], x[3] <= 5

348

lower = [0, None, -1, None]

349

upper = [None, None, 1, 5]

350

bounds = [lower, upper]

351

352

x_best, es = cma.fmin2(

353

objective,

354

[0.5, 2, 0, 4],

355

0.3,

356

options={'bounds': bounds}

357

)

358

359

print(f"Mixed bounds result: {x_best}")

360

return x_best, es

361

362

# Pattern 3: Using BoundDomainTransform wrapper

363

def domain_transform_wrapper():

364

"""Using domain transformation wrapper."""

365

366

# Original function (designed for unbounded domain)

367

def unconstrained_function(x):

368

return sum(xi**2 for xi in x)

369

370

# Create bounded version

371

bounds = [[-5, -5], [5, 5]]

372

bounded_function = cma.BoundDomainTransform(unconstrained_function, bounds)

373

374

# Optimize bounded version (no bounds option needed)

375

x_best, es = cma.fmin2(bounded_function, [1, 1], 0.5)

376

377

print(f"Domain transform result: {x_best}")

378

return x_best, es

379

380

# Pattern 4: Manual boundary handler usage

381

def manual_boundary_handling():

382

"""Manual use of boundary handlers."""

383

384

def objective(x):

385

return sum(x**2)

386

387

bounds = [[-3, -3, -3], [3, 3, 3]]

388

389

# Create and configure boundary handler

390

bound_handler = cma.BoundTransform(bounds)

391

392

# Manual ask-and-tell with transformation

393

es = cma.CMAEvolutionStrategy([0, 0, 0], 0.5)

394

395

while not es.stop():

396

# Get solutions in internal coordinates

397

solutions = es.ask()

398

399

# Transform to feasible coordinates and evaluate

400

feasible_solutions = [bound_handler.transform(x) for x in solutions]

401

fitness_values = [objective(x) for x in feasible_solutions]

402

403

# Update with internal coordinates

404

es.tell(solutions, fitness_values)

405

406

if es.countiter % 50 == 0:

407

es.disp()

408

409

# Transform final result to feasible coordinates

410

final_solution = bound_handler.transform(es.result.xbest)

411

print(f"Manual handling result: {final_solution}")

412

413

return final_solution, es

414

415

# Pattern 5: Comparing boundary handlers

416

def compare_boundary_handlers():

417

"""Compare different boundary handling approaches."""

418

419

def objective(x):

420

# Function with optimum at boundary

421

return sum((x - 2)**2) # Optimum at [2, 2]

422

423

bounds = [[-1, -1], [1, 1]] # Optimum outside feasible region

424

425

results = {}

426

427

# BoundTransform (default)

428

x1, es1 = cma.fmin2(

429

objective, [0, 0], 0.3,

430

options={'bounds': bounds, 'BoundaryHandler': 'BoundTransform'}

431

)

432

results['BoundTransform'] = (x1, es1.result.fbest)

433

434

# BoundPenalty

435

x2, es2 = cma.fmin2(

436

objective, [0, 0], 0.3,

437

options={'bounds': bounds, 'BoundaryHandler': 'BoundPenalty'}

438

)

439

results['BoundPenalty'] = (x2, es2.result.fbest)

440

441

# Compare results

442

for method, (x, f) in results.items():

443

print(f"{method}: x = {x}, f = {f:.6f}")

444

445

return results

446

447

# Run examples

448

simple_box_constraints()

449

mixed_bounds_optimization()

450

domain_transform_wrapper()

451

manual_boundary_handling()

452

compare_boundary_handlers()

453

```

454

455

## General Nonlinear Constraints

456

457

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

458

459

### Constrained Optimization Functions

460

461

```python { .api }

462

def fmin_con2(

463

objective_function,

464

x0,

465

sigma0,

466

constraints=lambda x: [],

467

find_feasible_first=False,

468

find_feasible_final=False,

469

kwargs_confit=None,

470

**kwargs_fmin

471

):

472

"""

473

Optimize objective function with general inequality constraints.

474

475

This is the main interface for constrained optimization with CMA-ES

476

using the augmented Lagrangian method.

477

478

Parameters:

479

-----------

480

objective_function : callable

481

Function to minimize, called as objective_function(x).

482

483

x0 : array-like

484

Initial solution estimate.

485

486

sigma0 : float

487

Initial step size.

488

489

constraints : callable, optional

490

Constraint function returning list of constraint values.

491

Feasibility means all values <= 0. For equality constraint h(x) = 0,

492

use two inequalities: [h(x) - eps, -h(x) - eps] with eps >= 0.

493

494

find_feasible_first : bool, optional

495

Whether to find feasible solution before optimization (default False).

496

497

find_feasible_final : bool, optional

498

Whether to find feasible solution after optimization (default False).

499

500

kwargs_confit : dict, optional

501

Keyword arguments for ConstrainedFitnessAL instance.

502

503

**kwargs_fmin : dict

504

Additional arguments passed to fmin2.

505

506

Returns:

507

--------

508

tuple[numpy.ndarray, CMAEvolutionStrategy]

509

(xbest, es) where xbest is best feasible solution found and es

510

contains optimization details and constraint handler as

511

es.objective_function attribute.

512

513

Examples:

514

---------

515

>>> import cma

516

>>> import numpy as np

517

>>>

518

>>> # Minimize sphere subject to constraint x[0]^2 + x[1]^2 <= 1

519

>>> def objective(x):

520

... return sum(x**2)

521

>>>

522

>>> def constraints(x):

523

... return [x[0]**2 + x[1]**2 - 1] # <= 0 for feasibility

524

>>>

525

>>> x_best, es = cma.fmin_con2(

526

... objective,

527

... [0.5, 0.5],

528

... 0.3,

529

... constraints=constraints,

530

... options={'maxfevals': 2000, 'verb_disp': 100}

531

... )

532

>>>

533

>>> print(f"Best solution: {x_best}")

534

>>> print(f"Constraint value: {constraints(x_best)[0]:.6f}") # Should be <= 0

535

>>>

536

>>> # Multiple constraints

537

>>> def multiple_constraints(x):

538

... return [

539

... x[0]**2 + x[1]**2 - 1, # Circle constraint

540

... -x[0] - 0.5, # x[0] >= -0.5

541

... -x[1] - 0.5 # x[1] >= -0.5

542

... ]

543

>>>

544

>>> x_best, es = cma.fmin_con2(

545

... objective,

546

... [0, 0],

547

... 0.3,

548

... constraints=multiple_constraints

549

... )

550

>>>

551

>>> # Equality constraint h(x) = 0 as two inequalities

552

>>> def equality_as_inequalities(x):

553

... h_val = x[0] + x[1] - 1 # Want x[0] + x[1] = 1

554

... eps = 1e-6

555

... return [h_val - eps, -h_val - eps] # h-eps <= 0 and -h-eps <= 0

556

>>>

557

>>> x_best, es = cma.fmin_con2(

558

... objective,

559

... [0.5, 0.5],

560

... 0.2,

561

... constraints=equality_as_inequalities

562

... )

563

"""

564

pass

565

566

def fmin_con(

567

objective_function,

568

x0,

569

sigma0,

570

g=lambda x: [],

571

h=lambda x: [],

572

**kwargs

573

):

574

"""

575

DEPRECATED: Use fmin_con2 instead.

576

577

Legacy interface for constrained optimization. Provides separate

578

inequality (g) and equality (h) constraint functions.

579

580

Parameters:

581

-----------

582

objective_function : callable

583

Function to minimize.

584

585

x0 : array-like

586

Initial solution.

587

588

sigma0 : float

589

Initial step size.

590

591

g : callable, optional

592

Inequality constraints, g(x) <= 0 for feasibility.

593

594

h : callable, optional

595

Equality constraints, h(x) = 0 for feasibility.

596

597

**kwargs : dict

598

Additional arguments for fmin2.

599

600

Returns:

601

--------

602

tuple[numpy.ndarray, CMAEvolutionStrategy]

603

(xbest, es) with constraint information in es.best_feasible.

604

"""

605

pass

606

```

607

608

### Constraint Handler Classes

609

610

```python { .api }

611

class ConstrainedFitnessAL:

612

"""

613

Augmented Lagrangian fitness function for constrained optimization.

614

615

Transforms constrained optimization problem into sequence of unconstrained

616

problems using augmented Lagrangian method. Can be used directly for

617

fine-grained control over constraint handling.

618

"""

619

620

def __init__(

621

self,

622

objective_function,

623

constraints_function,

624

find_feasible_first=False,

625

logging=True

626

):

627

"""

628

Initialize augmented Lagrangian constraint handler.

629

630

Parameters:

631

-----------

632

objective_function : callable

633

Original objective function to minimize.

634

635

constraints_function : callable

636

Function returning list of constraint values (g(x) <= 0).

637

638

find_feasible_first : bool, optional

639

Whether to find feasible point before optimization.

640

641

logging : bool, optional

642

Whether to enable constraint violation logging.

643

644

Examples:

645

---------

646

>>> import cma

647

>>>

648

>>> def objective(x):

649

... return sum(x**2)

650

>>>

651

>>> def constraints(x):

652

... return [x[0] + x[1] - 1] # x[0] + x[1] <= 1

653

>>>

654

>>> # Create augmented Lagrangian fitness

655

>>> al_fitness = cma.ConstrainedFitnessAL(objective, constraints)

656

>>>

657

>>> # Use with CMA-ES

658

>>> es = cma.CMAEvolutionStrategy([0, 0], 0.3)

659

>>>

660

>>> while not es.stop():

661

... solutions = es.ask()

662

... fitness_values = [al_fitness(x) for x in solutions]

663

... es.tell(solutions, fitness_values)

664

... al_fitness.update(es) # Update Lagrange multipliers

665

>>>

666

>>> # Best feasible solution

667

>>> x_best = al_fitness.best_feasible.x

668

>>> print(f"Best feasible: {x_best}")

669

"""

670

pass

671

672

def __call__(self, x):

673

"""

674

Evaluate augmented Lagrangian function.

675

676

Parameters:

677

-----------

678

x : array-like

679

Solution to evaluate.

680

681

Returns:

682

--------

683

float

684

Augmented Lagrangian value (objective + constraint penalties).

685

"""

686

pass

687

688

def update(self, es):

689

"""

690

Update Lagrange multipliers and penalty parameters.

691

692

Should be called after each CMA-ES iteration (tell operation).

693

694

Parameters:

695

-----------

696

es : CMAEvolutionStrategy

697

Evolution strategy instance to get population information.

698

"""

699

pass

700

701

@property

702

def best_feasible(self):

703

"""

704

Best feasible solution found so far.

705

706

Returns:

707

--------

708

object

709

Object with attributes x (solution), f (objective value),

710

g (constraint values), and info dictionary.

711

"""

712

pass

713

714

class AugmentedLagrangian:

715

"""

716

Lower-level augmented Lagrangian implementation.

717

718

Provides direct access to augmented Lagrangian computations for

719

advanced users who need fine control over the constraint handling process.

720

"""

721

722

def __init__(self, dimension):

723

"""

724

Initialize augmented Lagrangian handler.

725

726

Parameters:

727

-----------

728

dimension : int

729

Problem dimension.

730

"""

731

pass

732

733

def __call__(self, x, f_x, g_x):

734

"""

735

Compute augmented Lagrangian value.

736

737

Parameters:

738

-----------

739

x : array-like

740

Solution vector.

741

742

f_x : float

743

Objective function value at x.

744

745

g_x : array-like

746

Constraint values at x.

747

748

Returns:

749

--------

750

float

751

Augmented Lagrangian value.

752

"""

753

pass

754

755

def update(self, X, F, G):

756

"""

757

Update Lagrange multipliers and penalty parameters.

758

759

Parameters:

760

-----------

761

X : list[array]

762

Population of solutions.

763

764

F : list[float]

765

Objective function values.

766

767

G : list[list]

768

Constraint values for each solution.

769

"""

770

pass

771

```

772

773

### Constrained Optimization Usage Patterns

774

775

```python { .api }

776

import cma

777

import numpy as np

778

779

# Pattern 1: Simple inequality constraint

780

def simple_inequality_constraint():

781

"""Optimization with single inequality constraint."""

782

783

def objective(x):

784

# Minimize distance to point [2, 2]

785

return sum((x - np.array([2, 2]))**2)

786

787

def constraints(x):

788

# Must stay within unit circle: x[0]^2 + x[1]^2 <= 1

789

return [x[0]**2 + x[1]**2 - 1]

790

791

x_best, es = cma.fmin_con2(

792

objective,

793

[0.5, 0.5], # Start inside feasible region

794

0.2,

795

constraints=constraints,

796

options={'maxfevals': 3000, 'verb_disp': 100}

797

)

798

799

print(f"Simple constraint result: {x_best}")

800

print(f"Constraint violation: {max(0, constraints(x_best)[0]):.6f}")

801

802

return x_best, es

803

804

# Pattern 2: Multiple inequality constraints

805

def multiple_inequality_constraints():

806

"""Optimization with multiple inequality constraints."""

807

808

def objective(x):

809

# Quadratic function with minimum at [1, 1, 1]

810

return sum((x - 1)**2)

811

812

def constraints(x):

813

return [

814

x[0] + x[1] + x[2] - 2, # x[0] + x[1] + x[2] <= 2

815

-x[0], # x[0] >= 0

816

-x[1], # x[1] >= 0

817

-x[2], # x[2] >= 0

818

x[0]**2 + x[1]**2 - 1 # x[0]^2 + x[1]^2 <= 1

819

]

820

821

x_best, es = cma.fmin_con2(

822

objective,

823

[0.3, 0.3, 0.3],

824

0.2,

825

constraints=constraints,

826

options={'maxfevals': 5000}

827

)

828

829

print(f"Multiple constraints result: {x_best}")

830

constraint_vals = constraints(x_best)

831

print(f"All constraints satisfied: {all(g <= 1e-6 for g in constraint_vals)}")

832

833

return x_best, es

834

835

# Pattern 3: Equality constraints via inequalities

836

def equality_constraint_example():

837

"""Handle equality constraints as pairs of inequalities."""

838

839

def objective(x):

840

return x[0]**2 + x[1]**2

841

842

def constraints_with_equality(x):

843

# Equality: x[0] + x[1] = 1 (as two inequalities)

844

h1 = x[0] + x[1] - 1

845

tolerance = 1e-6

846

847

return [

848

h1 - tolerance, # x[0] + x[1] <= 1 + tol

849

-h1 - tolerance, # x[0] + x[1] >= 1 - tol

850

-x[0] + 0.1, # x[0] >= -0.1 (inequality)

851

-x[1] + 0.1 # x[1] >= -0.1 (inequality)

852

]

853

854

x_best, es = cma.fmin_con2(

855

objective,

856

[0.5, 0.5],

857

0.2,

858

constraints=constraints_with_equality,

859

options={'maxfevals': 2000}

860

)

861

862

print(f"Equality constraint result: {x_best}")

863

print(f"Equality satisfied: {abs(x_best[0] + x_best[1] - 1):.6f}")

864

865

return x_best, es

866

867

# Pattern 4: Direct use of ConstrainedFitnessAL

868

def direct_constraint_handler_use():

869

"""Direct use of ConstrainedFitnessAL for advanced control."""

870

871

def objective(x):

872

return sum(x**4) # Quartic function

873

874

def constraints(x):

875

return [

876

x[0]**2 + x[1]**2 - 4, # Stay in circle of radius 2

877

x[0] * x[1] - 0.5 # Product constraint

878

]

879

880

# Create constraint handler directly

881

constraint_handler = cma.ConstrainedFitnessAL(

882

objective,

883

constraints,

884

find_feasible_first=True # Find feasible start

885

)

886

887

# Manual optimization loop

888

es = cma.CMAEvolutionStrategy([1, 1], 0.5)

889

890

iteration = 0

891

while not es.stop() and iteration < 200:

892

solutions = es.ask()

893

894

# Evaluate augmented Lagrangian fitness

895

fitness_values = [constraint_handler(x) for x in solutions]

896

897

es.tell(solutions, fitness_values)

898

899

# Update constraint handler

900

constraint_handler.update(es)

901

902

if iteration % 50 == 0:

903

best_feasible = constraint_handler.best_feasible

904

if best_feasible.x is not None:

905

print(f"Iter {iteration}: best feasible f = {best_feasible.f:.6f}")

906

else:

907

print(f"Iter {iteration}: no feasible solution yet")

908

909

iteration += 1

910

911

# Extract best feasible solution

912

best_feasible = constraint_handler.best_feasible

913

if best_feasible.x is not None:

914

print(f"Final feasible solution: {best_feasible.x}")

915

print(f"Final objective value: {best_feasible.f}")

916

print(f"Final constraint values: {best_feasible.g}")

917

else:

918

print("No feasible solution found")

919

920

return best_feasible, es

921

922

# Pattern 5: Feasibility search

923

def feasibility_search_example():

924

"""Find feasible solutions for difficult constraint sets."""

925

926

def objective(x):

927

# Minimize sum of squares (not the main goal)

928

return sum(x**2)

929

930

def difficult_constraints(x):

931

return [

932

x[0]**2 + x[1]**2 - 0.25, # Inside small circle

933

-(x[0] - 1)**2 - (x[1] - 1)**2 + 0.5, # Outside another circle

934

x[0] + x[1] - 1.2, # Above line

935

-x[0] - x[1] + 0.8 # Below line

936

]

937

938

# First find any feasible solution

939

x_feasible, es = cma.fmin_con2(

940

lambda x: 0, # Dummy objective - just find feasible point

941

[0, 0],

942

0.5,

943

constraints=difficult_constraints,

944

find_feasible_first=True,

945

options={'maxfevals': 5000, 'verb_disp': 200}

946

)

947

948

constraint_vals = difficult_constraints(x_feasible)

949

is_feasible = all(g <= 1e-6 for g in constraint_vals)

950

951

print(f"Found feasible solution: {is_feasible}")

952

if is_feasible:

953

print(f"Feasible point: {x_feasible}")

954

955

# Now optimize from feasible starting point

956

x_optimal, es2 = cma.fmin_con2(

957

objective, # Real objective

958

x_feasible, # Start from feasible point

959

0.1, # Smaller step size

960

constraints=difficult_constraints,

961

options={'maxfevals': 3000}

962

)

963

964

print(f"Optimized feasible solution: {x_optimal}")

965

print(f"Final objective: {objective(x_optimal):.6f}")

966

967

return x_feasible, es

968

969

# Run constraint examples

970

simple_inequality_constraint()

971

multiple_inequality_constraints()

972

equality_constraint_example()

973

direct_constraint_handler_use()

974

feasibility_search_example()

975

```

976

977

## Integration with Box Constraints

978

979

Combining general constraints with box constraints for comprehensive constraint handling.

980

981

```python { .api }

982

import cma

983

import numpy as np

984

985

def combined_constraints_example():

986

"""Example combining box constraints with general constraints."""

987

988

def objective(x):

989

# Rosenbrock function

990

return sum(100*(x[1:] - x[:-1]**2)**2 + (1 - x[:-1])**2)

991

992

def general_constraints(x):

993

# Nonlinear constraint

994

return [x[0]**2 + x[1]**2 - 2] # Stay in circle

995

996

# Box constraints: each variable in [-3, 3]

997

box_bounds = [[-3, -3], [3, 3]]

998

999

x_best, es = cma.fmin_con2(

1000

objective,

1001

[0.5, 0.5],

1002

0.3,

1003

constraints=general_constraints,

1004

options={

1005

'bounds': box_bounds, # Box constraints via options

1006

'maxfevals': 5000,

1007

'verb_disp': 100

1008

}

1009

)

1010

1011

print(f"Combined constraints result: {x_best}")

1012

print(f"In box bounds: {all(-3 <= xi <= 3 for xi in x_best)}")

1013

print(f"Satisfies circle: {general_constraints(x_best)[0] <= 0}")

1014

1015

return x_best, es

1016

1017

def integer_constrained_optimization():

1018

"""Example with integer variables and constraints."""

1019

1020

def objective(x):

1021

# Integer variables x[0], x[1] should be close to [3, 5]

1022

return (x[0] - 3)**2 + (x[1] - 5)**2 + sum(x[2:]**2)

1023

1024

def constraints(x):

1025

# Sum of integer variables must be <= 7

1026

return [x[0] + x[1] - 7]

1027

1028

x_best, es = cma.fmin_con2(

1029

objective,

1030

[2, 4, 0, 0],

1031

0.5,

1032

constraints=constraints,

1033

options={

1034

'integer_variables': [0, 1], # x[0], x[1] are integers

1035

'bounds': [[0, 0, -5, -5], [10, 10, 5, 5]], # Box bounds

1036

'maxfevals': 3000

1037

}

1038

)

1039

1040

print(f"Integer constrained result: {x_best}")

1041

print(f"Integer values: {[int(round(x_best[i])) for i in [0, 1]]}")

1042

print(f"Sum constraint: {x_best[0] + x_best[1]} <= 7")

1043

1044

return x_best, es

1045

1046

combined_constraints_example()

1047

integer_constrained_optimization()

1048

```

1049

1050

## Advanced Constraint Handling

1051

1052

Techniques for difficult constraint scenarios and performance optimization.

1053

1054

```python { .api }

1055

import cma

1056

import numpy as np

1057

1058

def constraint_violation_monitoring():

1059

"""Monitor and analyze constraint violations during optimization."""

1060

1061

class ConstraintMonitor:

1062

def __init__(self):

1063

self.violations = []

1064

self.iterations = []

1065

1066

def __call__(self, es):

1067

# Custom callback to monitor constraint violations

1068

if hasattr(es, 'objective_function'):

1069

cf = es.objective_function

1070

if hasattr(cf, 'best_feasible') and cf.best_feasible.x is not None:

1071

max_violation = max(0, max(cf.best_feasible.g))

1072

else:

1073

max_violation = float('inf')

1074

1075

self.violations.append(max_violation)

1076

self.iterations.append(es.countiter)

1077

1078

def objective(x):

1079

return sum((x - np.array([1, 2]))**2)

1080

1081

def constraints(x):

1082

return [

1083

x[0]**2 + x[1]**2 - 4, # Circle constraint

1084

-x[0] - x[1] + 2.5 # Linear constraint

1085

]

1086

1087

monitor = ConstraintMonitor()

1088

1089

x_best, es = cma.fmin_con2(

1090

objective,

1091

[0, 0],

1092

0.5,

1093

constraints=constraints,

1094

callback=monitor, # Add monitoring callback

1095

options={'maxfevals': 2000, 'verb_disp': 100}

1096

)

1097

1098

print(f"Monitored optimization result: {x_best}")

1099

print(f"Final max violation: {monitor.violations[-1]:.6f}")

1100

print(f"Violation decreased monotonically: {all(monitor.violations[i] >= monitor.violations[i+1] for i in range(len(monitor.violations)-1))}")

1101

1102

return x_best, monitor

1103

1104

def adaptive_constraint_parameters():

1105

"""Example with adaptive constraint handling parameters."""

1106

1107

def objective(x):

1108

return sum(x**2)

1109

1110

def constraints(x):

1111

return [x[0] + x[1] - 1, x[0]**2 + x[1]**2 - 2]

1112

1113

# Custom constraint handler with adaptive parameters

1114

constraint_handler = cma.ConstrainedFitnessAL(

1115

objective,

1116

constraints,

1117

logging=True

1118

)

1119

1120

# Manual optimization with parameter adaptation

1121

es = cma.CMAEvolutionStrategy([0.5, 0.5], 0.3)

1122

1123

while not es.stop():

1124

solutions = es.ask()

1125

fitness_values = [constraint_handler(x) for x in solutions]

1126

es.tell(solutions, fitness_values)

1127

constraint_handler.update(es)

1128

1129

# Adaptive parameter adjustment

1130

if es.countiter % 50 == 0:

1131

# Access internal augmented Lagrangian

1132

al = constraint_handler.al

1133

1134

# Example: adapt penalty parameters based on progress

1135

if hasattr(al, 'gamma') and es.countiter > 100:

1136

# Increase penalty if not making progress

1137

best = constraint_handler.best_feasible

1138

if best.x is not None and max(best.g) > 1e-3:

1139

al.gamma *= 1.1 # Increase penalty parameter

1140

1141

best_solution = constraint_handler.best_feasible

1142

print(f"Adaptive parameters result: {best_solution.x}")

1143

1144

return best_solution, es

1145

1146

def constraint_approximation_example():

1147

"""Handle expensive constraints using approximation."""

1148

1149

def objective(x):

1150

return sum((x - 1)**2)

1151

1152

# Simulate expensive constraint evaluation

1153

constraint_call_count = [0]

1154

1155

def expensive_constraints(x):

1156

constraint_call_count[0] += 1

1157

# Simulate computational cost

1158

import time

1159

time.sleep(0.001) # Simulate 1ms computation

1160

1161

return [

1162

x[0]**2 + x[1]**2 - 1, # Circle constraint

1163

x[0] * x[1] + 0.5 # Product constraint

1164

]

1165

1166

# Cache constraint evaluations

1167

constraint_cache = {}

1168

1169

def cached_constraints(x):

1170

x_key = tuple(np.round(x, 6)) # Round for cache key

1171

if x_key in constraint_cache:

1172

return constraint_cache[x_key]

1173

1174

result = expensive_constraints(x)

1175

constraint_cache[x_key] = result

1176

return result

1177

1178

x_best, es = cma.fmin_con2(

1179

objective,

1180

[0.5, 0.5],

1181

0.2,

1182

constraints=cached_constraints,

1183

options={'maxfevals': 1000, 'verb_disp': 50}

1184

)

1185

1186

print(f"Cached constraints result: {x_best}")

1187

print(f"Constraint evaluations: {constraint_call_count[0]}")

1188

print(f"Cache entries: {len(constraint_cache)}")

1189

1190

return x_best, es

1191

1192

# Run advanced constraint examples

1193

constraint_violation_monitoring()

1194

adaptive_constraint_parameters()

1195

constraint_approximation_example()

1196

```