Re: [AMPL 24932] Ampl model

I am guessing that a “food” and a “commodity” are the same thing in your description. You could set up the food groups like this:

set FOODS;
set FOOD_TYPES;
set FOOD_GROUP {FOOD_TYPES} within FOODS;

Then the data would look something like this (where just for this example, I have used some arbitrary letters for the food names):

set FOODS := a b c d e f g h i j ;

set FOOD_TYPES := vegetables fruits sides staples ;

set FOOD_GROUP[vegetables] := a b c ;
set FOOD_GROUP[fruits] := d e ;
set FOOD_GROUP[sides] := f ;
set FOOD_GROUP[staples] := g h i  j;

Your “minrat” constraint could then be written like this (after T, R, and minrat are defined appropriately):

subject to MinRatio {ft in FOOD_TYPES, t in T}:
   sum {f in FOOD_GROUP[ft]} R[f,t] >= minrat[ft];

Then, how to write the parameter of minrat and maxrat. Would you mind give me some examples please ?

To test my example, I added these definitions:

set T;
param minrat {FOOD_TYPES};
var R {FOODS,T} >= 0;

Then I made up some test data for T and minrat.

can ampl solve the multi-objective? minimize and maximize?

Some solvers have options for multi-objective optimization. For example, to use Gurobi’s multi-objective feature, you will need to first specify

option gurobi_options ‘multiobj=1’;

or, if you are already defining a gurobi_options string, add multiobj=1 to it. Then to provide priority and/or weighting information, you will need to use AMPL “suffix” commands to define some suffixes, which may include the following:

suffix objpriority IN;
suffix objweight IN;
suffix objreltol IN;
suffix objabstol IN;

As an example, suppose that your model has three AMPL “minimize” statements, which define three objective functions: A, B, and C. If you want Gurobi to minimize a weighted combination 8A + 2B + C, use AMPL “let” statements to assign objweight values to the objectives:

let A.objweight := 8;
let B.objweight := 2;
let C.objweight := 1;

Or, if you want Gurobi to first minimize A, then fix A at its minimum value and minimize B, then also fix B at its minimum value and minimize C, you can assign objpriority values to the objectives:

let A.objpriority := 3;
let B.objpriority := 2;
let C.objpriority := 1;

You can use any integers as objpriority values; the objective with the highest priority is minimized first, then the objective with the next highest priority, and so forth. Gurobi also offers two generalizations of this approach:

  • If two objectives have the same priority, then their weighted sum is minimized, using weights given by objweight. For example if B.objpriority is set instead to 1 above, then after A is minimized, Gurobi fixes A at its minimum value and minimizes 2*B + C.
  • You can assign an objreltol or objabstol value to an objective to allow its objective value to be degraded by a limited amount when lower-priority objectives are optimized. For example, if in addition to the priorities shown above, you set A.objreltol to 0.05, then instead of fixing A at its minimum value, Gurobi adds a constraint that A’s value must be <= its minimum value plus 5%. Or, if you set A.objabstol to 100, then instead of fixing A at its minimum value, Gurobi adds a constraint that A’s value must be <= its minimum value plus 100.
    These generalizations can be combined to specify a variety of ways in which objective functions are handled. Also the same ideas apply to maximized objective functions – but if you are using .objweight, you need to have all objectives be minimize or all objective be maximize at each priority level.

There are similar options in CPLEX. Also Xpress has more limited features for priorities only. If you are using another solver, you will have to program the multi-objective optimization yourself, which will involve writing an objective that is a weighted sum of objective expressions, and/or making a series of solves…

what about the preemptive method in ampl? how to solve it when it has multi-objectives?

If you use .objpriority (rather than .objweight) then you get a preemptive method:

  • The solver first solves with the objective that has the highest priority.
  • Then the solver fixes the highest-priority objective to its optimal value, and solves with the objective that has the second-highest priority.
  • Then the solver fixes the second-highest-priority objective to its optimal value, and solves with the objective that has the third-highest priority.
    The procedure continues in this way until all objectives have been considered.

ok, thank you Robert. I will try that.

im so sorry. To make me understand better, can you give me some examples?

