Trace inclusion between program and state machine - coq

I'm trying to show that the semantics of a program and state machine are equivalent.
For some background, say we have the following language with sequencing and print statements.
Inductive prog : Type :=
| Print : nat -> prog -> prog
| Skip : prog.
A state machine is defined like this:
Definition pre := state -> bool.
Definition post := state -> state.
Inductive transition :=
| Transition : pre -> nat -> post -> transition.
Notation state_machine := (list transition).
Compilation from program to state machine adds an extra pc variable for control state.
The program Output 1 (Output 2 Skip) thus results in these two transitions:
transition 1
pre: pc = 0
post: write 1, set pc to 1
transition 2
pre: pc = 1
post: write 2, set pc to 2
When pc = 2, the state machine terminates as no more preconditions are enabled.
The problem I'm having is that the state machine does not "get smaller" in the course of execution; preconditions merely get disabled and never re-enabled once corresponding program steps execute.
This causes trouble when I try to prove the following trace inclusion lemma (pretty much following the development from chapter 10 of FRAP: compile is a function which translates program to state machine,
pstep and lstep are small-step reduction relations,
traces are lists of nats).
Inductive ptrace : prog -> trace -> Prop :=
| PEnd : forall p, ptrace p []
| PTerminate : ptrace Skip []
| PStep : forall p1 p2 t ts,
pstep p1 t p2 ->
ptrace p2 ts ->
ptrace p1 (t :: ts).
Inductive strace : state_machine -> trace -> Prop :=
| SEnd : forall m, strace m []
| SStep : forall state1 state2 m ms t ts,
In m ms ->
sstep state1 m state2 t ->
strace ms ts -> (* <- ms does not get smaller, we just choose some transition m to execute *)
strace ms (t :: ts).
Theorem trace_incl : forall p s t, ptrace p t -> compile p = s -> strace s t.
I end up with hypotheses compile p1 = s and compile p2 = s where p1 reduces to p2.
How should I avoid this problem? Is there a better way to model state machines?
The reason I'm using this approach is that I want to eventually add recursion to programs, allowing the same transitions to be repeatedly taken.
Any recommended (introductory) reading on verifying compilers and simulation relations would also be welcome. I've read FRAP and the paper mentioned here, but not much else.

For situations like this, in the formal development you would probably introduce an explicit variable that tracks the length of the trace, and then you can proceed by (strong) induction on that. So the statement of the lemma would look more like:
Lemma trace_incl_expl : forall n p s t, n = length t -> ptrace p t -> compile p = s -> strace s t.
This is what's alluded to in the proof idea of Theorem 10.11 of http://adam.chlipala.net/frap/frap_book.pdf, for example. The final statement version of the theorem doesn't necessarily include the explicit length value, but within the Coq development it would either be there, or the theorem would simply delegate to a lemma that includes it. The code for the CompilerCorrectness chapter, for example, at one point it introduces a predicate that explicitly keeps track of "how many steps it took to generate a trace."

Related

Stuck with a proof

I'm fairly new to Coq, and was doing some Katas on CodeWars for fun and learning.
I'm stuck with one of them and want to hear some ideas from you.
So, I have:
Record iso (A B : Set) : Set :=
bijection {
A_to_B : A -> B;
B_to_A : B -> A;
A_B_A : forall a : A, B_to_A (A_to_B a) = a;
B_A_B : forall b : B, A_to_B (B_to_A b) = b
}.
(* nat_plus_nat : a set having size(nat) more elements than nat. (provided in preloaded) *)
Inductive nat_plus_nat : Set := left (n : nat) | right (n : nat).
Theorem nat_iso_natpnat : iso nat nat_plus_nat.
I have and idea, but I can't implement it, and I don't know if it's feasible. Basically, I want to map every odd nat to one constructor(left, for example) and every even nat to another(right, for example). Will this work? If no, how can it be done?
Right now I'm stuck with the fact, that A_to_B defined as fun n => if odd n then left n else right n and B_to_A defined as fun n => match n with | left n' => n' | right n' => n' end won't give me enough facts to eliminate some cases.
You need to do the math correctly first: find two functions that are inverse of each other.
You initial intent is correct: odd numbers to one side, even numbers to the other side, but what you store on each side should cover all the natural numbers, so you will probably have to divide by 2 somewhere.
For Coq usage, You should load the Arith package, by starting with the following line:
Require Import Arith.
This way, you can benefit from existing functions, like Nat.div2 and Nat.even and all the existing theorems about them. To find the relevant theorems, I suggest commands like:
Search Nat.even 2.
Search Nat.div2.
Last hint: proving properties of Nat.div2 by induction is rather difficult for beginners. Try to use the existing theorems as much as possible. If you choose to perform a proof by induction concerning div2, go look in the sources in file theories/Arith/Div2.v : the author of that file designed a specific induction theorem called ìnd_0_1_SS just for that purpose.

