1:- module(graphviz, []).
@ true. % ?- graphvizsld2.
    9%%% Plotting terms as trees using Graphviz %%%
   10
   11term_list_linear(false).	% change to true for plotting lists linearly
   12
   13term(Term):-
   14	gv_start('term.dot'),
   15	Term =.. [Functor|Subterms],
   16	gv_root(Functor,0),
   17	term_list(Subterms,0),
   18	gv_stop.
   19
   20term(Term,N):-
   21	var(Term),!,
   22	gv_node(N,Term,_).
   23term(Term,N):-
   24	term_list_linear(true),
   25	list(Term),!,
   26	gv_node(N,Term,N1),
   27	term_list(Term,N1).
   28term([],N):-!,
   29	gv_node(N,'$empty_list',_).
   30term(Term,N):-
   31	Term =.. [Functor|Subterms],
   32	gv_node(N,Functor,N1),
   33	term_list(Subterms,N1).
   34
   35term_list([],_).
   36term_list([Term|Terms],N):-
   37	term(Term,N),
   38	term_list(Terms,N).
   39
   40% testing
   41term1:-term([a,b,b,a]).
   42term2:-term(
   43html(head(title('Peter A. Flach')),
   44	body([img([align=right,src='logo.jpg']),
   45		img([align=left,src='peter.jpg']),
   46		h1('Peter Flach\'s homepage'),
   47		h2('Research interests'),
   48		ul([li('Learning from structured data'),
   49			bla,
   50			li(a([href='CV.pdf'],'Full CV'))]),
   51		h2('Current activities'),
   52		bla,
   53		h2('Past activities'),
   54		bla,
   55		h2('Archives'),
   56		bla,
   57		hr,address(bla)
   58	    ])
   59         )
   60).
   61
   62% ?- graphviz: (sld(append([a],[b],X))).
   63
   64%%% Meta-interpreter plotting (part of) the SLD-tree using Graphviz %%%
   65
   66:-op(1100,fx,sld).	% can write ?-sld Goal instead of ?-sld(Goal)
   67
   68sld(Goal):-
   69	sld(Goal,5).	% default depth bound
   70
   71sld(Goal,D):-
   72	gv_start('sld.dot'),
   73	gv_root((?-Goal),0),
   74	prove_d(Goal,Goal,0,D),
   75	fail.	% failure-driven loop to get all solutions
   76sld(_,_):-
   77	gv_stop.
   78
   79% meta-interpreter with complete resolvent and depth bound
   80prove_d(true,Goal,N,_):-!,
   81	gv_answer(N,Goal).
   82prove_d((A,B),Goal,N,D):-!,
   83	D>0, D1 is D-1,
   84	resolve(A,C),
   85	conj_append(C,B,E),
   86	gv_node(N,(:-E),N1),
   87	prove_d(E,Goal,N1,D1).
   88prove_d(A,Goal,N,D):-
   89	D>0, D1 is D-1,
   90	resolve(A,B),
   91	gv_node(N,(:-B),N1),
   92	prove_d(B,Goal,N1,D1).
   93
   94resolve(A,true):-
   95	predicate_property(A,built_in),!,
   96	call(A).
   97resolve(A,B):-
   98	clause(A,B).
   99
  100% testing
  101student_of(X,T):-follows(X,C),teaches(T,C).
  102follows(paul,computer_science).
  103follows(paul,expert_systems).
  104follows(maria,ai_techniques).
  105teaches(adrian,expert_systems).
  106teaches(peter,ai_techniques).
  107teaches(peter,computer_science).
  108
  109brother_of(paul,peter).
  110brother_of(peter,adrian).
  111brother_of(X,Y):-brother_of(X,Z),brother_of(Z,Y).
  112brother_of(X,Y):-brother_of(Y,X).
  113
  114sld1:-sld student_of(_,peter).
  115sld2:-sld brother_of(paul,_).
  116
  117
  118%%% Utilities %%%
  119
  120list([]).
  121list([_|T]):-list(T).
  122
  123conj_element(X,X):-	% single-element conjunction
  124	X \= true,
  125	X \= (_,_).
  126conj_element(X,(X,_)).
  127conj_element(X,(_,Ys)):-
  128	conj_element(X,Ys).
  129
  130conj_append(true,Ys,Ys).
  131conj_append(X,Ys,(X,Ys)):-	% single-element conjunction
  132	X \= true, 
  133	X \= (_,_).
  134conj_append((X,Xs),Ys,(X,Zs)):-
  135	conj_append(Xs,Ys,Zs).
  136
  137writes([]):-!,nl.
  138writes([H|T]):-!,writes(H),writes(T).
  139writes((A,B)):-!,writes(A),write(',\\n'),writes(B).	% break up conjunctions
  140writes(:-A):-!,write(':-'),writes(A).
  141writes(?-A):-!,write('?-'),writes(A).
  142writes('$empty_list'):-!,write([]).
  143writes(A):-write(A).	% catch-all
  144	
  145	
  146%%% Graphviz utilities %%%
  147
  148gv_max_id(1000).	% max number of nodes in the graph
  149
  150% open file and start new graph
  151gv_start(FileName):-
  152	tell(FileName),
  153	writes(['digraph {']),
  154	%writes(['graph [size="4,6"];']),
  155	writes(['node [shape=plaintext, fontname=Courier, fontsize=12]']).
  156
  157% next graph
  158gv_next:-
  159	writes(['}']),
  160	writes(['digraph {']),
  161	writes(['node [shape=plaintext, fontname=Courier, fontsize=12]']).
  162
  163% finish graph and close file
  164gv_stop:-
  165	writes(['}']),
  166	told.
  167
  168% start new subgraph
  169gv_cluster_start:-
  170	( retract('$gv_cluster'(N)) -> N1 is N+1
  171	; otherwise -> N1=0
  172	),assert('$gv_cluster'(N1)),
  173	writes(['subgraph cluster_',N1,' {']),
  174	writes(['[style=filled, color=lightgrey];']),
  175	writes(['node [style=filled,color=white];']).
  176
  177% finish subgraph 
  178gv_cluster_stop:-
  179	writes(['}']).
  180
  181% write the root of a tree and initialise node IDs
  182gv_root(L,N):-
  183	writes([N,' [label="',L,'"];']),
  184	gv_init_ids(N).
  185
  186% add a node with label L and parent N0
  187gv_node(N0,L,N):-
  188	gv_id(N),
  189	writes([N,' [label="',L,'"];']),
  190	writes([N0,' -> ',N,';']).
  191
  192% add a specially formatted leaf
  193gv_answer(N0,L):-
  194	gv_id(N),
  195	writes([N,' [label="Answer:\\n',L,'", shape=ellipse, style=dotted, fontsize=10];']),
  196	writes([N0,' -> ',N,' [style=dotted, arrowhead=none];']).
  197	%writes(['{rank=same;',N0,';',N,';}']).
  198
  199% generate a new node ID
  200gv_id(N):-
  201	retract('$gv_id'(N0)),
  202	gv_max_id(M),
  203	N0 < M,	% don't generate infinite graphs
  204	N is N0+1,
  205	assert('$gv_id'(N)).
  206
  207% initialise node IDs, next free ID is N+1
  208gv_init_ids(N) :-
  209	retractall('$gv_id'(_)),
  210	assert('$gv_id'(N))