esa / pygmo2

A Python platform to perform parallel computations of optimisation tasks (global and local) via the asynchronous generalized island model.
https://esa.github.io/pygmo2/
Mozilla Public License 2.0
414 stars 56 forks source link

[BUG] Batch fitness does not work with pygmo.unconstrain() #155

Open julioasotodv opened 4 months ago

julioasotodv commented 4 months ago

Describe the bug

Hi!

Thank you for your work on this library. It works very well and is blazing fast 😊

It seems like the combination of:

  1. Trying to perform batch fitness evaluation on a problem through the method batch_fitness
  2. With an algorithm capable of using batch fitness (the ones with method .set_bfe() defined)
  3. In a constrained problem turned into unconstrained through [pygmo.unconstrain](https://esa.github.io/pygmo2/problems.html#pygmo.unconstrain)

Does not work and throws an exception that comes from the C++ code.

To Reproduce

Here's a simple reproducible example:

import numpy as np
import pygmo as pg

# Define problem:
class MyProblem:

    def fitness(self, x):

        feval_res = x[0] - x[1]**2

        # Inequality constraints: x[0] + x[1] <=10
        inequality_constraints = x[0] + x[1] - 10

        # Return:
        return np.array([feval_res, inequality_constraints])

    def batch_fitness(self, x):

        x_matrix = x.reshape(-1, 2)

        feval_res = x_matrix[:,0] - x_matrix[:,1]**2

        inequality_constraints = x_matrix[:,0] + x_matrix[:,1] - 10

        return np.hstack([feval_res.reshape(-1,1), 
                          inequality_constraints.reshape(-1,1)
                         ]).reshape(-1)

    def has_batch_fitness(self):
        return True

    def get_nobj(self):
        return 1

    def get_nec(self):
        return 0

    def get_nic(self):
        return 1

    def get_bounds(self):
        return np.array([[0, 2], 
                         [8, 9]])

problem = MyProblem()

# Define algo which supports
# batch_fitness:
alg = pg.cmaes(gen=50,
               ftol=-1.0,
               xtol=-1.0,
               sigma0=0.1,
               force_bounds=True,
               seed=42)
algo = pg.algorithm(alg)

# Turn constrained problem into
# unconstrained one:
unconstrained_problem = pg.unconstrain(problem, method="death penalty")

# Create population
pop = pg.population(unconstrained_problem, size=500, seed=42)

# Evolve it. Non-batch works
# fine:
pop = algo.evolve(pop)

# Now batch:
batch_evaluator = pg.bfe()
alg.set_bfe(batch_evaluator)

algo = pg.algorithm(alg)

# Re-create population:
pop = pg.population(unconstrained_problem, size=500, seed=42)

# Evolve it. But batch fitness
# fails:
pop = algo.evolve(pop)

Running the code above fails with:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[23], line 12
      8 pop = pg.population(unconstrained_problem, size=500, seed=42)
     10 # Evolve it. But batch fitness
     11 # fails:
---> 12 pop = algo.evolve(pop)

ValueError: 
function: feasibility_f
where: D:\bld\pagmo_1705775542311\work\src\problem.cpp, 714
what: The fitness passed as argument has dimension of: 1, while the problem defines a fitness size of: 2

It seems that for some reason the fitness passed to feasibility_f is incorrect.

Expected behavior No exception should be thrown,

Environment (please complete the following information):

Thank you!