Project-Platypus / Platypus

A Free and Open Source Python Library for Multiobjective Optimization
GNU General Public License v3.0
553 stars 152 forks source link

My inputs and outputs are not lining up when using Integer decision variables #200

Closed this-josh closed 1 year ago

this-josh commented 1 year ago

Essentially Platypus will say my best input is [False, True,False] which is 2 and gives the objective 5. But when I do objective(2) I get either infeasible or a different objective. Below is a minimal reproducible example. Which has a list of random values r and the following problem: (Please excuse the awful formatting and pseudo maths, Github doesn't seem to like maths)

$$ Min. r_{x1} * r{x_2} $$

$$ S.T. x_1 \ne x_2 $$

$$ x_1 \mod 2 ==1
$$

$$ x_2 \mod 2 ==1 $$

import platypus as plt
import numpy as np

n = 8
r = np.arange(n) +1

_res = []
def obj(x):
    x = [x[0], x[1]]
    result = r[x[0]] * r[x[1]]
    t_stat = (x[0]%2) * (x[1])%2 * (x[0]!=x[1])
    _res.append(dict(x= x, result=result, t_stat=t_stat))
    return [result], [int(t_stat)]

n_iter=100
_res = []

prob = plt.Problem(2,1, 1)
prob.types[:] = [plt.Integer(1,n-1),plt.Integer(1,n-1)]
prob.function = obj
prob.constraints[0] = plt.Constraint("==", 1.0)
algo = plt.NSGAII(prob)
algo.run(n_iter)

all_res = [r for r in algo.result]
valid_res = [r for r in algo.result if r.feasible]

Now compare the platypus valid_res with the ones we tracked ourselves in _res. Look at valid_res and chose an objective value, here I choose 16. Then consider which inputs gives the result 16 in _res, in my experience there is no overlap

>>> [r for r in _res if r["result"] == 16]
[{'x': [3, 3], 'result': 16, 't_stat': 0}, {'x': [7, 1], 'result': 16, 't_stat': 1}, {'x': [1, 7], 'result': 16, 't_stat': 1}, {'x': [7, 1], 'result': 16, 't_stat': 1}, {'x': [1, 7], 'result': 16, 't_stat': 1}]
>>> [r for r in valid_res if r.objectives[0] == 16]
[Solution[[True, False, True],[False, False, False]|16|0.0], Solution[[False, False, False],[True, False, True]|16|0.0], Solution[[True, False, True],[False, False, False]|16|0.0], Solution[[False, False, False],[True, False, True]|16|0.0]]

So here _res is saying [3,3] (not this isn't feasible) and [7,1] give the output. While valid_res gives [[True, False, True],[False, False, False]] which is equal to [5,0], but note that our Integer space starts at 1 (plt.Integer(1,n-1) so I think 0 represents 1, as such the platypus result is [6,1]

Note This isn't the actual problem I'm trying to solve, in my actual problem Platypus says the best objective is achieved with inputs [0,56] and my _res says [1,48] gets these results. I have also tried this with plt.Integer(0,n-1) and found the same issue.

So my question is, why are the platypus recorded inputs not aligning with their outputs?

this-josh commented 1 year ago

From reading through the test files, it seems that also.result[0].variables[0] is not a binary number, it is Gray code. Might I suggest this is made more clear as the word 'gray' doesn't appear at all in the docs and most people who see [True, False, True] will view it as binary. In heinseit I even studied gray code at university 😃