View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2026, SWI-Prolog Solutions b.v.
    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:- module(prolog_table_utils,
   36          [ table_statistics/0,			% Statistics
   37            table_statistics/1,			% ?Variant
   38            table_statistics_by_predicate/0,
   39            table_statistics_by_predicate/1,	% +Options
   40            table_statistics/2,                 % ?Stat, -Value
   41            table_statistics/3,                 % ?Variant, ?Stat, -Value
   42            tstat/2,                            % ?Stat, ?Top
   43            tstat/3,                            % ?Variant, ?Stat, ?Top
   44            tdump/0,                            % Dump tables
   45            tdump/1,                            % :Goal
   46            tdump/2,                            % :Goal, +Options
   47            tidg/0,
   48            tidg/1,                             % :Goal
   49            summarize_idg/0,
   50            summarize_idg/1                     % +Top
   51          ]).   52:- autoload(library(lists), [member/2]).   53:- autoload(library(aggregate), [aggregate_all/3]).   54:- autoload(library(ansi_term), [ansi_format/3]).   55:- autoload(library(apply), [exclude/3, maplist/2]).   56:- autoload(library(dif), [dif/2]).   57:- autoload(library(error), [domain_error/2]).   58:- autoload(library(option), [option/3, option/2]).   59:- autoload(library(prolog_code), [pi_head/2]).   60:- autoload(library(solution_sequences), [limit/2, order_by/2]).   61:- autoload(library(varnumbers), [numbervars/1]).   62
   63:- meta_predicate
   64    tdump(:),
   65    tdump(:, +),
   66    idg(:),
   67    summarize_idg(:),
   68    table_statistics(:),
   69    table_statistics(:, ?, -),
   70    tstat(:, ?, ?).

Table inspection and statistics utilities

This library provides tools intended to be used at the interactive toplevel for inspecting tables, table dependencies and table statistics. */

   78summary_table_width(55).
 table_statistics(?Stat, -Value) is nondet
 table_statistics(?Variant, ?Stat, -Value) is nondet
Give summary statistics for the tables associated with all subgoals of Variant. The table_statistics/2 version considers all tables.

The values for Stat are:

tables
Total number of answer tries
answers
Total number of answers in the combined tries
duplicate_ratio
Ratio of generated (and thus ignored) duplicate answers. 1 means no duplicates. 2 means for every answer there was (on everage) a duplicate generated.
space_ratio
Number of nodes with a value divided by the total number of nodes in a trie. The maximum is 1. A low number implies that a large amount of differently shaped data is included in the answer tries.
complete_call
Number of times answers are generated from a completed table, i.e., times answers are reused.
invalidated
Number of times an incremental table was invalidated.
reevaluated
Number of times an invalidated table wa reevaluated. If lower than invalidated this implies that dependent nodes of the IDG were reevaluated to the same answer set.
space
Summed memory usage of the answer tries in bytes.
compiled_space
Summed size for the compiled representation of completed tables.
  116table_statistics(Stat, Value) :-
  117    table_statistics(_:_, Stat, Value).
  118
  119table_statistics(Variant, Stat, Value) :-
  120    (   var(Stat)
  121    ->  table_statistics_(Variant, Stat, Value)
  122    ;   table_statistics_(Variant, Stat, Value)
  123    ->  true
  124    ).
  125
  126table_statistics_(Variant, tables, NTables) :-
  127    aggregate_all(count, table(Variant, _), NTables).
  128table_statistics_(Variant, Stat, Total) :-
  129    variant_trie_stat(Stat, _What),
  130    \+ hidden_stat(Stat, Variant),
  131    (   avg(Stat)
  132    ->  aggregate_all(sum(Ratio)+count,
  133                      variant_stat(Stat, Variant, Ratio),
  134                      Sum+Count),
  135        Count > 0,
  136        Total is Sum/Count
  137    ;   aggregate_all(sum(Count), variant_stat(Stat, Variant, Count), Total)
  138    ).
  139
  140hidden_stat(variables, _).
  141hidden_stat(lookup, _).
  142hidden_stat(invalidated, Variant) :-
  143    callable(Variant),
  144    \+ predicate_property(Variant, tabled(incremental)).
  145hidden_stat(reevaluated, Variant) :-
  146    callable(Variant),
  147    \+ predicate_property(Variant, tabled(incremental)).
  148
  149avg(space_ratio).
  150avg(duplicate_ratio).
 table_statistics
