Skip to content

Research · Lean source

StrategicPitch.lean

The file exactly as Lean, a program that checks every step of a proof, checks it: 51 theorems in 497 lines.

What is proved, and what is not

import Std

/- Conditional finite model, Lean 4.24.0 / Std only.
   Root is sole compiler/runner. Finite reporting game, not a repeated game.
   No economic value is treated as an auditable fact. -/
namespace StrategicReporting

inductive Action where
  | claim | record | silent
  deriving DecidableEq, Repr

structure Strategy where
  qualified : Action
  unsupported : Action
  deriving DecidableEq, Repr

def Strategy.at (s : Strategy) (qualified : Bool) : Action :=
  if qualified then s.qualified else s.unsupported

structure Game where
  prior : Rat
  benefit : Rat
  qQualified : Rat
  qUnsupported : Rat
  fineQualified : Rat
  fineUnsupported : Rat
  claimBurden : Rat
  disclosureUtility : Rat
  auditRate : Rat
  deriving DecidableEq, Repr

/-- Flag exclusion and sanction enforcement are separate. The private fine
    is expected disutility; it is never counted as sponsor revenue. -/
def auditGame (prior benefit audit falsePositive sensitivity enforcement penalty
    claimBurden disclosureUtility : Rat) : Game :=
  { prior := prior
    benefit := benefit
    qQualified := 1-audit*falsePositive
    qUnsupported := 1-audit*sensitivity
    fineQualified := audit*falsePositive*enforcement*penalty
    fineUnsupported := audit*sensitivity*enforcement*penalty
    claimBurden := claimBurden
    disclosureUtility := disclosureUtility
    auditRate := audit }

structure Valid (g : Game) : Prop where
  prior0 : 0 ≤ g.prior
  prior1 : g.prior ≤ 1
  qualified0 : 0 ≤ g.qQualified
  qualified1 : g.qQualified ≤ 1
  unsupported0 : 0 ≤ g.qUnsupported
  unsupported1 : g.qUnsupported ≤ 1
  benefit0 : 0 ≤ g.benefit

def eligible (g : Game) (qualified : Bool) (a : Action) : Rat :=
  match a with
  | .claim => if qualified then g.qQualified else g.qUnsupported
  | .record | .silent => 0

def rivalMass (g : Game) (s : Strategy) : Rat :=
  g.prior * eligible g true s.qualified +
  (1-g.prior) * eligible g false s.unsupported

def claimValue (benefit q fine burden rival : Rat) : Rat :=
  benefit*q*(1-rival/2)-fine-burden

def utility (g : Game) (qualified : Bool) (a : Action) (rival : Strategy) : Rat :=
  match a with
  | .claim =>
      claimValue g.benefit (if qualified then g.qQualified else g.qUnsupported)
        (if qualified then g.fineQualified else g.fineUnsupported)
        g.claimBurden (rivalMass g rival)
  | .record => g.disclosureUtility
  | .silent => 0

/-- All three deviations are retained explicitly, permitting exact finite
    checks without claiming a preferred equilibrium selection. -/
def WeakChoice (score : Action → Rat) (chosen : Action) : Prop :=
  score .claim ≤ score chosen ∧ score .record ≤ score chosen ∧ score .silent ≤ score chosen

def StrictChoice (score : Action → Rat) (chosen : Action) : Prop :=
  ∀ other : Action, other ≠ chosen → score other < score chosen

def StrictAt (score : Action → Rat) : Action → Prop
  | .claim => score .record < score .claim ∧ score .silent < score .claim
  | .record => score .claim < score .record ∧ score .silent < score .record
  | .silent => score .claim < score .silent ∧ score .record < score .silent

def Equilibrium (g : Game) (first second : Strategy) : Prop :=
  WeakChoice (fun a => utility g true a second) first.qualified ∧
  WeakChoice (fun a => utility g false a second) first.unsupported ∧
  WeakChoice (fun a => utility g true a first) second.qualified ∧
  WeakChoice (fun a => utility g false a first) second.unsupported

