This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/CLPBN/clpbn/bdd.yap
2012-04-03 15:01:14 +01:00

660 lines
19 KiB
Prolog

/************************************************
BDDs in CLP(BN)
A variable is represented by the N possible cases it can take
V = v(Va, Vb, Vc)
The generic formula is
V <- X, Y
Va <- P*X1*Y1 + Q*X2*Y2 + ...
**************************************************/
:- module(clpbn_bdd,
[bdd/3,
set_solver_parameter/2,
init_bdd_solver/4,
run_bdd_solver/3,
finalize_bdd_solver/1,
check_if_bdd_done/1
]).
:- use_module(library('clpbn/dists'),
[dist/4,
get_dist_domain/2,
get_dist_domain_size/2,
get_dist_params/2
]).
:- use_module(library('clpbn/display'),
[clpbn_bind_vals/3]).
:- use_module(library('clpbn/aggregates'),
[check_for_agg_vars/2]).
:- use_module(library(atts)).
:- use_module(library(hacks)).
:- use_module(library(lists)).
:- use_module(library(dgraphs)).
:- use_module(library(bdd)).
:- use_module(library(rbtrees)).
:- dynamic network_counting/1.
:- attribute order/1.
check_if_bdd_done(_Var).
bdd([[]],_,_) :- !.
bdd([QueryVars], AllVars, AllDiffs) :-
init_bdd_solver(_, AllVars, _, BayesNet),
run_bdd_solver([QueryVars], LPs, BayesNet),
finalize_bdd_solver(BayesNet),
clpbn_bind_vals([QueryVars], [LPs], AllDiffs).
init_bdd_solver(_, AllVars0, _, bdd(Term, Leaves, Tops)) :-
% check_for_agg_vars(AllVars0, AllVars1),
sort_vars(AllVars0, AllVars, Leaves),
order_vars(AllVars, 0),
rb_new(Vars0),
rb_new(Pars0),
init_tops(Leaves,Tops),
get_vars_info(AllVars, Vars0, _Vars, Pars0, _Pars, Leaves, Tops, Term, []).
order_vars([], _).
order_vars([V|AllVars], I0) :-
put_atts(V, [order(I0)]),
I is I0+1,
order_vars(AllVars, I).
init_tops([],[]).
init_tops(_.Leaves,_.Tops) :-
init_tops(Leaves,Tops).
sort_vars(AllVars0, AllVars, Leaves) :-
dgraph_new(Graph0),
build_graph(AllVars0, Graph0, Graph),
dgraph_leaves(Graph, Leaves),
dgraph_top_sort(Graph, AllVars).
build_graph([], Graph, Graph).
build_graph(V.AllVars0, Graph0, Graph) :-
clpbn:get_atts(V, [dist(_DistId, Parents)]), !,
dgraph_add_vertex(Graph0, V, Graph1),
add_parents(Parents, V, Graph1, GraphI),
build_graph(AllVars0, GraphI, Graph).
build_graph(_V.AllVars0, Graph0, Graph) :-
build_graph(AllVars0, Graph0, Graph).
add_parents([], _V, Graph, Graph).
add_parents(V0.Parents, V, Graph0, GraphF) :-
dgraph_add_edge(Graph0, V0, V, GraphI),
add_parents(Parents, V, GraphI, GraphF).
get_vars_info([], Vs, Vs, Ps, Ps, _, _) --> [].
get_vars_info([V|MoreVs], Vs, VsF, Ps, PsF, Lvs, Outs) -->
{ clpbn:get_atts(V, [dist(DistId, Parents)]) }, !,
%{writeln(v:DistId:Parents)},
[DIST],
{ get_var_info(V, DistId, Parents, Vs, Vs2, Ps, Ps1, Lvs, Outs, DIST) },
get_vars_info(MoreVs, Vs2, VsF, Ps1, PsF, Lvs, Outs).
get_vars_info([_|MoreVs], Vs0, VsF, Ps0, PsF, VarsInfo, Lvs, Outs) :-
get_vars_info(MoreVs, Vs0, VsF, Ps0, PsF, VarsInfo, Lvs, Outs).
%
% let's have some fun with avg
%
get_var_info(V, avg(Domain), Parents0, Vs, Vs2, Ps, Ps, Lvs, Outs, DIST) :- !,
reorder_vars(Parents0, Parents),
length(Domain, DSize),
% run_though_avg(V, DSize, Domain, Parents, Vs, Vs2, Lvs, Outs, DIST).
bup_avg(V, DSize, Domain, Parents, Vs, Vs2, Lvs, Outs, DIST).
% standard random variable
get_var_info(V, DistId, Parents, Vs, Vs2, Ps, Ps1, Lvs, Outs, DIST) :-
% clpbn:get_atts(V, [key(K)]), writeln(V:K:DistId:Parents),
check_p(DistId, Parms, _ParmVars, Ps, Ps1),
unbound_parms(Parms, ParmVars),
check_v(V, DistId, DIST, Vs, Vs1),
DIST = info(V, Tree, Ev, Values, Formula, ParmVars, Parms),
% get a list of form [[P00,P01], [P10,P11], [P20,P21]]
get_parents(Parents, PVars, Vs1, Vs2),
cross_product(Values, Ev, PVars, ParmVars, Formula0),
% (numbervars(Formula0,0,_),writeln(formula0:Ev:Formula0), fail ; true),
get_evidence(V, Tree, Ev, Formula0, Formula, Lvs, Outs).
%, (numbervars(Formula,0,_),writeln(formula:Formula), fail ; true)
reorder_vars(Vs, OVs) :-
add_pos(Vs, PVs),
keysort(PVs, SVs),
remove_key(SVs, OVs1),
reverse(OVs1, OVs).
add_pos([], []).
add_pos([V|Vs], [K-V|PVs]) :-
get_atts(V,[order(K)]),
add_pos(Vs, PVs).
remove_key([], []).
remove_key([_-V|SVs], [V|OVs]) :-
remove_key(SVs, OVs).
%%%%%%%%%%%%%%%%%%%%%%%%%
%
% use top-down to generate average
%
run_though_avg(V, 3, Domain, Parents, Vs, Vs2, Lvs, Outs, DIST) :-
check_v(V, avg(Domain,Parents), DIST, Vs, Vs1),
DIST = info(V, Tree, Ev, [V0,V1,V2], Formula, [], []),
get_parents(Parents, PVars, Vs1, Vs2),
length(Parents, N),
generate_3tree(F00, PVars, 0, 0, 0, N, N0, N1, N2, R, (N1+2*N2 =< N/2), (N1+2*(N2+R) =< N/2)),
simplify_exp(F00, F0),
% generate_3tree(F1, PVars, 0, 0, 0, N, N0, N1, N2, R, ((N1+2*(N2+R) > N/2, N1+2*N2 < (3*N)/2))),
generate_3tree(F20, PVars, 0, 0, 0, N, N0, N1, N2, R, (N1+2*(N2+R) >= (3*N)/2), N1+2*N2 >= (3*N)/2),
simplify_exp(F20, F2),
Formula0 = [V0=F0*Ev0,V2=F2*Ev2,V1=not(F0+F2)*Ev1],
Ev = [Ev0,Ev1,Ev2],
get_evidence(V, Tree, Ev, Formula0, Formula, Lvs, Outs).
generate_3tree(OUT, _, I00, I10, I20, IR0, N0, N1, N2, R, _Exp, ExpF) :-
IR is IR0-1,
satisf(I00, I10, I20, IR, N0, N1, N2, R, ExpF),
!,
OUT = 1.
generate_3tree(OUT, [[P0,P1,P2]], I00, I10, I20, IR0, N0, N1, N2, R, Exp, _ExpF) :-
IR is IR0-1,
( satisf(I00+1, I10, I20, IR, N0, N1, N2, R, Exp) ->
L0 = [P0|L1]
;
L0 = L1
),
( satisf(I00, I10+1, I20, IR, N0, N1, N2, R, Exp) ->
L1 = [P1|L2]
;
L1 = L2
),
( satisf(I00, I10, I20+1, IR, N0, N1, N2, R, Exp) ->
L2 = [P2]
;
L2 = []
),
to_disj(L0, OUT).
generate_3tree(OUT, [[P0,P1,P2]|Ps], I00, I10, I20, IR0, N0, N1, N2, R, Exp, ExpF) :-
IR is IR0-1,
( satisf(I00+1, I10, I20, IR, N0, N1, N2, R, Exp) ->
I0 is I00+1, generate_3tree(O0, Ps, I0, I10, I20, IR, N0, N1, N2, R, Exp, ExpF)
->
L0 = [P0*O0|L1]
;
L0 = L1
),
( satisf(I00, I10+1, I20, IR0, N0, N1, N2, R, Exp) ->
I1 is I10+1, generate_3tree(O1, Ps, I00, I1, I20, IR, N0, N1, N2, R, Exp, ExpF)
->
L1 = [P1*O1|L2]
;
L1 = L2
),
( satisf(I00, I10, I20+1, IR0, N0, N1, N2, R, Exp) ->
I2 is I20+1, generate_3tree(O2, Ps, I00, I10, I2, IR, N0, N1, N2, R, Exp, ExpF)
->
L2 = [P2*O2]
;
L2 = []
),
to_disj(L0, OUT).
satisf(I0, I1, I2, IR, N0, N1, N2, R, Exp) :-
\+ \+ ( I0 = N0, I1=N1, I2=N2, IR=R, call(Exp) ).
not_satisf(I0, I1, I2, IR, N0, N1, N2, R, Exp) :-
\+ ( I0 = N0, I1=N1, I2=N2, IR=R, call(Exp) ).
%%%%%%%%%%%%%%%%%%%%%%%%%
%
% use bottom-up dynamic programming to generate average
%
bup_avg(V, Size, Domain, Parents, Vs, Vs2, Lvs, Outs, DIST) :-
check_v(V, avg(Domain,Parents), DIST, Vs, Vs1),
DIST = info(V, Tree, Ev, OVs, Formula, [], []),
get_parents(Parents, PVars, Vs1, Vs2),
generate_sums(PVars, Size, Max, Sums, F0),
% length(Parents, N),
% Max is (Size-1)*N, % This should be true
% easier to do recursion on lists
Sums =.. [_|LSums],
generate_avg(0, Size, 0, Max, LSums, OVs, Ev, F1, []),
reverse(F0, RF0),
get_evidence(V, Tree, Ev, F1, F2, Lvs, Outs),
append(RF0, F2, Formula).
generate_sums([PVals], Size, Max, Sum, []) :- !,
Max is Size-1,
Sum =.. [sum|PVals].
generate_sums([PVals|Parents], Size, Max, NewSums, F) :-
generate_sums(Parents, Size, Max0, Sums, F0),
Max is Max0+(Size-1),
Max1 is Max+1,
functor(NewSums, sum, Max1),
expand_sums(PVals, 0, Max0, Max1, Size, Sums, NewSums, F, F0).
%
% outer loop: generate array of sums at level j= Sum[j0...jMax]
%
expand_sums(_Parents, Max, _, Max, _Size, _Sums, _NewSums, F0, F0) :- !.
expand_sums(Parents, I0, Max0, Max, Size, Sums, NewSums, F, F0) :-
I is I0+1,
arg(I, NewSums, O),
sum_all(Parents, 0, I0, Max0, Sums, List),
to_disj(List, SUM),
expand_sums(Parents, I, Max0, Max, Size, Sums, NewSums, F, [O=SUM|F0]).
%
%inner loop: find all parents that contribute to A_ji,
% that is generate Pk*Sum_(j-1)l and k+l st k+l = i
%
sum_all([], _, _, _, _, []).
sum_all([V|Vs], Pos, I, Max0, Sums, [V*S0|List]) :-
J is I-Pos,
J >= 0,
J =< Max0, !,
J1 is J+1,
arg(J1, Sums, S0),
Pos1 is Pos+1,
sum_all(Vs, Pos1, I, Max0, Sums, List).
sum_all([_V|Vs], Pos, I, Max0, Sums, List) :-
Pos1 is Pos+1,
sum_all(Vs, Pos1, I, Max0, Sums, List).
generate_avg(Size, Size, _J, _Max, [], [], [], F, F).
generate_avg(I0, Size, J0, Max, LSums, [O|OVs], [Ev|Evs], [O=Disj*Ev|F], F0) :-
I is I0+1,
Border is (I*Max)/Size,
fetch_for_avg(J0, Border, J, LSums, MySums, RSums),
to_disj(MySums, Disj),
generate_avg(I, Size, J, Max, RSums, OVs, Evs, F, F0).
fetch_for_avg(J, Border, J, RSums, [], RSums) :-
J > Border, !.
fetch_for_avg(J0, Border, J, [S|LSums], [S|MySums], RSums) :-
J1 is J0+1,
fetch_for_avg(J1, Border, J, LSums, MySums, RSums).
to_disj([], 0).
to_disj([V], V).
to_disj([V,V1|Vs], Out) :-
to_disj2([V1|Vs], V, Out).
to_disj2([V], V0, V0+V).
to_disj2([V,V1|Vs], V0, Out) :-
to_disj2([V1|Vs], V0+V, Out).
%
% look for parameters in the rb-tree, or add a new.
% distid is the key
%
check_p(DistId, Parms, ParmVars, Ps, Ps) :-
rb_lookup(DistId, theta(Parms, ParmVars), Ps), !.
check_p(DistId, Parms, ParmVars, Ps, PsF) :-
get_dist_params(DistId, Parms0),
length(Parms0, L0),
get_dist_domain_size(DistId, Size),
L1 is L0 div Size,
L is L0-L1,
initial_maxes(L1, Multipliers),
copy(L, Multipliers, NextMults, NextMults, Parms0, Parms, ParmVars),
%writeln(t:Size:Parms0:Parms:ParmVars),
rb_insert(Ps, DistId, theta(Parms, ParmVars), PsF).
%
% we are using switches by two
%
initial_maxes(0, []) :- !.
initial_maxes(Size, [1.0|Multipliers]) :- !,
Size1 is Size-1,
initial_maxes(Size1, Multipliers).
copy(0, [], [], _, _Parms0, [], []) :- !.
copy(N, [], [], Ms, Parms0, Parms, ParmVars) :-!,
copy(N, Ms, NewMs, NewMs, Parms0, Parms, ParmVars).
copy(N, D.Ds, ND.NDs, New, El.Parms0, NEl.Parms, V.ParmVars) :-
N1 is N-1,
(El == 0.0 ->
NEl = 0,
ND = D,
V = NEl
;El == 1.0 ->
NEl = 1,
ND = 0.0,
V = NEl
;El == 0 ->
NEl = 0,
ND = D,
V = NEl
;El =:= 1 ->
NEl = 1,
ND = 0.0,
V = NEl
;
NEl is El/D,
ND is D-El,
V = NEl
),
copy(N1, Ds, NDs, New, Parms0, Parms, ParmVars).
unbound_parms([], []).
unbound_parms(_.Parms, _.ParmVars) :-
unbound_parms(Parms, ParmVars).
check_v(V, _, INFO, Vs, Vs) :-
rb_lookup(V, INFO, Vs), !.
check_v(V, DistId, INFO, Vs0, Vs) :-
get_dist_domain_size(DistId, Size),
length(Values, Size),
length(Ev, Size),
INFO = info(V, _Tree, Ev, Values, _Formula, _, _),
rb_insert(Vs0, V, INFO, Vs).
get_parents([], [], Vs, Vs).
get_parents(V.Parents, Values.PVars, Vs0, Vs) :-
clpbn:get_atts(V, [dist(DistId, _)]),
check_v(V, DistId, INFO, Vs0, Vs1),
INFO = info(V, _Parent, _Ev, Values, _, _, _),
get_parents(Parents, PVars, Vs1, Vs).
%
% construct the formula, this is the key...
%
cross_product(Values, Ev, PVars, ParmVars, Formulas) :-
arrangements(PVars, Arranges),
apply_parents_first(Values, Ev, ParmCombos, ParmCombos, Arranges, Formulas, ParmVars).
%
% if we have the parent variables with two values, we get
% [[XP,YP],[XP,YN],[XN,YP],[XN,YN]]
%
arrangements([], [[]]).
arrangements([L1|Ls],O) :-
arrangements(Ls, LN),
expand(L1, LN, O, []).
expand([], _LN) --> [].
expand([H|L1], LN) -->
concatenate_all(H, LN),
expand(L1, LN).
concatenate_all(_H, []) --> [].
concatenate_all(H, L.LN) -->
[[H|L]],
concatenate_all(H, LN).
%
% core of algorithm
%
% Values -> Output Vars for BDD
% Es -> Evidence variables
% Previous -> top of difference list with parameters used so far
% P0 -> end of difference list with parameters used so far
% Pvars -> Parents
% Eqs -> Output Equations
% Pars -> Output Theta Parameters
%
apply_parents_first([Value], [E], Previous, [], PVars, [Value=Disj*E], Parameters) :- !,
apply_last_parent(PVars, Previous, Disj),
flatten(Previous, Parameters).
apply_parents_first([Value|Values], [E|Ev], Previous, P0, PVars, (Value=Disj*E).Formulas, Parameters) :-
P0 = [TheseParents|End],
apply_first_parent(PVars, Disj, TheseParents),
apply_parents_second(Values, Ev, Previous, End, PVars, Formulas, Parameters).
apply_parents_second([Value], [E], Previous, [], PVars, [Value=Disj*E], Parameters) :- !,
apply_last_parent(PVars, Previous, Disj),
flatten(Previous, Parameters).
apply_parents_second([Value|Values], [E|Ev], Previous, P0, PVars, (Value=Disj*E).Formulas, Parameters) :-
apply_middle_parent(PVars, Previous, Disj, TheseParents),
% this must be done after applying middle parents because of the var
% test.
P0 = [TheseParents|End],
apply_parents_second(Values, Ev, Previous, End, PVars, Formulas, Parameters).
apply_first_parent([Parents], Conj, [Theta]) :- !,
parents_to_conj(Parents,Theta,Conj).
apply_first_parent(Parents.PVars, Conj+Disj, Theta.TheseParents) :-
parents_to_conj(Parents,Theta,Conj),
apply_first_parent(PVars, Disj, TheseParents).
apply_middle_parent([Parents], Other, Conj, [ThetaPar]) :- !,
skim_for_theta(Other, Theta, _, ThetaPar),
parents_to_conj(Parents,Theta,Conj).
apply_middle_parent(Parents.PVars, Other, Conj+Disj, ThetaPar.TheseParents) :-
skim_for_theta(Other, Theta, Remaining, ThetaPar),
parents_to_conj(Parents,(Theta),Conj),
apply_middle_parent(PVars, Remaining, Disj, TheseParents).
apply_last_parent([Parents], Other, Conj) :- !,
parents_to_conj(Parents,(Theta),Conj),
skim_for_theta(Other, Theta, _, _).
apply_last_parent(Parents.PVars, Other, Conj+Disj) :-
parents_to_conj(Parents,(Theta),Conj),
skim_for_theta(Other, Theta, Remaining, _),
apply_last_parent(PVars, Remaining, Disj).
%
%
% simplify stuff, removing process that is cancelled by 0s
%
parents_to_conj([], Theta, Theta) :- !.
parents_to_conj(Ps, Theta, Theta*Conj) :-
parents_to_conj2(Ps, Conj).
parents_to_conj2([P],P) :- !.
parents_to_conj2(P.Ps,P*Conj) :-
parents_to_conj2(Ps,Conj).
%
% first case we haven't reached the end of the list so we need
% to create a new parameter variable
%
skim_for_theta([[P|Other]|V], not(P)*New, [Other|_], New) :- var(V), !.
%
% last theta, it is just negation of the other ones
%
skim_for_theta([[P|Other]], not(P), [Other], _) :- !.
%
% recursive case, build-up
%
skim_for_theta([[P|Other]|More], not(P)*Ps, [Other|Left], New ) :-
skim_for_theta(More, Ps, Left, New ).
get_evidence(V, Tree, Ev, F0, F, Leaves, Finals) :-
clpbn:get_atts(V, [evidence(Pos)]), !,
zero_pos(0, Pos, Ev),
insert_output(Leaves, V, Finals, Tree, Outs, SendOut),
get_outs(F0, F, SendOut, Outs).
% hidden deterministic node, can be removed.
get_evidence(V, _Tree, Ev, F0, [], _Leaves, _Finals) :-
clpbn:get_atts(V, [key(K)]),
functor(K, Name, 2),
( Name = 'AVG' ; Name = 'MAX' ; Name = 'MIN' ),
!,
one_list(Ev),
eval_outs(F0).
%% no evidence !!!
get_evidence(V, Tree, _Values, F0, F1, Leaves, Finals) :-
insert_output(Leaves, V, Finals, Tree, Outs, SendOut),
get_outs(F0, F1, SendOut, Outs).
zero_pos(_, _Pos, []).
zero_pos(Pos, Pos, 1.Values) :- !,
I is Pos+1,
zero_pos(I, Pos, Values).
zero_pos(I0, Pos, 0.Values) :-
I is I0+1,
zero_pos(I, Pos, Values).
one_list([]).
one_list(1.Ev) :-
one_list(Ev).
%
% insert a node with the disj of all alternatives, this is only done if node ends up to be in the output
%
insert_output([], _V, [], _Out, _Outs, []).
insert_output(V._Leaves, V0, [Top|_], Top, Outs, [Top = Outs]) :- V == V0, !.
insert_output(_.Leaves, V, _.Finals, Top, Outs, SendOut) :-
insert_output(Leaves, V, Finals, Top, Outs, SendOut).
get_outs([V=F], [V=NF|End], End, V) :- !,
% writeln(f0:F),
simplify_exp(F,NF).
get_outs((V=F).Outs, (V=NF).NOuts, End, (F0 + V)) :-
% writeln(f0:F),
simplify_exp(F,NF),
get_outs(Outs, NOuts, End, F0).
eval_outs([]).
eval_outs((V=F).Outs) :-
simplify_exp(F,NF),
V = NF,
eval_outs(Outs).
%simplify_exp(V,V) :- !.
simplify_exp(V,V) :- var(V), !.
simplify_exp(S1+S2,NS) :- !,
simplify_exp(S1, SS1),
simplify_exp(S2, SS2),
simplify_sum(SS1, SS2, NS).
simplify_exp(S1*S2,NS) :- !,
simplify_exp(S1, SS1),
simplify_exp(S2, SS2),
simplify_prod(SS1, SS2, NS).
simplify_exp(not(S),NS) :- !,
simplify_exp(S, SS),
simplify_not(SS, NS).
simplify_exp(S,S).
simplify_sum(V1, V2, O) :-
( var(V1) ->
( var(V2) ->
( V1 == V2 -> O = V1 ; O = V1+V2 ) ; /* var(V1) , var(V2) */
( V2 == 0 -> O = V1 ; V2 == 1 -> O = 1 ; O = V1+V2 ) /* var(V1) , nonvar(V2) */
) ;
( var(V2) ->
( V1 == 0 -> O = V2 ; V1 == 1 -> O = 1 ; O = V1+V2 ) ; /* nonvar(V1) , var(V2) */
( V2 == 0 -> O = V1 ; V2 == 1 -> O = 1 ; V1 == 0 -> O = V2 ; V1 == 1 -> O = 1; O = V1+V2 ) /* nonvar(V1) , nonvar(V2) */
)
).
simplify_prod(V1, V2, O) :-
( var(V1) ->
( var(V2) ->
( V1 == V2 -> O = V1 ; O = V1*V2 ) ; /* var(V1) , var(V2) */
( V2 == 0 -> O = 0 ; V2 == 1 -> O = V1 ; O = V1*V2 ) /* var(V1) , nonvar(V2) */
) ;
( var(V2) ->
( V1 == 0 -> O = 0 ; V1 == 1 -> O = V2 ; O = V1*V2 ) ; /* nonvar(V1) , var(V2) */
( V2 == 0 -> O = 0 ; V2 == 1 -> O = V1 ; V1 == 0 -> O = 0 ; V1 == 1 -> O = V2; V1 == V2 -> O = V1 ; O = V1*V2 ) /* nonvar(V1) , nonvar(V2) */
)
).
simplify_not(V, not(V)) :- var(V), !.
simplify_not(0, 1) :- !.
simplify_not(1, 0) :- !.
simplify_not(SS, not(SS)).
run_bdd_solver([[V]], LPs, bdd(Term, _Leaves, Nodes)) :-
build_out_node(Nodes, Node),
findall(Prob, get_prob(Term, Node, V, Prob),TermProbs),
sumlist(TermProbs, Sum),
writeln(TermProbs:Sum),
normalise(TermProbs, Sum, LPs).
build_out_node([_Top], []).
build_out_node([T,T1|Tops], [Top = T*Top]) :-
build_out_node2(T1.Tops, Top).
build_out_node2([Top], Top).
build_out_node2([T,T1|Tops], T*Top) :-
build_out_node2(T1.Tops, Top).
get_prob(Term, Node, V, SP) :-
bind_all(Term, Node, Bindings, V, AllParms, AllParmValues),
% reverse(AllParms, RAllParms),
term_variables(AllParms, NVs),
build_bdd(Bindings, NVs, AllParms, AllParmValues, Bdd),
bdd_to_probability_sum_product(Bdd, SP),
bdd_close(Bdd).
build_bdd(Bindings, NVs, VTheta, Theta, Bdd) :-
bdd_from_list(Bindings, NVs, Bdd),
bdd_size(Bdd, Len),
% number_codes(Len,Codes),
% atom_codes(Name,Codes),
% bdd_print(Bdd, Name),
writeln(length=Len),
VTheta = Theta.
bind_all([], End, End, _V, [], []).
bind_all(info(V, _Tree, Ev, _Values, Formula, ParmVars, Parms).Term, End, BindsF, V0, ParmVars.AllParms, Parms.AllTheta) :-
V0 == V, !,
set_to_one_zeros(Ev),
bind_formula(Formula, BindsF, BindsI),
bind_all(Term, End, BindsI, V0, AllParms, AllTheta).
bind_all(info(_V, _Tree, Ev, _Values, Formula, ParmVars, Parms).Term, End, BindsF, V0, ParmVars.AllParms, Parms.AllTheta) :-
set_to_ones(Ev),!,
bind_formula(Formula, BindsF, BindsI),
bind_all(Term, End, BindsI, V0, AllParms, AllTheta).
% evidence: no need to add any stuff.
bind_all(info(_V, _Tree, _Ev, _Values, Formula, ParmVars, Parms).Term, End, BindsF, V0, ParmVars.AllParms, Parms.AllTheta) :-
bind_formula(Formula, BindsF, BindsI),
bind_all(Term, End, BindsI, V0, AllParms, AllTheta).
bind_formula([], L, L).
bind_formula(B.Formula, B.BsF, Bs0) :-
bind_formula(Formula, BsF, Bs0).
set_to_one_zeros([1|Values]) :-
set_to_zeros(Values).
set_to_one_zeros([0|Values]) :-
set_to_one_zeros(Values).
set_to_zeros([]).
set_to_zeros(0.Values) :-
set_to_zeros(Values).
set_to_ones([]).
set_to_ones(1.Values) :-
set_to_ones(Values).
normalise([], _Sum, []).
normalise(P.TermProbs, Sum, NP.LPs) :-
NP is P/Sum,
normalise(TermProbs, Sum, LPs).
finalize_bdd_solver(_).