Print a summary of statistics relevant to tabling.
See also
- table_statistics/2 for an explanation
  158table_statistics :-
  159    (   (   '$tbl_global_variant_table'(Table),
  160            call_table_properties(shared, Table)
  161        ;   '$tbl_local_variant_table'(Table),
  162            call_table_properties(private, Table)
  163        ),
  164        fail
  165    ;   true
  166    ),
  167    ansi_format([bold], 'Summary of answer trie statistics:', []),
  168    nl,
  169    table_statistics_(_:_, [tables(false)]).
 table_statistics(:Variant)
Print a summary for the statistics of all tables for subgoals of Variant. See table_statistics/2 for an explanation.
  176table_statistics(Variant) :-
  177    table_statistics_(Variant, []).
  178
  179table_statistics_(Variant, Options) :-
  180    table_statistics_dict(Variant, Dict),
  181    print_table_statistics(Dict, Options).
  182
  183table_statistics_dict(Variant, Dict) :-
  184    findall(Stat-Value, table_statistics(Variant, Stat, Value), Pairs),
  185    dict_create(Dict, table_stat, [variant-Variant|Pairs]).
  186
  187print_table_statistics(Dict, Options) :-
  188    summary_table_width(DefWidth),
  189    option(width(Width), Options, DefWidth),
  190    (   option(tables(false), Options)
  191    ->  dif(Stat, tables)
  192    ;   true
  193    ),
  194    (   option(header(true), Options)
  195    ->  print_table_predicate_header(Dict.variant, [width(Width)|Options])
  196    ;   true
  197    ),
  198    (   variant_trie_stat0(Stat, What),
  199        Value = Dict.get(Stat),
  200        (   integer(Value)
  201        ->  format('  ~w ~`.t ~D~*|~n', [What, Value, Width])
  202        ;   format('  ~w ~`.t ~2f~*|~n', [What, Value, Width])
  203        ),
  204        fail
  205    ;   true
  206    ).
  207
  208print_table_predicate_header(Pred, Options) :-
  209    option(width(Width), Options),
  210    Pred = M:Head,
  211    tflags(Pred, Flags),
  212    functor(Head, Name, Arity),
  213    format('~n~`\u2015t~*|~n', [Width]),
  214    format('~t~p~t~w~*|~n', [M:Name/Arity, Flags, Width]),
  215    format('~`\u2015t~*|~n', [Width]).
  216
  217tflags(Pred, Flags) :-
  218    findall(F, tflag(Pred, F), List),
  219    atomic_list_concat(List, Flags).
  220
  221tflag(Pred, Flag) :-
  222    predicate_property(Pred, tabled(How)),
  223    tflag_name(How, Flag).
  224
  225tflag_name(variant,     'V').
  226tflag_name(subsumptive, 'S').
  227tflag_name(shared,      'G').
  228tflag_name(incremental, 'I').
  229
  230
  231variant_trie_stat0(tables, "Answer tables").
  232variant_trie_stat0(Stat, What) :-
  233    dif(Stat, tables),
  234    variant_trie_stat(Stat, What).
  235
  236call_table_properties(Which, Trie) :-
  237    ansi_format([bold], 'Statistics for ~w call trie:', [Which]),
  238    nl,
  239    summary_table_width(Width),
  240    (   call_trie_property_name(P, Label, Value),
  241        atrie_prop(Trie, P),
  242        (   integer(Value)
  243        ->  format('  ~w ~`.t ~D~*|~n', [Label, Value, Width])
  244        ;   format('  ~w ~`.t ~1f~*|~n', [Label, Value, Width])
  245        ),
  246        fail
  247    ;   true
  248    ).
  249
  250call_trie_property_name(value_count(N), 'Number of tables',     N).
  251call_trie_property_name(size(N),        'Memory for call trie', N).
  252call_trie_property_name(space_ratio(N), 'Space efficiency',     N).
 table_statistics_by_predicate is det
 table_statistics_by_predicate(+Options) is det