def StrictEquilibrium (g : Game) (first second : Strategy) : Prop :=
  StrictAt (fun a => utility g true a second) first.qualified ∧
  StrictAt (fun a => utility g false a second) first.unsupported ∧
  StrictAt (fun a => utility g true a first) second.qualified ∧
  StrictAt (fun a => utility g false a first) second.unsupported

/-- The probability comes from sole eligibility plus a fair shared tie. -/
theorem award_probability (q r : Rat) :
    q*(1-r)+q*r/2 = q*(1-r/2) := by grind

theorem eligible_bounds (g : Game) (hv : Valid g) (t : Bool) (a : Action) :
    0 ≤ eligible g t a ∧ eligible g t a ≤ 1 := by
  cases a with
  | claim =>
    cases t with
    | false => exact ⟨hv.unsupported0,hv.unsupported1⟩
    | true => exact ⟨hv.qualified0,hv.qualified1⟩
  | record => simp only [eligible]; constructor <;> decide +kernel
  | silent => simp only [eligible]; constructor <;> decide +kernel

theorem rival_mass_bounds (g : Game) (hv : Valid g) (s : Strategy) :
    0 ≤ rivalMass g s ∧ rivalMass g s ≤ 1 := by
  have ht := eligible_bounds g hv true s.qualified
  have hl := eligible_bounds g hv false s.unsupported
  have hp : 0 ≤ 1-g.prior := by have h := hv.prior1; grind
  have n1 := Rat.mul_nonneg hv.prior0 ht.1
  have n2 := Rat.mul_nonneg hp hl.1
  have u1 := Rat.mul_le_mul_of_nonneg_left ht.2 hv.prior0
  have u2 := Rat.mul_le_mul_of_nonneg_left hl.2 hp
  unfold rivalMass
  constructor <;> grind

theorem claim_lower_bound (b q fine burden r : Rat)
    (hbq : 0 ≤ b*q) (hr : r ≤ 1) :
    b*q/2-fine-burden ≤ claimValue b q fine burden r := by
  have h1 : 0 ≤ 1-r := by grind
  have hm := Rat.mul_nonneg hbq h1
  have hh : (0:Rat) ≤ 1/2 := by decide +kernel
  have hn := Rat.mul_nonneg hm hh
  have he : b*q*(1-r)*(1/2) = claimValue b q fine burden r - (b*q/2-fine-burden) := by
    unfold claimValue
    grind
  rw [he] at hn
  grind

theorem claim_upper_bound (b q fine burden r : Rat)
    (hbq : 0 ≤ b*q) (hr : 0 ≤ r) :
    claimValue b q fine burden r ≤ b*q-fine-burden := by
  have hm := Rat.mul_nonneg hbq hr
  have hh : (0:Rat) ≤ 1/2 := by decide +kernel
  have hn := Rat.mul_nonneg hm hh
  have he : b*q*r*(1/2) = (b*q-fine-burden) - claimValue b q fine burden r := by
    unfold claimValue
    grind
  rw [he] at hn
  grind

theorem strict_choice_weak (score : Action → Rat) (best : Action)
    (h : StrictChoice score best) : WeakChoice score best := by
  have hall : ∀ a : Action, score a ≤ score best := by
    intro a
    by_cases he : a = best
    · subst a; exact Rat.le_refl
    · exact Rat.le_of_lt (h a he)
  exact ⟨hall .claim,hall .record,hall .silent⟩

theorem strict_choice_unique (score : Action → Rat) (best selected : Action)
    (hstrict : StrictChoice score best) (hweak : WeakChoice score selected) :
    selected = best := by
  by_cases he : selected = best
  · exact he
  have hne := he
  exfalso
  have hl := hstrict selected hne
  have hu : score best ≤ score selected := by
    cases best with
    | claim => exact hweak.1
    | record => exact hweak.2.1
    | silent => exact hweak.2.2
  grind

