pygmo2 icon indicating copy to clipboard operation
pygmo2 copied to clipboard

[BUG] solutions violating the bounds are declared feasible

Open thisandthatuser opened this issue 1 year ago • 1 comments

Describe the bug The feasibility_x method returns True for solutions that clearly violate the declared bounds. On the other hand, if I explicitly add constraints for the bounds (within the fitness method) the feasibility_x method works as expected. I do not know if this is an intended behaviour but it seems to be contradictory to me.

To Reproduce

I adapted the code from one of the tutorials to reproduce this behaviour. In the example that follows, bounds are declared using the get_bounds method and no errors are generated for solutions that violate the bounds.

import pygmo as pg

class sphere_function:

    def __init__(self, dim):
        self.dim = dim

    def fitness(self, x):
        return [sum(x*x)]

    def get_bounds(self):
        return ([-1] * self.dim, [1] * self.dim)

    def get_name(self):
        return "Sphere Function"

    def get_extra_info(self):
        return "\tDimensions: " + str(self.dim)

prob_dim = 3
pop_size = 10
prob = pg.problem(sphere_function(prob_dim))
algo = pg.algorithm(pg.bee_colony(gen = 20, limit = 20))
pop = pg.population(prob, pop_size)
pop = algo.evolve(pop)

# create solution not violating the bounds
new_solution = [1 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution)
# create another solution not violating the bounds
new_solution2 = [-1 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution2)

# create solution clearly violating the bounds
new_solution = [2 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution)
# create another solution clearly violating the bounds
new_solution2 = [-2 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution2)

# add the solutions to the population
pop.push_back(new_solution)
pop.push_back(new_solution2)
# obtain solutions from the population
x_pool = pop.get_x()
# they are considered feasible
assert prob.feasibility_x(x_pool[pop_size])
assert prob.feasibility_x(x_pool[pop_size+1])

If I add constraints to the fitness method, the feasibility_x returns the expected result (solutions that violate the bounds are infeasible).

import pygmo as pg

class sphere_function2:

    def __init__(self, dim):
        self.dim = dim
    
    def get_bounds(self):
        return ([-1] * self.dim, [1] * self.dim)

    def get_name(self):
        return "Sphere Function"

    def get_extra_info(self):
        return "\tDimensions: " + str(self.dim)

    # inequality constraints
    def get_nic(self):
        return 2*self.dim
    
    # fitness with constraints for the bounds
    def fitness(self, x):
        # 
        obj = sum(x*x)
                        
        # upper bound
        ineq_ub = [x[i]-1 for i in range(self.dim)]
        
        # lowe bound
        ineq_lb = [-x[i]-1 for i in range(self.dim)]
                
        return [obj, *ineq_ub, *ineq_lb]

prob_dim = 3
pop_size = 10
prob = pg.problem(sphere_function2(prob_dim))
algo = pg.algorithm(pg.ihs(gen=20))
pop = pg.population(prob, pop_size)
pop = algo.evolve(pop)

# create solution not violating the bounds
new_solution = [1 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution)
# create another solution not violating the bounds
new_solution2 = [-1 for i in range(prob_dim)]
# it is considered feasible
assert prob.feasibility_x(new_solution2)

# create solution clearly violating the bounds
new_solution = [2 for i in range(prob_dim)]
# it is not considered feasible
assert not prob.feasibility_x(new_solution)
# create another solution clearly violating the bounds
new_solution2 = [-2 for i in range(prob_dim)]
# it is not considered feasible
assert not prob.feasibility_x(new_solution2)

# add the solutions to the population
pop.push_back(new_solution)
pop.push_back(new_solution2)
# obtain solutions from the population
x_pool = pop.get_x()
# they are not considered feasible
assert not prob.feasibility_x(x_pool[pop_size])
assert not prob.feasibility_x(x_pool[pop_size+1])

Expected behavior

I expected the feasibility_x method to return False for solutions that clearly violate the bounds. It does not seem to do at the moment, only when constraints are added explicitly.

Screenshots If applicable, add screenshots to help explain your problem.

Environment (please complete the following information):

  • OS: Ubuntu 22.04
  • Installation method: compiled from source
  • Version: 2.19.6

Additional context

The problem formulated in the paper does not include the bounds in the constraints section, so maybe this behaviour is intended.

thisandthatuser avatar Aug 27 '24 13:08 thisandthatuser

Hi @thisandthatuser !

I was looking at the code for the feasibility_x() method, and it does not seem to me like the notion of box bounds is considered at all when computing the feasibility of a decision vector. I think the method was never intended to check for box bounds, but probably we should ask @darioizzo as I think he wrote that specific piece of code.

bluescarni avatar Aug 29 '24 11:08 bluescarni