Sunday, July 27, 2014

[번역] 콕, 빨리빨리 4장 / [Translation] Ch.4, Coq in a Hurry

Coq in a Hurry (Yves Bertot, April 2010)
 - http://cel.archives-ouvertes.fr/docs/00/47/58/07/PDF/coq-hurry.pdf
 - Keyword: Tutorial, Theorem Proving, Prover, Logic, Software Verification, Agda, Nurpl

콕, 빨리빨리 (최광훈, 2014년 7월)

목차
 1. 식과 논리식
 2. 콕으로 프로그래밍하기
 3. 명제와 증명
 4. 숫자 프로그램의 성질을 증명하기
 5. 리스트 프로그램의 성질을 증명하기
 6. 새로운 데이터 타입 정의하기
 7. 콕 시스템의 숫자
 8. 귀납적 성질
 9. 연습문제 (원문 참고)
 10. 해답 (원문 참고)


4. 숫자 프로그램의 성질을 증명하기

자연수에 대해 계산하는 프로그램의 성질을 증명하기위해 보통 여러 계산과정이 어떤 방식으로 연관되어 있음을 보일수 있다. 주어진 함수가 재귀방식이면 대개 귀납적 방식으로  증명한다. 이때 알아야할 전술은 다음과 같다.

  • induction 전술은 자연수 n에 대한 귀납적 증명을 한다. 증명을 두 부분으로 나누어, 우선 n이 0일때 증명하고자하는 성질이 성립함을 확인한다. 그 다음 p에 대한 성질로부터 S p에 대한 성질을 증명한다. p에 대한 가정을 귀납적 가정이라 부르고 IH로 시작하는 이름을 보통 붙인다. 이 전술은 0에 대한 목적 명제와 S p에 대한 목적 명제 두 개를 만든다. 
  • simpl 전술은 복잡한 인자를 갖는 재귀함수 호출을 해당하는 값으로 대체한다. 함수의 정의에 의해 이 값을 결정한다. 
  • discriminate 전술은 0 = S ...이나 true = false와 같이 선언하는 가정이 있을때 유용하다.
  • injection 전술은 S x = S y 모양의 가정에서 x = y를 알아내는데 도움이 된다. 

4.1 귀납적 증명

증명 예제를 살펴보자. 0부터 n개의 자연수들의 합이 잘 알려진 다항식을 이룬다는 것을 증명해보자.

Lemma sum_n_p : forall n, 2 * sum_n n + n = n * n.
induction n.

2 subgoals
  
  ============================
   2 * sum_n 0 + 0 = 0 * 0

subgoal 2 is:
 2 * sum_n (S n) + S n = S n * S n

새로 생성된 두 목적 명제에서, 첫번째 것은 n을 0으로 바꾸었고, 두번째는 n을 S n으로 바꾼것이다.  두번째 목적 명제에서 n에 대해 성립한다는 가정은 표시되어 있지 않다. 

첫번째 목적 명제는 매우 쉽다. sum_n, 덧셈, 곱셈의 정의에 의하면 등식의 양쪽이 모두 0이기 때문이다. reflexivity 전술로 해결한다. 

reflexivity.

이 전술을 쓰면 두번째 목적 명제만 남았고 콕 시스템에서 전체를 보여준다. 이제 귀납적 가정을 볼 수 있다. 

1 subgoal
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  ============================
   2 * sum_n (S n) + S n = S n * S n

여기에서 조금 생각할 필요가 있다. S n * S n을 n * n이 나타나는 식으로 펼칠수 있다. 그 다음 IHn으로 오른편에서 왼쪽 방향으로 다시 쓸 수 있다. assert  전술을 사용해 S n * S n과 새로운 값이 같다고 임시로 선언하고, ring 전술로 이 동등 선언이 사실 성립함을 보인다. 

assert (SnSn : S n * S n = n * n + 2 * n + 1).

2 subgoals
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  ============================
   S n * S n = n * n + 2 * n + 1

subgoal 2 is:
 2 * sum_n (S n) + S n = S n * S n

ring.

1 subgoal
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  SnSn : S n * S n = n * n + 2 * n + 1
  ============================
   2 * sum_n (S n) + S n = S n * S n

rewrite SnSn.

1 subgoal
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  SnSn : S n * S n = n * n + 2 * n + 1
  ============================
   2 * sum_n (S n) + S n = n * n + 2 * n + 1

이번에는 귀납적 가정을 이용해서 n * n을 2 * sum_n n + n으로 다시 쓴다. 

rewrite <- IHn.

1 subgoal
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  SnSn : S n * S n = n * n + 2 * n + 1
  ============================
   2 * sum_n (S n) + S n = 2 * sum_n n + n + 2 * n + 1

다음 단계에서 sum_n (S n)을 정의에 따라 펼쳐서 그 값을 sum_n n으로 표현해본다. simpl 전술을 사용하는데, 이것은 sum_n 뿐만 아니라 곱셈도 그 정의에 따라 펼치는 것을 시도한다.

sumpl.

1 subgoal
  
  n : nat
  IHn : 2 * sum_n n + n = n * n
  SnSn : S n * S n = n * n + 2 * n + 1
  ============================
   n + sum_n n + (n + sum_n n + 0) + S n =
   sum_n n + (sum_n n + 0) + n + (n + (n + 0)) + 1

그 다음 단계에서 ring 전술을 사용해서 목적 명제의 등식이 덧셈의 결합성과 교환 규칙에 의해 성립함을 보인다.

ring.
Qed.

4.2 귀납에서 더 강력한 문장

