10

I am reading the book "Prolog and Natural-Language Analysis" (pdf) by Pereira and Shieber and I got stuck on a remark in Problem 2.7 which reads:

In semantic network representations, we often want to ask [...] “What relationships hold between Ford and the class of companies?”

Modify your representation of semantic networks to allow both this new kind of question and the kind in the previous problem. HINT: Treat semantic network relations as Prolog individuals. This is an important Prolog programming technique, sometimes called reification in philosophical circles.

I am not familiar with this reification technique.

OK, let's assume this database of facts and rules:

isa('Ole Black', 'Mustangs').
isa('Lizzy', 'Automobiles').
isa('Ford','Companies').
isa('GM','Companies').
isa('1968','Dates').

ako('Model T', 'Automobiles').
ako('Mustangs', 'Automobiles').
ako('Companies', 'Legal Persons').
ako('Humans', 'Legal Persons').
ako('Humans', 'Physical Objects').
ako('Automobiles', 'Physical Objects').
ako('Legal Persons', 'Universal').
ako('Dates', 'Universal').
ako('Physical Objects', 'Universal').

have_mass('Physical Objects').
self_propelled('Automobiles').

company(X) :- isa(X,'Companies').
legal_persons(X) :- ako(X,'Legal Persons').

How do I write a query that, in the code above finds that the relationship between 'Ford' and 'Companies' is isa? Of course I could always write something like

fact(isa, 'Ford','Companies').

and query ?- fact(X, 'Ford','Companies'). but somehow I do not think that this is the right way to do it.

Can anybody explain me how to do it properly?

TylerH
  • 20,799
  • 66
  • 75
  • 101
acortis
  • 400
  • 3
  • 15
  • "How do I write a query that, in the code above finds that the relationship between 'Ford' and 'Companies' is isa?" Do you mean to find the predicate which "binds" both? – A.A. Aug 20 '19 at 12:02
  • If I understand correctly the meaning of "binding", yes, how do I find the predicate that binds both variables. – acortis Aug 20 '19 at 12:17
  • I haven't tried it, but based on this answer and your query, I guess the following may work: `forall(fact(X, 'Ford', 'Companies'), writeln(X))` – jcsahnwaldt Reinstate Monica Aug 20 '19 at 15:18
  • ...but the part "Modify your representation of semantic networks" sounds like you shouldn't really write such a query and you should change the data structures instead... don't know how... – jcsahnwaldt Reinstate Monica Aug 20 '19 at 15:21
  • 2
    @jcsahnwaldt I dont think he wanted to solve it that way, isnt there a SWI-Prolog function which prints out the predicate. I looked for it but could not find anything – A.A. Aug 20 '19 at 16:24

4 Answers4

6

Combining aspects of both Paul's and Carlo's answers, another possible solution is to introduce a meta-relation predicate. For example:

relation(isa/2).
relation(ako/2).
relation(have_mass/1).
...

This avoids loosing the benefit of first-argument indexing for some of the queries using the original database because of the reification of relations. It also avoids, in Carlo's solution, the call to current_predicate/2 picking up e.g. auxiliary predicates that should not be considered.

With the definition above of the relation/2 predicate, we can write:

relation(Relation, Entity1, Entity2) :-
    relation(Relation/2),
    call(Relation, Entity1, Entity2).

relation(Relation, Entity) :-
    relation(Relation/1),
    call(Relation, Entity).

and then query:

?- relation(Relation, 'Ford', 'Companies').
Relation = isa.
Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
5

I'd like to add a little bit of background to the previous answers (which are very helpful).

As far as I can tell, there's no generally accepted defintion for reification in Prolog. One could say that the relationships in your original code are already partly reified. Examples:

isa('Ford', 'Companies').
% ...is a reification of...
company('Ford').

ako('Companies', 'Legal Persons').
% ...is a reification of...
legal_person(X) :- company(X).

have_mass('Physical Objects').
% ...is a reification of...
has_mass(X) :- physical_object(X).

