Commit 25a61943 authored by Barthelet Thibault's avatar Barthelet Thibault
Browse files

archived

parent 968585cc
Loading
Loading
Loading
Loading
+0 −234
Original line number Diff line number Diff line
#!/usr/bin/env python3
"""
Demo script showing how to use the prediction phase in the supply chain simulation.

This demonstrates both scenarios:
1. Basic scenario - normal operations
2. Unplanned problem scenario - 50% demand drop in months 3-4
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from supply_chain_sim.simulation import run_simulation_with_student_predictions
from supply_chain_sim.synthetic_setup import SyntheticEntitySetup

def demo_basic_scenario():
    """Demonstrate the basic scenario with good predictions."""
    
    print("\n" + "="*70)
    print(" DEMO 1: BASIC SCENARIO WITH GOOD PREDICTIONS ".center(70))
    print("="*70)
    
    # Configuration
    config = {
        'seed': 42,
        'n_brands': 1,
        'n_suppliers': 4,
        'n_components': 8,
        'n_watches': 3,  # 3 products to predict
        'n_warehouses': 1,
        'n_retailers': 4,
        'n_customers': 200,
        'n_months': 6,  # 6 months simulation
        'scenario': 'basic'  # Normal operations
    }
    
    # First, let's see what products we have
    print("\n[SETUP] Generating products...")
    setup = SyntheticEntitySetup(seed=config['seed'])
    setup.generate_all(config)
    
    print("\nProducts in simulation:")
    for watch in setup.watches:
        print(f"  • Watch ID {watch.id}: {watch.name} ({watch.category})")
        print(f"    Cost: CHF {watch.base_cost:.0f}, Price: CHF {watch.sell_price:.0f}")
    
    # Student makes predictions for each product, each month
    print("\n[PREDICTION PHASE] Student enters predictions...")
    print("Student predicts demand for 6 months:\n")
    
    # Example: Good predictions (close to realistic values)
    predictions = {
        0: [3, 4, 4, 3, 5, 4],      # Luxury watch - low volume
        1: [15, 18, 16, 17, 19, 15], # Sport watch - medium volume  
        2: [25, 28, 26, 24, 30, 27]  # Casual watch - high volume
    }
    
    for watch_id, monthly_pred in predictions.items():
        watch = next(w for w in setup.watches if w.id == watch_id)
        print(f"  {watch.name}: {monthly_pred}")
    
    print("\n[SIMULATION] Running simulation with student predictions...")
    print("Supply chain will order components based on these predictions.\n")
    
    # Run simulation
    sim = run_simulation_with_student_predictions(config, predictions)
    
    print("\n" + "="*70)
    print(" DEMO 1 COMPLETE ".center(70))
    print("="*70)
    
    return sim


def demo_unplanned_problem_scenario():
    """Demonstrate the unplanned problem scenario with a sudden demand drop."""
    
    print("\n\n" + "="*70)
    print(" DEMO 2: UNPLANNED PROBLEM SCENARIO ".center(70))
    print("="*70)
    print("\n⚠️  In this scenario, demand drops 50% in months 3-4 (unexpected!)")
    print("The student's predictions won't account for this.\n")
    
    # Configuration with unplanned problem
    config = {
        'seed': 42,
        'n_brands': 1,
        'n_suppliers': 4,
        'n_components': 8,
        'n_watches': 3,
        'n_warehouses': 1,
        'n_retailers': 4,
        'n_customers': 200,
        'n_months': 6,
        'scenario': 'unplanned_problem'  # Problem scenario
    }
    
    print("[PREDICTION PHASE] Student enters predictions (unaware of problem)...")
    print("Student predicts stable demand:\n")
    
    # Student predicts stable demand, but doesn't know about the problem
    predictions = {
        0: [3, 4, 4, 4, 5, 4],       # Predicts steady demand
        1: [15, 18, 16, 16, 19, 15], # Predicts steady demand
        2: [25, 28, 26, 26, 30, 27]  # Predicts steady demand
    }
    
    setup = SyntheticEntitySetup(seed=config['seed'])
    setup.generate_all(config)
    
    for watch_id, monthly_pred in predictions.items():
        watch = next(w for w in setup.watches if w.id == watch_id)
        print(f"  {watch.name}: {monthly_pred}")
    
    print("\n[SIMULATION] Running simulation...")
    print("⚠️  Actual demand will drop 50% in months 3-4!")
    print("This will cause overstock since predictions were too high.\n")
    
    # Run simulation
    sim = run_simulation_with_student_predictions(config, predictions)
    
    print("\n" + "="*70)
    print(" DEMO 2 COMPLETE ".center(70))
    print("="*70)
    
    print("\nNOTE: Compare the prediction reports between both scenarios.")
    print("Scenario 2 should show larger errors and overstock in months 3-4.")
    
    return sim


def demo_bad_predictions():
    """Demonstrate what happens with poor predictions."""
    
    print("\n\n" + "="*70)
    print(" DEMO 3: POOR PREDICTIONS (LEARNING OPPORTUNITY) ".center(70))
    print("="*70)
    print("\nStudent makes bad predictions to see the impact.\n")
    
    config = {
        'seed': 42,
        'n_brands': 1,
        'n_suppliers': 4,
        'n_components': 8,
        'n_watches': 3,
        'n_warehouses': 1,
        'n_retailers': 4,
        'n_customers': 200,
        'n_months': 6,
        'scenario': 'basic'
    }
    
    print("[PREDICTION PHASE] Student enters predictions (intentionally poor)...")
    
    # Deliberately bad predictions - way too high
    predictions = {
        0: [50, 50, 50, 50, 50, 50],  # Luxury: predicting 50, reality ~3-5
        1: [100, 100, 100, 100, 100, 100],  # Sport: predicting 100, reality ~15-20
        2: [200, 200, 200, 200, 200, 200]   # Casual: predicting 200, reality ~25-30
    }
    
    setup = SyntheticEntitySetup(seed=config['seed'])
    setup.generate_all(config)
    
    for watch_id, monthly_pred in predictions.items():
        watch = next(w for w in setup.watches if w.id == watch_id)
        print(f"  {watch.name}: {monthly_pred}")
    
    print("\n[SIMULATION] Running simulation...")
    print("Expected outcome: Massive overstock, high costs, poor GMROI\n")
    
    sim = run_simulation_with_student_predictions(config, predictions)
    
    print("\n" + "="*70)
    print(" DEMO 3 COMPLETE ".center(70))
    print("="*70)
    
    print("\nLEARNING POINT: Overpredicting leads to:")
    print("  • Excessive component orders")
    print("  • High inventory holding costs")
    print("  • Poor GMROI (return on inventory investment)")
    print("  • Wasted capital")
    
    return sim


def main():
    """Run all demos."""
    
    print("\n" + "="*70)
    print(" SUPPLY CHAIN SIMULATION WITH PREDICTION PHASE ".center(70))
    print(" EDUCATIONAL DEMO ".center(70))
    print("="*70)
    
    print("\nThis demo shows three scenarios:")
    print("  1. Basic scenario with good predictions")
    print("  2. Unplanned problem scenario (50% demand drop)")
    print("  3. Poor predictions (learning opportunity)")
    
    print("\nPress Enter to start Demo 1...")
    input()
    
    # Demo 1: Good predictions
    demo_basic_scenario()
    
    print("\n\nPress Enter to start Demo 2...")
    input()
    
    # Demo 2: Unplanned problem
    demo_unplanned_problem_scenario()
    
    print("\n\nPress Enter to start Demo 3...")
    input()
    
    # Demo 3: Bad predictions
    demo_bad_predictions()
    
    print("\n\n" + "="*70)
    print(" ALL DEMOS COMPLETE ".center(70))
    print("="*70)
    
    print("\nKEY TAKEAWAYS:")
    print("  ✓ Prediction accuracy directly impacts supply chain performance")
    print("  ✓ Overpredicting causes overstock and waste")
    print("  ✓ Underpredicting causes stockouts and lost sales")
    print("  ✓ Unplanned problems require adaptability")
    print("  ✓ GMROI measures return on inventory investment")
    
    print("\n" + "="*70)


if __name__ == "__main__":
    main()

HorloML_EDU_Main/web_app/app.py

deleted100644 → 0
+0 −140
Original line number Diff line number Diff line
from flask import Flask, render_template, request, jsonify, session
from flask_session import Session
import sys
import os

# Add the parent directory to path to import supply_chain_sim
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from simulation_engine_new import SimulationEngine

app = Flask(__name__)

# Configure Flask-Session
app.config['SECRET_KEY'] = 'dev-secret-key-supply-chain-2024'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = './flask_session'
app.config['SESSION_PERMANENT'] = False

Session(app)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/configure')
def configure():
    return render_template('configure.html')

@app.route('/setup_prediction', methods=['POST'])
def setup_prediction():
    """Setup the simulation and proceed to prediction phase."""
    try:
        data = request.json
        
        # Configuration
        config = {
            'seed': int(data.get('seed', 42)),
            'n_watches': int(data.get('n_watches', 3)),
            'n_customers': int(data.get('n_customers', 200)),
            'n_retailers': int(data.get('n_retailers', 4)),
            'n_months': int(data.get('n_months', 12)),
            'scenario': data.get('scenario', 'basic'),  # 'basic' or 'unplanned_problem'
            # Additional required parameters
            'n_brands': 1,
            'n_suppliers': 4,
            'n_components': 10,
            'n_warehouses': 1
        }
        
        # Validate
        if not (1 <= config['n_months'] <= 24):
            return jsonify({'success': False, 'error': 'Mois: 1-24'})
        if not (1 <= config['n_watches'] <= 6):
            return jsonify({'success': False, 'error': 'Montres: 1-6'})
        if not (50 <= config['n_customers'] <= 5000):
            return jsonify({'success': False, 'error': 'Clients: 50-5000'})
        if not (2 <= config['n_retailers'] <= 8):
            return jsonify({'success': False, 'error': 'Détaillants: 2-8'})
        
        # Initialize simulation engine
        engine = SimulationEngine(config)
        
        # Get prediction data
        prediction_data = engine.get_prediction_data()
        
        # Store in session
        session['config'] = config
        session['prediction_data'] = prediction_data
        
        return jsonify({'success': True})
    
    except Exception as e:
        import traceback
        print(traceback.format_exc())
        return jsonify({'success': False, 'error': str(e)})

@app.route('/predict')
def predict():
    """Show prediction interface."""
    prediction_data = session.get('prediction_data')
    config = session.get('config')
    
    if not prediction_data or not config:
        return render_template('error.html', 
                             message="Configuration non trouvée. Veuillez recommencer depuis la configuration.")
    
    return render_template('predict.html', 
                          products=prediction_data['products'],
                          historical=prediction_data['historical'],
                          config=config)

@app.route('/run_simulation_with_predictions', methods=['POST'])
def run_simulation_with_predictions():
    """Run simulation with student predictions."""
    try:
        data = request.json
        predictions = data.get('predictions', {})
        
        config = session.get('config')
        
        if not config:
            return jsonify({'success': False, 'error': 'Configuration non trouvée'})
        
        # Convert string keys to integers
        predictions = {int(k): v for k, v in predictions.items()}
        
        # Re-initialize engine and run simulation
        engine = SimulationEngine(config)
        results = engine.run_simulation(predictions)
        
        # Store results in session
        session['results'] = results
        session['predictions'] = predictions
        
        return jsonify({'success': True})
    
    except Exception as e:
        import traceback
        print(traceback.format_exc())
        return jsonify({'success': False, 'error': str(e)})

@app.route('/results')
def results():
    """Show simulation results."""
    results = session.get('results')
    config = session.get('config')
    predictions = session.get('predictions')
    
    if not results:
        return render_template('error.html', 
                             message="Aucune simulation. Lancez-en une d'abord.")
    
    return render_template('results.html', 
                          results=results, 
                          config=config,
                          predictions=predictions)

if __name__ == '__main__':
    os.makedirs('flask_session', exist_ok=True)
    app.run(debug=True, host='0.0.0.0', port=5000)
+0 −3
Original line number Diff line number Diff line
flask
Flask-Session
numpy
 No newline at end of file
+0 −160
Original line number Diff line number Diff line
import numpy as np

class SimulationEngine:
    def __init__(self, config):
        self.config = config
        np.random.seed(config.get('seed', 42))
        self.setup_entities()
        
    def setup_entities(self):
        # 4 components
        self.components = [
            {'id': 0, 'name': 'Bracelet', 'cost': 8.0},
            {'id': 1, 'name': 'Boîtier', 'cost': 20.0},
            {'id': 2, 'name': 'Mouvement', 'cost': 40.0},
            {'id': 3, 'name': 'Verre', 'cost': 15.0}
        ]
        
        # Watches
        n_watches = self.config.get('n_watches', 3)
        categories = ['Luxury', 'Sport', 'Casual']
        self.watches = []
        
        for i in range(n_watches):
            base_cost = sum(c['cost'] for c in self.components) * 1.2
            category = categories[i % len(categories)]
            markup = {'Luxury': 3.5, 'Sport': 2.25, 'Casual': 1.75}[category]
            
            self.watches.append({
                'id': i,
                'name': f'{category}_Watch_{i}',
                'category': category,
                'base_cost': float(base_cost),
                'sell_price': float(base_cost * markup)
            })
        
        # Customers
        n_customers = self.config.get('n_customers', 100)
        regions = ['North', 'South', 'East', 'West']
        self.customers = []
        
        for i in range(n_customers):
            wealth = int(min(10, max(1, np.random.pareto(1.5) + 1)))
            budget = wealth * np.random.uniform(300, 1200)
            if wealth >= 7:
                budget *= np.random.uniform(2, 4)
            
            self.customers.append({
                'id': i,
                'wealth_class': wealth,
                'region': regions[i % 4],
                'budget': float(budget)
            })
        
        # Retailers
        n_retailers = self.config.get('n_retailers', 4)
        self.retailers = [
            {'id': i, 'name': f'Retailer_{i}', 'region': regions[i % 4], 'markup': 1.2}
            for i in range(n_retailers)
        ]
        
        # Inventory
        self.warehouse_stock = {w['id']: 50 for w in self.watches}
        self.component_stock = {c['id']: 100 for c in self.components}
        self.retailer_stock = {r['id']: {w['id']: 20 for w in self.watches} for r in self.retailers}
    
    def run_simulation(self, n_months=12):
        results = {'monthly_data': [], 'sales_by_watch': {}}
        
        for month in range(n_months):
            month_data = self.simulate_month(month)
            results['monthly_data'].append(month_data)
            
            for sale in month_data['sales']:
                wid = sale['watch_id']
                if wid not in results['sales_by_watch']:
                    results['sales_by_watch'][wid] = {'count': 0, 'revenue': 0.0, 'name': sale['watch_name']}
                results['sales_by_watch'][wid]['count'] += 1
                results['sales_by_watch'][wid]['revenue'] += sale['price']
        
        # Calculate summary
        results['summary'] = {
            'total_revenue': sum(m['kpis']['revenue'] for m in results['monthly_data']),
            'total_sales': sum(m['kpis']['sales_count'] for m in results['monthly_data']),
            'total_margin': sum(m['kpis']['gross_margin'] for m in results['monthly_data']),
            'total_costs': sum(m['kpis']['order_costs'] for m in results['monthly_data'])
        }
        
        return results
    
    def simulate_month(self, month):
        month_data = {'month': month + 1, 'sales': [], 'kpis': {}}
        
        # Simple forecasting
        total_demand = sum(np.random.uniform(3, 8) for _ in self.watches for _ in range(4))
        
        # Restock components if needed
        order_costs = 0
        for comp in self.components:
            if self.component_stock[comp['id']] < 50:
                order_qty = 100
                order_costs += comp['cost'] * order_qty
                self.component_stock[comp['id']] += order_qty
        
        # Assembly
        assembled = 0
        for watch in self.watches:
            to_assemble = min(30, *[self.component_stock[c['id']] for c in self.components])
            if to_assemble > 0:
                for comp in self.components:
                    self.component_stock[comp['id']] -= to_assemble
                self.warehouse_stock[watch['id']] += to_assemble
                assembled += to_assemble
        
        # Distribution
        for watch in self.watches:
            stock = self.warehouse_stock[watch['id']]
            if stock > 0:
                per_retailer = stock // len(self.retailers)
                for retailer in self.retailers:
                    qty = min(per_retailer, 10)
                    self.retailer_stock[retailer['id']][watch['id']] += qty
                    self.warehouse_stock[watch['id']] -= qty
        
        # Sales
        shoppers = np.random.choice(len(self.customers), int(len(self.customers) * 0.15), replace=False)
        
        for idx in shoppers:
            customer = self.customers[idx]
            region_retailers = [r for r in self.retailers if r['region'] == customer['region']]
            if not region_retailers:
                continue
            
            retailer = region_retailers[np.random.randint(0, len(region_retailers))]
            
            for watch in self.watches:
                if self.retailer_stock[retailer['id']][watch['id']] > 0:
                    price = watch['sell_price'] * retailer['markup']
                    if price <= customer['budget'] * 1.5 and np.random.random() < 0.4:
                        self.retailer_stock[retailer['id']][watch['id']] -= 1
                        month_data['sales'].append({
                            'watch_id': watch['id'],
                            'watch_name': watch['name'],
                            'price': price
                        })
                        break
        
        # KPIs
        revenue = sum(s['price'] for s in month_data['sales'])
        cogs = sum(next(w['base_cost'] for w in self.watches if w['id'] == s['watch_id']) 
                   for s in month_data['sales'])
        
        month_data['kpis'] = {
            'revenue': revenue,
            'sales_count': len(month_data['sales']),
            'gross_margin': revenue - cogs,
            'order_costs': order_costs,
            'assembled': assembled
        }
        
        return month_data
 No newline at end of file
+0 −220

File deleted.

Preview size limit exceeded, changes collapsed.

Loading