class TestSpaceNonToroidal(unittest.TestCase): ''' Testing a toroidal continuous space. ''' def setUp(self): ''' Create a test space and populate with Mock Agents. ''' self.space = ContinuousSpace(70, 20, False, -30, -30, 100, 100) self.agents = [] for i, pos in enumerate(TEST_AGENTS): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS): a = self.agents[i] assert a.pos == pos def test_distance_calculations(self): ''' Test toroidal distance calculations. ''' pos_2 = (70, 20) pos_3 = (-30, -20) assert self.space.get_distance(pos_2, pos_3) == 107.70329614269008 def test_neighborhood_retrieval(self): ''' Test neighborhood retrieval ''' neighbors_1 = self.space.get_neighbors((-20, -20), 1) assert len(neighbors_1) == 2 neighbors_2 = self.space.get_neighbors((40, -10), 10) assert len(neighbors_2) == 0 neighbors_3 = self.space.get_neighbors((-30, -30), 10) assert len(neighbors_3) == 0 def test_bounds(self): ''' Test positions outside of boundary ''' for i, pos in enumerate(OUTSIDE_POSITIONS): a = MockAgent(len(self.agents) + i, None) with self.assertRaises(Exception): self.space.place_agent(a, pos) a = self.agents[0] for pos in OUTSIDE_POSITIONS: assert self.space.out_of_bounds(pos) with self.assertRaises(Exception): self.space.move_agent(a, pos)
class TestSpaceToroidal(unittest.TestCase): ''' Testing a toroidal continuous space. ''' def setUp(self): ''' Create a test space and populate with Mock Agents. ''' self.space = ContinuousSpace(70, 20, True, -30, -30) self.agents = [] for i, pos in enumerate(TEST_AGENTS): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS): a = self.agents[i] assert a.pos == pos def test_agent_matching(self): ''' Ensure that the agents are all placed and indexed properly. ''' for i, agent in self.space._index_to_agent.items(): assert agent.pos == tuple(self.space._agent_points[i, :]) assert i == self.space._agent_to_index[agent] def test_distance_calculations(self): ''' Test toroidal distance calculations. ''' pos_1 = (-30, -30) pos_2 = (70, 20) assert self.space.get_distance(pos_1, pos_2) == 0 pos_3 = (-30, -20) assert self.space.get_distance(pos_1, pos_3) == 10 pos_4 = (20, -5) pos_5 = (20, -15) assert self.space.get_distance(pos_4, pos_5) == 10 pos_6 = (-30, -29) pos_7 = (21, -5) assert self.space.get_distance(pos_6, pos_7) == np.sqrt(49**2 + 24**2) def test_heading(self): pos_1 = (-30, -30) pos_2 = (70, 20) self.assertEqual((0, 0), self.space.get_heading(pos_1, pos_2)) pos_1 = (65, -25) pos_2 = (-25, -25) self.assertEqual((10, 0), self.space.get_heading(pos_1, pos_2)) def test_neighborhood_retrieval(self): ''' Test neighborhood retrieval ''' neighbors_1 = self.space.get_neighbors((-20, -20), 1) assert len(neighbors_1) == 2 neighbors_2 = self.space.get_neighbors((40, -10), 10) assert len(neighbors_2) == 0 neighbors_3 = self.space.get_neighbors((-30, -30), 10) assert len(neighbors_3) == 1 def test_bounds(self): ''' Test positions outside of boundary ''' boundary_agents = [] for i, pos in enumerate(OUTSIDE_POSITIONS): a = MockAgent(len(self.agents) + i, None) boundary_agents.append(a) self.space.place_agent(a, pos) for a, pos in zip(boundary_agents, OUTSIDE_POSITIONS): adj_pos = self.space.torus_adj(pos) assert a.pos == adj_pos a = self.agents[0] for pos in OUTSIDE_POSITIONS: assert self.space.out_of_bounds(pos) self.space.move_agent(a, pos)
class TestSpaceNonToroidal(unittest.TestCase): """ Testing a toroidal continuous space. """ def setUp(self): """ Create a test space and populate with Mock Agents. """ self.space = ContinuousSpace(70, 20, False, -30, -30) self.agents = [] for i, pos in enumerate(TEST_AGENTS): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): """ Ensure that the agents are all placed properly. """ for i, pos in enumerate(TEST_AGENTS): a = self.agents[i] assert a.pos == pos def test_agent_matching(self): """ Ensure that the agents are all placed and indexed properly. """ for i, agent in self.space._index_to_agent.items(): assert agent.pos == tuple(self.space._agent_points[i, :]) assert i == self.space._agent_to_index[agent] def test_distance_calculations(self): """ Test toroidal distance calculations. """ pos_2 = (70, 20) pos_3 = (-30, -20) assert self.space.get_distance(pos_2, pos_3) == 107.70329614269008 def test_heading(self): pos_1 = (-30, -30) pos_2 = (70, 20) self.assertEqual((100, 50), self.space.get_heading(pos_1, pos_2)) pos_1 = (65, -25) pos_2 = (-25, -25) self.assertEqual((-90, 0), self.space.get_heading(pos_1, pos_2)) def test_neighborhood_retrieval(self): """ Test neighborhood retrieval """ neighbors_1 = self.space.get_neighbors((-20, -20), 1) assert len(neighbors_1) == 2 neighbors_2 = self.space.get_neighbors((40, -10), 10) assert len(neighbors_2) == 0 neighbors_3 = self.space.get_neighbors((-30, -30), 10) assert len(neighbors_3) == 0 def test_bounds(self): """ Test positions outside of boundary """ for i, pos in enumerate(OUTSIDE_POSITIONS): a = MockAgent(len(self.agents) + i, None) with self.assertRaises(Exception): self.space.place_agent(a, pos) a = self.agents[0] for pos in OUTSIDE_POSITIONS: assert self.space.out_of_bounds(pos) with self.assertRaises(Exception): self.space.move_agent(a, pos)
class PredatorPreyModel(Model): def __init__(self, height=100, width=100, init_prey=100, prey_reproduction=0.03, init_predator=10, predator_vision=1, predator_reproduction=0.5, predator_death=0.02, local_offspring=False, max_iters=500, seed=None): super().__init__() self.height = height self.width = width self.init_prey = init_prey self.prey_reproduction = prey_reproduction self.init_predator = init_predator self.predator_vision = predator_vision self.predator_reproduction = predator_reproduction self.predator_death = predator_death self.local_offspring = local_offspring self.iteration = 0 self.max_iters = max_iters self.schedule = RandomActivation(self) self.space = ContinuousSpace(height, width, torus=True) model_reporters = { 'Prey': lambda model: self.count('Prey'), 'Predator': lambda model: self.count('Predator'), } self.datacollector = DataCollector(model_reporters=model_reporters) # Place prey for i in range(self.init_prey): x = self.random.uniform(0, self.width) y = self.random.uniform(0, self.height) # next_id() starts at 1 prey = Prey(self.next_id(), self, (x, y), self.prey_reproduction) self.space.place_agent(prey, (x, y)) self.schedule.add(prey) # Place predators for i in range(self.init_predator): x = self.random.uniform(0, self.width) y = self.random.uniform(0, self.height) predator = Predator(self.next_id(), self, (x, y), self.predator_reproduction, self.predator_death, self.predator_vision) self.space.place_agent(predator, (x, y)) self.schedule.add(predator) self.running = True self.datacollector.collect(self) def step(self): """ Advance the model by one step and collect data. Returns ------- None. """ self.schedule.step() self.iteration += 1 self.datacollector.collect(self) # Stop system if maximum of iterations is reached if self.iteration > self.max_iters: self.running = False return None def count(self, breed): """ Count agent by breed. Parameters ---------- breed : string Breed of agent Can be 'Prey' or 'Predator'. Returns ------- count : int Number of agents of type breed. """ count = 0 for agent in self.schedule.agents: if agent.breed == breed: count += 1 if breed == 'Predator' and count == 0: self.running = False return count def warm_up(self): for agent in self.schedule.agents: if agent.breed == 'Prey': continue neighbors = self.space.get_neighbors(agent.pos, radius=agent.vision) for prey in neighbors: if prey.breed == 'Prey': x = self.random.uniform(0, self.width) y = self.random.uniform(0, self.height) self.space.move_agent(prey, (x, y))
class ChaosModel(Model): ''' The stochastic highway simulation model ''' ################################################################# ## INITIALIZATION FUNCTIONS ################################################################# def __init__(self, agent_type, width=500, height=500, num_adversaries=8, road_width=60, frozen=True, epsilon=0, episode_duration=100, Q=None, N=None): self.num_adversaries = num_adversaries self.road_width = road_width self.episode_duration = episode_duration self.schedule = RandomActivation(self) self.space = ContinuousSpace(width, height, True) self.cars = [] self.make_agents(AgentType(agent_type), frozen, epsilon, Q, N) self.running = True self.step_count = 0 self.count = 0 self.curr_reward = 0 self.reset() self.datacollector = DataCollector( model_reporters={"Agent rewards sum": get_rewards_sum_log}) def agent_start_state(self): pos = np.array((self.space.x_max / 2, self.space.y_max / 2 + 100)) target_speed = 10 speed = target_speed heading = np.radians(-90) return (pos, target_speed, speed, heading) def reset(self): (pos, target_speed, speed, heading) = self.agent_start_state() # speed = util.rand_min_max(target_speed*.8, target_speed*1.2) # speed = util.rand_min_max(6,14) # speed = 0 # heading = util.rand_min_max(np.radians(-89),np.radians(-91)) # heading = np.radians(-85) self.learn_agent.speed = speed self.learn_agent.heading = heading self.space.move_agent(self.learn_agent, pos) self.step_count = 0 self.rewards_sum = -1 def make_adversary(self, unique_id): # Initial speed and heading speed = util.rand_min_max(0, 3) heading = np.radians(-90) # Randomly add large (blue), medium (orange), or small (green) car val = random.random() if val < 0.33: color = "Blue" target_speed = util.rand_min_max(2, 4) width = util.rand_min_max(8, 9) length = util.rand_min_max(35, 45) elif val < 0.66: color = "Orange" target_speed = util.rand_min_max(3, 6) width = util.rand_min_max(7, 8) length = util.rand_min_max(16, 30) else: color = "Green" target_speed = util.rand_min_max(6, 7) width = util.rand_min_max(5, 6) length = util.rand_min_max(12, 16) # Add car to a random position if not overlapping within a margin # Try (num_adversaries * 2) times before giving up pos = None for _ in range(self.num_adversaries * 2): x = util.rand_center_spread(self.space.x_max / 2, self.road_width) y = random.uniform(self.space.y_min + 1, self.space.y_max - 1) if not util.is_overlapping(x, y, width, length, self.cars): pos = np.array((x, y)) break if pos is None: return None return Car(unique_id, self, pos, speed, heading, color, target_speed=target_speed, width=width, length=length) def make_learn_agent(self, agent_type, unique_id, epsilon, Q, N): (pos, target_speed, speed, heading) = self.agent_start_state() width = 6 length = 12 color = "Black" if agent_type == AgentType.QLEARN: return QCar(unique_id, self, pos, speed, heading, color, target_speed=target_speed, length=length, width=width, Q=Q, N=N) elif agent_type == AgentType.DEEPQ: return DeepQCar(unique_id, self, pos, speed, heading, color, target_speed=target_speed, width=width, length=length, epsilon=epsilon) else: # agent_type == AgentType.BASIC return Car(unique_id, self, pos, speed, heading, color, target_speed=target_speed, width=width, length=length) def make_frozen(self, unique_id): pos = np.array((self.space.x_max / 2, self.space.y_max / 2)) return Car(unique_id, self, pos, 0, np.radians(-90), "Indigo", target_speed=0, width=6, length=12) def make_agents(self, agent_type, frozen, epsilon, Q, N): ''' Add all agents to model space ''' # Car agents for i in range(0, self.num_adversaries + 1): if i == 0: car = self.make_learn_agent(agent_type, i, epsilon, Q, N) elif frozen: car = self.make_frozen(i) frozen = False else: car = self.make_adversary(i) if car is None: print("WARNING: Could only add %d adversaries" % (i - 1)) break self.cars.append(car) self.space.place_agent(car, car.pos) self.schedule.add(car) # Barriers x = (self.space.x_max + self.road_width + 10) / 2 self.add_barrier(len(self.cars), x) self.add_barrier(len(self.cars) + 1, x - self.road_width - 10) def add_barrier(self, unique_id, x): y = self.space.y_max / 2 barrier = Barrier(unique_id, self, np.array([x, y]), "Black", 1, self.space.y_max) self.schedule.add(barrier) ################################################################# ## RUNNING SIMULATION FUNCTIONS ################################################################# @property def learn_agent(self): return self.cars[0] def step(self): self.step_count += 1 if self.step_count > self.episode_duration and \ type(self.learn_agent) is not DeepQCar: self.reset() self.datacollector.collect(self) # First loop through and have all cars choose an action # before the action is actually propagated forward for car in self.cars: car.choose_action() # Propagate forward one step based on chosen actions self.schedule.step() self.curr_reward = self.reward() self.rewards_sum += self.curr_reward agent = self.learn_agent left = (self.space.x_max - self.road_width) / 2 # if type(agent) is DeepQCar and \ # (agent.pos[0] - agent.length / 2 <= left or \ # agent.pos[0] + agent.length / 2 >= left + self.road_width): # self.running = False if type(agent) is DeepQCar and \ (agent.pos[0] - agent.length / 2 <= left or \ agent.pos[0] + agent.length / 2 >= left + self.road_width or \ util.is_overlapping(agent.pos[0], agent.pos[1], agent.width, agent.length, agent.get_neighbors(), margin=0)): self.running = False def reward(self): agent = self.learn_agent # heading_cost = np.abs(agent.heading - agent.target_heading) * -2 # steering_cost = np.abs(agent.steer) * -2 # acceleration_cost = np.abs(agent.accel) * -1 vy = -agent.speed * np.sin(agent.heading) speed_reward = 0 if (vy > agent.target_speed * 1.1): speed_reward = -20 elif (vy > agent.target_speed * 1.05): speed_reward = -2 elif (vy > agent.target_speed * 0.90): speed_reward = 0 else: speed_reward = -3 # penalizes collisoins from the front more collision_cost = agent.collided * -500 # Alternate speed reward speed_reward = np.abs(vy - agent.target_speed) * -1 if speed_reward > -0.5: speed_reward = 0 elif speed_reward < -agent.target_speed / 2: speed_reward = -20 # Try to reward position # pos_cost = 0 # if agent.pos[1] < self.space.y_max/2 - 70 or vy < agent.target_speed/2: # pos_cost = -10 # print(pos_reward) # print("speed reward {}, accel cost {}, steer cost {}, collision cost {}".format( # speed_reward, acceleration_cost, steering_cost, collision_cost)) # return speed_reward + acceleration_cost + steering_cost + \ # collision_cost + heading_cost # print(speed_reward, collision_cost, heading_cost) # print(vy, agent.target_speed, speed_reward, collision_cost, a, agent.steer, agent.accel) return speed_reward + collision_cost def deepq_reward(self, agent): left = (self.space.x_max - self.road_width) / 2 # Large negative reward for collision if agent.pos[0] - agent.length / 2 <= left or \ agent.pos[0] + agent.length / 2 >= left + self.road_width: return -1 if util.is_overlapping(agent.pos[0], agent.pos[1], agent.width, agent.length, agent.get_neighbors(), margin=0): return -0.5 return 0 # Large positive reward for staying at target speed and heading # Linearly decreases based on magnitude of difference from actual # Reward of 0.5 for staying at average (y) speed of all cars # Assumes target speed > average speed # Reward of 0.5 for changing lanes within one time step (TEST THIS) # Assumes actions have no cost, but rather cause cost by changing # resulting velocity (THIS MAY CHANGE) average_speed = np.mean([-car.speed * np.sin(car.heading) \ for car in self.cars]) agent_vel = agent.vel_components() x_cost = abs(agent_vel[0]) / 20 y_cost = abs(-agent_vel[1] - agent.target_speed) / \ (agent.target_speed - average_speed) / 2 return max(1 - x_cost - y_cost, -1)
class TestSpaceToroidal(unittest.TestCase): ''' Testing a toroidal continuous space. ''' def setUp(self): ''' Create a test space and populate with Mock Agents. ''' self.space = ContinuousSpace(70, 20, True, -30, -30, 100, 100) self.agents = [] for i, pos in enumerate(TEST_AGENTS): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS): a = self.agents[i] assert a.pos == pos def test_distance_calculations(self): ''' Test toroidal distance calculations. ''' pos_1 = (-30, -30) pos_2 = (70, 20) assert self.space.get_distance(pos_1, pos_2) == 0 pos_3 = (-30, -20) assert self.space.get_distance(pos_1, pos_3) == 10 def test_heading(self): pos_1 = (-30, -30) pos_2 = (70, 20) self.assertEqual((0, 0), self.space.get_heading(pos_1, pos_2)) pos_1 = (65, -25) pos_2 = (-25, -25) self.assertEqual((10, 0), self.space.get_heading(pos_1, pos_2)) def test_neighborhood_retrieval(self): ''' Test neighborhood retrieval ''' neighbors_1 = self.space.get_neighbors((-20, -20), 1) assert len(neighbors_1) == 2 neighbors_2 = self.space.get_neighbors((40, -10), 10) assert len(neighbors_2) == 0 neighbors_3 = self.space.get_neighbors((-30, -30), 10) assert len(neighbors_3) == 1 def test_bounds(self): ''' Test positions outside of boundary ''' boundary_agents = [] for i, pos in enumerate(OUTSIDE_POSITIONS): a = MockAgent(len(self.agents) + i, None) boundary_agents.append(a) self.space.place_agent(a, pos) for a, pos in zip(boundary_agents, OUTSIDE_POSITIONS): adj_pos = self.space.torus_adj(pos) assert a.pos == adj_pos a = self.agents[0] for pos in OUTSIDE_POSITIONS: assert self.space.out_of_bounds(pos) self.space.move_agent(a, pos)
class TestSpaceNonToroidal(unittest.TestCase): ''' Testing a toroidal continuous space. ''' def setUp(self): ''' Create a test space and populate with Mock Agents. ''' self.space = ContinuousSpace(70, 20, False, -30, -30) self.agents = [] for i, pos in enumerate(TEST_AGENTS): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS): a = self.agents[i] assert a.pos == pos def test_agent_matching(self): ''' Ensure that the agents are all placed and indexed properly. ''' for i, agent in self.space._index_to_agent.items(): assert agent.pos == tuple(self.space._agent_points[i, :]) assert i == self.space._agent_to_index[agent] def test_distance_calculations(self): ''' Test toroidal distance calculations. ''' pos_2 = (70, 20) pos_3 = (-30, -20) assert self.space.get_distance(pos_2, pos_3) == 107.70329614269008 def test_heading(self): pos_1 = (-30, -30) pos_2 = (70, 20) self.assertEqual((100, 50), self.space.get_heading(pos_1, pos_2)) pos_1 = (65, -25) pos_2 = (-25, -25) self.assertEqual((-90, 0), self.space.get_heading(pos_1, pos_2)) def test_neighborhood_retrieval(self): ''' Test neighborhood retrieval ''' neighbors_1 = self.space.get_neighbors((-20, -20), 1) assert len(neighbors_1) == 2 neighbors_2 = self.space.get_neighbors((40, -10), 10) assert len(neighbors_2) == 0 neighbors_3 = self.space.get_neighbors((-30, -30), 10) assert len(neighbors_3) == 0 def test_bounds(self): ''' Test positions outside of boundary ''' for i, pos in enumerate(OUTSIDE_POSITIONS): a = MockAgent(len(self.agents) + i, None) with self.assertRaises(Exception): self.space.place_agent(a, pos) a = self.agents[0] for pos in OUTSIDE_POSITIONS: assert self.space.out_of_bounds(pos) with self.assertRaises(Exception): self.space.move_agent(a, pos)