Print statistics on memory usage and lookups per predicate. The version without options dumps all predicates without ordering. Options:
order_by(+Key)
Order the predicates according to Key. Default is tables, the number of answer tables. See table_statistics/2 for a list of values for Key.
top(N)
Only show the top N predicates.
module(Module)
Limit the results to predicates of the given module.
  270table_statistics_by_predicate :-
  271    Pred = _:_,
  272    summary_table_width(Width),
  273    (   tabled_predicate_with_tables(Pred),
  274        print_table_predicate_header(Pred, [width(Width)]),
  275        table_statistics(Pred),
  276        fail
  277    ;   true
  278    ).
  279
  280table_statistics_by_predicate(Options) :-
  281    option(order_by(OrderBy), Options, tables),
  282    option(top(Top), Options, infinite),
  283    option(module(M), Options, _),
  284    Pred = (M:_),
  285    findall(Dict,
  286            (  tabled_predicate_with_tables(Pred),
  287               table_statistics_dict(Pred, Dict)
  288            ),
  289            Dicts),
  290    exclude(has_no_key(OrderBy), Dicts, Dicts1),
  291    (   integer(Top), Top < 0
  292    ->  Order = @=<,
  293        TopN is -Top
  294    ;   Order = @>=,
  295        TopN is Top
  296    ),
  297    sort(OrderBy, Order, Dicts1, Sorted),
  298    forall(limit(TopN, member(Dict, Sorted)),
  299           print_table_statistics(Dict, [header(true)|Options])).
  300
  301has_no_key(Key, Dict) :-
  302    \+ _ = Dict.get(Key).
  303
  304tabled_predicate_with_tables(Pred) :-
  305    Pred = _:_,
  306    predicate_property(Pred, tabled),
  307    \+ predicate_property(Pred, imported_from(_)),
  308    \+ \+ table(Pred, _).
 tstat(?Value, ?Top)
 tstat(?Variant, ?Value, ?Top)
Print the top-N (for positive Top) or bottom-N (for negative Top) for Stat for all tabled subgoals of Variant (or all tabled subgoals for tstat/2). Stat is one of
answers
duplicate_ratio
space_ratio
gen(call)
space
compiled_space
See table_statistics/2.
deadlock
Times this table was involved in a deadlock (cycle of threads waiting for each others table to complete)
wait
Times a thread waited for this table.
variables
The number of variables in the variant. The tabling logic adds a term ret(...) to the table for each answer, where each variable is an argument of the ret(...) term. The arguments are placed in depth-first lef-right order they appear in the variant. Optimal behaviour of the trie is achieved if the variance is as much as possible to the rightmost arguments. Poor allocation shows up as a low space_ratio statistics.

