Every operation requires . The queries have postconditions “return ” and “return ” respectively, leaving both functions unchanged. An incoming increment additionally requires and hasThe outgoing increment analogously requires , increments only , and preserves . These frame conditions prevent an implementation from accidentally changing another user's count. Here a subscript zero denotes the old program state.
trait Payments {
// All methods require 1 <= user && user <= 99999999.
// Queries leave the abstract state unchanged.
def incoming(user: Int): Int
def outgoing(user: Int): Int
// Require the corresponding old count < Int.MaxValue.
// Increment only that user's named count by one.
def recordIncoming(user: Int): Unit
def recordOutgoing(user: Int): Unit
}The preconditions and postconditions are part of the formal specification; the trait's type signatures alone cannot express all of them. The implementation below uses checked
require calls for the relevant invalid operations.Choose a fixed positive capacity and represent the abstract state by a finite partial function . Its domain of a partial function has at most members; each belongs to and is a fixed point:The blocks of a set partition are the nonempty fibres of
rep, and their shared values are their disjoint-set representatives. Initially , so . Representatives need not be the minimum members of their blocks.trait DisjointSets {
val capacity: Int
// State: rep is a finite idempotent map, |dom rep| <= capacity.
// Initially: rep is empty; capacity is fixed and positive.
// Pre: x is absent and |dom rep| < capacity.
// Post: add x -> x; leave every old mapping unchanged.
def makeSet(x: Int): Unit
// Pre: x belongs to dom rep.
// Post: state unchanged; return rep(x).
def find(x: Int): Int
// Pre: x and y belong to dom rep.
// Post: merge their blocks, retaining old rep(x).
def union(x: Int, y: Int): Unit
}Precisely,
makeSet(x) extends the domain of a partial function by with . For union(x,y), snapshot and , keep unchanged, and setThis merges the two blocks and keeps the representative of the first argument's block. If , it is an identity operation. This deterministic design choice satisfies disjoint set union without imposing minimum-element representatives. The capacity bound is explicit in the abstract data type so that the bounded implementation does not silently strengthen makeSet's precondition.A
Rel3 provides only a membership test for a supplied pair. It gives no way to enumerate the finite middle carrier Y, enumerate one element's relatives, or even obtain a first value of type Y. Finiteness alone supplies no generic enumeration operation. To answer the existential query for the composed relation, supply a complete finite enumeration of the middle carrier explicitly:def compose3[X, Y, Z](r: Rel3[X, Y], s: Rel3[Y, Z],
middleValues: List[Y]): Rel3[X, Z] = {
val witnesses = middleValues.distinct
new Rel3[X, Z] {
def related(x: X, z: Z): Boolean =
witnesses.exists(y => r.related(x, y) && s.related(y, z))
}
}The precondition is that
middleValues contains every admissible middle value, or at least every possible witness. Extra irrelevant values do not change the answer if the two membership tests are defined for them. The postcondition is precisely . .exists and && use short-circuit evaluation; with an empty middle carrier, the answer is false. Each query uses at most pairs of membership tests, apart from the one-time duplicate elimination.An alternative interface can expose
def rightDomain: List[Y] together with the first predicate, with an explicit completeness contract. Such an output position is incompatible with retaining a contravariant Y in that extended trait, so its variance must be reconsidered. Passing the enumeration separately leaves the safe base Rel3[-X,-Y] unchanged.The information gap is substantive. A black-box algorithm making finitely many membership queries cannot rule out a witness at an unexamined middle value: two relations can agree on every inspected pair while differing only by a fresh witness that makes the composition true. A finite but unenumerated carrier can be enlarged to include that witness. A complete explicit enumeration, a known traversable carrier, or an equivalent relatives-enumeration interface removes this obstruction. This is finite-domain composition from relation predicates, rather than a problem solvable merely by nesting the two Boolean-valued methods.
Plan 2026-10-24
In deterministic classical planning, a finite sequence of actions applicable successively from the initial state and ending in a state satisfying the goal. A STRIPS plan must respect every action's precondition set and both add and delete effects. A numerical bound on a possible plan's cost does not itself specify the action sequence.
Planning safety certificate 2026-10-25
An auxiliary planning fluent certifying that a safety condition has been checked for the current state in automated planning. A checking action requires facts establishing the condition and adds the certificate; the hazardous action requires that certificate.
Every action capable of invalidating the check must delete the certificate. Otherwise checking a safe configuration and then changing it could authorize an unsafe action using stale evidence. For river crossing, certify that the departure bank contains no incompatible pair, invalidate the certificate on every load, unload or crossing, and require it before sailing. This compiles a safety test into positive STRIPS preconditions when a suitable finite static compatibility relation is supplied.
Postcondition 2026-10-18
The condition required of a final program state when a program finishes under its precondition. The summation loop verification requires returned value to satisfy and preserves the input . Relating the answer to the original input matters: allowing the program to replace by zero would change the intended contract.
A postcondition alone gives no promise of algorithm termination. It also need not specify a unique output: allows many answers, while the exact triangular number equation determines one integer answer for each permitted . Stronger postconditions express more obligations; proving only a weaker consequence cannot justify the stronger requirement.
Precondition Created 2026-10-18 Updated 2026-10-25
The condition required of a starting program state for a stated correctness guarantee. In the summation loop verification, and admit and , but exclude . The accompanying model assumes exact integer arithmetic and terminating elementary operations. The initial values of need no restriction because initialization overwrites them before use.
A precondition is a logical assumption, not automatically an executable check. Writing
require in a procedure states its contract; an implementation that checks or rejects invalid inputs needs that behavior specified too. Weakening a precondition admits more starts and therefore demands more proof: dropping here makes a terminating counterexample to , since initialization returns zero while . Precondition-free STRIPS planning 2026-10-24
Automated planning with ground actions that are always applicable because their precondition sets are empty. Delete effects are still permitted, so taking the union of all add effects does not decide reachability. With a positive conjunctive goal, the last-achiever algorithm for precondition-free planning gives a polynomial-time algorithm.
Prefix maximum correctness example 2026-10-18
Given a nonempty finite sequence of integers, return its maximum value. Represent the sequence by an array whose contents remain unchanged during execution. The precondition requires an integer length and exact, terminating integer comparisons and variable assignments. The postcondition for returned value isThus the output belongs to the finite set of entries and is at least every entry. The contract permits ties and negative integers; it asks for a value, not the position of a particular occurrence. There is no maximum of an empty set of integers, so empty input is deliberately outside this precondition. A service accepting empty inputs needs a different formal specification, such as a separate absent result.
This Python implementation makes the variable assignments explicit. Its index arithmetic models exact integer arithmetic; fixed-width implementations additionally need enough index range for :For the original input , initialization gives and . Reading keeps ; reading changes it to ; reading and keeps . At successive condition checks, the pairs are . The returned maximum is .
def find_max(a):
n = len(a)
best = a[0]
k = 1
while k < n:
if a[k] > best:
best = a[k]
k = k + 1
return bestAt each condition check, use the loop invarianttogether with the fact that the input array is unchanged. The prefix is the first entries of the sequence, so its finite set of values is nonempty. The maximum need not be unique as an occurrence: the invariant concerns its value.
Initialization establishes : and , the maximum of a one-element set. For preservation, suppose and hold. Then is a valid array entry. Either the comparison replaces with the larger value , or it keeps a value at least . Before increasing , the resulting value isThe variable assignment restores the complete loop invariant. Between the two variable assignments, the old prefix formula may temporarily be false; the loop invariant is required at condition checks, not at every instruction.
At exit, , while gives . Hence , and gives exactly the required postcondition. This establishes partial correctness. The preservation argument is mathematical induction on the number of completed iterations, covering every permitted integer sequence, rather than just the pictured input.
For algorithm termination, the loop variant is a nonnegative integer whenever holds. If , then . Every body terminates under the precondition and changes to . Starting at , it reaches zero after exactly iterations. For , the initial value is already zero and the body is never entered. Together with partial correctness, this proves total correctness.
Starting at zero instead of fails on the original negative input : it returns zero, which is neither an entry nor the maximum . Keeping forever preserves the correct initial prefix claim but loops forever when ; a loop invariant alone does not ensure algorithm termination. Using
>= instead of > still returns the same maximum value when entries tie, although a version returning a position would need an explicit tie rule. Sequential SATPlan correctness 2026-10-25
A sequential SATPlan encoding has a complete initial assignment of planning fluents, final goal constraints, action precondition axioms, successor-state axioms and action exclusion axioms allowing at most one action per layer. Zero selected actions is an idle step: the successor-state axioms preserve all facts. This encodes an upper bound on plan length rather than forcing exactly physical actions.
For soundness, read selected ground actions in layer order. The initial assignment is the real initial state. By mathematical induction, each selected action has its preconditions in the current state and the successor-state axioms give precisely its add/delete update; an empty layer leaves that state unchanged. The final goal constraint therefore proves that the decoded plan succeeds. Conversely, take any plan of length at most , assign its actual state trajectory to the fluent variables, select its actions in successive layers and pad with idle layers. All encoding components hold. Complete initial negations and persistence conditions are essential: omitting either can introduce unsupported facts and spurious plans.
STRIPS 2026-10-24
A representation for automated planning in which a state is a set of true ground facts and an action has a positive precondition set , add set and delete set . Under the closed-world assumption, facts absent from are false. The action applies when and producesNegative effects are delete-list entries; they do not require negative preconditions. A movement action must delete an object's old location as well as add its destination. Capacity and safety restrictions require explicit action conditions or auxiliary planning fluents; they do not follow automatically from an intended physical interpretation.
Subtype polymorphism Created 2026-10-25 Updated 2026-10-31
Subtype polymorphism lets a value implementing a more specific type be used through a common supertype object interface. Calls to overridable instance methods can select implementations by dynamic dispatch. Behavioral subtyping additionally requires the concrete implementation to honor the common contract, including preconditions, postconditions and state invariants; structural access to object method names alone does not establish that guarantee.
Summation loop verification Created 2026-10-18 Updated 2026-10-31
Compute , the triangular number , for a nonnegative integer . The empty sum gives zero when . Use exact mathematical integers and keep the input unchanged; every comparison and variable assignment is a terminating operation. The original sequential procedure isThe order of the two variable assignments matters: the addition uses the newly incremented . For , the pairs at successive condition checks are . At the last check, is false, and the returned sum is .
require n is an integer and n >= 0
i = 0
s = 0
while i < n:
i = i + 1
s = s + i
return sThe precondition is with , under the exact-arithmetic model just stated. The postcondition for returned value is , with still equal to its starting value. At each condition check use the loop invariantand the loop variant . The equation says that the accumulator is the triangular number for precisely the processed prefix ; the inequalities connect that prefix to the loop's condition.
The summation loop restores its invariant before each condition check
. Original sequential control flow for the summation loop verification. The blue loop head carries the loop invariant; the orange body increments before adding it to . A false condition leads to the postcondition. The loop variant decreases on the return arrow. All values are mathematical integers and remains unchanged. Details.Initialization gives , so holds, including when . For preservation, let the old values satisfy and . Write and for the values after both variable assignments. Since are integers, gives , andThus the completed body restores . Between the two variable assignments, the equation can be false; the loop invariant is required at the loop head. This preservation step is mathematical induction on the number of completed iterations.
At exit, the false condition gives , while gives . Hence , so : this proves partial correctness. For algorithm termination, gives the nonnegative integer . When the condition is true, ; the terminating body gives . Therefore the loop cannot repeat forever. Together these arguments prove total correctness for every permitted input, including .
Two changes expose what the proof checks. Swapping the two body instructions computes , returning rather than for . Leaving unchanged can preserve an initial equation forever without producing any answer. The first change breaks the intended postcondition; the second breaks algorithm termination. A trace can reveal these failures, while the two proof obligations explain them.
The exact-arithmetic assumption has a concrete boundary. In a stipulated unsigned eight-bit model where each addition wraps modulo , fits in the input representation, but its intended triangular number is . The stored accumulator instead finishes at , so the mathematical postcondition fails. This is integer overflow, and the proof above does not apply to that altered model. Restricting the same implementation to prevents overflow in its stored loop values, since ; alternatively use a representation supporting the required exact integer arithmetic. The equations in the proof are mathematical statements, not instructions to evaluate products in eight bits.
Total correctness 2026-10-18
Total correctness requires partial correctness and algorithm termination for every program state allowed by the precondition. For a deterministic sequential program, every permitted start must reach a final program state satisfying the postcondition after finitely many steps. Thus an infinite execution can satisfy partial correctness, but refutes total correctness for a permitted start.
In the summation loop verification, the loop invariant establishes the answer conditional on exit, and the loop variant establishes exit. Both parts matter: replacing the body by a step that never returns would defeat total correctness even if every completed iteration preserved the loop invariant. Likewise, a terminating loop with the wrong sum fails partial correctness and therefore fails total correctness.
These obligations depend on the formal specification. A program that rejects every input might correctly implement a rejection service, but does not meet a contract promising a sum for every nonnegative integer. A proof of total correctness also gives no particular physical response-time guarantee: time complexity and the execution environment require their own assumptions.
Type signature 2026-10-24
The type-level description of a value or operation, including parameter and result types. A type signature can rule out some invalid uses, but ordinary type annotations alone do not express every precondition, postcondition or relationship to an abstract program state.
Well-founded progress in recursion 2026-10-18
A base case and a decreasing natural number measure work together. Check that every invocation satisfies its precondition, each base case finishes with the correct output, and every other invocation makes finitely many recursive calls with strictly smaller measure. All local work must also terminate. By well-founded induction, the children finish, so their caller finishes; if their answers are correct and the combination rule preserves the postcondition, the caller is correct too. This proves total correctness, not just correctness conditional on return.
Strict decrease without a lower bound is insufficient: subtracting one forever from a negative integer never reaches zero. Even a positive real-valued measure can decrease indefinitely, as shows. A well-founded relation rules out that descent. In structural recursion, a proper child subtree of a finite binary tree supplies progress without numeric arguments. The proof obligation is the relation between parent and child instances, together with terminating local work and complete base cases.


