Here is an expereiment on ZDD, which might be interesting for the discourse.
It is easy to build such a (big) set F of clauses of literals in ZDD
- F has N varialbles. (N: given).
- F is valid as DNF.
- F is unsatisifiable as CNF.
By 2 the number of solutions of F should be 2^N. By 3 a refutation for F should succeed.
In fact, as log below, predicate ltr_refute below for F succeeds, and ltr_card for F returns 2^N.
Time statisics sounds to say: counting is much faster than refutation. Of course my current codes of ltr_refute is so simple and naive as the source codes listing below. Sadly, I have no idea how to improve codes. Anyway although ZDD implementation of mine is based on my simple-minded background on ZDD, I have already shown up, I hope, that some advertised powerful features of ZDD is possible in SWI-Prolog in small prolog codes.
The current version of the pack pac is 1.5.6. After pack_install the pac, the following log shoud be reproduced:
% ?- use_modul(library(pac)).
% ?- use_modul(zdd(zdd)).
% ?- module(zdd).
% ?- time(zdd((a_big_cnf(100, X), ltr_card(100, X, C)))), C =:= 2^100.
%@ % 2,392,546 inferences, 0.290 CPU in 0.409 seconds (71% CPU, 8246121 Lips)
%@ X = 30001,
%@ C = 1267650600228229401496703205376 .
% ?- call_with_time_limit(100, time(zdd((a_big_cnf(30, X), ltr_refute(X))))).
%@ % 123,551,444 inferences, 14.271 CPU in 14.439 seconds (99% CPU, 8657765 Lips)
%@ X = 2701.
a_big_cnf(X, Y):- shift(a_big_cnf(X, Y)).
a_big_cnf(0, 1, _).
a_big_cnf(N, X, S):- N>0,
N0 is N-1,
a_big_cnf(N0, X0, S),
ltr_insert(N, X0, R, S),
ltr_insert(-N, X0, L, S),
zdd_join(L, R, X, S).
% refutation by resolution in ZDD.
ltr_refute(X, S) :- zdd_has_empty(X, S), !.
ltr_refute(X, S) :- X > 1,
cofact(X, t(A, L, R), S),
( L = 0 -> Y = R
; cofact(L, t(B, L0, R0), S),
( B = -(A)
-> ltr_merge(R0, R, Y0, S),
ltr_join(L0, Y0, Y, S)
; ltr_join(L, R, Y, S)
family_card/2 below classifies a given family of sets by cardiality.
The log below is for the powerset of a set U = [1,2,3,…,1000],
and shows the number of subsets of U which has 500 elements.
I think that time 10.355 CPU in 10.540 seconds is reasonable
as for ZDD in SWI-Prolog. The codes below contains reduntant in my sence
because I see multiple time term_hash calll in a redundant way, but
I am almost new for this subttle thing on programming, and busy enough
only for naive version.
% ?- N = 1000, numlist(1, N, Ns),
% time(zdd((X<<pow(Ns), family_card(X, P),
% zmemo(family_with_card(500)-L), card(L, C)))).
%@ % 90,174,772 inferences, 10.355 CPU in 10.540 seconds (98% CPU, 8708585 Lips)
%@ C = 270288240945436569515614693625975275496152008446548287007392875106625428705522193898612483924502370165362606085021546104802209750050679917549894219699518475423665484263751733356162464079737887344364574161119497604571044985756287880514600994219426752366915856603136862602484428109296905863799821216320.
% Classify sets in a family by cardinality.
max(A, B, A):- A@>B, !.
max(_, B, B).
family_card(X, Y):- shift(family_card(X, Y)).
family_card(0, 0, _):-!.
family_card(1, 0, S):-!, zmemo(family_with_card(0)-1, S).
family_card(I, P, S):- zmemo(family_card_done(I)-B, S),
( nonvar(B) -> true
; cofact(I, t(A, L, R), S),
family_card(L, Pl, S),
family_card(R, Pr, S),
max(Pl, Pr, P0),
insert_one_to_family(A, P0, New, S),
P is P0 + 1,
B = true
insert_one_to_family(A, 0, G, S):-!, zmemo(family_with_card(0)-F, S),
insert_atom(A, F, G, S).
insert_one_to_family(A, P, G, S):- P0 is P-1,
insert_one_to_family(A, P0, G0, S),
insert_atom(A, Fp, G, S),
zdd_join(Fp, G0, Gp, S),