def test_normal_distribution_mean(self): """ Verifies mean and variance for random normal distribution. """ # arrange app_dir = os.path.dirname(__file__, ) rel_path = 'supplychainpy/data.csv' abs_file_path = os.path.abspath(os.path.join(app_dir, '..', rel_path)) # act orders_analysis = model_inventory.analyse_orders_abcxyz_from_file( file_path=abs_file_path, z_value=Decimal(1.28), reorder_cost=Decimal(5000), file_type="csv") sim = monte_carlo.SetupMonteCarlo( analysed_orders=orders_analysis.orders, period_length=1) for sku in orders_analysis.orders: item = sku.orders_summary() if item['sku'] == 'KR202-209': # assertd diff = abs( float(item['average_order']) - float( np.mean(sim.normal_random_distribution[0]['KR202-209'] [0][0]))) self.assertLess(diff, float(item['standard_deviation']))
def test_build_window(self): """Test building simulation window, a transactioin block for the discrete events simulation.""" sim_dict = {} period_length = 12 sim_collection = [] simulation = monte_carlo.SetupMonteCarlo( analysed_orders=self.__orders_analysis) random_demand = simulation.generate_normal_random_distribution( period_length=period_length) for sim_window in simulation.build_window( random_normal_demand=random_demand, period_length=period_length): sim_dict = { "index": "{:.0f}".format(sim_window.index), "period": "{:.0f}".format(sim_window.position), "sku_id": sim_window.sku_id, "opening_stock": "{:.0f}".format(sim_window.opening_stock), "demand": "{:.0f}".format(sim_window.demand), "closing_stock": "{:.0f}".format(sim_window.closing_stock), "delivery": "{:.0f}".format(sim_window.purchase_order_receipt_qty), "backlog": "{:.0f}".format(sim_window.backlog) } sim_collection.append([sim_dict]) self.assertEqual(len(sim_collection), 1)
def test_variance(self): """ Verifies mean and variance for random normal distribution.""" sim = monte_carlo.SetupMonteCarlo(analysed_orders=self.__orders_analysis, period_length=1) for sku in self.__orders_analysis: item = sku.orders_summary() if item['sku'] == 'KR202-209': self.assertLess((float(item['standard_deviation']) - \ float(sim.normal_random_distribution[0]['KR202-209'][0][0])), Decimal(item['standard_deviation']))
def setUp(self): self.__skus = ['KR202-209', 'KR202-210', 'KR202-211'] self.__orders_analysis = analyse_orders_abcxyz_from_file( file_path=ABS_FILE_PATH['COMPLETE_CSV_SM'], z_value=Decimal(1.28), reorder_cost=Decimal(5000), file_type="csv", length=12) self.__sim = monte_carlo.SetupMonteCarlo( analysed_orders=self.__orders_analysis, period_length=10)
def test_build_window(self): app_dir = os.path.dirname(__file__, ) rel_path = 'supplychainpy/data.csv' abs_file_path = os.path.abspath(os.path.join(app_dir, '..', rel_path)) orders_analysis = model_inventory.analyse_orders_abcxyz_from_file( file_path=abs_file_path, z_value=Decimal(1.28), reorder_cost=Decimal(5000), file_type="csv") period_length = 12 sim_collection = [] for k in range(0, 1): simulation = monte_carlo.SetupMonteCarlo( analysed_orders=orders_analysis.orders) random_demand = simulation.generate_normal_random_distribution( period_length=period_length) for sim_window in simulation.build_window( random_normal_demand=random_demand, period_length=period_length): sim_dict = { "index": "{:.0f}".format(sim_window.index), "period": "{:.0f}".format(sim_window.position), "sku_id": sim_window.sku_id, "opening_stock": "{:.0f}".format(sim_window.opening_stock), "demand": "{:.0f}".format(sim_window.demand), "closing_stock": "{:.0f}".format(sim_window.closing_stock), "delivery": "{:.0f}".format(sim_window.purchase_order_receipt_qty), "backlog": "{:.0f}".format(sim_window.backlog) } sim_collection.append([sim_dict]) self.assertEqual(len(sim_collection), 1)
def run_monte_carlo(orders_analysis: list, runs: int, period_length: int = 12) -> list: """Runs monte carlo simulation. Generates random distribution for demand over period specified and creates a simulation window for opening_stock, demand, closing_stock, delivery and backlog for each sku in the data set. Creates a transaction summary window for inventory movements. Args: orders_analysis (list): list of UncertainDemand objects, containing the results of inventory analysis. period_length (int): The number of periods define the simulation window e.g. 12 weeks, months etc. runs (int): The number of times to run the simulation. Returns: list: A list containing the transaction summary for each period. [[{'closing_stock': '0', 'backlog': '240', 'po_quantity': '6009', 'po_raised': 'PO 49', 'revenue': '1145799', 'opening_stock': '1700', 'index': '9', 'period': '1', 'shortage_units': '240.4612', 'po_received': '', 'delivery': '0', 'shortage_cost': '48621', 'demand': '1940', 'quantity_sold': '1699', 'sku_id': 'KR202-217'}] [{'closing_stock': '240', 'backlog': '852', 'po_quantity': '6380', 'po_raised': 'PO 59', 'revenue': '0', 'opening_stock': '0', 'index': '9', 'period': '2', 'shortage_units': '611.5989', 'po_received': '', 'delivery': '0', 'shortage_cost': '123665', 'demand': '612', 'quantity_sold': '0', 'sku_id': 'KR202-217'}] [{'closing_stock': '0', 'backlog': '383', 'po_quantity': '6152', 'po_raised': 'PO 69', 'revenue': '162070', 'opening_stock': '240', 'index': '9', 'period': '3', 'shortage_units': '383.4993', 'po_received': '', 'delivery': '0', 'shortage_cost': '77543', 'demand': '624', 'quantity_sold': '240', 'sku_id': 'KR202-217'}] [{'closing_stock': '4463', 'backlog': '383', 'po_quantity': '1689', 'po_raised': 'PO 79', 'revenue': '2749508', 'opening_stock': '0', 'index': '9', 'period': '4', 'shortage_units': '0.000000', 'po_received': 'PO 49', 'delivery': '6010', 'shortage_cost': '0', 'demand': '1547', 'quantity_sold': '4079', 'sku_id': 'KR202-217'}] [{'closing_stock': '8002', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '5393458', 'opening_stock': '4463', 'index': '9', 'period': '5', 'shortage_units': '0.000000', 'po_received': 'PO 59', 'delivery': '6381', 'shortage_cost': '0', 'demand': '2841', 'quantity_sold': '8002', 'sku_id': 'KR202-217'}] [{'closing_stock': '13594', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '9162457', 'opening_stock': '8002', 'index': '9', 'period': '6', 'shortage_units': '0.000000', 'po_received': 'PO 69', 'delivery': '6153', 'shortage_cost': '0', 'demand': '561', 'quantity_sold': '13594', 'sku_id': 'KR202-217'}] [{'closing_stock': '13144', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '8859056', 'opening_stock': '13594', 'index': '9', 'period': '7', 'shortage_units': '0.000000', 'po_received': 'PO 79', 'delivery': '1690', 'shortage_cost': '0', 'demand': '2140', 'quantity_sold': '13144', 'sku_id': 'KR202-217'}] [{'closing_stock': '11666', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '7862688', 'opening_stock': '13144', 'index': '9', 'period': '8', 'shortage_units': '0.000000', 'po_received': '', 'delivery': '0', 'shortage_cost': '0', 'demand': '1478', 'quantity_sold': '11665', 'sku_id': 'KR202-217'}] [{'closing_stock': '8542', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '5757540', 'opening_stock': '11666', 'index': '9', 'period': '9', 'shortage_units': '0.000000', 'po_received': '', 'delivery': '0', 'shortage_cost': '0', 'demand': '3123', 'quantity_sold': '8542', 'sku_id': 'KR202-217'}] [{'closing_stock': '7832', 'backlog': '0', 'po_quantity': '0', 'po_raised': '', 'revenue': '5278665', 'opening_stock': '8542', 'index': '9', 'period': '10', 'shortage_units': '0.000000', 'po_received': '', 'delivery': '0', 'shortage_cost': '0', 'demand': '710', 'quantity_sold': '7831', 'sku_id': 'KR202-217'}] [{'closing_stock': '5379', 'backlog': '0', 'po_quantity': '389', 'po_raised': 'PO 149', 'revenue': '3625634', 'opening_stock': '7832', 'index': '9', 'period': '11', 'shortage_units': '0.000000', 'po_received': '', 'delivery': '0', 'shortage_cost': '0', 'demand': '2453', 'quantity_sold': '5379', 'sku_id': 'KR202-217'}] [{'closing_stock': '3799', 'backlog': '0', 'po_quantity': '1969', 'po_raised': 'PO 159', 'revenue': '2560610', 'opening_stock': '5379', 'index': '9', 'period': '12', 'shortage_units': '0.000000', 'po_received': '', 'delivery': '0', 'shortage_cost': '0', 'demand': '1580', 'quantity_sold': '3799', 'sku_id': 'KR202-217'}],...] """ Transaction_report = [] # add shortage cost, for k in range(0, runs): simulation = monte_carlo.SetupMonteCarlo( analysed_orders=orders_analysis) random_demand = simulation.generate_normal_random_distribution( period_length=period_length) for sim_window in simulation.build_window( random_normal_demand=random_demand, period_length=period_length): sim_dict = { "index": "{}".format(sim_window.index), "period": "{}".format(sim_window.position), "sku_id": sim_window.sku_id, "opening_stock": "{}".format(round(sim_window.opening_stock)), "demand": "{}".format(round(sim_window.demand)), "closing_stock": "{}".format(round(sim_window.closing_stock)), "delivery": "{}".format(round(sim_window.purchase_order_receipt_qty)), "backlog": "{:.0f}".format(sim_window.backlog), "po_raised": "{}".format(sim_window.po_number_raised), "po_received": "{}".format(sim_window.po_number_received), "po_quantity": "{:.0f}".format(int(sim_window.purchase_order_raised_qty)), "shortage_cost": "{:.0f}".format(Decimal(sim_window.shortage_cost)), "revenue": "{:.0f}".format(sim_window.revenue), "quantity_sold": "{:0.0f}".format(sim_window.sold), "shortage_units": "{:.0f}".format(sim_window.shortage_units) } Transaction_report.append([sim_dict]) return Transaction_report
def random_demand(orders_analysis=None, period_length=None)->list: """""" simulation = monte_carlo.SetupMonteCarlo(analysed_orders=orders_analysis) r_demand = simulation.generate_normal_random_distribution(period_length=period_length) return r_demand