재귀 함수가 몇 단계의 패턴 매칭을 사용한다면 연속적인 여러 개의 숫자들에 대해 성질을 기술하는 문장을 증명하는 것이 낫다. evenb 함수에 대한 증명의 예로 살펴보자.

Lemma evenb_p : forall n, evenb n = true -> exists x, n = 2 * x.
assert (Main: forall n, (evenb n = true -> exists x, n = 2 * x) /\
                    (evenb (S n) = true -> exists x, S n = 2 * x)).

여기에서 원래 목적 명제보다 더 강력한 문장을 증명해본다. 즉, n 뿐만 아니라 S n에 대해서도 성립함을 증명한다. 그 다음 이전처럼 귀납 방식으로 진행한다. 더 강력한 문장을 귀납 방식으로 증명하는 것은 재귀함수 증명에서 흔히 볼 수 있다.

induction n.

3 subgoals
  
  ============================
   (evenb 0 = true -> exists x : nat, 0 = 2 * x) /\
   (evenb 1 = true -> exists x : nat, 1 = 2 * x)

subgoal 2 is:
 (evenb (S n) = true -> exists x : nat, S n = 2 * x) /\
 (evenb (S (S n)) = true -> exists x : nat, S (S n) = 2 * x)
subgoal 3 is:
 forall n : nat, evenb n = true -> exists x : nat, n = 2 * x

첫번째 목적 명제는 두 부분으로 나뉜다. 처음 부분은 결론에 나타난 존재 한정이 있는 문장이다. 전술 테이블을 보면 exists 전술로 어떤 값을 주면 된다. 이 경우 x의 값을 0으로 놓으면 0 = 2 * 0이 성립함을 쉽게 알 수 있다. 두번째 부분은 다르다. x의 값으로 놓을 수 있는 것이 없다. 하지만 evenb 1 = true는 성립하지 않는다. 왜냐하면 evenb 1은 false이고 false = true는 불가능하기 때문이다. 일관성이 없는 경우는 discriminate 전술을 사용한다.

split.
   exists 0; ring.
simpl; intros H; discriminate H.

나머지 증명에 대해 자세히 설명하지 않는다. 독자 스스로 증명해볼 것을 권한다. 

  split.
    destruct IHn as [_ IHn'].
    exact IHn'.

  simpl.
  intros H.
  destruct IHn as [IHn' _].
  assert (H' : exists x, n = 2 * x).
    apply IHn'.
    exact H.
  destruct H' as [x q].
  exists (x + 1).
  rewrite q.
  ring.

가정 Main을 일단 증명하면 원래 증명하려고 했던 명제로 돌아갈수 있다. 이때 firstorder 전술로 증명을 마무리할 수 있다. 설명을 위해 다른 방법을 보여줄 것이다.

1 subgoal
  
  Main : forall n : nat,
         (evenb n = true -> exists x : nat, n = 2 * x) /\
         (evenb (S n) = true -> exists x : nat, S n = 2 * x)
  ============================
   forall n : nat, evenb n = true -> exists x : nat, n = 2 * x

intros n ev.

1 subgoal
  
  Main : forall n : nat,
         (evenb n = true -> exists x : nat, n = 2 * x) /\
         (evenb (S n) = true -> exists x : nat, S n = 2 * x)
  n : nat
  ev : evenb n = true
  ============================
   exists x : nat, n = 2 * x

여기서 지금까지 언급하지 않았던 콕 시스템의 한가지 편의 기능을 사용할 수 있다. 정리의 문장이 전칭 한정이거나 함축의 형태이면 이 정리를 마치 함수인 것 처럼 쓸 수 있다. 어떤 식에 적용하면 이것으로 사례화된 새로운 정리를 얻을 수 있다. 위의 예에서 Main 가정을 사용해서 n에 대해 사례화시킬수 있다. 

  destruct (Main n) as [H _].
  apply H.
  exact ev.

Qed.

덧셈에 다른 정의에 관한 연습문제

자연수에 대한 덧셈을 새롭게 정의할 수 있다.

Fixpoint add n m := match n with 0 => m | S p => add p (S m) end.

다음을 증명하시오.

forall n m, add n (S m) = S (add n m)
forall n m, add (S n) m = S (add n m)
forall n m, add n m = n + m

새로운 연습문제를 증명할때 이전에 증명한 보조 정리를 사용할 수 있다.

홀수의 합에 대한 연습문제

앞에서부터 n개의 홀수를 모두 더한 합은 다음 함수로 정의한다.

Fixpoint sum_odd_n (n:nat) : nat :=
   match n with 0 => 0 | S p => 1 + 2 * p + sum_odd_n p end.

다음 문장을 증명하시오.

forall n:nat, sum_odd_n n = n * n


===
번역

(1장)
Expressions : 식
Formulas : 식
infix : 인픽스
Logical Formulas : 논리식
Symbolic Computation : 기호 계산
Terms : 텀
Well-formed : 제대로 된 형태

(2장)
Keyword : 키워드
Boolean : 부울
Conjunction : 곱
Disjunction : 합
Negation : 부정
Construct : 구문
Pattern-matching : 패턴 매칭
Recursivity : 재귀 형태
Structural Recursion : 구조적 재귀
Subterm : 부분식
Sublist : 부분리스트

(3장)
Fact: 사실 명제
Theorem: 정리
Implication : 함축
Predicate : (술어) 명제
Rewriting : 다시쓰기
Equality : 등식
Goal : 목적 명제
Tactic : 전술
Hypothesis : 가정
Universal Quantification : 전칭 한정
Transitive : 추이적
Instantiation : 사례화
Associativity : 결합성

(4장)
Proof by induction : 귀납적 증명
Existential : 존재
Lemma : 보조 정리


No comments: