4

I'm writing a trivial metainterpreter in Prolog for self-education. Basically I want to carry along the "probability" of a given solution. To do this I just declare the probability of my clause being correct. I expect if this works I will extend it with a better foundation but for now I'm just interested in solving the immediate problem, which is that my metainterpreter loops:

Code:

fuzzy_prove(true, 1.0) :- !.
fuzzy_prove(probability(P), P) :- !.
fuzzy_prove((A,B), Prob) :-
    fuzzy_prove(A, P1),
    fuzzy_prove(B, P2),
    Prob is P1 * P2.
fuzzy_prove(A, P) :-
    clause(A, B), fuzzy_prove(B, P).

father(dave, jean).
father(dave, don) :- probability(0.5).
father(don, claudia).
father(don, jimmy) :- probability(0.5).
father(jean, davey).

grandfather(Grandpop, Babs) :-
    father(Grandpop, Dad),
    father(Dad, Babs).

My queries seem at first to work:

?- fuzzy_prove(grandfather(X, Z), Prob).
X = dave,
Z = davey,
Prob = 1.0 ;
X = dave,
Z = claudia,
Prob = 0.5 ;
X = dave,
Z = jimmy,
Prob = 0.25 ;

When I ask for the next solution, I get crazy looping. If I interrupt it, it looks like this:

continue (trace mode)
   Call: (3,973,299) fuzzy_prove(call((father(_G3, _G28), father(_G28, _G4))), _G5) ? 
^  Call: (3,973,300) clause(call((father(_G3, _G28), father(_G28, _G4))), _G2044) ? 
^  Exit: (3,973,300) clause(call((father(_G3, _G28), father(_G28, _G4))), call((father(_G3, _G28), father(_G28, _G4)))) ? 
   Call: (3,973,300) fuzzy_prove(call((father(_G3, _G28), father(_G28, _G4))), _G5) ? 
^  Call: (3,973,301) clause(call((father(_G3, _G28), father(_G28, _G4))), _G2051) ? 
^  Exit: (3,973,301) clause(call((father(_G3, _G28), father(_G28, _G4))), call((father(_G3, _G28), father(_G28, _G4)))) ? 
   Call: (3,973,301) fuzzy_prove(call((father(_G3, _G28), father(_G28, _G4))), _G5) ? 
^  Call: (3,973,302) clause(call((father(_G3, _G28), father(_G28, _G4))), _G2058) ? 
^  Exit: (3,973,302) clause(call((father(_G3, _G28), father(_G28, _G4))), call((father(_G3, _G28), father(_G28, _G4)))) ? 
   Call: (3,973,302) fuzzy_prove(call((father(_G3, _G28), father(_G28, _G4))), _G5) ? 
^  Call: (3,973,303) clause(call((father(_G3, _G28), father(_G28, _G4))), _G2065) ? 
^  Exit: (3,973,303) clause(call((father(_G3, _G28), father(_G28, _G4))), call((father(_G3, _G28), father(_G28, _G4)))) ? 
   Call: (3,973,303) fuzzy_prove(call((father(_G3, _G28), father(_G28, _G4))), _G5) ? 
^  Call: (3,973,304) clause(call((father(_G3, _G28), father(_G28, _G4))), _G2072) ? 
^  Exit: (3,973,304) clause(call((father(_G3, _G28), father(_G28, _G4))), call((father(_G3, _G28), father(_G28, _G4)))) ? 

I'm sure I'm doing something obviously stupid but I'm having trouble seeing what it is.

false
  • 10,264
  • 13
  • 101
  • 209
Daniel Lyons
  • 22,421
  • 2
  • 50
  • 77

2 Answers2

3

Your clause with (,) functors matches twice: both the 3rd and 4th clauses of fuzzy_prove, when it matches the 4th clause it will eventually loop infinitely because the clause body is itself an and and cannot be simplified.

This query will demonstrate what is happening:

clause(grandfather(A,B), C0), clause(C0, C1), clause(C1, C2).

On my interpreter at least C1 = C2, and we have an infinite loop.

You need at least to check that the functor of your clause body is not (,) before recursing. I suspect there will be other corner cases like this so it may in fact be better to work out how to handle the call functor correctly.

tobyodavies
  • 27,347
  • 5
  • 42
  • 57
  • Thanks! Can you elaborate a little on what is suggested by handling the call functor correctly? I was basing my code on [this page](http://kti.ms.mff.cuni.cz/~bartak/prolog/meta_interpret.html). – Daniel Lyons Dec 11 '15 at 15:00
  • @DanielLyons I don't know exactly, just that `call` is the functor which immediately loops, so perhaps there exists something that can be done there... – tobyodavies Dec 14 '15 at 09:24
2

The problem you encountered is specific to SWI. Only SWI succeeds for a goal clause((A,B),R). ISO demands to issue a permission_error(access,private_procedure,Culprit) and B, IF, IV, GNU, SICStus, XSB, and YAP all produce this error. Note the actual reason: private_procedure, for built-ins and control constructs are all private, i.e., they cannot be accessed via clause/2.

There are many other issues with your code.

The goal fuzzy_prove(true,0.0) produces that very error instead of failing silently. Brief, your code lacks steadfastness.

Another issue is fuzzy_prove(G, P) which probably should rather give a corresponding instantiation error instead of G = true.

fuzzy_prove(probability(1.0),0.0) depends on the presence of a corresponding predicate, probably this is not intended. And if not, SWI will warn you about the nonexistence of probability/1.

false
  • 10,264
  • 13
  • 101
  • 209
  • I appreciate the information. It would be even more helpful if you could show me a robust approach. – Daniel Lyons Dec 29 '15 at 19:04
  • This would not be an answer to your question. – false Dec 29 '15 at 20:51
  • If I ask another question to see your approach would you indulge me? :) – Daniel Lyons Dec 29 '15 at 22:24
  • ((I had to lookup what indulge means - seems there is a difference between to i. something and to i. someone ...)) My point is simply that answers to questions different than the actual question asked tend to be ignored on SO (for later reference). – false Dec 29 '15 at 22:37
  • 1
    I was more-or-less copying a metainterpreter I found online somewhere. Your answer suggests that the whole approach is wrong. I'd like to know the right way to do it. If you won't tell me here, what do I do to learn a better approach that you might use? Do I ask another question? – Daniel Lyons Dec 29 '15 at 22:40
  • 1
    Indeed, ask a question (always keep in mind that one purpose of writing on SO is to keep the writing for later reference)! – false Dec 29 '15 at 22:47