Recursion, Induction and Rewriting¶
In this notebook, we're going to use Imandra to prove some interesting properties of functional programs. We'll learn a bit about induction, lemmas and rewrite rules along the way.
Let's start by defining our own append
function on lists. It should take two lists as input, and compute the result of appending (concatenating) the elements of the first list with those of the second list.
Note: Imandra contains a List
module with predefined functions like List.append
(i.e., (@)
), List.rev
, etc. However, we define our own versions of these functions by hand in this notebook so that all definitions and proofs are illustrated from scratch.
let rec append x y =
match x with
| [] -> y
| x :: xs -> x :: append xs y
val append : 'a list -> 'a list -> 'a list = <fun>
Termination proof
original: | append x y | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub: | append (List.tl x) y | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal: | Ordinal.Int (_cnt x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal: | Ordinal.Int (_cnt (List.tl x)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path: | [x <> []] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof: | detailed proof
Expand
|
Let's compute a bit with this function to understand how it works.
We can ask Imandra some questions about append
. For example, do there exist lists x
and y
such that append x y = [1;2;3;4;5;6;7;8]
?
verify (fun x y -> append x y <> [1;2;3;4;5;6;7;8])
- : Z.t list -> Z.t list -> bool = <fun> module CX : sig val x : Z.t list val y : Z.t list end
Counterexample (after 1 steps, 0.010s): let x : int list = [] let y : int list = [1; 2; 3; 4; 5; 6; 7; 8]
ground_instances: | 1 | ||||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||||
search_time: | 0.010s | ||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.010s] not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8])
- unroll
expr: (append_428/server x_1262/client y_1263/client)
expansions: - Sat (Some let x : int list = [] let y : int list = [(Z.of_nativeint (1n)); (Z.of_nativeint (2n)); (Z.of_nativeint (3n)); (Z.of_nativeint (4n)); (Z.of_nativeint (5n)); (Z.of_nativeint (6n)); (Z.of_nativeint (7n)); (Z.of_nativeint (8n))] )
Ah, of course! What if we make the problem a little harder -- perhaps we want x
and y
to be the same length:
verify (fun x y -> List.length x = List.length y ==> append x y <> [1;2;3;4;5;6;7;8])
- : Z.t list -> Z.t list -> bool = <fun> module CX : sig val x : Z.t list val y : Z.t list end
Counterexample (after 15 steps, 0.021s): let x : int list = [1; 2; 3; 4] let y : int list = [5; 6; 7; 8]
ground_instances: | 15 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time: | 0.021s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.021s] List.length ( :var_0: ) = List.length ( :var_1: ) ==> not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8])
simplify
into: not (List.length ( :var_0: ) = List.length ( :var_1: )) || not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8])
expansions: []
rewrite_steps: forward_chaining: - unroll
expr: (append_445/server x_1265/client y_1266/client)
expansions: - unroll
expr: (|List.length_441/server| y_1266/client)
expansions: - unroll
expr: (|List.length_441/server| x_1265/client)
expansions: - unroll
expr: (append_445/server (|get.::.1_440/server| x_1265/client) y_1266/client)
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| y_1266/client))
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| x_1265/client))
expansions: - unroll
expr: (append_445/server (|get.::.1_440/server| (|get.::.1_440/server| x_1265/client)) y_1266/client)
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| y_1266/client)))
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| x_1265/client)))
expansions: - unroll
expr: (append_445/server (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server| x_1265…
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server|…
expansions: - unroll
expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server|…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/…
expansions: - Sat (Some let x : int list = [(Z.of_nativeint (1n)); (Z.of_nativeint (2n)); (Z.of_nativeint (3n)); (Z.of_nativeint (4n))] let y : int list = [(Z.of_nativeint (5n)); (Z.of_nativeint (6n)); (Z.of_nativeint (7n)); (Z.of_nativeint (8n))] )
Nice! Remember that counterexamples are always reflected into the runtime in a module called CX
. So, we can compute with this counterexample and see that it indeed refutes our conjecture:
Let's now investigate a more interesting conjecture: That append
is associative.
That is,
(forall (x,y,z) : 'a list. append x (append y z) = append (append x y) z)
.
Is this property true? Let's ask Imandra.
verify (fun x y z -> append x (append y z) = append (append x y) z)
- : 'a list -> 'a list -> 'a list -> bool = <fun>
expanded: |
|
blocked: |
|
ground_instances: | 100 |
definitions: | 0 |
inductions: | 0 |
search_time: | 0.137s |
start[0.137s] append ( :var_0: ) (append ( :var_1: ) ( :var_2: )) = append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
- unroll
expr: (append_384/server (append_384/server x_377/server y_378/server) z_379/server)
expansions: - unroll
expr: (append_384/server x_377/server y_378/server)
expansions: - unroll
expr: (append_384/server x_377/server (append_384/server y_378/server z_379/server))
expansions: - unroll
expr: (append_384/server y_378/server z_379/server)
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (append_384/server x_377/server y_378/server)) z_379/s…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| x_377/server) y_378/server)
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| x_377/server) (append_384/server y_378/server z_379/se…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| y_378/server) z_379/server)
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (append_384/server x_377/serv…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| x_377/server)) y_378/server)
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| x_377/server)) (append_384/serv…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| x_377/…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| x_377/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| y_378/server)) z_379/server)
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| y_378/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/…
expansions:
Imandra tells us that there are no counterexamples to this conjecture up to our current recursion unrolling bound (100
).
This gives us some confidence that this conjecture is true. Let's now ask Imandra to prove it for all possible cases by induction.
verify (fun x y z -> append x (append y z) = append (append x y) z) [@@induct]
- : 'a list -> 'a list -> 'a list -> bool = <fun> Goal: append x (append y z) = append (append x y) z. 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- append x (append y z) = append (append x y) z Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. append x (append y z) = append (append x y) z But simplification reduces this to true, using the definition of append. Subgoal 1.1: H0. x <> [] H1. append (List.tl x) (append y z) = append (append (List.tl x) y) z |--------------------------------------------------------------------------- append x (append y z) = append (append x y) z But simplification reduces this to true, using the definition of append. ⓘ Rules: (:def append) (:induct append)
ground_instances: | 0 |
definitions: | 5 |
inductions: | 1 |
search_time: | 0.072s |
start[0.072s, "Goal"] append ( :var_0: ) (append ( :var_1: ) ( :var_2: )) = append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
subproof
append x (append y z) = append (append x y) zstart[0.072s, "1"] append x (append y z) = append (append x y) z
induction on (functional ?) :scheme (not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : sko_ty_0 list) = append y z in let (_x_2 : bool) = append x _x_1 = append (append x y) z in let (_x_3 : sko_ty_0 list) = List.tl x in (_x_0 || _x_2) && (not (_x_0 && (append _x_3 _x_1 = append (append _x_3 y) z)) || _x_2) :cases [x <> [] || (append x (append y z) = append (append x y) z); let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)start[0.039s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)
simplify
into: true
expansions: [append, append, append]
rewrite_steps: forward_chaining:
subproof
x <> [] || (append x (append y z) = append (append x y) z)start[0.039s, "1.2"] x <> [] || (append x (append y z) = append (append x y) z)
simplify
into: true
expansions: [append, append]
rewrite_steps: forward_chaining:
Beautiful! So, we see Imandra proved our goal by induction on x
, using a structural induction principle derived from the 'a list
datatype. We now know the property holds for all possible inputs (of which there are infinitely many!).
By the way, we can always view Imandra's current session configuration with the #config
directive:
#config
---------------------------------------------------------------------------- Imandra session configuration ---------------------------------------------------------------------------- 0. Mode: logic (see: program,logic) 1. Load path: [] (see: dir) 2. Undo (implicit push): false (see: undo) 3. Console tags: waterfall, suggestions (see: console_tags) 4. Console color: true (see: console_color) 5. Interactive hints: true (see: interactive_hints) 6. Timeout: 60000 (see: timeout) 7. Allow redefinition: true (see: redef) 8. Recursion unroll depth: 100 (see: unroll) 9. SAT seed: 0 (see: sat_seed) 10. SMT seed: 0 (see: smt_seed) 11. Solver seed: 0 (see: solver_seed) 12. Top results: true (see: top_results) 13. Validate definitions: true (see: validate) 14. Skip proofs: false (see: skip_proofs) 15. Max induction depth: 3 (see: max_induct) 16. Induction unroll depth: 8 (see: induct_unroll) 17. Backchain limit: 100 (see: backchain_limit) 18. Enable all definitions when unrolling: true (see: unroll_enable_all) 19. Algebraic number approx. precision: 10 (see: pp_approx) 20. Reflect approximate models: false (see: reflect_approx_models) 21. Max size for callgraphs: 80 (see: callgraph_size) 22. Include expansion steps in callgraphs: false (see: callgraph_include_expansions) 23. Store proofs: false (see: store_proofs) ----------------------------------------------------------------------------
Lemmas and Rules¶
When we use the verify
command, we give it a closed formula (e.g., a lambda term such as (fun x y z -> foo x y z)
and ask Imandra to prove that the goal always evaluates to true
. Notice that the verify
command does not give the verification goal a name.
Once we've proved a goal, we often want to record it as a theorem
. This is both to document our verification progress and to make it possible for our proved goal to be used as a lemma in subsequent verification efforts.
This can be done by the theorem
command.
For example, we could name our result assoc_append
and install it as a theorem as follows:
theorem assoc_append x y z = append x (append y z) = append (append x y) z) [@@induct]
By default, a theorem
is not installed as a rule that will be used automatically in subsequent verification efforts. To install the theorem
as a rule, we can use the [@@rewrite]
or [@@forward_chaining]
attributes.
Let's focus in this notebook on the use of theorems as rewrite rules.
Rewrite Rules¶
A rewrite rule is a theorem of the form (H1 && ... && Hk ==> LHS = RHS)
. It instructs Imandra's simplifier to replace terms it matches with the LHS
with the suitably instantiated RHS
, provided that the corresponding instantiations of the hypotheses H1, ..., Hk
can be proved.
It is also allowed for the conclusion of the rule to be a Boolean term, e.g., (H1 && ... && Hk ==> foo)
is interpreted with an RHS
of true
, i.e., (H1 && ... && Hk ==> foo = true)
.
Good rewrite rules can have a powerful normalising effect on formulas. We usually want to orient them so that the RHS
is a better (i.e., simpler or more canonical) term than the LHS
.
Let us see the use of rewrite rules through a prove about the function reverse
.
Reverse¶
Let's define a function to reverse a list. Note how it uses our append
function we defined above:
let rec reverse x =
match x with
| [] -> []
| x :: xs -> append (reverse xs) [x]
val reverse : 'a list -> 'a list = <fun>
Termination proof
original: | reverse x | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub: | reverse (List.tl x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal: | Ordinal.Int (_cnt x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal: | Ordinal.Int (_cnt (List.tl x)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path: | [x <> []] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof: | detailed proof
Expand
|
Let's compute with reverse
to help gain confidence we defined it correctly.
Let's ask Imandra, are there any lists that are their own reverse?
instance (fun x -> reverse x = x)
- : 'a list -> bool = <fun> module CX : sig type t = C_t_0 val x : t list end
Instance (after 1 steps, 0.009s): type t = C_t_0 let x : t list = []
ground_instances: | 1 | ||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||
search_time: | 0.009s | ||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.009s] reverse ( :var_0: ) = ( :var_0: )
- unroll
expr: (reverse_795/server x_792/server)
expansions: - Sat (Some type t = C_t_0 let x : t list = [] )
Ah! Of course, the empty list is its own reverse. What about longer lists, such as those of length 5?
Note that here we use the keyword instance
, which tries to find values that satisfy the given property, instead of trying to prove the property.
instance (fun x -> List.length x >= 5 && reverse x = x)
- : 'a list -> bool = <fun> module CX : sig type t = C_t_0 | C_t_1 | C_t_2 | C_t_3 val x : t list end
Instance (after 33 steps, 0.036s): type t = C_t_0 | C_t_1 | C_t_2 | C_t_3 let x : t list = [C_t_1; C_t_2; C_t_3; C_t_2; C_t_1]
ground_instances: | 33 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time: | 0.036s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.036s] (List.length ( :var_0: ) >= 5) && (reverse ( :var_0: ) = ( :var_0: ))
- unroll
expr: (reverse_841/server x_834/server)
expansions: - unroll
expr: (|List.length_837/server| x_834/server)
expansions: - unroll
expr: (append_845/server (reverse_841/server (|get.::.1_833/server| x_834/server)) (|::| (|get.::.0_83…
expansions: - unroll
expr: (reverse_841/server (|get.::.1_833/server| x_834/server))
expansions: - unroll
expr: (|List.length_837/server| (|get.::.1_833/server| x_834/server))
expansions: - unroll
expr: (append_845/server (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| x_834/ser…
expansions: - unroll
expr: (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| x_834/server)))
expansions: - unroll
expr: (|List.length_837/server| (|get.::.1_833/server| (|get.::.1_833/server| x_834/server)))
expansions: - unroll
expr: (append_845/server (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/server| x_834/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser…
expansions: - unroll
expr: (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/server| x_834…
expansions: - unroll
expr: (|List.length_837/server| (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/server|…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/…
expansions: - unroll
expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se…
expansions: - Sat (Some type t = C_t_0 | C_t_1 | C_t_2 | C_t_3 let x : t list = [C_t_1; C_t_2; C_t_3; C_t_2; C_t_1] )
Ah! Of course, palindromes! This counterexample is also interesting because, in addition to giving us a concrete value CX.x
, this counterexample also involves the synthesis of an algebraic datatype. This is because our goal was polymorphic. If we want, for example, an int list
counterexample, we can simply annotate our goal with types:
instance (fun (x : int list) -> List.length x >= 5 && reverse x = x)
- : Z.t list -> bool = <fun> module CX : sig val x : Z.t list end
Instance (after 32 steps, 0.035s): let x : int list = [6; 10; 8; 10; 6]
ground_instances: | 32 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time: | 0.035s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.035s] (List.length ( :var_0: ) >= 5) && (reverse ( :var_0: ) = ( :var_0: ))
- unroll
expr: (reverse_955/server x_1289/client)
expansions: - unroll
expr: (|List.length_951/server| x_1289/client)
expansions: - unroll
expr: (append_959/server (reverse_955/server (|get.::.1_950/server| x_1289/client)) (|::| (|get.::.0_9…
expansions: - unroll
expr: (reverse_955/server (|get.::.1_950/server| x_1289/client))
expansions: - unroll
expr: (|List.length_951/server| (|get.::.1_950/server| x_1289/client))
expansions: - unroll
expr: (append_959/server (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| x_1289/cl…
expansions: - unroll
expr: (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| x_1289/client)))
expansions: - unroll
expr: (|List.length_951/server| (|get.::.1_950/server| (|get.::.1_950/server| x_1289/client)))
expansions: - unroll
expr: (append_959/server (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/server| x_1289/cl…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser…
expansions: - unroll
expr: (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/server| x_128…
expansions: - unroll
expr: (|List.length_951/server| (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/server|…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se…
expansions: - unroll
expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se…
expansions: - Sat (Some let x : int list = [(Z.of_nativeint (6n)); (Z.of_nativeint (10n)); (Z.of_nativeint (8n)); (Z.of_nativeint (10n)); (Z.of_nativeint (6n))] )
Excellent! And we can of course compute with our counterexample:
Reverse of Reverse is the Identity¶
Now that we've defined our reverse
function and experimented a bit with it, let's try to prove an interesting theorem. Let's prove that
(forall x, (reverse (reverse x)) = x)
.
That is, if we take an arbitrary list x
and we reverse it twice, we always get our original x
back unscathed.
As usual, let's start by asking Imandra to verify
this via bounded checking (a.k.a. recursive unrolling).
verify (fun x -> reverse (reverse x) = x)
- : 'a list -> bool = <fun>
expanded: |
|
blocked: |
|
ground_instances: | 100 |
definitions: | 0 |
inductions: | 0 |
search_time: | 0.197s |
start[0.197s] reverse (reverse ( :var_0: )) = ( :var_0: )
- unroll
expr: (reverse_530/server (reverse_530/server x_527/server))
expansions: - unroll
expr: (reverse_530/server x_527/server)
expansions: - unroll
expr: (append_534/server (reverse_530/server (|get.::.1_526/server| (reverse_530/server x_527/server))) …
expansions: - unroll
expr: (reverse_530/server (|get.::.1_526/server| (reverse_530/server x_527/server)))
expansions: - unroll
expr: (append_534/server (reverse_530/server (|get.::.1_526/server| x_527/server)) (|::| (|get.::.0_52…
expansions: - unroll
expr: (reverse_530/server (|get.::.1_526/server| x_527/server))
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (append_534/server (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/server| x_527/ser…
expansions: - unroll
expr: (append_534/server (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| x_527/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| x_527/server)))
expansions: - unroll
expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/server x_527/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/server| x_527…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser…
expansions: - unroll
expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser…
expansions:
Imandra tells us that there are no counterexamples up to our current unrolling bound (100
).
So, our conjecture seems like a good candidate for proof by induction.
verify (fun x -> reverse (reverse x) = x) [@@induct]
- : 'a list -> bool = <fun> Goal: reverse (reverse x) = x. 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (reverse x) = x Must try induction. We shall induct according to a scheme derived from reverse. Induction scheme: (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (reverse x) = x But simplification reduces this to true, using the definition of reverse. Subgoal 1.1: H0. x <> [] H1. reverse (reverse (List.tl x)) = List.tl x |--------------------------------------------------------------------------- reverse (reverse x) = x This simplifies, using the definition of reverse to: Subgoal 1.1': H0. x <> [] H1. reverse (reverse (List.tl x)) = List.tl x |--------------------------------------------------------------------------- reverse (append (reverse (List.tl x)) [List.hd x]) = x We can eliminate destructors by the following substitution: x -> x1 :: x2 This produces the modified subgoal: Subgoal 1.1'': H0. reverse (reverse x2) = x2 |--------------------------------------------------------------------------- reverse (append (reverse x2) [x1]) = x1 :: x2 Cross-fertilizing with: reverse (reverse x2) = x2 This produces the modified subgoal: Subgoal 1.1''': |--------------------------------------------------------------------------- reverse (append (reverse x2) [x1]) = x1 :: (reverse (reverse x2)) Candidates for generalization: reverse x2 This produces the modified subgoal: Subgoal 1.1'''': |--------------------------------------------------------------------------- reverse (append gen_1 [x1]) = x1 :: (reverse gen_1) Must try induction. The recursive terms in the conjecture suggest 2 inductions. Subsumption and merging reduces this to 1. We shall induct according to a scheme derived from append. Induction scheme: (not (gen_1 <> []) ==> φ gen_1 x1) && (gen_1 <> [] && φ (List.tl gen_1) x1 ==> φ gen_1 x1). 2 nontautological subgoals. Subgoal 1.1''''.2: |--------------------------------------------------------------------------- C0. gen_1 <> [] C1. reverse (append gen_1 [x1]) = x1 :: (reverse gen_1) But simplification reduces this to true, using the definitions of append and reverse. Subgoal 1.1''''.1: H0. gen_1 <> [] H1. reverse (append (List.tl gen_1) [x1]) = x1 :: (reverse (List.tl gen_1)) |--------------------------------------------------------------------------- reverse (append gen_1 [x1]) = x1 :: (reverse gen_1) This simplifies, using the definitions of append and reverse to: Subgoal 1.1''''.1': H0. gen_1 <> [] H1. reverse (append (List.tl gen_1) [x1]) = x1 :: (reverse (List.tl gen_1)) |--------------------------------------------------------------------------- append (reverse (append (List.tl gen_1) [x1])) [List.hd gen_1] = x1 :: (append (reverse (List.tl gen_1)) [List.hd gen_1]) But we verify Subgoal 1.1''''.1' by recursive unrolling. ⓘ Rules: (:def append) (:def reverse) (:induct append) (:induct reverse)
ground_instances: | 2 | ||||||||||||||||||||||||||||||||||||||||
definitions: | 11 | ||||||||||||||||||||||||||||||||||||||||
inductions: | 2 | ||||||||||||||||||||||||||||||||||||||||
search_time: | 0.445s | ||||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.445s, "Goal"] reverse (reverse ( :var_0: )) = ( :var_0: )
subproof
reverse (reverse x) = xstart[0.444s, "1"] reverse (reverse x) = x
induction on (functional ?) :scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : bool) = reverse (reverse x) = x in let (_x_2 : sko_ty_0 list) = List.tl x in (_x_0 || _x_1) && (not (_x_0 && (reverse (reverse _x_2) = _x_2)) || _x_1) :cases [x <> [] || (reverse (reverse x) = x); let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)start[0.402s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)
simplify
into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse _x_0 in not (x <> []) || not (reverse _x_1 = _x_0) || (reverse (append _x_1 [List.hd x]) = x)
expansions: reverse
rewrite_steps: forward_chaining: Elim_destructor (:cstor ( :: ) :replace x1 :: x2 :context [])
Generalize (let (_x_0 : sko_ty_0 list) = reverse x2 in reverse (append _x_0 [x1]) = x1 :: (reverse _x_0) :as (gen_1 : sko_ty_0 list))
induction on (functional ?) :scheme (not (gen_1 <> []) ==> φ gen_1 x1) && (gen_1 <> [] && φ (List.tl gen_1) x1 ==> φ gen_1 x1)
Split (let (_x_0 : bool) = gen_1 <> [] in let (_x_1 : sko_ty_0 list) = [x1] in let (_x_2 : bool) = reverse (append gen_1 _x_1) = x1 :: (reverse gen_1) in let (_x_3 : sko_ty_0 list) = List.tl gen_1 in (_x_0 || _x_2) && (not (_x_0 && (reverse (append _x_3 _x_1) = x1 :: (reverse _x_3))) || _x_2) :cases [gen_1 <> [] || (reverse (append gen_1 [x1]) = x1 :: (reverse gen_1)); let (_x_0 : sko_ty_0 list) = List.tl gen_1 in let (_x_1 : sko_ty_0 list) = [x1] in not (gen_1 <> []) || not (reverse (append _x_0 _x_1) = x1 :: (reverse _x_0)) || (reverse (append gen_1 _x_1) = x1 :: (reverse gen_1))])
Subproof
Subproof
subproof
x <> [] || (reverse (reverse x) = x)start[0.402s, "1.2"] x <> [] || (reverse (reverse x) = x)
simplify
into: true
expansions: [reverse, reverse]
rewrite_steps: forward_chaining:
Success!
Imandra proves this fact automatically, and its inductive proof actually involves a nested subinduction.
Imandra has powerful techniques for automating complex inductions, involving simplification, destructor elimination, generalization and more. But it's often the case that nested inductions actually suggest lemmas that could be of general use.
To illustrate this, let's take a look at one of the interesting subgoals in our proof:
reverse (append gen_1 (x1 :: [])) = x1 :: (reverse gen_1)
This looks like a very nice fact. Instead of Imandra having to derive this fact as a subinduction, let's prove it as a theorem
itself and install it as a rewrite rule. Then we will be able to apply it later and shorten our subsequent proofs.
theorem rev_app_single x y = reverse (append x [y]) = y :: (reverse x) [@@induct] [@@rewrite]
val rev_app_single : 'a list -> 'a -> bool = <fun> Goal: reverse (append x [y]) = y :: (reverse x). 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (append x [y]) = y :: (reverse x) Must try induction. The recursive terms in the conjecture suggest 2 inductions. Subsumption and merging reduces this to 1. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (append x [y]) = y :: (reverse x) But simplification reduces this to true, using the definitions of append and reverse. Subgoal 1.1: H0. x <> [] H1. reverse (append (List.tl x) [y]) = y :: (reverse (List.tl x)) |--------------------------------------------------------------------------- reverse (append x [y]) = y :: (reverse x) This simplifies, using the definitions of append and reverse to: Subgoal 1.1': H0. x <> [] H1. reverse (append (List.tl x) [y]) = y :: (reverse (List.tl x)) |--------------------------------------------------------------------------- append (reverse (append (List.tl x) [y])) [List.hd x] = y :: (append (reverse (List.tl x)) [List.hd x]) But we verify Subgoal 1.1' by recursive unrolling. ⓘ Rules: (:def append) (:def reverse) (:induct append)
ground_instances: | 2 | ||||||||||||||||||||||||||||||||||||||||
definitions: | 8 | ||||||||||||||||||||||||||||||||||||||||
inductions: | 1 | ||||||||||||||||||||||||||||||||||||||||
search_time: | 0.162s | ||||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.162s, "Goal"] reverse (append ( :var_0: ) [( :var_1: )]) = ( :var_1: ) :: (reverse ( :var_0: ))
subproof
reverse (append x [y]) = y :: (reverse x)start[0.161s, "1"] reverse (append x [y]) = y :: (reverse x)
induction on (functional ?) :scheme (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : sko_ty_0 list) = [y] in let (_x_2 : bool) = reverse (append x _x_1) = y :: (reverse x) in let (_x_3 : sko_ty_0 list) = List.tl x in (_x_0 || _x_2) && (not (_x_0 && (reverse (append _x_3 _x_1) = y :: (reverse _x_3))) || _x_2) :cases [x <> [] || (reverse (append x [y]) = y :: (reverse x)); let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = [y] in not (x <> []) || not (reverse (append _x_0 _x_1) = y :: (reverse _x_0)) || (reverse (append x _x_1) = y :: (reverse x))])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = [y] in not (x <> []) || not (reverse (append _x_0 _x_1) = y :: (reverse _x_0)) || (reverse (append x _x_1) = y :: (reverse x))start[0.123s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = [y] in not (x <> []) || not (reverse (append _x_0 _x_1) = y :: (reverse _x_0)) || (reverse (append x _x_1) = y :: (reverse x))
simplify
into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [y]) in let (_x_2 : sko_ty_0 list) = reverse _x_0 in let (_x_3 : sko_ty_0 list) = [List.hd x] in not (x <> []) || not (_x_1 = y :: _x_2) || (append _x_1 _x_3 = y :: (append _x_2 _x_3))
expansions: [reverse, reverse, append]
rewrite_steps: forward_chaining: subproof
let (_x_0 : sko_ty_0 list) = List.tl ( :var_0: ) in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [( :var_1: )]) in let (_x_2 : sko_ty_0 list) = reverse _x_0 in let (_x_3 : sko_ty_0 list) = [List.hd ( :var_0: )] in not (( :var_0: ) <> []) || not (_x_1 = ( :var_1: ) :: _x_2) || (append _x_1 _x_3 = ( :var_1: ) :: (append _x_2 _x_3))Start (let (_x_0 : sko_ty_0 list) = List.tl ( :var_0: ) in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [( :var_1: )]) in let (_x_2 : sko_ty_0 list) = reverse _x_0 in let (_x_3 : sko_ty_0 list) = [List.hd ( :var_0: )] in not (( :var_0: ) <> []) || not (_x_1 = ( :var_1: ) :: _x_2) || (append _x_1 _x_3 = ( :var_1: ) :: (append _x_2 _x_3)) :time 0.010s)
subproof
x <> [] || (reverse (append x [y]) = y :: (reverse x))start[0.123s, "1.2"] x <> [] || (reverse (append x [y]) = y :: (reverse x))
simplify
into: true
expansions: [reverse, reverse, append, reverse, append]
rewrite_steps: forward_chaining:
Now, if we were to try to prove our original reverse (reverse x)
goal again, we'd get a shorter proof that makes use of our new lemma:
verify (fun x -> reverse (reverse x) = x) [@@induct]
- : 'a list -> bool = <fun> Goal: reverse (reverse x) = x. 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (reverse x) = x Must try induction. We shall induct according to a scheme derived from reverse. Induction scheme: (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (reverse x) = x But simplification reduces this to true, using the definition of reverse. Subgoal 1.1: H0. x <> [] H1. reverse (reverse (List.tl x)) = List.tl x |--------------------------------------------------------------------------- reverse (reverse x) = x This simplifies, using the definition of reverse to: Subgoal 1.1': H0. x <> [] H1. reverse (reverse (List.tl x)) = List.tl x |--------------------------------------------------------------------------- reverse (append (reverse (List.tl x)) [List.hd x]) = x But simplification reduces this to true, using the rewrite rule rev_app_single. ⓘ Rules: (:def reverse) (:rw rev_app_single) (:induct reverse)
ground_instances: | 0 |
definitions: | 3 |
inductions: | 1 |
search_time: | 0.124s |
start[0.124s, "Goal"] reverse (reverse ( :var_0: )) = ( :var_0: )
subproof
reverse (reverse x) = xstart[0.124s, "1"] reverse (reverse x) = x
induction on (functional ?) :scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : bool) = reverse (reverse x) = x in let (_x_2 : sko_ty_0 list) = List.tl x in (_x_0 || _x_1) && (not (_x_0 && (reverse (reverse _x_2) = _x_2)) || _x_1) :cases [x <> [] || (reverse (reverse x) = x); let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)start[0.089s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)
simplify
into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse _x_0 in not (x <> []) || not (reverse _x_1 = _x_0) || (reverse (append _x_1 [List.hd x]) = x)
expansions: reverse
rewrite_steps: forward_chaining: simplify
into: true
expansions: []
rewrite_steps: rev_app_single
forward_chaining:
subproof
x <> [] || (reverse (reverse x) = x)start[0.089s, "1.2"] x <> [] || (reverse (reverse x) = x)
simplify
into: true
expansions: [reverse, reverse]
rewrite_steps: forward_chaining:
Excellent!
Reverse Append¶
Now, let's try to prove another interesting conjecture about our functions append
and reverse
:
reverse (append x y) = append (reverse y) (reverse x)
Actually, first let's pretend we made a mistake in formulating this conjecture, and accidentally swapped the x
and y
on the RHS (Right Hand Side) of the equality. Let's constrain the types of lists involved to make the counterexample especially easy to read:
verify (fun (x : int list) y -> reverse (append x y) = append (reverse x) (reverse y))
- : Z.t list -> Z.t list -> bool = <fun> module CX : sig val x : Z.t list val y : Z.t list end
Counterexample (after 21 steps, 0.026s): let x : int list = [0] let y : int list = [2]
ground_instances: | 21 | ||||||||||||||||||||||||||||||||||||||
definitions: | 0 | ||||||||||||||||||||||||||||||||||||||
inductions: | 0 | ||||||||||||||||||||||||||||||||||||||
search_time: | 0.026s | ||||||||||||||||||||||||||||||||||||||
details: | Expand
|
start[0.026s] reverse (append ( :var_0: ) ( :var_1: )) = append (reverse ( :var_0: )) (reverse ( :var_1: ))
- unroll
expr: (append_2149/server (reverse_2154/server x_1304/client) (reverse_2154/server y_1305/client))
expansions: - unroll
expr: (reverse_2154/server y_1305/client)
expansions: - unroll
expr: (reverse_2154/server x_1304/client)
expansions: - unroll
expr: (reverse_2154/server (append_2149/server x_1304/client y_1305/client))
expansions: - unroll
expr: (append_2149/server x_1304/client y_1305/client)
expansions: - unroll
expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server x_1304/client)) (reverse_2154/s…
expansions: - unroll
expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| y_1305/client)) (|::| (|get.::.…
expansions: - unroll
expr: (reverse_2154/server (|get.::.1_2148/server| y_1305/client))
expansions: - unroll
expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| x_1304/client)) (|::| (|get.::.…
expansions: - unroll
expr: (reverse_2154/server (|get.::.1_2148/server| x_1304/client))
expansions: - unroll
expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| (append_2149/server x_1304/cl…
expansions: - unroll
expr: (reverse_2154/server (|get.::.1_2148/server| (append_2149/server x_1304/client y_1305/client)))
expansions: - unroll
expr: (append_2149/server (|get.::.1_2148/server| x_1304/client) y_1305/client)
expansions: - unroll
expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/server| x_130…
expansions: - unroll
expr: (append_2149/server (|get.::.1_2148/server| (|get.::.1_2148/server| (reverse_2154/server x_130…
expansions: - unroll
expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| x_130…
expansions: - unroll
expr: (let ((a!1 (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/…
expansions: - unroll
expr: (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| (append_2149/server x_130…
expansions: - unroll
expr: (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| x_1304/client)))
expansions: - unroll
expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/server| y_130…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/…
expansions: - Sat (Some let x : int list = [(Z.of_nativeint (0n))] let y : int list = [(Z.of_nativeint (2n))] )
Ah! Let's do it right this time:
verify (fun (x : int list) y -> reverse (append x y) = append (reverse y) (reverse x))
- : Z.t list -> Z.t list -> bool = <fun>
expanded: |
|
blocked: |
|
ground_instances: | 100 |
definitions: | 0 |
inductions: | 0 |
search_time: | 0.245s |
start[0.245s] reverse (append ( :var_0: ) ( :var_1: )) = append (reverse ( :var_1: )) (reverse ( :var_0: ))
- unroll
expr: (append_1082/server (reverse_1087/server y_1081/server) (reverse_1087/server x_1080/server))
expansions: - unroll
expr: (reverse_1087/server x_1080/server)
expansions: - unroll
expr: (reverse_1087/server y_1081/server)
expansions: - unroll
expr: (reverse_1087/server (append_1082/server x_1080/server y_1081/server))
expansions: - unroll
expr: (append_1082/server x_1080/server y_1081/server)
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server y_1081/server)) (reverse_1087/s…
expansions: - unroll
expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| x_1080/server)) (|::| (|get.::.…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| x_1080/server))
expansions: - unroll
expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| y_1081/server)) (|::| (|get.::.…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| y_1081/server))
expansions: - unroll
expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (append_1082/server x_1080/se…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (append_1082/server x_1080/server y_1081/server)))
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| x_1080/server) y_1081/server)
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/server| x_108…
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087/server y_108…
expansions: - unroll
expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_108…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_1080/server)))
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/server| y_108…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (append_1082/server x_108…
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_1080/server)) y_1081/serve…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| y_108…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| y_1081/server)))
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| x…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| x_…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| y…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/…
expansions: - unroll
expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10…
expansions: - unroll
expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/…
expansions:
Our conjecture has passed through to depth 100 recursive unrolling, so we feel pretty confident it is true. Let's try to prove it by induction:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]
- : 'a list -> 'a list -> bool = <fun> Goal: reverse (append x y) = append (reverse y) (reverse x). 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (append x y) = append (reverse y) (reverse x) This simplifies, using the definitions of append and reverse to: Subgoal 1.2': |--------------------------------------------------------------------------- C0. x <> [] C1. reverse y = append (reverse y) [] Must try induction. We shall induct according to a scheme derived from reverse. Induction scheme: (not (y <> []) ==> φ x y) && (y <> [] && φ x (List.tl y) ==> φ x y). 2 nontautological subgoals. Subgoal 1.2'.2: |--------------------------------------------------------------------------- C0. y <> [] C1. x <> [] C2. reverse y = append (reverse y) [] But simplification reduces this to true, using the definitions of append and reverse. Subgoal 1.2'.1: H0. y <> [] H1. x <> [] || (reverse (List.tl y) = append (reverse (List.tl y)) []) |--------------------------------------------------------------------------- C0. x <> [] C1. reverse y = append (reverse y) [] This simplifies, using the definition of reverse to: Subgoal 1.2'.1': H0. reverse (List.tl y) = append (reverse (List.tl y)) [] H1. y <> [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (reverse (List.tl y)) [List.hd y] = append (append (reverse (List.tl y)) [List.hd y]) [] We can eliminate destructors by the following substitution: y -> y1 :: y2 This produces the modified subgoal: Subgoal 1.2'.1'': H0. reverse y2 = append (reverse y2) [] |--------------------------------------------------------------------------- C0. append (reverse y2) [y1] = append (append (reverse y2) [y1]) [] C1. x <> [] Cross-fertilizing with: reverse y2 = append (reverse y2) [] This produces the modified subgoal: Subgoal 1.2'.1''': |--------------------------------------------------------------------------- C0. append (append (reverse y2) []) [y1] = append (append (reverse y2) [y1]) [] C1. x <> [] Must try induction. We shall induct according to a scheme derived from reverse. Induction scheme: (not (y2 <> []) ==> φ x y1 y2) && (y2 <> [] && φ x y1 (List.tl y2) ==> φ x y1 y2). 2 nontautological subgoals. Subgoal 1.2'.1'''.2: |--------------------------------------------------------------------------- C0. x <> [] C1. y2 <> [] C2. append (append (reverse y2) []) [y1] = append (append (reverse y2) [y1]) [] But simplification reduces this to true, using the definitions of append and reverse. Subgoal 1.2'.1'''.1: H0. y2 <> [] H1. (append (append (reverse (List.tl y2)) []) [y1] = append (append (reverse (List.tl y2)) [y1]) []) || x <> [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (append (reverse y2) []) [y1] = append (append (reverse y2) [y1]) [] This simplifies, using the definition of reverse to: Subgoal 1.2'.1'''.1': H0. append (append (reverse (List.tl y2)) []) [y1] = append (append (reverse (List.tl y2)) [y1]) [] H1. y2 <> [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (append (append (reverse (List.tl y2)) [List.hd y2]) []) [y1] = append (append (append (reverse (List.tl y2)) [List.hd y2]) [y1]) [] We can eliminate destructors by the following substitution: y2 -> y21 :: y22 This produces the modified subgoal: Subgoal 1.2'.1'''.1'': H0. append (append (reverse y22) []) [y1] = append (append (reverse y22) [y1]) [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (append (append (reverse y22) [y21]) []) [y1] = append (append (append (reverse y22) [y21]) [y1]) [] Candidates for generalization: reverse y22 This produces the modified subgoal: Subgoal 1.2'.1'''.1''': H0. append (append gen_1 []) [y1] = append (append gen_1 [y1]) [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (append (append gen_1 [y21]) []) [y1] = append (append (append gen_1 [y21]) [y1]) [] Must try induction. ⚠ Aborting proof attempt for _verify_target. ⓘ Rules: (:def append) (:def reverse) (:induct append) (:induct reverse) Checkpoints: H0. append (append gen_1 []) [y1] = append (append gen_1 [y1]) [] |--------------------------------------------------------------------------- C0. x <> [] C1. append (append (append gen_1 [y21]) []) [y1] = append (append (append gen_1 [y21]) [y1]) [] |--------------------------------------------------------------------------- C0. append (append (reverse y2) []) [y1] = append (append (reverse y2) [y1]) [] C1. x <> [] |--------------------------------------------------------------------------- C0. x <> [] C1. reverse y = append (reverse y) [] Error: Maximum induction depth reached (3). You can set this with #max_induct. At <none>:1
Success! And in a proof with three inductions! If we inspect the proof, we'll see that those subsequent inductions actually suggest some very useful lemmas.
In fact, this phenomenon of goals proved by subinductions suggesting useful lemmas happens so often, that we commonly like to work with a very low #max_induct
value. Let's attempt the same proof as above, but instruct Imandra to only perform inductions of depth 1 (i.e., no subinductions allowed):
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]
- : 'a list -> 'a list -> bool = <fun> Goal: reverse (append x y) = append (reverse y) (reverse x). 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (append x y) = append (reverse y) (reverse x) This simplifies, using the definitions of append and reverse to: Subgoal 1.2': |--------------------------------------------------------------------------- C0. x <> [] C1. reverse y = append (reverse y) [] Must try induction. ⚠ Aborting proof attempt for _verify_target. ⓘ Rules: (:def append) (:def reverse) (:induct append) Checkpoints: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse y = append (reverse y) [] Error: Maximum induction depth reached (1). You can set this with #max_induct. At <none>:1
We see above that Imandra is unable to prove our theorem with #max_induct
set to 1
. However, by inspecting the subgoal Imandra was working on before it hit its #max_induct
limit (nicely presented to us as a Checkpoint
at the end of the proof attempt), we can find a useful lemma we should prove!
In particular, note this checkpoint:
gen_1 = append gen_1 []
This, oriented as append gen_1 [] = gen_1
is a great rule. Note that it really does require induction, as append
recurses on its first argument, not its second.
Let's ask Imandra to prove this as a theorem
and to install it as a rewrite rule:
theorem append_nil x = append x [] = x [@@induct] [@@rewrite]
val append_nil : 'a list -> bool = <fun> Goal: append x [] = x. 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- append x [] = x Must try induction. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. append x [] = x But simplification reduces this to true, using the definition of append. Subgoal 1.1: H0. x <> [] H1. append (List.tl x) [] = List.tl x |--------------------------------------------------------------------------- append x [] = x But simplification reduces this to true, using the definition of append. ⓘ Rules: (:def append) (:induct append)
ground_instances: | 0 |
definitions: | 2 |
inductions: | 1 |
search_time: | 0.101s |
start[0.101s, "Goal"] append ( :var_0: ) [] = ( :var_0: )
subproof
append x [] = xstart[0.101s, "1"] append x [] = x
induction on (functional ?) :scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : bool) = append x [] = x in let (_x_2 : sko_ty_0 list) = List.tl x in (_x_0 || _x_1) && (not (_x_0 && (append _x_2 [] = _x_2)) || _x_1) :cases [x <> [] || (append x [] = x); let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (append _x_0 [] = _x_0) || (append x [] = x)])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (append _x_0 [] = _x_0) || (append x [] = x)start[0.054s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (append _x_0 [] = _x_0) || (append x [] = x)
simplify
into: true
expansions: append
rewrite_steps: forward_chaining:
subproof
x <> [] || (append x [] = x)start[0.054s, "1.2"] x <> [] || (append x [] = x)
simplify
into: true
expansions: append
rewrite_steps: forward_chaining:
Success! Now, let's return to our main goal and see if we progress any further (still with #max_induct 1
) now that we have append_nil
available as a rewrite rule:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]
- : 'a list -> 'a list -> bool = <fun> Goal: reverse (append x y) = append (reverse y) (reverse x). 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (append x y) = append (reverse y) (reverse x) But simplification reduces this to true, using the definitions of append and reverse, and the rewrite rule append_nil. Subgoal 1.1: H0. x <> [] H1. reverse (append (List.tl x) y) = append (reverse y) (reverse (List.tl x)) |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) This simplifies, using the definitions of append and reverse to: Subgoal 1.1': H0. x <> [] H1. reverse (append (List.tl x) y) = append (reverse y) (reverse (List.tl x)) |--------------------------------------------------------------------------- append (reverse (append (List.tl x) y)) [List.hd x] = append (reverse y) (append (reverse (List.tl x)) [List.hd x]) We can eliminate destructors by the following substitution: x -> x1 :: x2 This produces the modified subgoal: Subgoal 1.1'': H0. reverse (append x2 y) = append (reverse y) (reverse x2) |--------------------------------------------------------------------------- append (reverse (append x2 y)) [x1] = append (reverse y) (append (reverse x2) [x1]) Cross-fertilizing with: reverse (append x2 y) = append (reverse y) (reverse x2) This produces the modified subgoal: Subgoal 1.1''': |--------------------------------------------------------------------------- append (append (reverse y) (reverse x2)) [x1] = append (reverse y) (append (reverse x2) [x1]) Candidates for generalization: reverse x2 reverse y This produces the modified subgoal: Subgoal 1.1'''': |--------------------------------------------------------------------------- append (append gen_2 gen_1) [x1] = append gen_2 (append gen_1 [x1]) Must try induction. ⚠ Aborting proof attempt for _verify_target. ⓘ Rules: (:def append) (:def reverse) (:rw append_nil) (:induct append) Checkpoints: |--------------------------------------------------------------------------- append (append gen_2 gen_1) [x1] = append gen_2 (append gen_1 [x1]) Error: Maximum induction depth reached (1). You can set this with #max_induct. At <none>:1
Yes! If we inspect Imandra's aborted proof, we see that our rewrite rule append_nil
was applied exactly where we wanted it, and we now see another interesting subgoal which looks quite familiar:
append (append gen_1 gen_2) (x1 :: []) = append gen_1 (append gen_2 (x1 :: []))
That is, Imandra has derived, as a subgoal to be proved, an instance of the associativity of append
!
If we set our #max_induct
to 2
, then Imandra would finish this proof automatically.
But, this fact about append seems very useful and of general purpose, so let's prove it as a rewrite rule and make it available to our subsequent proof efforts.
theorem assoc_append x y z = append x (append y z) = append (append x y) z [@@induct] [@@rewrite]
val assoc_append : 'a list -> 'a list -> 'a list -> bool = <fun> Goal: append x (append y z) = append (append x y) z. 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- append x (append y z) = append (append x y) z Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. append x (append y z) = append (append x y) z But simplification reduces this to true, using the definition of append. Subgoal 1.1: H0. x <> [] H1. append (List.tl x) (append y z) = append (append (List.tl x) y) z |--------------------------------------------------------------------------- append x (append y z) = append (append x y) z But simplification reduces this to true, using the definition of append. ⓘ Rules: (:def append) (:induct append)
ground_instances: | 0 |
definitions: | 5 |
inductions: | 1 |
search_time: | 0.074s |
start[0.074s, "Goal"] append ( :var_0: ) (append ( :var_1: ) ( :var_2: )) = append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
subproof
append x (append y z) = append (append x y) zstart[0.074s, "1"] append x (append y z) = append (append x y) z
induction on (functional ?) :scheme (not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : sko_ty_0 list) = append y z in let (_x_2 : bool) = append x _x_1 = append (append x y) z in let (_x_3 : sko_ty_0 list) = List.tl x in (_x_0 || _x_2) && (not (_x_0 && (append _x_3 _x_1 = append (append _x_3 y) z)) || _x_2) :cases [x <> [] || (append x (append y z) = append (append x y) z); let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)start[0.040s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)
simplify
into: true
expansions: [append, append, append]
rewrite_steps: forward_chaining:
subproof
x <> [] || (append x (append y z) = append (append x y) z)start[0.040s, "1.2"] x <> [] || (append x (append y z) = append (append x y) z)
simplify
into: true
expansions: [append, append]
rewrite_steps: forward_chaining:
Success! Now, let's go back to our original goal and see if we can prove it with the help of our proved rules (still with max_induct 1
):
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]
- : 'a list -> 'a list -> bool = <fun> Goal: reverse (append x y) = append (reverse y) (reverse x). 1 nontautological subgoal. Subgoal 1: |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) Must try induction. The recursive terms in the conjecture suggest 3 inductions. Subsumption and merging reduces this to 2. Only 1 of those schemes are unflawed. We shall induct according to a scheme derived from append. Induction scheme: (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y). 2 nontautological subgoals. Subgoal 1.2: |--------------------------------------------------------------------------- C0. x <> [] C1. reverse (append x y) = append (reverse y) (reverse x) But simplification reduces this to true, using the definitions of append and reverse, and the rewrite rule append_nil. Subgoal 1.1: H0. x <> [] H1. reverse (append (List.tl x) y) = append (reverse y) (reverse (List.tl x)) |--------------------------------------------------------------------------- reverse (append x y) = append (reverse y) (reverse x) This simplifies, using the definitions of append and reverse to: Subgoal 1.1': H0. x <> [] H1. reverse (append (List.tl x) y) = append (reverse y) (reverse (List.tl x)) |--------------------------------------------------------------------------- append (reverse (append (List.tl x) y)) [List.hd x] = append (reverse y) (append (reverse (List.tl x)) [List.hd x]) But simplification reduces this to true, using the rewrite rule assoc_append. ⓘ Rules: (:def append) (:def reverse) (:rw append_nil) (:rw assoc_append) (:induct append)
ground_instances: | 0 |
definitions: | 6 |
inductions: | 1 |
search_time: | 0.166s |
start[0.166s, "Goal"] reverse (append ( :var_0: ) ( :var_1: )) = append (reverse ( :var_1: )) (reverse ( :var_0: ))
subproof
reverse (append x y) = append (reverse y) (reverse x)start[0.166s, "1"] reverse (append x y) = append (reverse y) (reverse x)
induction on (functional ?) :scheme (not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y)
Split (let (_x_0 : bool) = x <> [] in let (_x_1 : sko_ty_0 list) = reverse y in let (_x_2 : bool) = reverse (append x y) = append _x_1 (reverse x) in let (_x_3 : sko_ty_0 list) = List.tl x in (_x_0 || _x_2) && (not (_x_0 && (reverse (append _x_3 y) = append _x_1 (reverse _x_3))) || _x_2) :cases [x <> [] || (reverse (append x y) = append (reverse y) (reverse x)); let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse y in not (x <> []) || not (reverse (append _x_0 y) = append _x_1 (reverse _x_0)) || (reverse (append x y) = append _x_1 (reverse x))])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse y in not (x <> []) || not (reverse (append _x_0 y) = append _x_1 (reverse _x_0)) || (reverse (append x y) = append _x_1 (reverse x))start[0.118s, "1.1"] let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse y in not (x <> []) || not (reverse (append _x_0 y) = append _x_1 (reverse _x_0)) || (reverse (append x y) = append _x_1 (reverse x))
simplify
into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 y) in let (_x_2 : sko_ty_0 list) = reverse y in let (_x_3 : sko_ty_0 list) = reverse _x_0 in let (_x_4 : sko_ty_0 list) = [List.hd x] in not (x <> []) || not (_x_1 = append _x_2 _x_3) || (append _x_1 _x_4 = append _x_2 (append _x_3 _x_4))
expansions: [reverse, reverse, append]
rewrite_steps: forward_chaining: simplify
into: true
expansions: []
rewrite_steps: assoc_append
forward_chaining:
subproof
x <> [] || (reverse (append x y) = append (reverse y) (reverse x))start[0.118s, "1.2"] x <> [] || (reverse (append x y) = append (reverse y) (reverse x))
simplify
into: true
expansions: [append, reverse, append]
rewrite_steps: append_nil
forward_chaining:
Success! And our rewrite rules were used exactly where we'd hoped.
Happy proving!