Lecture notes 20210324 Small Step Semantics 1

Remark. Some material in this lecture is from << Software Foundation >> volume 1 and volume 2.
Require Import Coq.micromega.Psatz.
Require Import PL.Imp.
Require Import PL.RTClosure.

General Idea: Small Step

Till now, we have learnt how to define a programming language's denotational semantics. Most commonly, the denotation of a program is defined as a binary relation between program states. Such denotational semantics are also called big-step operational semantics — the denotation of a program tells the result of program execution (given the starting state), "all in one step".
Small step semantics, on the other hand, talks more about intermediate states. It defines how a program will execute, step by step. If we use the following expression's evaluation process as example,
    2 + 2 + 3 * 4,
denotational semantics says:
    2 + 2 + 3 * 4 ==> 16
while small step operational semantics says:
      2 + 2 + 3 * 4
      --> 4 + 3 * 4
      --> 4 + 12
      --> 16.
In short, we will learn how to define a "small-step" relation that specifies, for a given program, how the "atomic steps" of computation are performed.

Small Step Semantics for Expression Evaluation

It can be useful to think of this step relation of expression evaluation as an abstract machine.
  • At any moment, the state of the machine is an integer expression.
  • A step of this machine is an atomic unit of computation — here, a single arithmetic operation or loading program variable's value.
  • The halting states of the machine are ones where there is no more computation to be done.
Given an expression a, we can compute its value as follows:
  • Take a as the starting state of the machine.
  • Repeatedly use the step relation to find a sequence of machine states, starting with a, where each state steps to the next.
  • When no more forward step is possible, "read out" the final state of the machine as the result of computation.
Intuitively, it is clear that the final states of the machine are always constant expressions Anum n for some n.
Inductive aexp_halt: aexp -> Prop :=
  | AH_num : n, aexp_halt (ANum n).
Of course, we could define it using a Coq function instead of an inductive predicate. Here is this alternative (but equivalent approach) approach.
Module Playground_Halt.

Definition aexp_halt (a: aexp): Prop :=
  match a with
  | ANum _True
  | _False
  end.