def truth : Strategy := ⟨.claim,.record⟩
def pooling : Strategy := ⟨.claim,.claim⟩
def quiet : Strategy := ⟨.claim,.silent⟩
def allRecords : Strategy := ⟨.record,.record⟩

/-- SUBSTANTIVE dominance result, uniform over every rival strategy. -/
theorem truthful_actions_strictly_dominant (g : Game) (hv : Valid g)
    (hg : 0 < g.disclosureUtility)
    (hq : g.disclosureUtility <
      g.benefit*g.qQualified/2-g.fineQualified-g.claimBurden)
    (hu : g.benefit*g.qUnsupported-g.fineUnsupported-g.claimBurden <
      g.disclosureUtility)
    (t : Bool) (rival : Strategy) :
    StrictChoice (fun a => utility g t a rival) (truth.at t) := by
  have hr := rival_mass_bounds g hv rival
  have nq := Rat.mul_nonneg hv.benefit0 hv.qualified0
  have nu := Rat.mul_nonneg hv.benefit0 hv.unsupported0
  have lq := claim_lower_bound g.benefit g.qQualified g.fineQualified
    g.claimBurden (rivalMass g rival) nq hr.2
  have uu := claim_upper_bound g.benefit g.qUnsupported g.fineUnsupported
    g.claimBurden (rivalMass g rival) nu hr.1
  intro other hne
  cases t <;> cases other <;>
    simp only [truth,Strategy.at,utility] at * <;> grind

/-- General uniqueness from strict type-contingent dominance. This quantifies
    over arbitrary asymmetric strategy pairs, not only symmetric candidates. -/
theorem unique_equilibrium_of_strict_dominance (g : Game) (target : Strategy)
    (hdom : ∀ t : Bool, ∀ rival : Strategy,
      StrictChoice (fun a => utility g t a rival) (target.at t))
    (first second : Strategy) :
    Equilibrium g first second ↔ first = target ∧ second = target := by
  constructor
  · intro he
    have h1 := strict_choice_unique (fun a => utility g true a second)
      (target.at true) first.qualified (hdom true second) he.1
    have h2 := strict_choice_unique (fun a => utility g false a second)
      (target.at false) first.unsupported (hdom false second) he.2.1
    have h3 := strict_choice_unique (fun a => utility g true a first)
      (target.at true) second.qualified (hdom true first) he.2.2.1
    have h4 := strict_choice_unique (fun a => utility g false a first)
      (target.at false) second.unsupported (hdom false first) he.2.2.2
    cases first
    cases second
    cases target
    simp_all [Strategy.at]
  · intro h
    rw [h.1,h.2]
    exact ⟨strict_choice_weak _ _ (hdom true target),
      strict_choice_weak _ _ (hdom false target),
      strict_choice_weak _ _ (hdom true target),
      strict_choice_weak _ _ (hdom false target)⟩

theorem unique_truthful_equilibrium (g : Game) (hv : Valid g)
    (hg : 0 < g.disclosureUtility)
    (hq : g.disclosureUtility <
      g.benefit*g.qQualified/2-g.fineQualified-g.claimBurden)
    (hu : g.benefit*g.qUnsupported-g.fineUnsupported-g.claimBurden <
      g.disclosureUtility)
    (first second : Strategy) :
    Equilibrium g first second ↔ first = truth ∧ second = truth := by
  exact unique_equilibrium_of_strict_dominance g truth
    (truthful_actions_strictly_dominant g hv hg hq hu) first second

/-- Adverse disclosure cannot be elicited by auditing funding claims alone
    when disclosure has negative net private utility and silence is free. -/
theorem costly_disclosure_not_best_reply (g : Game) (hg : g.disclosureUtility < 0)
    (t : Bool) (rival : Strategy) :
    ¬ WeakChoice (fun a => utility g t a rival) .record := by
  intro h
  have hs := h.2.2
  change 0 ≤ g.disclosureUtility at hs
  grind

