paulknysh/blackbox

TypeError: unsupported operand type(s) for /: 'NoneType' and 'float'

Closed this issue · 7 comments

Thank you for providing this very useful toolbox!

When I try to use it for my optimization problem, it starts out quite well but it raises an error after having evaluated around half of the batches:

File "/Users/philipp/miniconda3/lib/python3.6/site-packages/black_box/blackbox.py", line 106, in search_min
  points[n+batch*i:n+batch*(i+1), -1] = list(e.map(f, list(map(cubetobox, points[n+batch*i:n+batch*(i+1), 0:-1]))))/fmax
TypeError: unsupported operand type(s) for /: 'NoneType' and 'float' 

It seems, that the map somehow returns a NoneType. I couldn't find the reason for it. Do you have any suggestions?

Unfortunately, I can not provide a MWE, since my cost-function is a very advanced black-box.
The function is called with:

fit_par = bb.search_min(f=cost_function, domain=bounds_list, budget=50, batch=4, resfile = 'bb_output.csv')

Tried this on a Linux and MacOs Machine.
I am working in a anaconda environment with
Python 3.7.9
black-box 1.0.2

Upon further investigation I discorvered, that there is a nan appearing at the last position of all the sublists in the points list. Could this be the problem and how could this have happened?

For completeness, my domain is

[[1000000000.0, 30000000000.0],
 [1000000000.0, 30000000000.0],
 [2e+18, 5e+18],
 [-1e-07, 3e-07],
 [10000.0, 100000.0],
 [-5000.0, 5000.0],
 [10000.0, 100000.0],
 [-5000.0, 5000.0]]

It looks like your objective function returns None or np.nan for some inputs, so then in line 106 it tries to divide None by float, and errors.

Can you ensure your objective function always returns a number (something like 2 x (average expected error) to penalize this behavior), when result is None or np.nan?

def fun(par):
    ...
    if output == None or output == np.nan:
        return 2.  # or whatever number makes sense
    else:
        return output

I ensured, as you said, that my ojective junction can not return None or np.nan. (Only changed == by is for the conditions in this case). However the problem still persists. The output of the points list is

points [[0.42159932 0.34934531 0.28275606 0.22138745 0.16483018 0.11270704
  0.06467039 0.02039985        nan]
 [0.34319864 0.19869061 0.06551211 0.9427749  0.82966036 0.72541409
  0.62934079 0.5407997         nan]
 [0.26479796 0.04803592 0.84826817 0.66416235 0.49449055 0.33812113
  0.19401118 0.06119955        nan]
 [0.18639728 0.89738122 0.63102422 0.38554979 0.15932073 0.95082817
  0.75868158 0.5815994         nan]
 [0.1079966  0.74672653 0.41378028 0.10693724 0.82415091 0.56353522
  0.32335197 0.10199926        nan]
 [0.02959592 0.59607184 0.19653634 0.82832469 0.48898109 0.17624226
  0.88802237 0.62239911        nan]
 [0.95119524 0.44541714 0.97929239 0.54971214 0.15381127 0.7889493
  0.45269276 0.14279896        nan]
 [0.87279456 0.29476245 0.76204845 0.27109959 0.81864146 0.40165635
  0.01736315 0.66319881        nan]
 [0.79439388 0.14410775 0.5448045  0.99248704 0.48347164 0.01436339
  0.58203355 0.18359866        nan]
 [0.7159932  0.99345306 0.32756056 0.71387449 0.14830182 0.62707043
  0.14670394 0.70399851        nan]
 [0.63759252 0.84279837 0.11031662 0.43526194 0.813132   0.23977748
  0.71137434 0.22439836        nan]
 [0.55919184 0.69214367 0.89307267 0.15664938 0.47796218 0.85248452
  0.27604473 0.74479821        nan]
 [0.9480136  0.33366756 0.68078511 0.74954686 0.32619811 0.07428944
  0.46780869 0.69765786 0.        ]
 [0.02448316 0.08405715 0.36927055 0.44635047 0.52201819 0.85325304
  0.28174998 0.28101705 0.        ]
 [0.05727996 0.96678861 0.91978072 0.79837666 0.14672598 0.47075986
  0.09539721 0.1492017  0.        ]
 [0.57640019 0.48054213 0.41132201 0.4275182  0.3374541  0.31468188
  0.96287894 0.48349808 0.        ]]

I assume the problem, somehow has to do with how points is constructed with

points[batch*i:batch*(i+1), -1] = list(e.map(f, list(map(cubetobox, points[batch*i:batch*(i+1), 0:-1]))))

at line 73 in blackbox.py

Are you able to run example in README file (some simple function)?

Here is one more thing to try:

Can you add the following function to blackbox.py:

def get_default_executor():
    Pool = mp.Pool
    return Pool

And also modify definition of search_min function to be:

def search_min(f, domain, budget, batch, resfile,
               rho0=0.5, p=1.0,
               executor=get_default_executor()):

And try running your procedure like that (of course you would need to import blackbox from source directory then).

It's one change that I've made recently to executor logic. Doesn't seem like this would cause any problems, but maybe I'm missing something..

Also, what is your objective function doing under the hood? If you could briefly describe, or provide some code, that might be useful. I'm still wondering if something is causing nan's inside of it due to some side effects. One issue could be that objective function itself is using parallelism, and that might confuse mp.Pool()

Thank you for your suggestions.
I am able to run the simple function in the README.
Somehow, I was also able to have two successful test runs with my cost function now. The strange thing is, that I didn't really change anything relevant I am aware of. I just executed the file at another time.
Also, the objective function never enters the if-statement that checks if the output isnan or NoneType.
My objective function is a fit of a quantum physical system. Therefore it solves something like the Schrödinger Equation (which is a differential equation) and integrates it's solutions. There is no parallelisation inside the objective function which I am aware of.
My first guess would be, that it had something to do with the multiprocessing. But the strange thing is, that I could observe the same behaviour on different machines (my local MacBook) and our University's Linux Cluster.
I am now running a more serious test on the cluster to see if it works.

That is weird. I would assume since it sometimes works and sometimes doesn't (and also works well on some trivial function), there is indeed some side effect inside your objective function that causes it to behave unstable.

Let me know if you find something.

I made some more extensive calculations and it is running very stable now. Unfortunately, I still don't know why it didn't work at the beginning, as I didn't change anything relevant which I am aware of.
I am very impressed by the performance of your toolbox. I also tried some more conventional minimizing-algorithm which are implemented in scipy, however I really appreciated your toolbox as it gives good results and it is very easy to parallelise.
It was especially helpful for my case, as I am dealing with an objective function that is very expensive to calculate.