-
Notifications
You must be signed in to change notification settings - Fork 0
/
ps.lisp
1525 lines (1381 loc) · 52.7 KB
/
ps.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
;;; *The PS production system*
;;;
;;;Implementation of a small Production System based on XPS developed by
;;;Eugene Charniak at al, and presented in the authors' book "Artificial
;;;Intelligence Programming, Second Edition"
;;;
;;;Although the base framework of the original program is retained, substantial
;;;modifications have been introduced to the system to make it more efficient.
;;;
;;;Modifications made:
;;; 1, Rules are represented by KR objects.
;;; The behavior of the individual rules are inherited from the
;;; prototype object Ps-Rule and accessed by sending messages
;;; to the rule-objects.
;;; Main advantage:
;;; - Dynamic Rule modification can be achieved.
;;; That is the rules can be modified at run time for machine
;;; learning purposes.
;;;
;;; 2, Rule structure is modified.
;;; a, Explicit control by salience values is introduced.
;;; b, Rule documentation added.
;;; c, Several modification in naming convention is made to make the
;;; system more CLIPS-like. (CLIPS is a Production System
;;; developed by NASA at the Artificial Intelligence Section of
;;; the Johnson Space Centre.)
;;;
;;; 3, Conflict resolution is modified to use the new salience control.
;;;
;;; 4, Rule Sets (Rule buckets) are introduced to be able to separate
;;; independent knowledge sets. This modification makes the overall speed
;;; of execution considerable faster.
;;;
;;; 5, Concurrent Rule Firing is introduced.
;;; This method can be used when the order in which satisfied rules are
;;; fired is not important. That is when the firing of rules
;;; does not modify the agenda by changing the state of the working
;;; memory such a way that the previously satisfied rules are not
;;; satisfied any more.
;;; (By using this method a speed up of 250-300% has been achieved
;;; with the feature recognition knowledge base.)
;;;
;;; 5, Rules are linked to the blackboard on both sides of the rule.
;;; That is the rules can access the declarative knowledge represented by
;;; objects, consequently the system speed is increased substantially
;;; by eliminating unnecessary pattern matching.
;;;
;;; 6, Watch command is introduced to get appropriate debugging information.
;;; The state of execution can be watched to debug the knowledge base.
;;;
;;; 7, Unnecessary rules can be retracted by the Excise-Rule command.
;;;
;;;The syntax of defining rules in PS:
;;;
;;; (DEFRULE <rule-set.rule-name> "documentation string"
;;; { (:DECLARE-SALIENCE salience-value) | nil }
;;; <<patterns>> ;Left-Hand Side (LHS) or prerequisite of the rule.
;;; ->
;;; <<Actions>>) ;Right-Hand Side (RHS) of the rule.
;;; ;The hole LISP environment can be used on this
;;; ;side of the rule. That feature makes PS a very
;;; ;effective rule based system. LHS of the rules
;;; ;can be compiled which makes execution faster.
;;;
;;;Eg:(Defrule Raw-Chucking.Barstock-Chucking-Rule-2
;;; "Rule to establish chucking position for the raw material"
;;; (:Declare-Salience 100) ;Rule priority is high.
;;; (assign ?a (exist ?part)) ;If part exists assign it to ?a
;;; (exist ?raw-material) ;Raw material is known
;;; (is-a-p ?raw-material barstock) ;Raw material is a BARSTOCK
;;; (assign ?b (position ?pos)) ;Bar stock position is known
;;; (assign ?c (chuck ?chuck)) ;Chuck is known assign it to ?c
;;; (between (get-diameter ?raw-material)
;;; (get-min-chucking-diameter ?chuck) 30)
;;; ;Raw material diameter is between the minimum
;;; ;chucking diameter of the chuck used and 30
;;; -> ;then
;;; (retract a b c) ;Retract a, b and c from working
;;; ;memory
;;; (set-base-position !raw-material ;Leave 0.5 mm for facing
;;; (+ (get-part-length !part) !pos 1))
;;; (store '(goal centering-method)));Store that centreing method
;;; ;is to be established next
(declaim (optimize speed (safety 0)))
(in-package "PS")
(enable-reader-macros)
;;; *Working Memory*
;;;
;;;Working memory is organised into a discrimination net represented
;;;by a key and list of sublinks to the next nodes.
;;;
(defstruct Link (key nil) (link-list nil))
;;;
;;;(Link-Contents link) returns the contents of the node to which the link
;;;points.
;;;
(defun Link-Contents (link) (link-link-list link))
(defsetf Link-Contents (link) (value)
`(setf (link-link-list ,link) ,value))
;;;
;;;*Working-Memory* is initialised by creating the empty discrimination net.
;;;
(defvar *working-memory* (make-link))
;;;
;;;Working memory elements are represented by a structure consisting of a
;;;time-tag and the pattern.
;;;
(defstruct Wme time-tag pattern)
;;;
;;;The empty working memory element is established by creating a wme with
;;;zero time-tag and empty pattern.
;;;
(defvar *empty-wme* (make-wme :time-tag 0 :pattern nil))
;;;
;;;(New-Time-Tag) returns a new time tag by incrementing the previous one
;;;by 1.
;;;
(let ((time 0))
(defun New-Time-Tag () (setq time (1+ time))))
;;;
;;;(Index-1 item key link) stores item in a discrimination net starting at
;;;link, indexed by pattern.
;;;
(defun Index-1 (item key link)
(establish-links-1 key link
#'(lambda (link)
(pushnew item (link-contents link)))
#'(lambda (key link succeed)
(let ((l (make-link :key key)))
(push l (link-link-list link))
;add link on failure
(funcall succeed l)))))
;;;
;;;
(defun Establish-Links-1 (item link succeed fail)
(cond ((pcvar-p item) (traverse-link '*var* link succeed fail))
((atom item) (traverse-link item link succeed fail))
(t (labels ((establish-cons (link)
(traverse-link '*cons* link #'establish-car fail))
(establish-car (link)
(establish-links-1 (car item) link #'establish-cdr fail))
(establish-cdr (link)
(establish-links-1 (cdr item) link succeed fail)))
(establish-cons link)))))
;;;
;;;(Store <<pattens>>) stores facts into the working memory.
;;;One or more facts can be given as parameters to store.
;;;
(defun Store (&rest patterns)
(dolist (pattern patterns)
(index-1 (make-wme :time-tag (new-time-tag) :pattern pattern)
pattern
*working-memory*)))
;;;
;;;Definition of Deffects prototype object.
;;;
(create-instance '*deffacts* nil
(:name)
(:comment)
(:ruleset)
(:facts))
;;;
;;;(List-Deffects) lists the names of deffects that are currently loaded
;;;in PS.
;;;
(defun List-Deffacts ()
(let ((deffacts (gv *deffacts* :is-a-inv)))
(dolist (factset deffacts)
(format t "~%~s" (gv factset :name))))
(values))
;;;
;;;(Deffacts deffacts-name "comment"" <<facts>>) grups facts wich represent
;;;initial knowledge.
;;;
;;;Eg. (Deffacts machine "Facts about machines"
;;; (machine cns-machine-1)
;;; (status cnc-machine-1 idle))
;;;
(defmacro Deffacts (deffacts-name comment &rest facts)
(let ((rule-set (get-bucket-for deffacts-name))
(name (get-name-for deffacts-name)))
`(let ((factset (kr::schema-name
(create-instance ',name *deffacts*
(:name ',deffacts-name)
(:comment ',comment)
(:ruleset ',rule-set)
(:facts ',facts)))))
factset)))
;;;
;;;(Get-All-Deffacts-Facts) returns all the initial facts defined by deffacts.
;;;
(defun Get-All-Deffacts-Facts ()
(let ((factsets (gv *deffacts* :is-a-inv))
(facts nil))
(dolist (factset factsets)
(dolist (fact (gv factset :facts))
(push fact facts)))
facts))
;;;
;;;(Reset) asserts all the initial facts defined by Deffects into working
;;;memory.
;;;
(defun Reset ()
(let ((facts (get-all-deffacts-facts)))
(init-wm)
(when facts
(dolist (fact facts)
(store fact)))))
;;;
;;;(Retract <<facts>>) ratracts facts from working memory.
;;;
(defun Retract (&rest wme)
(if (listp wme)
(dolist (wm wme)
(unindex-1 wm (wme-pattern wm) *working-memory*))
(unindex-1 wme (wme-pattern wme) *working-memory*)))
;;;
;;;(Unindex-1 item pattern link) deletes item, indexed by pattern, from the
;;;discrimination tree starting at link.
;;;It returns true if item, indexed by pattern, removed from tree.
;;;
(defun Unindex-1 (item pattern link)
(let ((sublinks (link-link-list link)))
(if (null sublinks) nil
(delete-links pattern link (car sublinks) link
#'(lambda (last-link pcl pcp)
(let ((items (link-contents last-link)))
(prog1 (member item items)
(if (null
(setf (link-contents last-link)
(delete item items
:test #'variants-p)))
(detach-link pcl pcp)))))
#'(lambda (item link succeed)
(declare (ignore item link succeed))
nil)))))
;;;
;;;(Variant-P pat1 pat2) returns true if pattern2 is the same as pattern1,
;;;except for variable names.
;;;
(defun Variants-P (pat1 pat2)
(let ((alist '()))
(labels ((variant-ids-p (id1 id2)
(let ((entry1 (assoc id1 alist))
(entry2 (rassoc id2 alist)))
(if (and (null entry1)
(null entry2))
(push (cons id1 id2) alist))
(eq entry1 entry2)))
(test (pat1 pat2)
(or (and (pcvar-p pat1)
(pcvar-p pat2)
(variant-ids-p (pcvar-id pat1) (pcvar-id pat2)))
(eql pat1 pat2)
(and (consp pat1)
(consp pat2)
(test (car pat1) (car pat2))
(test (cdr pat1) (cdr pat2))))))
(test pat1 pat2))))
;;;
;;;(Delete-Links pattern tree potential-cut-link potential-cut-pattern
;;; success-continuation failure-continuation)
;;;Delete-links deletes the links from the discrimination tree when the
;;;patterns are to be deleted. The actual deletion of the individual links are
;;;performed by the Delete-link function.
;;;It is called by Unindex-1
;;;
(defun Delete-Links (pattern link pcl pcp succeed fail)
(cond ((pcvar-p pattern)
(delete-link '*var* link pcl pcp succeed fail))
((atom pattern)
(delete-link pattern link pcl pcp succeed fail))
(t (labels ((delete-cons (link pcl pcp)
(delete-link '*cons* link pcl pcp #'delete-car fail))
(delete-car (link pcl pcp)
(delete-links (car pattern) link pcl pcp
#'delete-cdr fail))
(delete-cdr (link pcl pcp)
(delete-links (cdr pattern) link pcl pcp
succeed fail)))
(delete-cons link pcl pcp)))))
;;;
;;;(Next-Link key link) returns the next link pointing to key.
;;;
(defun Next-Link (key link)
(find key (link-link-list link) :key #'link-key))
;;;
;;;(Traverse-Link item link succeed fail) traverses the discrimination net
;;;for links. If a link is found it returns the succeed continuation if not
;;;it returns the fail continuation which is nil.
;;;
(defun Traverse-Link (item link succeed fail)
(let ((l (next-link item link)))
(if (link-p l) (funcall succeed l)
(funcall fail item link succeed))))
;;;
;;;(Delete-Link atom link pcl pcp success-continuation failure-continuation)
;;;Traverses the sublink for atom, if possible, and updates the potential
;;;cut point if more than one sublink exists.
;;;
(defun Delete-Link (item link pcl pcp succeed fail)
(traverse-link item link
#'(lambda (sublink)
(if (null (cdr (link-link-list link)))
(funcall succeed sublink pcl pcp)
(funcall succeed sublink sublink link))) fail))
;;;
;;;(Detach-Link link parent) removes link from the link-list of parent.
;;;
(defun Detach-Link (link parent)
(if parent (setf (link-link-list parent)
(delete link (link-link-list parent) :test #'variants-p))))
;;;
;;;(Init-Wm) initializes *working-memory* by resetting it.
;;;
(defun Init-Wm ()
(setq *working-memory* (make-link)))
;;;
;;;(Init-Pm rule-buckets) initializes the knowledge-base set stored in
;;;rule-bucket by destroying the rule objects, thus removing them from
;;;the rule set.
;;;Individual rule-bucket or a list of rule-bucket can be given as parameter(s)
;;;to the Init-Pm function.
;;;
(defun Init-Pm (&rest rule-buckets)
(dolist (rule-bucket rule-buckets)
(dolist (rule (get-ps-rules rule-bucket))
(kr:destroy-schema rule))
(s-value rule-bucket :is-a-inv nil)))
;;;
;;;(Clear) initializes the entire knowledge base by first initializing
;;;*working-memory* the calling Init-Pm for the existing rule-buckets.
;;;
(defun Clear ()
(init-wm)
(dolist (rule-bucket (get-ps-rule-buckets))
(init-pm rule-bucket)))
;;;
;;;Type declaration for the Left-Hend Side slot of the rule.
;;;
(def-kr-type lhs-type () '(or list null) "Either list or NIL")
;;;
;;;Type declaration for the symbols.
;;;
(def-kr-type symbol-type () '(satisfies symbolp) "Must be a symbol")
;;;
;;;Type declaration for the action functions.
;;;
(def-kr-type function-type () '(or (satisfies functionp) null)
"Either a function or NIL")
;;;
;;;Type declaration for integer between -10000 and 10000
;;;
(def-kr-type salience-type () '(or (integer -10000 10000) null)
"An Integer type between -10000 and 10000")
;;;
;;;Definition of the Ps-Rule prototype object from which the individual rules
;;;are instantiated and their behavior is inherited.
;;;
(setq kr::*Print-As-Structure* nil
kr::*Print-New-Structure* nil
kr::*Warning-On-Create-Schema nil)
(create-instance 'Ps-Rule nil
:declare ((:parameters :name :bucket :documentation :salience :patterns
:psvars :action :time-tags)
(:type (salience-type :salience)
(string :documentation)
(lhs-type :patterns)
(list :pcvars :time-tags)
(symbol-type :name :bucket)
(function-type :action))
(:sorted-slots :is-a :name :bucket :documentation :salience
:pcvars :action :time-tags))
(:name)
(:bucket)
(:documentation "")
(:salience 0)
(:patterns)
(:pcvars)
(:action)
(:time-tags))
;;;
;;;Interface methods and functions to access the slots of rules.
;;;
;;;The methods are defined for the prototype object Ps-Rule and
;;;inherited by the actual rules thet are instances of the prototype
;;;object.
;;;
(define-method :get-ps-rule-name ps-rule (rule)
(gv rule :name))
(defun Ps-Rule-Name (rule)
(kr-send ps-rule :get-ps-rule-name rule))
(define-method :get-ps-rule-bucket ps-rule (rule)
(gv rule :bucket))
(defun Ps-Rule-Bucket (rule)
(kr-send ps-rule :get-ps-rule-bucket rule))
(define-method :get-ps-rule-buckets ps-rule ()
(gv ps-rule :is-a-inv))
(defun Get-Ps-Rule-Buckets ()
(kr-send ps-rule :get-ps-rule-buckets))
(define-method :get-ps-rule-documentation ps-rule (rule)
(gv rule :documentation))
(defun Ps-Rule-Documentation (rule)
(kr-send ps-rule :get-ps-rule-documentation rule))
(define-method :get-ps-rule-salience ps-rule (rule)
(gv rule :salience))
(defun Ps-Rule-Salience (rule)
(kr-send ps-rule :get-ps-rule-salience rule))
(define-method :get-ps-rule-patterns ps-rule (rule)
(gv rule :patterns))
(defun Ps-Rule-Patterns (rule)
(kr-send ps-rule :get-ps-rule-patterns rule))
(define-method :get-ps-rule-pcvars ps-rule (rule)
(gv rule :pcvars))
(defun Ps-Rule-Pcvars (rule)
(kr-send ps-rule :get-ps-rule-pcvars rule))
(define-method :get-ps-rule-action ps-rule (rule)
(gv rule :action))
(defun Ps-Rule-Action (rule)
(kr-send ps-rule :get-ps-rule-action rule))
(define-method :get-ps-rule-time-tags ps-rule (rule)
(gv rule :time-tags))
(defun Ps-Rule-Time-Tags (rule)
(kr-send ps-rule :get-ps-rule-time-tags rule))
(define-method :get-ps-rules ps-rule (rule)
(gv rule :is-a-inv))
;;;
;;;(Get-Ps-Rule ruleset) returns the rules of the given ruleset.
;;;
(defun Get-Ps-Rules (rule-bucket)
(kr-send ps-rule :get-ps-rules rule-bucket))
;;;
;;;(Define-Rule-Sets <<rulesets>>) creates one or more rule-sets.
;;;
;;;To create one ruleset only a single ruleset must be given as parameter.
;;;Eg. 1, (Define-Rule-Sets Recognizer) creates ruleset Recognizer.
;;;
;;;To create more then one rulesets a list of the rulesets must be given as
;;;parameter.
;;; 2, (Define-Rule-Sets Recognizer Machinesel Rawmatsel)
;;; Creates rulesets Recognizer, Machinesel and Rawmatsel.
;;;
;;;Alternatively new ruleset can be defined by creating the ruleset directly
;;;by instantiating it from Ps-Rule.
;;;
;;;Eq. (create-instance 'new-ruleset Ps-Rule)
;;;
(defmacro Define-Rule-Sets (&rest rulesets)
`(dolist (set ',rulesets)
(create-instance set ps-rule)))
;;;
;;;Creation of four rulesets.
;;; - Recognizer for feature recognition.
;;; - Machinesel for machine selection.
;;; - Raw-Chucking for raw material chucking determination.
;;; - Rawmatsel for raw material selection.
;;;
; (Define-Rule-Sets Recognizer Machinesel Raw-Chucking Rawmatsel)
;;;
;;;(Ps-Rule-P rule) is a predicate function and returns true if rule is a
;;;valid rule, otherwise it returns false.
;;;
(defun Ps-Rule-P (rule)
(is-a-p rule ps-rule))
;;;
;;;(Get-Bucket-For rule) Returns the rule bucket of the rule.
;;;
;;;Eg. (Get-Bucket-For 'Recognizer.Rule-1) returns Recognizer.
;;;
(defun Get-Bucket-For (rule)
(let* ((name-string (string rule))
(bucket (read-from-string (subseq name-string 0
(position #\. name-string )))))
bucket))
;;;
;;;(Get-Name-For rule) returns the name of the rule.
;;;
;;;Eg. (Get-Name-For 'Recognizer.Rule-1) returns Rule-1.
;;;
(defun Get-Name-For (rule)
(let* ((name-string (string rule))
(rule-name (read-from-string (subseq name-string
(1+ (position #\. name-string ))))))
rule-name))
;;;
;;;Macro for defining Rules.
;;;
;;;The rule definition syntax is given in the header section of this file.
;;;
(defmacro Defrule (rule doc-string (&key (declare-salience 0)) . rest)
(let ((bucket (get-bucket-for rule))
(rule-name (get-name-for rule)))
(parse-ps-rule rest
#'(lambda (lhs rhs)
(let* ((pcvars (pcvars-in lhs '()))
(lambda-vars (mapcar #'pcvar-id pcvars)))
`(let ((ps-rule (kr::schema-name
(create-instance ',rule ,bucket
(:name ',rule-name)
(:bucket ',bucket)
(:documentation ',doc-string)
(:salience ',declare-salience)
(:patterns ',lhs)
(:pcvars ',pcvars)
(:action #'(lambda ,lambda-vars ,@rhs))
(:time-tags '())))))
ps-rule))))))
;;;
;;;(Parse-Ps-Rule rule-form continuation) cuts the rule into LHS and
;;;RHS and calls the continuation function with the left and right hand
;;;side of the rule.
;;;
;;;It gives an error message "Missing -> in <rule-name>" if the then
;;;sine -> is missing from the rule definition.
;;;
(defun Parse-Ps-Rule (rule-form -c-)
(let ((l (member '-> rule-form)))
(if (null l)
(error "Missing -> in ~s~%." rule-form)
(funcall -c- (ldiff rule-form l) (cdr l)))))
;;;
;;;(Pcvars-In pattern vars) returns the list of pattern variables.
;;;
(defun Pcvars-In (pattern vars)
(cond ((pcvar-p pattern)
(if (member (pcvar-id pattern) vars :key #'pcvar-id)
vars
(cons pattern vars)))
((atom pattern) vars)
(t (pcvars-in (cdr pattern)
(pcvars-in (car pattern) vars)))))
;;;
;;;(Rules rule-bucket) returns the names and the total number of rules
;;;in rule-backet
;;;
(defun Rules (rule-bucket)
(format t "~%Rule-bucket: ~s~%" rule-bucket)
(let ((rules (get-ps-rules rule-bucket)))
(dolist (rule rules)
(format t "~s~%" rule))
(format t "~%Number of rules in rule-bucket ~s is : ~s" rule-bucket
(length rules)))
(values))
;;;
;;;(Pprules rule-bucket) returns the rules of the ruleset by listing
;;;the structure of the rules, as well as the total number of rules in the
;;;ruleset.
;;;
(defun Pprules (rule-bucket)
(format t "~%Rule-bucket: ~s" rule-bucket)
(let ((rules (get-ps-rules rule-bucket)))
(dolist (rule rules)
(format t "Name : ~s~%Rule-bucket : ~s~%Documentation : ~s
Salience : ~s~%Patterns :~%~{~s~%~} ->~%Action : ~%~s~2%"
(ps-rule-name rule)
(ps-rule-bucket rule)
(ps-rule-documentation rule)
(ps-rule-salience rule)
(ps-rule-patterns rule)
(ps-rule-action rule)))
(format t "~2%Number of rules in rule-bucket ~s is : ~s" rule-bucket
(length rules)))
(values))
;;;
;;;(Pprule rule) returns the structure of the rule.
;;;If the rule is not in the ruleset message is given and some hints
;;;how to figure out the existance of the rule.
;;;
(defun Pprule (rule)
(let* ((rule-bucket (get-bucket-for (kr::schema-name rule)))
(rules-in-bucket (get-ps-rules (eval rule-bucket))))
(if (not (member rule rules-in-bucket))
(progn
(format t "~%~s is not a member of Ps-Rules." (kr::schema-name rule))
(format t "~%Check if ~s is a legal Rule Bucket!" rule-bucket)
(format t "~%~s is a legal Rule Bucket if it's a sub-class of Ps-Rules."
rule-bucket)
(format t "~%Hint : Please evaluate (is-a-p ~s ps-rule) and check if it's T."
rule-bucket))
(format t "Name : ~s~%Rule-Bucket : ~s~%Documentation : ~s~%Salience : ~s
~%Patterns :~%~{~s~%~} ->~%Action :~%~s~%"
(ps-rule-name rule)
(ps-rule-bucket rule)
(ps-rule-documentation rule)
(ps-rule-salience rule)
(ps-rule-patterns rule)
(ps-rule-action rule)))))
;;;
;;;(Excise rules) removes the rules from the ruleset by destroying
;;;the object of the rule.
;;;
;;;Eg. 1, (Excise Recognizer.Rule-1) removes Rule-1 from Recognizer ruleset.
;;;
;;; 2, (Excise Recognizer.Rule-1 Recognizer.Rule-2) removes Rule-1 and
;;; Rule-2 from ruleset Recognizer.
;;;
(defun Excise (&rest rules)
(dolist (rule rules)
(if (ps-rule-p rule)
(kr::destroy-schema rule)
(format t "~s is not a valid rule.~%" rule)))
(values))
;;;
;;;Global variable definition for *concurrent-fire-p*.
;;;
(defvar *concurrent-fire-p* nil)
;;;
;;;(Set-Concurrent-Firing-Mode) sets concurrent firing mode of the
;;;inference engine.
;;;
(defun Set-Concurrent-Firing-Mode ()
(setq *concurrent-fire-p* t))
;;;
;;;(Reset-Concurrent-Firing-Mode) resets concurrent firing mode.
;;;
(defun Reset-Concurrent-Firing-Mode ()
(setq *concurrent-fire-p* nil))
;;;
;;;Global variable for *rule-firing*.
;;;
(defvar *rule-firing* t)
;;;
;;;Helpping functions for the Watch command.
;;;(Watch-Rule-Firing) sets rule firing mode of the inference engine.
;;;
(defun Watch-Rule-Firing ()
(setq *rule-firing* t))
;;;
;;;(Unwatch-Rule-Firing) resets rule fireing mode of the inference engine.
;;;
(defun Unwatch-Rule-Firing ()
(setq *rule-firing* nil))
(defvar *consider-print* nil)
;;;
;;;(Watch-Rule-Consideration) sets rule consideration execution mode of the
;;;inference engine.
;;;
(defun Watch-Rule-Consideration ()
(setq *consider-print* t))
;;;
;;;(Unwatch-Rule-Consideratio) resets rule consideration execution mode of the
;;;inference engine.
;;;
(defun Unwatch-Rule-Consideration ()
(setq *consider-print* nil))
;;;
;;;(Watch conditions) sets conditions for debuging.
;;;Different conditions can be given to be watched during exicution.
;;;
;;;Eq. 1, (Watch rule-fireing) sets rule-fireing to be watched.
;;; If this command, is given the inferrence engine prints the
;;; message "Firing rule <rule-name>.")
;;;
;;; 2, (Watch rule-fireing rule-consideration) sets rule-fireing
;;; and rule-considerations to be watched.
;;; If this commands is given the inferrence engine is
;;; instructed to print the rule-firing message as well as the
;;; rule-consideration message.
;;; "I am comsidering rule <rule-name> for firing..."
;;;
(defmacro Watch (&rest conditions)
`(dolist (condition ',conditions)
(case condition
(rule-firing (watch-rule-firing))
(rule-consideration (watch-rule-consideration))
(all (watch-rule-firing)
(watch-rule-consideration)))))
;;;
;;;(Unwatch conditions) resets the watching conditions.
;;;The syntax of Unwatch is the same as the syntax of Watch.
;;;
(defmacro Unwatch (&rest conditions)
`(dolist (condition ',conditions)
(case condition
(rule-firing (unwatch-rule-firing))
(rule-consideration (unwatch-rule-consideration))
(all (unwatch-rule-fireing)
(unwatch-rule-consideration)))))
;;;
;;;(Run rule-bucket) runs the production system with rule-bucket.
;;;
;;;Run is the top level command of the PS production system. The cycle is to
;;;compute the conflict set, that is to find the rules in rule-bucket that are
;;;sattisfied by fects, choose one rule from the conflict set, fire it and
;;;start over.
;;;
;;;If the conflict set is empty, which means there is no rule which is
;;;sattisfied by fects thus no rules can fire, terminate the loop with the
;;;message "Empty conflict set." and halt the system.
;;;
;;;The other way to halt the system is to have a rule action call the
;;;function HALT.
;;;
(defun Run (rule-bucket)
(declare (optimize speed (safety 0)))
(catch 'ps-run
(loop
(let ((conflict-set (compute-agenda rule-bucket)))
(cond ((null conflict-set)
(format t "~%Empty conflict set~%")
(return))
(t (fire (resolve-conflicts conflict-set))))))))
;;;
;;;(Halt return-value) halts the system and returns the returm-value by
;;;calling the throw command that throws the catched symbol 'xps-run.
;;;
(defun Halt (return-value) (throw 'ps-run return-value))
;;;
;;;(Facts [<start> [<end> [<maximum>]]]) returns the list of the facts in
;;;working memory by listing their time-tags and patterns.
;;;
;;;When no argument is given the entire working memory is listed.
;;;
;;;If the <start> argument is specified but not end and maximum, all facts with
;;;time-tags greater then or equal to <start> are displayed.
;;;
;;;If <start> and <end> are specified, all facts with time-tags greater then or
;;;equal to <start> and less then or equal to <end> are displayed.
;;;
;;;If the <maximum> argument is also specified along with <start> and <end>,
;;;no more than <maximum> facts will be displayed.
;;;
(defun Facts (&optional start end maximum)
(let ((sorted-facts (sort (ps-fetch '?x) #'< :key #'wme-time-tag)))
(cond ((not start)
(dolist (wme sorted-facts)
(format t "F-~s : ~s~%"(wme-time-tag wme)(wme-pattern wme))))
((and start (not end))
(loop for wme in sorted-facts
when (>= (wme-time-tag wme) start)
do (format t "F-~s : ~s~%"(wme-time-tag wme)(wme-pattern wme))))
((and start end)
(loop for wme in sorted-facts
when (and (>= (wme-time-tag wme) start)
(<= (wme-time-tag wme) end))
do (format t "F-~s : ~s~%"(wme-time-tag wme)(wme-pattern wme))))
((and start end maximum)
(loop for wme in sorted-facts
when (and (>= (wme-time-tag wme) start)
(<= (wme-time-tag wme) end)
(< (wme-time-tag wme) (+ start maximum)))
do (format t "F-~s : ~s~%"(wme-time-tag wme)(wme-pattern wme)))
)))
(values))
;;;
;;;(Fact time-tag) returns the fact with wme-time-tag equal to time-tag.
;;;
(defun Show-Fact (time-tag)
(let* ((wme (find time-tag (ps-fetch '?x) :key #'wme-time-tag)))
(format t "F-~s : ~s~%" (wme-time-tag wme) (wme-pattern wme)))
(values))
(Def-Kr-Type Rule-Type () '(or null (satisfies ps-rule-p)))
(create-instance 'Instances nil
:declare ((:parameters :rule :sub :time-tags)
(:type (rule-type :rule)
(list :sub :time-tags))
(:sorted-slots :rule :sub :time-tags))
(:rule)
(:sub)
(:time-tags))
;;;
;;;Interface methods and functions to access slots of agenda rules.
;;;
(define-method :get-instances-rule instances (instance)
(gv instance :rule))
(defun Instances-Rule (instance)
(kr-send instances :get-instances-rule instance))
(define-method :get-instances-sub instances (instance)
(gv instance :sub))
(defun Instances-Sub (instance)
(kr-send instances :get-instances-sub instance))
(define-method :get-instances-time-tags instances (instance)
(gv instance :time-tags))
(defun Instances-Time-Tags (instance)
(kr-send instances :get-instances-time-tags instance))
;;;
;;;(Compute-Agenda rule-bucket) goes through all the production rules
;;;of the given ruleset and tests each rule with Test-Pattern to check if
;;;the rule is satisfied by facts. If the rule is satisfied it creates
;;;an instance of the satisfied rule with the substitution list and time-tags
;;;and puts it into the agenda. (Agenda is the set of the satisfied rules in
;;;a given cycle ofo execution.)
;;;When there is no more rule found the agenda is return for conflict
;;;resolution.
;;;
;;;Refraction is taken care of here by checking if the rule has already been
;;;fired or not. If the rule has already been fired on the same fact
;;;refraction blocks the rule to fire again.
;;;
(defun Compute-Agenda (rule-bucket)
(declare (optimize speed (safety 0)))
(let ((agenda '()))
(dolist (rule (get-ps-rules rule-bucket))
(if *consider-print*
(format t "I am considering ~S for firing...~%"
(ps-rule-name rule)))
(test-patterns (ps-rule-patterns rule) '() '()
#'(lambda (sub time-tags)
(unless (already-fired-p rule time-tags)
(push (create-instance nil instances ;It is fuster with
(:rule rule) ;structure!!!!
(:sub sub)
(:time-tags time-tags))
agenda)))))
agenda))
;;;
;;;(Test-Patterns pattern substitution time-tags continuation) returns both
;;;the variable bindings and the time tags of the satisfied facts retrieved
;;;from working memory by doing conjunct retrieval on the pattern.
;;;If all the patterns in the rule, that is the entire LHS is satisfied, than
;;;the continuation passed to Test-Pattern is called.
;;;
(defun Test-Patterns (patterns sub time-tags -c-)
(declare (optimize speed (safety 0)))
(if (null patterns)
(funcall -c- sub time-tags)
(ps-retrieve (replace-variables (car patterns) sub)
#'(lambda (sub2 wme)
(test-patterns (cdr patterns) (append sub2 sub)
(cons (wme-time-tag wme) time-tags)
-c-)))))
;;;
;;;(Replace-Variable pattern substitution) replaces all the variables with
;;;their values, if any, as determined by using Pcvar-Value and substitution.
;;;The value of a variable with no binding in substitution is itself.
;;;
(defun Replace-Variables (pat sub)
(cond ((pcvar-p pat) (pcvar-value pat sub))
((atom pat) pat)
(t (cons (replace-variables (car pat) sub)
(replace-variables (cdr pat) sub)))))
;;;
;;;(Pcvar-Value pattern substitution) If variable is bound to another variable,
;;;or to a functional term containing variables, it replaces those variables
;;;with their bindings, if any, as determined by substitution.
;;;
(defun Pcvar-Value (pat sub)
(let ((binding (pcvar-binding pat sub)))
(cond ((null binding) pat)
(t (let ((value (binding-value binding)))
(cond ((eql value pat) pat)
(t (replace-variables value sub))))))))
;;;
;;;(Already-Fired-P ps-rule time-tags) returns not nil if the rule has been
;;;fired already on the fact.
;;;This function carries out the refraction of the rule.
;;;
(defun Already-Fired-P (ps-rule time-tags)
(member time-tags (ps-rule-time-tags ps-rule) :test #'equal))
;;;
;;;(Ps-Retrieve pattern continuation) uses fetcher to get working memory
;;;elements, and the unifier to match patterns against working memory elements.
;;;
;;;For every match, the retriever returns both the substitution list and the
;;;working memory elements.
;;;
(defun Ps-Retrieve (pat -c-)
(let ((method (and (consp pat)
(ps-retrieval-method (pattern-head pat)))))
(if method
(funcall method pat -c-)
(dolist (wme (ps-fetch pat))
(dolist (sub (unify pat (wme-pattern wme)))
(funcall -c- sub wme))))))
;;;
;;;(Patter-Head pattern ) returns the first element (head) of the pattern.
;;;
(defun Pattern-Head (pat) (car pat))
;;;
;;;(Pattern-Args pattern) returns the elements of the pattern but the first
;;;one.
;;;
(defun Pattern-Args (pat) (cdr pat))
;;;
;;; *The pattern fetcher for PS*
;;;
;;;(Ps-Fetch pattern) gets working memory elements.
;;;
(defun Ps-Fetch (pat)
(declare (optimize speed (safety 0)))
(fetch-2 pat *working-memory*))
;;;
;;;(Fetch-2 item link) fetchs item or items from working memory and returns
;;;the ones that match the pattern of item as a list or nil if there is no
;;;metching pattern in the working memory.
;;;
;;;Eg. *working-memory* = ((insert roughing i-1) ;In reality, facts are
;;; (insert roughing i-13) ;stored as discrimination
;;; (insert finishing i-25)) ;net.
;;; Then
;;;(Fatch-2 '(insert roughing ?x) *working-memory*) returns
;;;
;;;> ((insert roughing i-1) (insert roughing i-13))
;;;
(defun Fetch-2 (item link)
(declare (optimize speed (safety 0)))
(let ((results '()))
(traverse-links-2 item link
#'(lambda (link)