Skip to content

[BUG] solutions violating the bounds are declared feasible #171

@thisandthatuser

Description

@thisandthatuser

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions