Skip to content

Commit 0e6db8a

Browse files
authored
Merge pull request #41 from LAMPSPUC/dev
add docs examples
2 parents d2b6de0 + d8a6049 commit 0e6db8a

3 files changed

Lines changed: 288 additions & 0 deletions

File tree

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Documenter.makedocs(;
1212
"tutorials",
1313
["getting_started.md", "modes.md", "custom_forecast.md"],
1414
),
15+
"Examples" => joinpath.("examples", ["scheduling.md", "newsvendor.md"]),
1516
"API Reference" => "reference.md",
1617
],
1718
)

docs/src/examples/newsvendor.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Newsvendor Problem
2+
3+
This example shows how to use the ApplicationDrivenLearning.jl package to solve a newsvendor problem.
4+
5+
The newsvendor problem is a classic inventory management problem where a company must decide how many units to produce to meet demand. The company wants to minimize the cost of holding excess inventory while also minimizing the cost of lost sales.
6+
7+
## The model
8+
9+
For this problem, both plan and assess models are defined as:
10+
11+
```math
12+
\min c.x - q.y - r.w \\
13+
\text{s.t.} \quad x,y,w \geq 0 \\
14+
y + w \leq x \\
15+
y \leq d
16+
```
17+
18+
The plan model will be run considering the demand `d` equal to the output of the forecast model, defining the policy `x` that will be used in the assess model. The assess model will be run considering the demand `d` equal to the actual demand.
19+
20+
## Data
21+
22+
We will model two separate items. One of the items presents a low overstocking cost and the other presents a low under-stocking cost, generating different incentives that a decision maker could explore. This is achieved manipulating the costs:
23+
24+
```math
25+
i=1 \longrightarrow c=10; \quad q = 19;\quad r = 9
26+
```
27+
28+
```math
29+
i=2 \longrightarrow c=10; \quad q = 11;\quad r = 1
30+
```
31+
32+
The demand series for both items will be generated using a discrete uniform distribution.
33+
34+
Let's start by loading the necessary packages and defining the data.
35+
36+
```julia
37+
using Flux
38+
using JuMP
39+
using Random
40+
using Gurobi
41+
using ApplicationDrivenLearning
42+
43+
ADL = ApplicationDrivenLearning
44+
45+
# data
46+
Random.seed!(123)
47+
c = [10, 10]
48+
q = [19, 11]
49+
r = [9, 1]
50+
y_d = rand(10:100, (100, 2)) .|> Float32
51+
x_d = ones(100, 1) .|> Float32
52+
```
53+
54+
Now, we can initialize the application driven learning model, build the plan and assess models and set the forecast model.
55+
56+
```julia
57+
# init application driven learning model
58+
model = ADL.Model()
59+
@variables(model, begin
60+
x[i=1:2] >= 0, ADL.Policy
61+
d[i=1:2], ADL.Forecast
62+
end)
63+
64+
# plan model
65+
@variables(Plan(model), begin
66+
y_plan[i=1:2] >= 0
67+
w_plan[i=1:2] >= 0
68+
end)
69+
@constraints(Plan(model), begin
70+
[i=1:2], y_plan[i] + w_plan[i] <= x[i].plan
71+
[i=1:2], y_plan[i] <= d[i].plan
72+
end)
73+
@objective(Plan(model), Min, sum(c[i] * x[i].plan - q[i] * y_plan[i] - r[i] * w_plan[i] for i in 1:2))
74+
75+
# assess model
76+
@variables(Assess(model), begin
77+
y_assess[i=1:2] >= 0
78+
w_assess[i=1:2] >= 0
79+
end)
80+
@constraints(Assess(model), begin
81+
[i=1:2], y_assess[i] + w_assess[i] <= x[i].assess
82+
[i=1:2], y_assess[i] <= d[i].assess
83+
end)
84+
@objective(Assess(model), Min, sum(c[i] * x[i].assess - q[i] * y_assess[i] - r[i] * w_assess[i] for i in 1:2))
85+
86+
set_optimizer(model, Gurobi.Optimizer)
87+
set_silent(model)
88+
89+
# forecast model
90+
pred = Flux.Dense(1 => 2, exp)
91+
ADL.set_forecast_model(model, pred)
92+
```
93+
94+
We can check how the model performs by computing the assess cost with the initial (random) forecast model.
95+
96+
```julia
97+
julia> ADL.compute_cost(model, x_d, y_d)
98+
-5.118482679128647
99+
```
100+
101+
Now let's train the model using the GradientMode.
102+
103+
```julia
104+
julia> gd_sol = ApplicationDrivenLearning.train!(
105+
model, x_d, y_d,
106+
ApplicationDrivenLearning.Options(
107+
ApplicationDrivenLearning.GradientMode,
108+
rule=Flux.Adam(0.1),
109+
epochs=30
110+
)
111+
)
112+
Epoch 1 | Time = 0.5s | Cost = -5.12
113+
Epoch 2 | Time = 1.0s | Cost = -6.25
114+
Epoch 3 | Time = 1.5s | Cost = -7.64
115+
Epoch 4 | Time = 2.1s | Cost = -9.33
116+
Epoch 5 | Time = 2.6s | Cost = -11.4
117+
Epoch 6 | Time = 3.1s | Cost = -13.93
118+
Epoch 7 | Time = 3.6s | Cost = -17.02
119+
Epoch 8 | Time = 4.1s | Cost = -20.82
120+
Epoch 9 | Time = 4.7s | Cost = -25.17
121+
Epoch 10 | Time = 5.2s | Cost = -29.51
122+
Epoch 11 | Time = 5.7s | Cost = -33.42
123+
Epoch 12 | Time = 6.3s | Cost = -37.56
124+
Epoch 13 | Time = 6.8s | Cost = -42.92
125+
Epoch 14 | Time = 7.5s | Cost = -50.45
126+
Epoch 15 | Time = 8.2s | Cost = -60.3
127+
Epoch 16 | Time = 8.9s | Cost = -72.4
128+
Epoch 17 | Time = 9.5s | Cost = -87.18
129+
Epoch 18 | Time = 10.2s | Cost = -105.31
130+
Epoch 19 | Time = 10.8s | Cost = -127.53
131+
Epoch 20 | Time = 11.4s | Cost = -154.36
132+
Epoch 21 | Time = 12.1s | Cost = -185.68
133+
Epoch 22 | Time = 12.8s | Cost = -222.14
134+
Epoch 23 | Time = 13.4s | Cost = -265.19
135+
Epoch 24 | Time = 14.0s | Cost = -315.45
136+
Epoch 25 | Time = 14.5s | Cost = -370.01
137+
Epoch 26 | Time = 15.1s | Cost = -425.62
138+
Epoch 27 | Time = 15.7s | Cost = -464.52
139+
Epoch 28 | Time = 16.3s | Cost = -461.25
140+
Epoch 29 | Time = 16.9s | Cost = -439.36
141+
Epoch 30 | Time = 17.5s | Cost = -419.52
142+
ApplicationDrivenLearning.Solution(-464.5160680770874, Real[1.6317965f0, 1.7067692f0, 2.7623773f0, 0.9124785f0])
143+
144+
julia> ADL.compute_cost(model, x_d, y_d)
145+
-464.5160680770874
146+
```
147+
148+
After training, we can check the cost of the solution found by the gradient mode and even analyze the predictions from it.
149+
150+
```julia
151+
julia> model.forecast(x_d[1,:])
152+
2-element Vector{Float32}:
153+
80.977684
154+
13.725394
155+
```
156+
157+
As we can see, the forecast model overestimates the demand for the first item and underestimates the demand for the second item (both items average demand is 55), following the incentives from the model structure.

