File ‹papp_impossibility.ML›
signature PAPP_IMPOSSIBILITY =
sig
type clause = int list * thm
type sat_problem = (int * cterm option array) * (int * clause Array_Map.T) * thm * Proof.context
val mk_sat_problem : Proof.context -> Path.T -> sat_problem
val mk_dimacs : sat_problem -> string
val write_dimacs : sat_problem -> Path.T -> unit
val replay_grat : Proof.context -> sat_problem -> Path.T -> thm
val derive_false : Proof.context -> Path.T -> Path.T -> thm
end
structure PAPP_Impossibility : PAPP_IMPOSSIBILITY =
struct
type clause = int list * thm
type sat_problem = (int * cterm option array) * (int * clause Array_Map.T) * thm * Proof.context
exception CLAUSE of cterm * thm
fun decode_approval_list i =
let
val i' = i + 1
in
filter (fn j => IntInf.andb (i', IntInf.<< (1, Word.fromInt j)) <> 0) (0 upto 3)
end
fun mk_committees k n =
let
fun go (lo, hi, n) =
if hi <= lo orelse n < 0 then []
else if n = 0 then [[]]
else map (fn xs => lo :: xs) (go (lo, hi, n-1)) @ go (lo+1, hi, n)
in
go (0, k, n)
end
fun ccand 0 = \<^cterm>‹C1›
| ccand 1 = \<^cterm>‹C2›
| ccand 2 = \<^cterm>‹C3›
| ccand 3 = \<^cterm>‹C4›
| ccand _ = raise Match
val cand = Thm.term_of o ccand
val candT = \<^typ>‹'a›
val candsetT = HOLogic.mk_setT candT
fun mk_msetT T = Type (\<^type_name>‹Multiset.multiset›, [T])
fun mk_empty_mset T = Const (\<^const_name>‹Groups.zero›, mk_msetT T)
fun mk_add_mset T =
let
val T' = mk_msetT T
in
Const (\<^const_name>‹Multiset.add_mset›, T --> T' --> T')
end
val add_mset_cterm_candset' = \<^cterm>‹add_mset :: 'a set ⇒ _›
val profileT = mk_msetT candsetT
val commT = mk_msetT candT
fun mk_mset T ts =
let
val empty = mk_empty_mset T
val add = mk_add_mset T
fun add' a b = add $ a $ b
in
fold_rev add' ts empty
end
type profile = {id : int, profile : int list, cterm : cterm, wf_thm : thm}
val is_pref_profile_iff_thm = @{thm eq_reflection[OF is_pref_profile_iff]}
val profile_cterm = (#cterm : profile -> cterm)
val profile_term = Thm.term_of o profile_cterm
val eq_trueD = @{lemma "PROP P ≡ PROP Trueprop True ⟹ PROP P" by simp}
fun simp_prove ctxt ct =
let
val thm = Simplifier.rewrite ctxt ct
val rhs = thm |> Thm.cconcl_of |> Thm.dest_equals_rhs
in
if Thm.term_of rhs = \<^prop>‹True› then
Thm.implies_elim
(Thm.instantiate (TVars.empty, Vars.make [((("P", 0), propT), ct)]) eq_trueD) thm
else
raise CTERM ("simp_prove: Failed to finish proof", [rhs])
end
fun simp_discharge ctxt thm =
let
val prem = Thm.cprem_of thm 1
in
Thm.implies_elim thm (simp_prove ctxt prem)
end
fun simp_discharge_all ctxt thm =
if Thm.nprems_of thm = 0 then thm else simp_discharge_all ctxt (simp_discharge ctxt thm)
fun consolidate_hyps thms =
let
val thm = Conjunction.intr_balanced thms
val thm' = Thm.assume (Thm.cprop_of thm)
val thms' = Conjunction.elim_balanced (length thms) thm'
in (thms', thm) end
val mset_swap_conv =
Conv.rewr_conv @{thm eq_reflection[OF add_mset_commute]}
fun norm_profile_conv [] ct = Conv.all_conv ct
| norm_profile_conv [_] ct = Conv.all_conv ct
| norm_profile_conv (x :: y :: xs) ct =
if x > y then
(mset_swap_conv
then_conv Conv.arg_conv (norm_profile_conv (x :: xs))) ct
else
Conv.all_conv ct
fun committee_score app (cs : int list) = length (filter (member op= app) cs)
fun committee_less app cs1 (cs2 : int list) = committee_score app cs1 < committee_score app cs2
fun group [] = []
| group (x :: xs) =
let
fun go (x, n) [] acc = rev ((x, n) :: acc)
| go (x, n) (y :: ys) acc =
if x = y then go (x, n+1) ys acc else go (y, 1) ys ((x, n) :: acc)
in
go (x, 1) xs []
end
fun pairs [] = []
| pairs (x :: xs) = map (fn y => (x, y)) xs @ pairs xs
fun encode_var (i, j) = i * 20 + j + 1
fun encode_lit (b, i, j) =
let val v = encode_var (i, j) in if b then ~v else v end
fun decode_var v = IntInf.divMod (v-1, 20)
fun decode_lit l = case decode_var (abs l) of (i, j) => (l < 0, i, j)
fun mk_disjs [] = raise Empty
| mk_disjs [x] = x
| mk_disjs (x :: xs) = HOLogic.mk_disj (x, mk_disjs xs)
fun rev_maps_pairs f xs =
let
fun go [] acc = acc
| go (x :: xs) acc =
let
fun go' [] acc = acc
| go' (y :: ys) acc = go' ys (f (x, y) @ acc)
in
go xs (go' xs acc)
end
in
go xs []
end
fun get_approval_list ct 0 = Thm.dest_arg1 ct
| get_approval_list ct i = get_approval_list (Thm.dest_arg ct) (i - 1)
val not_manipulable_thm' =
@{lemma "is_pref_profile A ⟹ is_pref_profile B ⟹ A' ≡ A ⟹ B' ≡ B ⟹ add_mset Y A ≡ add_mset X B ⟹
¬r A ≺[Comm(X)] r B"
by (rule not_manipulable'[of A B Y X]) auto}
val stratproof_aux_thm =
@{lemma "¬r B ≻[Comm(X)] r A ⟹ A' ≡ A ⟹ B' ≡ B ⟹ W ≺[Comm(X)] W' ⟹ r A' ≠ W ∨ r B' ≠ W'" by blast}
fun add_mset_cterm_candset ct1 profile =
Thm.apply (Thm.apply add_mset_cterm_candset' ct1) (profile_cterm profile)
fun prove_add_mset_eq (xs, ct1) (ys, ct2) =
let
val thm1 = norm_profile_conv xs ct1
val thm2 = norm_profile_conv ys ct2
in
Thm.transitive thm1 (Thm.symmetric thm2)
end
fun is_subset [] _ = true
| is_subset _ [] = false
| is_subset (x :: xs) (y :: ys) = (x = y andalso is_subset xs ys) orelse is_subset (x :: xs) ys
fun exists_index p xs =
let
fun go _ [] = false
| go i (x :: xs) = p (i, x) orelse go (i + 1) xs
in
go 0 xs
end
fun mk_sat_problem ctxt profiles_file =
let
val committee_list = mk_committees 4 3
val committees = Array.fromList committee_list
fun committee i = Array.sub (committees, i)
val approval_list_cache' =
Array.tabulate (14, decode_approval_list #> map cand #> HOLogic.mk_set candT)
val approval_list_cache =
Array.tabulate (14, fn i => Thm.cterm_of ctxt (Array.sub (approval_list_cache', i)))
fun holify_approval_list i = Array.sub (approval_list_cache, i)
fun holify_approval_list' i = Array.sub (approval_list_cache', i)
val holify_profile' =
map holify_approval_list' #> mk_mset candsetT
fun mk_profile ctxt id profile =
let
val term = holify_profile' profile
val goal = \<^instantiate>‹A = "term" in prop ‹is_pref_profile A››
val thm =
Goal.prove ctxt [] [] goal
(fn {context, ...} =>
HEADGOAL (EqSubst.eqsubst_tac ctxt [0] [is_pref_profile_iff_thm]
THEN_ALL_NEW Simplifier.simp_tac context))
val cterm = thm |> Thm.cconcl_of |> Thm.dest_arg |> Thm.dest_arg
in
{id = id, profile = profile, cterm = cterm, wf_thm = thm} : profile
end
fun read_profiles ctxt path =
let
fun process_line (id, l) =
l
|> space_explode " "
|> map (the o Int.fromString)
|> mk_profile ctxt id
in
File.read_lines path
|> filter (fn l => l <> "")
|> map_index I
|> chop_groups 50
|> Par_List.map (map process_line)
|> flat
end
val profs = read_profiles ctxt profiles_file
val profile_array = Array.fromList profs
local
fun mk_def prof =
((Binding.make ("prof" ^ Int.toString (#id prof),