Constraint Programming and Global Constraints
Constraint programming (CP) is useful when the decisions are naturally discrete and the rules are easier to state as relations than as a large collection of linear rows. A CP model declares finite domains, global constraints, and an objective. The solver then combines propagation, search, and objective bounds to find or prove a schedule, assignment, packing, or routing plan.
This page is a mathematical tutorial. It describes the meaning that an OSPF model should preserve; it is not a list of Kotlin or Rust API calls.
1. When finite-domain CP is a good fit
Use finite-domain CP when a decision has a small or explicitly bounded set of alternatives:
- a start time measured in integer minutes or shifts;
- a worker, room, vehicle, or machine selected from a finite set;
- a sequence position, color, or rank;
- an optional activity with a presence decision.
CP is especially expressive when several decisions interact through global rules. A single AllDifferent constraint can describe a permutation, NoOverlap can describe a disjunctive machine, and Cumulative can describe a renewable resource profile. The global form communicates the business rule directly and can let a CP solver propagate more strongly than an ad-hoc encoding.
Finite does not mean that every possible value must be listed one by one. A domain such as
2. Core definitions
2.1 Finite-domain variables
A finite-domain variable
An interval notation such as
2.2 AllDifferent
For variables
It is often used for worker slots, permutation positions, or one-to-one assignments. It does not by itself say that every value in a larger universe is used, nor does it say that a task is assigned to exactly one resource unless the variable domains and other constraints give that meaning.
2.3 NoOverlap
For a positive-duration interval activity
The usual interval is half-open,
Thus one activity may finish exactly when another starts. If a model admits zero-duration activities, it must state separately whether their empty intervals participate in ordering or resource conventions. If activities are optional, the semantic definition also includes their presence literals: two absent activities impose no ordering, and an absent activity contributes no occupied time.
2.4 Cumulative
Let activity
For integer time, it is enough to check each relevant time bucket. A cumulative resource can allow overlap: two activities of demand one may run together on a capacity-two resource, while an activity of demand two cannot overlap either of them if the capacity is two.
3. Worked example: a small resource-constrained schedule
3.1 Business statement and data
Four jobs must be scheduled. Starts are integer time units. Jobs
| job | duration | power demand | worker domain |
|---|---|---|---|
The start domains are
3.2 Variables, intermediates, and constraints
Decision variables:
Derived end variables are defined by
The rules are
The last row is an epigraph for the interval ends. Under the stated minimization objective, its optimal value equals
The power rule is not a second machine-order rule. It is a time-indexed capacity rule: power alone would allow NoOverlap and because their combined demand would be
3.3 A hand-checkable candidate
Consider the following values:
| job | ||||
|---|---|---|---|---|
The worker values are AllDifferent holds. The machine intervals for
The resource profile is easy to check by time bucket:
| time bucket | active jobs | load |
|---|---|---|
Every load is at most
3.4 Why is optimal
The two machine activities have a disjunction. If
If
4. Propagation is part of the value of the global form
A solver may derive consequences before branching. For this example, choosing
It is important to distinguish:
- the original global constraint, such as
NoOverlap; - any backend representation used to solve it;
- evidence returned to a user, such as the occupied intervals and maximum resource load.
If a global constraint is expanded into pairwise rows or auxiliary variables, those rows are implementation details. A diagnostic report should still identify the original machine or power rule.
5. Conceptual organization in OSPF
An OSPF model can organize this problem around semantic responsibilities:
- An input or scheduling context owns job durations, resource demands, worker availability, and the time unit.
- A decision context owns starts, ends, assignments, optional presence, and the finite domains that make those decisions meaningful.
- A rule context owns precedence, machine disjunctions,
AllDifferent, and cumulative capacity. It should expose the original business identity of each rule. - An objective context owns the makespan definition and direction.
- A result context records the witness values, objective value, feasibility or optimality status, and any bound or tolerance used for interpretation.
The compiler boundary can translate the semantic global constraints to a backend representation when that backend supports them, or to an exact reformulation when one is appropriate. The translation must preserve interval endpoints, presence semantics, domains, and constraint identity. The compiler architecture page gives the broader separation between semantic models and backend mechanisms, while solving results describes what a result should prove and what it should leave unknown. This list is a conceptual organization, not a claim that each item is a current public class.
For critical-constraint explanations, report the original NoOverlap, Cumulative, or precedence rule rather than an arbitrary internal row. A global rule may have no single scalar slack. See critical constraint analysis for the distinction between activity, effectiveness, and a target-relative blocking explanation.
6. Common modeling pitfalls
6.1 Mixing closed and half-open intervals
With
6.2 Declaring a domain that is too small or accidentally continuous
A start domain ending at
6.3 Forgetting presence semantics for optional work
An optional interval needs a presence decision. Its end relation, precedence, machine occupancy, and cumulative demand must be conditioned on presence. Merely giving an interval a nullable label while leaving its constraints unconditional can make an absent job consume capacity.
6.4 Confusing AllDifferent with assignment coverage
AllDifferent(w_i) says that the selected worker values differ. If five jobs may use ten workers, it does not require every worker to receive a job. If a worker can process several non-overlapping jobs, an AllDifferent assignment rule may be too strong; use a resource or per-worker NoOverlap semantics instead.
6.5 Treating cumulative capacity as a pairwise rule
Pairwise non-overlap is stronger than a capacity profile in some cases and weaker in others. A capacity-three resource permits three demand-one jobs together, but a pairwise rule would incorrectly forbid them. Conversely, a demand-two job and a demand-one job can overlap on capacity three even though two machine operations might still require NoOverlap for a separate reason.
6.6 Using an objective to repair an incomplete constraint
Minimizing
6.7 Assuming every global constraint has a useful slack
The numeric residual of one internal linearization row is not a universal slack for AllDifferent or NoOverlap. Use semantic evidence such as a conflicting pair, a maximum resource load, or a violated domain, and label activity separately from objective impact.