How to add a custom cut

Sometimes you may want to add a set of cuts to the value function that you have computed outside of SDDP.jl. The easiest way to achieve this is via the read_cuts_from_file function.

To see the naming convention required by read_cuts_from_file, train your model for one iteration, and use write_cuts_to_file to write the cuts to file:

julia> using SDDP, HiGHSjulia> function create_model()           return SDDP.LinearPolicyGraph(;        stages = 3,        lower_bound = 0.0,        optimizer = HiGHS.Optimizer,           ) do sp, t        @variable(sp, 0 <= x <= 100, Int, SDDP.State, initial_value = 0)        @variable(sp, 0 <= u_p <= 200, Int)        @variable(sp, u_o >= 0, Int)        @variable(sp, w)        @constraint(sp, x.out == x.in + u_p + u_o - w)        @stageobjective(sp, 100 * u_p + 300 * u_o + 50 * x.out)        Ω = [[100.0], [100.0, 300.0], [100.0, 300.0]]        SDDP.parameterize-> fix(w, ω), sp, Ω[t])           end       endcreate_model (generic function with 1 method)julia> model = create_model();julia> SDDP.train(model; iteration_limit = 1, print_level = 0)julia> SDDP.write_cuts_to_file(model, "cuts.json")julia> print(read("cuts.json", String))[{"multi_cuts":[],"node":"2","risk_set_cuts":[],"single_cuts":[{"coefficients":{"x":-200.0},"intercept":30000.0,"state":{"x":0.0}}]},{"multi_cuts":[],"node":"3","risk_set_cuts":[],"single_cuts":[]},{"multi_cuts":[],"node":"1","risk_set_cuts":[],"single_cuts":[{"coefficients":{"x":-200.0},"intercept":57500.0,"state":{"x":0.0}}]}]

Then create a new file containing the cut. The formula for the cut is

theta >= intercept + sum(coefficients[k] * (x[k] - state[k]) for k in keys(state))

In this example, we add the cut $\theta \ge 55500 - 200 * (x - 10)$ to node 1:

julia> model = create_model();julia> SDDP.calculate_bound(model)10000.0julia> write(           "new_cuts.json",           """           [{               "node": "1",               "single_cuts": [{                   "state": {"x": 10.0},                   "coefficients": {"x": -200.0},                   "intercept": 55500.0               }],               "risk_set_cuts": [],               "multi_cuts": []           }]           """       );julia> SDDP.read_cuts_from_file(model, "new_cuts.json")julia> SDDP.calculate_bound(model)62500.0

For most purposes, you can ignore the risk_set_cuts and multi_cuts fields of the JSON file; they are used when you are using SDDP.MULTI_CUT and a risk measure.