Theory Social_Choice_Functions

(*  
  Title:    Social_Choice_Functions.thy
  Author:   Manuel Eberl, TU München

  Definitions of Social Choice Functions, their properties, and related concepts
*)

section ‹Social Choice Functions›

theory Social_Choice_Functions
imports 
  "Randomised_Social_Choice.Preference_Profile_Cmd"
begin

subsection ‹Weighted majority graphs›

definition supporters :: "('agent, 'alt) pref_profile  'alt  'alt  'agent set" where
  supporters_auxdef: "supporters R x y = {i. x ≽[R i] y}" 

definition weighted_majority :: "('agent, 'alt) pref_profile  'alt  'alt  int" where
  "weighted_majority R x y = int (card (supporters R x y)) - int (card (supporters R y x))"
  
lemma weighted_majority_refl [simp]: "weighted_majority R x x = 0"
  by (simp add: weighted_majority_def)
  
lemma weighted_majority_swap: "weighted_majority R x y = -weighted_majority R y x"
  by (simp add: weighted_majority_def)

lemma eval_set_filter: 
  "Set.filter P {} = {}" 
  "P x  Set.filter P (insert x A) = insert x (Set.filter P A)"
  "¬P x  Set.filter P (insert x A) = Set.filter P A"
  unfolding Set.filter_def by auto
  
context election
begin

lemma supporters_def: 
  assumes "is_pref_profile R"
  shows   "supporters R x y = {iagents. x ≽[R i] y}"
proof -
  interpret pref_profile_wf agents alts R by fact
  show ?thesis using not_outside unfolding supporters_auxdef by blast
qed

lemma supporters_nonagent1:
  assumes "is_pref_profile R" "x  alts"
  shows   "supporters R x y = {}"
proof -
  interpret pref_profile_wf agents alts R by fact
  from assms show ?thesis by (auto simp: supporters_def dest: not_outside)
qed

lemma supporters_nonagent2:
  assumes "is_pref_profile R" "y  alts"
  shows   "supporters R x y = {}"
proof -
  interpret pref_profile_wf agents alts R by fact
  from assms show ?thesis by (auto simp: supporters_def dest: not_outside)
qed

lemma weighted_majority_nonagent1:
  assumes "is_pref_profile R" "x  alts"
  shows   "weighted_majority R x y = 0"
  using assms by (simp add: weighted_majority_def supporters_nonagent1 supporters_nonagent2)

lemma weighted_majority_nonagent2:
  assumes "is_pref_profile R" "y  alts"
  shows   "weighted_majority R x y = 0"
  using assms by (simp add: weighted_majority_def supporters_nonagent1 supporters_nonagent2)

lemma weighted_majority_eq_iff:
  assumes "is_pref_profile R1" "is_pref_profile R2"
  shows   "weighted_majority R1 = weighted_majority R2 
             (xalts. yalts. weighted_majority R1 x y = weighted_majority R2 x y)"
proof (intro iffI ext)
  fix x y :: 'alt
  assume "xalts. yalts. weighted_majority R1 x y = weighted_majority R2 x y"
  with assms show "weighted_majority R1 x y = weighted_majority R2 x y"
    by (cases "x  alts"; cases "y  alts") 
       (auto simp: fun_eq_iff weighted_majority_nonagent1 weighted_majority_nonagent2)
qed auto

end

  
subsection ‹Definition of Social Choice Functions›

locale social_choice_function = election agents alts 
  for agents :: "'agent set" and alts :: "'alt set" +
  fixes scf :: "('agent, 'alt) pref_profile  'alt set"
  assumes scf_nonempty: "is_pref_profile R  scf R  {}"
  assumes scf_alts: "is_pref_profile R  scf R  alts"

subsection ‹Anonymity›

text ‹
  An SCF is anonymous if permuting the agents in the input does not change the result.
›
locale anonymous_scf = social_choice_function agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  assumes anonymous: "π permutes agents  is_pref_profile R  scf (R  π) = scf R" 
begin

lemma anonymous':
  assumes "anonymous_profile R1 = anonymous_profile R2"
  assumes "is_pref_profile R1" "is_pref_profile R2"
  shows   "scf R1 = scf R2"
