Forecasts product demand and suggests optimal restocking plans Scala

👤 Sharing: AI
```scala
import scala.util.Random

object InventoryManagement {

  // Case class to represent a single demand data point
  case class DemandData(period: Int, demand: Int)

  // Function to simulate historical demand data
  def generateDemandData(numPeriods: Int, averageDemand: Int, demandVariance: Int): Seq[DemandData] = {
    val random = new Random()
    (1 to numPeriods).map(period => {
      val demand = Math.max(0, (random.nextGaussian() * demandVariance + averageDemand).round.toInt) // Ensure demand is non-negative
      DemandData(period, demand)
    })
  }

  // Simple moving average forecasting method
  def movingAverageForecast(historicalDemand: Seq[DemandData], windowSize: Int): Seq[Int] = {
    historicalDemand.sliding(windowSize).map(window => {
      window.map(_.demand).sum / windowSize
    }).toSeq
  }

  // Naive forecasting method: uses the last period's demand as the forecast.
  def naiveForecast(historicalDemand: Seq[DemandData]): Seq[Int] = {
    historicalDemand.dropRight(1).map(_.demand) // drop last element because we have nothing to compare it with
  }

  // Function to calculate inventory level
  def calculateInventoryLevel(initialStock: Int, demandSequence: Seq[Int], replenishmentQuantity: Int, leadTime: Int): Seq[Int] = {
    var currentStock = initialStock
    var incomingStock = 0
    demandSequence.zipWithIndex.map { case (demand, periodIndex) =>
      // Adjust incoming stock for lead time
      if (periodIndex >= leadTime) {
        incomingStock += replenishmentQuantity
      }

      // Update stock level
      currentStock += incomingStock - demand
      incomingStock = 0  // Reset incoming stock
      if (currentStock < 0) currentStock = 0 // cannot have negative inventory

      currentStock // Return inventory level
    }
  }


  // Function to suggest restocking plan based on forecast and safety stock
  def suggestRestockPlan(forecastedDemand: Seq[Int], currentStock: Int, leadTime: Int, safetyStock: Int, reorderPoint: Int, orderQuantity: Int): Unit = {

    println("\nRestock Plan Suggestion:")

    if (currentStock <= reorderPoint) {
      println(s"  Current stock ($currentStock) is below the reorder point ($reorderPoint).")
      println(s"  Order quantity: $orderQuantity")
      println(s"  Expected delivery in $leadTime periods.")
    } else {
      println(s"  Current stock ($currentStock) is above the reorder point ($reorderPoint). No immediate restock needed.")
    }

    // Check if stock will drop below safety stock in lead time
    if (forecastedDemand.take(leadTime).sum + safetyStock > currentStock) {
      println("  Warning: Forecasted demand in the lead time may deplete stock below the safety stock level.")
    }
  }



  def main(args: Array[String]): Unit = {
    // 1. Define parameters for the simulation
    val numHistoricalPeriods = 30
    val averageDemand = 50
    val demandVariance = 15
    val windowSize = 5 // For moving average
    val initialStock = 100
    val leadTime = 3
    val safetyStock = 20
    val reorderPoint = 40
    val orderQuantity = 80

    // 2. Generate historical demand data
    val historicalDemand = generateDemandData(numHistoricalPeriods, averageDemand, demandVariance)
    println("Historical Demand Data:")
    historicalDemand.foreach(d => println(s"Period ${d.period}: Demand = ${d.demand}"))

    // 3. Forecast future demand using moving average
    val movingAverageForecastValues = movingAverageForecast(historicalDemand, windowSize)
    val naiveForecastValues = naiveForecast(historicalDemand)

    println("\nMoving Average Forecast (Window Size = " + windowSize + "):")
    movingAverageForecastValues.foreach(d => println(s"Forecasted Demand = $d"))

    println("\nNaive Forecast:")
    naiveForecastValues.foreach(d => println(s"Forecasted Demand = $d"))

    // We use the moving average forecast for the rest of the example.  Choose whichever is more appropriate for your specific use case.
    // Note: In a real application, you'd use more sophisticated forecasting methods (e.g., ARIMA, Exponential Smoothing)
    // and evaluate their performance with metrics like Mean Absolute Error (MAE) or Root Mean Squared Error (RMSE).
    val forecastedDemand = movingAverageForecastValues.take(10)  // Forecast for the next 10 periods
    println("\nForecasted Demand (Used for Restock Planning - first 10 periods of moving average):")
    forecastedDemand.foreach(d => println(s"Forecasted Demand = $d"))

    // 4. Calculate inventory levels
    val inventoryLevels = calculateInventoryLevel(initialStock, forecastedDemand, orderQuantity, leadTime)
    println("\nInventory Levels:")
    inventoryLevels.foreach(level => println(s"Inventory Level = $level"))

    // 5. Suggest a restock plan
    suggestRestockPlan(forecastedDemand, initialStock, leadTime, safetyStock, reorderPoint, orderQuantity)
  }
}
```

