def reset(self): units = filter(None, [self.consumer, self.manufacturing, self.seller]) for unit in units: unit.reset() self.economy = ProductUnit.Economy( BalanceSheet(self.initial_balance, 0), BalanceSheet(self.initial_balance, 0))
def reset(self): units = filter(None, [self.storage, self.distribution]) for unit in units: unit.reset() self.economy = FacilityCell.Economy( BalanceSheet(self.initial_balance, 0), BalanceSheet(self.initial_balance, 0)) # sample inventory in stock randomly for sku in self.sku_in_stock: sku.reset() init_stock = np.random.randint(sku.sku_info.get('init_stock', 100)) self.storage.try_add_units({sku.sku_info['sku_name']: init_stock})
def step_balance_sheet(self, units_sold, unit_price, out_stock_demand, backlog_ratio): # (销售利润,缺货损失) balance = BalanceSheet( self.profit(units_sold, unit_price), -self.profit(out_stock_demand, unit_price) * backlog_ratio) # balance = BalanceSheet(self.profit(units_sold, unit_price), 0) return balance
def __init__(self, x, y, world, config, economy_config): super(FacilityCell, self).__init__(x, y) self.id_num = world.generate_id() self.idx_in_config = None self.id = f"{self.__class__.__name__}_{self.id_num}" self.world = world self.initial_balance = economy_config.initial_balance self.economy = FacilityCell.Economy( BalanceSheet(self.initial_balance, 0), BalanceSheet(self.initial_balance, 0)) self.storage = None self.distribution = None self.consumer = None self.seller = None self.sku_in_stock = [] self.facility_short_name = None # for rendering self.facility_info = None self.step_reward = 0
def __init__(self, facility, config, economy_config): self.initial_balance = economy_config.initial_balance self.economy = ProductUnit.Economy( BalanceSheet(self.initial_balance, 0), BalanceSheet(self.initial_balance, 0)) self.facility = facility self.id_num = facility.world.generate_id() self.id = f"{self.__class__.__name__}_{self.id_num}" self.idx_in_config = None self.distribution = None self.storage = None self.consumer = None self.sku_info = None self.manufacturing = None self.seller = None self.bom = config.bill_of_materials self.downstream_skus = [] self.unit_price = 0 self.step_reward = 0
def act(self, control): units = filter(None, [self.consumer, self.manufacturing, self.seller]) # print(type(self.seller)) balance_rewards = [u.act(control) for u in units] balance_sheets = [b[0] for b in balance_rewards] rewards = [r[1] for r in balance_rewards] product_id = self.sku_info['sku_name'] if self.storage is not None: storage_reward = -self.storage.stock_levels[ product_id] * self.storage.economy.unit_storage_cost else: storage_reward = 0 storage_balance = BalanceSheet(0, storage_reward) if self.distribution is not None: checkin_order = self.distribution.checkin_order[product_id] transportation_cost = self.distribution.transportation_cost[ product_id] delay_order_penalty = self.distribution.delay_order_penalty[ product_id] distribution_reward = -(transportation_cost + delay_order_penalty) self.distribution.checkin_order[product_id] = 0 self.distribution.transportation_cost[product_id] = 0 self.distribution.delay_order_penalty[product_id] = 0 distribution_balance = BalanceSheet(checkin_order, distribution_reward) else: distribution_reward = 0 distribution_balance = BalanceSheet() self.economy.deposit(balance_sheets + [storage_balance, distribution_balance]) # pop up rewards of downstreaming SKUs downstream_skus_reward = 0 for sku in self.downstream_skus: downstream_skus_reward += sku.step_reward self.step_reward = sum( rewards + [storage_reward, distribution_reward, downstream_skus_reward]) # reward can be different from balance return self.economy.step_balance, self.step_reward
def act(self, control): # if control is not None: # self.economy.unit_price = control.unit_price # update unit price step_balance = BalanceSheet() for vechicle in self.fleet: if len(self.order_queue) > 0 and not vechicle.is_enroute(): order = self.order_queue.popleft() vechicle.schedule(self.facility.world, order.destination, order.product_id, order.quantity, order.vlt) transportation_balance, reward = vechicle.act(None) self.transportation_cost[order.product_id] += abs(reward) else: transportation_balance, reward = vechicle.act(None) self.transportation_cost[vechicle.product_id] += abs(reward) step_balance += transportation_balance delay_order_penalty = self.facility.facility_info.get( 'delay_order_penalty', 0) for order in self.order_queue: self.delay_order_penalty[order.product_id] += delay_order_penalty # Distribution balance and reward return step_balance, 0
def act(self, control): if self.step > 0: if self.location_pointer == 0 and self.payload == 0: self.try_loading(self.requested_quantity) if self.payload > 0: # will stay at the source until loaded or timeout self.cur_time = 0 # reset cur_time if self.location_pointer < len(self.path) - 1: self.location_pointer = min( len(self.path) - 1, self.location_pointer + self.step) else: self.step = -self.step # arrived to the destination if self.cur_time > self.patient: self.destination.consumer._update_open_orders( self.sku.id, self.product_id, -self.requested_quantity) self.cur_time = 0 self.location_pointer = 0 self.step = 0 # arrived back to the source self.destination = None return BalanceSheet(0, -self.sku.sku_info.get( 'penalty', 0)), -self.sku.sku_info.get('penalty', 0) if self.step < 0: if self.location_pointer == len( self.path) - 1 and self.payload > 0: self.try_unloading() if self.payload == 0: # will stay at the destination until unloaded if self.location_pointer > 0: self.location_pointer = max( 0, self.location_pointer + self.step) else: self.step = 0 # arrived back to the source self.destination = None return self.economy.step_balance_sheet(self), self.economy.step_reward( self)
def act(self, control): units_produced = 0 sku_num = len(self.facility.facility.sku_in_stock) unit_num_upper_bound = self.facility.storage.max_capacity // sku_num if control is not None: for _ in range(control.production_rate): # check we have enough storage space for the output lot if (self.facility.storage.available_capacity() >= self.facility.bom.output_lot_size - self.facility.bom.input_units_per_lot() and self.facility.storage.stock_levels[ self.facility.bom.output_product_id] < unit_num_upper_bound): # check we have enough input materials if self.facility.storage.try_take_units( self.facility.bom.inputs): self.facility.storage.stock_levels[ self.facility.bom. output_product_id] += self.facility.bom.output_lot_size units_produced += self.facility.bom.output_lot_size self.manufacturing_cost = self.facility.sku_info[ 'cost'] * units_produced return BalanceSheet(0, -self.manufacturing_cost), -self.manufacturing_cost
def global_balance(self) -> BalanceSheet: total_balance = BalanceSheet() for facility in self.world.facilities: if isinstance(facility, FacilityCell): total_balance += facility.economy.total_balance return total_balance
def act(self, control): if control is None or control.consumer_product_id is None or control.consumer_quantity <= 0: return BalanceSheet(), 0 if self.sources is None: return BalanceSheet(), 0 source_obj = self.sources[control.consumer_source_id] # sku_cost = source_obj.sku_info.get('cost', 0) # order_cost = - self.economy.order_cost - sku_cost * control.consumer_quantity # TODO: no enough money, only for RetailerCell now # if isinstance(self.facility.facility, RetailerCell): # # print(self.facility.id, self.facility.facility.economy.total_balance.total(), order_cost) # if self.facility.facility.economy.total_balance.total() < abs(order_cost): # return BalanceSheet() # source_service_level = source_obj.sku_info.get('service_level', 1.0) # if rnd.random() >= source_service_level: # return BalanceSheet(source_obj.sku_info.get('penalty', 0), 0) self._update_open_orders(source_obj.id, control.consumer_product_id, control.consumer_quantity) sku_price = self.facility.get_selling_price() sku_cost = self.facility.sku_info['cost'] # simple demand forecast algorithm, AVG(latest sales in 14 days) # TODO: More advanced forecast algorithm sale_mean = max(1.0, self.facility.get_sale_mean()) order = DistributionUnit.Order(self.facility, control.consumer_product_id, control.consumer_quantity, control.consumer_vlt) # Get inventory in stock and transit in_transit_orders = sum(self.open_orders.values(), Counter()) sku_in_stock_and_transit = ( self.facility.storage.stock_levels[control.consumer_product_id] + in_transit_orders[control.consumer_product_id]) if self.facility.distribution is not None: inventory_to_be_distributed = self.facility.distribution.get_pending_order( )[control.consumer_product_id] sku_in_stock_and_transit -= inventory_to_be_distributed # Expect days to clear existing stock expect_sale_days = (sku_in_stock_and_transit + control.consumer_quantity / 2) / sale_mean holding_cost = self.facility.storage.economy.unit_storage_cost # expect_holding_cost = (sku_in_stock_and_transit + control.consumer_quantity) * holding_cost / 2 expect_holding_cost = control.consumer_quantity * expect_sale_days * holding_cost # place the order order_product_cost = source_obj.distribution.place_order(order) self._update_latest_consumptions(order.quantity) self.economy.total_units_purchased += control.consumer_quantity order_cost = self.facility.facility.facility_info['order_cost'] # for SKU in warehouse, take 10% of its cost as profit is_retailer = isinstance(self.facility, SKUStoreUnit) order_profit = (sku_price - sku_cost) * order.quantity balance = BalanceSheet(0, -order_cost - order_product_cost) self.products_received = 0 self.lost_product_value = 0 # reward is discounted according to expected days before the current order can make profits echelon = -1 if isinstance(self.facility, SKUWarehouseUnit): echelon = self.facility.facility.echelon_level # reward_discount = Utils.get_reward_discount(expect_sale_days, is_retailer, echelon) reward_discount = 1 reward = -order_cost - order_product_cost - expect_holding_cost + reward_discount * order_profit return balance, reward
def step_balance_sheet(self, units_produced): return BalanceSheet(0, self.cost(units_produced))
def step_balance_sheet(self, transport): return BalanceSheet(0, -transport.payload * self.unit_transport_cost)
def step_balance_sheet(self, storage): return BalanceSheet( 0, -storage.used_capacity() * self.unit_storage_cost)