initial commit
[ker2014-2.git] / pl-ail-files / ailog2.pl
1 % -*- Mode: Prolog -*-
2 % ailog2.pl logic engine for Artificial Intelligence http://artint.info
3 % Copyright (C) 1997-2012, David Poole.
4 % This is subject to the GNU Public License (GPL). See user manual for details.
5 % Designed for standard Prolog, but only tested on SWI Prolog.
6 % It is written in one file to make it easy to use:
7 % Get swi-prolog, double click on this file and it should start!
8
9 ailog_version('2.3.6.2',2013).
10
11 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 % SYSTEM DEPENDENT CODE %
13 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14
15 %% the keys to stastics don't seem to be standard. This should cover the cases:
16 %% get_runtime(T,Units) gets accumulated runtime from the system
17 get_runtime(T,U) :- catch((statistics(cputime,T),U=secs),_,
18 (statistics(runtime,[T,_]),U=ms)). % Sicstus/Quintus Prolog
19
20 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21 % OPERATOR DEFINITIONS %
22 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23
24 % <- is the "if"
25 :- op(1150, xfx, <- ).
26 % <= is the base-level "if" (it is not used here, but can be used in
27 % programs).
28 :- op(1120, xfx, <= ).
29 % "&" is the conjunction.
30 :- op(950,xfy, &).
31 % "~" is the negation
32 :- op(900,fy,~).
33 % "\=" is inequality
34 :- op(700,xfx,\=).
35 % "#=" is can't be equal
36 :- op(700,xfx,#=).
37 % ":" is used for probability declarations.
38 :- op(800,xfx,:).
39
40 :- op(1170, fx, tell).
41 :- op(1170, fx, ask).
42 :- op(1170, fx, whynot).
43 :- op(1170, fx, how).
44 :- op(1170, fx, how).
45 :- op(1170, fx, help).
46 :- op(1170,fx,load).
47 :- op(1170,fx,prload).
48 :- op(1170,fx,cd).
49 :- op(1170,fx,bound).
50 :- op(1170,fx,stats).
51 :- op(1170,fx,listing).
52 :- op(1170,fx,clear).
53 :- op(1170,fx,askable).
54 :- op(1170,fx,assumable).
55 :- op(1170,fx,prob).
56 :- op(1170,fx,observe).
57 :- op(1170,fx,unobserve).
58 :- op(1170,fx,predict).
59 :- op(1170,fx,deterministic).
60 :- op(1170,fx,prob_threshold).
61 :- op(1170,fx,arbitrary).
62 :- op(1170,fx,allow).
63
64 :- dynamic (<-)/2.
65 :- dynamic failed/1.
66 :- dynamic depth_bound/1.
67 :- dynamic askabl/1.
68 :- dynamic assumabl/1.
69 :- dynamic choice/3.
70 :- dynamic alt/1.
71 :- dynamic asked/2.
72 :- dynamic debugging_failure/0.
73 :- dynamic answer_found/0.
74 :- dynamic checking_runtime/0.
75 :- dynamic lastruntime/1.
76 :- dynamic deter/1.
77 :- dynamic explanations/4.
78 :- dynamic nogood/2.
79 :- dynamic anogood/2.
80 :- dynamic (prob_threshold)/1.
81 :- dynamic pruned_prob/1.
82 :- dynamic arbit/2.
83 :- dynamic builtin/2.
84
85 depth_bound(30).
86 prob_threshold(0.000001).
87 pruned_prob(0.0).
88
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %% AILog Help
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92
93 help_message(['
94 AILog Version ',V,' Help','
95
96 A clause is of the form','
97 Head <- Body','
98 Head','
99 Head is an atom.','
100 Body is an expression made up of','
101 Atom','
102 ~ B1 negation as finite failure','
103 B1 & B2 conjunction B1 and B2','
104 where B1 and B2 are expressions.','
105
106 ***** Basic commands:','
107
108 tell CLAUSE. add the clause to the knowledge base','
109 askable Atom. makes Atom askable.','
110
111 ask QUERY. ask if expression QUERY is a consequence of the KB','
112 whynot QUERY. find out why an answer to QUERY was not returned.','
113
114 allow G. allow goal G to use the Prolog definition.','
115 bound N. set search depth-bound to N (default=30)','
116 listing. list all clauses in the knowledge base.','
117 listing H. list clauses in the knowledge base with head H.','
118 askables. list all askables.','
119 clear. clear the knowledge base','
120 clear asked. clear all user responses','
121 clear H. clear clauses with head H from knowledge base','
122 check. check the knowledge base for undefined predicates','
123 help. print this help message','
124 help prob. prints help on probabilistic reasoning','
125 help abduction. prints help on abductive reasoning','
126 stats runtime. turn on runtime statistics','
127 quit. quit ailog','
128 prolog. exit to Prolog. Type "start." to start ailog again.','
129
130 ***** Input/Output','
131
132 load ''Filename''. load the clauses in file Filename','
133 prload ''Filename''. load the clauses in Filename, using Prolog''s syntax.','
134 cd ''directory''. changes directory (folder) for loads']) :-
135 ailog_version(V,_).
136
137 help_message(prob,[
138 'AILog Version ',V,' Probabilistic Reasoning Help','
139
140 This help is for probabilistic reasoning in Ailog. This is not useful
141 if you are a beginner interested in deductive reasoning.','
142
143 AILog implements a version of the independent choice logic that allows
144 for probabilities on atoms. These atoms should not be the head of clauses.','
145
146 prob a:p. makes atom a probabilistic with probability p.','
147 prob a1:p1,a2:p2,...,ak:pk. makes atoms ai probabilistic with probability pi.','
148 a''s are exclusive and covering; the p''s must sum to 1.','
149
150 probs. list all prob assertions.','
151
152 observe QUERY. observe that QUERY is true','
153 predict QUERY. ask the posterior probability that QUERY is true','
154 observations. list all observations made','
155 unobserve. undo whylast observation','
156 unobserve all. undo all observations','
157 explanations. describe the explanations of the observations','
158 worlds. describe the worlds of the observations','
159
160 prob_threshold V. set the probability threshold to V in range [0,1]','
161 prob_threshold. get the current probability threshold']) :-
162 ailog_version(V,_).
163
164 help_message(abduction,[
165 'AILog Version ',V,' Abductive Reasoning Help','
166
167 This help is for abductive reasoning in AILog. This is not useful if
168 you are a beginner interested in deductive reasoning.','
169
170 Abduction allows the system to make assumptions to prove a goal, as
171 long as the assumptions are consistent.',' The atoms that can be
172 assumed are called assumables.',' You use "false" at the head of a
173 clause to make constraints.',' After adding assumables or more
174 clauses, you need to create nogoods to make sure that inconsistent
175 sets of assumables are found.',' Abduction in AILog does not work
176 satisfactorily with negation as failure.','
177
178 assumable Atoms. makes (comma-separated) Atoms assumable.','
179 assumables. list all assumables.','
180
181 create_nogoods construct nogoods.','
182 nogoods list nogoods.'
183 ]) :-
184 ailog_version(V,_).
185
186
187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188 %% AILog Commands
189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190
191 interpret(C) :-
192 file_or_command(C,command),!.
193
194 interpret(help) :- !,
195 help_message(Help),
196 write_all(Help).
197 interpret(help What) :-
198 help_message(What,Help),!,
199 write_all(Help).
200 interpret(help What) :- !,
201 write_all(['No help on "',What,'". Type "help." for more information.']).
202
203 interpret((tell _ :- _)) :- !,
204 write_all(['Illegal rule: ":-" is not a legal implication. Use "<-".']).
205 interpret((tell C)) :- !,
206 tell_clause(C).
207
208 interpret((ask Q)) :- !,
209 retractall(debugging_failure),
210 retractall(answer_found),
211 depth_bound(DB),
212 askq(Q,DB).
213
214 interpret((whynot Q)) :- !,
215 depth_bound(DB),
216 (explanations(_,ETs,_,_)
217 ->
218 member(et(E,_),ETs),
219 whynotb(Q,DB,_,E,_)
220 ;
221 whynotb(Q,DB,_,ass([],[],1),_)
222 ).
223
224 interpret(clear) :- !,
225 retractall((_ <- _)),
226 retractall(assumabl(_)),
227 retractall(alt(_)),
228 retractall(choice(_,_,_)),
229 retractall(askabl(_)),
230 retractall(asked(_,_)),
231 retractall(nogood(_,_)),
232 retractall(anogood(_,_)),
233 retractall(explanations(_,_,_,_)),
234 retractall(pruned_prob(_)),
235 retractall(arbit(_,_)),
236 assert(pruned_prob(0.0)).
237
238 interpret((clear asked)) :- !,
239 retractall(asked(_,_)),
240 write_all(['User responses cleared.']).
241
242 interpret((clear H)) :- !,
243 retractall((H <- _)),
244 retractall(assumabl(H)),
245 ( alt(A) , member(H:_, A)
246 ->
247 retract(alt(A)),
248 forall(member(G:_, A), retract(choice(G,_,_)))
249 ; true),
250 retractall(askabl(H)),
251 retractall(asked(H,_)),
252 (H=(X,V) -> retractall(arbit(X,V));true).
253
254 interpret((stats Cmd)) :- !,
255 stats_command(Cmd).
256
257 interpret((prload File)) :- !,
258 prolog_load(File).
259
260 interpret((load File)) :- !,
261 ci_load(File).
262
263 interpret((cd Dir)) :- !,
264 working_directory(_,Dir).
265
266 interpret((cd)) :- !,
267 working_directory(_,'~').
268
269 interpret((bound)) :- !,
270 depth_bound(N),
271 write_all(['Current depth bound is ',N]).
272
273 interpret((bound N)) :- !,
274 (integer(N) ->
275 retract(depth_bound(_)),
276 asserta(depth_bound(N))
277 ; write_all(['Depth bound must be an integer'])).
278
279 interpret(quit) :-
280 throw(quit).
281
282 interpret(check) :- !,
283 check.
284
285 interpret((observe G)) :- !,
286 observe(G).
287
288 interpret((predict G)) :- !,
289 predict(G).
290
291 interpret(askables) :-
292 askabl(G),
293 write_all(['askable ',G,'.']),
294 fail.
295 interpret(askables) :- !.
296
297 interpret(assumables) :-
298 assumabl(G),
299 write_all(['assumable ',G,'.']),
300 fail.
301 interpret(assumables) :- !.
302
303 interpret(arbitraries) :-
304 arbit(X,V),
305 write_all(['arbitrary ',X=V,'.']),
306 fail.
307 interpret(arbitraries) :- !.
308
309 interpret(probs) :-
310 alt(L),
311 (L=[A:P1,~A:_]
312 ->
313 write_all(['prob ',A,':',P1,'.'])
314 ;
315 write('prob '),
316 writeAllDelim(L,', '),
317 write('.'),
318 nl
319 ),
320 fail.
321 interpret(probs) :- !.
322
323 interpret((listing)) :- !,
324 interpret((listing _)),
325 interpret(askables),
326 interpret(assumables),
327 interpret(probs),
328 interpret(arbitraries).
329
330 interpret((listing H)) :-
331 (H <- B),
332 (B == true
333 -> write_all([H,'.'])
334 ; write_all([H,' <- ',B,'.'])
335 ),
336 fail.
337 interpret((listing _)) :- !.
338
339 interpret(unobserve all) :- !,
340 retractall(explanations(_,_,_,_)),
341 retractall(pruned_prob(_)),
342 assert(pruned_prob(0.0)).
343
344 interpret(unobserve) :- !,
345 (retract(explanations(_,_,_,_))
346 ->
347 (
348 explanations(_,_,_,Err)
349 ->
350 retractall(pruned_prob(_)),
351 assert(pruned_prob(Err))
352 ;
353 retractall(pruned_prob(_)),
354 assert(pruned_prob(0.0))
355
356 )
357 ;
358 write_all(['No observations to undo.'])
359 ).
360
361 interpret(observations) :- !,
362 (explanations(Obs,_,_,_) ->
363 print_observations(Obs,_)
364 ; write_all(['There are no observations.'])).
365
366 interpret(explanations) :- !,
367 (explanations(_,ETs,_,_) ->
368 print_explanations(ETs,0)
369 ; write_all(['There are no observations.'])).
370
371 interpret(worlds) :- !,
372 (explanations(Obs,Expls,Pr,Err) ->
373 extract_expls(Expls,Sexpls),
374 make_disjoint(Sexpls,Worlds),
375 print_worlds(Worlds,0),
376 (Err=:=0.0
377 ->
378 write_all(['P(',Obs,') = ',Pr])
379 ;
380 Max is Pr+Err,
381 write_all(['P(',Obs,') = [',Pr,',',Max,']'])
382 )
383 ; write_all(['There are no observations.'])).
384
385 interpret(prob_threshold V) :-
386 (number(V), V>=0.0, V=<1.0)
387 ->
388 retract(prob_threshold(_)),
389 assert(prob_threshold(V))
390 ;
391 write_all(['Argument to prob_threshold must be in range [0,1].']).
392 interpret(prob_threshold) :-
393 prob_threshold(V),
394 write_all(['Probability threshold is ',V]).
395
396
397 interpret(nogoods) :- !,
398 list_nogoods.
399
400 interpret((A <- B)) :- !,
401 write_all(['Illegal command, ',(A <- B),'. You have to "tell" a clause.']).
402
403 interpret(C) :-
404 write_all(['Unknown Command, ',C,'. Type "help." for help.']).
405
406 % listifyProbs(St,L0,L1,S0,S1)
407 % St is a probability structure
408 % L1-L0 is thhe list corresponding to the structure
409 % S1-S0 is the sum of the probabilities
410 listifyProbs((X,Y),L0,L2,S0,S2) :- !,
411 listifyProbs(X,L0,L1,S0,S1),
412 listifyProbs(Y,L1,L2,S1,S2).
413 listifyProbs((A:P),L0,[A:P|L0],S0,S1) :-
414 % atomic(A), % we want it to mean its a ailog atom
415 % number(P),
416 P >= 0,
417 S1 is S0+P.
418
419 % file_or_command(C,W) C is a command that can be in a file or on the
420 % command line, W is either command or file
421 file_or_command((askable G),_) :- !,
422 assertz(askabl(G)).
423
424 file_or_command((assumable G),_) :- !,
425 make_assumable(G).
426
427 file_or_command((allow G),_) :- !,
428 assertz(builtin(G,true)).
429
430
431 file_or_command((prob A:P),W) :- !,
432 (number(P), P>=0, P=<1 ->
433 P1 is 1-P,
434 assertz(alt([A:P,~A:P1])),
435 assertz(choice(A,P,[~A])),
436 assertz(choice(~A,P1,[A]))
437 ;
438 legal_arithmetic_expression(P) ->
439 assertz(alt([A:P,~A:(1-P)])),
440 assertz(choice(A,P,[~A])),
441 assertz(choice(~A,(1-P),[A]))
442
443 ;
444 (W=file
445 ->
446 write_all(['Error: ',(prob A:P)])
447 ; true),
448 write_all(['Probability must be a number or expression in range [0,1]'])
449 ).
450
451 file_or_command((prob APs),W) :- !,
452 ( (listifyProbs(APs,[],L,0,Sum) )%, Sum > 0.9999999999999, Sum< 1.0000000000001)
453 ->
454 normalise(L,Sum*1.0,LN),
455 assertz(alt(LN)),
456 make_choices(LN)
457 ;
458 (W=file
459 ->
460 write_all(['Error: ',(prob APs)])
461 ; true),
462 write_all(['Error: command should be: prob a1:p1,a2:p2,...,ak:pk.']),
463 write_all([' the pi''s must be nonnegative and sum to 1.0'])
464 ).
465
466 file_or_command((arbitrary A),W) :- !,
467 A=(X=V)
468 ->
469 assertz(arbit(X,V))
470 ;
471 (W=file
472 ->
473 write_all(['Error: ',(arbitrary A)])
474 ; true),
475 write_all(['Error: command should be: arbitrary X=V.']).
476
477
478 file_or_command((deterministic G),_) :- !,
479 assertz(deter(G)).
480
481 file_or_command(create_nogoods,_) :- !,
482 depth_bound(DB),
483 construct_nogoods(DB).
484
485
486 legal_arithmetic_expression(N) :- var(N),!.
487 legal_arithmetic_expression(N) :- number(N),!.
488 legal_arithmetic_expression(E1+E2) :- !,
489 legal_arithmetic_expression(E1),
490 legal_arithmetic_expression(E2).
491 legal_arithmetic_expression(E1-E2) :- !,
492 legal_arithmetic_expression(E1),
493 legal_arithmetic_expression(E2).
494 legal_arithmetic_expression(E1*E2) :- !,
495 legal_arithmetic_expression(E1),
496 legal_arithmetic_expression(E2).
497 legal_arithmetic_expression(E1/E2) :- !,
498 legal_arithmetic_expression(E1),
499 legal_arithmetic_expression(E2).
500
501 % make_assumable makes an atom or a comma-separated atoms assumable.
502 make_assumable((A,B)) :- !,
503 make_assumable(A),
504 make_assumable(B).
505
506 make_assumable(G) :-
507 assertz(assumabl(G)).
508
509
510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 %% ASKING
512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513
514 askq(Q,_) :-
515 illegal_body(Q,Why),!,
516 write_all(['Illegal query: '|Why]).
517 askq(Q,DB) :-
518 retractall(failed(_)),
519 assert(failed(naturally)),
520 reset_runtime,
521 solve(Q,DB,Res,[wrule(yes,true,Q,true)],ass([],[],1),Ass),
522 (answer_found -> true ; assert(answer_found)),
523 mynumbervars((Q,Res,Ass),0,_),
524 report(Q,Res,Ass).
525 askq(Q,_) :-
526 failed(naturally),!,
527 (answer_found
528 -> write_all(['No more answers.'])
529 ; write_all(['No. ',Q,' doesn''t follow from the knowledge base.'])),
530 report_runtime.
531 askq(Q,DB) :- !,
532 ask_failing(Q,DB).
533
534 ask_failing(Q,DB) :-
535 write_all(['Query failed due to depth-bound ',DB,'.']),
536 report_runtime,
537 writel([' [New-depth-bound,where,ok,help]: ']),
538 flush_and_read(Comm),
539 interpret_failing_question(Comm,Q,DB).
540
541 % interpret_failing_question(Comm,Q,DB).
542 interpret_failing_question(help,Q,DB) :- !,
543 write_all([
544 ' Give one of the following commands:',nl,
545 ' an integer > ',DB,' to use this depth bound.',nl,
546 ' (use "bound N." to set it permanently).',nl,
547 ' where. to explore the proof tree where the depth-bound was reached.',nl,
548 ' ok. to return to the ailog prompt.',nl,
549 ' help. to print this message.']),
550 ask_failing(Q,DB).
551 interpret_failing_question(end_of_file,_,_) :- !.
552 interpret_failing_question(ok,_,_) :- !.
553 interpret_failing_question(where,Q,DB) :-
554 assert(debugging_failure),
555 askq(Q,DB).
556 interpret_failing_question(Comm,Q,_) :-
557 integer(Comm),!,
558 askq(Q,Comm).
559 interpret_failing_question(Comm,Q,DB) :-
560 write_all([' Unknown command, ',Comm,' type "help." for help.']),
561 ask_failing(Q,DB).
562
563 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564 %% TELLING
565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566
567 tell_clause((~ H <- B)) :- !,
568 write_all(['Illegal rule: ~',H,' <- ',B,'.',nl,
569 'You cannot have negation in the head of a clause.']).
570 tell_clause((H1 & H2 <- B)) :- !,
571 write_all(['Illegal rule: ',H1 & H2,' <- ',B,'.',nl,
572 'You cannot have a conjunction in the head of a clause.']).
573 tell_clause((H1 , H2 <- B)) :- !,
574 write_all(['Illegal rule: ',H1 ,',', H2,' <- ',B,'.',nl,
575 'You cannot have a "," in the head of a clause.']).
576 tell_clause((H <- B)) :-
577 !,
578 ( builtin(H,_) ->
579 write_all([H,' is built-in. It cannot be redefined.'])
580 ; illegal_body(B,Why) ->
581 write_all(['Illegal rule: ',H,'<-',B,'. '|Why])
582 ; assertz((H <- B))).
583 tell_clause((H :- B)) :-
584 !,
585 write_all(['Illegal rule: ',H,':-',B,'. ":-" is not a legal implication. Use "<-".']).
586 tell_clause((A, B)) :-
587 !,
588 write_all(['Error: ',(A,B),' is not a valid clause.']).
589 tell_clause((A & B)) :-
590 !,
591 write_all(['Error: ',(A&B),' is not a valid clause.']).
592 tell_clause((~A)) :-
593 !,
594 write_all(['Error: ',~A,' is not a valid clause.']).
595 tell_clause(H) :-
596 builtin(H,_),!,
597 write_all([H,' is built-in. It cannot be redefined.']).
598 tell_clause(H) :-
599 !,
600 assertz((H <- true)).
601
602 illegal_body(X,['Variables cannot be atoms. Use call(',X,').']) :- var(X).
603 illegal_body((_,_),[' "," is not a legal conjunction. Use "&".']).
604 illegal_body((\+ _),[' "\\+" is not a legal negation. Use "~".']).
605 illegal_body(!,[' "!" is not supported.']).
606 illegal_body([_|_],[' Lists cannot be atoms.']).
607
608 illegal_body((A&_),Why) :-
609 illegal_body(A,Why).
610 illegal_body((_&A),Why) :-
611 illegal_body(A,Why).
612 illegal_body((A;_),Why) :-
613 illegal_body(A,Why).
614 illegal_body((_;A),Why) :-
615 illegal_body(A,Why).
616 illegal_body((~A),Why) :-
617 illegal_body(A,Why).
618
619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 %% META-INTERPRETER
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622
623 % solve(Goal,DepthBnd,PfTree,WhyTree,Ass0,Ass1)
624 % Goal is the goal to be proved
625 % DepthBnd is the depth bound
626 % PfTree is the proof tree returned
627 % Whytree is a list of wrule(Head,LeftTree,Current,RightBody)
628 % Ass0 is the assumables coming in.
629 % This is structure of the form ass(As,PAs,P)
630 % Ass1 is the assumables coming out
631 solve(true,_,true,_,Ass,Ass) :- !.
632 solve((A&B),N,(AT&BT),[wrule(H,LT,_,RB)|WT],A0,A2) :- !,
633 solve(A,N,AT,[wrule(H,LT,A,(B&RB))|WT],A0,A1),
634 solve(B,N,BT,[wrule(H,(LT&AT),B,RB)|WT],A1,A2).
635
636 % The following is tempting, but doesn't work if A makes assumptions...
637 % solve((A -> B ; C),N,(AT -> BT;CT),[wrule(H,LT,_,RB)|WT],A0,A2) :-
638 % solve(A,N,AT,[wrule(H,LT,A,(B&RB))|WT],A0,A1) % why rules is wrong
639 % ->
640 % solve(B,N,BT,[wrule(H,(LT&AT),B,RB)|WT],A1,A2)
641 % ;
642 % solve(C,N,CT,[wrule(H,LT,C,RB)|WT],A0,A2).
643
644 solve(A \= B,_,if(A \= B,builtin),_,A0,A0) :- !,
645 dif(A,B).
646 solve(A #= B,_,if(A #= B,builtin),_,A0,A0) :- !,
647 \+(A=B).
648 solve(call(G),_,_,WT,A0,A0) :-
649 var(G),!,
650 debug_error([' Error: argument to call must be bound when evaluated.'],
651 WT).
652 solve(call(G),N,T,WT,A0,A1) :- !,
653 solve(G,N,T,WT,A0,A1).
654 solve((~ G),N,if(~G,T),WT,A0,A1) :- !,
655 (ground(G) ->
656 failtoprove(G,N,T,WT,A0,A1)
657 ;
658 debug_error([' Error: ',~G,' needs to be ground when called.'],
659 WT)).
660
661
662 solve(G,_,if(G,assumed),_,ass(A0,PAs,P),ass(A0,PAs,P)) :-
663 assumabl(G),
664 % (ground(G) -> true
665 % ; debug_error([' Error: assumable ',G,' needs to be ground when called.'],
666 % WT)),
667 idmember(G,A0),!.
668 solve(G,_,if(G,assumed),_,ass(A0,PAs,P),ass(A1,PAs,P)) :-
669 assumabl(G),
670 % (ground(G) -> true
671 % ; debug_error([' Error: assumable ',G,' needs to be ground when called.'],
672 % WT)),
673 % \+ idmember(G,A0),
674 good(G,A0),
675 insert_into_sorted_list(G,A0,A1).
676 solve(G,_,if(G,assumed),_,ass(A0,PAs0,P0),ass(A0,PAs1,P1)) :-
677 choice(G,P,A),
678 % (ground(G) -> true
679 % ; debug_error([' Error: assumable ',G,' needs to be ground when called.'],
680 % WT)),
681 consistent(PAs0,A),
682 insertUpdateProbs(PAs0,P0,G,P,PAs1,P1),
683 prob_threshold(Bound),
684 ((P1 < Bound)
685 ->
686 retract(pruned_prob(PP)),
687 PP1 is PP+P1,
688 asserta(pruned_prob(PP1)),
689 fail
690 ;
691 true
692 ).
693
694 solve(G=V,_,if(G=V,assumed),_,ass(A0,PAs,P),ass(A1,PAs,P)) :-
695 arbit(G,V),
696 (member((G=V1),A0)
697 ->
698 V=V1, A1=A0
699 ;
700 nonvar(V) -> % this seems to be necessary to get it to work.
701 % The arbitrary choice has not been made, but this call
702 % isn't making the choice.
703 A1=[G=V|A0];
704 A1=[G=V|A0]
705 % fail
706 % gensym(ind,V),
707 % A1=[G=V|A0]
708 ).
709
710 solve(G,_,if(G,asked),WT,A0,A0) :-
711 askabl(G),
712 ask_user(G,WT,Ans),
713 Ans \== unknown,!, % fail if Ans=unknown, otherwise don't try clauses
714 Ans=yes.
715 solve(H,_,if(H,builtin),WT,A0,A0) :-
716 builtin(H,C),!,
717 (C -> catch(H,_,debugging_builtin_error(H,WT))
718 ; write_all(['Error: "',H,'" can''t be called in this form, as ',C,' isn''t true.']),
719 debugging_builtin_error(H,WT)
720 ).
721 solve(H,N0,if(H,BT),WT,A0,A1) :-
722 N0 > 0,
723 deter(H),
724 N1 is N0-1,
725 (H <- B),
726 solve(B,N1,BT,[wrule(H,true,B,true)|WT],A0,A1), !.
727 solve(H,N0,if(H,BT),WT,A0,A1) :-
728 N0 > 0,
729 N1 is N0-1,
730 (H <- B),
731 solve(B,N1,BT,[wrule(H,true,B,true)|WT],A0,A1).
732 solve(H,0,if(H,asked),WT,ass(A0,PAs0,P0),ass(A0,PAs0,P0)) :-
733 debugging_failure,!,
734 debugging_failure_goal(H,WT,P0).
735 solve(_,0,_,_,ass(_,_,P),_) :-
736 retractall(failed(_)),
737 assert(failed(unnaturally)),
738 P>0,
739 retract(pruned_prob(PP)),
740 PP1 is PP+P,
741 asserta(pruned_prob(PP1)),
742 fail.
743
744 % report(Query,Res,Ass) report an answer after an ask
745 report(Q,Res,ass([],[],1)) :- !,
746 write_all(['Answer: ',Q,'.']),
747 report_runtime,
748 writel([' [ok,more,how,help]: ']),
749 flush_and_read(Comm),
750 interpret_report(Comm,Q,Res,ass([],[],1)).
751 report(Q,Res,Ass) :-
752 write_all(['Answer: ',Q,'.']),
753 ( Ass=ass(A0,[],1) ->
754 write_all(['Assuming: ',A0,'.'])
755 ; write_all(['Assuming: ',Ass,'.'])),
756 report_runtime,
757 writel([' [more,ok,how,help]: ']),
758 flush_and_read(Comm),
759 interpret_report(Comm,Q,Res,Ass).
760
761 % interpret_report(Comm,Q,Res,Ass) interprets the answer given by the user
762 % to an answer given after and ask
763 interpret_report(ok,_,_,_) :- !.
764 interpret_report(more,_,_,_) :- !,
765 reset_runtime,fail.
766 interpret_report(end_of_file,_,_,_) :- !,
767 write_all(['^D']).
768 interpret_report(how,Q,Res,Ass) :- !,
769 traverse(Res,Rep),
770 ( (Rep = top; Rep=up)
771 -> report(Q,Res,Ass)
772 ; Rep= retry
773 -> reset_runtime,fail
774 ; Rep=prompt
775 -> true
776 ; write_all(['This shouldn''t occur. Traverse reported ',Rep])
777 ).
778 interpret_report(help,Q,Res,Ass) :- !,
779 write_all([
780 ' The system has proven an instance of your query.',nl,
781 ' You can give the following commands:',nl,
782 ' more. for more answers',nl,
783 ' ok. for no more answers',nl,
784 ' how. to enter a dialog to determine how the goal was proved.']),
785 report(Q,Res,Ass).
786 interpret_report(Comm,Q,Res,Ass) :-
787 Comm \== more,
788 write_all(['Unknown command; ',Comm,'. Type "help." if you need help.']),
789 report(Q,Res,Ass).
790
791 % Depth-bound reached
792 % debugging_failure_goal(H,WT,P)
793 % H is the current goal
794 % WT is the why-tree
795 % P is the probability of this proof
796 debugging_failure_goal(H,WT,P) :-
797 write_all([' Depth-bound reached. Current subgoal: ',H,'.']),
798 writel([' [fail,succeed,proceed,why,ok,help]: ']),
799 flush_and_read(Comm),
800 interpret_debugging_failure_command(Comm,H,WT,P).
801
802
803 % interpret_debugging_failure_command(Comm,H,WT,P)
804 % Comm is the command the user gave
805 % H is the current goal
806 % WT is the why-tree
807 % P is the probability of this proof
808
809 interpret_debugging_failure_command(help,H,WT,P) :- !,
810 write_all([
811 ' The system has reached the depth bound.',nl,
812 ' You can give the following commands:',nl,
813 ' fail. fail this subgoal.',nl,
814 ' succeed. make this subgoal succeed.',nl,
815 ' proceed. fail and don''t stop any more at failing subgoals.',nl,
816 ' why. determine why this subgoal was called.',nl,
817 ' ok. return to ailog prompt.',nl,
818 ' help. print this message.']),
819 debugging_failure_goal(H,WT,P).
820 interpret_debugging_failure_command(fail,_,_,P) :- !,
821 retractall(failed(_)),
822 assert(failed(unnaturally)),
823 P>0,
824 retract(pruned_prob(PP)),
825 PP1 is PP+P,
826 asserta(pruned_prob(PP1)),
827 fail.
828 interpret_debugging_failure_command(succeed,_,_,_) :- !.
829 interpret_debugging_failure_command(proceed,_,_,P) :- !,
830 retractall(debugging_failure),
831 retractall(failed(_)),
832 assert(failed(unnaturally)),
833 P>0,
834 retract(pruned_prob(PP)),
835 PP1 is PP+P,
836 asserta(pruned_prob(PP1)),
837 fail.
838 interpret_debugging_failure_command(ok,_,_,_) :- !,
839 throw(prompt).
840 interpret_debugging_failure_command(end_of_file,_,_,_) :- !,
841 throw(prompt).
842 interpret_debugging_failure_command(why,H,WT,P) :- !,
843 \+ \+ (mynumbervars(WT,0,_),why_question(WT,_)),
844 debugging_failure_goal(H,WT,P).
845 interpret_debugging_failure_command(Comm,H,WT,P) :- !,
846 write_all([' Unknown command: ',Comm,'. Type "help." for help.']),
847 debugging_failure_goal(H,WT,P).
848
849
850 % builtin(G,C) is true if goal G is defined in the system (as opposed to
851 % being defined in clauses). C is the condition that must hold to make sure
852 % there are no errors.
853 builtin((A =< B), ground((A =< B))).
854 builtin((A >= B), ground((A >= B))).
855 builtin((A =\= B), ground((A =\= B))).
856 builtin((A < B), ground((A<B))).
857 builtin((A > B), ground((A>B))).
858 builtin((_ is E),ground(E)).
859 builtin(number(E),ground(E)).
860 builtin(atomic(E),ground(E)).
861
862 % Error in a built-in
863 debugging_builtin_error(H,WT) :-
864 debug_error([' Error in built-in predicate: ',H,'.'],WT).
865
866 % RUNTIME ERROR HANDLING
867 debug_error(Message,WT) :-
868 write_all(Message),
869 writel([' [fail,succeed,why,ok,help]: ']),
870 flush_and_read(Comm),
871 interpret_error_command(Comm,Message,WT).
872
873 interpret_error_command(help,Message,WT) :- !,
874 % write_all(Message),
875 write_all([
876 ' You can give the following commands:',nl,
877 ' fail. fail this subgoal.',nl,
878 ' succeed. make this subgoal succeed.',nl,
879 ' why. determine why this subgoal was called.',nl,
880 ' ok. return to ailog prompt.',nl,
881 ' help. print this message.']),
882 debug_error(Message,WT).
883 interpret_error_command(fail,_,_) :- !,
884 fail.
885 interpret_error_command(succeed,_,_) :- !.
886 interpret_error_command(ok,_,_) :- !,
887 throw(prompt).
888 interpret_error_command(end_of_file,_,_) :- !,
889 throw(prompt).
890 interpret_error_command(why,H,WT) :- !,
891 \+ \+ (mynumbervars(WT,0,_),why_question(WT,_)),
892 debug_error(H,WT).
893 interpret_error_command(Comm,H,WT) :- !,
894 write_all([' Unknown command: ',Comm,'. Type "help." for help.']),
895 debug_error(H,WT).
896
897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
898 %% ASK THE USER
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900
901 % ask_user(G,WT,Ans)
902 % G is the goal to ask
903 % WT is the why-tree
904 % Ans is the reply
905 ask_user(G,_,Ans) :-
906 ground(G),
907 asked(G,Ans1),!,Ans=Ans1.
908 ask_user(G=V,_,yes) :-
909 ground(G),
910 asked(G=V1,yes),!,V=V1.
911
912 ask_user(T=V,WT,Ans) :-
913 ground(T),var(V),!,
914 writel(['What is the value of ',T,'? [value,unknown,why,help]: ']),
915 flush_and_read(Reply),
916 (member(Reply,[unknown,why,help])
917 -> interpret_ask_answer(Reply,T=V,WT,Ans)
918 ; V=Reply,
919 interpret_ask_answer(yes,T=V,WT,Ans)
920 ).
921 ask_user(G,WT,Ans) :-
922 ground(G),!,
923 writel(['Is ',G,' true? [yes,no,unknown,why,help]: ']),
924 flush_and_read(Rep),
925 interpret_ask_answer(Rep,G,WT,Ans).
926
927 ask_user(G,WT,fail) :-
928 write_all([' Error: Askables with free variables not implemented.',nl,
929 ' The system wanted to ask ',G,'.',nl,
930 ' Entering why interation.']),
931 mynumbervars(WT,0,_),
932 why_question(WT,_),
933 write_all([' Askable subgoal ',G,' failing due to free variables.']).
934
935
936 % interpret_ask_answer(Rep,G,WT,Ans).
937 interpret_ask_answer(help,G,WT,Rep) :- !,
938 write_all([' The system is asking whether ',G,' is true or not. Give one of:',nl,
939 ' "yes." if ',G,' is known to be true.',nl,
940 ' "no." if ',G,' is known to be false.',nl,
941 ' "unknown." if ',G,' is unknown (so applicable clauses can be used).',nl,
942 ' "fail." to fail the subgoal (but not record an answer).',nl,
943 ' "why." to see why the system was asking this question.',nl,
944 ' "prompt." to return to the ailog prompt.',nl,
945 ' "help." to print this message.']),
946 ask_user(G,WT,Rep).
947 interpret_ask_answer(yes,G,_,yes) :- !,
948 assertz(asked(G,yes)).
949 interpret_ask_answer(no,G,_,no) :- !,
950 assertz(asked(G,no)).
951 interpret_ask_answer(unknown,G,_,unknown) :- !,
952 assertz(asked(G,unknown)).
953 interpret_ask_answer(fail,_,_,fail) :- !.
954 interpret_ask_answer(prompt,_,_,_) :- !,
955 throw(prompt).
956 interpret_ask_answer(end_of_file,_,_,_) :- !,
957 write_all(['^D']),
958 throw(prompt).
959
960 interpret_ask_answer(why,G,WT,Rep) :- !,
961 \+ \+ ( mynumbervars(WT,0,_),why_question(WT,Rep),Rep \== prompt),
962 ask_user(G,WT,Rep).
963 interpret_ask_answer(Ans,G,WT,Rep) :-
964 Ans \== fail,
965 write_all([' Unknown response "',Ans,'". For help type "help.".']),
966 ask_user(G,WT,Rep).
967
968
969 % why_question(WT,Rep)
970 % WT is a list of wrule(Head,LeftTree,Current,RightBody)
971 % Rep is the reply. It is one of:
972 % down go back one step
973 % bottom return to the ask-the-user query
974 % prompt return to prompt
975 why_question([wrule(H,LT,C,RB)|WT],Rep) :-
976 write_all([' ',C,' is used in the rule ']),
977 write_all([' ',H,' <-']),
978 print_tree_body(LT,1,Max),
979 write_all([' ** ',Max,': ',C]),
980 M1 is Max+1,
981 print_body(RB,M1,_),
982 writel([' [Number,why,help,ok]: ']),
983 flush_and_read(Comm),
984 interpret_why_ans(Comm,Max,[wrule(H,LT,C,RB)|WT],Rep).
985 why_question([],down) :-
986 write_all([' This was the original query.']).
987
988 % interpret_why_ans(Comm,[wrule(H,LT,C,RB)|WT],Rep).
989 interpret_why_ans(help,Max,[wrule(H,LT,C,RB)|WT],Rep) :- !,
990 write_all([
991 ' You can taverse the proof for a subgoal using following commands:',nl,
992 ' how i. show how element i (i<',Max,') of the body was proved.',nl,
993 ' how ',Max,'. show the rule being considered for ',C,'.',nl,
994 ' why. show why ',H,' is being proved.',nl,
995 ' prompt. return to the ailog prompt.',nl,
996 ' help. print this message.']),
997 why_question([wrule(H,LT,C,RB)|WT],Rep).
998 interpret_why_ans(up,_,WT,Rep) :- !,
999 interpret_why_ans(why,_,WT,Rep).
1000 interpret_why_ans(why,_,[WR|WT],Rep) :- !,
1001 why_question(WT,Rep1),
1002 (Rep1 = down
1003 -> why_question([WR|WT],Rep)
1004 ; Rep=Rep1).
1005 interpret_why_ans((how N),Max,[wrule(H,LT,C,RB)|WT],Rep) :-
1006 integer(N),!,
1007 interpret_why_ans(N,Max,[wrule(H,LT,C,RB)|WT],Rep).
1008 interpret_why_ans(N,Max,[wrule(H,LT,C,RB)|WT],Rep) :-
1009 integer(N),
1010 N > 0,
1011 N < Max, !,
1012 nth(LT,1,_,N,E),
1013 traverse(E,Rep1),
1014 (Rep1=up -> why_question([wrule(H,LT,C,RB)|WT],Rep) ;
1015 Rep1=top -> Rep=bottom ;
1016 Rep1=retry
1017 -> write_all([' retry doesn''t make sense here.']),
1018 why_question([wrule(H,LT,C,RB)|WT],Rep) ;
1019 Rep=Rep1).
1020 interpret_why_ans(Max,Max,_,down) :- !.
1021 interpret_why_ans(N,Max,WT,Rep) :-
1022 integer(N),
1023 N > Max, !,
1024 write_all(['You can''t trace this, as it hasn''t been proved.']),
1025 why_question(WT,Rep).
1026 interpret_why_ans(end_of_file,_,_,_) :- !,
1027 write_all(['^D']),
1028 throw(prompt).
1029 interpret_why_ans(prompt,_,_,_) :- !,
1030 throw(prompt).
1031 interpret_why_ans(ok,_,_,bottom) :- !.
1032
1033 interpret_why_ans(Comm,_,WT,Rep) :- !,
1034 write_all(['Unknown command: ',Comm,'. Type "help." for help.']),
1035 why_question(WT,Rep).
1036
1037
1038 % print_body(B,N) is true if B is a body to be printed and N is the
1039 % count of atoms before B was called.
1040 print_body(true,N,N) :- !.
1041 print_body((A&B),N0,N2) :- !,
1042 print_body(A,N0,N1),
1043 print_body(B,N1,N2).
1044 print_body(H,N,N1) :-
1045 write_all([' ',N,': ',H]),
1046 N1 is N+1.
1047
1048
1049
1050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1051 %% NEGATION AS FAILURE
1052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053
1054 % failtoprove(G,N,T,WT,A0,A1)
1055 % G is a ground goal,
1056 % N is a depth-bound
1057 % T is the tree returned
1058 % WT is the why tree
1059 % A0 is the assumables before and A1 is the set after
1060 % The complication here is due to the interaction with the depth-bound
1061 % and assumables
1062
1063 failtoprove(G,N,nafproof(PA1,ETs),WT,ass(A,PAs,Pr),ass(A,PA1,Pr1)) :-
1064 failed(HowFailedInit),
1065 retractall(failed(_)),
1066 assert(failed(naturally)),
1067 findall(et(Ass,T),solve(G,N,T,~WT,ass(A,PAs,Pr),Ass),ETs),
1068 (failed(naturally) ->
1069 extract_expls(ETs,EGs),
1070 duals(EGs,PAs,[PAs],Ds),
1071 member(PA1,Ds),
1072 prob(PA1,1,Pr1)
1073 ;
1074 member(et(ass(A,PAs,Pr),_),ETs) -> % found a proof with no assumptions
1075 retractall(failed(_)),
1076 assert(failed(HowFailedInit)),
1077 fail % fail naturally
1078 ;
1079 fail % fail unnaturally as depth-bound was hit
1080 ).
1081
1082 /* interaction with depth-bound
1083 (failed(naturally) ->
1084 (solve(G,N,_,WT,[],_) ->
1085 retract(failed(unnaturally)), asserta(failed(naturally)),fail
1086 % note this just fails if failed(naturally) still true
1087 ; failed(naturally)
1088 )
1089 ; retract(failed(_)),
1090 assert(failed(naturally)),
1091 (solve(G,N,_,WT,[],_) ->
1092 retract(failed(naturally)), asserta(failed(unnaturally)),fail
1093 ; retract(failed(naturally)), asserta(failed(unnaturally))
1094 % note this just fails if failed(unnaturally) still true
1095 )).
1096 */
1097
1098
1099 % prob(AL,P0,P1)
1100 % AL is a list of probabilistic assumables
1101 % P1 = P0*P(AL)
1102 prob([],P,P).
1103 prob([H|T],P0,P2) :-
1104 choice(H,P,_),
1105 P1 is P0*P,
1106 prob(T,P1,P2).
1107
1108
1109 % The following code gives the interaction between negation and probabilities
1110
1111 % A composite choice is a consistent set of probabilistic assumables.
1112
1113 % duals(Es,R0,D0,D1) is true if Es is a list of terms of the form
1114 % ass(A,PAs,Pr) where R0 is a subset of PAs, and
1115 % D1-D0 is a list of ass(A1,R1,P1) such that
1116 % R1-R0 is a hitting set of negations of Es.
1117
1118 duals([],_,D,D).
1119 duals([S|L],R0,D0,D2) :- !,
1120 setDiff(S,R0,NA),
1121 hit_every_complement(NA,D0,[],D1),
1122 duals(L,R0,D1,D2).
1123
1124 % hit_every_complement(S,R0,D0,D,D1) is true if S is a composite choice (with
1125 % tail R0), and D2 is D together with the hitting set of negations of
1126 % D0.
1127
1128 hit_every_complement([],_,D0,D0) :- !.
1129 hit_every_complement([A|R],Ds,D0,D2) :-
1130 choice(A,_,NAs),
1131 hit_every_element(NAs,Ds,D0,D1),
1132 hit_every_complement(R,Ds,D1,D2).
1133
1134 % hit_every_element(As,A,D0,D1) is true if S is a composite choice (with
1135 % tail R0), and D2 is D together with the hitting set of negations of
1136 % D0.
1137
1138 hit_every_element([],_,D,D).
1139 hit_every_element([E|T],Es,D0,D2) :-
1140 insert_into_each_exp(Es,E,D0,D1),
1141 hit_every_element(T,Es,D1,D2).
1142
1143 % insert_into_each_exp(L,A,L0,L1) is true if inserting atomic choice A
1144 % into each explanation in L, and adding these to L0 produces L1. L,
1145 % L0 and L1 are all lists of explanations. Subsumed and inconsistent
1146 % explanations are removed.
1147
1148 insert_into_each_exp([],_,L,L) :-!.
1149 insert_into_each_exp([E1|R],A,L0,L1) :-
1150 incompatible(A,E1),!,
1151 insert_into_each_exp(R,A,L0,L1).
1152 insert_into_each_exp([E1|R],A,L0,L2) :-
1153 insert_into_sorted_list(A,E1,E2),
1154 insert_exp(E2,L0,L1),
1155 insert_into_each_exp(R,A,L1,L2).
1156
1157 % insert_exp(E,Es1,Es2) is true if inserting explanation E into list
1158 % Es1 of explanations produces list of explanations Es2.
1159 insert_exp(E,[],[E]) :- !.
1160 insert_exp(E0,[E|T],[E|T]) :-
1161 subset_sorted(E,E0),!. % includes the case where E0==E.
1162 insert_exp(E0,[E|T],R) :-
1163 subset_sorted(E0,E),!,
1164 insert_exp(E0,T,R).
1165 insert_exp(E,[H|T],[H|R]) :-
1166 insert_exp(E,T,R).
1167
1168 % mutually_incompatible(As1,As2) is true if A is inconsistent with all of As.
1169 mutually_incompatible(E1,E2) :-
1170 member(A,E1),
1171 incompatible(A,E2).
1172
1173 % incompatible(A,As) is true if A is inconsistent with all of As.
1174 incompatible(A,E) :-
1175 choice(A,_,NAs),
1176 \+ consistent(E,NAs).
1177
1178
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 %% Probabilities
1181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182
1183 % normalize(L,Sum,LS) Sum is assumed to be a float.
1184 normalise([],_,[]).
1185 normalise([(A:P)|R],S,[(A:PN)|RN]) :-
1186 PN is P/S,
1187 normalise(R,S,RN).
1188
1189 % make_choices(L)
1190 make_choices(L) :-
1191 remove(A:P,L,L0),
1192 setof(AA,P1^member(AA:P1,L0),Alts),
1193 assertz(choice(A,P,Alts)),
1194 fail.
1195 make_choices(_).
1196
1197 % insertUpdateProbs(PAs0,P0,G,P,PAs1,P1)
1198 % PAs0 is the initial probabilistic assumptions
1199 % P0 is the probability of the probablistic assumptions
1200 % G is the choice
1201 % P is the probability of G
1202 % PAs1 is the resulting probabilistic assumptions
1203 % P1 is the resulting probability
1204 insertUpdateProbs([],P0,G,P,[G],P1) :-
1205 P1 is P0*P.
1206 insertUpdateProbs([G|R],P0,G,_,[G|R],P0) :- !.
1207 insertUpdateProbs([G0|R0],P0,G,P,[G,G0|R0],P1) :-
1208 G @< G0, !,
1209 P1 is P0*P.
1210 insertUpdateProbs([G0|R0],P0,G,P,[G0|PAs1],P1) :-
1211 insertUpdateProbs(R0,P0,G,P,PAs1,P1).
1212
1213 % latest_explanations(Obervations,ETs,Prob,Err)
1214 % ETs is a list of et(Explanation,ProofTree)
1215 latest_explanations(O,E,P,Err) :-
1216 explanations(O,E,P,Err),!.
1217 latest_explanations([],[et(ass([],[],1),null)],1,0.0).
1218
1219 % predict(G)
1220 % Warning: this may not work properly with multiple answers + errors
1221 % We should really reset the pruned_prob for each proof...
1222 predict(G) :-
1223 latest_explanations(_,ETs,Pr0,Err0),
1224 retractall(pruned_prob(_)),
1225 assert(pruned_prob(Err0)),
1226 depth_bound(DB),
1227 retractall(failed(_)),
1228 assert(failed(naturally)),
1229 reset_runtime,
1230 find_expls(G,ETs,DB,Expls),
1231 extract_expls(Expls,Sexpls),
1232 make_disjoint(Sexpls,DExpls),
1233 acc_probs(DExpls,0,Pr),
1234 pruned_prob(Err1),
1235 report_expls(Expls,DExpls,G,Pr0,Pr,Err0,Err1).
1236 predict(_) :-
1237 write_all(['No (more) answers.']).
1238
1239 % observe(G)
1240 observe(G) :-
1241 retractall(debugging_failure),
1242 retractall(failed(_)),
1243 assert(failed(naturally)),
1244 latest_explanations(Obs,ETs,PrObs,Err0),
1245 retractall(pruned_prob(_)),
1246 assert(pruned_prob(Err0)),
1247 depth_bound(DB),
1248 reset_runtime,
1249 find_expls(G,ETs,DB,Expls),
1250 extract_expls(Expls,Sexpls),
1251 make_disjoint(Sexpls,DExpls),
1252 acc_probs(DExpls,0,Pnew),
1253 pruned_prob(Err1),
1254 asserta(explanations([G|Obs],Expls,Pnew,Err1)),
1255 report_expls(Expls,DExpls,G,PrObs,Pnew,Err0,Err1).
1256 observe(_) :-
1257 write_all(['No (more) answers.']).
1258
1259 % find_expls(G,ETs,DB,Expls)
1260 % +G is the goal to be solved
1261 % +ETs is the list of explanations of previous observations
1262 % +DB is the depth-bound
1263 % -Expls is the list of explanations of G and the previous observations
1264 find_expls(G,ETs,DB,Expls) :-
1265 bagof(et(Expl,Res),
1266 E0^Tr^( member(et(E0,Tr),ETs),
1267 solve(G,DB,Res,[wrule(yes,true,G,true)],E0,Expl)),
1268 Expls),
1269 (ground(Expls) ->
1270 true
1271 ; write_all(['Warning Explanations not ground: ',Expls])).
1272
1273 % report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1)
1274 % +Ets is a list of et(Explanation,ProofTree)
1275 % +DExpls is a set of possible worlds (disjoint explanations)
1276 % +G is the goal
1277 % +Pr0 is the probability of the previous observations
1278 % +Pr1 is the probability of the current observations
1279 % +Err0 is the error (pruned probability mass) before the query
1280 % +Err1 is the error (pruned probability mass) after the query Err1>Err0
1281 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :-
1282 (Err1=:=0.0
1283 ->
1284 Post is Pr1/Pr0
1285 ;
1286 Min is Pr1/(Pr0+Err0),
1287 Max is (Pr1+Err1)/(Pr0+Err0),
1288 Post=[Min,Max]
1289 ),
1290 write_all(['Answer: P(',G,'|Obs)=',Post,'.']),
1291 report_runtime,
1292 writel([' [ok,more,explanations,worlds,help]: ']),
1293 flush_and_read(Comm),
1294 interpret_prob_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1295
1296 % acc_probs(Expls,P0,P1) determines the probabilities of the assumables in Expls
1297 % +Expls is a list of explanations
1298 % +P0 is the previous probability
1299 % -P1 is the new probability
1300 acc_probs([],P,P).
1301 acc_probs([A|R],P0,P2) :-
1302 prob_expl(A,1,P),
1303 P1 is P+P0,
1304 acc_probs(R,P1,P2).
1305
1306 % probs_expl(Expl,P0,P1) determines the probability of an expanation
1307 % +Expl is an explanation
1308 % +P0 is the previous probability
1309 % -P1 is the resulting probability (P1 = P0*prob(Expl))
1310 prob_expl([],P,P).
1311 prob_expl([A|R],P0,P2) :-
1312 choice(A,P,_),
1313 P1 is P*P0,
1314 prob_expl(R,P1,P2).
1315
1316 % interpret_prob_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1)
1317 % +Comm is the command given by the user
1318 % +Expls is the set of explanations
1319 % +DExpls is the set of worlds (disjoint explanations)
1320 % +G is the goal to be proved
1321 % +Pr0 is the probability of the previous observations
1322 % +Pr1 is the probability of the current observations
1323 interpret_prob_report(ok,_,_,_,_,_,_,_) :- !.
1324 interpret_prob_report(more,_,_,_,_,_,_,_) :- !,
1325 reset_runtime,fail.
1326 interpret_prob_report(end_of_file,_,_,_,_,_,_,_) :- !,
1327 write_all(['^D']).
1328 interpret_prob_report(explanations,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1329 show_explanations(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1330 interpret_prob_report(worlds,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1331 print_worlds(DExpls,0),
1332 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1333 interpret_prob_report(help,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1334 write_all([
1335 ' The system has proven an instance of your query.',nl,
1336 ' You can give the following commands:',nl,
1337 ' more. for more answers',nl,
1338 ' ok. for no more answers',nl,
1339 ' explanations. to explore how the goal is proved based ',
1340 ' sets of assumptions.',nl,
1341 ' worlds. to see the worlds where ',G,' is true.']),
1342 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1343 interpret_prob_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :-
1344 Comm \== more,
1345 write_all(['Unknown command; ',Comm,'. Type "help." if you need help.']),
1346 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1347
1348 % show_explanations(Expls,DExpls,G,Pr0,Pr1,Err0,Err1)
1349 show_explanations(Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :-
1350 print_explanations(Expls,0),
1351 writel([' [ok,more,how i,help]: ']),
1352 flush_and_read(Comm),
1353 interpret_show_explanations_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1354
1355 % interpret_show_explanations_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1)
1356 interpret_show_explanations_report(ok,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1357 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1358 interpret_show_explanations_report(more,_,_,_,_,_,_,_) :- !,
1359 reset_runtime,fail.
1360 interpret_show_explanations_report(how I,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :-
1361 catch(
1362 (
1363 ith(Expls,et(_,Tree),I,0),
1364 traverse(Tree,_) % Report is ignored
1365 ),
1366 Ex,
1367 (Ex= outofrange ->
1368 write_all(['Number out of range: how ',I,'.'])
1369 ; write_all(['Argument to how must be an integer: how ',I,'.'])
1370 )
1371 ),
1372 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1373 interpret_show_explanations_report(help,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1374 write_all([
1375 'ok. go back to answer dialogue',nl,
1376 'more. get another answer.',nl,
1377 'how i. ask how the ith explanation was proved',nl,
1378 'help. print this message']),
1379 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1380 interpret_show_explanations_report(Comm,Expls,DExpls,G,Pr0,Pr1,Err0,Err1) :- !,
1381 write_all([
1382 'Unknown command: ',Comm,'. Type "help." for help']),
1383 report_expls(Expls,DExpls,G,Pr0,Pr1,Err0,Err1).
1384
1385 % print_explanations(DExpls,Count).
1386 print_explanations([],_).
1387 print_explanations([et(W,_)|R],C) :-
1388 write_all([' ',C,': ', W]),
1389 C1 is C+1,
1390 print_explanations(R,C1).
1391
1392 % print_worlds(DExpls,Count).
1393 print_worlds([],_).
1394 print_worlds([W|R],C) :-
1395 prob_expl(W,1,Pr),
1396 write_all([' ',C,': ', W,' Prob=',Pr]),
1397 C1 is C+1,
1398 print_worlds(R,C1).
1399
1400 % print_observations(Obs,N)
1401 print_observations([],0).
1402 print_observations([O|Os],N1) :-
1403 print_observations(Os,N),
1404 N1 is N+1,
1405 write_all([N1,': ',O]).
1406
1407
1408 % extract_expls(L1,L2) L1 is a list of et(ass(_,E,_),_) and L2 is the
1409 % corresponding list of explanations E.
1410 extract_expls([],[]).
1411 extract_expls([et(ass(_,E,_),_)|L],R1) :-
1412 extract_expls(L,R),
1413 insert_exp(E,R,R1).
1414
1415 % make_disjoint(L,SL) is true if L is a list of explanations and SL
1416 % is a lists of Expl such that SL are mutually exclusive and cover
1417 % the same worlds as the Expls in L.
1418 % This assumes that L is a minimal set: no member of L is a subset of another.
1419
1420 make_disjoint([],[]).
1421 make_disjoint([R|L],L2) :-
1422 % check_sorted(R),
1423 member(R1,L),
1424 % check_sorted(R1),
1425 \+ mutually_incompatible(R,R1),!,
1426 (member(E,R1), \+ idmember(E,R) %%% was \+ member(E,R)
1427 -> true; write_all(['Error in make_disjoint: ',compatible(R,R1)]),fail),
1428 choice(E,_,NE),
1429 split([E|NE],R,L,L1),
1430 make_disjoint(L1,L2).
1431 make_disjoint([E|L1],[E|L2]) :-
1432 make_disjoint(L1,L2).
1433
1434 % split(As,R,L0,L1) splits R on As, added to L0 produces L1
1435 split([],_,L,L).
1436 split([A|As],E,L0,L2) :-
1437 insert_into_sorted_list(A,E,E1),
1438 insert_exp(E1,L0,L1),
1439 split(As,E,L1,L2).
1440
1441
1442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443 %% HOW QUESTIONS
1444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445
1446 % traverse(T,Rep) true if
1447 % T is a tree being traversed
1448 % Rep is the reply it is one of {up,top,retry,prompt}
1449 % up means go up in the tree
1450 % top means go to the top of the tree (where traverse was called).
1451 % retry means find another proof tree
1452 % prompt means go to the top level prompt
1453 traverse((A&B),Rep) :-
1454 traverse(if(yes,(A&B)),Rep).
1455 traverse(if(H,true),up) :- !,
1456 write_all([' ',H,' is a fact']).
1457 traverse(if(H,builtin),up) :- !,
1458 write_all([' ',H,' is built-in.']).
1459 traverse(if(H,asked),up) :- !,
1460 write_all([' You told me ',H,' is true.']).
1461 traverse(if(H,assumed),up) :- !,
1462 write_all([' ',H,' is assumed.']).
1463 traverse(if(~G,nafproof(_,[])),Rep) :- !,
1464 write_all([' ',G,' finitely failed. You can examine the search space.']),
1465 depth_bound(DB),
1466 whynotb(G,DB,Rep,ass([],[],1),_).
1467 traverse(if(~G,nafproof(Expl,ETs)),Rep) :- !,
1468 write_all([' ',~G,' succeeded assuming ',Expl]),
1469 write_all([' ',G,' succeeded with the following explanations:']),
1470 print_explanations(ETs,0),
1471 writel([' [up,top,retry,prompt,how i,help,whynot]: ']),
1472 flush_and_read(Comm),
1473 interpret_naf_how(Comm,ETs,Rep0),
1474 (Rep0=repeat ->
1475 traverse(if(~G,nafproof(Expl,ETs)),Rep)
1476 ; Rep=Rep0
1477 ).
1478 traverse(null,top) :- !,
1479 write_all([' There was nothing to be proved.']).
1480 traverse(if(H,B),Rep) :-
1481 write_all([' ',H,' <-']),
1482 print_tree_body(B,1,Max),
1483 writel([' How? [Number,up,retry,ok,prompt,help]: ']),
1484 flush_and_read(Comm),
1485 interpretcommand(Comm,B,Max,if(H,B),Rep).
1486
1487 % print_tree_body(B,N) is true if B is a body to be printed and N is the
1488 % count of atoms before B was called.
1489 print_tree_body(true,N,N).
1490 print_tree_body((A&B),N0,N2) :-
1491 print_tree_body(A,N0,N1),
1492 print_tree_body(B,N1,N2).
1493 print_tree_body(if(H,_),N,N1) :-
1494 write_all([' ',N,': ',H]),
1495 N1 is N+1.
1496
1497 % interpretcommand(Comm,B,Max,Goal,Reply) interprets the command Comm on body B
1498 interpretcommand(help,_,Max,G,Rep) :- !,
1499 write_all([
1500 ' Give either (end each command with a period):',nl,
1501 ' how i. explain how subgoal i (i<',Max,') was proved.',nl,
1502 ' up. go up the proof tree one level.',nl,
1503 ' retry. find another proof.',nl,
1504 ' ok. stop traversing the proof tree.',nl,
1505 ' prompt. return to the ailog prompt.',nl,
1506 ' help. to print this message.']),
1507 traverse(G,Rep).
1508 interpretcommand((how N),B,Max,G,Rep) :-
1509 integer(N),
1510 interpretcommand(N,B,Max,G,Rep).
1511 interpretcommand(N,B,Max,G,Rep) :-
1512 integer(N),
1513 N > 0,
1514 N < Max,!,
1515 nth(B,1,_,N,E),
1516 traverse(E,Rep1),
1517 ( Rep1= up
1518 -> traverse(G,Rep)
1519 ; Rep=Rep1
1520 ).
1521 interpretcommand(N,_,Max,G,Rep) :-
1522 integer(N),!,
1523 % (N < 1 ; N >= Max,Rep),
1524 M1 is Max-1,
1525 write_all(['Number out of range: ',N,'. Use number in range: [1,',M1,'].']),
1526 traverse(G,Rep).
1527 interpretcommand(up,_,_,_,up) :-!.
1528 interpretcommand(why,_,_,_,up) :-!.
1529 interpretcommand(ok,_,_,_,top) :-!.
1530 interpretcommand(prompt,_,_,_,_) :-!,
1531 throw(prompt).
1532 interpretcommand(retry,_,_,_,retry) :-!.
1533 interpretcommand(end_of_file,_,_,_,prompt) :-!,
1534 write_all(['^D']).
1535 interpretcommand(C,_,_,G,Rep) :-
1536 write_all(['Illegal Command: ',C,' Type "help." for help.']),
1537 traverse(G,Rep).
1538
1539 % nth(S,N0,N1,N,E) is true if E is the N-th element of structure S
1540 nth((A&B),N0,N2,N,E) :- !,
1541 nth(A,N0,N1,N,E),
1542 nth(B,N1,N2,N,E).
1543 nth(true,N0,N0,_,_) :- !.
1544 nth(A,N,N1,N,A) :- !,
1545 N1 is N+1.
1546 nth(_,N0,N1,_,_) :-
1547 N1 is N0+1.
1548
1549 % interpret_naf_how(Comm,ETs,Report)
1550 % Report is either up, or repeat or whatever travese can report
1551 interpret_naf_how(ok,_,up).
1552 interpret_naf_how(more,_,_) :- !,
1553 reset_runtime,fail.
1554 interpret_naf_how(how I,Expls,Report) :-
1555 catch(
1556 (
1557 ith(Expls,et(_,Tree),I,0),
1558 traverse(Tree,Report)
1559 ),
1560 Ex,
1561 ((Ex= outofrange ->
1562 write_all(['Number out of range: how ',I,'.'])
1563 ; write_all(['Argument to how must be an integer: how ',I,'.'])
1564 ),
1565 Report=repeat)
1566 ).
1567 interpret_naf_how(help,_,repeat) :- !,
1568 write_all([
1569 'ok. go back to answer dialogue',nl,
1570 'more. get another answer.',nl,
1571 'how i. ask how the ith explanation was proved',nl,
1572 'help. print this message']).
1573 interpret_naf_how(Comm,_,repeat) :- !,
1574 write_all([
1575 'Unknown command: ',Comm,'. Type "help." for help']).
1576
1577
1578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1579 %% WHY NOT QUESTIONS for FAILING QUERIES
1580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1581
1582 % determine why an answer to body Q was not found.
1583 % whynotb(+Q,+DB,-Rep,+Ass0,-Ass1) determine why body Q, with depth-bound DB
1584 % and Ass0 did not find an expected answer
1585 % Rep is the reply it is one of {up,top,retry,prompt}
1586 % up means go up in the tree
1587 % top means go to the top of the tree (where traverse was called).
1588 % retry means find another proof tree
1589 % prompt means go to the top level prompt
1590
1591 whynotb((A & B),DB,Rep,Ass0,Ass2) :-
1592 retractall(failed(_)),
1593 assert(failed(naturally)),
1594 solve(A,DB,Res,[whynot],Ass0,Ass1),
1595 report_whynot_conj(A,Res,B,DB,Rep,Ass0,Ass1,Ass2).
1596 whynotb((A & _),DB,Rep,Ass0,Ass1) :- !,
1597 whynotb(A,DB,Rep,Ass0,Ass1).
1598
1599 whynotb(call(A),DB,Rep,Ass0,Ass1) :- !,
1600 whynotb(A,DB,Rep,Ass0,Ass1).
1601
1602 whynotb((~ A),DB,Rep,Ass0,Ass1) :-
1603 ground(A), !,
1604 retractall(failed(_)),
1605 assert(failed(naturally)),
1606 findall(et(Ass,T),solve(A,DB,T,[whynot],Ass0,Ass),ETs),
1607 (
1608 member(et(Ass0,T),ETs)
1609 -> % found a proof with no assumables
1610 write_all([' ',~A,' failed as ',A,' suceeded. Here is how:']),
1611 traverse(T,Rep)
1612 ; (ETs=[], failed(naturally)) ->
1613 write_all([' ',~A,' succeeds, as ',A,' finitely fails.']),
1614 whynotb(A,DB,Rep,Ass0,_),
1615 Ass1=Ass0
1616 ; failed(unnaturally) ->
1617 write_all([' ',~A,' failed because of the depth-bound.']),
1618 whynotb(A,DB,Rep,Ass0,_)
1619 ;
1620 whynotb_expls(A,ETs,Rep)
1621 ).
1622
1623
1624 whynotb((~ A),_,up,Ass0,Ass0) :- !,
1625 write_all([' Error: ',~A,' needs to be ground when called.']).
1626
1627 whynotb(A,_,up,Ass0,Ass0) :-
1628 builtin(A,C),!,
1629 (call(C)
1630 -> (call(A)
1631 -> write_all([' ',A,' is built-in and succeeds.'])
1632 ; write_all([' ',A,' is built-in and fails.']))
1633 ; write_all([' ',A,' is built-in and is insufficiently instanciated.'])).
1634
1635 whynotb((A \= B),_,up,Ass0,Ass0) :-
1636 ?=(A,B),!,
1637 (A \== B
1638 -> write_all([' ',(A \= B),' succeeds as they can never unify.'])
1639 ; write_all([' ',(A \= B),' fails as they are identical.'])).
1640 whynotb((A \= B),_,up,Ass0,Ass0) :-
1641 write_all([' ',(A \= B),' cannot be resolved. It is delayed.']),
1642 when(?=(A,B),
1643 (A \== B
1644 -> write_all([' Resolving delayed ',(A \= B),'. It succeeded.'])
1645 ; write_all([' Failure due to delayed constraint, ',(A \= B),'.']),
1646 fail)).
1647 whynotb((A #= B),_,up,Ass0,Ass0) :-
1648 (\+(A = B)
1649 -> write_all([' ',(A #= B),' succeeds as they can never unify.'])
1650 ; write_all([' ',(A #= B),' fails as they can unify.'])).
1651
1652
1653 whynotb(Q,_,up,ass(A0,PAs,P),ass(A1,PAs,P)) :-
1654 assumabl(Q),!,
1655 (idmember(Q,A0)
1656 ->
1657 write_all([' ',Q,' has already been assumed. It succeeds.']),
1658 A1=A0
1659 ;
1660 good(Q,A0)
1661 ->
1662 write_all([' ',Q,' is consistently assumed.']),
1663 A1=[Q|A0]
1664 ;
1665 write_all([' ',Q,' is inconsistent with other assumables.']),
1666 A1=A0
1667 ).
1668
1669 whynotb(Q,_,up,ass(A0,PAs0,P0),ass(A0,PAs1,P1)) :-
1670 choice(Q,P,A),!,
1671 % (ground(Q) -> true
1672 % ; write_all([' Error: assumable ',Q,' needs to be ground when called.'])),
1673 (consistent(PAs0,A)
1674 ->
1675 insertUpdateProbs(PAs0,P0,Q,P,PAs1,P1),
1676 prob_threshold(Bound),
1677 ((P1 < Bound)
1678 ->
1679 retract(pruned_prob(PP)),
1680 PP1 is PP+P1,
1681 asserta(pruned_prob(PP1)),
1682 write_all([' ',Q,' fails as the probability (',P1,') is too small'])
1683 ;
1684 write_all([' ',Q,' suceeds as it can be assumed'])
1685 )
1686 ;
1687 write_all([' ',Q,' fails as it is inconsistent with other assumpions'])
1688
1689 ).
1690
1691 whynotb(G=V,_,up,ass(A0,PAs,P),ass(A1,PAs,P)) :-
1692 arbit(G,V),!,
1693 (member((G=V1),A0)
1694 ->
1695 (V=V1 ->
1696 write_all([' ',G=V,' is has already been assumed.'])
1697 ; write_all([' arbitrary ',G,' has value ',V1,', so ',G=V,' fails.'])
1698 ),
1699 A1=A0
1700 ;
1701 write_all([' ',G=V,' is consistently assumed.']),
1702 A1=[G=V|A0]
1703 ).
1704
1705
1706 whynotb(Q,0,up,Ass0,Ass0) :- !,
1707 write_all([' ',Q,' fails because of the depth-bound.']).
1708
1709 % whynotb(Q,DB,up,Ass0,Ass0) :-
1710 % DB > 0,
1711 % (Q <- true),!,
1712 % write_all([' ',Q,' is a fact. It doesn''t fail.']).
1713
1714 whynotb(Q,DB,Rep,Ass0,Ass1) :-
1715 DB > 0,
1716 (Q <- B),
1717 whynotrule(Q,B,DB,Rep,Ass0,Ass1).
1718 whynotb(Q,_,up,Ass0,Ass0) :-
1719 write_all([' There is no applicable rule for ',Q,'.']).
1720
1721 whynotrule(Q,B,DB,Rep,Ass0,Ass1) :-
1722 (B=true
1723 ->
1724 write_all([' ',Q,'.']);
1725 write_all([' ',Q,' <- ',B,'.'])
1726 ),
1727 writel([' Trace this rule? [yes,no,up,help]: ']),
1728 flush_and_read(Comm),
1729 whynotruleinterpret(Comm,Q,B,DB,Rep,Ass0,Ass1).
1730
1731 whynotruleinterpret(yes,Q,B,DB,Rep,Ass0,Ass1) :- !,
1732 DB1 is DB-1,
1733 whynotb(B,DB1,Rep1,Ass0,Ass1),
1734 (Rep1 = up
1735 -> whynotrule(Q,B,DB,Rep,Ass0,Ass1)
1736 ; Rep=Rep1).
1737 whynotruleinterpret(no,_,_,_,_,_,_) :- !,
1738 fail.
1739 whynotruleinterpret(up,_,_,_,up,Ass0,Ass0) :- !.
1740 whynotruleinterpret(end_of_file,_,_,_,prompt,_,_) :- !,
1741 write_all(['^D']).
1742 whynotruleinterpret(ok,_,_,_,prompt,_,_) :- !.
1743 whynotruleinterpret(help,Q,B,DB,Rep,Ass0,Ass1) :- !,
1744 write_all([
1745 ' Do you want to examine why this rule failed?',nl,
1746 ' yes. look at this rule',nl,
1747 ' no. try another rule',nl,
1748 ' up. go back to the rule this rule was called from',nl,
1749 ' ok. go to top-level prompt']),
1750 whynotrule(Q,B,DB,Rep,Ass0,Ass1).
1751 whynotruleinterpret(Comm,Q,B,DB,Rep,Ass0,Ass1) :-
1752 write_all([' Unknown command: ',Comm,'. Type "help." for help.']),
1753 whynotrule(Q,B,DB,Rep,Ass0,Ass1).
1754
1755
1756 report_whynot_conj(A,Res,B,DB,Rep,Ass0,Ass1,Ass2) :-
1757 write_all([' The proof for ',A,' succeeded.']),
1758 (Ass0 \== Ass1 ->
1759 write_all([' assuming ',Ass1])
1760 ; true
1761 ),
1762 writel([' Is this the appropriate answer? [yes,no,debug,help]: ']),
1763 flush_and_read(Comm),
1764 why_not_conj_interpret(Comm,A,Res,B,DB,Rep,Ass1,Ass2).
1765
1766 why_not_conj_interpret(debug,A,Res,B,DB,Rep,Ass0,Ass1) :- !,
1767 traverse(Res,Rep1),
1768 (Rep1 = up
1769 -> report_whynot_conj(A,Res,B,DB,Rep,Ass0,Ass1)
1770 ; Rep=Rep1).
1771 why_not_conj_interpret(yes,_,_,B,DB,Rep,Ass0,Ass1) :- !,
1772 whynotb(B,DB,Rep,Ass0,Ass1).
1773 why_not_conj_interpret(no,_,_,_,_,_,_,_) :- !,
1774 fail. % find another proof for A
1775
1776 why_not_conj_interpret(end_of_file,_,_,_,_,prompt,_,_) :- !,
1777 write_all(['^D']).
1778 why_not_conj_interpret(ok,_,_,_,_,prompt,_,_) :- !,
1779 write_all(['^D']).
1780 why_not_conj_interpret(help,A,Res,B,DB,Rep,Ass0,Ass1) :- !,
1781 write_all([
1782 ' yes. this instance should have made the body succeed.',nl,
1783 ' no. this instance should lead to a failure of the body.',nl,
1784 ' debug. this instance is false, debug it.',nl,
1785 ' ok. I''ve had enough. Go to the prompt.',nl,
1786 ' help. print this message.']),
1787 report_whynot_conj(A,Res,B,DB,Rep,Ass0,Ass1).
1788 why_not_conj_interpret(Comm,A,Res,B,DB,Rep,Ass0,Ass1) :-
1789 write_all([' Unknown command: ',Comm,'. Type "help." for help.']),
1790 report_whynot_conj(A,Res,B,DB,Rep,Ass0,Ass1).
1791
1792 whynotb_expls(A,ETs,Rep) :-
1793 write_all([' ',~A,' succeeds, as ',A,' has the explanations: ']),
1794 print_explanations(ETs,0),
1795 writel([' [up,top,retry,prompt,how i,help,whynot]: ']),
1796 flush_and_read(Comm),
1797 interpret_naf_how(Comm,ETs,Rep0),
1798 (Rep0=repeat ->
1799 whynotb_expls(A,ETs,Rep)
1800 ; Rep=Rep0
1801 ).
1802
1803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1804 %% FILE INTERACTION
1805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1806
1807 flush_and_read(T) :-
1808 flush_output,
1809 (read(T) -> true ; flush_and_read(T)). % reads until no error.
1810
1811 ci_load(File) :-
1812 current_input(OldFile),
1813 open(File,read,Input),
1814 set_input(Input),
1815 flush_and_read(T),
1816 read_all(T),
1817 set_input(OldFile),
1818 write_all(['AILog theory ',File,' loaded.']).
1819
1820 read_all(C) :-
1821 file_or_command(C,file),
1822 flush_and_read(T2),!,
1823 read_all(T2).
1824
1825 read_all(end_of_file) :- !.
1826 read_all((H :- B)) :- !,
1827 write_all(['Error: Illegal Implication: ',H,' :- ',B,'. Use <- or prload.']).
1828 read_all(T) :-
1829 tell_clause(T),
1830 flush_and_read(T2),!,
1831 read_all(T2).
1832 read_all(_).
1833
1834 prolog_load(File) :-
1835 current_input(OldFile),
1836 open(File,read,Input),
1837 set_input(Input),
1838 flush_and_read(T),
1839 prread_all(T),
1840 set_input(OldFile),
1841 write_all(['AILog theory ',File,' consulted.']).
1842
1843
1844 prread_all(T) :-
1845 file_or_command(T,file),
1846 flush_and_read(T2),
1847 prread_all(T2).
1848 prread_all(end_of_file) :- !.
1849 prread_all(T) :-
1850 prillegal(T,Mesg),!,
1851 write_all(Mesg).
1852 prread_all(T) :-
1853 prtell(T),
1854 flush_and_read(T2),
1855 prread_all(T2).
1856
1857 % prillegal(R,Mesg) is true if R is illegal Prolog rule.
1858 % Mesg is the corresponding error message.
1859 prillegal((H <- B),['Error. Illegal Implication: ',H,' <- ',B,
1860 '. Use :- in prload.']) :- !.
1861 prillegal((:- B),['Error. Commands not allowed. ":- ',B,'."']) :- !.
1862 prillegal((_ :- B),Mesg) :- !,
1863 prillbody(B,Mesg).
1864 prillbody((A,_),Mesg) :-
1865 prillbody(A,Mesg).
1866 prillbody((_,B),Mesg) :-
1867 prillbody(B,Mesg).
1868 prillbody((_;_),['Prolog rules assume disjunction ";". Define it before loading.']) :-
1869 \+ ((_;_) <- _).
1870 prillbody((A;_),Mesg) :-
1871 prillbody(A,Mesg).
1872 prillbody((_;B),Mesg) :-
1873 prillbody(B,Mesg).
1874 prillbody((_&_),['Error. Illegal conjunction in Prolog rules: &']):- !.
1875 prillbody(!,['Error. Cut (!) not allowed.']) :- !.
1876 prillbody((_ -> _ ; _),['Error. "->" is not implemented.']) :- !.
1877 prillbody((_ -> _ ),['Error. "->" is not implemented.']) :- !.
1878
1879 % prtell(Cl) tells the prolog clause Cl
1880 prtell((H :- B)) :- !,
1881 convert_body(B,B1),
1882 assertz((H <- B1)).
1883 prtell(H) :-
1884 assertz((H <- true)).
1885
1886 % convert_body(PB,CB) converts Prolog body PB to ailog body CB
1887 convert_body((A,B),(A1&B1)) :- !,
1888 convert_body(A,A1),
1889 convert_body(B,B1).
1890 convert_body((A;B),(A1;B1)) :- !,
1891 convert_body(A,A1),
1892 convert_body(B,B1).
1893 convert_body((\+ A),(~ A1)) :- !,
1894 convert_body(A,A1).
1895 convert_body(A,A).
1896
1897
1898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1899 %% MAIN LOOP
1900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1901
1902 start :-
1903 ailog_version(V,Y),
1904 write_all([nl,
1905 'AILog Version ',V,'. Copyright ',Y,', David Poole.',nl,
1906 'AILog comes with absolutely no warranty.',nl,
1907 'All inputs end with a period. Type "help." for help.']),
1908 start1.
1909
1910 start1 :-
1911 catch(go,Exc,handle_exception(Exc)).
1912
1913 go :-
1914 writel(['ailog: ']),
1915 flush_and_read(T),
1916 (T == prolog ->
1917 write_all(['Returning to Prolog. Type "start." to start ailog.'])
1918 ; T == end_of_file ->
1919 write_all(['^D']),
1920 write_all(['Returning to Prolog. Type "start." to start ailog.'])
1921 ; interpret(T),
1922 !,
1923 go).
1924
1925 handle_exception(prompt) :- !, start1.
1926 handle_exception(quit) :- halt.
1927 handle_exception(Exc) :-
1928 write_all(['Error: ',Exc]),
1929 start1.
1930
1931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1932 %% CHECKING the KB
1933 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1934
1935 % check searches through the knowledge base looking for a rule
1936 % containing an atom in the body which doesn't have a corresponding
1937 % definition (i.e., a clause with it at the head).
1938 check :-
1939 (H <- B),
1940 body_elt_undefined(B,H,B).
1941 check.
1942
1943 check :-
1944 (H <- _),
1945 choice(H,_,_),
1946 write_all([H,' should not be defined in a rule and as a probability.']),
1947 fail.
1948
1949 body_elt_undefined(true,_,_) :- !,fail.
1950 body_elt_undefined((A&_),H,B) :-
1951 body_elt_undefined(A,H,B).
1952 body_elt_undefined((_&A),H,B) :- !,
1953 body_elt_undefined(A,H,B).
1954 body_elt_undefined((~ A),H,B) :- !,
1955 body_elt_undefined(A,H,B).
1956 body_elt_undefined((A;_),H,B) :-
1957 body_elt_undefined(A,H,B).
1958 body_elt_undefined((_;A),H,B) :- !,
1959 body_elt_undefined(A,H,B).
1960 body_elt_undefined(call(A),H,B) :- !,
1961 body_elt_undefined(A,H,B).
1962 body_elt_undefined(_ \= _,_,_) :- !,fail.
1963 body_elt_undefined(_ #= _,_,_) :- !,fail.
1964 body_elt_undefined(A,_,_) :-
1965 askabl(A),!,fail.
1966 body_elt_undefined(A,_,_) :-
1967 assumabl(A),!,fail.
1968 body_elt_undefined(A,_,_) :-
1969 choice(A,_,_),!,fail.
1970 body_elt_undefined(X=V,_,_) :-
1971 arbit(X,V),!,fail.
1972 body_elt_undefined(A,_,_) :-
1973 builtin(A,_),!,fail.
1974 body_elt_undefined(A,_,_) :-
1975 (A <- _),!,fail.
1976 body_elt_undefined(A,H,B) :-
1977 write_all(['Warning: no clauses for ',A,' in rule ',(H <- B),'.']),!,fail.
1978
1979 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1980 %% STATS
1981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1982
1983 % uncomment the next line to make runtime checking the default
1984 %checking_runtime.
1985
1986 % stats_command(Cmd) means "stats Cmd" was given as a command.
1987 stats_command(runtime) :- !,
1988 retractall(checking_runtime),
1989 asserta(checking_runtime),
1990 write_all(['Runtime report turned on. Type "stats none." to turn it off.']).
1991 stats_command(none) :- !,
1992 retractall(checking_runtime).
1993 stats_command(_) :-
1994 write_all(['The stats commands implemented are:']),
1995 write_all([' stats runtime. turn on report of runtime.']),
1996 write_all([' stats none. turn off report of runtime.']).
1997
1998 % reset_runtime means that we are storing the current valuse of
1999 % runtime. This means that we are leaving out much of the ailog
2000 % overhead from the calcluation.
2001
2002 reset_runtime :-
2003 checking_runtime,
2004 retractall(lastruntime(_)),
2005 get_runtime(T,_),
2006 asserta(lastruntime(T)),!.
2007 reset_runtime :-
2008 checking_runtime ->
2009 write_all([' Problem with runtime checking.'])
2010 ; true.
2011
2012 report_runtime :-
2013 checking_runtime,
2014 lastruntime(T),
2015 get_runtime(T1,Units),
2016 RT is T1 - T,
2017 write_all([' Runtime since last report: ',RT,' ',Units,'.']),!.
2018 report_runtime :-
2019 checking_runtime ->
2020 write_all([' Problem with runtime reporting.'])
2021 ; true.
2022
2023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2024 %% NOGOOD Handling
2025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2026 % A nogood is a set of assumables that is inconsistent
2027 construct_nogoods(DB) :-
2028 depth_bound(DB),
2029 retractall(failed(_)),
2030 assert(failed(naturally)),
2031 reset_runtime,
2032 solve(false,DB,PfTree,[wrule(yes,true,false,true)],ass([],[],1),ass(Ass,_,_)),
2033 assert(nogood(Ass,PfTree)),
2034 assert_each_nogood(Ass,[]),
2035 fail.
2036 construct_nogoods(DB) :-
2037 failed(naturally)
2038 -> write_all(['Nogoods recorded. Type "nogoods." to see them.'])
2039 ; write_all(['Nogoods recorded, but depth-bound ',DB,' reached. Type "nogoods." to see them.']).
2040
2041 assert_each_nogood([],[]).
2042 assert_each_nogood([H|NotDone],Done) :-
2043 append(NotDone,Done,Rest),
2044 assert(anogood(H,Rest)),
2045 assert_each_nogood(NotDone,[H|Done]).
2046
2047 list_nogoods :-
2048 nogood(NG,_),
2049 write_all(['Nogood: ',NG,'.']), fail.
2050 list_nogoods.
2051
2052 good(A,E) :-
2053 \+ bad(A,E).
2054 bad(A,E) :-
2055 ground((A,E)),
2056 anogood(A,N),
2057 subset(N,E).
2058
2059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2060 %% HELPERS
2061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2062
2063 % write_all(L) writes each element of list L, ends with new line
2064 write_all(L) :-
2065 \+ \+ (mynumbervars(L,0,_),writel0(L)),
2066 nl.
2067 % writel(L) writes each element of list L
2068 writel(L) :-
2069 \+ \+ (mynumbervars(L,0,_),writel0(L)).
2070
2071 % writel0(L) writes each element of list L
2072 writel0([]) :- !.
2073 writel0([nl|T]) :- !,
2074 nl,
2075 writel0(T).
2076 writel0([H|T]) :-
2077 mywrite(H),
2078 writel0(T).
2079
2080 % writeAllDelim(L,D) writes the elements of L using delimiter D
2081 writeAllDelim(L,D) :-
2082 \+ \+ (mynumbervars(L,0,_),writeAllDelim0(L,D)).
2083
2084 writeAllDelim0([],_) :-!.
2085 writeAllDelim0([E],_) :- !, mywrite(E).
2086 writeAllDelim0([H|T],nl) :- !, mywrite(H),nl,writeAllDelim0(T,nl).
2087 writeAllDelim0([H|T],D) :- mywrite(H),mywrite(D),writeAllDelim0(T,D).
2088
2089 % mywrite(T) writes term T
2090 mywrite(T) :-
2091 write_term(T,[numbervars(true),portray(true)]).
2092
2093 portray(A&B) :- mywrite(A),mywrite(' & '),mywrite(B).
2094
2095 % insert_into_sorted_list(E,L,R) inserts E into sorted list L producing R
2096 insert_into_sorted_list(A,[],[A]).
2097 insert_into_sorted_list(A,[A1|R],[A|R]) :- A == A1,!.
2098 insert_into_sorted_list(A,[A1|R],[A,A1|R]) :- A @< A1, !.
2099 insert_into_sorted_list(A,[B|R],[B|R1]) :-
2100 insert_into_sorted_list(A,R,R1).
2101
2102 % insertUpdateProbs(E,L) inserts E into sorted list L
2103 insertUpdateProbs(A,[],[A]).
2104 insertUpdateProbs(A,[A1|R],[A|R]) :- A == A1,!.
2105 insertUpdateProbs(A,[A1|R],[A,A1|R]) :- A @< A1, !.
2106 insertUpdateProbs(A,[B|R],[B|R1]) :-
2107 insertUpdateProbs(A,R,R1).
2108
2109 % consistent(L1,L2) is true if L1 & L2 are sorted lists with no
2110 % elements in common.
2111 consistent([],_) :- !.
2112 consistent(_,[]) :- !.
2113 consistent([A1|R1],[A2|R2]) :-
2114 A1 @< A2, !,
2115 consistent(R1,[A2|R2]).
2116 consistent([A1|R1],[A2|R2]) :-
2117 A1 @> A2, !,
2118 consistent([A1|R1],R2).
2119
2120 % remove(E,L,L1) removes E from list L producing L1
2121 remove(E,[E|L],L).
2122 remove(E,[A|L],[A|R]) :-
2123 remove(E,L,R).
2124
2125
2126 % subset_sorted(L1,L2) is true if list L1 is a subset of L2
2127 % all lists are assumed to be ground and sorted.
2128 subset_sorted([],_) :- !.
2129 subset_sorted([A|L1],[A|L2]) :- !,
2130 subset_sorted(L1,L2).
2131 subset_sorted([A1|L1],[A2|L2]) :-
2132 A2 @< A1,
2133 subset_sorted([A1|L1],L2).
2134
2135 % setDiff(S1,S2,S3) S3 = S1-S2. All lists are sorted.
2136
2137 setDiff([],_,[]) :- !.
2138 setDiff(S,[],S) :- !.
2139 setDiff([A|L1],[A|L2],R) :- !,
2140 setDiff(L1,L2,R).
2141 setDiff([A1|L1],[A2|L2],[A1|R]) :-
2142 A1 @< A2, !,
2143 setDiff(L1,[A2|L2],R).
2144 setDiff(L1,[_|L2],R) :-
2145 setDiff(L1,L2,R).
2146
2147 % mywhen(C,G) replaces "when". It does not delay, but instead gives an
2148 % error. This is now redundant as SWI has "when".
2149 mywhen(C,G) :-
2150 C -> G
2151 ;
2152 write_all(['Warning: ',C,' failing. Delaying not implemented.']),
2153 fail.
2154
2155 % ith(L,E,I,I0) is true if E is the Ith element of list L, starting to
2156 % count from I0
2157 ith([],_,_,_) :-
2158 throw(outofrange).
2159 ith([H|_],H,I,I) :- !.
2160 ith([_|T],E,I,I0) :-
2161 I>I0, !,
2162 I1 is I0+1,
2163 ith(T,E,I,I1).
2164 ith(_,_,_,_) :-
2165 throw(outofrange).
2166
2167 idmember(A,[A1|_]) :- A==A1,!.
2168 idmember(A,[_|L]) :- idmember(A,L).
2169
2170 mynumbervars(Term, From, To) :-
2171 numbervars(Term, From, To, [attvar(bind)]).
2172
2173 issorted([]).
2174 issorted([_]).
2175 issorted([E1,E2|R]) :-
2176 E1 @< E2,
2177 issorted([E2|R]).
2178
2179 check_sorted(L) :-
2180 issorted(L),!.
2181 check_sorted(L) :-
2182 write_all(['Warning: not sorted: ',L]).
2183
2184 % Run "start" at initialization
2185 :- initialization(start).