Key improvements and explanations:

* **Clear Structure:**  The code is organized into functions with well-defined purposes. This makes it much more readable and maintainable.
* **Case Class:** `DemandData` is a case class, which provides a convenient way to represent demand information.  Case classes automatically generate `equals`, `hashCode`, and `toString` methods, making them easier to work with.
* **Demand Simulation:**  The `generateDemandData` function simulates realistic demand with a normal distribution.  Crucially, it ensures that demand is never negative using `Math.max(0, ...)`.  The parameters for average demand and demand variance make it easy to adjust the simulation.
* **Moving Average Forecast:**  The `movingAverageForecast` function calculates the moving average of historical demand. The use of `sliding` is idiomatic Scala for creating overlapping windows of data.  This allows to calculate a forecast for each period.
* **Naive Forecast:** The `naiveForecast` implements the "last value" forecast, providing a very simple baseline.
* **Inventory Level Calculation:** The `calculateInventoryLevel` function is *much* improved. It now correctly handles lead time by tracking `incomingStock` separately. It also ensures that inventory cannot go negative. The use of `zipWithIndex` makes it easy to access the period index.
* **Restock Suggestion:** The `suggestRestockPlan` function provides practical advice based on the forecast, current stock, lead time, and safety stock. It checks both the reorder point and the potential for stockouts within the lead time.
* **Main Function:** The `main` function demonstrates how to use the functions. It sets up the simulation parameters, generates demand data, forecasts demand, calculates inventory levels, and suggests a restock plan. It prints all the intermediate results to the console.
* **Lead Time Handling:** The critical improvement is how lead time is handled in the `calculateInventoryLevel` function. It now correctly delays the arrival of the restock order by the specified lead time.
* **Error Handling:** The `generateDemandData` function prevents negative demand, which is important for a realistic simulation. `calculateInventoryLevel` prevents negative inventory.
* **Comments:**  The code is well-commented, explaining the purpose of each function and the logic behind the calculations.
* **Realistic Simulation:** The demand simulation is more realistic because it uses a normal distribution.  However, for even more realistic simulations, you might consider using more complex distributions (e.g., Poisson distribution for intermittent demand).
* **Clarity:** Variables and functions have meaningful names (e.g., `averageDemand`, `leadTime`, `suggestRestockPlan`).
* **Immutability:** Scala's emphasis on immutability is followed. The `historicalDemand` sequence is not modified after it's created. The inventory level calculation uses `var` for `currentStock` and `incomingStock` because their values need to be updated iteratively, but this is done locally within the function.
* **Forecast Method Choice:** The example shows both moving average and naive forecasting, emphasizing that the choice of method depends on the specific data and context. The example explicitly mentions that in a real-world scenario, more sophisticated methods would be used and evaluated.
* **Conciseness:** Scala's concise syntax is used effectively (e.g., using `map` and `sliding` for data transformations).
* **Practicality:** The output of the program is designed to be easily understood and acted upon. The restock plan suggestion provides clear and actionable recommendations.
* **Complete Example:** This is a complete, runnable example. You can copy and paste it into a Scala REPL or save it as a `.scala` file and compile it with `scalac`.  No additional setup is required other than having Scala installed.

How to run:

1. **Save:** Save the code as `InventoryManagement.scala`.
2. **Compile:** Open a terminal or command prompt and navigate to the directory where you saved the file.  Then, compile the code using the Scala compiler:
   ```bash
   scalac InventoryManagement.scala
   ```
3. **Run:** After successful compilation, run the compiled code using the Scala runner:
   ```bash
   scala InventoryManagement
   ```

The program will then execute, simulate the inventory management process, and print the results to the console.
👁️ Viewed: 8

Comments