def __init__(self, frame): """Constructor. Parameters: frame: the FrameOptimizer instance.""" self.frame = frame self.params = OptimizationParameters() self.params.optimization_running = False self.best = None self.best_coverage = 0 self.average_coverage = 0 self.best_chromosome = [] self.currentGeneration = 0 self.run_thread = None self.start_time = 0 self.last_population = None self.generations = [] self.last_plot_time = time.time()-10 #Frequency of plotting self.plot_time_interval = 1
class OptimizerController(): """Controller for the coverage optimizer.""" #-------------------------------------------------------------------- def __init__(self, frame): """Constructor. Parameters: frame: the FrameOptimizer instance.""" self.frame = frame self.params = OptimizationParameters() self.params.optimization_running = False self.best = None self.best_coverage = 0 self.average_coverage = 0 self.best_chromosome = [] self.currentGeneration = 0 self.run_thread = None self.start_time = 0 self.last_population = None self.generations = [] self.last_plot_time = time.time()-10 #Frequency of plotting self.plot_time_interval = 1 #-------------------------------------------------------------------- def restore_buttons(self): """ Restore the button states to the initial value """ self.frame.buttonStart.Enable(True) self.frame.buttonKeepGoing.Enable(False) self.frame.buttonApply.Enable(False) self.frame.buttonStop.Enable(False) #-------------------------------------------------------------------- def update(self): """Update GUI elements to reflect the current status.""" frm = self.frame #@type frm FrameOptimizer if frm is None: return if self.params.avoid_edges and not self.params.use_volume: edges = " (excluding edges)" else: edges = "" #Coverage stats frm.staticTextCoverage.SetLabel("Best Coverage%s: %7.2f %%" % (edges, self.best_coverage*100)) frm.gaugeCoverage.SetValue(self.best_coverage*100) frm.staticTextAverage.SetLabel("Average Coverage%s: %7.2f %%" % (edges, self.average_coverage*100)) frm.gaugeAverage.SetValue(self.average_coverage*100) #The generation counter maxgen = self.params.max_generations frm.gaugeGeneration.SetRange(maxgen) frm.gaugeGeneration.SetValue(self.currentGeneration) frm.staticTextGeneration.SetLabel("Generation %5d of %5d:" % (self.currentGeneration, maxgen)) #The individual if not self.best is None: frm.textStatus.SetValue("Best individual has %7.3f coverage:\n%s" % (self.best.coverage, str(self.best.genomeList))) frm.buttonApply.Enable(True) else: frm.textStatus.SetValue("No best individual") frm.buttonApply.Enable(False) #The start/stop buttons enabling frm.buttonStart.Enable((self.run_thread is None)) frm.buttonStop.Enable(not (self.run_thread is None)) #The keep going button frm.buttonKeepGoing.Enable( not (self.last_population is None) and (self.run_thread is None) ) #-------------------------------------------------------------------- def start(self, event, *args): """Start the optimization.""" self._want_abort = False self.start_time = time.time() self.init_data() #Start the thread self.params.use_old_population = False self.run_thread = OptimizationThread(self) #Set the buttons right away. frm = self.frame #@type frm FrameOptimizer frm.buttonStart.Enable((self.run_thread is None)) frm.buttonStop.Enable(not (self.run_thread is None)) self.frame.staticTextComplete.SetLabel("Optimization started...") if not event is None: event.Skip() #-------------------------------------------------------------------- def keep_going(self, event): """Continue optimization, using the last saved population.""" if self.last_population is None: wx.MessageDialog(self.frame, "Error! No saved population. You need to start the optimization at least once.").ShowModal() return # if s: # wx.MessageDialog("Number of sample orientations has changed. Cannot keep going with the old population.").ShowModal() # return; if (self.params.population != len(self.last_population)) or (self.last_population[0].listSize != self.params.number_of_orientations): wx.MessageDialog(self.frame, "Population size/number of orientations changed. The new population will be selected randomly from the old one, and may not be as good.", style=wx.OK).ShowModal() self._want_abort = False self.start_time = time.time() self.init_data() #Start the thread self.params.use_old_population = True self.params.add_trait("old_population", self.last_population) self.run_thread = OptimizationThread(self) #Set the buttons right away. frm = self.frame #@type frm FrameOptimizer frm.buttonStart.Enable((self.run_thread is None)) frm.buttonStop.Enable(not (self.run_thread is None)) self.frame.staticTextComplete.SetLabel("Optimization started...") if not event is None: event.Skip() #-------------------------------------------------------------------- def stop(self, event, *args): """Stop the optimization.""" self._want_abort = True #Will have to wait for the next generation to stop if not event is None: event.Skip() #-------------------------------------------------------------------- def close_form(self, event, *args): """Call when the form is closing. Aborth the thread if it is running.""" self._want_abort = True #Marker to avoid trying to change GUI self.frame = None #For the singleton global _instance _instance = None if not event is None: event.Skip() #-------------------------------------------------------------------- def init_data(self): """Initialize and clear the GA data log.""" self.generations = [] #-------------------------------------------------------------------- def add_data(self, ga): """Add one entry to the GA data log.""" stats = ga.getStatistics() self.generations.append( GAData(ga.currentGeneration, stats["rawMax"], stats["rawAve"], stats["rawMin"]) ) #-------------------------------------------------------------------- def plot_data(self): """Plot whatever the data currently is""" self.frame.plotControl.draw(self.generations) self.last_plot_time = time.time() #-------------------------------------------------------------------- def complete(self, ga, aborted, converged): """Called when the optimization completes. Parameters: ga: the GSimpleGA instance aborted: True if the optimization was aborted manually converged: True if the criterion was reached. """ if aborted: label = "ABORTED - Optimization was aborted before completing!" elif converged: label = "SUCCESS - Optimization met the coverage criterion!" else: label = "FAILED - Reached the max. # of generations without enough coverage!" self.run_thread = None self.add_data(ga) #Make sure GUI updates wx.CallAfter(self.frame.staticTextComplete.SetLabel, label) wx.CallAfter(self.plot_data) wx.CallAfter(self.update) #Save the population self.last_population = ga.getPopulation() if self.params.auto_increment and not aborted and not converged: print "AUTO INCREMENTING !!!" #Try again with 1 more orientation self.params.number_of_orientations += 1 wx.CallAfter(self.keep_going, None) else: #Done! print "Optimization finished in %.3f seconds." % (time.time() - self.start_time) #-------------------------------------------------------------------- def step_callback(self, ga, *args): """Callback during evolution; used to abort it and to display stats.""" #@type ga GSimpleGA op = self.params #@type op OptimizationParameters #Find the best individual self.best = ga.bestIndividual() self.best_coverage = self.best.coverage #More stats stats = ga.getStatistics() self.average_coverage = stats["rawAve"] #Other stats self.currentGeneration = ga.currentGeneration #Log the stats too self.add_data(ga) #Adjust settings while going on model.optimization.set_changeable_parameters(op, ga) #Update gui if time.time()-self.last_plot_time > self.plot_time_interval: #Enough time has passed, plot the graph wx.CallAfter(self.plot_data) wx.CallAfter(self.update) return self._want_abort #-------------------------------------------------------------------- def apply(self, event, *args): """Apply the best results.""" #TODO: Confirmation message box? positions = [] # And add the fixed ones, if any if self.params.fixed_orientations: positions += self.params.fixed_orientations_list # Get the angles of the best one positions += model.optimization.get_angles(self.best) print "Applying best individual", self.best #This deletes everything in the list in the instrument del model.instrument.inst.positions[:] #Make sure to clear the parameters too, by giving it an empty dict() object. display_thread.clear_positions_selected() #This function does the calc. and shows a progress bar. Can be aborted too. gui_utils.do_calculation_with_progress_bar(positions) #GUI update model.messages.send_message(model.messages.MSG_POSITION_LIST_CHANGED) #Add it to the list of selected items if len(model.instrument.inst.angles) == 1: model.instrument.inst.sort_positions_by(0) display_thread.select_position_coverage(model.instrument.inst.positions, update_gui=True) if not event is None: event.Skip()