forked from acl2/acl2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
linear-a.lisp
3606 lines (3049 loc) · 149 KB
/
linear-a.lisp
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
; ACL2 Version 8.4 -- A Computational Logic for Applicative Common Lisp
; Copyright (C) 2022, 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: [email protected] and [email protected]
; Department of Computer Science
; University of Texas at Austin
; Austin, TX 78712 U.S.A.
(in-package "ACL2")
;=================================================================
; This file defines the basics of the linear arithmetic decision
; procedure. We also include clause histories, parent trees,
; tag-trees, and assumptions; all of which are needed by add-poly
; and friends.
;=================================================================
; We begin with some general support functions. They should
; probably be organized and moved to axioms.lisp.
(defmacro ts-acl2-numberp (ts)
`(ts-subsetp ,ts *ts-acl2-number*))
(defmacro ts-rationalp (ts)
`(ts-subsetp ,ts *ts-rational*))
(defmacro ts-real/rationalp (ts)
#+non-standard-analysis
`(ts-subsetp ,ts *ts-real*)
#-non-standard-analysis
`(ts-subsetp ,ts *ts-rational*))
(defmacro ts-integerp (ts)
`(ts-subsetp ,ts *ts-integer*))
;=================================================================
; Clause Histories
; Clauses carry with them their histories, which describe which processes
; have produced them. A clause history is a list of history-entry records.
; A process, such as simplify-clause, might inspect the history of its
; input clause to help decide whether to perform a certain transformation.
(defrec history-entry
; Important Note: This record is laid out this way so that we can use
; assoc-eq on histories to detect the presence of a history-entry for
; a given processor. Do not move the processor field!
(processor ttree clause signal . cl-id)
t)
; Processor is a waterfall processor (e.g., 'simplify-clause). The
; ttree and signal are, respectively, the ttree and signal produced by
; the processor on clause. Each history-entry is built in the
; waterfall, but we inspect them for the first time in this file.
;=================================================================
; Essay on Parent Trees
; Structurally, a "parent tree" or pt is either nil, a number, or the cons
; of two parent trees. Parent trees are used to represent sets of
; literals. In particular, every number in a pt is the position of some
; literal in the current-clause variable of simplify-clause1 and the tree
; may be thought of as representing that set of literals. Pts are used
; to avoid tail biting. An earlier implementation of this used "clause-tails."
; We explain everything below.
; "Tail biting" is our name for the insidious phenomenon that occurs when
; one assumes p false while trying to prove p and then, carelessly,
; rewrites the goal p to false on the basis of that assumption. Observe
; that this is sound but detrimental to success. One way to prevent
; tail biting is to not assume p false while trying to prove it, but we
; found that too weak. The way we avoid tail biting is to keep careful
; track of what we're trying to prove, which literal we are working on,
; and what assumptions have been used to derive what results; we never use
; the assumption that p is false (or anything derived from it) to rewrite
; p to false. Despite our efforts, tail biting by simplify-clause is
; possible. See "On Tail Biting by Simplify-clause" for more.
; The easiest to understand use of parent trees in this regard is in
; linear arithmetic. In simplify-clause1 we setup the
; simplify-clause-pot-lst, by expressing all the arithmetic hypotheses of
; the conjecture as polynomial inequalities. When new inequalities are
; introduced, as when trying to relieve the hypothesis of some rule, we
; can combine them with the preprocessed "polys" to quickly settle certain
; arithmetic statements. To avoid duplication of effort, our
; simplify-clause-pot-lst contains polys derived from all possible
; literals of the current clause. This is because a great deal of work
; may be done (via linear lemmas and rewriting) to derive a poly about a
; given suggestive subterm of a given literal and we do not want to do it
; each time we assume that literal false. Note the ease with which we
; could bite our tail: the list of inequalities is derived from the
; negations of every literal so we might easily use an inequality to
; falsify the literal from which it was derived. To avoid this, each poly
; is tagged with one or more parent trees. Intuitively the poly derived
; from an inequality literal is tagged with that literal. But other
; literals may have been used, e.g., to establish certain terms rational,
; so one must think of the polys as being tagged with sets of literals.
; Then, when we are rewriting a particular literal we tell ourselves (by
; making a note in the :pt field of the rcnst) to avoid any poly
; descending from the goal literal. Similar use is made of parent trees
; in the fc-pair-lst -- a list of preprocessed conclusions obtained by
; forward chaining from the current clause.
; The problem is made subtle by the fact that the literals we are
; rewriting change before we get to them and thus cannot be recognized by
; their structure alone. Consider the clause {lit1 lit2 lit3}. Now
; suppose we forward chain from ~lit3 and deduce concl. Then fc-pair-lst
; will contain (concl . ttree) where ttree contains a parent tree
; acknowledging our dependence on lit3. We may thus use concl when we are
; working on lit1 and lit2. But suppose that in simplifying lit1 we
; produce the literal (not (equal var 27)). Then we can substitute 27 for
; var everywhere and will actually do so. Thus, by the time we get to
; work on the third literal of the clause above it will not be lit3 but
; some reduced instance, lit3', of lit3. If the parent tree literally
; contained lit3, it would be impossible to recognize that concl was to be
; avoided.
; Therefore, we actually refer to literals by their position in the
; current-clause of simplify-clause1 (from which the preprocessing was
; done) and we keep careful track as we simplify what the original pt for
; each literal was. As we scan over the literals to simplify we maintain
; a map, an enumeration of pts, giving the pt for each literal. Thus,
; while we actually go to work on lit3' above, we will actually have in
; our hand the fact that lit3 is its parent. Keeping track of the parents
; of the literals we are working on is made harder by the fact that
; sometimes literal merge. For example, in {lit1 lit2 lit3} lit1 may
; simplify to lit3 and thus we may merge them. The surviving literal is
; given the parent tree that contains both 1 and 3 so we know not to use
; conclusions derived from either. The rewrite-constant, rcnst, in use
; below simplify-clause1 contains as one of its fields the
; :current-clause. Thus, given the rewrite-constant and a pt it is
; possible to recover the original parent literals.
; We generally use "pt" to refer to a single parent tree. "Pts" is a list
; of parent trees, implicitly in "weak 1:1 correspondence" with some list
; of terms. By "weak" we mean pts may be shorter than the list of terms
; and "excess terms" have the nil pt. That is, it is ok to cdr pts as you
; cdr down the list of terms and every time you need a pt for a term you
; take the car of pts. There is no need to store the nil pt in tag-trees,
; so we don't. Thus, a commonly used convention is to supply a pts of nil
; to a function that stores 'pts, causing it to store no pts.
; In the early days we did not use parent trees but "clause-tails" -- the
; tail of clause starting with the parent literal. This was adopted to
; avoid the confusion caused by duplicate literals. But it was rendered
; unworkable when we implemented the Satriani hacks and started
; substituting for variables as we went. It also suffered other problems
; due to sloppy implementation.
(defun pt-occur (n pt)
; Determine whether n occurs in the set denoted by pt.
(cond ((null pt) nil)
((consp pt) (or (pt-occur n (car pt)) (pt-occur n (cdr pt))))
(t (= n pt))))
(defun pt-intersectp (pt1 pt2)
; Determine whether the intersection of the sets denoted by pt1 and pt2
; is nonempty.
(cond ((null pt1) nil)
((consp pt1)
(or (pt-intersectp (car pt1) pt2)
(pt-intersectp (cdr pt1) pt2)))
(t (pt-occur pt1 pt2))))
;=================================================================
; Essay on Tag-Trees
; If you add a new tag, be sure to include it in all-runes-in-ttree!
; Tags in Tag-Trees
; After Version_4.2 we switched to a representation of a tag-tree as an alist,
; associating a key with a non-empty list of values, rather than building up
; tag-trees with operations (acons tag value ttree) and (cons ttree1 ttree2).
; Note that we view these lists as sets, and are free to ignore order and
; duplications (though we attempt to avoid duplicates). Our motivation was to
; allow the addition of a new key, associated with many values, without
; degrading performance significantly.
; Each definition of a primitive for manipulating tag-trees has the comment: "
; Note: Tag-tree primitive".
; See all-runes-in-ttree for the set of all legal tags and their associated
; values. Some of the tags and associated values are as follows.
; 'lemma
; The tagged object is a rune.
; 'pt
; The tagged object is a "parent tree". See the Essay on Parent Trees.
; The tree identifies a set of literals in the current-clause of
; simplify-clause1 used in the derivation of poly or term with which the
; tree is associated. We need this information for two reasons. First,
; in order to avoid tail biting (see below) we do not use any poly that
; descends from the assumption of the falsity of the literal we are trying
; to prove. Second, in find-equational-poly we seek two polys that can be
; combined to derive an equality, and we use 'pt to identify those that
; themselves descend from equality hypotheses.
; 'assumption
; The tagged object is an assumption record containing, among other things, a
; type-alist and a term which must be true under the type-alist in order to
; assure the validity of the poly or rewrite with which the tree is associated.
; We cannot linearize (- x), for example, without knowing (rationalp x). If we
; cannot establish it by type set reasoning, we add that 'assumption to the
; poly generated. If we eventually use the poly in a derivation, the
; 'assumption will infect it and when we get up to the simplify-clause level we
; will split on them.
; 'find-equational-poly
; The tagged object is a pair of polynomials. During simplify clause
; we try to find two polys that can be combined to form an equation we
; don't have explicitly in the clause. If we succeed, we add the
; equation to the clause. However, it may be simplified into
; unrecognizable form and we need a way to avoid re-generation of the
; equation in future calls of simplify. We do this by infecting the
; tag-tree with this tag and the two polys used.
; Historical Note from the days when tag-trees were constructed using (acons
; tag value ttree) and (cons-tag-trees ttree1 ttree2):
; ; The invention of tag-trees came about during the designing of the linear
; ; package. Polynomials have three "arithmetic" fields, the constant, alist,
; ; and relation. But they then have many other fields, like lemmas,
; ; assumptions, and literals. At the time of this writing they have 5 other
; ; fields. All of these fields are contaminants in the sense that all of the
; ; contaminants of a poly contaminate any result formed from that poly. The
; ; same is true with the second answer of rewrite.
; ; If we represented the 5-tuple of the ttree of a poly as full-fledged fields
; ; in the poly the best we could do is to use a balanced binary tree with 8
; ; tips. In that case the average time to change some field (including the
; ; time to cons a new element onto any of the 5 contaminants) is 3.62 conses.
; ; But if we clump all the contaminants into a single field represented as a
; ; tag-tree, the cost of adding a single element to any one of them is 2
; ; conses and the average cost of changing any of the 4 fields in a poly is
; ; 2.5 conses. Furthermore, we can effectively union all 5 contaminants of
; ; two different polys in one cons!
; The following function determines whether val with tag tag occurs in
; tree:
(defun tag-tree-occur (tag val ttree)
; Note: Tag-tree primitive
(let ((pair (assoc-eq tag ttree)))
(and pair ; optimization
(member-equal val (cdr pair)))))
(defun remove-tag-from-tag-tree (tag ttree)
; Note: Tag-tree primitive
; In this function we do not assume that tag is a key of ttree. See also
; remove-tag-from-tag-tree, which does make that assumption.
(cond ((assoc-eq tag ttree)
(remove1-assoc-eq tag ttree))
(t ttree)))
(defun remove-tag-from-tag-tree! (tag ttree)
; Note: Tag-tree primitive
; In this function we assume that tag is a key of ttree. See also
; remove-tag-from-tag-tree, which does not make that assumption.
(remove1-assoc-eq tag ttree))
; To add a tagged object to a tree we use the following function. Observe
; that it does nothing if the object is already present.
; Note:
; If you add a new tag, be sure to include it in all-runes-in-ttree!
(defmacro extend-tag-tree (tag vals ttree)
; Note: Tag-tree primitive
; Warning: We assume that tag is not a key of ttree and vals is not nil.
`(acons ,tag ,vals ,ttree))
(defun add-to-tag-tree (tag val ttree)
; Note: Tag-tree primitive
; See also add-to-tag-tree!, for the case that tag is known not to be a key of
; ttree.
(cond
((eq ttree nil) ; optimization
(list (list tag val)))
(t
(let ((pair (assoc-eq tag ttree)))
(cond (pair (cond ((member-equal val (cdr pair))
ttree)
(t (acons tag
(cons val (cdr pair))
(remove-tag-from-tag-tree! tag ttree)))))
(t (acons tag (list val) ttree)))))))
(defun add-to-tag-tree! (tag val ttree)
; Note: Tag-tree primitive
; It is legal (and more efficient) to use this instead of add-to-tag-tree if we
; know that tag is not a key of ttree.
(extend-tag-tree tag (list val) ttree))
; A Little Foreshadowing:
; We will soon introduce the notion of a "rune" or "rule name." To
; each rune there corresponds a numeric equivalent, or "nume," which
; is the index into the "enabled structure" for the named rule. We
; push runes into ttrees under the 'lemma property to record their
; use.
; We have occasion for "fake-runes" which look like runes but are not.
; See the Essay on Fake-Runes below. One such rune is shown below,
; and is the name of otherwise anonymous rules that are always considered
; enabled. When this rune is used, its use is not recorded in the
; tag-tree.
(defconst *fake-rune-for-anonymous-enabled-rule*
'(:FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE nil))
(defmacro fake-rune-for-anonymous-enabled-rule-p (rune)
; Rather than pay the price of recognizing the
; *fake-rune-for-anonymous-enabled-rule* perfectly we exploit the fact that no
; true rune has :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE as its token.
`(eq (car ,rune) :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE))
(defabbrev push-lemma (rune ttree)
; This is just (add-to-tag-tree 'lemma rune ttree) and is named in honor of the
; corresponding act in Nqthm. We do not record uses of the fake rune.
(cond ((fake-rune-for-anonymous-enabled-rule-p rune) ttree)
(t (add-to-tag-tree 'lemma rune ttree))))
; Historical Note from the days when tag-trees were constructed using (acons
; tag value ttree) and (cons-tag-trees ttree1 ttree2):
; ; To join two trees we use cons-tag-trees. Observe that if the first tree is
; ; nil we return the second (we can't cons a nil tag-tree on and their union
; ; is the second anyway). Otherwise we cons, possibly duplicating elements.
; ; But starting in Version_3.2, we keep tagged objects unique in tag-trees, by
; ; calling scons-tag-trees when necessary, unioning the tag-trees rather than
; ; merely consing them. The immediate prompt for this change was a report
; ; from Eric Smith on getting stack overflows from tag-tree-occur, but this
; ; problem has also occurred in the past (as best Matt can recall).
(defun remove1-assoc-eq-assoc-eq-1 (alist1 alist2)
(declare (xargs :guard (and (symbol-alistp alist1)
(symbol-alistp alist2))))
(cond ((endp alist2)
(mv nil nil))
(t (mv-let (changedp x)
(remove1-assoc-eq-assoc-eq-1 alist1 (cdr alist2))
(cond ((assoc-eq (caar alist2) alist1)
(mv t x))
(changedp
(mv t (cons (car alist2) x)))
(t (mv nil alist2)))))))
(defun remove1-assoc-eq-assoc-eq (alist1 alist2)
(mv-let (changedp x)
(remove1-assoc-eq-assoc-eq-1 alist1 alist2)
(declare (ignore changedp))
x))
(defun cons-tag-trees1 (ttree1 ttree2 ttree3)
; Note: Tag-tree primitive supporter
; Accumulate into ttree3, whose keys are disjoint from those of ttree1, the
; values of keys in ttree1 each augmented by their values in ttree2.
; It might be more efficient to accumulate into ttree3, so that this function
; is tail-recursive. But we prefer that the tags at the front of ttree1 are
; also at the front of the returned ttree, since presumably the values of those
; tags are more likely to be updated frequently, and an update generates fewer
; conses the closer the tag is to the front of the ttree.
(cond ((endp ttree1) ttree3)
(t (let ((pair (assoc-eq (caar ttree1) ttree2)))
(cond (pair (acons (caar ttree1)
(union-equal (cdar ttree1) (cdr pair))
(cons-tag-trees1 (cdr ttree1) ttree2 ttree3)))
(t (cons (car ttree1)
(cons-tag-trees1 (cdr ttree1) ttree2 ttree3))))))))
(defun cons-tag-trees (ttree1 ttree2)
; Note: Tag-tree primitive
; We return a tag-tree whose set of keys is the union of the keys of ttree1 and
; ttree2, and whose value for each key is the union of the values of the key in
; ttree1 and ttree2 (in that order). In addition, we attempt to avoid needless
; consing.
(cond ((null ttree1) ttree2)
((null ttree2) ttree1)
((null (cdr ttree2))
(let* ((pair2 (car ttree2))
(tag (car pair2))
(pair1 (assoc-eq tag ttree1)))
(cond (pair1 (acons tag
(union-equal (cdr pair1) (cdr pair2))
(remove1-assoc-eq tag ttree1)))
(t (cons pair2 ttree1)))))
(t (let ((ttree3 (remove1-assoc-eq-assoc-eq ttree1 ttree2)))
(cons-tag-trees1 ttree1 ttree2 ttree3)))))
(defmacro tagged-objects (tag ttree)
; Note: Tag-tree primitive
; See also tagged-objectsp for a corresponding predicate.
`(cdr (assoc-eq ,tag ,ttree)))
(defmacro tagged-objectsp (tag ttree)
; Note: Tag-tree primitive
; This is used instead of tagged-objects (but is Boolean equivalent to it) when
; we want to emphasize that our only concern is whether or not there is at
; least one tagged object associated with tag in ttree.
`(assoc-eq ,tag ,ttree))
(defun tagged-object (tag ttree)
; Note: Tag-tree primitive
; This function returns obj for the unique obj associated with tag in ttree, or
; nil if there is no object with that tag. If there may be more than one
; object associated with tag in ttree, use (car (tagged-objects tag ttree))
; instead to obtain one such object, or use (tagged-objectsp tag ttree) if you
; only want to answer the question "Is there any object associated with tag in
; ttree?".
(let ((objects (tagged-objects tag ttree)))
(and objects
(assert$ (null (cdr objects))
(car objects)))))
; We accumulate our ttree into the state global 'accumulated-ttree so that if a
; proof attempt is aborted, we can still recover the lemmas used within it. If
; we know a ttree is going to be part of the ttree returned by a successful
; event, then we want to store it in state. We are especially concerned about
; storing a ttree if we are about to inform the user, via output, that the
; runes in it have been used. (That is, we want to make sure that if a proof
; fails after the system has reported using some rune then that rune is tagged
; as a 'lemma in the 'accumulated-ttree of the final state.) This encourages
; us to cons a new ttree into the accumulator every time we do output.
(deflock *ttree-lock*)
(defun@par accumulate-ttree-and-step-limit-into-state (ttree step-limit state)
; We add ttree to the 'accumulated-ttree in state and return an error triple
; whose value is ttree. Before Version_3.2 we handled tag-trees a bit
; differently, allowing duplicates and using special markers for portions that
; had already been accumulated into state. Now we keep tag-trees
; duplicate-free and avoid adding such markers to the returned value.
; We similarly save the given step-limit in state, unless its value is :skip.
(declare (ignorable state))
(pprogn@par
(cond ((eq step-limit :skip) (state-mac@par))
(t
; Parallelism no-fix: the following call of (f-put-global@par 'last-step-limit
; ...) may be overridden by another similar call performed by a concurrent
; thread. But we can live with that because step-limits do not affect
; soundness.
(f-put-global@par 'last-step-limit step-limit state)))
(cond
((eq ttree nil) (value@par nil))
(t (pprogn@par
(with-ttree-lock
; In general, it is dangerous to set the same state global in two different
; threads, because the first setting is blown away by the second. But here, we
; are _accumulating_ into a state global (namely, 'accumulated-ttree), and we
; don't care about the order in which the accumulation occurs (even though such
; nondeterminism isn't explained logically -- after all, we are modifying state
; without passing it in, so we already are punting on providing a logical story
; here). Our only concern is that two such accumulations interfere with each
; other, but the lock just above takes care of that (i.e., provides mutual
; exclusion).
(f-put-global@par 'accumulated-ttree
(cons-tag-trees ttree
(f-get-global 'accumulated-ttree
state))
state))
(value@par ttree))))))
(defun pts-to-ttree-lst (pts)
(cond ((null pts) nil)
(t (cons (add-to-tag-tree! 'pt (car pts) nil)
(pts-to-ttree-lst (cdr pts))))))
; Previously, we stored the parents of a poly in the poly's :ttree field
; and used to-be-ignoredp. However, tests have shown that under certain
; conditions to-be-ignoredp was taking up to 80% of the time spent by
; add-poly. We now store the poly's parents in a separate field and
; use ignore-polyp. The next few functions are used in the implementation
; of this change.
(defun marry-parents (parents1 parents2)
; We return the 'eql union of the two arguments. When we create a
; new poly from two other polys via cancellation, we need to ensure
; that the new poly depends on all the literals that either of the
; others do.
(if (null parents1)
parents2
(marry-parents (cdr parents1)
(add-to-set-eql (car parents1) parents2))))
(defun collect-parents1 (pt ans)
(cond ((null pt)
ans)
((consp pt)
(collect-parents1 (car pt)
(collect-parents1 (cdr pt) ans)))
(t
(add-to-set-eql pt ans))))
(defun collect-parents0 (pts ans)
(cond
((null pts) ans)
(t
(collect-parents0 (cdr pts)
(collect-parents1 (car pts) ans)))))
(defun collect-parents (ttree)
; We accumulate in reverse order all the objects (parents) in the pts in the
; ttree. When we create a new poly via linearize, we extract a list of all its
; parents from the poly's 'ttree and store this list in the poly's 'parents
; field. This function does the extracting.
(collect-parents0 (tagged-objects 'pt ttree) nil))
(defun ignore-polyp (parents pt)
; Consider the set, P, of all parents mentioned in the list parents.
; Consider the set, B, of all parents mentioned in the parent tree pt. We
; return t iff P and B have a non-empty intersection. From a more applied
; perspective, assuming parents is the parents list associated with some
; poly, P is the set of literals upon which the poly depends. B is
; generally the set of literals we are to avoid dependence upon. The poly
; should be ignored if it depends on some literal we are to avoid.
(if (null parents)
nil
(or (pt-occur (car parents) pt)
(ignore-polyp (cdr parents) pt))))
(defun to-be-ignoredp1 (pts pt)
(cond ((endp pts) nil)
(t (or (pt-intersectp (car pts) pt)
(to-be-ignoredp1 (cdr pts) pt)))))
(defun to-be-ignoredp (ttree pt)
; Consider the set, P, of all parents mentioned in the 'pt tags of ttree.
; Consider the set, B, of all parents mentioned in the parent tree pt. We
; return t iff P and B have a non-empty intersection. From a more applied
; perspective, assuming ttree is the tree associated with some poly, P is the
; set of literals upon which the poly depends. B is generally the set of
; literals we are to avoid dependence upon. The poly should be ignored if it
; depends on some literal we are to avoid.
; This function was originally written to do the job described above. But then
; Robert Krug suggested the efficiency of maintaining the parents list and
; introduced ignore-polyp. Now this function is only used elsewhere, but the
; above comments still apply mutatis mutandis.
(to-be-ignoredp1 (tagged-objects 'pt ttree) pt))
;=================================================================
; Assumptions
; We are prepared to force assumptions of certain terms by adding
; them to the tag-tree under the 'assumption tag. This is always done
; via force-assumption. All assumptions are embedded in an
; assumption record:
(defrec assumnote (cl-id rune . target) t)
; The cl-id is the clause id (as maintained by the waterfall) of the clause
; currently being worked upon. Rune is either the rune (or a symbol, as per
; force-assumption) that forced this assumption. Target is the term to which
; rune was being applied. Because the :assumnotes field of an assumption is
; always non-nil, there is at least one assumnote in it, but the cl-id field in
; that assumnote might be nil because we do not know the clause id just yet.
; We fill in the :cl-id field later so that we don't have to pass such static
; information all the way down to the places where assumptions are forced.
; When an assumption is generated, it has exactly one assumnote. But later we
; will "merge" assumptions together (actually, delete some via subsumption) and
; when we do we will union the assumnotes together to keep track of why we are
; dealing with that assumption.
(defrec assumption
((type-alist . term) immediatep rewrittenp . assumnotes)
t)
; An assumption record records the fact that we must prove term under
; the assumption of type-alist. Immediatep indicates whether it is
; the user's desire to split the main goal on term immediately
; (immediatep = 'case-split), prove the term under alist immediately
; (t) or delay the proof to a forcing round (nil).
; WARNING: The system can be unsound if immediatep takes on any but
; these three values. In functions like collect-assumptions we assume
; that collecting all the 'case-splits and then collecting all the t's
; gets all the non-nils!
; Assumnotes is involved with explaining to the user what we are doing. It is
; a non-empty list of assumnote records.
; We now turn to the question of whether term has been rewritten or not. If it
; has not been, and we know the context in which rewriting should be tried, it
; is presumably a good idea to try to rewrite term before we try a full-fledged
; proof: a proof requires converting the type-alist and term into a clause and
; then simplifying all the literals of that clause, whereas we expect many
; times that the type-alist will allow term to rewrite to t. One might ask why
; we don't always rewrite before forcing. The answer is simple: type-set
; forces and cannot use the rewriter because it is defined well before the
; rewriter. So type-set forces unrewritten terms often. The problem with the
; simple idea of trying first to prove those terms by rewriting is that REWRITE
; takes many additional context-specifying arguments, the most complicated
; being the simplify-clause-pot-lst. Having set the stage for an explanation,
; we now give it:
; Rewrittenp indicates whether we have already tried to rewrite term. Recall
; that relieve-hyp first rewrites and forces the rewritten term only if
; rewriting fails. Thus, at least within the rewriter, we will see both
; rewritten and unrewritten assumptions coming back in the ttrees we generate.
; Rewrittenp is either a term or nil. If it is a term, forced-term, then it is
; the term we were asked to force and term is the result of rewriting
; forced-term. We use the unrewritten term in a heuristic that sometimes
; throws out supposedly irrelevant hypotheses from the clauses we ultimately
; prove to establish the assumptions. See the discussion of "disguarding." If
; rewrittenp is nil, we have not yet tried to rewrite term and term is
; literally what was forced. The simplifier will collect the unrewritten
; assumptions generated during rewrite and will rewrite them in the
; "appropriate context" as discussed below.
; The view we take is that from within the rewriter, all assumptions are
; rewritten before being forced. That cannot be implemented directly, so
; we do it cleverly, by rewriting them after the force but not telling
; the user. It just seems like a good idea for the rewriter, of all the
; processes, to produce only rewritten assumptions. Now those rewritten
; assumptions aren't maximally rewritten. For example, an assumption
; might rewrite to an if and normalization etc. might produce a provable
; set of assumptions. But we do not use normalization or clausification on
; assumptions until it is time to hit them with the full prover.
; The following record definition is decidedly out of place, belonging as it
; does to the code for forward-chaining. But we must make it now to allow
; us to define contain-assumptionp. This record is documented in comments
; in the essay entitled: "Forward Chaining Derivations - fc-derivation - fcd"
(defrec fc-derivation
(((concl . ttree) . (fn-cnt . p-fn-cnt))
.
((inst-trigger . rune) . (fc-round . unify-subst)))
t)
; WARNING: If you change fc-derivation, go visit the "virtual" declaration of
; the record in simplify.lisp and update the comments. See the essay "Forward
; Chaining Derivations - fc-derivation - fcd".
(mutual-recursion
(defun contains-assumptionp (ttree)
; We return t iff ttree contains an assumption "at some level" where we
; know that 'fc-derivations contain ttrees that may contain assumptions.
; See the discussion in force-assumption.
(or (tagged-objectsp 'assumption ttree)
(contains-assumptionp-fc-derivations
(tagged-objects 'fc-derivation ttree))))
(defun contains-assumptionp-fc-derivations (lst)
(cond ((endp lst) nil)
(t (or (contains-assumptionp (access fc-derivation (car lst) :ttree))
(contains-assumptionp-fc-derivations (cdr lst))))))
)
(defun remove-assumption-entries-from-type-alist (type-alist)
; We delete from type-alist any entry, (term ts . ttree), whose ttree contains
; an assumption. Thus, if ttree2 below is the
; only one of the three to contain an assumption, the type-alist
; ((v1 ts1 . ttree1)(v2 ts2 . ttree2)(v3 ts3 . ttree3))
; is transformed into
; ((v1 ts1 . ttree1)(v3 ts3 . ttree3)).
; It is always sound to delete a hypothesis. See the discussion in
; force-assumption.
(cond
((endp type-alist) nil)
((contains-assumptionp (cddar type-alist))
(remove-assumption-entries-from-type-alist (cdr type-alist)))
(t (cons-with-hint
(car type-alist)
(remove-assumption-entries-from-type-alist (cdr type-alist))
type-alist))))
(defun force-assumption1
(rune target term type-alist rewrittenp immediatep ttree)
(let* ((term (cond ((equal term *nil*)
(er hard 'force-assumption
"Attempt to force nil!"))
((null rune)
(er hard 'force-assumption
"Attempt to force the nil rune!"))
(t term))))
(cond ((not (member-eq immediatep '(t nil case-split)))
(er hard 'force-assumption1
"The :immediatep of an ASSUMPTION record must be ~
t, nil, or 'case-split, but we have tried to create ~
one with ~x0."
immediatep))
(t
(add-to-tag-tree 'assumption
(make assumption
:type-alist type-alist
:term term
:rewrittenp rewrittenp
:immediatep immediatep
:assumnotes
(list (make assumnote
:cl-id nil
:rune rune
:target target)))
ttree)))))
(defun dumb-occur-in-type-alist (var type-alist)
(cond
((null type-alist)
nil)
((dumb-occur var (caar type-alist))
t)
(t
(dumb-occur-in-type-alist var (cdr type-alist)))))
(defun all-dumb-occur-in-type-alist (vars type-alist)
(cond
((null vars)
t)
(t (and (dumb-occur-in-type-alist (car vars) type-alist)
(all-dumb-occur-in-type-alist (cdr vars) type-alist)))))
(defconst *force-xrune*
'(:EXECUTABLE-COUNTERPART FORCE))
(defun force-assumption
(rune target term type-alist rewrittenp immediatep force-flg ttree)
; Warning: Rune may not be a rune! It may be a function symbol.
; This function adds (implies type-alist' term) as an 'assumption to ttree.
; Rewrittenp is either nil, meaning term has not yet been rewritten, or is the
; term that was rewritten to obtain term. Rune is the name of the rule in
; whose behalf term is being assumed, and rune is being applied to the target
; term target. If rune is a symbol then it is actually a primitive
; function symbol and this is a split on the guard of that symbol. There is
; even an exception to that: sometimes rune is the function symbol equal. But
; the guard of equal is t and so is never forced! What is going on? In
; linearize we force term2 to be real if term1 is real and we are
; linearizing (equal term1 term2).
; The type-alist actually stored in the assumption record, type-alist', is not
; type-alist! We remove from type-alist all the entries depending upon
; assumptions. It is legitimate to throw away any hypothesis, thus we can
; delete the entries we choose. Why do we throw out the type-alist entries
; depending on assumptions? The reason is that in the forcing round we
; actually generate a formula representing (implies type-alist' term) and this
; formula does not encode the fact that a given hyp depends upon certain
; assumptions.
; Because assumptions can be generated during forward chaining, the type-alist
; may contain 'fc-derivations tags among its ttrees. These records record how
; a given hypothesis was derived and may itself have 'assumptions in its ttree.
; We therefore consider a ttree to "contain assumptions" if it contains an
; fc-derivation that contains assumptions.
; It could be thought that the creation of type-alist' from type-alist is
; merely an efficiency aimed at saving a few conses. This is not correct.
; This change has a dramatic effect on the size of our ttrees. Before we did
; this, it was possible for a ttree to contain an assumption which (by virtue
; of the :type-alist) contained a ttree which contained an assumption which
; contained a ttree, etc. We have seen this sort of thing nested to depth 9.
; Furthermore, it was frequently the case that a ttree contained some proper
; subttree x which occurred also in an assumption contained in the parent
; ttree. Thus, the ttree x was duplicated. While the parent ttree was small
; (in the sense that it contained on a few nodes) the tree was very large when
; printed, because of this duplication. We have seen a ttree that contained 5
; million nodes (when explored in this root-and-branch way through 'assumptions
; and 'fc-derivations) but which actually was composed of only 100 distinct
; (non-equal) subtrees. Again, one might think this was a problem only if one
; printed out the ttree, but some processes, such as expunge-fc-derivations, do
; root-and-branch exploration. On the tree in question the system simply hung
; up and appeared to be in an infinite loop. This fix keeps ttrees small (even
; when viewed in the root-and-branch way) and is crucial to our practice of
; using them.
; Once upon a time, we allowed rune to be nil. We have since changed that and
; now use the *fake-rune-for-anonymous-enabled-rule* when we don't know a
; better rune. But we have put a check in here to make sure no one uses the
; nil "rune" anymore. Wanting a genuine rune here is just a reflection of the
; output routine that explains the origins of each forcing round.
; Force-flg is known to be non-nil; it may be either t or 'weak. It's tempting
; to allow force-flg = nil and handle that case here (trivially), but the case
; structure in functions like type-set-binary-+ suggests that it's better to
; deal with that case up front, in order to avoid lots of tests that are
; irrelevant (since the same trivial thing happens in all cases when force-flg
; is nil).
; This function is a No-Change Loser, meaning that if it fails and returns nil
; as its first result, it returns the unmodified ttree as its second. Note
; that either force-flg or nil is returned as the first argument; hence, a
; "successful" force with force-flg = 'weak will result in an unchanged
; force-flg being returned. If the first value returned is nil, we are to
; pretend that we weren't allowed to force in the first place.
; At the time of this writing we have temporarily abandoned the idea of
; allowing force-flg to be 'weak: it will always be t or nil. See the comment
; in ok-to-force.
(let ((type-alist (remove-assumption-entries-from-type-alist type-alist))
(ttree (push-lemma *force-xrune* ttree)))
(cond
((not force-flg)
(mv force-flg
(er hard 'force-assumption
"Force-assumption called with null force-flg!")))
; We experimented with allowing force-flg to be 'weak. However, currently
; force-flg is known to be t or nil. See the comment in ok-to-force.
; ((or (eq force-flg t)
; (all-dumb-occur-in-type-alist (all-vars term) type-alist))
; (mv force-flg
; (force-assumption1
; rune target term type-alist rewrittenp immediatep ttree)))
; (t
; (mv nil ttree))
(t (mv force-flg
(force-assumption1
rune target term type-alist rewrittenp immediatep ttree))))))
(defun tag-tree-occur-assumption-nil-1 (lst)
(cond ((endp lst) nil)
((equal (access assumption (car lst) :term)
*nil*)
t)
(t (tag-tree-occur-assumption-nil-1 (cdr lst)))))
(defun tag-tree-occur-assumption-nil (ttree)
; This is just (tag-tree-occur 'assumption <*nil*> ttree) where by <*nil*> we
; mean any assumption record with :term *nil*.
(tag-tree-occur-assumption-nil-1 (tagged-objects 'assumption ttree)))
(defun assumption-free-ttreep (ttree)
; This is a predicate that returns t if ttree contains no 'assumption tag. It
; also checks for 'fc-derivation tags, since they could hide 'assumptions. An
; error-causing version of this function is chk-assumption-free-ttree. Keep
; these two in sync.
; This check is stronger than necessary, of course, since an fc-derivation
; object need not contain an assumption. See also contains-assumptionp (and
; chk-assumption-free-ttree-1) for a slightly more expensive, but more precise,
; check.
(cond ((tagged-objectsp 'assumption ttree) nil)
((tagged-objectsp 'fc-derivation ttree) nil)
(t t)))
; The following assumption is impossible to satisfy and is used as a marker
; that we sometimes put into a ttree to indicate to our caller that the
; attempted force should be abandoned.
(defconst *impossible-assumption*
(make assumption
:type-alist nil
:term *nil*
:rewrittenp *nil*
:immediatep nil ; must be t, nil, or 'case-split
:assumnotes (list (make assumnote
:cl-id nil
:rune *fake-rune-for-anonymous-enabled-rule*
:target *nil*))))
;=================================================================
; We are about to get into the linear arithmetic stuff quite heavily.
; This code started in Nqthm in 1979 and migrated more or less
; untouched into ACL2, with the exception of the addition of the
; rationals. However, around 1998, Robert Krug began working on an
; improved arithmetic book and after a year or so realized he wanted
; to make serious changes in the linear arithmetic procedures.
; Robert's hand is now felt all over this code.
; Essay on the Logical Basis for Linear Arithmetic.
; This essay was written for some early version of ACL2. It still
; applies to the linear arithmetic decision procedure as of Version_2.7,
; although some of the details may need revision.
; We list here the "algebraic laws" we assume. We point back to this
; list from the places we assume them. It is crucial to realize that
; by < and + here we do not mean the familiar "guarded" functions of
; Common Lisp and algebra, but rather the "completed" functions of the
; ACL2 logic. In particular, nonnumeric arguments to + are defaulted
; to 0 and complex numbers may be added to rational ones to yield
; complex ones, etc. The < relation coerces nonnumeric arguments to 0
; and then compares the resulting numbers lexicographically on the
; real and imaginary parts, using the familiar less-than relation on
; the rationals.
; Let us use << as the "familiar" less-than. Then
; (< x y) = (let ((x1 (if (acl2-numberp x) x 0))
; (y1 (if (acl2-numberp y) y 0)))
; (or (<< (realpart x1) (realpart y1))
; (and (= (realpart x1) (realpart y1))
; (<< (imagpart x1) (imagpart y1)))))
; The wonderful thing about this definition, is that it enjoys the algebraic
; laws we need to support linear arithmetic. The "box" below contains the
; complete listing of the algebraic laws supporting linear arithmetic
; ("alsla").
; However, interspersed around them in the box are some events that ACL2's
; completed < and + have the ALSLA properties. To enable us to use the theorem
; prover, we define some new symbols like < and + and prove that those symbols
; have the desired properties. This is a bit tricky because the completed <
; and + must be defined in terms of the partial < and + which work on the
; rationals and complexes, respectively, and we do not want to rely on any
; built in properties of those primitive symbols.