/usr/share/acl2-8.0dfsg/books/make-event/defspec.lisp is in acl2-books-source 8.0dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 | ; Copyright (C) 2013, Regents of the University of Texas
; Written by Sandip Ray and Matt Kaufmann, October, 2006
; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
(in-package "ACL2")
#|
defspec.lisp
~~~~~~~~~~~~
Authors: Sandip Ray and Matt Kaufmann
Date: Wed Oct 18 01:01:24 2006
[ Caveat: This book is experimental and might undergo some modfications soon. ]
[Incorporates some suggestions and advice by J Strother Moore.]
[ Uninteresting Aside: Parenthesis in many places at the prefatory comments
have been replaced by square brackets to facilitate syntax highlighting in
emacs.]
The Problem
-----------
Suppose you have created an encapsulation with a collection of constraints.
The encapsulation might model some system or some policy or some property in an
abstract fashion. Suppose you then want to refine this model, possibly by
introducing a collection of concrete definitions. You might then want to
insure that the concrete model is indeed an instance of the abstract one, in
that the concrete definitions satisfy the constraints posited by the abstract
one. How do you provide a formal answer to this question in ACL2? Functional
instantiation provides only a partial answer, in that if we had proven a
*property* of the abstract model then we could functionally instantiate this
abstract property to derive the corresponding property for the concrete one.
But there are situations in which we do not have a specific property that we
want to funtionally instantiate, but rather just want to say in some formal way
that "all the abstract constraints are satisfied by the concrete functions".
An example for that might be a security policy implemented by a system. One
wants to model the security policy in as abstract a manner as possible so that
the security evaluators can check the policy and satisfy themselves that it is
indeed a valid security policy. Then one might provide a definition of a
concrete system and want to say that the system implements the security policy
specified. Clearly there is no *theorem* here to be functionally instantiated.
Of course one can prove the concrete versions of the constraints separately and
get done with it. But a concrete model might be very complicated and given
such a concrete theorem (in contrast with an abstract specification via
encapsulation) it is a substantial effort for an evaluator to realize that it
indeed does implement the security policy desired. Some formal way of showing
the correspondence is clearly better.
The real problem is that ACL2 is a theorem prover for first order logic, not
higher-order logic, while the statement we want to make is inherently a
higher-order statement. In higher order logic we could have, for instance,
defined the security policy as a closed form formula parameterized with
[abstract] Functions, and then the notion of a concrete model satisfying the
policy could have been written as an instance of the same formula with the
concrete functions. But in first-order logic this is not possible. Although
functional instantiation gives the illusion of higher-order reasoning, it is
after all a derived rule of inference.
The Approach
-------------
Since the notion "concrete functions implement the abstract ones" is a
higher-order notion, we cannot hope to write that notion as a closed-form
formula in ACL2. We must therefore be content with "merely" providing an
illusion of this higher-order notion via macros, and proving all the relevant
first-order proof obligations. The first order obligations, of course, are
just that the concrete versions of the constraints are theorems. In an
appropriate higher-order logic, these would be sufficient to prove the final
closed-form (higher-order) theorems. Here we merely simulate the notion of the
closed-form theorem with macros.
Our approach consists of two parts, each supported by a macro. In the first
part, we introduce the notion of an abstract system satisfying certain
constraints. This is done by the macro defspec. The macro defspec expands
into an encapsulate with the appropriate constraining of the function symbols
involved. It then creates a table event with a table to store the constraints
together with a name. We think of the name as the higher-order predicate which
has the constrained functions as parameters and the constraints on the
functions specify when this (parametermized) higher-order predicate is true.
The second part is to justify that the instances of the constraints defined for
the abstract model. We do this by essentially creating the theorem that is
produced by replacing terms composed of application of abstract functions by
their concrete counterparts in the constraints generated by the defspec. This
macro, which is called definstance, simply uses the built-in ACL2 function for
building functional instance to create the instance of the constraints and
generate a theorem which is then presented to ACL2. The macro takes the name
of the key the table event introduced by defspec and a functional substitution
to generate this theorem. We can view this macro as producing essentially an
instance of the higher-order predicate with the concrete versions of the
function parameters.
One might argue that this is nowhere near the closed-form formula that we want
to ideally state, and the author agrees. After all macros are syntactic sugar.
But having one macro that generates the instances will give the evaluators the
ability to trust it, rather than hand-coded proofs that all the "corresponding"
constraints are satisfied. The hope is that with a lot of use the macros here
will be conventionally thought of as higher-order representations. There is
precedence of such. For instance, ACL2 is a quantifier-free logic, and
defun-sk is just a macro (that is, syntactic sugar). However, most ACL2 users
who use defun-sk (including the author) tend to think of a predicate introduced
by this macro as a quantified formula. This argument is not intended to
justify the weakness of the logic, but merely to show that there is
precedent for such conventions and with sufficient use conventions become
practice.
Notes
-----
1. There is really no need to introduce the table event and defspec, at least
technically. The macro definstance could have taken a functional substitution
and one of the function names constrained and generated the constraints that
were stored in the table. I prefer the table only because I can then provide a
name. The name enables us to think, at least informally, of defspec as a
higher-order function.
2. Technically, there is no need for the encapsulate. All that matters is
that we generate the term in the table event. I generate the encapsulate more
to help the evaluator in checking that the abstract functions do mean
something. This of course precludes adding constraints as defaxioms. But I
consider this a good rather than a bad thing, because it provides a consistency
guarantee at the time a defspec is admitted.
3. There is one little wrinkle in that some include-book event (or something
else) occurring between the defspec and definstance events overwrites the key
in the table. I can't handle that with a guard on the table event that only
talks about the key. The work-around, suggested by Matt Kaufmann, is to
introduce a deflabel event with the same name as the key and put the
[non-]existence of deflabel as the table guard. Since the deflabel event will
be subsequently present, the key cannot be overwritten.
TODOS
-----
[These were raised by Matt Kaufmann in emails dated: Wed, 18 Oct 2006 08:06:03]
1. Address issues due to variable capture.
[ Relevant part of Matt's email:
The comment on sublis-fn (in history-management.lisp) says:
It is assumed that alist will not allow capturing of its free
variables by lambda expressions in term.
It goes on to say that a "draconion check" ensures this. So I wonder
if you need a similar draconian check. ]
Note on this:
Kaufmann has extended the built-in system function sublis-fn to incorporate the
check in there [and thereby got rid of the "draconican check" mentioned above.
With the new sublis-fn, we do not need further checks; we can just use
sublis-fn.
2. Add error-checking.
[ Relevant part of Matt's email:
I notice that you didn't do any error checking for the
functional substitution in definstance. The ACL2 source function
translate-functional-substitution might be of use here. Maybe you
wouldn't then need create-alist-from-func-list, but I haven't thought
this through. ]
3. Think about implicit constraints.
[ Relevant part of Matt's email:
The constraints introduced by an encapsulate aren't necessarily
what the user might expect, because definitions of functions not
in the signature might or might not contribute to the constraint. ]
|#
;; We first create the table event. The second conjunct of the guard, together
;; with the fact that defspec generates a deflabel event insures that the key
;; cannot be subsequently overwritten.
(table spec-table nil nil
:guard
(and (symbolp key)
(not (getprop key 'label nil 'current-acl2-world world))))
(defun constraint (fn wrld)
(declare (xargs :mode :program))
(mv-let (sym x)
(constraint-info fn wrld)
(cond
((eq x *unknown-constraints*)
(er hard 'constraint
"Unable to determine constraints on ~x0! Presumably this ~
function was introduced with ~
define-trusted-clause-processor; see :DOC ~
define-trusted-clause-processor."))
(sym (conjoin x))
(t x))))
(defmacro defspec (name &rest args)
`(progn
;; First we want to lay down the encapsulate.
(encapsulate ,@args)
;; Then we look at the world via make-event to create the appropriate
;; table-event. Note that we are assuming that the form we are given is
;; something that physically looks like an encapsulate, with defspec name
;; instead of an encapulate. We do not, for instance, handle something
;; that is a macro but expands into the right form. The reason is that I
;; do not want to get into the business of macro expansion. Of course I
;; guess I could just have looked at the world, using scan-to-event, to
;; figure out the actual event that was just introduced. But I don't
;; bother.
(make-event
(let* ((signatures (quote ,(first args)))
(sig (first signatures))
;; The reason for the if statement here is that there are two
;; flavors of signature, for instance (f (x) t) and ((f *) =>
;; *). I do not do further error-checking here since the
;; encapsulate presumably had passed the ACL2 check. The farg is
;; the first function symbol that has been introduced during the
;; encapsulation. I just care about that since the constraints
;; are all associated with this function.
(farg (if (consp (first sig))
(first (first sig))
(first sig)))
;; I am collecting the constraint generated from the
;; encapsulation, which can be thought of as the formula
;; representing the higher-order notion of formula parameterized
;; by the functions in the signature.
(val-term (constraint farg (w state)))
(name (quote ,name)))
;; Now generate the final event.
`(progn
(table spec-table (quote ,name) (quote ,val-term))
;; I generated the table event but I want to insure that this entry
;; will remain in this table. I do so by now geneating a deflabel
;; with the key. Notice that the table-guard for the table says
;; that the key is not one of the labels, which guarantees that
;; other people in some include-book for instance cannot overwrite
;; this entry.
(deflabel ,name))))))
;; This function is not necessary. I write it just so that I can write a
;; functional substitution as a list ((f0 g0) (f1 g1)) rather than ((f0 . g0)
;; (f1. g1)). Nothing more than aesthetics here.
(defun create-alist-from-func-list (lst)
(if (endp lst) nil
(acons (first (first lst))
(second (first lst))
(create-alist-from-func-list (rest lst)))))
;; Now we write the macro definstance to instantiate the abstract functions
;; introduced via defspec to the concrete functions. To do so I create a
;; functional substitution of the constraints and then prove that as a
;; theorem. The functional substitution is implemented with the same functions
;; that are used to compute substitution for functional instance in ACL2.
(defmacro definstance
(spec-name thm-name
&key
(functional-substitution 'nil)
instructions hints otf-flg rule-classes doc)
`(make-event
(mv-let
;; I should really start using er-let*. I like the mv-let form since it
;; makes the arguments explicit.
(erp term state)
;; So I get the constraint term from the table.
(table spec-table (quote ,spec-name))
(assert$
(null erp)
(let*
(
;; First create the alist as necessary for this function. Going from
;; aesthetic to practical.
(substitution
(create-alist-from-func-list (quote ,functional-substitution)))
;; Use the theorem name for creating the defthm.
(thm-name (quote ,thm-name))
;; The rest of the keywords are all arguments to defthm.
(hints (quote ,hints))
(instructions (quote ,instructions))
(doc ,doc)
(rule-classes ,rule-classes)
(otf-flg ,otf-flg)
;; Here is where I create the appropriate form.
(thm-form
(mv-let (erp thm)
(sublis-fn substitution term nil)
(declare (ignore erp))
thm)))
; Matt K. mod: :doc is no longer supported for defthm after v7-1:
(declare (ignore doc))
(value
;; And finally I lay out the defthm event. Notice that the default is
;; rule-classes nil, and the reason is that I didn't see the
;; constraints as particularly good rewrite rules in the first place.
`(defthm ,thm-name
,thm-form
:hints ,hints
:instructions ,instructions
; Matt K. mod: :doc is no longer supported for defthm after v7-1
;; :doc ,doc
:rule-classes ,rule-classes
:otf-flg ,otf-flg)))))))
;; Now we test the macros. Since I want to include both success and failure I
;; use the must-succeed and must-fail macros.
(local
(include-book "misc/eval" :dir :system))
;; All the tests are marked local. I do it this way rather than in comments
;; since I want to make sure that the tests execute when I certify the book,
;; but I don't want to have them clutter things when I include the books. I
;; also put the different tests in different encapsulates (with all events
;; local) so that I can do independent testing.
(local
(must-succeed
(local
(encapsulate
()
(local
(defspec foo
(((f *) => *))
(local (defun f (x) x))
(defthm foo-identity (equal (f x) x))))
(local (defun g (x) x))
(local
(definstance foo g-is-identity
:functional-substitution
((f (lambda (x) (g x))))))))))
;; The following succeeds, surprisingly. Should it fail? I think the current
;; behavior is actually correct. Here we introduce an "empty" encapsulate, and
;; then instantiate it with an arbitrary concrete function. The instantiation
;; succeeds since the empty model is implemented by every definition.
(local
(must-succeed
(local
(encapsulate
()
(local
(defspec bar () (local (defun f (x) x))))
(local (defun g (x) (cons x x)))
(local
(definstance bar g-is-arbitrary
:functional-substitution
((f (lambda (x) (g x))))))))))
;; This one fails since the constraints don't hold.
(local
(must-fail ; See note about ld-skip-proofsp in the definition of must-fail.
(local
(encapsulate
()
(local
(defspec baz
(((f *) => *))
(local (defun f (x) x))
(defthm f-is-identity (equal (f x) x))))
(local (defun g (x) (cons x x)))
(local
(definstance baz g-is-identity
:functional-substitution
((f (lambda (x) (g x))))))))))
;; Now we do a sequence of tests to justify the security of the table event
;; with deflabel.
;; This one fails since we cannot introduce two defspecs with the same h. The
;; first one lays down a deflabel and the next one cannot succeed because of
;; the presence of the deflabel.
(local
(must-fail ; See note about ld-skip-proofsp in the definition of must-fail.
(local
(encapsulate
()
(local
(defspec h
(((f *) => *))
(local (defun f (x) x))
(defthm f-is-identity (equal (f x) x))))
(local
(defspec h
(((g *) => *))
(local (defun g (x) x))
(defthm g-is-identity (equal (g x) x))))))))
;; Now we try to manually modify the table spec-table. This fails because of
;; the table-guard.
(local
(must-fail ; See note about ld-skip-proofsp in the definition of must-fail.
(local
(encapsulate
()
(local
(defspec i
(((f *) => *))
(local (defun f (x) x))
(defthm f-is-identity (equal (f x) x))))
(table spec-table 'i 10)))))
; Added by Matt K. This example responds to an observation from Bob Boyer,
; that it would be nice for there to be a general method of specifying notions
; like "bijection" to avoid errors in an ad hoc attempt to specify that a
; particular function is a bijection. I don't claim that this example is a
; anything like a complete solution to that problem -- in particular, it
; specifies bijection only for a function mapping pairs from a given domain --
; but I hope it suggests how one might develop a library of such "higher-order"
; specifications.
(local (progn
; Specify that a function bij2: dom2 x dom2 -> ran2 is a bijection.
(defspec bijection2
(((bij2 * *) => *)
((inv1 *) => *)
((inv2 *) => *)
((dom2 *) => *)
((ran2 *) => *))
(local (defun bij2 (x y) (cons x y)))
(local (defun inv1 (x) (car x)))
(local (defun inv2 (x) (cdr x)))
(local (defun dom2 (x) (declare (ignore x)) t))
(local (defun ran2 (x) (consp x)))
(defthm ran2-bij2
(implies (and (dom2 x1)
(dom2 x2))
(ran2 (bij2 x1 x2))))
(defthm dom2-inv1
(implies (ran2 x)
(dom2 (inv1 x))))
(defthm dom2-inv2
(implies (ran2 x)
(dom2 (inv2 x))))
(defthm bij2-is-injective
(implies (and (dom2 x1)
(dom2 x2)
(dom2 y1)
(dom2 y2)
(equal (bij2 x1 x2)
(bij2 y1 y2)))
(and (equal x1 y1)
(equal x2 y2)))
:rule-classes nil)
(defthm bij2-is-surjective
(implies (ran2 x)
(equal (bij2 (inv1 x) (inv2 x))
x))))
; Include Harsh's revised book.
(include-book "system/cantor-pairing-bijective" :dir :system)
; Separate out three key lemmas.
(defthm lemma1
(implies (and (natp x1)
(natp x2)
(natp y1)
(natp y2)
(equal (cantor-pairing x1 x2)
(cantor-pairing y1 y2)))
(equal x1 y1))
:hints (("Goal"
:use ((:instance cantor-pairing-one-one
(a1 x1) (b1 x2) (a2 y1) (b2 y2)))))
:rule-classes nil)
(defthm lemma2
(implies (and (natp x1)
(natp x2)
(natp y1)
(natp y2)
(equal (cantor-pairing x1 x2)
(cantor-pairing y1 y2)))
(equal x2 y2))
:hints (("Goal"
:use ((:instance cantor-pairing-one-one
(a1 x1) (b1 x2) (a2 y1) (b2 y2)))))
:rule-classes nil)
(defthm lemma3
(implies (natp x)
(equal (cantor-pairing (car (cantor-pairing-inverse x))
(cadr (cantor-pairing-inverse x)))
x))
:hints (("Goal"
:use ((:instance cantor-pairing-onto
(n x)))))
:rule-classes nil)
; Prove that cantor-pairing is a bijection.
(definstance bijection2 cantor-pairing-is-bijection
:functional-substitution
((bij2 cantor-pairing)
(inv1 (lambda (x) (car (cantor-pairing-inverse x))))
(inv2 (lambda (x) (cadr (cantor-pairing-inverse x))))
(dom2 natp)
(ran2 natp))
:hints (("Goal" :use (lemma1 lemma2 lemma3))))
; Finally, just for fun, show what was actually proved above. I would like to
; use (set-enforce-redundancy t) here. But unfortunately, one cannot use
; set-enforce-redundancy in a local context.
(defthm cantor-pairing-is-bijection
(and (implies (and (natp x1) (natp x2))
(natp (cantor-pairing x1 x2)))
(implies (natp x)
(natp (car (cantor-pairing-inverse x))))
(implies (natp x)
(natp (cadr (cantor-pairing-inverse x))))
(implies (and (natp x1)
(natp x2)
(natp y1)
(natp y2)
(equal (cantor-pairing x1 x2)
(cantor-pairing y1 y2)))
(and (equal x1 y1) (equal x2 y2)))
(implies (natp x)
(equal (cantor-pairing (car (cantor-pairing-inverse x))
(cadr (cantor-pairing-inverse x)))
x)))
:rule-classes nil)
))
|