View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2008-2016, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(aggregate,
   37          [ foreach/2,                  % :Generator, :Goal
   38            aggregate/3,                % +Templ, :Goal, -Result
   39            aggregate/4,                % +Templ, +Discrim, :Goal, -Result
   40            aggregate_all/3,            % +Templ, :Goal, -Result
   41            aggregate_all/4,            % +Templ, +Discrim, :Goal, -Result
   42            free_variables/4            % :Generator, :Template, +Vars0, -Vars
   43          ]).   44:- autoload(library(apply),[maplist/4,maplist/5]).   45:- autoload(library(error),
   46	    [instantiation_error/1,type_error/2,domain_error/2]).   47:- autoload(library(lists),
   48	    [append/3,member/2,sum_list/2,max_list/2,min_list/2]).   49:- autoload(library(ordsets),[ord_subtract/3,ord_intersection/3]).   50:- autoload(library(pairs),[pairs_values/2]).   51
   52
   53:- meta_predicate
   54    foreach(0,0),
   55    aggregate(?,^,-),
   56    aggregate(?,?,^,-),
   57    aggregate_all(?,0,-),
   58    aggregate_all(?,?,0,-).

Aggregation operators on backtrackable predicates

This library provides aggregating operators over the solutions of a predicate. The operations are a generalisation of the bagof/3, setof/3 and findall/3 built-in predicates. The defined aggregation operations are counting, computing the sum, minimum, maximum, a bag of solutions and a set of solutions. We first give a simple example, computing the country with the smallest area:

smallest_country(Name, Area) :-
        aggregate(min(A, N), country(N, A), min(Area, Name)).

There are four aggregation predicates (aggregate/3, aggregate/4, aggregate_all/3 and aggregate/4), distinguished on two properties.

aggregate vs. aggregate_all
The aggregate predicates use setof/3 (aggregate/4) or bagof/3 (aggregate/3), dealing with existential qualified variables (Var^Goal) and providing multiple solutions for the remaining free variables in Goal. The aggregate_all/3 predicate uses findall/3, implicitly qualifying all free variables and providing exactly one solution, while aggregate_all/4 uses sort/2 over solutions that Discriminator (see below) generated using findall/3.
The Discriminator argument
The versions with 4 arguments deduplicate redundant solutions of Goal. Solutions for which both the template variables and Discriminator are identical will be treated as one solution. For example, if we wish to compute the total population of all countries, and for some reason country(belgium, 11000000) may succeed twice, we can use the following to avoid counting the population of Belgium twice:
    aggregate(sum(P), Name, country(Name, P), Total)

All aggregation predicates support the following operators below in Template. In addition, they allow for an arbitrary named compound term, where each of the arguments is a term from the list below. For example, the term r(min(X), max(X)) computes both the minimum and maximum binding for X.

count
Count number of solutions. Same as sum(1).
sum(Expr)
Sum of Expr for all solutions.
min(Expr)
Minimum of Expr for all solutions.
min(Expr, Witness)
A term min(Min, Witness), where Min is the minimal version of Expr over all solutions, and Witness is any other template applied to solutions that produced Min. If multiple solutions provide the same minimum, Witness corresponds to the first solution.
max(Expr)
Maximum of Expr for all solutions.
max(Expr, Witness)
As min(Expr, Witness), but producing the maximum result.
set(X)
An ordered set with all solutions for X.
bag(X)
A list of all solutions for X.

Acknowledgements

The development of this library was sponsored by SecuritEase, http://www.securitease.com

Compatibility
- Quintus, SICStus 4. The forall/2 is a SWI-Prolog built-in and term_variables/3 is a SWI-Prolog built-in with different semantics.
To be done
- Analysing the aggregation template and compiling a predicate for the list aggregation can be done at compile time.
-
aggregate_all/3 can be rewritten to run in constant space using non-backtrackable assignment on a term. */
  140                 /*******************************
  141                 *           AGGREGATE          *
  142                 *******************************/
 aggregate(+Template, :Goal, -Result) is nondet
Aggregate bindings in Goal according to Template. The aggregate/3 version performs bagof/3 on Goal.
  149aggregate(Template, Goal0, Result) :-
  150    template_to_pattern(bag, Template, Pattern, Goal0, Goal, Aggregate),
  151    bagof(Pattern, Goal, List),
  152    aggregate_list(Aggregate, List, Result).
 aggregate(+Template, +Discriminator, :Goal, -Result) is nondet