Below are some examples

  340%     - Find the tables with poor space behaviour (bad)
  341%
  342%           ?- tstat(space_ratio, -10).
  343%
  344%     - Find the tables with a high number of duplicates (good, but
  345%       if the number of duplicates can easily be reduced a lot it
  346%       makes your code faster):
  347%
  348%           ?- tstat(duplicate_ratio, 10).
  349
  350tstat(Stat, Top) :-
  351    tstat(_:_, Stat, Top).
  352tstat(Variant, Stat, Top) :-
  353    variant_trie_stat(Stat, What),
  354    top(Top, Count, Limit, Dir, Order),
  355    findall(Variant-Count,
  356            limit(Limit, order_by([Order], variant_stat(Stat, Variant, Count))),
  357            Pairs),
  358    write_variant_table('~w ~w count per variant'-[Dir, What], Pairs).
  359
  360top(Top, Var, 10, "Top", desc(Var)) :-
  361    var(Top), !.
  362top(Top, Var, Top, "Top", desc(Var)) :-
  363    Top >= 0, !.
  364top(Top, Var, Limit, "Bottom", asc(Var)) :-
  365    Limit is -Top.
  366
  367variant_stat(Stat, V, Count) :-
  368    variant_trie_stat(Stat, _, Count, Property),
  369    table(V, T),
  370    atrie_prop(T, Property).
  371
  372atrie_prop(T, size(Bytes)) :-
  373    '$trie_property'(T, size(Bytes)).
  374atrie_prop(T, compiled_size(Bytes)) :-
  375    '$trie_property'(T, compiled_size(Bytes)).
  376atrie_prop(T, value_count(Count)) :-
  377    '$trie_property'(T, value_count(Count)).
  378atrie_prop(T, space_ratio(Values/Nodes)) :-
  379    '$trie_property'(T, value_count(Values)),
  380    Values > 0,
  381    '$trie_property'(T, node_count(Nodes)).
  382atrie_prop(T, lookup_count(Count)) :-
  383    '$trie_property'(T, lookup_count(Count)).
  384atrie_prop(T, duplicate_ratio(Ratio)) :-
  385    '$trie_property'(T, value_count(Values)),
  386    Values > 0,
  387    '$trie_property'(T, lookup_count(Lookup)),
  388    Ratio is (Lookup - Values)/Values.
  389atrie_prop(T, gen_call_count(Count)) :-
  390    '$trie_property'(T, gen_call_count(Count)).
  391atrie_prop(T, invalidated(Count)) :-
  392    '$trie_property'(T, invalidated(Count)).
  393atrie_prop(T, reevaluated(Count)) :-
  394    '$trie_property'(T, reevaluated(Count)).
  395atrie_prop(T, deadlock(Count)) :-
  396    '$trie_property'(T, deadlock(Count)),
  397    Count > 0.
  398atrie_prop(T, wait(Count)) :-
  399    '$trie_property'(T, wait(Count)),
  400    Count > 0.
  401atrie_prop(T, variables(Count)) :-
  402    '$tbl_table_status'(T, _Status, _Wrapper, Skeleton),
  403    functor(Skeleton, ret, Count).
  404
  405variant_trie_stat(Stat, What) :-
  406    (   variant_trie_stat(Stat, What, _, _)
  407    *-> true
  408    ;   domain_error(tstat_key, Stat)
  409    ).
  410
  411variant_trie_stat(answers,        "Number of answers",
  412                  Count, value_count(Count)).
  413variant_trie_stat(duplicate_ratio,"Duplicate answer ratio",
  414                  Ratio, duplicate_ratio(Ratio)).
  415variant_trie_stat(space_ratio,    "Space efficiency",
  416                  Ratio, space_ratio(Ratio)).
  417variant_trie_stat(complete_call,  "Calls to completed tables",
  418                  Count, gen_call_count(Count)).
  419variant_trie_stat(invalidated,    "Times the tables were invalidated",
  420                  Count, invalidated(Count)).
  421variant_trie_stat(reevaluated,    "Times the tables were reevaluated",
  422                  Count, reevaluated(Count)).
  423variant_trie_stat(space,          "Memory usage for answer tables",
  424                  Bytes, size(Bytes)).
  425variant_trie_stat(compiled_space, "Memory usage for compiled answer tables",
  426                  Bytes, compiled_size(Bytes)).
  427variant_trie_stat(variables,      "Number of variables in answer skeletons",
  428                  Count, variables(Count)).
  429variant_trie_stat(wait,		  "Times table was waited for",
  430                  Count, wait(Count)).
  431variant_trie_stat(deadlock,	  "Times table was involved in a deadlock",
  432                  Count, deadlock(Count)).
 write_variant_table(+Title, +Pairs)
  436write_variant_table(Format-Args, Pairs) :-
  437    format(string(Title), Format, Args),
  438    tty_size(_, Cols),
  439    W is Cols - 8,
  440    format('~`\u2015t~*|~n', [W]),
  441    format('~t~w~t~*|~n', [Title, W]),
  442    format('~`\u2015t~*|~n', [W]),
  443    maplist(write_variant_stat(W), Pairs).
  444
  445write_variant_stat(W, V-Stat) :-
  446    \+ \+ ( numbervars(V, 0, _, [singletons(true)]),
  447            (   integer(Stat)
  448            ->  format('~p ~`.t ~D~*|~n', [V, Stat, W])
  449            ;   format('~p ~`.t ~2f~*|~n', [V, Stat, W])
  450            )
  451          ).
  452
  453table(M:Variant, Trie) :-
  454    '$tbl_variant_table'(VariantTrie),
  455    trie_gen(VariantTrie, M:Variant, Trie).
  456
  457
  458                /*******************************
  459                *         DUMP TABLES          *
  460                *******************************/
 tdump is det
 tdump(:Goal) is det
 tdump(:Goal, +Options) is det
