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-2025, Benoit Desouter, 7 Jan Wielemaker, 8 Fabrizio Riguzzi 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module('$tabling', 39 [ (table)/1, % :PI ... 40 untable/1, % :PI ... 41 42 (tnot)/1, % :Goal 43 not_exists/1, % :Goal 44 undefined/0, 45 answer_count_restraint/0, 46 radial_restraint/0, 47 48 current_table/2, % :Variant, ?Table 49 abolish_all_tables/0, 50 abolish_private_tables/0, 51 abolish_shared_tables/0, 52 abolish_table_subgoals/1, % :Subgoal 53 abolish_module_tables/1, % +Module 54 abolish_nonincremental_tables/0, 55 abolish_nonincremental_tables/1, % +Options 56 abolish_monotonic_tables/0, 57 58 start_tabling/3, % +Closure, +Wrapper, :Worker 59 start_subsumptive_tabling/3,% +Closure, +Wrapper, :Worker 60 start_abstract_tabling/3, % +Closure, +Wrapper, :Worker 61 start_moded_tabling/5, % +Closure, +Wrapper, :Worker, 62 % :Variant, ?ModeArgs 63 64 '$tbl_answer'/4, % +Trie, -Return, -ModeArgs, -Delay 65 66 '$wrap_tabled'/2, % :Head, +Mode 67 '$moded_wrap_tabled'/5, % :Head, +Opts, +ModeTest, +Varnt, +Moded 68 '$wfs_call'/2, % :Goal, -Delays 69 70 '$set_table_wrappers'/1, % :Head 71 '$start_monotonic'/2 % :Head, :Wrapped 72 ]). 73 74:- meta_predicate 75 table(), 76 untable(), 77 tnot(), 78 not_exists(), 79 tabled_call(), 80 start_tabling(, , ), 81 start_abstract_tabling(, , ), 82 start_moded_tabling(, , , , ), 83 current_table(, ), 84 abolish_table_subgoals(), 85 '$wfs_call'(, ).
97% Enable debugging using debug(tabling(Topic)) when compiled with 98% -DO_DEBUG 99goal_expansion(tdebug(Topic, Fmt, Args), Expansion) :- 100 ( current_prolog_flag(prolog_debug, true) 101 -> Expansion = debug(tabling(Topic), Fmt, Args) 102 ; Expansion = true 103 ). 104goal_expansion(tdebug(Goal), Expansion) :- 105 ( current_prolog_flag(prolog_debug, true) 106 -> Expansion = ( debugging(tabling(_)) 107 -> ( Goal 108 -> true 109 ; print_message(error, 110 format('goal_failed: ~q', [Goal])) 111 ) 112 ; true 113 ) 114 ; Expansion = true 115 ). 116 117:- if(current_prolog_flag(prolog_debug, true)). 118:- set_prolog_flag(optimise_debug, false). 119:- autoload(library(debug), [debug/3, debugging/1]). 120 121wl_goal(tnot(WorkList), ~(Goal), Skeleton) :- 122 !, 123 '$tbl_wkl_table'(WorkList, ATrie), 124 trie_goal(ATrie, Goal, Skeleton). 125wl_goal(WorkList, Goal, Skeleton) :- 126 '$tbl_wkl_table'(WorkList, ATrie), 127 trie_goal(ATrie, Goal, Skeleton). 128 129trie_goal(ATrie, Goal, Skeleton) :- 130 '$tbl_table_status'(ATrie, _Status, M:Variant, Skeleton), 131 ( M:'$table_mode'(Goal0, Variant, _Moded) 132 -> true 133 ; Goal0 = Variant % dynamic IDG nodes 134 ), 135 unqualify_goal(M:Goal0, user, Goal). 136 137delay_goals(List, Goal) :- 138 delay_goals(List, user, Goal). 139 140user_goal(Goal, UGoal) :- 141 unqualify_goal(Goal, user, UGoal). 142 143:- multifile 144 prolog:portray/1. 145 146user:portray(ATrie) :- 147 '$is_answer_trie'(ATrie, _), 148 trie_goal(ATrie, Goal, _Skeleton), 149 ( '$idg_falsecount'(ATrie, FalseCount) 150 -> ( '$idg_forced'(ATrie) 151 -> format('~q [fc=~d/F] for ~p', [ATrie, FalseCount, Goal]) 152 ; format('~q [fc=~d] for ~p', [ATrie, FalseCount, Goal]) 153 ) 154 ; format('~q for ~p', [ATrie, Goal]) 155 ). 156user:portray(Cont) :- 157 compound(Cont), 158 compound_name_arguments(Cont, '$cont$', [_Context, Clause, PC | Args]), 159 clause_property(Clause, file(File)), 160 file_base_name(File, Base), 161 clause_property(Clause, line_count(Line)), 162 clause_property(Clause, predicate(PI)), 163 format('~q at ~w:~d @PC=~w, ~p', [PI, Base, Line, PC, Args]). 164 165:- endif.
:- 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.
190table(M:PIList) :- 191 setup_call_cleanup( 192 '$set_source_module'(OldModule, M), 193 expand_term((:- table(PIList)), Clauses), 194 '$set_source_module'(OldModule)), 195 dyn_tabling_list(Clauses, M). 196 197dyn_tabling_list([], _). 198dyn_tabling_list([H|T], M) :- 199 dyn_tabling(H, M), 200 dyn_tabling_list(T, M). 201 202dyn_tabling(M:Clause, _) :- 203 !, 204 dyn_tabling(Clause, M). 205dyn_tabling((:- multifile(PI)), M) :- 206 !, 207 multifile(M:PI), 208 dynamic(M:PI). 209dyn_tabling(:- initialization(Wrap, now), M) :- 210 !, 211 M:Wrap. 212dyn_tabling('$tabled'(Head, TMode), M) :- 213 ( clause(M:'$tabled'(Head, OMode), true, Ref), 214 ( OMode \== TMode 215 -> erase(Ref), 216 fail 217 ; true 218 ) 219 -> true 220 ; assertz(M:'$tabled'(Head, TMode)) 221 ). 222dyn_tabling('$table_mode'(Head, Variant, Moded), M) :- 223 ( clause(M:'$table_mode'(Head, Variant0, Moded0), true, Ref) 224 -> ( t(Head, Variant, Moded) =@= t(Head, Variant0, Moded0) 225 -> true 226 ; erase(Ref), 227 assertz(M:'$table_mode'(Head, Variant, Moded)) 228 ) 229 ; assertz(M:'$table_mode'(Head, Variant, Moded)) 230 ). 231dyn_tabling(('$table_update'(Head, S0, S1, S2) :- Body), M) :- 232 ( clause(M:'$table_update'(Head, S00, S10, S20), Body0, Ref) 233 -> ( t(Head, S0, S1, S2, Body) =@= t(Head, S00, S10, S20, Body0) 234 -> true 235 ; erase(Ref), 236 assertz(M:('$table_update'(Head, S0, S1, S2) :- Body)) 237 ) 238 ; assertz(M:('$table_update'(Head, S0, S1, S2) :- Body)) 239 ).
250untable(M:PIList) :- 251 untable(PIList, M). 252 253untable(Var, _) :- 254 var(Var), 255 !, 256 '$instantiation_error'(Var). 257untable(M:Spec, _) :- 258 !, 259 '$must_be'(atom, M), 260 untable(Spec, M). 261untable((A,B), M) :- 262 !, 263 untable(A, M), 264 untable(B, M). 265untable(Name//Arity, M) :- 266 atom(Name), integer(Arity), Arity >= 0, 267 !, 268 Arity1 is Arity+2, 269 untable(Name/Arity1, M). 270untable(Name/Arity, M) :- 271 !, 272 functor(Head, Name, Arity), 273 ( '$get_predicate_attribute'(M:Head, tabled, 1) 274 -> abolish_table_subgoals(M:Head), 275 dynamic(M:'$tabled'/2), 276 dynamic(M:'$table_mode'/3), 277 retractall(M:'$tabled'(Head, _TMode)), 278 retractall(M:'$table_mode'(Head, _Variant, _Moded)), 279 unwrap_predicate(M:Name/Arity, table), 280 '$set_predicate_attribute'(M:Head, tabled, false), 281 '$set_predicate_attribute'(M:Head, opaque, false), 282 '$set_predicate_attribute'(M:Head, incremental, false), 283 '$set_predicate_attribute'(M:Head, monotonic, false), 284 '$set_predicate_attribute'(M:Head, lazy, false) 285 ; true 286 ). 287untable(Head, M) :- 288 callable(Head), 289 !, 290 functor(Head, Name, Arity), 291 untable(Name/Arity, M). 292untable(TableSpec, _) :- 293 '$type_error'(table_declaration, TableSpec). 294 295untable_reconsult(PI) :- 296 print_message(informational, untable(PI)), 297 untable(PI). 298 299:- initialization 300 prolog_listen(untable, untable_reconsult). 301 302 303'$wrap_tabled'(Head, Options) :- 304 get_dict(mode, Options, subsumptive), 305 !, 306 set_pattributes(Head, Options), 307 '$wrap_predicate'(Head, table, Closure, Wrapped, 308 start_subsumptive_tabling(Closure, Head, Wrapped)). 309'$wrap_tabled'(Head, Options) :- 310 get_dict(subgoal_abstract, Options, _Abstract), 311 !, 312 set_pattributes(Head, Options), 313 '$wrap_predicate'(Head, table, Closure, Wrapped, 314 start_abstract_tabling(Closure, Head, Wrapped)). 315'$wrap_tabled'(Head, Options) :- 316 !, 317 set_pattributes(Head, Options), 318 '$wrap_predicate'(Head, table, Closure, Wrapped, 319 start_tabling(Closure, Head, Wrapped)).
:- table Head as (Attr1,...) directive.326set_pattributes(Head, Options) :- 327 '$set_predicate_attribute'(Head, tabled, true), 328 ( tabled_attribute(Attr), 329 get_dict(Attr, Options, Value), 330 '$set_predicate_attribute'(Head, Attr, Value), 331 fail 332 ; current_prolog_flag(table_monotonic, lazy), 333 '$set_predicate_attribute'(Head, lazy, true), 334 fail 335 ; true 336 ). 337 338tabled_attribute(incremental). 339tabled_attribute(dynamic). 340tabled_attribute(tshared). 341tabled_attribute(max_answers). 342tabled_attribute(subgoal_abstract). 343tabled_attribute(answer_abstract). 344tabled_attribute(monotonic). 345tabled_attribute(opaque). 346tabled_attribute(lazy).
362start_tabling(Closure, Wrapper, Worker) :- 363 '$tbl_variant_table'(Closure, Wrapper, Trie, Status, Skeleton, IsMono), 364 ( IsMono == true 365 -> shift(dependency(Skeleton, Trie, Mono)), 366 ( Mono == true 367 -> tdebug(monotonic, 'Monotonic new answer: ~p', [Skeleton]) 368 ; start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) 369 ) 370 ; start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) 371 ). 372 373start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) :- 374 tdebug(deadlock, 'Got table ~p, status ~p', [Trie, Status]), 375 ( Status == complete 376 -> trie_gen_compiled(Trie, Skeleton) 377 ; functor(Status, fresh, 2) 378 -> catch(create_table(Trie, Status, Skeleton, Wrapper, Worker), 379 deadlock, 380 restart_tabling(Closure, Wrapper, Worker)) 381 ; Status == invalid 382 -> reeval(Trie, Wrapper, Skeleton) 383 ; % = run_follower, but never fresh and Status is a worklist 384 shift_for_copy(call_info(Skeleton, Status)) 385 ). 386 387create_table(Trie, Fresh, Skeleton, Wrapper, Worker) :- 388 tdebug(Fresh = fresh(SCC, WorkList)), 389 tdebug(wl_goal(WorkList, Goal, _)), 390 tdebug(schedule, 'Created component ~d for ~p', [SCC, Goal]), 391 setup_call_catcher_cleanup( 392 '$idg_set_current'(OldCurrent, Trie), 393 run_leader(Skeleton, Worker, Fresh, LStatus, Clause), 394 Catcher, 395 finished_leader(OldCurrent, Catcher, Fresh, Wrapper)), 396 tdebug(schedule, 'Leader ~p done, status = ~p', [Goal, LStatus]), 397 done_leader(LStatus, Fresh, Skeleton, Clause).
407restart_tabling(Closure, Wrapper, Worker) :- 408 tdebug(user_goal(Wrapper, Goal)), 409 tdebug(deadlock, 'Deadlock running ~p; retrying', [Goal]), 410 sleep(0.000001), 411 start_tabling(Closure, Wrapper, Worker). 412 413restart_abstract_tabling(Closure, Wrapper, Worker) :- 414 tdebug(user_goal(Wrapper, Goal)), 415 tdebug(deadlock, 'Deadlock running ~p; retrying', [Goal]), 416 sleep(0.000001), 417 start_abstract_tabling(Closure, Wrapper, Worker).
answer(s).
429start_subsumptive_tabling(Closure, Wrapper, Worker) :-
430 ( '$tbl_existing_variant_table'(Closure, Wrapper, Trie, Status, Skeleton)
431 -> '$idg_add_edge'(Trie),
432 mon_consumer(Wrapper, Trie, Skeleton,
433 start_tabling_2(Closure, Wrapper, Worker,
434 Trie, Status, Skeleton))
435 ; more_general_table(Wrapper, ATrie),
436 '$tbl_table_status'(ATrie, complete, Wrapper, Skeleton)
437 -> '$idg_add_edge'(ATrie),
438 mon_consumer(Wrapper, ATrie, Skeleton,
439 '$tbl_answer_update_dl'(ATrie, Skeleton)) % see (*)
440 ; more_general_table(Wrapper, ATrie),
441 '$tbl_table_status'(ATrie, Status, GenWrapper, GenSkeleton)
442 -> ( Status == invalid
443 -> forall(reeval(ATrie, GenWrapper, GenSkeleton), true),
444 start_subsumptive_tabling(Closure, Wrapper, Worker)
445 ; wrapper_skeleton(GenWrapper, GenSkeleton, Wrapper, Skeleton),
446 mon_consumer(Wrapper, ATrie, Skeleton,
447 ( shift_for_copy(call_info(GenSkeleton, Skeleton, Status)),
448 unify_subsumptive(Skeleton, GenSkeleton)
449 ))
450 )
451 ; start_tabling(Closure, Wrapper, Worker)
452 ).459wrapper_skeleton(GenWrapper, GenSkeleton, Wrapper, Skeleton) :- 460 copy_term(GenWrapper+GenSkeleton, Wrapper+Skeleton), 461 tdebug(call_subsumption, 'GenSkeleton+Skeleton = ~p', 462 [GenSkeleton+Skeleton]). 463 464unify_subsumptive(X,X).
478mon_consumer(Wrapper, Trie, Skeleton, Goal) :- 479 '$get_predicate_attribute'(Wrapper, monotonic, 1), 480 '$tbl_collect_mono_dep', 481 !, 482 shift(dependency(Skeleton, Trie, Mono)), 483 ( Mono == true 484 -> tdebug(monotonic, 'Monotonic new answer: ~p', [Skeleton]) 485 ; call(Goal) 486 ). 487mon_consumer(_, _, _, Goal) :- 488 call(Goal).
table p/1 as subgoal_abstract(N). This is a merge
between variant and subsumptive tabling. If the goal is not
abstracted this is simple variant tabling. If the goal is abstracted
we must solve the more general goal and use answers from the
abstract table.
Wrapper is e.g., user:p(s(s(s(X))),Y)
Worker is e.g., call(<closure>(p/2)(s(s(s(X))),Y))
501start_abstract_tabling(Closure, Wrapper, Worker) :- 502 '$tbl_abstract_table'(Closure, Wrapper, Trie, _Abstract, Status, Skeleton), 503 tdebug(abstract, 'Wrapper=~p, Worker=~p, Skel=~p', 504 [Wrapper, Worker, Skeleton]), 505 mon_consumer(Wrapper, Trie, Skeleton, 506 start_abstract_tabling_2(Closure, Wrapper, Worker, 507 Trie, Status, Skeleton)). 508 509start_abstract_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) :- 510 ( is_most_general_term(Skeleton) % TBD: Fill and test Abstract 511 -> start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) 512 ; Status == complete 513 -> '$tbl_answer_update_dl'(Trie, Skeleton) 514 ; functor(Status, fresh, 2) 515 -> '$tbl_table_status'(Trie, _, GenWrapper, GenSkeleton), 516 abstract_worker(Worker, GenWrapper, GenWorker), 517 catch(create_abstract_table(Trie, Status, Skeleton, GenSkeleton, GenWrapper, 518 GenWorker), 519 deadlock, 520 restart_abstract_tabling(Closure, Wrapper, Worker)) 521 ; Status == invalid 522 -> '$tbl_table_status'(Trie, _, GenWrapper, GenSkeleton), 523 forall(reeval(Trie, GenWrapper, GenSkeleton), true), 524 '$tbl_answer_update_dl'(Trie, Skeleton) 525 ; shift_for_copy(call_info(GenSkeleton, Skeleton, Status)), 526 unify_subsumptive(Skeleton, GenSkeleton) 527 ). 528 529create_abstract_table(Trie, Fresh, Skeleton, GenSkeleton, Wrapper, Worker) :- 530 tdebug(Fresh = fresh(SCC, WorkList)), 531 tdebug(wl_goal(WorkList, Goal, _)), 532 tdebug(schedule, 'Created component ~d for ~p', [SCC, Goal]), 533 setup_call_catcher_cleanup( 534 '$idg_set_current'(OldCurrent, Trie), 535 run_leader(GenSkeleton, Worker, Fresh, LStatus, _Clause), 536 Catcher, 537 finished_leader(OldCurrent, Catcher, Fresh, Wrapper)), 538 tdebug(schedule, 'Leader ~p done, status = ~p', [Goal, LStatus]), 539 Skeleton = GenSkeleton, 540 done_abstract_leader(LStatus, Fresh, GenSkeleton, Trie). 541 542abstract_worker(_:call(Term), _M:GenWrapper, call(GenTerm)) :- 543 functor(Term, Closure, _), 544 GenWrapper =.. [_|Args], 545 GenTerm =.. [Closure|Args]. 546 547:- '$hide'((done_abstract_leader/4)). 548 549done_abstract_leader(complete, _Fresh, Skeleton, Trie) :- 550 !, 551 '$tbl_answer_update_dl'(Trie, Skeleton). 552done_abstract_leader(final, fresh(SCC, _Worklist), Skeleton, Trie) :- 553 !, 554 '$tbl_free_component'(SCC), 555 '$tbl_answer_update_dl'(Trie, Skeleton). 556done_abstract_leader(_,_,_,_).
565:- '$hide'((done_leader/4, finished_leader/4)). 566 567done_leader(complete, _Fresh, Skeleton, Clause) :- 568 !, 569 trie_gen_compiled(Clause, Skeleton). 570done_leader(final, fresh(SCC, _Worklist), Skeleton, Clause) :- 571 !, 572 '$tbl_free_component'(SCC), 573 trie_gen_compiled(Clause, Skeleton). 574done_leader(_,_,_,_). 575 576finished_leader(OldCurrent, Catcher, Fresh, Wrapper) :- 577 '$idg_set_current'(OldCurrent), 578 ( Catcher == exit 579 -> true 580 ; Catcher == fail 581 -> true 582 ; Catcher = exception(_) 583 -> Fresh = fresh(SCC, _), 584 '$tbl_table_discard_all'(SCC) 585 ; print_message(error, tabling(unexpected_result(Wrapper, Catcher))) 586 ).
complete, in which case local
completion finished or merged if running the completion finds an
open (not completed) active goal that resides in a parent component.
In this case, this SCC has been merged with this parent.
If the SCC is merged, the answers it already gathered are added to the worklist and we shift (suspend), turning our leader into an internal node for the upper SCC.
601run_leader(Skeleton, Worker, fresh(SCC, Worklist), Status, Clause) :- 602 tdebug(wl_goal(Worklist, Goal, Skeleton)), 603 tdebug(schedule, '-> Activate component ~p for ~p', [SCC, Goal]), 604 activate(Skeleton, Worker, Worklist), 605 tdebug(schedule, '-> Complete component ~p for ~p', [SCC, Goal]), 606 completion(SCC, Status, Clause), 607 tdebug(schedule, '-> Completed component ~p for ~p: ~p', [SCC, Goal, Status]), 608 ( Status == merged 609 -> tdebug(merge, 'Turning leader ~p into follower', [Goal]), 610 '$tbl_wkl_make_follower'(Worklist), 611 shift_for_copy(call_info(Skeleton, Worklist)) 612 ; true % completed 613 ). 614 615activate(Skeleton, Worker, WorkList) :- 616 tdebug(activate, '~p: created wl=~p', [Skeleton, WorkList]), 617 ( reset_delays, 618 delim(Skeleton, Worker, WorkList, []), 619 fail 620 ; true 621 ).
637delim(Skeleton, Worker, WorkList, Delays) :-
638 reset(Worker, SourceCall, Continuation),
639 tdebug(wl_goal(WorkList, Goal, _)),
640 ( Continuation == 0
641 -> tdebug('$tbl_add_global_delays'(Delays, AllDelays)),
642 tdebug(delay_goals(AllDelays, Cond)),
643 tdebug(answer, 'New answer ~p for ~p (delays = ~p)',
644 [Skeleton, Goal, Cond]),
645 '$tbl_wkl_add_answer'(WorkList, Skeleton, Delays, Complete),
646 Complete == !,
647 !
648 ; SourceCall = call_info(SrcSkeleton, SourceWL)
649 -> '$tbl_add_global_delays'(Delays, AllDelays),
650 tdebug(wl_goal(SourceWL, SrcGoal, _)),
651 tdebug(wl_goal(WorkList, DstGoal, _)),
652 tdebug(schedule, 'Suspended ~p, for solving ~p', [SrcGoal, DstGoal]),
653 '$tbl_wkl_add_suspension'(
654 SourceWL,
655 dependency(SrcSkeleton, Continuation, Skeleton, WorkList, AllDelays))
656 ; SourceCall = call_info(SrcSkeleton, InstSkeleton, SourceWL)
657 -> '$tbl_add_global_delays'(Delays, AllDelays),
658 tdebug(wl_goal(SourceWL, SrcGoal, _)),
659 tdebug(wl_goal(WorkList, DstGoal, _)),
660 tdebug(schedule, 'Suspended ~p, for solving ~p', [SrcGoal, DstGoal]),
661 '$tbl_wkl_add_suspension'(
662 SourceWL,
663 InstSkeleton,
664 dependency(SrcSkeleton, Continuation, Skeleton, WorkList, AllDelays))
665 ; '$tbl_wkl_table'(WorkList, ATrie),
666 mon_assert_dep(SourceCall, Continuation, Skeleton, ATrie)
667 -> delim(Skeleton, Continuation, WorkList, Delays)
668 ).675'$moded_wrap_tabled'(Head, Options, ModeTest, WrapperNoModes, ModeArgs) :- 676 set_pattributes(Head, Options), 677 '$wrap_predicate'(Head, table, Closure, Wrapped, 678 ( ModeTest, 679 start_moded_tabling(Closure, Head, Wrapped, 680 WrapperNoModes, ModeArgs) 681 )). 682 683 684start_moded_tabling(Closure, Wrapper, Worker, WrapperNoModes, ModeArgs) :- 685 '$tbl_moded_variant_table'(Closure, WrapperNoModes, Trie, 686 Status, Skeleton, IsMono), 687 ( IsMono == true 688 -> shift(dependency(Skeleton/ModeArgs, Trie, Mono)), 689 ( Mono == true 690 -> tdebug(monotonic, 'Monotonic new answer: ~p', [Skeleton]) 691 ; start_moded_tabling_2(Closure, Wrapper, Worker, ModeArgs, 692 Trie, Status, Skeleton) 693 ) 694 ; start_moded_tabling_2(Closure, Wrapper, Worker, ModeArgs, 695 Trie, Status, Skeleton) 696 ). 697 698start_moded_tabling_2(_Closure, Wrapper, Worker, ModeArgs, 699 Trie, Status, Skeleton) :- 700 ( Status == complete 701 -> moded_gen_answer(Trie, Skeleton, ModeArgs) 702 ; functor(Status, fresh, 2) 703 -> setup_call_catcher_cleanup( 704 '$idg_set_current'(OldCurrent, Trie), 705 moded_run_leader(Wrapper, Skeleton/ModeArgs, 706 Worker, Status, LStatus), 707 Catcher, 708 finished_leader(OldCurrent, Catcher, Status, Wrapper)), 709 tdebug(schedule, 'Leader ~p done, modeargs = ~p, status = ~p', 710 [Wrapper, ModeArgs, LStatus]), 711 moded_done_leader(LStatus, Status, Skeleton, ModeArgs, Trie) 712 ; Status == invalid 713 -> reeval(Trie, Wrapper, Skeleton), 714 moded_gen_answer(Trie, Skeleton, ModeArgs) 715 ; % = run_follower, but never fresh and Status is a worklist 716 shift_for_copy(call_info(Skeleton/ModeArgs, Status)) 717 ). 718 719:- public 720 moded_gen_answer/3. % XSB tables.pl 721 722moded_gen_answer(Trie, Skeleton, ModedArgs) :- 723 trie_gen(Trie, Skeleton), 724 '$tbl_answer_update_dl'(Trie, Skeleton, ModedArgs). 725 726'$tbl_answer'(ATrie, Skeleton, ModedArgs, Delay) :- 727 trie_gen(ATrie, Skeleton), 728 '$tbl_answer_c'(ATrie, Skeleton, ModedArgs, Delay). 729 730moded_done_leader(complete, _Fresh, Skeleton, ModeArgs, Trie) :- 731 !, 732 moded_gen_answer(Trie, Skeleton, ModeArgs). 733moded_done_leader(final, fresh(SCC, _WorkList), Skeleton, ModeArgs, Trie) :- 734 !, 735 '$tbl_free_component'(SCC), 736 moded_gen_answer(Trie, Skeleton, ModeArgs). 737moded_done_leader(_, _, _, _, _). 738 739moded_run_leader(Wrapper, SkeletonMA, Worker, fresh(SCC, Worklist), Status) :- 740 tdebug(wl_goal(Worklist, Goal, _)), 741 tdebug(schedule, '-> Activate component ~p for ~p', [SCC, Goal]), 742 moded_activate(SkeletonMA, Worker, Worklist), 743 tdebug(schedule, '-> Complete component ~p for ~p', [SCC, Goal]), 744 completion(SCC, Status, _Clause), % TBD: propagate 745 tdebug(schedule, '-> Completed component ~p for ~p: ~p', [SCC, Goal, Status]), 746 ( Status == merged 747 -> tdebug(merge, 'Turning leader ~p into follower', [Wrapper]), 748 '$tbl_wkl_make_follower'(Worklist), 749 shift_for_copy(call_info(SkeletonMA, Worklist)) 750 ; true % completed 751 ). 752 753moded_activate(SkeletonMA, Worker, WorkList) :- 754 ( reset_delays, 755 delim(SkeletonMA, Worker, WorkList, []), 756 fail 757 ; true 758 ).
true, A1 should be deleted.
776:- public 777 update/7. 778 779% both unconditional 780update(0b11, Wrapper, M, Agg, New, Next, delete) :- 781 !, 782 M:'$table_update'(Wrapper, Agg, New, Next), 783 Agg \=@= Next. 784% old unconditional, new conditional 785update(0b10, Wrapper, M, Agg, New, Next, keep) :- 786 !, 787 M:'$table_update'(Wrapper, Agg, New, Next0), 788 ( Next0 =@= Agg 789 -> Next = Agg 790 ; Next = Next0 791 ). 792% old conditional, new unconditional, 793update(0b01, Wrapper, M, Agg, New, Next, keep) :- 794 !, 795 M:'$table_update'(Wrapper, New, Agg, Next0), 796 ( Next0 =@= Agg 797 -> Next = Agg 798 ; Next = Next0 799 ). 800% both conditional 801update(0b00, _Wrapper, _M, _Agg, New, New, keep) :- 802 !.
merged, completed or final. If Status is not merged,
Clause is a compiled representation for the answer trie of the
Component leader.811completion(SCC, Status, Clause) :- 812 ( reset_delays, 813 completion_(SCC), 814 fail 815 ; '$tbl_table_complete_all'(SCC, Status, Clause), 816 tdebug(schedule, 'SCC ~p: ~p', [scc(SCC), Status]) 817 ). 818 819completion_(SCC) :- 820 repeat, 821 ( '$tbl_pop_worklist'(SCC, WorkList) 822 -> tdebug(wl_goal(WorkList, Goal, _)), 823 tdebug(schedule, 'Complete ~p in ~p', [Goal, scc(SCC)]), 824 completion_step(WorkList) 825 ; ! 826 ).
The suspension added by '$tbl_wkl_add_suspension'/2 is a term
dependency(SrcWrapper, Continuation, Wrapper, WorkList, Delays).
Note that:
857completion_step(SourceWL) :- 858 '$tbl_wkl_work'(SourceWL, 859 Answer, Continuation, TargetSkeleton, TargetWL, Delays), 860 tdebug(wl_goal(SourceWL, SourceGoal, _)), 861 tdebug(wl_goal(TargetWL, TargetGoal, _Skeleton)), 862 tdebug('$tbl_add_global_delays'(Delays, AllDelays)), 863 tdebug(delay_goals(AllDelays, Cond)), 864 tdebug(schedule, 'Resuming ~p, calling ~p with ~p (delays = ~p)', 865 [TargetGoal, SourceGoal, Answer, Cond]), 866 delim(TargetSkeleton, Continuation, TargetWL, Delays), 867 fail. 868 869 870 /******************************* 871 * STRATIFIED NEGATION * 872 *******************************/
(*): Only variant tabling is allowed under tnot/1.
880tnot(Goal0) :- 881 '$tnot_implementation'(Goal0, Goal), % verifies Goal is tabled 882 ( '$tbl_existing_variant_table'(_, Goal, Trie, Status, Skeleton), 883 Status \== invalid 884 -> '$idg_add_edge'(Trie), 885 ( '$tbl_answer_dl'(Trie, _, true) 886 -> fail 887 ; '$tbl_answer_dl'(Trie, _, _) 888 -> tdebug(tnot, 'tnot: adding ~p to delay list', [Goal]), 889 add_delay(Trie) 890 ; Status == complete 891 -> true 892 ; negation_suspend(Goal, Skeleton, Status) 893 ) 894 ; tdebug(tnot, 'tnot: ~p: fresh', [Goal]), 895 ( '$wrapped_implementation'(Goal, table, Implementation), % see (*) 896 functor(Implementation, Closure, _), 897 start_tabling(Closure, Goal, Implementation), 898 fail 899 ; '$tbl_existing_variant_table'(_, Goal, Trie, NewStatus, NewSkeleton), 900 tdebug(tnot, 'tnot: fresh ~p now ~p', [Goal, NewStatus]), 901 ( '$tbl_answer_dl'(Trie, _, true) 902 -> fail 903 ; '$tbl_answer_dl'(Trie, _, _) 904 -> add_delay(Trie) 905 ; NewStatus == complete 906 -> true 907 ; negation_suspend(Goal, NewSkeleton, NewStatus) 908 ) 909 ) 910 ). 911 912floundering(Goal) :- 913 format(string(Comment), 'Floundering goal in tnot/1: ~p', [Goal]), 914 throw(error(instantiation_error, context(_Stack, Comment))).
The completion step will resume negative worklists that have no solutions, causing this to succeed.
925negation_suspend(Wrapper, Skeleton, Worklist) :-
926 tdebug(tnot, 'negation_suspend ~p (wl=~p)', [Wrapper, Worklist]),
927 '$tbl_wkl_negative'(Worklist),
928 shift_for_copy(call_info(Skeleton, tnot(Worklist))),
929 tdebug(tnot, 'negation resume ~p (wl=~p)', [Wrapper, Worklist]),
930 '$tbl_wkl_is_false'(Worklist).939not_exists(Goal) :- 940 ground(Goal), 941 '$get_predicate_attribute'(Goal, tabled, 1), 942 !, 943 tnot(Goal). 944not_exists(Goal) :- 945 ( tabled_call(Goal), fail 946 ; tnot(tabled_call(Goal)) 947 ). 948 949 /******************************* 950 * DELAY LISTS * 951 *******************************/ 952 953add_delay(Delay) :- 954 '$tbl_delay_list'(DL0), 955 '$tbl_set_delay_list'([Delay|DL0]). 956 957reset_delays :- 958 '$tbl_set_delay_list'([]).
966'$wfs_call'(Goal, M:Delays) :- 967 '$tbl_delay_list'(DL0), 968 reset_delays, 969 call(Goal), 970 '$tbl_delay_list'(DL1), 971 ( delay_goals(DL1, M, Delays) 972 -> true 973 ; Delays = undefined 974 ), 975 '$append'(DL0, DL1, DL), 976 '$tbl_set_delay_list'(DL). 977 978delay_goals([], _, true) :- 979 !. 980delay_goals([AT+AN|T], M, Goal) :- 981 !, 982 ( integer(AN) 983 -> at_delay_goal(AT, M, G0, Answer, Moded), 984 ( '$tbl_is_trienode'(Moded) 985 -> trie_term(AN, Answer) 986 ; true % TBD: Generated moded answer 987 ) 988 ; AN = Skeleton/ModeArgs 989 -> '$tbl_table_status'(AT, _, M1:GNoModes, Skeleton), 990 M1:'$table_mode'(G0plain, GNoModes, ModeArgs), 991 G0 = M1:G0plain 992 ; '$tbl_table_status'(AT, _, G0, AN) 993 ), 994 GN = G0, 995 ( T == [] 996 -> Goal = GN 997 ; Goal = (GN,GT), 998 delay_goals(T, M, GT) 999 ). 1000delay_goals([AT|T], M, Goal) :- 1001 atrie_goal(AT, G0), 1002 unqualify_goal(G0, M, G1), 1003 GN = tnot(G1), 1004 ( T == [] 1005 -> Goal = GN 1006 ; Goal = (GN,GT), 1007 delay_goals(T, M, GT) 1008 ). 1009 1010at_delay_goal(tnot(Trie), M, tnot(Goal), Skeleton, Moded) :- 1011 is_trie(Trie), 1012 !, 1013 at_delay_goal(Trie, M, Goal, Skeleton, Moded). 1014at_delay_goal(Trie, M, Goal, Skeleton, Moded) :- 1015 is_trie(Trie), 1016 !, 1017 '$tbl_table_status'(Trie, _Status, M2:Variant, Skeleton), 1018 M2:'$table_mode'(Goal0, Variant, Moded), 1019 unqualify_goal(M2:Goal0, M, Goal). 1020 1021atrie_goal(Trie, M:Goal) :- 1022 '$tbl_table_status'(Trie, _Status, M:Variant, _Skeleton), 1023 M:'$table_mode'(Goal, Variant, _Moded). 1024 1025unqualify_goal(M:Goal, M, Goal0) :- 1026 !, 1027 Goal0 = Goal. 1028unqualify_goal(Goal, _, Goal). 1029 1030 1031 /******************************* 1032 * CLEANUP * 1033 *******************************/
Abolishes both local and shared tables. Possibly incomplete tables are marked for destruction upon completion. The dependency graphs for incremental and monotonic tabling are reclaimed as well.
1045abolish_all_tables :- 1046 ( '$tbl_abolish_local_tables' 1047 -> true 1048 ; true 1049 ), 1050 ( '$tbl_variant_table'(VariantTrie), 1051 trie_gen(VariantTrie, _, Trie), 1052 '$tbl_destroy_table'(Trie), 1053 fail 1054 ; true 1055 ). 1056 1057abolish_private_tables :- 1058 ( '$tbl_abolish_local_tables' 1059 -> true 1060 ; ( '$tbl_local_variant_table'(VariantTrie), 1061 trie_gen(VariantTrie, _, Trie), 1062 '$tbl_destroy_table'(Trie), 1063 fail 1064 ; true 1065 ) 1066 ). 1067 :- 1069 ( '$tbl_global_variant_table'(VariantTrie), 1070 trie_gen(VariantTrie, _, Trie), 1071 '$tbl_destroy_table'(Trie), 1072 fail 1073 ; true 1074 ).
1083abolish_table_subgoals(SubGoal0) :- 1084 '$tbl_implementation'(SubGoal0, M:SubGoal), 1085 !, 1086 '$must_be'(acyclic, SubGoal), 1087 ( '$tbl_variant_table'(VariantTrie), 1088 trie_gen(VariantTrie, M:SubGoal, Trie), 1089 '$tbl_destroy_table'(Trie), 1090 fail 1091 ; true 1092 ). 1093abolish_table_subgoals(_).
1099abolish_module_tables(Module) :- 1100 '$must_be'(atom, Module), 1101 '$tbl_variant_table'(VariantTrie), 1102 current_module(Module), 1103 !, 1104 forall(trie_gen(VariantTrie, Module:_, Trie), 1105 '$tbl_destroy_table'(Trie)). 1106abolish_module_tables(_).
1112abolish_nonincremental_tables :-
1113 ( '$tbl_variant_table'(VariantTrie),
1114 trie_gen(VariantTrie, _, Trie),
1115 '$tbl_table_status'(Trie, Status, Goal, _),
1116 ( Status == complete
1117 -> true
1118 ; '$permission_error'(abolish, incomplete_table, Trie)
1119 ),
1120 \+ predicate_property(Goal, incremental),
1121 '$tbl_destroy_table'(Trie),
1122 fail
1123 ; true
1124 ).1133abolish_nonincremental_tables(Options) :- 1134 ( Options = on_incomplete(Action) 1135 -> Action == skip 1136 ; '$option'(on_incomplete(skip), Options) 1137 ), 1138 !, 1139 ( '$tbl_variant_table'(VariantTrie), 1140 trie_gen(VariantTrie, _, Trie), 1141 '$tbl_table_status'(Trie, complete, Goal, _), 1142 \+ predicate_property(Goal, incremental), 1143 '$tbl_destroy_table'(Trie), 1144 fail 1145 ; true 1146 ). 1147abolish_nonincremental_tables(_) :- 1148 abolish_nonincremental_tables. 1149 1150 1151 /******************************* 1152 * EXAMINE TABLES * 1153 *******************************/
1162current_table(Variant, Trie) :- 1163 ct_generate(Variant), 1164 !, 1165 current_table_gen(Variant, Trie). 1166current_table(Variant, Trie) :- 1167 current_table_lookup(Variant, Trie), 1168 !. 1169 1170current_table_gen(M:Variant, Trie) :- 1171 '$tbl_local_variant_table'(VariantTrie), 1172 trie_gen(VariantTrie, M:NonModed, Trie), 1173 M:'$table_mode'(Variant, NonModed, _Moded). 1174current_table_gen(M:Variant, Trie) :- 1175 '$tbl_global_variant_table'(VariantTrie), 1176 trie_gen(VariantTrie, M:NonModed, Trie), 1177 \+ '$tbl_table_status'(Trie, fresh), % shared tables are not destroyed 1178 M:'$table_mode'(Variant, NonModed, _Moded). 1179 1180current_table_lookup(M:Variant, Trie) :- 1181 M:'$table_mode'(Variant, NonModed, _Moded), 1182 '$tbl_local_variant_table'(VariantTrie), 1183 trie_lookup(VariantTrie, M:NonModed, Trie). 1184current_table_lookup(M:Variant, Trie) :- 1185 M:'$table_mode'(Variant, NonModed, _Moded), 1186 '$tbl_global_variant_table'(VariantTrie), 1187 trie_lookup(VariantTrie, NonModed, Trie), 1188 \+ '$tbl_table_status'(Trie, fresh). 1189 1190ct_generate(M:Variant) :- 1191 ( var(Variant) 1192 -> true 1193 ; var(M) 1194 ). 1195 1196 /******************************* 1197 * WRAPPER GENERATION * 1198 *******************************/ 1199 1200:- multifile 1201 system:term_expansion/2, 1202 tabled/2. 1203:- dynamic 1204 system:term_expansion/2. 1205 1206wrappers(Spec, M) --> 1207 { tabling_defaults( 1208 [ (table_incremental=true) - (incremental=true), 1209 (table_shared=true) - (tshared=true), 1210 (table_subsumptive=true) - ((mode)=subsumptive), 1211 call(subgoal_size_restraint(Level)) - (subgoal_abstract=Level) 1212 ], 1213 #{}, Defaults) 1214 }, 1215 wrappers(Spec, M, Defaults). 1216 1217wrappers(Var, _, _) --> 1218 { var(Var), 1219 !, 1220 '$instantiation_error'(Var) 1221 }. 1222wrappers(M:Spec, _, Opts) --> 1223 !, 1224 { '$must_be'(atom, M) }, 1225 wrappers(Spec, M, Opts). 1226wrappers(Spec as Options, M, Opts0) --> 1227 !, 1228 { table_options(Options, Opts0, Opts) }, 1229 wrappers(Spec, M, Opts). 1230wrappers((A,B), M, Opts) --> 1231 !, 1232 wrappers(A, M, Opts), 1233 wrappers(B, M, Opts). 1234wrappers(Name//Arity, M, Opts) --> 1235 { atom(Name), integer(Arity), Arity >= 0, 1236 !, 1237 Arity1 is Arity+2 1238 }, 1239 wrappers(Name/Arity1, M, Opts). 1240wrappers(Name/Arity, Module, Opts) --> 1241 { '$option'(mode(TMode), Opts, variant), 1242 atom(Name), integer(Arity), Arity >= 0, 1243 !, 1244 functor(Head, Name, Arity), 1245 '$tbl_trienode'(Reserved) 1246 }, 1247 qualify(Module, 1248 [ '$tabled'(Head, TMode), 1249 '$table_mode'(Head, Head, Reserved) 1250 ]), 1251 [ (:- initialization('$wrap_tabled'(Module:Head, Opts), now)) 1252 ]. 1253wrappers(ModeDirectedSpec, Module, Opts) --> 1254 { '$option'(mode(TMode), Opts, variant), 1255 callable(ModeDirectedSpec), 1256 !, 1257 functor(ModeDirectedSpec, Name, Arity), 1258 functor(Head, Name, Arity), 1259 extract_modes(ModeDirectedSpec, Head, Variant, Modes, Moded), 1260 updater_clauses(Modes, Head, UpdateClauses), 1261 mode_check(Moded, ModeTest), 1262 ( ModeTest == true 1263 -> WrapClause = '$wrap_tabled'(Module:Head, Opts), 1264 TVariant = Head 1265 ; WrapClause = '$moded_wrap_tabled'(Module:Head, Opts, ModeTest, 1266 Module:Variant, Moded), 1267 TVariant = Variant 1268 ) 1269 }, 1270 qualify(Module, 1271 [ '$tabled'(Head, TMode), 1272 '$table_mode'(Head, TVariant, Moded) 1273 ]), 1274 [ (:- initialization(WrapClause, now)) 1275 ], 1276 qualify(Module, UpdateClauses). 1277wrappers(TableSpec, _M, _Opts) --> 1278 { '$type_error'(table_declaration, TableSpec) 1279 }. 1280 1281qualify(Module, List) --> 1282 { prolog_load_context(module, Module) }, 1283 !, 1284 clist(List). 1285qualify(Module, List) --> 1286 qlist(List, Module). 1287 1288clist([]) --> []. 1289clist([H|T]) --> [H], clist(T). 1290 1291qlist([], _) --> []. 1292qlist([H|T], M) --> [M:H], qlist(T, M). 1293 1294 1295tabling_defaults([], Dict, Dict). 1296tabling_defaults([Condition-(Opt=Value)|T], Dict0, Dict) :- 1297 ( tabling_default(Condition) 1298 -> Dict1 = Dict0.put(Opt,Value) 1299 ; Dict1 = Dict0 1300 ), 1301 tabling_defaults(T, Dict1, Dict). 1302 1303tabling_default(Flag=FValue) :- 1304 !, 1305 current_prolog_flag(Flag, FValue). 1306tabling_default(call(Term)) :- 1307 call(Term). 1308 1309% Called from wrappers//2. 1310 1311subgoal_size_restraint(Level) :- 1312 current_prolog_flag(max_table_subgoal_size_action, abstract), 1313 current_prolog_flag(max_table_subgoal_size, Level).
1319table_options(Options, _Opts0, _Opts) :- 1320 var(Options), 1321 '$instantiation_error'(Options). 1322table_options((A,B), Opts0, Opts) :- 1323 !, 1324 table_options(A, Opts0, Opts1), 1325 table_options(B, Opts1, Opts). 1326table_options(subsumptive, Opts0, Opts1) :- 1327 !, 1328 put_dict(mode, Opts0, subsumptive, Opts1). 1329table_options(variant, Opts0, Opts1) :- 1330 !, 1331 put_dict(mode, Opts0, variant, Opts1). 1332table_options(incremental, Opts0, Opts1) :- 1333 !, 1334 put_dict(#{incremental:true,opaque:false}, Opts0, Opts1). 1335table_options(monotonic, Opts0, Opts1) :- 1336 !, 1337 put_dict(monotonic, Opts0, true, Opts1). 1338table_options(opaque, Opts0, Opts1) :- 1339 !, 1340 put_dict(#{incremental:false,opaque:true}, Opts0, Opts1). 1341table_options(lazy, Opts0, Opts1) :- 1342 !, 1343 put_dict(lazy, Opts0, true, Opts1). 1344table_options(dynamic, Opts0, Opts1) :- 1345 !, 1346 put_dict(dynamic, Opts0, true, Opts1). 1347table_options(shared, Opts0, Opts1) :- 1348 !, 1349 put_dict(tshared, Opts0, true, Opts1). 1350table_options(private, Opts0, Opts1) :- 1351 !, 1352 put_dict(tshared, Opts0, false, Opts1). 1353table_options(max_answers(Count), Opts0, Opts1) :- 1354 !, 1355 restraint(max_answers, Count, Opts0, Opts1). 1356table_options(subgoal_abstract(Size), Opts0, Opts1) :- 1357 !, 1358 restraint(subgoal_abstract, Size, Opts0, Opts1). 1359table_options(answer_abstract(Size), Opts0, Opts1) :- 1360 !, 1361 restraint(answer_abstract, Size, Opts0, Opts1). 1362table_options(Opt, _, _) :- 1363 '$domain_error'(table_option, Opt). 1364 1365restraint(Name, Value0, Opts0, Opts) :- 1366 '$table_option'(Value0, Value), 1367 ( Value < 0 1368 -> Opts = Opts0 1369 ; put_dict(Name, Opts0, Value, Opts) 1370 ).
1378mode_check(Moded, Check) :- 1379 var(Moded), 1380 !, 1381 Check = (var(Moded)->true;'$uninstantiation_error'(Moded)). 1382mode_check(Moded, true) :- 1383 '$tbl_trienode'(Moded), 1384 !. 1385mode_check(Moded, (Test->true;'$tabling':instantiated_moded_arg(Vars))) :- 1386 Moded =.. [s|Vars], 1387 var_check(Vars, Test). 1388 1389var_check([H|T], Test) :- 1390 ( T == [] 1391 -> Test = var(H) 1392 ; Test = (var(H),Rest), 1393 var_check(T, Rest) 1394 ). 1395 1396:- public 1397 instantiated_moded_arg/1. 1398 1399instantiated_moded_arg(Vars) :- 1400 '$member'(V, Vars), 1401 \+ var(V), 1402 '$uninstantiation_error'(V).
1414extract_modes(ModeSpec, Head, Variant, Modes, ModedAnswer) :- 1415 compound(ModeSpec), 1416 !, 1417 compound_name_arguments(ModeSpec, Name, ModeSpecArgs), 1418 compound_name_arguments(Head, Name, HeadArgs), 1419 separate_args(ModeSpecArgs, HeadArgs, VariantArgs, Modes, ModedArgs), 1420 length(ModedArgs, Count), 1421 atomic_list_concat([$,Name,$,Count], VName), 1422 Variant =.. [VName|VariantArgs], 1423 ( ModedArgs == [] 1424 -> '$tbl_trienode'(ModedAnswer) 1425 ; ModedArgs = [ModedAnswer] 1426 -> true 1427 ; ModedAnswer =.. [s|ModedArgs] 1428 ). 1429extract_modes(Atom, Atom, Variant, [], ModedAnswer) :- 1430 atomic_list_concat([$,Atom,$,0], Variant), 1431 '$tbl_trienode'(ModedAnswer).
1441separate_args([], [], [], [], []). 1442separate_args([HM|TM], [H|TA], [H|TNA], Modes, TMA):- 1443 indexed_mode(HM), 1444 !, 1445 separate_args(TM, TA, TNA, Modes, TMA). 1446separate_args([M|TM], [H|TA], TNA, [M|Modes], [H|TMA]):- 1447 separate_args(TM, TA, TNA, Modes, TMA). 1448 1449indexed_mode(Mode) :- % XSB 1450 var(Mode), 1451 !. 1452indexed_mode(index). % YAP 1453indexed_mode(+). % B
1460updater_clauses([], _, []) :- !. 1461updater_clauses([P], Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- !, 1462 update_goal(P, S0,S1,S2, Body). 1463updater_clauses(Modes, Head, [('$table_update'(Head, S0, S1, S2) :- Body)]) :- 1464 length(Modes, Len), 1465 functor(S0, s, Len), 1466 functor(S1, s, Len), 1467 functor(S2, s, Len), 1468 S0 =.. [_|Args0], 1469 S1 =.. [_|Args1], 1470 S2 =.. [_|Args2], 1471 update_body(Modes, Args0, Args1, Args2, true, Body). 1472 1473update_body([], _, _, _, Body, Body). 1474update_body([P|TM], [A0|Args0], [A1|Args1], [A2|Args2], Body0, Body) :- 1475 update_goal(P, A0,A1,A2, Goal), 1476 mkconj(Body0, Goal, Body1), 1477 update_body(TM, Args0, Args1, Args2, Body1, Body). 1478 1479update_goal(Var, _,_,_, _) :- 1480 var(Var), 1481 !, 1482 '$instantiation_error'(Var). 1483update_goal(lattice(M:PI), S0,S1,S2, M:Goal) :- 1484 !, 1485 '$must_be'(atom, M), 1486 update_goal(lattice(PI), S0,S1,S2, Goal). 1487update_goal(lattice(Name/Arity), S0,S1,S2, Goal) :- 1488 !, 1489 '$must_be'(oneof(integer, lattice_arity, [3]), Arity), 1490 '$must_be'(atom, Name), 1491 Goal =.. [Name,S0,S1,S2]. 1492update_goal(lattice(Head), S0,S1,S2, Goal) :- 1493 compound(Head), 1494 !, 1495 compound_name_arity(Head, Name, Arity), 1496 '$must_be'(oneof(integer, lattice_arity, [3]), Arity), 1497 Goal =.. [Name,S0,S1,S2]. 1498update_goal(lattice(Name), S0,S1,S2, Goal) :- 1499 !, 1500 '$must_be'(atom, Name), 1501 update_goal(lattice(Name/3), S0,S1,S2, Goal). 1502update_goal(po(Name/Arity), S0,S1,S2, Goal) :- 1503 !, 1504 '$must_be'(oneof(integer, po_arity, [2]), Arity), 1505 '$must_be'(atom, Name), 1506 Call =.. [Name, S0, S1], 1507 Goal = (Call -> S2 = S0 ; S2 = S1). 1508update_goal(po(M:Name/Arity), S0,S1,S2, Goal) :- 1509 !, 1510 '$must_be'(atom, M), 1511 '$must_be'(oneof(integer, po_arity, [2]), Arity), 1512 '$must_be'(atom, Name), 1513 Call =.. [Name, S0, S1], 1514 Goal = (M:Call -> S2 = S0 ; S2 = S1). 1515update_goal(po(M:Name), S0,S1,S2, Goal) :- 1516 !, 1517 '$must_be'(atom, M), 1518 '$must_be'(atom, Name), 1519 update_goal(po(M:Name/2), S0,S1,S2, Goal). 1520update_goal(po(Name), S0,S1,S2, Goal) :- 1521 !, 1522 '$must_be'(atom, Name), 1523 update_goal(po(Name/2), S0,S1,S2, Goal). 1524update_goal(Alias, S0,S1,S2, Goal) :- 1525 update_alias(Alias, Update), 1526 !, 1527 update_goal(Update, S0,S1,S2, Goal). 1528update_goal(Mode, _,_,_, _) :- 1529 '$domain_error'(tabled_mode, Mode). 1530 1531update_alias(first, lattice('$tabling':first/3)). 1532update_alias(-, lattice('$tabling':first/3)). 1533update_alias(last, lattice('$tabling':last/3)). 1534update_alias(min, lattice('$tabling':min/3)). 1535update_alias(max, lattice('$tabling':max/3)). 1536update_alias(sum, lattice('$tabling':sum/3)). 1537 1538mkconj(true, G, G) :- !. 1539mkconj(G1, G2, (G1,G2)). 1540 1541 1542 /******************************* 1543 * AGGREGATION * 1544 *******************************/
1554:- public first/3, last/3, min/3, max/3, sum/3. 1555 1556first(S, _, S). 1557last(_, S, S). 1558min(S0, S1, S) :- (S0 @< S1 -> S = S0 ; S = S1). 1559max(S0, S1, S) :- (S0 @> S1 -> S = S0 ; S = S1). 1560sum(S0, S1, S) :- S is S0+S1. 1561 1562 1563 /******************************* 1564 * DYNAMIC PREDICATES * 1565 *******************************/
1572'$set_table_wrappers'(Pred) :- 1573 ( '$get_predicate_attribute'(Pred, incremental, 1), 1574 \+ '$get_predicate_attribute'(Pred, opaque, 1) 1575 -> wrap_incremental(Pred) 1576 ; unwrap_incremental(Pred) 1577 ), 1578 ( '$get_predicate_attribute'(Pred, monotonic, 1) 1579 -> wrap_monotonic(Pred) 1580 ; unwrap_monotonic(Pred) 1581 ). 1582 1583 /******************************* 1584 * MONOTONIC TABLING * 1585 *******************************/
1592mon_assert_dep(dependency(Dynamic), Cont, Skel, ATrie) :- 1593 '$idg_add_mono_dyn_dep'(Dynamic, 1594 dependency(Dynamic, Cont, Skel), 1595 ATrie). 1596mon_assert_dep(dependency(SrcSkel, SrcTrie, IsMono), Cont, Skel, ATrie) :- 1597 '$idg_add_monotonic_dep'(SrcTrie, 1598 dependency(SrcSkel, IsMono, Cont, Skel), 1599 ATrie).
A table with more than one monotonic dependency indexes them by their skeleton, so only the dependencies whose skeleton unifies with SrcReturn are visited rather than every dependency of SrcTrie.
1613monotonic_affects(SrcTrie, SrcSkel, IsMono, Cont, Skel, ATrie) :-
1614 ( '$idg_mono_index'(SrcTrie, Index)
1615 -> '$trie_gen_node'(Index, SrcSkel-ATrie, Leaf),
1616 '$idg_mono_index_deps'(SrcTrie, Leaf, ATrie,
1617 dependency(SrcSkel, IsMono, Cont, Skel))
1618 ; '$idg_mono_affects_eager'(SrcTrie, ATrie,
1619 dependency(SrcSkel, IsMono, Cont, Skel))
1620 ).
1626monotonic_dyn_affects(Head, Cont, Skel, ATrie) :-
1627 dyn_affected(Head, DTrie),
1628 '$idg_mono_affects_eager'(DTrie, ATrie,
1629 dependency(Head, Cont, Skel)).
1637wrap_monotonic(Head) :-
1638 '$wrap_predicate'(Head, monotonic, _Closure, Wrapped,
1639 '$start_monotonic'(Head, Wrapped)),
1640 '$pi_head'(PI, Head),
1641 prolog_listen(PI, monotonic_update).
1647unwrap_monotonic(Head) :-
1648 '$pi_head'(PI, Head),
1649 ( unwrap_predicate(PI, monotonic)
1650 -> prolog_unlisten(PI, monotonic_update)
1651 ; true
1652 ).
1660'$start_monotonic'(Head, Wrapped) :-
1661 ( '$tbl_collect_mono_dep'
1662 -> shift(dependency(Head)),
1663 tdebug(monotonic, 'Cont in $start_dynamic/2 with ~p', [Head]),
1664 ,
1665 tdebug(monotonic, ' --> ~p', [Head])
1666 ;
1667 ).1673:- public monotonic_update/2. 1674monotonic_update(Action, ClauseRef) :- 1675 ( atomic(ClauseRef) % avoid retractall, start(_) 1676 -> '$clause'(Head, _Body, ClauseRef, _Bindings), 1677 mon_propagate(Action, Head, ClauseRef) 1678 ; true 1679 ).
1686mon_propagate(Action, Head, ClauseRef) :- 1687 assert_action(Action), 1688 !, 1689 setup_call_cleanup( 1690 '$tbl_propagate_start'(Old), 1691 propagate_assert(Head), % eager monotonic dependencies 1692 '$tbl_propagate_end'(Old)), 1693 forall(dyn_affected(Head, ATrie), 1694 '$mono_idg_changed'(ATrie, ClauseRef)). % lazy monotonic dependencies 1695mon_propagate(retract, Head, _) :- 1696 !, 1697 mon_invalidate_dependents(Head). 1698mon_propagate(rollback(Action), Head, _) :- 1699 mon_propagate_rollback(Action, Head). 1700 1701mon_propagate_rollback(Action, _Head) :- 1702 assert_action(Action), 1703 !. 1704mon_propagate_rollback(retract, Head) :- 1705 mon_invalidate_dependents(Head). 1706 1707assert_action(asserta). 1708assert_action(assertz).
1714propagate_assert(Head) :-
1715 tdebug(monotonic, 'Asserted ~p', [Head]),
1716 ( monotonic_dyn_affects(Head, Cont, Skel, ATrie),
1717 tdebug(monotonic, 'Propagating dyn ~p to ~p', [Head, ATrie]),
1718 '$idg_set_current'(_, ATrie),
1719 pdelim(Cont, Skel, ATrie),
1720 fail
1721 ; true
1722 ).
1729incr_propagate_assert(Head) :-
1730 tdebug(monotonic, 'New dynamic answer ~p', [Head]),
1731 ( dyn_affected(Head, DTrie),
1732 '$idg_mono_affects'(DTrie, ATrie,
1733 dependency(Head, Cont, Skel)),
1734 tdebug(monotonic, 'Propagating dyn ~p to ~p', [Head, ATrie]),
1735 '$idg_set_current'(_, ATrie),
1736 pdelim(Cont, Skel, ATrie),
1737 fail
1738 ; true
1739 ).
1746propagate_answer(SrcTrie, SrcSkel) :-
1747 ( monotonic_affects(SrcTrie, SrcSkel, true, Cont, Skel, ATrie),
1748 tdebug(monotonic, 'Propagating tab ~p to ~p', [SrcTrie, ATrie]),
1749 pdelim(Cont, Skel, ATrie),
1750 fail
1751 ; true
1752 ).
1764pdelim(Worker, Skel, ATrie) :-
1765 reset(Worker, Dep, Cont),
1766 ( Cont == 0
1767 -> '$tbl_monotonic_add_answer'(ATrie, Skel),
1768 propagate_answer(ATrie, Skel)
1769 ; mon_assert_dep(Dep, Cont, Skel, ATrie),
1770 pdelim(Cont, Skel, ATrie)
1771 ).
1779mon_invalidate_dependents(Head) :-
1780 tdebug(monotonic, 'Invalidate dependents for ~p', [Head]),
1781 forall(dyn_affected(Head, ATrie),
1782 '$idg_mono_invalidate'(ATrie)).1790abolish_monotonic_tables :- 1791 ( '$tbl_variant_table'(VariantTrie), 1792 trie_gen(VariantTrie, Goal, ATrie), 1793 '$get_predicate_attribute'(Goal, monotonic, 1), 1794 '$tbl_destroy_table'(ATrie), 1795 fail 1796 ; true 1797 ). 1798 1799 /******************************* 1800 * INCREMENTAL TABLING * 1801 *******************************/
1807wrap_incremental(Head) :- 1808 tdebug(monotonic, 'Wrapping ~p', [Head]), 1809 abstract_goal(Head, Abstract), 1810 '$pi_head'(PI, Head), 1811 ( Head == Abstract 1812 -> prolog_listen(PI, dyn_update) 1813 ; prolog_listen(PI, dyn_update(Abstract)) 1814 ). 1815 1816abstract_goal(M:Head, M:Abstract) :- 1817 compound(Head), 1818 '$get_predicate_attribute'(M:Head, abstract, 1), 1819 !, 1820 compound_name_arity(Head, Name, Arity), 1821 functor(Abstract, Name, Arity). 1822abstract_goal(Head, Head).
1832:- public dyn_update/2, dyn_update/3. 1833 1834dyn_update(_Action, ClauseRef) :- 1835 ( atomic(ClauseRef) % avoid retractall, start(_) 1836 -> '$clause'(Head, _Body, ClauseRef, _Bindings), 1837 dyn_changed_pattern(Head) 1838 ; true 1839 ). 1840 1841dyn_update(Abstract, _, _) :- 1842 dyn_changed_pattern(Abstract). 1843 1844dyn_changed_pattern(Term) :- 1845 forall(dyn_affected(Term, ATrie), 1846 '$idg_changed'(ATrie)). 1847 1848dyn_affected(Term, ATrie) :- 1849 '$tbl_variant_table'(VTable), 1850 trie_gen(VTable, Term, ATrie).
abstract property and remove possible tables.
1857unwrap_incremental(Head) :-
1858 '$pi_head'(PI, Head),
1859 abstract_goal(Head, Abstract),
1860 ( Head == Abstract
1861 -> prolog_unlisten(PI, dyn_update)
1862 ; '$set_predicate_attribute'(Head, abstract, 0),
1863 prolog_unlisten(PI, dyn_update(_))
1864 ),
1865 ( '$tbl_variant_table'(VariantTrie)
1866 -> forall(trie_gen(VariantTrie, Head, ATrie),
1867 '$tbl_destroy_table'(ATrie))
1868 ; true
1869 ).This finds all dependency paths to dynamic predicates and then evaluates the nodes in a breath-first fashion starting at the level just above the dynamic predicates and moving upwards. Bottom up evaluation is used to profit from upward propagation of not-modified events that may cause the evaluation to stop early.
Note that false paths either end in a dynamic node or a complete node. The latter happens if we have and IDG "D -> P -> Q" and we first re-evaluate P for some reason. Now Q can still be invalid after P has been re-evaluated.
1895reeval(ATrie, Goal, Return) :- 1896 catch(try_reeval(ATrie, Goal, Return), deadlock, 1897 retry_reeval(ATrie, Goal)). 1898 1899retry_reeval(ATrie, Goal) :- 1900 '$tbl_reeval_abandon'(ATrie), 1901 tdebug(deadlock, 'Deadlock re-evaluating ~p; retrying', [ATrie]), 1902 sleep(0.000001), 1903 call(Goal). 1904 1905try_reeval(ATrie, Goal, Return) :- 1906 nb_current('$tbl_reeval', true), 1907 !, 1908 tdebug(reeval, 'Nested re-evaluation for ~p', [ATrie]), 1909 do_reeval(ATrie, Goal, Return). 1910try_reeval(ATrie, Goal, Return) :- 1911 tdebug(reeval, 'Planning reeval for ~p', [ATrie]), 1912 findall(Path, false_path(ATrie, Path), Paths0), 1913 sort(0, @>, Paths0, Paths1), 1914 clean_paths(Paths1, Paths), 1915 tdebug(forall('$member'(Path, Paths), 1916 tdebug(reeval, ' Re-eval complete path: ~p', [Path]))), 1917 reeval_paths(Paths, ATrie), 1918 do_reeval(ATrie, Goal, Return). 1919 1920do_reeval(ATrie, Goal, Return) :- 1921 '$tbl_reeval_prepare_top'(ATrie, Clause), 1922 ( Clause == 0 % complete and answer subsumption 1923 -> '$tbl_table_status'(ATrie, _Status, M:Variant, Return), 1924 M:'$table_mode'(Goal0, Variant, ModeArgs), 1925 Goal = M:Goal0, 1926 moded_gen_answer(ATrie, Return, ModeArgs) 1927 ; nonvar(Clause) % complete 1928 -> trie_gen_compiled(Clause, Return) 1929 ; call(Goal) % actually re-evaluate 1930 ).
1939clean_paths([], []). 1940clean_paths([[_|Path]|T0], [Path|T]) :- 1941 clean_paths(T0, Path, T). 1942 1943clean_paths([], _, []). 1944clean_paths([[_|CPath]|T0], CPath, T) :- 1945 !, 1946 clean_paths(T0, CPath, T). 1947clean_paths([[_|Path]|T0], _, [Path|T]) :- 1948 clean_paths(T0, Path, T).
1957reeval_paths([], _) :- 1958 !. 1959reeval_paths(BottomUp, ATrie) :- 1960 is_invalid(ATrie), 1961 !, 1962 reeval_heads(BottomUp, ATrie, BottomUp1), 1963 tdebug(assertion(BottomUp \== BottomUp1)), 1964 '$list_to_set'(BottomUp1, BottomUp2), 1965 reeval_paths(BottomUp2, ATrie). 1966reeval_paths(_, _). 1967 1968reeval_heads(_, ATrie, []) :- % target is valid again 1969 \+ is_invalid(ATrie), 1970 !. 1971reeval_heads([], _, []). 1972reeval_heads([[H]|B], ATrie, BT) :- % Last one of a falsepath 1973 reeval_node(H), 1974 !, 1975 reeval_heads(B, ATrie, BT). 1976reeval_heads([[H|T]|B], ATrie, [T|BT]) :- 1977 reeval_node(H), 1978 !, 1979 reeval_heads(B, ATrie, BT). 1980reeval_heads([FP|B], ATrie, [FP|BT]) :- 1981 reeval_heads(B, ATrie, BT).
s(Rank,Length,ATrie) that is used for sorting the paths.
If we find a table along the way that is being worked on by some other thread we wait for it.
1993false_path(ATrie, BottomUp) :- 1994 false_path(ATrie, Path, []), 1995 '$reverse'(Path, BottomUp). 1996 1997false_path(ATrie, [ATrie|T], Seen) :- 1998 \+ memberchk(ATrie, Seen), 1999 '$idg_false_edge'(ATrie, Dep, Status), 2000 tdebug(reeval, ' ~p has dependent ~p (~w)', [ATrie, Dep, Status]), 2001 ( Status == invalid 2002 -> ( false_path(Dep, T, [ATrie|Seen]) 2003 -> true 2004 ; length(Seen, Len), % invalid has no dependencies: 2005 T = [s(2, Len, [])] % dynamic and tabled or explicitly 2006 ) % invalidated 2007 ; status_rank(Status, Rank), 2008 length(Seen, Len), 2009 T = [s(Rank,Len,Dep)] 2010 ). 2011 2012status_rank(dynamic, 2) :- !. 2013status_rank(monotonic, 2) :- !. 2014status_rank(complete, 1) :- !. 2015status_rank(Status, Rank) :- 2016 var(Rank), 2017 !, 2018 format(user_error, 'Re-eval from status ~p~n', [Status]), 2019 Rank = 0. 2020status_rank(Rank, Rank) :- 2021 format(user_error, 'Re-eval from rank ~p~n', [Rank]). 2022 2023is_invalid(ATrie) :- 2024 '$idg_falsecount'(ATrie, FalseCount), 2025 FalseCount > 0.
Fails if the node is not ready for evaluation. This is the case if it is valid or it is a lazy table that has invalid dependencies.
2041reeval_node(ATrie) :- 2042 '$tbl_reeval_prepare'(ATrie, M:Variant), 2043 !, 2044 M:'$table_mode'(Goal0, Variant, _Moded), 2045 Goal = M:Goal0, 2046 tdebug(reeval, 'Re-evaluating ~p', [Goal]), 2047 ( '$idg_reset_current', 2048 setup_call_cleanup( 2049 nb_setval('$tbl_reeval', true), 2050 ignore(Goal), % assumes local scheduling 2051 nb_delete('$tbl_reeval')), 2052 fail 2053 ; tdebug(reeval, 'Re-evaluated ~p', [Goal]) 2054 ). 2055reeval_node(ATrie) :- 2056 '$mono_reeval_prepare'(ATrie, Size), 2057 !, 2058 reeval_monotonic_node(ATrie, Size). 2059reeval_node(ATrie) :- 2060 \+ is_invalid(ATrie). 2061 2062reeval_monotonic_node(ATrie, Size) :- 2063 setup_call_cleanup( 2064 '$tbl_propagate_start'(Old), 2065 reeval_monotonic_node(ATrie, Size, Deps), 2066 '$tbl_propagate_end'(Old)), 2067 ( Deps == [] 2068 -> tdebug(reeval, 'Re-evaluation for ~p complete', [ATrie]) 2069 ; Deps == false 2070 -> tdebug(reeval, 'Re-evaluation for ~p queued new answers', [ATrie]), 2071 reeval_node(ATrie) 2072 ; tdebug(reeval, 'Re-evaluation for ~p: new invalid deps: ~p', 2073 [ATrie, Deps]), 2074 reeval_nodes(Deps), 2075 reeval_node(ATrie) 2076 ).
2084reeval_nodes([]). 2085reeval_nodes([H|T]) :- 2086 reeval_node(H), 2087 reeval_nodes(T). 2088 2089reeval_monotonic_node(ATrie, Size, Deps) :- 2090 tdebug(reeval, 'Re-evaluating lazy monotonic ~p', [ATrie]), 2091 ( '$idg_mono_affects_lazy'(ATrie, _0SrcTrie, Dep, DepRef, Answers), 2092 length(Answers, Count), 2093 '$idg_mono_empty_queue'(DepRef, Count), 2094 ( Dep = dependency(Head, Cont, Skel) 2095 -> ( '$member'(ClauseRef, Answers), 2096 '$clause'(Head, _Body, ClauseRef, _Bindings), 2097 tdebug(monotonic, 'Propagating ~p from ~p to ~p', 2098 [Head, _0SrcTrie, ATrie]), 2099 '$idg_set_current'(_, ATrie), 2100 pdelim(Cont, Skel, ATrie), 2101 fail 2102 ; true 2103 ) 2104 ; Dep = dependency(SrcSkel, true, Cont, Skel) 2105 -> ( '$member'(Node, Answers), 2106 '$tbl_node_answer'(Node, SrcSkel), 2107 tdebug(monotonic, 'Propagating ~p from ~p to ~p', 2108 [Skel, _0SrcTrie, ATrie]), 2109 '$idg_set_current'(_, ATrie), 2110 pdelim(Cont, Skel, ATrie), 2111 fail 2112 ; true 2113 ) 2114 ; tdebug(monotonic, 'Skipped queued ~p, answers ~p', 2115 [Dep, Answers]) 2116 ), 2117 fail 2118 ; '$mono_reeval_done'(ATrie, Size, Deps) 2119 ). 2120 2121 2122 /******************************* 2123 * EXPAND DIRECTIVES * 2124 *******************************/ 2125 2126systemterm_expansion((:- table(Preds)), Expansion) :- 2127 \+ current_prolog_flag(xref, true), 2128 prolog_load_context(module, M), 2129 phrase(wrappers(Preds, M), Clauses), 2130 multifile_decls(Clauses, Directives0), 2131 sort(Directives0, Directives), 2132 '$append'(Directives, Clauses, Expansion). 2133 2134multifile_decls([], []). 2135multifile_decls([H0|T0], [H|T]) :- 2136 multifile_decl(H0, H), 2137 !, 2138 multifile_decls(T0, T). 2139multifile_decls([_|T0], T) :- 2140 multifile_decls(T0, T). 2141 2142multifile_decl(M:(Head :- _Body), (:- multifile(M:Name/Arity))) :- 2143 !, 2144 functor(Head, Name, Arity). 2145multifile_decl(M:Head, (:- multifile(M:Name/Arity))) :- 2146 !, 2147 functor(Head, Name, Arity). 2148multifile_decl((Head :- _Body), (:- multifile(Name/Arity))) :- 2149 !, 2150 functor(Head, Name, Arity). 2151multifile_decl(Head, (:- multifile(Name/Arity))) :- 2152 !, 2153 Head \= (:-_), 2154 functor(Head, Name, Arity). 2155 2156 2157 /******************************* 2158 * ANSWER COMPLETION * 2159 *******************************/ 2160 2161:- public answer_completion/2.
simplify_component() detects there are
conditional answers after simplification.
Note that we are called recursively from C. Our caller prepared a clean new tabling environment and restores the old one after this predicate terminates.
2177answer_completion(AnswerTrie, Return) :- 2178 tdebug(trie_goal(AnswerTrie, Goal, _Return)), 2179 tdebug(ac(start), 'START: Answer completion for ~p', [Goal]), 2180 call_cleanup(answer_completion_guarded(AnswerTrie, Return, Propagated), 2181 abolish_table_subgoals(eval_subgoal_in_residual(_,_))), 2182 ( Propagated > 0 2183 -> answer_completion(AnswerTrie, Return) 2184 ; true 2185 ). 2186 2187answer_completion_guarded(AnswerTrie, Return, Propagated) :- 2188 ( eval_subgoal_in_residual(AnswerTrie, Return), 2189 fail 2190 ; true 2191 ), 2192 delete_answers_for_failing_calls(Propagated), 2193 ( Propagated == 0 2194 -> mark_succeeding_calls_as_answer_completed 2195 ; true 2196 ).
false and
return the number of additional answers that changed status as a
consequence of additional simplification propagation.2204delete_answers_for_failing_calls(Propagated) :- 2205 State = state(0), 2206 ( subgoal_residual_trie(ASGF, ESGF), 2207 \+ trie_gen(ESGF, _ETmp), 2208 tdebug(trie_goal(ASGF, Goal0, _)), 2209 tdebug(trie_goal(ASGF, Goal, _0Return)), 2210 '$trie_gen_node'(ASGF, _0Return, ALeaf), 2211 tdebug(ac(prune), ' Removing answer ~p from ~p', [Goal, Goal0]), 2212 '$tbl_force_truth_value'(ALeaf, false, Count), 2213 arg(1, State, Prop0), 2214 Prop is Prop0+Count-1, 2215 nb_setarg(1, State, Prop), 2216 fail 2217 ; arg(1, State, Propagated) 2218 ). 2219 2220mark_succeeding_calls_as_answer_completed :- 2221 ( subgoal_residual_trie(ASGF, _ESGF), 2222 ( '$tbl_answer_dl'(ASGF, _0Return, _True) 2223 -> tdebug(trie_goal(ASGF, Answer, _0Return)), 2224 tdebug(trie_goal(ASGF, Goal, _0Return)), 2225 tdebug(ac(prune), ' Completed ~p on ~p', [Goal, Answer]), 2226 '$tbl_set_answer_completed'(ASGF) 2227 ), 2228 fail 2229 ; true 2230 ). 2231 2232subgoal_residual_trie(ASGF, ESGF) :- 2233 '$tbl_variant_table'(VariantTrie), 2234 context_module(M), 2235 trie_gen(VariantTrie, M:eval_subgoal_in_residual(ASGF, _), ESGF).
2242eval_dl_in_residual(true) :- 2243 !. 2244eval_dl_in_residual((A;B)) :- 2245 !, 2246 ( eval_dl_in_residual(A) 2247 ; eval_dl_in_residual(B) 2248 ). 2249eval_dl_in_residual((A,B)) :- 2250 !, 2251 eval_dl_in_residual(A), 2252 eval_dl_in_residual(B). 2253eval_dl_in_residual(tnot(G)) :- 2254 !, 2255 tdebug(ac, ' ? tnot(~p)', [G]), 2256 current_table(G, SGF), 2257 '$tbl_table_status'(SGF, _Status, _Wrapper, Return), 2258 tnot(eval_subgoal_in_residual(SGF, Return)). 2259eval_dl_in_residual(G) :- 2260 tdebug(ac, ' ? ~p', [G]), 2261 ( current_table(G, SGF) 2262 -> true 2263 ; more_general_table(G, SGF) 2264 -> true 2265 ; writeln(user_error, 'MISSING CALL? '(G)), 2266 fail 2267 ), 2268 '$tbl_table_status'(SGF, _Status, _Wrapper, Return), 2269 eval_subgoal_in_residual(SGF, Return). 2270 2271more_general_table(G, Trie) :- 2272 term_attvars(G, []), 2273 !, 2274 term_variables(G, Vars), 2275 '$tbl_variant_table'(VariantTrie), 2276 trie_gen(VariantTrie, G, Trie), 2277 is_most_general_term(Vars). 2278more_general_table(G, _Trie) :- 2279 '$type_error'(free_of_attvar, G). 2280 2281:- table eval_subgoal_in_residual/2.
2288eval_subgoal_in_residual(AnswerTrie, _Return) :- 2289 '$tbl_is_answer_completed'(AnswerTrie), 2290 !, 2291 undefined. 2292eval_subgoal_in_residual(AnswerTrie, Return) :- 2293 '$tbl_answer'(AnswerTrie, Return, Condition), 2294 tdebug(trie_goal(AnswerTrie, Goal, Return)), 2295 tdebug(ac, 'Condition for ~p is ~p', [Goal, Condition]), 2296 eval_dl_in_residual(Condition). 2297 2298 2299 /******************************* 2300 * TRIPWIRES * 2301 *******************************/
abstract and
bounded_rationality.2309:- public tripwire/3. 2310:- multifile prolog:tripwire/2. 2311 2312tripwire(Wire, _Action, Context) :- 2313 prolog:tripwire(Wire, Context), 2314 !. 2315tripwire(Wire, Action, Context) :- 2316 Error = error(resource_error(tripwire(Wire, Context)), _), 2317 tripwire_action(Action, Error). 2318 2319tripwire_action(warning, Error) :- 2320 print_message(warning, Error). 2321tripwire_action(error, Error) :- 2322 throw(Error). 2323tripwire_action(suspend, Error) :- 2324 print_message(warning, Error), 2325 break. 2326 2327 2328 /******************************* 2329 * SYSTEM TABLED PREDICATES * 2330 *******************************/ 2331 2332:- table 2333 system:undefined/0, 2334 system:answer_count_restraint/0, 2335 system:radial_restraint/0, 2336 system:tabled_call/1.
2342system(undefined :-
2343 tnot(undefined)).2351system(answer_count_restraint :- 2352 tnot(answer_count_restraint)). 2353 2354system(radial_restraint :- 2355 tnot(radial_restraint)). 2356 2357system(tabled_call(X) :- call(X))
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.