Call similarity can also be based on subsumption. A term
subsumes a term
if
is more specific than
5.3. Furthermore, we say that
properly subsumes
if
is not a variant of
.
Under subsumption-based tabling, when a tabled call
is issued, a
search is performed for a table entry containing a subsuming subgoal
. Notice that, if such an entry exists, then its answer set
logically contains all the solutions to satisfy
. The
subset of answers
which unify with
are said
to be relevant to
. Likewise, upon the derivation of an
answer
for a producing subgoal
,
is inserted into the
answer set
of
if and only if
is not subsumed by some
answer
already present in
.
Notice that subsumption-based tabling permits greater reuse of computed results, thus avoiding even more program resolution, and thereby can lead to time and space performances superior to variant-based tabling. However, there is a downside to this paradigm. First of all, subsumptively tabled predicates do not interact well with certain Prolog constructs with which variant-tabled predicates can (see Example 5.2.4 below). Further, in the current implementation of subsumption-based tabling, subsumptive predicates may not take part in negative computations which result in the delay of a literal containing a subsumptive subgoal (see Section 12.1). This requires subcomputations in which subsumptive predicates take part to be LRD-stratified (see Section 5.3.2).
The above examples show how a variant-based tabled evaluation can reduce certain redundant subcomputations over SLD. However, even more redundancy can be eliminated, as the following example shows.
?- abolish_all_tables.
?- path(X,Y), fail.
Notice that only a single table entry is created during the evaluation
of this query. You can check that this is the case by invoking the
following query
?- get_calls_for_table(path/2,Call).
Now evaluate the query
?- path(1,5), fail.
and again check the subgoals in the table. Notice that two more have
been added. Further notice that these new subgoals are
subsumed by that of the original entry. Correspondingly, the
answers derived for these newer subgoals are already present in the
original entry. You can check the answers contained in a table entry
by invoking get_returns_for_call/2 on a tabled subgoal. For
example:
?- get_returns_for_call(p(1,_),Answer).
Compare these answers to those of p(X,Y) and p(1,5).
Notice that the same answer can, and in this case does, appear in
multiple table entries.
Now, let's again abolish all the tables and change the evaluation strategy of path/2 to use subsumption.
?- abolish_all_tables.
?- use_subsumptive_tabling path/2.
And re-perform the first few queries:
?- path(X,Y),fail.
?- get_calls_for_table(path/2,Call).
?- path(1,5).
?- get_calls_for_table(path/2,Call).
Notice that this time the table has not changed! Only a single entry
is present, that for the original query p(X,Y).