Looking for some help understanding where I'm going from (Software Foundations, Binom.v)

I am working through Software Foundations and am a bit stuck. Here is a link for reference: https://softwarefoundations.cis.upenn.edu/vfa-current/Binom.html
I am stuck on the proof "abs_perm," reproduced here.
Theorem abs_perm: forall p al bl,
priq p -> Abs p al -> Abs p bl -> Permutation al bl.
This is a "2 star" question, so it should be pretty easy. The fact that it is proving to be difficult makes me think that the issue is my "Abs" relation. My inductive relation is as follows:
Inductive priqueue_elems: list tree -> list key -> Prop :=
| priqueue_elems_base: priqueue_elems [] nil
| priqueue_elems_next_list: forall l b b' v,
priqueue_elems l b ->
Permutation b' (v::b) ->
priqueue_elems (insert v l) b'
The issue I end up having is that I get something like "insert x1 l1 = insert x2 l2" (via inversions in my proof) and I can't go anywhere from there.
insert in this case is not injective...it seems like insert x [] = insert x [Leaf].
The above relation allows me to directly know that I have "priq l" available, but priq is defined such that if there is more than one element, the first is a Leaf so...yeah.
I think the issue is with my relation (which is modeled after their tree_elems), but I'm having a bit of "proof block." My guess is that I shouldn't have insert in the relation construction, but it's unclear what other structure there is to the priqueue.
Another avenue would just to have it straight up on "list tree," but then it seems like well-formedness could be an issue.
Otherwise I need some sort of theorem on insert. I tried a bunch, but wasn't able to get a proof together.
For example,
Lemma equals_inserts_permute: forall l1 l2 p1 p2 v1 v2,
priqueue_elems l1 p1 ->
priqueue_elems l2 p2 ->
insert v1 l1 = insert v2 l2 -> Permutation (v1::p1) (v2::p2)
Which would give me the ability to relate insert that I need, but...I haven't been able to crack that proof either.
Would appreciate any help!

Why is my definition not allowed because of strict positivity?