Aggregate bindings in Goal according to Template. The aggregate/4 version performs setof/3 on Goal.
  159aggregate(Template, Discriminator, Goal0, Result) :-
  160    template_to_pattern(bag, Template, Pattern, Goal0, Goal, Aggregate),
  161    setof(Discriminator-Pattern, Goal, Pairs),
  162    pairs_values(Pairs, List),
  163    aggregate_list(Aggregate, List, Result).
 aggregate_all(+Template, :Goal, -Result) is semidet
Aggregate bindings in Goal according to Template. The aggregate_all/3 version performs findall/3 on Goal. Note that this predicate fails if Template contains one or more of min(X), max(X), min(X,Witness) or max(X,Witness) and Goal has no solutions, i.e., the minumum and maximum of an empty set is undefined.
  174aggregate_all(Var, _, _) :-
  175    var(Var),
  176    !,
  177    instantiation_error(Var).
  178aggregate_all(count, Goal, Count) :-
  179    !,
  180    aggregate_all(sum(1), Goal, Count).
  181aggregate_all(sum(X), Goal, Sum) :-
  182    !,
  183    State = state(0),
  184    (  call(Goal),
  185           arg(1, State, S0),
  186           S is S0 + X,
  187           nb_setarg(1, State, S),
  188           fail
  189    ;  arg(1, State, Sum)
  190    ).
  191aggregate_all(max(X), Goal, Max) :-
  192    !,
  193    State = state(X),
  194    (  call(Goal),
  195           arg(1, State, M0),
  196           M is max(M0,X),
  197           nb_setarg(1, State, M),
  198           fail
  199    ;  arg(1, State, Max),
  200           nonvar(Max)
  201    ).
  202aggregate_all(min(X), Goal, Min) :-
  203    !,
  204    State = state(X),
  205    (  call(Goal),
  206           arg(1, State, M0),
  207           M is min(M0,X),
  208           nb_setarg(1, State, M),
  209           fail
  210    ;  arg(1, State, Min),
  211           nonvar(Min)
  212    ).
  213aggregate_all(max(X,W), Goal, max(Max,Witness)) :-
  214    !,
  215    State = state(false, _Max, _Witness),
  216    (  call(Goal),
  217           (   State = state(true, Max0, _)
  218           ->  X > Max0,
  219               nb_setarg(2, State, X),
  220               nb_setarg(3, State, W)
  221           ;   number(X)
  222           ->  nb_setarg(1, State, true),
  223               nb_setarg(2, State, X),
  224               nb_setarg(3, State, W)
  225           ;   type_error(number, X)
  226           ),
  227           fail
  228    ;  State = state(true, Max, Witness)
  229    ).
  230aggregate_all(min(X,W), Goal, min(Min,Witness)) :-
  231    !,
  232    State = state(false, _Min, _Witness),
  233    (  call(Goal),
  234           (   State = state(true, Min0, _)
  235           ->  X < Min0,
  236               nb_setarg(2, State, X),
  237               nb_setarg(3, State, W)
  238           ;   number(X)
  239           ->  nb_setarg(1, State, true),
  240               nb_setarg(2, State, X),
  241               nb_setarg(3, State, W)
  242           ;   type_error(number, X)
  243           ),
  244           fail
  245    ;  State = state(true, Min, Witness)
  246    ).
  247aggregate_all(Template, Goal0, Result) :-
  248    template_to_pattern(all, Template, Pattern, Goal0, Goal, Aggregate),
  249    findall(Pattern, Goal, List),
  250    aggregate_list(Aggregate, List, Result).
 aggregate_all(+Template, +Discriminator, :Goal, -Result) is semidet
