How can I optimize more than one objective function?

You can define any number of objective functions in an AMPL model. However, solvers only minimize or maximize one objective at a time. If your model has, say, three minimize statements that define objectives A, B, and C, then you can minimize each one of them separately (subject to the same constraints) by including their names in solve commands:

solve A;
solve B;
solve C;

But usually, a solution that is optimal for one objective will not be optimal for other objectives. There might be multiple solutions that minimize A, for instance, but you should not expect any of them to also be minimal for B or C. It happens only rarely that some combination of model and data allow a solution to be optimal for more than one objective at the same time.

There do exist strategies for balancing the goals of different objectives, to produce solutions that, if not optimal, are at least very good for every objective function of interest. AMPL supports a generic collection of solver options for multi-objective optimization, which work with any solver that uses the MP interface library. Additionally, some solvers offer their own implementations of related options, which work somewhat differently but which may be more efficient. Availability of these features is summarized in our MP Solver Support table.

To use MP’s generic multi-objective feature, add obj:multi=2 to your solver’s option string. Then to provide priority and/or weighting information, you will need to use AMPL suffix command to define multiobjective properties, which may include the following:

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

Use the objweight suffix to specify a weighted sum of your model’s objectives. For instance, if you want the solver to minimize a weighted combination 80*A + 20*B + 10*C, you can use AMPL let statements* to assign corresponding objweight values to the objectives:

let A.objweight := 80;
let B.objweight := 20;
let C.objweight := 10;

Use the objpriority suffix to specify a series of optimizations, in order of importance. For example, if you want the solver to first minimize A, then fix A at its minimum value and minimize B, then also fix B at the resulting minimum value and minimize C, you can assign corresponding objpriority values to the objectives:

let A.objpriority := 5;
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.

Weighting and priority may be used together. If several objectives have the same priority, then their weighted sum is minimized with the priority given by objpriority, using weights given by objweight. So for example if you set

let A.objpriority := 5;
let B.objpriority := 1;
let C.objpriority := 1;

let A.objweight := 1;
let B.objweight := 9;
let C.objweight := 6;

then after A is minimized, the solver fixes A at its minimum value and minimizes 9*B + 6*C. (A.objweight must be assigned a positive value, even if it is 1, as otherwise it would default to 0.)

If you are willing to accept a value for a higher-priority objective that is a bit greater than its minimum, you might be able to find a solution that has a substantially better value for lower-priority objectives. To explore such a possibility, you can assign a “tolerance” to an objective, which specifies how much its optimal value may be degraded when lower-priority objectives are optimized. There are two possibilities;

  • objreltol specifies a fraction by which an objective value may become worse when lower-priority objectives are optimized.

  • objabstol specifies an amount by which an objective value may become worse when lower-priority objectives are optimized.

For instance, continuing with the previous example, if you set A.objreltol to 0.05, then instead of fixing A at its minimum value, the solver adds a constraint that A’s value must be \leq its minimum value times 1.05. If you set A.objabstol to 100, then instead of fixing A at its minimum value, the solver adds a constraint that A’s value must be \leq its minimum value plus 100.

It is possible for more than one objreltol and/or objabstol setting to apply at the same priority level, in which case the one specifying the greatest tolerance is used. Consider the situation where the settings in our example are instead

let A.objpriority := 1;
let B.objpriority := 5;
let C.objpriority := 5;

let A.objweight := 1;
let B.objweight := 9;
let C.objweight := 6;

let B.objreltol := 0.025;
let C.objreltol := 0.05;
let B objabstol := 30;

B and C share the highest priority, so 9*B + 6*C is first minimized; suppose its optimal value is 800. Then since tolerance values have been set for B and C, the solver adds a constraint that the value of 9*B + 6*C must be \leq 800 + max (0.025*800, 0.05*800, 30) = 840, after which it computes the minimum of the lower-priority objective A. (Since C.objabstol is not set, it defaults to 0, which does not make any difference to the chosen tolerance.)

All of these features apply equally well to maximization objectives, with any tolerances being enforced by \geq constraints. Also minimization and maximization can be combined, though this should be done with care:

  • Objectives that are combined using objweight typically have the same sense (minimize or maximize). Weighted combinations that involve objectives of both senses are allowed, but in that case one sense will be chosen for the combined objective, and objectives of the other sense will be negated in the combination.

  • objpriority, objweight, objreltol, and objabstol values are typically >= 0. Negative objweight settings are accepted, but they may change the objective sense; see your solver’s obj:multi:weight option for details.

  • Multi-objective features tend to work best with linear objectives and constraints. More general model expressions are accepted but may may encounter numerical issues that make them difficult to solve accurately.

To use the multi-objective features that are native (built in) to certain solvers, add obj:multi=1 to your solver’s option string, and consult the appropriate solver documentation:

You will use the objpriority, objweight, objreltol, and objabstol suffixes as before, but the concepts of “priority”, “weight”, and “tolerance” may be defined somewhat differently, and as a result the suffix values may be subject to different interpretations and rules. Additional obj: options may also be available; check the solver’s option listing for details. (For MP-based solvers not listed above, obj:multi=1 and obj:multi=2 both select the generic multi-objective feature described previously.)

Whereas the generic multi-objective feature calls the solver separately for each priority level, native multi-objective optimization is performed in a single solver call. As a consequence, native alternatives can be substantially faster, especially where priorities are specified, or where the solver spends a significant amount of time in preprocessing phases. Native alternatives may be more limited in the problem types they accept, however, and their results are sometimes harder to interpret.

* As an alternative to let statements, you can specify suffix values at the end of an objective’sminimize or maximize statement in the model; for example,

minimize A:
   sum {j in PROD} cost[j] * Make[j],
      suffix objweight 8, suffix objpriority 5;