-
Notifications
You must be signed in to change notification settings - Fork 1
/
induct.lisp
3497 lines (2914 loc) · 150 KB
/
induct.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) 2021, 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")
(defun select-x-cl-set (cl-set induct-hint-val)
; This function produces the clause set we explore to collect
; induction candidates. The x in this name means "explore." If
; induct-hint-val is non-nil and non-t, we use the user-supplied
; induction hint value (which, if t means use cl-set); otherwise, we
; use cl-set.
(cond ((null induct-hint-val) cl-set)
((equal induct-hint-val *t*) cl-set)
(t (list (list induct-hint-val)))))
(defun unchangeables (formals args quick-block-info subset ans)
; We compute the set of all variable names occurring in args in
; unchanging measured formal positions. We accumulate the answer onto
; ans.
(cond ((null formals) ans)
((and (member-eq (car formals) subset)
(eq (car quick-block-info) 'unchanging))
(unchangeables (cdr formals) (cdr args) (cdr quick-block-info) subset
(all-vars1 (car args) ans)))
(t
(unchangeables (cdr formals) (cdr args) (cdr quick-block-info) subset
ans))))
(defun changeables (formals args quick-block-info subset ans)
; We compute the args in changing measured formal positions. We
; accumulate the answer onto ans.
(cond ((null formals) ans)
((and (member-eq (car formals) subset)
(not (eq (car quick-block-info) 'unchanging)))
(changeables (cdr formals) (cdr args) (cdr quick-block-info)
subset
(cons (car args) ans)))
(t
(changeables (cdr formals) (cdr args) (cdr quick-block-info)
subset
ans))))
(defun sound-induction-principle-mask1 (formals args quick-block-info
subset
unchangeables
changeables)
; See sound-induction-principle-mask.
(cond
((null formals) nil)
(t (let ((var (car formals))
(arg (car args))
(q (car quick-block-info)))
(mv-let (mask-ele new-unchangeables new-changeables)
(cond ((member-eq var subset)
(cond ((eq q 'unchanging)
(mv 'unchangeable unchangeables changeables))
(t (mv 'changeable unchangeables changeables))))
((and (variablep arg)
(eq q 'unchanging))
(cond ((member-eq arg changeables)
(mv nil unchangeables changeables))
(t (mv 'unchangeable
(add-to-set-eq arg unchangeables)
changeables))))
((and (variablep arg)
(not (member-eq arg changeables))
(not (member-eq arg unchangeables)))
(mv 'changeable
unchangeables
(cons arg changeables)))
(t (mv nil unchangeables changeables)))
(cons mask-ele
(sound-induction-principle-mask1 (cdr formals)
(cdr args)
(cdr quick-block-info)
subset
new-unchangeables
new-changeables)))))))
(defun sound-induction-principle-mask (term formals quick-block-info subset)
; Term is a call of some fn on some args. The formals and
; quick-block-info are those of fn, and subset is one of fn's measured
; subset (a subset of formals). We wish to determine, in the
; terminology of ACL, whether the induction applies to term. If so we
; return a mask indicating how to build the substitutions for the
; induction from args and the machine for fn. Otherwise we return
; nil.
; Let the changeables be those args that are in measured formal
; positions that sometimes change in the recursion. Let the
; unchangeables be all of the variables occurring in measured actuals
; that never change in recursion. The induction applies if
; changeables is a sequence of distinct variable names and has an
; empty intersection with unchangeables.
; If the induction is applicable then the substitutions should
; substitute for the changeables just as the recursion would, and hold
; each unchangeable fixed -- i.e., substitute each for itself. With
; such substitutions it is possible to prove the measure lemmas
; analogous to those proved when justification of subset was stored,
; except that the measure is obtained by instantiating the measure
; term used in the justification by the measured actuals in unchanging
; slots. Actual variables that are neither among the changeables or
; unchangeables may be substituted for arbitrarily.
; If the induction is applicable we return a mask with as many
; elements as there are args. For each arg the mask contains either
; 'changeable, 'unchangeable, or nil. 'Changeable means the arg
; should be instantiated as specified in the recursion. 'Unchangeable
; means each var in the actual should be held fixed. Nil means that
; the corresponding substitution pairs in the machine for the function
; should be ignored.
; Abstractly, this function builds the mask by first putting either
; 'changeable or 'unchangeable in each measured slot. It then fills
; in the remaining slots from the left so as to permit the actual to
; be instantiated or held fixed as desired by the recursion, provided
; that in so doing it does not permit substitutions for previously
; allocated actuals.
(let ((unchangeables
(unchangeables formals (fargs term) quick-block-info subset nil))
(changeables
(changeables formals (fargs term) quick-block-info subset nil)))
(cond ((or (not (no-duplicatesp-equal changeables))
(not (all-variablep changeables))
(intersectp-eq changeables unchangeables))
nil)
(t (sound-induction-principle-mask1 formals
(fargs term)
quick-block-info
subset
unchangeables
changeables)))))
(defrec candidate
(score controllers changed-vars unchangeable-vars
tests-and-alists-lst justification induction-term other-terms
xinduction-term xother-terms xancestry
ttree)
nil)
; This record is used to represent one of the plausible inductions that must be
; considered. The SCORE represents some fairly artificial estimation of how
; many terms in the conjecture want this induction. CONTROLLERS is the list of
; the controllers -- including unchanging controllers -- for all the inductions
; merged to form this one. The CHANGED-VARS is a list of all those variables
; that will be instantiated (non-identically) in some induction hypotheses.
; Thus, CHANGED-VARS include both variables that actually contribute to why
; some measure goes down and variables like accumulators that are just along
; for the ride. UNCHANGEABLE-VARS is a list of all those vars which may not be
; changed by any substitution if this induction is to be sound for the reasons
; specified. TESTS-AND-ALISTS-LST is a list of TESTS-AND-ALISTS which
; indicates the case analysis for the induction and how the various induction
; hypotheses are obtained via substitutions. JUSTIFICATION is the
; JUSTIFICATION that justifies this induction, and INDUCTION-TERM is the term
; that suggested this induction and from which you obtain the actuals to
; substitute into the template. OTHER-TERMS are the induction-terms of
; candidates that have been merged into this one for heuristic reasons.
; Because of induction rules we can think of some terms being aliases for
; others. We have to make a distinction between the terms in the conjecture
; that suggested an induction and the actual terms that suggested the
; induction. For example, if an induction rule maps (fn x y) to (recur x y),
; then (recur x y) will be the INDUCTION-TERM because it suggested the
; induction and we will, perhaps, have to recover its induction machine or
; quick block information to implement various heuristics. But we do not wish
; to report (recur x y) to the user, preferring instead to report (fn x y).
; Therefore, corresponding to INDUCTION-TERM and OTHER-TERMS we have
; XINDUCTION-TERM and XOTHER-TERMS, which are maintained in exactly the same
; way as their counterparts but which deal completely with the user-level view
; of the induction. In practice this means that we will initialize the
; XINDUCTION-TERM field of a candidate with the term from the conjecture,
; initialize the INDUCTION-TERM with its alias, and then merge the fields
; completely independently but analogously. XANCESTRY is a field maintained by
; merging to contain the user-level terms that caused us to change our
; tests-and-alists. (Some candidates may be flushed or merged into this one
; without changing it.)
; The ttree of a candidate contains 'LEMMA tags listing the :induction rules
; (if any) involved in the suggestion of the candidate.
(defun count-non-nils (lst)
(cond ((null lst) 0)
((car lst) (1+ (count-non-nils (cdr lst))))
(t (count-non-nils (cdr lst)))))
(defun controllers (formals args subset ans)
(cond ((null formals) ans)
((member (car formals) subset)
(controllers (cdr formals) (cdr args) subset
(all-vars1 (car args) ans)))
(t (controllers (cdr formals) (cdr args) subset ans))))
(defun changed/unchanged-vars (x args mask ans)
(cond ((null mask) ans)
((eq (car mask) x)
(changed/unchanged-vars x (cdr args) (cdr mask)
(all-vars1 (car args) ans)))
(t (changed/unchanged-vars x (cdr args) (cdr mask) ans))))
(defrec tests-and-alists (tests alists) nil)
(defun tests-and-alists/alist (alist args mask call-args)
; Alist is an alist that maps the formals of some fn to its actuals,
; args. Mask is a sound-induction-principle-mask indicating the args
; for which we will build substitution pairs. Call-args is the list of
; arguments to some recursive call of fn occurring in the induction
; machine for fn. We build an alist mapping the masked args to the
; instantiations (under alist) of the values in call-args.
(cond
((null mask) nil)
((null (car mask))
(tests-and-alists/alist alist (cdr args) (cdr mask) (cdr call-args)))
((eq (car mask) 'changeable)
(cons (cons (car args)
(sublis-var alist (car call-args)))
(tests-and-alists/alist alist
(cdr args)
(cdr mask)
(cdr call-args))))
(t (let ((vars (all-vars (car args))))
(append (pairlis$ vars vars)
(tests-and-alists/alist alist
(cdr args)
(cdr mask)
(cdr call-args)))))))
(defun tests-and-alists/alists (alist args mask calls)
; Alist is an alist that maps the formals of some fn to its actuals,
; args. Mask is a sound-induction-principle-mask indicating the args
; for which we will build substitution pairs. Calls is the list of
; calls for a given case of the induction machine. We build the alists
; from those calls.
(cond
((null calls) nil)
(t (cons (tests-and-alists/alist alist args mask (fargs (car calls)))
(tests-and-alists/alists alist args mask (cdr calls))))))
; The following record contains the tests leading to a collection of
; recursive calls at the end of a branch through a defun. In general,
; the calls may not be to the function being defuned but rather to
; some other function in the same mutually recursive clique, but in
; the context of induction we know that all the calls are to the same
; fn because we haven't implemented mutually recursive inductions yet.
; A list of these records constitute the induction machine for a function.
(defrec tests-and-calls (tests . calls) nil)
; The justification record contains a subset of the formals of the function
; under which it is stored. Only the subset, ruler-extenders, and subversive-p
; fields have semantic content! The other fields are the domain predicate, mp;
; the relation, rel, which is well-founded on mp objects; and the mp-measure
; term which has been proved to decrease in the recursion. The latter three
; fields are correct at the time the function is admitted, but note that they
; might all be local and hence have disappeared by the time these fields are
; read. Thus, we include them only for heuristic purposes, for example as used
; in community book books/workshops/2004/legato/support/generic-theories.lisp.
; A list of justification records is stored under each function symbol by the
; defun principle.
(defrec justification
(subset . ((subversive-p . (mp . rel))
(measure . ruler-extenders)))
nil)
(defun tests-and-alists (alist args mask tc)
; Alist is an alist that maps the formals of some fn to its actuals,
; args. Mask is a sound-induction-principle-mask indicating the args
; for which we will build substitution pairs. Tc is one of the
; tests-and-calls in the induction machine for the function. We make
; a tests-and-alists record containing the instantiated tests of tc
; and alists derived from the calls of tc.
(make tests-and-alists
:tests (sublis-var-lst alist (access tests-and-calls tc :tests))
:alists (tests-and-alists/alists alist
args
mask
(access tests-and-calls tc :calls))))
(defun tests-and-alists-lst (alist args mask machine)
; We build a list of tests-and-alists from machine, instantiating it
; with alist, which is a map from the formals of the function to the
; actuals, args. Mask is the sound-induction-principle-mask that
; indicates the args for which we substitute.
(cond
((null machine) nil)
(t (cons (tests-and-alists alist args mask (car machine))
(tests-and-alists-lst alist args mask (cdr machine))))))
(defun flesh-out-induction-principle (term formals justification mask machine
xterm ttree)
; Term is a call of some function the indicated formals and induction machine.
; Justification is its 'justification and mask is a sound-induction-
; principle-mask for the term. We build the induction candidate suggested by
; term.
(make candidate
:score
(/ (count-non-nils mask) (length mask))
:controllers
(controllers formals (fargs term)
(access justification justification :subset)
nil)
:changed-vars
(changed/unchanged-vars 'changeable (fargs term) mask nil)
:unchangeable-vars
(changed/unchanged-vars 'unchangeable (fargs term) mask nil)
:tests-and-alists-lst
(tests-and-alists-lst (pairlis$ formals (fargs term))
(fargs term) mask machine)
:justification justification
:induction-term term
:xinduction-term xterm
:other-terms nil
:xother-terms nil
:xancestry nil
:ttree ttree))
(defun intrinsic-suggested-induction-cand
(induction-rune term formals quick-block-info justification machine xterm
ttree)
; Note: An "intrinsically suggested" induction scheme is an induction scheme
; suggested by a justification of a recursive function. The rune controlling
; the intrinsic suggestion from the justification of fn is (:induction fn). We
; distinguish between intrinsically suggested inductions and those suggested
; via records of induction-rule type. Intrinsic inductions have no embodiment
; as induction-rules but are, instead, the basis of the semantics of such
; rules. That is, the inductions suggested by (fn x y) is the union of those
; suggested by the terms to which it is linked via induction-rules together
; with the intrinsic suggestion for (fn x y).
; Term, above, is a call of some fn with the given formals, quick-block-info,
; justification and induction machine. We return a list of induction
; candidates, said list either being empty or the singleton list containing the
; induction candidate intrinsically suggested by term. Xterm is logically
; unrelated to term and is the term appearing in the original conjecture from
; which we (somehow) obtained term for consideration.
(let ((mask (sound-induction-principle-mask term formals
quick-block-info
(access justification
justification
:subset))))
(cond
(mask
(list (flesh-out-induction-principle term formals
justification
mask
machine
xterm
(push-lemma induction-rune
ttree))))
(t nil))))
; The following is now defined in rewrite.lisp.
; (defrec induction-rule (nume (pattern . condition) scheme . rune) nil)
(mutual-recursion
(defun apply-induction-rule (rule term type-alist xterm ttree seen ens wrld)
; We apply the induction-rule, rule, to term, and return a possibly empty list
; of suggested inductions. The basic idea is to check that the rule is enabled
; and that the :pattern of the rule matches term. If so, we check that the
; :condition of the rule is true under the current type-alist. This check is
; heuristic only and so we indicate that the guards have been checked and we
; allow forcing. We ignore the ttree because we are making a heuristic choice
; only. If type-set says the :condition is non-nil, we fire the rule by
; instantiating the :scheme and recursively getting the suggested inductions
; for that term. Note that we are not recursively exploring the instantiated
; scheme but just getting the inductions suggested by its top-level function
; symbol.
; Seen is a list of numes of induction-rules already encountered, used in order
; to prevent infinite loops. The following are two examples that looped before
; the use of this list of numes seen.
; From Daron Vroon:
; (defun zip1 (xs ys)
; (declare (xargs :measure (acl2-count xs)))
; (cond ((endp xs) nil)
; ((endp ys) nil)
; (t (cons (cons (car xs) (car ys)) (zip1 (cdr xs) (cdr ys))))))
; (defun zip2 (xs ys)
; (declare (xargs :measure (acl2-count ys)))
; (cond ((endp xs) nil)
; ((endp ys) nil)
; (t (cons (cons (car xs) (car ys)) (zip2 (cdr xs) (cdr ys))))))
; (defthm zip1-zip2-ind
; t
; :rule-classes ((:induction :pattern (zip1 xs ys)
; :scheme (zip2 xs ys))))
; (defthm zip2-zip1-ind
; t
; :rule-classes ((:induction :pattern (zip2 xs ys)
; :scheme (zip1 xs ys))))
; (defstub foo (*) => *)
; ;; the following overflows the stack.
; (thm
; (= (zip1 (foo xs) ys)
; (zip2 (foo xs) ys)))
; From Pete Manolios:
; (defun app (x y)
; (if (endp x)
; y
; (cons (car x) (app (cdr x) y))))
; (defthm app-ind
; t
; :rule-classes ((:induction :pattern (app x y)
; :condition (and (true-listp x) (true-listp y))
; :scheme (app x y))))
; (in-theory (disable (:induction app)))
; (defthm app-associative
; (implies (and (true-listp a)
; (true-listp b)
; (true-listp c))
; (equal (app (app a b) c)
; (app a (app b c)))))
(let ((nume (access induction-rule rule :nume)))
(cond
((and (not (member nume seen))
(enabled-numep nume ens))
(mv-let
(ans alist)
(one-way-unify (access induction-rule rule :pattern)
term)
(cond
(ans
(with-accumulated-persistence
(access induction-rule rule :rune)
(suggestions)
suggestions
(mv-let
(ts ttree1)
(type-set (sublis-var alist
(access induction-rule rule :condition))
t nil type-alist ens wrld nil nil nil)
(declare (ignore ttree1))
(cond
((ts-intersectp *ts-nil* ts) nil)
(t (let ((term1 (sublis-var alist
(access induction-rule rule :scheme))))
(cond ((or (variablep term1)
(fquotep term1))
nil)
(t (suggested-induction-cands term1 type-alist
xterm
(push-lemma
(access induction-rule
rule
:rune)
ttree)
nil ; eflg
(cons nume seen)
ens wrld)))))))))
(t nil))))
(t nil))))
(defun suggested-induction-cands1
(induction-rules term type-alist xterm ttree eflg seen ens wrld)
; We map down induction-rules and apply each enabled rule to term, which is
; known to be an application of the function symbol fn to some args. Each rule
; gives us a possibly empty list of suggested inductions. We append all these
; suggestions together. In addition, if fn is recursively defined and is
; enabled (or, even if fn is disabled if we are exploring a user-supplied
; induction hint) we collect the intrinsic suggestion for term as well.
; Seen is a list of numes of induction-rules already encountered, used in order
; to prevent infinite loops.
; Eflg represents an "enable flag". When true, an induction rune that
; represents the induction scheme of a recursive definition must be enabled in
; order to be applicable. When false (nil), it may be applied regardless.
(cond
((null induction-rules)
(let* ((fn (ffn-symb term))
(machine (getpropc fn 'induction-machine nil wrld)))
(cond
((null machine) nil)
(t
; Historical note: Before Version_2.6 we had the following note:
; Note: The intrinsic suggestion will be non-nil only if (:INDUCTION fn) is
; enabled and so here we have a case in which two runes have to be enabled
; (the :DEFINITION and the :INDUCTION runes) to get the desired effect. It
; is not clear if this is a good design but at first sight it seems to
; provide upward compatibility because in Nqthm a disabled function suggests
; no inductions.
; We no longer make any such requirement: the test above (t) replaces the
; following.
; (or (enabled-fnp fn nil ens wrld)
; (and induct-hint-val
; (not (equal induct-hint-val *t*))))
(let ((induction-rune (list :induction (ffn-symb term))))
(and (or (null eflg)
(enabled-runep induction-rune ens wrld))
(intrinsic-suggested-induction-cand
induction-rune
term
(formals fn wrld)
(getpropc fn 'quick-block-info
'(:error "See SUGGESTED-INDUCTION-CANDS1.")
wrld)
(getpropc fn 'justification
'(:error "See SUGGESTED-INDUCTION-CANDS1.")
wrld)
machine
xterm
ttree)))))))
(t (append (apply-induction-rule (car induction-rules)
term type-alist
xterm ttree seen ens wrld)
(suggested-induction-cands1 (cdr induction-rules)
term type-alist
xterm ttree eflg seen ens wrld)))))
(defun suggested-induction-cands
(term type-alist xterm ttree eflg seen ens wrld)
; Term is some fn applied to args. Xterm is some term occurring in the
; conjecture we are exploring and is the term upon which this induction
; suggestion will be "blamed" and from which we have obtained term via
; induction-rules. We return all of the induction candidates suggested by
; term. Lambda applications suggest no inductions. Disabled functions suggest
; no inductions -- unless we are applying the user's induct hint value (in
; which case we, quite reasonably, assume every function in the value is worthy
; of analysis since any function could have been omitted).
; Seen is a list of numes of induction-rules already encountered, used in order
; to prevent infinite loops.
(cond
((flambdap (ffn-symb term)) nil)
(t (suggested-induction-cands1
(getpropc (ffn-symb term) 'induction-rules nil wrld)
term type-alist xterm ttree eflg seen ens wrld))))
)
(mutual-recursion
(defun get-induction-cands (term type-alist ens wrld ans)
; We explore term and accumulate onto ans all of the induction
; candidates suggested by it.
(cond ((variablep term) ans)
((fquotep term) ans)
((eq (ffn-symb term) 'hide)
ans)
(t (get-induction-cands-lst
(fargs term)
type-alist ens wrld
(append (suggested-induction-cands term type-alist
term nil
t ; eflg
nil ens wrld)
ans)))))
(defun get-induction-cands-lst (lst type-alist ens wrld ans)
; We explore the list of terms, lst, and accumulate onto ans all of
; the induction candidates.
(cond ((null lst) ans)
(t (get-induction-cands
(car lst)
type-alist ens wrld
(get-induction-cands-lst
(cdr lst)
type-alist ens wrld ans)))))
)
(defun get-induction-cands-from-cl-set1 (cl-set ens oncep-override wrld state
ans)
; We explore cl-set and accumulate onto ans all of the induction
; candidates.
(cond
((null cl-set) ans)
(t (mv-let (contradictionp type-alist fc-pairs)
(forward-chain-top 'induct
(car cl-set) nil t
nil ; do-not-reconsiderp
wrld ens oncep-override state)
; We need a type-alist with which to compute induction aliases. It is of
; heuristic use only, so we may as well turn the force-flg on. We assume no
; contradiction is found. If by chance one is, then type-alist is nil, which
; is an acceptable type-alist.
(declare (ignore contradictionp fc-pairs))
(get-induction-cands-lst
(car cl-set)
type-alist ens wrld
(get-induction-cands-from-cl-set1 (cdr cl-set)
ens oncep-override wrld state
ans))))))
(defun get-induction-cands-from-cl-set (cl-set pspv wrld state)
; We explore cl-set and collect all induction candidates.
(let ((rcnst (access prove-spec-var pspv :rewrite-constant)))
(get-induction-cands-from-cl-set1 cl-set
(access rewrite-constant
rcnst
:current-enabled-structure)
(access rewrite-constant
rcnst
:oncep-override)
wrld
state
nil)))
; That completes the development of the code for exploring a clause set
; and gathering the induction candidates suggested.
; Section: Pigeon-Holep
; We next develop pigeon-holep, which tries to fit some "pigeons" into
; some "holes" using a function to determine the sense of the word
; "fit". Since ACL2 is first-order we can't pass arbitrary functions
; and hence we pass symbols and define our own special-purpose "apply"
; that interprets the particular symbols passed to calls of
; pigeon-holep.
; However, it turns out that the applications of pigeon-holep require
; passing functions that themselves call pigeon-holep. And so
; pigeon-holep-apply is mutually recursive with pigeon-holep (but only
; because the application functions use pigeon-holep).
(mutual-recursion
(defun pigeon-holep-apply (fn pigeon hole)
; See pigeon-holep for the problem and terminology. This function
; takes a symbol denoting a predicate and two arguments. It applies
; the predicate to the arguments. When the predicate holds we say
; the pigeon argument "fits" into the hole argument.
(case fn
(pair-fitp
; This predicate is applied to two pairs, each taken from two substitutions.
; We say (v1 . term1) (the "pigeon") fits into (v2 . term2) (the "hole")
; if v1 is v2 and term1 occurs in term2.
(and (eq (car pigeon) (car hole))
(occur (cdr pigeon) (cdr hole))))
(alist-fitp
; This predicate is applied to two substitutions. We say the pigeon
; alist fits into the hole alist if each pair of the pigeon fits into
; a pair of the hole and no pair of the hole is used more than once.
(pigeon-holep pigeon hole nil 'pair-fitp))
(tests-and-alists-fitp
; This predicate is applied to two tests-and-alists records. The
; first fits into the second if the tests of the first are a subset
; of those of the second and either they are both base cases (i.e., have
; no alists) or each substitution of the first fits into a substitution of
; the second, using no substitution of the second more than once.
(and (subsetp-equal (access tests-and-alists pigeon :tests)
(access tests-and-alists hole :tests))
(or (and (null (access tests-and-alists pigeon :alists))
(null (access tests-and-alists hole :alists)))
(pigeon-holep (access tests-and-alists pigeon :alists)
(access tests-and-alists hole :alists)
nil
'alist-fitp))))))
(defun pigeon-holep (pigeons holes filled-holes fn)
; Both pigeons and holes are lists of arbitrary objects. The holes
; are implicitly enumerated left-to-right from 0. Filled-holes is a
; list of the indices of holes we consider "filled." Fn is a
; predicate known to pigeon-holep-apply. If fn applied to a pigeon and
; a hole is non-nil, then we say the pigeon "fits" into the hole. We
; can only "put" a pigeon into a hole if the hole is unfilled and the
; pigeon fits. The act of putting the pigeon into the hole causes the
; hole to become filled. We return t iff it is possible to put each
; pigeon into a hole under these rules.
(cond
((null pigeons) t)
(t (pigeon-holep1 (car pigeons)
(cdr pigeons)
holes 0
holes filled-holes fn))))
(defun pigeon-holep1 (pigeon pigeons lst n holes filled-holes fn)
; Lst is a terminal sublist of holes, whose first element has index n.
; We map over lst looking for an unfilled hole h such that (a) we can
; put pigeon into h and (b) we can dispose of the rest of the pigeons
; after filling h.
(cond
((null lst) nil)
((member n filled-holes)
(pigeon-holep1 pigeon pigeons (cdr lst) (1+ n) holes filled-holes fn))
((and (pigeon-holep-apply fn pigeon (car lst))
(pigeon-holep pigeons holes
(cons n filled-holes)
fn))
t)
(t (pigeon-holep1 pigeon pigeons (cdr lst) (1+ n)
holes filled-holes fn))))
)
(defun flush-cand1-down-cand2 (cand1 cand2)
; This function takes two induction candidates and determines whether
; the first is subsumed by the second. If so, it constructs a new
; candidate that is logically equivalent (vis a vis the induction
; suggested) to the second but which now carries with it the weight
; and heuristic burdens of the first.
(cond
((and (subsetp-eq (access candidate cand1 :changed-vars)
(access candidate cand2 :changed-vars))
(subsetp-eq (access candidate cand1 :unchangeable-vars)
(access candidate cand2 :unchangeable-vars))
(pigeon-holep (access candidate cand1 :tests-and-alists-lst)
(access candidate cand2 :tests-and-alists-lst)
nil
'tests-and-alists-fitp))
(change candidate cand2
:score (+ (access candidate cand1 :score)
(access candidate cand2 :score))
:controllers (union-eq (access candidate cand1 :controllers)
(access candidate cand2 :controllers))
:other-terms (add-to-set-equal
(access candidate cand1 :induction-term)
(union-equal
(access candidate cand1 :other-terms)
(access candidate cand2 :other-terms)))
:xother-terms (add-to-set-equal
(access candidate cand1 :xinduction-term)
(union-equal
(access candidate cand1 :xother-terms)
(access candidate cand2 :xother-terms)))
:ttree (cons-tag-trees (access candidate cand1 :ttree)
(access candidate cand2 :ttree))))
(t nil)))
(defun flush-candidates (cand1 cand2)
; This function determines whether one of the two induction candidates
; given flushes down the other and if so returns the appropriate
; new candidate. This function is a mate and merge function used
; by m&m and is hence known to m&m-apply.
(or (flush-cand1-down-cand2 cand1 cand2)
(flush-cand1-down-cand2 cand2 cand1)))
; We now begin the development of the merging of two induction
; candidates. The basic idea is that if two functions both replace x
; by x', and one of them simultaneously replaces a by a' while the
; other replaces b by b', then we should consider inducting on x, a,
; and b, by x', a', and b'. Of course, by the time we get here, the
; recursion is coded into substitution alists: ((x . x') (a . a')) and
; ((x . x') (b . b')). We merge these two alists into ((x . x') (a .
; a') (b . b')). The merge of two sufficiently compatible alists is
; accomplished by just unioning them together.
; The key ideas are (1) recognizing when two alists are describing the
; "same" recursive step (i.e., they are both replacing x by x', where
; x is somehow a key variable); (2) observing that they do not
; explicitly disagree on what to do with the other variables.
(defun alists-agreep (alist1 alist2 vars)
; Two alists agree on vars iff for each var in vars the image of var under
; the first alist is equal to that under the second.
(cond ((null vars) t)
((equal (let ((temp (assoc-eq (car vars) alist1)))
(cond (temp (cdr temp))(t (car vars))))
(let ((temp (assoc-eq (car vars) alist2)))
(cond (temp (cdr temp))(t (car vars)))))
(alists-agreep alist1 alist2 (cdr vars)))
(t nil)))
(defun irreconcilable-alistsp (alist1 alist2)
; Two alists are irreconcilable iff there is a var v that they both
; explicitly map to different values. Put another way, there exists a
; v such that (v . a) is a member of alist1 and (v . b) is a member of
; alist2, where a and b are different. If two substitutions are
; reconcilable then their union is a substitution.
; We rely on the fact that this function return t or nil.
(cond ((null alist1) nil)
(t (let ((temp (assoc-eq (caar alist1) alist2)))
(cond ((null temp)
(irreconcilable-alistsp (cdr alist1) alist2))
((equal (cdar alist1) (cdr temp))
(irreconcilable-alistsp (cdr alist1) alist2))
(t t))))))
(defun affinity (aff alist1 alist2 vars)
; We say two alists that agree on vars but are irreconcilable are
; "antagonists". Two alists that agree on vars and are not irreconcilable
; are "mates".
; Aff is either 'antagonists or 'mates and denotes one of the two relations
; above. We return t iff the other args are in the given relation.
(and (alists-agreep alist1 alist2 vars)
(eq (irreconcilable-alistsp alist1 alist2)
(eq aff 'antagonists))))
(defun member-affinity (aff alist alist-lst vars)
; We determine if some member of alist-lst has the given affinity with alist.
(cond ((null alist-lst) nil)
((affinity aff alist (car alist-lst) vars)
t)
(t (member-affinity aff alist (cdr alist-lst) vars))))
(defun occur-affinity (aff alist lst vars)
; Lst is a list of tests-and-alists. We determine whether alist has
; the given affinity with some alist in lst. We call this occur
; because we are looking inside the elements of lst. But it is
; technically a misnomer because we don't look inside recursively; we
; treat lst as though it were a list of lists.
(cond
((null lst) nil)
((member-affinity aff alist
(access tests-and-alists (car lst) :alists)
vars)
t)
(t (occur-affinity aff alist (cdr lst) vars))))
(defun some-occur-affinity (aff alists lst vars)
; Lst is a list of tests-and-alists. We determine whether some alist
; in alists has the given affinity with some alist in lst.
(cond ((null alists) nil)
(t (or (occur-affinity aff (car alists) lst vars)
(some-occur-affinity aff (cdr alists) lst vars)))))
(defun all-occur-affinity (aff alists lst vars)
; Lst is a list of tests-and-alists. We determine whether every alist
; in alists has the given affinity with some alist in lst.
(cond ((null alists) t)
(t (and (occur-affinity aff (car alists) lst vars)
(all-occur-affinity aff (cdr alists) lst vars)))))
(defun contains-affinity (aff lst vars)
; We determine if two members of lst have the given affinity.
(cond ((null lst) nil)
((member-affinity aff (car lst) (cdr lst) vars) t)
(t (contains-affinity aff (cdr lst) vars))))
; So much for general-purpose scanners. We now develop the predicates
; that are used to determine if we can merge lst1 into lst2 on vars.
; See merge-tests-and-alists-lsts for extensive comments on the ideas
; behind the following functions.
(defun antagonistic-tests-and-alists-lstp (lst vars)
; Lst is a list of tests-and-alists. Consider just the set of all
; alists in lst. We return t iff that set contains an antagonistic
; pair.
; We operate as follows. Each element of lst contains some alists.
; We take the first element and ask whether its alists contain an
; antagonistic pair. If so, we're done. Otherwise, we ask whether
; any alist in that first element is antagonistic with the alists in
; the rest of lst. If so, we're done. Otherwise, we recursively
; look at the rest of lst.
(cond
((null lst) nil)
(t (or (contains-affinity
'antagonists
(access tests-and-alists (car lst) :alists)
vars)
(some-occur-affinity
'antagonists
(access tests-and-alists (car lst) :alists)
(cdr lst)
vars)
(antagonistic-tests-and-alists-lstp (cdr lst) vars)))))
(defun antagonistic-tests-and-alists-lstsp (lst1 lst2 vars)
; Both lst1 and lst2 are lists of tests-and-alists. We determine whether
; there exists an alist1 in lst1 and an alist2 in lst2 such that alist1
; and alist2 are antagonists.
(cond
((null lst1) nil)
(t (or (some-occur-affinity
'antagonists
(access tests-and-alists (car lst1) :alists)
lst2
vars)
(antagonistic-tests-and-alists-lstsp (cdr lst1) lst2 vars)))))
(defun every-alist1-matedp (lst1 lst2 vars)
; Both lst1 and lst2 are lists of tests-and-alists. We determine for every
; alist1 in lst1 there exists an alist2 in lst2 that agrees with alist1 on
; vars and that is not irreconcilable.
(cond ((null lst1) t)
(t (and (all-occur-affinity
'mates
(access tests-and-alists (car lst1) :alists)
lst2
vars)
(every-alist1-matedp (cdr lst1) lst2 vars)))))
; The functions above are used to determine that lst1 and lst2 contain
; no antagonistic pairs, that every alist in lst1 has a mate somewhere in