Aggregate bindings in Goal according to Template. The aggregate_all/4 version performs findall/3 followed by sort/2 on Goal. See aggregate_all/3 to understand why this predicate can fail.
  259aggregate_all(Template, Discriminator, Goal0, Result) :-
  260    template_to_pattern(all, Template, Pattern, Goal0, Goal, Aggregate),
  261    findall(Discriminator-Pattern, Goal, Pairs0),
  262    sort(Pairs0, Pairs),
  263    pairs_values(Pairs, List),
  264    aggregate_list(Aggregate, List, Result).
  265
  266template_to_pattern(All, Template, Pattern, Goal0, Goal, Aggregate) :-
  267    template_to_pattern(Template, Pattern, Post, Vars, Aggregate),
  268    existential_vars(Goal0, Goal1, AllVars, Vars),
  269    clean_body((Goal1, Post), Goal2),
  270    (   All == bag
  271    ->  add_existential_vars(AllVars, Goal2, Goal)
  272    ;   Goal = Goal2
  273    ).
  274
  275existential_vars(Var, Var) -->
  276    { var(Var) },
  277    !.
  278existential_vars(Var^G0, G) -->
  279    !,
  280    [Var],
  281    existential_vars(G0, G).
  282existential_vars(M:G0, M:G) -->
  283    !,
  284    existential_vars(G0, G).
  285existential_vars(G, G) -->
  286    [].
  287
  288add_existential_vars([], G, G).
  289add_existential_vars([H|T], G0, H^G1) :-
  290    add_existential_vars(T, G0, G1).
 clean_body(+Goal0, -Goal) is det
Remove redundant true from Goal0.
  297clean_body((Goal0,Goal1), Goal) :-
  298    !,
  299    clean_body(Goal0, GoalA),
  300    clean_body(Goal1, GoalB),
  301    (   GoalA == true
  302    ->  Goal = GoalB
  303    ;   GoalB == true
  304    ->  Goal = GoalA
  305    ;   Goal = (GoalA,GoalB)
  306    ).
  307clean_body(Goal, Goal).
 template_to_pattern(+Template, -Pattern, -Post, -Vars, -Aggregate)
Determine which parts of the goal we must remember in the findall/3 pattern.
Arguments:
Post- is a body-term that evaluates expressions to reduce storage requirements.
Vars- is a list of intermediate variables that must be added to the existential variables for bagof/3.
Aggregate- defines the aggregation operation to execute.
  321template_to_pattern(Term, Pattern, Goal, Vars, Aggregate) :-
  322    templ_to_pattern(Term, Pattern, Goal, Vars, Aggregate),
  323    !.
  324template_to_pattern(Term, Pattern, Goal, Vars, term(MinNeeded, Functor, AggregateArgs)) :-
  325    compound(Term),
  326    !,
  327    Term =.. [Functor|Args0],
  328    templates_to_patterns(Args0, Args, Goal, Vars, AggregateArgs),
  329    needs_one(AggregateArgs, MinNeeded),
  330    Pattern =.. [Functor|Args].
  331template_to_pattern(Term, _, _, _, _) :-
  332    invalid_template(Term).
  333
  334templ_to_pattern(sum(X),           X,         true,    [],   sum) :- var(X), !.
  335templ_to_pattern(sum(X0),          X,         X is X0, [X0], sum) :- !.
  336templ_to_pattern(count,            1,         true,    [],   count) :- !.
  337templ_to_pattern(min(X),           X,         true,    [],   min) :- var(X), !.
  338templ_to_pattern(min(X0),          X,         X is X0, [X0], min) :- !.
  339templ_to_pattern(min(X0, Witness), X-Witness, X is X0, [X0], min_witness) :- !.
  340templ_to_pattern(max(X0),          X,         X is X0, [X0], max) :- !.
  341templ_to_pattern(max(X0, Witness), X-Witness, X is X0, [X0], max_witness) :- !.
  342templ_to_pattern(set(X),           X,         true,    [],   set) :- !.
  343templ_to_pattern(bag(X),           X,         true,    [],   bag) :- !.
  344
  345templates_to_patterns([], [], true, [], []).
  346templates_to_patterns([H0], [H], G, Vars, [A]) :-
  347    !,
  348    sub_template_to_pattern(H0, H, G, Vars, A).
  349templates_to_patterns([H0|T0], [H|T], (G0,G), Vars, [A0|A]) :-
  350    sub_template_to_pattern(H0, H, G0, V0, A0),
  351    append(V0, RV, Vars),
  352    templates_to_patterns(T0, T, G, RV, A).
  353
  354sub_template_to_pattern(Term, Pattern, Goal, Vars, Aggregate) :-
  355    templ_to_pattern(Term, Pattern, Goal, Vars, Aggregate),
  356    !.
  357sub_template_to_pattern(Term, _, _, _, _) :-
  358    invalid_template(Term).
  359
  360invalid_template(Term) :-
  361    callable(Term),
  362    !,
  363    domain_error(aggregate_template, Term).
  364invalid_template(Term) :-
  365    type_error(aggregate_template, Term).
 needs_one(+Ops, -OneOrZero)
