def reset(self): self.simpy_env = simpy.Environment() if FIRE_LOCATIONS is not None: self.fires = [ Fire(self, self.simpy_env, i, FIRE_REWARDS[i], FIRE_PROB_PROFILES[i], fl) for i, fl in enumerate(FIRE_LOCATIONS) ] else: # we want to randomize fire_locations = ( 2.*np.random.random_sample((NUM_FIRES,2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, FIRE_REWARDS[i], FIRE_PROB_PROFILES[i], fl) for i, fl in enumerate(fire_locations) ] if START_POSITIONS is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp) for i,sp in enumerate(START_POSITIONS) ] else: # we want to randomize start_positions = ( 2.*np.random.random_sample((NUM_AGENTS,2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp) for i,sp in enumerate(start_positions) ] self.fire_events = [ fire.extinguish_event for fire in self.fires ] self.uav_events = [simpy.Event(self.simpy_env) for _ in range(self.n_agents)] self.max_simtime_event = simpy.Event(self.simpy_env) self.simpy_env.process( max_simtime_trigger(self.simpy_env, self.max_simtime_event) ) # Step with a hold at start locations return self.step( [ 5 ] * NUM_AGENTS )[0]
def __init__(self, env, config: CpuConfig, default_val=0): self.env = env self._access_queue = simpy.Store(env, config.mem_access_queue_size) self._access_queue_pop_event = simpy.Event(env) self._accesses_in_execution = simpy.FilterStore(env) self._accesses_in_execution_pop_event = simpy.Event(env) self.id = "MEM" self._memory = [default_val] * config.mem_size self._log = get_logger(env, self.id)
def reset(self): self.done = False self.simpy_env = simpy.Environment() self.fire_extinguish_events = [simpy.Event(self.simpy_env) for i in range(self.n_fires)] fire_levels = [] for i, n in enumerate(self.num_fires_of_each_size): fire_levels += [i+1] * n if self.fire_locations is not None: self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(self.fire_locations) ] else: # we want to randomize fire_locations = ( 2.*np.random.random_sample((self.n_fires,2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(fire_locations) ] if self.start_positions is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, None) for i,sp in enumerate(self.start_positions) ] else: # we want to randomize start_positions = ( 2.*np.random.random_sample((self.n_agents,2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, None) for i,sp in enumerate(start_positions) ] return
def __init__(self, env, simpy_env, id_num, level, location): self.env = env self.simpy_env = simpy_env self.id_num = id_num self.location = location self.status = True self.extinguish_event = simpy.Event( self.simpy_env) # Gets triggered when the fire is extinguished self.extinguish_party = [ ] # Number of agents trying to extinguish the fire self.prev_len_extinguish_party = 0 self.last_update_time = simpy_env.now self.interest_party = [] self.time_until_extinguish = np.inf self.level = level self.reward = level self.uav_seconds_left = float( truncnorm(-UAV_MINS_AVG * level / UAV_MINS_STD, np.inf).rvs(1)) self.uav_seconds_left = self.uav_seconds_left * UAV_MINS_STD + UAV_MINS_AVG * level if (PRINTING or FIRE_DEBUG): print('Fire %d has a %.2f UAV seconds left' % (self.id_num, self.uav_seconds_left))
def change_goal(self, hold_current = False, new_goal = None): if new_goal is None: new_goal = copy.deepcopy(self.goal_position) event = simpy.Event(self.simpy_env) if not hold_current: # stop attacking any fire you are attacking if(self.fire_attacking > -1): self.env.fires[self.fire_attacking].leave_extinguish_party(self) self.fire_attacking = -1 self.start_position = copy.deepcopy(self.current_position) self.goal_position = copy.deepcopy(new_goal) travel_time = np.linalg.norm( np.array(self.goal_position) - np.array(self.start_position) ) / UAV_VELOCITY self.simpy_env.process(timeout(self.simpy_env, event, travel_time)) if(PRINTING): print('UAV %d is heading from (%.2f, %.2f) to (%.2f, %.2f)' % (self.id_num, self.start_position[0], self.start_position[1], self.goal_position[0], self.goal_position[1] )) else: # Holding self.start_position = copy.deepcopy(self.current_position) self.goal_position = copy.deepcopy(self.start_position) self.simpy_env.process(timeout(self.simpy_env, event, HOLD_TIME)) if(PRINTING): print('UAV %d holding at (%.2f, %.2f)' % (self.id_num, self.current_position[0], self.current_position[1])) # If we're at a fire, join its extinguish party for i, f in enumerate(self.env.fires): if within_epsilon(self.current_position, f.location): if(self.fire_attacking != i): f.join_extinguish_party(self) self.fire_attacking = i break self.env.uav_events[self.id_num] = event self.action_time = self.simpy_env.now return event
def update_extinguish_time(self): party_size = len(self.extinguish_party) prev_party_size = self.prev_len_extinguish_party now = self.simpy_env.now # decrement uav_seconds_left according to how long its been # attacked for and by how many agents, since this function # was last called time_since_last_update = now - self.last_update_time decrement = time_since_last_update * prev_party_size # update state vars self.last_update_time = now self.prev_len_extinguish_party = party_size self.uav_seconds_left -= decrement # update event with new time remaining and new party size event = simpy.Event(self.simpy_env) time_to_extinguish = self.uav_seconds_left / party_size if party_size > 0 else np.inf self.simpy_env.process( timeout(self.simpy_env, event, time_to_extinguish)) self.extinguish_event = event self.time_until_extinguish = time_to_extinguish # update the event in main env self.env.fire_events[self.id_num] = self.extinguish_event if (FIRE_DEBUG): print( 'Fire %d has extinguish party size %d and %.2f UAV seconds left at time %.2f' % (self.id_num, party_size, self.uav_seconds_left, now)) return
def reset(self): self.simpy_env = simpy.Environment() fire_levels = [] for i, n in enumerate(self.num_fires_of_each_size): fire_levels += [i + 1] * n if self.fire_locations is not None: self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(self.fire_locations) ] else: # we want to randomize fire_locations = (2. * np.random.random_sample( (self.n_fires, 2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(fire_locations) ] if self.start_positions is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount) for i, sp in enumerate(self.start_positions) ] else: # we want to randomize start_positions = (2. * np.random.random_sample( (self.n_agents, 2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount) for i, sp in enumerate(start_positions) ] self.fire_events = [fire.extinguish_event for fire in self.fires] self.uav_events = [ simpy.Event(self.simpy_env) for _ in range(self.n_agents) ] self.max_simtime_event = simpy.Event(self.simpy_env) self.simpy_env.process( max_simtime_trigger(self.simpy_env, self.max_simtime_event)) # Step with a hold at start locations return self.step([5] * self.n_agents)[0]
def reset_and_sim(self, policies): self.simpy_env = simpy.Environment() self.done = False self.fire_extinguish_events = [simpy.Event(self.simpy_env) for i in range(self.n_fires)] fire_levels = [] for i, n in enumerate(self.num_fires_of_each_size): fire_levels += [i+1] * n if self.fire_locations is not None: self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(self.fire_locations) ] else: # we want to randomize fire_locations = ( 2.*np.random.random_sample((self.n_fires,2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(fire_locations) ] if self.start_positions is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, policies[i]) for i,sp in enumerate(self.start_positions) ] else: # we want to randomize start_positions = ( 2.*np.random.random_sample((self.n_agents,2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, policies[i]) for i,sp in enumerate(start_positions) ] # Process all UAVs for uav in self.env_agents: self.simpy_env.process( uav.sim() ) # Process all fires for fire in self.fires: self.simpy_env.process( fire.sim() ) done = simpy.AllOf(self.simpy_env, self.fire_extinguish_events) self.simpy_env.run(until = simpy.AnyOf(self.simpy_env, [done, self.simpy_env.timeout(MAX_SIMTIME)]) ) self.done = True if(True): for uav in self.env_agents: try: uav.action_event.interrupt() except RuntimeError: pass self.simpy_env.run(until = self.simpy_env.now*(1.0001)) assert sum([uav.get_reward() for uav in self.env_agents]) == 0, 'There were unaccounted for rewards' # Collect observations, actions, etc.. and return them observations = [ u.observations for u in self.env_agents] actions = [ u.actions for u in self.env_agents] rewards = [ u.rewards for u in self.env_agents] agent_infos = [ u.agent_infos for u in self.env_agents] env_infos = [ u.env_infos for u in self.env_agents] offset_t_sojourn = [ u.offset_t_sojourn for u in self.env_agents ] return observations, actions, rewards, agent_infos, env_infos, offset_t_sojourn
def address_resolution_complete(self, rs): current_rs = yield self._access_queue.get() if rs != current_rs: raise Exception("Wrong order!") ev, self._access_queue_pop_event = self._access_queue_pop_event, simpy.Event(self.env) yield self._accesses_in_execution.put(rs) ev.succeed()
def update_extinguish_time(self): party_size = len(self.extinguish_party) if party_size > 0: prob_profile_params = self.prob_profile[party_size - 1] # Sample a new time new_time_until_extinguish = float(np.random.exponential(prob_profile_params)) if(new_time_until_extinguish < self.time_until_extinguish): event = simpy.Event(self.simpy_env) self.simpy_env.process(timeout(self.simpy_env, event, new_time_until_extinguish)) self.extinguish_event = event self.time_until_extinguish = new_time_until_extinguish + self.simpy_env.now else: self.extinguish_event = simpy.Event(self.simpy_env) # will never trigger self.time_until_extinguish = np.inf # update the event in main env self.env.fire_events[self.id_num] = self.extinguish_event return
def extinguish(self): self.status = False for a in self.env.env_agents: a.accrue_reward(self.reward) # set event to one that never triggers self.extinguish_event = simpy.Event(self.simpy_env) self.env.fire_events[self.id_num] = self.extinguish_event self.time_until_extinguish = -1 if(PRINTING): print('Fire %d extinguished at %.2f' % (self.id_num, self.simpy_env.now)) return
def reset(self): global car_counter car_counter = 0 self.simpy_env = simpy.Environment() env = self.simpy_env self.env_agents = [TrafficLight(env, i) for i in range(self.n_agents)] self.max_simtime_event = simpy.Event(self.simpy_env) self.simpy_env.process( max_simtime_trigger(self.simpy_env, self.max_simtime_event)) # Set neighboring lights for i in range(self.connectivity.shape[0]): for j in range(self.connectivity.shape[1]): north = self.env_agents[self.connectivity[ i - 1, j]] if i > 0 else None south = self.env_agents[self.connectivity[ i + 1, j]] if i < self.connectivity.shape[0] - 1 else None east = self.env_agents[self.connectivity[i, j - 1]] if j > 0 else None west = self.env_agents[self.connectivity[ i, j + 1]] if j < self.connectivity.shape[1] - 1 else None self.env_agents[self.connectivity[i, j]].set_neighboring_lights([ north, south, east, west ]) # Car generators for i in range(self.connectivity.shape[0]): traffic_light_list = [ self.env_agents[j] for j in self.connectivity[i, :].tolist() ] # East-bound env.process(car_generator(env, traffic_light_list, 'east')) # West-bound env.process(car_generator(env, traffic_light_list[::-1], 'west')) for i in range(self.connectivity.shape[1]): traffic_light_list = [ self.env_agents[j] for j in self.connectivity[:, i].tolist() ] # South-bound env.process(car_generator(env, traffic_light_list, 'south')) # North-bound env.process(car_generator(env, traffic_light_list[::-1], 'north')) # Call this with initial actions time_range = 4 return self.step((np.random.rand(self.n_agents, 1) * time_range + self.min_stop_time).tolist())[0]
def __init__(self, env, simpy_env, id_num, reward, prob_profile, location): self.env = env self.simpy_env = simpy_env self.id_num = id_num self.reward = reward # on extinguishing self.location = location assert len(prob_profile) == NUM_AGENTS, 'Number of extinguish probabilities is not the number of agents' self.prob_profile = prob_profile # Specifies distribution params for extinguish times for number of agents trying self.status = True self.extinguish_event = simpy.Event(self.simpy_env) # Gets triggered when the fire is extinguished self.extinguish_party = [] # Number of agents trying to extinguish the fire self.time_until_extinguish = np.inf
def order_book(self, type_id): """ Provide an event which is triggered when the next book snapshot is available for the given type_id. :param type_id: the type_id to wait for :return: an Event which is triggered when the book is available (the value will be the requested book) """ if type_id in self.waiting_snaps: return self.waiting_snaps[type_id] book_event = simpy.Event(self.env) self.waiting_snaps[type_id] = book_event return book_event
def snoop(self, tag): """Returns a Simpy process that waits until a value with the given tag is written to the CDB. The returned process yields the value written to the CDB.""" events = self.waiting.get(tag, []) event = simpy.Event(self.env) events.append(event) self.waiting[tag] = events def _snoop(): write = yield event return write.value return self.env.process(_snoop())
def step(self, actions): # Takes an action set, outputs next observations, accumulated reward, done (boolean), info # Convention is: # If an agent is to act on this event, pass an observation and accumulated reward, # otherwise, pass None # "obs" variable will look like: [ [None], [None], [o3_t], [None], [o5_t] ] # "rewards" will look like: [ None , None , r3_r , None , r5_t ] # The action returned by the (decentralized) policy will look like # [ None , None , a3_t , None , a5_t ] for i, a in enumerate(actions): if a is not None: # need to bound action action = max(min(a[0], MAX_STOP_TIME), MIN_STOP_TIME) event = simpy.Event(self.simpy_env) self.agent_event_list[i] = event self.simpy_env.process(self.env_agents[i].change_traffic( event, action)) self.simpy_env.run( until=simpy.AnyOf(self.simpy_env, self.agent_event_list + [self.max_simtime_event])) whotriggered = who_triggered(self.agent_event_list) # Get next_obs, rewards # Check if max_simtime_reached try: self.max_simtime_event.ok done = True obs = [e.get_obs() for e in self.env_agents] rewards = [e.get_reward() for e in self.env_agents] except (AttributeError): done = False obs = [ self.env_agents[i].get_obs() if w else [None] for i, w in enumerate(whotriggered) ] rewards = [ self.env_agents[i].get_reward() if w else None for i, w in enumerate(whotriggered) ] return obs, rewards, done, {}
def getIn(self): """Erzeugt ein neues Event für den Kunden und gibt es zurück.""" minor = 2**31 for res in self.waitsFor: minor = min(minor, len(self.waitsFor[res])) #kleinste Warteschlange gefunden for res in self.waitsFor: if len(self.waitsFor[res]) == 0 or len( self.waitsFor[res]) <= minor: event = simpy.Event(self.env) self.waitsFor[res].append(event) if res.count == 0 and len(self.waitsFor[res]) == 1: event.succeed() return event #Angestellt. raise RuntimeError( "There had to be a list which has the shortest length!")
def waits(self, something, **kwargs): """ Make user wait for a specific time window to pass or wait for all resources in a list to be available Args: something (int/string/:class:`.Resource`): delay for timeout or list of resources to request Keyword Args: patience (float/:class:`datetime.timedelta`): maximum time user waits for obtaining the resources which (string): `all` or `any` resources in the list having (dict): properties that should match a specific value in the resources """ patience = kwargs.get('patience', 'unlimited') which = kwargs.get('which', 'all') having = kwargs.get('having', None) numbers = (int, float, np.int64, np.float64, datetime, timedelta) # Patience if isinstance(patience, numbers): waits_patience = self.env.timeout(parse_time(patience)) elif patience == 'unlimited': # Creating an event that never will be triggered, aiming to make # waits_patience neutral waits_patience = simpy.Event(self.env) else: raise ValueError('Patience must be a number or datetime object') # Timeout if isinstance(something, numbers): waits_time_or_resources = self.env.all_of( [self.env.timeout(parse_time(something))]) # Resources elif isinstance(something, (list, str, Resource)): requests = self.requests(something, which=which, having=having) if which == 'all': waits_time_or_resources = self.env.all_of(requests) elif which == 'any': waits_time_or_resources = self.env.any_of(requests) else: raise ValueError('Users can only wait for time or resources') return waits_time_or_resources | waits_patience
def __init__(self, env, oms, order_id, type_id, issue_time, duration, buy, price, volume, min_volume, broker_rate): self.order_id = order_id self.type_id = type_id self.issue_time = self.original_issue_time = issue_time self.duration = duration self.buy = buy self.price = price self.volume = volume self.volume_remaining = volume self.min_volume = min_volume self.status = MMOrderStatus.OPEN self.status_event = simpy.Event(env) self.env = env self.oms = oms self.broker_fees = price * volume * broker_rate self.sales_tax = 0 self.gross = 0
def reset(self): # This is a dummy reset just so agent obs/action spaces can be accessed self.done = False self.simpy_env = simpy.Environment() self.fire_extinguish_events = [ simpy.Event(self.simpy_env) for i in range(self.n_fires) ] fire_levels = [1] * self.n_fires # we want to randomize fire_locations = (2. * np.random.random_sample( (self.n_fires, 2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(fire_locations) ] if self.start_positions is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, None) for i, sp in enumerate(self.start_positions) ] else: # we want to randomize start_positions = (2. * np.random.random_sample( (self.n_agents, 2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, None) for i, sp in enumerate(start_positions) ] return
def memory_access_complete(self, rs): if rs not in self._accesses_in_execution.items: raise Exception("Something's wrong, please report this event as a bug") ev, self._accesses_in_execution_pop_event = self._accesses_in_execution_pop_event, simpy.Event(self.env) yield self._accesses_in_execution.get(lambda x: x == rs) ev.succeed()
def reset_and_sim(self, policies): self.simpy_env = simpy.Environment() self.done = False self.fire_extinguish_events = [ simpy.Event(self.simpy_env) for i in range(self.n_fires) ] if self.fire_locations is True: # Use presets assert self.num_fire_clusters == 3, 'Only 3 clusters / fires per cluster implemented right now :(' assert self.n_fires / self.num_fire_clusters == 3, 'Only 3 clusters / fires per cluster implemented right now :(' R = np.array([[np.cos(120 * deg), np.sin(-120 * deg)], [np.sin(120 * deg), np.cos(120 * deg)]]) f1 = np.reshape(np.array([-0.01, 1]), (2, 1)) f2 = np.reshape(np.array([0.01, 1]), (2, 1)) f3 = np.reshape(np.array([0, 1 - 0.02 * math.sin(60 * deg)]), (2, 1)) fire_locations = [ f1, f2, f3, R.dot(f1), R.dot(f2), R.dot(f3), R.T.dot(f1), R.T.dot(f2), R.T.dot(f3) ] fire_locations = [ np.reshape(f, (2, )).tolist() for f in fire_locations ] self.fires = [ Fire(self, self.simpy_env, i, 1, fl) for i, fl in enumerate(fire_locations) ] else: raise NotImplementedError # we want to randomize fire_locations = (2. * np.random.random_sample( (self.n_fires, 2)) - 1.).tolist() self.fires = [ Fire(self, self.simpy_env, i, fire_levels[i], fl) for i, fl in enumerate(fire_locations) ] if self.start_positions is not None: self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, policies[i]) for i, sp in enumerate(self.start_positions) ] else: # we want to randomize start_positions = (2. * np.random.random_sample( (self.n_agents, 2)) - 1.).tolist() self.env_agents = [ UAV(self, self.simpy_env, i, sp, sp, self.discount, policies[i]) for i, sp in enumerate(start_positions) ] # Process all UAVs agent_events = [] for uav in self.env_agents: agent_events.append(self.simpy_env.process(uav.sim())) # Process all fires for fire in self.fires: self.simpy_env.process(fire.sim()) self.max_simtime_event = self.simpy_env.timeout(MAX_SIMTIME) self.done_event = simpy.Event(self.simpy_env) self.simpy_env.run( until=simpy.AllOf(self.simpy_env, self.fire_extinguish_events) | self.max_simtime_event) self.done_event.succeed() self.done = True self.simpy_env.run(until=simpy.AllOf(self.simpy_env, agent_events)) rewards = [uav.get_reward() for uav in self.env_agents] if sum(rewards) != 0: print('There were unaccounted for rewards') [print(r) for r in rewards] raise RuntimeError # Collect observations, actions, etc.. and return them observations = [u.observations for u in self.env_agents] actions = [u.actions for u in self.env_agents] rewards = [u.rewards for u in self.env_agents] agent_infos = [u.agent_infos for u in self.env_agents] env_infos = [u.env_infos for u in self.env_agents] offset_t_sojourn = [u.offset_t_sojourn for u in self.env_agents] return observations, actions, rewards, agent_infos, env_infos, offset_t_sojourn
def load_unload(self): while True: event_load = simpy.Event(self.env) if self.selected_lot is not None: # Mask resource request & release self.mask_name = self.mask_shop.mask_map['M' + str(self.id)] with self.mask_shop.masks[ self.mask_name].request() as event_print: print('Now at %.1f,' % self.env.now, 'M%d' % self.id, 'requested', self.mask_name) yield event_print print('Now at %.1f,' % self.env.now, 'M%d' % self.id, 'owned', self.mask_name) current_loc = self.mask_shop.location[self.mask_name] dest_loc = self.mask_shop.machine_library['M' + str(self.id)] # Mask move process if current_loc != dest_loc: event_move = self.env.process( self.move_mask(current_loc, dest_loc)) yield event_move # Lot loading & unloading process self.states.update_load(self.selected_lot) self.states.update_mask( 'load' ) # mask no 추가 필요 / 박사님과 마스크 생각이 다를수 있으니 수정부탁드립니다. # selected_lot -> steptarget idx 변환 작업 필요 or selected lot의 step atrribute 저장 필요 self.states.update_step_target(self.selected_lot) yield event_load.succeed(value=( self.id, 'LOAD')) & self.machine.put(self.selected_lot) print('Now at %.1f,' % self.env.now, 'M%d loaded a lot :' % self.id, self.selected_lot) # Lot unloading & training_process event_unload = self.env.timeout(self.process_time, value=(self.id, 'UNLOAD')) yield event_unload unloading_lot = yield self.machine.get( ) # get() event의 items로 assign print('Now at %.1f,' % self.env.now, 'M%d unloaded a lot :' % self.id, unloading_lot) self.states.update_mask('unload') # Agent training print('Now at %.1f,' % self.env.now, 'M%d start training :' % self.id) action, next_state = self.agent.train(self.env, self.id, self.states, self.mask_shop, epsilon=1) reward = self.states.get_reward(action) # Mask release print('Now at %.1f,' % self.env.now, 'M%d' % self.id, 'released', self.mask_name) self.states.update_mask('release')
def main(): # Create a repeatable backlog of cases that have binomially distributed sizes and normally distributed values logging.info('Creating standard cases') np.random.seed(RANDOM_SEED) standard_cases = [] sizes = np.random.gamma(10, size=NUM_SOURCE_CASES).astype(np.int64) values = (np.random.normal(0, 1, size=NUM_SOURCE_CASES)) * 10 for idx, size in enumerate(sizes): standard_cases.append( Case(size=max(size, 1), value=values[idx], name='%d' % idx, workflow=StandardWorkflowFactory.make_workflow( dev_size=size))) # value = size # Do multiple executions of this model where behaviour of models varies runs = [] releases = [] current_release = None # a global resource, can only be one release active at a time for run in tqdm(range(NUM_RUNS)): logging.debug('---- Run %s -----' % run) # Create environment and start processes analysis_size = [] dev_size = [] review_size = [] dev_put_queue_size = [] qa_size = [] done_size = [] release_size = [] merge_size = [] cycle_times = [] total_values = [] # Choose a random strategy for each run dev_strategy = Developer.random_strategy() ba_strategy = BA.random_strategy() qa_strategy = QA.random_strategy() dev_review_choice_strategy = np.random.randint(3) env = simpy.Environment() # The dispatch pile is just a list, as it gets processed entirely each day dispatch_pile = [] # Create standard piles source_pile = StoreWithHistory(env, len(sizes)) done_pile = StoreWithHistory(env) released_pile = StoreWithHistory(env) progress_pile = StoreWithHistory(env) # Create named piles for specific work types, i.e., work that needs to be done to get the case to the Done pile work_piles = { wt.WORK_ANALYSIS: StoreWithHistory(env, len(sizes)), wt.WORK_QA: StoreWithHistory(env, MAX_QA_PILE), wt.WORK_DEV: StoreWithHistory(env, MAX_DEV_PILE), wt.WORK_REVIEW: FilterStoreWithHistory(env, MAX_REVIEW_PILE), wt.WORK_MERGE: FilterStoreWithHistory(env) } # Create a Run to store params sim_run = Run( params={ 'dev_strategy': dev_strategy, 'ba_strategy': ba_strategy, 'dev_review_choice_strategy': dev_review_choice_strategy }) # Create a pile of all remaining source cases for case in standard_cases[NUM_INITIAL_CASES:len(standard_cases)]: _case = copy.deepcopy(case) _case.set_env(env) source_pile.put(_case) # Create a pile of cases to get the system to a steady state. We will start by putting the first workflow step # for each case on the dispatcher's pile and let them allocate work based on the workflow step properties. for case in standard_cases[:NUM_INITIAL_CASES]: _case = copy.deepcopy(case) _case.set_env(env) dispatch_new_case_work(_case, dispatch_pile, progress_pile, work_piles) # Create some developers (who will do both development and reviews) with a random strategy developers = [] for i in range(NUM_DEVELOPERS): developers.append( env.process( Developer('Developer %d' % i, env, strategy=dev_strategy, dev_pile=work_piles[wt.WORK_DEV], review_pile=work_piles[wt.WORK_REVIEW], merge_pile=work_piles[wt.WORK_MERGE]).run())) # Create some QAs qas = [] for i in range(NUM_QA): qas.append( env.process( QA('QA %d' % i, env, qa_strategy, work_piles[wt.WORK_QA], current_release).run())) # Create some BAs bas = [] for i in range(NUM_BA): bas.append( env.process( BA('BA %d' % i, env, ba_strategy, work_piles[wt.WORK_ANALYSIS]).run())) # Create a Releaser releasers = [ env.process( Releaser('Releaser 0', env, current_release, done_pile, releases, released_pile, standard_cases).run()) ] # Create a Dispatcher dispatchers = [ env.process( dispatcher('Dispatcher 0', env, dispatch_pile, work_piles)) ] # Create a case Sourcer sourcers = [ env.process( sourcer('Sourcer 0', env, source_pile, dispatch_pile, progress_pile, work_piles)) ] # add monitoring, which will also terminate the sim on completion of all work finisher = simpy.Event(env) env.process( monitor(env, finisher, progress_pile, total_values, work_piles, done_pile, released_pile, standard_cases)) pub.subscribe(dispatch_work_listener, 'dispatch_work', dispatch_pile=dispatch_pile) pub.subscribe(case_done_listener, 'case_is_done', done_pile=done_pile) pub.subscribe(dispatch_next_work_items_listener, 'dispatch_next_work_items', dispatch_pile=dispatch_pile, work_piles=work_piles) # Execute! env.run(until=finisher) # Store cycle time values for case in released_pile.items: cycle_times.append(case.cycle_time()) # Store stats for this run sim_run.data = { wt.WORK_ANALYSIS: analysis_size, wt.WORK_DEV: dev_size, wt.WORK_REVIEW: review_size, wt.WORK_QA: qa_size, wt.WORK_MERGE: merge_size, 'done': done_size, 'release': release_size, 'cycle_time': cycle_times, 'total_value': total_values } runs.append(sim_run) #sys.exit(0) # Counts in each state for i, data in enumerate([x.run_data(wt.WORK_ANALYSIS) for x in runs]): _ = plt.plot(data, alpha=0.1, color='blue') for i, data in enumerate([x.run_data(wt.WORK_DEV) for x in runs]): _ = plt.plot(data, alpha=0.1, color='red') for i, data in enumerate([x.run_data(wt.WORK_QA) for x in runs]): _ = plt.plot(data, alpha=0.1, color='green') for i, data in enumerate([x.run_data(wt.WORK_REVIEW) for x in runs]): _ = plt.plot(data, alpha=0.1, color='green') for i, data in enumerate([x.run_data('done') for x in runs]): _ = plt.plot(data, alpha=0.1, color='orange') for i, data in enumerate([x.run_data('release') for x in runs]): _ = plt.plot(data, alpha=0.1, color='black') plt.show() # Cycle time ecdf colors = ['red', 'pink', 'green', 'blue', 'orange', 'black'] line_styles = ['-', '--', '-.', ':'] # solid, dash, dash-dot, dot for sim_run in runs: color = colors[sim_run.params['dev_strategy']] _ = plot_ecdf(sim_run.run_data('cycle_time'), color=color) plt.show() # Total value plot for sim_run in runs: color = colors[sim_run.params['dev_strategy']] line_style = line_styles[sim_run.params['ba_strategy']] _ = plt.plot(sim_run.run_data('total_value'), alpha=0.1, color=color, linestyle=line_style) plt.show()
# Create a Releaser releasers = [ env.process( Releaser('Releaser 0', env, current_release, done_pile, releases, released_pile, standard_cases).run()) ] # Create a Dispatcher dispatchers = [env.process(dispatcher('Dispatcher 0', env))] # Create a case Sourcer sourcers = [env.process(sourcer('Sourcer 0', env))] # add monitoring, which will also terminate the sim on completion of all work finisher = simpy.Event(env) env.process(monitor(env, finisher)) # Execute! env.run(until=finisher) # Store cycle time values for case in released_pile.items: cycle_times.append(case.cycle_time()) # Store stats for this run run.data = { wt.WORK_ANALYSIS: analysis_size, wt.WORK_DEV: dev_size, wt.WORK_REVIEW: review_size, wt.WORK_QA: qa_size,