Minimal Scheduling Problem

This example shows how to use the ApplicationDrivenLearning.jl package to solve a minimal scheduling problem.

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.

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.

First, let's load the necessary packages.

using Flux
using JuMP
using Gurobi
using ApplicationDrivenLearning

ADL = ApplicationDrivenLearning

Now, we can initialize the application driven learning model, build the plan and assess models and set the forecast model.

# init application driven learning model
model = ADL.Model()
@variable(model, z >= 0, ADL.Policy)
@variable(model, y, ADL.Forecast)

# plan model
@constraints(ADL.Plan(model), begin
    z.plan == y.plan
end)
@objective(ADL.Plan(model), Min, 10*z.plan)

# assess model
@variables(ADL.Assess(model), begin
    correction
    under_dispatch >= 0
    over_dispatch >= 0
end)
@constraints(ADL.Assess(model), begin
    correction == y.assess - z.assess
    under_dispatch >= correction
    over_dispatch >= -correction
end)
@objective(ADL.Assess(model), Min, 10*y.assess + 100*under_dispatch + 20*over_dispatch)

# forecast model
predictive = Dense(1 => 1, exp; bias=false)
ADL.set_forecast_model(model, predictive)

We can check how the model performs by computing the assess cost with the initial (random) forecast model.

X = ones(2, 1) .|> Float32
Y = zeros(2, 1) .|> Float32
Y[2, 1] = 2.0
set_optimizer(model, Gurobi.Optimizer)
set_silent(model)
julia> ADL.compute_cost(model, X, Y)
99.7323203086853

And finally, we can train the model using the Nelder-Mead mode.

julia> solution = ADL.train!(
    model, X, Y,
    ADL.Options(
        ADL.NelderMeadMode,
        iterations=100,
        show_trace=true
    )
)
Iter     Function value    √(Σ(yᵢ-ȳ)²)/n 
------   --------------    --------------
     0     9.973232e+01     2.466946e+00
 * time: 0.0
     1     9.973232e+01     3.148899e+01
 * time: 0.006000041961669922
     2     3.675434e+01     1.421373e+01
 * time: 0.014999866485595703
     3     3.675434e+01     2.673199e+00
 * time: 0.0279998779296875
     4     3.140795e+01     6.259584e-01
 * time: 0.039999961853027344
     5     3.015603e+01     1.546907e-01
 * time: 0.04699993133544922
     6     3.015603e+01     3.849030e-02
 * time: 0.05299997329711914
     7     3.007905e+01     3.841400e-02
 * time: 0.05799984931945801
     8     3.000222e+01     9.596825e-03
 * time: 0.06699991226196289
     9     3.000222e+01     2.398491e-03
 * time: 0.07599997520446777
    10     3.000222e+01     5.979546e-04
 * time: 0.0839998722076416
    11     3.000103e+01     3.366484e-04
 * time: 0.08899998664855957
    12     3.000035e+01     1.144409e-04
 * time: 0.09500002861022949
    13     3.000012e+01     3.814697e-05
 * time: 0.10699987411499023
    14     3.000005e+01     9.536743e-06
 * time: 0.11500000953674316
    15     3.000003e+01     9.536743e-06
 * time: 0.12199997901916504
    16     3.000001e+01     9.536743e-06
 * time: 0.1269998550415039
    17     2.999999e+01     3.015783e-06
 * time: 0.13899993896484375
    18     2.999999e+01     3.015783e-06
 * time: 0.14399981498718262
    19     2.999998e+01     1.907349e-06
 * time: 0.15400004386901855
    20     2.999998e+01     0.000000e+00
 * time: 0.1640000343322754
ApplicationDrivenLearning.Solution(29.99998f0, Real[0.6931467f0])

julia> model.forecast(X[1,:])  # previsão final
1-element Vector{Float32}:
 1.999999

julia> ADL.compute_cost(model, X, Y)  # custo final
29.99998