If one of the operations in Ops needs at least one answer, unify OneOrZero to 1. Else 0.
  372needs_one(Ops, 1) :-
  373    member(Op, Ops),
  374    needs_one(Op),
  375    !.
  376needs_one(_, 0).
  377
  378needs_one(min).
  379needs_one(min_witness).
  380needs_one(max).
  381needs_one(max_witness).
 aggregate_list(+Op, +List, -Answer) is semidet
Aggregate the answer from the list produced by findall/3, bagof/3 or setof/3. The latter two cases deal with compound answers.
To be done
- Compile code for incremental state update, which we will use for aggregate_all/3 as well. We should be using goal_expansion to generate these clauses.
  393aggregate_list(bag, List0, List) :-
  394    !,
  395    List = List0.
  396aggregate_list(set, List, Set) :-
  397    !,
  398    sort(List, Set).
  399aggregate_list(sum, List, Sum) :-
  400    sum_list(List, Sum).
  401aggregate_list(count, List, Count) :-
  402    length(List, Count).
  403aggregate_list(max, List, Sum) :-
  404    max_list(List, Sum).
  405aggregate_list(max_witness, List, max(Max, Witness)) :-
  406    max_pair(List, Max, Witness).
  407aggregate_list(min, List, Sum) :-
  408    min_list(List, Sum).
  409aggregate_list(min_witness, List, min(Min, Witness)) :-
  410    min_pair(List, Min, Witness).
  411aggregate_list(term(0, Functor, Ops), List, Result) :-
  412    !,
  413    maplist(state0, Ops, StateArgs, FinishArgs),
  414    State0 =.. [Functor|StateArgs],
  415    aggregate_term_list(List, Ops, State0, Result0),
  416    finish_result(Ops, FinishArgs, Result0, Result).
  417aggregate_list(term(1, Functor, Ops), [H|List], Result) :-
  418    H =.. [Functor|Args],
  419    maplist(state1, Ops, Args, StateArgs, FinishArgs),
  420    State0 =.. [Functor|StateArgs],
  421    aggregate_term_list(List, Ops, State0, Result0),
  422    finish_result(Ops, FinishArgs, Result0, Result).
  423
  424aggregate_term_list([], _, State, State).
  425aggregate_term_list([H|T], Ops, State0, State) :-
  426    step_term(Ops, H, State0, State1),
  427    aggregate_term_list(T, Ops, State1, State).
 min_pair(+Pairs, -Key, -Value) is det
 max_pair(+Pairs, -Key, -Value) is det
