src/HOL/Tools/Quickcheck/find_unused_assms.ML
author wenzelm
Mon, 25 Sep 2023 18:45:41 +0200
changeset 78705 fde0b195cb7d
parent 77893 dfc1db3f0fcb
child 80306 c2537860ccf8
permissions -rw-r--r--
clarified signature: avoid association with potentially dangerous Exn.capture;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     1
(*  Title:      HOL/Tools/Quickcheck/find_unused_assms.ML
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     2
    Author:     Lukas Bulwahn, TU Muenchen
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     3
50286
wenzelm
parents: 46961
diff changeset
     4
Finding unused assumptions in lemmas (using Quickcheck).
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     5
*)
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     6
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     7
signature FIND_UNUSED_ASSMS =
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
     8
sig
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
     9
  val check_unused_assms: Proof.context -> string * thm -> string * int list list option
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    10
  val find_unused_assms: Proof.context -> string -> (string * int list list option) list
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    11
  val print_unused_assms: Proof.context -> string option -> unit
50286
wenzelm
parents: 46961
diff changeset
    12
end
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    13
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    14
structure Find_Unused_Assms : FIND_UNUSED_ASSMS =
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    15
struct
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    16
60638
16d80e5ef2dc tuned signature;
wenzelm
parents: 59936
diff changeset
    17
fun thms_of thy thy_name =
16d80e5ef2dc tuned signature;
wenzelm
parents: 59936
diff changeset
    18
  Global_Theory.all_thms_of thy false
77889
5db014c36f42 clarified signature: explicitly distinguish theory_base_name vs. theory_long_name;
wenzelm
parents: 70117
diff changeset
    19
  |> filter (fn (_, th) => Thm.theory_base_name th = thy_name)
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    20
50286
wenzelm
parents: 46961
diff changeset
    21
fun do_while P f s list =
wenzelm
parents: 46961
diff changeset
    22
  if P s then
wenzelm
parents: 46961
diff changeset
    23
    let val s' = f s
wenzelm
parents: 46961
diff changeset
    24
    in do_while P f s' (cons s' list) end
wenzelm
parents: 46961
diff changeset
    25
  else list
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    26
50286
wenzelm
parents: 46961
diff changeset
    27
fun drop_indexes is xs =
wenzelm
parents: 46961
diff changeset
    28
  fold_index (fn (i, x) => if member (op =) is i then I else cons x) xs []
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    29
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    30
fun find_max_subsets [] = []
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    31
  | find_max_subsets (ss :: sss) = ss ::
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    32
      (find_max_subsets (map (filter_out (fn s => exists (fn s' => subset (op =) (s, s')) ss)) sss))
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    33
50286
wenzelm
parents: 46961
diff changeset
    34
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    35
(* main functionality *)
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    36
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    37
fun check_unused_assms ctxt =
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    38
  let
60825
bacfb7c45d81 more explicit context;
wenzelm
parents: 60638
diff changeset
    39
    val thy = Proof_Context.theory_of ctxt
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    40
    val ctxt' = ctxt
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    41
       |> Config.put Quickcheck.abort_potential true
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    42
       |> Config.put Quickcheck.quiet true
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    43
    fun check_single conjecture =