proof -
  from anonymous_profile_agent_permutation[OF assms finite_agents]
    obtain π where "π permutes agents" "R1 = R2  π" by blast
  with anonymous[of π R2] assms show ?thesis by simp
qed

lemma anonymity_prefs_from_table:
  assumes "prefs_from_table_wf agents alts xs" "prefs_from_table_wf agents alts ys"
  assumes "mset (map snd xs) = mset (map snd ys)"
  shows   "scf (prefs_from_table xs) = scf (prefs_from_table ys)"
proof -
  from assms obtain π where "π permutes agents" "prefs_from_table xs  π = prefs_from_table ys"
    by (rule prefs_from_table_agent_permutation)
  with anonymous[of π, of "prefs_from_table xs"] assms(1) show ?thesis 
    by (simp add: pref_profile_from_tableI)
qed

context
begin
qualified lemma anonymity_prefs_from_table_aux:
  assumes "R1 = prefs_from_table xs" "prefs_from_table_wf agents alts xs"
  assumes "R2 = prefs_from_table ys" "prefs_from_table_wf agents alts ys"
  assumes "mset (map snd xs) = mset (map snd ys)"
  shows   "scf R1 = scf R2" unfolding assms(1,3)
  by (rule anonymity_prefs_from_table) (simp_all add: assms del: mset_map)
end

end


subsection ‹Neutrality›

text ‹
  An SCF is neutral if permuting the alternatives in the input does not change the
  result, modulo the equivalent permutation in the output lottery.
›
locale neutral_scf = social_choice_function agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  assumes neutral: "σ permutes alts  is_pref_profile R  
                        scf (permute_profile σ R) = σ ` scf R"
begin

text ‹
  Alternative formulation of neutrality that shows that our definition is 
  equivalent to that in the paper.
›
lemma neutral':
  assumes "σ permutes alts"
  assumes "is_pref_profile R"
  assumes "a  alts"
  shows   "σ a  scf (permute_profile σ R)  a  scf R"
proof -
  have *: "x = y" if "σ x = σ y" for x y
    using permutes_inj[OF assms(1)] that by (auto dest: injD)
  from assms show ?thesis by (auto simp: neutral dest!: *)
qed

end


locale an_scf = 
  anonymous_scf agents alts scf + neutral_scf agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf
begin

lemma scf_anonymous_neutral:
  assumes perm: "σ permutes alts" and wf: "is_pref_profile R1" "is_pref_profile R2"
  assumes eq: "anonymous_profile R1 = 
                 image_mset (map (λA. σ ` A)) (anonymous_profile R2)"
  shows   "scf R1 = σ ` scf R2"