End Playground_Halt.
Then we define our step relation.
Inductive astep : state -> aexp -> aexp -> Prop :=
  | AS_Id : st X,
      astep st
        (AId X) (ANum (st X))

  | AS_Plus1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      astep st
        (APlus a1 a2) (APlus a1' a2)
  | AS_Plus2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      astep st
        (APlus a1 a2) (APlus a1 a2')
  | AS_Plus : st n1 n2,
      astep st
        (APlus (ANum n1) (ANum n2)) (ANum (n1 + n2))

  | AS_Minus1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      astep st
        (AMinus a1 a2) (AMinus a1' a2)
  | AS_Minus2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      astep st
        (AMinus a1 a2) (AMinus a1 a2')
  | AS_Minus : st n1 n2,
      astep st
        (AMinus (ANum n1) (ANum n2)) (ANum (n1 - n2))

  | AS_Mult1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      astep st
        (AMult a1 a2) (AMult a1' a2)
  | AS_Mult2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      astep st
        (AMult a1 a2) (AMult a1 a2')
  | AS_Mult : st n1 n2,
      astep st
        (AMult (ANum n1) (ANum n2)) (ANum (n1 * n2)).
This definition seems to be super long. Let's read it part by part. But please keep in mind that what we define here is only a single step evaluation relation.
The first part of this definition talks about the value of a program variable:
      astep st
        (AId X) (ANum (st X)).
In short, it says: a program variable X's variable is X's variable and this evaluation process has only one step.
The second part of this definition talks about how the sum of two subexpressions are computed.
  | AS_Plus1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      astep st
        (APlus a1 a2) (APlus a1' a2)
  | AS_Plus2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      astep st
        (APlus a1 a2) (APlus a1 a2')
  | AS_Plus : st n1 n2,
      astep st
        (APlus (ANum n1) (ANum n2)) (ANum (n1 + n2))
It says, the left side is computed first, then the right side. When both sides are computed, the sum of them can be computed in another step.
In the theory of programming languages, such inductive definitions are also inference rules. For example,



          -------------
           X  --> st X


           a1 --> a1'
    -----------------------
      a1 + a2 --> a1' + a2


           a2 --> a2'
    -----------------------
       n + a2 --> n + a2'


    -----------------------
      n1 + a2 --> n1 + n2
 ("+" of aexp)   ("+" of integers)
Usually, every inference rule has a horizontal line, what sits above it is assumptions and the conclusion is below the line.
Combining all rules together, we are already able to describe the evaluation process of some nontrivial examples. For example, when X's value is 1 and Y's value is 2, X + (3 + Y) will be evaluated by the following steps:
    X + (3 + Y)
    --> 1 + (3 + Y)
    --> 1 + (3 + 2)
    --> 1 + 5
    --> 6.
We can prove our examples in Coq.
Module Step_Example1.

Import Abstract_Pretty_Printing.

Example step_1: (X Y: var) (st: state),
  st X = 1 ->
  astep st (X + (3 + Y)) (1 + (3 +Y)).
Proof.
  intros.
  apply AS_Plus1.
  rewrite <- H.
  apply AS_Id.
Qed.

Example step_2: (Y: var) (st: state),
  st Y = 2 ->
  astep st (1 + (3 +Y)) (1 + (3 + 2)).
Proof.
  intros.
  apply AS_Plus2.
  { apply AH_num. }
  apply AS_Plus2.
  { apply AH_num. }
  rewrite <- H.
  apply AS_Id.
Qed.

Example step_3: (st: state),
  astep st (1 + (3 + 2)) (1 + 5).
Proof.
  intros.
  apply AS_Plus2.
  { apply AH_num. }
  apply AS_Plus.
Qed.

Example step_4: (st: state),
  astep st (1 + 5) 6.
Proof.
  intros.
  apply AS_Plus.
Qed.

End Step_Example1.
We define bool expressions' evaluation similarly. If you forget details about bexp's inductive definition, just use Print bexp as a cheat sheet.
(* Print bexp. *)

Inductive bexp_halt: bexp -> Prop :=
  | BH_True : bexp_halt BTrue
  | BH_False : bexp_halt BFalse.

Inductive bstep : state -> bexp -> bexp -> Prop :=

  | BS_Eq1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      bstep st
        (BEq a1 a2) (BEq a1' a2)
  | BS_Eq2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      bstep st
        (BEq a1 a2) (BEq a1 a2')
  | BS_Eq_True : st n1 n2,
      n1 = n2 ->
      bstep st
        (BEq (ANum n1) (ANum n2)) BTrue
  | BS_Eq_False : st n1 n2,
      n1n2 ->
      bstep st
        (BEq (ANum n1) (ANum n2)) BFalse


  | BS_Le1 : st a1 a1' a2,
      astep st
        a1 a1' ->
      bstep st
        (BLe a1 a2) (BLe a1' a2)
  | BS_Le2 : st a1 a2 a2',
      aexp_halt a1 ->
      astep st
        a2 a2' ->
      bstep st
        (BLe a1 a2) (BLe a1 a2')
  | BS_Le_True : st n1 n2,
      n1n2 ->
      bstep st
        (BLe (ANum n1) (ANum n2)) BTrue
  | BS_Le_False : st n1 n2,
      n1 > n2 ->
      bstep st
        (BLe (ANum n1) (ANum n2)) BFalse

  | BS_NotStep : st b1 b1',
      bstep st
        b1 b1' ->
      bstep st
        (BNot b1) (BNot b1')
  | BS_NotTrue : st,
      bstep st
        (BNot BTrue) BFalse
  | BS_NotFalse : st,
      bstep st
        (BNot BFalse) BTrue

  | BS_AndStep : st b1 b1' b2,
      bstep st
        b1 b1' ->
      bstep st
       (BAnd b1 b2) (BAnd b1' b2)
  | BS_AndTrue : st b,
      bstep st
       (BAnd BTrue b) b
  | BS_AndFalse : st b,
      bstep st
       (BAnd BFalse b) BFalse.
Remark: when evaluating a conjunction of two boolean expression, we use short circuit evaluation. That is, the right hand side will not be evaluated if the left hand side is false. For example, when X's value is 1, X 0 && 0 < X + 10 will be evaluated by the following steps:
    X ≤ 0 && 0 ≤ X + 10
    --> 1 ≤ 0 && 0 ≤ X + 10
    --> False && 0 ≤ X + 10
    --> False.
Module Step_Example2.

Import Abstract_Pretty_Printing.

Example step_1: (X: var) (st: state),
  st X = 1 ->
  bstep st ((X ≤ 0) && (0 ≤ X + 10)) ((1 ≤ 0) && (0 ≤ X + 10)).
Proof.
  intros.
  apply BS_AndStep.
  apply BS_Le1.
  rewrite <- H.
  apply AS_Id.
Qed.

Example step_2: (X: var) (st: state),
  bstep st ((1 ≤ 0) && (0 ≤ X + 10)) (BFalse && (0 ≤ X + 10)).
Proof.
  intros.
  apply BS_AndStep.
  apply BS_Le_False.
  omega.
Qed.

Example step_3: (X: var) (st: state),
  bstep st (BFalse && (0 ≤ X + 10)) BFalse.
Proof.
  intros.
  apply BS_AndFalse.
Qed.

End Step_Example2.

Multi-step Relation

We can now use the single-step relations and their reflexive, transitive closure to formalize an entire execution.
Definition multi_astep (st: state): aexp -> aexp -> Prop :=
  clos_refl_trans (astep st).

Definition multi_bstep (st: state): bexp -> bexp -> Prop :=
  clos_refl_trans (bstep st).
Intuitively, if multi_astep st a1 a2 is true, then a2 is an intermidiate result of evaluating a1 on st. And multi_bstep st b1 b2 says that b2 is an intermidiate result of evaluating b1 on st. Here is several examples.
Remark. In these examples and our later lecture notes, we will reason about different properties about reflexive transitive closures. Our RTClosure library provide tactic supports for these proofs. These tactics include:
  • induction_1n and induction_n1;
  • transitivity_1n, transitivity_n1, etransitivity_1n and etransitivity_n1;
  • unfold_with_1n and unfold_with_n1.
Also, since reflexive transitive closures are reflexive and transitive, RTClosure libraries provide corresponding instances so that Coq's reflexivity, transitivity and etransitivity can be used. You can find more detailed information in the additional reading part.
Module Example1.

Import Abstract_Pretty_Printing.

Example multi_step_ex1: (Y: var) (st: state),
  st Y = 2 ->
  multi_astep st (1 + (3 + Y)) (1 + 5).
  (* multi_astep st
       (APlus (ANum 1) (APlus (ANum 3) (AId Y)))
       (APlus (ANum 1) (ANum 5)) *)

Proof.
  intros.
  etransitivity_1n.
  {
    apply AS_Plus2.
    { apply AH_num. }
    apply AS_Plus2.
    { apply AH_num. }
    apply AS_Id.
  }
  (* multi_astep st
         (APlus (ANum 1) (APlus (ANum 3) (ANum (st Y))))
         (APlus (ANum 1) (ANum 5)) *)

  etransitivity_1n.
  { rewrite H.
    apply AS_Plus2.
    { apply AH_num. }
    apply AS_Plus.
  }
  reflexivity.
Qed.

Example multi_step_ex2: (X: var) (st: state),
  st X = 1 ->
  multi_bstep st ((X ≤ 0) && (0 ≤ X + 10)) BFalse.
Proof.
  intros.
  etransitivity_1n.
  { apply BS_AndStep. apply BS_Le1. apply AS_Id. }
  rewrite H.
  etransitivity_1n.
  { apply BS_AndStep. apply BS_Le_False. lia. }
  etransitivity_1n.
  { apply BS_AndFalse. }
  reflexivity.
Qed.

Example multi_step_ex3: (X: var) (st: state),
  st X = 1 ->
  multi_bstep st ((X ≤ 0) && (0 ≤ X + 10)) ((X ≤ 0) && (0 ≤ X + 10)).
Proof.
  intros.
  reflexivity.
Qed.

End Example1.
The multi-step relations satisfy the congruence property. For example, the following theorem says:
              a1 -->* a1'
        ----------------------
         a1 + a2 -->* a1' + a2
Why? Because for every single step from a1 to a1', we can construct a corresponding step from a1 + a2 to a1' + a2.
    a1   -->  b   -->  c   -->  d   -->   a1'


   a1+a2 --> b+a2 --> c+a2 --> d+a2 --> a1'+a2
Theorem multi_congr_APlus1: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Plus1.
      exact H.
Qed.
Remark: we could complete this proof using different induction principles like the style of trans, of trans_1n instead of trans_n1.
Theorem multi_congr_APlus2: st a1 a2 a2',
  aexp_halt a1 ->
  multi_astep st a2 a2' ->
  multi_astep st (a1 + a2) (a1 + a2').
Proof.
It is critical that we require a1 to be a constant.
  intros.
  induction_n1 H0.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Plus2.
Here we go. Without the assumption H: aexp_halt a1, we cannot complete our proof here.
      * exact H.
      * exact H0.
Qed.
We can prove similar properties for AMinus, AMult, BEq, BLe, BNot and BAnd.
Theorem multi_congr_AMinus1: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 - a2) (a1' - a2).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Minus1.
      exact H.
Qed.

Theorem multi_congr_AMinus2: st a1 a2 a2',
  aexp_halt a1 ->
  multi_astep st a2 a2' ->
  multi_astep st (a1 - a2) (a1 - a2').
Proof.
  intros.
  induction_n1 H0.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Minus2.
      * exact H.
      * exact H0.
Qed.

Theorem multi_congr_AMult1: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 * a2) (a1' * a2).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Mult1.
      exact H.
Qed.

Theorem multi_congr_AMult2: st a1 a2 a2',
  aexp_halt a1 ->
  multi_astep st a2 a2' ->
  multi_astep st (a1 * a2) (a1 * a2').
Proof.
  intros.
  induction_n1 H0.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Mult2.
      * exact H.
      * exact H0.
Qed.

Small Step Semantics for Simple Imperative Programes

The semantics of commands is more interesting. For expression evaluation, the program states are stable. In other words, we only talked about how an expression will be reduced to another one on a fixed program state. But for program execution, the program states are modified. Thus, a ternary relation among the program state, the command to execute and the residue command after one step is not expressive enough to describe programs' behavior. A quaternary relation is needed.
Specifically, we would like to define a predicate cstep such that
    cstep st1 c1 st2 c2
says if the program to execute is c1, the taking one step forward on state st1 may end in state st2 while the residue command is c2.
Traditionally, computer scientist will describe their theory in a slightly different form. They usually treat cstep as a binary relation between state-command pairs and use the following notation:
    (st1c1) --> (st2c2).
We will follow this style in our course, i.e. we will write
    cstep (st1c1) (st2c2).
Technically, this is also more convenient for us to define its reflexive transitive closure later.

Definition of Program Steps

Here is our definition of small step semantics for program execution. In this definition, we use (com * state) in Coq to represent the set of command-program pairs. You can find more information about this Coq data type in the additional reading part.
Import Abstract_Pretty_Printing.

Local Open Scope imp.

Inductive cstep : (com * state) -> (com * state) -> Prop :=
  | CS_AssStep : st X a a',
      astep st a a' ->
      cstep (CAss X a, st) (CAss X a', st)
  | CS_Ass : st1 st2 X n,
      st2 X = n ->
      (Y, XY -> st1 Y = st2 Y) ->
      cstep (CAss X (ANum n), st1) (Skip, st2)
  | CS_SeqStep : st c1 c1' st' c2,
      cstep (c1, st) (c1', st') ->
      cstep (c1 ;; c2 , st) (c1' ;; c2, st')
  | CS_Seq : st c2,
      cstep (Skip ;; c2, st) (c2, st)
  | CS_IfStep : st b b' c1 c2,
      bstep st b b' ->
      cstep
        (If b Then c1 Else c2 EndIf, st)
        (If b' Then c1 Else c2 EndIf, st)
  | CS_IfTrue : st c1 c2,
      cstep (If BTrue Then c1 Else c2 EndIf, st) (c1, st)
  | CS_IfFalse : st c1 c2,
      cstep (If BFalse Then c1 Else c2 EndIf, st) (c2, st)
  | CS_While : st b c,
      cstep
        (While b Do c EndWhile, st)
        (If b Then (c;; While b Do c EndWhile) Else Skip EndIf, st).
In this definition, we use two small tricks:
  • We use Skip as an "empty residue command".
    • An assignment command reduces to Skip (and an updated state).
    • The sequencing command waits until its left-hand subcommand has reduced to Skip, then throws it away so that reduction can continue with the right-hand subcommand.
  • We reduce a While command by transforming it into a conditional followed by the same While.
For example,
        X ::= 1;; Y ::= 2, st1
          (* in which st1(Z) = 0 for all program variables Z *)
    --> Skip;; Y ::= 2, st2
          (* in which st2(X) = 1 and
                      st2(Z) = 0 for all other program variables Z *)

    --> Y ::= 2, st2
    --> Skipst3
          (* in which st3(X) = 1 and
                      st3(Y) = 2 and
                      st3(Z) = 0 for all other program variables Z *)

Here is another example,
        While (0 ≤ XDo X ::= X - 1 EndWhilest1
          (* in which st1(Z) = 0 for all program variables Z *)
    --> If (0 ≤ X)
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst1
    --> If (0 ≤ 0)
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst1
    --> If BTrue
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst1
    --> X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhilest1
    --> X ::= 0 - 1;; While (0 ≤ XDo X ::= X - 1 EndWhilest1
    --> X ::= -1;; While (0 ≤ XDo X ::= X - 1 EndWhilest1
    --> Skip;; While (0 ≤ XDo X ::= X - 1 EndWhilest2
          (* in which st2(X) = -1 and
                      st2(Z) = 0 for all program variables Z *)

    --> While (0 ≤ XDo X ::= X - 1 EndWhilest2
    --> If (0 ≤ X)
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst2
    --> If (0 ≤ -1)
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst2
    --> If BFalse
        Then X ::= X - 1;; While (0 ≤ XDo X ::= X - 1 EndWhile
        Else Skip
        EndIfst2
    --> Skipst2

Multi-step Relation

Similar to the small step semantics for expression evaluation, the multi-step relation for cstep also has its own congruence property. We prove two typical ones here.
Definition multi_cstep: com * state -> com * state -> Prop :=
  clos_refl_trans cstep.

Theorem multi_congr_CSeq: st1 c1 st1' c1' c2,
  multi_cstep (c1, st1) (c1', st1') ->
  multi_cstep (c1 ;; c2, st1) (c1';; c2, st1').
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply CS_SeqStep.
      exact H.
Qed.

Theorem multi_congr_CIf: st b b' c1 c2,
  multi_bstep st b b' ->
  multi_cstep
    (If b Then c1 Else c2 EndIf, st)
    (If b' Then c1 Else c2 EndIf, st).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - exact IHrt.
    - apply CS_IfStep.
      exact H.
Qed.

Additional Reading: Pairs as Coq Data Type


Module Playground_Pair.
In Coq, product types are defined as the follow polymorphic inductive type. This declaration can be read: "There is just one way to construct an A-B pair: by applying the constructor pair to two arguments of type A and B."
Inductive prod (A B : Type) : Type :=
| pair (a : A) (b : B).

Arguments pair {A} {B} _ _.

Notation "( a , b )" := (pair a b).

Notation "A * B" := (prod A B) : type_scope.

Check (pair 3 5).
Here are simple functions for extracting the first and second components of a pair.
Definition fst {A B: Type} (p : A * B) : A :=
  match p with
  | pair x yx
  end.

Definition snd {A B: Type} (p : A * B) : B :=
  match p with
  | pair x yy
  end.

Example fst_example:
  fst (3, 5) = 3.
Proof. reflexivity. Qed.
Note that the pair notation can also be used in pattern matching.
Definition swap_pair {A B: Type} (p : A * B): B * A :=
  match p with
  | (x,y) ⇒ (y,x)
  end.
Let's try to prove a few simple facts about pairs. If we state things in a slightly peculiar way, we can complete proofs with just reflexivity (and its built-in simplification):
Theorem surjective_pairing' : (n m : Z),
  (n,m) = (fst (n,m), snd (n,m)).
Proof. intros. reflexivity. Qed.
Here, we can see the effect of simpl.
Theorem surjective_pairing'_alter : (n m : Z),
  (n,m) = (fst (n,m), snd (n,m)).
Proof. intros. simpl. reflexivity. Qed.
But reflexivity is not enough if we state the lemma in a more natural way:
Theorem surjective_pairing_stuck : (p : Z * Z),
  p = (fst p, snd p).
Proof.
  intros.
  simpl. (* Doesn't reduce anything! *)
Abort.
We have to expose the structure of p so that simpl can perform the pattern match in fst and snd. We can do this with destruct.
Theorem surjective_pairing : (p : Z * Z),
  p = (fst p, snd p).
Proof.
  intros p.
  destruct p as [n m].
  simpl.
  reflexivity.
Qed.

Exercise: 1 star, standard (snd_fst_is_swap)

Theorem snd_fst_is_swap : {A B: Type} (p : A * B),
  (snd p, fst p) = swap_pair p.
Proof.
  (* FILL IN HERE *) Admitted.

Exercise: 1 star, standard, optional (fst_swap_is_snd)

Theorem fst_swap_is_snd : {A B: Type} (p : A * B),
  fst (swap_pair p) = snd p.
Proof.
  (* FILL IN HERE *) Admitted.
End Playground_Pair.
These definitions about pairs are all built in Coq's standard library.
Print prod.
  (* Inductive prod (A B : Type) : Type :=  pair : A -> B -> A * B *)

Check fst.
  (* fst : ?A * ?B -> ?A *)

Check fst.
  (* snd : ?A * ?B -> ?B *)

Additional Reading: Support For Reflexive Transitive Closure

We already know that three different definitions of reflexive transitive closures are equivalent. If we define a multi-step relation using clos_refl_trans but want to prove related properties via an equivalent transformation to clos_refl_trans_1n, we can use unfold_with_1n. Here is an example:
Example multi_congr_APlus1_demo1: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  unfold_with_1n multi_astep.
  intros.
  induction H.
  + apply rt1n_refl.
  + eapply rt1n_trans_1n.
    - apply AS_Plus1.
      exact H.
    - apply IHclos_refl_trans_1n.
Qed.
We provide similar support for clos_refl_trans_n1.
Example multi_congr_APlus1_demo2: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  unfold_with_n1 multi_astep.
  intros.
  induction H.
  + apply rtn1_refl.
  + eapply rtn1_trans_n1.
    - apply AS_Plus1.
      exact H.
    - apply IHclos_refl_trans_n1.
Qed.
If you just want to do clos_refl_trans_1n's induction principle, you can use induction_1n.
Example multi_congr_APlus1_demo3: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_1n H.
  +
Abort.
We know that multi_astep must be reflexive since it is defined by a reflexive transitive closure. We can use reflexivity here.
Example multi_congr_APlus1_demo3: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_1n H.
  + reflexivity.
  +
Abort.
Here, if we directly use transitivity via intemidiate state a1 + a2, we have to prove in Coq that
  • multi_astep st (a0 + a2) (a1 + a2)
  • multi_astep st (a1 + a2) (a1' + a2)
Then, for the first target, we will use rt_step to complete the proof. It is inconvenient. Using a transitivity of rt_trans_1n's style is better. RTClosure library provides transitivity_1n.
Example multi_congr_APlus1_demo3: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_1n H.
  + reflexivity.
  + transitivity_1n (a1 + a2)%imp.
    - apply AS_Plus1.
      exact H.
    - apply IHrt.
Qed.
If you do not want to manually provide an intermediate state, you can use etransitivity_1n.
Example multi_congr_APlus1_demo4: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_1n H.
  + reflexivity.
  + etransitivity_1n.
    - apply AS_Plus1.
      exact H.
    - apply IHrt.
Qed.
RTClosure provides similar supports via induction_n1, transitivity_n1 and etransitivity_n1.
Example multi_congr_APlus1_demo5: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + transitivity_n1 (a1' + a2)%imp.
    - apply IHrt.
    - apply AS_Plus1.
      exact H.
Qed.

Example multi_congr_APlus1_demo6: st a1 a1' a2,
  multi_astep st a1 a1' ->
  multi_astep st (a1 + a2) (a1' + a2).
Proof.
  intros.
  induction_n1 H.
  + reflexivity.
  + etransitivity_n1.
    - apply IHrt.
    - apply AS_Plus1.
      exact H.
Qed.
In the end, we discuss a technique issue of doing manual induction in Coq. We will use multi_congr_CSeq as an example here.
Theorem multi_congr_CSeq_again: st1 c1 st1' c1' c2,
  multi_cstep (c1, st1) (c1', st1') ->
  multi_cstep (c1 ;; c2, st1) (c1';; c2, st1').
Proof.
This time, we will use unfold_with_n1 and try to use the induction principle of clos_refl_trans_n1 manually to complete the proof.
  unfold_with_n1 multi_cstep.
  intros.
  induction H.
  + (** Oops, Coq does not generate a proof goal as we expected. *)
Abort.
The problem here is, Coq's induction tactic on inductive predicates requires that predicate takes only Coq variables as its argument. Any other internal structures are not allowed, or else necessary information might be lost. In the case above, we do induction over:
    Hclos_refl_trans_n1 cstep (c1st1) (c1'st1')
In this hypothesis, (c1, st1) and (c1', st1') are not simple Coq variables. Thus, Coq cannot find a common pattern between big proof trees and small proof trees, which causes problems in manual induction. Coq provides the remember tactic for necessary transitions.
Theorem multi_congr_CSeq_again: st1 c1 st1' c1' c2,
  multi_cstep (c1, st1) (c1', st1') ->
  multi_cstep (c1 ;; c2, st1) (c1';; c2, st1').
Proof.
  unfold_with_n1 multi_cstep.
  intros.
  remember (c1, st1) as cst eqn:H0.
  remember (c1', st1') as cst' eqn:H1.
These two commands say: use cst and cst' to represent those two pairs. And use two equalities H0 and H1 to record this replacement.
  revert c1 st1 c1' st1' H0 H1; induction H.
  + intros.
This branch corresponds to the case that
        multi_cstep (c1st1) (c1'st1')
is true because of reflexivity. The subst will find H0: x = (c1, st1) and replace every occurrence of x with (c1, st1).
    subst.
Now, H1 says
         pair c1 st1 = pair c1' st1'
and pair is a constructor for product type. Since all constructors are injective, we can achieve c1 = c1' and st1 = st1'.
    injection H1 as ?H ?H.
    subst.
    apply rtn1_refl.
  + intros.
This branch corresponds to the case that
        multi_cstep (c1st1) (c1'st1')
is true because there exists another pair c1'', st'', such that
        multi_cstep (c1st1) (c1''st1'')
        cstep (c1''st1'') (c1'st1')
holds.
    subst.
    destruct y as [c1'' st1''].
    eapply rtn1_trans_n1.
    - apply CS_SeqStep.
      exact H.
    - apply IHclos_refl_trans_n1.
      * reflexivity.
      * reflexivity.
Qed.
Of course, manually work-around of using remember, revert, subst and injection is inconvenient. RTClosure's induction_n1 does that for you.
(* 2021-03-24 12:56 *)