Skip to content

Automatic Critical Constraint Analysis ​

A solver answers “What is the best plan?” Critical constraint analysis asks “Why can it not be better?” It separates boundary features of a solution, benefits from changing business rules, and combinations of conditions that prevent a target. The approach applies to constraint programming (CP), mixed-integer linear programming (MILP), and models with an exact linear reformulation.

This page explains the mathematics, analysis workflow, and interpretation of reports. Workflows and reports are conceptual examples, not calls to specific Kotlin/Rust APIs.

1. Three levels of evidence ​

First review solutions, bounds, and statuses. If the original model has no solution, start with Infeasibility Analysis and Constraint Relaxation; this chapter focuses on limits to better targets in a feasible model.

LevelQuestionMethodsScope of the conclusion
ActivityWhich constraints are at or near a boundary at this solution?Slack, boundary distance, semantic evaluationA feature of this solution, not proof of objective impact
EffectivenessDoes relaxing a constraint produce a benefit?Fixed-integer LP, finite perturbation, removal and reoptimizationDistinguishes a fixed integer pattern from the full model
Joint blockingWhich constraints jointly prevent a specified target?Target feasibility, conflict cores, minimal unsatisfiable subsetsCombination-level evidence relative to an explicit target and background

Use inexpensive analysis to prioritize candidates, then reoptimize for important candidates. Use joint analysis to explain target unreachability. The first two levels are not prerequisites for the third: CP constraints without a natural slack or LP dual can go directly to target feasibility.

text
Freeze the baseline model and objective
    → Solution activity
    → Local sensitivity / single-constraint perturbation
    → Feasibility of a better objective target
    → Conflict extraction and shrinking
    → Explanation grouped by original business rules

2. Establish the baseline and objective direction ​

Let X be the original feasible set, f(x) the objective, and x0 a baseline feasible solution with value z0=f(x0). If optimality is proven, write z∗=z0; otherwise z0 is only an incumbent value.

Activity requires a trusted feasible solution, but a “cannot improve” explanation must be limited to a specific target proven unreachable. A better target may be reachable from an unproven incumbent. Even ruling out one improvement amount does not exclude smaller improvements.

An analysis session retains the model snapshot, objective identity and direction, baseline solution, solve status, bounds, and tolerances. Integer fixing, RHS changes, temporary removal, and target conditions belong to derived models, not the business model. Cache keys also distinguish model fingerprints, objectives, perturbations, background conditions, and solver settings to prevent conclusions from leaking across scenarios.

For multiple objectives, specify which objective is analyzed and whether the others are fixed as conditions, preserved lexicographically, or combined into a scalar objective. Do not mix benefits across objective levels without defining that policy.

3. Activity: is this solution on the boundary? ​

For aiTx≤bi, define signed slack:

si=bi−aiTx0.

For aiTx≥bi, use si=aiTx0−bi so that feasible-side slack is positive. For an equality, record the residual aiTx0−bi; satisfying an equality does not establish objective impact.

With tolerance εi appropriate to units and numerical scale:

si<−εi⇒violated,|si|≤εi⇒active,si>εi⇒positive slack.

A separate threshold can identify nearly active constraints with positive slack. Record its definition and normalization. If the baseline violates a constraint, resolve feasibility or numerical consistency first; a violation is not activity.

For example, x+y≤10 has zero slack at (4,6), whereas x≤100 has slack 96. These facts describe the location of that point, not the change in the optimum after relaxation.

Global constraints such as AllDifferent, NoOverlap, and Cumulative may not have a unique natural slack. Retain satisfied, violated, or unknown status, or use an explicitly defined semantic metric. Do not present the slack of internal linearization rows as a universal slack for the original global constraint.

4. Effectiveness: does relaxation improve the objective? ​

4.1 Local sensitivity with a fixed integer pattern ​

For a MILP, fix the integer variables at their current values:

xI=xI0.

If the remaining problem is an LP, solve it and obtain dual values πi. Within the applicable sensitivity range, objective sign convention, and basis conditions, a local RHS change can be interpreted through πiΔbi.

