Re: clpfd constraints not taking effect

Markus Triska <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
Hi James,

James Hogan <[email protected]> writes:

> Renaming the "chron-bible" directory to just "bible" (or changing the
> incorrect path in src/wrapper.pl to '../../chron-bible/bible') should
> make it work.

OK, it now runs and I have looked into it. SWI runs out of stack in
this case because many of your predicates leave unnecessary
choice-points behind. The fact that you need a !/0 after process_db/2
already gives a clue that the predicate is intended to be
deterministic, but the system cannot detect this. Among other things,
this is because SWI does not (yet) perform deep indexing on predicate
arguments. You can apply simple changes which let the system detect
the determinism. For example, consider your apply_preargs/3:

   apply_preargs(_, [], []).
   apply_preargs(Events, [event_time(Event)|Tail], [Time|RTail]) :-
          get_event_time(Events, Event, time(Time, raw)),
          apply_preargs(Events, Tail, RTail).
   apply_preargs(Events, [pass(Data)|Tail], [Data|RTail]) :-
          apply_preargs(Events, Tail, RTail).

You can benefit from indexing if you rewrite this for example as:

   apply_preargs(Events, List0, List) :-
           maplist(apply_prearg(Events), List0, List).

   apply_prearg(Events, event_time(Event), Time) :-
           get_event_time(Events, Event, time(Time, raw)).
   apply_prearg(_, pass(Data), Data).

Notice that event_time/1 and pass/1 are now the outermost terms in the
auxiliary predicate's second argument (instead of being wrapped inside a
list) and can hence be distinguished by indexing.

As another example for non-determinism, consider your expand_event/1:

   expand_event(Simple, Simple) :-
           event(Simple).
   expand_event(Simple, Raw) :-
           event_simplifier(Middle, Simple),
           expand_event(Middle, Raw).

Here, there is not even a functor which can distinguish the cases.
Since in your case, the first argument is always instantiated when the
predicate is called, you can rewrite this for example as:

   event_expansion(Event0, Event) :-
           (   event(Event0) -> Event = Event0
           ;   event_simplifier(Event1, Event0),
               event_expansion(Event1, Event)
           ).

to obtain a deterministic predicate. A cleaner solution is of course
to use two functors to distinguish the two cases:

   event_expansion(simple(Simple), Simple).
   event_expansion(complex(Complex), Expansion) :- ...

This version can, in addition to being deterministic when the first
argument is instantiated, also *still* be used in all directions.

Other examples of probably unintended non-determinism are
get_event_time/1 and person/1.
   
You can use SWI-Prolog's graphical tracer to see which predicates
still leave choice-points behind:

   ?- gtrace, process_db(creation, Events).

Step through the goals (SPACE, "s" etc.) and check in the graphical
call tree where you leave choice-points behind.

SWI-Prolog's top-level also is of great help, for example:

   ?- person(enosh).
   true ;
   true ;
   false.

You know that everything is deterministic when

   ?- process_db(creation, Events).

yields a single answer and does not prompt you for more. At that
point, you do not need the !/0 in your sample queries any longer, and
you will also benefit from CLP(FD)'s improved propagation that
triggered this thread and which I will definitely re-enable in some
form after I have done more benchmarks. Your program will thus also
likely become more efficient independently of the CLP(FD) change.

All the best,
Markus
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.