3

I'm formalizing linguistic data into predicates and entities and doing some reasoning in prolog. Imagine I begin with:

breathe(X) :- snore(X).
sleep(X) :- snore(X).
rest(X) :- sleep(X).
live(X) :- breathe(X); eat(X); sleep(X).

snore(john).
sleep(lucy).

My data can get big enough and I would like to get a list of entities and predicates in order to iterate them and check how many predicates an entity verifies, the output can be lists like:

[john, [snore, breathe, sleep, rest, live]]
[lucy, [sleep, rest]]

or predicates

participant(john, [snore, breathe, sleep, rest, live]).
participant(lucy, [sleep, rest]).

Thanks for your help, I have no clue at this moment.

  • You want for forward-chain in backward-chaining Prolog. Works (because Prolog is a general programming language), but remains awkward. Maybe using CHR (if your Prolog has an extension for that) would be more appropriate. – David Tonhofer May 31 '21 at 12:11
  • @DavidTonhofer Is there a better alternative language to what I want than prolog, for instance in python? – EndingBeginningCycles Jun 03 '21 at 20:58
  • Well, if you know Prolog, you should stay with Prolog as it is high-level and useful enough. Going back to "imperative programming" à la Python means learning about (or even implementing) a library that provides needed functionality, which is doesn't save you. CapelliC's idea is correct. See also [Forward chaining in YAP Prolog?](https://stackoverflow.com/questions/10466682/forward-chaining-in-yap-prolog) and I had some excessive time/fun in writing [Forward and Backward Chaining](https://stackoverflow.com/questions/62376526/forward-and-backward-chaining). – David Tonhofer Jun 04 '21 at 09:32
  • Alternatively, you could invest in learning about high-level tools like [Grakn](https://grakn.ai/), if you really want to go that far and the cost/benefit ratio is favorable. Another possibility is that you could write your own mini-language and corresponding interpreter in Prolog but that also demands climbing the knowledge cliff. – David Tonhofer Jun 04 '21 at 09:35

3 Answers3

3

Representing live knowledge about an abstract world can get messy. There are a lot of different possibilities, and a lot of variance depending of which Prolog system you're using.

Here is an example of your code running in SWI-Prolog, but the same idea should work (more or less) unchanged on any Prolog out there that provides call/N and setof/3 builtins.

:- module(list_entities_that_verify_a_pred,
          [participant/2]).

:- redefine_system_predicate(sleep/1).
:- discontiguous breathe/1,sleep/1,rest/1,live/1,snore/1.

breathe(X) :- snore(X).
sleep(X) :- snore(X).
rest(X) :- sleep(X).
live(X) :- breathe(X); /* eat(X);*/ sleep(X).

snore(john).
sleep(lucy).

to_verify(breathe).
to_verify(sleep).
to_verify(rest).
to_verify(live).
to_verify(snore).

participant(Person,Verified) :-
    setof(Pred,(to_verify(Pred),call(Pred,Person)),Verified).

First, note I have commented the call to eat/1, to avoid a missing definition exception, so we can try to call the partecipant/2 predicate:

?- participant(P,L).
P = john,
L = [breathe, live, rest, sleep, snore] ;
P = lucy,
L = [live, rest, sleep].

From an architecture viewpoint, the main point to note it's the introduction of to_verify/1, to ease the workflow.

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • 1
    Is there a way to expose the eat predicate and the like? It looks like I'm going to have a lot of unmatched predicate networks like owl and proton because I'm going to use few facts and a lot of semantic relations from Wordnet. Thanks for your feedback – EndingBeginningCycles Jun 03 '21 at 19:24
  • Sorry, it's a difficult question, I don't have any 'silver bullet' ready. Which wordnet interface are you using ? That one provided by SWI-Prolog pack ? – CapelliC Jun 03 '21 at 20:51
  • I've converted a part of wordnet to implications, meaning postulates with python. Maybe prolog it's not the adequate tool to get a list of predicates. – EndingBeginningCycles Jun 03 '21 at 21:01
1

An alternative is using forward chaining using Constraint Handling Rules, an underappreciated paradigm of computation.

This is done using SWI-Prolog's CHR library. The rule engine is implemented "on top of Prolog" and adding a rule to the "constraint store" looks like calling a Prolog goal. The "constraint store" holding the current state of computation disappears once the goal completes.

Note that I'm currently not 100% certain of CHR semantics (it seems my brain is hardwired to read Prolog now) but this code seems to work.

In file sleep.pl:

:- module(forward,[
                  snore/1,
                  sleep/1,
                  collect/2,
                  pull/2
                  ]).

:- use_module(library(chr)).

:- chr_constraint snore/1, sleep/1, breathe/1.
:- chr_constraint eat/1, live/1, rest/1, collect/2, pull/2.

snore(X) ==> breathe(X).
snore(X) ==> sleep(X).
sleep(X) ==> rest(X).

breathe(X) ==> live(X).
eat(X)     ==> live(X).
sleep(X)   ==> live(X).

live(X) \ live(X) <=> true.  % eliminates duplicates

collect(Who,L),snore(Who)   <=> collect(Who,[snore|L]).
collect(Who,L),sleep(Who)   <=> collect(Who,[sleep|L]).
collect(Who,L),breathe(Who) <=> collect(Who,[breathe|L]).
collect(Who,L),eat(Who)     <=> collect(Who,[eat|L]).
collect(Who,L),live(Who)    <=> collect(Who,[live|L]).
collect(Who,L),rest(Who)    <=> collect(Who,[rest|L]).

pull(Who,L) \ collect(Who2,L2) <=> Who = Who2, L = L2.

Now we just need to start SWI-Prolog, and issue these commands:

?- [sleep].
true.

?- sleep(lucy),
   collect(lucy,[]),
   pull(Who,L).
Who = lucy,
L = [rest,live,sleep],
pull(lucy,[rest,live,sleep]).


?- snore(john),
   collect(john,[]),
   pull(Who,L).
Who = john,
L = [rest,live,breathe,sleep,snore],
pull(john,[rest,live,breathe,sleep,snore]).
David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
0

You asked in a comment to Carlo's answer:

Is there a way to expose the eat predicate and the like? It looks like I'm going to have a lot of unmatched predicate networks like owl and proton because I'm going to use few facts and a lot of semantic relations from Wordnet.

The issue here seems to be one of closed-world assumption (CWA) where predicates are declared (and thus can be called without generating errors) but not necessarily defined. In this case, as per CWA, what we cannot prove is true, is assumed to be false. E.g. the eat/1 predicate in your example or the "lot of unmatched predicate networks" in your comment.

A possible solution would be to define your entities as Logtalk objects that implement a protocol (or a set of protocols) that declare all the predicates you want to use. Reusing your example:

:- protocol(predicates).

    :- public([
        breathe/0, sleep/0, rest/0, live/0,
        snore/0, eat/0
    ]).

:- end_protocol.


:- category(generic,
    implements(predicates)).

    breathe :- ::snore.
    sleep :- ::snore.
    rest :- ::sleep.
    live :- ::breathe; ::eat; ::sleep.

:- end_category.


:- object(john, imports(generic)).

    snore.

:- end_object.


:- object(lucy, imports(generic)).

    sleep.

:- end_object.

If we ask an entity (object) about a predicate that it doesn't define, the query will simply fail. For example (using SWI-Prolog as backend here but you can use most Prolog systems; assuming the code above is saved in a code.lgt file):

$ swilgt
...
?- {code}.
...

?- lucy::eat.
false.

If we want to find all objects that satisfy e.g. the sleep/0 predicate:

?- findall(Object,
       (current_object(Object),
        conforms_to_protocol(Object, predicates),
        Object::sleep),
   Objects).
Objects = [john, lucy].

If we want to query all predicates satisfied by the objects (here with the simplifying assumption that all those predicates have zero arity):

?- setof(
       Name,
       Arity^(current_object(Object),
              conforms_to_protocol(Object, predicates),
              Object::current_predicate(Name/Arity),
              Object::Name),
       Predicates
   ).
Object = john,
Predicates = [breathe, live, rest, sleep, snore] ;
Object = lucy,
Predicates = [live, rest, sleep].

But, at least for the most common queries, handy predicate definitions would preferably be added to the generic category.

P.S. For more on the closed-world assumption and predicate semantics and also why Prolog modules fail to provide a sensible alternative solution see e.g. https://logtalk.org/2019/09/30/predicate-semantics.html

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33