self_propelled('Automobiles').
% ...is a reification of...
self_propelled(X) :- automobile(X).

See the Reification section in Notes on Semantic Nets and Frames by Matthew Huntbach for details.

4

Not sure this answer is what the book intended... I've read it many years ago, italian translated, and don't remember the question.

In SWI-Prolog, modules play an important role, so I would go with:

:- module(so_relationships, [which_rel/3]).

isa('Ole Black', 'Mustangs').
isa('Lizzy', 'Automobiles').
isa('Ford','Companies').
isa('GM','Companies').
isa('1968','Dates').

ako('Model T', 'Automobiles').
ako('Mustangs', 'Automobiles').
ako('Companies', 'Legal Persons').
ako('Humans', 'Legal Persons').
ako('Humans', 'Physical Objects').
ako('Automobiles', 'Physical Objects').
ako('Legal Persons', 'Universal').
ako('Dates', 'Universal').
ako('Physical Objects', 'Universal').

have_mass('Physical Objects').
self_propelled('Automobiles').

company(X) :- isa(X,'Companies').
legal_persons(X) :- ako(X,'Legal Persons').

which_rel(A,B,R) :-
    current_predicate(so_relationships:R/2),
    C=..[R,A,B],
    %catch(call(C),E,(writeln(E),fail)).
    call(C).

Note the (commented out) catch/3. Was required before qualification of the predicate indicator with the module name.

?- which_rel('Ford','Companies',R).
R = isa ;
false.
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • 2
    `C=..[R,A,B], call(C).` ----> `call(R,A,B).` – Paulo Moura Aug 22 '19 at 11:17
  • Great answer! I like it! See also my comment to Paulo. I like that I do not have to redefine the facts in the database. – acortis Aug 22 '19 at 16:26
  • These solutions are great when you can't modify the facts, but they're a generate and test algorithm, so O(n). If you can reify the relationship it's just a lookup, which with indexing can be O(1). – Paul Brown Aug 23 '19 at 10:19
3

To go from:

isa('Ford','Companies').

to:

fact(isa, 'Ford','Companies').

is the reification technique. In Prolog a functor must be an atom, as you've likely discovered a query like:

?- HowRelated('Ford', 'Companies').
HowRelated = isa.

would be invalid syntax, the predicate in Prolog is abstract and you can't reason about it directly. To make it into something concrete that you can reason with is reification. The typical ordering would be (Subject, Predicate, Object):

fact('Ford', isa, 'Companies').

Querying like so:

?- fact('Ford', HowRelated, 'Companies').
HowRelated = isa.

A Prolog implementation that goes beyond first-argument indexing (such as the mentioned in comments SWI-Prolog) is incredibly fast at looking up these kind of queries and you can now have any part of your fact as a variable in your queries. Rest-assured, this is the proper way to do it, it's also how it is done in RDF and OWL where predicates have properties such as reflexivity or transitivity.

I don't have a particular book reference for you, but there's a blog post "Reification by Example" on PrologHub.

Paul Brown
  • 2,235
  • 12
  • 18
  • 1
    "there ain't nothing in computer science can't be solved by adding [another level](https://stackoverflow.com/a/8882745/849891) of indirection [interpretation](https://stackoverflow.com/a/57061949/849891)". :) – Will Ness Aug 22 '19 at 16:17
  • Thanks Paul for you answer, you clarified reification for me, and it is along the lines of what I was thinking. However, the answers from Carlo and Paulo below are closer to the solution I imagined because I do not have to redefine my databse of facts. – acortis Aug 22 '19 at 16:29
  • What you imagined is a linear time workaround when a constant time solution exists. To figure out how 'Mustangs' is related to 'Universal' will take 9 lookups with your current database ordering. Change your facts to triples and it would be 3, no matter what order they are in. Even large databases of facts can be quickly redefined with a script and legacy access provided through rules, also written by script. When you can redefine, do so. – Paul Brown Aug 27 '19 at 09:56