I have the following two definitions that result in two different error messages.
The first definition is declined because of strict positivity and the second one because of a universe inconsistency.
(* non-strictly positive *)
Inductive SwitchNSP (A : Type) : Type :=
| switchNSP : SwitchNSP bool -> SwitchNSP A.
Fail Inductive UseSwitchNSP :=
| useSwitchNSP : SwitchNSP UseSwitchNSP -> UseSwitchNSP.
(* universe inconsistency *)
Inductive SwitchNSPI : Type -> Type :=
| switchNSPI : forall A, SwitchNSPI bool -> SwitchNSPI A.
Fail Inductive UseSwitchNSPI :=
| useSwitchNSPI : SwitchNSPI UseSwitchNSPI -> UseSwitchNSPI.
Chatting on gitter revealed that universe (in)consistencies are checked first, that is, the first definition adheres this check, but then fails because of a strict positivity issue.
As far as I understand the strict positivity restriction, if Coq allows non-strictly positivity data type definitions, I could construct non-terminating functions without using fix (which is pretty bad).
In order to make it even more confusing, the first definition is accepted in Agda and the second one gives a strict positivity error.
data Bool : Set where
True : Bool
False : Bool
data SwitchNSP (A : Set) : Set where
switchNSP : SwitchNSP Bool -> SwitchNSP A
data UseSwitchNSP : Set where
useSwitchNSP : SwitchNSP UseSwitchNSP -> UseSwitchNSP
data SwitchNSPI : Set -> Set where
switchNSPI : forall A -> SwitchNSPI Bool -> SwitchNSPI A
data UseSwitchNSPI : Set where
useSwitchNSP : SwitchNSPI UseSwitchNSPI -> UseSwitchNSPI
Now my question is two-folded: first, what is the "evil example" I could construct with the above definition? Second, which of the rules applies to the above definition?
Some notes:
To clarify, I think that I do understand why the second definition is not allowed type-checking-wise, but still feel that there is nothing "evil" happening here, when the definition is allowed.
I first thought that my example is an instance of this question, but enabling universe polymorphism does not help for the second definition.
Can I use some "trick" do adapt my definition such that it is accepted by Coq?
Unfortunately, there's nothing super deep about this example. As you noted Agda accepts it, and what trips Coq is the lack of uniformity in the parameters. For example, it accepts this:
Inductive SwitchNSPA (A : Type) : Type :=
| switchNSPA : SwitchNSPA A -> SwitchNSPA A.
Inductive UseSwitchNSPA :=
| useSwitchNSPA : SwitchNSPA UseSwitchNSPA -> UseSwitchNSPA.
Positivity criteria like the one used by Coq are not complete, so they will reject harmless types; the problem with supporting more types is that it often makes the positivity checker more complex, and that's already one of the most complex pieces of the kernel.
As for the concrete details of why it rejects it, well, I'm not 100% sure. Going by the rules in the manual, I think it should be accepted?
EDIT: The manual is being updated.
Specifically, using the following shorter names to simplify the following:
Inductive Inner (A : Type) : Type := inner : Inner bool -> Inner A.
Inductive Outer := outer : Inner Outer -> Outer.
Correctness rules
Positivity condition
Here,
X = Outer
T = forall x: Inner X, X
So we're in the second case with
U = Inner X
V = X
V is easy, so let's do that first:
V = (X) falls in the first case, with no t_i, hence is positive for X
For U: is U = Inner X strictly positive wrt X?
Here,
T = Inner X
Hence we're in the last case: T converts to (I a1) (no t_i) with
I = Inner
a1 = X
and X does not occur in the t_i, since there are no t_i.
Do the instantiated types of the constructors satisfy the nested positivity condition?
There is only one constructor:
inner : Inner bool -> Inner X.
Does this satisfy the nested positivity condition?
Here,
T = forall x: Inner bool, Inner X.
So we're in the second case with
U = Inner bool
V = Inner X
X does not occur in U, so X is strictly positive in U.
Does V satisfy the nested positivity condition for X?
Here,
T = Inner X
Hence we're in the first case: T converts to (I b1) (no u_i) with
I = Inner
b1 = X
There are no u_i, so V satisfies the nested positivity condition.
I have opened a bug report. The manual is being fixed.
Two more small things:
I can't resist pointing that your type is empty:
Theorem Inner_empty: forall A, Inner A -> False.
Proof. induction 1; assumption. Qed.
You wrote:
if Coq allows non-strictly positivity data type definitions, I could construct non-terminating functions without using fix (which is pretty bad).
That's almost correct, but not exactly: if Coq didn't enforce strict positivity, you could construct non-terminating functions period, which is bad. It doesn't matter whether they use fix or not: having non-termination in the logic basically makes it unsound (and hence Coq prevents you from writing fixpoints that do not terminate by lazy reduction).

Idiomatically expressing "The Following Are Equivalent" in Coq

