/usr/share/acl2-6.3/memoize.lisp is in acl2-source 6.3-5.
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 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 | ; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp
; Copyright (C) 2013, Regents of the University of Texas
; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright
; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0.
; This program is free software; you can redistribute it and/or modify
; it under the terms of the LICENSE file distributed with ACL2.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; LICENSE for more details.
; Written by: Matt Kaufmann and J Strother Moore
; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu
; Department of Computer Science
; University of Texas at Austin
; Austin, TX 78701 U.S.A.
; memoize.lisp -- Logical definitions for memoization functions, only
; to be included in the experimental HONS version of ACL2.
; The original version of this file was contributed by Bob Boyer and
; Warren A. Hunt, Jr. The design of this system of Hash CONS,
; function memoization, and fast association lists (applicative hash
; tables) was initially implemented by Boyer and Hunt.
(in-package "ACL2")
(defmacro defn (f a &rest r)
":Doc-Section Events
definition with ~il[guard] ~c[t]~/
~c[Defn] is ~ilc[defun] with ~il[guard] ~c[t].~/~/"
`(defun ,f ,a (declare (xargs :guard t)) ,@r))
(defmacro defnd (f a &rest r)
":Doc-Section Events
~il[disable]d definition with ~il[guard] ~c[t]~/
~c[Defnd] is ~ilc[defund] with ~il[guard] ~c[t].~/~/"
`(defund ,f ,a (declare (xargs :guard t)) ,@r))
(defdoc hons-and-memoization
":Doc-Section Hons-and-Memoization
hash cons, function memoization, and applicative hash tables~/
Bob Boyer and Warren Hunt, and later Jared Davis and Sol Swords, have
developed a canonical representation for ACL2 data objects and a function
memoization mechanism to facilitate reuse of previously computed results.
This facility includes procedures to read and print ACL2 expressions in such
a way that repetition of some ACL2 objects is eliminated, thereby permitting
a kind of on-the-fly file compression. The implementation does not alter the
semantics of ACL2 except to add a handful of definitions.
We give the name ``ACL2(h)'' to the resulting experimental extension of the
ACL2 system, which includes hash cons, function memoization, and fast
association lists (applicative hash tables). It is optimized for Clozure
Common Lisp (CCL), but other ANSI-compliant host Lisp implementations may
also work, provided there are sufficient machine resources.
If you want to use ACL2(h), you might find it helpful to consult the document
~c[centaur/README.html] in the ACL2 community books.
An easy way is to build ACL2(h) is to include the following with a `make'
command:
~bv[]
ACL2_HONS=h
~ev[]
So for example, to make an executable image and also documentation
(which will appear in subdirectories ~c[doc/EMACS] and ~c[doc/HTML]):
~bv[]
make large DOC ACL2_HONS=h
~ev[]~/
Much of the documentation for the remainder of this topic is taken from the
paper ``Function Memoization and Unique Object Representation for ACL2
Functions'' by Robert S. Boyer and Warren A. Hunt, Jr., which has appeared in
the Sixth International Workshop on the ACL2 Theorem Prover and Its
Applications, ACM Digital Library, 2006.
In the implementation of the ACL2 logic, ACL2 data objects are represented by
Common Lisp objects of the same type, and the ACL2 pairing operation is
internally implemented by the Common Lisp ~ilc[cons] function. In Common
Lisp, ~c[cons] is guaranteed to provide a new pair, distinct from any
previously created pair. We have defined a new ACL2 function ~ilc[HONS] that
is logically identical to the ACL2 ~c[cons] function, but whose
implementation usually reuses an existing pair if its components are
identical to the components of an existing pair. A record of ACL2 HONS
objects is kept, and when an ACL2 function calls ~c[hons] ACL2 searches for
an existing identical pair before allocating a new pair; this operation been
called ``hash consing''.
It appears that hash consing was first conceived by A. P. Ershov in 1957, to
speed up the recognition of common subexpressions. Ershov showed how to
collapse trees to minimal DAGs by traversing trees bottom up, and he used
hashing to eliminate the re-evaluation of common subexpressions. Later,
Eiichi Goto implemented a Lisp system with a built-in hash consing operation:
his h-CONS cells were rewrite protected and free of duplicate copies, and
Goto used this hash consing operation to facilitate the implementation of a
symbolic algebra system he developed.
Memoizing functions also has a long history. In 1967, Donald Michie proposed
using memoized functions to improve the performance of machine learning.
Rote learning was improved by a learning function not forgetting what it had
previously learned; this information was stored as memoized function values.
The use of hash consing has appeared many times. For instance, Henry Baker
used hash consing to improve the performance of the well-known Boyer
rewriting benchmark. Baker used both hash consing and function memoization
to improve the speed of the Takeuchi function, exactly in the spirit of our
implementation, but without the automated, system-wide integration we report
here.
The ACL2 implementation permits memoization of user-defined functions.
During execution a user may enable or disable function memoization on an
individual function basis, may clear memoization tables, and may even keep a
stack of memoization tables. This facility takes advantage of our
implementation where we keep one copy of each distinct ACL2 data object. Due
to the functional nature of ACL2, it is sufficient to have at most one copy
of any data structure; thus, a user may arrange to keep data canonicalized.
This implementation extends to the entire ACL2 system the benefits enjoyed by
BDDs: canonicalization, memoization, and fast equality check.
We have defined various algorithms using these features, and we have
observed, in some cases, substantial performance increases. For instance, we
have implemented unordered set intersection and union operations that operate
in time roughly linear in the size of the sets. Without using arrays, we
defined a canonical representation for Boolean functions using ACL2 objects.
We have investigated the performance of rewriting and tree consensus
algorithms to good effect, and we believe function memoization offers
interesting opportunities to simplify algorithm definition while
simultaneously providing performance improvements.
We recommend that users focus at first on the logical definitions of
~ilc[hons] and other primitives rather than their underlying Common Lisp
implementations. Integrated with this memoization system is a performance
monitoring system, which can provide real-time feedback on the operation and
usefulness of ~ilc[hons] and function memoization. For a more detailed
description of these tools, please see the ACL2 2006 workshop paper mentioned
above.
The Fibonacci function is a small example that demonstrates the utility of
function memoization. The following definition exhibits a runtime that is
exponential in its input argument.
~bv[]
(defun fib (x)
(declare (xargs :guard (natp x)))
(mbe
:logic
(cond ((zp x) 0)
((= x 1) 1)
(t (+ (fib (- x 1)) (fib (- x 2)))))
:exec
(if (< x 2)
x
(+ (fib (- x 1)) (fib (- x 2))))))
~ev[]
Below we show how the ACL2 ~ilc[time$] utility can measure the time to
execute a call to the ~c[fib] function (with some editing to avoid noise from
the underlying Common Lisp implementation). ~ilc[Memoize] is actually an
ACL2 macro that expands to a call of the ACL2 function used to identify a
function for memoization; ~pl[memoize]. This function also accepts a
well-formed term that must be true in order for the system to memoize a call
of the memoized function; to ensure that an instance of the term is safe for
evaluation in Common Lisp, a check is performed that if the ~il[guard] of the
memoized function is satisfied, then this instance will execute without
error.
~bv[]
ACL2 !>(time$ (fib 40))
; (EV-REC *RETURN-LAST-ARG3* ...) took
; 0.99 seconds realtime, 0.98 seconds runtime
; (1,296 bytes allocated).
102334155
ACL2 !>(memoize 'fib)
Summary
Form: ( TABLE MEMOIZE-TABLE ...)
Rules: NIL
Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
Summary
Form: ( PROGN (TABLE MEMOIZE-TABLE ...) ...)
Rules: NIL
Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
FIB
ACL2 !>(time$ (fib 40))
; (EV-REC *RETURN-LAST-ARG3* ...) took
; 0.00 seconds realtime, 0.00 seconds runtime
; (2,864 bytes allocated).
102334155
ACL2 !>(time$ (fib 100))
; (EV-REC *RETURN-LAST-ARG3* ...) took
; 0.00 seconds realtime, 0.00 seconds runtime
; (7,024 bytes allocated).
354224848179261915075
ACL2 !>(unmemoize 'fib)
~ev[]
We see that once the function ~c[fib] is identified as a function for which
function calls should be memoized, the execution times are substantially
reduced. Finally, we can prevent ~c[fib] from being further memoized; in
fact, ~ilc[unmemoize] erases the previously memoized values.
The implementation of hash consing, memoization, and applicative hash tables
involves several facets: canonical representation of ACL2 data, function
memoization, and the use of Lisp hash tables to improve the performance of
association list operations. We discuss each of these in turn, and we
mention some subtle interrelationships. Although it is not necessary to
understand the discussion in this section, it may permit some users to better
use this implementation. This discussion may be confusing for some ACL2
users as it makes references to Lisp implementation features.
The ACL2 system is actually implemented as a Lisp program that is layered on
top of a Common Lisp system implementation. ACL2 data constants are
implemented with their corresponding counterparts in Common Lisp; that is,
ACL2 cons pairs, strings, characters, numbers, and symbols are implemented
with their specific Common Lisp counterparts. This choice permits a number
of ACL2 primitive functions to be implemented with their corresponding Common
Lisp functions, but there are indeed differences. ACL2 is a logic, and as
such, it does not specify anything to do with physical storage or execution
performance. When ACL2 is used, the knowledgeable user can write functions
to facilitate the reuse of some previously computed values.
Recall the three mechanisms under discussion: hash consing, function
memoization, and fast association list operations. The function memoization
mechanism takes advantage of the canonical representation of data objects
provided by the ~ilc[hons] operation as does the fast association list
operation mechanism. Each time ~c[hons] is invoked, its arguments are
themselves converted, if necessary, to uniquely represented objects.
The ACL2 universe is recursively closed under the ~c[cons] pairing operation
and the atoms. Hash consing (~ilc[hons]) is logically identical to ~c[cons],
but a set of tables is used to record each ~c[hons] pair. When a ~c[hons]
pair is requested, the implementation checks, in the current set of tables,
whether the requested pair already exists. If not, a new pair is created and
a record of that pair is made; otherwise, a reference to the previously
created pair is returned. Thus, any data object created with ~c[hons] has a
unique representation, as does every subcomponent. We also arrange for
strings to have a unique representation ~-[] only one copy of each different
string is kept ~-[] and when any previously unseen string is an argument to
~c[hons], we add the string to a unique table of strings. A system-wide
benefit of having a canonical representation for data is that equality
comparisons between any two data objects can be done in constant time.
The definition of ~ilc[hons] in no way changes the operation of ~c[cons] ~-[]
~c[hons] creates a ~c[cons] pair. When asked to create a ~c[hons], the
implementation checks to see if there is a ~c[cons] with the same ~ilc[car]
and ~ilc[cdr] as the ~c[hons] being requested. Thus, the only difference
between the results of a ~c[hons] call and a corresponding ~c[cons] call is a
notation in some invisible (to the ACL2 logic) tables where we record which
~c[cons] elements are also ~c[hons] elements. Since a ~c[hons] is nothing
but a ~c[cons], the operation of ~c[car] and ~c[cdr] is unchanged. In fact,
the implementation is designed so that at any time it is safe to clear the
table identifying which ~c[cons] elements are also considered ~c[hons]
elements.
User-defined functions with defined and verified guards can be memoized.
When a function is memoized, a user-supplied condition restricts the domain
when memoization is attempted. When the condition is satisfied, memoization
is attempted (assuming that memoization for the function is presently
enabled); otherwise, the function is just evaluated. Each memoized function
has a hash table that is used to keep track of a unique list of function
arguments paired with their values. If appropriate, for each function the
corresponding table is checked to see if a previous call with exactly the
same arguments already exists in the table: if so, then the associated value
is returned; if not, then the function is evaluated and a new key-value pair
is added to the table of memoized values so that some future call will
benefit from the memoization. With ACL2 user functions memoization can be
dynamically enabled and disabled. There is an ACL2 function that clears a
specific memoization table. And finally, just as with the ~c[hons] table, a
stack of these function memoization tables is maintained; that is, it is
possible to memoize a function, use it a bit, set the memoized values aside,
start a new table, use it, and then return to the original table.
We next discuss the fast lookup operation for association lists. When a pair
is added to an association list using the functions ~ilc[hons-acons] or
~ilc[hons-acons!], the system also records the key-value pair in an
associated hash table. As long as a user only used these two functions when
placing key-value pairs on an association list, the key-value pairs in the
corresponding hash table will be synchronized with the key-value pairs in the
association list. Later, if ~ilc[hons-get] is used to look up a key, then
instead of performing a linear search of the association list we consult the
associated hash table. If a user does not strictly follow this discipline,
then a linear search may be required. In some sense, these association lists
are much like ACL2 arrays, but without the burden of explicitly naming the
arrays. The ACL2 array ~ilc[compress1] function is analogous to the
functions ~ilc[hons-shrink-alist] and ~ilc[hons-shrink-alist!]. There are
user-level ACL2 functions that allow the associated hash tables to be cleared
and removed.
Finally, we would advise anyone who is using CCL in a research environment to
stay plugged into the ``trunk'' or ``bleeding edge'' of CCL development.
This is very easy to do by typing a few commands to a shell, for example
standing above the target directory as follows, provided one has ~c[svn]
working.
~bv[]
For linux:
rm -rf ccl
svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx8664/ccl
For an x86 Macintosh running the Darwin OS:
svn co http://svn.clozure.com/publicsvn/openmcl/trunk/darwinx8664/ccl
To keep up to date, you may find it sufficient to do:
cd ccl
svn update
Whether obtaining a fresh CCL or just updating, finally issue these
commands.
./lx86cl64
(rebuild-ccl :full t)
(quit)
./lx86cl64
(rebuild-ccl :full t)
(quit)
~ev[]
~sc[References]
Baker, Henry G., The Boyer Benchmark at Warp Speed. ACM Lisp
Pointers V,3 (Jul-Sep 1992), pages 13-14.
Baker, Henry G., A Tachy 'TAK'. ACM Lisp Pointers Volume 3,
July-September, 1992, pages 22-23.
Robert S. Boyer and Warren A. Hunt, Jr., Function Memoization
and Unique Object Representation for ACL2 Functions, in the Sixth
International Workshop on the ACL2 Theorem Prover and Its
Applications, ACM Digital Library, 2006.
A. P. Ershov. On Programming of Arithmetic Operations. In
the Communications of the ACM, Volume 118, Number 3, August,
1958, pages 427-430.
Eiichi Goto, Monocopy and Associative Algorithms in Extended Lisp,
TR-74-03, University of Toyko, 1974.
Richard P. Gabriel. Performance and Evaluation of Lisp Systems.
MIT Press, 1985.
Donald Michie. Memo functions: a Language Feature with Rote
Learning Properties. Technical Report MIP-R-29, Department of
Artificial Intelligence, University of Edinburgh, Scotland, 1967.
Donald Michie. Memo Functions and Machine Learning. In Nature,
Volumne 218, 1968, pages 19-22.
~/")
#+(or acl2-loop-only (not hons))
(defn clear-memoize-table (fn)
":Doc-Section Hons-and-Memoization
forget values remembered for the given function~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization].
This function returns its argument, ~c[fn], unchanged. The values memoized
for ~c[fn] are forgotten.~/~/"
fn)
#+(or acl2-loop-only (not hons))
(defn clear-memoize-tables ()
":Doc-Section Hons-and-Memoization
forget values remembered for all the memoized functions~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization].
~c[Clear-memoize-tables] is a logical no-op. All memoized values are
forgotten. It returns ~c[nil], invoking ~ilc[clear-memoize-table] for each
memoized function.~/~/"
nil)
#+(or acl2-loop-only (not hons))
(defn memoize-summary ()
":Doc-Section Hons-and-Memoization
display all collected profiling and memoization table info~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization].
Logically, this function just returns ~c[nil], but it displays profiling and
memoization table information. The profiling statistics may be cleared with
~c[(]~ilc[clear-memoize-statistics]~c[)].~/~/"
nil)
#+(or acl2-loop-only (not hons))
(defn clear-memoize-statistics ()
":Doc-Section Hons-and-Memoization
clears all profiling info displayed by ~c[(]~ilc[memoize-summary]~c[)]~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization].
Logically, this function just returns ~c[nil]. It clears all profiling info
displayed by ~c[(]~ilc[memoize-summary]~c[)]~/~/"
nil)
(defmacro memsum ()
":Doc-Section Hons-and-Memoization
display all collected profiling and memoization info~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization].
This macro is an abbreviation for ~ilc[memoize-summary]. Logically, it just
returns ~c[nil].~/~/"
'(memoize-summary))
; The macros MEMOIZE-ON and MEMOIZE-OFF typically cause "under the hood"
; effects that, though not changing the semantics of what ACL2 returns, may
; affect the speed and/or space utilization of the computation.
; The functions memoize and unmemoize have rather innocent looking
; semantics. But under the hood, they enable and disable memoization.
; The function memoize might cause errors due to compilation problems.
(defconst *hons-primitive-fns*
;; This affects acl2-exports, maybe other stuff
'(hons-copy
hons
hons-equal
hons-equal-lite
hons-clear
hons-wash
hons-summary
hons-resize-fn
get-slow-alist-action
hons-get
hons-acons
hons-acons!
hons-shrink-alist
hons-shrink-alist!
fast-alist-len
fast-alist-free
fast-alist-summary
cons-subtrees
number-subtrees
clear-hash-tables
flush-hons-get-hash-table-link
;; from memoize.lisp
clear-memoize-table
clear-memoize-tables
memoize-summary
clear-memoize-statistics))
(defconst *hons-primitives*
;; hons-related macros and primitive fns
(append '(hons-resize
set-slow-alist-action
memoize
unmemoize
memoize-on
memoize-off
memsum)
*hons-primitive-fns*))
(defconst *mht-default-size* 60)
(defun memoize-form (fn
condition
condition-p
condition-fn
hints
otf-flg
inline
trace
commutative
forget
memo-table-init-size
aokp
ideal-okp)
; Jared Davis suggests that we consider bundling up these 13 parameters, for
; example into an alist. He says: "Various subsets of these arguments occur in
; spaghetti fashion throughout the code for memoize, add-trip, the
; memoize-table stuff, etc."
(declare (xargs :guard t))
(let ((condition (cond ((equal condition ''t) t)
((equal condition ''nil) nil)
(t condition))))
(cond
((and condition-fn (null condition-p))
`(progn (table memoize-table
(deref-macro-name ,fn (macro-aliases world))
(list* (cons :condition-fn ,condition-fn)
(cons :inline ,inline)
(cons :trace ,trace)
(cons :commutative ,commutative)
(cons :forget ,forget)
(cons :memo-table-init-size
,(or memo-table-init-size
*mht-default-size*))
(cons :aokp ,aokp)
(and (not (eq ,ideal-okp :default))
(list (cons :ideal-okp ,ideal-okp)))))
(value-triple (deref-macro-name
,fn
(macro-aliases (w state))))))
((and condition-p
(not (eq condition t))
(not (eq condition nil)))
`(make-event
(let* ((wrld (w state))
(fn (deref-macro-name ,fn (macro-aliases wrld)))
(condition ,condition)
(formals
(and (symbolp fn) ; guard for getprop
(getprop fn 'formals t
'current-acl2-world wrld)))
(stobjs-in (getprop fn 'stobjs-in t
'current-acl2-world wrld))
(condition-fn (or ,condition-fn
(intern-in-package-of-symbol
(concatenate
'string
(symbol-name fn)
"-MEMOIZE-CONDITION")
fn)))
(hints ,hints)
(otf-flg ,otf-flg)
(inline ,inline)
(trace ,trace)
(commutative ,commutative)
(forget ,forget)
(memo-table-init-size ,memo-table-init-size)
(aokp ,aokp)
(ideal-okp ,ideal-okp))
(cond ((not (and
(symbolp fn)
(not (eq t formals))
(not (eq t stobjs-in))
(not (eq t (getprop fn 'stobjs-out t
; Normally we would avoid getting the stobjs-out of return-last. But
; return-last will eventually be rejected for mamoization anyhow (by
; memoize-table-chk).
'current-acl2-world wrld)))
(cltl-def-from-name fn wrld)))
(er hard 'memoize
"The symbol ~x0 is not a known function symbol, and thus ~
it cannot be memoized."
fn))
; Certify-book seems to do things twice, so the following is commented out.
; ((cdr (assoc-eq fn (table-alist 'memoize-table wrld)))
; (er hard 'memoize "~x0 is already memoized." fn))
((not (member-eq inline '(t nil)))
(er hard 'memoize
"The value ~x0 for inline is illegal (must be ~x1 or ~x2)."
inline t nil))
((not (member-eq trace '(t nil)))
(er hard 'memoize
"The value ~x0 for trace is illegal (must be ~x1 or ~x2)."
trace t nil))
(t
`(progn
(defun ,condition-fn ,formals
(declare
(ignorable ,@formals)
(xargs :guard
,(getprop fn 'guard *t*
'current-acl2-world wrld)
:verify-guards nil
,@(let ((stobjs (remove nil stobjs-in)))
(and stobjs
`(:stobjs ,stobjs)))))
,condition)
(verify-guards ,condition-fn
,@(and hints `(:hints ,hints))
,@(and otf-flg
`(:otf-flg ,otf-flg)))
(table memoize-table
',fn
(list* (cons :condition-fn ',condition-fn)
(cons :inline ',inline)
(cons :trace ',trace)
(cons :commutative ',commutative)
(cons :forget ',forget)
(cons :memo-table-init-size
(or ,memo-table-init-size
*mht-default-size*))
(cons :aokp ',aokp)
(and (not (eq ',ideal-okp :default))
(list (cons :ideal-okp ',ideal-okp)))))
(value-triple ',fn)))))))
(t `(progn (table memoize-table
(deref-macro-name ,fn (macro-aliases world))
(list* (cons :condition-fn ,condition) ; t or nil
(cons :inline ,inline)
(cons :trace ,trace)
(cons :commutative ,commutative)
(cons :forget ,forget)
(cons :memo-table-init-size
(or ,memo-table-init-size
*mht-default-size*))
(cons :aokp ',aokp)
(and (not (eq ',ideal-okp :default))
(list (cons :ideal-okp ',ideal-okp)))))
(value-triple (deref-macro-name
,fn
(macro-aliases (w state)))))))))
(defmacro memoize (fn &key
(condition 't condition-p)
condition-fn hints otf-flg
(recursive 't)
trace
commutative
forget
memo-table-init-size
aokp
(ideal-okp ':default)
(verbose 't))
; WARNING: If you add a new argument here, consider making corresponding
; modifications to memoize-form, table-cltl-cmd, maybe-push-undo-stack, and
; add-trip. (These were the functions modified when adding the FORGET
; argument; the process was to see how the COMMUTATIVE argument was handled.)
; If condition and condition-fn are both non-nil, then the intent is
; that we do exactly what we would normally do for condition except
; that we use the name condition-fn.
; Parallelism blemish: when waterfall parallelism is enabled (detected by
; seeing whether ACL2 global 'waterfall-parallelism is non-nil), memoize and
; unmemoize should be changed to modify the 'saved-memoize-table instead of
; 'memoize-table.
":Doc-Section Events
turn on memoization for a specified function~/
This ~il[documentation] topic relates to an experimental extension of ACL2
under development by Bob Boyer and Warren Hunt. ~l[hons-and-memoization] for
a general discussion of memoization and the related features of hash consing
and applicative hash tables.
~bv[]
Examples:
(memoize 'foo) ; remember the values of calls
; of foo
(memoize 'foo :condition t) ; same as above
(memoize 'foo :condition '(test x)) ; memoize for args satisfying
; the given condition
(memoize 'foo :condition-fn 'test) ; memoize for args satisfying
; a call of the given function
(memoize 'foo :recursive nil) ; don't memoize recursive calls
(memoize 'foo :aokp t) ; attachments OK for stored results
(memoize 'foo :ideal-okp t) ; memoize even if foo is in :logic mode
; but has not been guard-verified~/
General Form:
(memoize fn ; memoizes fn and returns fn
:condition condition ; optional (default t)
:condition-fn condition-fn ; optional
:hints hints ; optional, for verifying the
; guards of condition-fn
:otf-flg otf-flg ; optional, for verifying the
; guards of condition-fn
:recursive t/nil ; optional (default t)
:commutative t/lemma-name ; optional (default nil)
:forget t/nil ; optional (default nil)
:memo-table-init-size size ; optional (default *mht-default-size*)
:aokp t/nil ; optional (default nil)
:ideal-okp t/:warn/nil ; optional (default nil)
:verbose t/nil ; optional (default t)
)
~ev[]
where ~c[fn] evaluates to a user-defined function symbol; ~c[condition] is
either ~c[t] (the default), ~c['t], ~c[nil], or ~c['nil], or else evaluates
to an expression whose free variables are among the formal parameters of
~c[fn]; and ~c[condition-fn] is either ~c[nil] (the default) or else
evaluates to a legal function symbol. Further restrictions and options are
discussed below. Note that all arguments are evaluated (but for the special
handling of value ~c[t] for ~c[:commutative], the argument must literally be
~c[t]; see below).
Generally ~c[fn] must evaluate to a defined function symbol. However, this
value can be the name of a macro that is associated with such a function
symbol; ~pl[macro-aliases-table]. That associated function symbol is the one
called ``memoized'' in the discussion below, but we make no more mention of
this subtlety.
In the most common case, ~c[memoize] takes a single argument, which evaluates
to a function symbol. We call this function symbol the ``memoized function''
because ``memos'' are saved and re-used, in the following sense. When a call
of the memoized function is evaluated, the result is ``memoized'' by
associating the call's arguments with that result, in a suitable table. But
first an attempt is made to avoid such evaluation, by doing a lookup in that
table on the given arguments for the result, as stored for a previous call on
those arguments. If such a result is found, then it is returned without
further computation. This paragraph also applies if ~c[:condition] is
supplied but is ~c[t] or ~c['t].
If keyword argument ~c[:condition-fn] is supplied, but ~c[:condition] is not,
then the result of evaluating ~c[:condition-fn] must be a defined function
symbol whose formal parameter list and ~ilc[guard] are the same as for the
function being memoized. If ~c[fn] is in ~c[:]~ilc[logic] mode, then
~il[guard]s must have been verified for ~c[:condition-fn]. Such a
``condition function'' will be run whenever the memoized function is called,
on the same parameters, and the lookup or table store described above are
only performed if the result from the condition function call is non-~c[nil].
Suppose however that ~c[:condition] is supplied. If the value supplied is
~c[t] or ~c['t], then the lookup and table store described above are always
done. If the value is ~c[nil] or ~c['nil], then this lookup and table store
are never done, although statistics may be gathered; ~pl[profile]. Now
consider other values for ~c[:condition]. An attempt will be made to define
a condition function whose ~il[guard] and formal parameters list are the same
as those of the memoized function, and whose body is the result, ~c[r], of
evaluating the given ~c[condition]. The name of that condition function is
the result of evaluating ~c[:condition-fn] if supplied, else is the result of
concatenating the string ~c[\"-MEMOIZE-CONDITION\"] to the end of the name of
the memoized function. The condition function will be defined with
~il[guard] verification turned off, but that definition will be followed
immediately by a ~ilc[verify-guards] event; and this is where the optional
~c[:hints] and ~c[:otf-flg] are attached. At evaluation time the condition
function is used as described in the preceding paragraph; so in effect, the
condition (~c[r], above) is evaluated, with its variables bound to the
corresponding actuals of the memoized function call, and the memoized
function attempts a lookup or table store if and only if the result of that
evaluation is non-~c[nil].
Note that ~c[fn] can be either a ~c[:]~ilc[logic] mode function or a
~c[:]~ilc[program] mode function. However, only the corresponding raw Lisp
function is actually memoized, so ~il[guard] violations can defeat
memoization, and ~c[:]~ilc[logic] mode functions without their ~il[guard]s
verified will only be memoized when called by ~c[:]~ilc[program] mode
functions. (~l[guards-and-evaluation] for more information about guards and
evaluation in ACL2.) If ~c[fn] is a ~c[:]~ilc[logic] mode function and
~c[:condition] is supplied and not ~c[t] or ~c[nil], then the condition must
be a ~il[guard]-verified function.
Calls of this macro generate events of the form
~c[(table memoize-table fn ((:condition-fn fn) ...))]. When successful, the
returned value is of the form ~c[(mv nil function-symbol state)].
Suppose that a function is already memoized. Then it is illegal to memoize
that function. Moreover, if the function was memoized with an associated
condition (i.e., was memoized with keyword ~c[:condition] or
~c[:condition-fn] having value other than ~c[t] or ~c[nil]), then it is also
illegal to convert the function from ~c[:]~ilc[program] to ~c[:]~ilc[logic]
mode (~pl[verify-termination]). To turn off memoization, ~pl[unmemoize].
~c[Memoize] is illegal for a function if its arguments include ~c[state] or
if it returns any ~il[stobj]s. Also, ~c[memoize] never allows attachments
to be used (~pl[defattach]); if an attachment is used during evaluation, then
the evaluation result will not be stored.
We conclude with by documenting keyword parameters not discussed above.
Keyword parameter ~c[:recursive] is ~c[t] by default, which means that
recursive calls of ~c[fn] will be memoized just as ``top-level'' calls of
~c[fn]. When ~c[:recursive] is instead set to ~c[nil], memoization is only
done at the top level. Using ~c[:recursive nil] is similar to writing a
wrapper function that just calls ~c[fn], and memoizing the wrapper instead of
~c[fn].
If ~c[:trace] has a non-~c[nil] value, then ~c[memoize] also traces in a
traditional Lisp style. If ~c[:trace] has value ~c[notinline] or
~c[notinline], then a corresponding declaration is added at the beginning of
the new definition of ~c[fn].
A non-~c[nil] value for ~c[:commutative] can be supplied if ~c[fn] is a
binary function in ~c[:logic] mode. If the ~c[memoize] event is successful,
then subsequently: whenever each argument to ~c[fn] is either a rational
number or a ~il[hons], then when the evaluation of ~c[fn] on those arguments
is memoized, the evaluation of ~c[fn] on the swap of those arguments is, in
essence, also memoized. If ~c[:commutative] is supplied and is not ~c[nil]
or ~c[t], then it should be the name of a previously-proved theorem whose
formula states the commutativity of ~c[fn], i.e., is the formula
~c[(equal (fn x y) (fn y x))] for a pair ~c[{x,y}] of distinct variables. If
~c[:commutative] is ~c[t] ~-[] but not merely an expression that evaluates to
~c[t] ~-[] then an attempt to prove such a lemma will be made on-the-fly.
The name of the lemma is the symbol in the same package as ~c[fn], obtained
by adding the suffix ~c[\"-COMMUTATIVE\"] to the ~ilc[symbol-name] of ~c[fn].
If the proof attempt fails, then you may want first to prove the lemma
yourself with appropriate hints and perhaps supporting lemmas, and then
supply the name of that lemma as the value of ~c[:commutative].
If ~c[:commutative] is supplied, and a non-commutative condition is provided
by ~c[:condition] or ~c[:condition-fn], then although the results will be
correct, the extra memoization afforded by ~c[:commutative] is unspecified.
If ~c[:memo-table-init-size] is supplied, then it should be a positive
integer specifying the initial size of an associated hash table.
Argument ~c[:aokp] is relevant only when attachments are used; ~pl[defattach]
for background on attachments. When ~c[:aokp] is ~c[nil], the default,
computed values are not stored when an attachment was used, or even when an
attachment may have been used because a function was called that had been
memoized using ~c[:aokp t]. Otherwise, computed values are always stored,
but saved values are not used except when attachments are allowed. To
summarize:
~bf[]
aokp=nil (default): ``Pure'', i.e., values do not depend on attachments
- Fetch: always legal
- Store: only store resulting value when attachments were not used
aokp=t: ``Impure'', i.e., values may depend on attachments
- Fetch: only legal when attachments are allowed (e.g., not during proofs)
- Store: always legal
~ef[]
If ~c[:ideal-okp] is supplied and not ~c[nil], then it is permitted to
memoize an ``ideal-mode'' function: one in ~c[:]~ilc[logic] mode whose
~il[guard]s have not been verified. In general, it is ill-advised to memoize
an ideal-mode function, because its calls are typically evaluated ``in the
logic'' without calling a memoized ``raw Lisp'' version of the function.
However, if the function is called by a ~c[:]~ilc[program] mode function,
evaluation can transfer to raw Lisp before reaching the call of the memoized
function, in which case memoization will take place. For such situations you
can provide value ~c[:warn] or ~c[t] for keyword parameter ~c[:ideal-okp].
Both of these values allow memoization of ideal-mode functions, but if
~c[:warn] is supplied then a warning will take place. Note that you may set
the key ~c[:memoize-ideal-okp] of the ~ilc[acl2-defaults-table] to value
~c[t] or ~c[:warn] to change the default, but if parameter ~c[:ideal-okp] is
supplied, the ~ilc[acl2-defaults-table] value is ignored.
If ~c[:verbose] is supplied, it should either be ~c[nil], which will inhibit
proof, event, and summary output (~pl[with-output]), or else ~c[t] (the
default), which does not inhibit output. If the output baffles you, try
~bv[]
:trans1 (memoize ...)
~ev[]
to see the single-step macroexpansion of your ~c[memoize] call.
The default for ~c[:forget] is ~c[nil]. If ~c[:forget] is supplied, and not
~c[nil], then it must be ~c[t], which causes all memoization done for a
top-level call of ~c[fn] to be forgotten when that top-level call exits.~/
:cite hons-and-memoization
:cited-by hons-and-memoization"
(declare (xargs :guard (booleanp recursive))
(ignorable condition-p condition condition-fn hints otf-flg
recursive trace commutative forget memo-table-init-size
aokp ideal-okp verbose))
#-acl2-loop-only
`(progn (when (eql *ld-level* 0)
; We are not inside the ACL2 loop (hence not in certify-book, for
; example).
(let ((state *the-live-state*))
(warning$ 'memoize nil
"No change for function ~x0: Memoization ~
requests are ignored in raw Lisp. In raw ~
Lisp, use memoize-fn."
',fn)))
(value-triple nil))
#+acl2-loop-only
(let* ((inline recursive)
(form
(cond
((eq commutative t)
`(make-event
(let* ((fn ,fn)
(commutative
(intern-in-package-of-symbol
(concatenate 'string (symbol-name fn) "-COMMUTATIVE")
fn)))
(list ; use encapsulate so that each form is printed first
'encapsulate
()
(list 'defthm commutative
(list 'equal
(list fn 'x 'y)
(list fn 'y 'x))
:rule-classes nil)
(memoize-form (kwote fn) ',condition ',condition-p
',condition-fn ',hints ',otf-flg ',inline
',trace
(kwote commutative)
',forget
',memo-table-init-size
',aokp
',ideal-okp)))))
(t (memoize-form fn condition condition-p condition-fn
hints otf-flg inline trace commutative forget
memo-table-init-size aokp ideal-okp)))))
(cond (verbose form)
(t `(with-output
:off (summary prove event)
:gag-mode nil
,form)))))
(defmacro unmemoize (fn)
":Doc-Section Events
turn off memoization for the specified function~/
~bv[]
Example:
(unmemoize 'foo) ; turn off memoization of foo~/
General Form:
(unmemoize fn)
~ev[]
where ~c[fn] evaluates to a function symbol that is currently
memoized; ~pl[memoize]. An exception is that as with ~ilc[memoize],
~c[fn] may evaluate to the name of a macro that is associated with
such a function symbol; ~pl[macro-aliases-table].
Calls of this macro generate events of the form
~c[(table memoize-table fn nil)]. When successful, the returned
value is of the form ~c[(mv nil function-symbol state)].
To remove the effects of all ~ilc[memoize] ~il[events], evaluate:
~c[(clear-memo-table)]. To save and restore memoization,
~pl[save-and-clear-memoization-settings] and
~pl[restore-memoization-settings].~/
:cite hons-and-memoization
:cited-by hons-and-memoization"
(declare (xargs :guard t))
#-acl2-loop-only
`(progn (when (eql *ld-level* 0)
; We are not inside the ACL2 loop (hence not in certify-book, for
; example).
(warning$ 'unmemoize nil
"No change for function ~x0: Unmemoization requests are ~
ignored in raw Lisp. In raw Lisp, use unmemoize-fn."
',fn))
(value-triple nil))
#+acl2-loop-only
`(with-output
:off summary
(progn (table memoize-table
(deref-macro-name ,fn (macro-aliases world))
nil)
(value-triple
(deref-macro-name ,fn (macro-aliases (w state)))))))
(defmacro profile (fn &rest r)
":Doc-Section Events
turn on profiling for one function~/
NOTE: An alternative to this utility, which has a lot less functionality but
doesn't depend on the experimental extension of ACL2 mentioned just below,
may be found in community book ~c[books/misc/profiling.lisp].
This documentation topic relates to an experimental extension of ACL2 under
development by Bob Boyer and Warren Hunt. ~l[hons-and-memoization] for a
general discussion of memoization, on which this ~c[profile] utility is
built, and the related features of hash consing and applicative hash tables.
~c[Profile] can be useful in Common Lisp debugging and performance analysis,
including examining the behavior of ACL2 functions.
~bv[]
Example:
(profile 'fn) ; keep count of the calls of fn
(profile 'fn ; as above, with with some memoize options
:trace t
:forget t)
(memsum) ; report statistics on calls of memoized functions (e.g., fn)
(clear-memoize-statistics) ; clear memoization (and profiling) statistics
~ev[]
Evaluation of ~c[(profile 'fn)] redefines ~c[fn] so that a count will be kept
of the calls of FN. The information recorded may be displayed, for example,
by invoking ~c[(]~ilc[memoize-summary]~c[)] or (equivalently) ~c[(memsum)].
If you wish to gather fresh statistics for the evaluation of a top-level
form, ~pl[clear-memoize-statistics].
~c[Profile] is just a macro that calls ~ilc[memoize] to do its work.
~c[Profile] gives the two keyword parameters ~c[:condition] and
~c[:recursive] of ~ilc[memoize] the value ~c[nil]. Other keyword parameters
for ~c[memoize], which must not include ~c[:condition], ~c[:condition-fn], or
~c[:recursive], are passed through. To eliminate profiling, use
~ilc[unmemoize]; for example, to eliminate profiling for function ~c[fn],
evaluate ~c[(unmemoize 'fn)].
You may find raw Lisp functions ~c[profile-all] and ~c[profile-acl2] to be
useful. Please contact the ACL2 developers if you want versions of these
functions to be executable from inside the ACL2 read-eval-print loop.~/~/"
(declare (xargs :guard (and (keyword-value-listp r)
(not (assoc-keyword :condition r))
(not (assoc-keyword :condition-fn r))
(not (assoc-keyword :recursive r)))))
`(memoize ,fn :condition nil :recursive nil ,@r))
#-hons
(defmacro memoize-on-raw (fn form)
(declare (ignore fn))
form)
(defmacro memoize-on (fn form)
; MEMOIZE-ON evaluates form. During the evaluation the symbol fn has as
; its symbol-function what it had immediately AFTER the memoization of
; fn. Hence, the values of calls of fn may be remembered during the
; evaluation and later. Warning: to use MEMOIZE-ON, fn must already
; be memoized.
`(return-last 'memoize-on-raw ,fn ,form))
#-hons
(defmacro memoize-off-raw (fn form)
(declare (ignore fn))
form)
(defmacro memoize-off (fn form)
; MEMOIZE-OFF evaluates form. During the evaluation the symbol fn has as
; its symbol-function what it had immediately BEFORE the memoization
; of fn. Hence the values of calls of fn may not be remembered during
; the evaluation. Warning: to use MEMOIZE-OFF, fn must already be
; memoized."
`(return-last 'memoize-off-raw ,fn ,form))
(defmacro memoizedp-world (fn wrld)
`(let ((fn ,fn)
(wrld ,wrld))
(cond
((not (global-val 'hons-enabled wrld))
(er hard 'memoizedp
"Memoizedp cannot be called in this ACL2 image, as it requires a ~
hons-aware ACL2. See :DOC hons-and-memoization."))
(t
(cdr (assoc-eq fn (table-alist 'memoize-table wrld)))))))
(defmacro memoizedp (fn)
(declare (xargs :guard t))
`(memoizedp-world ,fn (w state)))
;;; hons-shrink-alist
; HONS-SHRINK-ALIST, when called with an atomic second
; argument, produces an alist that is alist-equivalent
; to the first argument, but with all irrelevant entries in
; the first argument deleted. Informal remark: the alist
; returned is a hons when the initial ANS is not an atom.
; Comment about the last clause above. Or really?
; Counterexamples?
;
; mbu> stp
; ? (honsp (hons-shrink-alist '((a . b) (a . b2)) (hons-acons 1 2 3)))
; NIL
;
; mbu> stp
; ? (honsp (hons-shrink-alist '((a . b) (a . b2)) nil))
; NIL
; ?
; Some centaur/ books put entries in *never-profile-ht*. In order to allow
; those books to certify in vanilla ACL2, we define a default value for that
; variable here.
#+(and (not hons) (not acl2-loop-only))
(defparameter *never-profile-ht*
(make-hash-table :test 'eq))
#+(or acl2-loop-only (not hons))
(defun never-memoize-fn (fn)
(declare (xargs :guard (symbolp fn))
(ignore fn))
nil)
(defmacro never-memoize (fn)
":Doc-Section Hons-and-Memoization
Mark a function as unsafe to memoize.~/
This ~il[documentation] topic relates to the experimental extension of ACL2
supporting hash cons, fast alists, and memoization;
~pl[hons-and-memoization].
Logically, this function just returns ~c[nil]. But execution of
~c[(never-memoize fn)] records that ~c[fn] must never be memoized, so that
any attempt to memoize ~c[fn] will fail.
Any function can be marked as unsafe to memoize; in fact, ~c[fn] need not
even be defined at the time it is marked.
This is useful for prohibiting the memoization of functions that are known to
involve destructive functions like ~c[nreverse].~/~/"
(declare (xargs :guard (symbolp fn)))
`(make-event
(prog2$ (never-memoize-fn ',fn)
'(value-triple :invisible))
:check-expansion t))
|