Loading supply_chain_sim/research_logging.py +185 −13 Original line number Diff line number Diff line Loading @@ -128,8 +128,8 @@ class ResearchLogging: return avg_kpis def print_final_report(self): """Print final simulation report.""" def print_final_report(self, simulation_data=None): """Print comprehensive final simulation report.""" print("\n" + "="*60) print("SIMULATION FINAL REPORT") print("="*60) Loading @@ -137,33 +137,205 @@ class ResearchLogging: # Scenario parameters print("\nScenario Parameters:") for key, value in self.scenario_params.items(): if key != 'timestamp': print(f" {key}: {value}") # Show detailed product performance if simulation data provided if simulation_data: self._print_product_performance(simulation_data) self._print_supply_chain_summary(simulation_data) self._print_customer_summary(simulation_data) self._print_inventory_summary(simulation_data) # Latest KPIs if self.kpi_history: latest = self.kpi_history[-1] print("\nFinal KPIs:") print("\n📊 FINAL KPIs:") print(f" GMROI: {latest['gmroi']:.3f}") print(f" Fill Rate: {latest['fill_rate']:.2%}") print(f" Forecast Accuracy: {latest['forecast_accuracy']:.2%}") print(f" Total Revenue: ${latest['total_revenue']:,.0f}") print(f" Total Cost: ${latest['total_cost']:,.0f}") print(f" Total Sales: {latest['total_sales']}") print(f" Forecast MAPE: {latest['mape']:.1f}%") # Summary statistics summary = self.get_summary_statistics() if summary: print("\nAverage Performance:") print("\n📈 AVERAGE PERFORMANCE:") print(f" Avg GMROI: {summary.get('avg_gmroi', 0):.3f}") print(f" Avg Revenue: ${summary.get('avg_total_revenue', 0):,.0f}") print(f" Avg Fill Rate: {summary.get('avg_fill_rate', 0):.2%}") # Baseline comparison comparison = self.compare_against_baselines() if comparison: print("\nVs Baseline:") for metric, data in comparison.items(): symbol = "↑" if data['improved'] else "↓" print(f" {metric}: {data['change_pct']:+.1f}% {symbol}") print("="*60) def _print_product_performance(self, sim_data): """Print detailed product performance metrics.""" print("\n🏷️ PRODUCT PERFORMANCE (Predicted vs Actual Sales):") print("-" * 50) # Aggregate forecasts and sales by watch forecast_by_watch = {} sales_by_watch = {} revenue_by_watch = {} # Aggregate forecasts for f in sim_data.get('all_forecasts', []): if f.watch_id not in forecast_by_watch: forecast_by_watch[f.watch_id] = 0 forecast_by_watch[f.watch_id] += f.forecasted_demand # Aggregate sales for sale in sim_data.get('all_sales', []): if sale.watch_id not in sales_by_watch: sales_by_watch[sale.watch_id] = 0 revenue_by_watch[sale.watch_id] = 0 sales_by_watch[sale.watch_id] += sale.quantity revenue_by_watch[sale.watch_id] += sale.total_price # Get watch details watches = sim_data.get('watches', []) # Print header print(f"{'Watch Name':<20} {'Category':<10} {'Price':<10} {'Predicted':<12} {'Sold':<8} {'Accuracy':<10} {'Revenue':<12}") print("-" * 100) total_predicted = 0 total_sold = 0 total_revenue = 0 for watch in watches: predicted = forecast_by_watch.get(watch.id, 0) sold = sales_by_watch.get(watch.id, 0) revenue = revenue_by_watch.get(watch.id, 0) accuracy = (sold / predicted * 100) if predicted > 0 else 0 print(f"{watch.name:<20} {watch.category:<10} ${watch.base_price:<9.0f} " f"{predicted:<12.0f} {sold:<8} {accuracy:<9.1f}% ${revenue:<11,.0f}") total_predicted += predicted total_sold += sold total_revenue += revenue print("-" * 100) print(f"{'TOTAL':<20} {'':<10} {'':<10} {total_predicted:<12.0f} {total_sold:<8} " f"{(total_sold/total_predicted*100) if total_predicted > 0 else 0:<9.1f}% ${total_revenue:<11,.0f}") def _print_supply_chain_summary(self, sim_data): """Print supply chain metrics.""" print("\n📦 SUPPLY CHAIN SUMMARY:") print("-" * 50) # Component orders orders = sim_data.get('all_orders', []) if orders: print(f"Total component orders placed: {len(orders)}") total_order_cost = sum(o.cost for o in orders) print(f"Total ordering cost: ${total_order_cost:,.0f}") # Group by component by_component = {} for order in orders: if order.component_id not in by_component: by_component[order.component_id] = {'quantity': 0, 'cost': 0} by_component[order.component_id]['quantity'] += order.quantity by_component[order.component_id]['cost'] += order.cost print(f"\nTop 5 ordered components:") sorted_components = sorted(by_component.items(), key=lambda x: x[1]['quantity'], reverse=True)[:5] for comp_id, data in sorted_components: print(f" Component {comp_id}: {data['quantity']} units (${data['cost']:,.0f})") # Assembly assembled = sim_data.get('total_assembled', 0) print(f"\nTotal watches assembled: {assembled}") # Distribution distributions = sim_data.get('distribution_history', []) if distributions: total_distributed = sum(d['quantity'] for d in distributions) print(f"Total watches distributed: {total_distributed}") def _print_customer_summary(self, sim_data): """Print customer behavior summary.""" print("\n👥 CUSTOMER BEHAVIOR:") print("-" * 50) customers = sim_data.get('customers', []) sales = sim_data.get('all_sales', []) print(f"Total customers: {len(customers)}") # Calculate unique buyers unique_buyers = len(set(s.customer_id for s in sales)) print(f"Unique buyers: {unique_buyers} ({unique_buyers/len(customers)*100:.1f}%)") # Sales by wealth class sales_by_wealth = {} for sale in sales: customer = next((c for c in customers if c.id == sale.customer_id), None) if customer: wc = customer.wealth_class if wc not in sales_by_wealth: sales_by_wealth[wc] = {'count': 0, 'revenue': 0} sales_by_wealth[wc]['count'] += 1 sales_by_wealth[wc]['revenue'] += sale.total_price if sales_by_wealth: print("\nSales by wealth class:") for wc in sorted(sales_by_wealth.keys()): data = sales_by_wealth[wc] print(f" Class {wc}: {data['count']} sales (${data['revenue']:,.0f})") # Returns returns = sim_data.get('returns', []) if sales: return_rate = len(returns) / len(sales) * 100 print(f"\nReturn rate: {return_rate:.1f}% ({len(returns)} returns)") def _print_inventory_summary(self, sim_data): """Print inventory status.""" print("\n📊 INVENTORY STATUS:") print("-" * 50) warehouse_inv = sim_data.get('warehouse_inventories', []) retailer_inv = sim_data.get('retailer_inventories', []) # Warehouse inventory warehouse_components = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "component") warehouse_watches = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "watch") print(f"Warehouse inventory:") print(f" Components: {warehouse_components} units") print(f" Watches: {warehouse_watches} units") # Retailer inventory retailer_watches = sum(inv.quantity for inv in retailer_inv) print(f"\nRetailer inventory:") print(f" Watches: {retailer_watches} units") # Inventory value components = sim_data.get('components', []) watches = sim_data.get('watches', []) warehouse_value = 0 for inv in warehouse_inv: if inv.item_type == "component": comp = next((c for c in components if c.id == inv.item_id), None) if comp: warehouse_value += inv.quantity * comp.cost elif inv.item_type == "watch": watch = next((w for w in watches if w.id == inv.item_id), None) if watch: warehouse_value += inv.quantity * watch.base_price retailer_value = 0 for inv in retailer_inv: watch = next((w for w in watches if w.id == inv.watch_id), None) if watch: retailer_value += inv.quantity * watch.base_price print(f"\nInventory value:") print(f" Warehouse: ${warehouse_value:,.0f}") print(f" Retailers: ${retailer_value:,.0f}") print(f" Total: ${warehouse_value + retailer_value:,.0f}") No newline at end of file supply_chain_sim/run_simulation.py +5 −4 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ def main(): # Configure logging if args.debug: logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG, format='%(message)s') else: logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO, format='%(message)s') # Simulation configuration config = { Loading @@ -35,19 +35,20 @@ def main(): 'timestep_days': 1 } print("\SUPPLY CHAIN SIMULATION - WATCH INDUSTRY") print("\nSUPPLY CHAIN SIMULATION - WATCH INDUSTRY") print("=" * 60) print(f"Configuration:") print(f" • Simulation days: {config['simulation_days']}") print(f" • Customers: {config['n_customers']}") print(f" • Retailers: {config['n_retailers']}") print(f" • Watches: {config['n_watches']}") print(f" • Random seed: {config['seed']}") print("=" * 60) # Run simulation simulation = run_oneshot_simulation(config) print("\Simulation complete!") print("\nSimulation complete!") if __name__ == "__main__": main() No newline at end of file supply_chain_sim/simple_run.py 0 → 100644 +52 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 """ Simple runner for the supply chain simulation. Shows detailed product performance and comprehensive metrics. """ import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from supply_chain_sim import run_oneshot_simulation def main(): # Simple configuration - adjust as needed config = { 'seed': 42, # Random seed for reproducibility 'n_brands': 2, # Number of watch brands 'n_suppliers': 4, # Number of component suppliers 'n_components': 10, # Number of different components 'n_watches': 6, # Number of watch models 'n_warehouses': 2, # Number of warehouses 'n_retailers': 4, # Number of retailers 'n_customers': 200, # Number of customers 'simulation_days': 10, # Days to simulate 'timestep_days': 1 # Days per timestep } print("\n" + "🏭 WATCH INDUSTRY SUPPLY CHAIN SIMULATION 🏭".center(60)) print("=" * 60) print("\n📋 Configuration:") print(f" • {config['n_watches']} watch models") print(f" • {config['n_customers']} customers") print(f" • {config['n_retailers']} retailers") print(f" • {config['simulation_days']} simulation days") print("=" * 60) # Run the simulation print("\n🚀 Starting simulation...\n") simulation = run_oneshot_simulation(config) print("\n" + "✨ SIMULATION COMPLETE! ✨".center(60)) print("=" * 60) print("\nThe comprehensive report above shows:") print(" ✓ Product performance (predicted vs actual sales)") print(" ✓ Supply chain metrics (orders, assembly, distribution)") print(" ✓ Customer behavior (purchases by segment, returns)") print(" ✓ Inventory status (warehouse and retailer levels)") print(" ✓ Financial KPIs (GMROI, revenue, costs)") print("\n" + "=" * 60) if __name__ == "__main__": main() No newline at end of file supply_chain_sim/simulation.py +6 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,12 @@ class SupplyChainSimulation: self.warehouse_inventories = [] self.retailer_inventories = [] # Data collection for final report self.all_forecasts = [] self.all_sales = [] self.all_orders = [] self.total_assembled = 0 def _default_config(self): return { 'seed': 42, Loading Loading
supply_chain_sim/research_logging.py +185 −13 Original line number Diff line number Diff line Loading @@ -128,8 +128,8 @@ class ResearchLogging: return avg_kpis def print_final_report(self): """Print final simulation report.""" def print_final_report(self, simulation_data=None): """Print comprehensive final simulation report.""" print("\n" + "="*60) print("SIMULATION FINAL REPORT") print("="*60) Loading @@ -137,33 +137,205 @@ class ResearchLogging: # Scenario parameters print("\nScenario Parameters:") for key, value in self.scenario_params.items(): if key != 'timestamp': print(f" {key}: {value}") # Show detailed product performance if simulation data provided if simulation_data: self._print_product_performance(simulation_data) self._print_supply_chain_summary(simulation_data) self._print_customer_summary(simulation_data) self._print_inventory_summary(simulation_data) # Latest KPIs if self.kpi_history: latest = self.kpi_history[-1] print("\nFinal KPIs:") print("\n📊 FINAL KPIs:") print(f" GMROI: {latest['gmroi']:.3f}") print(f" Fill Rate: {latest['fill_rate']:.2%}") print(f" Forecast Accuracy: {latest['forecast_accuracy']:.2%}") print(f" Total Revenue: ${latest['total_revenue']:,.0f}") print(f" Total Cost: ${latest['total_cost']:,.0f}") print(f" Total Sales: {latest['total_sales']}") print(f" Forecast MAPE: {latest['mape']:.1f}%") # Summary statistics summary = self.get_summary_statistics() if summary: print("\nAverage Performance:") print("\n📈 AVERAGE PERFORMANCE:") print(f" Avg GMROI: {summary.get('avg_gmroi', 0):.3f}") print(f" Avg Revenue: ${summary.get('avg_total_revenue', 0):,.0f}") print(f" Avg Fill Rate: {summary.get('avg_fill_rate', 0):.2%}") # Baseline comparison comparison = self.compare_against_baselines() if comparison: print("\nVs Baseline:") for metric, data in comparison.items(): symbol = "↑" if data['improved'] else "↓" print(f" {metric}: {data['change_pct']:+.1f}% {symbol}") print("="*60) def _print_product_performance(self, sim_data): """Print detailed product performance metrics.""" print("\n🏷️ PRODUCT PERFORMANCE (Predicted vs Actual Sales):") print("-" * 50) # Aggregate forecasts and sales by watch forecast_by_watch = {} sales_by_watch = {} revenue_by_watch = {} # Aggregate forecasts for f in sim_data.get('all_forecasts', []): if f.watch_id not in forecast_by_watch: forecast_by_watch[f.watch_id] = 0 forecast_by_watch[f.watch_id] += f.forecasted_demand # Aggregate sales for sale in sim_data.get('all_sales', []): if sale.watch_id not in sales_by_watch: sales_by_watch[sale.watch_id] = 0 revenue_by_watch[sale.watch_id] = 0 sales_by_watch[sale.watch_id] += sale.quantity revenue_by_watch[sale.watch_id] += sale.total_price # Get watch details watches = sim_data.get('watches', []) # Print header print(f"{'Watch Name':<20} {'Category':<10} {'Price':<10} {'Predicted':<12} {'Sold':<8} {'Accuracy':<10} {'Revenue':<12}") print("-" * 100) total_predicted = 0 total_sold = 0 total_revenue = 0 for watch in watches: predicted = forecast_by_watch.get(watch.id, 0) sold = sales_by_watch.get(watch.id, 0) revenue = revenue_by_watch.get(watch.id, 0) accuracy = (sold / predicted * 100) if predicted > 0 else 0 print(f"{watch.name:<20} {watch.category:<10} ${watch.base_price:<9.0f} " f"{predicted:<12.0f} {sold:<8} {accuracy:<9.1f}% ${revenue:<11,.0f}") total_predicted += predicted total_sold += sold total_revenue += revenue print("-" * 100) print(f"{'TOTAL':<20} {'':<10} {'':<10} {total_predicted:<12.0f} {total_sold:<8} " f"{(total_sold/total_predicted*100) if total_predicted > 0 else 0:<9.1f}% ${total_revenue:<11,.0f}") def _print_supply_chain_summary(self, sim_data): """Print supply chain metrics.""" print("\n📦 SUPPLY CHAIN SUMMARY:") print("-" * 50) # Component orders orders = sim_data.get('all_orders', []) if orders: print(f"Total component orders placed: {len(orders)}") total_order_cost = sum(o.cost for o in orders) print(f"Total ordering cost: ${total_order_cost:,.0f}") # Group by component by_component = {} for order in orders: if order.component_id not in by_component: by_component[order.component_id] = {'quantity': 0, 'cost': 0} by_component[order.component_id]['quantity'] += order.quantity by_component[order.component_id]['cost'] += order.cost print(f"\nTop 5 ordered components:") sorted_components = sorted(by_component.items(), key=lambda x: x[1]['quantity'], reverse=True)[:5] for comp_id, data in sorted_components: print(f" Component {comp_id}: {data['quantity']} units (${data['cost']:,.0f})") # Assembly assembled = sim_data.get('total_assembled', 0) print(f"\nTotal watches assembled: {assembled}") # Distribution distributions = sim_data.get('distribution_history', []) if distributions: total_distributed = sum(d['quantity'] for d in distributions) print(f"Total watches distributed: {total_distributed}") def _print_customer_summary(self, sim_data): """Print customer behavior summary.""" print("\n👥 CUSTOMER BEHAVIOR:") print("-" * 50) customers = sim_data.get('customers', []) sales = sim_data.get('all_sales', []) print(f"Total customers: {len(customers)}") # Calculate unique buyers unique_buyers = len(set(s.customer_id for s in sales)) print(f"Unique buyers: {unique_buyers} ({unique_buyers/len(customers)*100:.1f}%)") # Sales by wealth class sales_by_wealth = {} for sale in sales: customer = next((c for c in customers if c.id == sale.customer_id), None) if customer: wc = customer.wealth_class if wc not in sales_by_wealth: sales_by_wealth[wc] = {'count': 0, 'revenue': 0} sales_by_wealth[wc]['count'] += 1 sales_by_wealth[wc]['revenue'] += sale.total_price if sales_by_wealth: print("\nSales by wealth class:") for wc in sorted(sales_by_wealth.keys()): data = sales_by_wealth[wc] print(f" Class {wc}: {data['count']} sales (${data['revenue']:,.0f})") # Returns returns = sim_data.get('returns', []) if sales: return_rate = len(returns) / len(sales) * 100 print(f"\nReturn rate: {return_rate:.1f}% ({len(returns)} returns)") def _print_inventory_summary(self, sim_data): """Print inventory status.""" print("\n📊 INVENTORY STATUS:") print("-" * 50) warehouse_inv = sim_data.get('warehouse_inventories', []) retailer_inv = sim_data.get('retailer_inventories', []) # Warehouse inventory warehouse_components = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "component") warehouse_watches = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "watch") print(f"Warehouse inventory:") print(f" Components: {warehouse_components} units") print(f" Watches: {warehouse_watches} units") # Retailer inventory retailer_watches = sum(inv.quantity for inv in retailer_inv) print(f"\nRetailer inventory:") print(f" Watches: {retailer_watches} units") # Inventory value components = sim_data.get('components', []) watches = sim_data.get('watches', []) warehouse_value = 0 for inv in warehouse_inv: if inv.item_type == "component": comp = next((c for c in components if c.id == inv.item_id), None) if comp: warehouse_value += inv.quantity * comp.cost elif inv.item_type == "watch": watch = next((w for w in watches if w.id == inv.item_id), None) if watch: warehouse_value += inv.quantity * watch.base_price retailer_value = 0 for inv in retailer_inv: watch = next((w for w in watches if w.id == inv.watch_id), None) if watch: retailer_value += inv.quantity * watch.base_price print(f"\nInventory value:") print(f" Warehouse: ${warehouse_value:,.0f}") print(f" Retailers: ${retailer_value:,.0f}") print(f" Total: ${warehouse_value + retailer_value:,.0f}") No newline at end of file
supply_chain_sim/run_simulation.py +5 −4 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ def main(): # Configure logging if args.debug: logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG, format='%(message)s') else: logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO, format='%(message)s') # Simulation configuration config = { Loading @@ -35,19 +35,20 @@ def main(): 'timestep_days': 1 } print("\SUPPLY CHAIN SIMULATION - WATCH INDUSTRY") print("\nSUPPLY CHAIN SIMULATION - WATCH INDUSTRY") print("=" * 60) print(f"Configuration:") print(f" • Simulation days: {config['simulation_days']}") print(f" • Customers: {config['n_customers']}") print(f" • Retailers: {config['n_retailers']}") print(f" • Watches: {config['n_watches']}") print(f" • Random seed: {config['seed']}") print("=" * 60) # Run simulation simulation = run_oneshot_simulation(config) print("\Simulation complete!") print("\nSimulation complete!") if __name__ == "__main__": main() No newline at end of file
supply_chain_sim/simple_run.py 0 → 100644 +52 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 """ Simple runner for the supply chain simulation. Shows detailed product performance and comprehensive metrics. """ import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from supply_chain_sim import run_oneshot_simulation def main(): # Simple configuration - adjust as needed config = { 'seed': 42, # Random seed for reproducibility 'n_brands': 2, # Number of watch brands 'n_suppliers': 4, # Number of component suppliers 'n_components': 10, # Number of different components 'n_watches': 6, # Number of watch models 'n_warehouses': 2, # Number of warehouses 'n_retailers': 4, # Number of retailers 'n_customers': 200, # Number of customers 'simulation_days': 10, # Days to simulate 'timestep_days': 1 # Days per timestep } print("\n" + "🏭 WATCH INDUSTRY SUPPLY CHAIN SIMULATION 🏭".center(60)) print("=" * 60) print("\n📋 Configuration:") print(f" • {config['n_watches']} watch models") print(f" • {config['n_customers']} customers") print(f" • {config['n_retailers']} retailers") print(f" • {config['simulation_days']} simulation days") print("=" * 60) # Run the simulation print("\n🚀 Starting simulation...\n") simulation = run_oneshot_simulation(config) print("\n" + "✨ SIMULATION COMPLETE! ✨".center(60)) print("=" * 60) print("\nThe comprehensive report above shows:") print(" ✓ Product performance (predicted vs actual sales)") print(" ✓ Supply chain metrics (orders, assembly, distribution)") print(" ✓ Customer behavior (purchases by segment, returns)") print(" ✓ Inventory status (warehouse and retailer levels)") print(" ✓ Financial KPIs (GMROI, revenue, costs)") print("\n" + "=" * 60) if __name__ == "__main__": main() No newline at end of file
supply_chain_sim/simulation.py +6 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,12 @@ class SupplyChainSimulation: self.warehouse_inventories = [] self.retailer_inventories = [] # Data collection for final report self.all_forecasts = [] self.all_sales = [] self.all_orders = [] self.total_assembled = 0 def _default_config(self): return { 'seed': 42, Loading