Dump all tables and their status that unify with Goal. Options:
scope(Scope)
Limit displayed tables to local or global.
limit(Count)
Limit the number of answers displayed to Count
reset(Boolean)
If true, also show reset (fresh) global tables. These are tables that have been abolished.
  476tdump :-
  477    tdump(_:_).
  478tdump(M:Goal) :-
  479    tdump(M:Goal, []).
  480
  481tdump(M:Goal, Options) :-
  482    option(scope(Scope), Options, _),
  483    option(limit(Limit), Options, 100),
  484    (   table(Scope, M:Goal, Trie),
  485	'$tbl_table_status'(Trie, Status, M:Variant, Skeleton),
  486        M:'$table_mode'(Head0, Variant, Moded),
  487        Head = M:Head0,
  488        (   option(reset(true), Options)
  489        ->  true
  490        ;   \+ (Scope == global, Status == fresh)
  491        ),
  492        ansi_format(comment, 'Trie for variant ', []),
  493        pflags(Variant, Flags),
  494        format('~s ', [Flags]),
  495        print_variant(Head),
  496        Answer = Head,
  497        '$tbl_trienode'(Reserved),
  498        (   Moded == Reserved
  499        ->  findall(Answer-Delay,
  500                    '$tbl_answer'(Trie, Skeleton, Delay), Pairs),
  501            ExtraProp = ''
  502        ;   findall(Answer-Delay,
  503                    '$tbl_answer'(Trie, Skeleton, Moded, Delay), Pairs),
  504            ExtraProp = 'moded, '
  505        ),
  506        sort(1, @<, Pairs, Sorted),
  507        length(Sorted, Count),
  508        status_color(Status, Color),
  509        ansi_format(comment, ' (~p,', [Scope]),
  510        ansi_format(Color,   ' ~p', [Status]),
  511        ansi_format(comment, ', ~w~D answers)~n', [ExtraProp, Count]),
  512        (   Count == 0
  513        ->  ansi_format(warning, '  (empty)~n', [])
  514        ;   forall(limit(Limit, member(Ans, Sorted)),
  515                   dump_answer(M, Ans))
  516        ),
  517        fail
  518    ;   true
  519    ).
  520
  521status_color(invalid, warning) :- !.
  522status_color(_, comment).
  523
  524
  525table(local, Variant, Trie) :-
  526    '$tbl_local_variant_table'(VariantTrie),
  527    trie_gen(VariantTrie, Variant0, Trie),
  528    subsumes_term(Variant, Variant0),
  529    Variant = Variant0.
  530table(global, Variant, Trie) :-
  531    '$tbl_global_variant_table'(VariantTrie),
  532    trie_gen(VariantTrie, Variant0, Trie),
  533    subsumes_term(Variant, Variant0),
  534    Variant = Variant0.
  535
  536print_variant(Head) :-
  537    term_attvars(Head, []),
  538    !,
  539    \+ \+ ( numbervars(Head, 0, _),
  540            ansi_format(code,  '~p', [Head])
  541          ).
  542print_variant(Head) :-
  543    copy_term(Head, Copy, Constraints),
  544    numbervars(Copy+Constraints, 0, _),
  545    format('~p', [Copy]),
  546    forall(member(C, Constraints),
  547           ansi_format(fg(blue), ', ~p', [C])).
  548
  549dump_answer(M, Answer0-true) :-
  550    !,
  551    unqualify(Answer0, M, Answer),
  552    \+ \+ print_answer(Answer).
  553dump_answer(M, Answer0-Condition) :-
  554    unqualify(Answer0, M, Answer),
  555    unqualify(Condition, M, SimpleCondition),
  556    \+ \+ ( numbervars(Answer+SimpleCondition, 0, _),
  557            format('  ~p', [Answer]),
  558            ansi_format(bold, ' :- ', []),
  559            ansi_format(fg(cyan), '~p~n', [SimpleCondition])
  560          ).
  561
  562print_answer(Answer) :-
  563    term_attvars(Answer, []),
  564    !,
  565    numbervars(Answer, 0, _),
  566    format('  ~p~n', [Answer]).
  567print_answer(Answer) :-
  568    copy_term(Answer, Copy, Constraints),
  569    numbervars(Copy+Constraints, 0, _),
  570    format('  ~p', [Copy]),
  571    forall(member(C, Constraints),
  572           ansi_format(fg(blue), ', ~p', [C])),
  573    nl.
  574
  575unqualify(Var, _M, Var) :-
  576    var(Var),
  577    !.
  578unqualify((A0,B0), M, (A,B)) :-
  579    !,
  580    unqualify(A0, M, A),
  581    unqualify(B0, M, B).
  582unqualify((A0;B0), M, (A;B)) :-
  583    !,
  584    unqualify(A0, M, A),
  585    unqualify(B0, M, B).
  586unqualify(tnot(A0), M, tnot(A)) :-
  587    !,
  588    unqualify(A0, M, A).
  589unqualify((M1:Variant)/ModeArgs, M, Goal) :-
  590    !,
  591    M1:'$table_mode'(G0, Variant, ModeArgs),
  592    unqualify(M1:G0, M, Goal).
  593unqualify(M:G, M, G) :-
  594    !.
  595unqualify(G, _, G).
 tidg is det
 tidg(:Goal) is det
