Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real Variable Type - Precision addition #399

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

DataGasmic
Copy link

For certain cases, the Real value data type might not need a default numpy precision for sampling, if we could set precision for every variable, that would prevent unnecessary exploration in the search space

Added precision for the Real variable type sample generation
@DataGasmic DataGasmic changed the title Datagasmic real value precision addition Real Variable Type - Precision addition Mar 20, 2023
@blankjul
Copy link
Collaborator

First of all, thank you for your contribution!

The main problem I see here is that this only is used in the sampling. However, the mutation and crossover would still allow in any type of precision. An elegant way to restrict the search is implementing a Repair.

e.g.

from pymoo.core.repair import Repair
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.problems import get_problem
from pymoo.optimize import minimize
from pymoo.visualization.scatter import Scatter

import numpy as np

problem = get_problem("zdt1")


class RoundingRepair(Repair):

    def _do(self, problem, X, **kwargs):
        return np.round(X, decimals=1)


algorithm = NSGA2(pop_size=100, repair=RoundingRepair())

res = minimize(problem,
               algorithm,
               ('n_gen', 200),
               seed=1,
               verbose=True)

plot = Scatter()
plot.add(problem.pareto_front(), plot_type="line", color="black", alpha=0.7)
plot.add(res.F, facecolor="none", edgecolor="red")
plot.show()

For mixed variables, this would look a little different though (you would have to iterate over each variable and round it).

Or is your use case strictly restricted to having a specific precision during sampling?

@DataGasmic
Copy link
Author

DataGasmic commented Mar 21, 2023

Hi @blankjul ,

First of all, I appreciate your response and thank you for building pymoo. I tried the approach after modifying the code for my use case, using the following custom "Repair" class -

class RoundingRepair(Repair):

       def _do(self, problem, X, **kwargs):
              m = pd.DataFrame(X).round(rounding_off_by_features).to_dict('records')
              vals = []
              for i in m:
                  vals.append(list(i.values())[0]
              return vals

Please Note : I had to use the dataframe because I have both categorical and numerical variables in my dataframe and the rounding off would be done for just the numerical columns of choice ("rounding_off_by_features"). the For loop post that is to convert to datatype expected by the objective and constraint functions.

Although, the res.X does not abide by the same laws, is there a way I can restrict mutation and crossover to also abide by the laws of precision I'm setting for each element in the decision space ?

After the first round of optimisation, where I tried to debug the code, the mutation and crossover eventually move the variable precisions back to default. Please let me know if there is a solution for this.

@blankjul
Copy link
Collaborator

blankjul commented Apr 3, 2023

Do you have a solution without pandas? So far pymoo does not have pandas as a dependency and I would not like to add it for one line of code at this point.

@DataGasmic
Copy link
Author

DataGasmic commented Apr 3, 2023

@blankjul I completely understand your concern. I tried this solution, although it involves for loops (which I ideally don't like to add to numpy calculations), but for my case, this worked perfectly. Please let me know your thoughts on this. I can help and would love to test this comprehensively if needed and get it added to the package.

class MyRoundingRepair(Repair):
     def _do(self, problem, X, **kwargs):
        for sample in X:
            for item in sample.keys():
                if type(sample[item]) not in [np.str_, np.bool_]:
                    sample[item] = round(sample[item], rounding_off_by_features[item])

Thanks.

@blankjul
Copy link
Collaborator

blankjul commented Apr 5, 2023

Did you test the code above on a purely vectorized problem? For example, NSGA-II on ZDT1?
Without testing it I think it would break running on other problems.

Originally, I was not planning to add mixed variables because it breaks whenever a numpy array is expected. I think this is a very good example where operators don't really work for all variable types at the same time.

@blankjul blankjul self-assigned this Apr 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants