The weakest precondition calculus, developed by Edsger Dijkstra, mechanizes backward reasoning about program correctness. For a command C and postcondition Q, the weakest precondition wp(C, Q) is the least restrictive condition on the initial state that guarantees Q after C executes. Any valid precondition P for the triple {P} C {Q} must imply wp(C, Q). The calculus provides recursive rules for computing wp through each language construct, turning correctness proofs into systematic predicate transformations rather than creative search for invariants.
Hoare logic proves program correctness using triples {P} C {Q}, but finding the right precondition P for a given command and postcondition often requires ingenuity. Dijkstra's weakest precondition calculus (1975) systematizes this by defining a function wp(C, Q) that computes the weakest (most general) precondition guaranteeing postcondition Q after executing command C. "Weakest" means that any other valid precondition must logically imply wp(C, Q) — it accepts exactly the initial states from which C is guaranteed to establish Q.
The calculus defines wp recursively for each language construct. For assignment: wp(x := E, Q) = Q[x/E], substituting E for x in Q — identical to Hoare logic's assignment axiom but now framed as a computable function. For sequencing: wp(C1; C2, Q) = wp(C1, wp(C2, Q)), composing from right to left. For conditionals: wp(if B then C1 else C2, Q) = (B implies wp(C1, Q)) and (not B implies wp(C2, Q)). Each rule is purely mechanical: given a postcondition, you propagate it backward through the program text to obtain the precondition.
The approach breaks down at loops. For `while B do C`, there is no fixed unwinding because the loop may execute arbitrarily many times. Computing wp requires a loop invariant I — an assertion that holds before every iteration and, combined with the loop's exit condition (not B), implies the desired postcondition. The calculus can verify that a proposed invariant works (check that wp(C, I) is implied by I and B, and that I and not B implies Q), but it cannot discover the invariant automatically in general. This is the point where verification requires either human insight or heuristic techniques like abstract interpretation or invariant generation.
Dijkstra conceived the weakest precondition not merely as a verification tool but as a program design methodology. By starting from the desired postcondition and computing backward, the developer derives what each statement must accomplish, potentially discovering the program's structure rather than verifying it after the fact. This "calculative" approach to programming influenced the development of refinement calculus and correct-by-construction software development.
The weakest precondition calculus is the theoretical backbone of modern automated verification tools. Systems like Boogie, Why3, and Dafny compute verification conditions by propagating weakest preconditions backward through annotated programs, then discharge the resulting logical obligations to SMT solvers. The human provides loop invariants and function contracts; the tool handles the mechanical predicate-transformer computation. This division of labor — human insight for invariants, machine computation for everything else — remains the dominant paradigm in deductive program verification.