docs/src/examples/scheduling.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Minimal Scheduling Problem
2+
3+
This example shows how to use the ApplicationDrivenLearning.jl package to solve a minimal scheduling problem.
4+
5+
In this problem, we have to define the dispatch of a single unit, considering it's operational constraints, costs and the demand. The cost for under-dispatching is 100 and the cost for over-dispatching is 20. The forecast model will be a simple linear model.
6+
7+
The plan model will only assign the dispatch of the unit equal to the forecast. The assess model will apply a correction to the dispatch, considering the under-dispatching and over-dispatching costs.
8+
9+
First, let's load the necessary packages.
10+
11+
```julia
12+
using Flux
13+
using JuMP
14+
using Gurobi
15+
using ApplicationDrivenLearning
16+
17+
ADL = ApplicationDrivenLearning
18+
```
19+
20+
Now, we can initialize the application driven learning model, build the plan and assess models and set the forecast model.
21+
22+
```julia
23+
# init application driven learning model
24+
model = ADL.Model()
25+
@variable(model, z >= 0, ADL.Policy)
26+
@variable(model, y, ADL.Forecast)
27+
28+
# plan model
29+
@constraints(ADL.Plan(model), begin
30+
z.plan == y.plan
31+
end)
32+
@objective(ADL.Plan(model), Min, 10*z.plan)
33+
34+
# assess model
35+
@variables(ADL.Assess(model), begin
36+
correction
37+
under_dispatch >= 0
38+
over_dispatch >= 0
39+
end)
40+
@constraints(ADL.Assess(model), begin
41+
correction == y.assess - z.assess
42+
under_dispatch >= correction
43+
over_dispatch >= -correction
44+
end)
45+
@objective(ADL.Assess(model), Min, 10*y.assess + 100*under_dispatch + 20*over_dispatch)
46+
47+
# forecast model
48+
predictive = Dense(1 => 1, exp; bias=false)
49+
ADL.set_forecast_model(model, predictive)
50+
```
51+
52+
We can check how the model performs by computing the assess cost with the initial (random) forecast model.
53+
54+
```julia
55+
X = ones(2, 1) .|> Float32
56+
Y = zeros(2, 1) .|> Float32
57+
Y[2, 1] = 2.0
58+
set_optimizer(model, Gurobi.Optimizer)
59+
set_silent(model)
60+
```
61+
62+
```julia
63+
julia> ADL.compute_cost(model, X, Y)
64+
99.7323203086853
65+
```
66+
67+
And finally, we can train the model using the Nelder-Mead mode.
68+
69+
```julia
70+
julia> solution = ADL.train!(
71+
model, X, Y,
72+
ADL.Options(
73+
ADL.NelderMeadMode,
74+
iterations=100,
75+
show_trace=true
76+
)
77+
)
78+
Iter Function value (Σ(yᵢ-ȳ)²)/n
79+
------ -------------- --------------
80+
0 9.973232e+01 2.466946e+00
81+
* time: 0.0
82+
1 9.973232e+01 3.148899e+01
83+
* time: 0.006000041961669922
84+
2 3.675434e+01 1.421373e+01
85+
* time: 0.014999866485595703
86+
3 3.675434e+01 2.673199e+00
87+
* time: 0.0279998779296875
88+
4 3.140795e+01 6.259584e-01
89+
* time: 0.039999961853027344
90+
5 3.015603e+01 1.546907e-01
91+
* time: 0.04699993133544922
92+
6 3.015603e+01 3.849030e-02
93+
* time: 0.05299997329711914
94+
7 3.007905e+01 3.841400e-02
95+
* time: 0.05799984931945801
96+
8 3.000222e+01 9.596825e-03
97+
* time: 0.06699991226196289
98+
9 3.000222e+01 2.398491e-03
99+
* time: 0.07599997520446777
100+
10 3.000222e+01 5.979546e-04
101+
* time: 0.0839998722076416
102+
11 3.000103e+01 3.366484e-04
103+
* time: 0.08899998664855957
104+
12 3.000035e+01 1.144409e-04
105+
* time: 0.09500002861022949
106+
13 3.000012e+01 3.814697e-05
107+
* time: 0.10699987411499023
108+
14 3.000005e+01 9.536743e-06
109+
* time: 0.11500000953674316
110+
15 3.000003e+01 9.536743e-06
111+
* time: 0.12199997901916504
112+
16 3.000001e+01 9.536743e-06
113+
* time: 0.1269998550415039
114+
17 2.999999e+01 3.015783e-06
115+
* time: 0.13899993896484375
116+
18 2.999999e+01 3.015783e-06
117+
* time: 0.14399981498718262
118+
19 2.999998e+01 1.907349e-06
119+
* time: 0.15400004386901855
120+
20 2.999998e+01 0.000000e+00
121+
* time: 0.1640000343322754
122+
ApplicationDrivenLearning.Solution(29.99998f0, Real[0.6931467f0])
123+
124+
julia> model.forecast(X[1,:]) # previsão final
125+
1-element Vector{Float32}:
126+
1.999999
127+
128+
julia> ADL.compute_cost(model, X, Y) # custo final
129+
29.99998
130+
```

0 commit comments

Comments
 (0)