EconCSLib.MarketDesign.Matching.GaleShapley #
Standard Gale-Shapley deferred-acceptance algorithm for two-sided matching markets.
Replaces the earlier batched-proposal (Boston-mechanism) gs_aux that committed
women in round 1 with no ability to upgrade — verified unstable via #eval (commit
1e14bf9: blocking pair (b, A) on the n=3 counterexample).
Scope (MT-L0 + MT-L1, #202 + #203) #
GS.Preferences n— list-based preference type for the algorithmic layer.- Standard DA:
GS.daStep,GS.daRun,GS.finalState,GS.gs. gs_bijective— proved via injectivity + pigeonhole.galeShapley_isStable— stability via the standard deferred-acceptance argument.- Bridge types:
MatchingMarket.ofEquivData,Matching.ofGS.
Algorithm design #
Standard men-proposing DA with explicit state (nextChoice, holding):
holding j = some i: womanjcurrently holds manitentatively.nextChoice i = k: mani's next proposal is(m.prefs i)[k].- A man is free when no woman holds him.
- Each step: every free man proposes his next-best woman; each woman keeps the best of {held, new proposers} by her preference list rank; free men advance nextChoice.
- Termination: structural recursion on fuel
n*n + 1. At mostn*nproposals total (n men × n proposals each).
Key invariants #
All preserved by every daStep (preservation lemmas listed in parentheses).
- Rank monotonicity — a woman's held man's rank only decreases over time;
once she upgrades, she never downgrades (
holding_rank_mono_step/_run). - Holding injectivity — at most one woman holds any given man at any
time (
holding_injective_step/_run). JInv— proposed ⇒ held: every woman who has ever been proposed to is currently held by someone (jinv_step).HoldInv— held ⇒ proposed: every currently-held pair(p, j)hasjhaving already proposed top(holdinv_step).RSInv— the deferred-acceptance rank invariant (the stability engine): every woman a man has proposed to is currently holding someone at least as preferred as that man (rsinv_step).- At termination: all men held → all women hold someone (by pigeonhole on
holding_injective).
Proof strategy (from primitives to stability) #
initState
│
│ daStep (preserves all six invariants)
▼
finalState
│ │ │
│ │ └─► holding_injective ──┐
│ │ ├─► gs_bijective ──┐
│ └─────► all men held ─┘ │
│ ▼
└─────► RSInv@finalState ──► no blocking pair (stability)
│ └────── HoldInv@finalState
│
└── relies on rank-mono + JInv
Roughly: cursors and holdings stay in two-sided correspondence (JInv
HoldInv); a held pair only upgrades (holding_rank_mono); at most one woman holds any man (holding_injective); cursors are bounded byn(nc_le_n_run) and grow monotonically (nc_sum_grows), so the fueln*n + 1always suffices for termination (finalState_no_free_men); and at terminationRSInvrules out blocking pairs (rsinv_stability).
References #
- [Gale-Shapley 1962] Am. Math. Monthly 69(1):9-15 — original existence proof.
- [Roth-Sotomayor 1990] Two-Sided Matching, Ch. 2-3 — textbook treatment of deferred acceptance and stability.
- [MSZ Ch.22, Alg 22.6 + Thm 22.7] Maschler, Solan, Zamir, Game Theory — the deferred-acceptance algorithm and its stability theorem.
Preference type #
Helper lemmas #
DA state #
Free-man predicate #
Proposal target #
propTarget returns some when k < n.
DA step #
One step of standard men-proposing DA.
All free men propose; women pick the best of {held, new proposers} by rank; free men advance their cursor by 1.
Equations
- One or more equations did not get rendered due to their size.
Instances For
One step of DA: woman p's new holding, expanded once.
This is the workhorse rewrite for any proof that needs to case-split on
what (daStep w m s).holding p is. It unfolds the outermost match
inside daStep's newHolding. The statement does inline the auxiliary
proposerList / bestNew formulae verbatim; downstream proofs re-abbreviate
them with set after rewriting (see holdinv_step).
The match shape is:
match s.holding p, bestNew p with
| none, none => none -- still free
| some h, none => some h -- old hold kept
| none, some q => some q -- new proposer
| some h, some q => if rank q < rank h then q else h -- upgrade?
where bestNew p = (proposerList p).argmin (rank in w.prefs p) and
proposerList p is the list of free men who proposed to p this step.
Provable by rfl because daStep is noncomputable def and the body
is a sequence of lets ending in a structure literal whose .holding p
projects directly to the inner match.
DA run (structurally recursive, no WF needed) #
finalState: run daRun with fuel n*n + 1 from initState.
Equations
- GS.finalState w m = GS.daRun w m (n * n + 1) (GS.initState n)
Instances For
Bridge to EconCSLib native types #
Build a MatchingMarket (Fin n) (Fin n) from list-based preference data.
some j ≻ some k iff j appears earlier in the list; none is worst.
Side transposition (read carefully). ofEquivData a b puts a on the
market's MEN (prefM) and b on the market's WOMEN (prefW). Hence when it
is applied as ofEquivData w m with the algorithm's w (women's / choosing
preferences) and m (men's / proposing preferences), the market's "men" are
the algorithm's women and the market's "women" are the algorithm's men —
the two sides are transposed. Stability is symmetric, so this is
harmless, but be aware that a market-M index corresponds to an algorithm
woman (and vice versa); the stability proof reasons in the algorithm frame.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Coerce a bijective f : Fin n → Fin n into Matching (Fin n) (Fin n).
Equations
- Matching.ofGS f hf = { matchM := fun (w : Fin n) => some (f w), matchW := fun (m : Fin n) => some ((Equiv.ofBijective f hf).symm m), consistent := ⋯ }
Instances For
Correctness (MT-L1 #203) #
After one daStep, woman j's held man's rank can only decrease (she only upgrades).
Rank-mono over any daRun.
Invariant 3: fuel sufficiency #
The following private lemmas are defined outside section GS_Correctness to avoid
implicit section variable clutter. All parameters are explicit.
Proposal rank bound: the core of Roth-Sotomayor #
Roth–Sotomayor rank invariant #
HoldInv (Held → Proposed): if woman p currently holds man j, then
j must already have proposed to p — i.e., p is in the proposed-prefix
(m.prefs j).take (s.nextChoice j) of j's preference list.
This is the converse direction of [JInv]: JInv says proposed ⇒ held;
HoldInv says held ⇒ proposed. Together they make the two-sided
correspondence between cursors and holdings airtight.
HoldInv is the one the stability proof's "the holder must be at least as
preferred as the blocker" step relies on, via RSInv — see the proof sketch
of [galeShapley_isStable] and the preservation lemma [holdinv_step].
Equations
Instances For
HoldInv is preserved by one DA step.
Proof structure: case-analyze on the pair (s.holding p, bestNew p) that
drives daStep's decision for woman p. There are four cases, two of
which (old hold preserved and new free-man wins) recur in mirrored
form depending on whether s.holding p was some h or none. We
extract those two arguments into the local helpers preserve_hold and
new_hold and dispatch each of the four cases with a single line.
Throughout, the key external lemmas are:
pl_free_props— anyone inproposerList pwas free and proposed top.daStep_nc_free/daStep_nc_held—nextChoice jadvances by 1 ifjwas free, stays put otherwise.not_isFree_iff—jis not free insiff some woman holdsj.
Invariant 3: termination #
At termination, every man is held (no free man remains).
Proof: if a free man remained, nc_sum_grows would give n*n + 1 ≤ n*n, contradiction.
Total matching #
At termination, every man is held by some woman.
finalState holding is injective.
At termination, every woman holds some man.
Bijectivity #
Stability #
Gale-Shapley stability: the DA output has no blocking pair.
The statement is in the market frame (IsBlocking … (i : M) (j : W)). But
MatchingMarket.ofEquivData w m transposes the two sides (see its
docstring), so the market-M index i is an algorithm woman and the
market-W index j is an algorithm man. The proof therefore reads in the
algorithm frame — i a woman, j a man — matching the inline comments.
Proof (standard deferred acceptance): suppose (i, j) blocks. In algorithm
terms man j strictly prefers woman i to his wife, so j proposed to i
at some round: his wife is in his proposed-prefix (holdinv_finalState) and
i is ranked at least as early (hprefJ). Then rsinv_stability (RSInv)
gives that at termination woman i holds a man she ranks at least as high as
j, so she does not strictly prefer j to her partner — contradicting the
blocking assumption.