Exercise 6.7 in Coq'Art, or the final exercise of the Logic chapter in Software Foundations: show that the following are equivalent.
Definition peirce := forall P Q:Prop, ((P->Q)->P)->P.
Definition classic := forall P:Prop, ~~P -> P.
Definition excluded_middle := forall P:Prop, P\/~P.
Definition de_morgan_not_and_not := forall P Q:Prop, ~(~P/\~Q)->P\/Q.
Definition implies_to_or := forall P Q:Prop, (P->Q)->(~P\/Q).
The solution set expresses this by a circular chain of implications, using five separate lemmas. But "TFAE" proofs are common enough in mathematics that I'd like to have an idiom to express them. Is there one in Coq?
This type of pattern is very easy to express in Coq, although setting up the infrastructure to do so might take some effort.
First, we define a proposition that expresses that all propositions in a list are equivalent:
Require Import Coq.Lists.List. Import ListNotations.
Definition all_equivalent (Ps : list Prop) : Prop :=
forall n m : nat, nth n Ps False -> nth m Ps True.
Next, we want to capture the standard pattern for proving this kind of result: if each proposition in the list implies the next one, and the last implies the first, we know they are all equivalent. (We could also have a more general pattern, where we replace a straight list of implications with a graph of implications between the propositions, whose transitive closure generates a complete graph. We'll avoid that in the interest of simplicity.) The premise of this pattern is easy to express; it is just a code transcription of the English explanation above.
Fixpoint all_equivalent'_aux
(first current : Prop) (rest : list Prop) : Prop :=
match rest with
| [] => current -> first
| P :: rest' => (current -> P) /\ all_equivalent'_aux first P rest'
end.
Definition all_equivalent' (Ps : list Prop) : Prop :=
match Ps with
| first :: second :: rest =>
(first -> second) /\ all_equivalent' first second rest
| _ => True
end.
The difficult part is showing that this premise implies the conclusion we want:
Lemma all_equivalentP Ps : all_equivalent' Ps -> all_equivalent Ps.
Showing that this lemma holds probably requires some ingenuity to find a strong enough inductive generalization. I can't quite prove it right now, but might add a solution later to the answer if you want.

Can I extract a Coq proof as a Haskell function?

Ever since I learned a little bit of Coq I wanted to learn to write a Coq proof of the so-called division algorithm that is actually a logical proposition: forall n m : nat, exists q : nat, exists r : nat, n = q * m + r
I recently accomplished that task using what I learned from Software Foundations.
Coq being a system for developing constructive proofs, my proof is in effect a method to construct suitable values q and r from values m and n.
Coq has an intriguing facility for "extracting" an algorithm in Coq's algorithm language (Gallina) to general-purpose functional programming languages including Haskell.
Separately I have managed to write the divmod operation as a Gallina Fixpoint and extract that. I want to note carefully that that task is not what I'm considering here.
Adam Chlipala has written in Certified Programming with Dependent Types that "Many fans of the Curry-Howard correspondence support the idea of extracting programs from proofs. In reality, few users of Coq and related tools do any such thing."
Is it even possible to extract the algorithm implicit in my proof to Haskell? If it is possible, how would it be done?
Thanks to Prof. Pierce's summer 2012 video 4.1 as Dan Feltey suggested, we see that the key is that the theorem to be extracted must provide a member of Type rather than the usual kind of propositions, which is Prop.
For the particular theorem the affected construct is the inductive Prop ex and its notation exists. Similarly to what Prof. Pierce has done, we can state our own alternate definitions ex_t and exists_t that replace occurrences of Prop with occurrences of Type.
Here is the usual redefinition of ex and exists similarly as they are defined in Coq's standard library.
Inductive ex (X:Type) (P : X->Prop) : Prop :=
ex_intro : forall (witness:X), P witness -> ex X P.
Notation "'exists' x : X , p" := (ex _ (fun x:X => p))
(at level 200, x ident, right associativity) : type_scope.
Here are the alternate definitions.
Inductive ex_t (X:Type) (P : X->Type) : Type :=
ex_t_intro : forall (witness:X), P witness -> ex_t X P.
Notation "'exists_t' x : X , p" := (ex_t _ (fun x:X => p))
(at level 200, x ident, right associativity) : type_scope.
Now, somewhat unfortunately, it is necessary to repeat both the statement and the proof of the theorem using these new definitions.
What in the world??
Why is it necessary to make a reiterated statement of the theorem and a reiterated proof of the theorem, that differ only by using an alternative definition of the quantifier??
I had hoped to use the existing theorem in Prop to prove the theorem over again in Type. That strategy fails when Coq rejects the proof tactic inversion for a Prop in the environment when that Prop uses exists and the goal is a Type that uses exists_t. Coq reports "Error: Inversion would require case analysis on sort Set which is not allowed
for inductive definition ex." This behavior occurred in Coq 8.3. I am not certain that it
still occurs in Coq 8.4.
I think the need to repeat the proof is actually profound although I doubt that I personally am quite managing to perceive its profundity. It involves the facts that Prop is "impredicative" and Type is not impredicative, but rather, tacitly "stratified". Predicativity is (if I understand correctly) vulnerability to Russell's paradox that the set S of sets that are not members of themselves can neither be a member of S, nor a non-member of S. Type avoids Russell's paradox by tacitly creating a sequence of higher types that contain lower types. Because Coq is drenched in the formulae-as-types interpretation of the Curry-Howard correspondence, and if I am getting this right, we can even understand stratification of types in Coq as a way to avoid Gödel incompleteness, the phenomenon that certain formulae express constraints on formulae such as themselves and thereby become unknowable as to their truth or falsehood.
Back on planet Earth, here is the repeated statement of the theorem using "exists_t".
Theorem divalg_t : forall n m : nat, exists_t q : nat,
exists_t r : nat, n = plus (mult q m) r.
As I have omitted the proof of divalg, I will also omit the proof of divalg_t. I will only mention that we do have the good fortune that proof tactics including "exists" and "inversion" work just the same with our new definitions "ex_t" and "exists_t".
Finally, the extraction itself is accomplished easily.
Extraction Language Haskell.
Extraction "divalg.hs" divalg_t.
The resulting Haskell file contains a number of definitions, the heart of which is the reasonably nice code, below. And I was only slightly hampered by my near-total ignorance of the Haskell programming language. Note that Ex_t_intro creates a result whose type is Ex_t; O and S are the zero and the successor function from Peano arithmetic; beq_nat tests Peano numbers for equality; nat_rec is a higher-order function that recurs over the function among its arguments. The definition of nat_rec is not shown here. At any rate it is generated by Coq according to the inductive type "nat" that was defined in Coq.
divalg :: Nat -> Nat -> Ex_t Nat (Ex_t Nat ())
divalg n m =
case m of {
O -> Ex_t_intro O (Ex_t_intro n __);
S m' ->
nat_rec (Ex_t_intro O (Ex_t_intro O __)) (\n' iHn' ->
case iHn' of {
Ex_t_intro q' hq' ->
case hq' of {
Ex_t_intro r' _ ->
let {k = beq_nat r' m'} in
case k of {
True -> Ex_t_intro (S q') (Ex_t_intro O __);
False -> Ex_t_intro q' (Ex_t_intro (S r') __)}}}) n}
Update 2013-04-24: I know a bit more Haskell now. To assist others in reading the extracted code above, I'm presenting the following hand-rewritten code that I claim is equivalent and more readable. I'm also presenting the extracted definitions Nat, O, S, and nat_rec that I did not eliminate.
-- Extracted: Natural numbers (non-negative integers)
-- in the manner in which Peano defined them.
data Nat =
O
| S Nat
deriving (Eq, Show)
-- Extracted: General recursion over natural numbers,
-- an interpretation of Nat in the manner of higher-order abstract syntax.
nat_rec :: a1 -> (Nat -> a1 -> a1) -> Nat -> a1
nat_rec f f0 n =
case n of {
O -> f;
S n0 -> f0 n0 (nat_rec f f0 n0)}
-- Given non-negative integers n and m, produce (q, r) with n = q * m + r.
divalg_t :: Nat -> Nat -> (Nat, Nat)
divalg_t n O = (O, n) -- n/0: Define quotient 0, remainder n.
divalg_t n (S m') = divpos n m' -- n/(S m')
where
-- Given non-negative integers n and m',
-- and defining m = m' + 1,
-- produce (q, r) with n = q * m + r
-- so that q = floor (n / m) and r = n % m.
divpos :: Nat -> Nat -> (Nat, Nat)
divpos n m' = nat_rec (O, O) (incrDivMod m') n
-- Given a non-negative integer m' and
-- a pair of non-negative integers (q', r') with r <= m',
-- and defining m = m' + 1,
-- produce (q, r) with q*m + r = q'*m + r' + 1 and r <= m'.
incrDivMod :: Nat -> Nat -> (Nat, Nat) -> (Nat, Nat)
incrDivMod m' _ (q', r')
| r' == m' = (S q', O)
| otherwise = (q', S r')
The current copy of Software Foundations dated July 25, 2012, answers this quite concisely in the late chapter "Extraction2". The answer is that it can certainly be done, much like this:
Extraction Language Haskell
Extraction "divalg.hs" divalg
One more trick is necessary. Instead of a Prop, divalg must be a Type. Otherwise it will be erased in the process of extraction.
Uh oh, #Anthill is correct, I haven't answered the question because I don't know how to explain how Prof. Pierce accomplished that in his NormInType.v variant of his Norm.v and MoreStlc.v.
OK, here's the rest of my partial answer anyway.
Where "divalg" appears above, it will be necessary to provide a space-separated list of all of the propositions (which must each be redefined as a Type rather than a Prop) on which divalg relies. For a thorough, interesting, and working example of a proof extraction, one may consult the chapter Extraction2 mentioned above. That example extracts to OCaml, but adapting it for Haskell is simply a matter of using Extraction Language Haskell as above.
In part, the reason that I spent some time not knowing the above answer is that I have been using the copy of Software Foundations dated October 14, 2010, that I downloaded in 2011.