호어 논리와 프로그램 검증(Hoare logic and program verification)
'P가 참인 상태에서 C를 실행해 끝나면 Q가 참이다'라는 주장 {P} C {Q}를 규칙으로 증명하는 논리. 반복문은 한 바퀴 돌아도 깨지지 않는 불변식으로, 끝남은 매번 줄어드는 음이 아닌 정수(integer)로 증명한다. 데이크스트라의 최약 전조건(weakest precondition)은 프로그램을 조건을 바꾸는 함수(function)로 본다. 끝남을 따지지 않는 판(wlp)은 실행해 닿는 상태들(최강 후조건, strongest postcondition)과 갈루아 연결(Galois connection)을 이룬다.
이진 탐색(binary search) 페이지는 이진 탐색이 '간단해 보여도 정확하게 짜기는 의외로 까다롭다'고 말합니다. 그렇다면 제대로 짰는지는 어떻게 알까요? 시험해 보는 방법은 넣어 본 입력에 대해서만 답합니다. 길이 16인 배열 하나에 찾는 값을 바꿔 가며 넣어 보아도, 길이가 다른 배열과 다른 값들은 끝없이 남습니다. 데이크스트라가 1970년 「구조적 프로그래밍(structured programming)에 관한 노트」에 적은 대로, 시험은 오류가 있음을 보일 수는 있어도 없음을 보일 수는 없습니다. 남은 길은 증명입니다. 모든 입력에 대해 한꺼번에, 프로그램의 글자만 보고.
생각의 싹은 일찍 나왔습니다. 1949년 튜링은 짧은 발표 「큰 루틴 확인하기」에서 프로그램의 몇 곳에 '여기서는 이것이 참'이라는 단언을 붙이고, 끝남은 줄어드는 양으로 보이는 방법을 선보였습니다. 이 발표는 오래 잊혔고, 1967년 미국의 로버트 플로이드가 순서도의 화살표마다 조건을 붙여 프로그램의 뜻을 정하면서 생각이 다시 나왔습니다. 1969년 영국의 토니 호어는 이것을 프로그램 글자 위의 논리로 바꾸었습니다.
규칙은 대입, 이어 붙이기, 조건문, 반복문 같은 문법 요소마다 하나씩 있고, 여기에 문법과 상관없이 조건을 바꿔 끼우는 결과 규칙이 더해집니다. 대입, 이어 붙이기, 결과 규칙은 이렇습니다.
대입 규칙은 거꾸로 읽힙니다.
핵심은 반복문의 규칙입니다(위의 식). 반복 조건 b가 참일 때 몸통 C를 한 번 실행해도 깨지지 않는 조건 I, 곧 반복 불변식(loop invariant)을 찾으면, 반복이 끝났을 때 I와 ¬b가 함께 성립합니다. 규칙이 옳은 이유는 수학적 귀납법(mathematical induction)입니다. 0바퀴 뒤에 I가 참이고(처음에 참), k바퀴 뒤에 참이면 k + 1바퀴 뒤에도 참이니(몸통이 지킴), 몇 바퀴 뒤에 끝나든 I는 참입니다. 끝남은 따로 보여야 합니다. 상태로 정해지는 정수 V가 불변식 아래에서 늘 0 이상이고 한 바퀴마다 엄격히 줄어들면, 반복은 V의 처음 값보다 많이 돌 수 없습니다. 이런 V를 변량(종료 측도(measure))이라 합니다. 음이 아닌 정수가 끝없이 줄어들 수는 없다는 사실은 귀납법의 다른 얼굴(정렬 원리, well-ordering principle)입니다.
이진 탐색으로 해 봅시다. 정렬된 배열
그림에서 lo 왼쪽의 파란 칸은 't보다 작다고 이미 확인된 곳', hi부터 오른쪽의 청록 칸은 't 이상이라고 확인된 곳입니다. 가운데 칸만 아직 모릅니다. 변량은 모르는 칸의 개수
증명은 네 가지 확인으로 끝납니다. (1) 처음: lo = 0, hi = n이면 I가 참입니다.
틀린 판 두 가지를 골라 보세요. 'lo := mid'로 쓰면 불변식은 지켜지지만 변량이 줄지 않는 순간이 옵니다.
1975년 데이크스트라는 방향을 바꿨습니다. 후조건 Q와 프로그램 C가 주어지면, C가 반드시 끝나고 끝난 뒤 Q가 참이 되는 시작 상태 전체를 나타내는 조건, 곧 최약 전조건 wp(C, Q)를 계산하자는 것입니다. 대입은
프로그램은 후조건을 전조건으로 바꾸는 함수, 곧 술어 변환기(predicate transformer)가 됩니다.
끝남을 따지지 않는 판인 최약 자유 전조건(weakest liberal precondition) wlp(C, Q)는 'C가 끝난다면 반드시 Q가 참'인 시작 상태의 조건입니다. 이것이 실행과 짝을 이룹니다. 시작 상태의 집합(set) P에서 C를 실행해 닿을 수 있는 끝 상태를 모두 모은 것을 최강 후조건 sp(C, P)라 하면
입니다. 양쪽 모두 {P} C {Q}, 곧 'P에서 출발해 끝나는 모든 실행이 Q에서 끝난다'는 말이기 때문입니다. 순서 사이의 이런 짝이 갈루아 연결이고, 수반 함자(adjoint functor) 페이지에서 본 '∃ ⊣ 역상 ⊣ ∀'와 같은 모양입니다. sp는 '어떤 실행이 그리로 가는가'(∃)를, wlp는 '모든 실행이 그리로 가는가'(∀)를 묻습니다. 아래 점을 눌러 P를, 위 점을 눌러 Q를 바꿔 보세요. 프로그램:
갈루아 연결에서 공짜로 따라 나오는 것이 있습니다. 오른쪽 짝은 '그리고'를 보존하므로
한계도 분명합니다. 모든 프로그램의 부분 정확성을 판정하는 알고리즘(algorithm)은 없습니다. {참} C {거짓}은 'C는 어떤 상태에서 시작해도 끝나지 않는다'는 뜻이라, 그런 알고리즘은 정지 문제(halting problem)를 풀어 버리기 때문입니다. 그래서 불변식을 기계가 늘 알아서 찾아 줄 수는 없고, 1978년 스티븐 쿡이 보인 (부분 정확성에 대한) 완전성도 '상대적'입니다. 조건을 적는 언어가 충분히 표현력이 있고 그 언어의 참인 문장을 모두 안다고 치면, 참인 삼중은 모두 규칙으로 증명됩니다. 실제 도구는 분업을 합니다. 사람이 불변식과 변량을 적고, 기계가 나머지 검증 조건(위의 네 확인 같은 논리식)을 자동 증명기로 확인합니다. 2009년 무렵 나온 언어 다프니(Dafny)에서는 반복문 옆에 invariant와 decreases를 적습니다. 포인터와 공유 메모리는 2001–2002년 존 레이놀즈, 피터 오헌 등이 내놓은 분리 논리(separation logic)로 다룹니다. 분리 논리의 '분리된 그리고'
흔한 오해 둘. 증명된 프로그램도 명세가 틀리면 틀립니다. '정렬한다'를 '출력이 정렬되어 있다'로만 적으면 빈 배열을 돌려주는 프로그램도 증명됩니다. 출력이 입력을 재배열한 것이라는 조건이 빠졌기 때문입니다. 호어 논리(Hoare logic)는 C가 명세를 지키는지 확인할 뿐, 명세가 원하는 것인지는 확인하지 않습니다. 또 불변식은 '언제나 참인 조건'이 아니라 반복 조건을 검사하는 순간마다 참인 조건입니다. 몸통을 실행하는 도중에는 잠깐 깨져도 됩니다. 배열의 합을 구하는 몸통 s := s + a[i]; i := i + 1의 불변식
이어지는 곳. 여기서 증명한 프로그램은 이진 탐색이고, 반복문 규칙의 정당성과 변량의 논리는 수학적 귀납법에서 옵니다. 이 논리를 세운 사람들의 이야기는 토니 호어와 에츠허르 데이크스트라에, 단언과 줄어드는 양이라는 첫 싹은 튜링에 있습니다. sp와 wlp의 짝은 갈루아 연결의 전형적인 예이며, 수반 함자의 ∃와 ∀를 프로그램에 옮긴 것입니다. 반복문의 뜻과 wp를 최소 고정점으로 계산하는 방법은 영역 이론과 이어지고, 모든 프로그램을 자동으로 검증할 수 없는 이유는 정지 문제입니다. 조건들은 '그리고, 또는, 아니다'로 계산하는 불 대수(Boolean algebra)의 원소(element)이고, 전조건은 약하게 후조건은 강하게라는 결과 규칙은 하위 타입(subtype)의 반변(contravariant)·공변(covariant)과 같은 모양입니다. 메모리를 자원처럼 나눠 쓰는 분리 논리는 선형 논리와 닮았고, 명세를 타입(type)에 적어 증명과 프로그램을 한 몸으로 만드는 다른 길은 의존 타입(dependent type)과 증명 보조기(proof assistant)에 있습니다.
이 개념이 나오는 긴 글
이 개념을 언급하는 페이지
- 최대공약수와 유클리드 호제법
… 아닌 나머지는 반복이 언젠가 끝남을 보장하는 양(변량)입니다. 이 둘로 반복문이 옳다는 것을 보이는 것이호어 논리의 틀입니다.
- 정지 문제
… 될 수 없는데, 이것은 같은 불가능을 순서의 말로 다시 본 것입니다. 프로그램이 명세를 지키는지 증명하는호어 논리도 이 벽에 부딪힙니다. '끝난다면 이 조건이 성립한다'는 부분 정확성으로 읽으면 {참} C {거짓}은 …
- 수학적 귀납법
… '하나 있다'는 부분이 재귀로 함수를 정의해도 된다는 보증입니다. 반복문이 끝났을 때 불변식이 참이라는호어 논리의 규칙도 돈 바퀴 수에 대한 귀납법으로 정당화됩니다.
- 알고리즘
… 반복문마다 깨지지 않는 조건(불변식)과 매번 줄어드는 양을 적어 그 증명을 한 줄씩 해 나가는 방법이호어 논리입니다. 답이 맞는지 확인하기는 쉬운데 빠른 알고리즘이 있는지조차 모르는 문제들을 둘러싼 큰 물음은 ⟦P …
- 이진 탐색
… hi − lo가 매번 줄어든다는 것을 증명합니다. 이 증명과, lo := mid로 쓰면 왜 영원히 도는지는호어 논리에서 한 줄씩 따라가 볼 수 있습니다.
- 정렬 알고리즘
… 프로그램 FIND가 옳다는 것을 반복 불변식으로 증명해 보이기도 했는데, 이렇게 프로그램을 증명하는 규칙이호어 논리입니다. 그 증명에서 '정렬한다'는 명세에는 출력이 정렬되어 있다는 것 말고도 출력이 입력을 재배열한 …
- 타입 이론
… 프로그램이 무엇을 뜻하는지는 영역 이론에서, 프로그램 옆에 조건을 적어 명세를 증명하는 다른 길은호어 논리에서 이어집니다.
- 의존 타입
… 정리⟧가 말해 줍니다. 명세를 타입에 적는 대신 프로그램 옆에 전조건과 후조건을 적어 증명하는 길은호어 논리이고, 귀납적으로 정의한 타입마다 생기는 재귀자가 결과의 타입이 입력에 따라 달라지도록 fold를 넓힌 …
- 증명 보조기
… 일이 타입 이론의 규칙을 따르는 일입니다. 반복문 옆에 불변식과 변량을 적는 다프니 같은 검증 도구는호어 논리의 방식을 따르고, Lean과 Rocq가 귀납적으로 정의한 타입마다 자동으로 만드는 재귀자는 ⟦시작 …
- 수반 함자
… 수반을 따로 갈루아 연결이라 부르며, 프로그램 실행의 최강 후조건과 최약 자유 전조건이 그런 짝입니다(호어 논리). 텐서곱과 ⊸ 사이의 커링 \mathrm{Hom}(A\otimes B, …
- 선형 논리와 선형 타입
… 구조는 모나드를 뒤집어 보면 보입니다. 프로그램 검증에서 메모리 조각을 자원처럼 나눠 쓰는 분리 논리는호어 논리에 있습니다. 복사가 이차식이라는 관찰은 선형변환의 정의에서 바로 나오며, 선형 논리 전체의 증명 …
- 영역 이론: 스콧과 재귀의 의미
… '반복문이 끝나고, 끝난 뒤 원하는 조건이 성립한다'를 보장하는 시작 조건 가운데 가장 느슨한 것이고,호어 논리와 함께 쓰는 계산법입니다. 1977년 고든 플롯킨은 재귀를 위한 상수 fix를 가진 작은 언어 …
- 하위 타입과 공변·반변
… 하고 후조건을 강하게(더 좁은 결과를 약속하게) 해도 됩니다. 함수 규칙의 반변·공변과 똑같은 모양이며,호어 논리의 결과 규칙이 바로 이 모양입니다. 이어지는 곳. 공변과 반변은 함자 페이지의 공변 함자와 반변 …
- 갈루아 연결
… 틀리지는 않습니다. 비행 제어 소프트웨어의 실행 오류가 없음을 보이는 정적 분석기가 이 원리를 씁니다(호어 논리, 영역 이론). 논리에서도 같은 극성이 있습니다. 문장들의 집합 T에는 그 문장을 모두 만족하는 …