View source with raw comments or as raw
   1/*  Part of SWI-Prolog
   2
   3    Author:        Jan Wielemaker
   4    E-mail:        J.Wielemaker@vu.nl
   5    WWW:           http://www.swi-prolog.org
   6    Copyright (C): 1985-2012, University of Amsterdam
   7			      VU University Amsterdam
   8
   9    This program is free software; you can redistribute it and/or
  10    modify it under the terms of the GNU General Public License
  11    as published by the Free Software Foundation; either version 2
  12    of the License, or (at your option) any later version.
  13
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18
  19    You should have received a copy of the GNU Lesser General Public
  20    License along with this library; if not, write to the Free Software
  21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  22
  23    As a special exception, if you link this library with other files,
  24    compiled with a Free Software compiler, to produce an executable, this
  25    library does not by itself cause the resulting executable to be covered
  26    by the GNU General Public License. This exception does not however
  27    invalidate any other reasons why the executable file might be covered by
  28    the GNU General Public License.
  29*/
  30
  31:- module(rdf_optimise,
  32	  [ rdf_optimise/1,		% :Query
  33	    rdf_optimise/2,		% +Query, -Optimised
  34	    rdf_optimise/4,		% +Query, -Optimised, -Space, -Time
  35	    rdf_complexity/3,		% :Goal, -SpaceEstimate, -TimeEstimate
  36	    serql_select_bind_null/2	% +Goal, -WithBind
  37	  ]).
  38:- use_module(library(semweb/rdf_db)).
  39:- use_module(library(debug)).
  40:- use_module(library(lists)).
  41:- use_module(library(pairs)).
  42:- use_module(library(ordsets)).
  43:- use_module(library(ugraphs)).
  44
  45:- meta_predicate
  46	rdf_optimise(0).
  47
  48/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  49Queries  as  returned  by  serql_compile_path/2    consists  of  a  path
  50expression which is compiled to a conjunction  of calls to rdf/3 and the
  51translation of the WHERE clause acting as an additional filter.
  52
  53Optimisation of a query basically means moving conditions into the rdf/3
  54calls where possible, moving other conditions   as  early as possibly in
  55the graph-matching and reordering graph matching calls (rdf/3) to reduce
  56non-determinism.
  57
  58Reordering
  59----------
  60
  61Reordering of graph expressions  is   required  to  reduce backtracking.
  62Roughly I see three approaches:
  63
  64	* Learning
  65	Create permutations of the query and make them run under time
  66	constraints.  Try to learn patterns that work (fast).
  67
  68	* Use statistics
  69	Given the number of solutions on a certain partially instantiated
  70	rdf/3 call (and the required execution time), reorganise them to
  71	minimalise the cost.
  72
  73	* Use constraint solving
  74	Instead of trying to solve an rdf/3 call, create a constraint from
  75	it and only try to solve it if there is more information.  This
  76	is especially attractive if some form of high-level reasoning
  77	from the language entailment rules can be applied or set-theory
  78	is a possibility.
  79
  80After experiments with using constraint solving,   this  module now uses
  81planning based on statistical information provided by the rdf_db module.
  82This  algorithm  reaches  optimal  performance  under  quite  reasonable
  83assumptions  while  the  planning  overhead   is  very  reasonable.  The
  84algorithm is described in "An  optimised   Semantic  Web  query language
  85implementation        in        Prolog",          available         from
  86http://www.swi-prolog.org/download/publications/ICLP05-SeRQL.pdf
  87
  88NOTES:
  89
  90	* SeRQL LIKE works on resources *and* literals.  Do we want this?
  91	  See http://rdf4j.org/forum/mvnforum/viewthread?thread=275
  92- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  93
  94:- multifile
  95	user:goal_expansion/2.
  96
  97user:goal_expansion(rdf_complexity(G0, C), rdf_complexity(G, C)) :-
  98	expand_goal(G0, G).
  99user:goal_expansion(rdf_optimise(G0, O), rdf_optimise(G, O)) :-
 100	expand_goal(G0, G).
 101user:goal_expansion(rdf_optimise(G0, O, C, E), rdf_optimise(G, O, C, E)) :-
 102	expand_goal(G0, G).
 103
 104
 105/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 106Plan (conjunctions)
 107
 108	* Generate permutations and costs.  Select cheapest
 109	* The above moves tests right after the RDF call filling
 110	  its input argument.  As a last optimisation some of these
 111	  searches may be integrated in the rdf/3 call to help using
 112	  indexing of the RDF database.
 113
 114complexity/2 needs to update the order of clauses inside meta calls
 115(notably optional path expressions).
 116- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 117
 118%%	rdf_optimise(:Goal) is nondet.
 119%
 120%	Optimise Goal and execute the result. Semantically equivalent to
 121%	call/1.
 122%
 123%	@tbd	Module handling is not correct.
 124
 125rdf_optimise(Module:Goal) :-
 126	rdf_optimise(Goal, Optimised),
 127	call(Module:Optimised).
 128
 129
 130%%	rdf_optimise(+Goal, -Optimized) is det.
 131%
 132%	Goal is a Prolog control-structure with   calls to the rdf_db.pl
 133%	and  SeRQL  runtime  predicates.  Optimized  is  a  semantically
 134%	equivalent goal, obtained by re-ordering   conjunctions  in Goal
 135%	and  processing  sub-queries  that  do    not   share  variables
 136%	independently.
 137
 138rdf_optimise(Conj, Optimised) :-
 139	rdf_optimise(Conj, Optimised, _, _).
 140
 141rdf_optimise(Conj, Optimised, Space, Time) :-
 142	debug(rdf_optimise, '*** OPTIMIZING ***~n', []),
 143	dbg_portray_body(Conj),
 144	term_variables(Conj, Vars),
 145	rdf_complexity(Conj, Conj, S0, E0),
 146	State = state(Vars-Conj, S0, E0, 1),
 147	debug(rdf_optimise, 'C0 = ~w~n', [E0]),
 148	(   reorder(Conj, Perm),
 149	    (	debugging(rdf_optimise(all))
 150	    ->	dbg_portray_body(Perm)
 151	    ;	true
 152	    ),
 153	    rdf_complexity(Perm, Perm1, S, C),
 154	    debug(rdf_optimise(all), '--> space=~w, time=~w~n', [S, C]),
 155
 156	    arg(4, State, N),
 157	    N2 is N + 1,
 158	    nb_setarg(4, State, N2),
 159
 160	    (	arg(3, State, C0),
 161		C < C0
 162	    ->	debug(rdf_optimise,
 163		      'BETTER ONE [~D]: --> space=~w, time=~w~n', [N, S, C]),
 164		dbg_portray_body(Perm1),
 165	        nb_setarg(3, State, C),
 166		nb_setarg(2, State, S),
 167		nb_setarg(1, State, Vars-Perm1)
 168	    ;	true
 169	    ),
 170	    fail
 171	;   arg(1, State, Vars-Optimised),
 172	    arg(2, State, Space),
 173	    arg(3, State, Time),
 174	    debug(rdf_optimise, '  --> optimised: s/t = ~w/~w --> ~w/~w~n',
 175		  [S0, E0, Space, Time]),
 176	    dbg_portray_body(Optimised)
 177	), !.
 178optimise_order(Conj, Conj, -1, -1) :-
 179	debug(rdf_optimise, 'Failed to optimise:~n', []),
 180	dbg_portray_body(Conj).
 181
 182
 183		 /*******************************
 184		 *	     REORDERING		*
 185		 *******************************/
 186
 187%%	reorder(Goal, Reordered)
 188%
 189%	Assuming Goal is  a  conjunction,   create  permutations  of its
 190%	order. Instead of blindly generating   permutations  however, we
 191%	get an arbitrary element of  the   conjunction  to the front and
 192%	compute subgraphs of goals connected   through variables _after_
 193%	executing the first goal.
 194
 195reorder(Goal, Reordered) :-
 196	State = bindings([]),
 197	conj_to_list(Goal, Conj0),
 198	reorder_conj(Conj0, State, Conj1),
 199	list_to_conj(Conj1, Reordered),
 200	arg(1, State, Bindings),
 201	unbind(Bindings).
 202
 203reorder_conj([One], _, [One]) :- !.
 204reorder_conj(List, State, Perm) :-
 205	group_by_cut(List, Before, Cut, After), !,
 206	reorder_conj(Before, State, PermBefore),
 207	bind_args(Before, State),		% this part is done
 208	reorder_conj(After, State, PermAfter),
 209	append(PermBefore, [Cut|PermAfter], Perm).
 210reorder_conj(List, State, Perm) :-
 211	group_by_optional(List, Normal, Optional), !,
 212	reorder_conj(Normal, State, PermNormal),
 213	bind_args(Normal, State),		% this part is done
 214	reorder_conj(Optional, State, PermOptional),
 215	append(PermNormal, PermOptional, Perm).
 216reorder_conj(List, State, [goal(_,independent(SubPerms),_)]) :-
 217	make_subgraphs(List, SubGraphs),
 218	SubGraphs \= [_], !,
 219	reorder_subgraph_conjs(SubGraphs, State, SubPerms).
 220reorder_conj(List, State, [Prefix|Perm]) :-
 221	select(Prefix, List, Rest),
 222	bind_args(Prefix, State),
 223	make_subgraphs(Rest, SubGraphs),
 224	(   SubGraphs = [SubGraph]
 225	->  reorder_conj2(SubGraph, State, Perm)
 226	;   Perm = [goal(_,independent(SubPerms),_)],
 227	    reorder_subgraph_conjs(SubGraphs, State, SubPerms)
 228	).
 229
 230
 231%%	reorder_subgraph_conjs(SubGraphs, -ReorderedSubGraphs)
 232%
 233%	Reorder  the  individual  subgraphs.  As    we  know  these  are
 234%	independent there is no need to  order the subgraphs themselves.
 235%	we also know they are  fully   connected,  and no longer contain
 236%	cuts, so we use the simplified  version of reorder_conj/2 called
 237%	reorder_conj2/2.
 238
 239reorder_subgraph_conjs([], _, []).
 240reorder_subgraph_conjs([H0|T0], State, [H|T]) :-
 241	reorder_conj2(H0, State, H1),
 242	list_to_conj(H1, H),
 243	reorder_subgraph_conjs(T0, State, T).
 244
 245reorder_conj2([One], _, [One]) :- !.
 246reorder_conj2(List, State, [Prefix|Perm]) :-
 247	select(Prefix, List, Rest),
 248	bind_args(Prefix, State),
 249	make_subgraphs(Rest, SubGraphs),
 250	(   SubGraphs = [SubGraph]
 251	->  reorder_conj2(SubGraph, State, Perm)
 252	;   Perm = [goal(_,independent(SubPerms),_)],
 253	    reorder_subgraph_conjs(SubGraphs, State, SubPerms)
 254	).
 255
 256%%	group_by_cut(+List, -BeforeCut, -Cut, -AfterCut)
 257%
 258%	Split the conjunction over a  cut   (!)  as  we cannot guarantee
 259%	proper behaviour when moving goals to the other side of a cut.
 260
 261group_by_cut(Goals, Before, Cut, After) :-
 262	Cut = goal(_, !, _),
 263	append(Before, [Cut|After], Goals), !.
 264
 265
 266%%	group_by_optional(+List, -NotOptional, -Optional)
 267%
 268%	Split the conjunction in optional and non-optional part as we
 269%	always want the optional part to happen last.
 270
 271group_by_optional(List, Normal, Optional) :-
 272	split_optional(List, Normal, Optional),
 273	Normal \== [],
 274	Optional \== [].
 275
 276split_optional([], [], []).
 277split_optional([H|T0], Normal, Optional) :-
 278	(   optional(H)
 279	->  Optional = [H|T],
 280	    split_optional(T0, Normal, T)
 281	;   Normal = [H|T],
 282	    split_optional(T0, T, Optional)
 283	).
 284
 285optional(G) :-
 286	goal(G, (_*->_;_)).
 287
 288%%	bind_args(Goal, !State)
 289%
 290%	Bind all arguments in  Goal  or   list  of  goals.  Assumes that
 291%	executing a goal grounds all its arguments.   Only  the goal A =
 292%	literal(B), generated by optimising where constraints is handled
 293%	special.
 294%
 295%	State is a term bindings(List)  that is destructively maintained
 296%	by instantiate/4.
 297
 298bind_args([], _) :- !.
 299bind_args([H|T], State) :- !,
 300	bind_args(H, State),
 301	bind_args(T, State).
 302bind_args(H, State) :-
 303	goal(H, A=literal(B)),
 304	var(A), var(B), !,
 305	(   instantiated(A, I),
 306	    I \== (-)
 307	->  instantiate(B, _, I, State)
 308	;   instantiated(B, I),
 309	    I \== (-)
 310	->  instantiate(A, _, I, State)
 311	;   true
 312	).
 313bind_args(Goal, State) :-
 314	vars(Goal, Vars),
 315	ground_vars(Vars, State).
 316
 317ground_vars([], _).
 318ground_vars([H|T], State) :-
 319	instantiate(H, _, r, State),
 320	ground_vars(T, State).
 321
 322
 323%%	make_subgraphs(+Goals, -SubGraphs)
 324%
 325%	Create a list of connected subgraphs   from  Goals, assuming the
 326%	variables in the assoc Grounded have been bound.
 327%
 328%	@param Goals is a list goal(Id, Goal, Vars).
 329
 330make_subgraphs([Goal], [[Goal]]) :- !.
 331make_subgraphs([G1,G2], Graphs) :- !,
 332	unbound_vars(G1, V1),
 333	unbound_vars(G2, V2),
 334	(   ord_intersect(V1, V2)
 335	->  Graphs = [[G1,G2]]
 336	;   Graphs = [[G1],[G2]]
 337	).
 338make_subgraphs(Goals, SubGraphs) :-
 339	map_list_to_pairs(unbound_vars, Goals, UnBoundKeyed),
 340	connected_pairs(UnBoundKeyed, Edges),
 341	vertices_edges_to_ugraph(Goals, Edges, UGraph),
 342	connected_vertices(UGraph, SubGraphs).
 343
 344connected_pairs([], []).
 345connected_pairs([H|T], Edges) :-
 346	connected_pairs(T, H, Edges, EdgeTail),
 347	connected_pairs(T, EdgeTail).
 348
 349connected_pairs([], _, Edges, Edges).
 350connected_pairs([H|T], To, Edges, EdgeTail) :-
 351	(   connected(H, To, GH, GT)
 352	->  Edges = [GH-GT,GT-GH|Edges1],
 353	    connected_pairs(T, To, Edges1, EdgeTail)
 354	;   connected_pairs(T, To, Edges, EdgeTail)
 355	).
 356
 357connected(V1-G1, V2-G2, G1, G2) :-
 358	ord_intersect(V1, V2).
 359
 360connected_vertices([], []) :- !.
 361connected_vertices(UGraph, [Set1|Sets]) :-
 362	UGraph = [V1-_|_],
 363	reachable(V1, UGraph, Set1),
 364	del_vertices(UGraph, Set1, UGraph2),
 365	connected_vertices(UGraph2, Sets).
 366
 367
 368%%	unbound_vars(+Goal, -Vars) is det.
 369%
 370%	True when Vars is an ordered set of unbound variables in Goal.
 371
 372unbound_vars(Goal, Vars) :-
 373	vars(Goal, AllVars),
 374	unbound(AllVars, Vars0),
 375	sort(Vars0, Vars).
 376
 377unbound([], []).
 378unbound([H|T0], [H|T]) :-
 379	instantiated(H, -), !,
 380	unbound(T0, T).
 381unbound([_|T0], T) :-
 382	unbound(T0, T).
 383
 384%%	conj_to_list(+Conj, -List)
 385%
 386%	Translate a conjunction into a list of elements of the format
 387%
 388%%		goal(Id, Goal, Vars)
 389%
 390%	Where Id is a goal identifier, Goal  is the goal itself and Vars
 391%	is a list of variables inside the  Goal. Variables are sorted to
 392%	the standard order of terms.
 393
 394conj_to_list(Conj, List) :-
 395	phrase(conj_to_list(Conj, 1, _), List).
 396
 397conj_to_list((A,B), N0, N) --> !,
 398	conj_to_list(A, N0, N1),
 399	conj_to_list(B, N1, N).
 400conj_to_list(true, N, N) --> !,
 401	[].
 402conj_to_list(G, N0, N) -->
 403	{ term_variables(G, Vars0),
 404	  sort(Vars0, Vars),
 405	  N is N0 + 1
 406	},
 407	[ goal(N0, G, Vars)
 408	].
 409
 410
 411%%	list_to_conj(+List, -Conj)
 412
 413
 414list_to_conj([], true).
 415list_to_conj([goal(_,G,_)], G) :- !.
 416list_to_conj([goal(_,G,_)|T0], (G,T)) :-
 417	list_to_conj(T0, T).
 418
 419
 420%%	id(G, Id) is det.
 421%%	goal(G, Goal) is det.
 422%%	vars(G, Vars) is det.
 423%
 424%	Extract fields from the goal structure.
 425
 426%id(goal(Id, _, _),     Id).
 427goal(goal(_, Goal, _), Goal).
 428vars(goal(_, _, Vars), Vars).
 429
 430
 431		 /*******************************
 432		 *	       BINDING		*
 433		 *******************************/
 434
 435/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 436Keep track of binding status of a   variable.  There are basically three
 437ways. One is to use a seperate   (assoc) table. Alternatively we can use
 438attributed variables and delete the attributes   at the end, and finally
 439we can use normal variables and recreate  the original goal by unbinding
 440them again.
 441
 442Using assocs requires us to pass  these   things  around  and involves a
 443log(N) complexity lookup. Using real terms  has the disadvantage that to
 444unbind we have to copy the term, thus  loosing bindings it may have with
 445the environment.  Using attributes suffers neither of these problems and
 446its only drawback is relying on non-standard Prolog features.
 447
 448Note that the remainder of the  algorithm   uses  sets  organised to the
 449standard order of terms.  As  putting   attributes  does  not change the
 450identity of global stack variables and goals are global stack terms this
 451is guaranteed.
 452- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 453
 454%%	instantiate_obj(+Arg, -Old, +New, !Bindings) is det.
 455%
 456%	Called to change the state of an   RDF object after running some
 457%	goal. Old is the current instantiation.  After executing the RDF
 458%	Goal the new instantiation is New (typically =b=). Bindings is a
 459%	term  bindings(List),  which  is    updated   using  destructive
 460%	assignment. List is the list of all  variables to which we added
 461%	attributes.
 462
 463instantiate_obj(Arg, Old, New, Bindings) :-
 464	(   var(Arg)
 465	;   ground(Arg)
 466	), !,
 467	instantiate(Arg, Old, New, Bindings).
 468instantiate_obj(literal(Pattern, Var), +(Pattern), New, Bindings) :-
 469	instantiate(Var, _, New, Bindings).
 470
 471instantiate(Var, Old, New, Bindings) :-
 472	instantiated(Var, Old),
 473	(   Old == (-)
 474	->  put_attr(Var, instantiated, New),
 475	    arg(1, Bindings, B0),
 476	    setarg(1, Bindings, [Var|B0])
 477	;   true
 478	).
 479
 480instantiated(Term, How) :-
 481	(   nonvar(Term)
 482	->  How = +(+)
 483	;   get_attr(Term, instantiated, H)
 484	->  How = +(H)
 485	;   How = -
 486	).
 487
 488uninstantiate(Term, How) :-
 489	(   get_attr(Term, instantiated, How)
 490	->  del_attr(Term, instantiated)
 491	;   true
 492	).
 493
 494%%	instantiate_unify(A, B, State) is det.
 495%
 496%	We encounter a plain unification. Ideally, we determine the most
 497%	specific binding and propagate this and execute the unification.
 498%	The latter however must be undone  if we evaluate a disjunction.
 499%	For now, the code is somewhat   simplified  and we merely decide
 500%	that if one  side  is  instantiated,   the  other  will  be too,
 501%	disregarding the actual value we might know.
 502
 503instantiate_unify(A, B, State) :-
 504	instantiated(B, +(_)), !,
 505	instantiate(A, _, b, State).
 506instantiate_unify(A, B, State) :-
 507	instantiated(A, +(_)), !,
 508	instantiate(B, _, b, State).
 509instantiate_unify(_, _, _).
 510
 511
 512%%	attr_unify_hook(+Attribute, +Value)
 513%
 514%	For now, the attribute unify hook   only  allows unifying with a
 515%	variable  with  the  same  attribute.    This   deals  with  the
 516%	unification that takes place in rdf_optimise/3 for the variables
 517%	of the saved copy.
 518
 519instantiated:attr_unify_hook(Attr, Value) :-
 520	get_attr(Value, instantiated, Attr).
 521
 522instantiated:attr_portray_hook(Value, _Var) :-
 523	write(+(Value)).
 524
 525%%	instantiate_term(+Term)
 526%
 527%	Instantiate all unbound variables
 528
 529instantiate_term(Term, How) :-
 530	compound(Term), !,
 531	functor(Term, _, Arity),
 532	instantiate_args(Arity, Term, How).
 533instantiate_term(Term, How) :-
 534	(   var(Term),
 535	    \+ get_attr(Term, instantiated, _)
 536	->  put_attr(Term, instantiated, How)
 537	;   true
 538	).
 539
 540instantiate_args(0, _, _) :- !.
 541instantiate_args(N, Term, How) :-
 542	arg(N, Term, A),
 543	instantiate_term(A, How),
 544	N2 is N - 1,
 545	instantiate_args(N2, Term, How).
 546
 547
 548%%	uninstantiate_term(+Term, +How)
 549%
 550%	Remove all skolem instantiations from Term.
 551
 552uninstantiate_term(Term, How) :-
 553	compound(Term), !,
 554	functor(Term, _, Arity),
 555	uninstantiate_args(Arity, Term, How).
 556uninstantiate_term(Term, How) :-
 557	uninstantiate(Term, How).
 558
 559uninstantiate_args(0, _, _) :- !.
 560uninstantiate_args(N, Term, How) :-
 561	arg(N, Term, A),
 562	uninstantiate_term(A, How),
 563	N2 is N - 1,
 564	uninstantiate_args(N2, Term, How).
 565
 566
 567%%	delete_instantiated(+List, -Unbound)
 568%
 569%	Delete all elements of List that are not instantiated (i.e. var
 570%	and without an instantiated attribute).
 571
 572delete_instantiated([], []).
 573delete_instantiated([H|T0], L) :-
 574	(   instantiated(H, -)
 575	->  L = [H|T],
 576	    delete_instantiated(T0, T)
 577	;   delete_instantiated(T0, L)
 578	).
 579
 580
 581		 /*******************************
 582		 *	    COMPLEXITY		*
 583		 *******************************/
 584
 585%%	rdf_complexity(+GoalIn, -GoalOut, -SpaceEstimate, -TimeEstimate)
 586%
 587%	Provide an estimate  for  the  size   of  the  search  space for
 588%	executing Goal. For this we  estimate   the  branching factor of
 589%	each subgoal in the conjunction. If   the  branching factors are
 590%	B0, B1, ... then the total complexity estimate is
 591%
 592%		E = 1 + B0 + B0*B1 + B0*B1*B2, ...
 593%
 594%	Non-RDF calls are supposed  to  be   boolean  tests  that can be
 595%	executed at the first opportunity all arguments are bound by RDF
 596%	calls. They have a probability of   failure, reducing the search
 597%	space. Using the above formula, any   number  lower than 1 moves
 598%	the test as far as possible to the head of the conjunction.
 599%
 600%	If GoalIn and GoalOut are the same   the  system will not try to
 601%	optimize local conjunctions.
 602%
 603%	ISSUES: control structures ;, if-then-else, etc.
 604
 605rdf_complexity(Goal, SpaceEstimate, TimeEstimate) :-
 606	rdf_complexity(Goal, Goal, SpaceEstimate, TimeEstimate).
 607
 608rdf_complexity(Goal0, Goal, Space, Time) :-
 609	State = bindings([]),
 610	complexity(Goal0, Goal, State, 1, Space, 0, Time),
 611	arg(1, State, Bindings),
 612	unbind(Bindings).
 613
 614unbind([]).
 615unbind([H|T]) :-
 616	del_attr(H, instantiated),
 617	unbind(T).
 618
 619%	complexity(:GoalIn, -GoalOut,
 620%		   +State,
 621%		   +SpaceIn, -SpaceOut,
 622%		   +CountIn, -CountOut)
 623%
 624%	Compute the complexity of Goal.  Vars   is  an assoc holding the
 625%	variables bound earlier in the conjunction. Space keeps the size
 626%	of the search space and Count is   the  cummulative count of the
 627%	costs for exploring the search space  espressed in the number of
 628%	nodes that will be visited.
 629%
 630%	The (G*->_=true;_=false) clause deals with   the  code generated
 631%	from optional graph specs as provided by SeRQL.
 632
 633complexity((A0,B0), (A,B), State, Sz0, Sz, C0, C) :- !,
 634	complexity(A0, A, State, Sz0, Sz1, C0, C1),
 635	complexity(B0, B, State, Sz1, Sz, C1, C).
 636complexity((G0*->True;False),
 637	   ( G*->True;False), State, Sz0, Sz, C0, C) :- !,
 638	(   var(G)
 639	->  optimise_order(G0, G, Sz1, C1),
 640	    Sz is Sz0 * Sz1,
 641	    C is C0+Sz0*C1
 642	;   complexity(G, G, State, Sz0, Sz, C0, C)
 643	).
 644complexity((If0->Then0;Else0),		% dubious
 645	   ( If->Then; Else), State, Sz0, Sz, C0, C) :- !,
 646	(   var(If)
 647	->  optimise_order(If0,   If,   Sz1, C1),
 648	    optimise_order(Then0, Then, Sz2, C2),
 649	    optimise_order(Else0, Else, Sz3, C3),
 650	    Sz is max(Sz0 * Sz1 * Sz2, Sz0 * Sz3),
 651	    C  is C0 + max(Sz0*C1+Sz0*Sz1*C2, Sz0*C1+Sz0*Sz1*C3)
 652	;   complexity(If,   If,   State, Sz0, Sz1, C0, C1),
 653	    complexity(Then, Then, State, Sz1, Sz2, C1, C2),
 654	    complexity(Else, Else, State, Sz0, Sz3, C0, C3),
 655	    Sz is max(Sz2, Sz3),
 656	    C  is max(C2, C3)
 657	).
 658complexity((A0;B0), (A;B), State, Sz0, Sz, C0, C) :- !,
 659	(   var(A)
 660	->  optimise_order(A0, A, _, _),	% First try the cheap one?
 661	    optimise_order(B0, B, _, _)
 662	;   A = A0,
 663	    B = B0
 664	),
 665	complexity(A, A, State, Sz0, SzA, C0, CA),
 666	complexity(B, B, State, Sz0, SzB, C0, CB),
 667	Sz is SzA + SzB,
 668	C is CA + CB.
 669complexity(sparql_group(G0), sparql_group(G), State, Sz0, Sz, C0, C) :- !,
 670	(   var(G)
 671	->  rdf_optimise(G0, G, Sz1, C1),
 672	    Sz is Sz0 * Sz1,
 673	    C is C0+Sz0*C1
 674	;   complexity(G, G, State, Sz0, Sz, C0, C)
 675	).
 676complexity(sparql_group(G0, OV, IV),
 677	   sparql_group(G, OV, IV),
 678	   State, Sz0, Sz, C0, C) :- !,
 679	(   var(G)
 680	->  rdf_optimise(G0, G, Sz1, C1),
 681	    Sz is Sz0 * Sz1,
 682	    C is C0+Sz0*C1
 683	;   complexity(G, G, State, Sz0, Sz, C0, C)
 684	).
 685complexity(rdfql_carthesian(Bags),
 686	   rdfql_carthesian(Bags), State, Sz0, Sz, C0, C) :- !,
 687	carth_complexity(Bags, State, Sz0, Sz, C0, 0, C).
 688complexity(independent(Goals0), Goal, State, Sz0, Sz, C0, C) :- !,
 689	independent_complexity(Goals0, Goal, State, Sz0, Sz, C0, 0, C).
 690complexity(Goal, Goal, State, Sz0, Sz, C0, C) :-
 691	Goal = member(Var, List), !,	% List is list of resources
 692	instantiate(Var, _, b, State),
 693	length(List, Branch),
 694	Sz is Sz0 * Branch,
 695	C is C0 + Sz0*0.2 + Sz*0.2.
 696complexity(Goal, Goal, State, Sz, Sz, C0, C) :-
 697	Goal = (A=B), !,
 698	instantiate_unify(A, B, State),
 699	C is C0 + 0.2.
 700complexity(Goal, Goal, State, Sz, Sz, C0, C) :-
 701	Goal = (Var=literal(V)), !,
 702	instantiated(V, +(_)),
 703	instantiate(Var, _, b, State),
 704	C is C0 + 0.2.
 705complexity(Goal, Goal, State, Sz0, Sz, C0, C) :-
 706	rdf_db_goal(Goal, S, P, O), !,
 707	instantiate(S, SI, b, State),
 708	instantiate(P, PI, b, State),
 709	instantiate_obj(O, OI, b, State),
 710	complexity0(SI, PI, OI, P, Goal, SetUp, PerAlt, Branch),
 711	Sz is Sz0 * Branch,
 712	C is C0 + Sz0*SetUp + Sz*PerAlt,
 713	debug(rdf_optimise(complexity), 'Complexity ~p: (~w) ~w --> ~w',
 714	      [i(SI,PI,OI), Goal, Branch, C]).
 715complexity(sparql_eval(E,V), sparql_eval(E,V), _, Sz0, Sz, C0, C) :- !,
 716	term_variables(E, Vars),
 717	all_bound(Vars),
 718	Sz is Sz0,			% probability of failure
 719	C is C0 + Sz*20.		% Sz * CostOfEval
 720complexity(sparql_true(E), sparql_true(E), _, Sz0, Sz, C0, C) :- !,
 721	term_variables(E, Vars),
 722	all_bound(Vars),
 723	Sz is Sz0 * 0.5,		% probability of failure
 724	C is C0 + Sz0*20.		% Sz * CostOfEval
 725complexity(G, G, _, Sz0, Sz, C0, C) :-	% non-rdf tests
 726	term_variables(G, Vars),
 727	all_bound(Vars),
 728	Sz is Sz0 * 0.5,		% probability of failure
 729	C is C0 + Sz0.			% Sz * CostOfTest
 730
 731all_bound([]).
 732all_bound([H|T]) :-
 733	instantiated(H, +(_)),
 734	all_bound(T).
 735
 736%%	carth_complexity(+Bags, +State,
 737%%			 +Size0, -Size,
 738%%			 +Time0, +TimeSum0, -TimeSum) is det.
 739%
 740%	Compute the time and space efficiency of the carthesian product.
 741%	the total cost in time is the sum  of the costs of all branches,
 742%	The search space at the end is still the same product.
 743
 744carth_complexity([], _, Sz, Sz, _, C, C).
 745carth_complexity([bag(_,G)|T], State,
 746		 Sz0, Sz,
 747		 C0, Csum0, Csumz) :-
 748	complexity(G, G, State, Sz0, Sz1, C0, C1),
 749	Csum1 is Csum0 + C1,
 750	carth_complexity(T, State, Sz1, Sz, C0, Csum1, Csumz).
 751
 752
 753%%	independent_complexity(+GoalsIn, -Goals, +State,
 754%%			       +Size0, -Size,
 755%%			       +Time0, +TimeSum0, -TimeSum) is det.
 756%
 757%	Compute the complexity of an independent conjunction.
 758%
 759%	@param Goals is a list of g(Goal, Branch, Cost), where Branch is
 760%	the number of solutions expected fromGoal and Cost is the
 761%	estimated CPU time.
 762
 763independent_complexity(GoalsIn, Goal, State,
 764		       Size0, Size,
 765		       Time0, TimeSum0, TimeSum) :-
 766	indep_complexity(GoalsIn, Goals1, State,
 767			 Size0, Size,
 768			 Time0, TimeSum0, TimeSum),
 769	keysort(Goals1, ByCost),
 770	pairs_values(ByCost, Goals2),
 771	simplify_carthesian(Goals2, Goal).
 772
 773indep_complexity([], [], _, Sz, Sz, _, C, C).
 774indep_complexity([G0|GT0], [SzG-bag(Vars,G,SzG,CG)|GT], State,
 775		       Sz0, Sz,
 776		       C0, Csum0, Csumz) :-
 777	complexity(G0, G, State, Sz0, Sz1, C0, C1),
 778	term_variables(G, VList),
 779	Vars =.. [v|VList],
 780	(   Sz0 =:= 0
 781	->  SzG = 0,
 782	    CG = 0
 783	;   SzG is Sz1/Sz0,
 784	    CG is (C1-C0)/Sz0
 785	),
 786	Csum1 is Csum0 + C1,
 787	indep_complexity(GT0, GT, State, Sz1, Sz, C0, Csum1, Csumz).
 788
 789%%	simplify_carthesian(+Bags, -Goal) is det.
 790%
 791%	Peer of the leading little branching   goals from the carthesian
 792%	handler. If there is a bag of one   left,  turn it into a normal
 793%	goal.
 794
 795simplify_carthesian([], true).
 796simplify_carthesian([bag(_,Goal,_Branch,_Cost)], Goal) :- !.
 797simplify_carthesian([bag(_,Goal,Branch,_Cost)|Bags], Final) :-
 798	(   Branch < 1.5
 799	->  !, Final = (Goal, Final1),
 800	    simplify_carthesian(Bags, Final1)
 801	;   Bags == []
 802	->  !, Final = Goal
 803	).
 804simplify_carthesian(Bags, rdfql_carthesian(Bags)).
 805
 806
 807%%	complexity0(+SI, +PI, +OI, +P, +G, -Setup, -PerAlt, -Branch).
 808%
 809%	SI,PI,OI describe the instantiation pattern.  P is the predicate
 810%	and G is the actual goal. Complexity   is unified to an estimate
 811%	of the size of the search space and therefore an estimate of the
 812%	execution time required to prove the goal.
 813%
 814%	Literal `like' matches come  out   as  +(like(Pattern)). We must
 815%	estimate the percentage of the literals that match this pattern.
 816%	Suppose the factor is 1,000. This means the branching is reduced
 817%	by 1,000, but finding each solution  is   slow  as it requires a
 818%	linear scan. It is faster than going  all the way back to Prolog
 819%	backtracking however, so we estimate a   factor  10 (TBD: verify
 820%	that number).
 821%
 822%	ISSUES: rdf_has/3 vs rdf_reachable/3.
 823
 824complexity0(+(_),+(_),+(_), _, _, 1, 0, 1) :- !.
 825complexity0(+(b),+(+),-, P, G, 1, 1, B) :- !,
 826	subj_branch_factor(G, B, Prop),
 827	rdf_predicate_property(P, Prop).
 828complexity0(-,+(+),+(b), P, G, 1, 1, B) :- !,
 829	obj_branch_factor(G, B, Prop),
 830	rdf_predicate_property(P, Prop).
 831complexity0(+(b), -, -, _, _, 1, 1, B) :- !,
 832	rdf_statistics(triples(Total)),
 833	subject_count(Subjs),
 834	(   Total == 0
 835	->  B = 0
 836	;   B is Total/Subjs
 837	).
 838complexity0(_,_,+(like(Pat)),_, G, Factor, Factor, B) :- !,
 839	rdf_estimate_complexity(G, B0),
 840	pattern_filter(Pat, Factor0),
 841	Factor is max(1, min(B0, Factor0)/10),
 842	B is B0/Factor.
 843complexity0(_,_,_, _, G, 1, 1, B) :-
 844	rdf_estimate_complexity(G, B).
 845
 846:- if(rdf_statistics(subjects(_))).
 847subject_count(Count) :-				% RDF-DB 2.x
 848	rdf_statistics(subjects(Count)).
 849:- else.
 850subject_count(Count) :-				% RDF-DB 3.x
 851	rdf_statistics(resources(Count)).
 852:- endif.
 853
 854:- multifile
 855        subj_branch_factor/3,
 856        obj_branch_factor/3.
 857
 858subj_branch_factor(rdf(_,_,_),           X, rdf_subject_branch_factor(X)).
 859subj_branch_factor(rdf_has(_,_,_),       X, rdfs_subject_branch_factor(X)).
 860subj_branch_factor(rdf_reachable(_,_,_), X, rdfs_subject_branch_factor(X)).
 861
 862obj_branch_factor(rdf(_,_,_),            X, rdf_object_branch_factor(X)).
 863obj_branch_factor(rdf_has(_,_,_),        X, rdfs_object_branch_factor(X)).
 864obj_branch_factor(rdf_reachable(_,_,_),  X, rdfs_object_branch_factor(X)).
 865
 866
 867%%	rdf_db_goal(+Goal, -Subject, -Predicate, -Object)
 868%
 869%	True if Goal is a pure (logical)   predicate on the RDF database
 870%	involving the given  Subject,  Predicate   and  Object.  Defined
 871%	multifile, allowing the  optimiser   to  understand user-defined
 872%	rdf/3 like predicates.
 873%
 874%	@tbd	Allow specifying different costs and branching factors
 875
 876:- multifile
 877	rdf_db_goal/4.
 878
 879rdf_db_goal(rdf(S,P,O),			S,P,O).
 880rdf_db_goal(rdf_has(S,P,O),		S,P,O).
 881rdf_db_goal(rdf_reachable(S,P,O),	S,P,O).
 882rdf_db_goal(rdf(S,P,O, _DB),		S,P,O). % TBD: less hits
 883
 884%%	pattern_filter(+Like, -Factor)
 885%
 886%	Estimate the efficiency of a pattern. This is a bit hard, as
 887%	characters are not independent.
 888
 889pattern_filter(Like, Factor) :-
 890	atom_codes(Like, Codes),
 891	pattern_factor(Codes, 1, Factor).
 892
 893pattern_factor([], F, F).
 894pattern_factor([0'*|T], F0, F) :- !,
 895	pattern_factor(T, F0, F).
 896pattern_factor([_|T], F0, F) :-
 897	F1 is F0*10,
 898	pattern_factor(T, F1, F).
 899
 900
 901%%	rdf_estimate_complexity(+Goal, -Complexity)
 902%
 903%	Estimate the branching factor introduced by  Goal. This uses the
 904%	rdf_db  statistics  of  the  hash  chains  which  are  based  on
 905%	exploiting the RDFS subPropertyOf reasoning.
 906%
 907%	In addition, rdf_reachable/3 introduces its own complexity which
 908%	must be estimate using the branching factor of the relation.
 909
 910rdf_estimate_complexity(G, C) :-
 911	rdf_db_goal(G, S, P0, O),
 912	map_predicate(P0, P),
 913	rdf_estimate_complexity(S, P, O, C).
 914
 915map(map_predicate(_,_)).
 916map(map_predicate(_,_):-_).
 917
 918term_expansion(In, Out) :-
 919	map(In), !,
 920	rdf_global_term(In, Out).
 921
 922map_predicate(X, X) :-
 923	var(X), !.
 924map_predicate(serql:directSubClassOf,    rdfs:subClassOf) :- !.
 925map_predicate(serql:directType,          rdf:type) :- !.
 926map_predicate(serql:directSubPropertyOf, rdfs:subPropertyOf) :- !.
 927map_predicate(X, X).
 928
 929
 930		 /*******************************
 931		 *     INSTANTIATE OPTIONAL	*
 932		 *******************************/
 933
 934/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 935In  SELECT  queries,  optional  parts  of   the  path  expression  leave
 936uninstantiated variables. These must be bound to  '$null$' to be able to
 937do correct merging for  DISTINCT.  The  naive   way  to  do  this  is to
 938instantiate all variables at the end  of   the  query.  On large selects
 939(i.e. involving many variables) this appears to be quite costly.  Doing
 940the job early, as in
 941
 942	(   Goal
 943	*-> true
 944	;   bind_null(VarsInGoal)
 945	)
 946
 947is not correct as well, as VarsInGoal may  be involved in other parts of
 948the code either before or after the optional path expression. So we need
 949to:
 950
 951	* Do abtract execution and find the bindings done before arriving
 952	  at Goal.
 953	* continue the execution, and watch for new bindings to these
 954	  variables.  If we find a binding for the second time, remove
 955	  it from the first and make a conditional binding for it.
 956
 957If we bind an argument unconditionally, we place an attribute 'b'. If it
 958is conditionally bound, we place an attribute   c(Set), where Set is the
 959set in which it was bound or plain  'c' if it was conditionally bound in
 960multiple places.
 961
 962TBD:	disjunctions and other control structures.
 963	queries that do not bind (such as SPARQL bound(X))
 964- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 965
 966serql_select_bind_null(Goal0, Goal) :-
 967	State = bindings([]),
 968	select_bind_null(Goal0, Goal1, State),
 969	arg(1, State, Bindings),
 970	c_unbind(Bindings, Left),
 971	(   Left == []
 972	->  Goal = Goal1
 973	;   Goal = (Goal1, rdfql_cond_bind_null(Left))
 974	).
 975
 976c_unbind([], []).
 977c_unbind([H|T0], L) :-
 978	(   get_attr(H, instantiated, c)
 979	->  L = [H|T]
 980	;   L = T
 981	),
 982	del_attr(H, instantiated),
 983	c_unbind(T0, T).
 984
 985
 986select_bind_null((A0,B0), (A,B), State) :- !,
 987	select_bind_null(A0, A, State),
 988	select_bind_null(B0, B, State).
 989select_bind_null((G0 *-> true; true),
 990		 ( G *-> true; Bind),
 991		 State) :- !,
 992	arg(1, State, B0),
 993	select_bind_null(G0, G, State),
 994	arg(1, State, B),
 995	Bind = rdfql_bind_null(Vars),
 996	c_bindings(B, B0, c(Bind), Vars).
 997select_bind_null(rdfql_carthesian(List0),
 998		 rdfql_carthesian(List), State) :- !,
 999	select_carth_bind_null(List0, List, State).
1000select_bind_null(sparql_group(A0), sparql_group(A), State) :- !,
1001	select_bind_null(A0, A, State).
1002select_bind_null(sparql_group(A0,Outer,Inner),
1003		 sparql_group(A, Outer,Inner),
1004		 State) :- !,
1005	select_bind_null(A0, A, State),
1006	term_variables(Outer, Vars),
1007	c_bind(Vars, State).
1008select_bind_null(Goal, Goal, State) :-
1009	term_variables(Goal, Vars),
1010	c_bind(Vars, State).
1011
1012%%	c_bindings(+AtEnd, +AtStart, +CVars, -Vars)
1013%
1014%	The  variables  of  the    difference-list   AtEnd..AtStart  are
1015%	conditionally bound. Tag each such  variable with CVars.
1016%
1017%	@param CVars	Term c(Vars), where Vars are the other variables
1018%			that have the same conditional binding.
1019
1020c_bindings(B, B0, _, Vars)  :-
1021	B == B0, !,
1022	Vars = [].
1023c_bindings([H|T0], B0, Attr, [H|Vars]) :-
1024	get_attr(H, instantiated, I),
1025	is_instantiated(I), !,
1026	put_attr(H, instantiated, Attr),
1027	c_bindings(T0, B0, Attr, Vars).
1028c_bindings([_|T0], B0, Attr, Vars) :-
1029	c_bindings(T0, B0, Attr, Vars).
1030
1031
1032is_instantiated(b).		% unconditionally bound
1033is_instantiated(c(_)).		% bound either by call or rdfql_bind_null/1
1034
1035
1036%%	c_bind(+Vars, +State)
1037%
1038%	Do  unconditional  binding  of   Vars.   Var    may   be   in  a
1039%	rdfql_bind_null/1 set. In that case, delete it from the set, and
1040%	set the class to 'c' to make a conditional binding at the end.
1041
1042c_bind([], _).
1043c_bind([H|T], State) :-
1044	(   get_attr(H, instantiated, I)
1045	->  (   I == b			% already unconditionally bound
1046	    ->	true
1047	    ;	I = c(Set)
1048	    ->	arg(1, Set, Vars0),
1049		del_var(H, Vars0, Vars),
1050		setarg(1, Set, Vars),
1051		put_attr(H, instantiated, c)
1052	    ;	I == c
1053	    ->	true
1054	    )
1055	;   put_attr(H, instantiated, b),
1056	    arg(1, State, B0),
1057	    setarg(1, State, [H|B0])
1058	),
1059	c_bind(T, State).
1060
1061del_var(H, [X|T0], T) :-
1062	(   H == X
1063	->  T = T0
1064	;   T = [X|T1],
1065	    del_var(H, T0, T1)
1066	).
1067
1068select_carth_bind_null([], [], _).
1069select_carth_bind_null([bag(Vars, G0)|T0], [bag(Vars, G)|T], State) :-
1070	select_bind_null(G0, G, State),
1071	select_carth_bind_null(T0, T, State).
1072
1073
1074		 /*******************************
1075		 *	  DEBUG SUPPORT		*
1076		 *******************************/
1077
1078dbg_portray_body(G) :-
1079	debugging(rdf_optimise), !,
1080	portray_body(G).
1081dbg_portray_body(_).
1082
1083portray_body(G) :-
1084	(   pp_instantiate_term(G),
1085	    debug(_, '~@',
1086		  [ portray_clause(current_output, (<> :- G), [module(sparql_runtime)])
1087		  ]),
1088	    fail
1089	;   true
1090	).
1091
1092pp_instantiate_term(Term) :-
1093	compound(Term), !,
1094	functor(Term, _, Arity),
1095	pp_instantiate_args(Arity, Term).
1096pp_instantiate_term(Term) :-
1097	var(Term),
1098	get_attr(Term, instantiated, H), !,
1099	del_attr(Term, instantiated),
1100	Term = +(H).
1101pp_instantiate_term(_).
1102
1103pp_instantiate_args(0, _) :- !.
1104pp_instantiate_args(N, Term) :-
1105	arg(N, Term, A),
1106	pp_instantiate_term(A),
1107	N2 is N - 1,
1108	pp_instantiate_args(N2, Term).
1109
1110
1111		 /*******************************
1112		 *	      SANDBOX		*
1113		 *******************************/
1114
1115:- multifile sandbox:safe_meta_predicate/1.
1116
1117sandbox:safe_meta_predicate(rdf_optimise:rdf_optimise/1).