Skip to content

Commit

Permalink
Merge pull request #82 from hdavid16/top_down_fulfillment
Browse files Browse the repository at this point in the history
  • Loading branch information
hdavid16 authored Nov 20, 2022
2 parents b755368 + 5f8facf commit a9631b4
Show file tree
Hide file tree
Showing 19 changed files with 282 additions and 186 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "InventoryManagement"
uuid = "2ad91f63-398d-4379-af6a-5a85689656d5"
authors = ["hdavid16 <[email protected]> and contributors"]
version = "0.5.5"
version = "0.6.0"

[deps]
Chain = "8be319e6-bccf-4806-a6f7-6fae938471bc"
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ The graph metadata should have the following fields in its metadata:
- `:holding_cost::Dict`: unit holding cost for each material (`keys`). Default = `0`.
- `:early_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `[node] ∪ inneighbors(SupplyChainEnv.network, node)`.
- `:customer_priority::Dict`: (*only when the node has at least 1 customer*) `Vector` of customers (from high to low priority) for each material (`keys`). Downstream requests are fulfilled by this prioritization (after giving priority first to due and then expired orders). Default = `outneighbors(SupplyChainEnv.network, node)`.
- `:production_capacity::Dict`: maximum production capacity for each material (`keys`). Default = `Inf`.
- `:make_to_order::Vector`: list of materials that are make-to-order. Default = `[]`.
- `:bill_of_materials::Union{Dict,NamedArray}`: `keys` are material `Tuples`, where the first element is the input material and the second element is the product/output material; the `values` indicate the amount of input material consumed to produce 1 unit of output material. Alternatively, a `NamedArray` can be passed where the input materials are the rows and the output materials are the columns. The following convention is used for the bill of material (BOM) values:
Expand All @@ -187,7 +188,8 @@ The graph metadata should have the following fields in its metadata:
- `:holding_cost::Dict`: unit holding cost for each material (`keys`). Default = `0`.
- `:early_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of supplier priorities (from high to low) for each material (`keys`). When a request cannot be fulfilled due to insufficient productio capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:customer_priority::Dict`: (*only when the node has at least 1 customer*) `Vector` of customers (from high to low priority) for each material (`keys`). Downstream requests are fulfilled by this prioritization (after giving priority first to due and then expired orders). Default = `outneighbors(SupplyChainEnv.network, node)`.

`Markets` will have the following fields in their node metadata:
- `:initial_inventory::Dict`: initial inventory for each material (`keys`). Default = `0`.
Expand All @@ -197,7 +199,7 @@ The graph metadata should have the following fields in its metadata:
- `:market_early_fulfillment::Dict`: (`true`/`false`) indicates if the market accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) indicates if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:market_partial_fulfillment::Dict`: (`true`/`false`) indicates if the market accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of supplier priorities (from high to low) for each material (`keys`). When a request cannot be fulfilled due to insufficient productio capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:demand_distribution::Dict`: probability distributions from [Distributions.jl](https://github.com/JuliaStats/Distributions.jl) for the market demands for each material (`keys`). For deterministic demand, instead of using a probability distribution, use `D where D <: Number`. Default = `0`.
- `:demand_frequency::Dict`: number of times demand occurs per period on average for each material (`keys`). Default = `1`.
- `:sales_price::Dict`: market sales price for each material (`keys`). Default = `0`.
Expand Down Expand Up @@ -230,6 +232,7 @@ This function takes the following inputs:
- `evaluate_profit::Bool = true`: the simulation will calculate the proft at each node if `true` and save the results in `SupplyChainEnv.profit`.
- Aditional Keyword Arguments:
- `discount::Float64 = 0.`: discount factor (i.e., interest rate) to account for the time-value of money.
- `numerical_precision::Int = 6`: Numerical precision (number of digits) for external demand sampling.
- `seed::Int = 0`: random seed for simulation.

## Simulation Outputs
Expand Down
Binary file modified docs/src/assets/ex2_inventory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/src/assets/ex3_echelon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions docs/src/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ The graph metadata should have the following fields in its metadata:
- `:holding_cost::Dict`: unit holding cost for each material (`keys`). Default = `0`.
- `:early_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `[node] ∪ inneighbors(SupplyChainEnv.network, node)`.
- `:customer_priority::Dict`: (*only when the node has at least 1 customer*) `Vector` of customers (from high to low priority) for each material (`keys`). Downstream requests are fulfilled by this prioritization (after giving priority first to due and then expired orders). Default = `outneighbors(SupplyChainEnv.network, node)`.
- `:production_capacity::Dict`: maximum production capacity for each material (`keys`). Default = `Inf`.
- `:make_to_order::Vector`: list of materials that are make-to-order. Default = `[]`.
- `:bill_of_materials::Union{Dict,NamedArray}`: `keys` are material `Tuples`, where the first element is the input material and the second element is the product/output material; the `values` indicate the amount of input material consumed to produce 1 unit of output material. Alternatively, a `NamedArray` can be passed where the input materials are the rows and the output materials are the columns. The following convention is used for the bill of material (BOM) values:
Expand All @@ -69,7 +70,8 @@ The graph metadata should have the following fields in its metadata:
- `:holding_cost::Dict`: unit holding cost for each material (`keys`). Default = `0`.
- `:early_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) on if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of supplier priorities (from high to low) for each material (`keys`). When a request cannot be fulfilled due to insufficient productio capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:customer_priority::Dict`: (*only when the node has at least 1 customer*) `Vector` of customers (from high to low priority) for each material (`keys`). Downstream requests are fulfilled by this prioritization (after giving priority first to due and then expired orders). Default = `outneighbors(SupplyChainEnv.network, node)`.