theorem zero_credit_disclosure_not_strict (g : Game) (hg : g.disclosureUtility = 0)
    (t : Bool) (rival : Strategy) :
    ¬ StrictChoice (fun a => utility g t a rival) .record := by
  intro h
  have hs := h .silent (by decide)
  change 0 < g.disclosureUtility at hs
  grind

/-- Nondiscriminating checks cannot strictly separate otherwise identical
    private preferences. This is not an assumption that future value is known. -/
theorem nondiscriminating_no_strict_separation (g : Game)
    (hq : g.qQualified = g.qUnsupported)
    (hf : g.fineQualified = g.fineUnsupported) (rival : Strategy) :
    ¬ (StrictChoice (fun a => utility g true a rival) .claim ∧
       StrictChoice (fun a => utility g false a rival) .record) := by
  rintro ⟨hc,hd⟩
  have h1 := hc .record (by decide)
  have h2 := hd .claim (by decide)
  simp only [utility] at h1 h2
  rw [hq,hf] at h1
  grind

/-- Prior technical failure is not the misconduct variable. A failure can
    be honestly disclosed in a qualified diagnostic case. -/
structure Evidence where
  qualified : Bool
  previousRunFailed : Bool
  deriving DecidableEq, Repr

def evidenceUtility (g : Game) (e : Evidence) (a : Action) (rival : Strategy) : Rat :=
  utility g e.qualified a rival

theorem honest_failure_not_penalized_as_such (g : Game) (q : Bool)
    (a : Action) (rival : Strategy) :
    evidenceUtility g ⟨q,true⟩ a rival = evidenceUtility g ⟨q,false⟩ a rival := by rfl

def allStrategies : List Strategy :=
  [⟨.claim,.claim⟩,⟨.claim,.record⟩,⟨.claim,.silent⟩,
   ⟨.record,.claim⟩,⟨.record,.record⟩,⟨.record,.silent⟩,
   ⟨.silent,.claim⟩,⟨.silent,.record⟩,⟨.silent,.silent⟩]

def allProfiles : List (Strategy × Strategy) :=
  allStrategies.flatMap (fun first => allStrategies.map (fun second => (first,second)))

theorem every_strategy_listed (s : Strategy) : s ∈ allStrategies := by
  cases s with
  | mk a b => cases a <;> cases b <;> decide

theorem every_profile_listed (first second : Strategy) : (first,second) ∈ allProfiles := by
  simp only [allProfiles,List.mem_flatMap,List.mem_map]
  exact ⟨first,every_strategy_listed first,second,every_strategy_listed second,rfl⟩

theorem eighty_one_profiles : allProfiles.length = 81 := by decide

/-- Realized allocation. Audit flags already include whether the audit ran. -/
def admitted (a : Action) (flagged : Bool) : Bool :=
  if a = .claim then !flagged else false

def chooseAward (first second coin : Bool) : Bool × Bool :=
  if first then (if second then (coin,!coin) else (true,false)) else (false,second)

def countFunded (x : Bool × Bool) : Nat :=
  (if x.1 then 1 else 0)+(if x.2 then 1 else 0)

theorem one_slot_capacity (a b coin : Bool) : countFunded (chooseAward a b coin) ≤ 1 := by
  cases a <;> cases b <;> cases coin <;> decide

theorem record_never_claims_slot (flag : Bool) : admitted .record flag = false := by rfl
theorem silence_never_claims_slot (flag : Bool) : admitted .silent flag = false := by rfl

def actionMass (g : Game) (s : Strategy) (a : Action) : Rat :=
  g.prior*(if s.qualified=a then 1 else 0) +
  (1-g.prior)*(if s.unsupported=a then 1 else 0)

