2

I would like to save all my variables and dual variables of my finished lp-optimization in an efficient manner. My current solution works, but is neither elegant nor suited for larger optimization programs with many variables and constraints because I define and push! every single variable into DataFrames separately. Is there a way to iterate through the variables using all_variables() and all_constraints() for the duals? While iterating, I would like to push the results into DataFrames with the variable index name as columns and save the DataFrame in a Dict(). A conceptual example would be for variables:

Result_vars = Dict()
for vari in all_variables(Model)
Resul_vars["vari"] = DataFrame(data=[indexval(vari),value(vari)],columns=[index(vari),"Value"]) 
end

An example of the appearance of the declared variable in JuMP and DataFrame:

@variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="Expected production")

And Result_vars[p] shall approximately look like:

t,n,m,Value
1,1,1,50
2,1,1,60 
3,1,1,145
Cameron Bieganek
  • 7,208
  • 1
  • 23
  • 40
werderneo
  • 41
  • 5

2 Answers2

2

Presumably, you could go something like:

x = all_variables(model)
DataFrame(
    name = variable_name.(x),
    Value = value.(x),
)

If you want some structure more complicated, you need to write custom code.

T, N, M, primal_solution = [], [], [], []
for t in s_time, n in s_n, m in s_m
    push!(T, t)
    push!(N, n)
    push!(M, m)
    push!(primal_solution, value(p[t, n, m]))
end
DataFrame(t = T, n = N, m = M, Value = primal_solution)

See here for constraints: https://jump.dev/JuMP.jl/stable/constraints/#Accessing-constraints-from-a-model-1. You want something like:

for (F, S) in list_of_constraint_types(model)
    for con in all_constraints(model, F, S)
        @show dual(con)
    end
end
Oscar Dowson
  • 2,395
  • 1
  • 5
  • 13
1

Thanks to Oscar, I have built a solution that could help to automatize the extraction of results. The solution is build around a naming convention using base_name in the variable definition. One can copy paste the variable definition into base_name followed by :. E.g.:

@variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="p[t=s_time,n=s_n,m=s_m]:")

The naming convention and syntax can be changed, comments can e.g. be added, or one can just not define a base_name. The following function divides the base_name into variable name, sets (if needed) and index:

function var_info(vars::VariableRef)
    split_conv = [":","]","[",","]
    x_str = name(vars)
    if occursin(":",x_str)
        x_str = replace(x_str, " " => "") #Deletes all spaces
        x_name,x_index = split(x_str,split_conv[1]) #splits raw variable name+ sets and index
        x_name = replace(x_name, split_conv[2] => "")
        x_name,s_set = split(x_name,split_conv[3])#splits raw variable name and sets
        x_set = split(s_set,split_conv[4])

        x_index = replace(x_index, split_conv[2] => "")
        x_index = replace(x_index, split_conv[3] => "")
        x_index = split(x_index,split_conv[4])
        return (x_name,x_set,x_index)
    else
        println("Var base_name not properly defined. Special Syntax required in form     var[s=set]:  ")
    end
    
end

The next functions create the columns and the index values plus columns for the primal solution ("Value").

function create_columns(x)
    col_ind=[String(var_info(x)[2][col]) for col in 1:size(var_info(x)[2])[1]]
    cols = append!(["Value"],col_ind)
    return cols
end

function create_index(x)
    col_ind=[String(var_info(x)[3][ind]) for ind in 1:size(var_info(x)[3])[1]]
    index = append!([string(value(x))],col_ind)
    return index
end

function create_sol_matrix(varss,model)
    nested_sol_array=[create_index(xx) for xx in all_variables(model) if varss[1]==var_info(xx)[1]]
    sol_array=hcat(nested_sol_array...)
    return sol_array
end

Finally, the last function creates the Dict which holds all results of the variables in DataFrames in the previously mentioned style:

function create_var_dict(model)
    Variable_dict=Dict(vars[1]
            =>DataFrame(Dict(vars[2][1][cols] 
                =>create_sol_matrix(vars,model)[cols,:] for cols in 1:size(vars[2][1])[1]))
                    for vars in unique([[String(var_info(x)[1]),[create_columns(x)]] for x in all_variables(model)]))
    return Variable_dict
end

When those functions are added to your script, you can simply retrieve all the solutions of the variables after the optimization by calling create_var_dict():

var_dict = create_var_dict(model)

Be aware: they are nested functions. When you change the naming convention, you might have to update the other functions as well. If you add more comments you have to avoid using [, ], and ,. This solution is obviously far from optimal. I believe there could be a more efficient solution falling back to MOI.

werderneo
  • 41
  • 5