function that is called when a stage is completed and
		needs to be analyzed befor further computations.

		The code here implements the original SH algorithms by
		advancing the k-best (lowest loss) configurations at the current
		budget. k is defined by the num_configs list (see __init__)
		and the current stage value.

		For more advanced methods like resampling after each stage,
		overload this function only.

def process_results(self):
		"""
		function that is called when a stage is completed and
		needs to be analyzed befor further computations.

		The code here implements the original SH algorithms by
		advancing the k-best (lowest loss) configurations at the current
		budget. k is defined by the num_configs list (see __init__)
		and the current stage value.

		For more advanced methods like resampling after each stage,
		overload this function only.
		"""
		self.stage += 1
		
		# collect all config_ids that need to be compared
		config_ids = list(filter(lambda cid: self.data[cid].status == 'REVIEW', self.data.keys()))

		if (self.stage >= len(self.num_configs)):
			self.finish_up()
			return

		budgets = [self.data[cid].budget for cid in config_ids]
		if len(set(budgets)) > 1:
			raise RuntimeError('Not all configurations have the same budget!')
		budget = self.budgets[self.stage-1]

		losses = np.array([self.data[cid].results[budget]['loss'] for cid in config_ids])

		advance = self._advance_to_next_stage(config_ids, losses)

		for i, a in enumerate(advance):
			if a:
				self.logger.debug('ITERATION: Advancing config %s to next budget %f'%(config_ids[i], self.budgets[self.stage]))

		for i, cid in enumerate(config_ids):
			if advance[i]:
				self.data[cid].status = 'QUEUED'
				self.data[cid].budget = self.budgets[self.stage]
				self.actual_num_configs[self.stage] += 1
			else:
				self.data[cid].status = 'TERMINATED'