Dump the incremental dependency graph. idg/1 dumps the graph around a given node
  603tidg :-
  604    ansi_format(comment,
  605                '% Node1 [falsecount] (affects -->) Node1 [falsecount]~n', []),
  606    forall(idg(t(_:From,FFC,FAC), affected, t(_:To,TFC,TAC)),
  607           \+ \+ ( numbervars(From),
  608                   numbervars(To),
  609                   print_edge(From, FFC,FAC, To, TFC,TAC)
  610                 )).
  611
  612tidg(M:Node) :-
  613    ansi_format(comment,
  614                '% Node1 [falsecount] (affects -->) Node1 [falsecount]~n', []),
  615    ansi_format([bold], 'Affected nodes~n', []),
  616    forall(idg(t(M:Node,FFC,FAC), affected, t(_:To,TFC,TAC)),
  617           \+ \+ ( numbervars(Node),
  618                   numbervars(To),
  619                   print_edge(Node, FFC,FAC, To, TFC,TAC)
  620                 )),
  621    ansi_format([bold], 'Dependent nodes~n', []),
  622    forall(idg(t(_:From,FFC,FAC), affected, t(M:Node,TFC,TAC)),
  623           \+ \+ ( numbervars(From),
  624                   numbervars(Node),
  625                   print_edge(From, FFC,FAC, Node, TFC,TAC)
  626                 )).
  627
  628
  629print_edge(From, FFC,FAC, To, TFC,TAC) :-
  630    format('  '),
  631    print_node(From, FFC,FAC),
  632    format(' --> '),
  633    print_node(To, TFC,TAC),
  634    nl.
  635
  636print_node(Variant, Falsecount, AnswerCount) :-
  637    pflags(Variant, Flags),
  638    format('~s ', [Flags]),
  639    ansi_format(code, '~p', [Variant]),
  640    format(' '),
  641    (   Falsecount == 0
  642    ->  ansi_format(comment, '[0]', [])
  643    ;   ansi_format([bg(red),fg(white)], '[~w]', [Falsecount])
  644    ),
  645    print_answer_count(AnswerCount).
  646
  647print_answer_count(answers(Count)) =>
  648    format(' (~Da)', [Count]).
  649print_answer_count(clauses(Count)) =>
  650    format(' (~Dc)', [Count]).
  651
  652pflags(Variant, Flags) :-
  653    findall(F, flag(Variant, F), Flags).
  654
  655flag(Variant, Flag) :-
  656    (   pflag(Variant, dynamic,     'D', Flag)
  657    ;   pflag(Variant, incremental, 'I', Flag)
  658    ;   pflag(Variant, monotonic,   'M', Flag)
  659    ).
  660
  661pflag(Variant, Property, Char, Flag) :-
  662    (   predicate_property(Variant, Property)
  663    ->  Flag = Char
  664    ;   Flag = ' '
  665    ).
  666
  667idg(t(FM:From,FFC,FAC), Dir, t(TM:To,TFC,TAC)) :-
  668    '$tbl_variant_table'(VTrie),
  669    trie_gen(VTrie, FM:FVariant, ATrie),
  670    (   FM:'$table_mode'(From1, FVariant, _FModed)
  671    ->  true
  672    ;   From1 = FVariant                 % dynamic incremental/monotonic
  673    ),
  674    subsumes_term(From, From1),
  675    From = From1,
  676    fc(ATrie, From, FFC,FAC),
  677    '$idg_edge'(ATrie, Dir, DepTrie),
  678    '$tbl_table_status'(DepTrie, _Status, TM:TVariant, _Return),
  679    TM:'$table_mode'(To1, TVariant, _TModed),
  680    subsumes_term(To, To1),
  681    To = To1,
  682    fc(DepTrie, To, TFC,TAC).
  683
  684fc(ATrie, Variant, FC, AC) :-
  685    (   predicate_property(Variant, tabled)
  686    ->  trie_property(ATrie, value_count(C)),
  687        AC = answers(C)
  688    ;   aggregate_all(count, clause(Variant,_), C),
  689        AC = clauses(C)
  690    ),
  691    (   '$idg_falsecount'(ATrie, FC0)
  692    ->  (   '$idg_forced'(ATrie)
  693        ->  FC = FC0/'F'
  694        ;   FC = FC0
  695        )
  696    ;   FC = 0
  697    ).
 summarize_idg is det
 summarize_idg(+TopN) is det