True if Key-Value has the smallest/largest key in Pairs. If multiple pairs share the smallest/largest key, the first pair is returned.
  437min_pair([M0-W0|T], M, W) :-
  438    min_pair(T, M0, W0, M, W).
  439
  440min_pair([], M, W, M, W).
  441min_pair([M0-W0|T], M1, W1, M, W) :-
  442    (   M0 < M1
  443    ->  min_pair(T, M0, W0, M, W)
  444    ;   min_pair(T, M1, W1, M, W)
  445    ).
  446
  447max_pair([M0-W0|T], M, W) :-
  448    max_pair(T, M0, W0, M, W).
  449
  450max_pair([], M, W, M, W).
  451max_pair([M0-W0|T], M1, W1, M, W) :-
  452    (   M0 > M1
  453    ->  max_pair(T, M0, W0, M, W)
  454    ;   max_pair(T, M1, W1, M, W)
  455    ).
 step(+AggregateAction, +New, +State0, -State1)
  459step(bag,   X, [X|L], L).
  460step(set,   X, [X|L], L).
  461step(count, _, X0, X1) :-
  462    succ(X0, X1).
  463step(sum,   X, X0, X1) :-
  464    X1 is X0+X.
  465step(max,   X, X0, X1) :-
  466    X1 is max(X0, X).
  467step(min,   X, X0, X1) :-
  468    X1 is min(X0, X).
  469step(max_witness, X-W, X0-W0, X1-W1) :-
  470    (   X > X0
  471    ->  X1 = X, W1 = W
  472    ;   X1 = X0, W1 = W0
  473    ).
  474step(min_witness, X-W, X0-W0, X1-W1) :-
  475    (   X < X0
  476    ->  X1 = X, W1 = W
  477    ;   X1 = X0, W1 = W0
  478    ).
  479step(term(Ops), Row, Row0, Row1) :-
  480    step_term(Ops, Row, Row0, Row1).
  481
  482step_term(Ops, Row, Row0, Row1) :-
  483    functor(Row, Name, Arity),
  484    functor(Row1, Name, Arity),
  485    step_list(Ops, 1, Row, Row0, Row1).
  486
  487step_list([], _, _, _, _).
  488step_list([Op|OpT], Arg, Row, Row0, Row1) :-
  489    arg(Arg, Row, X),
  490    arg(Arg, Row0, X0),
  491    arg(Arg, Row1, X1),
  492    step(Op, X, X0, X1),
  493    succ(Arg, Arg1),
  494    step_list(OpT, Arg1, Row, Row0, Row1).
  495
  496finish_result(Ops, Finish, R0, R) :-
  497    functor(R0, Functor, Arity),
  498    functor(R, Functor, Arity),
  499    finish_result(Ops, Finish, 1, R0, R).
  500
  501finish_result([], _, _, _, _).
  502finish_result([Op|OpT], [F|FT], I, R0, R) :-
  503    arg(I, R0, A0),
  504    arg(I, R, A),
  505    finish_result1(Op, F, A0, A),
  506    succ(I, I2),
  507    finish_result(OpT, FT, I2, R0, R).
  508
  509finish_result1(bag, Bag0, [], Bag) :-
  510    !,
  511    Bag = Bag0.
  512finish_result1(set, Bag,  [], Set) :-
  513    !,
  514    sort(Bag, Set).
  515finish_result1(max_witness, _, M-W, R) :-
  516    !,
  517    R = max(M,W).
  518finish_result1(min_witness, _, M-W, R) :-
  519    !,
  520    R = min(M,W).
  521finish_result1(_, _, A, A).
 state0(+Op, -State, -Finish)
  525state0(bag,   L, L).
  526state0(set,   L, L).
  527state0(count, 0, _).
  528state0(sum,   0, _).
 state1(+Op, +First, -State, -Finish)
  532state1(bag, X, L, [X|L]) :- !.
  533state1(set, X, L, [X|L]) :- !.
  534state1(_,   X, X, _).
  535
  536
  537                 /*******************************
  538                 *             FOREACH          *
  539                 *******************************/
 foreach(:Generator, :Goal)
True if conjunction of results is true. Unlike forall/2, which runs a failure-driven loop that proves Goal for each solution of Generator, foreach/2 creates a conjunction. Each member of the conjunction is a copy of Goal, where the variables it shares with Generator are filled with the values from the corresponding solution.

The implementation executes forall/2 if Goal does not contain any variables that are not shared with Generator.

Here is an example:

?- foreach(between(1,4,X), dif(X,Y)), Y = 5.
Y = 5.
?- foreach(between(1,4,X), dif(X,Y)), Y = 3.
false.
bug
- Goal is copied repeatedly, which may cause problems if attributed variables are involved.
  565foreach(Generator, Goal) :-
  566    term_variables(Generator, GenVars0), sort(GenVars0, GenVars),
  567    term_variables(Goal, GoalVars0), sort(GoalVars0, GoalVars),
  568    ord_subtract(GoalVars, GenVars, SharedGoalVars),
  569    (   SharedGoalVars == []
  570    ->  \+ (Generator, \+Goal)      % = forall(Generator, Goal)
  571    ;   ord_intersection(GenVars, GoalVars, SharedVars),
  572        Templ =.. [v|SharedVars],
  573        SharedTempl =.. [v|SharedGoalVars],
  574        findall(Templ, Generator, List),
  575        prove_list(List, Templ, SharedTempl, Goal)
  576    ).
  577
  578prove_list([], _, _, _).
  579prove_list([H|T], Templ, SharedTempl, Goal) :-
  580    copy_term(Templ+SharedTempl+Goal,
  581              H+SharedTempl+Copy),
  582    Copy,
  583    prove_list(T, Templ, SharedTempl, Goal).
 free_variables(:Generator, +Template, +VarList0, -VarList) is det