proof -
  interpret R1: pref_profile_wf agents alts R1 by fact
  interpret R2: pref_profile_wf agents alts R2 by fact
  from perm have wf': "is_pref_profile (permute_profile σ R2)"
    by (rule R2.wf_permute_alts)
  from eq perm have "anonymous_profile R1 = anonymous_profile (permute_profile σ R2)"
    by (simp add: R2.anonymous_profile_permute)
  from anonymous_profile_agent_permutation[OF this wf(1) wf']
    obtain π where "π permutes agents" "permute_profile σ R2  π = R1" by auto
  have "scf (permute_profile σ R2  π) = scf (permute_profile σ R2)"
    by (rule anonymous) fact+
  also have " = σ ` scf R2"
    by (rule neutral) fact+
  also have "permute_profile σ R2  π = R1" by fact
  finally show ?thesis .
qed


lemma scf_anonymous_neutral':
  assumes perm: "σ permutes alts" and wf: "is_pref_profile R1" "is_pref_profile R2"
  assumes eq: "anonymous_profile R1 = 
                 image_mset (map (λA. σ ` A)) (anonymous_profile R2)"
  shows   "σ x  scf R1  x  scf R2"
proof -
  have "scf R1 = σ ` scf R2" by (intro scf_anonymous_neutral) fact+
  also have "σ x    x  scf R2"
    by (blast dest: injD[OF permutes_inj[OF perm]])
  finally show ?thesis .
qed

lemma scf_automorphism:
  assumes perm: "σ permutes alts" and wf: "is_pref_profile R"
  assumes eq: "image_mset (map (λA. σ ` A)) (anonymous_profile R) = anonymous_profile R"
  shows   "σ ` scf R = scf R"
  using scf_anonymous_neutral[OF perm wf wf eq [symmetric]] ..

end

lemma an_scf_automorphism_aux:
  assumes wf: "prefs_from_table_wf agents alts yss" "R  prefs_from_table yss"
  assumes an: "an_scf agents alts scf"
  assumes eq: "mset (map ((map (λA. permutation_of_list xs ` A))  snd) yss) = mset (map snd yss)"
  assumes perm: "set (map fst xs)  alts" "set (map snd xs) = set (map fst xs)" 
                "distinct (map fst xs)" 
      and x: "x  alts" "y = permutation_of_list xs x"
  shows   "x  scf R  y  scf R"
proof -
  note perm = list_permutesI[OF perm]
  let  = "permutation_of_list xs"
  note perm' = permutation_of_list_permutes [OF perm]
  from wf have wf': "pref_profile_wf agents alts R" by (simp add: pref_profile_from_tableI)
  then interpret R: pref_profile_wf agents alts R .
  from perm' interpret R': pref_profile_wf agents alts "permute_profile  R"
    by (simp add: R.wf_permute_alts)
  from an interpret an_scf agents alts scf .

  from eq wf have eq': "image_mset (map (λA.  ` A)) (anonymous_profile R) = anonymous_profile R"
    by (simp add: anonymise_prefs_from_table mset_map multiset.map_comp)
  have "x  scf R   x   ` scf R"
    by (blast dest: injD[OF permutes_inj[OF perm']])
  also from eq' x wf' perm' have " ` scf R = scf R"
    by (intro scf_automorphism) 
       (simp_all add: R.anonymous_profile_permute pref_profile_from_tableI)
  finally show ?thesis using x by simp
qed


subsection ‹Weighted-majoritarian SCFs›

locale pairwise_scf = social_choice_function agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  assumes pairwise:
    "is_pref_profile R1  is_pref_profile R2  weighted_majority R1 = weighted_majority R2 
       scf R1 = scf R2"

subsection ‹Pareto efficiency›

locale pareto_efficient_scf = social_choice_function agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  assumes pareto_efficient: 
    "is_pref_profile R  scf R  pareto_losers R = {}"
begin

lemma pareto_efficient':
  assumes "is_pref_profile R" "y ≻[Pareto(R)] x"
  shows   "x  scf R"
  using pareto_efficient[of R] assms 
  by (auto simp: pareto_losers_def)

lemma pareto_efficient'':
  assumes "is_pref_profile R" "i  agents"  "iagents. y ≽[R i] x" "¬y ≼[R i] x"
  shows   "x  scf R"
proof -
  from assms(1) interpret pref_profile_wf agents alts R .
  from assms(2-) show ?thesis
    by (intro pareto_efficient'[OF assms(1), of _ y])
       (auto simp: Pareto_iff strongly_preferred_def)
qed

end


subsection ‹Set extensions›

type_synonym 'alt set_extension = "'alt relation  'alt set relation"

definition Kelly :: "'alt set_extension" where
  "A ≽[Kelly(R)] B  (aA. bB. a ≽[R] b)"

lemma Kelly_strict_iff: "A ≻[Kelly(R)] B  ((aA. bB. R b a)  ¬ (aB. bA. R b a))"
  unfolding strongly_preferred_def Kelly_def ..

lemmas Kelly_eval = Kelly_def Kelly_strict_iff

definition Fishb :: "'alt set_extension" where
  "A ≽[Fishb(R)] B  (aA. bB-A. a ≽[R] b)  (aA-B. bB. a ≽[R] b)"

lemma Fishb_strict_iff: 
  "A ≻[Fishb(R)] B  
     ((aA. bB - A. R b a)  (aA - B. bB. R b a)) 
     ¬ ((aB. bA - B. R b a)  (aB - A. bA. R b a))"
  unfolding strongly_preferred_def Fishb_def ..

lemmas Fishb_eval = Fishb_def Fishb_strict_iff


subsection ‹Strategyproofness›

locale strategyproof_scf = 
  social_choice_function agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  fixes set_ext :: "'alt set_extension"
  assumes strategyproof: 
    "is_pref_profile R  total_preorder_on alts Ri'  i  agents 
       ¬ scf (R(i := Ri')) ≻[set_ext(R i)] scf R"

locale strategyproof_anonymous_scf =
  anonymous_scf agents alts scf + strategyproof_scf agents alts scf set_ext
  for agents :: "'agent set" and alts :: "'alt set" and scf and set_ext
begin

lemma strategyproof':
  assumes "is_pref_profile R1" "is_pref_profile R2" "i  agents" "j  agents"
  assumes "anonymous_profile R2 = anonymous_profile R1 - 
             {#weak_ranking (R1 i)#} + {#weak_ranking (R2 j)#}"
  shows   "¬scf R2 ≻[set_ext (R1 i)] scf R1"
proof -
  let ?R3 = "R1(i := R2 j)"
  interpret R1: pref_profile_wf agents alts R1 by fact
  interpret R2: pref_profile_wf agents alts R2 by fact
  from j  agents have "total_preorder_on alts (R2 j)" by simp
  from strategyproof[OF assms(1) this i  agents] 
    have "¬scf ?R3 ≻[set_ext (R1 i)] scf R1" .
  also from assms have "scf ?R3 = scf R2"
    by (intro anonymous') (simp_all add: R1.anonymous_profile_update)
  finally show ?thesis .
qed

end

context preorder_on
begin

lemma strict_not_outside:
  assumes "x ≺[le] y"
  shows   "x  carrier" "y  carrier"
  using assms not_outside[of x y] unfolding strongly_preferred_def by blast+

end


subsection ‹Lifting preferences›

text ‹
  Preference profiles can be lifted to a setting with more agents and alternatives by padding
  them with dummy agents and alternatives in such a way that every original agent prefers and
  original alternative over any dummy alternative and is indifferent between the dummy alternatives.
  Moreover, every dummy agent is completely indifferent.
›

definition lift_prefs :: 
    "'alt set  'alt set  'alt relation  'alt relation" where
  "lift_prefs alts alts' R = (λx y. 
     x  alts'  y  alts'  (x = y  x  alts  (y  alts  R x y)))"

lemma lift_prefs_wf:
  assumes "total_preorder_on alts R" "alts  alts'"
  shows   "total_preorder_on alts' (lift_prefs alts alts' R)"
proof -
  interpret R: total_preorder_on alts R by fact
  show ?thesis
    by standard (insert R.total, auto dest: R.trans simp: lift_prefs_def)
qed

definition lift_pref_profile :: 
    "'agent set  'alt set  'agent set  'alt set 
       ('agent, 'alt) pref_profile  ('agent, 'alt) pref_profile" where
  "lift_pref_profile agents alts agents' alts' R = (λi x y. 
     x  alts'  y  alts'  i  agents' 
     (x = y  x  alts  i  agents  (y  alts  R i x y)))"

lemma lift_pref_profile_conv_vector:
  assumes "i  agents" "i  agents'"
  shows   "lift_pref_profile agents alts agents' alts' R i = lift_prefs alts alts' (R i)"
  using assms by (auto simp: lift_pref_profile_def lift_prefs_def)

lemma lift_pref_profile_wf:
  assumes "pref_profile_wf agents alts R"
  assumes "agents  agents'" "alts  alts'" "finite alts'"
  defines "R'  lift_pref_profile agents alts agents' alts' R"
  shows   "pref_profile_wf agents' alts' R'"
proof -
  from assms interpret R: pref_profile_wf agents alts by simp
  have "finite_total_preorder_on alts' (R' i)" 
    if i: "i  agents'" for i
  proof (cases "i  agents")
    case True
    then interpret finite_total_preorder_on alts "R i" by simp
    from True assms show ?thesis
      by unfold_locales (auto simp: lift_pref_profile_def dest: total intro: trans)
  next 
    case False
    with assms i show ?thesis
      by unfold_locales (simp_all add: lift_pref_profile_def)
  qed
  moreover have "R' i = (λ_ _. False)" if "i  agents'" for i
    unfolding lift_pref_profile_def R'_def using that by simp
  ultimately show ?thesis unfolding pref_profile_wf_def using assms by auto
qed

lemma lift_pref_profile_permute_agents:
  assumes "π permutes agents" "agents  agents'"
  shows   "lift_pref_profile agents alts agents' alts' (R  π) = 
             lift_pref_profile agents alts agents' alts' R  π"
  using assms permutes_subset[OF assms]
  by (auto simp add: lift_pref_profile_def o_def permutes_in_image)

lemma lift_pref_profile_permute_alts:
  assumes "σ permutes alts" "alts  alts'"
  shows   "lift_pref_profile agents alts agents' alts' (permute_profile σ R) = 
             permute_profile σ (lift_pref_profile agents alts agents' alts' R)"
proof -
  from assms have inv: "inv σ permutes alts" by (intro permutes_inv)
  from this assms(2) have "inv σ permutes alts'" by (rule permutes_subset)
  with inv show ?thesis using assms permutes_inj[OF inv σ permutes alts]
    by (fastforce simp add: lift_pref_profile_def permutes_in_image
          permute_profile_def fun_eq_iff dest: injD)
qed


context
  fixes agents alts R agents' alts' R'
  assumes R_wf: "pref_profile_wf agents alts R"
  assumes election: "agents  agents'" "alts  alts'" "alts  {}" "agents  {}" "finite alts'"
  defines "R'  lift_pref_profile agents alts agents' alts' R"
begin

interpretation R: pref_profile_wf agents alts R by fact
interpretation R': pref_profile_wf agents' alts' R' 
  using election R_wf by (simp add: R'_def lift_pref_profile_wf)

lemma lift_pref_profile_strict_iff:
  "x ≺[lift_pref_profile agents alts agents' alts' R i] y 
     i  agents  ((y  alts  x  alts' - alts)  x ≺[R i] y)"
proof (cases "i  agents")
  case True
  then interpret total_preorder_on alts "R i" by simp
  show ?thesis using not_outside election
    by (auto simp: lift_pref_profile_def strongly_preferred_def)
qed (simp_all add: lift_pref_profile_def strongly_preferred_def)

lemma preferred_alts_lift_pref_profile: 
  assumes i: "i  agents'" and x: "x  alts'"
  shows   "preferred_alts (R' i) x = 
             (if i  agents  x  alts then preferred_alts (R i) x else alts')"
proof (cases "i  agents")
  assume i: "i  agents"
  then interpret Ri: total_preorder_on alts "R i" by simp
  show ?thesis
  using i x election Ri.not_outside
    by (auto simp: preferred_alts_def R'_def lift_pref_profile_def Ri.refl)
qed (auto simp: preferred_alts_def R'_def lift_pref_profile_def i x)

lemma lift_pref_profile_Pareto_iff:
  "x ≼[Pareto(R')] y  x  alts'  y  alts'  (x  alts  x ≼[Pareto(R)] y)"
proof -
  from R.nonempty_agents obtain i where i: "i  agents" by blast
  then interpret Ri: finite_total_preorder_on alts "R i" by simp
  show ?thesis unfolding R'.Pareto_iff R.Pareto_iff unfolding R'_def lift_pref_profile_def
    using election i by (auto simp: preorder_on.refl[OF R.in_dom] 
      simp del: R.nonempty_alts R.nonempty_agents  intro: Ri.not_outside)
qed

lemma lift_pref_profile_Pareto_strict_iff:
  "x ≺[Pareto(R')] y  x  alts'  y  alts'  (x  alts  y  alts  x ≺[Pareto(R)] y)"
  by (auto simp: strongly_preferred_def lift_pref_profile_Pareto_iff R.Pareto.not_outside)

lemma pareto_losers_lift_pref_profile:
  shows   "pareto_losers R' = pareto_losers R  (alts' - alts)"
proof -
  have A: "x  alts" "y  alts" if "x ≺[Pareto(R)] y" for x y
    using that R.Pareto.not_outside unfolding strongly_preferred_def by auto
  have B: "x  alts'" if "x  alts" for x using election that by blast
  from R.nonempty_alts obtain x where x: "x  alts" by blast
  thus ?thesis unfolding pareto_losers_def lift_pref_profile_Pareto_strict_iff [abs_def]
    by (auto dest: A B)
qed

end


subsection ‹Lowering SCFs›

text ‹
  Using the preference lifting, we can now \emph{lower} an SCF to a setting with fewer agents
  and alternatives under mild conditions to the original SCF. This preserves many nice properties, 
  such as anonymity, neutrality, and strategyproofness.
›

locale scf_lowering = 
  pareto_efficient_scf agents alts scf
  for agents :: "'agent set" and alts :: "'alt set" and scf +
  fixes agents' alts' 
  assumes agents'_subset: "agents'  agents" and alts'_subset: "alts'  alts"
      and agents'_nonempty [simp]: "agents'  {}" and alts'_nonempty [simp]: "alts'  {}"
begin

lemma finite_agents' [simp]: "finite agents'"
  using agents'_subset finite_agents by (rule finite_subset)

lemma finite_alts' [simp]: "finite alts'"
  using alts'_subset finite_alts by (rule finite_subset)

abbreviation lift :: "('agent, 'alt) pref_profile  ('agent, 'alt) pref_profile" where
  "lift  lift_pref_profile agents' alts' agents alts"

definition lowered :: "('agent, 'alt) pref_profile  'alt set" where
  "lowered = scf  lift"

lemma lift_wf [simp, intro]: 
  "pref_profile_wf agents' alts' R  is_pref_profile (lift R)"
  using alts'_subset agents'_subset by (intro lift_pref_profile_wf) simp_all

sublocale lowered: election agents' alts'
  by unfold_locales simp_all

lemma preferred_alts_lift:
  "lowered.is_pref_profile R  i  agents  x  alts 
     preferred_alts (lift R i) x = 
       (if i  agents'  x  alts' then preferred_alts (R i) x else alts)"
  using alts'_subset agents'_subset
  by (intro preferred_alts_lift_pref_profile) simp_all

lemma pareto_losers_lift:
  "lowered.is_pref_profile R  pareto_losers (lift R) = pareto_losers R  (alts - alts')"
  using agents'_subset alts'_subset by (intro pareto_losers_lift_pref_profile) simp_all


sublocale lowered: social_choice_function agents' alts' lowered
proof
  fix R assume R_wf: "pref_profile_wf agents' alts' R"
  from R_wf have R'_wf: "pref_profile_wf agents alts (lift R)" by (rule lift_wf)
  show "lowered R  alts'"
  proof safe
    fix x assume "x  lowered R"
    hence "x  scf (lift R)" by (auto simp: o_def lowered_def)
    moreover have "scf (lift R)  pareto_losers (lift R) = {}"
      by (intro pareto_efficient R'_wf)
    ultimately show "x  alts'" using scf_alts[of "lift R"]
      by (auto simp: pareto_losers_lift R_wf R'_wf)
  qed
  show "lowered R  {}"
    using R'_wf by (auto simp: lowered_def scf_nonempty)
qed

sublocale lowered: pareto_efficient_scf agents' alts' lowered
proof
  fix R assume R_wf: "pref_profile_wf agents' alts' R"
  from R_wf have R'_wf: "pref_profile_wf agents alts (lift R)" by (rule lift_wf)
  have "lowered R  pareto_losers (lift R) = {}" unfolding lowered_def o_def
    by (intro pareto_efficient R'_wf)
  with R_wf show "lowered R  pareto_losers R = {}"
    by (auto simp: pareto_losers_lift)
qed

end


locale scf_lowering_anonymous = 
  anonymous_scf agents alts scf +
  scf_lowering agents alts scf agents' alts'
  for agents :: "'agent set" and alts :: "'alt set" and scf agents' alts'
begin

sublocale lowered: anonymous_scf agents' alts' lowered
proof
  fix π R
  assume "π permutes agents'" and "lowered.is_pref_profile R"
  thus "lowered (R  π) = lowered R" using agents'_subset
    by (auto simp: lowered_def lift_pref_profile_permute_agents anonymous permutes_subset)
qed

end


locale scf_lowering_neutral = 
  neutral_scf agents alts scf +
  scf_lowering agents alts scf agents' alts'
  for agents :: "'agent set" and alts :: "'alt set" and scf agents' alts'
begin

sublocale lowered: neutral_scf agents' alts' lowered
proof
  fix σ R
  assume "σ permutes alts'" and "lowered.is_pref_profile R"
  thus "lowered (permute_profile σ R) = σ ` lowered R" using alts'_subset
    by (auto simp: lowered_def lift_pref_profile_permute_alts neutral permutes_subset)
qed

end

text ‹
  The following is a technical condition that we need from a set extensions in order for
  strategyproofness to survive the lowering. The condition could probably be weakened a bit,
  but it is good enough for our purposes the way it is.
›
locale liftable_set_extension =
  fixes alts' alts :: "'alt set" and set_ext :: "'alt relation  'alt set relation"
  assumes set_ext_strong_lift:
    "total_preorder_on alts' R  A  {}  B  {}  A  alts'  B  alts' 
       A ≺[set_ext R] B  A ≺[set_ext (lift_prefs alts' alts R)] B"

lemma liftable_set_extensionI_weak:
  assumes "R A B. total_preorder_on alts' R  A  {}  B  {}  
                      A  alts'  B  alts' 
              A ≼[set_ext R] B  A ≼[set_ext (lift_prefs alts' alts R)] B"
  shows   "liftable_set_extension alts' alts set_ext"
proof (standard, goal_cases)
  case (1 R A B)
  from assms[of R A B] and assms[of R B A] and 1 show ?case
    by (auto simp: strongly_preferred_def)
qed

lemma Kelly_liftable:
  assumes "alts'  alts"
  shows   "liftable_set_extension alts' alts Kelly"
proof (rule liftable_set_extensionI_weak, goal_cases)
  case (1 R A B)
  interpret R: total_preorder_on alts' R by fact
  from 1(2-5) show ?case using assms  R.refl
    by (force simp: Kelly_def lift_prefs_def)
qed

lemma Fishburn_liftable:
  assumes "alts'  alts"
  shows   "liftable_set_extension alts' alts Fishb"
proof (rule liftable_set_extensionI_weak, goal_cases)
  case (1 R A B)
  interpret R: total_preorder_on alts' R by fact
  have conj_cong: "P1  Q1  P2  Q2" if "P1  P2" "Q1  Q2" for P1 P2 Q1 Q2
    using that by blast
  from 1(2-5) show ?case using assms
    unfolding Fishb_def lift_prefs_def by (intro conj_cong ball_cong refl) auto
qed

locale scf_lowering_strategyproof =
  strategyproof_scf agents alts scf set_ext +
  liftable_set_extension alts' alts set_ext +
  scf_lowering agents alts scf agents' alts'
  for agents :: "'agent set" and alts :: "'alt set" and scf agents' alts' set_ext
begin

sublocale lowered: strategyproof_scf agents' alts' lowered
proof
  fix R Ri' i
  assume R_wf: "lowered.is_pref_profile R" and Ri'_wf: "total_preorder_on alts' Ri'" 
     and i: "i  agents'"
  interpret R: pref_profile_wf agents' alts' R by fact
  interpret Ri': total_preorder_on alts' Ri' by fact
  from R_wf have R'_wf: "is_pref_profile (lift R)" by (rule lift_wf)

  ― ‹We lift the alternative preference for the agent @{term i} in @{term R} to 
      preferences in the lifted profile. ›
  define Ri'' where "Ri'' = lift_prefs alts' alts Ri'"

  have "¬scf (lift R) ≺[set_ext (lift R i)] scf ((lift R)(i := Ri''))"
    using i agents'_subset alts'_subset unfolding Ri''_def
    by (intro strategyproof R'_wf Ri'_wf lift_prefs_wf) auto
  also have "(lift R)(i := Ri'') = lift (R(i := Ri'))" using i agents'_subset
    by (auto simp: fun_eq_iff Ri''_def lift_pref_profile_def lift_prefs_def)
  finally have not_less: "¬scf (lift R) ≺[set_ext (lift R i)] scf (lift (R(i := Ri')))" .

  show "¬lowered R ≺[set_ext (R i)] lowered (R(i := Ri'))"
  proof
    assume "lowered R ≺[set_ext (R i)] lowered (R(i := Ri'))"
    hence "lowered R ≺[set_ext (lift_prefs alts' alts (R i))] lowered (R(i := Ri'))"
      by (intro set_ext_strong_lift R.prefs_wf'(1) i lowered.scf_nonempty lowered.scf_alts
                R.wf_update R_wf Ri'_wf)
    also have "lift_prefs alts' alts (R i) = lift R i"
      using agents'_subset i by (subst lift_pref_profile_conv_vector) auto
    finally show False using not_less unfolding lowered_def o_def by contradiction
  qed
qed
    
end

end