#|
Matrix transposition using alist representation.
|#
(include-book "data-structures/list-theory" :dir :system)
(include-book "data-structures/alist-theory" :dir :system)
;; Use deflist instead of doing a manual structural
;; recursion. deflist produces theorems that help
;; ACL2 rewrite nat*nat-listp when it interacts
;; with the other primitive list functions.
;; e.g., (nat*nat-listp (append X Y))
;; = (and (nat*nat-listp X)
;; (nat*nat-listp Y))
;; Recognize a list of pairs of natural numbers
(deflist nat*nat-listp (l)
(lambda (x)
(and (consp x)
(natp (car x))
(natp (cdr x)))))
;; Recognize a matrix.
(defun matrix? (x)
(and (alistp x)
(nat*nat-listp (domain x))))
;; Produce the entry in the matrix M at the given
;; row and column. Produces nil when row and col
;; are out of bounds.
(defun matrix-ref (M row col)
(if (endp M)
nil
(if (equal (caar M) (cons row col))
(cdar M)
(matrix-ref (cdr M) row col))))
;; Produce a matrix row whose entries are
;; consecutive numbers starting from `start'
(defun build-row (row cols start)
(if (zp cols)
nil
(acons (cons (1- row) (1- cols)) start
(build-row row (1- cols) (1+ start)))))
;; Produce a matrix of numbers with the given size
;; and whose entries are drawn from
;; (start, start+1, ..., start+(rows*cols)).
;; The matrices are (0,0)-indexed.
(defun build-matrix (rows cols start)
(if (zp rows)
nil
(append (build-row rows cols start)
(build-matrix (1- rows) cols (+ start cols)))))
#|
Let's pause here and try to prove that build-matrix really
does produce a matrix.
To do this, we must prove that build-matrix produces an
alist whose keys are pairs of natural numbers (why?).
If we inspect the definition of build-matrix, we see that
the items in the produced alist are actually coming from
build-row. build-matrix simply appends these rows togther
to form a larger alist.
That means that we must prove that build-row produces alists
whose keys are natural numbers.
(Note that this is a pattern: when we want to prove something
about a function that calls helper functions, we must
first prove theorems about the helper functions themselves.)
We have just discovered the theorems that we need:
(1) build-row produces alists
(2) these alists have keys that are pairs of natural numbers.
ACL2 can prove both of these easily.
|#
(defthm build-row-always-produces-an-alist
(alistp (build-row rows cols start)))
(defthm build-row-produces-natural-keys
(implies (posp rows)
(nat*nat-listp
(domain (build-row rows cols start)))))
#|
Often ACL2 needs to be told what kind of inputs a function
receives before it can predict anything about the output.
We were able to get away without doing so in the first of these
theorems, but we must specify that `rows' is a positive integer
in the second.
The game here is to specify just enough information for ACL2
to proceed, but to not overconstrain the system.
The fewer hypotheses that occur in your implications, the
easier it is for ACL2 to use the theorems in the ensuing
development.
|#
#| At this point, ACL2 can prove that build-matrix produces
a matrix. If you look at the end of the proof output
under `Summary', you'll see
...
(:REWRITE BUILD-ROW-ALWAYS-PRODUCES-AN-ALIST)
(:REWRITE BUILD-ROW-PRODUCES-NATURAL-KEYS)
...
This indicates that ACL2 has indeed used the lemmas we just
proved in order to prove build-matrix-produces-matrix.
|#
(defthm build-matrix-produces-matrix
(implies (and (posp rows) (posp cols) (integerp start))
(matrix? (build-matrix rows cols start))))
#|
Now we turn to matrix transposition. If M is a matrix,
then (transpose M) is a matrix whose rows are exactly the
columns of M. This operation is easily accomplished using
our representation of matrices:
|#
;; Consume (cons i j) and produce (cons j i)
(defun flip (pair)
(cons (cdr pair) (car pair)))
;; Consume a list of pairs and produce a new list
;; where each pair is flipped.
(defun flip-all (x)
(if (endp x)
nil
(cons (flip (car x)) (flip-all (cdr x)))))
;; Produce a matrix whose rows are just the columns
;; of the given matrix.
(defun transpose (x)
(let ((indices (domain x))
(elements (range x)))
(pairlis$ (flip-all indices) elements)))
#|
ACL2 can easily prove that transpose produces a matrix
when it is given a matrix. We're lucky here in that we
actually do not have to state any lemmas about flip-all
or flip.
Remember though that ACL2 often needs theorems about helper
functions before being able to cope with more complicated
functions that call the helpers.
|#
(defthm transpose-preserves-matrix?
(implies (matrix? x)
(matrix? (transpose x))))
#|
Here is the main result. It says that the element at
position (i,j) in M appears at position (j,i) in
(transpose M). ACL2 can prove this without the
prior theorem `transpose-preserves-matrix?'.
|#
(defthm transpose-is-correct-wrt-matrix-ref
(implies t ;(matrix? M)
(equal (matrix-ref (transpose M) i j)
(matrix-ref M j i))))
;;; EOF