def test_pick_agent_index_to_move(self): agent_indices = get_agent_indices(self.test_array) unsatisfied_agent_index_indices = [0, 1, 2, 3, 5, 6, 7, 8] unsatisfied_agent_indices = \ agent_indices[unsatisfied_agent_index_indices] expected_agent_index = _first_picker(unsatisfied_agent_indices) agent_index = _pick_agent_index_to_move(unsatisfied_agent_indices, _first_picker) with self.subTest(out=agent_index, expected=expected_agent_index): self.assertEqual(expected_agent_index, agent_index)
def check_get_unsatisfied_agent_indices_expected_output( self, parameters, satisficers): agent_indices = get_agent_indices(self.test_array) for utility_function, expected_output in parameters: utility = ut.get_utility_for_array(utility_function, self.test_array) output = _get_unsatisfied_agent_indices(utility, agent_indices, satisficers=satisficers) expected_output = agent_indices[np.array(expected_output)] with self.subTest(ut=utility_function, out=output, expected=expected_output): self.assertTrue(np.array_equal(output, expected_output))
def test_satisfied_percent(self): test_arrays = [(np.array([[0, 1, 1, 0], [1, 1, 0, 1], [0, 2, 2, 0], [0, 1, 2, 0]]), 8 * 100 / 9), (np.array([[0, 1, 2, 0], [1, 2, 2, 1], [1, 2, 2, 0], [1, 1, 1, 0]]), 9 * 100 / 12), (np.array([[1, 1, 1, 0], [1, 1, 0, 2], [0, 1, 2, 2], [0, 1, 2, 2]]), 100)] utility_function = create_flat_utility(0.5) for array, exepcted_output in test_arrays: ut = get_utility_for_array(utility_function, array, True) agent_indices = get_agent_indices(array) sp = satisfied_percent(ut, agent_indices) with self.subTest(): self.assertEqual(sp, exepcted_output)
def check_better_vacancy_expected_output(self, parameters, satisficers): utility = get_utility_for_array(create_flat_utility(0.5), self.test_array) agent_indices = get_agent_indices(self.test_array) vacancy_indices = get_vacancy_indices(self.test_array) for unsatisfied_agent_index, expected_output_indices in parameters: expected_output = vacancy_indices[expected_output_indices] i = agent_indices[unsatisfied_agent_index] output = _get_better_vacancies(self.test_array, i, utility, vacancy_indices, satisficers=satisficers) with self.subTest(i=unsatisfied_agent_index, out=output, expected=expected_output): self.assertTrue(np.array_equal(output, expected_output))
def is_simulation_halted(array, utility_function): utility = get_utility_for_array(utility_function, array, count_vacancies=True) agent_indices = get_agent_indices(array) if agent_indices.size == 0: return True unsatisfied_agent_indices = _get_unsatisfied_agent_indices(utility, agent_indices, satisficers=False) if unsatisfied_agent_indices.size == 0: return True vacancy_indices = get_vacancy_indices(array) if vacancy_indices.size == 0: return True for agent_index in unsatisfied_agent_indices: better_vacancies = _get_better_vacancies(array, agent_index, utility, vacancy_indices, satisficers=False) if better_vacancies.size != 0: return False return True
def run_simulation(settings, callback=lambda arr, res, i: None): """Run simulation with specified settings. Call the optional callback function after each iteration, passing the current array state, current result, and iteration number. Returns simulation result - segregation measures before each iteration. Args: settings (SimulationSettings): settings callback (callable, optional): Function to call after each iteration Returns: SimulationResult: Result - segregation measures for each iteration """ global _stop settings.validate() result = SimulationResult(settings.segregation_measure_names) array = create_array(settings.grid_size, settings.get_agent_type_proportions(), settings.initial_random_allocation) # utility - function: (index) -> (0,1) utility = get_utility_for_array(settings.utility_function, array, count_vacancies=settings.count_vacancies, radius=settings.radius) pickers = { 'random': _random_picker, 'first': _first_picker, } agent_pickers = dict(pickers, **{ 'roulette': _create_roulette_picker( settings.agent_roulette_base_weight, utility, for_agents=True), }) vacancy_pickers = dict(pickers, **{ 'roulette': _create_roulette_picker( settings.vacancy_roulette_base_weight, utility, for_agents=False), }) agent_picker = agent_pickers[settings.agent_picking_regime] vacancy_picker = vacancy_pickers[settings.vacancy_picking_regime] # if no agents, end simulation agent_indices = get_agent_indices(array) if agent_indices.shape[0] == 0: return result # _update_result(result, array, agent_indices, # settings.count_vacancies, settings.segregation_measure_names) for i in range(settings.iterations): while _pause: pass should_save_result = i % settings.save_period == 0 if should_save_result: callback(array, result, i) is_simulation_halted = update_array(array, utility, result, agent_picker, vacancy_picker, settings.count_vacancies, settings.segregation_measure_names, settings.satisficers, should_save_result) # if no further moves are possible, exit simulation early if is_simulation_halted: # Get measures for final state and save (unless it was just saved) if not should_save_result: agent_indices = get_agent_indices(array) _update_result(result, array, agent_indices, settings.count_vacancies, settings.segregation_measure_names) callback(array, result, i) break if _stop: break _stop = False return result
def update_array(array, utility, result, agent_picker, vacancy_picker, count_vacancies, segregation_measure_names, satisficers=False, save_result=True): """Do a single iteration of the simulation. Args: array (ndarray): array utility (callable): utility function - (agent index) -> (0, 1) radius (int): neighborhood radius result (SimulationResult): result object to store segregation measures before updating array satisficers (bool, optional): satisficer behavior setting """ agent_indices = get_agent_indices(array) if save_result: _update_result(result, array, agent_indices, count_vacancies, segregation_measure_names) unsatisfied_agent_indices = _get_unsatisfied_agent_indices(utility, agent_indices, satisficers=satisficers) # if all agents satisfied, end simulation if unsatisfied_agent_indices.size == 0: return True agent_index = _pick_agent_index_to_move(unsatisfied_agent_indices, agent_picker) vacancy_indices = get_vacancy_indices(array) if vacancy_indices.size == 0: return True # if vacancy picker is roulette, consider all vacancies if vacancy_picker.__name__ == 'roulette_picker': better_vacancy_indices = vacancy_indices else: better_vacancy_indices = _get_better_vacancies(array, agent_index, utility, vacancy_indices, satisficers=satisficers) if better_vacancy_indices.size == 0: # if relocation regime is move first, and the first agent has no better # vacancy, the simulation will halt. If this happens, try all # unsatisfied agents in list and pick the first one which can find a # better vacancy. If none have a better vacancy, end the simulation. if agent_picker is _first_picker: for agent_index in unsatisfied_agent_indices: better_vacancy_indices = _get_better_vacancies(array, agent_index, utility, vacancy_indices, satisficers=satisficers) if better_vacancy_indices.size != 0: break else: return True else: return False agent_type = array[agent_index] better_vacancy = _pick_better_vacancy_index(better_vacancy_indices, vacancy_picker, agent_type) _move(array, agent_index, better_vacancy) return False
def test_agent_indices(self): output = get_agent_indices(self.test_array) expected_output = self.agent_indices self.assertTrue(np.array_equal(output, expected_output))