NoneType error while finding minima with Scipy Optimize

Viewed 153

I am trying to find the global minima of a function using scipy.optimizer methods and keep running into NoneType issues. I have tried multiple algorithms including differential_evolution, shgo, and brute but keep running into errors.

Here is the setup:

def sizing_trade_study(ranges, payload):
    with open("config.yml", "r") as yml:
        cfg = yaml.load(yml)
    first_int = True
    km = []
    for range in ranges:

        km.append( range * 1000)
    print(km)
    params = (km, payload)

    if first_int:
        x0 = [float(cfg['design_variables']['initial_guess']['prop_radius']),
              float(cfg['design_variables']['initial_guess']['speed']),
              float(cfg['design_variables']['initial_guess']['battery_mass']),
              float(cfg['design_variables']['initial_guess']['motor_mass']),
              float(cfg['design_variables']['initial_guess']['mtow'])]

    lb = [float(cfg['design_variables']['lower_bound']['prop_radius']),
          float(cfg['design_variables']['lower_bound']['speed']),
          float(cfg['design_variables']['lower_bound']['battery_mass']),
          float(cfg['design_variables']['lower_bound']['motor_mass']),
          float(cfg['design_variables']['lower_bound']['mtow'])] # Min cruise at 1.3 * VStall

    ub = [float(cfg['design_variables']['upper_bound']['prop_radius']),
          float(cfg['design_variables']['upper_bound']['speed']),
          float(cfg['design_variables']['upper_bound']['battery_mass']),
          float(cfg['design_variables']['upper_bound']['motor_mass']),
          float(cfg['design_variables']['upper_bound']['mtow'])]

    # bounds = (slice(lb[0], ub[0]), slice(lb[1], ub[1]), slice(lb[2], ub[2]), slice(lb[3], ub[3]), slice(lb[4], ub[4]))
    # bounds = [(lb[0], ub[0]), (lb[1], ub[1]), (lb[2], ub[2]), (lb[3], ub[3]), (lb[4], ub[4])]

    bounds = optimize.Bounds(lb,ub)
    result = optimize.differential_evolution(objective_function, bounds, args=(params,))

    print(result)

def objective_function(x, *params):
    global trials
    trials = trials+1
    print(trials)
    performance.compute_performance(x, params[0][0], params[0][1])

Here is the function I am trying to optimize:

import yaml
import simple_mission
import reserve_mission
import config_weight

def compute_performance(x, range, payload):
    rprop = x[0]
    speed = x[1]
    battery = x[2]
    motors = x[3]
    mtow = x[4]

    w = mtow * 9.8
    with open("config.yml", "r") as yml:
        cfg = yaml.load(yml)
    bat_energy_density = int(cfg['performance']['bat_energy_density'])
    motor_power_density = int(cfg['performance']['motor_power_density'])
    discharge_depth = float(cfg['performance']['discharge_depth'])

    e_nominal, flight_time, hover_output, cruise_output = simple_mission.run_simple_mission(rprop, speed, w, range)

    reserve_e = reserve_mission.reserve_mission(rprop,speed, w, range)

    mass = config_weight.config_weight(battery,motors, rprop, w, mtow, hover_output, cruise_output, payload)

    batt = reserve_e - battery * bat_energy_density * discharge_depth / 1000
    motor = hover_output.pow_hover / 1000 - motors * motor_power_density
    weight = mass - w

    return batt+ motor+ weight

The failure doesn't happen immediately but after a couple of runs of the optimizer function. For example, with differential_evolution, it always happens after the 75th trial.

Here is the stacktrace:

69
70
71
72
73
74
75
76
Traceback (most recent call last):
  File "sizing_trade_study.py", line 62, in <module>
    sizing_trade_study(args.ranges, args.payload)
  File "sizing_trade_study.py", line 42, in sizing_trade_study
    result = optimize.differential_evolution(objective_function, bounds, args=(params,))
  File "/usr/local/anaconda3/envs/simple_mission/lib/python3.7/site-packages/scipy/optimize/_differentialevolution.py", line 308, in differential_evolution
    ret = solver.solve()
  File "/usr/local/anaconda3/envs/simple_mission/lib/python3.7/site-packages/scipy/optimize/_differentialevolution.py", line 759, in solve
    next(self)
  File "/usr/local/anaconda3/envs/simple_mission/lib/python3.7/site-packages/scipy/optimize/_differentialevolution.py", line 1082, in __next__
    self.constraint_violation[candidate]):
  File "/usr/local/anaconda3/envs/simple_mission/lib/python3.7/site-packages/scipy/optimize/_differentialevolution.py", line 1008, in _accept_trial
    return energy_trial <= energy_orig
TypeError: '>=' not supported between instances of 'float' and 'NoneType'

Any help is greatly appreciated!

1 Answers

The issue is with one of the retrieved values from your bounds or objective_function, which in turn is being passed in as a NoneType to energy_orig within differential_evolution()

Source: https://github.com/scipy/scipy/blob/master/scipy/optimize/_differentialevolution.py

if feasible_orig and feasible_trial:
    return energy_trial <= energy_orig

You should make sure that each key value is not empty from your config.yml or other function parameters. It's hard to tell which could be the problem. However, you could wrap it around a try/catch to get this to not stop on the 75th try for the meantime.

try:
    result = optimize.differential_evolution(
      objective_function,
      bounds,
      args=(params,),
    )
except TypeError:
  import pdb; pdb.set_trace()

I've set pdb, which will allow to to debug the values of each parameter, feel free to swap it out with a pass if you need to continue swiftly

Related