-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrule.lisp
503 lines (424 loc) · 18.8 KB
/
rule.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
;---------------------------------------------
; RULE: This file contains flavors, methods, and codelets for the rule and
; translated rule.
;---------------------------------------------
(in-package 'user)
(defflavor rule
(object-category1 descriptor1-facet descriptor1
(object-category2 nil) (descriptor2 nil)
(replaced-description-type nil) (relation nil))
(workspace-structure)
:gettable-instance-variables
:settable-instance-variables
:initable-instance-variables)
; Here are two examples of how the rule instance can be set up:
; Example 1: for the rule "Replace rightmost letter by successor":
; OBJECT-CATEGORY1 = plato-letter
; (The object-category of the initial-string object that changed.)
; DESCRIPTOR1 = "rightmost".
; DESCRIPTOR1-FACET = plato-string-position-category
; (This is the facet of the letter that's being described by descriptor1
; in the rule, not its letter-category or its length or anything
; else.)
; REPLACED-DESCRIPTION-TYPE = letter-category
; (This means that the rule is saying that "successor" refers to
; letter-category, not to any other facet of the two letters being
; related.)
; RELATION = plato-successor.
; (Since this is a "relation-rule", the other instance variables are
; ignored.)
; Example 2: for the rule "Replace C by D":
; OBJECT-CATEGORY1 = plato-letter
; DESCRIPTOR1-FACET = plato-letter-category
; DESCRIPTOR1 = plato-c
; OBJECT-CATEGORY2 = plato-letter
; REPLACED-DESCRIPTION-TYPE = plato-letter-category
; DESCRIPTOR2 = plato-d
;---------------------------------------------
(defun make-relation-rule (object-category1 descriptor1-facet descriptor1
object-category2 replaced-description-type relation)
; Returns a new relation rule.
(make-instance 'rule :object-category1 object-category1
:descriptor1-facet descriptor1-facet
:descriptor1 descriptor1
:object-category2 object-category2
:replaced-description-type replaced-description-type
:relation relation
:structure-category 'rule))
;---------------------------------------------
(defun make-non-relation-rule (object-category1 descriptor1-facet descriptor1
object-category2 replaced-description-type
descriptor2)
; Returns a new non-relation rule.
(make-instance 'rule :object-category1 object-category1
:descriptor1-facet descriptor1-facet
:descriptor1 descriptor1
:object-category2 object-category2
:replaced-description-type replaced-description-type
:descriptor2 descriptor2
:structure-category 'rule))
;---------------------------------------------
(defmethod (rule :relation?) ()
; Returns t if the rule expresses a relation between the modified-string
; object and the initial-string object.
relation)
;---------------------------------------------
(defmethod (rule :no-change?) ()
; Returns t if the rule specifies that no changes are to be made.
(null descriptor1))
;---------------------------------------------
(defmethod (rule :print) ()
(if* (send self :no-change?)
then (format t "Don't replace anything~&")
else (if* (send self :relation?)
then (format t "Replace ~a with ~a \"~a\" "
(send object-category1 :pname)
(send descriptor1-facet :pname)
(send descriptor1 :pname))
(format t "by ~a with ~a: ~a of ~a of ~a with ~a: ~a~&"
(send object-category2 :pname)
(send replaced-description-type :pname)
(send relation :pname)
(send replaced-description-type :pname)
(send object-category1 :pname)
(send descriptor1-facet :pname)
(send descriptor1 :pname))
else (format t "Replace ~a with ~a \"~a\" by ~a with ~a \"~a\"~&"
(send object-category1 :pname)
(send descriptor1-facet :pname)
(send descriptor1 :pname)
(send object-category2 :pname)
(send replaced-description-type :pname)
(send descriptor2 :pname)))))
;---------------------------------------------
(defun rule-equal? (r1 r2)
; Returns t if the two rules are the same.
(and (eq (send r1 :object-category1) (send r2 :object-category1))
(eq (send r1 :descriptor1-facet) (send r2 :descriptor1-facet))
(eq (send r1 :descriptor1) (send r2 :descriptor1))
(eq (send r1 :object-category2) (send r2 :object-category2))
(eq (send r1 :descriptor2) (send r2 :descriptor2))
(eq (send r1 :replaced-description-type) (send r2 :replaced-description-type))
(eq (send r1 :relation) (send r2 :relation))))
;---------------------------------------------
(defun rule-scout (&aux changed-objects i-obj m-obj
i-descriptions i-probabilities i-description
correspondence-slippage-list
m-descriptions m-probabilities m-description
related-descriptor)
; This codelet fills in the rule template (for the time being, we only
; have one: "Replace _____ by _____"). To do this, it chooses descriptions of
; the changed object in the initial-string and the object in the
; modified-string that replaces it. If a rule can be made,
; then one is proposed, and a rule-strength-tester codelet is posted with
; urgency a function of the degree of conceptual-depth of the chosen descriptions.
(block nil
(if* %verbose% then (format t "In rule-scout~&"))
; If not all replacements have been found, then fizzle.
(if* (send *workspace* :null-replacement?)
then (if* %verbose%
then (format t "Not all replacements have been found. Fizzling.~&"))
(return))
; Find changed object.
(setq changed-objects
(loop for obj in (send *initial-string* :object-list)
when (send obj :changed?) collect obj))
; If there is more than one changed object, then signal and error, and quit.
(if* (> (length changed-objects) 1)
then (format t "~%More than one letter changed.~&")
(format t "Sorry, I can't solve problems like this right now.~&")
(setq *quit-program* t)
(return))
; If no changed object, then propose rule specifying no changes.
(if* (null changed-objects)
then (propose-rule nil nil nil nil)
(return))
; Otherwise, go on.
(setq i-obj (car changed-objects))
(setq m-obj (send (send i-obj :replacement) :obj2))
; Get all relevant distinguishing descriptions that are shared
; (modulo slippage) between the changed object and the target-string object
; it corresponds to. If there is no target-string object corresponding to
; the changed object , then all the relevant distinguishing
; descriptions are considered.
(if* (null (send i-obj :correspondence))
then (setq i-descriptions
(send i-obj :rule-initial-string-descriptions))
else (setq correspondence-slippage-list
(send (send i-obj :correspondence) :slippage-list))
(setq i-descriptions
(loop for d
in (send i-obj :rule-initial-string-descriptions)
if (description-member?
(send d :apply-slippages
i-obj correspondence-slippage-list)
(send (send (send i-obj :correspondence) :obj2)
:relevant-descriptions))
collect d into shared-description-list
finally (return shared-description-list))))
(if* %verbose%
then (format t "i-descriptions: ")
(loop for d in i-descriptions do (send d :print))
(format t "~%"))
(if* (null i-descriptions)
then (if* %verbose%
then (format t "No i-descriptions. Fizzling.~&"))
(return))
; Choose the descriptor for the initial-string object probabilistically.
(setq i-probabilities
(get-temperature-adjusted-value-list
(send-method-to-list i-descriptions :conceptual-depth)))
(setq i-description
(nth (select-list-position i-probabilities) i-descriptions))
(if* %verbose%
then (format t "The i-description is: ")
(send i-description :print))
; Now choose the descriptor for the modified-string object.
; Get the usable descriptions of the modified-string object. For now,
; this includes all descriptions except string-position-category
; descriptions, and object-category descriptions.
(setq m-descriptions
(append (send m-obj :extrinsic-descriptions)
(send m-obj :rule-modified-string-descriptions)))
(if* %verbose%
then (format t "m-descriptions: ")
(loop for d in m-descriptions do
(if* (or (typep d 'description)
(typep d 'extrinsic-description))
then (send d :print) else (send d :pname))
(format t "; "))
(format t "~%"))
(if* (null m-descriptions)
then (if* %verbose%
then (format t "No m-descriptions. Fizzling.~&"))
(return))
(setq m-probabilities
(get-temperature-adjusted-value-list
(send-method-to-list m-descriptions :conceptual-depth)))
(setq m-description
(nth (select-list-position m-probabilities) m-descriptions))
; This is a kludge to avoid rules like "Replace C by successor of C".
; If a description like "successor of C" is chosen, then the description
; with the descriptor "D" is substituted for it.
(if* (and (typep m-description 'extrinsic-description)
(setq related-descriptor
(send (send i-description :descriptor)
:get-related-node (send m-description :relation))))
then (if* %verbose%
then (format t "fixing description~&"))
(setq m-description (loop for d in (send m-obj :descriptions)
when (eq (send d :descriptor)
related-descriptor)
return d)))
(if* %verbose%
then (format t "The m-description is: ")
(send m-description :print))
(propose-rule i-obj i-description m-obj m-description)))
;---------------------------------------------
(defun rule-strength-tester (proposed-rule &aux proposed-rule-strength
build-probability urgency)
; Calculates the proposed-rule's strength, and probabilistically decides
; whether or not to post a rule-builder codelet. If so, the urgency of
; the rule-builder codelet is a function of the strength.
(block nil
(if* %verbose%
then (format t "In rule-strength-tester with rule ")
(send proposed-rule :print))
; Calculate the proposed rule's strength.
(send proposed-rule :update-strength-values)
(setq proposed-rule-strength (send proposed-rule :total-strength))
(if* %verbose%
then (format t "Proposed-rule strength is ~a~&" proposed-rule-strength))
; Decide whether or not to post a rule-builder codelet, based on the
; strength of the proposed-rule.
(setq build-probability
(get-temperature-adjusted-probability
(/ proposed-rule-strength 100)))
(if* %verbose%
then (format t "Build-probability: ~a~&" build-probability))
(if* (eq (flip-coin build-probability) 'tails)
then (if* %verbose%
then (format t "Rule not strong enough. Fizzling.~&"))
(return))
(setq urgency proposed-rule-strength)
(if* %verbose%
then (format t "Strong enough! Posting rule-builder with urgency ~a~&"
(get-urgency-bin urgency)))
(send *coderack* :post
(make-codelet 'rule-builder (list proposed-rule)
(get-urgency-bin urgency)))))
;---------------------------------------------
(defun rule-builder (proposed-rule)
; Tries to build the proposed rule, fighting with competitors if necessary.
(block nil
(if* %verbose%
then (format t "In rule builder with proposed rule: ")
(send proposed-rule :print))
; If this rule already exists, then fizzle.
(if* *rule*
then (if* (rule-equal? *rule* proposed-rule)
then (if* %verbose%
then (format t "This rule already exists. Fizzling.~&"))
(activate-from-workspace-rule-descriptions proposed-rule)
(return)))
; If a different rule already exists, then fight.
(if* *rule*
then (if* %verbose%
then (format t "About to fight with old rule.~&"))
(if* (not (fight-it-out proposed-rule 1 (list *rule*) 1))
then (if* %verbose%
then (format t "Lost. Fizzling.~&"))
(return)
else (if* %verbose%
then (format t "Won against old rule!~&"))))
; Build this rule.
(if* *rule* then (break-rule *rule*))
(build-rule proposed-rule)))
;---------------------------------------------
(defun build-rule (new-rule)
; This function actually builds the new rule.
(setq *rule* new-rule)
(activate-from-workspace-rule-descriptions new-rule)
(if* %workspace-graphics% then (send *rule* :draw %rule-mode%)))
;---------------------------------------------
(defun build-translated-rule (new-translated-rule)
; This function builds the translated rule.
(setq *translated-rule* new-translated-rule)
(if* %workspace-graphics%
then (send *translated-rule* :draw %translated-rule-mode%)))
;---------------------------------------------
(defun break-rule (rule)
; Breaks the rule. The only reason this function has argument "rule" is so
; that it matchs the form of the other "break" functions, and thus the breaker
; codelets can call it.
(if* %workspace-graphics% then (send *rule* :erase %rule-mode%))
(setq *rule* nil))
;---------------------------------------------
(defun rule-translator (&aux slippage-list answer-temperature-threshold
changed-obj changed-obj-correspondence
new-translated-rule)
; This codelet translates the rule according to the translation rules given
; in the slippages on the workspace.
(block nil
(if* %verbose% then (format t "In rule-translator~&"))
; If no rule, fizzle.
(if* (null *rule*)
then (if* %verbose%
then (format t "No rule. Fizzling.~&"))
(return))
(if* (send *rule* :no-change?)
then (setq *translated-rule*
(make-non-relation-rule nil nil nil nil nil nil))
(if* %workspace-graphics%
then (send *translated-rule* :draw %translated-rule-mode%))
(return))
; If the temperature is too high (a threshold is probabilistically chosen),
; then fizzle.
(setq answer-temperature-threshold
(send (get-answer-temperature-threshold-distribution) :choose))
(if* %verbose%
then (format t "The answer-temperature-threshold is ~a~&"
answer-temperature-threshold))
(if* (> *temperature* answer-temperature-threshold)
then (if* %verbose%
then (format t "Temperature too high. Fizzling.~&"))
(return))
; Otherwise build translation of rule.
; Find changed object.
(setq changed-obj (loop for obj in (send *initial-string* :object-list)
when (send obj :changed?) return obj
finally (return nil)))
; If no changed object, then fizzle.
(if* (null changed-obj)
then (if* %verbose%
then (format t "There is no changed object.~&"))
(return))
(setq changed-obj-correspondence (send changed-obj :correspondence))
; Get slippages to use.
(setq slippage-list (send *workspace* :slippage-list))
(if* changed-obj-correspondence
then (loop for s in (send *workspace* :slippage-list) do
(loop for cm in (send changed-obj-correspondence
:concept-mapping-list)
when (contradictory-concept-mappings? cm s) do
(setq slippage-list (remove s slippage-list)))))
(setq new-translated-rule
(if* (send *rule* :relation?)
then (make-relation-rule
(send (send *rule* :object-category1)
:apply-slippages slippage-list)
(send (send *rule* :descriptor1-facet)
:apply-slippages slippage-list)
(send (send *rule* :descriptor1)
:apply-slippages slippage-list)
(send (send *rule* :object-category2)
:apply-slippages slippage-list)
(send (send *rule* :replaced-description-type)
:apply-slippages slippage-list)
(send (send *rule* :relation)
:apply-slippages slippage-list))
else (make-non-relation-rule
(send (send *rule* :object-category1)
:apply-slippages slippage-list)
(send (send *rule* :descriptor1-facet)
:apply-slippages slippage-list)
(send (send *rule* :descriptor1)
:apply-slippages slippage-list)
(send (send *rule* :object-category2)
:apply-slippages slippage-list)
(send (send *rule* :replaced-description-type)
:apply-slippages slippage-list)
(send (send *rule* :descriptor2)
:apply-slippages slippage-list))))
(build-translated-rule new-translated-rule)))
;---------------------------------------------
(defun propose-rule (i-obj i-description m-obj m-description
&aux proposed-rule urgency)
; Creates a proposed rule, and posts a rule-strength-tester codelet with
; urgency a function of the degree of conceptual-depth of the descriptions in the
; rule.
(if* (null i-obj)
then (setq proposed-rule (make-non-relation-rule nil nil nil nil nil nil))
else (if* (typep m-description 'extrinsic-description)
then (setq proposed-rule
(make-relation-rule
(send i-obj :get-descriptor plato-object-category)
(send i-description :description-type)
(send i-description :descriptor)
(send m-obj :get-descriptor plato-object-category)
(send m-description :description-type-related)
(send m-description :relation)))
else (setq proposed-rule
(make-non-relation-rule
(send i-obj :get-descriptor plato-object-category)
(send i-description :description-type)
(send i-description :descriptor)
(send m-obj :get-descriptor plato-object-category)
(send m-description :description-type)
(send m-description :descriptor)))))
(if* %verbose%
then (format t "The proposed rule is:~&") (send proposed-rule :print)
(format t "~%"))
(if* (null i-description)
then (setq urgency 100)
else ; The average alone is too low for low-conceptual-depth rules.
(setq urgency
(* 100 (sqrt (/ (average (send i-description :conceptual-depth)
(send m-description :conceptual-depth))
100)))))
(if* %verbose%
then (format t "Posting a rule-strength-tester with urgency ~a~&"
(get-urgency-bin urgency)))
(send *coderack* :post
(make-codelet 'rule-strength-tester (list proposed-rule)
(get-urgency-bin urgency))))
;---------------------------------------------
(defun activate-from-workspace-rule-descriptions (rule)
; Activate the nodes corresponding to the descriptions in the rule.
(if* (send rule :descriptor1)
then (send (send rule :descriptor1) :activate-from-workspace))
(if* (send rule :relation?)
then (send (send rule :relation) :activate-from-workspace)
else (if* (send rule :descriptor2)
then (send (send rule :descriptor2) :activate-from-workspace))))
;---------------------------------------------