Base-Stock Controller
=====================

For single-sourcing problems over an infinite time horizon, the base-stock controller maintains a fixed target inventory level. When inventory falls below this level due to demand fluctuations, a replenishment order is immediately placed to restore the stock to the target level.

Key Concepts
------------

- **Target Inventory Level:** The target inventory level that triggers replenishment orders whenever stock dips below it.

Mathematical Structure
----------------------

To mathematically describe the optimal order policy of single-sourcing problems (Arrow, Harris, & Marschak, 1951; Scarf & Karlin, 1958), we use :math:`l` and :math:`z^*` to respectively denote the replenishment lead time and the target inventory-position level (i.e., the target net inventory level plus all items ordered but not received yet). The inventory position of single-sourcing dynamics at time :math:`t`, :math:`\tilde{I}_t`, is given by

.. math::

   \tilde{I}_t =
   \begin{cases}
      I_t & \text{if} \,\, l=0 \\
      I_t + \sum_{i=1}^l q_{t-i} & \text{if} \,\, l>0 \,,
   \end{cases}

where :math:`I_t` and :math:`q_t` denote the net inventory at time :math:`t` and the replenishment order placed at time :math:`t`, respectively. 

The optimal order quantity is :math:`q_t=\max\{0, z^*-\tilde{I}_t\}`, where the target level :math:`z^*` is the parameter to be determined.

Example Usage
-------------

We now present two examples to demonstrate how the :class:`BaseStockController` can be called and evaluated in `idinn`.

.. doctest::

   >>> import torch
   >>> from idinn.sourcing_model import SingleSourcingModel
   >>> from idinn.single_controller import BaseStockController
   >>> from idinn.demand import UniformDemand

   >>> # First example
   >>> single_sourcing_model = SingleSourcingModel(
   ...     lead_time=0,
   ...     holding_cost=5,
   ...     shortage_cost=495,
   ...     init_inventory=10,
   ...     demand_generator=UniformDemand(low=0, high=4),
   ... )
   >>> controller_base = BaseStockController()
   >>> # z_star should be 4
   >>> controller_base.fit(single_sourcing_model)
   >>> print(f"z_star: {controller_base.z_star}")
   z_star: 4
   >>> # Avg. cost near 10
   >>> avg_cost = controller_base.get_average_cost(single_sourcing_model, sourcing_periods=1000, seed=42).mean()
   >>> print(f"Average Cost: {avg_cost:.2f}")
   Average Cost: 10.39

   >>> # Second example
   >>> single_sourcing_model = SingleSourcingModel(
   ...     lead_time=2,
   ...     holding_cost=5,
   ...     shortage_cost=495,
   ...     init_inventory=10,
   ...     demand_generator=UniformDemand(low=0, high=4),
   ... )
   >>> controller_base = BaseStockController()
   >>> controller_base.fit(single_sourcing_model)
   >>> # z_star should be 11
   >>> print(f"z_star: {controller_base.z_star}")
   z_star: 11
   >>> # Avg. cost near 29
   >>> avg_cost = controller_base.get_average_cost(single_sourcing_model, sourcing_periods=1000, seed=42).mean()
   >>> print(f"Average Cost: {avg_cost:.2f}")
   Average Cost: 29.53

References
----------
- Scarf, H., & Karlin, S. (1958). Inventory models of the Arrow-Harris-Marschak type with time lag. In K. J. Arrow, S. Karlin, & H. E. Scarf (Eds.), *Studies in the Mathematical Theory of Inventory and Production* (Stanford University Press, Stanford, CA).
- Arrow, K. J., Harris, T., & Marschak, J. (1951). Optimal inventory policy. *Econometrica*, 19(3), 250–272.