50287
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    44
      (case try (Quickcheck.test_terms ctxt' (true, true) []) [(conjecture, [])] of
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    45
        SOME (SOME _) => false
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    46
      | SOME NONE => true
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    47
      | NONE => false)
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    48
    fun build X Ss =
50287
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    49
      fold
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    50
        (fn S => fold
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    51
          (fn x =>
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    52
            if member (op =) S x then I
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    53
            else insert (eq_set (op =)) (insert (op =) x S)) X) Ss []
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    54
    fun check (name, th) =
60825
bacfb7c45d81 more explicit context;
wenzelm
parents: 60638
diff changeset
    55
      (case Logic.strip_horn (Thm.prop_of (Thm.unvarify_global thy th)) of
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    56
        ([], _) => (name, NONE)
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    57
      | (ts, t) =>
50287
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    58
          let
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    59
            fun mk_conjecture is = Logic.list_implies (drop_indexes is ts, t)
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    60
            val singles = filter (check_single o mk_conjecture o single) (0 upto (length ts - 1))
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    61
            val multiples =
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    62
              do_while (not o null)
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    63
                (fn I => filter (check_single o mk_conjecture) (build singles I))
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    64
                (map single singles) [(map single singles)]
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
    65
            val maximals = flat (find_max_subsets multiples)
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    66
          in (name, SOME maximals) end)
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    67
  in check end
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    68
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    69
fun find_unused_assms ctxt thy_name =
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    70
  let
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    71
    val thy = Proof_Context.theory_of ctxt
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    72
    val all_thms =
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    73
      thms_of thy thy_name
77893
wenzelm
parents: 77889
diff changeset
    74
      |> filter (fn (name, _) => Long_Name.count name = 2)  (* FIXME !? *)
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    75
      |> sort_by #1
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    76
    val check = check_unused_assms ctxt
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    77
  in rev (Par_List.map check all_thms) end
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    78
50286
wenzelm
parents: 46961
diff changeset
    79
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    80
(* printing results *)
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    81
50286
wenzelm
parents: 46961
diff changeset
    82
local
wenzelm
parents: 46961
diff changeset
    83
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    84
fun pretty_indexes is =
50578
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
    85
  Pretty.block
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
    86
    (flat (separate [Pretty.str " and", Pretty.brk 1]
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
    87
      (map (fn i => [Pretty.str (string_of_int (i + 1))]) (sort int_ord is))))
50286
wenzelm
parents: 46961
diff changeset
    88
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    89
fun pretty_thm (name, set_of_indexes) =
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    90
  Pretty.block (Pretty.str name :: Pretty.str ":" :: Pretty.brk 1 ::
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
    91
    Pretty.str "unnecessary assumption " ::
50578
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
    92
    separate (Pretty.str " or ") (map pretty_indexes set_of_indexes))
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    93
50286
wenzelm
parents: 46961
diff changeset
    94
in
wenzelm
parents: 46961
diff changeset
    95
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    96
fun print_unused_assms ctxt opt_thy_name =
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
    97
  let
77889
5db014c36f42 clarified signature: explicitly distinguish theory_base_name vs. theory_long_name;
wenzelm
parents: 70117
diff changeset
    98
    val thy_name =
5db014c36f42 clarified signature: explicitly distinguish theory_base_name vs. theory_long_name;
wenzelm
parents: 70117
diff changeset
    99
      the_default (Context.theory_base_name (Proof_Context.theory_of ctxt)) opt_thy_name
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   100
    val results = find_unused_assms ctxt thy_name
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   101
    val total = length results
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   102
    val with_assumptions = length (filter (is_some o snd) results)
50286
wenzelm
parents: 46961
diff changeset
   103
    val with_superfluous_assumptions =
70117
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
   104
      results |> map_filter
5ae2f266885f tuned signature;
wenzelm
parents: 70116
diff changeset
   105
        (fn (_, NONE) => NONE | (_, SOME []) => NONE | (name, SOME r) => SOME (name, r))
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   106
50578
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   107
    val msg =
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   108
      "Found " ^ string_of_int (length with_superfluous_assumptions) ^
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   109
      " theorems with (potentially) superfluous assumptions"
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   110
    val end_msg =
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   111
      "Checked " ^ string_of_int with_assumptions ^ " theorems with assumptions (" ^
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   112
      string_of_int total ^ " total) in the theory " ^ quote thy_name
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   113
  in
50287
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
   114
    [Pretty.str (msg ^ ":"), Pretty.str ""] @
6cb7a7b97eac simplified use of fold/map;
wenzelm
parents: 50286
diff changeset
   115
    map pretty_thm with_superfluous_assumptions @
50286
wenzelm
parents: 46961
diff changeset
   116
    [Pretty.str "", Pretty.str end_msg]
56334
6b3739fee456 some shortcuts for chunks, which sometimes avoid bulky string output;
wenzelm
parents: 56161
diff changeset
   117
  end |> Pretty.writeln_chunks
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   118
50286
wenzelm
parents: 46961
diff changeset
   119
end
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   120
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   121
val _ =
67149
e61557884799 prefer control symbol antiquotations;
wenzelm
parents: 65458
diff changeset
   122
  Outer_Syntax.command \<^command_keyword>\<open>find_unused_assms\<close>
50578
9efc99c990d5 more parallel find_unused_assms;
wenzelm
parents: 50287
diff changeset
   123
    "find theorems with (potentially) superfluous assumptions"
51658
21c10672633b discontinued Toplevel.no_timing complication -- also recovers timing of diagnostic commands, e.g. 'find_theorems';
wenzelm
parents: 50578
diff changeset
   124
    (Scan.option Parse.name >> (fn name =>
21c10672633b discontinued Toplevel.no_timing complication -- also recovers timing of diagnostic commands, e.g. 'find_theorems';
wenzelm
parents: 50578
diff changeset
   125
      Toplevel.keep (fn state => print_unused_assms (Toplevel.context_of state) name)))
46589
689311986778 adding new command "find_unused_assms"
bulwahn
parents:
diff changeset
   126
50286
wenzelm
parents: 46961
diff changeset
   127
end