BOUNDERS.html -- ACL2 Version 6.3

### BOUNDERS

intervals, bounder functions, and bounder correctness

    Major Section:  TAU-SYSTEM

    Bounder Forms 1 and 2:
    (implies (and (tau-intervalp i1)
                  ...
                  (or (equal (tau-interval-dom i1) 'dom1-1)
                      ...)
                  ...
                  (in-tau-intervalp x1 i1)
                  ...)
             (and (tau-intervalp (bounder-fn i1 ...))
                  (in-tau-intervalp target
                                    (bounder-fn i1 ...))))

where target is either `(fn x1 ... y1 ...)` or `(mv-nth 'n (fn x1 ... y1 ...))`, depending on whether we are in the Form 1 or Form 2 case, respectively. However, the shape above is meant just as a reminder. Details are given below.

This topic first explains the basic shape of Bounder Form 1. Then it illustrates Bounder Form 2. Finally, it deals briefly with proving bounder correctness theorems. The community book `tau-bounders/elementary-bounders` contains bounders for various elementary functions including `+`, `*`, `/`, `FLOOR`, `MOD`, `LOGAND`, `LOGNOT`, `LOGIOR`, `LOGORC1`, `LOGEQV`, `LOGXOR`, and `ASH`. You might look at or include this book to see more example theorems, to see how proofs of such theorems are managed, and to experiment with their effects on proving theorems involving arithmetic over finite or half-finite intervals.

A bounder correctness theorem establishes that `bounder-fn` is a ``bounder'' for the function `fn`. That means that when trying to compute a tau for a call of `fn` (or, in the case of Form 2, for the `n`th component of the multiple-value vector returned by a call of `fn`) the tau system can call `bounder-fn` on the intervals containing certain arguments of `fn`.

Let us start with an example. Let `fn` be the addition function, `+` (actually, `binary-+`). Consider the target term `(+ x y)` and contemplate the question: if you know intervals containing `x` and `y`, say `intx` and `inty` respectively, what is an interval containing their sum? The answer is pretty easy to state in English: the domain of the answer interval is the less restrictive of the domains of `intx` and `inty`. The lower bound of the answer interval is the sum of the lower bounds of `intx` and `inty`, and the lower relation is the stronger of the lower relations of `intx` and `inty`. Analogous comments define the upper bound and relation of the answer interval. So for example, if `x` is an `INTEGERP` such that `0 <= x <= 10` and `y` is a `RATIONALP` such that `0 < y <= 20`, then `(+ x y)` is a `RATIONALP` such that `0 < (+ x y) <= 30`.

Defining this precisely is more tedious than describing it in English because one must make precise the notions of ``less restrictive'' domains, ``weaker'' relations, and the possibility that either or both of the bounds could be ``infinite.'' But we can easily imagine defining the function `bounder-for-+` that returns the answer interval described, given `intx` and `inty`.

Then the following Bounder Form 1 formula establishes the correctness of `bounder-for-+` and allows the tau system to use it to produce bounds in the tau computed for `+`-expressions:

    (implies (and (tau-intervalp intx)
                  (tau-intervalp inty)
                  (in-tau-intervalp x intx)
                  (in-tau-intervalp y inty))
             (and (tau-intervalp (bounder-for-+ intx inty))
                  (in-tau-intervalp (+ x y)
                                    (bounder-for-+ intx inty))))

For example, suppose we have a formula with the following hypotheses

    (and (integerp a)
         (<= 0 a)
         (<= a 10)
         (rationalp b)
         (< 0 b)
         (<= b 20))

and suppose the tau system encounters the term `(+ a b)`. When the term is enountered, the tau for `a` would include an `INTEGERP` interval such that `0 <= a <= 10` and the tau for `b` would include a `RATIONALP` interval such that `0 < b <= 20`. In its most primitive configuration, the tau system would only know that the tau for `(+ a b)` includes the recognizer `RATIONALP` (and all that it is known to imply). But after the bounder theorem above is proved and available as a `:tau-system` rule the tau system would infer that `(+ a b)` was in the `RATIONALP` interval such that `0 < (+ a b) <= 30`.

Thus, by defining bounder functions and proving them correct the user can give the tau system the ability to compute the bounds on function calls as a function of the known bounds on their actuals.

It is sometimes useful to restrict the domains of the intervals to be considered. For example, in bounding `*`-expressions it is simplifying to restrict one's attention to intervals over the integers or rationals (and thus exclude the complex rationals so one need not think about the getting negative bounds by multiplying two ``positive'' complex rationals or how to ``round up'' from complex bounds to the rationals required by our intervals).

If we were to define `bounder-for-*` so that it works correctly to bound `*`-expressions, but only for integer or rational arguments, its correctness theorem would be:

    (implies (and (tau-intervalp intx)                             ; (a)
                  (tau-intervalp inty)
                  (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom intx) 'RATIONALP))
                  (or (equal (tau-interval-dom inty) 'INTEGERP)
                      (equal (tau-interval-dom inty) 'RATIONALP))
                  (in-tau-intervalp x intx)                        ; (c)
                  (in-tau-intervalp y inty))
             (and (tau-intervalp (bounder-for-* intx inty))       ; (d)
                  (in-tau-intervalp (* x y)                        ; (e)
                                    (bounder-for-* intx inty))))

In this case, `bounder-for-*` would be applied to the intervals for `x` and `y` only if those intervals were over the integers or the rationals.

The above theorem for `bounder-for-*` begins to suggest the general form of a bounder theorem and we will use it to explain the general form.

The hypotheses of a bounder theorem must be a conjunction and the conjuncts must be partitionable into three parts, (a), (b), and (c). The conclusion, must be a conjunction, must contain at least two conjuncts, (d) and (e), and is allowed to contain others that are simply ignored for purposes of bounders. (See the note below about why we allow but ignore additional conjuncts in the conclusion.)

Part (a) introduces some distinct ``interval variables,'' here called ``ivars,'' that are known to denote intervals; for the example above, the ivars are `intx` and `inty`. Each hypothesis in part (a) is of the form `(TAU-INTERVALP ivar)`.

Part (b) allows us to restrict the domains of some of the intervals. Each hypothesis in part (b) must be a disjunction and each of the disjuncts must be of the form `(EQUAL (TAU-INTERVAL-DOM ivar) 'dom)`, where `ivar` is one of the interval variables and `dom` is one of `INTEGERP`, `RATIONALP`, `ACL2-NUMBERP`, or `NIL`. It is not necessary to restrict every interval variable. Indeed, part (b) may be empty, as in the theorem for `bounder-for-+` above.

Part (c) consists of a set of `(IN-TAU-INTERVALP avar ivar)` hypotheses where each `avar` is a variable and no two hypotheses in part (c) use the same `avar` or `ivar`. We call the set of all such `avar` the ``actual variables'' or ``avars.'' The avars and ivars must be distinct. Part (c) sets up a correspondence between the avars and the ivars, each avar is in an interval denoted by one ivar.

Part (d) introduces the name of the bounder function, here `bounder-for-*`, and the order of its ivar arguments. We see that `bounder-for-*` takes two arguments and they correspond, in order, to the intervals containing `x` and `y`. Part (d) also establishes that the bounder function always returns an interval under hypotheses (a), (b), and (c). Note that it is sometimes useful to return the ``universal interval'' (one that contains everything) if you don't want to compute a better interval for some case; see `tau-intervalp` or `in-tau-intervalp`.

Part (e) introduces the name of the function being bounded, here `*`, and the order of its arguments. It establishes that the function being bounded really is bounded by the interval computed by the bounder function. In general, the function being bounded may take additional arguments. It is possible that the function being bounded takes some arguments that do not affect the bounds of its output.

Thus, parts (c) and (e) together establish a mapping between the actuals of a call of the function being bounded and the intervals to be supplied to the bounder.

The parts identified above may be presented in any order and the literals constituting those parts may be mingled. Thus, for example, here is another version of the theorem above that generates the same bounding information for the tau system. In this version, the hypotheses and conclusions are rearranged, `bounder-for-*` takes its arguments in the opposite order, and the theorem includes an additional conclusion.

    (implies (and (tau-intervalp intx)                             ; (a)
                  (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom intx) 'RATIONALP))
                  (in-tau-intervalp x intx)                        ; (c)

                  (tau-intervalp inty)                             ; (a)
                  (or (equal (tau-interval-dom inty) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom inty) 'RATIONALP))
                  (in-tau-intervalp y inty))
             (and (in-tau-intervalp (* x y)                        ; (e)
                                    (bounder-for-* inty intx))
                  (tau-intervalp (bounder-for-* inty intx))        ; (d)))

                  (or (equal (tau-interval-dom (bounder-for-* inty intx))
                             'INTEGERP)
                      (equal (tau-interval-dom (bounder-for-* inty intx))
                             'RATIONALP))

Note on why bounder forms allow additional conjuncts in the conclusion: It is often the case that one creates bounders by composing other bounders. To prove compositional bounds correct one must often prove more than the mere correctness of the components. For example, one might need to prove that the domain of the new bounding interval is `INTEGERP` or otherwise restricted. We allow such ``unnecessary'' conclusions simply to save the user the burden of stating multiple theorems.

An Illustration of Bounder Form 2: Suppose `(quad i)` is defined so that truncates the integer `i` to the largest multiple of 4 weakly below `i` and, additionally, returns the remainder. For example, `(quad 26)` returns `(mv 24 2)`. Then here are bounders for each of its return values:

    (defun quad-bounds-0 (i)
      (cond ((and (tau-interval-lo i)
                  (<= 0 (tau-interval-lo i)))
             (make-tau-interval 'integerp nil 0 nil (tau-interval-hi i)))
            (t (make-tau-interval nil nil nil nil nil))))

    (defun quad-bounds-1 (i)
      (cond ((and (tau-interval-lo i)
                  (<= 0 (tau-interval-lo i)))
             (make-tau-interval 'integerp nil 0 nil 3))
            (t (make-tau-interval nil nil nil nil nil))))

Note that the bounders assume `i` is an `INTEGERP` and return the universal interval when `i` is not a natural.

As noted in the discussion below about how to prove bounder correctness theorems, proving these bounders correct will require an arithmetic book, e.g.,

    (include-book "arithmetic-5/top" :dir :system)

Here then are two bounder correctness theorems of Form 2:

    (defthm quad-bounds-0-correct
      (implies (and (tau-intervalp i)
                    (equal (tau-interval-dom i) 'INTEGERP)
                    (in-tau-intervalp x i))
               (and (tau-intervalp (quad-bounds-0 i))
                    (in-tau-intervalp (mv-nth 0 (quad x))
                                      (quad-bounds-0 i))))
      :rule-classes :tau-system)

    (defthm quad-bounds-1-correct
      (implies (and (tau-intervalp i)
                    (equal (tau-interval-dom i) 'INTEGERP)
                    (in-tau-intervalp x i))
               (and (tau-intervalp (quad-bounds-1 i))
                    (in-tau-intervalp (mv-nth 1 (quad x)) (quad-bounds-1 i))))
      :rule-classes :tau-system)

As noted above, if these bounders are to be used in constructing other bounders, we might include (in the first theorem) an additional concluding conjunct, such as

    (equal (tau-interval-dom (quad-bounds-0 i)) 'INTEGERP)

so that we can keep `quad-bounds-0` disabled to allow us to use `quad-bounds-0-correct` as a `:rewrite` or other rule and still relieve hypotheses about the domain of the interval it produces. These hypotheses would arise if some other verified bounder was called on the produced interval. In addition, as noted below, we might replace the `:rule-classes` above with

    :rule-classes
     ((:rewrite)
      (:forward-chaining :trigger-terms ((quad-bounds-0 i))))

Since the theorem is being stored as some kind of rule and since it satisfies the Bounder Form 2 shape, it will additionally be stored as a `:tau-system` rule.

Note on proving bounder theorems: Proving bounder theorems is just like proving any other arithmetic theorem and you will need whatever libraries are appropriate for the problem domain you are working in. Do not expect the tau system to be of much use in proving bounder theorems. A typical bounder theorem might require you to prove a subgoal like `(< (fn x y) (g (tau-interval-hi int1) int2))`. But tau deals with inequalities relating terms to constants, e.g., `(< ... 16)`. A bounder theorem is a sort of ``metatheorem'' about how to construct bounded intervals from other bounded intervals. So when you undertake to define a bounder and prove it correct, go into the project with your eyes open!

But bounder functions can be broadly divided into two classes, those defined in terms of arithmetic on the interval bounds and those defined in terms of other bounders. For example, given that

    (LOGXOR x y) = (LOGNOT (LOGEQV x y))

an interval for bounding `LOGXOR` can be constructed by composing the constructions of intervals for `LOGEQV` and `LOGNOT`. So some bounder correctness proofs will involve direct manipulation of arithmetic inequalities and others might involve appeal to the correctness of other bounders, depending on how the new bounder is defined.

Regardless of which style of bounder we are dealing with, we have found it useful to prove the basic theorems relating the tau interval accessors to `MAKE-TAU-INTERVAL`, e.g.,

    (equal (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi)) dom)

and then disable those functions to avoid seeing excessive `car`s and `cdr`s.

When dealing with bounders defined in the direct, arithmetic style, we tend to keep `TAU-INTERVALP` and `IN-TAU-INTERVALP` enabled so they unfold and expose the algebra.

When dealing with bounders defined compositionally in terms of other verified bounders, we tend to keep `TAU-INTERVALP` and `IN-TAU-INTERVALP` disabled so we can rely on the previously proved bounder theorems as rewrite and forward chaining rules.

Note that this last remark means that when you prove bounder correctness theorems you should include corollaries that are useful `:rewrite` and possibly `:forward-chaining` rules if you anticipate using that bounder in more complex ones. We tend to trigger the forward chaining with the bounder expression itself, rather than one of the hypotheses. For example in the rule above for `bounder-for-*` we would include `(:forward-chaining :trigger-terms ((tau-bounder-expt2 int2)))` and let the `in-tau-intervalp` hypotheses select the free variables `x` and `y`.