Search code examples
databaseprologlogicdatalog

How do I prevent a Datalog rule from pruning nulls?


I have the following facts and rules:

% frequents(D,P) % D=drinker, P=pub
% serves(P,B) % B=beer
% likes(D,B)

frequents(janus, godthaab).
frequents(janus, goldenekrone).
frequents(yanai, goldenekrone).
frequents(dimi,  schlosskeller).

serves(godthaab, tuborg).
serves(godthaab, carlsberg).
serves(goldenekrone, pfungstaedter).
serves(schlosskeller, fix).

likes(janus, tuborg).
likes(janus, carlsberg).

count_good_beers_for_at(D,P,F) :- group_by((frequents(D,P), serves(P,B), likes(D,B)),[D,P],(F = count)).
possible_beers_served_for_at(D,P,B) :- lj(serves(P,B), frequents(D,R), P=R).

Now I would like to construct a rule that should work like a predicate returning "true" when the number of available "liked" beers at each pub that a "drinker" "frequents" is bigger than 0.

I would consider the predicate true when the rule returns no tuples. If the predicate is false, I was planning to make it return the bars not having a single "liked" beer.

As you can see, I already have a rule counting the good beers for a given drinker at a given pub. I also have a rule giving me the number of servable beers.

DES> count_good_beers_for_at(A,B,C)

{                                           
  count_good_beers_for_at(janus,godthaab,2)
}
Info: 1 tuple computed.          

As you can see, the counter doesn't return the pubs frequented but having 0 liked beers. I was planning to work around this by using a left outer join.

DES> is_happy_at(D,P,Z) :- lj(serves(P,B), count_good_beers_for_at(D,Y,Z), (Y=P))

Info: Processing:
  is_happy_at(D,P,Z) :-
    lj(serves(P,B),count_good_beers_for_at(D,Y,Z),Y = P).
{                                           
  is_happy_at(janus,godthaab,2),
  is_happy_at(null,goldenekrone,null),
  is_happy_at(null,schlosskeller,null)
}
Info: 3 tuples computed.

This is almost right, except it is also giving me the pubs not frequented. I try adding an extra condition:

DES> is_happy_at(D,P,Z) :- lj(serves(P,B), count_good_beers_for_at(D,Y,Z), (Y=P)), frequents(D,P)

Info: Processing:
  is_happy_at(D,P,Z) :-
    lj(serves(P,B),count_good_beers_for_at(D,Y,Z),Y = P),
    frequents(D,P).
{                                           
  is_happy_at(janus,godthaab,2)
}
Info: 1 tuple computed.

Now I somehow filtered everything containing nulls away! I suspect this is due to null-value logic in DES.

I recognize that I might be approaching this whole problem in a wrong way. Any help is appreciated.

EDIT: Assignment is "very_happy(D) ist wahr, genau dann wenn jede Bar, die Trinker D besucht, wenigstens ein Bier ausschenkt, das er mag." which translates to "very_happy(D) is true, iff each bar drinker D visits, serves at least 1 beer, that he likes". Since this assignment is about Datalog, I would think it is definitely possible to solve without using Prolog.


Solution

  • I think that for your assignement you should use basic Datalog, without abusing of aggregates. The point of the question is how to express universally quantified conditions. I googled for 'universal quantification datalog', and at first position I found deductnotes.pdf that asserts:

    An universally quantified condition can only be expressed by an equivalent condition with existential quantification and negation.

    In that PDF you will find also an useful example (pagg 9 & 10).

    Thus we must rephrase our question. I ended up with this code:

    not_happy(D) :-
      frequents(D, P),
      likes(D, B),
      not(serves(P, B)).
    
    very_happy(D) :-
      likes(D, _),
      not(not_happy(D)).
    

    that seems what's required:

    DES> very_happy(D)
    
    {                                           
    }
    Info: 0 tuple computed.          
    

    Note the likes(D, _), that's required to avoid that yanai and dimi get listed as very_happy, without explicit assertion of what them like (OT sorry my English really sucks...)

    EDIT: I'm sorry, but the above solution doesn't work. I've rewritten it this way:

    likes_pub(D, P) :-
      likes(D, B),
      serves(P, B).
    
    unhappy(D) :-
      frequents(D, P),
      not(likes_pub(D, P)).
    
    very_happy(D) :-
      likes(D, _),
      not(unhappy(D)).
    

    test:

    DES> unhappy(D)
    
    {                                           
      unhappy(dimi),
      unhappy(janus),
      unhappy(yanai)
    }
    Info: 3 tuples computed.          
    
    DES> very_happy(D)
    
    {                                           
    }
    Info: 0 tuples computed.          
    

    Now we add a fact:

    serves(goldenekrone, tuborg).
    

    and we can see the corrected code outcome:

    DES> unhappy(D)
    
    {                                           
      unhappy(dimi),
      unhappy(yanai)
    }
    Info: 2 tuples computed.          
    
    DES> very_happy(D)
    
    {                                           
      very_happy(janus)
    }
    Info: 1 tuple computed.