My proposals is:
table f/2 as a projection of g/3, where f(X, Y) is g(X, Y, S) for some S.
That is, f is a projection of g.
Below is an observation taken from my codes in ZDD library. The first codes xcard/3
is to count the number of members of a family of sets. xcard uses term_hash table
via memo/2. memo/2 is very powerful as shown in the log. But it is not somewhat elegant, I feel.
I would like to write ycard/3 for xcard/3 using table/1 in stead of memo/2. It works, but
unfortunately, it is much weaker than memo/2. I guess it is because of the third argument of ycard/3, which is usually large complex structure , but not necessary for tabling, due to the main property of ZDD, which is necessary for only computation purpose; “projective table” is, in a sense, “tabling without computation state.”
I am afraid you are surprised about this so wild proposal, but my intuition have been obtained from experience on ZDD. In short, the projective table is a smart replacement of the memo. Does this make sense ?
xcard/3
% ?- zdd((X<<pow([a,b]), xcard(X, C))).
% ?- N=2, numlist(1, N, Ns), zdd((X<<pow(Ns), xcard(X, C))).
% ?- N=10000, numlist(1, N, Ns),
% call_with_time_limit(100, time(zdd((X<<pow(Ns), xcard(X, C))))).
%@ % 880,818 inferences, 0.081 CPU in 0.105 seconds (77% CPU, 10811032 Lips)
%@ N = 10000,
%@ Ns = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
%@ X = 10001,
xcard(X, Y):-shift0(xcard(X, Y)).
%
xcard(I, I, _):- I < 2, !.
xcard(I, C, S):- memo(I-C, S),
( nonvar(C) -> true
; cofact(I, t(_, L, R), S),
xcard(R, Cr, S),
xcard(L, Cl, S),
C is Cl + Cr
).
ycard/3
% ?- zdd((X<<pow([a,b]), ycard(X, C))).
% ?- N=2, numlist(1, N, Ns), zdd((X<<pow(Ns), ycard(X, C))).
% ?- N=100, numlist(1, N, Ns),
% call_with_time_limit(100, time(zdd((X<<pow(Ns), ycard(X, C))))).
% ?- N=1000, numlist(1, N, Ns),
% call_with_time_limit(100, time(zdd((X<<pow(Ns), ycard(X, C))))).
%@ % 68,179 inferences, 3.119 CPU in 3.342 seconds (93% CPU, 21858 Lips)
%@ ERROR: '$tbl_variant_table'/6: Not enough resources: private_table_space
%@ ^ Exception: (17) setup_call_catcher_cleanup(system:true, prolog_statistics:catch(test:zdd((test:_20<<pow([...|...]), test:ycard(_20, _22))), _6224, (report(time{cpu:0.413386, inferences:3538886, wall:1604931141.241928}, 10), throw(_6224))), _223830, prolog_statistics:(_6246=true)) ? creep
%@ Call: (19) _6246=true ? abort
%@ % Execution Aborted
ycard(X, Y):-shift0(ycard(X, Y)).
%
:- table ycard/3.
ycard(I, I, _):- I < 2, !.
ycard(I, C, S):-
cofact(I, t(_, L, R), S),
ycard(R, Cr, S),
ycard(L, Cl, S),
C is Cl + Cr.