Simulation engine¶
Run simulations and read their results. Guide: The simulation engine.
SimulationEngine
¶
SimulationEngine(
verbose: int = 0,
*,
sku_column: Optional[str] = None,
date_column: str = "date",
period_column: str = "period",
demand_column: str = "y",
)
Run inventory simulations: the clock and the only place stock changes.
Each period the engine receives due deliveries, lets the policy decide on scheduled periods (then applies callbacks, ordering constraints and the supply model), serves demand, applies inventory processes and after-demand callbacks, and records one checked event table row per SKU. Everything that can be validated is checked before the first period.
Example
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
verbose
|
int
|
0 silent (default), 1 start/end/milestones, 2 one line per period. |
0
|
sku_column
|
Optional[str]
|
SKU column of the demand table; |
None
|
date_column
|
str
|
Date column of the demand table (default |
'date'
|
period_column
|
str
|
Period column of the demand table (default |
'period'
|
demand_column
|
str
|
Demand column of the demand table (default |
'y'
|
A column named other than its default must exist in the demand table, and the table must not also hold the default name. Results and event tables use the default names.
run
¶
run(
policy: BasePolicy,
demand_source: Union[DataFrame, Callable],
inventory: InventoryStateDataFrame,
n_periods: Optional[int] = None,
*,
freq: Optional[str] = None,
warmup_periods: int = 0,
scoring_periods: Optional[int] = None,
settlement_periods: int = 0,
order_during_settlement: Optional[bool] = None,
demand_source_name: Optional[str] = None,
random_seed: Optional[int] = None,
policy_schedule: Optional[
Mapping[int, BasePolicy]
] = None,
order_constraints: Optional[
Union[OrderingConstraints, Sequence]
] = None,
callbacks: Optional[
Sequence[SimulationCallback]
] = None,
supply: Optional[SupplyModel] = None,
processes: Optional[Sequence[InventoryProcess]] = None,
allow_target_reuse: bool = True,
missing_targets: str = "raise",
) -> SimulationResult
Simulate a policy against a demand path.
The engine copies inventory and policy, validates the whole
experiment, then plays n_periods demand periods: receive, decide (on
scheduled periods), meet demand, record. Your input objects are not changed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policy
|
BasePolicy
|
A fitted policy. |
required |
demand_source
|
Union[DataFrame, Callable]
|
A DataFrame with |
required |
inventory
|
InventoryStateDataFrame
|
The opening |
required |
n_periods
|
Optional[int]
|
Number of demand periods. Inferred from a DataFrame; required for a callable. |
None
|
freq
|
Optional[str]
|
Length of one period, a pandas frequency such as |
None
|
warmup_periods
|
int
|
Leading periods excluded from scoring (default 0). |
0
|
scoring_periods
|
Optional[int]
|
Periods that metrics describe by default (>= 1).
Defaults to the periods left after warmup and settlement. The
three windows must add up to |
None
|
settlement_periods
|
int
|
Trailing periods excluded from scoring (default 0). |
0
|
order_during_settlement
|
Optional[bool]
|
Whether the policy may order in settlement.
Required when |
None
|
demand_source_name
|
Optional[str]
|
Optional label for the demand, stored in the manifest. |
None
|
random_seed
|
Optional[int]
|
Optional seed behind the demand, stored in the manifest. |
None
|
policy_schedule
|
Optional[Mapping[int, BasePolicy]]
|
|
None
|
order_constraints
|
Optional[Union[OrderingConstraints, Sequence]]
|
A list of |
None
|
callbacks
|
Optional[Sequence[SimulationCallback]]
|
|
None
|
supply
|
Optional[SupplyModel]
|
A |
None
|
processes
|
Optional[Sequence[InventoryProcess]]
|
|
None
|
allow_target_reuse
|
bool
|
|
True
|
missing_targets
|
str
|
What a decision does for an inventory SKU its policy
has no fitted level for. |
'raise'
|
Returns:
| Type | Description |
|---|---|
SimulationResult
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If an input is incomplete or inconsistent, for example an unfitted policy, a demand calendar with gaps, a target for the wrong window, or windows that do not add up. |
run_comparison
¶
run_comparison(
policies: Union[
List[BasePolicy], Mapping[str, BasePolicy]
],
demand_source: Union[DataFrame, Callable],
inventory: InventoryStateDataFrame,
n_periods: Optional[int] = None,
*,
freq: Optional[str] = None,
warmup_periods: int = 0,
scoring_periods: Optional[int] = None,
settlement_periods: int = 0,
order_during_settlement: Optional[bool] = None,
demand_source_name: Optional[str] = None,
random_seed: Optional[int] = None,
labels: Optional[List[str]] = None,
policy_schedules: Optional[
Union[
List[Optional[Mapping[int, BasePolicy]]],
Mapping[str, Mapping[int, BasePolicy]],
]
] = None,
order_constraints: Optional[
Union[OrderingConstraints, Sequence]
] = None,
callbacks: Optional[
Sequence[SimulationCallback]
] = None,
supply: Optional[SupplyModel] = None,
processes: Optional[Sequence[InventoryProcess]] = None,
allow_target_reuse: bool = True,
missing_targets: str = "raise",
) -> ComparisonResult
Simulate several policies on the same experiment.
The demand is built once and shared; each policy starts from its own copy of
inventory. Constraints, callbacks, supply and processes apply to every
branch (callbacks and processes are reset between branches), and random
supplier lead times use the same seeded draws per period and SKU.
Seeded delivery outcomes follow each branch's own deliveries.
Arguments not listed below are those of run. To compare
policies with different allow_backorders, leave the inventory's
allow_backorders unset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policies
|
Union[List[BasePolicy], Mapping[str, BasePolicy]]
|
Fitted policies, as a list or as |
required |
labels
|
Optional[List[str]]
|
Names for the results of a list of policies; defaults to the policies' names. Not used with a dict, whose keys are the labels. |
None
|
policy_schedules
|
Optional[Union[List[Optional[Mapping[int, BasePolicy]]], Mapping[str, Mapping[int, BasePolicy]]]]
|
One |
None
|
Returns:
| Type | Description |
|---|---|
ComparisonResult
|
A |
SimulationResult
¶
SimulationResult(
history: DataFrame,
inventory: InventoryStateDataFrame,
n_periods: int,
policy_name: str,
event_frame: Optional[DataFrame] = None,
run_settings: Optional[dict] = None,
run_manifest: Optional[dict] = None,
callback_audit: Optional[DataFrame] = None,
order_frame: Optional[DataFrame] = None,
process_flows: Optional[DataFrame] = None,
)
The outcome of one simulation run.
Returned by SimulationEngine.run. The event table is the main record;
the other tables and the manifest add detail.
Attributes:
| Name | Type | Description |
|---|---|---|
inventory |
The final |
|
history |
State snapshots after each period. |
|
n_periods |
Number of simulated demand periods. |
|
policy_name |
Name of the policy. |
|
run_settings |
The run's settings (also in |
|
run_manifest |
A JSON-friendly record of inputs, settings and versions,
with the sections in |
Example
to_event_frame
¶
Return the event table: one row per SKU and period.
Every row satisfies the stock, pipeline and backorder balance identities.
Columns are described in the output-tables reference
(CANONICAL_EVENT_COLUMNS plus optional columns).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window
|
Optional[str]
|
|
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
A copy of the event table rows. |
to_callback_audit_frame
¶
Return one row per accepted callback effect (CALLBACK_AUDIT_COLUMNS).
to_order_frame
¶
Return the order table: one row per scheduled delivery.
Rows cover the opening pipeline and every order placed during the
run. Columns are stockcast.core.ORDER_FRAME_COLUMNS:
- order_id: order line identifier (deliveries of one line share it)
- unique_id, supplier_id: SKU and supplier (``None`` when unknown,
for example a pipeline given only as ``in_transit`` or a run
without ``supply``)
- source: ``"opening"`` for the opening pipeline, ``"placed"``
for orders placed in the run
- order_period / order_date: when the line was ordered (missing
for opening orders without a declared order period)
- due_period / due_date: when this delivery is received, before
that period's demand
- lead_time: ``due_period - order_period`` (realized, per delivery)
- ordered_quantity: quantity of the whole order line
- delivery_quantity: quantity of this delivery
- status: ``"received"`` or ``"open"`` at the end of the run
Per SKU and period, received deliveries add up to the event table's
received_units and open ones to on_order_end.
to_process_flow_frame
¶
Return one row per nonzero process flow, SKU and period.
Columns are stockcast.core.PROCESS_FLOW_COLUMNS: the SKU, the
event-table period/date/demand_period/run_window,
the process and flow names, the flow's direction
(inflow/outflow) and category (general/expiry),
the phase it acted in, and its nonnegative quantity.
Per SKU and period, expiry flows add up to the event table's
expired_units; general inflows and outflows add up to
process_inflow_units and process_outflow_units. The frame is
empty for a run without processes.
summary
¶
A few headline numbers for the scoring window.
Returns:
| Type | Description |
|---|---|
Dict
|
dict with keys:
- fill_rate: share of scoring demand served from stock in its own
period, |
ComparisonResult
¶
ComparisonResult(results: Dict[str, SimulationResult])
Results of SimulationEngine.run_comparison, one per policy.
Behaves like a read-only mapping from label to SimulationResult.
Attributes:
| Name | Type | Description |
|---|---|---|
results |
Dict of label to |