This concerns continuous adjustments with the integer pattern unchanged, not a global MILP shadow price. A zero dual does not exclude improvement through a different integer pattern after a larger perturbation. Degeneracy can also make dual solutions nonunique.

When the LP comes from an exact MIP reformulation, state which integer variables are fixed, including introduced discrete choices. Interpret internal row duals through their dependence on original parameters; do not arbitrarily sum them into a shadow price for a CP global constraint. Skip this level when no suitable LP representation exists.

4.2 Finite perturbation and full-model reoptimization ​

Relax an upper-bound constraint by bi↦bi+δ and a lower-bound constraint by bi↦bi−δ, with δ≥0. Equalities and global constraints need separately defined business changes, such as more capacity or a wider time window, rather than a universal “add to the RHS” operation.

Let zi(δ) be the perturbed optimum. Define positive gain consistently:

Gi(δ)={zi(δ)−z∗,maximize,z∗−zi(δ),minimize.

Integer variables may change during reoptimization, so the result is not limited to the original pattern. The ratio Gi(δ)/δ is an average gain over that perturbation, not a global derivative.

Call the difference a change in the optimum only when both optima are proven. With only a perturbed feasible solution, report observed gain and solver bounds; failure to observe a gain does not prove ineffectiveness. If the baseline is itself unproven, a better solution than its incumbent is not necessarily caused by relaxation.

4.3 Adaptive perturbation and removal ​

Try δ0,2δ0,4δ0,… until a benefit is found or the permitted business range or analysis budget is reached. For nested expanding feasible sets, reliable solve conclusions, and an explicit gain tolerance, refine an effective-threshold interval:

δi∗=inf{δ≥0:Gi(δ)>εz}.

Search discrete parameters over their allowed values. For continuous parameters, report an interval at the chosen precision. A timed-out solve is not evidence of “no gain” on one side of a binary search, and the gain function need not be smooth.

Removing a constraint entirely is a stronger comparison, but not necessarily an authorized business action. No gain after removal means only that removing it alone is insufficient. An unbounded result must be reported separately, not as an ordinary finite gain.

4.4 Example: an inactive constraint can still prevent improvement ​

Consider integer production quantity q:

maxqs.t.q∈Z≥0,c1:q≤10,c2:2q≤21.

The optimum is 10. Constraint c1 is active, while c2 has slack 1 and is inactive. Removing only c1 still leaves integer production at most 10; removing only c2 does the same. Relaxing both bounds to q≤11 and 2q≤22 allows production of 11.

Neither inactivity nor no gain from individual removal implies irrelevance to a bottleneck. Activity and local duals can prioritize analysis, but must not permanently exclude inactive constraints.

5. Turn objective improvement into feasibility ​

Instead of arbitrarily perturbing a multivariable solution by x0+ϵ, specify the desired improvement Δ>0:

τΔ(x)={f(x)≥z0+Δ,maximize,f(x)≤z0−Δ,minimize.

Then solve the satisfaction problem x∈X∧τΔ(x). “Transport at least 100 additional kilograms” is easier to interpret than “improve slightly.” A relative amount can use r|z0|, but a zero baseline requires another absolute scale. Integer objectives also require attention to attainable increments and tolerances.

ResultSupported conclusion
Reachable (SAT)A feasible plan meets the target and can be returned
Unreachable (UNSAT / Infeasible)The target is proven infeasible under the specified model and background
UnknownTime or resource limits, or another uncertainty, leave the question unresolved
UnsupportedThe selected path cannot express or handle the required semantics

Not finding a solution is not proof of unreachability. If the baseline model is already infeasible, diagnose infeasibility first rather than explain optimality.

6. From a conflict core to a minimal blocking set ​

Let B contain always-retained variable types and non-relaxable rules, C the diagnostic constraints, and τ the target. For an unreachable target, extract a conflict from B∧C∧τ and shrink it to M⊆C.

A target-relative minimal blocking set satisfies:

B∧⋀c∈Mc∧τis UNSAT,∀c∈M:B∧⋀d∈M∖{c}d∧τis SAT.

“Minimal” means inclusion-minimal: no member can be deleted. It does not mean globally minimum cardinality or uniqueness. If the background alone prevents the target, M can be empty. Reports must show the background and target, not just constraint names.

A backend unsat core or conflict set is not necessarily minimal. For an IIS, also specify whether irreducibility is defined over original business constraints, variable bounds, or internal solver rows. Mapping names alone does not establish an original-constraint-level MUS.

Deletion-based shrinking holds the target and background fixed and tries removing each candidate:

text
M = a confirmed blocking set
For each candidate c in M:
    Solve B ∧ (M without c) ∧ target
    If UNSAT is confirmed: remove c from M
    If SAT is confirmed: retain c and record the feasible witness
    If Unknown: retain c and mark minimality as unresolved

If uncertainty or budget ends the process, report a valid conflict without claiming proven minimality. Diagnostic activation must actually control the corresponding original constraint. Generated auxiliaries must not unintentionally keep restricting the model after their original rule is disabled.

6.1 Example: two capacities jointly block a target ​

Let x,y∈Z≥0 be production on two lines, with capacities cA:x≤5 and cB:y≤5. Maximize x+y. The optimum is 10, and target τ:x+y≥11 is infeasible.

Relative to the background that x,y are nonnegative integers, M={cA,cB} is a minimal blocking set. Without cA, (6,5) satisfies the remaining conditions; without cB, (5,6) does. The explanation is that the two capacities jointly limit total output, not that each constraint independently blocks the target.

7. Blocking explanations are not relaxation recommendations ​

Deleting any member of one minimal blocking set removes the obstruction from that set of conditions. It does not guarantee reachability in the full model: other constraints can form another conflict.

In the earlier integer-production example, target q≥11 is blocked separately by {c1} and {c2}. There are two singleton blocking sets, and removing just one is insufficient. This has a different business meaning from the two-member blocking set for the production lines.

A minimal correction set (MCS) instead identifies an inclusion-minimal set of permitted constraints whose removal from the full model restores target feasibility. It is a different object from a MUS. Minimum cardinality, minimum cost, and inclusion-minimality are also different objectives. Recommendations additionally need permitted relaxation ranges, costs, and approval conditions; diagnostic results must not directly execute rule changes.

8. Organizing analysis and business explanations in OSPF ​

Organize analysis around solver-neutral model semantics. Backend capabilities determine whether to use LP duals, full-model reoptimization, native conflict cores, or repeated satisfaction solving. Native CP global constraints without a suitable LP path can go directly to target feasibility and conflict analysis. Exact reformulations must also preserve original semantic identities.

Use stable ConstraintId, ObjectiveId, and constraint groups across derived models. Public evidence describes original constraints, variable bounds, sparse domains, and objective targets—not solver rows, auxiliary variables, or native handles. A global constraint expanded into many internal constraints still needs an explanation in terms of its original business rule.

Reports can start with domains or groups and expand into instances. However, grouping an instance-level minimal set does not automatically prove group-level minimality. Kotlin and Rust integrations follow the same objective-direction, status, tolerance, and evidence semantics without treating an optional backend capability as universal.

A conceptual report for the two production lines is:

text
Baseline: total output 10, proven optimal
Target: total output at least 11
Background: both production quantities are nonnegative integers
Activity: line A capacity and line B capacity are active
Target result: unreachable
Blocking set: {line A capacity <= 5, line B capacity <= 5}
Minimality: proven inclusion-minimal relative to the stated background and target
Business explanation: the two capacities jointly prevent total output of 11

Also retain model and objective identities, perturbation parameters, solve statuses, local-sensitivity scope, and uncertainty from incomplete analysis. Repeating analysis at several improvement amounts can reveal recurring constraints, but a finite sample is not a structural proof for every target level.

This lets a report explain where a plan is constrained while distinguishing observed benefits, evidence that a specific target is unreachable, and adjustments that still require business decisions. See Use Domain Driven Design Architecture for organizing the original constraints into business contexts.