def valueMass (g : Game) (jq ju fallback : Rat) (s : Strategy) : Rat :=
  g.prior*eligible g true s.qualified*(jq-fallback) +
  (1-g.prior)*eligible g false s.unsupported*(ju-fallback)

def gross (g : Game) (jq ju fallback : Rat) (first second : Strategy) : Rat :=
  (1-rivalMass g second/2)*valueMass g jq ju fallback first +
  (1-rivalMass g first/2)*valueMass g jq ju fallback second

theorem branch_value_identity (r1 r2 z1 z2 : Rat) :
    ((1-r2)*z1+r2*z1/2) + ((1-r1)*z2+r1*z2/2) =
      (1-r2/2)*z1+(1-r1/2)*z2 := by grind

def sponsor (g : Game) (jq ju fallback auditCost claimCost recordCost fixedCost : Rat)
    (first second : Strategy) : Rat :=
  gross g jq ju fallback first second -
  (actionMass g first .claim+actionMass g second .claim)*(g.auditRate*auditCost+claimCost) -
  (actionMass g first .record+actionMass g second .record)*recordCost-fixedCost

theorem sponsor_symmetric (g : Game) (jq ju fallback auditCost claimCost recordCost fixedCost : Rat)
    (first second : Strategy) :
    sponsor g jq ju fallback auditCost claimCost recordCost fixedCost first second =
    sponsor g jq ju fallback auditCost claimCost recordCost fixedCost second first := by
  unfold sponsor gross
  grind

def baseline : Game := auditGame (1/2) 4 1 (1/4) (3/4) 1 4 0 (1/8)
def noAudit : Game := auditGame (1/2) 4 0 (1/4) (3/4) 1 4 0 (1/8)
def weakEnforcement : Game := auditGame (1/2) 4 1 (1/4) (3/4) 0 4 0 (1/8)
def falseAccusations : Game := auditGame (1/2) 4 1 (3/4) (3/4) 1 4 0 (1/8)
def noDiscrimination : Game := auditGame (1/2) 4 1 (1/4) (1/4) 1 4 0 (1/8)
def zeroCredit : Game := auditGame (1/2) 4 1 (1/4) (3/4) 1 4 0 0
def costlyRecord : Game := auditGame (1/2) 4 1 (1/4) (3/4) 1 4 0 (-1/4)
def halfAudit : Game := auditGame (1/2) 4 (1/2) (1/4) (3/4) 1 4 0 (1/8)

theorem baseline_valid : Valid baseline := by
  constructor <;> decide +kernel

theorem baseline_unique (first second : Strategy) :
    Equilibrium baseline first second ↔ first=truth ∧ second=truth := by
  exact unique_truthful_equilibrium baseline baseline_valid (by decide +kernel)
    (by decide +kernel) (by decide +kernel) first second

theorem baseline_strict : StrictEquilibrium baseline truth truth := by
  simp only [StrictEquilibrium,StrictAt,truth]
  decide +kernel

theorem baseline_private_utilities :
    utility baseline true .claim truth = 23/16 ∧
    utility baseline false .claim truth = -35/16 ∧
    utility baseline false .record truth = 1/8 := by decide +kernel

theorem baseline_gross : gross baseline 8 (-4) 2 truth truth = 117/32 := by decide +kernel

theorem baseline_net : sponsor baseline 8 (-4) 2 (1/2) 0 (1/4) 0 truth truth = 93/32 := by
  decide +kernel

theorem overhead_loss : sponsor baseline 8 (-4) 2 4 0 (1/4) 0 truth truth = -19/32 := by
  decide +kernel

/-- Complete finite checks cover all asymmetric profiles as well. -/
theorem no_audit_unique (first second : Strategy) :
    Equilibrium noAudit first second ↔ first=pooling ∧ second=pooling := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem no_audit_pooling_strict : StrictEquilibrium noAudit pooling pooling := by
  simp only [StrictEquilibrium,StrictAt,pooling]
  decide +kernel

