View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Markus Triska
    4    E-mail:        triska@metalevel.at
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (C): 2014-2018 Markus Triska
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   36   CLP(B): Constraint Logic Programming over Boolean variables.
   37- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
   38
   39:- module(clpb, [
   40                 op(300, fy, ~),
   41                 op(500, yfx, #),
   42                 sat/1,
   43                 taut/2,
   44                 labeling/1,
   45                 sat_count/2,
   46                 weighted_maximum/3,
   47                 random_labeling/2
   48                ]).   49
   50:- use_module(library(error)).   51:- use_module(library(assoc)).   52:- use_module(library(apply_macros)).   53
   54:- create_prolog_flag(clpb_monotonic, false, []).   55:- create_prolog_flag(clpb_residuals, default, []).   56
   57/** <module> CLP(B): Constraint Logic Programming over Boolean Variables
   58
   59## Introduction                        {#clpb-intro}
   60
   61This library provides CLP(B), Constraint Logic Programming over
   62Boolean variables. It can be used to model and solve combinatorial
   63problems such as verification, allocation and covering tasks.
   64
   65CLP(B) is an instance of the general [CLP(_X_) scheme](<#clp>),
   66extending logic programming with reasoning over specialised domains.
   67
   68The implementation is based on reduced and ordered Binary Decision
   69Diagrams (BDDs).
   70
   71Benchmarks and usage examples of this library are available from:
   72[__https://www.metalevel.at/clpb/__](https://www.metalevel.at/clpb/)
   73
   74We recommend the following references for citing this library in
   75scientific publications:
   76
   77==
   78@inproceedings{Triska2016,
   79  author    = "Markus Triska",
   80  title     = "The {Boolean} Constraint Solver of {SWI-Prolog}:
   81               System Description",
   82  booktitle = "FLOPS",
   83  series    = "LNCS",
   84  volume    = 9613,
   85  year      = 2016,
   86  pages     = "45--61"
   87}
   88
   89@article{Triska2018,
   90  title = "Boolean constraints in {SWI-Prolog}:
   91           A comprehensive system description",
   92  journal = "Science of Computer Programming",
   93  volume = "164",
   94  pages = "98 - 115",
   95  year = "2018",
   96  note = "Special issue of selected papers from FLOPS 2016",
   97  issn = "0167-6423",
   98  doi = "https://doi.org/10.1016/j.scico.2018.02.001",
   99  url = "http://www.sciencedirect.com/science/article/pii/S0167642318300273",
  100  author = "Markus Triska",
  101  keywords = "CLP(B), Boolean unification, Decision diagrams, BDD"
  102}
  103==
  104
  105These papers are available from
  106[https://www.metalevel.at/swiclpb.pdf](https://www.metalevel.at/swiclpb.pdf)
  107and
  108[https://www.metalevel.at/boolean.pdf](https://www.metalevel.at/boolean.pdf)
  109respectively.
  110
  111## Boolean expressions {#clpb-exprs}
  112
  113A _Boolean expression_ is one of:
  114
  115    | `0`                | false                                |
  116    | `1`                | true                                 |
  117    | _variable_         | unknown truth value                  |
  118    | _atom_             | universally quantified variable      |
  119    | ~ _Expr_           | logical NOT                          |
  120    | _Expr_ + _Expr_    | logical OR                           |
  121    | _Expr_ * _Expr_    | logical AND                          |
  122    | _Expr_ # _Expr_    | exclusive OR                         |
  123    | _Var_ ^ _Expr_     | existential quantification           |
  124    | _Expr_ =:= _Expr_  | equality                             |
  125    | _Expr_ =\= _Expr_  | disequality (same as #)              |
  126    | _Expr_ =< _Expr_   | less or equal (implication)          |
  127    | _Expr_ >= _Expr_   | greater or equal                     |
  128    | _Expr_ < _Expr_    | less than                            |
  129    | _Expr_ > _Expr_    | greater than                         |
  130    | card(Is,Exprs)     | cardinality constraint (_see below_) |
  131    | `+(Exprs)`         | n-fold disjunction (_see below_)     |
  132    | `*(Exprs)`         | n-fold conjunction (_see below_)     |
  133
  134where _Expr_ again denotes a Boolean expression.
  135
  136The Boolean expression card(Is,Exprs) is true iff the number of true
  137expressions in the list `Exprs` is a member of the list `Is` of
  138integers and integer ranges of the form `From-To`. For example, to
  139state that precisely two of the three variables `X`, `Y` and `Z` are
  140`true`, you can use `sat(card([2],[X,Y,Z]))`.
  141
  142`+(Exprs)` and `*(Exprs)` denote, respectively, the disjunction and
  143conjunction of all elements in the list `Exprs` of Boolean
  144expressions.
  145
  146Atoms denote parametric values that are universally quantified. All
  147universal quantifiers appear implicitly in front of the entire
  148expression. In residual goals, universally quantified variables always
  149appear on the right-hand side of equations. Therefore, they can be
  150used to express functional dependencies on input variables.
  151
  152## Interface predicates   {#clpb-interface}
  153
  154The most frequently used CLP(B) predicates are:
  155
  156    * sat(+Expr)
  157      True iff the Boolean expression Expr is satisfiable.
  158
  159    * taut(+Expr, -T)
  160      If Expr is a tautology with respect to the posted constraints, succeeds
  161      with *T = 1*. If Expr cannot be satisfied, succeeds with *T = 0*.
  162      Otherwise, it fails.
  163
  164    * labeling(+Vs)
  165      Assigns truth values to the variables Vs such that all constraints
  166      are satisfied.
  167
  168The unification of a CLP(B) variable _X_ with a term _T_ is equivalent
  169to posting the constraint sat(X=:=T).
  170
  171## Examples                            {#clpb-examples}
  172
  173Here is an example session with a few queries and their answers:
  174
  175==
  176?- use_module(library(clpb)).
  177true.
  178
  179?- sat(X*Y).
  180X = Y, Y = 1.
  181
  182?- sat(X * ~X).
  183false.
  184
  185?- taut(X * ~X, T).
  186T = 0,
  187sat(X=:=X).
  188
  189?- sat(X^Y^(X+Y)).
  190sat(X=:=X),
  191sat(Y=:=Y).
  192
  193?- sat(X*Y + X*Z), labeling([X,Y,Z]).
  194X = Z, Z = 1, Y = 0 ;
  195X = Y, Y = 1, Z = 0 ;
  196X = Y, Y = Z, Z = 1.
  197
  198?- sat(X =< Y), sat(Y =< Z), taut(X =< Z, T).
  199T = 1,
  200sat(X=:=X*Y),
  201sat(Y=:=Y*Z).
  202
  203?- sat(1#X#a#b).
  204sat(X=:=a#b).
  205==
  206
  207The pending residual goals constrain remaining variables to Boolean
  208expressions and are declaratively equivalent to the original query.
  209The last example illustrates that when applicable, remaining variables
  210are expressed as functions of universally quantified variables.
  211
  212## Obtaining BDDs {#clpb-residual-goals}
  213
  214By default, CLP(B) residual goals appear in (approximately) algebraic
  215normal form (ANF). This projection is often computationally expensive.
  216You can set the Prolog flag `clpb_residuals` to the value `bdd` to see
  217the BDD representation of all constraints. This results in faster
  218projection to residual goals, and is also useful for learning more
  219about BDDs. For example:
  220
  221==
  222?- set_prolog_flag(clpb_residuals, bdd).
  223true.
  224
  225?- sat(X#Y).
  226node(3)- (v(X, 0)->node(2);node(1)),
  227node(1)- (v(Y, 1)->true;false),
  228node(2)- (v(Y, 1)->false;true).
  229==
  230
  231Note that this representation cannot be pasted back on the toplevel,
  232and its details are subject to change. Use copy_term/3 to obtain
  233such answers as Prolog terms.
  234
  235The variable order of the BDD is determined by the order in which the
  236variables first appear in constraints. To obtain different orders,
  237you can for example use:
  238
  239==
  240?- sat(+[1,Y,X]), sat(X#Y).
  241node(3)- (v(Y, 0)->node(2);node(1)),
  242node(1)- (v(X, 1)->true;false),
  243node(2)- (v(X, 1)->false;true).
  244==
  245
  246## Enabling monotonic CLP(B) {#clpb-monotonic}
  247
  248In the default execution mode, CLP(B) constraints are _not_ monotonic.
  249This means that _adding_ constraints can yield new solutions. For
  250example:
  251
  252==
  253?-          sat(X=:=1), X = 1+0.
  254false.
  255
  256?- X = 1+0, sat(X=:=1), X = 1+0.
  257X = 1+0.
  258==
  259
  260This behaviour is highly problematic from a logical point of view, and
  261it may render [**declarative
  262debugging**](https://www.metalevel.at/prolog/debugging)
  263techniques inapplicable.
  264
  265Set the flag `clpb_monotonic` to `true` to make CLP(B) *monotonic*. If
  266this mode is enabled, then you must wrap CLP(B) variables with the
  267functor `v/1`. For example:
  268
  269==
  270?- set_prolog_flag(clpb_monotonic, true).
  271true.
  272
  273?- sat(v(X)=:=1#1).
  274X = 0.
  275==
  276
  277@author [Markus Triska](https://www.metalevel.at)
  278*/
  279
  280
  281/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  282   Each CLP(B) variable belongs to exactly one BDD. Each CLP(B)
  283   variable gets an attribute (in module "clpb") of the form:
  284
  285        index_root(Index,Root)
  286
  287   where Index is the variable's unique integer index, and Root is the
  288   root of the BDD that the variable belongs to.
  289
  290   Each CLP(B) variable also gets an attribute in module clpb_hash: an
  291   association table node(LID,HID) -> Node, to keep the BDD reduced.
  292   The association table of each variable must be rebuilt on occasion
  293   to remove nodes that are no longer reachable. We rebuild the
  294   association tables of involved variables after BDDs are merged to
  295   build a new root. This only serves to reclaim memory: Keeping a
  296   node in a local table even when it no longer occurs in any BDD does
  297   not affect the solver's correctness. However, apply_shortcut/4
  298   relies on the invariant that every node that occurs in the relevant
  299   BDDs is also registered in the table of its branching variable.
  300
  301   A root is a logical variable with a single attribute ("clpb_bdd")
  302   of the form:
  303
  304        Sat-BDD
  305
  306   where Sat is the SAT formula (in original form) that corresponds to
  307   BDD. Sat is necessary to rebuild the BDD after variable aliasing,
  308   and to project all remaining constraints to a list of sat/1 goals.
  309
  310   Finally, a BDD is either:
  311
  312      *)  The integers 0 or 1, denoting false and true, respectively, or
  313      *)  A node of the form
  314
  315           node(ID, Var, Low, High, Aux)
  316               Where ID is the node's unique integer ID, Var is the
  317               node's branching variable, and Low and High are the
  318               node's low (Var = 0) and high (Var = 1) children. Aux
  319               is a free variable, one for each node, that can be used
  320               to attach attributes and store intermediate results.
  321
  322   Variable aliasing is treated as a conjunction of corresponding SAT
  323   formulae.
  324
  325   You should think of CLP(B) as a potentially vast collection of BDDs
  326   that can range from small to gigantic in size, and which can merge.
  327- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  328
  329/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  330   Type checking.
  331- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  332
  333is_sat(V)     :- var(V), !, non_monotonic(V).
  334is_sat(v(V))  :- var(V), !.
  335is_sat(v(I))  :- integer(I), between(0, 1, I).
  336is_sat(I)     :- integer(I), between(0, 1, I).
  337is_sat(A)     :- atom(A).
  338is_sat(~A)    :- is_sat(A).
  339is_sat(A*B)   :- is_sat(A), is_sat(B).
  340is_sat(A+B)   :- is_sat(A), is_sat(B).
  341is_sat(A#B)   :- is_sat(A), is_sat(B).
  342is_sat(A=:=B) :- is_sat(A), is_sat(B).
  343is_sat(A=\=B) :- is_sat(A), is_sat(B).
  344is_sat(A=<B)  :- is_sat(A), is_sat(B).
  345is_sat(A>=B)  :- is_sat(A), is_sat(B).
  346is_sat(A<B)   :- is_sat(A), is_sat(B).
  347is_sat(A>B)   :- is_sat(A), is_sat(B).
  348is_sat(+(Ls)) :- must_be(list, Ls), maplist(is_sat, Ls).
  349is_sat(*(Ls)) :- must_be(list, Ls), maplist(is_sat, Ls).
  350is_sat(X^F)   :- var(X), is_sat(F).
  351is_sat(card(Is,Fs)) :-
  352        must_be(list(ground), Is),
  353        must_be(list, Fs),
  354        maplist(is_sat, Fs).
  355
  356non_monotonic(X) :-
  357        (   var_index(X, _) ->
  358            % OK: already constrained to a CLP(B) variable
  359            true
  360        ;   current_prolog_flag(clpb_monotonic, true) ->
  361            instantiation_error(X)
  362        ;   true
  363        ).
  364
  365/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  366   Rewriting to canonical expressions.
  367   Atoms are converted to variables with a special attribute.
  368   A global lookup table maintains the correspondence between atoms and
  369   their variables throughout different sat/1 goals.
  370- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  371
  372% elementary
  373sat_rewrite(V, V)       :- var(V), !.
  374sat_rewrite(I, I)       :- integer(I), !.
  375sat_rewrite(A, V)       :- atom(A), !, clpb_atom_var(A, V).
  376sat_rewrite(v(V), V).
  377sat_rewrite(P0*Q0, P*Q) :- sat_rewrite(P0, P), sat_rewrite(Q0, Q).
  378sat_rewrite(P0+Q0, P+Q) :- sat_rewrite(P0, P), sat_rewrite(Q0, Q).
  379sat_rewrite(P0#Q0, P#Q) :- sat_rewrite(P0, P), sat_rewrite(Q0, Q).
  380sat_rewrite(X^F0, X^F)  :- sat_rewrite(F0, F).
  381sat_rewrite(card(Is,Fs0), card(Is,Fs)) :-
  382        maplist(sat_rewrite, Fs0, Fs).
  383% synonyms
  384sat_rewrite(~P, R)      :- sat_rewrite(1 # P, R).
  385sat_rewrite(P =:= Q, R) :- sat_rewrite(~P # Q, R).
  386sat_rewrite(P =\= Q, R) :- sat_rewrite(P # Q, R).
  387sat_rewrite(P =< Q, R)  :- sat_rewrite(~P + Q, R).
  388sat_rewrite(P >= Q, R)  :- sat_rewrite(Q =< P, R).
  389sat_rewrite(P < Q, R)   :- sat_rewrite(~P * Q, R).
  390sat_rewrite(P > Q, R)   :- sat_rewrite(Q < P, R).
  391sat_rewrite(+(Ls), R)   :- foldl(or, Ls, 0, F), sat_rewrite(F, R).
  392sat_rewrite(*(Ls), R)   :- foldl(and, Ls, 1, F), sat_rewrite(F, R).
  393
  394or(A, B, B + A).
  395
  396and(A, B, B * A).
  397
  398must_be_sat(Sat) :-
  399        must_be(acyclic, Sat),
  400        (   is_sat(Sat) -> true
  401        ;   no_truth_value(Sat)
  402        ).
  403
  404no_truth_value(Term) :- domain_error(clpb_expr, Term).
  405
  406parse_sat(Sat0, Sat) :-
  407        must_be_sat(Sat0),
  408        sat_rewrite(Sat0, Sat),
  409        term_variables(Sat, Vs),
  410        maplist(enumerate_variable, Vs).
  411
  412enumerate_variable(V) :-
  413        (   var_index_root(V, _, _) -> true
  414        ;   clpb_next_id('$clpb_next_var', Index),
  415            put_attr(V, clpb, index_root(Index,_)),
  416            put_empty_hash(V)
  417        ).
  418
  419var_index(V, I) :- var_index_root(V, I, _).
  420
  421var_index_root(V, I, Root) :- get_attr(V, clpb, index_root(I,Root)).
  422
  423put_empty_hash(V) :-
  424        empty_assoc(H0),
  425        put_attr(V, clpb_hash, H0).
  426
  427sat_roots(Sat, Roots) :-
  428        term_variables(Sat, Vs),
  429        maplist(var_index_root, Vs, _, Roots0),
  430        term_variables(Roots0, Roots).
  431
  432%% sat(+Expr) is semidet.
  433%
  434% True iff Expr is a satisfiable Boolean expression.
  435
  436sat(Sat0) :-
  437        (   phrase(sat_ands(Sat0), Ands), Ands = [_,_|_] ->
  438            maplist(sat, Ands)
  439        ;   parse_sat(Sat0, Sat),
  440            sat_bdd(Sat, BDD),
  441            sat_roots(Sat, Roots),
  442            roots_and(Roots, Sat0-BDD, And-BDD1),
  443            maplist(del_bdd, Roots),
  444            maplist(=(Root), Roots),
  445            root_put_formula_bdd(Root, And, BDD1),
  446            is_bdd(BDD1),
  447            satisfiable_bdd(BDD1)
  448        ).
  449
  450/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  451   Posting many small sat/1 constraints is better than posting a huge
  452   conjunction (or negated disjunction), because unneeded nodes are
  453   removed from node tables after BDDs are merged. This is not
  454   possible in sat_bdd/2 because the nodes may occur in other BDDs. A
  455   better version of sat_bdd/2 or a proper implementation of a unique
  456   table including garbage collection would make this obsolete and
  457   also improve taut/2 and sat_count/2 in such cases.
  458- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  459
  460sat_ands(X) -->
  461        (   { var(X) } -> [X]
  462        ;   { X = (A*B) } -> sat_ands(A), sat_ands(B)
  463        ;   { X = *(Ls) } -> sat_ands_(Ls)
  464        ;   { X = ~Y } -> not_ors(Y)
  465        ;   [X]
  466        ).
  467
  468sat_ands_([]) --> [].
  469sat_ands_([L|Ls]) --> [L], sat_ands_(Ls).
  470
  471not_ors(X) -->
  472        (   { var(X) } -> [~X]
  473        ;   { X = (A+B) } -> not_ors(A), not_ors(B)
  474        ;   { X = +(Ls) } -> not_ors_(Ls)
  475        ;   [~X]
  476        ).
  477
  478not_ors_([]) --> [].
  479not_ors_([L|Ls]) --> [~L], not_ors_(Ls).
  480
  481del_bdd(Root) :- del_attr(Root, clpb_bdd).
  482
  483root_get_formula_bdd(Root, F, BDD) :- get_attr(Root, clpb_bdd, F-BDD).
  484
  485root_put_formula_bdd(Root, F, BDD) :- put_attr(Root, clpb_bdd, F-BDD).
  486
  487roots_and(Roots, Sat0-BDD0, Sat-BDD) :-
  488        foldl(root_and, Roots, Sat0-BDD0, Sat-BDD),
  489        rebuild_hashes(BDD).
  490
  491root_and(Root, Sat0-BDD0, Sat-BDD) :-
  492        (   root_get_formula_bdd(Root, F, B) ->
  493            Sat = F*Sat0,
  494            bdd_and(B, BDD0, BDD)
  495        ;   Sat = Sat0,
  496            BDD = BDD0
  497        ).
  498
  499bdd_and(NA, NB, And) :-
  500        apply(*, NA, NB, And),
  501        is_bdd(And).
  502
  503%% taut(+Expr, -T) is semidet
  504%
  505% Tautology check. Succeeds with T = 0 if the Boolean expression Expr
  506% cannot be satisfied, and with T = 1 if Expr is always true with
  507% respect to the current constraints. Fails otherwise.
  508
  509taut(Sat0, T) :-
  510        parse_sat(Sat0, Sat),
  511        (   T = 0, \+ sat(Sat) -> true
  512        ;   T = 1, tautology(Sat) -> true
  513        ;   false
  514        ).
  515
  516/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  517   The algebraic equivalence: tautology(F) <=> \+ sat(~F) does NOT
  518   hold in CLP(B) because the quantifiers of universally quantified
  519   variables always implicitly appear in front of the *entire*
  520   expression. Thus we have for example: X+a is not a tautology, but
  521   ~(X+a), meaning forall(a, ~(X+a)), is unsatisfiable:
  522
  523      sat(~(X+a)) = sat(~X * ~a) = sat(~X), sat(~a) = X=0, false
  524
  525   The actual negation of X+a, namely ~forall(A,X+A), in terms of
  526   CLP(B): ~ ~exists(A, ~(X+A)), is of course satisfiable:
  527
  528      ?- sat(~ ~A^ ~(X+A)).
  529      %@ X = 0,
  530      %@ sat(A=:=A).
  531
  532   Instead, of such rewriting, we test whether the BDD of the negated
  533   formula is 0. Critically, this avoids constraint propagation.
  534- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  535
  536tautology(Sat) :-
  537        (   phrase(sat_ands(Sat), Ands), Ands = [_,_|_] ->
  538            maplist(tautology, Ands)
  539        ;   catch((sat_roots(Sat, Roots),
  540                   roots_and(Roots, _-1, _-Ands),
  541                   sat_bdd(1#Sat, BDD),
  542                   bdd_and(BDD, Ands, B),
  543                   B == 0,
  544                   % reset all attributes
  545                   throw(tautology)),
  546                  tautology,
  547                  true)
  548        ).
  549
  550satisfiable_bdd(BDD) :-
  551        (   BDD == 0 -> false
  552        ;   BDD == 1 -> true
  553        ;   (   bdd_nodes(var_unbound, BDD, Nodes) ->
  554                bdd_variables_classification(BDD, Nodes, Classes),
  555                partition(var_class, Classes, Eqs, Bs, Ds),
  556                domain_consistency(Eqs, Goal),
  557                aliasing_consistency(Bs, Ds, Goals),
  558                maplist(unification, [Goal|Goals])
  559            ;   % if any variable is instantiated, we do not perform
  560                % any propagation for now
  561                true
  562            )
  563        ).
  564
  565var_class(_=_, <).
  566var_class(further_branching(_,_), =).
  567var_class(negative_decisive(_), >).
  568
  569unification(true).
  570unification(A=B) :- A = B.      % safe_goal/1 detects safety of this call
  571
  572var_unbound(Node) :-
  573        node_var_low_high(Node, Var, _, _),
  574        var(Var).
  575
  576universal_var(Var) :- get_attr(Var, clpb_atom, _).
  577
  578/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  579   By aliasing consistency, we mean that all unifications X=Y, where
  580   taut(X=:=Y, 1) holds, are posted.
  581
  582   To detect this, we distinguish two kinds of variables among those
  583   variables that are not skipped in any branch: further-branching and
  584   negative-decisive. X is negative-decisive iff every node where X
  585   appears as a branching variable has 0 as one of its children. X is
  586   further-branching iff 1 is not a direct child of any node where X
  587   appears as a branching variable.
  588
  589   Any potential aliasing must involve one further-branching, and one
  590   negative-decisive variable. X=Y must hold if, for each low branch
  591   of nodes with X as branching variable, Y has high branch 0, and for
  592   each high branch of nodes involving X, Y has low branch 0.
  593- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  594
  595aliasing_consistency(Bs, Ds, Goals) :-
  596        phrase(aliasings(Bs, Ds), Goals).
  597
  598aliasings([], _) --> [].
  599aliasings([further_branching(B,Nodes)|Bs], Ds) -->
  600        { var_index(B, BI) },
  601        aliasings_(Ds, B, BI, Nodes),
  602        aliasings(Bs, Ds).
  603
  604aliasings_([], _, _, _) --> [].
  605aliasings_([negative_decisive(D)|Ds], B, BI, Nodes) -->
  606        { var_index(D, DI) },
  607        (   { DI > BI,
  608              always_false(high, DI, Nodes),
  609              always_false(low, DI, Nodes),
  610              var_or_atom(D, DA), var_or_atom(B, BA) } ->
  611            [DA=BA]
  612        ;   []
  613        ),
  614        aliasings_(Ds, B, BI, Nodes).
  615
  616var_or_atom(Var, VA) :-
  617        (   get_attr(Var, clpb_atom, VA) -> true
  618        ;   VA = Var
  619        ).
  620
  621always_false(Which, DI, Nodes) :-
  622        phrase(nodes_always_false(Nodes, Which, DI), Opposites),
  623        maplist(with_aux(unvisit), Opposites).
  624
  625nodes_always_false([], _, _) --> [].
  626nodes_always_false([Node|Nodes], Which, DI) -->
  627        { which_node_child(Which, Node, Child),
  628          opposite(Which, Opposite) },
  629        opposite_always_false(Opposite, DI, Child),
  630        nodes_always_false(Nodes, Which, DI).
  631
  632which_node_child(low, Node, Child) :-
  633        node_var_low_high(Node, _, Child, _).
  634which_node_child(high, Node, Child) :-
  635        node_var_low_high(Node, _, _, Child).
  636
  637opposite(low, high).
  638opposite(high, low).
  639
  640opposite_always_false(Opposite, DI, Node) -->
  641        (   { node_visited(Node) } -> []
  642        ;   { node_var_low_high(Node, Var, Low, High),
  643              with_aux(put_visited, Node),
  644              var_index(Var, VI) },
  645            [Node],
  646            (   { VI =:= DI } ->
  647                { which_node_child(Opposite, Node, Child),
  648                  Child == 0 }
  649            ;   opposite_always_false(Opposite, DI, Low),
  650                opposite_always_false(Opposite, DI, High)
  651            )
  652        ).
  653
  654further_branching(Node) :-
  655        node_var_low_high(Node, _, Low, High),
  656        Low \== 1,
  657        High \== 1.
  658
  659negative_decisive(Node) :-
  660        node_var_low_high(Node, _, Low, High),
  661        (   Low == 0 -> true
  662        ;   High == 0 -> true
  663        ;   false
  664        ).
  665
  666/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  667   Instantiate all variables that only admit a single Boolean value.
  668
  669   This is the case if: The variable is not skipped in any branch
  670   leading to 1 (its being skipped means that it may be assigned
  671   either 0 or 1 and can thus not be fixed yet), and all nodes where
  672   it occurs as a branching variable have either lower or upper child
  673   fixed to 0 consistently.
  674- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  675
  676domain_consistency(Eqs, Goal) :-
  677        maplist(eq_a_b, Eqs, Vs, Values),
  678        Goal = (Vs = Values). % propagate all assignments at once
  679
  680eq_a_b(A=B, A, B).
  681
  682consistently_false_(Which, Node) :-
  683        which_node_child(Which, Node, Child),
  684        Child == 0.
  685
  686/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  687   In essentially one sweep of the BDD, all variables can be classified:
  688   Unification with 0 or 1, further branching and/or negative decisive.
  689
  690   Strategy: Breadth-first traversal of the BDD, failing (and thus
  691   clearing all attributes) if the variable is skipped in some branch,
  692   and moving the frontier along each time.
  693
  694   A formula is only satisfiable if it is a tautology after all (also
  695   implicitly) existentially quantified variables are projected away.
  696   However, we only need to check this explicitly if at least one
  697   universally quantified variable appears. Otherwise, we know that
  698   the formula is satisfiable at this point, because its BDD is not 0.
  699- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  700
  701bdd_variables_classification(BDD, Nodes, Classes) :-
  702        nodes_variables(Nodes, Vs0),
  703        variables_in_index_order(Vs0, Vs),
  704        (   partition(universal_var, Vs, [_|_], Es) ->
  705            foldl(existential, Es, BDD, 1)
  706        ;   true
  707        ),
  708        phrase(variables_classification(Vs, [BDD]), Classes),
  709        maplist(with_aux(unvisit), Nodes).
  710
  711variables_classification([], _) --> [].
  712variables_classification([V|Vs], Nodes0) -->
  713        { var_index(V, Index) },
  714        (   { phrase(nodes_with_variable(Nodes0, Index), Nodes) } ->
  715            (   { maplist(consistently_false_(low), Nodes) } -> [V=1]
  716            ;   { maplist(consistently_false_(high), Nodes) } -> [V=0]
  717            ;   []
  718            ),
  719            (   { maplist(further_branching, Nodes) } ->
  720                [further_branching(V, Nodes)]
  721            ;   []
  722            ),
  723            (   { maplist(negative_decisive, Nodes) } ->
  724                [negative_decisive(V)]
  725            ;   []
  726            ),
  727            { maplist(with_aux(unvisit), Nodes) },
  728            variables_classification(Vs, Nodes)
  729        ;   variables_classification(Vs, Nodes0)
  730        ).
  731
  732nodes_with_variable([], _) --> [].
  733nodes_with_variable([Node|Nodes], VI) -->
  734        { Node \== 1 },
  735        (   { node_visited(Node) } -> nodes_with_variable(Nodes, VI)
  736        ;   { with_aux(put_visited, Node),
  737              node_var_low_high(Node, OVar, Low, High),
  738              var_index(OVar, OVI) },
  739            { OVI =< VI },
  740            (   { OVI =:= VI } -> [Node]
  741            ;   nodes_with_variable([Low,High], VI)
  742            ),
  743            nodes_with_variable(Nodes, VI)
  744        ).
  745
  746/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  747   Node management. Always use an existing node, if there is one.
  748- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  749
  750make_node(Var, Low, High, Node) :-
  751        (   Low == High -> Node = Low
  752        ;   low_high_key(Low, High, Key),
  753            (   lookup_node(Var, Key, Node) -> true
  754            ;   clpb_next_id('$clpb_next_node', ID),
  755                Node = node(ID,Var,Low,High,_Aux),
  756                register_node(Var, Key, Node)
  757            )
  758        ).
  759
  760make_node(Var, Low, High, Node) -->
  761        % make it conveniently usable within DCGs
  762        { make_node(Var, Low, High, Node) }.
  763
  764
  765% The key of a node for hashing is determined by the IDs of its
  766% children.
  767
  768low_high_key(Low, High, node(LID,HID)) :-
  769        node_id(Low, LID),
  770        node_id(High, HID).
  771
  772
  773rebuild_hashes(BDD) :-
  774        bdd_nodes(nodevar_put_empty_hash, BDD, Nodes),
  775        maplist(re_register_node, Nodes).
  776
  777nodevar_put_empty_hash(Node) :-
  778        node_var_low_high(Node, Var, _, _),
  779        empty_assoc(H0),
  780        put_attr(Var, clpb_hash, H0).
  781
  782re_register_node(Node) :-
  783        node_var_low_high(Node, Var, Low, High),
  784        low_high_key(Low, High, Key),
  785        register_node(Var, Key, Node).
  786
  787register_node(Var, Key, Node) :-
  788        get_attr(Var, clpb_hash, H0),
  789        put_assoc(Key, H0, Node, H),
  790        put_attr(Var, clpb_hash, H).
  791
  792lookup_node(Var, Key, Node) :-
  793        get_attr(Var, clpb_hash, H0),
  794        get_assoc(Key, H0, Node).
  795
  796
  797node_id(0, false).
  798node_id(1, true).
  799node_id(node(ID,_,_,_,_), ID).
  800
  801node_aux(Node, Aux) :- arg(5, Node, Aux).
  802
  803node_var_low_high(Node, Var, Low, High) :-
  804        Node = node(_,Var,Low,High,_).
  805
  806
  807/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  808   sat_bdd/2 converts a SAT formula in canonical form to an ordered
  809   and reduced BDD.
  810- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  811
  812sat_bdd(V, Node)           :- var(V), !, make_node(V, 0, 1, Node).
  813sat_bdd(I, I)              :- integer(I), !.
  814sat_bdd(V^Sat, Node)       :- !, sat_bdd(Sat, BDD), existential(V, BDD, Node).
  815sat_bdd(card(Is,Fs), Node) :- !, counter_network(Is, Fs, Node).
  816sat_bdd(Sat, Node)         :- !,
  817        Sat =.. [F,A,B],
  818        sat_bdd(A, NA),
  819        sat_bdd(B, NB),
  820        apply(F, NA, NB, Node).
  821
  822existential(V, BDD, Node) :-
  823        var_index(V, Index),
  824        bdd_restriction(BDD, Index, 0, NA),
  825        bdd_restriction(BDD, Index, 1, NB),
  826        apply(+, NA, NB, Node).
  827
  828/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  829   Counter network for card(Is,Fs).
  830- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  831
  832counter_network(Cs, Fs, Node) :-
  833        same_length([_|Fs], Indicators),
  834        fill_indicators(Indicators, 0, Cs),
  835        phrase(formulas_variables(Fs, Vars0), ExBDDs),
  836        maplist(unvisit, Vars0),
  837        % The counter network is built bottom-up, so variables with
  838        % highest index must be processed first.
  839        variables_in_index_order(Vars0, Vars1),
  840        reverse(Vars1, Vars),
  841        counter_network_(Vars, Indicators, Node0),
  842        foldl(existential_and, ExBDDs, Node0, Node).
  843
  844% Introduce fresh variables for expressions that are not variables.
  845% These variables are later existentially quantified to remove them.
  846% Also, new variables are introduced for variables that are used more
  847% than once, as in card([0,1],[X,X,Y]), to keep the BDD ordered.
  848
  849formulas_variables([], []) --> [].
  850formulas_variables([F|Fs], [V|Vs]) -->
  851        (   { var(F), \+ is_visited(F) } ->
  852            { V = F,
  853              put_visited(F) }
  854        ;   { enumerate_variable(V),
  855              sat_rewrite(V =:= F, Sat),
  856              sat_bdd(Sat, BDD) },
  857            [V-BDD]
  858        ),
  859        formulas_variables(Fs, Vs).
  860
  861counter_network_([], [Node], Node).
  862counter_network_([Var|Vars], [I|Is0], Node) :-
  863        foldl(indicators_pairing(Var), Is0, Is, I, _),
  864        counter_network_(Vars, Is, Node).
  865
  866indicators_pairing(Var, I, Node, Prev, I) :- make_node(Var, Prev, I, Node).
  867
  868fill_indicators([], _, _).
  869fill_indicators([I|Is], Index0, Cs) :-
  870        (   memberchk(Index0, Cs) -> I = 1
  871        ;   member(A-B, Cs), between(A, B, Index0) -> I = 1
  872        ;   I = 0
  873        ),
  874        Index1 is Index0 + 1,
  875        fill_indicators(Is, Index1, Cs).
  876
  877existential_and(Ex-BDD, Node0, Node) :-
  878        bdd_and(BDD, Node0, Node1),
  879        existential(Ex, Node1, Node),
  880        % remove attributes to avoid residual goals for variables that
  881        % are only used as substitutes for formulas
  882        del_attrs(Ex).
  883
  884/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  885   Compute F(NA, NB).
  886
  887   We use a DCG to thread through an implicit argument G0, an
  888   association table F(IDA,IDB) -> Node, used for memoization.
  889- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  890
  891apply(F, NA, NB, Node) :-
  892        empty_assoc(G0),
  893        phrase(apply(F, NA, NB, Node), [G0], _).
  894
  895apply(F, NA, NB, Node) -->
  896        (   { integer(NA), integer(NB) } -> { once(bool_op(F, NA, NB, Node)) }
  897        ;   { apply_shortcut(F, NA, NB, Node) } -> []
  898        ;   { node_id(NA, IDA), node_id(NB, IDB), Key =.. [F,IDA,IDB] },
  899            (   state(G0), { get_assoc(Key, G0, Node) } -> []
  900            ;   apply_(F, NA, NB, Node),
  901                state(G0, G),
  902                { put_assoc(Key, G0, Node, G) }
  903            )
  904        ).
  905
  906apply_shortcut(+, NA, NB, Node) :-
  907        (   NA == 0 -> Node = NB
  908        ;   NA == 1 -> Node = 1
  909        ;   NB == 0 -> Node = NA
  910        ;   NB == 1 -> Node = 1
  911        ;   false
  912        ).
  913
  914apply_shortcut(*, NA, NB, Node) :-
  915        (   NA == 0 -> Node = 0
  916        ;   NA == 1 -> Node = NB
  917        ;   NB == 0 -> Node = 0
  918        ;   NB == 1 -> Node = NA
  919        ;   false
  920        ).
  921
  922
  923apply_(F, NA, NB, Node) -->
  924        { var_less_than(NA, NB),
  925          !,
  926          node_var_low_high(NA, VA, LA, HA) },
  927        apply(F, LA, NB, Low),
  928        apply(F, HA, NB, High),
  929        make_node(VA, Low, High, Node).
  930apply_(F, NA, NB, Node) -->
  931        { node_var_low_high(NA, VA, LA, HA),
  932          node_var_low_high(NB, VB, LB, HB),
  933          VA == VB },
  934        !,
  935        apply(F, LA, LB, Low),
  936        apply(F, HA, HB, High),
  937        make_node(VA, Low, High, Node).
  938apply_(F, NA, NB, Node) --> % NB < NA
  939        { node_var_low_high(NB, VB, LB, HB) },
  940        apply(F, NA, LB, Low),
  941        apply(F, NA, HB, High),
  942        make_node(VB, Low, High, Node).
  943
  944
  945node_varindex(Node, VI) :-
  946        node_var_low_high(Node, V, _, _),
  947        var_index(V, VI).
  948
  949var_less_than(NA, NB) :-
  950        (   integer(NB) -> true
  951        ;   node_varindex(NA, VAI),
  952            node_varindex(NB, VBI),
  953            VAI < VBI
  954        ).
  955
  956bool_op(+, 0, 0, 0).
  957bool_op(+, 0, 1, 1).
  958bool_op(+, 1, 0, 1).
  959bool_op(+, 1, 1, 1).
  960
  961bool_op(*, 0, 0, 0).
  962bool_op(*, 0, 1, 0).
  963bool_op(*, 1, 0, 0).
  964bool_op(*, 1, 1, 1).
  965
  966bool_op(#, 0, 0, 0).
  967bool_op(#, 0, 1, 1).
  968bool_op(#, 1, 0, 1).
  969bool_op(#, 1, 1, 0).
  970
  971/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  972   Access implicit state in DCGs.
  973- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  974
  975state(S) --> state(S, S).
  976
  977state(S0, S), [S] --> [S0].
  978
  979/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  980   Unification. X = Expr is equivalent to sat(X =:= Expr).
  981
  982   Current limitation:
  983   ===================
  984
  985   The current interface of attributed variables is not general enough
  986   to express what we need. For example,
  987
  988       ?- sat(A + B), A = A + 1.
  989
  990   should be equivalent to
  991
  992       ?- sat(A + B), sat(A =:= A + 1).
  993
  994   However, attr_unify_hook/2 is only called *after* the unification
  995   of A with A + 1 has already taken place and turned A into a cyclic
  996   ground term, raised an error or failed (depending on the flag
  997   occurs_check), making it impossible to reason about the variable A
  998   in the unification hook. Therefore, a more general interface for
  999   attributed variables should replace the current one. In particular,
 1000   unification filters should be able to reason about terms before
 1001   they are unified with anything.
 1002- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1003
 1004attr_unify_hook(index_root(I,Root), Other) :-
 1005        (   integer(Other) ->
 1006            (   between(0, 1, Other) ->
 1007                root_get_formula_bdd(Root, Sat, BDD0),
 1008                bdd_restriction(BDD0, I, Other, BDD),
 1009                root_put_formula_bdd(Root, Sat, BDD),
 1010                satisfiable_bdd(BDD)
 1011            ;   no_truth_value(Other)
 1012            )
 1013        ;   atom(Other) ->
 1014            root_get_formula_bdd(Root, Sat0, _),
 1015            parse_sat(Sat0, Sat),
 1016            sat_bdd(Sat, BDD),
 1017            root_put_formula_bdd(Root, Sat0, BDD),
 1018            is_bdd(BDD),
 1019            satisfiable_bdd(BDD)
 1020        ;   % due to variable aliasing, any BDDs may now be unordered,
 1021            % so we need to rebuild the new BDD from the conjunction.
 1022            root_get_formula_bdd(Root, Sat0, _),
 1023            Sat = Sat0*OtherSat,
 1024            (   var(Other), var_index_root(Other, _, OtherRoot),
 1025                OtherRoot \== Root ->
 1026                root_get_formula_bdd(OtherRoot, OtherSat, _),
 1027                parse_sat(Sat, Sat1),
 1028                sat_bdd(Sat1, BDD1),
 1029                And = Sat,
 1030                sat_roots(Sat, Roots)
 1031            ;   parse_sat(Other, OtherSat),
 1032                sat_roots(Sat, Roots),
 1033                maplist(root_rebuild_bdd, Roots),
 1034                roots_and(Roots, 1-1, And-BDD1)
 1035            ),
 1036            maplist(del_bdd, Roots),
 1037            maplist(=(NewRoot), Roots),
 1038            root_put_formula_bdd(NewRoot, And, BDD1),
 1039            is_bdd(BDD1),
 1040            satisfiable_bdd(BDD1)
 1041        ).
 1042
 1043root_rebuild_bdd(Root) :-
 1044        (   root_get_formula_bdd(Root, F0, _) ->
 1045            parse_sat(F0, Sat),
 1046            sat_bdd(Sat, BDD),
 1047            root_put_formula_bdd(Root, F0, BDD)
 1048        ;   true
 1049        ).
 1050
 1051/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1052   Support for project_attributes/2.
 1053
 1054   This is called by the toplevel as
 1055
 1056      project_attributes(+QueryVars, +AttrVars)
 1057
 1058   in order to project all remaining constraints onto QueryVars.
 1059
 1060   All CLP(B) variables that do not occur in QueryVars or AttrVars
 1061   need to be existentially quantified, so that they do not occur in
 1062   residual goals. This is very easy to do in the case of CLP(B).
 1063- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1064
 1065project_attributes(QueryVars0, AttrVars) :-
 1066        append(QueryVars0, AttrVars, QueryVars1),
 1067        include(clpb_variable, QueryVars1, QueryVars),
 1068        maplist(var_index_root, QueryVars, _, Roots0),
 1069        sort(Roots0, Roots),
 1070        maplist(remove_hidden_variables(QueryVars), Roots).
 1071
 1072clpb_variable(Var) :- var_index(Var, _).
 1073
 1074/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1075   All CLP(B) variables occurring in BDDs but not in query variables
 1076   become existentially quantified. This must also be reflected in the
 1077   formula. In addition, an attribute is attached to these variables
 1078   to suppress superfluous sat(V=:=V) goals.
 1079- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1080
 1081remove_hidden_variables(QueryVars, Root) :-
 1082        root_get_formula_bdd(Root, Formula, BDD0),
 1083        maplist(put_visited, QueryVars),
 1084        bdd_variables(BDD0, HiddenVars0),
 1085        exclude(universal_var, HiddenVars0, HiddenVars),
 1086        maplist(unvisit, QueryVars),
 1087        foldl(existential, HiddenVars, BDD0, BDD),
 1088        foldl(quantify_existantially, HiddenVars, Formula, ExFormula),
 1089        root_put_formula_bdd(Root, ExFormula, BDD).
 1090
 1091quantify_existantially(E, E0, E^E0) :- put_attr(E, clpb_omit_boolean, true).
 1092
 1093/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1094   BDD restriction.
 1095- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1096
 1097bdd_restriction(Node, VI, Value, Res) :-
 1098        empty_assoc(G0),
 1099        phrase(bdd_restriction_(Node, VI, Value, Res), [G0], _),
 1100        is_bdd(Res).
 1101
 1102bdd_restriction_(Node, VI, Value, Res) -->
 1103        (   { integer(Node) } -> { Res = Node }
 1104        ;   { node_var_low_high(Node, Var, Low, High) } ->
 1105            (   { integer(Var) } ->
 1106                (   { Var =:= 0 } -> bdd_restriction_(Low, VI, Value, Res)
 1107                ;   { Var =:= 1 } -> bdd_restriction_(High, VI, Value, Res)
 1108                ;   { no_truth_value(Var) }
 1109                )
 1110            ;   { var_index(Var, I0),
 1111                  node_id(Node, ID) },
 1112                (   { I0 =:= VI } ->
 1113                    (   { Value =:= 0 } -> { Res = Low }
 1114                    ;   { Value =:= 1 } -> { Res = High }
 1115                    )
 1116                ;   { I0 > VI } -> { Res = Node }
 1117                ;   state(G0), { get_assoc(ID, G0, Res) } -> []
 1118                ;   bdd_restriction_(Low, VI, Value, LRes),
 1119                    bdd_restriction_(High, VI, Value, HRes),
 1120                    make_node(Var, LRes, HRes, Res),
 1121                    state(G0, G),
 1122                    { put_assoc(ID, G0, Res, G) }
 1123                )
 1124            )
 1125        ;   { domain_error(node, Node) }
 1126        ).
 1127
 1128/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1129   Relating a BDD to its elements (nodes and variables).
 1130
 1131   Note that BDDs can become quite big (easily millions of nodes), and
 1132   memory space is a major bottleneck for many problems. If possible,
 1133   we therefore do not duplicate the entire BDD in memory (as in
 1134   bdd_ites/2), but only extract its features as needed.
 1135- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1136
 1137bdd_nodes(BDD, Ns) :- bdd_nodes(ignore_node, BDD, Ns).
 1138
 1139ignore_node(_).
 1140
 1141% VPred is a unary predicate that is called for each node that has a
 1142% branching variable (= each inner node).
 1143
 1144bdd_nodes(VPred, BDD, Ns) :-
 1145        phrase(bdd_nodes_(VPred, BDD), Ns),
 1146        maplist(with_aux(unvisit), Ns).
 1147
 1148bdd_nodes_(VPred, Node) -->
 1149        (   { node_visited(Node) } -> []
 1150        ;   { call(VPred, Node),
 1151              with_aux(put_visited, Node),
 1152              node_var_low_high(Node, _, Low, High) },
 1153            [Node],
 1154            bdd_nodes_(VPred, Low),
 1155            bdd_nodes_(VPred, High)
 1156        ).
 1157
 1158node_visited(Node) :- integer(Node).
 1159node_visited(Node) :- with_aux(is_visited, Node).
 1160
 1161bdd_variables(BDD, Vs) :-
 1162        bdd_nodes(BDD, Nodes),
 1163        nodes_variables(Nodes, Vs).
 1164
 1165nodes_variables(Nodes, Vs) :-
 1166        phrase(nodes_variables_(Nodes), Vs),
 1167        maplist(unvisit, Vs).
 1168
 1169nodes_variables_([]) --> [].
 1170nodes_variables_([Node|Nodes]) -->
 1171        { node_var_low_high(Node, Var, _, _) },
 1172        (   { integer(Var) } -> []
 1173        ;   { is_visited(Var) } -> []
 1174        ;   { put_visited(Var) },
 1175            [Var]
 1176        ),
 1177        nodes_variables_(Nodes).
 1178
 1179unvisit(V) :- del_attr(V, clpb_visited).
 1180
 1181is_visited(V) :- get_attr(V, clpb_visited, true).
 1182
 1183put_visited(V) :- put_attr(V, clpb_visited, true).
 1184
 1185with_aux(Pred, Node) :-
 1186        node_aux(Node, Aux),
 1187        call(Pred, Aux).
 1188
 1189/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1190   Internal consistency checks.
 1191
 1192   To enable these checks, set the flag clpb_validation to true.
 1193- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1194
 1195is_bdd(BDD) :-
 1196        (   current_prolog_flag(clpb_validation, true) ->
 1197            bdd_ites(BDD, ITEs),
 1198            pairs_values(ITEs, Ls0),
 1199            sort(Ls0, Ls1),
 1200            (   same_length(Ls0, Ls1) -> true
 1201            ;   domain_error(reduced_ites, (ITEs,Ls0,Ls1))
 1202            ),
 1203            (   member(ITE, ITEs), \+ registered_node(ITE) ->
 1204                domain_error(registered_node, ITE)
 1205            ;   true
 1206            ),
 1207            (   member(I, ITEs), \+ ordered(I) ->
 1208                domain_error(ordered_node, I)
 1209            ;   true
 1210            )
 1211        ;   true
 1212        ).
 1213
 1214ordered(_-ite(Var,High,Low)) :-
 1215        (   var_index(Var, VI) ->
 1216            greater_varindex_than(High, VI),
 1217            greater_varindex_than(Low, VI)
 1218        ;   true
 1219        ).
 1220
 1221greater_varindex_than(Node, VI) :-
 1222        (   integer(Node) -> true
 1223        ;   node_var_low_high(Node, Var, _, _),
 1224            (   var_index(Var, OI) ->
 1225                OI > VI
 1226            ;   true
 1227            )
 1228        ).
 1229
 1230registered_node(Node-ite(Var,High,Low)) :-
 1231        (   var(Var) ->
 1232            low_high_key(Low, High, Key),
 1233            lookup_node(Var, Key, Node0),
 1234            Node == Node0
 1235        ;   true
 1236        ).
 1237
 1238bdd_ites(BDD, ITEs) :-
 1239        bdd_nodes(BDD, Nodes),
 1240        maplist(node_ite, Nodes, ITEs).
 1241
 1242node_ite(Node, Node-ite(Var,High,Low)) :-
 1243        node_var_low_high(Node, Var, Low, High).
 1244
 1245%% labeling(+Vs) is multi.
 1246%
 1247% Enumerate concrete solutions. Assigns truth values to the Boolean
 1248% variables Vs such that all stated constraints are satisfied.
 1249
 1250labeling(Vs0) :-
 1251        must_be(list, Vs0),
 1252        maplist(labeling_var, Vs0),
 1253        variables_in_index_order(Vs0, Vs),
 1254        maplist(indomain, Vs).
 1255
 1256labeling_var(V) :- var(V), !.
 1257labeling_var(V) :- V == 0, !.
 1258labeling_var(V) :- V == 1, !.
 1259labeling_var(V) :- domain_error(clpb_variable, V).
 1260
 1261variables_in_index_order(Vs0, Vs) :-
 1262        maplist(var_with_index, Vs0, IVs0),
 1263        keysort(IVs0, IVs),
 1264        pairs_values(IVs, Vs).
 1265
 1266var_with_index(V, I-V) :-
 1267        (   var_index_root(V, I, _) -> true
 1268        ;   I = 0
 1269        ).
 1270
 1271indomain(0).
 1272indomain(1).
 1273
 1274
 1275%% sat_count(+Expr, -Count) is det.
 1276%
 1277% Count the number of admissible assignments. Count is the number of
 1278% different assignments of truth values to the variables in the
 1279% Boolean expression Expr, such that Expr is true and all posted
 1280% constraints are satisfiable.
 1281%
 1282% A common form of invocation is `sat_count(+[1|Vs], Count)`: This
 1283% counts the number of admissible assignments to `Vs` without imposing
 1284% any further constraints.
 1285%
 1286% Examples:
 1287%
 1288% ==
 1289% ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count).
 1290% Vs = [A, B],
 1291% Count = 3,
 1292% sat(A=:=A*B).
 1293%
 1294% ?- length(Vs, 120),
 1295%    sat_count(+Vs, CountOr),
 1296%    sat_count(*(Vs), CountAnd).
 1297% Vs = [...],
 1298% CountOr = 1329227995784915872903807060280344575,
 1299% CountAnd = 1.
 1300% ==
 1301
 1302sat_count(Sat0, N) :-
 1303        catch((parse_sat(Sat0, Sat),
 1304               sat_bdd(Sat, BDD),
 1305               sat_roots(Sat, Roots),
 1306               roots_and(Roots, _-BDD, _-BDD1),
 1307               % we mark variables that occur in Sat0 as visited ...
 1308               term_variables(Sat0, Vs),
 1309               maplist(put_visited, Vs),
 1310               % ... so that they do not appear in Vs1 ...
 1311               bdd_variables(BDD1, Vs1),
 1312               partition(universal_var, Vs1, Univs, Exis),
 1313               % ... and then remove remaining variables:
 1314               foldl(universal, Univs, BDD1, BDD2),
 1315               foldl(existential, Exis, BDD2, BDD3),
 1316               variables_in_index_order(Vs, IVs),
 1317               foldl(renumber_variable, IVs, 1, VNum),
 1318               bdd_count(BDD3, VNum, Count0),
 1319               var_u(BDD3, VNum, P),
 1320               % Do not unify N directly, because we are not prepared
 1321               % for propagation here in case N is a CLP(B) variable.
 1322               N0 is 2^(P - 1)*Count0,
 1323               % reset all attributes and Aux variables
 1324               throw(count(N0))),
 1325              count(N0),
 1326              N = N0).
 1327
 1328universal(V, BDD, Node) :-
 1329        var_index(V, Index),
 1330        bdd_restriction(BDD, Index, 0, NA),
 1331        bdd_restriction(BDD, Index, 1, NB),
 1332        apply(*, NA, NB, Node).
 1333
 1334renumber_variable(V, I0, I) :-
 1335        put_attr(V, clpb, index_root(I0,_)),
 1336        I is I0 + 1.
 1337
 1338bdd_count(Node, VNum, Count) :-
 1339        (   integer(Node) -> Count = Node
 1340        ;   node_aux(Node, Count),
 1341            (   integer(Count) -> true
 1342            ;   node_var_low_high(Node, V, Low, High),
 1343                bdd_count(Low, VNum, LCount),
 1344                bdd_count(High, VNum, HCount),
 1345                bdd_pow(Low, V, VNum, LPow),
 1346                bdd_pow(High, V, VNum, HPow),
 1347                Count is LPow*LCount + HPow*HCount
 1348            )
 1349        ).
 1350
 1351
 1352bdd_pow(Node, V, VNum, Pow) :-
 1353        var_index(V, Index),
 1354        var_u(Node, VNum, P),
 1355        Pow is 2^(P - Index - 1).
 1356
 1357var_u(Node, VNum, Index) :-
 1358        (   integer(Node) -> Index = VNum
 1359        ;   node_varindex(Node, Index)
 1360        ).
 1361
 1362/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1363   Pick a solution in such a way that each solution is equally likely.
 1364- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1365
 1366%% random_labeling(+Seed, +Vs) is det.
 1367%
 1368% Select a single random solution. An admissible assignment of truth
 1369% values to the Boolean variables in Vs is chosen in such a way that
 1370% each admissible assignment is equally likely. Seed is an integer,
 1371% used as the initial seed for the random number generator.
 1372
 1373single_bdd(Vars0) :-
 1374        maplist(monotonic_variable, Vars0, Vars),
 1375        % capture all variables with a single BDD
 1376        sat(+[1|Vars]).
 1377
 1378random_labeling(Seed, Vars) :-
 1379        must_be(list, Vars),
 1380        set_random(seed(Seed)),
 1381        (   ground(Vars) -> true
 1382        ;   catch((single_bdd(Vars),
 1383                   once((member(Var, Vars),var(Var))),
 1384                   var_index_root(Var, _, Root),
 1385                   root_get_formula_bdd(Root, _, BDD),
 1386                   bdd_variables(BDD, Vs),
 1387                   variables_in_index_order(Vs, IVs),
 1388                   foldl(renumber_variable, IVs, 1, VNum),
 1389                   phrase(random_bindings(VNum, BDD), Bs),
 1390                   maplist(del_attrs, Vs),
 1391                   % reset all attribute modifications
 1392                   throw(randsol(Vars, Bs))),
 1393                  randsol(Vars, Bs),
 1394                  true),
 1395            maplist(call, Bs),
 1396            % set remaining variables to 0 or 1 with equal probability
 1397            include(var, Vars, Remaining),
 1398            maplist(maybe_zero, Remaining)
 1399        ).
 1400
 1401maybe_zero(Var) :-
 1402        (   maybe -> Var = 0
 1403        ;   Var = 1
 1404        ).
 1405
 1406random_bindings(_, Node) --> { Node == 1 }, !.
 1407random_bindings(VNum, Node) -->
 1408        { node_var_low_high(Node, Var, Low, High),
 1409          bdd_count(Node, VNum, Total),
 1410          bdd_count(Low, VNum, LCount) },
 1411        (   { maybe(LCount, Total) } ->
 1412            [Var=0], random_bindings(VNum, Low)
 1413        ;   [Var=1], random_bindings(VNum, High)
 1414        ).
 1415
 1416/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1417   Find solutions with maximum weight.
 1418- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1419
 1420%% weighted_maximum(+Weights, +Vs, -Maximum) is multi.
 1421%
 1422% Enumerate weighted optima over admissible assignments. Maximize a
 1423% linear objective function over Boolean variables Vs with integer
 1424% coefficients Weights. This predicate assigns 0 and 1 to the
 1425% variables in Vs such that all stated constraints are satisfied, and
 1426% Maximum is the maximum of sum(Weight_i*V_i) over all admissible
 1427% assignments.  On backtracking, all admissible assignments that
 1428% attain the optimum are generated.
 1429%
 1430% This predicate can also be used to _minimize_ a linear Boolean
 1431% program, since negative integers can appear in Weights.
 1432%
 1433% Example:
 1434%
 1435% ==
 1436% ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum).
 1437% A = 0, B = 1, C = 1, Maximum = 3.
 1438% ==
 1439
 1440weighted_maximum(Ws, Vars, Max) :-
 1441        must_be(list(integer), Ws),
 1442        must_be(list(var), Vars),
 1443        single_bdd(Vars),
 1444        Vars = [Var|_],
 1445        var_index_root(Var, _,  Root),
 1446        root_get_formula_bdd(Root, _, BDD0),
 1447        bdd_variables(BDD0, Vs),
 1448        % existentially quantify variables that are not considered
 1449        maplist(put_visited, Vars),
 1450        exclude(is_visited, Vs, Unvisited),
 1451        maplist(unvisit, Vars),
 1452        foldl(existential, Unvisited, BDD0, BDD),
 1453        maplist(var_with_index, Vars, IVs),
 1454        pairs_keys_values(Pairs0, IVs, Ws),
 1455        keysort(Pairs0, Pairs1),
 1456        pairs_keys_values(Pairs1, IVs1, WeightsIndexOrder),
 1457        pairs_values(IVs1, VarsIndexOrder),
 1458        % Pairs is a list of Var-Weight terms, in index order of Vars
 1459        pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
 1460        bdd_maximum(BDD, Pairs, Max),
 1461        max_labeling(BDD, Pairs).
 1462
 1463max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
 1464max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
 1465        max_upto(Pairs0, Var, Pairs),
 1466        get_attr(Aux, clpb_max, max(_,Dir)),
 1467        direction_labeling(Dir, Var, Low, High, Pairs).
 1468
 1469max_upto([], _, _).
 1470max_upto([Var0-Weight|VWs0], Var, VWs) :-
 1471        (   Var == Var0 -> VWs = VWs0
 1472        ;   Weight =:= 0 ->
 1473            (   Var0 = 0 ; Var0 = 1 ),
 1474            max_upto(VWs0, Var, VWs)
 1475        ;   Weight < 0 -> Var0 = 0, max_upto(VWs0, Var, VWs)
 1476        ;   Var0 = 1, max_upto(VWs0, Var, VWs)
 1477        ).
 1478
 1479direction_labeling(low, 0, Low, _, Pairs)   :- max_labeling(Low, Pairs).
 1480direction_labeling(high, 1, _, High, Pairs) :- max_labeling(High, Pairs).
 1481
 1482bdd_maximum(1, Pairs, Max) :-
 1483        pairs_values(Pairs, Weights0),
 1484        include(<(0), Weights0, Weights),
 1485        sum_list(Weights, Max).
 1486bdd_maximum(node(_,Var,Low,High,Aux), Pairs0, Max) :-
 1487        (   get_attr(Aux, clpb_max, max(Max,_)) -> true
 1488        ;   (   skip_to_var(Var, Weight, Pairs0, Pairs),
 1489                (   Low == 0 ->
 1490                    bdd_maximum_(High, Pairs, MaxHigh, MaxToHigh),
 1491                    Max is MaxToHigh + MaxHigh + Weight,
 1492                    Dir = high
 1493                ;   High == 0 ->
 1494                    bdd_maximum_(Low, Pairs, MaxLow, MaxToLow),
 1495                    Max is MaxToLow + MaxLow,
 1496                    Dir = low
 1497                ;   bdd_maximum_(Low, Pairs, MaxLow, MaxToLow),
 1498                    bdd_maximum_(High, Pairs, MaxHigh, MaxToHigh),
 1499                    Max0 is MaxToLow + MaxLow,
 1500                    Max1 is MaxToHigh + MaxHigh + Weight,
 1501                    Max is max(Max0,Max1),
 1502                    (   Max0 =:= Max1 -> Dir = _Any
 1503                    ;   Max0 < Max1 -> Dir = high
 1504                    ;   Dir = low
 1505                    )
 1506                ),
 1507                store_maximum(Aux, Max, Dir)
 1508            )
 1509        ).
 1510
 1511bdd_maximum_(Node, Pairs, Max, MaxTo) :-
 1512	bdd_maximum(Node, Pairs, Max),
 1513	between_weights(Node, Pairs, MaxTo).
 1514
 1515store_maximum(Aux, Max, Dir) :- put_attr(Aux, clpb_max, max(Max,Dir)).
 1516
 1517between_weights(Node, Pairs0, MaxTo) :-
 1518        (   Node == 1 -> MaxTo = 0
 1519        ;   node_var_low_high(Node, Var, _, _),
 1520            phrase(skip_to_var_(Var, _, Pairs0, _), Weights0),
 1521            include(<(0), Weights0, Weights),
 1522            sum_list(Weights, MaxTo)
 1523        ).
 1524
 1525skip_to_var(Var, Weight, Pairs0, Pairs) :-
 1526        phrase(skip_to_var_(Var, Weight, Pairs0, Pairs), _).
 1527
 1528skip_to_var_(Var, Weight, [Var0-Weight0|VWs0], VWs) -->
 1529        (   { Var == Var0 } ->
 1530            { Weight = Weight0, VWs0 = VWs }
 1531        ;   (   { Weight0 =< 0 } -> []
 1532            ;   [Weight0]
 1533            ),
 1534            skip_to_var_(Var, Weight, VWs0, VWs)
 1535        ).
 1536
 1537/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1538   Projection to residual goals.
 1539- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1540
 1541attribute_goals(Var) -->
 1542        { var_index_root(Var, _, Root) },
 1543        (   { root_get_formula_bdd(Root, Formula, BDD) } ->
 1544            { del_bdd(Root) },
 1545            (   { current_prolog_flag(clpb_residuals, bdd) } ->
 1546                { bdd_nodes(BDD, Nodes),
 1547                  phrase(nodes(Nodes), Ns) },
 1548                [clpb:'$clpb_bdd'(Ns)]
 1549            ;   { prepare_global_variables(BDD),
 1550                  phrase(sat_ands(Formula), Ands0),
 1551                  ands_fusion(Ands0, Ands),
 1552                  maplist(formula_anf, Ands, ANFs0),
 1553                  sort(ANFs0, ANFs1),
 1554                  exclude(eq_1, ANFs1, ANFs2),
 1555                  variables_separation(ANFs2, ANFs) },
 1556                sats(ANFs)
 1557            ),
 1558            (   { get_attr(Var, clpb_atom, Atom) } ->
 1559                [clpb:sat(Var=:=Atom)]
 1560            ;   []
 1561            ),
 1562            % formula variables not occurring in the BDD should be booleans
 1563            { bdd_variables(BDD, Vs),
 1564              maplist(del_clpb, Vs),
 1565              term_variables(Formula, RestVs0),
 1566              include(clpb_variable, RestVs0, RestVs) },
 1567            booleans(RestVs)
 1568        ;   boolean(Var)  % the variable may have occurred only in taut/2
 1569        ).
 1570
 1571del_clpb(Var) :- del_attr(Var, clpb).
 1572
 1573/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1574   To make residual projection work with recorded constraints, the
 1575   global counters must be adjusted so that new variables and nodes
 1576   also get new IDs. Also, clpb_next_id/2 is used to actually create
 1577   these counters, because creating them with b_setval/2 would make
 1578   them [] on backtracking, which is quite unfortunate in itself.
 1579- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1580
 1581prepare_global_variables(BDD) :-
 1582        clpb_next_id('$clpb_next_var', V0),
 1583        clpb_next_id('$clpb_next_node', N0),
 1584        bdd_nodes(BDD, Nodes),
 1585        foldl(max_variable_node, Nodes, V0-N0, MaxV0-MaxN0),
 1586        MaxV is MaxV0 + 1,
 1587        MaxN is MaxN0 + 1,
 1588        b_setval('$clpb_next_var', MaxV),
 1589        b_setval('$clpb_next_node', MaxN).
 1590
 1591max_variable_node(Node, V0-N0, V-N) :-
 1592        node_id(Node, N1),
 1593        node_varindex(Node, V1),
 1594        N is max(N0,N1),
 1595        V is max(V0,V1).
 1596
 1597/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1598   Fuse formulas that share the same variables into single conjunctions.
 1599- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1600
 1601ands_fusion(Ands0, Ands) :-
 1602        maplist(with_variables, Ands0, Pairs0),
 1603        keysort(Pairs0, Pairs),
 1604        group_pairs_by_key(Pairs, Groups),
 1605        pairs_values(Groups, Andss),
 1606        maplist(list_to_conjunction, Andss, Ands).
 1607
 1608with_variables(F, Vs-F) :-
 1609        term_variables(F, Vs0),
 1610        variables_in_index_order(Vs0, Vs).
 1611
 1612/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1613   If possible, separate variables into different sat/1 goals.
 1614   A formula F can be split in two if for two of its variables A and B,
 1615   taut((A^F)*(B^F) =:= F, 1) holds. In the first conjunct, A does not
 1616   occur, and in the second, B does not occur. We separate variables
 1617   until that is no longer possible. There may be a better way to do this.
 1618- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1619
 1620variables_separation(Fs0, Fs) :- separation_fixpoint(Fs0, [], Fs).
 1621
 1622separation_fixpoint(Fs0, Ds0, Fs) :-
 1623        phrase(variables_separation_(Fs0, Ds0, Rest), Fs1),
 1624        partition(anf_done, Fs1, Ds1, Fs2),
 1625        maplist(arg(1), Ds1, Ds2),
 1626        maplist(arg(1), Fs2, Fs3),
 1627        append(Ds0, Ds2, Ds3),
 1628        append(Rest, Fs3, Fs4),
 1629        sort(Fs4, Fs5),
 1630        sort(Ds3, Ds4),
 1631        (   Fs5 == [] -> Fs = Ds4
 1632        ;   separation_fixpoint(Fs5, Ds4, Fs)
 1633        ).
 1634
 1635anf_done(done(_)).
 1636
 1637variables_separation_([], _, []) --> [].
 1638variables_separation_([F0|Fs0], Ds, Rest) -->
 1639        (   { member(Done, Ds), F0 == Done } ->
 1640            variables_separation_(Fs0, Ds, Rest)
 1641        ;   { sat_rewrite(F0, F),
 1642              sat_bdd(F, BDD),
 1643              bdd_variables(BDD, Vs0),
 1644              exclude(universal_var, Vs0, Vs),
 1645              maplist(existential_(BDD), Vs, Nodes),
 1646              phrase(pairs(Nodes), Pairs),
 1647              group_pairs_by_key(Pairs, Groups),
 1648              phrase(groups_separation(Groups, BDD), ANFs) },
 1649            (   { ANFs = [_|_] } ->
 1650                list(ANFs),
 1651                { Rest = Fs0 }
 1652            ;   [done(F0)],
 1653                variables_separation_(Fs0, Ds, Rest)
 1654            )
 1655        ).
 1656
 1657
 1658existential_(BDD, V, Node) :- existential(V, BDD, Node).
 1659
 1660groups_separation([], _) --> [].
 1661groups_separation([BDD1-BDDs|Groups], OrigBDD) -->
 1662        { phrase(separate_pairs(BDDs, BDD1, OrigBDD), Nodes) },
 1663        (   { Nodes = [_|_] } ->
 1664            nodes_anfs([BDD1|Nodes])
 1665        ;   []
 1666        ),
 1667        groups_separation(Groups, OrigBDD).
 1668
 1669separate_pairs([], _, _) --> [].
 1670separate_pairs([BDD2|Ps], BDD1, OrigBDD) -->
 1671        (   { apply(*, BDD1, BDD2, And),
 1672              And == OrigBDD } ->
 1673            [BDD2]
 1674        ;   []
 1675        ),
 1676        separate_pairs(Ps, BDD1, OrigBDD).
 1677
 1678nodes_anfs([]) --> [].
 1679nodes_anfs([N|Ns]) --> { node_anf(N, ANF) }, [anf(ANF)], nodes_anfs(Ns).
 1680
 1681pairs([]) --> [].
 1682pairs([V|Vs]) --> pairs_(Vs, V), pairs(Vs).
 1683
 1684pairs_([], _) --> [].
 1685pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
 1686
 1687/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1688   Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes
 1689   as residuals. Note that they cannot be used as regular goals.
 1690- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1691
 1692nodes([]) --> [].
 1693nodes([Node|Nodes]) -->
 1694        { node_var_low_high(Node, Var0, Low, High),
 1695          var_or_atom(Var0, Var),
 1696          maplist(node_projection, [Node,High,Low], [ID,HID,LID]),
 1697          var_index(Var0, VI) },
 1698        [ID-(v(Var,VI) -> HID ; LID)],
 1699        nodes(Nodes).
 1700
 1701
 1702node_projection(Node, Projection) :-
 1703        node_id(Node, ID),
 1704        (   integer(ID) -> Projection = node(ID)
 1705        ;   Projection = ID
 1706        ).
 1707
 1708/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1709   By default, residual goals are sat/1 calls of the remaining formulas,
 1710   using (mostly) algebraic normal form.
 1711- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1712
 1713sats([]) --> [].
 1714sats([A|As]) --> [clpb:sat(A)], sats(As).
 1715
 1716booleans([]) --> [].
 1717booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs).
 1718
 1719boolean(Var) -->
 1720        (   { get_attr(Var, clpb_omit_boolean, true) } -> []
 1721        ;   [clpb:sat(Var =:= Var)]
 1722        ).
 1723
 1724/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1725   Relate a formula to its algebraic normal form (ANF).
 1726- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1727
 1728formula_anf(Formula0, ANF) :-
 1729        parse_sat(Formula0, Formula),
 1730        sat_bdd(Formula, Node),
 1731        node_anf(Node, ANF).
 1732
 1733node_anf(Node, ANF) :-
 1734        node_xors(Node, Xors0),
 1735        maplist(maplist(monotonic_variable), Xors0, Xors),
 1736        maplist(list_to_conjunction, Xors, Conjs),
 1737        (   Conjs = [Var,C|Rest], clpb_var(Var) ->
 1738            foldl(xor, Rest, C, RANF),
 1739            ANF = (Var =\= RANF)
 1740        ;   Conjs = [One,Var,C|Rest], One == 1, clpb_var(Var) ->
 1741            foldl(xor, Rest, C, RANF),
 1742            ANF = (Var =:= RANF)
 1743        ;   Conjs = [C|Cs],
 1744            foldl(xor, Cs, C, ANF)
 1745        ).
 1746
 1747monotonic_variable(Var0, Var) :-
 1748        (   var(Var0), current_prolog_flag(clpb_monotonic, true) ->
 1749            Var = v(Var0)
 1750        ;   Var = Var0
 1751        ).
 1752
 1753clpb_var(Var) :- var(Var), !.
 1754clpb_var(v(_)).
 1755
 1756list_to_conjunction([], 1).
 1757list_to_conjunction([L|Ls], Conj) :- foldl(and, Ls, L, Conj).
 1758
 1759xor(A, B, B # A).
 1760
 1761eq_1(V) :- V == 1.
 1762
 1763node_xors(Node, Xors) :-
 1764        phrase(xors(Node), Xors0),
 1765        % we remove elements that occur an even number of times (A#A --> 0)
 1766        maplist(sort, Xors0, Xors1),
 1767        pairs_keys_values(Pairs0, Xors1, _),
 1768        keysort(Pairs0, Pairs),
 1769        group_pairs_by_key(Pairs, Groups),
 1770        exclude(even_occurrences, Groups, Odds),
 1771        pairs_keys(Odds, Xors2),
 1772        maplist(exclude(eq_1), Xors2, Xors).
 1773
 1774even_occurrences(_-Ls) :- length(Ls, L), L mod 2 =:= 0.
 1775
 1776xors(Node) -->
 1777        (   { Node == 0 } -> []
 1778        ;   { Node == 1 } -> [[1]]
 1779        ;   { node_var_low_high(Node, Var0, Low, High),
 1780              var_or_atom(Var0, Var),
 1781              node_xors(Low, Ls0),
 1782              node_xors(High, Hs0),
 1783              maplist(with_var(Var), Ls0, Ls),
 1784              maplist(with_var(Var), Hs0, Hs) },
 1785            list(Ls0),
 1786            list(Ls),
 1787            list(Hs)
 1788        ).
 1789
 1790list([]) --> [].
 1791list([L|Ls]) --> [L], list(Ls).
 1792
 1793with_var(Var, Ls, [Var|Ls]).
 1794
 1795/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1796   Global variables for unique node and variable IDs and atoms.
 1797- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1798
 1799make_clpb_var('$clpb_next_var') :- nb_setval('$clpb_next_var', 0).
 1800
 1801make_clpb_var('$clpb_next_node') :- nb_setval('$clpb_next_node', 0).
 1802
 1803make_clpb_var('$clpb_atoms') :-
 1804        empty_assoc(E),
 1805        nb_setval('$clpb_atoms', E).
 1806
 1807:- multifile user:exception/3. 1808
 1809user:exception(undefined_global_variable, Name, retry) :-
 1810        make_clpb_var(Name), !.
 1811
 1812clpb_next_id(Var, ID) :-
 1813        b_getval(Var, ID),
 1814        Next is ID + 1,
 1815        b_setval(Var, Next).
 1816
 1817clpb_atom_var(Atom, Var) :-
 1818        b_getval('$clpb_atoms', A0),
 1819        (   get_assoc(Atom, A0, Var) -> true
 1820        ;   put_attr(Var, clpb_atom, Atom),
 1821            put_attr(Var, clpb_omit_boolean, true),
 1822            put_assoc(Atom, A0, Var, A),
 1823            b_setval('$clpb_atoms', A)
 1824        ).
 1825
 1826/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1827   The variable attributes below are not used as constraints by this
 1828   library. Project remaining attributes to empty lists of residuals.
 1829
 1830   Because accessing these hooks is basically a cross-module call, we
 1831   must declare them public.
 1832- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1833
 1834:- public
 1835        clpb_hash:attr_unify_hook/2,
 1836        clpb_bdd:attribute_goals//1,
 1837        clpb_hash:attribute_goals//1,
 1838        clpb_omit_boolean:attr_unify_hook/2,
 1839        clpb_omit_boolean:attribute_goals//1,
 1840        clpb_atom:attr_unify_hook/2,
 1841        clpb_atom:attribute_goals//1. 1842
 1843clpb_hash:attr_unify_hook(_,_).  % this unification is always admissible
 1844
 1845/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1846   If a universally quantified variable is unified to a Boolean value,
 1847   it indicates that the formula does not hold for the other value, so
 1848   it is false.
 1849- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1850
 1851clpb_atom:attr_unify_hook(_, _) :- false.
 1852
 1853clpb_omit_boolean:attr_unify_hook(_,_).
 1854
 1855clpb_bdd:attribute_goals(_)          --> [].
 1856clpb_hash:attribute_goals(_)         --> [].
 1857clpb_omit_boolean:attribute_goals(_) --> [].
 1858clpb_atom:attribute_goals(_)         --> [].
 1859
 1860% clpb_hash:attribute_goals(Var) -->
 1861%         { get_attr(Var, clpb_hash, Assoc),
 1862%           assoc_to_list(Assoc, List0),
 1863%           maplist(node_portray, List0, List) }, [Var-List].
 1864
 1865% node_portray(Key-Node, Key-Node-ite(Var,High,Low)) :-
 1866%         node_var_low_high(Node, Var, Low, High).
 1867
 1868/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1869   Messages
 1870- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1871
 1872:- multifile prolog:message//1. 1873
 1874prolog:message(clpb(bounded)) -->
 1875        ['Using CLP(B) with bounded arithmetic may yield wrong results.'-[]].
 1876
 1877warn_if_bounded_arithmetic :-
 1878        (   current_prolog_flag(bounded, true) ->
 1879            print_message(warning, clpb(bounded))
 1880        ;   true
 1881        ).
 1882
 1883:- initialization(warn_if_bounded_arithmetic). 1884
 1885/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1886   Sanbox declarations
 1887- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 1888
 1889:- multifile
 1890        sandbox:safe_global_variable/1,
 1891        sandbox:safe_primitive/1. 1892
 1893sandbox:safe_global_variable('$clpb_next_var').
 1894sandbox:safe_global_variable('$clpb_next_node').
 1895sandbox:safe_global_variable('$clpb_atoms').
 1896sandbox:safe_primitive(set_prolog_flag(clpb_residuals, _))