Some model requirements are strict: they must always be satisfied. Other requirements are preferences: they should be satisfied if possible, but they can be violated when the data or operating conditions make the full model impossible to satisfy.
If these preferences are modeled as hard constraints, the model may become infeasible. You can then diagnose the infeasibility with tools such as computeIIS(), but this is often less useful than modeling the preferences explicitly as soft constraints from the start. This can be worthwhile even if you expect all soft constraints to be satisfied most of the time, because it avoids the need for a potentially expensive infeasibility diagnosis when unusual data or operating conditions make the hard version of the model infeasible.
A common approach is to introduce violation variables for the soft constraints, then use Gurobi's multi-objective optimization features to minimize those violations before optimizing the original business objective.
This turns an infeasible model into a feasible model that clearly reports which soft constraints could not be fully satisfied.
When should I use this approach?
Use this approach when some constraints represent goals, targets, service levels, or business rules that may need to be relaxed in practice.
Examples include:
- customer demand targets,
- delivery date targets,
- staffing preferences,
- service-level requirements,
- inventory targets,
- production targets,
- preferred assignments.
These requirements may be important, but they are not always absolute. If the model cannot satisfy all of them, it is often better to return the best achievable solution and show the violations than to return INFEASIBLE.
This approach is not appropriate for constraints that must never be violated, such as physical conservation constraints, regulatory requirements, or logical relationships that define the validity of the model.
Even if soft constraints are rarely violated in practice, modeling them explicitly can still be useful. When all soft constraints can be satisfied, the model proceeds to the original business objective as usual. When they cannot all be satisfied, the model returns a solution with clear violation variables instead of returning INFEASIBLE and requiring a separate IIS analysis.
How does this work?
The basic pattern is:
- Keep truly mandatory constraints as hard constraints.
- Replace each soft constraint with a relaxed version that includes a nonnegative violation variable.
- Use higher-priority objectives to minimize the violation variables.
- Use a lower-priority objective for the original business objective.
For example, instead of writing a hard demand constraint like this:
ship_A >= demand_A
write a relaxed version:
ship_A + shortfall_A >= demand_A
where shortfall_A >= 0 measures the amount of unmet demand.
Then set a high-priority objective to minimize shortfall_A. Once Gurobi has minimized the violation of the soft constraints, it optimizes the lower-priority business objective, such as minimizing cost.
Why use multi-objective optimization?
You could combine soft-constraint violations and the original objective into one weighted objective. For example:
big_penalty * total_shortfall + total_cost
This can work, but it requires choosing penalty weights carefully. If the penalty is too small, the solver may prefer violating soft constraints to reduce cost. If the penalty is too large, numerical issues or poor scaling may result.
Multi-objective optimization provides a cleaner modeling structure. You can tell Gurobi that minimizing soft-constraint violations has higher priority than minimizing cost. Objectives with higher priority are optimized first. Lower-priority objectives are then optimized subject to the allowed degradation of higher-priority objectives.
In practice, this makes the model easier to explain:
First, satisfy the soft constraints as much as possible. Then, among those solutions, optimize the original objective.
Example
In the following example, a planner has 10 units available and two customer demand targets:
- customer A wants 8 units,
- customer B wants 5 units.
The total demand is 13 units, so it is impossible to satisfy both demand targets with only 10 units available.
Instead of making the demand constraints hard, we add shortfall variables. These shortfall variables measure the unmet demand for each customer.
We then use multi-objective optimization:
- First priority: minimize total unmet demand.
- Second priority: minimize shipping cost, without increasing total unmet demand beyond the allowed degradation.
import gurobipy as gp
from gurobipy import GRB
m = gp.Model("soft_demand_multiobj")
customers = ["A", "B"]
demand = {"A": 8, "B": 5}
shipping_cost = {"A": 2.0, "B": 1.0}
available_supply = 10
# Shipment variables
ship = m.addVars(customers, lb=0, name="ship")
# Shortfall variables for soft demand constraints
shortfall = m.addVars(customers, lb=0, name="shortfall")
# Hard constraint: available supply cannot be exceeded
m.addConstr(ship.sum() <= available_supply, name="available_supply")
# Soft demand constraints: unmet demand is allowed, but measured
for c in customers:
m.addConstr(ship[c] + shortfall[c] >= demand[c], name=f"demand_{c}")
# Objective 0: minimize total unmet demand
m.setObjectiveN(
shortfall.sum(),
index=0,
priority=2,
name="minimize_total_shortfall",
)
# Objective 1: minimize shipping cost after minimizing unmet demand
m.setObjectiveN(
gp.quicksum(shipping_cost[c] * ship[c] for c in customers),
index=1,
priority=1,
name="minimize_shipping_cost",
)
m.ModelSense = GRB.MINIMIZE
m.optimize()
if m.Status == GRB.OPTIMAL:
print("Solution:")
for c in customers:
print(
f"Customer {c}: "
f"ship {ship[c].X:g}, "
f"shortfall {shortfall[c].X:g}"
)
print(f"Total shortfall: {sum(shortfall[c].X for c in customers):g}")
print(f"Total cost: {sum(shipping_cost[c] * ship[c].X for c in customers):g}")The model is now feasible because any unmet demand can be captured by the shortfall variables.
The values of the shortfall variables explain which demand targets could not be fully satisfied. For example, if shortfall[B] = 3, then customer B has 3 units of unmet demand in the solution.
This is often easier for users to interpret than an IIS. Instead of reporting that the model is infeasible, the model returns the best achievable plan and explicitly identifies the violated soft constraints.
In this example, the first objective minimizes the total shortfall. If the minimum total shortfall is 3 units, the lower-priority shipping-cost objective may determine where those 3 units of shortfall occur. If some customers are more important than others, use customer-specific weights or separate objective priorities.
Extending the pattern
The same pattern can be extended to multiple classes of soft constraints.
For example, suppose unmet demand for priority customers should be avoided before unmet demand for regular customers. You could define separate violation expressions and assign them different objective priorities:
m.setObjectiveN(priority_customer_shortfall, index=0, priority=3, name="priority_shortfall") m.setObjectiveN(regular_customer_shortfall, index=1, priority=2, name="regular_shortfall") m.setObjectiveN(total_cost, index=2, priority=1, name="total_cost")
This tells Gurobi to optimize the objectives in priority order:
- minimize priority customer shortfall,
- then minimize regular customer shortfall,
- then minimize cost.
This is useful when not all soft constraints are equally important.
Interpreting the solution
After optimization, the violation variables provide a direct explanation of the solution.
For demand shortfall variables:
for c in customers:
if shortfall[c].X > 1e-6:
print(f"Demand for customer {c} was short by {shortfall[c].X:g} units")For other soft constraints, use the corresponding violation variables in the same way. The key point is that each violation variable should have a clear business meaning.
For example:
-
shortfall[c]= unmet demand for customerc, -
late[j]= lateness for jobj, -
understaffed[s]= staffing shortage for shifts, -
below_target[p]= missed production target for productp.
These variables make the model's feasibility trade-offs visible to the user.
Important modeling considerations
Only soften constraints that can legitimately be violated. Do not add violation variables to constraints that define the physical, logical, or legal validity of the model.
Use units that are meaningful to the user. If a violation variable measures unmet demand, it should be expressed in the same units as demand. If it measures lateness, it should be expressed in a time unit.
Be careful when combining different types of violations in the same objective. For example, one unit of unmet demand and one hour of lateness may not be comparable. In such cases, use separate objectives with different priorities, or use carefully chosen weights within a priority level.
The same issue can occur even when violations have the same units. For example, one unit of unmet demand for a high-priority customer may be more important than one unit of unmet demand for a lower-priority customer. You can model this either by assigning the corresponding violation expressions to different objective priorities, or by using weights within the same priority level.
Gurobi supports degradation tolerances for multi-objective optimization. This means lower-priority objectives may be allowed to degrade higher-priority objective values by specified absolute or relative amounts. For details, see the allowing multi-objective degradation.
Gurobi also supports multi-objective environments, which make it possible to set certain parameters separately for each objective pass. Parameters can also be set globally on the model. This can be useful when, for example, you want to spend more time proving optimality for the feasibility-related objectives than for the final business objective, or vice versa. For details, see the Gurobi documentation on multi-objective environments.
Also note that Gurobi multi-objective models require linear objectives. If the original business objective is quadratic or nonlinear, it may need to be reformulated or handled with a different approach.
How is this different from feasRelax?
Gurobi also provides feasibility relaxation routines that can modify an infeasible model by introducing artificial variables and constraints that allow selected bounds or constraints to be violated.
Feasibility relaxation is useful when you already have an infeasible model and want Gurobi to construct a relaxation automatically. It also provides useful modeling concepts for penalty design. In particular, the relaxobjtype argument controls how violation cost is measured:
-
relaxobjtype=0minimizes the weighted sum of violation magnitudes, -
relaxobjtype=1minimizes the weighted sum of squared violation magnitudes, -
relaxobjtype=2minimizes the weighted count of violations.
These options are closely related to choices you may need to make when designing soft-constraint violation objectives. For example, you may want to minimize total violation magnitude, penalize larger violations more heavily, or minimize the number of violated requirements.
The main difference is that feasibility relaxation is typically applied after a model is found to be infeasible. The soft-constraint multi-objective approach is built into the model from the start. This gives you more control over the meaning of each violation variable and often produces clearer explanations for end users.
For details, see the Gurobi documentation on feasibility relaxation.
Summary
If some model requirements are not truly mandatory, consider modeling them as soft constraints and minimizing their violations with Gurobi's multi-objective optimization features.
This approach can help avoid infeasible models, make trade-offs explicit, and produce solutions that are easier to explain. The main benefit is not just that the model avoids an INFEASIBLE status. The violation variables become part of the model's reporting layer: they directly identify which soft constraints could not be satisfied and by how much.
The lower-priority business objective still optimizes the original goal after the feasibility-related objectives have been addressed.
Related resources
- Gurobi Optimizer Reference Manual: Multiple Objectives
- Gurobi Optimizer Reference Manual: Multi-objective attributes
- Gurobi Python example: multiobj.py
- Gurobi Optimizer Reference Manual: Python
Model.setObjectiveN - Gurobi Optimizer Reference Manual: Python
Model.feasRelaxS - Gurobi Optimizer Reference Manual: Infeasibility analysis