`Markets` will have the following fields in their node metadata:
- `:initial_inventory::Dict`: initial inventory for each material (`keys`). Default = `0`.
Expand All @@ -79,7 +81,7 @@ The graph metadata should have the following fields in its metadata:
- `:market_early_fulfillment::Dict`: (`true`/`false`) indicates if the market accepts orders being fulfilled before their due date for each material (`keys`). Default = `true`.
- `:partial_fulfillment::Dict`: (*only when the node has at least 1 supplier*) (`true`/`false`) indicates if the node accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:market_partial_fulfillment::Dict`: (`true`/`false`) indicates if the market accepts orders being fulfilled partially for each material (`keys`). Default = `true`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of supplier priorities (from high to low) for each material (`keys`). When a request cannot be fulfilled due to insufficient productio capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:supplier_priority::Dict`: (*only when the node has at least 1 supplier*) `Vector` of suppliers (from high to low priority) for each material (`keys`). When a request cannot be fulfilled due to insufficient production capacity or on-hand inventory, the system will try to reallocate it to the supplier that is next in line on the priority list (if `reallocate = true`). Default = `inneighbors(SupplyChainEnv.network, node)`.
- `:demand_distribution::Dict`: probability distributions from [Distributions.jl](https://github.com/JuliaStats/Distributions.jl) for the market demands for each material (`keys`). For deterministic demand, instead of using a probability distribution, use `D where D <: Number`. Default = `0`.
- `:demand_frequency::Dict`: number of times demand occurs per period on average for each material (`keys`). Default = `1`.
- `:sales_price::Dict`: market sales price for each material (`keys`). Default = `0`.
Expand Down Expand Up @@ -112,6 +114,7 @@ This function takes the following inputs:
- `evaluate_profit::Bool = true`: the simulation will calculate the proft at each node if `true` and save the results in `SupplyChainEnv.profit`.
- Aditional Keyword Arguments:
- `discount::Float64 = 0.`: discount factor (i.e., interest rate) to account for the time-value of money.
- `numerical_precision::Int = 6`: Numerical precision (number of digits) for external demand sampling.
- `seed::Int = 0`: random seed for simulation.

## Simulation Outputs
Expand Down
Binary file modified examples/figs/ex2_inventory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/figs/ex3_echelon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/InventoryManagement.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ using Distributions
using LinearAlgebra
import StatsBase: mean, std

const Material = Union{String, Symbol}

@reexport using Graphs
@reexport using MetaGraphs

Expand Down
2 changes: 1 addition & 1 deletion src/actions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function (x::SupplyChainEnv)(action::Vector{T} where T <: Real)
#receive incoming orders
update_shipments!(x)
#place requests
place_orders!(x, act)
replenishment_orders!(x, act)
#discard any excess inventory
x.options[:capacitated_inventory] && enforce_inventory_limits!(x)
#markets open and demand occurs
Expand Down
Loading

2 comments on commit a9631b4

@hdavid16
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/72533

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.6.0 -m "<description of version>" a9631b4d18b0107ed6233e2d2ac3aa6d9ae91253
git push origin v0.6.0

Please sign in to comment.