1/* Part of SWI-Prolog 2 3 Author: Benoit Desouter <Benoit.Desouter@UGent.be> 4 Jan Wielemaker (SWI-Prolog port) 5 Fabrizio Riguzzi (mode directed tabling) 6 Copyright (c) 2016, Benoit Desouter, Jan Wielemaker, Fabrizio Riguzzi 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(tabling, 36 [ (table)/1, % +PI ... 37 38 current_table/2, % :Variant, ?Table 39 abolish_all_tables/0, 40 abolish_table_subgoals/1, % :Subgoal 41 42 start_tabling/2, % +Wrapper, :Worker. 43 44 op(1150, fx, table) 45 ]). 46:- use_module(library(error)). 47:- set_prolog_flag(generate_debug_info, false). 48 49:- meta_predicate 50 start_tabling( , ), 51 current_table( , ), 52 abolish_table_subgoals( ).
:- table edge/2, statement//1.
In addition to using predicate indicators, a predicate can be declared for mode directed tabling using a term where each argument declares the intended mode. For example:
:- table connection(_,_,min).
Mode directed tabling is discussed in the general introduction section about tabling.
85table(PIList) :-
86 throw(error(context_error(nodirective, table(PIList)), _)).
98start_tabling(Wrapper, Worker) :- 99 get_wrapper_no_mode_args(Wrapper, WrapperNoModes, ModeArgs), 100 '$tbl_variant_table'(WrapperNoModes, Trie, Status), 101 ( Status == complete 102 -> trie_gen(Trie, WrapperNoModes, ModeArgs) 103 ; ( '$tbl_scheduling_component'(false, true) 104 -> catch(run_leader(Wrapper, WrapperNoModes, Worker, Trie), 105 E, true), 106 ( var(E) 107 -> trie_gen(Trie, WrapperNoModes, ModeArgs) 108 ; '$tbl_table_discard_all', 109 throw(E) 110 ) 111 ; run_follower(Status, Wrapper, WrapperNoModes, Worker, Trie) 112 ) 113 ). 114 115get_wrapper_no_mode_args(M:Wrapper, M:WrapperNoModes, ModeArgs) :- 116 M:'$table_mode'(Wrapper, WrapperNoModes, ModeArgs). 117 118run_follower(fresh, Wrapper, WrapperNoModes, Worker, Trie) :- 119 !, 120 activate(Wrapper, WrapperNoModes, Worker, Trie, Worklist), 121 shift(call_info(Wrapper, Worklist)). 122run_follower(Worklist, Wrapper, _WrapperNoModes, _Worker, _Trie) :- 123 shift(call_info(Wrapper, Worklist)). 124 125run_leader(Wrapper, WrapperNoModes, Worker, Trie) :- 126 activate(Wrapper, WrapperNoModes, Worker, Trie, _Worklist), 127 completion, 128 '$tbl_scheduling_component'(_, false). 129 130activate(Wrapper, WrapperNoModes, Worker, Trie, WorkList) :- 131 '$tbl_new_worklist'(WorkList, Trie), 132 ( delim(Wrapper, WrapperNoModes, Worker, WorkList), 133 fail 134 ; true 135 ).
142delim(Wrapper, Worker, WorkList) :- 143 reset(, SourceCall, Continuation), 144 add_answer_or_suspend(Continuation, Wrapper, 145 WorkList, SourceCall). 146 147add_answer_or_suspend(0, Wrapper, WorkList, _) :- 148 !, 149 '$tbl_wkl_add_answer'(WorkList, Wrapper). 150add_answer_or_suspend(Continuation, Wrapper, WorkList, 151 call_info(SrcWrapper, SourceWL)) :- 152 '$tbl_wkl_add_suspension'( 153 SourceWL, 154 dependency(SrcWrapper, Continuation, Wrapper, WorkList)). 155 156delim(Wrapper, WrapperNoModes, Worker, WorkList) :- 157 reset(, SourceCall, Continuation), 158 add_answer_or_suspend(Continuation, Wrapper, WrapperNoModes, 159 WorkList, SourceCall). 160 161add_answer_or_suspend(0, Wrapper, WrapperNoModes, WorkList, _) :- 162 !, 163 get_wrapper_no_mode_args(Wrapper, _, ModeArgs), 164 '$tbl_wkl_mode_add_answer'(WorkList, WrapperNoModes, 165 ModeArgs, Wrapper). 166add_answer_or_suspend(Continuation, Wrapper, _WrapperNoModes, WorkList, 167 call_info(SrcWrapper, SourceWL)) :- 168 '$tbl_wkl_add_suspension'( 169 SourceWL, 170 dependency(SrcWrapper, Continuation, Wrapper, WorkList)).
179:- public 180 update/4. 181 182update(M:Wrapper, A1, A2, A3) :- 183 M:'$table_update'(Wrapper, A1, A2, A3), 184 A1 \=@= A3.
191completion :- 192 '$tbl_pop_worklist'(WorkList), 193 !, 194 completion_step(WorkList), 195 completion. 196completion :- 197 '$tbl_table_complete_all'. 198 199completion_step(SourceTable) :- 200 ( '$tbl_trienode'(Reserved), 201 '$tbl_wkl_work'(SourceTable, 202 Answer, ModeArgs, 203 Goal, Continuation, Wrapper, TargetTable), 204 ( ModeArgs == Reserved 205 -> Goal = Answer, 206 delim(Wrapper, Continuation, TargetTable) 207 ; get_wrapper_no_mode_args(Goal, Answer, ModeArgs), 208 get_wrapper_no_mode_args(Wrapper, WrapperNoModes, _), 209 delim(Wrapper, WrapperNoModes, Continuation, TargetTable) 210 ), 211 fail 212 ; true 213 ). 214 215 /******************************* 216 * CLEANUP * 217 *******************************/
228abolish_all_tables :-
229 '$tbl_abolish_all_tables'.
235abolish_table_subgoals(M:SubGoal) :- 236 '$tbl_variant_table'(VariantTrie), 237 current_module(M), 238 forall(trie_gen(VariantTrie, M:SubGoal, Trie), 239 '$tbl_destroy_table'(Trie)). 240 241 242 /******************************* 243 * EXAMINE TABLES * 244 *******************************/
250current_table(M:Variant, Trie) :- 251 '$tbl_variant_table'(VariantTrie), 252 ( (var(Variant) ; var(M)) 253 -> trie_gen(VariantTrie, M:Variant, Trie) 254 ; trie_lookup(VariantTrie, M:Variant, Trie) 255 ). 256 257 258 /******************************* 259 * WRAPPER GENERATION * 260 *******************************/ 261 262:- multifile 263 system:term_expansion/2, 264 prolog:rename_predicate/2, 265 tabled/2. 266:- dynamic 267 system:term_expansion/2. 268 269wrappers(Var) --> 270 { var(Var), 271 !, 272 instantiation_error(Var) 273 }. 274wrappers((A,B)) --> 275 !, 276 wrappers(A), 277 wrappers(B). 278wrappers(Name//Arity) --> 279 { atom(Name), integer(Arity), Arity >= 0, 280 !, 281 Arity1 is Arity+2 282 }, 283 wrappers(Name/Arity1). 284wrappers(Name/Arity) --> 285 { atom(Name), integer(Arity), Arity >= 0, 286 !, 287 functor(Head, Name, Arity), 288 atom_concat(Name, ' tabled', WrapName), 289 Head =.. [Name|Args], 290 WrappedHead =.. [WrapName|Args], 291 prolog_load_context(module, Module), 292 '$tbl_trienode'(Reserved) 293 }, 294 [ '$tabled'(Head), 295 '$table_mode'(Head, Head, Reserved), 296 ( Head :- 297 start_tabling(Module:Head, WrappedHead) 298 ) 299 ]. 300wrappers(ModeDirectedSpec) --> 301 { callable(ModeDirectedSpec), 302 !, 303 functor(ModeDirectedSpec, Name, Arity), 304 functor(Head, Name, Arity), 305 atom_concat(Name, ' tabled', WrapName), 306 Head =.. [Name|Args], 307 WrappedHead =.. [WrapName|Args], 308 extract_modes(ModeDirectedSpec, Head, Variant, Modes, Moded), 309 updater_clauses(Modes, Head, UpdateClauses), 310 prolog_load_context(module, Module) 311 }, 312 [ '$tabled'(Head), 313 '$table_mode'(Head, Variant, Moded), 314 ( Head :- 315 start_tabling(Module:Head, WrappedHead) 316 ) 317 | UpdateClauses 318 ]. 319wrappers(TableSpec) --> 320 { type_error(table_desclaration, TableSpec) 321 }.
333extract_modes(ModeSpec, Head, Variant, Modes, ModedAnswer) :-
334 compound_name_arguments(ModeSpec, Name, ModeSpecArgs),
335 compound_name_arguments(Head, Name, HeadArgs),
336 separate_args(ModeSpecArgs, HeadArgs, VariantArgs, Modes, ModedArgs),
337 Variant =.. [Name|VariantArgs],
338 ( ModedArgs == []
339 -> '$tbl_trienode'(ModedAnswer)
340 ; ModedArgs = [ModedAnswer]
341 -> true
342 ; ModedAnswer =.. [s|ModedArgs]
343 ).
353separate_args([], [], [], [], []). 354separate_args([HM|TM], [H|TA], [H|TNA], Modes, TMA):- 355 indexed_mode(HM), 356 !, 357 separate_args(TM, TA, TNA, Modes, TMA). 358separate_args([M|TM], [H|TA], TNA, [M|Modes], [H|TMA]):- 359 separate_args(TM, TA, TNA, Modes, TMA). 360 361indexed_mode(Mode) :- % XSB 362 var(Mode), 363 !. 364indexed_mode(index). % YAP 365indexed_mode(+). % B
372updater_clauses([], _, []) :- !. 373updater_clauses([P], Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- !, 374 update_goal(P, S0,S1,S2, Body). 375updater_clauses(Modes, Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- 376 length(Modes, Len), 377 functor(S0, s, Len), 378 functor(S1, s, Len), 379 functor(S2, s, Len), 380 S0 =.. [_|Args0], 381 S1 =.. [_|Args1], 382 S2 =.. [_|Args2], 383 update_body(Modes, Args0, Args1, Args2, true, Body). 384 385update_body([], _, _, _, Body, Body). 386update_body([P|TM], [A0|Args0], [A1|Args1], [A2|Args2], Body0, Body) :- 387 update_goal(P, A0,A1,A2, Goal), 388 mkconj(Body0, Goal, Body1), 389 update_body(TM, Args0, Args1, Args2, Body1, Body). 390 391update_goal(Var, _,_,_, _) :- 392 var(Var), 393 !, 394 instantiation_error(Var). 395update_goal(lattice(M:PI), S0,S1,S2, M:Goal) :- 396 !, 397 must_be(atom, M), 398 update_goal(lattice(PI), S0,S1,S2, Goal). 399update_goal(lattice(Name/Arity), S0,S1,S2, Goal) :- 400 !, 401 must_be(oneof([3]), Arity), 402 must_be(atom, Name), 403 Goal =.. [Name,S0,S1,S2]. 404update_goal(lattice(Name), S0,S1,S2, Goal) :- 405 !, 406 must_be(atom, Name), 407 update_goal(lattice(Name/3), S0,S1,S2, Goal). 408update_goal(po(Name/Arity), S0,S1,S2, Goal) :- 409 !, 410 must_be(oneof([2]), Arity), 411 must_be(atom, Name), 412 Call =.. [Name, S0, S1], 413 Goal = (Call -> S2 = S0 ; S2 = S1). 414update_goal(po(M:Name/Arity), S0,S1,S2, Goal) :- 415 !, 416 must_be(atom, M), 417 must_be(oneof([2]), Arity), 418 must_be(atom, Name), 419 Call =.. [Name, S0, S1], 420 Goal = (M:Call -> S2 = S0 ; S2 = S1). 421update_goal(po(M:Name), S0,S1,S2, Goal) :- 422 !, 423 must_be(atom, M), 424 must_be(atom, Name), 425 update_goal(po(M:Name/2), S0,S1,S2, Goal). 426update_goal(po(Name), S0,S1,S2, Goal) :- 427 !, 428 must_be(atom, Name), 429 update_goal(po(Name/2), S0,S1,S2, Goal). 430update_goal(Alias, S0,S1,S2, Goal) :- 431 update_alias(Alias, Update), 432 !, 433 update_goal(Update, S0,S1,S2, Goal). 434update_goal(Mode, _,_,_, _) :- 435 domain_error(tabled_mode, Mode). 436 437update_alias(first, lattice(tabling:first/3)). 438update_alias(-, lattice(tabling:first/3)). 439update_alias(last, lattice(tabling:last/3)). 440update_alias(min, lattice(tabling:min/3)). 441update_alias(max, lattice(tabling:max/3)). 442update_alias(sum, lattice(tabling:sum/3)). 443 444mkconj(true, G, G) :- !. 445mkconj(G1, G2, (G1,G2)). 446 447 448 /******************************* 449 * AGGREGATION * 450 *******************************/
460:- public first/3, last/3, min/3, max/3, sum/3. 461 462first(S, _, S). 463last(_, S, S). 464min(S0, S1, S) :- (S0 @< S1 -> S = S0 ; S = S1). 465max(S0, S1, S) :- (S0 @> S1 -> S = S0 ; S = S1). 466sum(S0, S1, S) :- S is S0+S1. 467 468 469 /******************************* 470 * RENAME WORKER * 471 *******************************/
478prologrename_predicate(M:Head0, M:Head) :- 479 '$flushed_predicate'(M:'$tabled'(_)), 480 call(M:'$tabled'(Head0)), 481 !, 482 rename_term(Head0, Head). 483 484rename_term(Compound0, Compound) :- 485 compound(Compound0), 486 !, 487 compound_name_arguments(Compound0, Name, Args), 488 atom_concat(Name, ' tabled', WrapName), 489 compound_name_arguments(Compound, WrapName, Args). 490rename_term(Name, WrapName) :- 491 atom_concat(Name, ' tabled', WrapName). 492 493 494systemterm_expansion((:- table(Preds)), 495 [ (:- multifile('$tabled'/1)), 496 (:- multifile('$table_mode'/3)), 497 (:- multifile('$table_update'/4)) 498 | Clauses 499 ]) :- 500 phrase(wrappers(Preds), Clauses). 501 502 503 /******************************* 504 * SANDBOX * 505 *******************************/ 506 507:- multifile 508 sandbox:safe_directive/1, 509 sandbox:safe_primitive/1, 510 sandbox:safe_meta/2.
516sandbox:safe_directive(Dir) :- 517 ground(Dir), 518 local_tabling_dir(Dir). 519 520local_tabling_dir(table(Preds)) :- 521 local_preds(Preds). 522 523local_preds((A,B)) :- 524 !, 525 local_preds(A), 526 local_preds(B). 527 528local_preds(Name/Arity) :- 529 atom(Name), integer(Arity). 530local_preds(Name//Arity) :- 531 atom(Name), integer(Arity). 532 533sandbox:safe_meta_predicate(tabling:start_tabling/2). 534 535sandbox:safe_primitive(tabling:abolish_all_tables). 536sandbox:safe_meta(tabling:abolish_table_subgoals(V), []) :- 537 \+ qualified(V). 538sandbox:safe_meta(tabling:current_table(V, _), []) :- 539 \+ qualified(V). 540 541qualified(V) :- 542 nonvar(V), 543 V = _:_
Tabled execution (SLG WAM)
This library handled tabled execution of predicates using the characteristics if the SLG WAM. The required suspension is realised using delimited continuations implemented by reset/3 and shift/1. The table space and work lists are part of the SWI-Prolog core.