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

[BUG] integer_idx not working (trial vectors are still float) #286

Open
samimia-swks opened this issue Jan 8, 2025 · 3 comments
Open

[BUG] integer_idx not working (trial vectors are still float) #286

samimia-swks opened this issue Jan 8, 2025 · 3 comments

Comments

@samimia-swks
Copy link

pycma 4.0.0
python 3.12 on ubuntu

I have a optimization problem involving 14 integer variables. I am calling fmin() in the manner below, where integer_idx is set to 0, ..., 13.
However, when I inspect the vector (of population members) passed to obj_func, they are not integers but floats. Do I need to do something other than setting integer_idx?

        opts = cma.CMAOptions()
        opts.set("integer_variables", integer_idx.tolist())
        opts.set("bounds", [lb.tolist(), up.tolist()])
        opts.set("ftarget", 0.0)
        opts.set("CMA_stds", std.tolist())

        print(f"{x0 = }")
        print(f"{integer_idx.tolist() = }")
        print(f"{lb.tolist() = }")
        print(f"{up.tolist() = }")
        print(f"{std.tolist() = }")

        res = cma.fmin(
            objective_function=obj_func,
            parallel_objective=obj_func,
            x0=x0,
            sigma0=1.0,
            options=opts,
            eval_initial_x=True,
            args=(opt_conf, opt_state),
            # restarts=1,
            # restart_from_best='True',
        )

Output:

x0 = array([  -9,    0,   25,   23,  -30,  -70,   -2,  125,  105, -138, -316,
         -3,  808, 1531])
integer_idx.tolist() = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
lb.tolist() = [-15, -6, 21, 20, -36, -79, -16, 121, 98, -149, -326, -25, 802, 1520]
up.tolist() = [-6, 4, 29, 33, -23, -67, 6, 130, 123, -124, -312, 10, 814, 1551]
std.tolist() = [2.25, 2.5, 2.0, 3.25, 3.25, 3.0, 5.5, 2.25, 6.25, 6.25, 3.5, 8.75, 3.0, 7.75]
(21_w,43)-aCMA-ES (mu_w=12.1,w_1=15%) in dimension 14 (seed=370228, Tue Jan  7 20:18:18 2025)
Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]
    1     44 6.938313512132006e-01 1.0e+00 8.75e-01  2e+00  7e+00 0:00.0
    2     87 1.541264009940674e-01 1.2e+00 8.38e-01  2e+00  7e+00 0:00.1
    3    130 2.978738982081726e-01 1.3e+00 7.79e-01  2e+00  6e+00 0:00.1
@nikohansen
Copy link
Contributor

nikohansen commented Jan 8, 2025

This is intended behavior. The option only signals to the code that the objective function does not "use decimal places". If the objective function requires integer values, it would be simple to (additionally) use a wrapper in the spirit of

import numpy as np

class FunWithIntegers:
    def __init__(self, fun, integer_indices):
        self.fun = fun
        self.integer_indices = integer_indices
    def __call__(self, x):
        x = np.array(x)
        for i in self.integer_indices:
            x[i] = int(x[i] + 0.5)
        return self.fun(x)

@samimia-swks
Copy link
Author

samimia-swks commented Jan 8, 2025

Thanks for the quick answer. So just to confirm, for rmy integer only problem, I can do np.round(x) at the top of my existing obj_func(x) and call it a day? int(x[i] + 0.5) in your code implies rounding to nearest integer but you also used the phrase "does not use decimal places" which implies rounding toward zero.

This seems like a rather odd choice to me. if rounding to nearest integer is done internally, why not pass the those vectors to the objective function as well?

In any case it would be nice to explicitly mention this here:
API docs cma.integer_centering.IntegerCentering

@nikohansen
Copy link
Contributor

nikohansen commented Jan 13, 2025

Thanks for the quick answer. So just to confirm, for rmy integer only problem, I can do np.round(x) at the top of my existing obj_func(x) and call it a day?

Yes and no, the variables which are rounded/integer must still be signalled to cma.fmin via the integer_variables option.

"does not use decimal places" which implies rounding toward zero

that was indeed incorrectly worded, sorry, integer centering indeed assumes rounding, this had slipped my mind.

This seems like a rather odd choice to me. if rounding to nearest integer is done internally, why not pass the those vectors to the objective function as well?

Integer variables may be rounded sometimes but are not always rounded. The tell method is not supposed to see only rounded values, hence the above line

x = np.array(x)

is important in that it copies x.

In any case it would be nice to explicitly mention this here:

The first sentence reads "round values of int-variables that are different from the int-mean", which seems to clearly suggest that not all integer variables are rounded. It then reads "This assumes that int-variables change the fitness only if their rounded (genotype) value change", which suggests that the fitness function needs to take care of rounding if necessary. I'll try to make it more clear.

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

No branches or pull requests

2 participants