Version: 4.1.4
2.4 "doublecheck"
(include-book "doublecheck" :dir :teachpacks) |
This teachpack defines automated random test generation as a complement to
theorem proving. Each property declared via DoubleCheck is tested randomly by
Dracula, and verified logically by ACL2. For a gentle introduction, see
Doublecheck in
Dracula: A Guide to ACL2 in DrScheme.
2.4.1 Properties
(defproperty name property-option ... (bind ...) test theorem-option ...) | | property-option | | = | | :repeat repeat-expr | | | | | | :limit limit-expr | | | | | | bind | | = | | var bind-option ... | | | | | | bind-option | | = | | :where hypothesis-expr | | | | | | :value random-expr | | | | | | :limit limit-expr | | | | | | theorem-option | | = | | :rule-classes rule-classes | | | | | | :instructions instructions | | | | | | :hints hints | | | | | | :otf-flg otf-flg | | | | | | :doc doc-string |
|
|
|
Use defproperty to define DoubleCheck properties in a file, then call
check-properties at the end to display the results. This opens a GUI
displaying results, including success, failure, and the values of randomly
chosen variables.
Each defproperty form defines a property called name which
states that test must be true for all assignments to each var
in the bindings. DoubleCheck attempts to run the test repeat times
(default 50), but limits the attempts to generate satisfactory random data to
limit times (default 2500).
Dracula generates values for each var by running random-expr,
which defaults to (random-sexp). The var is generated until
hypothesis-expr evaluates to true (non-nil), or limit
attempts have been made (defaulting to the property’s limit, defaulting
to 50 as noted above).
ACL2 evaluates each defproperty as a theorem (defthm)
equivalent to:
(defthm name (implies (and hypothesis-expr ...) test) theorem-option ...)
Here are some examples to illustrate the translation from theorems to
DoubleCheck properties.
Example 1:
(include-book "doublecheck" :dir :teachpacks) |
|
|
(defthm acl2-count-cons-theorem |
(> (acl2-count (cons a b)) (+ (acl2-count a) (acl2-count b)))) |
|
defproperty |
(defproperty acl2-count-cons-property |
(a b) |
(> (acl2-count (cons a b)) (+ (acl2-count a) (acl2-count b)))) |
|
(check-properties) |
Example 2:
(include-book "doublecheck" :dir :teachpacks) |
|
|
(defthm rationalp-/-theorem |
(implies (and (integerp x) (posp y)) |
(rationalp (/ x y)))) |
|
:where |
(defproperty rationalp-/-property |
(x :where (integerp x) |
y :where (posp y)) |
(rationalp (/ x y))) |
|
(check-properties) |
Example 3:
(include-book "doublecheck" :dir :teachpacks) |
|
|
(defthm member-equal-nth-theorem |
(implies (and (proper-consp lst) |
(natp idx) |
(< idx (len lst))) |
(member-equal (nth idx lst) lst)) |
:rule-classes (:rewrite :forward-chaining)) |
|
|
|
(defproperty member-equal-nth-property |
(lst :where (proper-consp lst) |
:value (random-list-of (random-sexp)) |
idx :where (and (natp idx) (< idx (len lst))) |
:value (random-between 0 (1- (len lst)))) |
(member-equal (nth idx lst) lst) |
:rule-classes (:rewrite :forward-chaining)) |
|
(check-properties) |
2.4.2 Random Distributions
Randomness is an inherently imperative process. As such, it is not reflected in
the logic of ACL2. The random distribution functions of DoubleCheck may only be
used within :value clauses of defproperty, or in other random
distributions.
|
|
(random-boolean) → booleanp |
|
(random-symbol) → symbolp |
|
(random-char) → characterp |
|
(random-string) → stringp |
|
(random-number) → acl2-numberp |
|
(random-rational) → rationalp |
|
(random-integer) → integerp |
|
|
These distributions produce random elements of the builtin Dracula types. When
no distribution is given for a property binding, defproperty uses
random-sexp by default.
(random-between lo hi) → integerp |
lo : integerp |
hi : integerp |
Produces an integer uniformly distributed between lo and hi,
inclusive; lo must be less than or equal to hi.
(random-data-size) → natp |
Produces a natural number weighted to prefer small numbers, appropriate for
limiting the size of randomly produced values. This is the default distribution
for the length of random lists and the size of random s-expressions.
(random-element-of lst) → t |
lst : proper-consp |
Chooses among the elements of lst, distributed uniformly.
(random-list-of expr maybe-size) |
|
maybe-size | | = | | | | | | | | :size size |
|
Constructs a random list of length size (default
(random-data-size)), each of whose elements is the result of evaluating
expr.
(random-sexp-of expr maybe-size) |
|
maybe-size | | = | | | | | | | | :size size |
|
Constructs a random cons-tree with size total
cons-pairs (default (random-data-size)), each of whose leaves
is the result of evaluating expr.
(defrandom name (arg ...) body) |
The defrandom form defines new random distributions. It takes the same
form as defun, but the body may refer to other random distributions.
Example:
|
(defrandom random-alist (len) |
(random-list-of (cons (random-atom) (random-sexp)) :size len)) |
|
|
(defproperty acons-preserves-alistp |
(alist :where (alistp alist) :value (random-alist (random-between 0 10)) |
key :where (atom key) :value (random-atom) |
datum) |
(alistp (acons key datum alist))) |
(random-case clause ...) |
|
clause | | = | | expr | | | | | | expr :weight weight |
|
Chooses an expression from the clauses, each with the associated weight
(defaulting to 1), and yields its result; the other expressions are not
evaluated. This is useful with defrandom for defining recursive
distributions.
Be careful of the branching factor; a distribution with a high probability of
unbounded recursion is often unlikely to terminate. It is useful to give a
depth parameter to limit recursion.
Example:
|
(defrandom random-expression (max-depth) |
(random-case |
(random-symbol) |
(random-string) |
(random-number) |
|
|
(list (random-symbol) (random-expression (1- max-depth))) |
:weight (- 1 (/ 1 max-depth)))) |