Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Why3
why3
Commits
a32e6807
Commit
a32e6807
authored
Jun 22, 2011
by
Jean-Christophe Filliâtre
Browse files
documentation: solution for VSTTE'10 problem 4
parent
8ba811e5
Changes
3
Hide whitespace changes
Inline
Side-by-side
doc/whyml.tex
View file @
a32e6807
...
...
@@ -466,15 +466,259 @@ the loop invariant.
\subsection
{
Problem 4: N-Queens
}
The fourth problem is probably the most challenging one.
We have to verify the implementation of a program which solves the
$
N
$
-queens puzzle: place
$
N
$
queens on an
$
N
\times
N
$
chess board so that no queen can capture another one with a
legal move.
The program should return a placement if there is a solution and
indicates that there is no solution otherwise. A placement is a
$
N
$
-element array which assigns the queen on row
$
i
$
to its column.
Thus we start our module by importing arithmetic and arrays:
\begin{verbatim}
module NQueens
use import int.Int
use import module array.Array
\end{verbatim}
The code is a simple backtracking algorithm, which tries to put a queen
on each row of the chess board, one by one (there is basically no
better way to solve the
$
N
$
-queens puzzle).
A building block is a function which checks whether the queen on a
given row may attack another queen on a previous row. To verify this
function, we first define a more elementary predicate, which expresses
that queens on row
\texttt
{
pos
}
and
\texttt
{
q
}
do no attack each other:
\begin{verbatim}
logic consistent
_
row (board: array int) (pos: int) (q: int) =
board[q] <> board[pos] and
board[q] - board[pos] <> pos - q and
board[pos] - board[q] <> pos - q
\end{verbatim}
Then it is possible to define the consistency of row
\texttt
{
pos
}
with respect to all previous rows:
\begin{verbatim}
logic is
_
consistent (board: array int) (pos: int) =
forall q:int. 0 <= q < pos -> consistent
_
row board pos q
\end{verbatim}
Implementing a function which decides this predicate is another
matter. In order for it to be efficient, we want to return
\texttt
{
False
}
as soon as a queen attacks the queen on row
\texttt
{
pos
}
. We use an exception for this purpose and it carries the
row of the attacking queen:
\begin{verbatim}
exception Inconsistent int
\end{verbatim}
The check is implemented by a function
\verb
|
check_is_consistent
|
,
which takes the board and the row
\texttt
{
pos
}
as arguments, and scans
rows from 0 to
\texttt
{
pos-1
}
looking for an attacking queen. As soon
as one is found, the exception is raised. It is caught immediately
outside the loop and
\texttt
{
False
}
is returned. Whenever the end of
the loop is reached,
\texttt
{
True
}
is returned.
\begin{verbatim}
let check
_
is
_
consistent (board: array int) (pos: int) =
{
0 <= pos < length board
}
try
for q = 0 to pos - 1 do
invariant
{
forall j:int. 0 <= j < q -> consistent
_
row board pos j
}
let bq = board[q] in
let bpos = board[pos] in
if bq = bpos then raise (Inconsistent q);
if bq - bpos = pos - q then raise (Inconsistent q);
if bpos - bq = pos - q then raise (Inconsistent q)
done;
True
with Inconsistent q ->
assert
{
not (consistent
_
row board pos q)
}
;
False
end
{
result=True <-> is
_
consistent board pos
}
\end{verbatim}
The assertion in the exception handler is a cut for SMT solvers.
This first part of the solution is given Figure~
\ref
{
fig:NQueens1
}
.
\begin{figure}
\centering
\begin{verbatim}
module NQueens
use import int.Int
use import module array.Array
logic consistent
_
row (board: array int) (pos: int) (q: int) =
board[q] <> board[pos] and
board[q] - board[pos] <> pos - q and
board[pos] - board[q] <> pos - q
logic is
_
consistent (board: array int) (pos: int) =
forall q:int. 0 <= q < pos -> consistent
_
row board pos q
exception Inconsistent int
let check
_
is
_
consistent (board: array int) (pos: int) =
{
0 <= pos < length board
}
try
for q = 0 to pos - 1 do
invariant
{
forall j:int. 0 <= j < q -> consistent
_
row board pos j
}
let bq = board[q] in
let bpos = board[pos] in
if bq = bpos then raise (Inconsistent q);
if bq - bpos = pos - q then raise (Inconsistent q);
if bpos - bq = pos - q then raise (Inconsistent q)
done;
True
with Inconsistent q ->
assert
{
not (consistent
_
row board pos q)
}
;
False
end
{
result=True <-> is
_
consistent board pos
}
\end{verbatim}
\vspace*
{
-2em
}
\hrulefill
\caption
{
Solution for VSTTE'10 competition problem 4 (1/2).
}
\label
{
fig:NQueens1
}
\end{figure}
We now proceed with the verification of the backtracking algorithm.
The specification requires us to define the notion of solution, which
is straightforward using the predicate
\verb
|
is_consistent
|
above.
However, since the algorithm will try to complete a given partial
solution, it is more convenient to define the notion of partial
solution, up to a given row. It is even more convenient to split it in
two predicates, one related to legal column values and another to
consistency of rows:
\begin{verbatim}
logic is
_
board (board: array int) (pos: int) =
forall q:int. 0 <= q < pos -> 0 <= board[q] < length board
logic solution (board: array int) (pos: int) =
is
_
board board pos and
forall q:int. 0 <= q < pos -> is
_
consistent board q
\end{verbatim}
The algorithm will not mutate the partial solution it is given and,
in case of a search failure, will claim that there is no solution
extending this prefix. For this reason, we introduce a predicate
comparing two chess boards for equality up to a given row:
\begin{verbatim}
logic eq
_
board (b1 b2: array int) (pos: int) =
forall q:int. 0 <= q < pos -> b1[q] = b2[q]
\end{verbatim}
The search itself makes use of an exception to signal a successful search:
\begin{verbatim}
exception Solution
\end{verbatim}
The backtracking code is a recursive function
\verb
|
bt_queens
|
which
takes the chess board, its size, and the starting row for the search.
The termination is ensured by the obvious variant
\texttt
{
n-pos
}
.
\begin{verbatim}
let rec bt
_
queens (board: array int) (n: int) (pos: int) variant
{
n-pos
}
=
\end{verbatim}
The precondition relates
\texttt
{
board
}
,
\texttt
{
pos
}
, and
\texttt
{
n
}
and requires
\texttt
{
board
}
to be a solution up to
\texttt
{
pos
}
:
\begin{verbatim}
{
length board = n and 0 <= pos <= n and solution board pos
}
label Init:
\end{verbatim}
We place a label
\texttt
{
Init
}
immediately after the precondition to
be able to refer to the value of
\texttt
{
board
}
in the pre-state.
Whenever we reach the end of the chess board, we have found a solution
and we signal it using exception
\texttt
{
Solution
}
:
\begin{verbatim}
if pos = n then raise Solution;
\end{verbatim}
Otherwise we scan all possible positions for the queen on row
\texttt
{
pos
}
with a
\texttt
{
for
}
loop:
\begin{verbatim}
for i = 0 to n - 1 do
\end{verbatim}
The loop invariant states that we have not modified the solution
prefix so far, and that we have not found any solution that would
extend this prefix with a queen on row
\texttt
{
pos
}
at a column below
\texttt
{
i
}
:
\begin{verbatim}
invariant
{
eq
_
board board (at board Init) pos and
forall b:array int. length b = n -> is
_
board b n ->
eq
_
board board b pos -> 0 <= b[pos] < i -> not (solution b n)
}
\end{verbatim}
Then we assign column
\texttt
{
i
}
to the queen on row
\texttt
{
pos
}
and
we check for a possible attack with
\verb
|
check_is_consistent
|
. If
not, we call
\verb
|
bt_queens
|
recursively on the next row.
\begin{verbatim}
board[pos] <- i;
if check
_
is
_
consistent board pos then bt
_
queens board n (pos + 1)
done
\end{verbatim}
This completes the loop and function
\verb
|
bt_queens
|
as well.
The postcondition is twofold: either the function exits normally and
then there is no solution extending the prefix in
\texttt
{
board
}
,
which has not been modified;
or the function raises
\texttt
{
Solution
}
and we have a solution in
\texttt
{
board
}
.
\begin{verbatim}
{
eq
_
board board (old board) pos and
forall b:array int. length b = n -> is
_
board b n ->
eq
_
board board b pos -> not (solution b n)
}
| Solution ->
{
solution board n
}
\end{verbatim}
Solving the puzzle is a simple call to
\verb
|
bt_queens
|
, starting the
search on row 0. The postcondition is also twofold, as for
\verb
|
bt_queens
|
, yet slightly simpler.
\begin{verbatim}
let queens (board: array int) (n: int) =
{
0 <= length board = n
}
bt
_
queens board n 0
{
forall b:array int. length b = n -> is
_
board b n -> not (solution b n)
}
| Solution ->
{
solution board n
}
\end{verbatim}
This second part of the solution is given Figure~
\ref
{
fig:NQueens2
}
.
With the help of a few auxiliary lemmas --- not given here but available
from
\why
's sources --- the verification conditions are all discharged
automatically, including the verification of the lemmas themselves.
\begin{figure}
\centering
\begin{verbatim}
logic is
_
board (board: array int) (pos: int) =
forall q:int. 0 <= q < pos -> 0 <= board[q] < length board
logic solution (board: array int) (pos: int) =
is
_
board board pos and
forall q:int. 0 <= q < pos -> is
_
consistent board q
logic eq
_
board (b1 b2: array int) (pos: int) =
forall q:int. 0 <= q < pos -> b1[q] = b2[q]
exception Solution
let rec bt
_
queens (board: array int) (n: int) (pos: int) variant
{
n - pos
}
=
{
length board = n and 0 <= pos <= n and solution board pos
}
label Init:
if pos = n then raise Solution;
for i = 0 to n - 1 do
invariant
{
eq
_
board board (at board Init) pos and
forall b:array int. length b = n -> is
_
board b n ->
eq
_
board board b pos -> 0 <= b[pos] < i -> not (solution b n)
}
board[pos] <- i;
if check
_
is
_
consistent board pos then bt
_
queens board n (pos + 1)
done
{
(* no solution *)
eq
_
board board (old board) pos and
forall b:array int. length b = n -> is
_
board b n ->
eq
_
board board b pos -> not (solution b n)
}
| Solution ->
{
(* a solution *)
solution board n
}
let queens (board: array int) (n: int) =
{
0 <= length board = n
}
bt
_
queens board n 0
{
forall b:array int. length b = n -> is
_
board b n -> not (solution b n)
}
| Solution ->
{
solution board n
}
end
\end{verbatim}
\vspace*
{
-2em
}
\hrulefill
\caption
{
Solution for VSTTE'10 competition problem 4.
}
\label
{
fig:NQueens
}
\caption
{
Solution for VSTTE'10 competition problem 4
(2/2)
.
}
\label
{
fig:NQueens
2
}
\end{figure}
\subsection
{
Problem 5: Amortized Queue
}
The last problem consists in verifying the implementation of a
...
...
examples/programs/vstte10_queens.mlw
View file @
a32e6807
...
...
@@ -49,7 +49,7 @@ module NQueens
exception Inconsistent int
let check_is_consistent (board: array int) pos =
let check_is_consistent (board: array int)
(
pos
: int)
=
{ 0 <= pos < length board }
try
for q = 0 to pos - 1 do
...
...
@@ -80,28 +80,27 @@ module NQueens
exception Solution
let rec bt_queens (board: array int)
n pos
variant { n - pos } =
let rec bt_queens (board: array int)
(n: int) (pos: int)
variant { n - pos } =
{ length board = n and 0 <= pos <= n and solution board pos }
label Init:
if pos = n then raise Solution;
for i = 0 to n - 1 do
invariant {
length board = n and
eq_board board (at board Init) pos and
eq_board board (at board Init) pos and
forall b:array int. length b = n -> is_board b n ->
eq_board board b pos -> 0 <= b[pos] < i -> not (solution b n) }
board[pos] <- i;
assert { eq_board board (at board Init) pos };
if check_is_consistent board pos then bt_queens board n (pos+1)
done
{ (* no solution *)
length board = n and
eq_board board (old board) pos and
eq_board board (old board) pos and
forall b:array int. length b = n -> is_board b n ->
eq_board board b pos -> not (solution b n) }
| Solution ->
{ (* a solution *)
solution board n }
let queens (board: array int)
n
=
let queens (board: array int)
(n: int)
=
{ 0 <= length board = n }
bt_queens board n 0
{ forall b:array int. length b = n -> is_board b n -> not (solution b n) }
...
...
examples/programs/vstte10_queens/why3session.xml
View file @
a32e6807
...
...
@@ -43,14 +43,14 @@
<result
status=
"valid"
time=
"0.60"
/>
</proof>
</goal>
<goal
name=
"WP_parameter bt_queens"
expl=
"correctness of parameter bt_queens"
sum=
"
b59f9ab1ac42b3755b8e06f7dc04fc2a
"
proved=
"true"
expanded=
"true"
>
<goal
name=
"WP_parameter bt_queens"
expl=
"correctness of parameter bt_queens"
sum=
"
90c2b563e0abbc3b4a70fec2410b7f1b
"
proved=
"true"
expanded=
"true"
>
<proof
prover=
"cvc3"
timelimit=
"20"
edited=
""
obsolete=
"false"
>
<result
status=
"valid"
time=
"0.
9
6"
/>
<result
status=
"valid"
time=
"0.
3
6"
/>
</proof>
</goal>
<goal
name=
"WP_parameter queens"
expl=
"correctness of parameter queens"
sum=
"
fa29a0090876c2c2eae4d8423017ff3c
"
proved=
"true"
expanded=
"true"
>
<goal
name=
"WP_parameter queens"
expl=
"correctness of parameter queens"
sum=
"
67746ab73fbf4bab85d11e7011676a38
"
proved=
"true"
expanded=
"true"
>
<proof
prover=
"alt-ergo"
timelimit=
"20"
edited=
""
obsolete=
"false"
>
<result
status=
"valid"
time=
"0.0
3
"
/>
<result
status=
"valid"
time=
"0.0
5
"
/>
</proof>
</goal>
</theory>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment