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-2018, Benoit Desouter, 7 Jan Wielemaker, 8 Fabrizio Riguzzi 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module('$tabling', 38 [ (table)/1, % +PI ... 39 40 current_table/2, % :Variant, ?Table 41 abolish_all_tables/0, 42 abolish_table_subgoals/1, % :Subgoal 43 44 start_tabling/2, % +Wrapper, :Worker 45 start_tabling/4 % +Wrapper, :Worker, :Variant, ?ModeArgs 46 ]). 47 48:- meta_predicate 49 start_tabling( , ), 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 '$tbl_variant_table'(Wrapper, Trie, Status), 100 ( Status == complete 101 -> trie_gen(Trie, Wrapper, _) 102 ; ( '$tbl_create_component' 103 -> catch(run_leader(Wrapper, Worker, Trie), 104 E, true), 105 ( var(E) 106 -> trie_gen(Trie, Wrapper, _) 107 ; '$tbl_table_discard_all', 108 throw(E) 109 ) 110 ; run_follower(Status, Wrapper, Worker, Trie) 111 ) 112 ). 113 114run_leader(Wrapper, Worker, Trie) :- 115 activate(Wrapper, Worker, Trie, _Worklist), 116 completion, 117 '$tbl_completed_component'. 118 119run_follower(fresh, Wrapper, Worker, Trie) :- 120 !, 121 activate(Wrapper, Worker, Trie, Worklist), 122 shift(call_info(Wrapper, Worklist)). 123run_follower(Worklist, Wrapper, _Worker, _Trie) :- 124 shift(call_info(Wrapper, Worklist)). 125 126activate(Wrapper, Worker, Trie, WorkList) :- 127 '$tbl_new_worklist'(WorkList, Trie), 128 ( delim(Wrapper, Worker, WorkList), 129 fail 130 ; true 131 ).
139start_tabling(Wrapper, Worker, WrapperNoModes, ModeArgs) :- 140 '$tbl_variant_table'(WrapperNoModes, Trie, Status), 141 ( Status == complete 142 -> trie_gen(Trie, WrapperNoModes, ModeArgs) 143 ; ( Status == fresh 144 -> '$tbl_create_subcomponent', 145 catch(run_leader(Wrapper, WrapperNoModes, ModeArgs, Worker, Trie), 146 E, true), 147 ( var(E) 148 -> trie_gen(Trie, WrapperNoModes, ModeArgs) 149 ; '$tbl_table_discard_all', 150 throw(E) 151 ) 152 ; % = run_follower, but never fresh and Status is a worklist 153 shift(call_info(Wrapper, Status)) 154 ) 155 ). 156 157get_wrapper_no_mode_args(M:Wrapper, M:WrapperNoModes, ModeArgs) :- 158 M:'$table_mode'(Wrapper, WrapperNoModes, ModeArgs). 159 160run_leader(Wrapper, WrapperNoModes, ModeArgs, Worker, Trie) :- 161 activate(Wrapper, WrapperNoModes, ModeArgs, Worker, Trie, _Worklist), 162 completion, 163 '$tbl_completed_component'. 164 165activate(Wrapper, WrapperNoModes, _ModeArgs, Worker, Trie, WorkList) :- 166 '$tbl_new_worklist'(WorkList, Trie), 167 ( delim(Wrapper, WrapperNoModes, Worker, WorkList), 168 fail 169 ; true 170 ).
176delim(Wrapper, Worker, WorkList) :- 177 reset(work_and_add_answer(Worker, Wrapper, WorkList), 178 SourceCall, Continuation), 179 add_answer_or_suspend(Continuation, Wrapper, 180 WorkList, SourceCall). 181 182work_and_add_answer(Worker, Wrapper, WorkList) :- 183 call(Worker), 184 '$tbl_wkl_add_answer'(WorkList, Wrapper). 185 186 187add_answer_or_suspend(0, _Wrapper, _WorkList, _) :- 188 !. 189add_answer_or_suspend(Continuation, Wrapper, WorkList, 190 call_info(SrcWrapper, SourceWL)) :- 191 '$tbl_wkl_add_suspension'( 192 SourceWL, 193 dependency(SrcWrapper, Continuation, Wrapper, WorkList)).
199delim(Wrapper, WrapperNoModes, Worker, WorkList) :- 200 reset(work_and_add_moded_answer(Worker, Wrapper, WrapperNoModes, WorkList), 201 SourceCall, Continuation), 202 add_answer_or_suspend(Continuation, Wrapper, WrapperNoModes, 203 WorkList, SourceCall). 204 205work_and_add_moded_answer(Worker, Wrapper, WrapperNoModes, WorkList) :- 206 call(Worker), 207 get_wrapper_no_mode_args(Wrapper, _, ModeArgs), 208 '$tbl_wkl_mode_add_answer'(WorkList, WrapperNoModes, 209 ModeArgs, Wrapper). 210 211add_answer_or_suspend(0, _Wrapper, _WrapperNoModes, _WorkList, _) :- 212 !. 213add_answer_or_suspend(Continuation, Wrapper, _WrapperNoModes, WorkList, 214 call_info(SrcWrapper, SourceWL)) :- 215 '$tbl_wkl_add_suspension'( 216 SourceWL, 217 dependency(SrcWrapper, Continuation, Wrapper, WorkList)).
227:- public 228 update/4. 229 230update(M:Wrapper, A1, A2, A3) :- 231 M:'$table_update'(Wrapper, A1, A2, A3), 232 A1 \=@= A3.
239completion :- 240 '$tbl_pop_worklist'(WorkList), 241 !, 242 completion_step(WorkList), 243 completion. 244completion :- 245 '$tbl_table_complete_all'. 246 247completion_step(SourceTable) :- 248 ( '$tbl_trienode'(Reserved), 249 '$tbl_wkl_work'(SourceTable, 250 Answer, ModeArgs, 251 Goal, Continuation, Wrapper, TargetTable), 252 ( ModeArgs == Reserved 253 -> Goal = Answer, 254 delim(Wrapper, Continuation, TargetTable) 255 ; get_wrapper_no_mode_args(Goal, Answer, ModeArgs), 256 get_wrapper_no_mode_args(Wrapper, WrapperNoModes, _), 257 delim(Wrapper, WrapperNoModes, Continuation, TargetTable) 258 ), 259 fail 260 ; true 261 ). 262 263 /******************************* 264 * CLEANUP * 265 *******************************/
276abolish_all_tables :-
277 '$tbl_abolish_all_tables'.
283abolish_table_subgoals(M:SubGoal) :- 284 '$tbl_variant_table'(VariantTrie), 285 current_module(M), 286 forall(trie_gen(VariantTrie, M:SubGoal, Trie), 287 '$tbl_destroy_table'(Trie)). 288 289 290 /******************************* 291 * EXAMINE TABLES * 292 *******************************/
298current_table(M:Variant, Trie) :- 299 '$tbl_variant_table'(VariantTrie), 300 ( (var(Variant) ; var(M)) 301 -> trie_gen(VariantTrie, M:Variant, Trie) 302 ; trie_lookup(VariantTrie, M:Variant, Trie) 303 ). 304 305 306 /******************************* 307 * WRAPPER GENERATION * 308 *******************************/ 309 310:- multifile 311 system:term_expansion/2, 312 prolog:rename_predicate/2, 313 tabled/2. 314:- dynamic 315 system:term_expansion/2. 316 317wrappers(Var) --> 318 { var(Var), 319 !, 320 '$instantiation_error'(Var) 321 }. 322wrappers((A,B)) --> 323 !, 324 wrappers(A), 325 wrappers(B). 326wrappers(Name//Arity) --> 327 { atom(Name), integer(Arity), Arity >= 0, 328 !, 329 Arity1 is Arity+2 330 }, 331 wrappers(Name/Arity1). 332wrappers(Name/Arity) --> 333 { atom(Name), integer(Arity), Arity >= 0, 334 !, 335 functor(Head, Name, Arity), 336 check_undefined(Name/Arity), 337 atom_concat(Name, ' tabled', WrapName), 338 Head =.. [Name|Args], 339 WrappedHead =.. [WrapName|Args], 340 prolog_load_context(module, Module), 341 '$tbl_trienode'(Reserved) 342 }, 343 [ '$tabled'(Head), 344 '$table_mode'(Head, Head, Reserved), 345 ( Head :- 346 start_tabling(Module:Head, WrappedHead) 347 ) 348 ]. 349wrappers(ModeDirectedSpec) --> 350 { callable(ModeDirectedSpec), 351 !, 352 functor(ModeDirectedSpec, Name, Arity), 353 functor(Head, Name, Arity), 354 check_undefined(Name/Arity), 355 atom_concat(Name, ' tabled', WrapName), 356 Head =.. [Name|Args], 357 WrappedHead =.. [WrapName|Args], 358 extract_modes(ModeDirectedSpec, Head, Variant, Modes, Moded), 359 updater_clauses(Modes, Head, UpdateClauses), 360 prolog_load_context(module, Module), 361 mode_check(Moded, ModeTest), 362 ( ModeTest == true 363 -> WrapClause = (Head :- start_tabling(Module:Head, WrappedHead)) 364 ; WrapClause = (Head :- ModeTest, 365 start_tabling(Module:Head, WrappedHead, 366 Module:Variant, Moded)) 367 ) 368 }, 369 [ '$tabled'(Head), 370 '$table_mode'(Head, Variant, Moded), 371 WrapClause 372 | UpdateClauses 373 ]. 374wrappers(TableSpec) --> 375 { '$type_error'(table_desclaration, TableSpec) 376 }.
384check_undefined(Name/Arity) :- 385 functor(Head, Name, Arity), 386 prolog_load_context(module, Module), 387 clause(Module:, _), 388 !, 389 '$permission_error'(table, procedure, Name/Arity). 390check_undefined(_).
397mode_check(Moded, Check) :- 398 var(Moded), 399 !, 400 Check = (var(Moded)->true;'$uninstantiation_error'(Moded)). 401mode_check(Moded, true) :- 402 '$tbl_trienode'(Moded), 403 !. 404mode_check(Moded, (Test->true;'$tabling':instantiated_moded_arg(Vars))) :- 405 Moded =.. [s|Vars], 406 var_check(Vars, Test). 407 408var_check([H|T], Test) :- 409 ( T == [] 410 -> Test = var(H) 411 ; Test = (var(H),Rest), 412 var_check(T, Rest) 413 ). 414 415:- public 416 instantiated_moded_arg/1. 417 418instantiated_moded_arg(Vars) :- 419 '$member'(V, Vars), 420 \+ var(V), 421 '$uninstantiation_error'(V).
433extract_modes(ModeSpec, Head, Variant, Modes, ModedAnswer) :-
434 compound_name_arguments(ModeSpec, Name, ModeSpecArgs),
435 compound_name_arguments(Head, Name, HeadArgs),
436 separate_args(ModeSpecArgs, HeadArgs, VariantArgs, Modes, ModedArgs),
437 length(ModedArgs, Count),
438 atomic_list_concat([$,Name,$,Count], VName),
439 Variant =.. [VName|VariantArgs],
440 ( ModedArgs == []
441 -> '$tbl_trienode'(ModedAnswer)
442 ; ModedArgs = [ModedAnswer]
443 -> true
444 ; ModedAnswer =.. [s|ModedArgs]
445 ).
455separate_args([], [], [], [], []). 456separate_args([HM|TM], [H|TA], [H|TNA], Modes, TMA):- 457 indexed_mode(HM), 458 !, 459 separate_args(TM, TA, TNA, Modes, TMA). 460separate_args([M|TM], [H|TA], TNA, [M|Modes], [H|TMA]):- 461 separate_args(TM, TA, TNA, Modes, TMA). 462 463indexed_mode(Mode) :- % XSB 464 var(Mode), 465 !. 466indexed_mode(index). % YAP 467indexed_mode(+). % B
474updater_clauses([], _, []) :- !. 475updater_clauses([P], Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- !, 476 update_goal(P, S0,S1,S2, Body). 477updater_clauses(Modes, Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- 478 length(Modes, Len), 479 functor(S0, s, Len), 480 functor(S1, s, Len), 481 functor(S2, s, Len), 482 S0 =.. [_|Args0], 483 S1 =.. [_|Args1], 484 S2 =.. [_|Args2], 485 update_body(Modes, Args0, Args1, Args2, true, Body). 486 487update_body([], _, _, _, Body, Body). 488update_body([P|TM], [A0|Args0], [A1|Args1], [A2|Args2], Body0, Body) :- 489 update_goal(P, A0,A1,A2, Goal), 490 mkconj(Body0, Goal, Body1), 491 update_body(TM, Args0, Args1, Args2, Body1, Body). 492 493update_goal(Var, _,_,_, _) :- 494 var(Var), 495 !, 496 '$instantiation_error'(Var). 497update_goal(lattice(M:PI), S0,S1,S2, M:Goal) :- 498 !, 499 '$must_be'(atom, M), 500 update_goal(lattice(PI), S0,S1,S2, Goal). 501update_goal(lattice(Name/Arity), S0,S1,S2, Goal) :- 502 !, 503 '$must_be'(oneof(integer, lattice_arity, [3]), Arity), 504 '$must_be'(atom, Name), 505 Goal =.. [Name,S0,S1,S2]. 506update_goal(lattice(Name), S0,S1,S2, Goal) :- 507 !, 508 '$must_be'(atom, Name), 509 update_goal(lattice(Name/3), S0,S1,S2, Goal). 510update_goal(po(Name/Arity), S0,S1,S2, Goal) :- 511 !, 512 '$must_be'(oneof(integer, po_arity, [2]), Arity), 513 '$must_be'(atom, Name), 514 Call =.. [Name, S0, S1], 515 Goal = (Call -> S2 = S0 ; S2 = S1). 516update_goal(po(M:Name/Arity), S0,S1,S2, Goal) :- 517 !, 518 '$must_be'(atom, M), 519 '$must_be'(oneof(integer, po_arity, [2]), Arity), 520 '$must_be'(atom, Name), 521 Call =.. [Name, S0, S1], 522 Goal = (M:Call -> S2 = S0 ; S2 = S1). 523update_goal(po(M:Name), S0,S1,S2, Goal) :- 524 !, 525 '$must_be'(atom, M), 526 '$must_be'(atom, Name), 527 update_goal(po(M:Name/2), S0,S1,S2, Goal). 528update_goal(po(Name), S0,S1,S2, Goal) :- 529 !, 530 '$must_be'(atom, Name), 531 update_goal(po(Name/2), S0,S1,S2, Goal). 532update_goal(Alias, S0,S1,S2, Goal) :- 533 update_alias(Alias, Update), 534 !, 535 update_goal(Update, S0,S1,S2, Goal). 536update_goal(Mode, _,_,_, _) :- 537 '$domain_error'(tabled_mode, Mode). 538 539update_alias(first, lattice('$tabling':first/3)). 540update_alias(-, lattice('$tabling':first/3)). 541update_alias(last, lattice('$tabling':last/3)). 542update_alias(min, lattice('$tabling':min/3)). 543update_alias(max, lattice('$tabling':max/3)). 544update_alias(sum, lattice('$tabling':sum/3)). 545 546mkconj(true, G, G) :- !. 547mkconj(G1, G2, (G1,G2)). 548 549 550 /******************************* 551 * AGGREGATION * 552 *******************************/
562:- public first/3, last/3, min/3, max/3, sum/3. 563 564first(S, _, S). 565last(_, S, S). 566min(S0, S1, S) :- (S0 @< S1 -> S = S0 ; S = S1). 567max(S0, S1, S) :- (S0 @> S1 -> S = S0 ; S = S1). 568sum(S0, S1, S) :- S is S0+S1. 569 570 571 /******************************* 572 * RENAME WORKER * 573 *******************************/
580prologrename_predicate(M:Head0, M:Head) :- 581 '$flushed_predicate'(M:'$tabled'(_)), 582 call(M:'$tabled'(Head0)), 583 \+ current_prolog_flag(xref, true), 584 !, 585 rename_term(Head0, Head). 586 587rename_term(Compound0, Compound) :- 588 compound(Compound0), 589 !, 590 compound_name_arguments(Compound0, Name, Args), 591 atom_concat(Name, ' tabled', WrapName), 592 compound_name_arguments(Compound, WrapName, Args). 593rename_term(Name, WrapName) :- 594 atom_concat(Name, ' tabled', WrapName). 595 596 597systemterm_expansion((:- table(Preds)), 598 [ (:- multifile('$tabled'/1)), 599 (:- multifile('$table_mode'/3)), 600 (:- multifile('$table_update'/4)) 601 | Clauses 602 ]) :- 603 \+ current_prolog_flag(xref, true), 604 phrase(wrappers(Preds), Clauses)
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.