Find free variables in bagof/setof template. In order to handle variables properly, we have to find all the universally quantified variables in the Generator. All variables as yet unbound are universally quantified, unless
  1. they occur in the template
  2. they are bound by X^P, setof/3, or bagof/3

free_variables(Generator, Template, OldList, NewList) finds this set using OldList as an accumulator.

author
- Richard O'Keefe
- Jan Wielemaker (made some SWI-Prolog enhancements)
license
- Public domain (from DEC10 library).
To be done
- Distinguish between control-structures and data terms.
- Exploit our built-in term_variables/2 at some places?
  605free_variables(Term, Bound, VarList, [Term|VarList]) :-
  606    var(Term),
  607    term_is_free_of(Bound, Term),
  608    list_is_free_of(VarList, Term),
  609    !.
  610free_variables(Term, _Bound, VarList, VarList) :-
  611    var(Term),
  612    !.
  613free_variables(Term, Bound, OldList, NewList) :-
  614    explicit_binding(Term, Bound, NewTerm, NewBound),
  615    !,
  616    free_variables(NewTerm, NewBound, OldList, NewList).
  617free_variables(Term, Bound, OldList, NewList) :-
  618    functor(Term, _, N),
  619    free_variables(N, Term, Bound, OldList, NewList).
  620
  621free_variables(0, _, _, VarList, VarList) :- !.
  622free_variables(N, Term, Bound, OldList, NewList) :-
  623    arg(N, Term, Argument),
  624    free_variables(Argument, Bound, OldList, MidList),
  625    M is N-1,
  626    !,
  627    free_variables(M, Term, Bound, MidList, NewList).
  628
  629%   explicit_binding checks for goals known to existentially quantify
  630%   one or more variables.  In particular \+ is quite common.
  631
  632explicit_binding(\+ _Goal,             Bound, fail,     Bound      ) :- !.
  633explicit_binding(not(_Goal),           Bound, fail,     Bound      ) :- !.
  634explicit_binding(Var^Goal,             Bound, Goal,     Bound+Var) :- !.
  635explicit_binding(setof(Var,Goal,Set),  Bound, Goal-Set, Bound+Var) :- !.
  636explicit_binding(bagof(Var,Goal,Bag),  Bound, Goal-Bag, Bound+Var) :- !.
 term_is_free_of(+Term, +Var) is semidet
True if Var does not appear in Term. This has been rewritten from the DEC10 library source to exploit our non-deterministic arg/3.
  644term_is_free_of(Term, Var) :-
  645    \+ var_in_term(Term, Var).
  646
  647var_in_term(Term, Var) :-
  648    Var == Term,
  649    !.
  650var_in_term(Term, Var) :-
  651    compound(Term),
  652    arg(_, Term, Arg),
  653    var_in_term(Arg, Var),
  654    !.
 list_is_free_of(+List, +Var) is semidet
True if Var is not in List.
  661list_is_free_of([Head|Tail], Var) :-
  662    Head \== Var,
  663    !,
  664    list_is_free_of(Tail, Var).
  665list_is_free_of([], _).
  666
  667
  668%       term_variables(+Term, +Vars0, -Vars) is det.
  669%
  670%       True if Vars is the union of variables in Term and Vars0.
  671%       We cannot have this as term_variables/3 is already defined
  672%       as a difference-list version of term_variables/2.
  673
  674%term_variables(Term, Vars0, Vars) :-
  675%       term_variables(Term+Vars0, Vars).
 sandbox:safe_meta(+Goal, -Called) is semidet
Declare the aggregate meta-calls safe. This cannot be proven due to the manipulations of the argument Goal.
  683:- multifile sandbox:safe_meta_predicate/1.  684
  685sandbox:safe_meta_predicate(aggregate:foreach/2).
  686sandbox:safe_meta_predicate(aggregate:aggregate/3).
  687sandbox:safe_meta_predicate(aggregate:aggregate/4).
  688sandbox:safe_meta_predicate(aggregate:aggregate_all/3).
  689sandbox:safe_meta_predicate(aggregate:aggregate_all/4)