Permalink
Browse files

Merge pull request #14 from michaelhush/fixbad

Fixed halting conditions and bad flags
  • Loading branch information...
2 parents dfb5cd3 + cfa5748 commit 8e7cff73bebd330ce82a41346282d2ce59cb5eac @michaelhush committed on GitHub Nov 4, 2016
Showing with 158 additions and 56 deletions.
  1. +37 −35 mloop/controllers.py
  2. +0 −1 mloop/launchers.py
  3. +17 −20 mloop/learners.py
  4. +24 −0 mloop/utilities.py
  5. +80 −0 tests/test_units.py
View
@@ -282,7 +282,7 @@ def _get_cost_and_in_dict(self):
except ValueError:
self.log.error('One of the values you provided in the cost dict could not be converted into the right type.')
raise
- if self.curr_bad and 'cost' in dict:
+ if self.curr_bad and ('cost' in in_dict):
self.log.warning('The cost provided with the bad run will be saved, but not used by the learners.')
self.in_costs.append(self.curr_cost)
@@ -334,6 +334,8 @@ def optimize(self):
self._start_up()
self._optimization_routine()
log.info('Controller finished. Closing down M-LOOP. Please wait a moment...')
+ except ControllerInterrupt:
+ self.log.warning('Controller ended by interruption.')
except (KeyboardInterrupt,SystemExit):
log.warning('!!! Do not give the interrupt signal again !!! \n M-LOOP stopped with keyboard interupt or system exit. Please wait at least 1 minute for the threads to safely shut down. \n ')
log.warning('Closing down controller.')
@@ -392,22 +394,19 @@ def _optimization_routine(self):
Runs controller main loop. Gives parameters to experiment and saves costs returned.
'''
self.log.debug('Start controller loop.')
- try:
+ self.log.info('Run:' + str(self.num_in_costs +1))
+ next_params = self._first_params()
+ self._put_params_and_out_dict(next_params)
+ self.save_archive()
+ self._get_cost_and_in_dict()
+ while self.check_end_conditions():
self.log.info('Run:' + str(self.num_in_costs +1))
- next_params = self._first_params()
+ next_params = self._next_params()
self._put_params_and_out_dict(next_params)
self.save_archive()
self._get_cost_and_in_dict()
- while self.check_end_conditions():
- self.log.info('Run:' + str(self.num_in_costs +1))
- next_params = self._next_params()
- self._put_params_and_out_dict(next_params)
- self.save_archive()
- self._get_cost_and_in_dict()
- self.log.debug('End controller loop.')
- except ControllerInterrupt:
- self.log.warning('Controller ended by interruption.')
-
+ self.log.debug('End controller loop.')
+
def _first_params(self):
'''
Checks queue to get first parameters.
@@ -619,7 +618,7 @@ def __init__(self, interface,
self.new_params_event = self.gp_learner.new_params_event
self.remaining_kwargs = self.gp_learner.remaining_kwargs
self.generation_num = self.gp_learner.generation_num
-
+
def _put_params_and_out_dict(self, params):
'''
Override _put_params_and_out_dict function, used when the training learner creates parameters. Makes the defualt param_type the training type and sets last_training_run_flag.
@@ -677,26 +676,34 @@ def _optimization_routine(self):
Overrides _optimization_routine. Uses the parent routine for the training runs. Implements a customized _optimization_rountine when running the Gaussian Process learner.
'''
#Run the training runs using the standard optimization routine. Adjust the number of max_runs
- save_max_num_runs = self.max_num_runs
- self.max_num_runs = self.num_training_runs - 1
self.log.debug('Starting training optimization.')
- super(GaussianProcessController,self)._optimization_routine()
-
- #Start last training run
self.log.info('Run:' + str(self.num_in_costs +1))
- next_params = self._next_params()
+ next_params = self._first_params()
self._put_params_and_out_dict(next_params)
-
- #Begin GP optimization routine
- self.max_num_runs = save_max_num_runs
-
- self.log.debug('Starting GP optimization.')
- self.new_params_event.set()
self.save_archive()
self._get_cost_and_in_dict()
+ while (self.num_in_costs < self.num_training_runs) and self.check_end_conditions():
+ self.log.info('Run:' + str(self.num_in_costs +1))
+ next_params = self._next_params()
+ self._put_params_and_out_dict(next_params)
+ self.save_archive()
+ self._get_cost_and_in_dict()
+
+ if self.check_end_conditions():
+ #Start last training run
+ self.log.info('Run:' + str(self.num_in_costs +1))
+ next_params = self._next_params()
+ self._put_params_and_out_dict(next_params)
+
+ self.log.debug('Starting GP optimization.')
+ self.new_params_event.set()
+ self.save_archive()
+ self._get_cost_and_in_dict()
+ self.log.debug('End training runs.')
+
+ gp_consec = 0
+ gp_count = 0
- gp_consec = 0
- gp_count = 0
while self.check_end_conditions():
self.log.info('Run:' + str(self.num_in_costs +1))
if gp_consec==self.generation_num or (self.no_delay and self.gp_learner_params_queue.empty()):
@@ -723,12 +730,7 @@ def _shut_down(self):
self.log.debug('GP learner end set.')
self.end_gp_learner.set()
self.gp_learner.join()
- #self.gp_learner.join(self.gp_learner.learner_wait*3)
- '''
- if self.gp_learner.is_alive():
- self.log.warning('GP Learner did not join in time had to terminate.')
- self.gp_learner.terminate()
- '''
+
self.log.debug('GP learner joined')
last_dict = None
while not self.gp_learner_params_queue.empty():
@@ -750,7 +752,7 @@ def _shut_down(self):
self.archive_dict.update(last_dict)
else:
if self.gp_learner.predict_global_minima_at_end or self.gp_learner.predict_local_minima_at_end:
- self.log.warning('GP Learner may not have closed properly unable to get best and/or all minima.')
+ self.log.info('GP Learner did not provide best and/or all minima.')
super(GaussianProcessController,self)._shut_down()
def print_results(self):
View
@@ -27,7 +27,6 @@ def launch_from_file(config_filename,
except (IOError, OSError):
print('Unable to open M-LOOP configuration file:' + repr(config_filename))
raise
-
file_kwargs.update(kwargs)
#Main run sequence
#Create interface and extract unused keywords
View
@@ -927,10 +927,7 @@ def __init__(self,
self.length_scale = np.squeeze(np.array(self.training_dict['length_scale']))
self.length_scale_history = list(self.training_dict['length_scale_history'])
self.noise_level = float(self.training_dict['noise_level'])
- if isinstance(self.training_dict['noise_level_history'], np.ndarray):
- self.noise_level_history = list(np.squeeze(self.training_dict['noise_level_history']))
- else:
- self.noise_level_history = list( self.training_dict['noise_level_history'])
+ self.noise_level_history = mlu.safe_cast_to_list(self.training_dict['noise_level_history'])
#Counters
self.costs_count = int(self.training_dict['costs_count'])
@@ -942,11 +939,7 @@ def __init__(self,
self.all_costs = np.squeeze(np.array(self.training_dict['all_costs'], dtype=float))
self.all_uncers = np.squeeze(np.array(self.training_dict['all_uncers'], dtype=float))
- if isinstance(self.training_dict['bad_run_indexs'], np.ndarray):
- self.bad_run_indexs = list(np.squeeze(self.training_dict['bad_run_indexs']))
- else:
- self.bad_run_indexs = list(self.training_dict['bad_run_indexs'])
-
+ self.bad_run_indexs = mlu.safe_cast_to_list(self.training_dict['bad_run_indexs'])
#Derived properties
self.best_cost = float(self.training_dict['best_cost'])
@@ -1073,9 +1066,9 @@ def __init__(self,
if self.default_bad_uncertainty < 0:
self.log.error('Default bad uncertainty must be positive.')
raise ValueError
- if (self.default_bad_cost is None) and (self.default_bad_cost is None):
+ if (self.default_bad_cost is None) and (self.default_bad_uncertainty is None):
self.bad_defaults_set = False
- elif (self.default_bad_cost is not None) and (self.default_bad_cost is not None):
+ elif (self.default_bad_cost is not None) and (self.default_bad_uncertainty is not None):
self.bad_defaults_set = True
else:
self.log.error('Both the default cost and uncertainty must be set for a bad run or they must both be set to None.')
@@ -1156,13 +1149,14 @@ def get_params_and_costs(self):
new_costs = []
new_uncers = []
new_bads = []
- new_costs_count = 0
update_bads_flag = False
while not self.costs_in_queue.empty():
(param, cost, uncer, bad) = self.costs_in_queue.get_nowait()
+ self.costs_count +=1
+
if bad:
- new_bads.append(self.data_count)
+ new_bads.append(self.costs_count-1)
if self.bad_defaults_set:
cost = self.default_bad_cost
uncer = self.default_bad_uncertainty
@@ -1181,18 +1175,15 @@ def get_params_and_costs(self):
self.log.error('Provided uncertainty must be larger or equal to zero:' + repr(uncer))
uncer = max(float(uncer), self.minimum_uncertainty)
- new_costs_count += 1
- self.costs_count +=1
-
cost_change_flag = False
if cost > self.worst_cost:
self.worst_cost = cost
- self.worst_index = self.costs_count
+ self.worst_index = self.costs_count-1
cost_change_flag = True
if cost < self.best_cost:
self.best_cost = cost
self.best_params = param
- self.best_index = self.costs_count
+ self.best_index = self.costs_count-1
cost_change_flag = True
if cost_change_flag:
self.cost_range = self.worst_cost - self.best_cost
@@ -1202,7 +1193,8 @@ def get_params_and_costs(self):
new_params.append(param)
new_costs.append(cost)
new_uncers.append(uncer)
-
+
+
if self.all_params.size==0:
self.all_params = np.array(new_params, dtype=float)
self.all_costs = np.array(new_costs, dtype=float)
@@ -1212,13 +1204,15 @@ def get_params_and_costs(self):
self.all_costs = np.concatenate((self.all_costs, np.array(new_costs, dtype=float)))
self.all_uncers = np.concatenate((self.all_uncers, np.array(new_uncers, dtype=float)))
+ self.bad_run_indexs.append(new_bads)
+
if self.all_params.shape != (self.costs_count,self.num_params):
self.log('Saved GP params are the wrong size. THIS SHOULD NOT HAPPEN:' + repr(self.all_params))
if self.all_costs.shape != (self.costs_count,):
self.log('Saved GP costs are the wrong size. THIS SHOULD NOT HAPPEN:' + repr(self.all_costs))
if self.all_uncers.shape != (self.costs_count,):
self.log('Saved GP uncertainties are the wrong size. THIS SHOULD NOT HAPPEN:' + repr(self.all_uncers))
-
+
if update_bads_flag:
self.update_bads()
@@ -1366,6 +1360,9 @@ def run(self):
raise LearnerInterrupt()
except LearnerInterrupt:
pass
+ if self.predict_global_minima_at_end or self.predict_local_minima_at_end:
+ self.get_params_and_costs()
+ self.fit_gaussian_process()
end_dict = {}
if self.predict_global_minima_at_end:
self.find_global_minima()
View
@@ -173,6 +173,30 @@ def check_file_type_supported(file_type):
'''
return file_type == 'mat' or 'txt' or 'pkl'
+def safe_cast_to_list(in_array):
+ '''
+ Attempts to safely cast a numpy array to a list, if not a numpy array just casts to list on the object.
+
+ Args:
+ in_array (array or equivalent): The array (or otherwise) to be converted to a list.
+
+ Returns:
+ list : List of elements from in_array
+
+ '''
+
+ if isinstance(in_array, np.ndarray):
+ t_array = np.squeeze(in_array)
+ if t_array.shape == ():
+ out_list = [t_array[()]]
+ else:
+ out_list = list(t_array)
+ else:
+ out_list = list(in_array)
+
+ return out_list
+
+
class NullQueueListener():
'''
Shell class with start and stop functions that do nothing. Queue listener is not implemented in python 2. Current fix is to simply use the multiprocessing class to pipe straight to the cmd line if running on python 2. This is class is just a placeholder.
View
@@ -0,0 +1,80 @@
+'''
+Unit test for all of the example scripts provided in the examples folder.
+'''
+from __future__ import absolute_import, division, print_function
+
+import os
+import unittest
+import math
+import mloop.interfaces as mli
+import mloop.controllers as mlc
+import numpy as np
+import multiprocessing as mp
+
+class CostListInterface(mli.Interface):
+ def __init__(self, cost_list):
+ super(CostListInterface,self).__init__()
+ self.call_count = 0
+ self.cost_list = cost_list
+ def get_next_cost_dict(self,params_dict):
+ if np.isfinite(self.cost_list[self.call_count]):
+ cost_dict = {'cost': self.cost_list[self.call_count]}
+ else:
+ cost_dict = {'bad': True}
+ self.call_count += 1
+ return cost_dict
+
+class TestUnits(unittest.TestCase):
+
+ def test_max_num_runs(self):
+ cost_list = [5.,4.,3.,2.,1.]
+ interface = CostListInterface(cost_list)
+ controller = mlc.create_controller(interface,
+ max_num_runs = 5,
+ target_cost = -1,
+ max_num_runs_without_better_params = 2)
+ controller.optimize()
+ self.assertTrue(controller.best_cost == 1.)
+ self.assertTrue(np.array_equiv(np.array(controller.in_costs),
+ np.array(cost_list)))
+
+
+ def test_max_num_runs_without_better_params(self):
+ cost_list = [1.,2.,3.,4.,5.]
+ interface = CostListInterface(cost_list)
+ controller = mlc.create_controller(interface,
+ max_num_runs = 10,
+ target_cost = -1,
+ max_num_runs_without_better_params = 4)
+ controller.optimize()
+ self.assertTrue(controller.best_cost == 1.)
+ self.assertTrue(np.array_equiv(np.array(controller.in_costs),
+ np.array(cost_list)))
+
+ def test_target_cost(self):
+ cost_list = [1.,2.,-1.]
+ interface = CostListInterface(cost_list)
+ controller = mlc.create_controller(interface,
+ max_num_runs = 10,
+ target_cost = -1,
+ max_num_runs_without_better_params = 4)
+ controller.optimize()
+ self.assertTrue(controller.best_cost == -1.)
+ self.assertTrue(np.array_equiv(np.array(controller.in_costs),
+ np.array(cost_list)))
+
+ def test_bad(self):
+ cost_list = [1., float('nan'),2.,float('nan'),-1.]
+ interface = CostListInterface(cost_list)
+ controller = mlc.create_controller(interface,
+ max_num_runs = 10,
+ target_cost = -1,
+ max_num_runs_without_better_params = 4)
+ controller.optimize()
+ self.assertTrue(controller.best_cost == -1.)
+ for x,y in zip(controller.in_costs,cost_list):
+ self.assertTrue(x==y or (math.isnan(x) and math.isnan(y)))
+
+if __name__ == "__main__":
+ mp.freeze_support()
+ unittest.main()

0 comments on commit 8e7cff7

Please sign in to comment.