Hi Markus,
Thanks. I have updated
https://swish.swi-prolog.org/p/Minato%20task.swinb
This actually starts looking like a principled way to deal with at least
a subclass of the cases for which you claim the SICStus interface is
required. The trick is that, if you have a structure where you need to
reason about the state of variables prior to unification you create a
copy of this structure (using copy_term/2) and then you merge the copy
with the original. In other words, you have
( X -> true ; ( Y -> true ; false ) )-[X,Y]
And I turn this into the term below:
z(X,Xc,true,z(Y,Yc,true,false))-[X-Xc,Y-Yc]
Now, we associate this structure using attvars to only the original
variables. When our hook is called, some part of the original variables
are bound, but our copies are all free and we can inspect them and
control when we bind them. We could also put shadow attributes on them,
but that is not needed for this case.
In our case, we restrict the ZDD using all known variables as below.
Note that we bind the shadow variables as we go along.
zdd_restriction(b(T), b(T)).
zdd_restriction(z(AV, V, Then0, Else0), ZDD) :-
( AV == 0
-> V = 0,
zdd_restriction(Else0, ZDD)
; AV == 1
-> V = 1,
zdd_restriction(Then0, ZDD)
; var(AV)
-> zdd_restriction(Then0, Then),
zdd_restriction(Else0, Else),
ZDD = z(AV, V, Then, Else)
; type_error(boolean, AV)
).
Now our final zero-binding becomes as below. The var(V) condition
tells me that the corresponding variable was not on the path to true
and must thus be 0. If V is nonvar, the value was on the true path
and thus already instantiated to an admissible value.
all_others_0([]).
all_others_0([AV-V|Vs]) :-
( var(V)
-> AV = 0
; true
),
all_others_0(Vs).
There is of course a lot to optimize. I do claim that I found a clean
solution for the ZDD problem that is not significantly different from
yours. I think this also implies that optimizations you can think of for
your implementation can be transferred to this one. There is a little
more work establishing the constraint, but the programming work is
straight forward (copy_term/2 and merge the structures). The runtime of
user code should be very compariable, while the runtime overhead in the
system is lower. Comparing space we use a little more in user space (the
variable copy) while we save on the system administration.
Cheers --- Jan
On 09/01/2017 05:40 PM, Markus Triska wrote:
> Hi Jan,
>
> thank you for taking a look, and for providing this environment!
>
> A variable may occur on both sides of a ZDD. For example, the formula
> X*Y + ~X*Y corresponds to the ZDD:
>
> ( X -> ( Y -> true ; false) ; ( Y -> true ; false ) )
>
>
> Therefore, we generally must not set all variables occurring in the
> other branch to 0. The above example shows a case where Y must be 1 if X
> is 1, although Y occurs in the other branch too.
>
> ZDDs are actually graphs, so in a real implementation of ZDDs, there
> would only be a single node for ( Y -> true ; false ), not two as in the
> case above. However, we need not consider such sharing for now.
>
> Please compare your code with the SICStus reference implementation I posted.
>
> There is also a (very limited) ZDD-based version of library(clpb) available:
>
>
https://github.com/triska/clpb/tree/master/zdd
>
> You can use it to inspect the ZDDs of Boolean expressions. For example:
>
> ?- zdd_set_vars([X,Y]),
> sat((X*Y) + (~X* Y)).
>
>
> This yields the ZDD mentioned above, /with/ sharing of isomorphic subgraphs:
>
> node(4)-(X->node(3);node(3)),
> node(3)-(Y->true;false),
>
>
> All the best,
> Markus
>