Implements XSB's statistics(summarize_idg)
  704:- module_transparent
  705    summarize_idg/0.  706
  707summarize_idg :-
  708    context_module(M),
  709    summarize_idg(infinite, M).
  710
  711summarize_idg(M:Top) :-
  712    summarize_idg(Top, M).
  713
  714summarize_idg(Top, M) :-
  715    tty_width(Width),
  716    header('Interior Nodes (Tabled Subgoals)', Width),
  717    format('Predicate~t #idg nodes~*|~n', [Width]),
  718    format('~`\u2015t~*|~n', [Width]),
  719    forall(limit(Top,
  720                 order_by([desc(Count),asc(PI)],
  721                          interior_nodes(_:_, M, PI, Count))),
  722           format('~q ~`.t ~D~*|~n', [PI, Count, Width])),
  723    nl,
  724    ColR is Width - 10,
  725    header('Leaf Nodes (Calls to Dynamic Predicates)', Width),
  726    format('Predicate~t #idg nodes~*|~t#facts~*|~n', [ColR, Width]),
  727    format('~`\u2015t~*|~n', [Width]),
  728    forall(limit(Top,
  729                 order_by([desc(Count),desc(Facts),asc(PI)],
  730                          leaf_nodes(_:_, M, PI, Count, Facts))),
  731           format('~q ~`.t ~D~*| ~`.t ~D~*|~n',
  732                  [PI, Count, ColR, Facts, Width])).
  733
  734interior_nodes(Variant, M, PI, Count) :-
  735    predicate_property(Variant, tabled(incremental)),
  736    \+ predicate_property(Variant, imported_from(_)),
  737    idg_node_count(Variant, Count),
  738    pi_head(QPI, Variant),
  739    unqualify_pi(QPI, M, PI).
  740
  741leaf_nodes(Variant, M, PI, Count, Facts) :-
  742    predicate_property(Variant, dynamic),
  743    predicate_property(Variant, incremental),
  744    \+ predicate_property(Variant, imported_from(_)),
  745    predicate_property(Variant, number_of_clauses(Facts)),
  746    idg_node_count(Variant, Count),
  747    pi_head(QPI, Variant),
  748    unqualify_pi(QPI, M, PI).
  749
  750
  751idg_node_count(Variant, Count) :-
  752    aggregate_all(count,
  753                  ( '$tbl_variant_table'(VTrie),
  754                    trie_gen(VTrie, Variant, _ATrie)
  755                  ),
  756                  Count).
  757
  758unqualify_pi(M:PI, M, PI) :- !.
  759unqualify_pi(PI, _, PI).
  760
  761tty_width(W) :-
  762    catch(tty_size(_, TtyW), _, fail),
  763    !,
  764    W is max(60, TtyW - 8).
  765tty_width(60).
  766
  767header(Title, Width) :-
  768    format('~N~`\u2015t~*|~n', [Width]),
  769    ansi_format([bold], '~t~w~t~*|', [Title,Width]),
  770    nl