diff --git a/docs/src/tutorials/linear/diet.jl b/docs/src/tutorials/linear/diet.jl index 701d3e7248d..d8f713413bd 100644 --- a/docs/src/tutorials/linear/diet.jl +++ b/docs/src/tutorials/linear/diet.jl @@ -79,7 +79,7 @@ limits = DataFrames.DataFrame( "fat" 0 65 "sodium" 0 1779 ], - ["name", "min", "max"], + ["nutrient", "min", "max"], ) # ## JuMP formulation @@ -90,32 +90,30 @@ limits = DataFrames.DataFrame( # HiGHS as our optimizer: model = Model(HiGHS.Optimizer) +set_silent(model) -# Next, we create a set of decision variables `x`, indexed over the foods in the -# `data` DataFrame. Each `x` has a lower bound of `0`. +# Next, we create a set of decision variables `x`, with one element for each row +# in the DataFrame, and each `x` has a lower bound of `0`: -@variable(model, x[foods.name] >= 0); +@variable(model, x[foods.name] >= 0) -# Our objective is to minimize the total cost of purchasing food. We can write -# that as a sum over the rows in `data`. +# To simplify things later on, we store the vector as a new column `x` in the +# DataFrame `foods`: -@objective( - model, - Min, - sum(food["cost"] * x[food["name"]] for food in eachrow(foods)), -); +foods.x = Array(x) + +# Our objective is to minimize the total cost of purchasing food: + +@objective(model, Min, sum(foods.cost .* foods.x)); # For the next component, we need to add a constraint that our total intake of -# each component is within the limits contained in the `limits` DataFrame. -# To make this more readable, we introduce a JuMP `@expression` - -for limit in eachrow(limits) - intake = @expression( - model, - sum(food[limit["name"]] * x[food["name"]] for food in eachrow(foods)), - ) - @constraint(model, limit.min <= intake <= limit.max) -end +# each component is within the limits contained in the `limits` DataFrame: + +@constraint( + model, + [row in eachrow(limits)], + row.min <= sum(foods[!, row.nutrient] .* foods.x) <= row.max, +); # What does our model look like? @@ -132,8 +130,8 @@ solution_summary(model) # Success! We found an optimal solution. Let's see what the optimal solution is: -for food in foods.name - println(food, " = ", value(x[food])) +for row in eachrow(foods) + println(row.name, " = ", value(row.x)) end # That's a lot of milk and ice cream! And sadly, we only get `0.6` of a @@ -155,7 +153,9 @@ filter!(row -> row.quantity > 0.0, solution) # constraints. Let's see what happens if we add a constraint that we can buy at # most 6 units of milk or ice cream combined. -@constraint(model, x["milk"] + x["ice cream"] <= 6) +dairy_foods = ["milk", "ice cream"] +is_dairy = map(name -> name in dairy_foods, foods.name) +@constraint(model, sum(foods[is_dairy, :x]) <= 6) optimize!(model) Test.@test termination_status(model) == INFEASIBLE #hide Test.@test primal_status(model) == NO_SOLUTION #hide