To run the attached example using Gurobi as the solver, execute “include diet.run” in AMPL. The model (diet.mod) has two objective functions, Total_Cost and Total_Vit_C:

minimize Total_Cost: sum {j in FOOD} cost[j] * Buy[j];
maximize Total_Vit_C: sum {j in FOOD} amt["C",j] * Buy[j];

In diet.run, Total_Cost is set to have a higher priority:

suffix objpriority IN;
let Total_Cost.objpriority := 2;
let Total_Vit_C.objpriority := 1;

Then Gurobi is set up for multi-objective optimization, and is invoked with a “solve” command:

option solver gurobi;
option gurobi_options 'multiobj=1';
solve;

Gurobi minimizes the higher priority objective (Total_Cost) first. Then it fixes Total_Cost at its optimal value, and maximizes the lower-priority objective (Total_Vit_C). The resulting solution is returned to AMPL, where you can view it:

display Total_Cost, Total_Vit_C;
display Buy;

diet.mod (515 Bytes)

diet.dat (750 Bytes)

diet.run (260 Bytes)

what if i have three objective functions and the third is about maximizing?

It works the same way with three objective functions. In your model, use an AMPL “minimize” or “maximize” statement to define each of the three objective functions. Then assign a different priority to each of the three objective functions, using .objpriority as shown in my example.

Solving with Gurobi is then also the same as in my example. Also the same setup works with CPLEX, using

option solver cplex;
option cplex_options 'multiobj=1';

oke, i got it. Thank you Robert

What does it mean?

The let command cannot assign to nutrient_value_score.

context: let nutrient_value_score := >>> 1; <<<

Gurobi 10.0.1: Gurobi 10.0.1: optimal solution; objective 579486.6704

20 simplex iterations

1 branching nodes

Objective = total_cost

total_cost = 579487

kilocalories = 19982.5

nutrient_value_score = 99.7933

Is nutrient_value_score the name of an objective function (from a “minimize” or “maximize” statement in your model)? If you try to use a “let” statement to assign a value to an objective function, then you will get an error message like the one you see. AMPL always computes the value of an objective automatically, from the current values of the variables.

If nutrient_value_score is not an objective function, then can you show the complete definition of nutrient_value_score from your model? With that information, it will be possible to give you more help.

Hi, what does it mean? Can you fix it?

#SHOW RESULTS

display total_cost, kilocalories, nutrient_value_score;

display ratio_commodity;

ampl: Gurobi 10.0.1: Gurobi 10.0.1: optimal solution; objective 61571.80715

23 simplex iterations

1 branching nodes

absmipgap=1.45519e-11, relmipgap=0

Objective = total_cost

total_cost = 61571.8

kilocalories = 1400

nutrient_value_score = 100

For a mixed-integer program, the solver has proved optimality when the gap between the lower bound and the upper bound is 0. However, to avoid extra work that makes only a very minor improvement, Gurobi may stop when the absolute gap (absmipgap) or the gap relative to the size of the solution (mipgap) is “small enough”.

In your example, the absmipgap is such a tiny number (1.45519e-11) that you should consider it to be the same as 0. Thus you should consider the solution returned by Gurobi to be optimal (and there is nothing that needs to be fixed).

Let me try

can ampl show the top 5 solutions for each objective function. If it yes, how to write in the running script?

Computing solutions is a solver function, so you need to use a solver that can compute and return multiple solutions. I see you have been using Gurobi, which is a solver that can do this. There are a number of Gurobi options for requesting multiple solutions of various kinds; as a start, I suggest setting the following options before solving:

option gurobi_options ‘sol_stub=bestgurobi sol:poolmode=2 sol:poollimit=5’;

(Note that, if you are already defining a gurobi_options string, then you should instead add these three options to it.) This will cause Gurobi to write solution files named bestgurobi1.sol, bestgurobi2.sol, etc. Then you can read and process them with a loop like this:

option solver gurobi;
option gurobi_options 'sol:stub=bestgurobi sol:poolmode=2 sol:poollimit=5';

solve;

for {i in 1..Total_Cost.npool}
{
   solution ("bestgurobi" & i & ".sol");
   # put commands to display or process each solution here
}

where you replace Total_Cost by the actual name of your AMPL objective function.