theorem no_audit_net : sponsor noAudit 8 (-4) 2 (1/2) 0 (1/4) 0 pooling pooling = 0 := by
  decide +kernel

theorem weak_enforcement_unique (first second : Strategy) :
    Equilibrium weakEnforcement first second ↔ first=pooling ∧ second=pooling := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem weak_enforcement_still_filters :
    sponsor weakEnforcement 8 (-4) 2 (1/2) 0 (1/4) 0 pooling pooling = 5/4 := by
  decide +kernel

theorem weak_enforcement_high_cost_loses :
    sponsor weakEnforcement 8 (-4) 2 2 0 (1/4) 0 pooling pooling = -7/4 := by
  decide +kernel

theorem false_accusations_unique (first second : Strategy) :
    Equilibrium falseAccusations first second ↔ first=allRecords ∧ second=allRecords := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem false_accusations_lose :
    sponsor falseAccusations 8 (-4) 2 (1/2) 0 (1/4) 0 allRecords allRecords = -1/2 := by
  decide +kernel

theorem nondiscriminating_unique (first second : Strategy) :
    Equilibrium noDiscrimination first second ↔ first=pooling ∧ second=pooling := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem nondiscriminating_audit_loses :
    sponsor noDiscrimination 8 (-4) 2 (1/2) 0 (1/4) 0 pooling pooling = -1 := by
  decide +kernel

/-- Exactly four pure equilibria, including two asymmetric ones. -/
theorem zero_credit_equilibria (first second : Strategy) :
    Equilibrium zeroCredit first second ↔
      (first=truth ∨ first=quiet) ∧ (second=truth ∨ second=quiet) := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem costly_record_unique (first second : Strategy) :
    Equilibrium costlyRecord first second ↔ first=quiet ∧ second=quiet := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem costly_record_no_records : actionMass costlyRecord quiet .record = 0 := by decide +kernel

theorem costly_record_one_shot_value :
    sponsor costlyRecord 8 (-4) 2 (1/2) 0 (1/4) 0 quiet quiet = 101/32 := by
  decide +kernel

/-- Partial auditing admits TWO strict asymmetric equilibria, not a selected
    symmetric fiction. Complete profile enumeration proves exhaustiveness. -/
theorem half_audit_equilibria (first second : Strategy) :
    Equilibrium halfAudit first second ↔
      (first=pooling ∧ second=truth) ∨ (first=truth ∧ second=pooling) := by
  rcases first with ⟨a,b⟩
  rcases second with ⟨c,d⟩
  cases a <;> cases b <;> cases c <;> cases d <;>
    unfold Equilibrium WeakChoice <;> decide +kernel

theorem half_audit_asymmetric_strict : StrictEquilibrium halfAudit pooling truth := by
  simp only [StrictEquilibrium,StrictAt,truth,pooling]
  decide +kernel

theorem half_audit_reverse_strict : StrictEquilibrium halfAudit truth pooling := by
  simp only [StrictEquilibrium,StrictAt,truth,pooling]
  decide +kernel

theorem half_audit_no_symmetric_pure (s : Strategy) : ¬ Equilibrium halfAudit s s := by
  rw [half_audit_equilibria]
  have h : pooling ≠ truth := by decide
  grind

theorem half_audit_deviation :
    utility halfAudit false .claim truth = 29/64 ∧
    utility halfAudit false .claim pooling = 1/16 := by decide +kernel

theorem half_audit_net :
    sponsor halfAudit 8 (-4) 2 (1/2) 0 (1/4) 0 pooling truth = 221/128 := by
  decide +kernel

theorem economic_misalignment_loses :
    sponsor baseline 0 8 2 (1/2) 0 (1/4) 0 truth truth = -63/32 := by
  decide +kernel

theorem economic_misalignment_uninformed :
    sponsor noAudit 0 8 2 (1/2) 0 (1/4) 0 pooling pooling = 2 := by
  decide +kernel

end StrategicReporting