Mathematical programming / Linearization

Logical Constraints (AND / OR / XOR)

Encode boolean logic between binary decisions — "at least one of", "exactly one of", "not both" — as linear constraints.

Note Intermediate

Short note. A compact reference, correct as far as it goes. It will be expanded into a full guide with a worked example and an interactive demonstration.

Problem

Real decisions come with rules in plain English: “open at most one of these three warehouses,” “if you hire this contractor, you can’t also hire that one,” “at least two of these five upgrades must happen.” Each maps to a different linear pattern over binaries.

When to use it

Whenever a requirement is naturally phrased as a boolean relationship between decisions rather than a numeric bound.

Formulation

Given binaries y_1, ..., y_n:

OR (at least one true):        sum(y_i) >= 1
AND (all true):                 y_i = 1 for all i   # rarely needed as a "constraint"
AT MOST ONE:                    sum(y_i) <= 1
EXACTLY ONE:                    sum(y_i) = 1
NOT BOTH (y_1, y_2):             y_1 + y_2 <= 1
IMPLICATION (y_1 => y_2):        y_1 <= y_2
XOR (exactly one of two):        y_1 + y_2 = 1

These compose — most “business rule” constraints in a real MILP are just combinations of these primitives layered together.

Code sketch

import pulp

prob = pulp.LpProblem("facility_rules", pulp.LpMinimize)
open_a = pulp.LpVariable("open_a", cat="Binary")
open_b = pulp.LpVariable("open_b", cat="Binary")
open_c = pulp.LpVariable("open_c", cat="Binary")

# at most one of A, B, C
prob += open_a + open_b + open_c <= 1

# if A opens, B must not open (mutually exclusive sites)
prob += open_a + open_b <= 1

Pitfalls

  • It’s tempting to encode complex nested logic (“(A or B) and not C”) in one giant constraint. Don’t — decompose into the primitives above; it stays readable and it’s what the presolver expects.
  • Watch for redundant constraints once you have several rules — an “at most one” combined with an “exactly one” on overlapping sets can silently make the model infeasible. Validate with a small feasible instance before scaling up.